hopr_internal_types/
account.rs1use std::fmt::{Display, Formatter};
2
3use hopr_crypto_types::prelude::*;
4use hopr_primitive_types::prelude::*;
5use multiaddr::Multiaddr;
6
7#[derive(Clone, Debug, PartialEq, Eq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub enum AccountType {
11 NotAnnounced,
13 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#[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 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 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 pub fn has_announced(&self) -> bool {
74 matches!(self.entry_type, AccountType::Announced { .. })
75 }
76
77 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 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}