libp2p_core/
peer_record.rs1use libp2p_identity::{Keypair, PeerId, SigningError};
2use quick_protobuf::{BytesReader, Writer};
3use web_time::SystemTime;
4
5use crate::{proto, signed_envelope, signed_envelope::SignedEnvelope, DecodeError, Multiaddr};
6
7const PAYLOAD_TYPE: &str = "/libp2p/routing-state-record";
8const DOMAIN_SEP: &str = "libp2p-routing-state";
9
10#[derive(Debug, PartialEq, Eq, Clone)]
15pub struct PeerRecord {
16 peer_id: PeerId,
17 seq: u64,
18 addresses: Vec<Multiaddr>,
19
20 envelope: SignedEnvelope,
25}
26
27impl PeerRecord {
28 pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result<Self, FromEnvelopeError> {
33 use quick_protobuf::MessageRead;
34
35 let (payload, signing_key) =
36 envelope.payload_and_signing_key(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes())?;
37 let mut reader = BytesReader::from_bytes(payload);
38 let record = proto::PeerRecord::from_reader(&mut reader, payload).map_err(DecodeError)?;
39
40 let peer_id = PeerId::from_bytes(&record.peer_id)?;
41
42 if peer_id != signing_key.to_peer_id() {
43 return Err(FromEnvelopeError::MismatchedSignature);
44 }
45
46 let seq = record.seq;
47 let addresses = record
48 .addresses
49 .into_iter()
50 .map(|a| a.multiaddr.to_vec().try_into())
51 .collect::<Result<Vec<_>, _>>()?;
52
53 Ok(Self {
54 peer_id,
55 seq,
56 addresses,
57 envelope,
58 })
59 }
60
61 pub fn new(key: &Keypair, addresses: Vec<Multiaddr>) -> Result<Self, SigningError> {
66 use quick_protobuf::MessageWrite;
67
68 let seq = SystemTime::now()
69 .duration_since(SystemTime::UNIX_EPOCH)
70 .expect("now() is never before UNIX_EPOCH")
71 .as_secs();
72 let peer_id = key.public().to_peer_id();
73
74 let payload = {
75 let record = proto::PeerRecord {
76 peer_id: peer_id.to_bytes(),
77 seq,
78 addresses: addresses
79 .iter()
80 .map(|m| proto::AddressInfo {
81 multiaddr: m.to_vec(),
82 })
83 .collect(),
84 };
85
86 let mut buf = Vec::with_capacity(record.get_size());
87 let mut writer = Writer::new(&mut buf);
88 record
89 .write_message(&mut writer)
90 .expect("Encoding to succeed");
91
92 buf
93 };
94
95 let envelope = SignedEnvelope::new(
96 key,
97 String::from(DOMAIN_SEP),
98 PAYLOAD_TYPE.as_bytes().to_vec(),
99 payload,
100 )?;
101
102 Ok(Self {
103 peer_id,
104 seq,
105 addresses,
106 envelope,
107 })
108 }
109
110 pub fn to_signed_envelope(&self) -> SignedEnvelope {
111 self.envelope.clone()
112 }
113
114 pub fn into_signed_envelope(self) -> SignedEnvelope {
115 self.envelope
116 }
117
118 pub fn peer_id(&self) -> PeerId {
119 self.peer_id
120 }
121
122 pub fn seq(&self) -> u64 {
123 self.seq
124 }
125
126 pub fn addresses(&self) -> &[Multiaddr] {
127 self.addresses.as_slice()
128 }
129}
130
131#[derive(thiserror::Error, Debug)]
132pub enum FromEnvelopeError {
133 #[error("Failed to extract payload from envelope")]
135 BadPayload(#[from] signed_envelope::ReadPayloadError),
136 #[error("Failed to decode bytes as PeerRecord")]
138 InvalidPeerRecord(#[from] DecodeError),
139 #[error("Failed to decode bytes as PeerId")]
141 InvalidPeerId(#[from] libp2p_identity::ParseError),
142 #[error("The signer of the envelope is different than the peer id in the record")]
144 MismatchedSignature,
145 #[error("Failed to decode bytes as MultiAddress")]
147 InvalidMultiaddr(#[from] multiaddr::Error),
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 const HOME: &str = "/ip4/127.0.0.1/tcp/1337";
155
156 #[test]
157 fn roundtrip_envelope() {
158 let key = Keypair::generate_ed25519();
159
160 let record = PeerRecord::new(&key, vec![HOME.parse().unwrap()]).unwrap();
161
162 let envelope = record.to_signed_envelope();
163 let reconstructed = PeerRecord::from_signed_envelope(envelope).unwrap();
164
165 assert_eq!(reconstructed, record)
166 }
167
168 #[test]
169 fn mismatched_signature() {
170 use quick_protobuf::MessageWrite;
171
172 let addr: Multiaddr = HOME.parse().unwrap();
173
174 let envelope = {
175 let identity_a = Keypair::generate_ed25519();
176 let identity_b = Keypair::generate_ed25519();
177
178 let payload = {
179 let record = proto::PeerRecord {
180 peer_id: identity_a.public().to_peer_id().to_bytes(),
181 seq: 0,
182 addresses: vec![proto::AddressInfo {
183 multiaddr: addr.to_vec(),
184 }],
185 };
186
187 let mut buf = Vec::with_capacity(record.get_size());
188 let mut writer = Writer::new(&mut buf);
189 record
190 .write_message(&mut writer)
191 .expect("Encoding to succeed");
192
193 buf
194 };
195
196 SignedEnvelope::new(
197 &identity_b,
198 String::from(DOMAIN_SEP),
199 PAYLOAD_TYPE.as_bytes().to_vec(),
200 payload,
201 )
202 .unwrap()
203 };
204
205 assert!(matches!(
206 PeerRecord::from_signed_envelope(envelope),
207 Err(FromEnvelopeError::MismatchedSignature)
208 ));
209 }
210}