mod common;
use nebu::Goldilocks;
use zheng::execution::ExecutionNoun as N;
use zheng::machine::{self, MachineProof, statement};
fn arena(r: &mut nox::Reduction<8192>, n: &N) -> nox::Order {
match n {
N::Atom(v) => r.atom(Goldilocks::new(*v)).unwrap(),
N::Pair(a, b) => {
let a = arena(r, a);
let b = arena(r, b);
r.pair(a, b).unwrap()
}
}
}
fn back(r: &nox::Reduction<8192>, o: nox::Order) -> N {
match r.get(o).unwrap().inner {
nox::data::Data::Atom { value } => N::Atom(value.as_u64()),
nox::data::Data::Pair { left, right } => {
N::Pair(Box::new(back(r, left)), Box::new(back(r, right)))
}
}
}
fn native(program: &N, input: &[u64], budget: u64) -> (N, u64) {
let mut r = Box::new(nox::Reduction::<8192>::new());
let s = arena(&mut r, &statement::subject(input));
let f = arena(&mut r, program);
match nox::reduce(&mut r, s, f, budget, &nox::NullCalls, &mut nox::NoTrace) {
nox::Outcome::Ok(o, rem) => (back(&r, o), budget - rem),
other => panic!("native: {other:?}"),
}
}
fn whir() -> lens::WhirParams {
lens::WhirParams {
log_inv_rate: 3,
pow_bits: 16,
..lens::WhirParams::default()
}
}
fn agrees(program: &N, input: &[u64], seg: u32) -> machine::Run {
let budget = 1 << 40;
let run = machine::execute_with(program, input, budget, seg).expect("machine");
let (out, cycles) = native(program, input, budget);
assert_eq!(statement::parse(&run.statement.output).unwrap(), out);
assert_eq!(run.statement.cycles, cycles);
run
}
#[test]
fn joy_fixtures_agree_with_native_nox() {
let (hash, add) = (common::parse(common::HASH), common::parse(common::ADD));
for (prog, input) in [
(&hash, vec![7]),
(&hash, vec![common::P - 1]),
(&hash, vec![0]),
(&add, vec![7, 5]),
(&add, vec![0, 0]),
(&add, vec![common::P - 1, 3]),
] {
agrees(prog, &input, machine::SEGMENT_LOG_ROWS);
}
agrees(&common::hash_chain(11), &[3], machine::SEGMENT_LOG_ROWS);
}
#[test]
fn joy_fixtures_prove_and_bind_their_statements() {
let w = whir();
for (prog, input) in [
(common::parse(common::HASH), vec![7]),
(common::parse(common::ADD), vec![7, 5]),
] {
let run = agrees(&prog, &input, machine::SEGMENT_LOG_ROWS);
let proof = machine::prove_run(&run, &w).unwrap();
let bytes = proof.to_bytes();
let parsed = MachineProof::from_bytes(&bytes).unwrap();
assert_eq!(parsed, proof);
machine::verify(&run.statement, &parsed, &w).unwrap();
let st = &run.statement;
let mut forged = vec![st.clone(), st.clone(), st.clone(), st.clone()];
forged[0].input[0] += 1;
forged[1].cycles -= 1;
forged[2].output = statement::tokens(&N::Atom(1));
forged[3].program = statement::tokens(&N::Pair(Box::new(N::Atom(1)), Box::new(N::Atom(0))));
for f in &forged {
assert!(machine::verify(f, &parsed, &w).is_err());
}
}
}
#[test]
fn a_merkle_path_and_a_multi_segment_run_prove() {
let w = whir();
let root = native(&common::merkle_program(8, None), &[5], 1 << 30).0;
let prog = common::merkle_program(8, Some(&root));
let run = agrees(&prog, &[5], machine::SEGMENT_LOG_ROWS);
assert_eq!(statement::parse(&run.statement.output).unwrap(), N::Atom(0));
let proof = machine::prove_run(&run, &w).unwrap();
machine::verify(&run.statement, &proof, &w).unwrap();
let bad = common::merkle_program(8, Some(&N::Atom(9)));
let run_bad = agrees(&bad, &[5], machine::SEGMENT_LOG_ROWS);
assert_eq!(
statement::parse(&run_bad.statement.output).unwrap(),
N::Atom(1)
);
let mut lie = run_bad.statement.clone();
lie.output = statement::tokens(&N::Atom(0));
let proof_bad = machine::prove_run(&run_bad, &w).unwrap();
assert!(machine::verify(&lie, &proof_bad, &w).is_err());
let tree = common::tree_program(7);
let run = agrees(&tree, &[3], 8);
assert!(run.segments() > 4, "{} segments", run.segments());
let proof = machine::prove_run(&run, &w).unwrap();
let parsed = MachineProof::from_bytes(&proof.to_bytes()).unwrap();
machine::verify(&run.statement, &parsed, &w).unwrap();
let mut lie = run.statement.clone();
lie.cycles += 1;
lie.budget += 1;
assert!(machine::verify(&lie, &parsed, &w).is_err());
let rec = common::rec_program();
let run = agrees(&rec, &[6], 9);
assert_eq!(
statement::parse(&run.statement.output).unwrap(),
N::Atom(64)
);
let p2 = machine::prove_run(&run, &w).unwrap();
machine::verify(&run.statement, &p2, &w).unwrap();
let mut swapped = parsed.clone();
swapped.air.segments.swap(1, 2);
assert!(machine::verify(&run.statement, &swapped, &w).is_err());
}
#[test]
fn the_machine_rides_the_envelope_as_profile_four() {
use zheng::envelope::{Envelope, Profile};
let w = whir();
let run = agrees(
&common::parse(common::ADD),
&[7, 5],
machine::SEGMENT_LOG_ROWS,
);
let proof = machine::prove_run(&run, &w).unwrap();
let env = Envelope::Machine {
params: w,
statement: run.statement.public(),
proof: Box::new(proof),
};
let program = &run.statement.program;
let bytes = env.to_bytes();
assert_eq!(bytes[10], Profile::Machine as u8);
let back = Envelope::from_bytes_for(&bytes, program).unwrap();
assert_eq!(back, env);
back.verify_program(program, None).unwrap();
assert!(back.verify(None).is_err());
let hash = machine::statement::tokens(&common::parse(common::HASH));
assert!(back.verify_program(&hash, None).is_err());
assert_eq!(
Envelope::from_bytes_for(&bytes, &hash),
Err(zheng::envelope::EnvelopeError::ProgramMismatch)
);
let mut weak = bytes.clone();
weak[11 + 2] = 1; if let Ok(e) = Envelope::from_bytes(&weak) {
assert!(e.verify_program(program, None).is_err());
}
assert!(Envelope::from_bytes(&bytes[..bytes.len() - 1]).is_err());
let mut longer = bytes.clone();
longer.push(0);
assert!(Envelope::from_bytes(&longer).is_err());
}
#[test]
#[ignore]
fn bit_flip_scan_of_a_full_machine_envelope() {
use std::sync::atomic::{AtomicUsize, Ordering};
use zheng::envelope::Envelope;
let w = zheng::execution::succinct::params_for(20);
let run = agrees(
&common::parse(common::ADD),
&[7, 5],
machine::SEGMENT_LOG_ROWS,
);
let proof = machine::prove_run(&run, &w).unwrap();
let env = Envelope::Machine {
params: w,
statement: run.statement.public(),
proof: Box::new(proof),
};
let program = &run.statement.program;
let bytes = env.to_bytes();
let accepted = AtomicUsize::new(0);
let decoded = AtomicUsize::new(0);
let workers = std::thread::available_parallelism().map_or(4, |n| n.get());
let t = std::time::Instant::now();
std::thread::scope(|s| {
for k in 0..workers {
let (bytes, accepted, decoded) = (&bytes, &accepted, &decoded);
s.spawn(move || {
for i in (k..bytes.len()).step_by(workers) {
for bit in 0..8 {
let mut b = bytes.clone();
b[i] ^= 1 << bit;
if let Ok(e) = Envelope::from_bytes(&b) {
decoded.fetch_add(1, Ordering::Relaxed);
if e.verify_program(program, None).is_ok() {
accepted.fetch_add(1, Ordering::Relaxed);
}
}
}
}
});
}
});
println!(
"machine envelope add.tri: {} bytes, {} flips, {} decoded, {} accepted, {:.0} s",
bytes.len(),
bytes.len() * 8,
decoded.load(Ordering::Relaxed),
accepted.load(Ordering::Relaxed),
t.elapsed().as_secs_f64()
);
assert_eq!(accepted.load(Ordering::Relaxed), 0);
}
#[test]
fn long_runs_agree_with_native_nox() {
let run = agrees(&common::rec_program(), &[14], machine::SEGMENT_LOG_ROWS);
assert_eq!(run.statement.cycles, 393_202);
assert_eq!(
statement::parse(&run.statement.output).unwrap(),
N::Atom(1 << 14)
);
let run = agrees(&common::tree_program(14), &[3], machine::SEGMENT_LOG_ROWS);
assert_eq!(run.statement.cycles, 4 * (1 << 14) - 1);
}