add PublicInputGateV2 and refactor build, prove, and verify.

This commit is contained in:
M Alghazwi 2025-03-17 15:05:06 +01:00
parent bdfb86b46e
commit 96ce399bb5
No known key found for this signature in database
GPG Key ID: 646E567CAD7DB607
24 changed files with 687 additions and 49 deletions

View File

@ -97,6 +97,10 @@ harness = false
name = "reverse_index_bits" name = "reverse_index_bits"
harness = false harness = false
[[bench]]
name = "pi_gate_v2"
harness = false
# Display math equations properly in documentation # Display math equations properly in documentation
[package.metadata.docs.rs] [package.metadata.docs.rs]
rustdoc-args = ["--html-in-header", ".cargo/katex-header.html"] rustdoc-args = ["--html-in-header", ".cargo/katex-header.html"]

View File

@ -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<F: RichField + Extendable<D>, const D:usize, C: GenericConfig<D, F = F>,>(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::<F, D>::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::<C::Hasher>()));
// Benchmark the Circuit Building Phase
group.bench_function("Build Circuit", |b| {
b.iter(|| {
let config = CircuitConfig::standard_recursion_config();
let mut local_builder = CircuitBuilder::<F, D>::new(config);
for _ in 0..num_dummy_gates {
local_builder.add_gate(NoopGate, vec![]);
}
let _data = local_builder.build_unhashed_pi::<C>();
})
});
let data = builder.build_unhashed_pi::<C>();
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::<F,D,C1>(c, 12, 10).expect("failed");
// bench_circuit::<F,D,C2>(c, 12, 10).expect("failed");
//
// bench_circuit::<F,D,C1>(c, 12, 50).expect("failed");
// bench_circuit::<F,D,C2>(c, 12, 50).expect("failed");
//
// bench_circuit::<F,D,C1>(c, 12, 100).expect("failed");
// bench_circuit::<F,D,C2>(c, 12, 100).expect("failed");
// bench_circuit::<F,D,C1>(c, 12, 500).expect("failed");
// bench_circuit::<F,D,C2>(c, 12, 500).expect("failed");
bench_circuit::<F,D,C1>(c, 12, 1000).expect("failed");
bench_circuit::<F,D,C2>(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);

View File

@ -247,6 +247,7 @@ where
let prover_opts = ProverOptions { let prover_opts = ProverOptions {
export_witness: Some(format!("{}_witness.json",name)), export_witness: Some(format!("{}_witness.json",name)),
print_hash_statistics: HashStatisticsPrintLevel::Summary, // ::None, print_hash_statistics: HashStatisticsPrintLevel::Summary, // ::None,
hash_public_input: true,
}; };
let mut timing = TimingTree::new("prove", Level::Debug); let mut timing = TimingTree::new("prove", Level::Debug);
@ -265,6 +266,7 @@ where
let verifier_opts = VerifierOptions { let verifier_opts = VerifierOptions {
print_hash_statistics: HashStatisticsPrintLevel::Summary, print_hash_statistics: HashStatisticsPrintLevel::Summary,
hash_public_input: true,
}; };
data.verify_with_options(proof.clone(), &verifier_opts)?; data.verify_with_options(proof.clone(), &verifier_opts)?;

View File

@ -44,6 +44,7 @@ fn main() -> Result<()> {
let prover_opts = ProverOptions { let prover_opts = ProverOptions {
export_witness: Some(String::from("fibonacci_witness.json")), export_witness: Some(String::from("fibonacci_witness.json")),
print_hash_statistics: HashStatisticsPrintLevel::Info, print_hash_statistics: HashStatisticsPrintLevel::Info,
hash_public_input: true,
}; };
let proof = data.prove_with_options(pw, &prover_opts)?; let proof = data.prove_with_options(pw, &prover_opts)?;
@ -55,6 +56,7 @@ fn main() -> Result<()> {
let verifier_opts = VerifierOptions { let verifier_opts = VerifierOptions {
print_hash_statistics: HashStatisticsPrintLevel::Summary, print_hash_statistics: HashStatisticsPrintLevel::Summary,
hash_public_input: true,
}; };
data.verify_with_options(proof, &verifier_opts) data.verify_with_options(proof, &verifier_opts)

View File

@ -145,6 +145,7 @@ fn main() -> Result<()> {
let prover_opts = ProverOptions { let prover_opts = ProverOptions {
export_witness: Some(String::from("lookup_witness.json")), export_witness: Some(String::from("lookup_witness.json")),
print_hash_statistics: HashStatisticsPrintLevel::None, print_hash_statistics: HashStatisticsPrintLevel::None,
hash_public_input: true,
}; };
let proof = data.prove_with_options(pw, &prover_opts)?; let proof = data.prove_with_options(pw, &prover_opts)?;

View File

@ -89,6 +89,7 @@ fn main() -> Result<()> {
let prover_opts = ProverOptions { let prover_opts = ProverOptions {
export_witness: Some(String::from("multi_lookup_witness.json")), export_witness: Some(String::from("multi_lookup_witness.json")),
print_hash_statistics: HashStatisticsPrintLevel::None, print_hash_statistics: HashStatisticsPrintLevel::None,
hash_public_input: true,
}; };
let proof = data.prove_with_options(pw, &prover_opts)?; let proof = data.prove_with_options(pw, &prover_opts)?;

View File

@ -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::<F, D>::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::<C1>();
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(())
}

View File

@ -43,6 +43,7 @@ fn main() -> Result<()> {
let verifier_opts = VerifierOptions { let verifier_opts = VerifierOptions {
print_hash_statistics: HashStatisticsPrintLevel::Summary, print_hash_statistics: HashStatisticsPrintLevel::Summary,
hash_public_input: true,
}; };
assert!(data.verify_with_options(proof, &verifier_opts).is_ok()); assert!(data.verify_with_options(proof, &verifier_opts).is_ok());

View File

@ -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::<F, D>::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::<C1>();
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::<F, D>::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::<C1>(&proof_t, &verifier_data_t, &data.common);
let mut t_list: Vec<Target> = 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::<C1>();
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(())
}

View File

@ -864,6 +864,7 @@ mod tests {
local_constants: &[], local_constants: &[],
local_wires: &get_wires(shift, values, eval_point), local_wires: &get_wires(shift, values, eval_point),
public_inputs_hash: &HashOut::rand(), public_inputs_hash: &HashOut::rand(),
public_inputs: &[F::rand()],
}; };
assert!( assert!(

View File

@ -430,6 +430,7 @@ mod tests {
local_constants: &[], local_constants: &[],
local_wires: &get_wires(base, power as u64), local_wires: &get_wires(base, power as u64),
public_inputs_hash: &HashOut::rand(), public_inputs_hash: &HashOut::rand(),
public_inputs: &[F::rand()],
}; };
assert!( assert!(
gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()), gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()),

View File

@ -99,10 +99,12 @@ pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + S
.map(|w| F::Extension::from_basefield(*w)) .map(|w| F::Extension::from_basefield(*w))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let public_inputs_hash = &vars_base.public_inputs_hash; let public_inputs_hash = &vars_base.public_inputs_hash;
let public_inputs = &vars_base.public_inputs;
let vars = EvaluationVars { let vars = EvaluationVars {
local_constants, local_constants,
local_wires, local_wires,
public_inputs_hash, public_inputs_hash,
public_inputs,
}; };
let values = self.eval_unfiltered(vars); let values = self.eval_unfiltered(vars);

View File

@ -28,6 +28,7 @@ pub fn test_low_degree<F: RichField + Extendable<D>, G: Gate<F, D>, const D: usi
let constant_ldes = random_low_degree_matrix::<F::Extension>(gate.num_constants(), rate_bits); let constant_ldes = random_low_degree_matrix::<F::Extension>(gate.num_constants(), rate_bits);
assert_eq!(wire_ldes.len(), constant_ldes.len()); assert_eq!(wire_ldes.len(), constant_ldes.len());
let public_inputs_hash = &HashOut::rand(); let public_inputs_hash = &HashOut::rand();
let pi_rand = F::rand_vec(4);
let constraint_evals = wire_ldes let constraint_evals = wire_ldes
.iter() .iter()
@ -36,6 +37,7 @@ pub fn test_low_degree<F: RichField + Extendable<D>, G: Gate<F, D>, const D: usi
local_constants, local_constants,
local_wires, local_wires,
public_inputs_hash, public_inputs_hash,
public_inputs: &pi_rand,
}) })
.map(|vars| gate.eval_unfiltered(vars)) .map(|vars| gate.eval_unfiltered(vars))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -107,13 +109,16 @@ pub fn test_eval_fns<
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let public_inputs_hash = HashOut::rand(); let public_inputs_hash = HashOut::rand();
let rand_pi = F::rand_vec(4);
// Batch of 1. // Batch of 1.
let vars_base_batch = 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 { let vars = EvaluationVars {
local_constants: &constants, local_constants: &constants,
local_wires: &wires, local_wires: &wires,
public_inputs_hash: &public_inputs_hash, public_inputs_hash: &public_inputs_hash,
public_inputs: &rand_pi
}; };
let evals_base = gate.eval_unfiltered_base_batch(vars_base_batch); let evals_base = gate.eval_unfiltered_base_batch(vars_base_batch);
@ -146,6 +151,7 @@ pub fn test_eval_fns<
local_constants: &constants, local_constants: &constants,
local_wires: &wires, local_wires: &wires,
public_inputs_hash: &public_inputs_hash, public_inputs_hash: &public_inputs_hash,
public_inputs: &rand_pi,
}; };
let evals = gate.eval_unfiltered(vars); let evals = gate.eval_unfiltered(vars);

View File

@ -38,6 +38,7 @@ pub mod packed_util;
pub mod poseidon; pub mod poseidon;
pub mod poseidon_mds; pub mod poseidon_mds;
pub mod public_input; pub mod public_input;
pub mod public_input_v2;
pub mod random_access; pub mod random_access;
pub mod reducing; pub mod reducing;
pub mod reducing_extension; pub mod reducing_extension;

View File

@ -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<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PublicInputGateV2 {
fn id(&self) -> String {
"PublicInputGateV2".into()
}
fn short_id(&self) -> String {
"PublicInputGateV2".into()
}
fn serialize(
&self,
dst: &mut Vec<u8>,
_common_data: &CommonCircuitData<F, D>,
) -> IoResult<()> {
dst.write_usize(self.num_pub_inputs)?;
dst.write_usize(self.index)
}
fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData<F, D>) -> IoResult<Self> {
let num_pub_inputs = src.read_usize()?;
let index = src.read_usize()?;
Ok(Self {
num_pub_inputs,
index
})
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
// 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<F>,
_yield_constr: StridedConstraintConsumer<F>,
) {
panic!("use eval_unfiltered_base_packed instead");
}
fn eval_unfiltered_base_batch(&self, vars_base: EvaluationVarsBaseBatch<F>) -> Vec<F> {
self.eval_unfiltered_base_batch_packed(vars_base)
}
fn eval_unfiltered_circuit(
&self,
builder: &mut CircuitBuilder<F, D>,
vars: EvaluationTargets<D>,
) -> Vec<ExtensionTarget<D>> {
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<WitnessGeneratorRef<F, D>> {
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<F: RichField + Extendable<D>, const D: usize> PackedEvaluableBase<F, D> for PublicInputGateV2 {
fn eval_unfiltered_base_packed<P: PackedField<Scalar = F>>(
&self,
vars: EvaluationVarsBasePacked<P>,
mut yield_constr: StridedConstraintConsumer<P>,
) {
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::<GoldilocksField, _, 4>(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 = <C as GenericConfig<D>>::F;
// test_eval_fns::<F, C, _, D>(PublicInputGateV2{num_pub_inputs:4,index:0})
}
}

View File

@ -526,6 +526,7 @@ mod tests {
&constants, &constants,
), ),
public_inputs_hash: &HashOut::rand(), public_inputs_hash: &HashOut::rand(),
public_inputs: &[F::rand()],
}; };
let bad_claimed_elements = F::rand_vec(4); let bad_claimed_elements = F::rand_vec(4);
let bad_vars = EvaluationVars { let bad_vars = EvaluationVars {
@ -538,6 +539,7 @@ mod tests {
&constants, &constants,
), ),
public_inputs_hash: &HashOut::rand(), public_inputs_hash: &HashOut::rand(),
public_inputs: &[F::rand()],
}; };
assert!( assert!(

View File

@ -36,6 +36,8 @@ pub fn test_gate_constraints<F: RichField + Extendable<D>, const D: usize>() {
[ make_fext::<F,D>(666) [ make_fext::<F,D>(666)
, make_fext::<F,D>(77) , make_fext::<F,D>(77)
]; ];
let pi = F::rand_vec(4);
let input_hash = HashOut{ elements: let input_hash = HashOut{ elements:
[ F::from_canonical_u64(101) [ F::from_canonical_u64(101)
, F::from_canonical_u64(102) , F::from_canonical_u64(102)
@ -47,7 +49,8 @@ pub fn test_gate_constraints<F: RichField + Extendable<D>, const D: usize>() {
let vars = EvaluationVars let vars = EvaluationVars
{ local_constants: &loc_constants { local_constants: &loc_constants
, local_wires: &loc_wires , 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(); let circuit_config: CircuitConfig = CircuitConfig::standard_recursion_config();

View File

@ -30,6 +30,7 @@ use crate::gates::lookup::{Lookup, LookupGate};
use crate::gates::lookup_table::LookupTable; use crate::gates::lookup_table::LookupTable;
use crate::gates::noop::NoopGate; use crate::gates::noop::NoopGate;
use crate::gates::public_input::PublicInputGate; 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::gates::selectors::{selector_ends_lookups, selector_polynomials, selectors_lookup};
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField}; use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
use crate::hash::merkle_proofs::MerkleProofTarget; use crate::hash::merkle_proofs::MerkleProofTarget;
@ -1061,8 +1062,9 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn build_with_options<C: GenericConfig<D, F = F>>( pub fn build_with_options<C: GenericConfig<D, F = F>>(
self, self,
commit_to_sigma: bool, commit_to_sigma: bool,
hash_public_input: bool,
) -> CircuitData<F, C, D> { ) -> CircuitData<F, C, D> {
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 { if !success {
panic!("Failed to build circuit"); panic!("Failed to build circuit");
} }
@ -1072,6 +1074,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn try_build_with_options<C: GenericConfig<D, F = F>>( pub fn try_build_with_options<C: GenericConfig<D, F = F>>(
mut self, mut self,
commit_to_sigma: bool, commit_to_sigma: bool,
hash_public_input: bool,
) -> (CircuitData<F, C, D>, bool) { ) -> (CircuitData<F, C, D>, bool) {
let mut timing = TimingTree::new("preprocess", Level::Trace); let mut timing = TimingTree::new("preprocess", Level::Trace);
@ -1085,21 +1088,52 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
// Hash the public inputs, and route them to a `PublicInputGate` which will enforce that // Hash the public inputs, and route them to a `PublicInputGate` which will enforce that
// those hash wires match the claimed public inputs. // those hash wires match the claimed public inputs.
let num_public_inputs = self.public_inputs.len(); let num_public_inputs = self.public_inputs.len();
let public_inputs_hash =
self.hash_n_to_hash_no_pad::<C::InnerHasher>(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 <https://github.com/0xPolygonZero/plonky2/issues/456> // only hash public input if flag is set
// however randomization makes debugging harder as runs are not deterministic if hash_public_input {
if self.config.randomize_unused_wires { let public_inputs_hash =
self.randomize_unused_pi_wires(pi_gate); self.hash_n_to_hash_no_pad::<C::InnerHasher>(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 <https://github.com/0xPolygonZero/plonky2/issues/456>
// 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. // Place LUT-related gates.
@ -1329,11 +1363,17 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Builds a "full circuit", with both prover and verifier data. /// Builds a "full circuit", with both prover and verifier data.
pub fn build<C: GenericConfig<D, F = F>>(self) -> CircuitData<F, C, D> { pub fn build<C: GenericConfig<D, F = F>>(self) -> CircuitData<F, C, D> {
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<C: GenericConfig<D, F = F>>(self) -> CircuitData<F, C, D> {
self.build_with_options(true, false)
} }
pub fn mock_build<C: GenericConfig<D, F = F>>(self) -> MockCircuitData<F, C, D> { pub fn mock_build<C: GenericConfig<D, F = F>>(self) -> MockCircuitData<F, C, D> {
let circuit_data = self.build_with_options(false); let circuit_data = self.build_with_options(false, true);
MockCircuitData { MockCircuitData {
prover_only: circuit_data.prover_only, prover_only: circuit_data.prover_only,
common: circuit_data.common, common: circuit_data.common,

View File

@ -46,8 +46,8 @@ use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::config::{GenericConfig, Hasher}; use crate::plonk::config::{GenericConfig, Hasher};
use crate::plonk::plonk_common::PlonkOracle; use crate::plonk::plonk_common::PlonkOracle;
use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs}; use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs};
use crate::plonk::prover::{prove, prove_with_options, ProverOptions}; use crate::plonk::prover::{prove, prove_unhashed_pi, prove_with_options, ProverOptions};
use crate::plonk::verifier::{verify, verify_with_options, VerifierOptions}; use crate::plonk::verifier::{verify, verify_with_options, VerifierOptions, verify_unhashed_pi};
use crate::util::serialization::{ use crate::util::serialization::{
Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write,
}; };
@ -199,6 +199,15 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
) )
} }
pub fn prove_unhashed_pi(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>> {
prove_unhashed_pi::<F, C, D>(
&self.prover_only,
&self.common,
inputs,
&mut TimingTree::default(),
)
}
pub fn prove_with_options(&self, inputs: PartialWitness<F>, opts: &ProverOptions) -> Result<ProofWithPublicInputs<F, C, D>> { pub fn prove_with_options(&self, inputs: PartialWitness<F>, opts: &ProverOptions) -> Result<ProofWithPublicInputs<F, C, D>> {
prove_with_options::<F, C, D>( prove_with_options::<F, C, D>(
&self.prover_only, &self.prover_only,
@ -213,6 +222,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
verify::<F, C, D>(proof_with_pis, &self.verifier_only, &self.common) verify::<F, C, D>(proof_with_pis, &self.verifier_only, &self.common)
} }
pub fn verify_unhashed_pi(&self, proof_with_pis: ProofWithPublicInputs<F, C, D>) -> Result<()> {
verify_unhashed_pi::<F, C, D>(proof_with_pis, &self.verifier_only, &self.common)
}
pub fn verify_with_options(&self, proof_with_pis: ProofWithPublicInputs<F, C, D>, verifier_options: &VerifierOptions) -> Result<()> { pub fn verify_with_options(&self, proof_with_pis: ProofWithPublicInputs<F, C, D>, verifier_options: &VerifierOptions) -> Result<()> {
verify_with_options::<F, C, D>(proof_with_pis, &self.verifier_only, &self.common, verifier_options) verify_with_options::<F, C, D>(proof_with_pis, &self.verifier_only, &self.common, verifier_options)
} }
@ -302,6 +315,15 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
buffer.read_prover_circuit_data(gate_serializer, generator_serializer) buffer.read_prover_circuit_data(gate_serializer, generator_serializer)
} }
pub fn prove_unhashed_pi(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>> {
prove_unhashed_pi::<F, C, D>(
&self.prover_only,
&self.common,
inputs,
&mut TimingTree::default(),
)
}
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>> { pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>> {
prove::<F, C, D>( prove::<F, C, D>(
&self.prover_only, &self.prover_only,
@ -354,6 +376,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
verify::<F, C, D>(proof_with_pis, &self.verifier_only, &self.common) verify::<F, C, D>(proof_with_pis, &self.verifier_only, &self.common)
} }
pub fn verify_unhashed_pi(&self, proof_with_pis: ProofWithPublicInputs<F, C, D>) -> Result<()> {
verify_unhashed_pi::<F, C, D>(proof_with_pis, &self.verifier_only, &self.common)
}
pub fn verify_compressed( pub fn verify_compressed(
&self, &self,
compressed_proof_with_pis: CompressedProofWithPublicInputs<F, C, D>, compressed_proof_with_pis: CompressedProofWithPublicInputs<F, C, D>,

View File

@ -15,7 +15,7 @@ use crate::iop::challenger::{Challenger, RecursiveChallenger};
use crate::iop::target::Target; use crate::iop::target::Target;
use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CommonCircuitData; 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::{ use crate::plonk::proof::{
CompressedProof, CompressedProofWithPublicInputs, FriInferredElements, OpeningSet, CompressedProof, CompressedProofWithPublicInputs, FriInferredElements, OpeningSet,
OpeningSetTarget, Proof, ProofChallenges, ProofChallengesTarget, ProofTarget, OpeningSetTarget, Proof, ProofChallenges, ProofChallengesTarget, ProofTarget,
@ -24,6 +24,7 @@ use crate::plonk::proof::{
use crate::util::reverse_bits; use crate::util::reverse_bits;
fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>( fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
public_inputs: Option<Vec<F>>,
public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash, public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
wires_cap: &MerkleCap<F, C::Hasher>, wires_cap: &MerkleCap<F, C::Hasher>,
plonk_zs_partial_products_cap: &MerkleCap<F, C::Hasher>, plonk_zs_partial_products_cap: &MerkleCap<F, C::Hasher>,
@ -34,6 +35,7 @@ fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, cons
pow_witness: F, pow_witness: F,
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash, circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
common_data: &CommonCircuitData<F, D>, common_data: &CommonCircuitData<F, D>,
hash_public_input: bool,
) -> anyhow::Result<ProofChallenges<F, D>> { ) -> anyhow::Result<ProofChallenges<F, D>> {
let config = &common_data.config; let config = &common_data.config;
let num_challenges = config.num_challenges; let num_challenges = config.num_challenges;
@ -43,7 +45,12 @@ fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, cons
// Observe the instance. // Observe the instance.
challenger.observe_hash::<C::Hasher>(*circuit_digest); challenger.observe_hash::<C::Hasher>(*circuit_digest);
challenger.observe_hash::<C::InnerHasher>(public_inputs_hash); if hash_public_input {
challenger.observe_hash::<C::InnerHasher>(public_inputs_hash);
} else {
let pi_felts = public_inputs.unwrap().into_generic_field_vec();
challenger.observe_elements(&pi_felts);
}
challenger.observe_cap::<C::Hasher>(wires_cap); challenger.observe_cap::<C::Hasher>(wires_cap);
let plonk_betas = challenger.get_n_challenges(num_challenges); let plonk_betas = challenger.get_n_challenges(num_challenges);
@ -98,9 +105,11 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
&self, &self,
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash, circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
common_data: &CommonCircuitData<F, D>, common_data: &CommonCircuitData<F, D>,
hash_public_input: bool,
) -> anyhow::Result<Vec<usize>> { ) -> anyhow::Result<Vec<usize>> {
let pi = if hash_public_input { None } else {Some(self.public_inputs.clone())};
Ok(self 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_challenges
.fri_query_indices) .fri_query_indices)
} }
@ -108,9 +117,11 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
/// Computes all Fiat-Shamir challenges used in the Plonk proof. /// Computes all Fiat-Shamir challenges used in the Plonk proof.
pub fn get_challenges( pub fn get_challenges(
&self, &self,
public_inputs: Option<Vec<F>>,
public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash, public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash, circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
common_data: &CommonCircuitData<F, D>, common_data: &CommonCircuitData<F, D>,
hash_public_input: bool,
) -> anyhow::Result<ProofChallenges<F, D>> { ) -> anyhow::Result<ProofChallenges<F, D>> {
let Proof { let Proof {
wires_cap, wires_cap,
@ -126,19 +137,25 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
}, },
} = &self.proof; } = &self.proof;
get_challenges::<F, C, D>( let challenges =
public_inputs_hash, get_challenges::<F, C, D>(
wires_cap, public_inputs,
plonk_zs_partial_products_cap, public_inputs_hash,
quotient_polys_cap, wires_cap,
openings, plonk_zs_partial_products_cap,
commit_phase_merkle_caps, quotient_polys_cap,
final_poly, openings,
*pow_witness, commit_phase_merkle_caps,
circuit_digest, final_poly,
common_data, *pow_witness,
) circuit_digest,
common_data,
hash_public_input
);
challenges
} }
} }
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
@ -166,6 +183,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
} = &self.proof; } = &self.proof;
get_challenges::<F, C, D>( get_challenges::<F, C, D>(
None,
public_inputs_hash, public_inputs_hash,
wires_cap, wires_cap,
plonk_zs_partial_products_cap, plonk_zs_partial_products_cap,
@ -176,6 +194,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
*pow_witness, *pow_witness,
circuit_digest, circuit_digest,
common_data, common_data,
true,
) )
} }

View File

@ -93,7 +93,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash, circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
common_data: &CommonCircuitData<F, D>, common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<CompressedProofWithPublicInputs<F, C, D>> { ) -> anyhow::Result<CompressedProofWithPublicInputs<F, C, D>> {
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); let compressed_proof = self.proof.compress(&indices, &common_data.fri_params);
Ok(CompressedProofWithPublicInputs { Ok(CompressedProofWithPublicInputs {
public_inputs: self.public_inputs, public_inputs: self.public_inputs,
@ -224,6 +224,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
.decompress(&challenges, fri_inferred_elements, &common_data.fri_params); .decompress(&challenges, fri_inferred_elements, &common_data.fri_params);
verify_with_challenges::<F, C, D>( verify_with_challenges::<F, C, D>(
decompressed_proof, decompressed_proof,
self.public_inputs,
public_inputs_hash, public_inputs_hash,
challenges, challenges,
verifier_data, verifier_data,

View File

@ -22,7 +22,7 @@ use crate::fri::oracle::PolynomialBatch;
use crate::gates::lookup::LookupGate; use crate::gates::lookup::LookupGate;
use crate::gates::lookup_table::LookupTableGate; use crate::gates::lookup_table::LookupTableGate;
use crate::gates::selectors::{LookupSelectors}; use crate::gates::selectors::{LookupSelectors};
use crate::hash::hash_types::RichField; use crate::hash::hash_types::{HashOut, RichField};
use crate::hash::hashing::*; use crate::hash::hashing::*;
use crate::iop::challenger::Challenger; use crate::iop::challenger::Challenger;
use crate::iop::generator::generate_partial_witness; use crate::iop::generator::generate_partial_witness;
@ -123,11 +123,19 @@ pub fn set_lookup_wires<
pub struct ProverOptions { pub struct ProverOptions {
pub export_witness: Option<String>, // export the full witness into the given file pub export_witness: Option<String>, // export the full witness into the given file
pub print_hash_statistics: HashStatisticsPrintLevel, pub print_hash_statistics: HashStatisticsPrintLevel,
pub hash_public_input: bool,
} }
pub const DEFAULT_PROVER_OPTIONS: ProverOptions = ProverOptions { pub const DEFAULT_PROVER_OPTIONS: ProverOptions = ProverOptions {
export_witness: None, export_witness: None,
print_hash_statistics: HashStatisticsPrintLevel::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 // things we want to export to be used by third party tooling
@ -201,6 +209,21 @@ fn collect_things_to_export<F: RichField + Extendable<D>, C: GenericConfig<D, F
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
pub fn prove_unhashed_pi<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
prover_data: &ProverOnlyCircuitData<F, C, D>,
common_data: &CommonCircuitData<F, D>,
inputs: PartialWitness<F>,
timing: &mut TimingTree,
) -> Result<ProofWithPublicInputs<F, C, D>> {
prove_with_options(
prover_data,
common_data,
inputs,
timing,
&UNHASHED_PI_PROVER_OPTIONS,
)
}
pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>( pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
prover_data: &ProverOnlyCircuitData<F, C, D>, prover_data: &ProverOnlyCircuitData<F, C, D>,
common_data: &CommonCircuitData<F, D>, common_data: &CommonCircuitData<F, D>,
@ -269,8 +292,13 @@ where
set_lookup_wires(prover_data, common_data, &mut partition_witness)?; set_lookup_wires(prover_data, common_data, &mut partition_witness)?;
let public_inputs = partition_witness.get_targets(&prover_data.public_inputs); let public_inputs = partition_witness.get_targets(&prover_data.public_inputs);
let pi_felts: Vec<GenericField<F>> = public_inputs.clone().into_generic_field_vec(); let public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash =
let public_inputs_hash = C::InnerHasher::hash_no_pad(&pi_felts); if prover_options.hash_public_input {
let pi_felts: Vec<GenericField<F>> = public_inputs.clone().into_generic_field_vec();
C::InnerHasher::hash_no_pad(&pi_felts)
}else{
HashOut::<F>::default()
};
let witness = timed!( let witness = timed!(
timing, timing,
@ -320,7 +348,12 @@ where
// Observe the instance. // Observe the instance.
challenger.observe_hash::<C::Hasher>(prover_data.circuit_digest); challenger.observe_hash::<C::Hasher>(prover_data.circuit_digest);
challenger.observe_hash::<C::InnerHasher>(public_inputs_hash); if prover_options.hash_public_input {
challenger.observe_hash::<C::InnerHasher>(public_inputs_hash);
} else{
let pi_felts = public_inputs.clone().into_generic_field_vec();
challenger.observe_elements(&pi_felts);
}
challenger.observe_cap::<C::Hasher>(&wires_commitment.merkle_tree.cap); challenger.observe_cap::<C::Hasher>(&wires_commitment.merkle_tree.cap);
@ -397,6 +430,7 @@ where
compute_quotient_polys::<F, C, D>( compute_quotient_polys::<F, C, D>(
common_data, common_data,
prover_data, prover_data,
&public_inputs,
&public_inputs_hash, &public_inputs_hash,
&wires_commitment, &wires_commitment,
&partial_products_zs_and_lookup_commitment, &partial_products_zs_and_lookup_commitment,
@ -757,6 +791,7 @@ fn compute_quotient_polys<
>( >(
common_data: &CommonCircuitData<F, D>, common_data: &CommonCircuitData<F, D>,
prover_data: &'a ProverOnlyCircuitData<F, C, D>, prover_data: &'a ProverOnlyCircuitData<F, C, D>,
public_inputs: &[F],
public_inputs_hash: &<<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash, public_inputs_hash: &<<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
wires_commitment: &'a PolynomialBatch<F, C, D>, wires_commitment: &'a PolynomialBatch<F, C, D>,
zs_partial_products_and_lookup_commitment: &'a PolynomialBatch<F, C, D>, zs_partial_products_and_lookup_commitment: &'a PolynomialBatch<F, C, D>,
@ -913,12 +948,14 @@ fn compute_quotient_polys<
} }
} }
let vars_batch = EvaluationVarsBaseBatch::new( let vars_batch =
xs_batch.len(), EvaluationVarsBaseBatch::new(
&local_constants_batch, xs_batch.len(),
&local_wires_batch, &local_constants_batch,
public_inputs_hash, &local_wires_batch,
); &public_inputs_hash,
public_inputs,
);
let mut quotient_values_batch = eval_vanishing_poly_base_batch::<F, D>( let mut quotient_values_batch = eval_vanishing_poly_base_batch::<F, D>(
common_data, common_data,

View File

@ -15,6 +15,7 @@ pub struct EvaluationVars<'a, F: RichField + Extendable<D>, const D: usize> {
pub local_constants: &'a [F::Extension], pub local_constants: &'a [F::Extension],
pub local_wires: &'a [F::Extension], pub local_wires: &'a [F::Extension],
pub public_inputs_hash: &'a HashOut<F>, pub public_inputs_hash: &'a HashOut<F>,
pub public_inputs: &'a [F],
} }
/// A batch of evaluation vars, in the base field. /// 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_constants: &'a [F],
pub local_wires: &'a [F], pub local_wires: &'a [F],
pub public_inputs_hash: &'a HashOut<F>, pub public_inputs_hash: &'a HashOut<F>,
pub public_inputs: &'a [F],
} }
/// A view into `EvaluationVarsBaseBatch` for a particular evaluation point. Does not copy the data. /// 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_constants: PackedStridedView<'a, F>,
pub local_wires: PackedStridedView<'a, F>, pub local_wires: PackedStridedView<'a, F>,
pub public_inputs_hash: &'a HashOut<F>, pub public_inputs_hash: &'a HashOut<F>,
pub public_inputs: &'a [F],
} }
/// Like `EvaluationVarsBase`, but packed. /// Like `EvaluationVarsBase`, but packed.
@ -44,6 +47,7 @@ pub struct EvaluationVarsBasePacked<'a, P: PackedField> {
pub local_constants: PackedStridedView<'a, P>, pub local_constants: PackedStridedView<'a, P>,
pub local_wires: PackedStridedView<'a, P>, pub local_wires: PackedStridedView<'a, P>,
pub public_inputs_hash: &'a HashOut<P::Scalar>, pub public_inputs_hash: &'a HashOut<P::Scalar>,
pub public_inputs: &'a [P::Scalar],
} }
impl<F: RichField + Extendable<D>, const D: usize> EvaluationVars<'_, F, D> { impl<F: RichField + Extendable<D>, const D: usize> EvaluationVars<'_, F, D> {
@ -67,6 +71,7 @@ impl<'a, F: Field> EvaluationVarsBaseBatch<'a, F> {
local_constants: &'a [F], local_constants: &'a [F],
local_wires: &'a [F], local_wires: &'a [F],
public_inputs_hash: &'a HashOut<F>, public_inputs_hash: &'a HashOut<F>,
public_inputs: &'a [F],
) -> Self { ) -> Self {
assert_eq!(local_constants.len() % batch_size, 0); assert_eq!(local_constants.len() % batch_size, 0);
assert_eq!(local_wires.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_constants,
local_wires, local_wires,
public_inputs_hash, public_inputs_hash,
public_inputs
} }
} }
@ -99,6 +105,7 @@ impl<'a, F: Field> EvaluationVarsBaseBatch<'a, F> {
local_constants, local_constants,
local_wires, local_wires,
public_inputs_hash: self.public_inputs_hash, 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_constants,
local_wires, local_wires,
public_inputs_hash: self.vars_batch.public_inputs_hash, public_inputs_hash: self.vars_batch.public_inputs_hash,
public_inputs: self.vars_batch.public_inputs,
}; };
self.i += P::WIDTH; self.i += P::WIDTH;
Some(res) Some(res)

View File

@ -5,7 +5,7 @@ use anyhow::{ensure, Result};
use crate::field::extension::Extendable; use crate::field::extension::Extendable;
use crate::field::types::Field; use crate::field::types::Field;
use crate::fri::verifier::verify_fri_proof; 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::hash::hashing::*;
use crate::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData}; use crate::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData};
use crate::plonk::config::{GenericConfig, Hasher}; use crate::plonk::config::{GenericConfig, Hasher};
@ -27,10 +27,17 @@ pub enum HashStatisticsPrintLevel {
#[derive(Debug,Clone)] #[derive(Debug,Clone)]
pub struct VerifierOptions { pub struct VerifierOptions {
pub print_hash_statistics: HashStatisticsPrintLevel, pub print_hash_statistics: HashStatisticsPrintLevel,
pub hash_public_input: bool,
} }
pub const DEFAULT_VERIFIER_OPTIONS: VerifierOptions = VerifierOptions { pub const DEFAULT_VERIFIER_OPTIONS: VerifierOptions = VerifierOptions {
print_hash_statistics: HashStatisticsPrintLevel::None, 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<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>( pub(crate) fn verify<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
@ -45,6 +52,20 @@ pub(crate) fn verify<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, c
&DEFAULT_VERIFIER_OPTIONS, &DEFAULT_VERIFIER_OPTIONS,
) )
} }
pub(crate) fn verify_unhashed_pi<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
proof_with_pis: ProofWithPublicInputs<F, C, D>,
verifier_data: &VerifierOnlyCircuitData<C, D>,
common_data: &CommonCircuitData<F, D>,
) -> Result<()> {
verify_with_options(
proof_with_pis,
verifier_data,
common_data,
&UNHASHED_PI_VERIFIER_OPTIONS,
)
}
pub(crate) fn verify_with_options<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>( pub(crate) fn verify_with_options<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
proof_with_pis: ProofWithPublicInputs<F, C, D>, proof_with_pis: ProofWithPublicInputs<F, C, D>,
verifier_data: &VerifierOnlyCircuitData<C, D>, verifier_data: &VerifierOnlyCircuitData<C, D>,
@ -56,16 +77,25 @@ pub(crate) fn verify_with_options<F: RichField + Extendable<D>, C: GenericConfig
validate_proof_with_pis_shape(&proof_with_pis, common_data)?; 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<F> =
if verifier_options.hash_public_input {
proof_with_pis.get_public_inputs_hash()
}else {
HashOut::<F>::default()
};
if verifier_options.print_hash_statistics >= HashStatisticsPrintLevel::Info { if verifier_options.print_hash_statistics >= HashStatisticsPrintLevel::Info {
print_hash_counters("after PI"); 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( let challenges = proof_with_pis.get_challenges(
pi,
public_inputs_hash, public_inputs_hash,
&verifier_data.circuit_digest, &verifier_data.circuit_digest,
common_data, common_data,
verifier_options.hash_public_input,
)?; )?;
if verifier_options.print_hash_statistics >= HashStatisticsPrintLevel::Info { if verifier_options.print_hash_statistics >= HashStatisticsPrintLevel::Info {
@ -74,6 +104,7 @@ pub(crate) fn verify_with_options<F: RichField + Extendable<D>, C: GenericConfig
let result = verify_with_challenges::<F, C, D>( let result = verify_with_challenges::<F, C, D>(
proof_with_pis.proof, proof_with_pis.proof,
proof_with_pis.public_inputs,
public_inputs_hash, public_inputs_hash,
challenges, challenges,
verifier_data, verifier_data,
@ -93,6 +124,7 @@ pub(crate) fn verify_with_challenges<
const D: usize, const D: usize,
>( >(
proof: Proof<F, C, D>, proof: Proof<F, C, D>,
public_inputs: Vec<F>,
public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash, public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
challenges: ProofChallenges<F, D>, challenges: ProofChallenges<F, D>,
verifier_data: &VerifierOnlyCircuitData<C, D>, verifier_data: &VerifierOnlyCircuitData<C, D>,
@ -105,6 +137,7 @@ pub(crate) fn verify_with_challenges<
local_constants, local_constants,
local_wires, local_wires,
public_inputs_hash: &public_inputs_hash, public_inputs_hash: &public_inputs_hash,
public_inputs: &public_inputs,
}; };
let local_zs = &proof.openings.plonk_zs; let local_zs = &proof.openings.plonk_zs;
let next_zs = &proof.openings.plonk_zs_next; let next_zs = &proof.openings.plonk_zs_next;