aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-08 21:57:17 +0000
committertv <tv@krebsco.de>2026-11-08 22:59:08 +0100
commit7fecb4567b9173e8c7eb46db5cb170d685016a58 (patch)
tree86cb8f21fc86299cdec1763f7bc26c33932b17a1
parent512b0f2c2ce2f51218d18943afdb36c61f99151a (diff)
roots: a peer contradicting --genesis-root refused at the handshake
Review 3, R5: a node given its root (--genesis-root) refused a peer naming another only change by change while its document was empty, each with an entry among the refused; the handshake went through, as there was no root of its own yet to compare with. The root given is now compared too: a peer naming another is refused at the handshake, logged once, as one whose document starts from another root.
-rw-r--r--docs/access.md5
-rw-r--r--src/swarm/roots.rs20
2 files changed, 21 insertions, 4 deletions
diff --git a/docs/access.md b/docs/access.md
index 034b9fa..4c41e3d 100644
--- a/docs/access.md
+++ b/docs/access.md
@@ -330,8 +330,9 @@ is judged as one that never had one).
An epoch's root is pinned. A remake's is its epoch's ID, the hash of its snapshot. The
first epoch's is its genesis change, which its genesis node logs (`The swarm's root`)
and `GET /admin/epochs` shows (`root`). Every node names its root in its handshakes: a
-node whose document starts from another root of the same epoch refuses the handshake
-(another history under the swarm's ID: a forged root, or a genesis started again).
+node whose document starts from another root of the same epoch, or that was given
+another (`--genesis-root`), refuses the handshake (another history under the swarm's
+ID: a forged root, or a genesis started again).
A node joining with an empty document takes as its root only the one it was given
(`--genesis-root`), or else the one its peers name, and none while they name different
ones; and none before each of its seeds named one, or ten seconds passed since the
diff --git a/src/swarm/roots.rs b/src/swarm/roots.rs
index 9b17218..d12f089 100644
--- a/src/swarm/roots.rs
+++ b/src/swarm/roots.rs
@@ -5,8 +5,9 @@
//! sent it first. So every node names its epoch's root in its handshakes
//! (`HelloRequest::root`, `HelloReply::root`), and:
//!
-//! * a node whose document holds another root of the same epoch refuses the handshake:
-//! another history under this swarm's ID (a forged root, or a genesis started again);
+//! * a node whose document holds another root of the same epoch, or that was given
+//! another (`--genesis-root`), refuses the handshake: another history under this
+//! swarm's ID (a forged root, or a genesis started again);
//! * a joining node takes as its root only the one given (`--genesis-root`), or else the
//! one its peers name, and none while they name different ones (`RootPin`); and none
//! before each of its seeds named one, or `ROOT_WAIT` passed since the first did, so
@@ -119,6 +120,21 @@ pub(crate) async fn heard(
bail!("{peer} starts epoch {epoch} from root {root}, ours from {ours}");
}
if epoch.generation == 0 {
+ // The root given: a peer naming another is another history, whatever this
+ // node's document holds yet.
+ if let Some(given) = state.genesis_root
+ && given != root
+ {
+ if state
+ .roots
+ .lock_or_recover()
+ .differing
+ .insert(peer.to_owned())
+ {
+ error!(peer = %peer, given = %given, theirs = %root, "A peer's document starts from another root than the one given (--genesis-root): another history under this swarm's ID; not synced with");
+ }
+ bail!("{peer} starts the first epoch from root {root}, not the one given ({given})");
+ }
state
.roots
.lock_or_recover()