From a3eadeeeff9f4ae8ce91251e51d8312a6ffbc2ca Mon Sep 17 00:00:00 2001 From: tv Date: Sun, 8 Nov 2026 21:49:45 +0000 Subject: roots: a joiner takes a root once its seeds named it Review 3, R2: the root pin was set by the first HELLO_REPLY, and the first sync, a round trip later, took that root; a peer naming another afterwards was refused as another history. So the seed that answered first decided, not "none while they disagree", and a malicious seed makes itself fastest. A node joining with an empty document of the first epoch now takes no root (RootPin::Awaiting, roots left for later) until each of its seeds named one, or ROOT_WAIT (10 s) passed since the first did; then the one they name, none while they differ, as before. With --genesis-root it takes that one alone, at once. access.md, the option's help and example.toml say so; switching roots later, while the joiner holds nothing of its own, is postponed. --- src/access/screen.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/access/screen.rs') diff --git a/src/access/screen.rs b/src/access/screen.rs index beae149..a4ecdc2 100644 --- a/src/access/screen.rs +++ b/src/access/screen.rs @@ -82,13 +82,15 @@ pub(crate) struct Screening { /// Which change an empty document of the first epoch takes as its root: any (nothing /// heard of it, nor given), the one given (`--genesis-root`) or that its peers name, or -/// none while they name different ones. +/// none while they name different ones, or while a joining node waits for its seeds to +/// name theirs (`swarm::roots`). #[derive(Debug, Clone, Default, PartialEq, Eq)] pub(crate) enum RootPin { #[default] Any, Only(ChangeHash), Disputed, + Awaiting, } /// A member refused as a whole: since when, for which change of its, and why. @@ -691,7 +693,8 @@ pub(crate) fn apply_bytes( let mut screening = screening.lock_or_recover(); let screening = screening.of(epoch_seq); // Into an empty document of the first epoch, a root only as pinned: one given or - // named by the peers, none while they disagree (left for later). What builds on a + // named by the peers, none while they disagree or the seeds are awaited (left for + // later). What builds on a // root refused is refused with it; on one left, it waits. let mut pinned_out = Vec::new(); if epoch.generation == 0 && doc.get_heads().is_empty() && screening.root_pin != RootPin::Any { -- cgit v1.3.1