celestia_tendermint/node/
id.rs

1//! Tendermint node IDs
2
3use core::{
4    fmt::{self, Debug, Display},
5    str::FromStr,
6};
7
8use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
9use subtle::{self, ConstantTimeEq};
10use subtle_encoding::hex;
11
12use crate::serializers::cow_str::CowStr;
13use crate::{error::Error, prelude::*};
14
15/// Length of a Node ID in bytes
16pub const LENGTH: usize = 20;
17
18/// Node IDs
19#[allow(clippy::derived_hash_with_manual_eq)]
20#[derive(Copy, Clone, Eq, Hash, PartialOrd, Ord)]
21pub struct Id([u8; LENGTH]);
22
23impl Id {
24    /// Create a new Node ID from raw bytes
25    pub fn new(bytes: [u8; LENGTH]) -> Id {
26        Id(bytes)
27    }
28
29    /// Borrow the node ID as a byte slice
30    pub fn as_bytes(&self) -> &[u8] {
31        &self.0[..]
32    }
33}
34
35impl AsRef<[u8]> for Id {
36    fn as_ref(&self) -> &[u8] {
37        self.as_bytes()
38    }
39}
40
41impl ConstantTimeEq for Id {
42    fn ct_eq(&self, other: &Id) -> subtle::Choice {
43        self.as_bytes().ct_eq(other.as_bytes())
44    }
45}
46
47impl Display for Id {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        for byte in &self.0 {
50            write!(f, "{byte:02x}")?;
51        }
52        Ok(())
53    }
54}
55
56impl Debug for Id {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        write!(f, "node::Id({self})")
59    }
60}
61
62#[cfg(feature = "rust-crypto")]
63mod key_conversions {
64    use super::{Id, LENGTH};
65    use crate::crypto::default::Sha256;
66    use crate::public_key::{Ed25519, PublicKey};
67    use crate::Error;
68    use digest::Digest;
69
70    impl From<Ed25519> for Id {
71        fn from(pk: Ed25519) -> Id {
72            let digest = Sha256::digest(pk.as_bytes());
73            let mut bytes = [0u8; LENGTH];
74            bytes.copy_from_slice(&digest[..LENGTH]);
75            Id(bytes)
76        }
77    }
78
79    impl TryFrom<PublicKey> for Id {
80        type Error = Error;
81
82        fn try_from(pk: PublicKey) -> Result<Self, Self::Error> {
83            match pk {
84                PublicKey::Ed25519(ed25519) => Ok(Id::from(ed25519)),
85                #[cfg(feature = "secp256k1")]
86                _ => Err(Error::unsupported_key_type()),
87            }
88        }
89    }
90}
91
92/// Decode Node ID from hex
93impl FromStr for Id {
94    type Err = Error;
95
96    fn from_str(s: &str) -> Result<Self, Self::Err> {
97        // Accept either upper or lower case hex
98        let bytes = hex::decode_upper(s)
99            .or_else(|_| hex::decode(s))
100            .map_err(Error::subtle_encoding)?;
101
102        if bytes.len() != LENGTH {
103            return Err(Error::parse("invalid length".to_string()));
104        }
105
106        let mut result_bytes = [0u8; LENGTH];
107        result_bytes.copy_from_slice(&bytes);
108        Ok(Id(result_bytes))
109    }
110}
111
112impl PartialEq for Id {
113    fn eq(&self, other: &Id) -> bool {
114        self.ct_eq(other).into()
115    }
116}
117
118impl<'de> Deserialize<'de> for Id {
119    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
120    where
121        D: Deserializer<'de>,
122    {
123        let s = CowStr::deserialize(deserializer)?;
124        Self::from_str(&s).map_err(|_| {
125            de::Error::custom(format!(
126                "expected {}-character hex string, got {:?}",
127                LENGTH * 2,
128                s
129            ))
130        })
131    }
132}
133
134impl Serialize for Id {
135    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
136        self.to_string().serialize(serializer)
137    }
138}