Skip to main content

fakecloud_managedblockchain/
shared.rs

1//! Primitives shared across the Amazon Managed Blockchain (`managedblockchain`)
2//! handlers: resource-id minting, ARN synthesis, deterministic framework
3//! endpoint derivation and timestamps. Kept in one place so the create / get
4//! paths cannot diverge on wire format.
5
6use rand::Rng;
7
8/// Mint a fresh `26`-character uppercase-alphanumeric resource suffix, the
9/// form Managed Blockchain uses for network / member / node / proposal ids.
10fn suffix26() -> String {
11    const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
12    let mut rng = rand::thread_rng();
13    (0..26)
14        .map(|_| ALPHABET[rng.gen_range(0..ALPHABET.len())] as char)
15        .collect()
16}
17
18/// A network id, `n-<26 uppercase alnum>`.
19pub fn new_network_id() -> String {
20    format!("n-{}", suffix26())
21}
22
23/// A member id, `m-<26 uppercase alnum>`.
24pub fn new_member_id() -> String {
25    format!("m-{}", suffix26())
26}
27
28/// A node id, `nd-<26 uppercase alnum>`.
29pub fn new_node_id() -> String {
30    format!("nd-{}", suffix26())
31}
32
33/// A proposal id, `p-<26 uppercase alnum>`.
34pub fn new_proposal_id() -> String {
35    format!("p-{}", suffix26())
36}
37
38/// An invitation id, `i-<26 uppercase alnum>`.
39pub fn new_invitation_id() -> String {
40    format!("i-{}", suffix26())
41}
42
43/// An accessor id (a UUID, matching the real accessor id form).
44pub fn new_accessor_id() -> String {
45    uuid::Uuid::new_v4().to_string()
46}
47
48/// An accessor billing token (an opaque `40`-char base62 token).
49pub fn new_billing_token() -> String {
50    const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
51    let mut rng = rand::thread_rng();
52    (0..40)
53        .map(|_| ALPHABET[rng.gen_range(0..ALPHABET.len())] as char)
54        .collect()
55}
56
57/// The network ARN. Managed Blockchain networks are global, so the ARN carries
58/// no region and no account.
59pub fn network_arn(id: &str) -> String {
60    format!("arn:aws:managedblockchain:::networks/{id}")
61}
62
63/// The member ARN, `arn:aws:managedblockchain:{region}:{account}:members/{id}`.
64pub fn member_arn(region: &str, account: &str, id: &str) -> String {
65    format!("arn:aws:managedblockchain:{region}:{account}:members/{id}")
66}
67
68/// The node ARN, `arn:aws:managedblockchain:{region}:{account}:nodes/{id}`.
69pub fn node_arn(region: &str, account: &str, id: &str) -> String {
70    format!("arn:aws:managedblockchain:{region}:{account}:nodes/{id}")
71}
72
73/// The proposal ARN,
74/// `arn:aws:managedblockchain:{region}:{account}:proposals/{id}`.
75pub fn proposal_arn(region: &str, account: &str, id: &str) -> String {
76    format!("arn:aws:managedblockchain:{region}:{account}:proposals/{id}")
77}
78
79/// The invitation ARN,
80/// `arn:aws:managedblockchain:{region}:{account}:invitations/{id}`.
81pub fn invitation_arn(region: &str, account: &str, id: &str) -> String {
82    format!("arn:aws:managedblockchain:{region}:{account}:invitations/{id}")
83}
84
85/// The accessor ARN,
86/// `arn:aws:managedblockchain:{region}:{account}:accessors/{id}`.
87pub fn accessor_arn(region: &str, account: &str, id: &str) -> String {
88    format!("arn:aws:managedblockchain:{region}:{account}:accessors/{id}")
89}
90
91/// Current time as an ISO-8601 `date-time` string (the format every Managed
92/// Blockchain timestamp member declares).
93pub fn iso_now() -> String {
94    chrono::Utc::now()
95        .format("%Y-%m-%dT%H:%M:%S%.3fZ")
96        .to_string()
97}
98
99/// The Hyperledger Fabric ordering-service endpoint a network advertises.
100/// Deterministically derived from the network id; well-formed but pointing at
101/// no live orderer (see the crate-level honest gap).
102pub fn fabric_ordering_endpoint(network_id: &str, region: &str) -> String {
103    let short = network_id.trim_start_matches("n-").to_lowercase();
104    format!("orderer.{short}.managedblockchain.{region}.amazonaws.com:30001")
105}
106
107/// The Fabric certificate-authority endpoint a member advertises.
108pub fn fabric_ca_endpoint(member_id: &str, region: &str) -> String {
109    let short = member_id.trim_start_matches("m-").to_lowercase();
110    format!("ca.{short}.managedblockchain.{region}.amazonaws.com:30002")
111}
112
113/// The Fabric peer endpoint a node advertises.
114pub fn fabric_peer_endpoint(node_id: &str, region: &str) -> String {
115    let short = node_id.trim_start_matches("nd-").to_lowercase();
116    format!("{short}.managedblockchain.{region}.amazonaws.com:30003")
117}
118
119/// The Fabric peer-event endpoint a node advertises.
120pub fn fabric_peer_event_endpoint(node_id: &str, region: &str) -> String {
121    let short = node_id.trim_start_matches("nd-").to_lowercase();
122    format!("{short}.managedblockchain.{region}.amazonaws.com:30004")
123}
124
125/// The Ethereum JSON-RPC HTTP endpoint a node advertises.
126pub fn ethereum_http_endpoint(node_id: &str, region: &str) -> String {
127    let short = node_id.trim_start_matches("nd-").to_lowercase();
128    format!("https://{short}.ethereum.managedblockchain.{region}.amazonaws.com")
129}
130
131/// The Ethereum JSON-RPC WebSocket endpoint a node advertises.
132pub fn ethereum_ws_endpoint(node_id: &str, region: &str) -> String {
133    let short = node_id.trim_start_matches("nd-").to_lowercase();
134    format!("wss://{short}.wss.ethereum.managedblockchain.{region}.amazonaws.com")
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn ids_have_expected_prefixes() {
143        assert!(new_network_id().starts_with("n-"));
144        assert_eq!(new_network_id().len(), 28);
145        assert!(new_member_id().starts_with("m-"));
146        assert!(new_node_id().starts_with("nd-"));
147        assert!(new_proposal_id().starts_with("p-"));
148        assert!(new_invitation_id().starts_with("i-"));
149        // Accessor ids are UUIDs.
150        assert_eq!(new_accessor_id().len(), 36);
151        assert_eq!(new_billing_token().len(), 40);
152    }
153}