From 96ce399bb5405c9b52eb39467f1d357ff7ffd4bb Mon Sep 17 00:00:00 2001 From: M Alghazwi Date: Mon, 17 Mar 2025 15:05:06 +0100 Subject: [PATCH] add PublicInputGateV2 and refactor build, prove, and verify. --- plonky2/Cargo.toml | 4 + plonky2/benches/pi_gate_v2.rs | 116 +++++++++++++ plonky2/examples/bench_recursion.rs | 2 + plonky2/examples/fibonacci.rs | 2 + plonky2/examples/lookup_example.rs | 1 + plonky2/examples/multi_lookup_example.rs | 1 + plonky2/examples/pi_gate_v2.rs | 74 +++++++++ plonky2/examples/poseidon2_bn254_example.rs | 1 + plonky2/examples/recursive_pi_gate_v2.rs | 87 ++++++++++ plonky2/src/gates/coset_interpolation.rs | 1 + plonky2/src/gates/exponentiation.rs | 1 + plonky2/src/gates/gate.rs | 2 + plonky2/src/gates/gate_testing.rs | 8 +- plonky2/src/gates/mod.rs | 1 + plonky2/src/gates/public_input_v2.rs | 170 ++++++++++++++++++++ plonky2/src/gates/random_access.rs | 2 + plonky2/src/gates/test_gate_constraints.rs | 5 +- plonky2/src/plonk/circuit_builder.rs | 74 +++++++-- plonky2/src/plonk/circuit_data.rs | 30 +++- plonky2/src/plonk/get_challenges.rs | 49 ++++-- plonky2/src/plonk/proof.rs | 3 +- plonky2/src/plonk/prover.rs | 57 +++++-- plonky2/src/plonk/vars.rs | 8 + plonky2/src/plonk/verifier.rs | 37 ++++- 24 files changed, 687 insertions(+), 49 deletions(-) create mode 100644 plonky2/benches/pi_gate_v2.rs create mode 100644 plonky2/examples/pi_gate_v2.rs create mode 100644 plonky2/examples/recursive_pi_gate_v2.rs create mode 100644 plonky2/src/gates/public_input_v2.rs diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index 109b86e3..ed8e382f 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -97,6 +97,10 @@ harness = false name = "reverse_index_bits" harness = false +[[bench]] +name = "pi_gate_v2" +harness = false + # Display math equations properly in documentation [package.metadata.docs.rs] rustdoc-args = ["--html-in-header", ".cargo/katex-header.html"] diff --git a/plonky2/benches/pi_gate_v2.rs b/plonky2/benches/pi_gate_v2.rs new file mode 100644 index 00000000..3542be78 --- /dev/null +++ b/plonky2/benches/pi_gate_v2.rs @@ -0,0 +1,116 @@ +use std::any::type_name; +use anyhow::{anyhow, Result}; +use criterion::{criterion_group, criterion_main, Criterion}; +use plonky2::gates::noop::NoopGate; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::witness::{PartialWitness, WitnessWrite}; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::circuit_data::CircuitConfig; +use plonky2::plonk::config::{GenericConfig, Poseidon2BN254Config, PoseidonGoldilocksConfig}; +use plonky2_field::extension::Extendable; +use plonky2_field::goldilocks_field::GoldilocksField; + +/// Benchmark for building, proving, and verifying the Plonky2 circuit. +fn bench_circuit, const D:usize, C: GenericConfig,>(c: &mut Criterion, circuit_size: usize, num_of_pi: usize) -> Result<()>{ + + // Create the circuit configuration + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let num_dummy_gates = match circuit_size { + 0 => return Err(anyhow!("size must be at least 1")), + 1 => 0, + 2 => 1, + n => (1 << (n - 1)) + 1, + }; + + for _ in 0..num_dummy_gates { + builder.add_gate(NoopGate, vec![]); + } + + // The public inputs + let mut t_list= vec![]; + for _ in 0..num_of_pi { + let t = builder.add_virtual_public_input(); + t_list.push(t); + } + + // Benchmark Group + let mut group = c.benchmark_group(format!("Circuit size = {}, pi size = {}, and Hasher = {}", circuit_size, num_of_pi, type_name::())); + + // Benchmark the Circuit Building Phase + group.bench_function("Build Circuit", |b| { + b.iter(|| { + let config = CircuitConfig::standard_recursion_config(); + let mut local_builder = CircuitBuilder::::new(config); + for _ in 0..num_dummy_gates { + local_builder.add_gate(NoopGate, vec![]); + } + let _data = local_builder.build_unhashed_pi::(); + }) + }); + + let data = builder.build_unhashed_pi::(); + println!("Circuit size (degree bits): {:?}", data.common.degree_bits()); + + // Create a PartialWitness + let mut pw = PartialWitness::new(); + for i in 0..num_of_pi { + pw.set_target(t_list[i], F::ZERO)?; + } + + // Benchmark the Proving Phase + group.bench_function("Prove Circuit", |b| { + b.iter(|| { + let local_pw = pw.clone(); + data.prove_unhashed_pi(local_pw).expect("Failed to prove circuit") + }) + }); + + // Generate the proof once for verification benchmarking + let proof_with_pis = data.prove_unhashed_pi(pw.clone()).expect("Failed to prove circuit"); + let verifier_data = data.verifier_data(); + + println!("Proof size: {} bytes", proof_with_pis.to_bytes().len()); + println!("num of pi = {}", proof_with_pis.public_inputs.len()); + + // Benchmark the Verifying Phase + group.bench_function("Verify Proof", |b| { + b.iter(|| { + verifier_data.verify_unhashed_pi(proof_with_pis.clone()).expect("Failed to verify proof"); + }) + }); + + group.finish(); + Ok(()) +} + +fn bench_multiple_pi_values(c: &mut Criterion){ + const D: usize = 2; + type C1 = PoseidonGoldilocksConfig; + type C2 = Poseidon2BN254Config; + type F = GoldilocksField; + + // bench_circuit::(c, 12, 10).expect("failed"); + // bench_circuit::(c, 12, 10).expect("failed"); + // + // bench_circuit::(c, 12, 50).expect("failed"); + // bench_circuit::(c, 12, 50).expect("failed"); + // + // bench_circuit::(c, 12, 100).expect("failed"); + // bench_circuit::(c, 12, 100).expect("failed"); + + // bench_circuit::(c, 12, 500).expect("failed"); + // bench_circuit::(c, 12, 500).expect("failed"); + + bench_circuit::(c, 12, 1000).expect("failed"); + bench_circuit::(c, 12, 1000).expect("failed"); +} + +/// Criterion benchmark group +criterion_group!{ + name = prove_verify_benches; + config = Criterion::default().sample_size(10); + targets = bench_multiple_pi_values +} +criterion_main!(prove_verify_benches); diff --git a/plonky2/examples/bench_recursion.rs b/plonky2/examples/bench_recursion.rs index a85f0048..a77b96e7 100644 --- a/plonky2/examples/bench_recursion.rs +++ b/plonky2/examples/bench_recursion.rs @@ -247,6 +247,7 @@ where let prover_opts = ProverOptions { export_witness: Some(format!("{}_witness.json",name)), print_hash_statistics: HashStatisticsPrintLevel::Summary, // ::None, + hash_public_input: true, }; let mut timing = TimingTree::new("prove", Level::Debug); @@ -265,6 +266,7 @@ where let verifier_opts = VerifierOptions { print_hash_statistics: HashStatisticsPrintLevel::Summary, + hash_public_input: true, }; data.verify_with_options(proof.clone(), &verifier_opts)?; diff --git a/plonky2/examples/fibonacci.rs b/plonky2/examples/fibonacci.rs index 22d2beb0..3d83b626 100644 --- a/plonky2/examples/fibonacci.rs +++ b/plonky2/examples/fibonacci.rs @@ -44,6 +44,7 @@ fn main() -> Result<()> { let prover_opts = ProverOptions { export_witness: Some(String::from("fibonacci_witness.json")), print_hash_statistics: HashStatisticsPrintLevel::Info, + hash_public_input: true, }; let proof = data.prove_with_options(pw, &prover_opts)?; @@ -55,6 +56,7 @@ fn main() -> Result<()> { let verifier_opts = VerifierOptions { print_hash_statistics: HashStatisticsPrintLevel::Summary, + hash_public_input: true, }; data.verify_with_options(proof, &verifier_opts) diff --git a/plonky2/examples/lookup_example.rs b/plonky2/examples/lookup_example.rs index ac666ada..0c0b330b 100644 --- a/plonky2/examples/lookup_example.rs +++ b/plonky2/examples/lookup_example.rs @@ -145,6 +145,7 @@ fn main() -> Result<()> { let prover_opts = ProverOptions { export_witness: Some(String::from("lookup_witness.json")), print_hash_statistics: HashStatisticsPrintLevel::None, + hash_public_input: true, }; let proof = data.prove_with_options(pw, &prover_opts)?; diff --git a/plonky2/examples/multi_lookup_example.rs b/plonky2/examples/multi_lookup_example.rs index 7f83747d..306249fa 100644 --- a/plonky2/examples/multi_lookup_example.rs +++ b/plonky2/examples/multi_lookup_example.rs @@ -89,6 +89,7 @@ fn main() -> Result<()> { let prover_opts = ProverOptions { export_witness: Some(String::from("multi_lookup_witness.json")), print_hash_statistics: HashStatisticsPrintLevel::None, + hash_public_input: true, }; let proof = data.prove_with_options(pw, &prover_opts)?; diff --git a/plonky2/examples/pi_gate_v2.rs b/plonky2/examples/pi_gate_v2.rs new file mode 100644 index 00000000..80e9cf60 --- /dev/null +++ b/plonky2/examples/pi_gate_v2.rs @@ -0,0 +1,74 @@ +use std::fs; +use anyhow::Result; +use plonky2::field::types::Field; +use plonky2::gates::noop::NoopGate; +use plonky2::iop::witness::{PartialWitness, WitnessWrite}; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::circuit_data::CircuitConfig; +use plonky2::plonk::config::{Poseidon2BN254Config, PoseidonGoldilocksConfig}; +use plonky2::plonk::prover::ProverOptions; +use plonky2::plonk::verifier::{HashStatisticsPrintLevel, VerifierOptions}; +use plonky2_field::goldilocks_field::GoldilocksField; + +/// An example of using Plonky2 to prove a circuit with size S +/// and with P number of public inputs +/// uses the `PublicInputGateV2` which doesn't hash the public input +fn main() -> Result<()> { + const D: usize = 2; + + type C1 = PoseidonGoldilocksConfig; + type C2 = Poseidon2BN254Config; + type F = GoldilocksField; + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + const S: usize = 5; + let num_dummy_gates = (1 << (S - 1)) + 1; + for _ in 0..num_dummy_gates { + builder.add_gate(NoopGate, vec![]); + } + + const P: usize = 1000; + // The public inputs + let mut t_list= vec![]; + for _ in 0..P { + let t = builder.add_virtual_public_input(); + t_list.push(t); + } + + // Provide initial values. + let mut pw = PartialWitness::new(); + for i in 0..P { + pw.set_target(t_list[i], F::ZERO)?; + } + + let data = builder.build_unhashed_pi::(); + println!("circuit size = {}", data.common.degree_bits()); + + let prover_opts = ProverOptions { + export_witness: Some(String::from("pi_gate_v2_witness.json")), + print_hash_statistics: HashStatisticsPrintLevel::Info, + hash_public_input: false, + }; + + let proof = data.prove_with_options(pw, &prover_opts)?; + println!("num pi = {}", proof.public_inputs.len()); + + let verifier_opts = VerifierOptions { + print_hash_statistics: HashStatisticsPrintLevel::Summary, + hash_public_input: false, + }; + + let common_circuit_data_serialized = serde_json::to_string(&data.common).unwrap(); + let verifier_only_circuit_data_serialized = serde_json::to_string(&data.verifier_only).unwrap(); + let proof_serialized = serde_json::to_string(&proof).unwrap(); + fs::write("pi_gate_v2_common.json", common_circuit_data_serialized ).expect("Unable to write file"); + fs::write("pi_gate_v2_vkey.json" , verifier_only_circuit_data_serialized ).expect("Unable to write file"); + fs::write("pi_gate_v2_proof.json" , proof_serialized ).expect("Unable to write file"); + + + assert!(data.verify_with_options(proof, &verifier_opts).is_ok()); + + Ok(()) +} diff --git a/plonky2/examples/poseidon2_bn254_example.rs b/plonky2/examples/poseidon2_bn254_example.rs index 45e15b4b..362d6f91 100644 --- a/plonky2/examples/poseidon2_bn254_example.rs +++ b/plonky2/examples/poseidon2_bn254_example.rs @@ -43,6 +43,7 @@ fn main() -> Result<()> { let verifier_opts = VerifierOptions { print_hash_statistics: HashStatisticsPrintLevel::Summary, + hash_public_input: true, }; assert!(data.verify_with_options(proof, &verifier_opts).is_ok()); diff --git a/plonky2/examples/recursive_pi_gate_v2.rs b/plonky2/examples/recursive_pi_gate_v2.rs new file mode 100644 index 00000000..a788ff7f --- /dev/null +++ b/plonky2/examples/recursive_pi_gate_v2.rs @@ -0,0 +1,87 @@ +use anyhow::Result; +use plonky2::field::types::Field; +use plonky2::gates::noop::NoopGate; +use plonky2::iop::target::Target; +use plonky2::iop::witness::{PartialWitness, WitnessWrite}; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::circuit_data::CircuitConfig; +use plonky2::plonk::config::{Poseidon2BN254Config, PoseidonGoldilocksConfig}; +use plonky2_field::goldilocks_field::GoldilocksField; + +/// An example of using Plonky2 to prove a proof in-circuit +/// and with P number of public inputs +/// uses the `PublicInputGateV2` which doesn't hash the public input +fn main() -> Result<()> { + const D: usize = 2; + type C1 = PoseidonGoldilocksConfig; + type C2 = Poseidon2BN254Config; + type F = GoldilocksField; + + //---------- inner layer ------------ + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + const S: usize = 5; + let num_dummy_gates = (1 << (S - 1)) + 1; + for _ in 0..num_dummy_gates { + builder.add_gate(NoopGate, vec![]); + } + + const P: usize = 150; + // The public inputs + let mut t_list= vec![]; + for _ in 0..P { + let t = builder.add_virtual_public_input(); + t_list.push(t); + } + + // Provide initial values. + let mut pw = PartialWitness::new(); + for i in 0..P { + pw.set_target(t_list[i], F::ZERO)?; + } + + let data = builder.build::(); + println!("inner layer: circuit size = {}", data.common.degree_bits()); + + let proof = data.prove(pw)?; + println!("inner layer: num pi = {}", proof.public_inputs.len()); + + assert!(data.verify(proof.clone()).is_ok()); + + //------------ outer layer ---------------- + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let proof_t = builder.add_virtual_proof_with_pis(&data.common); + let verifier_data_t = builder.add_virtual_verifier_data(builder.config.fri_config.cap_height); + builder.verify_proof::(&proof_t, &verifier_data_t, &data.common); + + let mut t_list: Vec = vec![]; + + for (i,pi) in proof_t.public_inputs.iter().enumerate() { + let t = builder.add_virtual_public_input(); + builder.connect(*pi,t.clone()); + t_list.push(t); + } + + // Provide initial values. + let mut pw = PartialWitness::new(); + for i in 0..P { + pw.set_target(t_list[i], F::ZERO)?; + } + pw.set_proof_with_pis_target(&proof_t,&proof); + pw.set_verifier_data_target(&verifier_data_t,&data.verifier_only); + + let data = builder.build_unhashed_pi::(); + println!("outer layer: circuit size = {}", data.common.degree_bits()); + + let proof = data.prove_unhashed_pi(pw)?; + println!("outer layer: num pi = {}", proof.public_inputs.len()); + + assert!(data.verify_unhashed_pi(proof.clone()).is_ok()); + + Ok(()) +} diff --git a/plonky2/src/gates/coset_interpolation.rs b/plonky2/src/gates/coset_interpolation.rs index 8d093968..b7b4e6fd 100644 --- a/plonky2/src/gates/coset_interpolation.rs +++ b/plonky2/src/gates/coset_interpolation.rs @@ -864,6 +864,7 @@ mod tests { local_constants: &[], local_wires: &get_wires(shift, values, eval_point), public_inputs_hash: &HashOut::rand(), + public_inputs: &[F::rand()], }; assert!( diff --git a/plonky2/src/gates/exponentiation.rs b/plonky2/src/gates/exponentiation.rs index 9b5d6943..18689cad 100644 --- a/plonky2/src/gates/exponentiation.rs +++ b/plonky2/src/gates/exponentiation.rs @@ -430,6 +430,7 @@ mod tests { local_constants: &[], local_wires: &get_wires(base, power as u64), public_inputs_hash: &HashOut::rand(), + public_inputs: &[F::rand()], }; assert!( gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()), diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index eac2b368..0b00310f 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -99,10 +99,12 @@ pub trait Gate, const D: usize>: 'static + Send + S .map(|w| F::Extension::from_basefield(*w)) .collect::>(); let public_inputs_hash = &vars_base.public_inputs_hash; + let public_inputs = &vars_base.public_inputs; let vars = EvaluationVars { local_constants, local_wires, public_inputs_hash, + public_inputs, }; let values = self.eval_unfiltered(vars); diff --git a/plonky2/src/gates/gate_testing.rs b/plonky2/src/gates/gate_testing.rs index fe8c1cee..d3eea1ad 100644 --- a/plonky2/src/gates/gate_testing.rs +++ b/plonky2/src/gates/gate_testing.rs @@ -28,6 +28,7 @@ pub fn test_low_degree, G: Gate, const D: usi let constant_ldes = random_low_degree_matrix::(gate.num_constants(), rate_bits); assert_eq!(wire_ldes.len(), constant_ldes.len()); let public_inputs_hash = &HashOut::rand(); + let pi_rand = F::rand_vec(4); let constraint_evals = wire_ldes .iter() @@ -36,6 +37,7 @@ pub fn test_low_degree, G: Gate, const D: usi local_constants, local_wires, public_inputs_hash, + public_inputs: &pi_rand, }) .map(|vars| gate.eval_unfiltered(vars)) .collect::>(); @@ -107,13 +109,16 @@ pub fn test_eval_fns< .collect::>(); let public_inputs_hash = HashOut::rand(); + let rand_pi = F::rand_vec(4); + // Batch of 1. let vars_base_batch = - EvaluationVarsBaseBatch::new(1, &constants_base, &wires_base, &public_inputs_hash); + EvaluationVarsBaseBatch::new(1, &constants_base, &wires_base, &public_inputs_hash, &rand_pi); let vars = EvaluationVars { local_constants: &constants, local_wires: &wires, public_inputs_hash: &public_inputs_hash, + public_inputs: &rand_pi }; let evals_base = gate.eval_unfiltered_base_batch(vars_base_batch); @@ -146,6 +151,7 @@ pub fn test_eval_fns< local_constants: &constants, local_wires: &wires, public_inputs_hash: &public_inputs_hash, + public_inputs: &rand_pi, }; let evals = gate.eval_unfiltered(vars); diff --git a/plonky2/src/gates/mod.rs b/plonky2/src/gates/mod.rs index 478e3c5a..9379fd78 100644 --- a/plonky2/src/gates/mod.rs +++ b/plonky2/src/gates/mod.rs @@ -38,6 +38,7 @@ pub mod packed_util; pub mod poseidon; pub mod poseidon_mds; pub mod public_input; +pub mod public_input_v2; pub mod random_access; pub mod reducing; pub mod reducing_extension; diff --git a/plonky2/src/gates/public_input_v2.rs b/plonky2/src/gates/public_input_v2.rs new file mode 100644 index 00000000..536d61e1 --- /dev/null +++ b/plonky2/src/gates/public_input_v2.rs @@ -0,0 +1,170 @@ +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; +use core::ops::Range; + +use crate::field::extension::Extendable; +use crate::field::packed::PackedField; +use crate::gates::gate::Gate; +use crate::gates::packed_util::PackedEvaluableBase; +use crate::gates::util::StridedConstraintConsumer; +use crate::hash::hash_types::RichField; +use crate::iop::ext_target::ExtensionTarget; +use crate::iop::generator::WitnessGeneratorRef; +use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::circuit_data::CommonCircuitData; +use crate::plonk::vars::{ + EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch, + EvaluationVarsBasePacked, +}; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; + +/// A gate which enforces that each wire matches a corresponding public-input element. +/// +/// Specifically, if this gate has `num_pub_inputs` wires, then for each wire i in +/// [0..num_pub_inputs): +/// +/// local_wires[i] == public_inputs[i] +/// +/// If the circuit has more public inputs than the circuit config's `num_wires` you'll need multiple gates +#[derive(Debug)] +pub struct PublicInputGateV2 { + /// How many public inputs are enforced by this gate. + pub num_pub_inputs: usize, + /// start index from which we take the public input + pub index: usize, +} + +impl PublicInputGateV2 { + + /// careful with this fn, you must ensure `num_pub_inputs` <= the circuit config's `num_wires`. + pub fn new(num_pub_inputs: usize, index: usize) -> Self { + Self { + num_pub_inputs, + index + } + } +} + +impl, const D: usize> Gate for PublicInputGateV2 { + fn id(&self) -> String { + "PublicInputGateV2".into() + } + + fn short_id(&self) -> String { + "PublicInputGateV2".into() + } + + fn serialize( + &self, + dst: &mut Vec, + _common_data: &CommonCircuitData, + ) -> IoResult<()> { + dst.write_usize(self.num_pub_inputs)?; + dst.write_usize(self.index) + } + + fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData) -> IoResult { + let num_pub_inputs = src.read_usize()?; + let index = src.read_usize()?; + + Ok(Self { + num_pub_inputs, + index + }) + } + + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + // For each i in [0..num_pub_inputs], the constraint is: local_wires[i] - public_inputs[i] + // That must be 0 if the public input is correct. + (0..self.num_pub_inputs) + .map(|i| { + let wire_value = vars.local_wires[i]; + let pi_value = vars.public_inputs[self.index + i].into(); + wire_value - pi_value + }) + .collect() + } + + fn eval_unfiltered_base_one( + &self, + _vars: EvaluationVarsBase, + _yield_constr: StridedConstraintConsumer, + ) { + panic!("use eval_unfiltered_base_packed instead"); + } + + fn eval_unfiltered_base_batch(&self, vars_base: EvaluationVarsBaseBatch) -> Vec { + self.eval_unfiltered_base_batch_packed(vars_base) + } + + fn eval_unfiltered_circuit( + &self, + builder: &mut CircuitBuilder, + vars: EvaluationTargets, + ) -> Vec> { + todo!() + // // Circuit-level version of the same logic. + // (0..self.num_pub_inputs) + // .map(|i| { + // // local_wires[i] - public_inputs[i] + // let pi_part_ex = builder.convert_to_ext(vars.public_inputs[self.index + i]); + // builder.sub_extension(vars.local_wires[i], pi_part_ex) + // }) + // .collect() + } + + fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec> { + Vec::new() + } + + fn num_wires(&self) -> usize { + self.num_pub_inputs + } + + fn num_constants(&self) -> usize { + 0 + } + + fn degree(&self) -> usize { + 1 + } + + fn num_constraints(&self) -> usize { + self.num_pub_inputs + } +} + +impl, const D: usize> PackedEvaluableBase for PublicInputGateV2 { + fn eval_unfiltered_base_packed>( + &self, + vars: EvaluationVarsBasePacked

, + mut yield_constr: StridedConstraintConsumer

, + ) { + yield_constr.many( + (0..self.num_pub_inputs).map(|i| vars.local_wires[i] - vars.public_inputs[self.index + i]), + ); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::field::goldilocks_field::GoldilocksField; + use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; + use crate::gates::public_input_v2::PublicInputGateV2; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + + #[test] + fn pi_v2_low_degree() { + test_low_degree::(PublicInputGateV2{num_pub_inputs:4,index:0}) + } + + #[test] + fn pi_v2_eval_fns() -> anyhow::Result<()> { + todo!() + // const D: usize = 2; + // type C = PoseidonGoldilocksConfig; + // type F = >::F; + // test_eval_fns::(PublicInputGateV2{num_pub_inputs:4,index:0}) + } +} diff --git a/plonky2/src/gates/random_access.rs b/plonky2/src/gates/random_access.rs index f1748a6f..6587c01d 100644 --- a/plonky2/src/gates/random_access.rs +++ b/plonky2/src/gates/random_access.rs @@ -526,6 +526,7 @@ mod tests { &constants, ), public_inputs_hash: &HashOut::rand(), + public_inputs: &[F::rand()], }; let bad_claimed_elements = F::rand_vec(4); let bad_vars = EvaluationVars { @@ -538,6 +539,7 @@ mod tests { &constants, ), public_inputs_hash: &HashOut::rand(), + public_inputs: &[F::rand()], }; assert!( diff --git a/plonky2/src/gates/test_gate_constraints.rs b/plonky2/src/gates/test_gate_constraints.rs index 26b8e8a6..b8e27c40 100644 --- a/plonky2/src/gates/test_gate_constraints.rs +++ b/plonky2/src/gates/test_gate_constraints.rs @@ -36,6 +36,8 @@ pub fn test_gate_constraints, const D: usize>() { [ make_fext::(666) , make_fext::(77) ]; + + let pi = F::rand_vec(4); let input_hash = HashOut{ elements: [ F::from_canonical_u64(101) , F::from_canonical_u64(102) @@ -47,7 +49,8 @@ pub fn test_gate_constraints, const D: usize>() { let vars = EvaluationVars { local_constants: &loc_constants , local_wires: &loc_wires - , public_inputs_hash: &input_hash + , public_inputs_hash: &input_hash + , public_inputs: &pi }; let circuit_config: CircuitConfig = CircuitConfig::standard_recursion_config(); diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 290abaa9..8dbb9b9d 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -30,6 +30,7 @@ use crate::gates::lookup::{Lookup, LookupGate}; use crate::gates::lookup_table::LookupTable; use crate::gates::noop::NoopGate; use crate::gates::public_input::PublicInputGate; +use crate::gates::public_input_v2::PublicInputGateV2; use crate::gates::selectors::{selector_ends_lookups, selector_polynomials, selectors_lookup}; use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField}; use crate::hash::merkle_proofs::MerkleProofTarget; @@ -1061,8 +1062,9 @@ impl, const D: usize> CircuitBuilder { pub fn build_with_options>( self, commit_to_sigma: bool, + hash_public_input: bool, ) -> CircuitData { - let (circuit_data, success) = self.try_build_with_options(commit_to_sigma); + let (circuit_data, success) = self.try_build_with_options(commit_to_sigma, hash_public_input); if !success { panic!("Failed to build circuit"); } @@ -1072,6 +1074,7 @@ impl, const D: usize> CircuitBuilder { pub fn try_build_with_options>( mut self, commit_to_sigma: bool, + hash_public_input: bool, ) -> (CircuitData, bool) { let mut timing = TimingTree::new("preprocess", Level::Trace); @@ -1085,21 +1088,52 @@ impl, const D: usize> CircuitBuilder { // Hash the public inputs, and route them to a `PublicInputGate` which will enforce that // those hash wires match the claimed public inputs. let num_public_inputs = self.public_inputs.len(); - let public_inputs_hash = - self.hash_n_to_hash_no_pad::(self.public_inputs.clone()); - let pi_gate = self.add_gate(PublicInputGate, vec![]); - for (&hash_part, wire) in public_inputs_hash - .elements - .iter() - .zip(PublicInputGate::wires_public_inputs_hash()) - { - self.connect(hash_part, Target::wire(pi_gate, wire)) - } - // See - // however randomization makes debugging harder as runs are not deterministic - if self.config.randomize_unused_wires { - self.randomize_unused_pi_wires(pi_gate); + // only hash public input if flag is set + if hash_public_input { + let public_inputs_hash = + self.hash_n_to_hash_no_pad::(self.public_inputs.clone()); + let pi_gate = self.add_gate(PublicInputGate, vec![]); + for (&hash_part, wire) in public_inputs_hash + .elements + .iter() + .zip(PublicInputGate::wires_public_inputs_hash()) + { + self.connect(hash_part, Target::wire(pi_gate, wire)) + } + + // See + // however randomization makes debugging harder as runs are not deterministic + if self.config.randomize_unused_wires { + self.randomize_unused_pi_wires(pi_gate); + } + } else { + let num_of_routed_wires = self.config.num_routed_wires; + let num_of_pi_gates = num_public_inputs.div_ceil(num_of_routed_wires); + let mut pi_gates = vec![]; + let all_pi = self.public_inputs.clone(); + for i in 0..num_of_pi_gates{ + // start index + let start_i = i*num_of_routed_wires; + // make sure we do not go past the number of public inputs. + let end_i = core::cmp::min(start_i + num_of_routed_wires, num_public_inputs); + // the number of public inputs in this gate. + let num_pi_in_gate = end_i - start_i; + + let pi_gate = self.add_gate( + PublicInputGateV2::new(num_pi_in_gate, start_i) + , vec![]); + + // connect + let wire_range = 0..num_pi_in_gate; + for (&pi_part, wire) in all_pi[start_i .. end_i] + .iter() + .zip(wire_range) + { + self.connect(pi_part, Target::wire(pi_gate, wire)) + } + pi_gates.push(pi_gate); + } } // Place LUT-related gates. @@ -1329,11 +1363,17 @@ impl, const D: usize> CircuitBuilder { /// Builds a "full circuit", with both prover and verifier data. pub fn build>(self) -> CircuitData { - self.build_with_options(true) + self.build_with_options(true, true) + } + + /// Builds a "full circuit", with both prover and verifier data. + /// the public input are not hashed when calling this function + pub fn build_unhashed_pi>(self) -> CircuitData { + self.build_with_options(true, false) } pub fn mock_build>(self) -> MockCircuitData { - let circuit_data = self.build_with_options(false); + let circuit_data = self.build_with_options(false, true); MockCircuitData { prover_only: circuit_data.prover_only, common: circuit_data.common, diff --git a/plonky2/src/plonk/circuit_data.rs b/plonky2/src/plonk/circuit_data.rs index 52bc653c..d1f60541 100644 --- a/plonky2/src/plonk/circuit_data.rs +++ b/plonky2/src/plonk/circuit_data.rs @@ -46,8 +46,8 @@ use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::config::{GenericConfig, Hasher}; use crate::plonk::plonk_common::PlonkOracle; use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs}; -use crate::plonk::prover::{prove, prove_with_options, ProverOptions}; -use crate::plonk::verifier::{verify, verify_with_options, VerifierOptions}; +use crate::plonk::prover::{prove, prove_unhashed_pi, prove_with_options, ProverOptions}; +use crate::plonk::verifier::{verify, verify_with_options, VerifierOptions, verify_unhashed_pi}; use crate::util::serialization::{ Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, }; @@ -199,6 +199,15 @@ impl, C: GenericConfig, const D: usize> ) } + pub fn prove_unhashed_pi(&self, inputs: PartialWitness) -> Result> { + prove_unhashed_pi::( + &self.prover_only, + &self.common, + inputs, + &mut TimingTree::default(), + ) + } + pub fn prove_with_options(&self, inputs: PartialWitness, opts: &ProverOptions) -> Result> { prove_with_options::( &self.prover_only, @@ -213,6 +222,10 @@ impl, C: GenericConfig, const D: usize> verify::(proof_with_pis, &self.verifier_only, &self.common) } + pub fn verify_unhashed_pi(&self, proof_with_pis: ProofWithPublicInputs) -> Result<()> { + verify_unhashed_pi::(proof_with_pis, &self.verifier_only, &self.common) + } + pub fn verify_with_options(&self, proof_with_pis: ProofWithPublicInputs, verifier_options: &VerifierOptions) -> Result<()> { verify_with_options::(proof_with_pis, &self.verifier_only, &self.common, verifier_options) } @@ -302,6 +315,15 @@ impl, C: GenericConfig, const D: usize> buffer.read_prover_circuit_data(gate_serializer, generator_serializer) } + pub fn prove_unhashed_pi(&self, inputs: PartialWitness) -> Result> { + prove_unhashed_pi::( + &self.prover_only, + &self.common, + inputs, + &mut TimingTree::default(), + ) + } + pub fn prove(&self, inputs: PartialWitness) -> Result> { prove::( &self.prover_only, @@ -354,6 +376,10 @@ impl, C: GenericConfig, const D: usize> verify::(proof_with_pis, &self.verifier_only, &self.common) } + pub fn verify_unhashed_pi(&self, proof_with_pis: ProofWithPublicInputs) -> Result<()> { + verify_unhashed_pi::(proof_with_pis, &self.verifier_only, &self.common) + } + pub fn verify_compressed( &self, compressed_proof_with_pis: CompressedProofWithPublicInputs, diff --git a/plonky2/src/plonk/get_challenges.rs b/plonky2/src/plonk/get_challenges.rs index 5872cf69..5bc2333d 100644 --- a/plonky2/src/plonk/get_challenges.rs +++ b/plonky2/src/plonk/get_challenges.rs @@ -15,7 +15,7 @@ use crate::iop::challenger::{Challenger, RecursiveChallenger}; use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CommonCircuitData; -use crate::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; +use crate::plonk::config::{AlgebraicHasher, GenericConfig, Hasher, IntoGenericFieldVec}; use crate::plonk::proof::{ CompressedProof, CompressedProofWithPublicInputs, FriInferredElements, OpeningSet, OpeningSetTarget, Proof, ProofChallenges, ProofChallengesTarget, ProofTarget, @@ -24,6 +24,7 @@ use crate::plonk::proof::{ use crate::util::reverse_bits; fn get_challenges, C: GenericConfig, const D: usize>( + public_inputs: Option>, public_inputs_hash: <>::InnerHasher as Hasher>::Hash, wires_cap: &MerkleCap, plonk_zs_partial_products_cap: &MerkleCap, @@ -34,6 +35,7 @@ fn get_challenges, C: GenericConfig, cons pow_witness: F, circuit_digest: &<>::Hasher as Hasher>::Hash, common_data: &CommonCircuitData, + hash_public_input: bool, ) -> anyhow::Result> { let config = &common_data.config; let num_challenges = config.num_challenges; @@ -43,7 +45,12 @@ fn get_challenges, C: GenericConfig, cons // Observe the instance. challenger.observe_hash::(*circuit_digest); - challenger.observe_hash::(public_inputs_hash); + if hash_public_input { + challenger.observe_hash::(public_inputs_hash); + } else { + let pi_felts = public_inputs.unwrap().into_generic_field_vec(); + challenger.observe_elements(&pi_felts); + } challenger.observe_cap::(wires_cap); let plonk_betas = challenger.get_n_challenges(num_challenges); @@ -98,9 +105,11 @@ impl, C: GenericConfig, const D: usize> &self, circuit_digest: &<>::Hasher as Hasher>::Hash, common_data: &CommonCircuitData, + hash_public_input: bool, ) -> anyhow::Result> { + let pi = if hash_public_input { None } else {Some(self.public_inputs.clone())}; Ok(self - .get_challenges(self.get_public_inputs_hash(), circuit_digest, common_data)? + .get_challenges(pi, self.get_public_inputs_hash(), circuit_digest, common_data, hash_public_input)? .fri_challenges .fri_query_indices) } @@ -108,9 +117,11 @@ impl, C: GenericConfig, const D: usize> /// Computes all Fiat-Shamir challenges used in the Plonk proof. pub fn get_challenges( &self, + public_inputs: Option>, public_inputs_hash: <>::InnerHasher as Hasher>::Hash, circuit_digest: &<>::Hasher as Hasher>::Hash, common_data: &CommonCircuitData, + hash_public_input: bool, ) -> anyhow::Result> { let Proof { wires_cap, @@ -126,19 +137,25 @@ impl, C: GenericConfig, const D: usize> }, } = &self.proof; - get_challenges::( - public_inputs_hash, - wires_cap, - plonk_zs_partial_products_cap, - quotient_polys_cap, - openings, - commit_phase_merkle_caps, - final_poly, - *pow_witness, - circuit_digest, - common_data, - ) + let challenges = + get_challenges::( + public_inputs, + public_inputs_hash, + wires_cap, + plonk_zs_partial_products_cap, + quotient_polys_cap, + openings, + commit_phase_merkle_caps, + final_poly, + *pow_witness, + circuit_digest, + common_data, + hash_public_input + ); + + challenges } + } impl, C: GenericConfig, const D: usize> @@ -166,6 +183,7 @@ impl, C: GenericConfig, const D: usize> } = &self.proof; get_challenges::( + None, public_inputs_hash, wires_cap, plonk_zs_partial_products_cap, @@ -176,6 +194,7 @@ impl, C: GenericConfig, const D: usize> *pow_witness, circuit_digest, common_data, + true, ) } diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index 461f648f..519a4f07 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -93,7 +93,7 @@ impl, C: GenericConfig, const D: usize> circuit_digest: &<>::Hasher as Hasher>::Hash, common_data: &CommonCircuitData, ) -> anyhow::Result> { - let indices = self.fri_query_indices(circuit_digest, common_data)?; + let indices = self.fri_query_indices(circuit_digest, common_data, true)?; let compressed_proof = self.proof.compress(&indices, &common_data.fri_params); Ok(CompressedProofWithPublicInputs { public_inputs: self.public_inputs, @@ -224,6 +224,7 @@ impl, C: GenericConfig, const D: usize> .decompress(&challenges, fri_inferred_elements, &common_data.fri_params); verify_with_challenges::( decompressed_proof, + self.public_inputs, public_inputs_hash, challenges, verifier_data, diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index 85ed9394..feeff09f 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -22,7 +22,7 @@ use crate::fri::oracle::PolynomialBatch; use crate::gates::lookup::LookupGate; use crate::gates::lookup_table::LookupTableGate; use crate::gates::selectors::{LookupSelectors}; -use crate::hash::hash_types::RichField; +use crate::hash::hash_types::{HashOut, RichField}; use crate::hash::hashing::*; use crate::iop::challenger::Challenger; use crate::iop::generator::generate_partial_witness; @@ -123,11 +123,19 @@ pub fn set_lookup_wires< pub struct ProverOptions { pub export_witness: Option, // export the full witness into the given file pub print_hash_statistics: HashStatisticsPrintLevel, + pub hash_public_input: bool, } pub const DEFAULT_PROVER_OPTIONS: ProverOptions = ProverOptions { export_witness: None, print_hash_statistics: HashStatisticsPrintLevel::None, + hash_public_input: true, +}; + +pub const UNHASHED_PI_PROVER_OPTIONS: ProverOptions = ProverOptions { + export_witness: None, + print_hash_statistics: HashStatisticsPrintLevel::None, + hash_public_input: false, }; // things we want to export to be used by third party tooling @@ -201,6 +209,21 @@ fn collect_things_to_export, C: GenericConfig, C: GenericConfig, const D: usize>( + prover_data: &ProverOnlyCircuitData, + common_data: &CommonCircuitData, + inputs: PartialWitness, + timing: &mut TimingTree, +) -> Result> { + prove_with_options( + prover_data, + common_data, + inputs, + timing, + &UNHASHED_PI_PROVER_OPTIONS, + ) +} + pub fn prove, C: GenericConfig, const D: usize>( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, @@ -269,8 +292,13 @@ where set_lookup_wires(prover_data, common_data, &mut partition_witness)?; let public_inputs = partition_witness.get_targets(&prover_data.public_inputs); - let pi_felts: Vec> = public_inputs.clone().into_generic_field_vec(); - let public_inputs_hash = C::InnerHasher::hash_no_pad(&pi_felts); + let public_inputs_hash: <>::InnerHasher as Hasher>::Hash = + if prover_options.hash_public_input { + let pi_felts: Vec> = public_inputs.clone().into_generic_field_vec(); + C::InnerHasher::hash_no_pad(&pi_felts) + }else{ + HashOut::::default() + }; let witness = timed!( timing, @@ -320,7 +348,12 @@ where // Observe the instance. challenger.observe_hash::(prover_data.circuit_digest); - challenger.observe_hash::(public_inputs_hash); + if prover_options.hash_public_input { + challenger.observe_hash::(public_inputs_hash); + } else{ + let pi_felts = public_inputs.clone().into_generic_field_vec(); + challenger.observe_elements(&pi_felts); + } challenger.observe_cap::(&wires_commitment.merkle_tree.cap); @@ -397,6 +430,7 @@ where compute_quotient_polys::( common_data, prover_data, + &public_inputs, &public_inputs_hash, &wires_commitment, &partial_products_zs_and_lookup_commitment, @@ -757,6 +791,7 @@ fn compute_quotient_polys< >( common_data: &CommonCircuitData, prover_data: &'a ProverOnlyCircuitData, + public_inputs: &[F], public_inputs_hash: &<>::InnerHasher as Hasher>::Hash, wires_commitment: &'a PolynomialBatch, zs_partial_products_and_lookup_commitment: &'a PolynomialBatch, @@ -913,12 +948,14 @@ fn compute_quotient_polys< } } - let vars_batch = EvaluationVarsBaseBatch::new( - xs_batch.len(), - &local_constants_batch, - &local_wires_batch, - public_inputs_hash, - ); + let vars_batch = + EvaluationVarsBaseBatch::new( + xs_batch.len(), + &local_constants_batch, + &local_wires_batch, + &public_inputs_hash, + public_inputs, + ); let mut quotient_values_batch = eval_vanishing_poly_base_batch::( common_data, diff --git a/plonky2/src/plonk/vars.rs b/plonky2/src/plonk/vars.rs index d70cb2c3..d1e0f310 100644 --- a/plonky2/src/plonk/vars.rs +++ b/plonky2/src/plonk/vars.rs @@ -15,6 +15,7 @@ pub struct EvaluationVars<'a, F: RichField + Extendable, const D: usize> { pub local_constants: &'a [F::Extension], pub local_wires: &'a [F::Extension], pub public_inputs_hash: &'a HashOut, + pub public_inputs: &'a [F], } /// A batch of evaluation vars, in the base field. @@ -26,6 +27,7 @@ pub struct EvaluationVarsBaseBatch<'a, F: Field> { pub local_constants: &'a [F], pub local_wires: &'a [F], pub public_inputs_hash: &'a HashOut, + pub public_inputs: &'a [F], } /// A view into `EvaluationVarsBaseBatch` for a particular evaluation point. Does not copy the data. @@ -34,6 +36,7 @@ pub struct EvaluationVarsBase<'a, F: Field> { pub local_constants: PackedStridedView<'a, F>, pub local_wires: PackedStridedView<'a, F>, pub public_inputs_hash: &'a HashOut, + pub public_inputs: &'a [F], } /// Like `EvaluationVarsBase`, but packed. @@ -44,6 +47,7 @@ pub struct EvaluationVarsBasePacked<'a, P: PackedField> { pub local_constants: PackedStridedView<'a, P>, pub local_wires: PackedStridedView<'a, P>, pub public_inputs_hash: &'a HashOut, + pub public_inputs: &'a [P::Scalar], } impl, const D: usize> EvaluationVars<'_, F, D> { @@ -67,6 +71,7 @@ impl<'a, F: Field> EvaluationVarsBaseBatch<'a, F> { local_constants: &'a [F], local_wires: &'a [F], public_inputs_hash: &'a HashOut, + public_inputs: &'a [F], ) -> Self { assert_eq!(local_constants.len() % batch_size, 0); assert_eq!(local_wires.len() % batch_size, 0); @@ -75,6 +80,7 @@ impl<'a, F: Field> EvaluationVarsBaseBatch<'a, F> { local_constants, local_wires, public_inputs_hash, + public_inputs } } @@ -99,6 +105,7 @@ impl<'a, F: Field> EvaluationVarsBaseBatch<'a, F> { local_constants, local_wires, public_inputs_hash: self.public_inputs_hash, + public_inputs: self.public_inputs, } } @@ -196,6 +203,7 @@ impl<'a, P: PackedField> Iterator for EvaluationVarsBaseBatchIterPacked<'a, P> { local_constants, local_wires, public_inputs_hash: self.vars_batch.public_inputs_hash, + public_inputs: self.vars_batch.public_inputs, }; self.i += P::WIDTH; Some(res) diff --git a/plonky2/src/plonk/verifier.rs b/plonky2/src/plonk/verifier.rs index f1292bfb..b2d7df84 100644 --- a/plonky2/src/plonk/verifier.rs +++ b/plonky2/src/plonk/verifier.rs @@ -5,7 +5,7 @@ use anyhow::{ensure, Result}; use crate::field::extension::Extendable; use crate::field::types::Field; use crate::fri::verifier::verify_fri_proof; -use crate::hash::hash_types::RichField; +use crate::hash::hash_types::{HashOut, RichField}; use crate::hash::hashing::*; use crate::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData}; use crate::plonk::config::{GenericConfig, Hasher}; @@ -27,10 +27,17 @@ pub enum HashStatisticsPrintLevel { #[derive(Debug,Clone)] pub struct VerifierOptions { pub print_hash_statistics: HashStatisticsPrintLevel, + pub hash_public_input: bool, } pub const DEFAULT_VERIFIER_OPTIONS: VerifierOptions = VerifierOptions { print_hash_statistics: HashStatisticsPrintLevel::None, + hash_public_input: true, +}; + +pub const UNHASHED_PI_VERIFIER_OPTIONS: VerifierOptions = VerifierOptions { + print_hash_statistics: HashStatisticsPrintLevel::None, + hash_public_input: false, }; pub(crate) fn verify, C: GenericConfig, const D: usize>( @@ -45,6 +52,20 @@ pub(crate) fn verify, C: GenericConfig, c &DEFAULT_VERIFIER_OPTIONS, ) } + +pub(crate) fn verify_unhashed_pi, C: GenericConfig, const D: usize>( + proof_with_pis: ProofWithPublicInputs, + verifier_data: &VerifierOnlyCircuitData, + common_data: &CommonCircuitData, +) -> Result<()> { + verify_with_options( + proof_with_pis, + verifier_data, + common_data, + &UNHASHED_PI_VERIFIER_OPTIONS, + ) +} + pub(crate) fn verify_with_options, C: GenericConfig, const D: usize>( proof_with_pis: ProofWithPublicInputs, verifier_data: &VerifierOnlyCircuitData, @@ -56,16 +77,25 @@ pub(crate) fn verify_with_options, C: GenericConfig validate_proof_with_pis_shape(&proof_with_pis, common_data)?; - let public_inputs_hash = proof_with_pis.get_public_inputs_hash(); + let public_inputs_hash: HashOut = + if verifier_options.hash_public_input { + proof_with_pis.get_public_inputs_hash() + }else { + HashOut::::default() + }; if verifier_options.print_hash_statistics >= HashStatisticsPrintLevel::Info { print_hash_counters("after PI"); } + let pi = if verifier_options.hash_public_input { None } else {Some(proof_with_pis.public_inputs.clone())}; + let challenges = proof_with_pis.get_challenges( + pi, public_inputs_hash, &verifier_data.circuit_digest, common_data, + verifier_options.hash_public_input, )?; if verifier_options.print_hash_statistics >= HashStatisticsPrintLevel::Info { @@ -74,6 +104,7 @@ pub(crate) fn verify_with_options, C: GenericConfig let result = verify_with_challenges::( proof_with_pis.proof, + proof_with_pis.public_inputs, public_inputs_hash, challenges, verifier_data, @@ -93,6 +124,7 @@ pub(crate) fn verify_with_challenges< const D: usize, >( proof: Proof, + public_inputs: Vec, public_inputs_hash: <>::InnerHasher as Hasher>::Hash, challenges: ProofChallenges, verifier_data: &VerifierOnlyCircuitData, @@ -105,6 +137,7 @@ pub(crate) fn verify_with_challenges< local_constants, local_wires, public_inputs_hash: &public_inputs_hash, + public_inputs: &public_inputs, }; let local_zs = &proof.openings.plonk_zs; let next_zs = &proof.openings.plonk_zs_next;