use nebu::Goldilocks as F;
use zheng::envelope::{Envelope, EnvelopeError, MAGIC};
use zheng::execution::relation::{SubjectShape, compile_relation};
use zheng::execution::state::certify_state_execution;
use zheng::execution::state_evidence::{StateEvidence, StateTable};
use zheng::execution::{
Certificate, ExecutionNoun, certificate, certify_execution, verify_certificate,
};
use zheng::types::CCSWitness;
const P: u64 = 0xFFFF_FFFF_0000_0001;
const HASH: &str = "[2 [[3 [[4 [[9 [[0 3] [1 0]]] [[1 0] [8 [1 0]]]]] [0 1]]] [1 [2 [[0 3] [1 [2 [[3 [[5 [[0 2] [1 0]]] [1 0]]] [1 [15 [3 [[0 2] [3 [[1 0] [3 [[1 0] [3 [[1 0] [3 [[1 0] [3 [[1 0] [3 [[1 0] [3 [[1 0] [1 0]]]]]]]]]]]]]]]]]]]]]]]]]]]";
const ADD: &str = "[2 [[3 [[4 [[9 [[0 7] [1 0]]] [[1 0] [8 [1 0]]]]] [0 1]]] [1 [2 [[0 3] [1 [2 [[3 [[5 [[0 2] [1 0]]] [3 [[5 [[0 6] [1 0]]] [1 0]]]]] [1 [2 [[3 [[5 [[0 6] [0 2]]] [0 1]]] [1 [7 [[0 2] [0 14]]]]]]]]]]]]]]]";
fn parse(text: &str) -> ExecutionNoun {
fn go(b: &[u8], p: &mut usize) -> ExecutionNoun {
while b[*p].is_ascii_whitespace() {
*p += 1;
}
if b[*p] == b'[' {
*p += 1;
let a = go(b, p);
let c = go(b, p);
while b[*p].is_ascii_whitespace() {
*p += 1;
}
*p += 1;
ExecutionNoun::Pair(Box::new(a), Box::new(c))
} else {
let s = *p;
while b[*p].is_ascii_digit() {
*p += 1;
}
ExecutionNoun::Atom(std::str::from_utf8(&b[s..*p]).unwrap().parse().unwrap())
}
}
go(text.as_bytes(), &mut 0)
}
fn public(program: &str, input: &[u64]) -> Envelope {
let (statement, certificate) = certify_execution(&parse(program), input, 1_000_000).unwrap();
Envelope::Public {
statement,
certificate,
}
}
fn rejected(envelope: &Envelope) -> bool {
let bytes = envelope.to_bytes();
Envelope::from_bytes(&bytes).map_or(true, |e| e.verify(None).is_err())
}
#[test]
fn fixture_forged_io_is_rejected() {
for (program, input) in [(HASH, vec![7]), (ADD, vec![7, 5])] {
let honest = public(program, &input);
assert!(!rejected(&honest));
let Envelope::Public {
statement,
certificate,
} = honest
else {
unreachable!()
};
let forge = |edit: &dyn Fn(&mut zheng::execution::ExecutionStatement)| {
let mut s = statement.clone();
edit(&mut s);
rejected(&Envelope::Public {
statement: s,
certificate: certificate.clone(),
})
};
assert!(
forge(&|s| s.public_input[0] = (s.public_input[0] + 1) % P),
"input"
);
assert!(
forge(&|s| s.public_output[0] = (s.public_output[0] + 1) % P),
"output"
);
assert!(forge(&|s| s.public_output.push(0)), "extra output");
assert!(forge(&|s| s.cycles += 1), "cycles");
assert!(forge(&|s| s.cycles -= 1), "fewer cycles");
}
}
#[test]
fn fixture_forged_program_is_rejected() {
let Envelope::Public {
statement,
certificate,
} = public(ADD, &[7, 5])
else {
unreachable!()
};
let mut other = statement.clone();
let mul = other
.program
.iter()
.rposition(|t| *t == zheng::execution::NounToken::Atom(7))
.unwrap();
other.program[mul] = zheng::execution::NounToken::Atom(5);
assert!(rejected(&Envelope::Public {
statement: other,
certificate: certificate.clone()
}));
let Envelope::Public {
statement: hash,
certificate: hash_cert,
} = public(HASH, &[7])
else {
unreachable!()
};
assert!(rejected(&Envelope::Public {
statement: hash,
certificate: certificate.clone()
}));
assert!(rejected(&Envelope::Public {
statement,
certificate: hash_cert
}));
}
#[test]
fn fixture_zeroed_constant_is_rejected() {
let shape = SubjectShape::Pair(
Box::new(SubjectShape::Atom),
Box::new(SubjectShape::Pair(
Box::new(SubjectShape::Atom),
Box::new(SubjectShape::Atom),
)),
);
let relation = compile_relation(&parse(ADD), &shape).unwrap();
let zero = CCSWitness {
z: vec![F::ZERO; relation.instance.num_cols],
};
assert!(
relation.instance.is_satisfied_by(&zero),
"the hole the pin closes"
);
assert!(certificate::verify(&relation.instance, &Certificate { free: vec![] }, &[]).is_err());
assert!(
certificate::verify(
&relation.instance,
&Certificate { free: vec![] },
&[(0, F::ZERO)]
)
.is_err(),
"z[0] is not a public coordinate a statement can set"
);
let Envelope::Public { mut statement, .. } = public(ADD, &[0, 0]) else {
unreachable!()
};
statement.public_output = vec![0];
statement.cycles = 0;
assert!(verify_certificate(&statement, &Certificate { free: vec![] }).is_err());
}
#[test]
fn fixture_meaningless_witness_is_rejected() {
let Envelope::Public { certificate, .. } = public(ADD, &[7, 5]) else {
unreachable!()
};
for (program, input) in [(ADD, vec![1, 2]), (ADD, vec![0, 0]), (HASH, vec![7])] {
let Envelope::Public { statement, .. } = public(program, &input) else {
unreachable!()
};
assert!(
verify_certificate(&statement, &certificate).is_err(),
"{input:?}"
);
}
}
#[test]
fn fixture_truncated_and_overlong_certificates_are_rejected() {
for (program, input) in [(HASH, vec![7]), (ADD, vec![7, 5])] {
let Envelope::Public {
statement,
certificate,
} = public(program, &input)
else {
unreachable!()
};
let check = |c: Certificate| verify_certificate(&statement, &c).is_err();
let mut truncated = certificate.clone();
truncated.free.truncate(certificate.free.len() / 2);
assert!(check(truncated), "truncated");
let mut overlong = certificate.clone();
overlong.free.extend([1; 64]);
assert!(check(overlong), "overlong");
assert!(check(Certificate { free: vec![] }), "empty");
let bytes = Envelope::Public {
statement: statement.clone(),
certificate,
}
.to_bytes();
assert!(Envelope::from_bytes(&bytes[..bytes.len() - 1]).is_err());
assert!(Envelope::from_bytes(&[bytes.as_slice(), &[1]].concat()).is_err());
}
}
#[test]
fn fixture_wrong_state_root_is_rejected() {
let pair = |a, b| ExecutionNoun::Pair(Box::new(a), Box::new(b));
let q = |v| pair(ExecutionNoun::Atom(1), ExecutionNoun::Atom(v));
let read = pair(ExecutionNoun::Atom(17), pair(q(2), q(11)));
let program = pair(ExecutionNoun::Atom(7), pair(read, q(3)));
let state = |value: u64| {
let mut body = vec![0u64; 9];
body[8] = value;
StateEvidence::with_tables(vec![StateTable::with_body(2, &body)])
};
let (statement, certificate) =
certify_state_execution(&program, &[], 1000, true, &state(42)).unwrap();
let honest = Envelope::StatePublic {
statement: statement.clone(),
certificate: certificate.clone(),
};
honest.verify(Some(&state(42))).unwrap();
assert!(honest.verify(None).is_err());
assert!(honest.verify(Some(&state(43))).is_err());
for limb in 0..4 {
let mut wrong = statement.clone();
wrong.state_root[limb] = (wrong.state_root[limb] + 1) % P;
let envelope = Envelope::StatePublic {
statement: wrong,
certificate: certificate.clone(),
};
let decoded = Envelope::from_bytes(&envelope.to_bytes()).unwrap();
assert!(decoded.verify(Some(&state(42))).is_err(), "limb {limb}");
}
}
#[test]
fn fixture_envelope_with_wrong_magic_version_or_profile_is_rejected() {
let bytes = public(ADD, &[7, 5]).to_bytes();
assert_eq!(&bytes[..8], MAGIC);
let mut magic = bytes.clone();
magic[0] ^= 1;
assert_eq!(Envelope::from_bytes(&magic), Err(EnvelopeError::BadMagic));
let mut version = bytes.clone();
version[8] = 2;
assert_eq!(
Envelope::from_bytes(&version),
Err(EnvelopeError::UnsupportedVersion(2))
);
for profile in [1u8, 2, 3, 4] {
let mut wrong = bytes.clone();
wrong[10] = profile;
let accepted = Envelope::from_bytes(&wrong).is_ok_and(|e| e.verify(None).is_ok());
assert!(!accepted, "profile {profile}");
}
}