aboutsummaryrefslogtreecommitdiffstats
path: root/src/simtest
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-08 18:17:14 +0000
committertv <tv@krebsco.de>2026-11-08 19:29:10 +0100
commit96ba15cf1a6f8725f70f429695a5a9661f706eb0 (patch)
tree36feecb42a8bfa5317f27a052266f74489312dfc /src/simtest
parent4ce3bde36bb8283d82b914f0f73e30d89c935113 (diff)
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.
Diffstat (limited to 'src/simtest')
-rw-r--r--src/simtest/policy.rs30
1 files changed, 25 insertions, 5 deletions
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<String> = 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;