1use agent_scroll::{seal_chain, verify, SignOpts};
4use ed25519_dalek::SigningKey;
5use rand::rngs::OsRng;
6use serde_json::{json, Value};
7
8fn main() {
9 let sk = SigningKey::generate(&mut OsRng);
11 let sign = SignOpts {
12 privkey: sk.to_bytes(),
13 pubkey: sk.verifying_key().to_bytes(),
14 };
15
16 let mk = |turn: u64, role: &str, content: &str, ts_offset: u64| {
18 json!({
19 "version": "scroll/0.1",
20 "turn": turn,
21 "role": role,
22 "model": { "vendor": "anthropic", "id": "claude-opus-4-7" },
23 "params": { "temperature": 0, "top_p": 1 },
24 "messages": [{ "role": role, "content": content }],
25 "timestamp_ns": 1_700_000_000_000_000_000u64 + ts_offset,
26 })
27 };
28 let turns: Vec<Value> = vec![
29 mk(0, "user", "What is the capital of France?", 0),
30 mk(1, "assistant", "Paris.", 1),
31 mk(2, "user", "Thanks!", 2),
32 ];
33
34 let chain = seal_chain(&turns, Some(&sign)).expect("seal_chain");
36 println!("sealed {} turns, all hashes set", chain.len());
37
38 let clean = verify(&chain, Some(&sign.pubkey));
40 println!("verify clean: ok={}", clean.ok);
41
42 let mut tampered = chain.clone();
44 let h = tampered[1]["hash"].as_str().unwrap().to_string();
45 let last = h.chars().last().unwrap();
46 let new_last = if last == '0' { 'f' } else { '0' };
47 let mut new_h: String = h[..h.len() - 1].to_string();
48 new_h.push(new_last);
49 tampered[1]["hash"] = json!(new_h);
50
51 let bad = verify(&tampered, Some(&sign.pubkey));
52 let reason = bad.failures.first().map(|f| f.reason).unwrap_or("none");
53 println!("verify tampered: ok={} reason={}", bad.ok, reason);
54}