Skip to main content

radicle_protocol/
service.rs

1#![allow(clippy::too_many_arguments)]
2#![deny(clippy::unwrap_used)]
3pub mod command;
4pub use command::{Command, QueryState};
5
6pub mod filter;
7pub mod gossip;
8pub mod io;
9pub mod limiter;
10pub mod message;
11pub mod session;
12
13use std::collections::hash_map::Entry;
14use std::collections::{BTreeSet, HashMap, HashSet};
15use std::net::IpAddr;
16use std::ops::{Deref, DerefMut};
17use std::sync::Arc;
18use std::sync::mpsc;
19use std::{fmt, net, time};
20
21use fastrand::Rng;
22use localtime::{LocalDuration, LocalTime};
23use log::*;
24use nonempty::NonEmpty;
25
26use radicle::identity::Doc;
27use radicle::node;
28use radicle::node::address;
29use radicle::node::address::Store as _;
30use radicle::node::address::{AddressBook, AddressType, KnownAddress};
31use radicle::node::config::{PeerConfig, RateLimit};
32use radicle::node::refs::Store as _;
33use radicle::node::routing::Store as _;
34use radicle::node::seed;
35use radicle::node::seed::Store as _;
36use radicle::node::{Penalty, Severity};
37use radicle::storage::refs::{FeatureLevel, SIGREFS_BRANCH};
38use radicle::storage::{RepositoryError, RepositoryInfo, SignedRefsInfo};
39use radicle_fetch::policy::SeedingPolicy;
40
41use crate::fetcher;
42use crate::fetcher::FetcherState;
43use crate::fetcher::RefsToFetch;
44use crate::fetcher::service::FetcherService;
45use crate::service::gossip::Store as _;
46use crate::service::message::{
47    Announcement, AnnouncementMessage, Info, NodeAnnouncement, Ping, RefsAnnouncement, RefsStatus,
48};
49use crate::service::policy::{Scope, store::Write};
50use radicle::identity::RepoId;
51use radicle::node::events::Emitter;
52use radicle::node::routing;
53use radicle::node::routing::InsertResult;
54use radicle::node::{Address, Features, FetchResult, HostName, Seed, Seeds, SyncStatus, SyncedAt};
55use radicle::prelude::*;
56use radicle::storage;
57use radicle::storage::{Namespaces, ReadStorage, refs::RefsAt};
58// use radicle::worker::fetch;
59// use crate::worker::FetchError;
60use radicle::crypto;
61use radicle::crypto::Signer as _;
62use radicle::node::Link;
63use radicle::node::PROTOCOL_VERSION;
64
65use crate::bounded::BoundedVec;
66use crate::service::filter::Filter;
67pub use crate::service::message::{Message, ZeroBytes};
68pub use crate::service::session::{QueuedFetch, Session};
69use crate::worker::FetchError;
70use radicle::node::events::{Event, Events};
71use radicle::node::{Config, NodeId};
72
73use radicle::node::policy::config as policy;
74
75use self::io::Outbox;
76use self::limiter::RateLimiter;
77use self::message::InventoryAnnouncement;
78use self::policy::NamespacesError;
79
80/// How often to run the "idle" task.
81pub const IDLE_INTERVAL: LocalDuration = LocalDuration::from_secs(30);
82/// How often to run the "gossip" task.
83pub const GOSSIP_INTERVAL: LocalDuration = LocalDuration::from_secs(6);
84/// How often to run the "announce" task.
85pub const ANNOUNCE_INTERVAL: LocalDuration = LocalDuration::from_mins(60);
86/// How often to run the "sync" task.
87pub const SYNC_INTERVAL: LocalDuration = LocalDuration::from_secs(60);
88/// How often to run the "prune" task.
89pub const PRUNE_INTERVAL: LocalDuration = LocalDuration::from_mins(30);
90/// Duration to wait on an unresponsive peer before dropping its connection.
91pub const STALE_CONNECTION_TIMEOUT: LocalDuration = LocalDuration::from_mins(2);
92/// How much time should pass after a peer was last active for a *ping* to be sent.
93pub const KEEP_ALIVE_DELTA: LocalDuration = LocalDuration::from_mins(1);
94/// Maximum number of latency values to keep for a session.
95pub const MAX_LATENCIES: usize = 16;
96/// Maximum time difference between the local time, and an announcement timestamp.
97pub const MAX_TIME_DELTA: LocalDuration = LocalDuration::from_mins(60);
98/// Maximum attempts to connect to a peer before we give up.
99pub const MAX_CONNECTION_ATTEMPTS: usize = 3;
100/// How far back from the present time should we request gossip messages when connecting to a peer,
101/// when we come online for the first time.
102pub const INITIAL_SUBSCRIBE_BACKLOG_DELTA: LocalDuration = LocalDuration::from_mins(60 * 24);
103/// When subscribing, what margin of error do we give ourselves. A greater delta means we ask for
104/// messages further back than strictly necessary, to account for missed messages.
105pub const SUBSCRIBE_BACKLOG_DELTA: LocalDuration = LocalDuration::from_mins(3);
106/// Minimum amount of time to wait before reconnecting to a peer.
107pub const MIN_RECONNECTION_DELTA: LocalDuration = LocalDuration::from_secs(3);
108/// Maximum amount of time to wait before reconnecting to a peer.
109pub const MAX_RECONNECTION_DELTA: LocalDuration = LocalDuration::from_mins(60);
110/// Connection retry delta used for ephemeral peers that failed to connect previously.
111pub const CONNECTION_RETRY_DELTA: LocalDuration = LocalDuration::from_mins(10);
112/// How long to wait for a fetch to stall before aborting, default is 30s.
113pub const FETCH_TIMEOUT: time::Duration = time::Duration::from_secs(30);
114/// Target number of peers to maintain connections to.
115pub const TARGET_OUTBOUND_PEERS: usize = 8;
116
117/// Maximum external address limit imposed by message size limits.
118pub use message::ADDRESS_LIMIT;
119/// Maximum inventory limit imposed by message size limits.
120pub use message::INVENTORY_LIMIT;
121/// Maximum number of project git references imposed by message size limits.
122pub use message::REF_REMOTE_LIMIT;
123
124/// Metrics we track.
125#[derive(Clone, Debug, Default, serde::Serialize)]
126#[serde(rename_all = "camelCase")]
127pub struct Metrics {
128    /// Metrics for each peer.
129    pub peers: HashMap<NodeId, PeerMetrics>,
130    /// Tasks queued in worker queue.
131    pub worker_queue_size: usize,
132    /// Current open channel count.
133    pub open_channels: usize,
134}
135
136impl Metrics {
137    /// Get metrics for the given peer.
138    pub fn peer(&mut self, nid: NodeId) -> &mut PeerMetrics {
139        self.peers.entry(nid).or_default()
140    }
141}
142
143/// Per-peer metrics we track.
144#[derive(Clone, Debug, Default, serde::Serialize)]
145#[serde(rename_all = "camelCase")]
146pub struct PeerMetrics {
147    pub received_git_bytes: usize,
148    pub received_fetch_requests: usize,
149    pub received_bytes: usize,
150    pub received_gossip_messages: usize,
151    pub sent_bytes: usize,
152    pub sent_fetch_requests: usize,
153    pub sent_git_bytes: usize,
154    pub sent_gossip_messages: usize,
155    pub streams_opened: usize,
156    pub inbound_connection_attempts: usize,
157    pub outbound_connection_attempts: usize,
158    pub disconnects: usize,
159}
160
161/// Result of syncing our routing table with a node's inventory.
162#[derive(Default)]
163struct SyncedRouting {
164    /// Repo entries added.
165    added: Vec<RepoId>,
166    /// Repo entries removed.
167    removed: Vec<RepoId>,
168    /// Repo entries updated (time).
169    updated: Vec<RepoId>,
170}
171
172impl SyncedRouting {
173    fn is_empty(&self) -> bool {
174        self.added.is_empty() && self.removed.is_empty() && self.updated.is_empty()
175    }
176}
177
178/// A peer we can connect to.
179#[derive(Debug, Clone)]
180struct Peer {
181    nid: NodeId,
182    addresses: Vec<KnownAddress>,
183    penalty: Penalty,
184}
185
186/// General service error.
187#[derive(thiserror::Error, Debug)]
188pub enum Error {
189    #[error(transparent)]
190    Git(#[from] radicle::git::raw::Error),
191    #[error(transparent)]
192    Storage(#[from] storage::Error),
193    #[error(transparent)]
194    Gossip(#[from] gossip::Error),
195    #[error(transparent)]
196    Refs(#[from] storage::refs::Error),
197    #[error(transparent)]
198    Routing(#[from] routing::Error),
199    #[error(transparent)]
200    Address(#[from] address::Error),
201    #[error(transparent)]
202    Database(#[from] node::db::Error),
203    #[error(transparent)]
204    Seeds(#[from] seed::Error),
205    #[error(transparent)]
206    Policy(#[from] policy::Error),
207    #[error(transparent)]
208    Repository(#[from] radicle::storage::RepositoryError),
209    #[error("namespaces error: {0}")]
210    Namespaces(Box<NamespacesError>),
211}
212
213impl From<NamespacesError> for Error {
214    fn from(e: NamespacesError) -> Self {
215        Self::Namespaces(Box::new(e))
216    }
217}
218
219#[derive(thiserror::Error, Debug)]
220pub enum ConnectError {
221    #[error("attempted connection to peer {nid} which already has a session")]
222    SessionExists { nid: NodeId },
223    #[error("attempted connection to self")]
224    SelfConnection,
225    #[error("outbound connection limit reached when attempting {nid} ({addr})")]
226    LimitReached { nid: NodeId, addr: Address },
227    #[error(
228        "attempted connection to {nid}, via {addr} but addresses of this kind are not supported"
229    )]
230    UnsupportedAddress { nid: NodeId, addr: Address },
231    #[error("attempted connection with blocked peer {nid}")]
232    Blocked { nid: NodeId },
233}
234
235/// A store for all node data.
236pub trait Store:
237    address::Store + gossip::Store + routing::Store + seed::Store + node::refs::Store
238{
239}
240
241impl Store for radicle::node::Database {}
242
243/// Fetch state for an ongoing fetch.
244#[derive(Debug)]
245pub struct FetchState {
246    /// Node we're fetching from.
247    pub from: NodeId,
248    /// What refs we're fetching.
249    pub refs_at: Vec<RefsAt>,
250    /// Channels waiting for fetch results.
251    pub subscribers: Vec<mpsc::Sender<FetchResult>>,
252}
253
254/// Holds all node stores.
255#[derive(Debug)]
256pub struct Stores<D>(D);
257
258impl<D> Stores<D>
259where
260    D: Store,
261{
262    /// Get the database as a routing store.
263    pub fn routing(&self) -> &impl routing::Store {
264        &self.0
265    }
266
267    /// Get the database as a routing store, mutably.
268    pub fn routing_mut(&mut self) -> &mut impl routing::Store {
269        &mut self.0
270    }
271
272    /// Get the database as an address store.
273    pub fn addresses(&self) -> &impl address::Store {
274        &self.0
275    }
276
277    /// Get the database as an address store, mutably.
278    pub fn addresses_mut(&mut self) -> &mut impl address::Store {
279        &mut self.0
280    }
281
282    /// Get the database as a gossip store.
283    pub fn gossip(&self) -> &impl gossip::Store {
284        &self.0
285    }
286
287    /// Get the database as a gossip store, mutably.
288    pub fn gossip_mut(&mut self) -> &mut impl gossip::Store {
289        &mut self.0
290    }
291
292    /// Get the database as a seed store.
293    pub fn seeds(&self) -> &impl seed::Store {
294        &self.0
295    }
296
297    /// Get the database as a seed store, mutably.
298    pub fn seeds_mut(&mut self) -> &mut impl seed::Store {
299        &mut self.0
300    }
301
302    /// Get the database as a refs db.
303    pub fn refs(&self) -> &impl node::refs::Store {
304        &self.0
305    }
306
307    /// Get the database as a refs db, mutably.
308    pub fn refs_mut(&mut self) -> &mut impl node::refs::Store {
309        &mut self.0
310    }
311}
312
313impl<D> AsMut<D> for Stores<D> {
314    fn as_mut(&mut self) -> &mut D {
315        &mut self.0
316    }
317}
318
319impl<D> From<D> for Stores<D> {
320    fn from(db: D) -> Self {
321        Self(db)
322    }
323}
324
325/// The node service.
326#[derive(Debug)]
327pub struct Service<D, S> {
328    /// Service configuration.
329    config: Config,
330    /// Our cryptographic signer and key.
331    secret_key: crypto::SigningKey,
332    /// Project storage.
333    storage: S,
334    /// Node database.
335    db: Stores<D>,
336    /// Policy configuration.
337    policies: policy::Config<Write>,
338    /// Peer sessions, currently or recently connected.
339    sessions: Sessions,
340    /// Clock. Tells the time.
341    clock: LocalTime,
342    /// Who relayed what announcement to us. We keep track of this to ensure that
343    /// we don't relay messages to nodes that already know about these messages.
344    relayed_by: HashMap<gossip::AnnouncementId, Vec<NodeId>>,
345    /// I/O outbox.
346    outbox: Outbox,
347    /// Cached local node announcement.
348    node: NodeAnnouncement,
349    /// Cached local inventory announcement.
350    inventory: InventoryAnnouncement,
351    /// Source of entropy.
352    rng: Rng,
353    fetcher: FetcherService<command::Responder<FetchResult>>,
354    /// Request/connection rate limiter.
355    limiter: RateLimiter,
356    /// Current seeded repositories bloom filter.
357    filter: Filter,
358    /// Last time the service was idle.
359    last_idle: LocalTime,
360    /// Last time the gossip messages were relayed.
361    last_gossip: LocalTime,
362    /// Last time the service synced.
363    last_sync: LocalTime,
364    /// Last time the service routing table was pruned.
365    last_prune: LocalTime,
366    /// Last time the announcement task was run.
367    last_announce: LocalTime,
368    /// Timestamp of last local inventory announced.
369    last_inventory: LocalTime,
370    /// Last timestamp used for announcements.
371    last_timestamp: Timestamp,
372    /// Time when the service was initialized, or `None` if it wasn't initialized.
373    started_at: Option<LocalTime>,
374    /// Time when the service was last online, or `None` if this is the first time.
375    last_online_at: Option<LocalTime>,
376    /// Publishes events to subscribers.
377    emitter: Emitter<Event>,
378    /// Local listening addresses.
379    listening: Vec<net::SocketAddr>,
380    /// Latest metrics for all nodes connected to since the last start.
381    metrics: Metrics,
382}
383
384impl<D, S> Service<D, S> {
385    /// Get the local service time.
386    pub fn local_time(&self) -> LocalTime {
387        self.clock
388    }
389
390    pub fn emitter(&self) -> Emitter<Event> {
391        self.emitter.clone()
392    }
393}
394
395impl<D, S> Service<D, S>
396where
397    D: Store,
398    S: WriteStorage + 'static,
399{
400    /// Initialize service with current time. Call this once.
401    pub fn initialize(&mut self, time: LocalTime) -> Result<(), Error> {
402        debug!(target: "service", "Init @{}", time.as_millis());
403        assert_ne!(time, LocalTime::default());
404
405        let nid = *self.nid();
406
407        self.clock = time;
408        self.started_at = Some(time);
409        self.last_online_at = match self.db.gossip().last() {
410            Ok(Some(last)) => Some(last.to_local_time()),
411            Ok(None) => None,
412            Err(e) => {
413                warn!(target: "service", "Failed to get the latest gossip message from db: {e}");
414                None
415            }
416        };
417
418        // Populate refs database. This is only useful as part of the upgrade process for nodes
419        // that have been online since before the refs database was created.
420        match self.db.refs().count() {
421            Ok(0) => {
422                info!(target: "service", "Empty refs database, populating from storage..");
423                if let Err(e) = self.db.refs_mut().populate(&self.storage) {
424                    warn!(target: "service", "Failed to populate refs database: {e}");
425                }
426            }
427            Ok(n) => debug!(target: "service", "Refs database has {n} cached references"),
428            Err(e) => {
429                warn!(target: "service", "Failed to retrieve count of refs from database: {e}")
430            }
431        }
432
433        let announced = self
434            .db
435            .seeds()
436            .seeded_by(&nid)?
437            .collect::<Result<HashMap<_, _>, _>>()?;
438        let mut inventory = BTreeSet::new();
439        let mut private = BTreeSet::new();
440
441        for repo in self.storage.repositories()? {
442            let repo = self.upgrade_sigrefs(repo)?;
443            let rid = repo.rid;
444
445            // If we're not seeding this repo, just skip it.
446            if !self.policies.is_seeding(&rid)? {
447                debug!(target: "service", "Local repository {rid} is not seeded");
448                continue;
449            }
450            // Add public repositories to inventory.
451            if repo.doc.is_public() {
452                inventory.insert(rid);
453            } else {
454                private.insert(rid);
455            }
456            // If we have no owned refs for this repo, then there's nothing to announce.
457            let Some(updated_at) = repo.synced_at else {
458                continue;
459            };
460            // Skip this repo if the sync status matches what we have in storage.
461            if let Some(announced) = announced.get(&rid)
462                && updated_at.oid == announced.oid
463            {
464                continue;
465            }
466            // Make sure our local node's sync status is up to date with storage.
467            if self.db.seeds_mut().synced(
468                &rid,
469                &nid,
470                updated_at.oid,
471                updated_at.timestamp.into(),
472            )? {
473                debug!(target: "service", "Saved local sync status for {rid}..");
474            }
475            // If we got here, it likely means a repo was updated while the node was stopped.
476            // Therefore, we pre-load a refs announcement for this repo, so that it is included in
477            // the historical gossip messages when a node connects and subscribes to this repo.
478            if let Ok((ann, _)) = self.refs_announcement_for(rid, [nid]) {
479                debug!(target: "service", "Adding refs announcement for {rid} to historical gossip messages..");
480                self.db.gossip_mut().announced(&nid, &ann)?;
481            }
482        }
483
484        // Ensure that our inventory is recorded in our routing table, and we are seeding
485        // all of it. It can happen that inventory is not properly seeded if for eg. the
486        // user creates a new repository while the node is stopped.
487        self.db
488            .routing_mut()
489            .add_inventory(inventory.iter(), nid, time.into())?;
490        self.inventory = gossip::inventory(self.timestamp(), inventory);
491
492        // Ensure that private repositories are not in our inventory. It's possible that
493        // a repository was public and then it was made private.
494        self.db
495            .routing_mut()
496            .remove_inventories(private.iter(), &nid)?;
497
498        // Set up subscription filter for seeded repos.
499        self.filter = Filter::allowed_by(self.policies.seed_policies()?);
500        // Connect to configured peers.
501        let addrs = self.config.connect.clone();
502        for (id, addr) in addrs.into_iter().map(|ca| ca.into()) {
503            if let Err(e) = self.connect(id, addr) {
504                debug!(target: "service", "Service::initialization connection error: {e}");
505            }
506        }
507        // Try to establish some connections.
508        self.maintain_connections();
509        // Start periodic tasks.
510        self.outbox.wakeup(IDLE_INTERVAL);
511        self.outbox.wakeup(GOSSIP_INTERVAL);
512
513        Ok(())
514    }
515
516    fn upgrade_sigrefs(&mut self, mut info: RepositoryInfo) -> Result<RepositoryInfo, Error> {
517        if !matches!(info.refs, SignedRefsInfo::NeedsMigration) {
518            return Ok(info);
519        }
520
521        let rid = info.rid;
522
523        log::info!(
524            "Migrating `rad/sigrefs` of {rid} to force feature level {}.",
525            FeatureLevel::LATEST
526        );
527
528        let repo = self.storage.repository_mut(rid)?;
529        // NOTE: We assume to reach `FeatureLevel::LATEST` by signing refs.
530        let refs = repo.force_sign_refs(&self.secret_key)?;
531
532        let repo = self.storage.repository(rid)?;
533        let synced_at = SyncedAt::new(refs.at, &repo)?;
534
535        info.synced_at = Some(synced_at);
536        info.refs = SignedRefsInfo::Some(refs);
537
538        Ok(info)
539    }
540}
541
542impl<D, S> Service<D, S>
543where
544    D: Store,
545    S: ReadStorage + 'static,
546{
547    pub fn new(
548        config: Config,
549        db: Stores<D>,
550        storage: S,
551        policies: policy::Config<Write>,
552        secret_key: crypto::SigningKey,
553        rng: Rng,
554        node: NodeAnnouncement,
555        emitter: Emitter<Event>,
556    ) -> Self {
557        let sessions = Sessions::new(rng.clone());
558        let limiter = RateLimiter::new(config.peers());
559        let last_timestamp = node.timestamp;
560        let clock = LocalTime::default(); // Updated on initialize.
561        let inventory = gossip::inventory(clock.into(), []); // Updated on initialize.
562        let fetcher = {
563            let config = fetcher::Config::new()
564                .with_max_concurrency(
565                    std::num::NonZeroUsize::new(config.limits.fetch_concurrency.into())
566                        .expect("fetch concurrency was zero, must be at least 1"),
567                )
568                .with_max_capacity(fetcher::MaxQueueSize::default());
569            FetcherService::new(config)
570        };
571        Self {
572            config,
573            storage,
574            policies,
575            secret_key,
576            rng,
577            inventory,
578            node,
579            clock,
580            db,
581            outbox: Outbox::default(),
582            limiter,
583            sessions,
584            fetcher,
585            filter: Filter::empty(),
586            relayed_by: HashMap::default(),
587            last_idle: LocalTime::default(),
588            last_gossip: LocalTime::default(),
589            last_sync: LocalTime::default(),
590            last_prune: LocalTime::default(),
591            last_timestamp,
592            last_announce: LocalTime::default(),
593            last_inventory: LocalTime::default(),
594            started_at: None,     // Updated on initialize.
595            last_online_at: None, // Updated on initialize.
596            emitter,
597            listening: vec![],
598            metrics: Metrics::default(),
599        }
600    }
601
602    /// Whether the service was started (initialized) and if so, at what time.
603    pub fn started(&self) -> Option<LocalTime> {
604        self.started_at
605    }
606
607    /// Return the next i/o action to execute.
608    #[allow(clippy::should_implement_trait)]
609    pub fn next(&mut self) -> Option<io::Io> {
610        self.outbox.next()
611    }
612
613    /// Seed a repository.
614    /// Returns whether or not the repo policy was updated.
615    pub fn seed(&mut self, id: &RepoId, scope: Scope) -> Result<bool, policy::Error> {
616        let updated = self.policies.seed(id, scope)?;
617        self.filter.insert(id);
618
619        Ok(updated)
620    }
621
622    /// Unseed a repository.
623    /// Returns whether or not the repo policy was updated.
624    /// Note that when unseeding, we don't announce anything to the network. This is because by
625    /// simply not announcing it anymore, it will eventually be pruned by nodes.
626    pub fn unseed(&mut self, id: &RepoId) -> Result<bool, policy::Error> {
627        let updated = self.policies.unseed(id)?;
628
629        if updated {
630            // Nb. This is potentially slow if we have lots of repos. We should probably
631            // only re-compute the filter when we've unseeded a certain amount of repos
632            // and the filter is really out of date.
633            self.filter = Filter::allowed_by(self.policies.seed_policies()?);
634            // Update and announce new inventory.
635            if let Err(e) = self.remove_inventory(id) {
636                warn!(target: "service", "Failed to update inventory after unseed: {e}");
637            }
638        }
639        Ok(updated)
640    }
641
642    /// Find the closest `n` peers by proximity in seeding graphs.
643    /// Returns a sorted list from the closest peer to the furthest.
644    /// Peers with more seedings in common score higher.
645    #[allow(unused)]
646    pub fn closest_peers(&self, n: usize) -> Vec<NodeId> {
647        todo!()
648    }
649
650    /// Get the database.
651    pub fn database(&self) -> &Stores<D> {
652        &self.db
653    }
654
655    /// Get the mutable database.
656    pub fn database_mut(&mut self) -> &mut Stores<D> {
657        &mut self.db
658    }
659
660    /// Get the storage instance.
661    pub fn storage(&self) -> &S {
662        &self.storage
663    }
664
665    /// Get the mutable storage instance.
666    pub fn storage_mut(&mut self) -> &mut S {
667        &mut self.storage
668    }
669
670    /// Get the node policies.
671    pub fn policies(&self) -> &policy::Config<Write> {
672        &self.policies
673    }
674
675    /// Get the local secret key.
676    #[cfg(any(test, feature = "test"))]
677    pub fn secret_key(&self) -> &radicle::crypto::SigningKey {
678        &self.secret_key
679    }
680
681    /// Subscriber to inner `Emitter` events.
682    pub fn events(&mut self) -> Events {
683        Events::from(self.emitter.subscribe())
684    }
685
686    pub fn fetcher(&self) -> &FetcherState {
687        self.fetcher.state()
688    }
689
690    /// Get I/O outbox.
691    pub fn outbox(&mut self) -> &mut Outbox {
692        &mut self.outbox
693    }
694
695    /// Get configuration.
696    pub fn config(&self) -> &Config {
697        &self.config
698    }
699
700    /// Lookup a repository, both locally and in the routing table.
701    pub fn lookup(&self, rid: RepoId) -> Result<Lookup, LookupError> {
702        let this = self.nid();
703        let local = self.storage.get(rid)?;
704        let remote = self
705            .db
706            .routing()
707            .get(&rid)?
708            .iter()
709            .filter(|nid| nid != &this)
710            .cloned()
711            .collect();
712
713        Ok(Lookup { local, remote })
714    }
715
716    pub fn tick(&mut self, now: LocalTime, metrics: &Metrics) {
717        trace!(
718            target: "service",
719            "Tick +{}",
720            now - self.started_at.expect("Service::tick: service must be initialized")
721        );
722        if now >= self.clock {
723            self.clock = now;
724        } else {
725            // Nb. In tests, we often move the clock forwards in time to test different behaviors,
726            // so this warning isn't applicable there.
727            #[cfg(not(test))]
728            warn!(
729                target: "service",
730                "System clock is not monotonic: {now} is not greater or equal to {}", self.clock
731            );
732        }
733        self.metrics = metrics.clone();
734    }
735
736    pub fn wake(&mut self) {
737        let now = self.clock;
738
739        trace!(
740            target: "service",
741            "Wake +{}",
742            now - self.started_at.expect("Service::wake: service must be initialized")
743        );
744
745        if now - self.last_idle >= IDLE_INTERVAL {
746            trace!(target: "service", "Running 'idle' task…");
747
748            self.keep_alive(&now);
749            self.disconnect_unresponsive_peers(&now);
750            self.idle_connections();
751            self.maintain_connections();
752            self.dequeue_fetches();
753            self.outbox.wakeup(IDLE_INTERVAL);
754            self.last_idle = now;
755        }
756        if now - self.last_gossip >= GOSSIP_INTERVAL {
757            trace!(target: "service", "Running 'gossip' task…");
758
759            if let Err(e) = self.relay_announcements() {
760                warn!(target: "service", "Failed to relay stored announcements: {e}");
761            }
762            self.outbox.wakeup(GOSSIP_INTERVAL);
763            self.last_gossip = now;
764        }
765        if now - self.last_sync >= SYNC_INTERVAL {
766            trace!(target: "service", "Running 'sync' task…");
767
768            if let Err(e) = self.fetch_missing_repositories() {
769                warn!(target: "service", "Failed to fetch missing inventory: {e}");
770            }
771            self.outbox.wakeup(SYNC_INTERVAL);
772            self.last_sync = now;
773        }
774        if now - self.last_announce >= ANNOUNCE_INTERVAL {
775            trace!(target: "service", "Running 'announce' task…");
776
777            self.announce_inventory();
778            self.outbox.wakeup(ANNOUNCE_INTERVAL);
779            self.last_announce = now;
780        }
781        if now - self.last_prune >= PRUNE_INTERVAL {
782            trace!(target: "service", "Running 'prune' task…");
783
784            if let Err(err) = self.prune_routing_entries(&now) {
785                warn!(target: "service", "Failed to prune routing entries: {err}");
786            }
787            if let Err(err) = self
788                .db
789                .gossip_mut()
790                .prune((now - LocalDuration::from(self.config.limits.gossip_max_age)).into())
791            {
792                warn!(target: "service", "Failed to prune gossip entries: {err}");
793            }
794
795            self.outbox.wakeup(PRUNE_INTERVAL);
796            self.last_prune = now;
797        }
798
799        // Always check whether there are persistent peers that need reconnecting.
800        self.maintain_persistent();
801    }
802
803    pub fn command(&mut self, cmd: Command) {
804        info!(target: "service", "Received command {cmd:?}");
805
806        match cmd {
807            Command::Connect(nid, addr, opts) => {
808                if opts.persistent {
809                    self.config.connect.insert((nid, addr.clone()).into());
810                }
811                if let Err(e) = self.connect(nid, addr) {
812                    match e {
813                        ConnectError::SessionExists { nid } => {
814                            self.emitter.emit(Event::PeerConnected { nid });
815                        }
816                        e => {
817                            // N.b. using the fact that the call to connect waits for an event
818                            self.emitter.emit(Event::PeerDisconnected {
819                                nid,
820                                reason: e.to_string(),
821                            });
822                        }
823                    }
824                }
825            }
826            Command::Disconnect(nid) => {
827                self.outbox.disconnect(nid, DisconnectReason::Command);
828            }
829            Command::Config(resp) => {
830                resp.ok(self.config.clone()).ok();
831            }
832            Command::ListenAddrs(resp) => {
833                resp.ok(self.listening.clone()).ok();
834            }
835            Command::Seeds(rid, namespaces, resp) => match self.seeds(&rid, namespaces) {
836                Ok(seeds) => {
837                    let (connected, disconnected) = seeds.partition();
838                    debug!(
839                        target: "service",
840                        "Found {} connected seed(s) and {} disconnected seed(s) for {}",
841                        connected.len(), disconnected.len(),  rid
842                    );
843                    resp.ok(seeds).ok();
844                }
845                Err(e) => {
846                    warn!(target: "service", "Failed to get seeds for {rid}: {e}");
847                    resp.err(e).ok();
848                }
849            },
850            Command::Fetch(rid, seed, timeout, signed_references_minimum_feature_level, resp) => {
851                let feature_level = signed_references_minimum_feature_level
852                    .unwrap_or(self.config.fetch.feature_level_min());
853                let config = self
854                    .fetch_config()
855                    .with_timeout(timeout)
856                    .with_minimum_feature_level(feature_level);
857                self.fetch(rid, seed, vec![], config, Some(resp));
858            }
859            Command::Seed(rid, scope, resp) => {
860                // Update our seeding policy.
861                let seeded = self
862                    .seed(&rid, scope)
863                    .expect("Service::command: error seeding repository");
864                resp.ok(seeded).ok();
865
866                // Let all our peers know that we're interested in this repo from now on.
867                self.outbox.broadcast(
868                    Message::subscribe(self.filter(), self.clock.into(), Timestamp::MAX),
869                    self.sessions.connected().map(|(_, s)| s),
870                );
871            }
872            Command::Unseed(id, resp) => {
873                let updated = self
874                    .unseed(&id)
875                    .expect("Service::command: error unseeding repository");
876                resp.ok(updated).ok();
877            }
878            Command::Follow(id, alias, resp) => {
879                let seeded = self
880                    .policies
881                    .follow(&id, alias.as_ref())
882                    .expect("Service::command: error following node");
883                resp.ok(seeded).ok();
884            }
885            Command::Unfollow(id, resp) => {
886                let updated = self
887                    .policies
888                    .unfollow(&id)
889                    .expect("Service::command: error unfollowing node");
890                resp.ok(updated).ok();
891            }
892            Command::Block(id, resp) => {
893                let updated = self
894                    .policies
895                    .set_follow_policy(&id, policy::Policy::Block)
896                    .expect("Service::command: error blocking node");
897                if updated {
898                    self.outbox.disconnect(id, DisconnectReason::Policy);
899                }
900                resp.ok(updated).ok();
901            }
902            Command::AnnounceRefs(id, namespaces, resp) => {
903                let doc = match self.storage.get(id) {
904                    Ok(Some(doc)) => doc,
905                    Ok(None) => {
906                        warn!(target: "service", "Failed to announce refs: repository {id} not found");
907                        resp.err(command::Error::custom(format!("repository {id} not found")))
908                            .ok();
909                        return;
910                    }
911                    Err(e) => {
912                        warn!(target: "service", "Failed to announce refs: doc error: {e}");
913                        resp.err(e).ok();
914                        return;
915                    }
916                };
917
918                match self.announce_own_refs(id, doc, namespaces) {
919                    Ok((refs, _timestamp)) => {
920                        // TODO(finto): currently the command caller only
921                        // expects one `RefsAt`, this should be fixed in the
922                        // trait, eventually.
923                        if let Some(refs) = refs.first() {
924                            resp.ok(*refs).ok();
925                        } else {
926                            resp.err(command::Error::custom(format!(
927                                "no refs were announced for {id}"
928                            )))
929                            .ok();
930                        }
931                    }
932                    Err(err) => {
933                        warn!(target: "service", "Failed to announce refs: {err}");
934                        resp.err(err).ok();
935                    }
936                }
937            }
938            Command::AnnounceInventory => {
939                self.announce_inventory();
940            }
941            Command::AddInventory(rid, resp) => match self.add_inventory(rid) {
942                Ok(updated) => {
943                    resp.ok(updated).ok();
944                }
945                Err(e) => {
946                    warn!(target: "service", "Failed to add {rid} to inventory: {e}");
947                    resp.err(e).ok();
948                }
949            },
950            Command::QueryState(query, sender) => {
951                sender.send(query(self)).ok();
952            }
953        }
954    }
955
956    /// Initiate an outgoing fetch for some repository, based on another node's announcement.
957    /// Returns `true` if the fetch was initiated and `false` if it was skipped.
958    fn fetch_refs_at(
959        &mut self,
960        rid: RepoId,
961        from: NodeId,
962        refs: NonEmpty<RefsAt>,
963        scope: Scope,
964        config: fetcher::FetchConfig,
965    ) -> bool {
966        match self.refs_status_of(rid, refs, &scope) {
967            Ok(status) => {
968                if status.want.is_empty() {
969                    debug!(target: "service", "Skipping fetch for {rid}, all refs are already in storage");
970                } else {
971                    self.fetch(rid, from, status.want, config, None);
972                    return true;
973                }
974            }
975            Err(e) => {
976                warn!(target: "service", "Failed to get the refs status of {rid}: {e}");
977            }
978        }
979        // We didn't try to fetch anything.
980        false
981    }
982
983    fn fetch(
984        &mut self,
985        rid: RepoId,
986        from: NodeId,
987        refs_at: Vec<RefsAt>,
988        config: fetcher::FetchConfig,
989        channel: Option<command::Responder<FetchResult>>,
990    ) {
991        let session = {
992            let reason = format!("peer {from} is not connected; cannot initiate fetch");
993            let Some(session) = self.sessions.get_mut(&from) else {
994                if let Some(c) = channel {
995                    c.ok(FetchResult::Failed { reason }).ok();
996                }
997                return;
998            };
999            if !session.is_connected() {
1000                if let Some(c) = channel {
1001                    c.ok(FetchResult::Failed { reason }).ok();
1002                }
1003                return;
1004            }
1005            session
1006        };
1007
1008        let cmd = fetcher::state::command::Fetch {
1009            from,
1010            rid,
1011            refs: refs_at.into(),
1012            config,
1013        };
1014        let fetcher::service::FetchInitiated { event, rejected } = self.fetcher.fetch(cmd, channel);
1015
1016        if let Some(c) = rejected {
1017            c.ok(FetchResult::Failed {
1018                reason: "fetch queue at capacity".to_string(),
1019            })
1020            .ok();
1021        }
1022
1023        match event {
1024            fetcher::state::event::Fetch::Started {
1025                rid,
1026                from,
1027                refs: refs_at,
1028                config,
1029            } => {
1030                debug!(target: "service", "Starting fetch for {rid} from {from}");
1031                self.outbox.fetch(
1032                    session,
1033                    rid,
1034                    refs_at.into(),
1035                    self.config.limits.fetch_pack_receive,
1036                    config,
1037                );
1038            }
1039            fetcher::state::event::Fetch::Queued { rid, from } => {
1040                debug!(target: "service", "Queued fetch for {rid} from {from}");
1041            }
1042            fetcher::state::event::Fetch::AlreadyFetching { rid, from } => {
1043                debug!(target: "service", "Already fetching {rid} from {from}");
1044            }
1045            fetcher::state::event::Fetch::QueueAtCapacity { rid, from, .. } => {
1046                debug!(target: "service", "Queue at capacity for {from}, rejected {rid}");
1047            }
1048        }
1049    }
1050
1051    pub fn fetched(
1052        &mut self,
1053        rid: RepoId,
1054        from: NodeId,
1055        result: Result<crate::worker::fetch::FetchResult, crate::worker::FetchError>,
1056    ) {
1057        let cmd = fetcher::state::command::Fetched { from, rid };
1058        let fetcher::service::FetchCompleted { event, subscribers } = self.fetcher.fetched(cmd);
1059
1060        // Dequeue next fetches
1061        self.dequeue_fetches();
1062
1063        match event {
1064            fetcher::state::event::Fetched::NotFound { from, rid } => {
1065                debug!(target: "service", "Unexpected fetch result for {rid} from {from}");
1066            }
1067            fetcher::state::event::Fetched::Completed { from, rid, refs: _ } => {
1068                // Notify responders
1069                let fetch_result = match &result {
1070                    Ok(success) => FetchResult::Success {
1071                        updated: success.updated.clone(),
1072                        namespaces: success.namespaces.clone(),
1073                        clone: success.clone,
1074                    },
1075                    Err(e) => FetchResult::Failed {
1076                        reason: e.to_string(),
1077                    },
1078                };
1079                for responder in subscribers {
1080                    responder.ok(fetch_result.clone()).ok();
1081                }
1082                match result {
1083                    Ok(crate::worker::fetch::FetchResult {
1084                        updated,
1085                        canonical,
1086                        namespaces,
1087                        clone,
1088                        doc,
1089                    }) => {
1090                        info!(target: "service", "Fetched {rid} from {from} successfully");
1091                        // Update our routing table in case this fetch was user-initiated and doesn't
1092                        // come from an announcement.
1093                        self.seed_discovered(rid, from, self.clock.into());
1094
1095                        for update in &updated {
1096                            if update.is_skipped() {
1097                                trace!(target: "service", "Ref skipped: {update} for {rid}");
1098                            } else {
1099                                debug!(target: "service", "Ref updated: {update} for {rid}");
1100                            }
1101                        }
1102                        self.emitter.emit(Event::RefsFetched {
1103                            remote: from,
1104                            rid,
1105                            updated: updated.clone(),
1106                        });
1107                        self.emitter
1108                            .emit_all(canonical.into_iter().map(|(refname, target)| {
1109                                Event::CanonicalRefUpdated {
1110                                    rid,
1111                                    refname,
1112                                    target,
1113                                }
1114                            }));
1115
1116                        // Announce our new inventory if this fetch was a full clone.
1117                        // Only update and announce inventory for public repositories.
1118                        if clone && doc.is_public() {
1119                            debug!(target: "service", "Updating and announcing inventory for cloned repository {rid}..");
1120
1121                            if let Err(e) = self.add_inventory(rid) {
1122                                warn!(target: "service", "Failed to announce inventory for {rid}: {e}");
1123                            }
1124                        }
1125
1126                        // It's possible for a fetch to succeed but nothing was updated.
1127                        if updated.is_empty() || updated.iter().all(|u| u.is_skipped()) {
1128                            debug!(target: "service", "Nothing to announce, no refs were updated..");
1129                        } else {
1130                            // Finally, announce the refs. This is useful for nodes to know what we've synced,
1131                            // beyond just knowing that we have added an item to our inventory.
1132                            if let Err(e) = self.announce_refs(rid, doc.into(), namespaces, false) {
1133                                warn!(target: "service", "Failed to announce new refs: {e}");
1134                            }
1135                        }
1136                    }
1137                    Err(err) => {
1138                        warn!(target: "service", "Fetch failed for {rid} from {from}: {err}");
1139
1140                        // For now, we only disconnect the from in case of timeout. In the future,
1141                        // there may be other reasons to disconnect.
1142                        if err.is_timeout() {
1143                            self.outbox.disconnect(from, DisconnectReason::Fetch(err));
1144                        }
1145                    }
1146                }
1147            }
1148        }
1149    }
1150
1151    /// Attempt to dequeue fetches from all peers.
1152    /// At most one fetch is dequeued per peer. If the fetch cannot be processed,
1153    /// it is put back on the queue for that peer.
1154    ///
1155    /// Fetches are queued for two reasons:
1156    /// 1. The RID was already being fetched.
1157    /// 2. The session was already at fetch capacity.
1158    pub fn dequeue_fetches(&mut self) {
1159        let sessions = self
1160            .sessions
1161            .shuffled()
1162            .map(|(k, _)| *k)
1163            .collect::<Vec<_>>();
1164
1165        for nid in sessions {
1166            #[allow(clippy::unwrap_used)]
1167            let sess = self.sessions.get_mut(&nid).unwrap();
1168            if !sess.is_connected() {
1169                continue;
1170            }
1171
1172            let Some(fetcher::QueuedFetch {
1173                rid,
1174                refs: refs_at,
1175                config,
1176            }) = self.fetcher.dequeue(&nid)
1177            else {
1178                continue;
1179            };
1180
1181            // Check seeding policy
1182            let repo_entry = self
1183                .policies
1184                .seed_policy(&rid)
1185                .expect("error accessing repo seeding configuration");
1186
1187            let SeedingPolicy::Allow { scope } = repo_entry.policy else {
1188                debug!(target: "service", "Repository {} no longer seeded, skipping", rid);
1189                continue;
1190            };
1191
1192            debug!(target: "service", "Dequeued fetch for {} from {}", rid, nid);
1193
1194            match refs_at {
1195                RefsToFetch::Refs(refs) => {
1196                    self.fetch_refs_at(rid, nid, refs, scope, config);
1197                }
1198                RefsToFetch::All => {
1199                    // Channel is `None` since they will already be
1200                    // registered with the fetcher service.
1201                    self.fetch(rid, nid, vec![], config, None);
1202                }
1203            }
1204        }
1205    }
1206
1207    /// Inbound connection attempt.
1208    pub fn accepted(&mut self, ip: IpAddr) -> bool {
1209        // Always accept localhost connections, even if we already reached
1210        // our inbound connection limit.
1211        if ip.is_loopback() || ip.is_unspecified() {
1212            return true;
1213        }
1214        // Check for inbound connection limit.
1215        if self.sessions.inbound().count() >= self.config.limits.connection.inbound.into() {
1216            return false;
1217        }
1218        match self.db.addresses().is_ip_banned(ip) {
1219            Ok(banned) => {
1220                if banned {
1221                    debug!(target: "service", "Rejecting inbound connection from banned ip {ip}");
1222                    return false;
1223                }
1224            }
1225            Err(e) => warn!(target: "service", "Failed to query ban status for {ip}: {e}"),
1226        }
1227        let host: HostName = ip.into();
1228        let tokens = self.config.limits.rate.inbound;
1229
1230        if self.limiter.limit(host.clone(), None, &tokens, self.clock) {
1231            trace!(target: "service", "Rate limiting inbound connection from {host}..");
1232            return false;
1233        }
1234        true
1235    }
1236
1237    pub fn attempted(&mut self, nid: NodeId, addr: Address) {
1238        debug!(target: "service", "Attempted connection to {nid} ({addr})");
1239
1240        if let Some(sess) = self.sessions.get_mut(&nid) {
1241            sess.to_attempted();
1242        } else {
1243            #[cfg(debug_assertions)]
1244            panic!("Service::attempted: unknown session {nid}@{addr}");
1245        }
1246    }
1247
1248    pub fn listening(&mut self, local_addr: net::SocketAddr) {
1249        info!(target: "node", "Listening on {local_addr}..");
1250
1251        self.listening.push(local_addr);
1252    }
1253
1254    pub fn connected(&mut self, remote: NodeId, addr: Address, link: Link) {
1255        if let Ok(true) = self.policies.is_blocked(&remote) {
1256            self.emitter.emit(Event::PeerDisconnected {
1257                nid: remote,
1258                reason: format!("{remote} is blocked"),
1259            });
1260            info!(target: "service", "Disconnecting blocked inbound peer {remote}");
1261            self.outbox.disconnect(remote, DisconnectReason::Policy);
1262            return;
1263        }
1264
1265        info!(target: "service", "Connected to {remote} ({addr}) ({link:?})");
1266        self.emitter.emit(Event::PeerConnected { nid: remote });
1267
1268        let msgs = self.initial(link);
1269
1270        if link.is_outbound() {
1271            if let Some(peer) = self.sessions.get_mut(&remote) {
1272                peer.to_connected(self.clock);
1273                self.outbox.write_all(peer, msgs);
1274            }
1275        } else {
1276            match self.sessions.entry(remote) {
1277                Entry::Occupied(mut e) => {
1278                    // In this scenario, it's possible that our peer is persistent, and
1279                    // disconnected. We get an inbound connection before we attempt a re-connection,
1280                    // and therefore we treat it as a regular inbound connection.
1281                    //
1282                    // It's also possible that a disconnection hasn't gone through yet and our
1283                    // peer is still in connected state here, while a new inbound connection from
1284                    // that same peer is made. This results in a new connection from a peer that is
1285                    // already connected from the perspective of the service. This appears to be
1286                    // a bug in the underlying networking library.
1287                    let peer = e.get_mut();
1288                    debug!(
1289                        target: "service",
1290                        "Connecting peer {remote} already has a session open ({peer})"
1291                    );
1292                    peer.link = link;
1293                    peer.to_connected(self.clock);
1294                    self.outbox.write_all(peer, msgs);
1295                }
1296                Entry::Vacant(e) => {
1297                    if let HostName::Ip(ip) = addr.host
1298                        && !address::is_local(&ip)
1299                        && let Err(e) =
1300                            self.db
1301                                .addresses_mut()
1302                                .record_ip(&remote, ip, self.clock.into())
1303                    {
1304                        log::debug!(target: "service", "Failed to record IP address for {remote}: {e}");
1305                    }
1306                    let peer = e.insert(Session::inbound(
1307                        remote,
1308                        addr,
1309                        self.config.is_persistent(&remote),
1310                        self.rng.clone(),
1311                        self.clock,
1312                    ));
1313                    self.outbox.write_all(peer, msgs);
1314                }
1315            }
1316        }
1317    }
1318
1319    pub fn disconnected(&mut self, remote: NodeId, link: Link, reason: &DisconnectReason) {
1320        let since = self.local_time();
1321        let Some(session) = self.sessions.get_mut(&remote) else {
1322            // Since we sometimes disconnect the service eagerly, it's not unusual to get a second
1323            // disconnection event once the transport is dropped.
1324            trace!(target: "service", "Redundant disconnection for {remote} ({reason})");
1325            return;
1326        };
1327        // In cases of connection conflicts, there may be disconnections of one of the two
1328        // connections. In that case we don't want the service to remove the session.
1329        if session.link != link {
1330            return;
1331        }
1332
1333        info!(target: "service", "Disconnected from {remote} ({reason})");
1334        self.emitter.emit(Event::PeerDisconnected {
1335            nid: remote,
1336            reason: reason.to_string(),
1337        });
1338
1339        let link = session.link;
1340        let addr = session.addr.clone();
1341
1342        let cmd = fetcher::state::command::Cancel { from: remote };
1343        let fetcher::service::FetchesCancelled { event, orphaned } = self.fetcher.cancel(cmd);
1344
1345        match event {
1346            fetcher::state::event::Cancel::Unexpected { from } => {
1347                debug!(target: "service", "No fetches to cancel for {from}");
1348            }
1349            fetcher::state::event::Cancel::Canceled {
1350                from,
1351                active,
1352                queued,
1353            } => {
1354                debug!(target: "service", "Cancelled {} ongoing, {} queued for {from}", active.len(), queued.len());
1355            }
1356        }
1357
1358        // Notify orphaned responders
1359        for (rid, responder) in orphaned {
1360            responder
1361                .ok(FetchResult::Failed {
1362                    reason: format!("failed fetch to {rid}, peer disconnected: {reason}"),
1363                })
1364                .ok();
1365        }
1366
1367        // Attempt to re-connect to persistent peers.
1368        if self.config.is_persistent(&remote) {
1369            let delay = LocalDuration::from_secs(2u64.saturating_pow(session.attempts() as u32))
1370                .clamp(MIN_RECONNECTION_DELTA, MAX_RECONNECTION_DELTA);
1371
1372            // Nb. We always try to reconnect to persistent peers, even when the error appears
1373            // to not be transient.
1374            session.to_disconnected(since, since + delay);
1375
1376            debug!(target: "service", "Reconnecting to {remote} in {delay}..");
1377
1378            self.outbox.wakeup(delay);
1379        } else {
1380            debug!(target: "service", "Dropping peer {remote}..");
1381            self.sessions.remove(&remote);
1382
1383            let severity = match reason {
1384                DisconnectReason::Dial(_)
1385                | DisconnectReason::Fetch(_)
1386                | DisconnectReason::Connection(_) => {
1387                    if self.is_online() {
1388                        // If we're "online", there's something wrong with this
1389                        // peer connection specifically.
1390                        Severity::Medium
1391                    } else {
1392                        Severity::Low
1393                    }
1394                }
1395                DisconnectReason::Session(e) => e.severity(),
1396                DisconnectReason::Command
1397                | DisconnectReason::Conflict
1398                | DisconnectReason::Policy
1399                | DisconnectReason::SelfConnection => Severity::Low,
1400            };
1401
1402            if let Err(e) = self
1403                .db
1404                .addresses_mut()
1405                .disconnected(&remote, &addr, severity)
1406            {
1407                debug!(target: "service", "Failed to update address store: {e}");
1408            }
1409            // Only re-attempt outbound connections, since we don't care if an inbound connection
1410            // is dropped.
1411            if link.is_outbound() {
1412                self.maintain_connections();
1413            }
1414        }
1415        self.dequeue_fetches();
1416    }
1417
1418    pub fn received_message(&mut self, remote: NodeId, message: Message) {
1419        if let Err(err) = self.handle_message(&remote, message) {
1420            // If there's an error, stop processing messages from this peer.
1421            // However, we still relay messages returned up to this point.
1422            self.outbox
1423                .disconnect(remote, DisconnectReason::Session(err));
1424
1425            // FIXME: The peer should be set in a state such that we don't
1426            // process further messages.
1427        }
1428    }
1429
1430    /// Handle an announcement message.
1431    ///
1432    /// Returns `true` if this announcement should be stored and relayed to connected peers,
1433    /// and `false` if it should not.
1434    pub fn handle_announcement(
1435        &mut self,
1436        relayer: &NodeId,
1437        relayer_addr: &Address,
1438        announcement: &Announcement,
1439    ) -> Result<Option<gossip::AnnouncementId>, session::Error> {
1440        if !announcement.verify() {
1441            return Err(session::Error::Misbehavior);
1442        }
1443        let Announcement {
1444            node: announcer,
1445            message,
1446            ..
1447        } = announcement;
1448
1449        // Ignore our own announcements, in case the relayer sent one by mistake.
1450        if announcer == self.nid() {
1451            return Ok(None);
1452        }
1453        let now = self.clock;
1454        let timestamp = message.timestamp();
1455
1456        // Don't allow messages from too far in the future.
1457        if timestamp.saturating_sub(now.as_millis()) > MAX_TIME_DELTA.as_millis() as u64 {
1458            return Err(session::Error::future_timestamp(timestamp, now.into()));
1459        }
1460
1461        // We don't process announcements from nodes we don't know, since the node announcement is
1462        // what provides DoS protection.
1463        //
1464        // Note that it's possible to *not* receive the node announcement, but receive the
1465        // subsequent announcements of a node in the case of historical gossip messages requested
1466        // from the `subscribe` message. This can happen if the cut-off time is after the node
1467        // announcement timestamp, but before the other announcements. In that case, we simply
1468        // ignore all announcements of that node until we get a node announcement.
1469        if let AnnouncementMessage::Inventory(_) | AnnouncementMessage::Refs(_) = message {
1470            match self.db.addresses().get(announcer) {
1471                Ok(node) => {
1472                    if node.is_none() {
1473                        debug!(target: "service", "Ignoring announcement from unknown node {announcer} (t={timestamp})");
1474                        return Ok(None);
1475                    }
1476                }
1477                Err(e) => {
1478                    debug!(target: "service", "Failed to look up node in address book: {e}");
1479                    return Ok(None);
1480                }
1481            }
1482        }
1483
1484        // Discard announcement messages we've already seen; otherwise, update our last seen time.
1485        let relay = match self.db.gossip_mut().announced(announcer, announcement) {
1486            Ok(Some(id)) => {
1487                log::debug!(
1488                    target: "service",
1489                    "Stored announcement from {announcer} to be broadcast in {} (t={timestamp})",
1490                    (self.last_gossip + GOSSIP_INTERVAL) - self.clock
1491                );
1492                // Keep track of who relayed the message for later.
1493                self.relayed_by.entry(id).or_default().push(*relayer);
1494
1495                // Decide whether or not to relay this message, if it's fresh.
1496                // To avoid spamming peers on startup with historical gossip messages,
1497                // don't relay messages that are too old. We make an exception for node announcements,
1498                // since they are cached, and will hence often carry old timestamps.
1499                let relay = message.is_node_announcement()
1500                    || now - timestamp.to_local_time() <= MAX_TIME_DELTA;
1501                relay.then_some(id)
1502            }
1503            Ok(None) => {
1504                // FIXME: Still mark as relayed by this peer.
1505                // FIXME: Refs announcements should not be delayed, since they are only sent
1506                // to subscribers.
1507                debug!(target: "service", "Ignoring stale announcement from {announcer} (t={timestamp})");
1508                return Ok(None);
1509            }
1510            Err(e) => {
1511                debug!(target: "service", "Failed to update gossip entry from {announcer}: {e}");
1512                return Ok(None);
1513            }
1514        };
1515
1516        match message {
1517            // Process a peer inventory update announcement by (maybe) fetching.
1518            AnnouncementMessage::Inventory(message) => {
1519                self.emitter.emit(Event::InventoryAnnounced {
1520                    nid: *announcer,
1521                    inventory: message.inventory.to_vec(),
1522                    timestamp: message.timestamp,
1523                });
1524                match self.sync_routing(
1525                    message.inventory.iter().cloned(),
1526                    *announcer,
1527                    message.timestamp,
1528                ) {
1529                    Ok(synced) => {
1530                        if synced.is_empty() {
1531                            trace!(target: "service", "No routes updated by inventory announcement from {announcer}");
1532                            return Ok(None);
1533                        }
1534                    }
1535                    Err(e) => {
1536                        debug!(target: "service", "Failed to process inventory from {announcer}: {e}");
1537                        return Ok(None);
1538                    }
1539                }
1540                let mut missing = Vec::new();
1541                let nid = *self.nid();
1542
1543                // Here we handle the special case where the inventory we received is that of
1544                // a connected peer, as opposed to being relayed to us.
1545                if let Some(sess) = self.sessions.get_mut(announcer) {
1546                    for id in message.inventory.as_slice() {
1547                        // If we are connected to the announcer of this inventory, update the peer's
1548                        // subscription filter to include all inventory items. This way, we'll
1549                        // relay messages relating to the peer's inventory.
1550                        if let Some(sub) = &mut sess.subscribe {
1551                            sub.filter.insert(id);
1552                        }
1553
1554                        // If we're seeding and connected to the announcer, and we don't have
1555                        // the inventory, fetch it from the announcer.
1556                        if self.policies.is_seeding(id).expect(
1557                            "Service::handle_announcement: error accessing seeding configuration",
1558                        ) {
1559                            // Only if we do not have the repository locally do we fetch here.
1560                            // If we do have it, only fetch after receiving a ref announcement.
1561                            match self.db.routing().entry(id, &nid) {
1562                                Ok(entry) => {
1563                                    if entry.is_none() {
1564                                        missing.push(*id);
1565                                    }
1566                                }
1567                                Err(e) => debug!(
1568                                    target: "service",
1569                                    "Error checking local inventory for {id}: {e}"
1570                                ),
1571                            }
1572                        }
1573                    }
1574                }
1575                // Since we have limited fetch capacity, it may be that we can't fetch an entire
1576                // inventory from a peer. Therefore we randomize the order of the RIDs to fetch
1577                // different RIDs from different peers in case multiple peers announce the same
1578                // RIDs.
1579                self.rng.shuffle(&mut missing);
1580
1581                for rid in missing {
1582                    debug!(target: "service", "Missing seeded inventory {rid}; initiating fetch..");
1583                    self.fetch(rid, *announcer, vec![], self.fetch_config(), None);
1584                }
1585                return Ok(relay);
1586            }
1587            AnnouncementMessage::Refs(message) => {
1588                self.emitter.emit(Event::RefsAnnounced {
1589                    nid: *announcer,
1590                    rid: message.rid,
1591                    refs: message.refs.to_vec(),
1592                    timestamp: message.timestamp,
1593                });
1594                // Empty announcements can be safely ignored.
1595                let Some(refs) = NonEmpty::from_vec(message.refs.to_vec()) else {
1596                    debug!(target: "service", "Skipping fetch, no refs in announcement for {} (t={timestamp})", message.rid);
1597                    return Ok(None);
1598                };
1599                // We update inventories when receiving ref announcements, as these could come
1600                // from a new repository being initialized.
1601                self.seed_discovered(message.rid, *announcer, message.timestamp);
1602
1603                // Update sync status of announcer for this repo.
1604                if let Some(refs) = refs.iter().find(|r| &r.remote == self.nid()) {
1605                    debug!(
1606                        target: "service",
1607                        "Refs announcement of {announcer} for {} contains our own remote at {} (t={})",
1608                        message.rid, refs.at, message.timestamp
1609                    );
1610                    match self.db.seeds_mut().synced(
1611                        &message.rid,
1612                        announcer,
1613                        refs.at,
1614                        message.timestamp,
1615                    ) {
1616                        Ok(updated) => {
1617                            if updated {
1618                                debug!(
1619                                    target: "service",
1620                                    "Updating sync status of {announcer} for {} to {}",
1621                                    message.rid, refs.at
1622                                );
1623                                self.emitter.emit(Event::RefsSynced {
1624                                    rid: message.rid,
1625                                    remote: *announcer,
1626                                    at: refs.at,
1627                                });
1628                            } else {
1629                                debug!(
1630                                    target: "service",
1631                                    "Sync status of {announcer} was not updated for {}",
1632                                    message.rid,
1633                                );
1634                            }
1635                        }
1636                        Err(e) => {
1637                            debug!(target: "service", "Failed to update sync status for {}: {e}", message.rid);
1638                        }
1639                    }
1640                }
1641                let repo_entry = self.policies.seed_policy(&message.rid).expect(
1642                    "Service::handle_announcement: error accessing repo seeding configuration",
1643                );
1644                let SeedingPolicy::Allow { scope } = repo_entry.policy else {
1645                    debug!(
1646                        target: "service",
1647                        "Ignoring refs announcement from {announcer}: repository {} isn't seeded (t={timestamp})",
1648                        message.rid
1649                    );
1650                    return Ok(None);
1651                };
1652                // Ref announcements may be relayed by peers who don't have the
1653                // actual refs in storage, therefore we only check whether we
1654                // are connected to the *announcer*, which is required by the
1655                // protocol to only announce refs it has.
1656                //
1657                // TODO(Ade): Perhaps it makes sense to establish connections to
1658                // followed but unconnected peers. Consider:
1659                //   Connections: Alice ←→ Bob ←→ Eve
1660                //   Follows:     Alice ←→ Eve
1661                // Eve announces refs, and Bob relays these announcements to Alice.
1662                // Then, Alice might determine that Bob does not have Eve's refs,
1663                // and therefore connect directly to Eve in order to fetch.
1664                let Some(remote) = self.sessions.get(announcer).cloned() else {
1665                    trace!(
1666                        target: "service",
1667                        "Skipping fetch of {}, no sessions connected to {announcer}",
1668                        message.rid
1669                    );
1670                    return Ok(relay);
1671                };
1672                // Finally, start the fetch.
1673                self.fetch_refs_at(message.rid, remote.id, refs, scope, self.fetch_config());
1674
1675                return Ok(relay);
1676            }
1677            AnnouncementMessage::Node(
1678                ann @ NodeAnnouncement {
1679                    features,
1680                    addresses,
1681                    ..
1682                },
1683            ) => {
1684                self.emitter.emit(Event::NodeAnnounced {
1685                    nid: *announcer,
1686                    alias: ann.alias.clone(),
1687                    timestamp: ann.timestamp,
1688                    features: *features,
1689                    addresses: addresses.to_vec(),
1690                });
1691                // If this node isn't a seed, we're not interested in adding it
1692                // to our address book, but other nodes may be, so we relay the message anyway.
1693                if !features.has(Features::SEED) {
1694                    return Ok(relay);
1695                }
1696
1697                match self.db.addresses_mut().insert(
1698                    announcer,
1699                    ann.version,
1700                    ann.features,
1701                    &ann.alias,
1702                    ann.work(),
1703                    &ann.agent,
1704                    timestamp,
1705                    addresses
1706                        .iter()
1707                        // Ignore non-routable addresses unless received from a local network
1708                        // peer. This allows the node to function in a local network.
1709                        .filter(|a| a.is_routable() || relayer_addr.is_local())
1710                        .map(|a| KnownAddress::new(a.clone(), address::Source::Peer)),
1711                ) {
1712                    Ok(updated) => {
1713                        // Only relay if we received new information.
1714                        if updated {
1715                            debug!(
1716                                target: "service",
1717                                "Address store entry for node {announcer} updated at {timestamp}"
1718                            );
1719                            return Ok(relay);
1720                        }
1721                    }
1722                    Err(err) => {
1723                        // An error here is due to a fault in our address store.
1724                        warn!(target: "service", "Failed to process node announcement from {announcer}: {err}");
1725                    }
1726                }
1727            }
1728        }
1729        Ok(None)
1730    }
1731
1732    pub fn handle_info(&mut self, remote: NodeId, info: &Info) -> Result<(), session::Error> {
1733        match info {
1734            // Nb. We don't currently send this message.
1735            Info::RefsAlreadySynced { rid, at } => {
1736                debug!(target: "service", "Refs already synced for {rid} by {remote}");
1737                self.emitter.emit(Event::RefsSynced {
1738                    rid: *rid,
1739                    remote,
1740                    at: *at,
1741                });
1742            }
1743        }
1744
1745        Ok(())
1746    }
1747
1748    pub fn handle_message(
1749        &mut self,
1750        remote: &NodeId,
1751        message: Message,
1752    ) -> Result<(), session::Error> {
1753        let local = *self.nid();
1754        let relay = self.config.is_relay();
1755        let Some(peer) = self.sessions.get_mut(remote) else {
1756            debug!(target: "service", "Session not found for {remote}");
1757            return Ok(());
1758        };
1759        peer.last_active = self.clock;
1760
1761        let limit: RateLimit = match peer.link {
1762            Link::Outbound => self.config.limits.rate.outbound.into(),
1763            Link::Inbound => self.config.limits.rate.inbound.into(),
1764        };
1765        if self
1766            .limiter
1767            .limit(peer.addr.clone().into(), Some(remote), &limit, self.clock)
1768        {
1769            debug!(target: "service", "Rate limiting message from {remote} ({})", peer.addr);
1770            return Ok(());
1771        }
1772        message.log(log::Level::Debug, remote, Link::Inbound);
1773
1774        let connected = match &mut peer.state {
1775            session::State::Disconnected { .. } => {
1776                debug!(target: "service", "Ignoring message from disconnected peer {}", peer.id);
1777                return Ok(());
1778            }
1779            // In case of a discrepancy between the service state and the state of the underlying
1780            // wire protocol, we may receive a message from a peer that we consider not fully connected
1781            // at the service level. To remedy this, we simply transition the peer to a connected state.
1782            //
1783            // This is not ideal, but until the wire protocol and service are unified, it's the simplest
1784            // solution to converge towards the same state.
1785            session::State::Attempted | session::State::Initial => {
1786                debug!(target: "service", "Received unexpected message from connecting peer {}", peer.id);
1787                debug!(target: "service", "Transitioning peer {} to 'connected' state", peer.id);
1788
1789                peer.to_connected(self.clock);
1790
1791                None
1792            }
1793            session::State::Connected {
1794                ping, latencies, ..
1795            } => Some((ping, latencies)),
1796        };
1797
1798        trace!(target: "service", "Received message {message:?} from {remote}");
1799
1800        match message {
1801            // Process a peer announcement.
1802            Message::Announcement(ann) => {
1803                let relayer = remote;
1804                let relayer_addr = peer.addr.clone();
1805
1806                if let Some(id) = self.handle_announcement(relayer, &relayer_addr, &ann)?
1807                    && self.config.is_relay()
1808                {
1809                    if let AnnouncementMessage::Inventory(_) = ann.message {
1810                        if let Err(e) = self
1811                            .database_mut()
1812                            .gossip_mut()
1813                            .set_relay(id, gossip::RelayStatus::Relay)
1814                        {
1815                            warn!(target: "service", "Failed to set relay flag for message: {e}");
1816                            return Ok(());
1817                        }
1818                    } else {
1819                        self.relay(id, ann);
1820                    }
1821                }
1822            }
1823            Message::Subscribe(subscribe) => {
1824                // Filter announcements by interest.
1825                match self
1826                    .db
1827                    .gossip()
1828                    .filtered(&subscribe.filter, subscribe.since, subscribe.until)
1829                {
1830                    Ok(anns) => {
1831                        for ann in anns {
1832                            let ann = match ann {
1833                                Ok(a) => a,
1834                                Err(e) => {
1835                                    debug!(target: "service", "Failed to read gossip message from store: {e}");
1836                                    continue;
1837                                }
1838                            };
1839                            // Don't send announcements authored by the remote, back to the remote.
1840                            if ann.node == *remote {
1841                                continue;
1842                            }
1843                            // Only send messages if we're a relay, or it's our own messages.
1844                            if relay || ann.node == local {
1845                                self.outbox.write(peer, ann.into());
1846                            }
1847                        }
1848                    }
1849                    Err(e) => {
1850                        warn!(target: "service", "Failed to query gossip messages from store: {e}");
1851                    }
1852                }
1853                peer.subscribe = Some(subscribe);
1854            }
1855            Message::Info(info) => {
1856                self.handle_info(*remote, &info)?;
1857            }
1858            Message::Ping(Ping { ponglen, .. }) => {
1859                // Ignore pings which ask for too much data.
1860                if ponglen > Ping::MAX_PONG_ZEROES {
1861                    return Ok(());
1862                }
1863                self.outbox.write(
1864                    peer,
1865                    Message::Pong {
1866                        zeroes: ZeroBytes::new(ponglen),
1867                    },
1868                );
1869            }
1870            Message::Pong { zeroes } => {
1871                if let Some((ping, latencies)) = connected
1872                    && let session::PingState::AwaitingResponse {
1873                        len: ponglen,
1874                        since,
1875                    } = *ping
1876                    && (ponglen as usize) == zeroes.len()
1877                {
1878                    *ping = session::PingState::Ok;
1879                    // Keep track of peer latency.
1880                    latencies.push_back(self.clock - since);
1881                    if latencies.len() > MAX_LATENCIES {
1882                        latencies.pop_front();
1883                    }
1884                }
1885            }
1886        }
1887        Ok(())
1888    }
1889
1890    /// A convenient method to check if we should fetch from a `RefsAnnouncement` with `scope`.
1891    fn refs_status_of(
1892        &self,
1893        rid: RepoId,
1894        refs: NonEmpty<RefsAt>,
1895        scope: &policy::Scope,
1896    ) -> Result<RefsStatus, Error> {
1897        let mut refs = RefsStatus::new(rid, refs, self.db.refs())?;
1898        // Check that there's something we want.
1899        if refs.want.is_empty() {
1900            return Ok(refs);
1901        }
1902        // Check scope.
1903        let mut refs = match scope {
1904            policy::Scope::All => refs,
1905            policy::Scope::Followed => match self.policies.namespaces_for(&self.storage, &rid) {
1906                Ok(Namespaces::All) => refs,
1907                Ok(Namespaces::Followed(followed)) => {
1908                    refs.want.retain(|r| followed.contains(&r.remote));
1909                    refs
1910                }
1911                Err(e) => return Err(e.into()),
1912            },
1913        };
1914        // Remove our own remote, we don't want to fetch that.
1915        refs.want.retain(|r| r.remote != *self.nid());
1916
1917        Ok(refs)
1918    }
1919
1920    /// Add a seed to our routing table.
1921    fn seed_discovered(&mut self, rid: RepoId, nid: NodeId, time: Timestamp) {
1922        if let Ok(result) = self.db.routing_mut().add_inventory([&rid], nid, time)
1923            && let &[(_, InsertResult::SeedAdded)] = result.as_slice()
1924        {
1925            self.emitter.emit(Event::SeedDiscovered { rid, nid });
1926            debug!(target: "service", "Routing table updated for {rid} with seed {nid}");
1927        }
1928    }
1929
1930    /// Set of initial messages to send to a peer.
1931    fn initial(&mut self, _link: Link) -> Vec<Message> {
1932        let now = self.clock();
1933        let filter = self.filter();
1934
1935        // TODO: Only subscribe to outbound connections; otherwise, we will consume too
1936        // much bandwidth.
1937
1938        // If we've been previously connected to the network, we'll have received gossip messages.
1939        // Instead of simply taking the last timestamp we try to ensure we don't miss any
1940        // messages due un-synchronized clocks.
1941        //
1942        // If this is our first connection to the network, we just ask for a fixed backlog
1943        // of messages to get us started.
1944        let since = if let Some(last) = self.last_online_at {
1945            Timestamp::from(last - SUBSCRIBE_BACKLOG_DELTA)
1946        } else {
1947            (*now - INITIAL_SUBSCRIBE_BACKLOG_DELTA).into()
1948        };
1949        debug!(target: "service", "Subscribing to messages since timestamp {since}..");
1950
1951        vec![
1952            Message::node(self.node.clone(), &self.secret_key),
1953            Message::inventory(self.inventory.clone(), &self.secret_key),
1954            Message::subscribe(filter, since, Timestamp::MAX),
1955        ]
1956    }
1957
1958    /// Try to guess whether we're online or not.
1959    fn is_online(&self) -> bool {
1960        self.sessions
1961            .connected()
1962            .filter(|(_, s)| s.addr.is_routable() && s.last_active >= self.clock - IDLE_INTERVAL)
1963            .count()
1964            > 0
1965    }
1966
1967    /// Remove a local repository from our inventory.
1968    fn remove_inventory(&mut self, rid: &RepoId) -> Result<bool, Error> {
1969        let node = *self.nid();
1970        let now = self.timestamp();
1971
1972        let removed = self.db.routing_mut().remove_inventory(rid, &node)?;
1973        if removed {
1974            self.refresh_and_announce_inventory(now)?;
1975        }
1976        Ok(removed)
1977    }
1978
1979    /// Add a local repository to our inventory.
1980    fn add_inventory(&mut self, rid: RepoId) -> Result<bool, Error> {
1981        let node = *self.nid();
1982        let now = self.timestamp();
1983
1984        if !self.storage.contains(&rid)? {
1985            debug!(target: "service", "Attempt to add non-existing inventory {rid}: repository not found in storage");
1986            return Ok(false);
1987        }
1988        // Add to our local inventory.
1989        let updates = self.db.routing_mut().add_inventory([&rid], node, now)?;
1990        let updated = !updates.is_empty();
1991
1992        if updated {
1993            self.refresh_and_announce_inventory(now)?;
1994        }
1995        Ok(updated)
1996    }
1997
1998    /// Update cached inventory message, and announce new inventory to peers.
1999    fn refresh_and_announce_inventory(&mut self, time: Timestamp) -> Result<(), Error> {
2000        let inventory = self.inventory()?;
2001
2002        self.inventory = gossip::inventory(time, inventory);
2003        self.announce_inventory();
2004
2005        Ok(())
2006    }
2007
2008    /// Get our local inventory.
2009    ///
2010    /// A node's inventory is the advertised list of repositories offered by a node.
2011    ///
2012    /// A node's inventory consists of *public* repositories that are seeded and available locally
2013    /// in the node's storage. We use the routing table as the canonical state of all inventories,
2014    /// including the local node's.
2015    ///
2016    /// When a repository is unseeded, it is also removed from the inventory. Private repositories
2017    /// are *not* part of a node's inventory.
2018    fn inventory(&self) -> Result<HashSet<RepoId>, Error> {
2019        self.db
2020            .routing()
2021            .get_inventory(self.nid())
2022            .map_err(Error::from)
2023    }
2024
2025    /// Process a peer inventory announcement by updating our routing table.
2026    /// This function expects the peer's full inventory, and prunes entries that are not in the
2027    /// given inventory.
2028    fn sync_routing(
2029        &mut self,
2030        inventory: impl IntoIterator<Item = RepoId>,
2031        from: NodeId,
2032        timestamp: Timestamp,
2033    ) -> Result<SyncedRouting, Error> {
2034        let mut synced = SyncedRouting::default();
2035        let included = inventory.into_iter().collect::<BTreeSet<_>>();
2036        let mut events = Vec::new();
2037
2038        for (rid, result) in
2039            self.db
2040                .routing_mut()
2041                .add_inventory(included.iter(), from, timestamp)?
2042        {
2043            match result {
2044                InsertResult::SeedAdded => {
2045                    debug!(target: "service", "Routing table updated for {rid} with seed {from}");
2046                    events.push(Event::SeedDiscovered { rid, nid: from });
2047
2048                    if self
2049                        .policies
2050                        .is_seeding(&rid)
2051                        .expect("Service::process_inventory: error accessing seeding configuration")
2052                    {
2053                        // TODO: We should fetch here if we're already connected, case this seed has
2054                        // refs we don't have.
2055                    }
2056                    synced.added.push(rid);
2057                }
2058                InsertResult::TimeUpdated => {
2059                    synced.updated.push(rid);
2060                }
2061                InsertResult::NotUpdated => {}
2062            }
2063        }
2064
2065        synced.removed.extend(
2066            self.db
2067                .routing()
2068                .get_inventory(&from)?
2069                .into_iter()
2070                .filter(|rid| !included.contains(rid)),
2071        );
2072        self.db
2073            .routing_mut()
2074            .remove_inventories(&synced.removed, &from)?;
2075        events.extend(
2076            synced
2077                .removed
2078                .iter()
2079                .map(|&rid| Event::SeedDropped { rid, nid: from }),
2080        );
2081
2082        self.emitter.emit_all(events);
2083
2084        Ok(synced)
2085    }
2086
2087    /// Return a refs announcement including the given remotes.
2088    fn refs_announcement_for(
2089        &mut self,
2090        rid: RepoId,
2091        remotes: impl IntoIterator<Item = NodeId>,
2092    ) -> Result<(Announcement, Vec<RefsAt>), Error> {
2093        let repo = self.storage.repository(rid)?;
2094        let timestamp = self.timestamp();
2095        let mut refs = BoundedVec::<_, REF_REMOTE_LIMIT>::new();
2096
2097        for remote_id in remotes.into_iter() {
2098            let refs_at = RefsAt::new(&repo, remote_id).map_err(|err| {
2099                radicle::storage::Error::Refs(radicle::storage::refs::Error::Read(err))
2100            })?;
2101
2102            if refs.push(refs_at).is_err() {
2103                warn!(
2104                    target: "service",
2105                    "refs announcement limit ({REF_REMOTE_LIMIT}) exceeded, peers will see only some of your repository references",
2106                );
2107                break;
2108            }
2109        }
2110
2111        let msg = AnnouncementMessage::from(RefsAnnouncement {
2112            rid,
2113            refs: refs.clone(),
2114            timestamp,
2115        });
2116        Ok((msg.signed(&self.secret_key), refs.into()))
2117    }
2118
2119    /// Announce our own refs for the given repo.
2120    fn announce_own_refs(
2121        &mut self,
2122        rid: RepoId,
2123        doc: Doc,
2124        namespaces: impl IntoIterator<Item = NodeId>,
2125    ) -> Result<(Vec<RefsAt>, Timestamp), Error> {
2126        let (refs, timestamp) = self.announce_refs(rid, doc, namespaces, true)?;
2127
2128        // Update refs database with our signed refs branches.
2129        // This isn't strictly necessary for now, as we only use the database for fetches, and
2130        // we don't fetch our own refs that are announced, but it's for good measure.
2131        for r in refs.iter() {
2132            self.emitter.emit(Event::LocalRefsAnnounced {
2133                rid,
2134                refs: *r,
2135                timestamp,
2136            });
2137            if let Err(e) = self.database_mut().refs_mut().set(
2138                &rid,
2139                &r.remote,
2140                &SIGREFS_BRANCH,
2141                r.at,
2142                timestamp.to_local_time(),
2143            ) {
2144                warn!(
2145                    target: "service",
2146                    "Error updating refs database for `rad/sigrefs` of {} in {rid}: {e}",
2147                    r.remote
2148                );
2149            }
2150        }
2151        Ok((refs, timestamp))
2152    }
2153
2154    /// Announce local refs for given repo.
2155    fn announce_refs(
2156        &mut self,
2157        rid: RepoId,
2158        doc: Doc,
2159        remotes: impl IntoIterator<Item = NodeId>,
2160        own: bool,
2161    ) -> Result<(Vec<RefsAt>, Timestamp), Error> {
2162        let (ann, refs) = self.refs_announcement_for(rid, remotes)?;
2163        let timestamp = ann.timestamp();
2164        let peers = self.sessions.connected().map(|(_, p)| p);
2165
2166        // Update our sync status for our own refs. This is useful for determining if refs were
2167        // updated while the node was stopped.
2168        for r in refs.iter().filter(|r| own || r.remote == ann.node) {
2169            info!(
2170                target: "service",
2171                "Announcing refs {rid}/{r} to peers (t={timestamp})..",
2172            );
2173            // Update our local node's sync status to mark the refs as announced.
2174            if let Err(e) = self.db.seeds_mut().synced(&rid, &ann.node, r.at, timestamp) {
2175                warn!(target: "service", "Failed to update sync status for local node: {e}");
2176            } else {
2177                debug!(target: "service", "Saved local sync status for {rid}..");
2178            }
2179        }
2180
2181        self.outbox.announce(
2182            ann,
2183            peers.filter(|p| {
2184                // Only announce to peers who are allowed to view this repo.
2185                doc.is_visible_to(&p.id.into())
2186            }),
2187            self.db.gossip_mut(),
2188        );
2189        Ok((refs, timestamp))
2190    }
2191
2192    fn reconnect(&mut self, nid: NodeId, addr: Address) -> bool {
2193        if let Some(sess) = self.sessions.get_mut(&nid) {
2194            sess.to_initial();
2195            self.outbox.connect(nid, addr);
2196
2197            return true;
2198        }
2199        false
2200    }
2201
2202    fn connect(&mut self, nid: NodeId, addr: Address) -> Result<(), ConnectError> {
2203        debug!(target: "service", "Connecting to {nid} ({addr})..");
2204
2205        if nid == *self.nid() {
2206            return Err(ConnectError::SelfConnection);
2207        }
2208        if let Ok(true) = self.policies.is_blocked(&nid) {
2209            return Err(ConnectError::Blocked { nid });
2210        }
2211        if !self.is_supported_address(&addr) {
2212            return Err(ConnectError::UnsupportedAddress { nid, addr });
2213        }
2214        if self.sessions.contains_key(&nid) {
2215            return Err(ConnectError::SessionExists { nid });
2216        }
2217        if self.sessions.outbound().count() >= self.config.limits.connection.outbound.into() {
2218            return Err(ConnectError::LimitReached { nid, addr });
2219        }
2220        let persistent = self.config.is_persistent(&nid);
2221        let timestamp: Timestamp = self.clock.into();
2222
2223        if let Err(e) = self.db.addresses_mut().attempted(&nid, &addr, timestamp) {
2224            warn!(target: "service", "Failed to update address book with connection attempt: {e}");
2225        }
2226        self.sessions.insert(
2227            nid,
2228            Session::outbound(nid, addr.clone(), persistent, self.rng.clone()),
2229        );
2230        self.outbox.connect(nid, addr);
2231
2232        Ok(())
2233    }
2234
2235    fn seeds(&self, rid: &RepoId, namespaces: HashSet<PublicKey>) -> Result<Seeds, Error> {
2236        let mut seeds = Seeds::new(self.rng.clone());
2237
2238        // First, build a list of peers that have synced refs for `namespaces`, if any.
2239        // This step is skipped:
2240        //  1. For the repository (and thus all `namespaces`), if it not exist in storage.
2241        //  2. For each `namespace` in `namespaces`, which does not exist in storage.
2242        if let Ok(repo) = self.storage.repository(*rid) {
2243            for namespace in namespaces.iter() {
2244                let Ok(local) = RefsAt::new(&repo, *namespace) else {
2245                    continue;
2246                };
2247
2248                for seed in self.db.seeds().seeds_for(rid)? {
2249                    let seed = seed?;
2250                    let state = self.sessions.get(&seed.nid).map(|s| s.state.clone());
2251                    let synced = if local.at == seed.synced_at.oid {
2252                        SyncStatus::Synced { at: seed.synced_at }
2253                    } else {
2254                        let local = SyncedAt::new(local.at, &repo)?;
2255
2256                        SyncStatus::OutOfSync {
2257                            local,
2258                            remote: seed.synced_at,
2259                        }
2260                    };
2261                    seeds.insert(Seed::new(seed.nid, seed.addresses, state, Some(synced)));
2262                }
2263            }
2264        }
2265
2266        // Then, add peers we know about but have no information about the sync status.
2267        // These peers have announced that they seed the repository via an inventory
2268        // announcement, but we haven't received any ref announcements from them.
2269        for nid in self.db.routing().get(rid)? {
2270            if namespaces.contains(&nid) {
2271                continue;
2272            }
2273            if seeds.contains(&nid) {
2274                // We already have a richer entry for this node.
2275                continue;
2276            }
2277            let addrs = self.db.addresses().addresses_of(&nid)?;
2278            let state = self.sessions.get(&nid).map(|s| s.state.clone());
2279
2280            seeds.insert(Seed::new(nid, addrs, state, None));
2281        }
2282        Ok(seeds)
2283    }
2284
2285    /// Return a new filter object, based on our seeding policy.
2286    fn filter(&self) -> Filter {
2287        if self.config.seeding_policy.is_allow() {
2288            // TODO: Remove bits for blocked repos.
2289            Filter::default()
2290        } else {
2291            self.filter.clone()
2292        }
2293    }
2294
2295    /// Get a timestamp for using in announcements.
2296    /// Never returns the same timestamp twice.
2297    fn timestamp(&mut self) -> Timestamp {
2298        let now = Timestamp::from(self.clock);
2299        if *now > *self.last_timestamp {
2300            self.last_timestamp = now;
2301        } else {
2302            self.last_timestamp = self.last_timestamp + 1;
2303        }
2304        self.last_timestamp
2305    }
2306
2307    fn relay(&mut self, id: gossip::AnnouncementId, ann: Announcement) {
2308        let announcer = ann.node;
2309        let relayed_by = self.relayed_by.get(&id);
2310        let rid = if let AnnouncementMessage::Refs(RefsAnnouncement { rid, .. }) = ann.message {
2311            Some(rid)
2312        } else {
2313            None
2314        };
2315        // Choose peers we should relay this message to.
2316        // 1. Don't relay to a peer who sent us this message.
2317        // 2. Don't relay to the peer who signed this announcement.
2318        let relay_to = self
2319            .sessions
2320            .connected()
2321            .filter(|(id, _)| {
2322                relayed_by
2323                    .map(|relayers| !relayers.contains(id))
2324                    .unwrap_or(true) // If there are no relayers we let it through.
2325            })
2326            .filter(|(id, _)| **id != announcer)
2327            .filter(|(id, _)| {
2328                if let Some(rid) = rid {
2329                    // Only relay this message if the peer is allowed to know about the
2330                    // repository. If we don't have the repository, return `false` because
2331                    // we can't determine if it's private or public.
2332                    self.storage
2333                        .get(rid)
2334                        .ok()
2335                        .flatten()
2336                        .map(|doc| doc.is_visible_to(&(*id).into()))
2337                        .unwrap_or(false)
2338                } else {
2339                    // Announcement doesn't concern a specific repository, let it through.
2340                    true
2341                }
2342            })
2343            .map(|(_, p)| p);
2344
2345        self.outbox.relay(ann, relay_to);
2346    }
2347
2348    ////////////////////////////////////////////////////////////////////////////
2349    // Periodic tasks
2350    ////////////////////////////////////////////////////////////////////////////
2351
2352    fn relay_announcements(&mut self) -> Result<(), Error> {
2353        let now = self.clock.into();
2354        let rows = self.database_mut().gossip_mut().relays(now)?;
2355        let local = *self.nid();
2356
2357        for (id, msg) in rows {
2358            let announcer = msg.node;
2359            if announcer == local {
2360                // Don't relay our own stored gossip messages.
2361                continue;
2362            }
2363            self.relay(id, msg);
2364        }
2365        Ok(())
2366    }
2367
2368    /// Announce our inventory to all connected peers, unless it was already announced.
2369    fn announce_inventory(&mut self) {
2370        let timestamp = self.inventory.timestamp.to_local_time();
2371
2372        if self.last_inventory == timestamp {
2373            debug!(target: "service", "Skipping redundant inventory announcement (t={})", self.inventory.timestamp);
2374            return;
2375        }
2376        let msg = AnnouncementMessage::from(self.inventory.clone());
2377
2378        self.outbox.announce(
2379            msg.signed(&self.secret_key),
2380            self.sessions.connected().map(|(_, p)| p),
2381            self.db.gossip_mut(),
2382        );
2383        self.last_inventory = timestamp;
2384    }
2385
2386    fn prune_routing_entries(&mut self, now: &LocalTime) -> Result<(), routing::Error> {
2387        let count = self.db.routing().len()?;
2388        if count <= self.config.limits.routing_max_size.into() {
2389            return Ok(());
2390        }
2391
2392        let delta = count - usize::from(self.config.limits.routing_max_size);
2393        let nid = *self.nid();
2394        self.db.routing_mut().prune(
2395            (*now - LocalDuration::from(self.config.limits.routing_max_age)).into(),
2396            Some(delta),
2397            &nid,
2398        )?;
2399        Ok(())
2400    }
2401
2402    fn disconnect_unresponsive_peers(&mut self, now: &LocalTime) {
2403        let stale = self
2404            .sessions
2405            .connected()
2406            .filter(|(_, session)| *now - session.last_active >= STALE_CONNECTION_TIMEOUT);
2407
2408        for (_, session) in stale {
2409            debug!(target: "service", "Disconnecting unresponsive peer {}..", session.id);
2410
2411            // TODO: Should we switch the session state to "disconnected" even before receiving
2412            // an official "disconnect"? Otherwise we keep pinging until we get the disconnection.
2413
2414            self.outbox.disconnect(
2415                session.id,
2416                DisconnectReason::Session(session::Error::Timeout),
2417            );
2418        }
2419    }
2420
2421    /// Ensure connection health by pinging connected peers.
2422    fn keep_alive(&mut self, now: &LocalTime) {
2423        let inactive_sessions = self
2424            .sessions
2425            .connected_mut()
2426            .filter(|(_, session)| *now - session.last_active >= KEEP_ALIVE_DELTA)
2427            .map(|(_, session)| session);
2428        for session in inactive_sessions {
2429            session.ping(self.clock, &mut self.outbox).ok();
2430        }
2431    }
2432
2433    /// Get a list of peers available to connect to, sorted by lowest penalty.
2434    fn available_peers(&mut self) -> Vec<Peer> {
2435        match self.db.addresses().entries() {
2436            Ok(entries) => {
2437                // Nb. we don't want to connect to any peers that already have a session with us,
2438                // even if it's in a disconnected state. Those sessions are re-attempted automatically.
2439                let mut peers = entries
2440                    .filter(|entry| entry.version == PROTOCOL_VERSION)
2441                    .filter(|entry| !entry.address.banned)
2442                    .filter(|entry| !entry.penalty.is_connect_threshold_reached())
2443                    .filter(|entry| !self.sessions.contains_key(&entry.node))
2444                    .filter(|entry| !self.policies.is_blocked(&entry.node).unwrap_or(false))
2445                    .filter(|entry| !self.config.external_addresses.contains(&entry.address.addr))
2446                    .filter(|entry| &entry.node != self.nid())
2447                    .filter(|entry| self.is_supported_address(&entry.address.addr))
2448                    .fold(HashMap::new(), |mut acc, entry| {
2449                        acc.entry(entry.node)
2450                            .and_modify(|e: &mut Peer| e.addresses.push(entry.address.clone()))
2451                            .or_insert_with(|| Peer {
2452                                nid: entry.node,
2453                                addresses: vec![entry.address],
2454                                penalty: entry.penalty,
2455                            });
2456                        acc
2457                    })
2458                    .into_values()
2459                    .collect::<Vec<_>>();
2460                peers.sort_by_key(|p| p.penalty);
2461                peers
2462            }
2463            Err(e) => {
2464                warn!(target: "service", "Unable to lookup available peers in address book: {e}");
2465                Vec::new()
2466            }
2467        }
2468    }
2469
2470    /// Fetch all repositories that are seeded but missing from storage.
2471    fn fetch_missing_repositories(&mut self) -> Result<(), Error> {
2472        let policies = self.policies.seed_policies()?.collect::<Vec<_>>();
2473        for policy in policies {
2474            let policy = match policy {
2475                Ok(policy) => policy,
2476                Err(err) => {
2477                    debug!(target: "protocol::filter", "Failed to read seed policy: {err}");
2478                    continue;
2479                }
2480            };
2481
2482            let rid = policy.rid;
2483
2484            if !policy.is_allow() {
2485                continue;
2486            }
2487            match self.storage.contains(&rid) {
2488                Ok(exists) => {
2489                    if exists {
2490                        continue;
2491                    }
2492                }
2493                Err(err) => {
2494                    log::debug!(target: "protocol::filter", "Failed to check if {rid} exists: {err}");
2495                    continue;
2496                }
2497            }
2498            match self.seeds(&rid, [*self.nid()].into()) {
2499                Ok(seeds) => {
2500                    if let Some(connected) = NonEmpty::from_vec(seeds.connected().collect()) {
2501                        for seed in connected {
2502                            self.fetch(rid, seed.nid, vec![], self.fetch_config(), None);
2503                        }
2504                    } else {
2505                        // TODO: We should make sure that this fetch is retried later, either
2506                        // when we connect to a seed, or when we discover a new seed.
2507                        // Since new connections and routing table updates are both conditions for
2508                        // fetching, we should trigger fetches when those conditions appear.
2509                        // Another way to handle this would be to update our database, saying
2510                        // that we're trying to fetch a certain repo. We would then just
2511                        // iterate over those entries in the above circumstances. This is
2512                        // merely an optimization though, we can also iterate over all seeded
2513                        // repos and check which ones are not in our inventory.
2514                        debug!(target: "service", "No connected seeds found for {rid}..");
2515                    }
2516                }
2517                Err(e) => {
2518                    debug!(target: "service", "Couldn't fetch missing repo {rid}: failed to lookup seeds: {e}");
2519                }
2520            }
2521        }
2522        Ok(())
2523    }
2524
2525    /// Run idle task for all connections.
2526    fn idle_connections(&mut self) {
2527        for sess in self.sessions.values_mut() {
2528            sess.idle(self.clock);
2529
2530            if sess.is_stable() {
2531                // Mark as connected once connection is stable.
2532                if let Err(e) =
2533                    self.db
2534                        .addresses_mut()
2535                        .connected(&sess.id, &sess.addr, self.clock.into())
2536                {
2537                    warn!(target: "service", "Failed to update address book with connection: {e}");
2538                }
2539            }
2540        }
2541    }
2542
2543    /// Try to maintain a target number of connections.
2544    fn maintain_connections(&mut self) {
2545        let PeerConfig::Dynamic = self.config.peers else {
2546            return;
2547        };
2548        trace!(target: "service", "Maintaining connections..");
2549
2550        let target = TARGET_OUTBOUND_PEERS;
2551        let now = self.clock;
2552        let outbound = self
2553            .sessions
2554            .values()
2555            .filter(|s| s.link.is_outbound())
2556            .filter(|s| s.is_connected() || s.is_connecting())
2557            .count();
2558        let wanted = target.saturating_sub(outbound);
2559
2560        // Don't connect to more peers than needed.
2561        if wanted == 0 {
2562            return;
2563        }
2564
2565        // Peers available to connect to.
2566        let available = self
2567            .available_peers()
2568            .into_iter()
2569            .filter_map(|peer| {
2570                peer.addresses
2571                    .into_iter()
2572                    .find(|ka| match (ka.last_success, ka.last_attempt) {
2573                        // If we succeeded the last time we tried, this is a good address.
2574                        // If it's been long enough that we failed to connect, we also try again.
2575                        (Some(success), Some(attempt)) => {
2576                            success > attempt || now - attempt >= CONNECTION_RETRY_DELTA
2577                        }
2578                        // If we haven't succeeded yet, and we waited long enough, we can try this address.
2579                        (None, Some(attempt)) => now - attempt >= CONNECTION_RETRY_DELTA,
2580                        // If we have no failed attempts for this address, it's worth a try.
2581                        (_, None) => true,
2582                    })
2583                    .map(|ka| (peer.nid, ka))
2584            })
2585            .filter(|(_, ka)| self.is_supported_address(&ka.addr));
2586
2587        // Peers we are going to attempt connections to.
2588        let connect = available.take(wanted).collect::<Vec<_>>();
2589        if connect.len() < wanted {
2590            log::debug!(
2591                target: "service",
2592                "Not enough available peers to connect to (available={}, wanted={wanted})",
2593                connect.len()
2594            );
2595        }
2596        for (id, ka) in connect {
2597            if let Err(e) = self.connect(id, ka.addr.clone()) {
2598                warn!(target: "service", "Service::maintain_connections connection error: {e}");
2599            }
2600        }
2601    }
2602
2603    /// Maintain persistent peer connections.
2604    fn maintain_persistent(&mut self) {
2605        trace!(target: "service", "Maintaining persistent peers..");
2606
2607        let now = self.local_time();
2608        let mut reconnect = Vec::new();
2609
2610        for (nid, session) in self.sessions.iter_mut() {
2611            if self.config.is_persistent(nid) {
2612                if self.policies.is_blocked(nid).unwrap_or(false) {
2613                    continue;
2614                }
2615                if let session::State::Disconnected { retry_at, .. } = &mut session.state {
2616                    // TODO: Try to reconnect only if the peer was attempted. A disconnect without
2617                    // even a successful attempt means that we're unlikely to be able to reconnect.
2618
2619                    if now >= *retry_at {
2620                        reconnect.push((*nid, session.addr.clone(), session.attempts()));
2621                    }
2622                }
2623            }
2624        }
2625
2626        for (nid, addr, attempts) in reconnect {
2627            if self.reconnect(nid, addr) {
2628                debug!(target: "service", "Reconnecting to {nid} (attempts={attempts})…");
2629            }
2630        }
2631    }
2632
2633    /// Checks if the given [`Address`] is supported for connecting to.
2634    ///
2635    /// # IPv4/IPv6/DNS
2636    ///
2637    /// Always returns `true`.
2638    ///
2639    /// # Tor
2640    ///
2641    /// If the [`Address`] is an `.onion` address and the service supports onion
2642    /// routing then this will return `true`.
2643    ///
2644    /// # I2P
2645    ///
2646    /// If the [`Address`] is an I2P address and the service supports I2P
2647    /// connections then this will return `true`.
2648    fn is_supported_address(&self, address: &Address) -> bool {
2649        match AddressType::from(address) {
2650            // Only consider onion addresses if configured.
2651            #[cfg(feature = "tor")]
2652            AddressType::Onion => self.config.onion != radicle::node::config::AddressConfig::Drop,
2653            #[cfg(feature = "i2p")]
2654            AddressType::I2p => self.config.i2p != radicle::node::config::AddressConfig::Drop,
2655            AddressType::Dns | AddressType::Ipv4 | AddressType::Ipv6 => true,
2656            _ => false,
2657        }
2658    }
2659
2660    fn fetch_config(&self) -> fetcher::FetchConfig {
2661        let timeout: LocalDuration = self.config.limits.fetch_timeout.into();
2662        fetcher::FetchConfig::default()
2663            .with_timeout(timeout.into())
2664            .with_minimum_feature_level(self.config.fetch.feature_level_min())
2665    }
2666}
2667
2668/// Gives read access to the service state.
2669pub trait ServiceState {
2670    /// Get the Node ID.
2671    fn nid(&self) -> &NodeId;
2672    /// Get the existing sessions.
2673    fn sessions(&self) -> &Sessions;
2674    /// Get fetch state.
2675    fn fetching(&self) -> &FetcherState;
2676    /// Get outbox.
2677    fn outbox(&self) -> &Outbox;
2678    /// Get rate limiter.
2679    fn limiter(&self) -> &RateLimiter;
2680    /// Get event emitter.
2681    fn emitter(&self) -> &Emitter<Event>;
2682    /// Get a repository from storage.
2683    fn get(&self, rid: RepoId) -> Result<Option<Doc>, RepositoryError>;
2684    /// Get the clock.
2685    fn clock(&self) -> &LocalTime;
2686    /// Get the clock mutably.
2687    fn clock_mut(&mut self) -> &mut LocalTime;
2688    /// Get service configuration.
2689    fn config(&self) -> &Config;
2690    /// Get service metrics.
2691    fn metrics(&self) -> &Metrics;
2692}
2693
2694impl<D, S> ServiceState for Service<D, S>
2695where
2696    D: routing::Store,
2697    S: ReadStorage,
2698{
2699    fn nid(&self) -> &NodeId {
2700        self.secret_key.public_key()
2701    }
2702
2703    fn sessions(&self) -> &Sessions {
2704        &self.sessions
2705    }
2706
2707    fn fetching(&self) -> &FetcherState {
2708        self.fetcher.state()
2709    }
2710
2711    fn outbox(&self) -> &Outbox {
2712        &self.outbox
2713    }
2714
2715    fn limiter(&self) -> &RateLimiter {
2716        &self.limiter
2717    }
2718
2719    fn emitter(&self) -> &Emitter<Event> {
2720        &self.emitter
2721    }
2722
2723    fn get(&self, rid: RepoId) -> Result<Option<Doc>, RepositoryError> {
2724        self.storage.get(rid)
2725    }
2726
2727    fn clock(&self) -> &LocalTime {
2728        &self.clock
2729    }
2730
2731    fn clock_mut(&mut self) -> &mut LocalTime {
2732        &mut self.clock
2733    }
2734
2735    fn config(&self) -> &Config {
2736        &self.config
2737    }
2738
2739    fn metrics(&self) -> &Metrics {
2740        &self.metrics
2741    }
2742}
2743
2744/// Disconnect reason.
2745#[derive(Debug)]
2746pub enum DisconnectReason {
2747    /// Error while dialing the remote. This error occurs before a connection is
2748    /// even established. Errors of this kind are usually not transient.
2749    Dial(Arc<dyn std::error::Error + Sync + Send>),
2750    /// Error with an underlying established connection. Sometimes, reconnecting
2751    /// after such an error is possible.
2752    Connection(Arc<dyn std::error::Error + Sync + Send>),
2753    /// Error with a fetch.
2754    Fetch(FetchError),
2755    /// Session error.
2756    Session(session::Error),
2757    /// Session conflicts with existing session.
2758    Conflict,
2759    /// Connection to self.
2760    SelfConnection,
2761    /// Peer is blocked by policy
2762    Policy,
2763    /// User requested disconnect
2764    Command,
2765}
2766
2767impl DisconnectReason {
2768    pub fn is_dial_err(&self) -> bool {
2769        matches!(self, Self::Dial(_))
2770    }
2771
2772    pub fn is_connection_err(&self) -> bool {
2773        matches!(self, Self::Connection(_))
2774    }
2775
2776    pub fn connection() -> Self {
2777        DisconnectReason::Connection(Arc::new(std::io::Error::from(
2778            std::io::ErrorKind::ConnectionReset,
2779        )))
2780    }
2781}
2782
2783impl fmt::Display for DisconnectReason {
2784    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2785        match self {
2786            Self::Dial(err) => write!(f, "{err}"),
2787            Self::Connection(err) => write!(f, "{err}"),
2788            Self::Command => write!(f, "command"),
2789            Self::SelfConnection => write!(f, "self-connection"),
2790            Self::Conflict => write!(f, "conflict"),
2791            Self::Policy => write!(f, "policy"),
2792            Self::Session(err) => write!(f, "{err}"),
2793            Self::Fetch(err) => write!(f, "fetch: {err}"),
2794        }
2795    }
2796}
2797
2798/// Result of a project lookup.
2799#[derive(Debug)]
2800pub struct Lookup {
2801    /// Whether or not the project was found locally.
2802    pub local: Option<Doc>,
2803    /// A list of remote peers on which the project is known to exist.
2804    pub remote: Vec<NodeId>,
2805}
2806
2807#[derive(thiserror::Error, Debug)]
2808pub enum LookupError {
2809    #[error(transparent)]
2810    Routing(#[from] routing::Error),
2811    #[error(transparent)]
2812    Repository(#[from] RepositoryError),
2813}
2814
2815#[derive(Debug, Clone)]
2816/// Holds currently (or recently) connected peers.
2817pub struct Sessions(AddressBook<NodeId, Session>);
2818
2819impl Sessions {
2820    pub fn new(rng: Rng) -> Self {
2821        Self(AddressBook::new(rng))
2822    }
2823
2824    /// Iterator over fully connected peers.
2825    pub fn connected(&self) -> impl Iterator<Item = (&NodeId, &Session)> + Clone {
2826        self.0
2827            .iter()
2828            .filter_map(move |(id, sess)| match &sess.state {
2829                session::State::Connected { .. } => Some((id, sess)),
2830                _ => None,
2831            })
2832    }
2833
2834    /// Iterator over connected inbound peers.
2835    pub fn inbound(&self) -> impl Iterator<Item = (&NodeId, &Session)> + Clone {
2836        self.connected().filter(|(_, s)| s.link.is_inbound())
2837    }
2838
2839    /// Iterator over outbound peers.
2840    pub fn outbound(&self) -> impl Iterator<Item = (&NodeId, &Session)> + Clone {
2841        self.connected().filter(|(_, s)| s.link.is_outbound())
2842    }
2843
2844    /// Iterator over mutable fully connected peers.
2845    pub fn connected_mut(&mut self) -> impl Iterator<Item = (&NodeId, &mut Session)> {
2846        self.0.iter_mut().filter(move |(_, s)| s.is_connected())
2847    }
2848
2849    /// Iterator over disconnected peers.
2850    pub fn disconnected_mut(&mut self) -> impl Iterator<Item = (&NodeId, &mut Session)> {
2851        self.0.iter_mut().filter(move |(_, s)| s.is_disconnected())
2852    }
2853
2854    /// Return whether this node has a fully established session.
2855    pub fn is_connected(&self, id: &NodeId) -> bool {
2856        self.0.get(id).map(|s| s.is_connected()).unwrap_or(false)
2857    }
2858
2859    /// Return whether this node can be connected to.
2860    pub fn is_disconnected(&self, id: &NodeId) -> bool {
2861        self.0.get(id).map(|s| s.is_disconnected()).unwrap_or(true)
2862    }
2863}
2864
2865impl Deref for Sessions {
2866    type Target = AddressBook<NodeId, Session>;
2867
2868    fn deref(&self) -> &Self::Target {
2869        &self.0
2870    }
2871}
2872
2873impl DerefMut for Sessions {
2874    fn deref_mut(&mut self) -> &mut Self::Target {
2875        &mut self.0
2876    }
2877}