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