Skip to main content

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