avalanche_types/ids/
short.rs

1//! Node short ID used in AvalancheGo.
2use std::{
3    cmp::Ordering,
4    fmt,
5    hash::{Hash, Hasher},
6    io::{self, Error, ErrorKind},
7    str::FromStr,
8};
9
10use crate::{formatting, hash, key::secp256k1};
11use lazy_static::lazy_static;
12use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
13use zerocopy::{AsBytes, FromBytes, FromZeroes, Unaligned};
14
15pub const LEN: usize = 20;
16
17lazy_static! {
18    static ref EMPTY: Vec<u8> = vec![0; LEN];
19}
20
21/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/ids#ShortID>
22/// ref. <https://docs.rs/zerocopy/latest/zerocopy/trait.AsBytes.html#safety>
23#[derive(Debug, Clone, Eq, AsBytes, FromZeroes, FromBytes, Unaligned)]
24#[repr(transparent)]
25pub struct Id([u8; LEN]);
26
27impl Default for Id {
28    fn default() -> Self {
29        Self::empty()
30    }
31}
32
33impl Id {
34    pub fn empty() -> Self {
35        Id([0; LEN])
36    }
37
38    pub fn is_empty(&self) -> bool {
39        (*self) == Self::empty()
40    }
41
42    pub fn from_slice(d: &[u8]) -> Self {
43        assert!(d.len() <= LEN);
44        let mut d: Vec<u8> = Vec::from(d);
45        if d.len() < LEN {
46            d.resize(LEN, 0);
47        }
48        let d: [u8; LEN] = d.try_into().unwrap();
49        Id(d)
50    }
51
52    /// "hashing.PubkeyBytesToAddress"
53    /// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/utils/hashing#PubkeyBytesToAddress>
54    pub fn from_public_key_bytes<S>(pub_key_bytes: S) -> io::Result<Self>
55    where
56        S: AsRef<[u8]>,
57    {
58        let hashed = hash::sha256_ripemd160(pub_key_bytes)?;
59
60        // "ids.Id.String"
61        // ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/ids#Id.String
62        let encoded = formatting::encode_cb58_with_checksum_string(&hashed);
63        Self::from_str(&encoded)
64    }
65}
66
67impl AsRef<[u8]> for Id {
68    fn as_ref(&self) -> &[u8] {
69        &self.0
70    }
71}
72
73/// ref. <https://doc.rust-lang.org/std/string/trait.ToString.html>
74/// ref. <https://doc.rust-lang.org/std/fmt/trait.Display.html>
75/// Use "Self.to_string()" to directly invoke this.
76impl fmt::Display for Id {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        let s = formatting::encode_cb58_with_checksum_string(&self.0);
79        write!(f, "{}", s)
80    }
81}
82
83/// ref. <https://doc.rust-lang.org/std/str/trait.FromStr.html>
84impl FromStr for Id {
85    type Err = Error;
86    fn from_str(s: &str) -> Result<Self, Self::Err> {
87        // trim in case it's parsed from list
88        let decoded = formatting::decode_cb58_with_checksum(s.trim()).map_err(|e| {
89            Error::new(
90                ErrorKind::Other,
91                format!("failed decode_cb58_with_checksum '{}'", e),
92            )
93        })?;
94        Ok(Self::from_slice(&decoded))
95    }
96}
97
98/// Custom serializer.
99/// ref. <https://serde.rs/impl-serialize.html>
100impl Serialize for Id {
101    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
102    where
103        S: Serializer,
104    {
105        serializer.serialize_str(&self.to_string())
106    }
107}
108
109/// Custom deserializer.
110/// ref. <https://serde.rs/impl-deserialize.html>
111impl<'de> Deserialize<'de> for Id {
112    fn deserialize<D>(deserializer: D) -> Result<Id, D::Error>
113    where
114        D: Deserializer<'de>,
115    {
116        let s: String = Deserialize::deserialize(deserializer)?;
117        let ss: Vec<&str> = s.split('-').collect();
118        if ss.len() == 1 {
119            return Id::from_str(&s).map_err(serde::de::Error::custom);
120        }
121
122        let addr = ss[1];
123        let (_, short_bytes) = secp256k1::address::avax_address_to_short_bytes("", addr)
124            .map_err(serde::de::Error::custom)?;
125        Ok(Id::from_slice(&short_bytes))
126    }
127}
128
129fn fmt_id<'de, D>(deserializer: D) -> Result<Id, D::Error>
130where
131    D: Deserializer<'de>,
132{
133    let s = String::deserialize(deserializer)?;
134    Id::from_str(&s).map_err(serde::de::Error::custom)
135}
136
137/// Custom deserializer.
138/// ref. <https://serde.rs/impl-deserialize.html>
139pub fn deserialize_id<'de, D>(deserializer: D) -> Result<Option<Id>, D::Error>
140where
141    D: Deserializer<'de>,
142{
143    #[derive(Deserialize)]
144    struct Wrapper(#[serde(deserialize_with = "fmt_id")] Id);
145    let v = Option::deserialize(deserializer)?;
146    Ok(v.map(|Wrapper(a)| a))
147}
148
149/// Custom deserializer.
150/// Use #[serde(deserialize_with = "ids::must_deserialize_id")] to serde without derive.
151/// ref. <https://serde.rs/impl-deserialize.html>
152pub fn must_deserialize_id<'de, D>(deserializer: D) -> Result<Id, D::Error>
153where
154    D: Deserializer<'de>,
155{
156    #[derive(Deserialize)]
157    struct Wrapper(#[serde(deserialize_with = "fmt_id")] Id);
158    let v = Option::deserialize(deserializer)?;
159    match v.map(|Wrapper(a)| a) {
160        Some(unwrapped) => Ok(unwrapped),
161        None => Err(serde::de::Error::custom(
162            "empty short::Id from deserialization",
163        )),
164    }
165}
166
167/// Custom deserializer.
168/// ref. <https://serde.rs/impl-deserialize.html>
169pub fn deserialize_ids<'de, D>(deserializer: D) -> Result<Option<Vec<Id>>, D::Error>
170where
171    D: Deserializer<'de>,
172{
173    #[derive(Deserialize)]
174    struct Wrapper(#[serde(deserialize_with = "fmt_ids")] Vec<Id>);
175    let v = Option::deserialize(deserializer)?;
176    Ok(v.map(|Wrapper(a)| a))
177}
178
179/// Custom deserializer.
180/// Use #[serde(deserialize_with = "short::must_deserialize_ids")] to serde with derive.
181/// ref. <https://serde.rs/impl-deserialize.html>
182pub fn must_deserialize_ids<'de, D>(deserializer: D) -> Result<Vec<Id>, D::Error>
183where
184    D: Deserializer<'de>,
185{
186    #[derive(Deserialize)]
187    struct Wrapper(#[serde(deserialize_with = "fmt_ids")] Vec<Id>);
188    let v = Option::deserialize(deserializer)?;
189    match v.map(|Wrapper(a)| a) {
190        Some(unwrapped) => Ok(unwrapped),
191        None => Err(serde::de::Error::custom("empty Ids from deserialization")),
192    }
193}
194
195fn fmt_ids<'de, D>(deserializer: D) -> Result<Vec<Id>, D::Error>
196where
197    D: serde::Deserializer<'de>,
198{
199    type Strings = Vec<String>;
200    let ss = Strings::deserialize(deserializer)?;
201    match ss
202        .iter()
203        .map(|x| x.parse::<Id>())
204        .collect::<Result<Vec<Id>, Error>>()
205    {
206        Ok(x) => Ok(x),
207        Err(e) => Err(serde::de::Error::custom(format!(
208            "failed to deserialize Ids {}",
209            e
210        ))),
211    }
212}
213
214/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::short::test_serialize --exact --show-output
215#[test]
216fn test_serialize() {
217    let id = Id::from_slice(&<Vec<u8>>::from([
218        0x3d, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
219        0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
220    ]));
221    assert_eq!(id.to_string(), "6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx");
222
223    #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
224    struct Data {
225        id: Id,
226        id2: Option<Id>,
227        ids: Vec<Id>,
228    }
229    let d = Data {
230        id: id.clone(),
231        id2: Some(id.clone()),
232        ids: vec![id.clone(), id.clone(), id.clone(), id.clone(), id.clone()],
233    };
234
235    let yaml_encoded = serde_yaml::to_string(&d).unwrap();
236    assert!(yaml_encoded.contains("6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"));
237    let yaml_decoded = serde_yaml::from_str(&yaml_encoded).unwrap();
238    assert_eq!(d, yaml_decoded);
239
240    let json_encoded = serde_json::to_string(&d).unwrap();
241    assert!(json_encoded.contains("6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"));
242    let json_decoded = serde_json::from_str(&json_encoded).unwrap();
243    assert_eq!(d, json_decoded);
244
245    let pk = crate::key::secp256k1::private_key::Key::generate().unwrap();
246    let pubkey = pk.to_public_key();
247    let short_addr = pubkey.to_short_id().unwrap();
248    let p_addr = pubkey.to_hrp_address(1, "P").unwrap();
249
250    let d1: Data = serde_json::from_str(
251        format!(
252            "{{\"id\": \"{}\", \"ids\": [\"{}\", \"{}\"]}}",
253            short_addr, p_addr, p_addr
254        )
255        .as_str(),
256    )
257    .unwrap();
258    let d2 = Data {
259        id: short_addr.clone(),
260        id2: None,
261        ids: vec![short_addr.clone(), short_addr.clone()],
262    };
263    assert_eq!(d1, d2);
264
265    let id = Id::from_str("6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV").unwrap();
266    println!("{}", id);
267
268    let d: Data = serde_json::from_str("{\"id\": \"6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV\", \"ids\": [\"6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV\", \"6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV\"]}")
269    .unwrap();
270    println!("{:?}", d);
271}
272
273/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::short::test_id --exact --show-output
274#[test]
275fn test_id() {
276    let id = Id::from_slice(&<Vec<u8>>::from([
277        0x3d, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
278        0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
279    ]));
280    assert_eq!(id.to_string(), "6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx");
281    let id_from_str = Id::from_str("6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx").unwrap();
282    assert_eq!(id, id_from_str);
283}
284
285impl Ord for Id {
286    fn cmp(&self, other: &Id) -> Ordering {
287        self.0.cmp(&(other.0))
288    }
289}
290
291impl PartialOrd for Id {
292    fn partial_cmp(&self, other: &Id) -> Option<Ordering> {
293        Some(self.cmp(other))
294    }
295}
296
297impl PartialEq for Id {
298    fn eq(&self, other: &Id) -> bool {
299        self.cmp(other) == Ordering::Equal
300    }
301}
302
303/// ref. <https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq>
304impl Hash for Id {
305    fn hash<H: Hasher>(&self, state: &mut H) {
306        self.0.hash(state);
307    }
308}
309
310#[derive(Debug, Eq, Clone)]
311pub struct Ids(Vec<Id>);
312
313impl Ids {
314    pub fn new(ids: &[Id]) -> Self {
315        Ids(Vec::from(ids))
316    }
317}
318
319impl From<Vec<Id>> for Ids {
320    fn from(ids: Vec<Id>) -> Self {
321        Self::new(&ids)
322    }
323}
324
325impl Ord for Ids {
326    fn cmp(&self, other: &Ids) -> Ordering {
327        // packer encodes the array length first
328        // so if the lengths differ, the ordering is decided
329        let l1 = self.0.len();
330        let l2 = other.0.len();
331        l1.cmp(&l2) // returns when lengths are not Equal
332            .then_with(
333                || self.0.cmp(&other.0), // if lengths are Equal, compare the ids
334            )
335    }
336}
337
338impl PartialOrd for Ids {
339    fn partial_cmp(&self, other: &Ids) -> Option<Ordering> {
340        Some(self.cmp(other))
341    }
342}
343
344impl PartialEq for Ids {
345    fn eq(&self, other: &Ids) -> bool {
346        self.cmp(other) == Ordering::Equal
347    }
348}
349
350/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::short::test_sort --exact --show-output
351#[test]
352fn test_sort() {
353    // lengths of individual ids do not matter since all are fixed-sized
354    let id1 = Id::from_slice(&<Vec<u8>>::from([0x01, 0x00, 0x00, 0x00]));
355    let id2 = Id::from_slice(&<Vec<u8>>::from([0x01, 0x00, 0x00, 0x00, 0x00]));
356    assert!(id1 == id2);
357
358    // lengths of individual ids do not matter since all are fixed-sized
359    let id1 = Id::from_slice(&<Vec<u8>>::from([0x01, 0x00, 0x00, 0x00, 0x00]));
360    let id2 = Id::from_slice(&<Vec<u8>>::from([0x02]));
361    assert!(id1 < id2);
362
363    // lengths of individual ids do not matter since all are fixed-sized
364    let id1 = Id::from_slice(&<Vec<u8>>::from([0x02, 0x00, 0x00, 0x00, 0x00]));
365    let id2 = Id::from_slice(&<Vec<u8>>::from([0x01, 0x00, 0x00, 0x00, 0x00]));
366    assert!(id1 > id2);
367
368    // lengths of Id matter
369    let ids1 = Ids(vec![
370        Id::from_slice(&<Vec<u8>>::from([0x01])),
371        Id::from_slice(&<Vec<u8>>::from([0x02])),
372        Id::from_slice(&<Vec<u8>>::from([0x03])),
373    ]);
374    let ids2 = Ids(vec![
375        Id::from_slice(&<Vec<u8>>::from([0x01])),
376        Id::from_slice(&<Vec<u8>>::from([0x02])),
377        Id::from_slice(&<Vec<u8>>::from([0x03])),
378    ]);
379    assert!(ids1 == ids2);
380
381    // lengths of Id matter
382    let ids1 = Ids(vec![
383        Id::from_slice(&<Vec<u8>>::from([0x05])),
384        Id::from_slice(&<Vec<u8>>::from([0x06])),
385        Id::from_slice(&<Vec<u8>>::from([0x07])),
386    ]);
387    let ids2 = Ids(vec![
388        Id::from_slice(&<Vec<u8>>::from([0x01])),
389        Id::from_slice(&<Vec<u8>>::from([0x02])),
390        Id::from_slice(&<Vec<u8>>::from([0x03])),
391        Id::from_slice(&<Vec<u8>>::from([0x04])),
392    ]);
393    assert!(ids1 < ids2);
394
395    // lengths of Id matter
396    let ids1 = Ids(vec![
397        Id::from_slice(&<Vec<u8>>::from([0x01])),
398        Id::from_slice(&<Vec<u8>>::from([0x02])),
399        Id::from_slice(&<Vec<u8>>::from([0x03])),
400        Id::from_slice(&<Vec<u8>>::from([0x04])),
401    ]);
402    let ids2 = Ids(vec![
403        Id::from_slice(&<Vec<u8>>::from([0x09])),
404        Id::from_slice(&<Vec<u8>>::from([0x09])),
405        Id::from_slice(&<Vec<u8>>::from([0x09])),
406    ]);
407    assert!(ids1 > ids2);
408
409    // lengths of Id matter
410    let ids1 = Ids(vec![
411        Id::from_slice(&<Vec<u8>>::from([0x01])),
412        Id::from_slice(&<Vec<u8>>::from([0x02])),
413        Id::from_slice(&<Vec<u8>>::from([0x03])),
414    ]);
415    let ids2 = Ids(vec![
416        Id::from_slice(&<Vec<u8>>::from([0x01])),
417        Id::from_slice(&<Vec<u8>>::from([0x02])),
418        Id::from_slice(&<Vec<u8>>::from([0x05])),
419    ]);
420    assert!(ids1 < ids2);
421
422    let mut ids1 = vec![
423        Id::from_slice(&<Vec<u8>>::from([0x03])),
424        Id::from_slice(&<Vec<u8>>::from([0x02])),
425        Id::from_slice(&<Vec<u8>>::from([0x01])),
426    ];
427    ids1.sort();
428    let ids2 = vec![
429        Id::from_slice(&<Vec<u8>>::from([0x01])),
430        Id::from_slice(&<Vec<u8>>::from([0x02])),
431        Id::from_slice(&<Vec<u8>>::from([0x03])),
432    ];
433    assert!(ids1 == ids2);
434}