1use crate::{
5 Error,
6 peer::{
7 Answer, Behaviour, CALL_TIMEOUT, Command, PeerNetwork, Pending, bind_response, request,
8 },
9 peer_limits::LimitDenied,
10 peer_protocol::{
11 GossipPeer, Kind, PEER_ADDRESSES_MAX, PEERS_LIMIT, PEERS_MAX, Reply, Response,
12 },
13 source::check_cancelled,
14};
15use bitcoin::BlockHash;
16use libp2p::{
17 Multiaddr, PeerId, Swarm,
18 multiaddr::Protocol,
19 swarm::{ConnectionId, DialError, dial_opts::DialOpts},
20};
21use std::{
22 collections::{BTreeSet, HashMap, VecDeque},
23 net::{Ipv4Addr, Ipv6Addr},
24 sync::{
25 Mutex,
26 atomic::{AtomicBool, Ordering},
27 mpsc::{self, RecvTimeoutError},
28 },
29 time::{Duration, Instant, SystemTime, UNIX_EPOCH},
30};
31
32pub(crate) struct Shared {
35 local: PeerId,
36 gossip: Mutex<Vec<GossipPeer>>,
37 observed: Mutex<ObservedAddrs>,
38 connected: Mutex<BTreeSet<PeerId>>,
39 identified: Mutex<VecDeque<(PeerId, Vec<Multiaddr>)>>,
40}
41
42#[derive(Default)]
45pub struct ObservedAddrs {
46 candidates: Vec<(Multiaddr, BTreeSet<PeerId>)>,
47 confirmed: Vec<Multiaddr>,
48}
49
50impl ObservedAddrs {
51 pub const WITNESSES: usize = 2;
52 const CANDIDATES_MAX: usize = 32;
53 const CONFIRMED_MAX: usize = 8;
54
55 pub fn observe(&mut self, address: Multiaddr, reporter: PeerId) -> Option<Multiaddr> {
58 if !is_public(&address)
59 || validate_gossip_address(&address).is_err()
60 || self.confirmed.contains(&address)
61 {
62 return None;
63 }
64 let index = match self
65 .candidates
66 .iter()
67 .position(|(known, _)| *known == address)
68 {
69 Some(index) => index,
70 None => {
71 if self.candidates.len() == Self::CANDIDATES_MAX {
72 self.candidates.remove(0);
73 }
74 self.candidates.push((address, BTreeSet::new()));
75 self.candidates.len() - 1
76 }
77 };
78 self.candidates[index].1.insert(reporter);
79 if self.candidates[index].1.len() < Self::WITNESSES {
80 return None;
81 }
82 let (address, _) = self.candidates.remove(index);
83 if self.confirmed.len() == Self::CONFIRMED_MAX {
84 self.confirmed.remove(0);
85 }
86 self.confirmed.push(address.clone());
87 Some(address)
88 }
89
90 pub fn confirmed(&self) -> Vec<Multiaddr> {
91 self.confirmed.clone()
92 }
93}
94
95impl Shared {
96 const GOSSIP_MAX: usize = 256;
97 const IDENTIFIED_MAX: usize = 64;
98 const ENVELOPE: usize = 1024;
99
100 pub(crate) fn new(local: PeerId) -> Self {
101 Self {
102 local,
103 gossip: Mutex::new(Vec::new()),
104 observed: Mutex::new(ObservedAddrs::default()),
105 connected: Mutex::new(BTreeSet::new()),
106 identified: Mutex::new(VecDeque::new()),
107 }
108 }
109
110 pub(crate) fn identified(&self, peer: PeerId, advertised: Vec<Multiaddr>) {
112 let addresses: Vec<Multiaddr> = advertised
113 .into_iter()
114 .filter(|address| validate_gossip_address(address).is_ok() && is_public(address))
115 .take(PEER_ADDRESSES_MAX)
116 .collect();
117 if addresses.is_empty() || peer == self.local {
118 return;
119 }
120 let mut queue = match self.identified.lock() {
121 Ok(queue) => queue,
122 Err(poisoned) => poisoned.into_inner(),
123 };
124 queue.retain(|(known, _)| *known != peer);
125 if queue.len() == Self::IDENTIFIED_MAX {
126 queue.pop_front();
127 }
128 queue.push_back((peer, addresses));
129 }
130
131 fn drain_identified(&self) -> Vec<(PeerId, Vec<Multiaddr>)> {
132 match self.identified.lock() {
133 Ok(mut queue) => queue.drain(..).collect(),
134 Err(poisoned) => poisoned.into_inner().drain(..).collect(),
135 }
136 }
137
138 fn set_gossip(&self, mut peers: Vec<GossipPeer>) {
139 peers.truncate(Self::GOSSIP_MAX);
140 if let Ok(mut gossip) = self.gossip.lock() {
141 *gossip = peers;
142 }
143 }
144
145 fn observed(&self) -> Vec<Multiaddr> {
146 match self.observed.lock() {
147 Ok(observed) => observed.confirmed(),
148 Err(poisoned) => poisoned.into_inner().confirmed(),
149 }
150 }
151
152 pub(crate) fn is_connected(&self, peer: PeerId) -> bool {
153 match self.connected.lock() {
154 Ok(connected) => connected.contains(&peer),
155 Err(poisoned) => poisoned.into_inner().contains(&peer),
156 }
157 }
158
159 fn connected(&self) -> Vec<PeerId> {
160 match self.connected.lock() {
161 Ok(connected) => connected.iter().copied().collect(),
162 Err(poisoned) => poisoned.into_inner().iter().copied().collect(),
163 }
164 }
165
166 pub(crate) fn set_connected(&self, peer: PeerId, connected: bool) {
168 let mut set = match self.connected.lock() {
169 Ok(set) => set,
170 Err(poisoned) => poisoned.into_inner(),
171 };
172 match connected {
173 true => set.insert(peer),
174 false => set.remove(&peer),
175 };
176 }
177
178 pub(crate) fn observe(&self, address: Multiaddr, reporter: PeerId) -> Option<Multiaddr> {
181 match self.observed.lock() {
182 Ok(mut observed) => observed.observe(address, reporter),
183 Err(poisoned) => poisoned.into_inner().observe(address, reporter),
184 }
185 }
186
187 pub(crate) fn serve(&self, requester: PeerId, loopback: bool, limit: u32) -> Reply {
191 let Ok(limit) = usize::try_from(limit) else {
192 return Reply::Refused;
193 };
194 if limit > usize::try_from(PEERS_MAX).unwrap_or(0) {
195 return Reply::Refused;
196 }
197 let Ok(snapshot) = self.gossip.lock() else {
198 return Reply::Busy;
199 };
200 let now = unix_now();
201 let mut peers = Vec::new();
202 let listed = snapshot
203 .iter()
204 .filter(|entry| entry.peer_id != requester && entry.peer_id != self.local);
205 for entry in listed {
206 if peers.len() == limit {
207 break;
208 }
209 let addrs: Vec<Multiaddr> = entry
210 .addrs
211 .iter()
212 .filter(|address| validate_gossip_address(address).is_ok())
213 .filter(|address| loopback || is_public(address))
214 .take(PEER_ADDRESSES_MAX)
215 .cloned()
216 .collect();
217 if !addrs.is_empty() {
218 peers.push(GossipPeer {
219 peer_id: entry.peer_id,
220 addrs,
221 last_seen: entry.last_seen.min(now),
222 });
223 }
224 }
225 loop {
226 match serde_json::to_vec(&peers) {
227 Ok(bytes) if bytes.len() + Self::ENVELOPE <= PEERS_LIMIT => break,
228 Ok(_) => {
229 peers.pop();
230 }
231 Err(_) => return Reply::Busy,
232 }
233 }
234 Reply::Peers(peers)
235 }
236}
237
238pub fn is_public(address: &Multiaddr) -> bool {
241 match address.iter().next() {
242 Some(Protocol::Ip4(ip)) => public_v4(ip),
243 Some(Protocol::Ip6(ip)) => match ip.to_ipv4_mapped() {
244 Some(mapped) => public_v4(mapped),
245 None => public_v6(ip),
246 },
247 Some(
248 Protocol::Dns(name)
249 | Protocol::Dns4(name)
250 | Protocol::Dns6(name)
251 | Protocol::Dnsaddr(name),
252 ) => name != "localhost" && !name.ends_with(".localhost"),
253 _ => false,
254 }
255}
256
257fn public_v4(ip: Ipv4Addr) -> bool {
258 let [first, second, third, _] = ip.octets();
259 let reserved = first == 0
260 || (first == 100 && (second & 0xc0) == 64)
261 || (first == 192 && second == 0 && third == 0)
262 || (first == 198 && (second & 0xfe) == 18)
263 || first >= 240;
264 !(reserved
265 || ip.is_loopback()
266 || ip.is_private()
267 || ip.is_link_local()
268 || ip.is_multicast()
269 || ip.is_documentation())
270}
271
272fn public_v6(ip: Ipv6Addr) -> bool {
273 let documentation = ip.segments()[..2] == [0x2001, 0x0db8];
274 !(documentation
275 || ip.is_loopback()
276 || ip.is_unspecified()
277 || ip.is_multicast()
278 || ip.is_unique_local()
279 || ip.is_unicast_link_local())
280}
281
282fn unix_now() -> u64 {
283 match SystemTime::now().duration_since(UNIX_EPOCH) {
284 Ok(elapsed) => elapsed.as_secs(),
285 Err(_clock_before_epoch) => 0,
286 }
287}
288
289pub(crate) fn is_loopback(address: &Multiaddr) -> bool {
290 match address.iter().next() {
291 Some(Protocol::Ip4(ip)) => ip.is_loopback(),
292 Some(Protocol::Ip6(ip)) => ip.is_loopback(),
293 _ => false,
294 }
295}
296
297pub fn validate_gossip_address(address: &Multiaddr) -> Result<Option<PeerId>, Error> {
300 match address.iter().next() {
301 Some(Protocol::Ip4(_) | Protocol::Ip6(_)) => validate_dial(address),
302 _ => Err(Error::Invalid(
303 "gossiped addresses must be IP literals with TCP".into(),
304 )),
305 }
306}
307
308pub fn validate_announce(address: &Multiaddr, local: PeerId) -> Result<Multiaddr, Error> {
311 if validate_gossip_address(address)?.is_some_and(|named| named != local) {
312 return Err(Error::Invalid("announce address names another peer".into()));
313 }
314 if !is_public(address) {
315 return Err(Error::Invalid("announce address must be public".into()));
316 }
317 Ok(address
318 .iter()
319 .filter(|part| !matches!(part, Protocol::P2p(_)))
320 .collect())
321}
322
323pub fn validate_dial(address: &Multiaddr) -> Result<Option<PeerId>, Error> {
326 let invalid = || Error::Invalid("peer dial requires host, TCP port and optional /p2p".into());
327 let mut parts = address.iter();
328 match parts.next() {
329 Some(Protocol::Dnsaddr(_)) => (),
330 Some(
331 Protocol::Ip4(_)
332 | Protocol::Ip6(_)
333 | Protocol::Dns(_)
334 | Protocol::Dns4(_)
335 | Protocol::Dns6(_),
336 ) => match parts.next() {
337 Some(Protocol::Tcp(port)) if port != 0 => (),
338 _ => return Err(invalid()),
339 },
340 _ => return Err(invalid()),
341 }
342 let peer = match parts.next() {
343 None => None,
344 Some(Protocol::P2p(peer)) => Some(peer),
345 Some(_) => return Err(invalid()),
346 };
347 if parts.next().is_some() {
348 return Err(invalid());
349 }
350 Ok(peer)
351}
352
353pub(crate) fn validate_peers(
355 genesis: BlockHash,
356 peer: PeerId,
357 pending: &Pending,
358 response: Response,
359) -> Result<Vec<GossipPeer>, Error> {
360 bind_response(genesis, peer, pending, &response)?;
361 let Kind::Peers { limit } = pending.kind else {
362 return Err(Error::Invalid("peers reply to another request".into()));
363 };
364 match response.result {
365 Reply::Peers(peers) => {
366 let limit = usize::try_from(limit).map_err(|e| Error::Invalid(e.to_string()))?;
367 if peers.len() > limit {
368 return Err(Error::Invalid(
369 "peer gossip exceeds the requested limit".into(),
370 ));
371 }
372 for entry in &peers {
373 check_gossip(entry)?;
374 }
375 Ok(peers)
376 }
377 Reply::Busy => Err(Error::PeerBusy),
378 _ => Err(Error::Invalid(
379 "peer refused the peers request or answered another kind".into(),
380 )),
381 }
382}
383
384fn check_gossip(entry: &GossipPeer) -> Result<(), Error> {
385 let peer = entry.peer_id;
386 if entry.addrs.is_empty() || entry.addrs.len() > PEER_ADDRESSES_MAX {
387 return Err(Error::Invalid(
388 "peer gossip address count out of range".into(),
389 ));
390 }
391 for address in &entry.addrs {
392 if validate_gossip_address(address)?.is_some_and(|named| named != peer) {
393 return Err(Error::Invalid(
394 "peer gossip address names another peer".into(),
395 ));
396 }
397 }
398 Ok(())
399}
400
401pub(crate) struct Dial {
402 address: Multiaddr,
403 deadline: Instant,
404 answer: mpsc::SyncSender<Result<PeerId, Error>>,
405}
406
407impl Dial {
408 fn answer(&self, result: Result<PeerId, Error>) {
409 let _ = self.answer.try_send(result);
410 }
411}
412
413#[derive(Default)]
415pub(crate) struct Dials(HashMap<ConnectionId, (Option<PeerId>, Dial)>);
416
417impl Dials {
418 const MAX: usize = 8;
419
420 pub(crate) fn start(&mut self, swarm: &mut Swarm<Behaviour>, dial: Dial) {
421 let expected = match validate_dial(&dial.address) {
422 Ok(expected) => expected,
423 Err(error) => return dial.answer(Err(error)),
424 };
425 if Instant::now() >= dial.deadline || self.0.len() >= Self::MAX {
426 return dial.answer(Err(Error::Busy("peer dial capacity or deadline".into())));
427 }
428 if let Some(peer) = expected.filter(|peer| swarm.is_connected(peer)) {
429 return dial.answer(Ok(peer));
430 }
431 let options = match expected {
432 Some(peer) => DialOpts::peer_id(peer)
433 .addresses(vec![dial.address.clone()])
434 .build(),
435 None => DialOpts::unknown_peer_id()
436 .address(dial.address.clone())
437 .build(),
438 };
439 let connection = options.connection_id();
440 match swarm.dial(options) {
441 Ok(()) => {
442 self.0.insert(connection, (expected, dial));
443 }
444 Err(error) => dial.answer(Err(dial_error(&error))),
445 }
446 }
447
448 pub(crate) fn established(&mut self, connection: ConnectionId, peer: PeerId) {
449 if let Some((expected, dial)) = self.0.remove(&connection) {
450 if expected.is_some_and(|expected| expected != peer) {
451 return dial.answer(Err(Error::Invalid(
452 "dialed peer authenticated as another identity".into(),
453 )));
454 }
455 dial.answer(Ok(peer));
456 }
457 }
458
459 pub(crate) fn failed(&mut self, connection: ConnectionId, error: &DialError) {
460 if let Some((_, dial)) = self.0.remove(&connection) {
461 dial.answer(Err(dial_error(error)));
462 }
463 }
464
465 pub(crate) fn expire(&mut self) {
467 let now = Instant::now();
468 self.0.retain(|_, (_, dial)| {
469 if now < dial.deadline {
470 return true;
471 }
472 dial.answer(Err(Error::Busy("peer dial deadline".into())));
473 false
474 });
475 }
476
477 pub(crate) fn close(&mut self) {
478 for (_, (_, dial)) in self.0.drain() {
479 dial.answer(Err(Error::Busy("peer network stopped".into())));
480 }
481 }
482}
483
484fn dial_error(error: &DialError) -> Error {
485 match error {
486 DialError::Denied { cause } => match cause.downcast_ref::<LimitDenied>() {
487 Some(LimitDenied(limit)) => Error::DialDenied(*limit),
488 None => Error::Invalid(format!("peer dial: {error}")),
489 },
490 DialError::DialPeerConditionFalse(condition) => {
491 Error::Busy(format!("peer dial already pending ({condition:?})"))
492 }
493 other => Error::Invalid(format!("peer dial: {other}")),
494 }
495}
496
497impl PeerNetwork {
498 const DIAL_POLL: Duration = Duration::from_millis(50);
499
500 pub fn set_gossip(&self, peers: Vec<GossipPeer>) {
503 self.shared.set_gossip(peers);
504 }
505
506 pub fn observed_addrs(&self) -> Vec<Multiaddr> {
508 self.shared.observed()
509 }
510
511 pub fn connected_peer_ids(&self) -> Vec<PeerId> {
513 self.shared.connected()
514 }
515
516 pub fn drain_identified(&self) -> Vec<(PeerId, Vec<Multiaddr>)> {
519 self.shared.drain_identified()
520 }
521
522 pub fn dial(
528 &self,
529 address: Multiaddr,
530 cancelled: &AtomicBool,
531 deadline: Instant,
532 ) -> Result<PeerId, Error> {
533 validate_dial(&address)?;
534 check_cancelled(cancelled)?;
535 if self.stop.load(Ordering::Acquire) {
536 return Err(Error::Busy("peer network stopped".into()));
537 }
538 let deadline = deadline.min(Instant::now() + CALL_TIMEOUT);
539 let (answer, receiver) = mpsc::sync_channel(1);
540 self.commands
541 .try_send(Command::Dial(Dial {
542 address,
543 deadline,
544 answer,
545 }))
546 .map_err(|_| Error::Busy("peer command queue unavailable".into()))?;
547 let peer = loop {
548 check_cancelled(cancelled)?;
549 let left = deadline.saturating_duration_since(Instant::now());
550 if left.is_zero() {
551 return Err(Error::Busy("peer dial deadline".into()));
552 }
553 match receiver.recv_timeout(left.min(Self::DIAL_POLL)) {
554 Ok(result) => break result?,
555 Err(RecvTimeoutError::Timeout) => (),
556 Err(RecvTimeoutError::Disconnected) => {
557 return Err(Error::Busy("peer network stopped".into()));
558 }
559 }
560 };
561 match self.peers_of(peer, 0, cancelled, deadline) {
562 Ok(_) => Ok(peer),
563 Err(busy @ (Error::Busy(_) | Error::PeerBusy)) => Err(busy),
564 Err(error) => {
565 let _ = self.commands.try_send(Command::Disconnect(peer));
566 Err(error)
567 }
568 }
569 }
570
571 pub fn peers_of(
574 &self,
575 peer: PeerId,
576 limit: u32,
577 cancelled: &AtomicBool,
578 deadline: Instant,
579 ) -> Result<Vec<GossipPeer>, Error> {
580 if limit > PEERS_MAX {
581 return Err(Error::Invalid("peers request limit exceeds 32".into()));
582 }
583 check_cancelled(cancelled)?;
584 let kind = Kind::Peers { limit };
585 request(
586 &self.commands,
587 &self.stop,
588 (peer, Vec::new()),
589 kind,
590 deadline,
591 Answer::Peers,
592 )
593 }
594}