From 2e76c1f0e070fabc0f89dbe51b7f0498a9deaf62 Mon Sep 17 00:00:00 2001 From: ashpect Date: Mon, 16 Feb 2026 01:37:05 +0530 Subject: [PATCH 1/4] feat: add check_pubinputs command --- .gitignore | 3 + provekit/prover/src/lib.rs | 10 +- tooling/cli/Cargo.toml | 1 + tooling/cli/src/cmd/check_pubinputs.rs | 129 +++++++++++++++++++++++++ tooling/cli/src/cmd/mod.rs | 3 + 5 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 tooling/cli/src/cmd/check_pubinputs.rs diff --git a/.gitignore b/.gitignore index 165e92b5..5a8ac08a 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ Cargo.lock # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ circuit_stats_examples/ + +# Benchmark inputs +noir-examples/noir-passport/merkle_age_check/benchmark-inputs/logs/ \ No newline at end of file diff --git a/provekit/prover/src/lib.rs b/provekit/prover/src/lib.rs index bb89b790..edc24780 100644 --- a/provekit/prover/src/lib.rs +++ b/provekit/prover/src/lib.rs @@ -1,5 +1,8 @@ +mod r1cs; +mod whir_r1cs; +mod witness; + use { - crate::{r1cs::R1CSSolver, whir_r1cs::WhirR1CSProver}, acir::native_types::WitnessMap, anyhow::{Context, Result}, bn254_blackbox_solver::Bn254BlackBoxSolver, @@ -10,10 +13,7 @@ use { std::path::Path, tracing::instrument, }; - -mod r1cs; -mod whir_r1cs; -mod witness; +pub use {r1cs::R1CSSolver, whir_r1cs::WhirR1CSProver}; pub trait Prove { fn generate_witness(&mut self, input_map: InputMap) -> Result>; diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 76436aee..9de42046 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -18,6 +18,7 @@ provekit-verifier.workspace = true # Noir language acir.workspace = true +noir_artifact_cli.workspace = true # 3rd party anyhow.workspace = true diff --git a/tooling/cli/src/cmd/check_pubinputs.rs b/tooling/cli/src/cmd/check_pubinputs.rs new file mode 100644 index 00000000..07187867 --- /dev/null +++ b/tooling/cli/src/cmd/check_pubinputs.rs @@ -0,0 +1,129 @@ +use { + super::Command, + anyhow::{ensure, Context, Result}, + argh::FromArgs, + noir_artifact_cli::fs::inputs::read_inputs_from_file, + provekit_common::{file::read, FieldElement, IOPattern, NoirProof, Prover}, + provekit_prover::{Prove, R1CSSolver, WhirR1CSProver}, + std::{mem, path::PathBuf}, + tracing::{info, instrument}, +}; + +/// Check that public inputs in a proof match witness values from circuit inputs +#[derive(FromArgs, PartialEq, Eq, Debug)] +#[argh(subcommand, name = "check-pubinputs")] +pub struct Args { + /// path to the prover key package (.pkp) + #[argh(positional)] + prover_path: PathBuf, + + /// path to the proof file (.np) + #[argh(positional)] + proof_path: PathBuf, + + /// path to the input values (Prover.toml) + #[argh(positional)] + input_path: PathBuf, +} + +impl Command for Args { + #[instrument(skip_all)] + fn run(&self) -> Result<()> { + let mut prover: Prover = + read(&self.prover_path).context("while reading Provekit Prover")?; + let (constraints, witnesses) = prover.size(); + info!(constraints, witnesses, "Read prover key package"); + + let proof: NoirProof = read(&self.proof_path).context("while reading proof")?; + let proof_public_inputs = &proof.public_inputs; + info!( + num_public_inputs = proof_public_inputs.len(), + "Read proof with public inputs" + ); + + let (input_map, _expected_return) = + read_inputs_from_file(&self.input_path, prover.witness_generator.abi())?; + + let acir_witness_idx_to_value_map = prover + .generate_witness(input_map) + .context("while generating witness from inputs")?; + + let acir_public_inputs = prover.program.functions[0].public_inputs().indices(); + let num_public_inputs = acir_public_inputs.len(); + + if num_public_inputs == 0 && proof_public_inputs.is_empty() { + info!("All 0 public inputs match!"); + return Ok(()); + } + + let io: IOPattern = prover.whir_for_witness.create_io_pattern(); + let mut merlin = io.to_prover_state(); + + let mut witness: Vec> = vec![None; prover.r1cs.num_witnesses()]; + + prover.r1cs.solve_witness_vec( + &mut witness, + prover.split_witness_builders.w1_layers.clone(), + &acir_witness_idx_to_value_map, + &mut merlin, + ); + + if prover.whir_for_witness.num_challenges > 0 { + let w1 = witness[..prover.whir_for_witness.w1_size] + .iter() + .map(|w| w.ok_or_else(|| anyhow::anyhow!("Some witnesses in w1 are missing"))) + .collect::>>()?; + + prover + .whir_for_witness + .commit(&mut merlin, &prover.r1cs, w1, true) + .context("While committing to w1")?; + + prover.r1cs.solve_witness_vec( + &mut witness, + prover.split_witness_builders.w2_layers.clone(), + &acir_witness_idx_to_value_map, + &mut merlin, + ); + } + + mem::forget(merlin); + + let computed_public_inputs: Vec = witness[1..=num_public_inputs] + .iter() + .map(|w| w.ok_or_else(|| anyhow::anyhow!("Missing public input witness"))) + .collect::>>()?; + + ensure!( + computed_public_inputs.len() == proof_public_inputs.len(), + "Public input count mismatch: computed {} vs proof {}", + computed_public_inputs.len(), + proof_public_inputs.len() + ); + + let mut all_match = true; + for (i, (computed, from_proof)) in computed_public_inputs + .iter() + .zip(proof_public_inputs.0.iter()) + .enumerate() + { + if computed != from_proof { + info!( + index = i, + computed = ?computed, + from_proof = ?from_proof, + "Public input mismatch" + ); + all_match = false; + } + } + + if all_match { + info!("All {} public inputs match!", num_public_inputs); + } else { + anyhow::bail!("Public input verification failed: some values do not match"); + } + + Ok(()) + } +} diff --git a/tooling/cli/src/cmd/mod.rs b/tooling/cli/src/cmd/mod.rs index 096490a5..500a1b7c 100644 --- a/tooling/cli/src/cmd/mod.rs +++ b/tooling/cli/src/cmd/mod.rs @@ -1,4 +1,5 @@ mod analyze_pkp; +mod check_pubinputs; mod circuit_stats; mod generate_gnark_inputs; mod prepare; @@ -38,6 +39,7 @@ pub struct Args { #[argh(subcommand)] enum Commands { AnalyzePkp(analyze_pkp::Args), + CheckPubinputs(check_pubinputs::Args), Prepare(prepare::Args), Prove(prove::Args), CircuitStats(circuit_stats::Args), @@ -55,6 +57,7 @@ impl Command for Commands { fn run(&self) -> Result<()> { match self { Self::AnalyzePkp(args) => args.run(), + Self::CheckPubinputs(args) => args.run(), Self::Prepare(args) => args.run(), Self::Prove(args) => args.run(), Self::CircuitStats(args) => args.run(), From 357a9b001ef601e12233b7c86f9189a5bf072b46 Mon Sep 17 00:00:00 2001 From: ashpect Date: Mon, 16 Feb 2026 03:29:45 +0530 Subject: [PATCH 2/4] fix: add check_public_inputs --- Cargo.toml | 2 + README.md | 7 + tooling/cli/Cargo.toml | 1 - tooling/cli/src/cmd/check_pubinputs.rs | 129 ---------------- tooling/cli/src/cmd/check_public_inputs.rs | 169 +++++++++++++++++++++ tooling/cli/src/cmd/mod.rs | 6 +- 6 files changed, 181 insertions(+), 133 deletions(-) delete mode 100644 tooling/cli/src/cmd/check_pubinputs.rs create mode 100644 tooling/cli/src/cmd/check_public_inputs.rs diff --git a/Cargo.toml b/Cargo.toml index 34a0f41a..c00b5f9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,6 +95,8 @@ provekit-verifier-server = { path = "tooling/verifier-server" } # 3rd party anyhow = "1.0.93" argh = "0.1.12" +bincode = "1.3.3" +flate2 = "1.0" axum = "0.8.4" base64 = "0.22.1" bytes = "1.10.1" diff --git a/README.md b/README.md index ad88d465..2a124285 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,13 @@ Analyze PKP file size breakdown: cargo run --release --bin provekit-cli analyze-pkp ./prover.pkp ``` +Check if the proof generated for the public inputs in proof.json/proof.np match the noir circuit. + +```sh +nargo execute +cargo run --release --bin provekit-cli check-public-inputs ./target/basic.json ./target/basic.gz ./proof.np +``` + Recursively verify in a Gnark proof: ```sh diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 9de42046..76436aee 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -18,7 +18,6 @@ provekit-verifier.workspace = true # Noir language acir.workspace = true -noir_artifact_cli.workspace = true # 3rd party anyhow.workspace = true diff --git a/tooling/cli/src/cmd/check_pubinputs.rs b/tooling/cli/src/cmd/check_pubinputs.rs deleted file mode 100644 index 07187867..00000000 --- a/tooling/cli/src/cmd/check_pubinputs.rs +++ /dev/null @@ -1,129 +0,0 @@ -use { - super::Command, - anyhow::{ensure, Context, Result}, - argh::FromArgs, - noir_artifact_cli::fs::inputs::read_inputs_from_file, - provekit_common::{file::read, FieldElement, IOPattern, NoirProof, Prover}, - provekit_prover::{Prove, R1CSSolver, WhirR1CSProver}, - std::{mem, path::PathBuf}, - tracing::{info, instrument}, -}; - -/// Check that public inputs in a proof match witness values from circuit inputs -#[derive(FromArgs, PartialEq, Eq, Debug)] -#[argh(subcommand, name = "check-pubinputs")] -pub struct Args { - /// path to the prover key package (.pkp) - #[argh(positional)] - prover_path: PathBuf, - - /// path to the proof file (.np) - #[argh(positional)] - proof_path: PathBuf, - - /// path to the input values (Prover.toml) - #[argh(positional)] - input_path: PathBuf, -} - -impl Command for Args { - #[instrument(skip_all)] - fn run(&self) -> Result<()> { - let mut prover: Prover = - read(&self.prover_path).context("while reading Provekit Prover")?; - let (constraints, witnesses) = prover.size(); - info!(constraints, witnesses, "Read prover key package"); - - let proof: NoirProof = read(&self.proof_path).context("while reading proof")?; - let proof_public_inputs = &proof.public_inputs; - info!( - num_public_inputs = proof_public_inputs.len(), - "Read proof with public inputs" - ); - - let (input_map, _expected_return) = - read_inputs_from_file(&self.input_path, prover.witness_generator.abi())?; - - let acir_witness_idx_to_value_map = prover - .generate_witness(input_map) - .context("while generating witness from inputs")?; - - let acir_public_inputs = prover.program.functions[0].public_inputs().indices(); - let num_public_inputs = acir_public_inputs.len(); - - if num_public_inputs == 0 && proof_public_inputs.is_empty() { - info!("All 0 public inputs match!"); - return Ok(()); - } - - let io: IOPattern = prover.whir_for_witness.create_io_pattern(); - let mut merlin = io.to_prover_state(); - - let mut witness: Vec> = vec![None; prover.r1cs.num_witnesses()]; - - prover.r1cs.solve_witness_vec( - &mut witness, - prover.split_witness_builders.w1_layers.clone(), - &acir_witness_idx_to_value_map, - &mut merlin, - ); - - if prover.whir_for_witness.num_challenges > 0 { - let w1 = witness[..prover.whir_for_witness.w1_size] - .iter() - .map(|w| w.ok_or_else(|| anyhow::anyhow!("Some witnesses in w1 are missing"))) - .collect::>>()?; - - prover - .whir_for_witness - .commit(&mut merlin, &prover.r1cs, w1, true) - .context("While committing to w1")?; - - prover.r1cs.solve_witness_vec( - &mut witness, - prover.split_witness_builders.w2_layers.clone(), - &acir_witness_idx_to_value_map, - &mut merlin, - ); - } - - mem::forget(merlin); - - let computed_public_inputs: Vec = witness[1..=num_public_inputs] - .iter() - .map(|w| w.ok_or_else(|| anyhow::anyhow!("Missing public input witness"))) - .collect::>>()?; - - ensure!( - computed_public_inputs.len() == proof_public_inputs.len(), - "Public input count mismatch: computed {} vs proof {}", - computed_public_inputs.len(), - proof_public_inputs.len() - ); - - let mut all_match = true; - for (i, (computed, from_proof)) in computed_public_inputs - .iter() - .zip(proof_public_inputs.0.iter()) - .enumerate() - { - if computed != from_proof { - info!( - index = i, - computed = ?computed, - from_proof = ?from_proof, - "Public input mismatch" - ); - all_match = false; - } - } - - if all_match { - info!("All {} public inputs match!", num_public_inputs); - } else { - anyhow::bail!("Public input verification failed: some values do not match"); - } - - Ok(()) - } -} diff --git a/tooling/cli/src/cmd/check_public_inputs.rs b/tooling/cli/src/cmd/check_public_inputs.rs new file mode 100644 index 00000000..a70cf4a1 --- /dev/null +++ b/tooling/cli/src/cmd/check_public_inputs.rs @@ -0,0 +1,169 @@ +use { + super::Command, + acir::{circuit::Program, native_types::WitnessStack}, + anyhow::{ensure, Context, Result}, + argh::FromArgs, + base64::Engine, + provekit_common::{file::read, utils::noir_to_native, FieldElement, NoirElement, NoirProof}, + std::{collections::BTreeSet, fs, path::PathBuf}, + tracing::{info, instrument}, +}; + +/// Check that public inputs in a proof match witness values from nargo execute +#[derive(FromArgs, PartialEq, Eq, Debug)] +#[argh(subcommand, name = "check-public-inputs")] +pub struct Args { + /// path to the ACIR circuit file (.json) + #[argh(positional)] + circuit_path: PathBuf, + + /// path to the witness file (.gz) from nargo execute + #[argh(positional)] + witness_path: PathBuf, + + /// path to the proof file (.np/.json) + #[argh(positional)] + proof_path: PathBuf, +} + +impl Command for Args { + #[instrument(skip_all)] + fn run(&self) -> Result<()> { + let (public_indices, return_indices) = load_public_input_indices(&self.circuit_path)?; + let all_public: BTreeSet = public_indices.union(&return_indices).copied().collect(); + info!( + num_public_params = public_indices.len(), + num_return_values = return_indices.len(), + total = all_public.len(), + "Loaded circuit" + ); + + let witness_values = load_witness_values(&self.witness_path)?; + info!(num_witnesses = witness_values.len(), "Loaded witness file"); + + let proof: NoirProof = read(&self.proof_path).context("while reading proof")?; + let proof_public_inputs = &proof.public_inputs; + info!( + num_public_inputs = proof_public_inputs.len(), + "Loaded proof" + ); + + if all_public.is_empty() && proof_public_inputs.is_empty() { + info!("No public inputs - nothing to check"); + return Ok(()); + } + + let mut sorted_indices: Vec = all_public.iter().copied().collect(); + sorted_indices.sort(); + + let computed_public_inputs: Vec = sorted_indices + .iter() + .map(|&idx| { + witness_values + .iter() + .find(|(w, _)| w.witness_index() == idx) + .map(|(_, v)| noir_to_native(*v)) + .ok_or_else(|| anyhow::anyhow!("Missing witness at index {}", idx)) + }) + .collect::>>()?; + + ensure!( + computed_public_inputs.len() == proof_public_inputs.len(), + "Public input count mismatch: witness has {} but proof has {}", + computed_public_inputs.len(), + proof_public_inputs.len() + ); + + println!( + "Verifying {} public inputs:\n", + computed_public_inputs.len() + ); + + let mut all_match = true; + for ((idx, computed), from_proof) in sorted_indices + .iter() + .zip(computed_public_inputs.iter()) + .zip(proof_public_inputs.0.iter()) + { + let matches = computed == from_proof; + let status = if matches { "✓ match" } else { "✗ MISMATCH" }; + let input_type = if return_indices.contains(idx) { + "return" + } else { + "param" + }; + + println!(" w{} ({}):", idx, input_type); + println!(" witness: {:?}", computed); + println!(" proof: {:?}", from_proof); + println!(" {}\n", status); + + if !matches { + all_match = false; + } + } + + println!(); + if all_match { + info!( + count = computed_public_inputs.len(), + "All public inputs match!" + ); + Ok(()) + } else { + anyhow::bail!("Public input verification failed: some values do not match") + } + } +} + +fn load_public_input_indices(path: &PathBuf) -> Result<(BTreeSet, BTreeSet)> { + let json_string = fs::read_to_string(path) + .with_context(|| format!("while reading circuit file `{}`", path.display()))?; + + let json: serde_json::Value = + serde_json::from_str(&json_string).context("while parsing circuit JSON")?; + + let bytecode_str = json["bytecode"] + .as_str() + .context("expected 'bytecode' field in circuit JSON")?; + + let bytecode = base64::prelude::BASE64_STANDARD + .decode(bytecode_str) + .context("while decoding base64 bytecode")?; + + let program: Program = + Program::deserialize_program(&bytecode).context("while deserializing ACIR program")?; + + let circuit = program + .functions + .first() + .context("program has no functions")?; + + let public_params: BTreeSet = circuit + .public_parameters + .0 + .iter() + .map(|w| w.witness_index()) + .collect(); + + let return_values: BTreeSet = circuit + .return_values + .0 + .iter() + .map(|w| w.witness_index()) + .collect(); + + Ok((public_params, return_values)) +} + +fn load_witness_values(path: &PathBuf) -> Result> { + let compressed_bytes = fs::read(path) + .with_context(|| format!("while reading witness file `{}`", path.display()))?; + + let mut witness_stack: WitnessStack = WitnessStack::deserialize(&compressed_bytes) + .context("while deserializing witness stack")?; + + let stack_item = witness_stack.pop().context("witness stack is empty")?; + + Ok(stack_item.witness.into_iter().collect()) +} diff --git a/tooling/cli/src/cmd/mod.rs b/tooling/cli/src/cmd/mod.rs index 500a1b7c..598addda 100644 --- a/tooling/cli/src/cmd/mod.rs +++ b/tooling/cli/src/cmd/mod.rs @@ -1,5 +1,5 @@ mod analyze_pkp; -mod check_pubinputs; +mod check_public_inputs; mod circuit_stats; mod generate_gnark_inputs; mod prepare; @@ -39,7 +39,7 @@ pub struct Args { #[argh(subcommand)] enum Commands { AnalyzePkp(analyze_pkp::Args), - CheckPubinputs(check_pubinputs::Args), + CheckPublicInputs(check_public_inputs::Args), Prepare(prepare::Args), Prove(prove::Args), CircuitStats(circuit_stats::Args), @@ -57,7 +57,7 @@ impl Command for Commands { fn run(&self) -> Result<()> { match self { Self::AnalyzePkp(args) => args.run(), - Self::CheckPubinputs(args) => args.run(), + Self::CheckPublicInputs(args) => args.run(), Self::Prepare(args) => args.run(), Self::Prove(args) => args.run(), Self::CircuitStats(args) => args.run(), From ef73407d47af3384f088f99f8591a8c0c0008a02 Mon Sep 17 00:00:00 2001 From: ashpect Date: Mon, 16 Feb 2026 04:38:20 +0530 Subject: [PATCH 3/4] fix: ordering of pub wit acc to acir index --- .../common/src/witness/scheduling/splitter.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/provekit/common/src/witness/scheduling/splitter.rs b/provekit/common/src/witness/scheduling/splitter.rs index 80979b04..4cb5d0cd 100644 --- a/provekit/common/src/witness/scheduling/splitter.rs +++ b/provekit/common/src/witness/scheduling/splitter.rs @@ -233,12 +233,11 @@ impl<'a> WitnessSplitter<'a> { w1_indices: Vec, acir_public_inputs_indices_set: &HashSet, ) -> Result, SplitError> { - let mut public_input_builder_indices = Vec::new(); + let mut public_input_builders_with_acir: Vec<(usize, u32)> = Vec::new(); let mut rest_indices = Vec::new(); let w1_indices_set = w1_indices.iter().copied().collect::>(); - // Build ACIR index -> builder index map for O(1) lookups (O(B) once) let acir_to_builder: HashMap = self .witness_builders .iter() @@ -252,10 +251,7 @@ impl<'a> WitnessSplitter<'a> { }) .collect(); - // Sanity check: all public inputs must have builders in w1 (O(P) lookups) for &acir_idx in acir_public_inputs_indices_set { - // ACIR witness 0 is always the constant-one witness, handled - // separately via mandatory_w1.insert(0) above — not a regular ACIR witness. if acir_idx == 0 { continue; } @@ -271,13 +267,12 @@ impl<'a> WitnessSplitter<'a> { } } - // Separate into: 0, public inputs, and rest for builder_idx in w1_indices { if builder_idx == 0 { - continue; // Will add 0 first + continue; } else if let WitnessBuilder::Acir(_, acir_idx) = &self.witness_builders[builder_idx] { if acir_public_inputs_indices_set.contains(&(*acir_idx as u32)) { - public_input_builder_indices.push(builder_idx); + public_input_builders_with_acir.push((builder_idx, *acir_idx as u32)); continue; } } @@ -285,10 +280,14 @@ impl<'a> WitnessSplitter<'a> { } rest_indices.sort_unstable(); + public_input_builders_with_acir.sort_unstable_by_key(|&(_, acir_idx)| acir_idx); - // Reorder: 0 first, then public inputs, then rest let mut new_w1_indices = vec![0]; - new_w1_indices.extend(public_input_builder_indices); + new_w1_indices.extend( + public_input_builders_with_acir + .into_iter() + .map(|(idx, _)| idx), + ); new_w1_indices.extend(rest_indices); Ok(new_w1_indices) } From ff7c42fd659ed5000043f9539db881b803d01f90 Mon Sep 17 00:00:00 2001 From: ashpect Date: Mon, 16 Feb 2026 04:38:56 +0530 Subject: [PATCH 4/4] feat: add scripts for passport pubwit checks --- .../scripts/tbs_1300/check-public-inputs.sh | 29 ++++++++++++++++ .../scripts/tbs_1300/execute-circuits.sh | 26 +++++++++++++++ .../scripts/tbs_720/check-public-inputs.sh | 28 ++++++++++++++++ .../scripts/tbs_720/execute-circuits.sh | 25 ++++++++++++++ tooling/cli/src/cmd/check_public_inputs.rs | 33 ++++++++----------- 5 files changed, 122 insertions(+), 19 deletions(-) create mode 100755 noir-examples/noir-passport/merkle_age_check/scripts/tbs_1300/check-public-inputs.sh create mode 100755 noir-examples/noir-passport/merkle_age_check/scripts/tbs_1300/execute-circuits.sh create mode 100755 noir-examples/noir-passport/merkle_age_check/scripts/tbs_720/check-public-inputs.sh create mode 100755 noir-examples/noir-passport/merkle_age_check/scripts/tbs_720/execute-circuits.sh diff --git a/noir-examples/noir-passport/merkle_age_check/scripts/tbs_1300/check-public-inputs.sh b/noir-examples/noir-passport/merkle_age_check/scripts/tbs_1300/check-public-inputs.sh new file mode 100755 index 00000000..ccb31fa9 --- /dev/null +++ b/noir-examples/noir-passport/merkle_age_check/scripts/tbs_1300/check-public-inputs.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Requires: Run execute-circuits.sh first to generate witness files + +CIRCUITS=( + "t_add_dsc_hash_1300" + "t_add_dsc_verify_1300" + "t_add_id_data_1300" + "t_add_integrity_commit" + "t_attest" +) + +cd ../.. + +LOG_DIR="./benchmark-inputs/logs/check-public-inputs/tbs_1300" +mkdir -p "$LOG_DIR" + +strip_ansi() { + sed $'s/\x1b\[[0-9;]*m//g' +} + +for circuit in "${CIRCUITS[@]}"; do + echo "Checking public inputs for $circuit" + cargo run --release --bin provekit-cli -- check-public-inputs \ + ./target/$circuit.json \ + ./target/$circuit.gz \ + ./benchmark-inputs/$circuit-proof.np 2>&1 | strip_ansi | tee "$LOG_DIR/$circuit.log" + echo "Checked $circuit" +done diff --git a/noir-examples/noir-passport/merkle_age_check/scripts/tbs_1300/execute-circuits.sh b/noir-examples/noir-passport/merkle_age_check/scripts/tbs_1300/execute-circuits.sh new file mode 100755 index 00000000..e116cd0d --- /dev/null +++ b/noir-examples/noir-passport/merkle_age_check/scripts/tbs_1300/execute-circuits.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +CIRCUITS=( + "t_add_dsc_hash_1300" + "t_add_dsc_verify_1300" + "t_add_id_data_1300" + "t_add_integrity_commit" + "t_attest" +) + +cd ../.. + +LOG_DIR="./benchmark-inputs/logs/execute/tbs_1300" +mkdir -p "$LOG_DIR" + +strip_ansi() { + sed $'s/\x1b\[[0-9;]*m//g' +} + +for circuit in "${CIRCUITS[@]}"; do + echo "Executing $circuit" + cp "./benchmark-inputs/tbs_1300/$circuit.toml" "./$circuit/Prover.toml" + nargo execute --package "$circuit" 2>&1 | strip_ansi | tee "$LOG_DIR/$circuit.log" + rm "./$circuit/Prover.toml" + echo "Executed $circuit" +done diff --git a/noir-examples/noir-passport/merkle_age_check/scripts/tbs_720/check-public-inputs.sh b/noir-examples/noir-passport/merkle_age_check/scripts/tbs_720/check-public-inputs.sh new file mode 100755 index 00000000..cbb770fc --- /dev/null +++ b/noir-examples/noir-passport/merkle_age_check/scripts/tbs_720/check-public-inputs.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Requires: Run execute-circuits.sh first to generate witness files + +CIRCUITS=( + "t_add_dsc_720" + "t_add_id_data_720" + "t_add_integrity_commit" + "t_attest" +) + +cd ../.. + +LOG_DIR="./benchmark-inputs/logs/check-public-inputs/tbs_720" +mkdir -p "$LOG_DIR" + +strip_ansi() { + sed $'s/\x1b\[[0-9;]*m//g' +} + +for circuit in "${CIRCUITS[@]}"; do + echo "Checking public inputs for $circuit" + cargo run --release --bin provekit-cli -- check-public-inputs \ + ./target/$circuit.json \ + ./target/$circuit.gz \ + ./benchmark-inputs/$circuit-proof.np 2>&1 | strip_ansi | tee "$LOG_DIR/$circuit.log" + echo "Checked $circuit" +done diff --git a/noir-examples/noir-passport/merkle_age_check/scripts/tbs_720/execute-circuits.sh b/noir-examples/noir-passport/merkle_age_check/scripts/tbs_720/execute-circuits.sh new file mode 100755 index 00000000..73c5f124 --- /dev/null +++ b/noir-examples/noir-passport/merkle_age_check/scripts/tbs_720/execute-circuits.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +CIRCUITS=( + "t_add_dsc_720" + "t_add_id_data_720" + "t_add_integrity_commit" + "t_attest" +) + +cd ../.. + +LOG_DIR="./benchmark-inputs/logs/execute/tbs_720" +mkdir -p "$LOG_DIR" + +strip_ansi() { + sed $'s/\x1b\[[0-9;]*m//g' +} + +for circuit in "${CIRCUITS[@]}"; do + echo "Executing $circuit" + cp "./benchmark-inputs/tbs_720/$circuit.toml" "./$circuit/Prover.toml" + nargo execute --package "$circuit" 2>&1 | strip_ansi | tee "$LOG_DIR/$circuit.log" + rm "./$circuit/Prover.toml" + echo "Executed $circuit" +done diff --git a/tooling/cli/src/cmd/check_public_inputs.rs b/tooling/cli/src/cmd/check_public_inputs.rs index a70cf4a1..cf78f17f 100644 --- a/tooling/cli/src/cmd/check_public_inputs.rs +++ b/tooling/cli/src/cmd/check_public_inputs.rs @@ -79,40 +79,35 @@ impl Command for Args { computed_public_inputs.len() ); - let mut all_match = true; for ((idx, computed), from_proof) in sorted_indices .iter() .zip(computed_public_inputs.iter()) .zip(proof_public_inputs.0.iter()) { - let matches = computed == from_proof; - let status = if matches { "✓ match" } else { "✗ MISMATCH" }; let input_type = if return_indices.contains(idx) { "return" } else { "param" }; - println!(" w{} ({}):", idx, input_type); - println!(" witness: {:?}", computed); - println!(" proof: {:?}", from_proof); - println!(" {}\n", status); + ensure!( + computed == from_proof, + "Public input mismatch at w{} ({}):\n witness: {:?}\n proof: {:?}", + idx, + input_type, + computed, + from_proof + ); - if !matches { - all_match = false; - } + println!(" w{} ({}): ✓ match", idx, input_type); } println!(); - if all_match { - info!( - count = computed_public_inputs.len(), - "All public inputs match!" - ); - Ok(()) - } else { - anyhow::bail!("Public input verification failed: some values do not match") - } + info!( + count = computed_public_inputs.len(), + "All public inputs match!" + ); + Ok(()) } }