use std::path::PathBuf;
use joy_rs::Warrior;
use trident::runtime::artifact::BundleCost;
use trident::runtime::{ProgramBundle, ProgramInput, Prover, Runner, Verifier};
fn fixture(name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures")
.join(name)
}
fn bundle(assembly: &str) -> ProgramBundle {
ProgramBundle {
name: "test".to_string(),
version: "0.1.0".to_string(),
target_vm: "nox".to_string(),
target_os: None,
assembly: assembly.to_string(),
entry_point: "main".to_string(),
functions: Vec::new(),
cost: BundleCost {
table_values: Vec::new(),
table_names: Vec::new(),
padded_height: 0,
estimated_proving_ns: 0,
},
source_hash: trident::hash::ContentHash(trident::hash::content_hash_bytes(
assembly.as_bytes(),
))
.to_hex(),
reads_state: false,
}
}
fn input(public: &[u64], secret: &[u64]) -> ProgramInput {
ProgramInput {
public: public.to_vec(),
secret: secret.to_vec(),
digests: Vec::new(),
}
}
#[test]
fn add_literals_reduces_to_eight() {
let warrior = Warrior::new();
let result = warrior
.run(&bundle("[5 [[1 3] [1 5]]]"), &input(&[], &[]))
.expect("run failed");
assert_eq!(result.output, vec![8]);
assert_eq!(result.cycle_count, 3);
}
#[test]
fn public_inputs_bind_as_subject() {
let formula = "[2 [[3 [[5 [[0 6] [0 2]]] [0 1]]] [1 [7 [[0 2] [0 14]]]]]]";
let warrior = Warrior::new();
let result = warrior
.run(&bundle(formula), &input(&[3, 5], &[]))
.expect("run failed");
assert_eq!(result.output, vec![24]);
}
#[test]
fn secret_input_served_by_call_pattern() {
let warrior = Warrior::new();
let result = warrior
.run(&bundle("[16 [[1 0] [1 0]]]"), &input(&[], &[42]))
.expect("run failed");
assert_eq!(result.output, vec![42]);
}
#[test]
fn missing_secret_halts_honestly() {
let warrior = Warrior::new();
let err = warrior
.run(&bundle("[16 [[1 0] [1 0]]]"), &input(&[], &[]))
.expect_err("must halt without witness");
assert!(err.contains("halted"), "unexpected error: {}", err);
}
#[test]
fn budget_exhaustion_halts() {
let warrior = Warrior::with_budget(1);
let err = warrior
.run(&bundle("[5 [[1 3] [1 5]]]"), &input(&[], &[]))
.expect_err("must halt on budget");
assert!(err.contains("halted"), "unexpected error: {}", err);
}
#[test]
fn wrong_target_vm_rejected() {
let mut b = bundle("[1 0]");
b.target_vm = "triton".to_string();
let err = Warrior::new()
.run(&b, &input(&[], &[]))
.expect_err("must reject non-nox bundle");
assert!(err.contains("triton"), "unexpected error: {}", err);
}
#[test]
fn fixture_bundle_json_runs() {
let text = std::fs::read_to_string(fixture("add.bundle.json")).expect("fixture missing");
let b = ProgramBundle::from_json(&text).expect("bundle parse failed");
assert_eq!(b.target_vm, "nox");
let result = Warrior::new()
.run(&b, &input(&[], &[]))
.expect("run failed");
assert_eq!(result.output, vec![8]);
}
#[test]
fn verify_by_rerun_pass_and_fail() {
let warrior = Warrior::new();
let b = bundle("[5 [[1 3] [1 5]]]");
assert!(warrior
.verify_by_rerun(&b, &input(&[], &[]), &[8])
.expect("verify failed"));
assert!(!warrior
.verify_by_rerun(&b, &input(&[], &[]), &[9])
.expect("verify failed"));
}
#[test]
fn compile_tri_end_to_end() {
let mut options = trident::CompileOptions::for_profile("debug");
options.target_config = joy_rs::nox_terrain();
let b = trident::compile_to_bundle(&fixture("add.tri"), &options).expect("compile failed");
assert_eq!(b.target_vm, "nox");
let result = Warrior::new()
.run(&b, &input(&[3, 5], &[]))
.expect("run failed");
assert_eq!(result.output, vec![24]);
assert!(result.cycle_count > 0);
}
#[test]
fn prove_works_on_a_minimal_bundle() {
let warrior = Warrior::new();
let pd = warrior
.prove(&bundle("[5 [[1 3] [1 5]]]"), &input(&[], &[]))
.expect("prove failed");
assert_eq!(pd.format, joy_rs::EXECUTION_FORMAT);
assert!(warrior
.verify(&pd)
.expect("public execution claim verification"));
}
#[test]
fn prove_refuses_single_row_traces_honestly() {
let err = Warrior::new()
.prove_zheng(&bundle("[1 5]"), &input(&[], &[]))
.expect_err("single-row trace must be refused");
assert!(err.contains("row"), "unexpected error: {}", err);
}
#[test]
fn verify_refuses_foreign_proof_formats() {
let proof = trident::runtime::ProofData {
claim: trident::field::proof::Claim {
program_hash: Vec::new(),
public_input: Vec::new(),
public_output: Vec::new(),
},
proof_bytes: Vec::new(),
format: "stark-triton-v2".to_string(),
};
let err = Warrior::new()
.verify(&proof)
.expect_err("foreign formats are refused, not guessed at");
assert!(err.contains("format"), "unexpected error: {}", err);
}
fn compiled_add() -> ProgramBundle {
let mut options = trident::CompileOptions::for_profile("debug");
options.target_config = joy_rs::nox_terrain();
trident::compile_to_bundle(&fixture("add.tri"), &options).expect("compile failed")
}
#[test]
fn prove_verify_roundtrip() {
let warrior = Warrior::new();
let b = compiled_add();
let (artifact, result) = warrior
.prove_zheng(&b, &input(&[3, 5], &[]))
.expect("prove failed");
assert_eq!(result.output, vec![24]);
assert_eq!(artifact.meta.output, vec![24]);
assert_eq!(artifact.format, joy_rs::PROOF_FORMAT);
assert!(
warrior.verify_zheng(&b, &artifact).expect("verify errored"),
"honest proof must verify"
);
}
#[test]
fn prove_verify_roundtrip_through_traits_and_disk() {
let warrior = Warrior::new();
let b = compiled_add();
let pd = warrior
.prove(&b, &input(&[3, 5], &[]))
.expect("trait prove failed");
assert_eq!(pd.format, joy_rs::EXECUTION_FORMAT);
assert_eq!(pd.claim.public_output, vec![24]);
assert!(warrior
.verify(&pd)
.expect("public execution claim verification"));
let dir = std::env::temp_dir().join("joy-proof-test");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("add.zheng.json");
let artifact = joy_rs::ExecutionArtifact::from_bytes(&pd.proof_bytes).unwrap();
artifact.save(&path).expect("save failed");
let loaded = joy_rs::ExecutionArtifact::load(&path).expect("load failed");
loaded.verify().expect("execution verification failed");
}
#[test]
fn tampered_proof_rejected() {
let warrior = Warrior::new();
let b = compiled_add();
let (artifact, _) = warrior
.prove_zheng(&b, &input(&[3, 5], &[]))
.expect("prove failed");
let mut v: serde_json::Value =
serde_json::from_str(&serde_json::to_string(&artifact).unwrap()).unwrap();
let ev = v["proof"]["universal"]["proof"]["eval_value"]
.as_u64()
.unwrap();
v["proof"]["universal"]["proof"]["eval_value"] = serde_json::Value::from(ev ^ 1);
let tampered: joy_rs::ProofArtifact = serde_json::from_value(v).unwrap();
assert!(
!warrior.verify_zheng(&b, &tampered).expect("verify errored"),
"tampered proof must be rejected"
);
}
#[test]
fn proof_for_a_different_program_rejected() {
let warrior = Warrior::new();
let b = compiled_add();
let (artifact, _) = warrior
.prove_zheng(&b, &input(&[3, 5], &[]))
.expect("prove failed");
let other = bundle("[5 [[1 3] [1 5]]]");
assert!(
!warrior
.verify_zheng(&other, &artifact)
.expect("verify errored"),
"proof must bind to its program"
);
}
#[test]
fn artifact_load_error_paths() {
let missing = joy_rs::ProofArtifact::load(std::path::Path::new("/nonexistent/p.zheng.json"));
assert!(missing.is_err());
let dir = std::env::temp_dir().join("joy-proof-test");
std::fs::create_dir_all(&dir).unwrap();
let bad = dir.join("bad.zheng.json");
std::fs::write(&bad, b"{not json").unwrap();
assert!(joy_rs::ProofArtifact::load(&bad).is_err());
let warrior = Warrior::new();
let b = compiled_add();
let (mut artifact, _) = warrior
.prove_zheng(&b, &input(&[3, 5], &[]))
.expect("prove failed");
artifact.format = "not-zheng".to_string();
let p = dir.join("wrongformat.zheng.json");
artifact.save(&p).unwrap();
let err = joy_rs::ProofArtifact::load(&p);
assert!(err.is_err(), "unknown format must be refused");
}
#[test]
fn prove_refuses_look_rows_honestly() {
let warrior = Warrior::new();
let b = bundle("[17 [[1 0] [1 2]]]");
let r = warrior.prove_zheng(&b, &input(&[], &[]));
assert!(r.is_err(), "no proof without a bbg state");
}
fn compiled_hash() -> ProgramBundle {
let mut options = trident::CompileOptions::for_profile("debug");
options.target_config = joy_rs::nox_terrain();
trident::compile_to_bundle(&fixture("hash.tri"), &options).expect("compile failed")
}
#[test]
fn hash_program_proves_and_verifies() {
let warrior = Warrior::new();
let b = compiled_hash();
let (artifact, result) = warrior
.prove_zheng(&b, &input(&[42], &[]))
.expect("hash prove failed");
assert_eq!(result.output.len(), 4, "hash returns a 4-limb digest");
assert!(
warrior.verify_zheng(&b, &artifact).expect("verify errored"),
"hash proof must verify"
);
}
#[test]
fn tampered_hash_group_rejected() {
let warrior = Warrior::new();
let b = compiled_hash();
let (artifact, _) = warrior
.prove_zheng(&b, &input(&[42], &[]))
.expect("hash prove failed");
let mut v: serde_json::Value =
serde_json::from_str(&serde_json::to_string(&artifact).unwrap()).unwrap();
let wc = &mut v["proof"]["binding"]["accumulator"]["witness_commitment"];
let b0 = wc[0].as_u64().unwrap();
wc[0] = serde_json::Value::from(b0 ^ 1);
let tampered: joy_rs::ProofArtifact = serde_json::from_value(v).unwrap();
assert!(
!warrior.verify_zheng(&b, &tampered).expect("verify errored"),
"tampered hash proof must be rejected"
);
}
fn look_assembly(root: &[u8; 32], ns: u64, key: u64) -> String {
let l: Vec<_> = root
.chunks_exact(8)
.map(|b| u64::from_le_bytes(b.try_into().unwrap()))
.collect();
let (l0, l1, l2, l3) = (l[0], l[1], l[2], l[3]);
format!("[2 [[3 [[3 [[1 {l0}] [3 [[1 {l1}] [3 [[1 {l2}] [1 {l3}]]]]]]] [1 0]]] [1 [17 [[1 {ns}] [1 {key}]]]]]]")
}
fn sample_state() -> bbg::BbgState {
let mut state = bbg::BbgState::new();
for (key, energy) in [(1, 77), (2, 88)] {
let mut record = bbg::types::ParticleRecord::zero();
record.energy = energy;
state.particles.insert([key; 32], record);
}
state
}
#[test]
fn look_program_proves_and_verifies_against_state() {
let state = sample_state();
let root = state.root();
let b = bundle(&look_assembly(&root, 0, 11));
let warrior = Warrior::new();
let (artifact, result) = warrior
.prove_state_execution(&b, &input(&[], &[]), &state, 1000)
.unwrap();
assert_eq!(result.output, vec![77]);
assert_eq!(
artifact
.statement
.state_root
.iter()
.flat_map(|v| v.to_le_bytes())
.collect::<Vec<_>>(),
root
);
assert!(artifact.matches_program(&b).unwrap());
artifact.verify().unwrap();
let decoded =
joy_rs::StateExecutionArtifact::from_bytes(&artifact.to_bytes().unwrap()).unwrap();
decoded.verify().unwrap();
assert!(warrior.verify(&decoded.proof_data().unwrap()).unwrap());
}
#[test]
fn look_against_stale_root_refused_at_prove() {
let mut state = sample_state();
let old = state.root();
state.particles.get_mut(&[1; 32]).unwrap().energy = 99;
let b = bundle(&look_assembly(&old, 0, 11));
assert!(Warrior::new()
.prove_state_execution(&b, &input(&[], &[]), &state, 1000)
.is_err());
state.refresh_root();
assert!(Warrior::new()
.prove_state_execution(&b, &input(&[], &[]), &state, 1000)
.is_err());
}
#[test]
fn look_artifact_root_mismatch_rejected() {
let state = sample_state();
let b = bundle(&look_assembly(&state.root(), 0, 11));
let (artifact, _) = Warrior::new()
.prove_state_execution(&b, &input(&[], &[]), &state, 1000)
.unwrap();
for i in 0..4 {
let mut bad = artifact.clone();
bad.statement.state_root[i] ^= 1;
assert!(bad.verify().is_err());
}
}
#[test]
fn look_artifact_tampered_binding_and_table_rejected() {
let state = sample_state();
let b = bundle(&look_assembly(&state.root(), 0, 11));
let (artifact, _) = Warrior::new()
.prove_state_execution(&b, &input(&[], &[]), &state, 1000)
.unwrap();
for which in 0..7 {
let mut bad = artifact.clone();
match which {
0 => bad.statement.reads[0].namespace = 1,
1 => bad.statement.reads[0].key += 1,
2 => bad.statement.reads[0].value += 1,
3 => bad.statement.execution.public_output[0] += 1,
4 => bad.certificate.dimensions[0].fields[11] += 1,
5 => bad.statement.reads.clear(),
_ => bad.source_hash.push('0'),
}
assert!(bad.verify().is_err(), "mutation {which}");
}
}
#[test]
fn artifact_is_self_contained_and_binds_its_assembly() {
let warrior = Warrior::new();
let b = compiled_add();
let (mut artifact, _) = warrior
.prove_zheng(&b, &input(&[3, 5], &[]))
.expect("prove failed");
assert_eq!(
artifact.meta.assembly_text().unwrap().as_deref(),
Some(b.assembly.as_str())
);
assert!(
artifact.meta.assembly.is_none(),
"assembly travels deflated"
);
assert!(
warrior.verify_artifact(&artifact).expect("verify errored"),
"self-contained artifact must verify without its bundle"
);
artifact.meta.set_assembly("[1 5]");
assert!(
!warrior.verify_artifact(&artifact).expect("verify errored"),
"artifact with a different assembly must reject"
);
artifact.meta.assembly = None;
artifact.meta.assembly_deflate = None;
assert!(warrior.verify_artifact(&artifact).is_err());
}
#[test]
fn divine_secrets_prove_and_verify() {
let warrior = Warrior::new();
let mut options = trident::CompileOptions::default();
options.target_config = joy_rs::nox_terrain();
let src = "program hp\nfn main() -> Field {\n let a: Field = divine()\n let b: Field = divine()\n a + b\n}\n";
let dir = std::env::temp_dir().join("joy-divine-test");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("hp.tri");
std::fs::write(&path, src).unwrap();
let b = trident::compile_to_bundle(&path, &options).expect("compile failed");
let (artifact, result) = warrior
.prove_zheng(&b, &input(&[], &[7, 13]))
.expect("prove failed");
assert_eq!(result.output, vec![20]);
assert!(
warrior.verify_artifact(&artifact).expect("verify errored"),
"call/divine trace must verify (nox r6 carries the value, not the Order)"
);
}
#[test]
fn metadata_cannot_authorize_an_output_claim() {
let b = compiled_add();
let warrior = Warrior::new();
let (mut artifact, _) = warrior.prove_zheng(&b, &input(&[3, 5], &[])).unwrap();
artifact.meta.output = vec![999];
assert!(warrior.verify_artifact(&artifact).unwrap());
assert!(joy_rs::proof::require_statement_only(Some(&artifact.meta.output)).is_err());
let mut pd = warrior.prove(&b, &input(&[3, 5], &[])).unwrap();
pd.claim.public_output = vec![999];
assert!(!warrior.verify(&pd).unwrap());
}