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