mod common;
use nebu::{Fp3, Goldilocks};
use zheng::machine::{self, MachineStatement};
use zheng::recursion::ivc;
use zheng::recursion::wrap::{self, FinalProof, Inner, Mode, WrapKey, WrapParams};
const STEP: u32 = 15;
fn whir(rate: u8) -> lens::WhirParams {
let mut w = zheng::execution::succinct::params_for(20);
w.log_inv_rate = rate;
w.pow_bits = 24;
w
}
fn refuses(st: &MachineStatement, fp: &FinalProof, k: &WrapKey, what: &str) {
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| verify(st, fp, k)));
assert!(matches!(r, Ok(Err(_))), "{what} accepted");
}
fn verify(st: &MachineStatement, fp: &FinalProof, k: &WrapKey) -> Result<(), String> {
wrap::verify_statement(st, &whir(4), k, fp)
}
#[test]
fn a_wrapped_proof_verifies_and_binds_its_statement() {
let prog = common::parse(common::ADD);
let (st, p) = ivc::prove(&prog, &[7, 5], 1 << 20, &whir(4), STEP).unwrap();
let ikey = ivc::key(&whir(4), STEP as usize).unwrap();
let prep = ivc::prepare(&st, &whir(4), STEP, p.start, p.segments, p.chain).unwrap();
let pn = ivc::verify_claim(&prep, &p).unwrap();
let k0 = wrap::derive_key_ivc(
WrapParams {
whir: whir(4),
n: 0,
mode: Mode::Inner,
},
&ikey,
)
.unwrap();
let (w0, x0) = wrap::prove(
&k0,
&Inner::Ivc {
key: &ikey,
proof: &p,
},
&prep.publics,
&pn,
)
.unwrap();
wrap::verify_native(&k0, x0, &w0).unwrap();
let k1 = wrap::derive_key_wrap(
WrapParams {
whir: whir(4),
n: 0,
mode: Mode::Final,
},
&k0,
)
.unwrap();
let (w1, x1) = wrap::prove(
&k1,
&Inner::Wrap {
key: &k0,
proof: &w0,
},
&prep.publics,
&pn,
)
.unwrap();
assert_eq!(x0, x1, "every level binds the same public values");
let fp = FinalProof {
log_rows: p.log_rows,
start: p.start,
segments: p.segments,
chain: p.chain,
pn: pn.clone(),
wrap: w1,
};
let bytes = fp.to_bytes(&k1);
let fp = FinalProof::from_bytes(&bytes, &k1).unwrap();
verify(&st, &fp, &k1).unwrap();
let mut bad = w0.clone();
bad.local[5] += Fp3::ONE;
assert!(wrap::verify_native(&k0, x0, &bad).is_err());
let mut other = st.clone();
other.output = machine::statement::tokens(&zheng::execution::ExecutionNoun::Atom(13));
refuses(&other, &fp, &k1, "a wrong output");
let mut other = st.clone();
other.input = vec![7, 6];
refuses(&other, &fp, &k1, "a wrong input");
let mut other = st.clone();
other.cycles += 1;
refuses(&other, &fp, &k1, "a wrong cycle count");
let mut bad = fp.clone();
bad.pn.value += Fp3::ONE;
refuses(&st, &bad, &k1, "a deferred claim value");
let mut bad = fp.clone();
bad.pn.point[3] += Fp3::ONE;
refuses(&st, &bad, &k1, "a deferred claim point");
type Tamper = Box<dyn Fn(&mut FinalProof)>;
let tamper: Vec<Tamper> = vec![
Box::new(|p| p.wrap.roots[0][1] += Goldilocks::ONE),
Box::new(|p| p.wrap.ood[0][0] += Fp3::ONE),
Box::new(|p| p.wrap.zerocheck[2][4] += Fp3::ONE),
Box::new(|p| p.wrap.local[17] += Fp3::ONE),
Box::new(|p| p.wrap.next[0] += Fp3::ONE),
Box::new(|p| p.wrap.whir.sumcheck0[1] += Fp3::ONE),
Box::new(|p| p.wrap.whir.final_poly[0] += Fp3::ONE),
Box::new(|p| p.wrap.whir.rounds[0].open[0][0].symbols[2] += Fp3::ONE),
Box::new(|p| p.segments += 1),
Box::new(|p| p.chain[0] += Goldilocks::ONE),
Box::new(|p| p.start += 32),
];
for (i, f) in tamper.iter().enumerate() {
let mut bad = fp.clone();
f(&mut bad);
refuses(&st, &bad, &k1, &format!("tamper {i}"));
}
}