1use crate::{Error, VerifyPeerError};
4use base16ct::mixed as hex;
5use prost::DecodeError;
6use std::{
7 fmt::{self, Debug, Display},
8 str::FromStr,
9};
10use subtle::{Choice, ConstantTimeEq};
11
12#[allow(clippy::derived_hash_with_manual_eq)]
15#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)]
16pub struct PeerId(pub [u8; Self::LENGTH]);
17
18impl PeerId {
19 pub const LENGTH: usize = 20;
21
22 pub fn new(bytes: [u8; Self::LENGTH]) -> Self {
24 Self(bytes)
25 }
26
27 pub fn as_bytes(&self) -> &[u8; Self::LENGTH] {
29 &self.0
30 }
31
32 pub fn to_bytes(self) -> [u8; Self::LENGTH] {
34 self.0
35 }
36
37 pub fn verify(self, expected_peer_id: PeerId) -> Result<(), VerifyPeerError> {
40 if bool::from(self.ct_eq(&expected_peer_id)) {
41 Ok(())
42 } else {
43 Err(VerifyPeerError {
44 expected_peer_id,
45 actual_peer_id: self,
46 })
47 }
48 }
49}
50
51impl AsRef<[u8]> for PeerId {
52 fn as_ref(&self) -> &[u8] {
53 self.as_bytes()
54 }
55}
56
57impl ConstantTimeEq for PeerId {
58 fn ct_eq(&self, other: &Self) -> Choice {
59 self.0.ct_eq(&other.0)
60 }
61}
62
63impl Display for PeerId {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 for byte in &self.0 {
66 write!(f, "{byte:02x}")?;
67 }
68 Ok(())
69 }
70}
71
72impl Debug for PeerId {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "node::Id({self})")
75 }
76}
77
78impl From<[u8; PeerId::LENGTH]> for PeerId {
79 fn from(bytes: [u8; PeerId::LENGTH]) -> PeerId {
80 PeerId(bytes)
81 }
82}
83
84impl From<PeerId> for [u8; PeerId::LENGTH] {
85 fn from(peer_id: PeerId) -> Self {
86 peer_id.0
87 }
88}
89
90impl From<&PeerId> for [u8; PeerId::LENGTH] {
91 fn from(peer_id: &PeerId) -> Self {
92 peer_id.0
93 }
94}
95
96impl FromStr for PeerId {
98 type Err = Error;
99
100 fn from_str(s: &str) -> Result<Self, Error> {
101 let bytes = hex::decode_vec(s).map_err(|_| DecodeError::new("hex decoding error"))?;
103 bytes
104 .try_into()
105 .map(Self)
106 .map_err(|_| DecodeError::new("invalid peer ID length").into())
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::PeerId;
113 use crate::VerifyPeerError;
114
115 const PEER_ID_1: PeerId = PeerId([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
116 const PEER_ID_2: PeerId = PeerId([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
117
118 #[test]
119 fn verify_peer() {
120 assert!(PEER_ID_1.verify(PEER_ID_1).is_ok());
121
122 let err = PEER_ID_1.verify(PEER_ID_2).unwrap_err();
123 assert_eq!(
124 err,
125 VerifyPeerError {
126 expected_peer_id: PEER_ID_2,
127 actual_peer_id: PEER_ID_1
128 }
129 )
130 }
131}