hopr_internal_types/
account.rs

1use std::fmt::{Display, Formatter};
2
3use hopr_crypto_types::prelude::*;
4use hopr_primitive_types::prelude::*;
5use multiaddr::Multiaddr;
6
7/// Type of the node account.
8#[derive(Clone, Debug, PartialEq, Eq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub enum AccountType {
11    /// Node is not announced.
12    NotAnnounced,
13    /// Node is announced with a multi-address
14    Announced { multiaddr: Multiaddr, updated_block: u32 },
15}
16
17impl Display for AccountType {
18    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
19        match &self {
20            Self::NotAnnounced => {
21                write!(f, "Not announced")
22            }
23            Self::Announced {
24                multiaddr,
25                updated_block,
26            } => f
27                .debug_struct("AccountType")
28                .field("MultiAddr", multiaddr)
29                .field("UpdatedAt", updated_block)
30                .finish(),
31        }
32    }
33}
34
35/// Represents a node announcement entry on the block chain.
36/// This contains node's public key and optional announcement information (multiaddress, block number).
37#[derive(Clone, Debug, PartialEq, Eq)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39pub struct AccountEntry {
40    pub public_key: OffchainPublicKey,
41    pub chain_addr: Address,
42    pub entry_type: AccountType,
43    pub published_at: u32,
44}
45
46impl AccountEntry {
47    /// Gets the block number of the announcement if this peer ID has been announced.
48    pub fn updated_at(&self) -> Option<u32> {
49        match &self.entry_type {
50            AccountType::NotAnnounced => None,
51            AccountType::Announced { updated_block, .. } => Some(*updated_block),
52        }
53    }
54
55    /// Returns the computed key ID for this account.
56    // TODO: change this to use assigned ID from the SC in the next version
57    pub fn key_id(&self) -> KeyIdent<4> {
58        let id_hash = Hash::create(&[
59            self.public_key.as_ref(),
60            self.chain_addr.as_ref(),
61            &self.published_at.to_be_bytes(),
62        ]);
63
64        u32::from_be_bytes(
65            id_hash.as_ref()[0..std::mem::size_of::<u32>()]
66                .try_into()
67                .expect("4 byte must fit into u32"),
68        )
69        .into()
70    }
71
72    /// Is the node announced?
73    pub fn has_announced(&self) -> bool {
74        matches!(self.entry_type, AccountType::Announced { .. })
75    }
76
77    /// If the node has announced, did it announce with routing information?
78    pub fn contains_routing_info(&self) -> bool {
79        match &self.entry_type {
80            AccountType::NotAnnounced => false,
81            AccountType::Announced { multiaddr, .. } => {
82                multiaddr.protocol_stack().any(|p| p == "ip4" || p == "dns4")
83                    && multiaddr.protocol_stack().any(|p| p == "tcp")
84            }
85        }
86    }
87
88    pub fn get_multiaddr(&self) -> Option<Multiaddr> {
89        match &self.entry_type {
90            AccountType::NotAnnounced => None,
91            AccountType::Announced { multiaddr, .. } => Some(multiaddr.clone()),
92        }
93    }
94
95    /// Used to either set a PRN or revoke being a PRN
96    ///
97    /// Examples:
98    /// - a node transitions from being a PRN to an edge node
99    /// - a node becomes a PRN
100    /// - the IP of a PRN has changed, e.g. due to relocation
101    pub fn update(&mut self, new_entry_type: AccountType) {
102        self.entry_type = new_entry_type;
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use hex_literal::hex;
109    use hopr_crypto_types::types::OffchainPublicKey;
110    use hopr_primitive_types::prelude::*;
111    use multiaddr::Multiaddr;
112
113    use crate::account::{
114        AccountEntry,
115        AccountType::{Announced, NotAnnounced},
116    };
117
118    const PRIVATE_KEY: [u8; 32] = hex!("c14b8faa0a9b8a5fa4453664996f23a7e7de606d42297d723fc4a794f375e260");
119    const CHAIN_ADDR: [u8; 20] = hex!("2cDD13ddB0346E0F620C8E5826Da5d7230341c6E");
120
121    #[test]
122    fn test_account_entry_non_routable() -> anyhow::Result<()> {
123        let public_key = OffchainPublicKey::from_privkey(&PRIVATE_KEY)?;
124        let chain_addr = Address::try_from(CHAIN_ADDR.as_ref())?;
125
126        let ae1 = AccountEntry {
127            public_key,
128            chain_addr,
129            published_at: 1,
130            entry_type: Announced {
131                multiaddr: "/p2p/16Uiu2HAm3rUQdpCz53tK1MVUUq9NdMAU6mFgtcXrf71Ltw6AStzk".parse::<Multiaddr>()?,
132                updated_block: 1,
133            },
134        };
135
136        assert!(ae1.has_announced());
137        assert_eq!(1, ae1.updated_at().expect("should be present"));
138        assert!(!ae1.contains_routing_info());
139
140        Ok(())
141    }
142
143    #[test]
144    fn test_account_entry_routable() -> anyhow::Result<()> {
145        let public_key = OffchainPublicKey::from_privkey(&PRIVATE_KEY)?;
146        let chain_addr = Address::try_from(CHAIN_ADDR.as_ref())?;
147
148        let ae1 = AccountEntry {
149            public_key,
150            chain_addr,
151            published_at: 1,
152            entry_type: Announced {
153                multiaddr: "/ip4/34.65.237.196/tcp/9091/p2p/16Uiu2HAm3rUQdpCz53tK1MVUUq9NdMAU6mFgtcXrf71Ltw6AStzk"
154                    .parse::<Multiaddr>()?,
155                updated_block: 1,
156            },
157        };
158
159        assert!(ae1.has_announced());
160        assert_eq!(1, ae1.updated_at().expect("should be present"));
161        assert!(ae1.contains_routing_info());
162        assert_eq!("0x4e1ddc66", ae1.key_id().to_hex());
163
164        Ok(())
165    }
166
167    #[test]
168    fn test_account_entry_not_announced() -> anyhow::Result<()> {
169        let public_key = OffchainPublicKey::from_privkey(&PRIVATE_KEY)?;
170        let chain_addr = Address::try_from(CHAIN_ADDR.as_ref())?;
171
172        let ae1 = AccountEntry {
173            public_key,
174            chain_addr,
175            published_at: 0,
176            entry_type: NotAnnounced,
177        };
178
179        assert!(!ae1.has_announced());
180        assert!(ae1.updated_at().is_none());
181        assert!(!ae1.contains_routing_info());
182
183        Ok(())
184    }
185}