hopr_internal_types/
account.rs1use hopr_crypto_types::prelude::*;
2use hopr_primitive_types::prelude::*;
3use multiaddr::Multiaddr;
4use std::fmt::{Display, Formatter};
5
6#[derive(Clone, Debug, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum AccountType {
10 NotAnnounced,
12 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#[derive(Clone, Debug, PartialEq, Eq)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38pub struct AccountEntry {
39 pub public_key: OffchainPublicKey,
40 pub chain_addr: Address,
41 pub entry_type: AccountType,
42 pub published_at: u32,
43}
44
45impl AccountEntry {
46 pub fn updated_at(&self) -> Option<u32> {
48 match &self.entry_type {
49 AccountType::NotAnnounced => None,
50 AccountType::Announced { updated_block, .. } => Some(*updated_block),
51 }
52 }
53
54 pub fn key_id(&self) -> KeyIdent<4> {
57 let id_hash = Hash::create(&[
58 self.public_key.as_ref(),
59 self.chain_addr.as_ref(),
60 &self.published_at.to_be_bytes(),
61 ]);
62
63 u32::from_be_bytes(
64 id_hash.as_ref()[0..std::mem::size_of::<u32>()]
65 .try_into()
66 .expect("4 byte must fit into u32"),
67 )
68 .into()
69 }
70
71 pub fn has_announced(&self) -> bool {
73 matches!(self.entry_type, AccountType::Announced { .. })
74 }
75
76 pub fn contains_routing_info(&self) -> bool {
78 match &self.entry_type {
79 AccountType::NotAnnounced => false,
80 AccountType::Announced { multiaddr, .. } => {
81 multiaddr.protocol_stack().any(|p| p == "ip4" || p == "dns4")
82 && multiaddr.protocol_stack().any(|p| p == "tcp")
83 }
84 }
85 }
86
87 pub fn get_multiaddr(&self) -> Option<Multiaddr> {
88 match &self.entry_type {
89 AccountType::NotAnnounced => None,
90 AccountType::Announced { multiaddr, .. } => Some(multiaddr.clone()),
91 }
92 }
93
94 pub fn update(&mut self, new_entry_type: AccountType) {
101 self.entry_type = new_entry_type;
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use crate::account::{
108 AccountEntry,
109 AccountType::{Announced, NotAnnounced},
110 };
111 use hex_literal::hex;
112 use hopr_crypto_types::types::OffchainPublicKey;
113 use hopr_primitive_types::prelude::*;
114 use multiaddr::Multiaddr;
115
116 const PRIVATE_KEY: [u8; 32] = hex!("c14b8faa0a9b8a5fa4453664996f23a7e7de606d42297d723fc4a794f375e260");
117 const CHAIN_ADDR: [u8; 20] = hex!("2cDD13ddB0346E0F620C8E5826Da5d7230341c6E");
118
119 #[test]
120 fn test_account_entry_non_routable() -> anyhow::Result<()> {
121 let public_key = OffchainPublicKey::from_privkey(&PRIVATE_KEY)?;
122 let chain_addr = Address::try_from(CHAIN_ADDR.as_ref())?;
123
124 let ae1 = AccountEntry {
125 public_key,
126 chain_addr,
127 published_at: 1,
128 entry_type: Announced {
129 multiaddr: "/p2p/16Uiu2HAm3rUQdpCz53tK1MVUUq9NdMAU6mFgtcXrf71Ltw6AStzk".parse::<Multiaddr>()?,
130 updated_block: 1,
131 },
132 };
133
134 assert!(ae1.has_announced());
135 assert_eq!(1, ae1.updated_at().expect("should be present"));
136 assert!(!ae1.contains_routing_info());
137
138 Ok(())
139 }
140
141 #[test]
142 fn test_account_entry_routable() -> anyhow::Result<()> {
143 let public_key = OffchainPublicKey::from_privkey(&PRIVATE_KEY)?;
144 let chain_addr = Address::try_from(CHAIN_ADDR.as_ref())?;
145
146 let ae1 = AccountEntry {
147 public_key,
148 chain_addr,
149 published_at: 1,
150 entry_type: Announced {
151 multiaddr: "/ip4/34.65.237.196/tcp/9091/p2p/16Uiu2HAm3rUQdpCz53tK1MVUUq9NdMAU6mFgtcXrf71Ltw6AStzk"
152 .parse::<Multiaddr>()?,
153 updated_block: 1,
154 },
155 };
156
157 assert!(ae1.has_announced());
158 assert_eq!(1, ae1.updated_at().expect("should be present"));
159 assert!(ae1.contains_routing_info());
160 assert_eq!("0x4e1ddc66", ae1.key_id().to_hex());
161
162 Ok(())
163 }
164
165 #[test]
166 fn test_account_entry_not_announced() -> anyhow::Result<()> {
167 let public_key = OffchainPublicKey::from_privkey(&PRIVATE_KEY)?;
168 let chain_addr = Address::try_from(CHAIN_ADDR.as_ref())?;
169
170 let ae1 = AccountEntry {
171 public_key,
172 chain_addr,
173 published_at: 0,
174 entry_type: NotAnnounced,
175 };
176
177 assert!(!ae1.has_announced());
178 assert!(ae1.updated_at().is_none());
179 assert!(!ae1.contains_routing_info());
180
181 Ok(())
182 }
183}