tentacle_secio/
peer_id.rs

1/// Most of the code for this module comes from `rust-libp2p`.
2use std::fmt;
3
4use rand::{Rng, thread_rng};
5use unsigned_varint::{decode, encode};
6
7use crate::handshake::handshake_struct::PublicKey;
8
9const SHA256_CODE: u64 = 0x12;
10const SHA256_SIZE: u8 = 32;
11
12/// Identifier of a peer of the network
13///
14/// The data is a hash of the public key of the peer
15#[derive(Clone, PartialOrd, PartialEq, Eq, Hash)]
16pub struct PeerId {
17    inner: Vec<u8>,
18}
19
20impl PeerId {
21    /// Builds a `PeerId` from a public key.
22    #[inline]
23    pub fn from_public_key(public_key: &PublicKey) -> Self {
24        let key_inner = public_key.inner_ref();
25        Self::from_seed(key_inner)
26    }
27
28    /// If data is a valid `PeerId`, return `PeerId`, else return error
29    pub fn from_bytes(data: Vec<u8>) -> Result<Self, Error> {
30        if data.is_empty() {
31            return Err(Error::Empty);
32        }
33
34        let (code, bytes) = decode::u64(&data).map_err(|_| Error::InvalidData)?;
35
36        if code != SHA256_CODE {
37            return Err(Error::NotSupportHashCode);
38        }
39
40        if bytes.len() != SHA256_SIZE as usize + 1 {
41            return Err(Error::WrongLength);
42        }
43
44        if bytes[0] != SHA256_SIZE {
45            return Err(Error::InvalidData);
46        }
47
48        Ok(PeerId { inner: data })
49    }
50
51    /// Return a random `PeerId`
52    pub fn random() -> Self {
53        let mut seed = [0u8; 20];
54        thread_rng().fill(&mut seed[..]);
55        Self::from_seed(&seed)
56    }
57
58    /// Return `PeerId` which used hashed seed as inner.
59    fn from_seed(seed: &[u8]) -> Self {
60        let mut buf = encode::u64_buffer();
61        let code = encode::u64(SHA256_CODE, &mut buf);
62
63        let header_len = code.len() + 1;
64
65        let mut inner = vec![0; header_len + SHA256_SIZE as usize];
66        inner[..code.len()].copy_from_slice(code);
67        inner[code.len()] = SHA256_SIZE;
68
69        let mut ctx = crate::sha256_compat::Context::new();
70        ctx.update(seed);
71        inner[header_len..].copy_from_slice(ctx.finish().as_ref());
72        PeerId { inner }
73    }
74
75    /// Return raw bytes representation of this peer id
76    #[inline]
77    pub fn as_bytes(&self) -> &[u8] {
78        &self.inner
79    }
80
81    /// Consume self, return raw bytes representation of this peer id
82    #[inline]
83    pub fn into_bytes(self) -> Vec<u8> {
84        self.inner
85    }
86
87    /// Returns a base-58 encoded string of this `PeerId`.
88    #[inline]
89    pub fn to_base58(&self) -> String {
90        bs58::encode(self.inner.clone()).into_string()
91    }
92
93    /// Returns the raw bytes of the hash of this `PeerId`.
94    #[inline]
95    pub fn digest(&self) -> &[u8] {
96        let (_, bytes) = decode::u16(&self.inner).expect("a invalid digest");
97        &bytes[1..]
98    }
99
100    /// Checks whether the public key passed as parameter matches the public key of this `PeerId`.
101    pub fn is_public_key(&self, public_key: &PublicKey) -> bool {
102        let peer_id = Self::from_public_key(public_key);
103        &peer_id == self
104    }
105}
106
107impl fmt::Debug for PeerId {
108    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109        write!(f, "PeerId({})", self.to_base58())
110    }
111}
112
113impl fmt::Display for PeerId {
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        write!(f, "{}", self.to_base58())
116    }
117}
118
119impl From<PublicKey> for PeerId {
120    #[inline]
121    fn from(key: PublicKey) -> PeerId {
122        PeerId::from_public_key(&key)
123    }
124}
125
126impl ::std::str::FromStr for PeerId {
127    type Err = Error;
128
129    #[inline]
130    fn from_str(s: &str) -> Result<Self, Self::Err> {
131        let bytes = bs58::decode(s).into_vec().map_err(|_| Error::InvalidData)?;
132        PeerId::from_bytes(bytes)
133    }
134}
135
136/// Error code from generate peer id
137#[derive(Debug)]
138pub enum Error {
139    /// invalid data
140    InvalidData,
141    /// data has wrong length
142    WrongLength,
143    /// not support hash code
144    NotSupportHashCode,
145    /// empty data
146    Empty,
147}
148
149impl fmt::Display for Error {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        match self {
152            Error::Empty => write!(f, "data is empty"),
153            Error::InvalidData => write!(f, "invalid data"),
154            Error::WrongLength => write!(f, "wrong length"),
155            Error::NotSupportHashCode => write!(f, "not support hash code"),
156        }
157    }
158}
159
160impl ::std::error::Error for Error {}
161
162#[cfg(test)]
163mod tests {
164    use crate::{SecioKeyPair, peer_id::PeerId};
165
166    #[test]
167    fn peer_id_is_public_key() {
168        let pub_key = SecioKeyPair::secp256k1_generated().public_key();
169        let peer_id = PeerId::from_public_key(&pub_key);
170        assert!(peer_id.is_public_key(&pub_key));
171    }
172
173    #[test]
174    fn peer_id_into_bytes_then_from_bytes() {
175        let peer_id = SecioKeyPair::secp256k1_generated().peer_id();
176        let second = PeerId::from_bytes(peer_id.as_bytes().to_vec()).unwrap();
177        assert_eq!(peer_id, second);
178    }
179
180    #[test]
181    fn peer_id_to_base58_then_back() {
182        let peer_id = SecioKeyPair::secp256k1_generated().peer_id();
183        let second: PeerId = peer_id.to_base58().parse().unwrap();
184        assert_eq!(peer_id, second);
185    }
186
187    #[test]
188    fn peer_id_randomness() {
189        let peer_id = PeerId::random();
190        let second: PeerId = PeerId::random();
191        assert_ne!(peer_id, second);
192    }
193}