changes claude never committed
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "replay_corpus"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
world_model = { path = "../world_model" }
|
||||
rune_ir = { path = "../rune_ir" }
|
||||
trace_model = { path = "../trace_model" }
|
||||
generators = { path = "../generators" }
|
||||
reference_runtime = { path = "../reference_runtime" }
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
@@ -0,0 +1,145 @@
|
||||
//! `replay_corpus` — every case is permanent and must replay bit-for-bit.
|
||||
//! A replay case stores the seeds plus the three canonical hashes (trace,
|
||||
//! delta, future). Replaying regenerates the case deterministically from its
|
||||
//! master seed, re-executes the reference runtime, and asserts zero hash drift.
|
||||
|
||||
use generators::generate_accepted_case;
|
||||
use reference_runtime::{execute, EngineConfig, ResolutionInput};
|
||||
use world_model::Hash;
|
||||
|
||||
/// A permanent replay case (per spec) plus the master seed needed to
|
||||
/// regenerate the full case deterministically.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct ReplayCase {
|
||||
pub master_seed: u64,
|
||||
pub world_seed: u64,
|
||||
pub program_seed: u64,
|
||||
pub contract_seed: u64,
|
||||
pub perturbation_seed: u64,
|
||||
pub expected_trace_hash: Hash,
|
||||
pub expected_delta_hash: Hash,
|
||||
pub expected_future_hash: Hash,
|
||||
}
|
||||
|
||||
fn input_for(master_seed: u64) -> (ResolutionInput, u64, u64, u64, u64) {
|
||||
let (case, accepted_seed) = generate_accepted_case(master_seed);
|
||||
let _ = accepted_seed;
|
||||
let input = ResolutionInput {
|
||||
world: case.world.clone(),
|
||||
program: case.program.clone(),
|
||||
contexts: case.contexts.clone(),
|
||||
contract_seed: case.contract_seed,
|
||||
perturbation_seed: case.perturbation_seed,
|
||||
};
|
||||
(
|
||||
input,
|
||||
case.world_seed,
|
||||
case.program_seed,
|
||||
case.contract_seed,
|
||||
case.perturbation_seed,
|
||||
)
|
||||
}
|
||||
|
||||
/// Build a single replay case from a master seed.
|
||||
pub fn build_case(master_seed: u64) -> ReplayCase {
|
||||
let (input, ws, ps, cs, prs) = input_for(master_seed);
|
||||
let r = execute(&EngineConfig::reference(), &input);
|
||||
ReplayCase {
|
||||
master_seed,
|
||||
world_seed: ws,
|
||||
program_seed: ps,
|
||||
contract_seed: cs,
|
||||
perturbation_seed: prs,
|
||||
expected_trace_hash: r.trace.canonical_hash(),
|
||||
expected_delta_hash: r.delta.hash(),
|
||||
expected_future_hash: r.replay.future_hash,
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a replay corpus of `n` cases.
|
||||
pub fn build_corpus(n: usize, base_seed: u64) -> Vec<ReplayCase> {
|
||||
(0..n)
|
||||
.map(|i| build_case(base_seed ^ (i as u64).wrapping_mul(0x9e3779b97f4a7c15)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// A single replay verification outcome.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ReplayDrift {
|
||||
pub master_seed: u64,
|
||||
pub trace_ok: bool,
|
||||
pub delta_ok: bool,
|
||||
pub future_ok: bool,
|
||||
}
|
||||
|
||||
impl ReplayDrift {
|
||||
pub fn ok(&self) -> bool {
|
||||
self.trace_ok && self.delta_ok && self.future_ok
|
||||
}
|
||||
}
|
||||
|
||||
/// Replay one case and check for drift.
|
||||
pub fn replay(case: &ReplayCase) -> ReplayDrift {
|
||||
let (input, ..) = input_for(case.master_seed);
|
||||
let r = execute(&EngineConfig::reference(), &input);
|
||||
ReplayDrift {
|
||||
master_seed: case.master_seed,
|
||||
trace_ok: r.trace.canonical_hash() == case.expected_trace_hash,
|
||||
delta_ok: r.delta.hash() == case.expected_delta_hash,
|
||||
future_ok: r.replay.future_hash == case.expected_future_hash,
|
||||
}
|
||||
}
|
||||
|
||||
/// Aggregate replay report.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ReplayReport {
|
||||
pub total: usize,
|
||||
pub deterministic: usize,
|
||||
pub drift: Vec<u64>,
|
||||
}
|
||||
|
||||
impl ReplayReport {
|
||||
pub fn passed(&self, minimum: usize) -> bool {
|
||||
self.drift.is_empty() && self.total >= minimum
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify the whole corpus replays deterministically.
|
||||
pub fn verify_corpus(corpus: &[ReplayCase]) -> ReplayReport {
|
||||
let mut drift = Vec::new();
|
||||
let mut deterministic = 0;
|
||||
for case in corpus {
|
||||
let d = replay(case);
|
||||
if d.ok() {
|
||||
deterministic += 1;
|
||||
} else {
|
||||
drift.push(case.master_seed);
|
||||
}
|
||||
}
|
||||
ReplayReport {
|
||||
total: corpus.len(),
|
||||
deterministic,
|
||||
drift,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn replay_is_deterministic_zero_drift() {
|
||||
let corpus = build_corpus(80, 0x5EED);
|
||||
let report = verify_corpus(&corpus);
|
||||
assert_eq!(report.total, 80);
|
||||
assert_eq!(report.deterministic, 80);
|
||||
assert!(report.drift.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_hashes_are_stable() {
|
||||
let a = build_case(123);
|
||||
let b = build_case(123);
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user