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