From ab595dfb91e66a8beda9b2dd407a4fceebe3b977 Mon Sep 17 00:00:00 2001 From: tv Date: Sun, 8 Nov 2026 15:46:28 +0000 Subject: refusals: a drop taking much waits for a second member, and shows A node told (REFUSALS) that it holds a change the swarm's rules refuse, and whose own rules refuse it too, dropped it and everything built on it on one peer's word, however much that was; its own writes among them went to the quarantine, the others' nowhere. * A drop taking more than 100 changes, or more than ten and a tenth of the document, now waits until a second member names the same change: the node's rules and one peer's word could both be off. Logged as an error, counted in /admin/stats (sync.dropsHeld). * The others' changes dropped are listed with the refused (GET /admin/refused, "taken unjudged, dropped"), for operators to see. The cheap half of asking the others (future-work.md, Telling a malicious change from a wrong policy). --- src/access/screen.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'src/access/screen.rs') diff --git a/src/access/screen.rs b/src/access/screen.rs index e15b407..89ca007 100644 --- a/src/access/screen.rs +++ b/src/access/screen.rs @@ -70,6 +70,9 @@ pub(crate) struct Screening { refusals: Refusals, /// The members refused as a whole, by node ID (`refusing`). refusing: std::collections::BTreeMap, + /// Changes this node holds that peers say the swarm's rules refuse, and its own do + /// too, whose drop would take much: each with the peers that said so (`hold_drop`). + held_drops: HashMap>, } /// A member refused as a whole: since when, for which change of its, and why. @@ -139,6 +142,30 @@ impl Screening { } } + /// `peer` says `root`, held here, is refused by the swarm's rules, its drop taking + /// much (`AppState::drop_taken`): how many peers said so of it, this one with them. + pub(crate) fn hold_drop(&mut self, root: ChangeHash, peer: &str) -> usize { + let named = self.held_drops.entry(root).or_default(); + named.insert(peer.to_owned()); + named.len() + } + + /// Drops held, for another peer to say so too (`hold_drop`). + pub(crate) fn drops_held(&self) -> usize { + self.held_drops.len() + } + + /// `dropped`, changes this node took unjudged and dropped (`AppState::drop_taken`), + /// kept with those refused, the oldest going past `MAX_REFUSED`; logged once, there. + pub(crate) fn keep_dropped(&mut self, dropped: Vec) { + for dropped in dropped { + self.refused.push_back(dropped); + if self.refused.len() > MAX_REFUSED { + self.refused.pop_front(); + } + } + } + /// Those refused, oldest first. pub(crate) fn refused(&self) -> impl DoubleEndedIterator { self.refused.iter() -- cgit v1.3.1