Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

### Fixed

- Incremental snapshot state roots now include the existing v1 domain separator and agree with canonical snapshot hashing. Previously emitted incorrect accumulator roots are not migrated.

- The SPDX checker preserves Markdown frontmatter when it checks or repairs license headers.

- Action WAL recovery reuses ordered verified replay cursors instead of retaining a full state for each basis tick. It preserves basis, Tick, result, obstruction and conflict checks, including delayed stale bases.
Expand Down
92 changes: 92 additions & 0 deletions crates/warp-core/src/snapshot_accum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,7 @@ impl SnapshotAccumulator {
use blake3::Hasher;

let mut hasher = Hasher::new();
hasher.update(crate::domain::STATE_ROOT_V1);

// Root binding
hasher.update(&root.warp_id.0);
Expand Down Expand Up @@ -984,6 +985,97 @@ fn hash_attachment_value(hasher: &mut blake3::Hasher, value: &AttachmentValue) {
mod tests {
use super::*;

#[test]
fn accumulator_root_matches_the_canonical_snapshot_root() {
let state = crate::WorldlineState::empty();
let accumulator = SnapshotAccumulator::from_warp_state(state.warp_state());
let actual = accumulator.build(state.root(), [0; 32], 0);
assert_eq!(
actual.state_root,
crate::snapshot::compute_state_root(state.warp_state(), state.root())
);
}
Comment thread
flyingrobots marked this conversation as resolved.
Resolved

#[test]
fn accumulator_root_matches_canonical_reachable_content() {
use crate::attachment::AtomPayload;
use crate::graph::GraphStore;
use crate::warp_state::WarpState;

let warp = make_warp_id("parity-root");
let descended = make_warp_id("parity-descended");
let root = make_node_id("root");
let child = make_node_id("child");
let inner = make_node_id("inner");
let edge = make_edge_id("root-child");
let root_key = NodeKey {
warp_id: warp,
local_id: root,
};
let mut store = GraphStore::new(warp);
for node in [root, child] {
store.insert_node(
node,
NodeRecord {
ty: make_type_id("Node"),
},
);
}
store.insert_edge(
root,
EdgeRecord {
id: edge,
from: root,
to: child,
ty: make_type_id("Link"),
},
);
store.set_node_attachment(
root,
Some(AttachmentValue::Atom(AtomPayload::new(
make_type_id("Payload"),
bytes::Bytes::from_static(b"parity"),
))),
);
store.set_node_attachment(child, Some(AttachmentValue::Descend(descended)));
let parent = AttachmentKey {
owner: AttachmentOwner::Node(NodeKey {
warp_id: warp,
local_id: child,
}),
plane: AttachmentPlane::Alpha,
};
let mut inner_store = GraphStore::new(descended);
inner_store.insert_node(
inner,
NodeRecord {
ty: make_type_id("Inner"),
},
);
let mut state = WarpState::new();
state.upsert_instance(
WarpInstance {
warp_id: warp,
root_node: root,
parent: None,
},
store,
);
state.upsert_instance(
WarpInstance {
warp_id: descended,
root_node: inner,
parent: Some(parent),
},
inner_store,
);
let accumulator = SnapshotAccumulator::from_warp_state(&state);
assert_eq!(
accumulator.build(&root_key, [0; 32], 0).state_root,
crate::snapshot::compute_state_root(&state, &root_key)
);
}

#[test]
fn test_empty_accumulator() {
let acc = SnapshotAccumulator::new();
Expand Down
2 changes: 2 additions & 0 deletions docs/spec/merkle-commit.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ access to the original process.

The state root is BLAKE3 over canonical encoding of reachable WARP state from a root `NodeKey`. Reachability follows outbound skeleton edges and descended attachment portals from reachable node/edge slots.

Both canonical snapshots and incremental snapshot accumulation prepend the existing `echo:state_root:v1\0` domain separator before the root binding. Incremental accumulation must produce the same state root as canonical snapshot hashing for the same reachable state. Previously emitted accumulator roots that omitted this prefix do not satisfy that law; this correction does not migrate retained artifacts.

## Decision 2: `commit_id` v2 commits to replay, not narration

Commit header v2 commits to version, parents, state root, patch digest, and policy id. `patch_digest` is the digest of the replayable tick patch.
Expand Down
Loading