From 96ba15cf1a6f8725f70f429695a5a9661f706eb0 Mon Sep 17 00:00:00 2001 From: tv Date: Sun, 8 Nov 2026 18:17:14 +0000 Subject: strict: a manifest serves one remake, at its base Review 2, N1: a strict manifest wasn't bound to its base. Any member could propose an old, legitimately signed one again at the current heads; nodes would verify it and vote, and every value at a signed place changed or added since (a ban, a new user, a rotated key, a changed userSigned or sealed declaration) would be deleted, nothing after the base being left to replay. The manifest now names the epoch and the heads it was made at, and a node takes it (proposing or voting) only for a remake of that epoch with exactly that base. GET /admin/strict-manifest makes it so; continuum-signed strict proposes at those heads already. --- src/simtest/policy.rs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'src/simtest/policy.rs') diff --git a/src/simtest/policy.rs b/src/simtest/policy.rs index 7e881e7..9f38e44 100644 --- a/src/simtest/policy.rs +++ b/src/simtest/policy.rs @@ -958,7 +958,16 @@ async fn a_swarm_turning_strict_through_a_remake() { let swarm = store::read_path(&replica.doc, &path("_continuum/swarm")) .unwrap() .unwrap(); - let made = strict::manifest_of(&replica.doc, swarm.as_str().unwrap(), policy, "alice", 1); + let epoch = crate::document::replica::EpochRecord::from(replica.epoch); + let made = strict::manifest_of( + &replica.doc, + swarm.as_str().unwrap(), + epoch, + &replica.doc.get_heads(), + policy, + "alice", + 1, + ); let mut manifest: strict::Manifest = serde_json::from_str(&made).unwrap(); manifest .keep @@ -1041,19 +1050,30 @@ async fn a_strict_remake_too_few_take() { } }) .await; - let text = { + let (text, heads) = { let replica = a.doc.read().await; let swarm = store::read_path(&replica.doc, &path("_continuum/swarm")) .unwrap() .unwrap(); let policy = json!({"version": 1, "values": "signed"}); - let line = strict::manifest_of(&replica.doc, swarm.as_str().unwrap(), policy, "alice", 1); + let epoch = crate::document::replica::EpochRecord::from(replica.epoch); + let heads = replica.doc.get_heads(); + let line = strict::manifest_of( + &replica.doc, + swarm.as_str().unwrap(), + epoch, + &heads, + policy, + "alice", + 1, + ); let signed = sshsig::sign(&key, strict::NAMESPACE, line.as_bytes()); - format!("{line}\n{signed}") + let heads: Vec = heads.iter().map(ToString::to_string).collect(); + (format!("{line}\n{signed}"), heads) }; let (status, report) = crate::remake::testing::start( a.clone(), - json!({"kind": "strict", "spec": {"manifest": text}, "settle": "3s"}), + json!({"kind": "strict", "spec": {"manifest": text}, "baseHeads": heads, "settle": "3s"}), None, ) .await; -- cgit v1.3.1