celestia_tendermint/node/
id.rs1use 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
15pub const LENGTH: usize = 20;
17
18#[allow(clippy::derived_hash_with_manual_eq)]
20#[derive(Copy, Clone, Eq, Hash, PartialOrd, Ord)]
21pub struct Id([u8; LENGTH]);
22
23impl Id {
24 pub fn new(bytes: [u8; LENGTH]) -> Id {
26 Id(bytes)
27 }
28
29 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
92impl FromStr for Id {
94 type Err = Error;
95
96 fn from_str(s: &str) -> Result<Self, Self::Err> {
97 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}