use affidavit::chain::{deserialize_receipt, recompute_chain, serialize_receipt, ChainAssembler};
use affidavit::ocel::{build_event, object_ref, SeqCounter};
use affidavit::types::{Blake3Hash, Receipt};
fn main() {
let mut counter = SeqCounter::new();
let mut asm = ChainAssembler::new();
assert!(asm.is_empty(), "fresh assembler must hold no events");
asm.append(
build_event(
"artifact.seed",
vec![object_ref("a1", "artifact")],
b"seed payload",
&mut counter,
)
.expect("event 0 well-formed"),
)
.expect("append event 0");
asm.append(
build_event(
"artifact.transform",
vec![object_ref("a1", "artifact")],
b"transform payload",
&mut counter,
)
.expect("event 1 well-formed"),
)
.expect("append event 1");
asm.append(
build_event(
"artifact.release",
vec![object_ref("a1", "artifact")],
b"release payload",
&mut counter,
)
.expect("event 2 well-formed"),
)
.expect("append event 2");
assert_eq!(asm.len(), 3, "exactly three events appended");
assert!(!asm.is_empty());
let receipt: Receipt = asm.finalize();
println!("chain_hash = {}", receipt.chain_hash.as_hex());
let recomputed = recompute_chain(&receipt.events).expect("recompute over honest events");
assert_eq!(
recomputed, receipt.chain_hash,
"chain must be reproducible from event bytes alone"
);
println!("reproduced chain_hash from events: MATCH");
let mut tampered = receipt.events.clone();
tampered[0].payload_commitment = Blake3Hash::from_bytes(b"forged seed payload");
let forged = recompute_chain(&tampered).expect("recompute over tampered events");
assert_ne!(
forged, receipt.chain_hash,
"tampering an event's commitment MUST break the chain hash"
);
println!("tampered chain_hash = {}", forged.as_hex());
println!("tamper detected: forged chain hash differs from sealed receipt");
let rebuilt = ChainAssembler::from_events(receipt.events.clone())
.expect("honest events rehydrate into an assembler");
assert_eq!(
rebuilt.events(),
receipt.events.as_slice(),
"events() returns the held sequence"
);
let refinalized = rebuilt.finalize();
assert_eq!(
refinalized.chain_hash, receipt.chain_hash,
"re-finalizing the same events reproduces the chain hash (determinism)"
);
let bytes = serialize_receipt(&receipt).expect("canonical serialization");
let reloaded = deserialize_receipt(&bytes).expect("canonical bytes reload");
assert_eq!(
reloaded.chain_hash, receipt.chain_hash,
"round-trip preserves the chain hash"
);
assert_eq!(
reloaded.events.len(),
receipt.events.len(),
"round-trip preserves all events"
);
let mut corrupt: serde_json::Value = serde_json::from_slice(&bytes).expect("valid json");
corrupt["chain_hash"] = serde_json::json!("0".repeat(64));
let corrupt_bytes = serde_json::to_vec(&corrupt).expect("reserialize");
let err = deserialize_receipt(&corrupt_bytes)
.expect_err("a chain-hash mismatch must be refused at deserialization");
assert!(
format!("{err}").contains("chain hash mismatch"),
"deserialize must reject with a chain-hash-mismatch error; got: {err}"
);
println!("from_events re-finalize MATCH; serialize/deserialize round-trip OK; corrupt hash refused at decode: {err}");
}