From 1e8f5b225959e4166a767c8c0be0cad75af09701 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Mon, 6 Mar 2023 12:10:19 +0000 Subject: [PATCH] node: determine fetching namespace based on repo policy To determine the correct Namespaces to use, the repo Policy and Scope are consulted. If the policy is Block then this is a failure state and is reported to the other end. Otherwise, if the policy is Track, then the scope is checked. For the Trusted Scope then the Namespaces should consist of all tracked nodes and delegates for the given repository. However, the repository might not exist and so it is necessary for the whole repository to be fetched. For the All Scope, all Namespaces are fetched. Signed-off-by: Fintan Halpenny X-Clacks-Overhead: GNU Terry Pratchett --- radicle-cli/tests/commands.rs | 4 ++ radicle-node/src/service.rs | 24 +++++---- radicle-node/src/service/tracking.rs | 78 ++++++++++++++++++++++++++++ radicle-node/src/test/peer.rs | 2 +- 4 files changed, 98 insertions(+), 10 deletions(-) diff --git a/radicle-cli/tests/commands.rs b/radicle-cli/tests/commands.rs index 66a71e68..13435528 100644 --- a/radicle-cli/tests/commands.rs +++ b/radicle-cli/tests/commands.rs @@ -476,6 +476,10 @@ fn test_replication_via_seed() { ) .unwrap(); + alice + .rad("track", &[&bob.id.to_human()], working.join("alice")) + .unwrap(); + alice.routes_to(&[(rid, alice.id), (rid, seed.id)]); seed.routes_to(&[(rid, alice.id), (rid, seed.id)]); bob.routes_to(&[(rid, alice.id), (rid, seed.id)]); diff --git a/radicle-node/src/service.rs b/radicle-node/src/service.rs index 5c75b9b6..480f6c45 100644 --- a/radicle-node/src/service.rs +++ b/radicle-node/src/service.rs @@ -33,6 +33,7 @@ use crate::prelude::*; use crate::service::message::{Announcement, AnnouncementMessage, Ping}; use crate::service::message::{NodeAnnouncement, RefsAnnouncement}; use crate::service::reactor::FetchDirection; +use crate::service::tracking::Scope; use crate::storage; use crate::storage::{Inventory, ReadRepository, RefUpdate, WriteStorage}; use crate::storage::{Namespaces, ReadStorage}; @@ -116,7 +117,7 @@ pub enum Command { /// Fetch the given repository from the network. Fetch(Id, NodeId, chan::Sender), /// Track the given repository. - TrackRepo(Id, tracking::Scope, chan::Sender), + TrackRepo(Id, Scope, chan::Sender), /// Untrack the given repository. UntrackRepo(Id, chan::Sender), /// Track the given node. @@ -267,7 +268,7 @@ where /// Track a repository. /// Returns whether or not the tracking policy was updated. - pub fn track_repo(&mut self, id: &Id, scope: tracking::Scope) -> Result { + pub fn track_repo(&mut self, id: &Id, scope: Scope) -> Result { self.out_of_sync = self.tracking.track_repo(id, scope)?; self.filter.insert(id); @@ -1093,13 +1094,18 @@ where } debug!(target: "service", "Fetch accepted for {rid} from {remote}.."); - let namespaces = if let Ok(_repo) = self.storage.repository(rid) { - // FIXME(finto): using remotes breaks test_gossip_during_fetch, so we use default for now - Namespaces::default() - } else { - Namespaces::default() + let namespaces = match self.tracking.namespaces_for(&self.storage, &rid) { + Ok(ns) => ns, + Err(err) => { + if let Some(resp) = self.fetch_reqs.get(&rid) { + resp.send(FetchResult::Failed { + reason: err.to_string(), + }) + .ok(); + } + return Ok(()); + } }; - // Instruct the transport to handover the socket to the worker. self.reactor .fetch(peer, rid, FetchDirection::Initiator { namespaces }); @@ -1204,7 +1210,7 @@ where Namespaces::Many(pks) => { for remote_id in pks.into_iter() { if refs - .push((*remote_id, repo.remote(&remote_id)?.refs.unverified())) + .push((*remote_id, repo.remote(remote_id)?.refs.unverified())) .is_err() { warn!( diff --git a/radicle-node/src/service/tracking.rs b/radicle-node/src/service/tracking.rs index bba1a311..ff7405e3 100644 --- a/radicle-node/src/service/tracking.rs +++ b/radicle-node/src/service/tracking.rs @@ -2,6 +2,14 @@ mod store; use std::ops; +use log::{error, warn}; +use nonempty::NonEmpty; +use thiserror::Error; + +use radicle::crypto::PublicKey; +use radicle::identity::IdentityError; +use radicle::storage::{Namespaces, ReadRepository as _, ReadStorage}; + use crate::prelude::Id; use crate::service::NodeId; @@ -10,6 +18,32 @@ pub use crate::node::tracking::{Alias, Node, Policy, Repo, Scope}; pub use store::Config as Store; pub use store::Error; +#[derive(Debug, Error)] +pub enum NamespacesError { + #[error("Failed to find tracking policy for {rid}")] + FailedPolicy { + rid: Id, + #[source] + err: Error, + }, + #[error("The policy for {rid} is to block fetching")] + BlockedPolicy { rid: Id }, + #[error("Failed to get tracking nodes for {rid}")] + FailedNodes { + rid: Id, + #[source] + err: Error, + }, + #[error("Failed to get delegate nodes for {rid}")] + FailedDelegates { + rid: Id, + #[source] + err: IdentityError, + }, + #[error("Could not find any trusted nodes for {rid}")] + NoTrusted { rid: Id }, +} + /// Tracking configuration. #[derive(Debug)] pub struct Config { @@ -62,6 +96,50 @@ impl Config { policy: self.policy, })) } + + pub fn namespaces_for(&self, storage: &S, rid: &Id) -> Result + where + S: ReadStorage, + { + use NamespacesError::*; + + let entry = self + .repo_policy(rid) + .map_err(|err| FailedPolicy { rid: *rid, err })?; + match entry.policy { + Policy::Block => { + error!(target: "service", "Attempted to fetch blocked repo {rid}"); + Err(NamespacesError::BlockedPolicy { rid: *rid }) + } + Policy::Track => match self.scope { + Scope::All => Ok(Namespaces::All), + Scope::Trusted => { + let nodes = self + .node_entries() + .map_err(|err| FailedNodes { rid: *rid, err })?; + let mut trusted: Vec<_> = nodes + .filter_map(|node| (node.policy == Policy::Track).then_some(node.id)) + .collect(); + + let ns = if let Ok(repo) = storage.repository(*rid) { + let delegates = repo + .delegates() + .map_err(|err| FailedDelegates { rid: *rid, err })? + .map(PublicKey::from); + trusted.extend(delegates); + NonEmpty::from_vec(trusted).map(Namespaces::Many) + } else { + Some(Namespaces::All) + }; + + ns.ok_or_else(|| { + warn!(target: "service", "Attempted to fetch repo {rid} with no trusted peers"); + NoTrusted { rid: *rid } + }) + } + }, + } + } } impl ops::Deref for Config { diff --git a/radicle-node/src/test/peer.rs b/radicle-node/src/test/peer.rs index dbbc390b..22aae974 100644 --- a/radicle-node/src/test/peer.rs +++ b/radicle-node/src/test/peer.rs @@ -106,7 +106,7 @@ impl Default for Config { config: service::Config::default(), addrs: address::Book::memory().unwrap(), local_time: LocalTime::now(), - policy: Policy::Block, + policy: Policy::default(), scope: Scope::default(), signer, rng,