Skip to main content

avalanche_types/ids/
node.rs

1//! Node ID utilities.
2use std::{
3    cmp::Ordering,
4    collections::HashSet,
5    fmt,
6    hash::{Hash, Hasher},
7    io::{self, Error, ErrorKind},
8    path::Path,
9    str::FromStr,
10};
11
12use lazy_static::lazy_static;
13use serde::{self, de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
14use zerocopy::{AsBytes, FromBytes, FromZeroes, Unaligned};
15
16use crate::{formatting, hash, ids::short};
17
18pub const LEN: usize = 20;
19pub const ENCODE_PREFIX: &str = "NodeID-";
20
21lazy_static! {
22    static ref EMPTY: Vec<u8> = vec![0; LEN];
23}
24
25/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/ids#ShortID>
26/// ref. <https://docs.rs/zerocopy/latest/zerocopy/trait.AsBytes.html#safety>
27#[derive(Debug, Copy, Clone, Eq, AsBytes, FromZeroes, FromBytes, Unaligned)]
28#[repr(transparent)]
29pub struct Id([u8; LEN]);
30
31impl Default for Id {
32    fn default() -> Self {
33        Self::empty()
34    }
35}
36
37impl Id {
38    pub fn empty() -> Self {
39        Id([0; LEN])
40    }
41
42    pub fn is_empty(&self) -> bool {
43        (*self) == Self::empty()
44    }
45
46    pub fn to_vec(&self) -> Vec<u8> {
47        self.0.to_vec()
48    }
49
50    pub fn from_slice(d: &[u8]) -> Self {
51        assert_eq!(d.len(), LEN);
52        let d: [u8; LEN] = d.try_into().unwrap();
53        Id(d)
54    }
55
56    /// Loads a node ID from the PEM-encoded X509 certificate.
57    /// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/node#Node.Initialize>
58    pub fn from_cert_pem_file(cert_file_path: &str) -> io::Result<Self> {
59        log::info!("loading node ID from certificate {}", cert_file_path);
60        let pub_key_der = cert_manager::x509::load_pem_cert_to_der(cert_file_path)?;
61
62        // "ids.ToShortID(hashing.PubkeyBytesToAddress(StakingTLSCert.Leaf.Raw))"
63        // ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/node#Node.Initialize
64        Self::from_cert_der_bytes(pub_key_der)
65    }
66
67    /// Encodes the DER-encoded certificate bytes to a node ID.
68    /// It applies "sha256" and "ripemd160" on "Certificate.Leaf.Raw".
69    /// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/utils/hashing#PubkeyBytesToAddress>
70    /// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/ids#ToShortID>
71    pub fn from_cert_der_bytes<S>(cert_bytes: S) -> io::Result<Self>
72    where
73        S: AsRef<[u8]>,
74    {
75        let short_address = hash::sha256_ripemd160(cert_bytes)?;
76        let node_id = Self::from_slice(&short_address);
77        Ok(node_id)
78    }
79
80    /// Loads the existing staking certificates if exists,
81    /// and returns the loaded or generated node Id.
82    /// Returns "true" if generated.
83    pub fn load_or_generate_pem(key_path: &str, cert_path: &str) -> io::Result<(Self, bool)> {
84        let tls_key_exists = Path::new(&key_path).exists();
85        log::info!("staking TLS key {} exists? {}", key_path, tls_key_exists);
86
87        let tls_cert_exists = Path::new(&cert_path).exists();
88        log::info!("staking TLS cert {} exists? {}", cert_path, tls_cert_exists);
89
90        let mut generated = false;
91        if !tls_key_exists || !tls_cert_exists {
92            log::info!(
93                "generating staking TLS certs (key exists {}, cert exists {})",
94                tls_key_exists,
95                tls_cert_exists
96            );
97            cert_manager::x509::generate_and_write_pem(None, key_path, cert_path)?;
98            generated = true;
99        } else {
100            log::info!(
101                "loading existing staking TLS certificates from '{}' and '{}'",
102                key_path,
103                cert_path
104            );
105        }
106
107        let node_id = Self::from_cert_pem_file(cert_path)?;
108        Ok((node_id, generated))
109    }
110
111    pub fn short_id(&self) -> short::Id {
112        short::Id::from_slice(&self.0)
113    }
114}
115
116impl AsRef<[u8]> for Id {
117    fn as_ref(&self) -> &[u8] {
118        &self.0
119    }
120}
121
122/// ref. <https://doc.rust-lang.org/std/string/trait.ToString.html>
123/// ref. <https://doc.rust-lang.org/std/fmt/trait.Display.html>
124/// Use "Self.to_string()" to directly invoke this.
125impl fmt::Display for Id {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        let mut node_id = String::from(ENCODE_PREFIX);
128        let short_id = formatting::encode_cb58_with_checksum_string(&self.0);
129        node_id.push_str(&short_id);
130        write!(f, "{}", node_id)
131    }
132}
133
134/// ref. <https://doc.rust-lang.org/std/str/trait.FromStr.html>
135impl FromStr for Id {
136    type Err = Error;
137    fn from_str(s: &str) -> Result<Self, Self::Err> {
138        // trim in case it's parsed from list
139        let processed = s.trim().trim_start_matches(ENCODE_PREFIX);
140        let decoded = formatting::decode_cb58_with_checksum(processed).map_err(|e| {
141            Error::new(
142                ErrorKind::Other,
143                format!("failed decode_cb58_with_checksum '{}'", e),
144            )
145        })?;
146        Ok(Self::from_slice(&decoded))
147    }
148}
149
150/// Custom serializer.
151/// ref. <https://serde.rs/impl-serialize.html>
152impl Serialize for Id {
153    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
154    where
155        S: Serializer,
156    {
157        serializer.serialize_str(&self.to_string())
158    }
159}
160
161/// Custom deserializer.
162/// ref. <https://serde.rs/impl-deserialize.html>
163impl<'de> Deserialize<'de> for Id {
164    fn deserialize<D>(deserializer: D) -> std::result::Result<Id, D::Error>
165    where
166        D: Deserializer<'de>,
167    {
168        struct IdVisitor;
169
170        impl<'de> Visitor<'de> for IdVisitor {
171            type Value = Id;
172
173            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
174                formatter.write_str("a base-58 encoded ID-string with checksum")
175            }
176
177            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
178            where
179                E: serde::de::Error,
180            {
181                Id::from_str(v).map_err(E::custom)
182            }
183        }
184
185        deserializer.deserialize_any(IdVisitor)
186    }
187}
188
189/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::node::test_custom_de_serializer --exact --show-output
190#[test]
191fn test_custom_de_serializer() {
192    #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
193    struct Data {
194        node_id: Id,
195    }
196
197    let d = Data {
198        node_id: Id::from_str("NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx").unwrap(),
199    };
200
201    let yaml_encoded = serde_yaml::to_string(&d).unwrap();
202    println!("yaml_encoded:\n{}", yaml_encoded);
203    let yaml_decoded = serde_yaml::from_str(&yaml_encoded).unwrap();
204    assert_eq!(d, yaml_decoded);
205
206    let json_encoded = serde_json::to_string(&d).unwrap();
207    println!("json_encoded:\n{}", json_encoded);
208    let json_decoded = serde_json::from_str(&json_encoded).unwrap();
209    assert_eq!(d, json_decoded);
210
211    let json_decoded_2: Data =
212        serde_json::from_str(r#"{ "node_id":"NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx" }"#)
213            .unwrap();
214    assert_eq!(d, json_decoded_2);
215
216    let json_encoded_3 = serde_json::json!(
217        {
218            "node_id": "NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"
219        }
220    );
221    let json_decoded_3: Data = serde_json::from_value(json_encoded_3).unwrap();
222    assert_eq!(d, json_decoded_3);
223}
224
225fn fmt_id<'de, D>(deserializer: D) -> Result<Id, D::Error>
226where
227    D: Deserializer<'de>,
228{
229    let s = String::deserialize(deserializer)?;
230    Id::from_str(&s).map_err(serde::de::Error::custom)
231}
232
233/// Custom deserializer.
234/// ref. <https://serde.rs/impl-deserialize.html>
235pub fn deserialize_id<'de, D>(deserializer: D) -> Result<Option<Id>, D::Error>
236where
237    D: Deserializer<'de>,
238{
239    #[derive(Deserialize)]
240    struct Wrapper(#[serde(deserialize_with = "fmt_id")] Id);
241    let v = Option::deserialize(deserializer)?;
242    Ok(v.map(|Wrapper(a)| a))
243}
244
245/// Custom deserializer.
246/// Use #[serde(deserialize_with = "ids::must_deserialize_id")] to serde without derive.
247/// ref. <https://serde.rs/impl-deserialize.html>
248pub fn must_deserialize_id<'de, D>(deserializer: D) -> Result<Id, D::Error>
249where
250    D: Deserializer<'de>,
251{
252    #[derive(Deserialize)]
253    struct Wrapper(#[serde(deserialize_with = "fmt_id")] Id);
254    let v = Option::deserialize(deserializer)?;
255    match v.map(|Wrapper(a)| a) {
256        Some(unwrapped) => Ok(unwrapped),
257        None => Err(serde::de::Error::custom(
258            "empty node::Id from deserialization",
259        )),
260    }
261}
262
263/// Custom deserializer.
264/// ref. <https://serde.rs/impl-deserialize.html>
265pub fn deserialize_ids<'de, D>(deserializer: D) -> Result<Option<Vec<Id>>, D::Error>
266where
267    D: Deserializer<'de>,
268{
269    #[derive(Deserialize)]
270    struct Wrapper(#[serde(deserialize_with = "fmt_ids")] Vec<Id>);
271    let v = Option::deserialize(deserializer)?;
272    Ok(v.map(|Wrapper(a)| a))
273}
274
275/// Custom deserializer.
276/// Use #[serde(deserialize_with = "short::must_deserialize_ids")] to serde with derive.
277/// ref. <https://serde.rs/impl-deserialize.html>
278pub fn must_deserialize_ids<'de, D>(deserializer: D) -> Result<Vec<Id>, D::Error>
279where
280    D: Deserializer<'de>,
281{
282    #[derive(Deserialize)]
283    struct Wrapper(#[serde(deserialize_with = "fmt_ids")] Vec<Id>);
284    let v = Option::deserialize(deserializer)?;
285    match v.map(|Wrapper(a)| a) {
286        Some(unwrapped) => Ok(unwrapped),
287        None => Err(serde::de::Error::custom("empty Ids from deserialization")),
288    }
289}
290
291fn fmt_ids<'de, D>(deserializer: D) -> Result<Vec<Id>, D::Error>
292where
293    D: serde::Deserializer<'de>,
294{
295    type Strings = Vec<String>;
296    let ss = Strings::deserialize(deserializer)?;
297    match ss
298        .iter()
299        .map(|x| x.parse::<Id>())
300        .collect::<Result<Vec<Id>, Error>>()
301    {
302        Ok(x) => Ok(x),
303        Err(e) => Err(serde::de::Error::custom(format!(
304            "failed to deserialize Ids {}",
305            e
306        ))),
307    }
308}
309
310/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::node::test_serialize --exact --show-output
311#[test]
312fn test_serialize() {
313    let id = Id::from_slice(&<Vec<u8>>::from([
314        0x3d, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
315        0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
316    ]));
317    assert_eq!(id.to_string(), "NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx");
318
319    #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
320    struct Data {
321        id: Id,
322        id2: Option<Id>,
323        ids: Vec<Id>,
324    }
325    let d = Data {
326        id,
327        id2: Some(id),
328        ids: vec![id, id, id, id, id],
329    };
330
331    let yaml_encoded = serde_yaml::to_string(&d).unwrap();
332    assert!(yaml_encoded.contains("NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"));
333    let yaml_decoded = serde_yaml::from_str(&yaml_encoded).unwrap();
334    assert_eq!(d, yaml_decoded);
335
336    let json_encoded = serde_json::to_string(&d).unwrap();
337    assert!(json_encoded.contains("NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"));
338    let json_decoded = serde_json::from_str(&json_encoded).unwrap();
339    assert_eq!(d, json_decoded);
340}
341
342/// Set is a set of NodeIds
343/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/ids#NewNodeIDSet>
344pub type Set = HashSet<Id>;
345
346/// Return a new NodeIdSet with initial capacity \[size\].
347/// More or less than \[size\] elements can be added to this set.
348/// Using NewNodeIDSet() rather than ids.NodeIDSet{} is just an optimization that can
349/// be used if you know how many elements will be put in this set.
350pub fn new_set(size: usize) -> Set {
351    let set: HashSet<Id> = HashSet::with_capacity(size);
352    set
353}
354
355/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::node::test_from_cert_file --exact --show-output
356#[test]
357fn test_from_cert_file() {
358    let _ = env_logger::builder()
359        .filter_level(log::LevelFilter::Info)
360        .is_test(true)
361        .try_init();
362
363    let node_id = Id::from_slice(&<Vec<u8>>::from([
364        0x3d, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, //
365        0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, //
366    ]));
367    assert_eq!(
368        format!("{}", node_id),
369        "NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"
370    );
371    assert_eq!(
372        node_id.to_string(),
373        "NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"
374    );
375    assert_eq!(
376        node_id.short_id().to_string(),
377        "6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"
378    );
379    assert_eq!(
380        node_id,
381        Id::from_str("6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx").unwrap()
382    );
383    assert_eq!(
384        node_id,
385        Id::from_str("NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx").unwrap()
386    );
387
388    // copied from "avalanchego/staking/local/staking1.key,crt"
389    // verified by "avalanchego-compatibility/node-id" for compatibility with Go
390    let node_id = Id::from_cert_pem_file("./artifacts/staker1.insecure.crt").unwrap();
391    assert_eq!(
392        format!("{}", node_id),
393        "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg"
394    );
395    assert_eq!(
396        node_id.to_string(),
397        "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg"
398    );
399    assert_eq!(
400        node_id,
401        Id::from_str("7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg").unwrap()
402    );
403    assert_eq!(
404        node_id,
405        Id::from_str("NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg").unwrap()
406    );
407
408    let node_id = Id::from_cert_pem_file("./artifacts/staker2.insecure.crt").unwrap();
409    assert_eq!(
410        format!("{}", node_id),
411        "NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ"
412    );
413    assert_eq!(
414        node_id.to_string(),
415        "NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ"
416    );
417    assert_eq!(
418        node_id,
419        Id::from_str("MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ").unwrap()
420    );
421    assert_eq!(
422        node_id,
423        Id::from_str("NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ").unwrap()
424    );
425
426    let node_id = Id::from_cert_pem_file("./artifacts/staker3.insecure.crt").unwrap();
427    assert_eq!(
428        format!("{}", node_id),
429        "NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN"
430    );
431    assert_eq!(
432        node_id.to_string(),
433        "NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN"
434    );
435    assert_eq!(
436        node_id,
437        Id::from_str("NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN").unwrap()
438    );
439    assert_eq!(
440        node_id,
441        Id::from_str("NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN").unwrap()
442    );
443
444    let node_id = Id::from_cert_pem_file("./artifacts/staker4.insecure.crt").unwrap();
445    assert_eq!(
446        format!("{}", node_id),
447        "NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu"
448    );
449    assert_eq!(
450        node_id.to_string(),
451        "NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu"
452    );
453    assert_eq!(
454        node_id,
455        Id::from_str("GWPcbFJZFfZreETSoWjPimr846mXEKCtu").unwrap()
456    );
457    assert_eq!(
458        node_id,
459        Id::from_str("NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu").unwrap()
460    );
461
462    let node_id = Id::from_cert_pem_file("./artifacts/staker5.insecure.crt").unwrap();
463    assert_eq!(
464        format!("{}", node_id),
465        "NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5"
466    );
467    assert_eq!(
468        node_id.to_string(),
469        "NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5"
470    );
471    assert_eq!(
472        node_id,
473        Id::from_str("P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5").unwrap()
474    );
475    assert_eq!(
476        node_id,
477        Id::from_str("NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5").unwrap()
478    );
479
480    let node_id = Id::from_cert_pem_file("./artifacts/test.insecure.crt").unwrap();
481    assert_eq!(
482        format!("{}", node_id),
483        "NodeID-29HTAG5cfN2fw79A67Jd5zY9drcT51EBG"
484    );
485    assert_eq!(
486        node_id.to_string(),
487        "NodeID-29HTAG5cfN2fw79A67Jd5zY9drcT51EBG"
488    );
489    assert_eq!(
490        node_id,
491        Id::from_str("29HTAG5cfN2fw79A67Jd5zY9drcT51EBG").unwrap()
492    );
493    assert_eq!(
494        node_id,
495        Id::from_str("NodeID-29HTAG5cfN2fw79A67Jd5zY9drcT51EBG").unwrap()
496    );
497}
498
499impl Ord for Id {
500    fn cmp(&self, other: &Id) -> Ordering {
501        self.0.cmp(&(other.0))
502    }
503}
504
505impl PartialOrd for Id {
506    fn partial_cmp(&self, other: &Id) -> Option<Ordering> {
507        Some(self.cmp(other))
508    }
509}
510
511impl PartialEq for Id {
512    fn eq(&self, other: &Id) -> bool {
513        self.cmp(other) == Ordering::Equal
514    }
515}
516
517/// ref. <https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq>
518impl Hash for Id {
519    fn hash<H: Hasher>(&self, state: &mut H) {
520        self.0.hash(state);
521    }
522}
523
524#[derive(Debug, Eq, Clone)]
525pub struct Ids(Vec<Id>);
526
527impl Ids {
528    pub fn new(ids: &[Id]) -> Self {
529        Ids(Vec::from(ids))
530    }
531}
532
533impl From<Vec<Id>> for Ids {
534    fn from(ids: Vec<Id>) -> Self {
535        Self::new(&ids)
536    }
537}
538
539impl Ord for Ids {
540    fn cmp(&self, other: &Ids) -> Ordering {
541        // packer encodes the array length first
542        // so if the lengths differ, the ordering is decided
543        let l1 = self.0.len();
544        let l2 = other.0.len();
545        l1.cmp(&l2) // returns when lengths are not Equal
546            .then_with(
547                || self.0.cmp(&other.0), // if lengths are Equal, compare the ids
548            )
549    }
550}
551
552impl PartialOrd for Ids {
553    fn partial_cmp(&self, other: &Ids) -> Option<Ordering> {
554        Some(self.cmp(other))
555    }
556}
557
558impl PartialEq for Ids {
559    fn eq(&self, other: &Ids) -> bool {
560        self.cmp(other) == Ordering::Equal
561    }
562}
563
564/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::node::test_sort --exact --show-output
565#[test]
566fn test_sort() {
567    // lengths of individual ids do not matter since all are fixed-sized
568    let id1 = Id::from_slice(&<Vec<u8>>::from([
569        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
570        0x00, 0x00, 0x00, 0x00, 0x00,
571    ]));
572    let id2 = Id::from_slice(&<Vec<u8>>::from([
573        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
574        0x00, 0x00, 0x00, 0x00, 0x00,
575    ]));
576    assert!(id1 == id2);
577
578    // lengths of individual ids do not matter since all are fixed-sized
579    let id1 = Id::from_slice(&<Vec<u8>>::from([
580        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
581        0x00, 0x00, 0x00, 0x00, 0x00,
582    ]));
583    let id2 = Id::from_slice(&<Vec<u8>>::from([
584        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
585        0x00, 0x00, 0x00, 0x00, 0x00,
586    ]));
587    assert!(id1 < id2);
588
589    // lengths of individual ids do not matter since all are fixed-sized
590    let id1 = Id::from_slice(&<Vec<u8>>::from([
591        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
592        0x00, 0x00, 0x00, 0x00, 0x00,
593    ]));
594    let id2 = Id::from_slice(&<Vec<u8>>::from([
595        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
596        0x00, 0x00, 0x00, 0x00, 0x00,
597    ]));
598    assert!(id1 > id2);
599
600    // lengths of NodeIds matter
601    let ids1 = Ids(vec![
602        Id::from_slice(&<Vec<u8>>::from([
603            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
604            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
605        ])),
606        Id::from_slice(&<Vec<u8>>::from([
607            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
608            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
609        ])),
610        Id::from_slice(&<Vec<u8>>::from([
611            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
612            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
613        ])),
614    ]);
615    let ids2 = Ids(vec![
616        Id::from_slice(&<Vec<u8>>::from([
617            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
618            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
619        ])),
620        Id::from_slice(&<Vec<u8>>::from([
621            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
622            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
623        ])),
624        Id::from_slice(&<Vec<u8>>::from([
625            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
626            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
627        ])),
628    ]);
629    assert!(ids1 == ids2);
630
631    // lengths of NodeIds matter
632    let ids1 = Ids(vec![
633        Id::from_slice(&<Vec<u8>>::from([
634            0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
635            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
636        ])),
637        Id::from_slice(&<Vec<u8>>::from([
638            0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
639            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
640        ])),
641        Id::from_slice(&<Vec<u8>>::from([
642            0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
643            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
644        ])),
645    ]);
646    let ids2 = Ids(vec![
647        Id::from_slice(&<Vec<u8>>::from([
648            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
649            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
650        ])),
651        Id::from_slice(&<Vec<u8>>::from([
652            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
653            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
654        ])),
655        Id::from_slice(&<Vec<u8>>::from([
656            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
657            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
658        ])),
659        Id::from_slice(&<Vec<u8>>::from([
660            0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
661            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
662        ])),
663    ]);
664    assert!(ids1 < ids2);
665
666    // lengths of NodeIds matter
667    let ids1 = Ids(vec![
668        Id::from_slice(&<Vec<u8>>::from([
669            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
670            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
671        ])),
672        Id::from_slice(&<Vec<u8>>::from([
673            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
674            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
675        ])),
676        Id::from_slice(&<Vec<u8>>::from([
677            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
678            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
679        ])),
680        Id::from_slice(&<Vec<u8>>::from([
681            0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
682            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
683        ])),
684    ]);
685    let ids2 = Ids(vec![
686        Id::from_slice(&<Vec<u8>>::from([
687            0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
688            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
689        ])),
690        Id::from_slice(&<Vec<u8>>::from([
691            0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
692            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
693        ])),
694        Id::from_slice(&<Vec<u8>>::from([
695            0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
696            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
697        ])),
698    ]);
699    assert!(ids1 > ids2);
700
701    // lengths of NodeIds matter
702    let ids1 = Ids(vec![
703        Id::from_slice(&<Vec<u8>>::from([
704            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
705            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
706        ])),
707        Id::from_slice(&<Vec<u8>>::from([
708            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
709            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
710        ])),
711        Id::from_slice(&<Vec<u8>>::from([
712            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
713            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
714        ])),
715    ]);
716    let ids2 = Ids(vec![
717        Id::from_slice(&<Vec<u8>>::from([
718            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
719            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
720        ])),
721        Id::from_slice(&<Vec<u8>>::from([
722            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
723            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
724        ])),
725        Id::from_slice(&<Vec<u8>>::from([
726            0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
727            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
728        ])),
729    ]);
730    assert!(ids1 < ids2);
731
732    let mut ids1 = vec![
733        Id::from_slice(&<Vec<u8>>::from([
734            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
735            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
736        ])),
737        Id::from_slice(&<Vec<u8>>::from([
738            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
739            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
740        ])),
741        Id::from_slice(&<Vec<u8>>::from([
742            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
743            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
744        ])),
745    ];
746    ids1.sort();
747    let ids2 = vec![
748        Id::from_slice(&<Vec<u8>>::from([
749            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
750            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
751        ])),
752        Id::from_slice(&<Vec<u8>>::from([
753            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
754            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
755        ])),
756        Id::from_slice(&<Vec<u8>>::from([
757            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
758            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
759        ])),
760    ];
761    assert!(ids1 == ids2);
762}