diff options
| author | tv <tv@krebsco.de> | 2026-11-08 18:21:55 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-08 19:29:21 +0100 |
| commit | d14290ab6d0ea40b97cdb72f5e07354cc2721c45 (patch) | |
| tree | e52d4b9a988967cd037c4d655a1001c06a4aea7c /src/state | |
| parent | ff9e7c244c270853a8a8f58ee950859866358e1d (diff) | |
refusals: a big drop waits for a second member only where there is one
Review 2, N8: a node with one neighbour (a swarm of two, a leaf) never
hears a second member name the change: its big drop was held for good,
an error logged at every REFUSALS. It now waits only where it has
another neighbour than the one telling it; with one, one word is all
there is.
access.md also says what the wait is: it guards against one peer, not
two members saying the same (any node with the swarm key registers),
and never makes a node drop what its own rules take.
Diffstat (limited to 'src/state')
| -rw-r--r-- | src/state/host.rs | 15 |
1 files changed, 11 insertions, 4 deletions
diff --git a/src/state/host.rs b/src/state/host.rs index ac36171..e7fc61a 100644 --- a/src/state/host.rs +++ b/src/state/host.rs @@ -99,14 +99,20 @@ impl AppState { /// so). Every peer syncs anew. Nothing while a rejoin runs, or if the document doesn't /// hold `root`; nor if `root` is an epoch's root, or nothing would be kept, which no /// rule can mean (logged as an error). A drop taking much (`BIG_DROP`) waits for a - /// second member to say so (`peer` the first, `why` its rules' reason): its rules and - /// one peer's word might both be off, the swarm's not. + /// second member to say so (`peer` the first, `why` its rules' reason), where it has + /// another neighbour: its rules and one peer's word might both be off, the swarm's + /// not. Two members colluding pass it; it guards against one. pub(crate) async fn drop_taken(&self, root: ChangeHash, peer: &str, why: &str) { /// Changes a drop takes, at most, on one peer's word: more, or more than a tenth /// of the document (past `SMALL_DROP`), wait for another's. const BIG_DROP: usize = 100; const SMALL_DROP: usize = 10; let own = self.remake.own_actors(); + // Another member to hear it from: a node with one neighbour, the one telling, has + // only its word (a swarm of two, a leaf), and waiting would hold the drop for good. + let another = crate::swarm::topology::our_neighbours(self) + .iter() + .any(|other| other != peer); let state = self.clone(); let peer = peer.to_owned(); let why = why.to_owned(); @@ -136,8 +142,9 @@ impl AppState { error!(change = %root, "Dropping a change would leave nothing; kept it"); return None; } - if tainted.len() > BIG_DROP - || (tainted.len() > SMALL_DROP && tainted.len() * 10 > changes.len()) + if another + && (tainted.len() > BIG_DROP + || (tainted.len() > SMALL_DROP && tainted.len() * 10 > changes.len())) { use crate::locks::MutexExt; let mut screening = state.screening.lock_or_recover(); |
