1use std::fmt;
2
3use crate::hex::{self, FromHex};
4use serde::{Deserialize, Serialize};
5use sha3::Digest;
6
7use super::Address;
8
9#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)]
13pub struct NodeID(pub [u8; 32], pub [u8; 3]);
14
15impl NodeID {
16 pub fn new(id: [u8; 32]) -> Self {
17 let mut checksum = [0; 3];
18 let mut hash = sha3::Sha3_256::default();
20 hash.update(&id);
21 let hash = hash.finalize();
22 checksum[0] = hash[0];
24 checksum[1] = hash[1];
25 checksum[2] = 0; Self(id, checksum)
27 }
28}
29
30impl From<u64> for NodeID {
31 fn from(x: u64) -> NodeID {
32 let mut id = [0; 32];
33 id[32 - 8..].copy_from_slice(&x.to_be_bytes());
34 NodeID::new(id)
35 }
36}
37
38impl From<ed25519_compact::PublicKey> for NodeID {
39 fn from(pk: ed25519_compact::PublicKey) -> Self {
40 NodeID::new(*pk)
41 }
42}
43
44impl NodeID {
45 const ALPHABET: base32::Alphabet = base32::Alphabet::Rfc4648Lower { padding: false };
46
47 pub fn as_u64(&self) -> u64 {
48 u64::from_be_bytes(self.0[..8].try_into().unwrap())
49 }
50
51 pub fn as_ed25519_public_key(&self) -> ed25519_compact::PublicKey {
52 ed25519_compact::PublicKey::from_slice(&self.0).unwrap()
53 }
54
55 pub fn as_dns_label(&self) -> String {
56 let mut id: [u8; 35] = [0; 35];
58 id[..32].copy_from_slice(&self.0);
59 id[32..].copy_from_slice(&self.1);
60 base32::encode(Self::ALPHABET, &id)
61 }
62}
63
64impl fmt::Display for NodeID {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
66 write!(f, "Node-{}", self.as_dns_label())
67 }
68}
69
70impl fmt::Debug for NodeID {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
72 fmt::Display::fmt(self, f)
73 }
74}
75
76impl From<NodeID> for [u8; 32] {
77 fn from(val: NodeID) -> Self {
78 val.0
79 }
80}
81
82impl From<[u8; 32]> for NodeID {
83 fn from(bytes: [u8; 32]) -> Self {
84 Self::new(bytes)
85 }
86}
87
88impl<'a> TryFrom<&'a [u8]> for NodeID {
89 type Error = IDError;
90
91 fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
92 if bytes.len() != 32 {
93 return Err(IDError::Length);
94 }
95
96 let mut id = [0; 32];
97 id.copy_from_slice(bytes);
98 Ok(Self::new(id))
99 }
100}
101
102impl FromHex for NodeID {
103 type Error = hex::FromHexError;
104
105 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
106 let bytes = <[u8; 32]>::from_hex(hex)?;
107 Ok(Self::new(bytes))
108 }
109}
110
111impl AsRef<[u8]> for NodeID {
112 fn as_ref(&self) -> &[u8] {
113 &self.0
114 }
115}
116
117#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default)]
118pub struct RequiredLyquid(pub LyquidID);
119
120#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
121pub struct LyquidID(pub [u8; 20]);
122
123impl Serialize for LyquidID {
124 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
125 where
126 S: serde::Serializer,
127 {
128 if serializer.is_human_readable() {
129 serializer.serialize_str(&self.to_string())
130 } else {
131 self.0.serialize(serializer)
132 }
133 }
134}
135
136impl<'de> Deserialize<'de> for LyquidID {
137 fn deserialize<D>(deserializer: D) -> Result<LyquidID, D::Error>
138 where
139 D: serde::Deserializer<'de>,
140 {
141 if deserializer.is_human_readable() {
142 let s = <&str as Deserialize>::deserialize(deserializer)?;
143 s.parse().map_err(|e| serde::de::Error::custom(format!("{e:?}")))
144 } else {
145 let arr = <[u8; 20] as Deserialize>::deserialize(deserializer)?;
146 Ok(LyquidID(arr))
147 }
148 }
149}
150
151impl From<LyquidID> for [u8; 20] {
152 fn from(id: LyquidID) -> Self {
153 id.0
154 }
155}
156
157impl From<LyquidID> for Address {
158 fn from(id: LyquidID) -> Address {
159 Address(id.0.into())
160 }
161}
162
163impl From<[u8; 20]> for LyquidID {
164 fn from(bytes: [u8; 20]) -> Self {
165 Self(bytes)
166 }
167}
168
169impl From<&[u8; 20]> for LyquidID {
170 fn from(bytes: &[u8; 20]) -> Self {
171 Self(*bytes)
172 }
173}
174
175impl From<Address> for LyquidID {
176 fn from(addr: Address) -> Self {
177 Self(addr.into())
178 }
179}
180
181impl TryFrom<&[u8]> for LyquidID {
182 type Error = std::array::TryFromSliceError;
183 fn try_from(s: &[u8]) -> Result<LyquidID, Self::Error> {
184 Ok(Self(s.try_into()?))
185 }
186}
187
188impl From<u64> for LyquidID {
189 fn from(x: u64) -> Self {
190 let mut id = [0; 20];
191 id[20 - 8..].copy_from_slice(&x.to_be_bytes());
192 Self(id)
193 }
194}
195
196impl fmt::Display for LyquidID {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
198 write!(f, "Lyquid-{}", cb58::cb58_encode(self.0))
199 }
200}
201
202impl fmt::Debug for LyquidID {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
204 fmt::Display::fmt(self, f)
205 }
206}
207
208impl LyquidID {
209 pub fn from_owner_nonce(owner: &Address, nonce: u64) -> Self {
210 let mut hasher = blake3::Hasher::new();
211 hasher.update(owner.as_slice());
212 hasher.update(&nonce.to_be_bytes());
213 let hash: [u8; 32] = hasher.finalize().into();
214 hash[12..].try_into().unwrap()
215 }
216
217 pub fn readable_short(&self) -> String {
218 let s = self.to_string();
219 format!("{}..{}", &s[..15], &s[s.len() - 8..])
220 }
221}
222
223impl FromHex for LyquidID {
224 type Error = hex::FromHexError;
225
226 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
227 let bytes = <[u8; 20]>::from_hex(hex)?;
228 Ok(Self(bytes))
229 }
230}
231
232#[derive(Debug)]
233pub enum IDError {
234 Prefix,
235 CB58,
236 Length,
237}
238
239impl std::str::FromStr for LyquidID {
240 type Err = IDError;
241 fn from_str(s: &str) -> Result<Self, Self::Err> {
242 const PREFIX: &str = "Lyquid-";
243 if s.len() < PREFIX.len() || &s[..PREFIX.len()] != PREFIX {
244 return Err(IDError::Prefix);
245 }
246 let bytes = cb58::cb58_decode(&s[PREFIX.len()..]).ok_or(IDError::CB58)?;
247 Ok(LyquidID(bytes.try_into().map_err(|_| IDError::Length)?))
248 }
249}
250
251impl AsRef<[u8]> for LyquidID {
252 fn as_ref(&self) -> &[u8] {
253 &self.0
254 }
255}
256
257#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
258pub struct LyquidNumber {
260 pub image: u32,
262 pub var: u32,
264}
265
266impl fmt::Display for LyquidNumber {
267 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268 write!(f, "LyquidNumber(image={}, var={})", self.image, self.var)
269 }
270}
271
272impl LyquidNumber {
273 pub const ZERO: Self = LyquidNumber { image: 0, var: 0 };
274}
275
276impl From<u64> for LyquidNumber {
277 fn from(x: u64) -> Self {
278 Self {
279 image: (x >> 32) as u32,
280 var: x as u32,
281 }
282 }
283}
284
285impl From<&LyquidNumber> for u64 {
286 fn from(n: &LyquidNumber) -> u64 {
287 ((n.image as u64) << 32) | n.var as u64
288 }
289}
290
291impl From<LyquidNumber> for u64 {
292 fn from(n: LyquidNumber) -> u64 {
293 (&n).into()
294 }
295}
296
297impl std::str::FromStr for LyquidNumber {
298 type Err = std::num::ParseIntError;
299 fn from_str(s: &str) -> Result<Self, Self::Err> {
300 let x: u64 = s.parse()?;
301 Ok(x.into())
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use ed25519_compact::KeyPair;
309
310 #[test]
311 fn test_hex() {
312 let kp = KeyPair::from_seed([42u8; 32].into());
313 let id = NodeID::from(kp.pk);
314 let hex = hex::encode(*kp.pk);
315 let decoded_id = NodeID::from_hex(&hex).unwrap();
316 assert_eq!(id, decoded_id);
317 assert_eq!(hex, "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61");
318 assert_eq!(
319 id.to_string(),
320 "Node-df7wwi7bnsctfrvlza4pvtk6u6e34ddwwkjagnadtp5iwpjwrvqvfyya"
321 );
322 assert_eq!(
323 id.as_dns_label(),
324 "df7wwi7bnsctfrvlza4pvtk6u6e34ddwwkjagnadtp5iwpjwrvqvfyya"
325 );
326 }
327}