hopr_internal_types/
account.rs

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