Finding 4: full-trace evidence — reconstructed and re-derived, not summarized
Removes the deferral. Each sampled execution's FULL trace (every graph edge, count, weight) and delta are persisted as evidence; the independent attestor reconstructs them, recomputes the canonical trace hash and delta hash, re-derives the replay-record leaf, and confirms it is among the retained Merkle leaves the root is built from. The behavior-fingerprint hash is re-derived from features (not trusted), and f64 divergence is stored bit-exact for an identical hash. - trace_model: ExecutionTrace::serialize/deserialize (round-trips canonical_hash; total on garbage). Test: full_trace_serialize_roundtrips_canonical_hash. - world_model: WorldDelta::serialize/deserialize (round-trips hash). - ci_reports: retains TRACE_EVIDENCE_SAMPLE full traces; writes evidence/traces.tsv. - attestation: depends on trace_model/world_model; reconstructs each trace, recomputes the leaf, requires it to match the claimed leaf AND be a retained leaf. Negative control: tampered_trace_breaks_attestation (corrupting the full trace, leaving the claimed leaf, fails attestation). - merge-gates: requires evidence/traces.tsv; the separate attest step verifies it. End-to-end (fast profile): 256/256 full traces reconstructed and re-derived to retained leaves; recomputed root matches the claim over 6600 leaves. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -262,4 +262,45 @@ impl WorldDelta {
|
||||
}
|
||||
h.finish()
|
||||
}
|
||||
|
||||
/// Serialize the full delta to one line of whitespace-separated integers.
|
||||
/// Round-trips [`hash`] exactly (used as attestation evidence).
|
||||
pub fn serialize(&self) -> String {
|
||||
let mut t: Vec<String> = vec!["delta-v1".to_string(), self.turn_advance.to_string()];
|
||||
t.push(self.domain_deltas.len().to_string());
|
||||
for d in &self.domain_deltas {
|
||||
t.push(d.domain.0.to_string());
|
||||
for &v in &d.observed {
|
||||
t.push(v.to_string());
|
||||
}
|
||||
for &v in &d.hidden {
|
||||
t.push(v.to_string());
|
||||
}
|
||||
}
|
||||
t.join(" ")
|
||||
}
|
||||
|
||||
/// Reconstruct a delta from [`serialize`]. Total: `None` on malformed input.
|
||||
pub fn deserialize(s: &str) -> Option<WorldDelta> {
|
||||
let mut it = s.split_whitespace();
|
||||
if it.next()? != "delta-v1" {
|
||||
return None;
|
||||
}
|
||||
let turn_advance: u64 = it.next()?.parse().ok()?;
|
||||
let n: usize = it.next()?.parse().ok()?;
|
||||
let mut domain_deltas = Vec::with_capacity(n);
|
||||
for _ in 0..n {
|
||||
let domain = DomainId(it.next()?.parse().ok()?);
|
||||
let mut observed = [0i64; LANES];
|
||||
for v in observed.iter_mut() {
|
||||
*v = it.next()?.parse().ok()?;
|
||||
}
|
||||
let mut hidden = [0i64; HIDDEN_LANES];
|
||||
for v in hidden.iter_mut() {
|
||||
*v = it.next()?.parse().ok()?;
|
||||
}
|
||||
domain_deltas.push(DomainDelta { domain, observed, hidden });
|
||||
}
|
||||
Some(WorldDelta { domain_deltas, turn_advance })
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user