Skip to main content

urma_node/
peer_gossip.rs

1//! Peer gossip and explicit dialing. Gossiped addresses are untrusted routing
2//! hints; a dial proves only the remote transport identity, and the Peers probe
3//! that follows it only that the peer speaks this protocol on the same chain.
4use crate::{
5    Error,
6    peer::{
7        Answer, Behaviour, CALL_TIMEOUT, Command, PeerNetwork, Pending, bind_response, request,
8    },
9    peer_address::{AddressKind, Dialed, is_circuit, parse, relay},
10    peer_limits::{Class, LimitDenied},
11    peer_links::Links,
12    peer_nat::{Nat, PeerLink, Reachability},
13    peer_protocol::{
14        GossipPeer, Kind, PEER_ADDRESSES_MAX, PEERS_LIMIT, PEERS_MAX, Reply, Response,
15    },
16    source::check_cancelled,
17};
18use bitcoin::BlockHash;
19use libp2p::{
20    Multiaddr, PeerId, Swarm,
21    multiaddr::Protocol,
22    swarm::{ConnectionId, DialError, dial_opts::DialOpts},
23};
24use rand::seq::SliceRandom;
25use std::{
26    collections::{BTreeMap, BTreeSet, HashMap, VecDeque},
27    net::{Ipv4Addr, Ipv6Addr},
28    sync::{
29        Mutex, MutexGuard,
30        atomic::{AtomicBool, Ordering},
31        mpsc::{self, RecvTimeoutError},
32    },
33    time::{Duration, Instant, SystemTime, UNIX_EPOCH},
34};
35
36/// State shared between the network thread and the owner: the gossip snapshot
37/// the scheduler hands over, public addresses peers observed for us, the live
38/// connections and our bound listeners.
39pub(crate) struct Shared {
40    local: PeerId,
41    gossip: Mutex<Vec<GossipPeer>>,
42    observed: Mutex<ObservedAddrs>,
43    connected: Mutex<BTreeMap<PeerId, Links>>,
44    identified: Mutex<VecDeque<(PeerId, Vec<Multiaddr>)>>,
45    listening: Mutex<Vec<Multiaddr>>,
46    pub(crate) nat: Nat,
47}
48
49pub(crate) fn guard<T>(lock: &Mutex<T>) -> MutexGuard<'_, T> {
50    match lock.lock() {
51        Ok(guard) => guard,
52        Err(poisoned) => poisoned.into_inner(),
53    }
54}
55
56/// Public addresses outbound peers reported observing for this node. A
57/// candidate is confirmed only once WITNESSES distinct peers reported it.
58#[derive(Default)]
59pub struct ObservedAddrs {
60    candidates: Vec<(Multiaddr, BTreeSet<PeerId>)>,
61    confirmed: Vec<Multiaddr>,
62}
63
64impl ObservedAddrs {
65    pub const WITNESSES: usize = 2;
66    const CANDIDATES_MAX: usize = 32;
67    const CONFIRMED_MAX: usize = 8;
68
69    /// Record one report; non-public or non-literal addresses are ignored.
70    /// Returns the address when this report is the one that confirms it.
71    pub fn observe(&mut self, address: Multiaddr, reporter: PeerId) -> Option<Multiaddr> {
72        if !is_public(&address)
73            || validate_gossip_address(&address).is_err()
74            || self.confirmed.contains(&address)
75        {
76            return None;
77        }
78        let index = match self
79            .candidates
80            .iter()
81            .position(|(known, _)| *known == address)
82        {
83            Some(index) => index,
84            None => {
85                if self.candidates.len() == Self::CANDIDATES_MAX {
86                    self.candidates.remove(0);
87                }
88                self.candidates.push((address, BTreeSet::new()));
89                self.candidates.len() - 1
90            }
91        };
92        self.candidates[index].1.insert(reporter);
93        if self.candidates[index].1.len() < Self::WITNESSES {
94            return None;
95        }
96        let (address, _) = self.candidates.remove(index);
97        if self.confirmed.len() == Self::CONFIRMED_MAX {
98            self.confirmed.remove(0);
99        }
100        self.confirmed.push(address.clone());
101        Some(address)
102    }
103
104    pub fn confirmed(&self) -> Vec<Multiaddr> {
105        self.confirmed.clone()
106    }
107}
108
109impl Shared {
110    const GOSSIP_MAX: usize = 256;
111    const IDENTIFIED_MAX: usize = 64;
112    const ENVELOPE: usize = 1024;
113
114    pub(crate) fn new(local: PeerId) -> Self {
115        Self {
116            local,
117            gossip: Mutex::new(Vec::new()),
118            observed: Mutex::new(ObservedAddrs::default()),
119            connected: Mutex::new(BTreeMap::new()),
120            identified: Mutex::new(VecDeque::new()),
121            listening: Mutex::new(Vec::new()),
122            nat: Nat::default(),
123        }
124    }
125
126    /// Queue a connected peer's own literal public addresses (newest 64 peers).
127    pub(crate) fn identified(&self, peer: PeerId, advertised: Vec<Multiaddr>) {
128        let addresses: Vec<Multiaddr> = advertised
129            .into_iter()
130            .filter(|address| validate_gossip_address(address).is_ok() && is_public(address))
131            .take(PEER_ADDRESSES_MAX)
132            .collect();
133        if addresses.is_empty() || peer == self.local {
134            return;
135        }
136        let mut queue = match self.identified.lock() {
137            Ok(queue) => queue,
138            Err(poisoned) => poisoned.into_inner(),
139        };
140        queue.retain(|(known, _)| *known != peer);
141        if queue.len() == Self::IDENTIFIED_MAX {
142            queue.pop_front();
143        }
144        queue.push_back((peer, addresses));
145    }
146
147    fn drain_identified(&self) -> Vec<(PeerId, Vec<Multiaddr>)> {
148        match self.identified.lock() {
149            Ok(mut queue) => queue.drain(..).collect(),
150            Err(poisoned) => poisoned.into_inner().drain(..).collect(),
151        }
152    }
153
154    fn set_gossip(&self, mut peers: Vec<GossipPeer>) {
155        peers.truncate(Self::GOSSIP_MAX);
156        if let Ok(mut gossip) = self.gossip.lock() {
157            *gossip = peers;
158        }
159    }
160
161    fn observed(&self) -> Vec<Multiaddr> {
162        match self.observed.lock() {
163            Ok(observed) => observed.confirmed(),
164            Err(poisoned) => poisoned.into_inner().confirmed(),
165        }
166    }
167
168    pub(crate) fn is_connected(&self, peer: PeerId) -> bool {
169        guard(&self.connected).contains_key(&peer)
170    }
171
172    /// Whether the peer holds a data connection we dialed.
173    pub(crate) fn is_outbound(&self, peer: PeerId) -> bool {
174        guard(&self.connected)
175            .get(&peer)
176            .is_some_and(|links| links.outbound_data > 0)
177    }
178
179    fn connected(&self) -> Vec<PeerId> {
180        guard(&self.connected).keys().copied().collect()
181    }
182
183    /// Directly connected peers that spoke this protocol version on our
184    /// chain, those we dialed before those that only dialed us. A peer reached
185    /// only through a relay is left out: data never rides a circuit.
186    pub(crate) fn live(&self) -> Vec<PeerId> {
187        let connected = guard(&self.connected);
188        let (outbound, inbound): (Vec<_>, Vec<_>) = connected
189            .iter()
190            .filter(|(_, links)| links.on_chain && links.data > 0)
191            .partition(|(_, links)| links.outbound_data > 0);
192        outbound
193            .into_iter()
194            .chain(inbound)
195            .map(|(peer, _)| *peer)
196            .collect()
197    }
198
199    /// Whether the peer holds a direct data connection: the only kind that
200    /// carries records, inventory, parents or objects (or Peers gossip).
201    pub(crate) fn has_direct(&self, peer: PeerId) -> bool {
202        guard(&self.connected)
203            .get(&peer)
204            .is_some_and(|links| links.data > 0)
205    }
206
207    /// Whether discovery's uniform pick may ask the peer for Peers: a data
208    /// connection, or one we dialed that was demoted (it kept its record
209    /// handlers). Inbound demoted links are cheap to hold, so they never steer
210    /// what the book learns.
211    pub(crate) fn can_gossip(&self, peer: PeerId) -> bool {
212        guard(&self.connected)
213            .get(&peer)
214            .is_some_and(|links| links.data > 0 || links.outbound_demoted > 0)
215    }
216
217    /// Whether the peer holds any direct connection that carries records:
218    /// data, or demoted from data.
219    pub(crate) fn any_direct(&self, peer: PeerId) -> bool {
220        guard(&self.connected)
221            .get(&peer)
222            .is_some_and(|links| links.data > 0 || links.demoted > 0)
223    }
224
225    /// Connected peers that have a link (`Links::link`), sorted by peer.
226    pub(crate) fn links(&self) -> Vec<(PeerId, PeerLink)> {
227        guard(&self.connected)
228            .iter()
229            .filter_map(|(peer, links)| Some((*peer, links.link()?)))
230            .collect()
231    }
232
233    pub(crate) fn opened(&self, peer: PeerId, outbound: bool, class: Class) {
234        let mut connected = guard(&self.connected);
235        connected
236            .entry(peer)
237            .or_default()
238            .count(outbound, class, true);
239    }
240
241    /// The peer leaves with its last connection, chain proof included.
242    pub(crate) fn closed(&self, peer: PeerId, outbound: bool, class: Class) {
243        let mut connected = guard(&self.connected);
244        let Some(links) = connected.get_mut(&peer) else {
245            return;
246        };
247        links.count(outbound, class, false);
248        if links.inbound == 0 && links.outbound == 0 {
249            connected.remove(&peer);
250        }
251    }
252
253    /// One open connection of the peer changed class (a demotion or promotion).
254    pub(crate) fn reclassed(&self, peer: PeerId, outbound: bool, (from, to): (Class, Class)) {
255        if let Some(links) = guard(&self.connected).get_mut(&peer) {
256            links.count(outbound, from, false);
257            links.count(outbound, to, true);
258        }
259    }
260
261    pub(crate) fn is_on_chain(&self, peer: PeerId) -> bool {
262        guard(&self.connected)
263            .get(&peer)
264            .is_some_and(|links| links.on_chain)
265    }
266
267    /// A message carrying this protocol version and our genesis came from or
268    /// was answered by the peer over a live connection.
269    pub(crate) fn on_chain(&self, peer: PeerId) {
270        if let Some(links) = guard(&self.connected).get_mut(&peer) {
271            links.on_chain = true;
272        }
273    }
274
275    pub(crate) fn listening(&self, address: Multiaddr, bound: bool) {
276        let mut listening = guard(&self.listening);
277        listening.retain(|known| *known != address);
278        if bound {
279            listening.push(address);
280        }
281    }
282
283    pub(crate) fn listeners(&self) -> Vec<Multiaddr> {
284        guard(&self.listening).clone()
285    }
286
287    /// One identify report, counted only from outbound connections. Returns
288    /// the address once this report confirms it.
289    pub(crate) fn observe(&self, address: Multiaddr, reporter: PeerId) -> Option<Multiaddr> {
290        match self.observed.lock() {
291            Ok(mut observed) => observed.observe(address, reporter),
292            Err(poisoned) => poisoned.into_inner().observe(address, reporter),
293        }
294    }
295
296    /// Answer a Peers request: peers holding a reservation on this relay
297    /// alternate with the snapshot. Only IP-literal addresses are forwarded,
298    /// private and loopback ones only to loopback requesters. Neither the
299    /// requester nor this node is listed; future `last_seen` is clamped.
300    pub(crate) fn serve(&self, requester: PeerId, loopback: bool, limit: u32) -> Reply {
301        let Ok(limit) = usize::try_from(limit) else {
302            return Reply::Refused;
303        };
304        if limit > usize::try_from(PEERS_MAX).unwrap_or(0) {
305            return Reply::Refused;
306        }
307        let now = unix_now();
308        let reserved = self.reserved(requester, now);
309        let held: Vec<PeerId> = reserved.iter().map(|peer| peer.peer_id).collect();
310        let Ok(snapshot) = self.gossip.lock() else {
311            return Reply::Busy;
312        };
313        let mut listed = snapshot
314            .iter()
315            .filter(|entry| entry.peer_id != requester && entry.peer_id != self.local)
316            .filter(|entry| !held.contains(&entry.peer_id))
317            .filter_map(|entry| servable(entry, loopback, now));
318        let mut reserved = reserved.into_iter();
319        let mut peers = Vec::new();
320        while peers.len() < limit {
321            let (held, known) = (reserved.next(), listed.next());
322            if held.is_none() && known.is_none() {
323                break;
324            }
325            peers.extend(held.into_iter().chain(known));
326        }
327        peers.truncate(limit);
328        loop {
329            match serde_json::to_vec(&peers) {
330                Ok(bytes) if bytes.len() + Self::ENVELOPE <= PEERS_LIMIT => break,
331                Ok(_) => {
332                    peers.pop();
333                }
334                Err(_) => return Reply::Busy,
335            }
336        }
337        Reply::Peers(peers)
338    }
339
340    /// Peers holding an active reservation on this node's relay, in random
341    /// order, each with its circuit addresses through this node's public IPv4
342    /// external addresses. Empty while this node has none.
343    fn reserved(&self, requester: PeerId, now: u64) -> Vec<GossipPeer> {
344        let hops: Vec<Multiaddr> = self
345            .nat
346            .external()
347            .into_iter()
348            .map(|external| {
349                external
350                    .with(Protocol::P2p(self.local))
351                    .with(Protocol::P2pCircuit)
352            })
353            .filter(|hop| parse(hop).is_ok())
354            .take(PEER_ADDRESSES_MAX)
355            .collect();
356        if hops.is_empty() {
357            return Vec::new();
358        }
359        let mut holders = self.nat.reserved_by_others();
360        holders.retain(|holder| *holder != requester && *holder != self.local);
361        holders.shuffle(&mut rand::thread_rng());
362        holders
363            .into_iter()
364            .map(|peer_id| GossipPeer {
365                peer_id,
366                addrs: hops.clone(),
367                last_seen: now,
368            })
369            .collect()
370    }
371}
372
373/// A snapshot entry with only the addresses this requester may receive.
374fn servable(entry: &GossipPeer, loopback: bool, now: u64) -> Option<GossipPeer> {
375    let addrs: Vec<Multiaddr> = entry
376        .addrs
377        .iter()
378        .filter(|address| validate_gossip_address(address).is_ok())
379        .filter(|address| loopback || is_public(address))
380        .take(PEER_ADDRESSES_MAX)
381        .cloned()
382        .collect();
383    (!addrs.is_empty()).then(|| GossipPeer {
384        peer_id: entry.peer_id,
385        addrs,
386        last_seen: entry.last_seen.min(now),
387    })
388}
389
390/// Globally routable IP, or a DNS name other than localhost. IPv4-mapped IPv6
391/// addresses are judged as the IPv4 address they carry.
392pub fn is_public(address: &Multiaddr) -> bool {
393    match address.iter().next() {
394        Some(Protocol::Ip4(ip)) => public_v4(ip),
395        Some(Protocol::Ip6(ip)) => match ip.to_ipv4_mapped() {
396            Some(mapped) => public_v4(mapped),
397            None => public_v6(ip),
398        },
399        Some(
400            Protocol::Dns(name)
401            | Protocol::Dns4(name)
402            | Protocol::Dns6(name)
403            | Protocol::Dnsaddr(name),
404        ) => name != "localhost" && !name.ends_with(".localhost"),
405        _ => false,
406    }
407}
408
409fn public_v4(ip: Ipv4Addr) -> bool {
410    let [first, second, third, _] = ip.octets();
411    let reserved = first == 0
412        || (first == 100 && (second & 0xc0) == 64)
413        || (first == 192 && second == 0 && third == 0)
414        || (first == 198 && (second & 0xfe) == 18)
415        || first >= 240;
416    !(reserved
417        || ip.is_loopback()
418        || ip.is_private()
419        || ip.is_link_local()
420        || ip.is_multicast()
421        || ip.is_documentation())
422}
423
424fn public_v6(ip: Ipv6Addr) -> bool {
425    let documentation = ip.segments()[..2] == [0x2001, 0x0db8];
426    !(documentation
427        || ip.is_loopback()
428        || ip.is_unspecified()
429        || ip.is_multicast()
430        || ip.is_unique_local()
431        || ip.is_unicast_link_local())
432}
433
434fn unix_now() -> u64 {
435    match SystemTime::now().duration_since(UNIX_EPOCH) {
436        Ok(elapsed) => elapsed.as_secs(),
437        Err(_clock_before_epoch) => 0,
438    }
439}
440
441pub(crate) fn is_loopback(address: &Multiaddr) -> bool {
442    match address.iter().next() {
443        Some(Protocol::Ip4(ip)) => ip.is_loopback(),
444        Some(Protocol::Ip6(ip)) => ip.is_loopback(),
445        _ => false,
446    }
447}
448
449/// Gossip carries only IP-literal addresses, direct or circuit, each with an
450/// optional `/p2p`: names come from operators (seeds, manual), never peers.
451pub fn validate_gossip_address(address: &Multiaddr) -> Result<Option<PeerId>, Error> {
452    let shape = parse(address)?;
453    match shape.bare.iter().next() {
454        Some(Protocol::Ip4(_) | Protocol::Ip6(_)) => Ok(shape.peer),
455        _ => Err(Error::Invalid(
456            "gossiped addresses must be IP literals".into(),
457        )),
458    }
459}
460
461/// Whether a failed chain probe closes the peer's connections: only when it
462/// answered for another chain, version or protocol. Busy, transport and
463/// admission failures say nothing about the peer, and its other connections
464/// (a relay reservation among them) stay.
465pub(crate) fn probe_closes(error: &Error) -> bool {
466    matches!(error, Error::Invalid(_))
467}
468
469/// Dial targets: any direct address shape, or a circuit that names its
470/// target. Returns the named identity.
471pub fn validate_dial(address: &Multiaddr) -> Result<Option<PeerId>, Error> {
472    let shape = parse(address)?;
473    if shape.peer.is_none() && matches!(shape.kind, AddressKind::Circuit(_)) {
474        return Err(Error::Invalid(
475            "circuit dial must name its target peer".into(),
476        ));
477    }
478    Ok(shape.peer)
479}
480
481/// Bind the reply to its request, then check every gossiped hint's shape.
482pub(crate) fn validate_peers(
483    genesis: BlockHash,
484    peer: PeerId,
485    pending: &Pending,
486    response: Response,
487) -> Result<Vec<GossipPeer>, Error> {
488    bind_response(genesis, peer, pending, &response)?;
489    let Kind::Peers { limit } = pending.kind else {
490        return Err(Error::Invalid("peers reply to another request".into()));
491    };
492    match response.result {
493        Reply::Peers(peers) => {
494            let limit = usize::try_from(limit).map_err(|e| Error::Invalid(e.to_string()))?;
495            if peers.len() > limit {
496                return Err(Error::Invalid(
497                    "peer gossip exceeds the requested limit".into(),
498                ));
499            }
500            for entry in &peers {
501                check_gossip(entry)?;
502            }
503            Ok(peers)
504        }
505        Reply::Busy => Err(Error::PeerBusy),
506        _ => Err(Error::Invalid(
507            "peer refused the peers request or answered another kind".into(),
508        )),
509    }
510}
511
512fn check_gossip(entry: &GossipPeer) -> Result<(), Error> {
513    let peer = entry.peer_id;
514    if entry.addrs.is_empty() || entry.addrs.len() > PEER_ADDRESSES_MAX {
515        return Err(Error::Invalid(
516            "peer gossip address count out of range".into(),
517        ));
518    }
519    for address in &entry.addrs {
520        if validate_gossip_address(address)?.is_some_and(|named| named != peer)
521            || relay(address) == Some(peer)
522        {
523            return Err(Error::Invalid(
524                "peer gossip address names another peer".into(),
525            ));
526        }
527    }
528    Ok(())
529}
530
531pub(crate) struct Dial {
532    address: Multiaddr,
533    deadline: Instant,
534    answer: mpsc::SyncSender<Result<PeerId, Error>>,
535}
536
537impl Dial {
538    fn answer(&self, result: Result<PeerId, Error>) {
539        let _ = self.answer.try_send(result);
540    }
541}
542
543/// Outstanding explicit dials on the network thread, keyed by connection.
544#[derive(Default)]
545pub(crate) struct Dials(HashMap<ConnectionId, (Option<PeerId>, Dial)>);
546
547impl Dials {
548    const MAX: usize = 8;
549
550    pub(crate) fn start(&mut self, swarm: &mut Swarm<Behaviour>, dial: Dial) {
551        let expected = match validate_dial(&dial.address) {
552            Ok(expected) => expected,
553            Err(error) => return dial.answer(Err(error)),
554        };
555        if Instant::now() >= dial.deadline || self.0.len() >= Self::MAX {
556            return dial.answer(Err(Error::Busy("peer dial capacity or deadline".into())));
557        }
558        if let Some(peer) = expected.filter(|peer| swarm.is_connected(peer)) {
559            return dial.answer(Ok(peer));
560        }
561        let options = match expected {
562            Some(peer) => DialOpts::peer_id(peer)
563                .addresses(vec![dial.address.clone()])
564                .build(),
565            None => DialOpts::unknown_peer_id()
566                .address(dial.address.clone())
567                .build(),
568        };
569        let connection = options.connection_id();
570        match swarm.dial(options) {
571            Ok(()) => {
572                self.0.insert(connection, (expected, dial));
573            }
574            Err(error) => dial.answer(Err(dial_error(&error))),
575        }
576    }
577
578    pub(crate) fn established(&mut self, connection: ConnectionId, peer: PeerId) {
579        if let Some((expected, dial)) = self.0.remove(&connection) {
580            if expected.is_some_and(|expected| expected != peer) {
581                return dial.answer(Err(Error::Invalid(
582                    "dialed peer authenticated as another identity".into(),
583                )));
584            }
585            dial.answer(Ok(peer));
586        }
587    }
588
589    pub(crate) fn failed(&mut self, connection: ConnectionId, error: &DialError) {
590        if let Some((_, dial)) = self.0.remove(&connection) {
591            dial.answer(Err(dial_error(error)));
592        }
593    }
594
595    /// A dial past its deadline is answered now; a late connection idles out.
596    pub(crate) fn expire(&mut self) {
597        let now = Instant::now();
598        self.0.retain(|_, (_, dial)| {
599            if now < dial.deadline {
600                return true;
601            }
602            dial.answer(Err(Error::Busy("peer dial deadline".into())));
603            false
604        });
605    }
606
607    pub(crate) fn close(&mut self) {
608        for (_, (_, dial)) in self.0.drain() {
609            dial.answer(Err(Error::Busy("peer network stopped".into())));
610        }
611    }
612}
613
614fn dial_error(error: &DialError) -> Error {
615    match error {
616        DialError::Denied { cause } => match cause.downcast_ref::<LimitDenied>() {
617            Some(LimitDenied(limit)) => Error::DialDenied(*limit),
618            None => Error::Invalid(format!("peer dial: {error}")),
619        },
620        DialError::LocalPeerId { .. } => Error::LocalPeer,
621        DialError::DialPeerConditionFalse(condition) => {
622            Error::Busy(format!("peer dial already pending ({condition:?})"))
623        }
624        other => Error::Invalid(format!("peer dial: {other}")),
625    }
626}
627
628impl PeerNetwork {
629    const DIAL_POLL: Duration = Duration::from_millis(50);
630
631    /// Replace the snapshot served to Peers requests. Order is preserved; the
632    /// first entries are the ones a bounded request receives.
633    pub fn set_gossip(&self, peers: Vec<GossipPeer>) {
634        self.shared.set_gossip(peers);
635    }
636
637    /// Public addresses connected peers reported observing for this node.
638    pub fn observed_addrs(&self) -> Vec<Multiaddr> {
639        self.shared.observed()
640    }
641
642    /// Peers with at least one established connection right now, sorted.
643    pub fn connected_peer_ids(&self) -> Vec<PeerId> {
644        self.shared.connected()
645    }
646
647    /// Connected peers with their link, sorted by peer.
648    pub fn peer_links(&self) -> Vec<(PeerId, PeerLink)> {
649        self.shared.links()
650    }
651
652    /// Whether the peer holds a direct connection; relayed-only peers never
653    /// carry data.
654    pub fn has_direct(&self, peer: PeerId) -> bool {
655        self.shared.has_direct(peer)
656    }
657
658    /// Whether Peers gossip can ride an open connection to the peer.
659    pub fn can_gossip(&self, peer: PeerId) -> bool {
660        self.shared.can_gossip(peer)
661    }
662
663    /// Whether a direct connection that carries records is open to the peer.
664    pub fn any_direct(&self, peer: PeerId) -> bool {
665        self.shared.any_direct(peer)
666    }
667
668    pub fn reachability(&self) -> Reachability {
669        self.shared.nat.reachability()
670    }
671
672    /// Addresses advertised to peers: AutoNAT-confirmed or announced.
673    pub fn external_addresses(&self) -> Vec<Multiaddr> {
674        self.shared.nat.external()
675    }
676
677    /// Relays holding an active reservation for this node.
678    pub fn relays(&self) -> Vec<PeerId> {
679        self.shared.nat.relays()
680    }
681
682    /// Peers holding an active reservation on this node's relay server.
683    pub fn reserved_by_others(&self) -> Vec<PeerId> {
684        self.shared.nat.reserved_by_others()
685    }
686
687    /// This node's `/p2p-circuit` listen addresses through its relays.
688    pub fn circuit_addresses(&self) -> Vec<Multiaddr> {
689        self.shared.nat.circuits()
690    }
691
692    /// Connected peers' own literal public addresses learned through identify
693    /// since the last call, newest 64 peers; the queue is emptied.
694    pub fn drain_identified(&self) -> Vec<(PeerId, Vec<Multiaddr>)> {
695        self.shared.drain_identified()
696    }
697
698    /// `dial_any` for a peer that must be reached directly: a peer reached
699    /// only through a relay is a Busy error until DCUtR links it directly.
700    pub fn dial(
701        &self,
702        address: Multiaddr,
703        cancelled: &AtomicBool,
704        deadline: Instant,
705    ) -> Result<PeerId, Error> {
706        match self.dial_any(address, cancelled, deadline)? {
707            Dialed::Direct(peer) => Ok(peer),
708            Dialed::Relayed(peer) => Err(Error::Busy(format!("{peer} reachable only via relay"))),
709        }
710    }
711
712    /// Dial an address (with or without `/p2p`); a direct peer gets the chain
713    /// `probe`. A relayed-only peer is returned unprobed: records/5 never runs
714    /// on relayed links. The connection stays for later requests.
715    pub fn dial_any(
716        &self,
717        address: Multiaddr,
718        cancelled: &AtomicBool,
719        deadline: Instant,
720    ) -> Result<Dialed, Error> {
721        validate_dial(&address)?;
722        check_cancelled(cancelled)?;
723        if self.stop.load(Ordering::Acquire) {
724            return Err(Error::Busy("peer network stopped".into()));
725        }
726        let deadline = deadline.min(Instant::now() + CALL_TIMEOUT);
727        let circuit = is_circuit(&address);
728        let (answer, receiver) = mpsc::sync_channel(1);
729        self.commands
730            .try_send(Command::Dial(Dial {
731                address,
732                deadline,
733                answer,
734            }))
735            .map_err(|_| Error::Busy("peer command queue unavailable".into()))?;
736        let peer = loop {
737            check_cancelled(cancelled)?;
738            let left = deadline.saturating_duration_since(Instant::now());
739            if left.is_zero() {
740                return Err(Error::Busy("peer dial deadline".into()));
741            }
742            match receiver.recv_timeout(left.min(Self::DIAL_POLL)) {
743                Ok(result) => break result?,
744                Err(RecvTimeoutError::Timeout) => (),
745                Err(RecvTimeoutError::Disconnected) => {
746                    return Err(Error::Busy("peer network stopped".into()));
747                }
748            }
749        };
750        if circuit && !self.any_direct(peer) {
751            return Ok(Dialed::Relayed(peer));
752        }
753        self.probe(peer, cancelled, deadline).map(Dialed::Direct)
754    }
755
756    /// The chain probe: a zero-limit Peers request over a direct link. Only a
757    /// peer proven to be elsewhere (`probe_closes`) loses its connections.
758    pub fn probe(
759        &self,
760        peer: PeerId,
761        cancelled: &AtomicBool,
762        deadline: Instant,
763    ) -> Result<PeerId, Error> {
764        let error = match self.peers_of(peer, 0, cancelled, deadline) {
765            Ok(_) => return Ok(peer),
766            Err(error) => error,
767        };
768        if probe_closes(&error) {
769            let _ = self.commands.try_send(Command::Disconnect(peer));
770        }
771        Err(error)
772    }
773
774    /// One bounded Peers request over an existing connection (or addresses the
775    /// swarm already knows). `limit` is at most PEERS_MAX; 0 is a bare probe.
776    pub fn peers_of(
777        &self,
778        peer: PeerId,
779        limit: u32,
780        cancelled: &AtomicBool,
781        deadline: Instant,
782    ) -> Result<Vec<GossipPeer>, Error> {
783        if limit > PEERS_MAX {
784            return Err(Error::Invalid("peers request limit exceeds 32".into()));
785        }
786        check_cancelled(cancelled)?;
787        let kind = Kind::Peers { limit };
788        request(
789            &self.commands,
790            &self.stop,
791            (peer, Vec::new()),
792            kind,
793            deadline,
794            Answer::Peers,
795        )
796    }
797}