diff --git a/radicle-node/src/service.rs b/radicle-node/src/service.rs index 70d82648..d23ba4db 100644 --- a/radicle-node/src/service.rs +++ b/radicle-node/src/service.rs @@ -687,6 +687,7 @@ where .seeded_by(&nid)? .collect::, _>>()?; let mut inventory = BTreeSet::new(); + let mut private = BTreeSet::new(); for repo in self.storage.repositories()? { let rid = repo.rid; @@ -699,6 +700,8 @@ where // Add public repositories to inventory. if repo.doc.visibility.is_public() { inventory.insert(rid); + } else { + private.insert(rid); } // If we have no owned refs for this repo, then there's nothing to announce. let Some(updated_at) = repo.synced_at else { @@ -728,15 +731,19 @@ where } } - { - // Ensure that our inventory is recorded in our routing table, and we are seeding - // all of it. It can happen that inventory is not properly seeded if for eg. the - // user creates a new repository while the node is stopped. - self.db - .routing_mut() - .add_inventory(inventory.iter(), nid, time.into())?; - self.inventory = gossip::inventory(self.timestamp(), inventory); - } + // Ensure that our inventory is recorded in our routing table, and we are seeding + // all of it. It can happen that inventory is not properly seeded if for eg. the + // user creates a new repository while the node is stopped. + self.db + .routing_mut() + .add_inventory(inventory.iter(), nid, time.into())?; + self.inventory = gossip::inventory(self.timestamp(), inventory); + + // Ensure that private repositories are not in our inventory. It's possible that + // a repository was public and then it was made private. + self.db + .routing_mut() + .remove_inventories(private.iter(), &nid)?; // Setup subscription filter for seeded repos. self.filter = Filter::new( diff --git a/radicle/src/node/routing.rs b/radicle/src/node/routing.rs index 625661f8..c910a0b4 100644 --- a/radicle/src/node/routing.rs +++ b/radicle/src/node/routing.rs @@ -51,8 +51,14 @@ pub trait Store { node: NodeId, time: Timestamp, ) -> Result, Error>; - /// Remove a node for the given id. + /// Remove an inventory from the given node. fn remove_inventory(&mut self, id: &RepoId, node: &NodeId) -> Result; + /// Remove multiple inventories from the given node. + fn remove_inventories<'a>( + &mut self, + ids: impl IntoIterator, + node: &NodeId, + ) -> Result<(), Error>; /// Iterate over all entries in the routing table. fn entries(&self) -> Result>, Error>; /// Get the total number of routing entries. @@ -172,6 +178,27 @@ impl Store for Database { Ok(self.db.change_count() > 0) } + fn remove_inventories<'a>( + &mut self, + rids: impl IntoIterator, + nid: &NodeId, + ) -> Result<(), Error> { + let mut stmt = self + .db + .prepare("DELETE FROM routing WHERE repo = ? AND node = ?")?; + + transaction(&self.db, |_| { + for rid in rids.into_iter() { + stmt.bind((1, rid))?; + stmt.bind((2, nid))?; + + stmt.iter().next(); + stmt.reset()?; + } + Ok::<_, Error>(()) + }) + } + fn len(&self) -> Result { let stmt = self.db.prepare("SELECT COUNT(1) FROM routing")?; let count: i64 = stmt @@ -401,6 +428,22 @@ mod test { assert!(!db.remove_inventory(&id, &node).unwrap()); } + #[test] + fn test_remove_many() { + let id1 = arbitrary::gen::(1); + let id2 = arbitrary::gen::(1); + let id3 = arbitrary::gen::(1); + let node = arbitrary::gen::(1); + let mut db = database(":memory:"); + + db.add_inventory([&id1, &id2, &id3], node, Timestamp::EPOCH) + .unwrap(); + assert_eq!(db.len().unwrap(), 3); + + db.remove_inventories([&id1, &id3], &node).unwrap(); + assert_eq!(db.len().unwrap(), 1); + } + #[test] fn test_len() { let mut db = database(":memory:");