From dd4da393bac658b2ce1cfa7a45126dbb7c8e851e Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 28 Apr 2023 16:52:31 +0200 Subject: [PATCH 1/3] plonk::circuit::Circuit: test_prove_and_verify --- halo2_proofs/src/plonk/circuit.rs | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/halo2_proofs/src/plonk/circuit.rs b/halo2_proofs/src/plonk/circuit.rs index fb6a8b56..0f229ba9 100644 --- a/halo2_proofs/src/plonk/circuit.rs +++ b/halo2_proofs/src/plonk/circuit.rs @@ -11,6 +11,7 @@ use crate::{ circuit::{Layouter, Region, Value}, poly::Rotation, }; +use pasta_curves::arithmetic::CurveAffine; mod compress_selectors; @@ -482,6 +483,42 @@ pub trait Circuit { /// the caller will be different depending on the context, and they may or /// may not expect to have a witness present. fn synthesize(&self, config: Self::Config, layouter: impl Layouter) -> Result<(), Error>; + + /// Test proof creation and verification for this circuit. + fn test_prove_and_verify(self, instance: &[&[C::Scalar]]) -> bool + where + C: CurveAffine, + C::Scalar: ff::FromUniformBytes<64>, + Self: Sized, + { + use crate::{ + plonk::{create_proof, keygen_pk, keygen_vk, verify_proof, SingleVerifier}, + poly::commitment::Params, + transcript::{Blake2bRead, Blake2bWrite, Challenge255}, + }; + + let params = Params::::new(K); + + let vk = keygen_vk(¶ms, &self.without_witnesses()).expect("keygen_vk should not fail"); + let pk = + keygen_pk(¶ms, vk, &self.without_witnesses()).expect("keygen_pk should not fail"); + + let mut transcript = Blake2bWrite::<_, _, Challenge255>::init(vec![]); + create_proof( + ¶ms, + &pk, + &[self], + &[instance], + rand_core::OsRng, + &mut transcript, + ) + .expect("proof generation should not fail"); + let proof: Vec = transcript.finalize(); + + let strategy = SingleVerifier::new(¶ms); + let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); + verify_proof(¶ms, pk.get_vk(), strategy, &[instance], &mut transcript).is_ok() + } } /// Low-degree expression representing an identity that must hold over the committed columns. From 01c2f622902e6ab099d93825796146f8e7783ba1 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 28 Apr 2023 19:09:37 +0200 Subject: [PATCH 2/3] simple-example: Failing to verify real proof The constant value is passed in through the MyCircuit struct. In MyCircuit::without_witnesses(), this constant is set to F::ZERO (F::default()). Because keygen uses without_witnesses() to define the circuit, the constant value is set to zero in ConstraintSystem; however, at proving time, MyCircuit passes in a nonzero constant. This causes the equality constraint in assign_advice_from_constant to fail. In general, fixed values should be copied in without_witnesses(). --- halo2_proofs/examples/simple-example.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/halo2_proofs/examples/simple-example.rs b/halo2_proofs/examples/simple-example.rs index 85b87537..445bf162 100644 --- a/halo2_proofs/examples/simple-example.rs +++ b/halo2_proofs/examples/simple-example.rs @@ -6,6 +6,7 @@ use halo2_proofs::{ plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed, Instance, Selector}, poly::Rotation, }; +use pasta_curves::EqAffine; // ANCHOR: instructions trait NumericInstructions: Chip { @@ -237,7 +238,7 @@ impl NumericInstructions for FieldChip { /// In this struct we store the private input variables. We use `Option` because /// they won't have any value during key generation. During proving, if any of these /// were `None` we would get an error. -#[derive(Default)] +#[derive(Default, Copy, Clone)] struct MyCircuit { constant: F, a: Value, @@ -308,7 +309,7 @@ fn main() { // ANCHOR: test-circuit // The number of rows in our circuit cannot exceed 2^k. Since our example // circuit is very small, we can pick a very small value here. - let k = 4; + const K: u32 = 4; // Prepare the private and public inputs to the circuit! let constant = Fp::from(7); @@ -328,12 +329,14 @@ fn main() { let mut public_inputs = vec![c]; // Given the correct public input, our circuit will verify. - let prover = MockProver::run(k, &circuit, vec![public_inputs.clone()]).unwrap(); + let prover = MockProver::run(K, &circuit, vec![public_inputs.clone()]).unwrap(); assert_eq!(prover.verify(), Ok(())); + assert!(circuit.test_prove_and_verify::(&[&public_inputs])); + // If we try some other public input, the proof will fail! public_inputs[0] += Fp::one(); - let prover = MockProver::run(k, &circuit, vec![public_inputs]).unwrap(); + let prover = MockProver::run(K, &circuit, vec![public_inputs]).unwrap(); assert!(prover.verify().is_err()); // ANCHOR_END: test-circuit } From 5c84d46c897c7457f1de3be935a30ecf88fbdea6 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 28 Apr 2023 19:20:54 +0200 Subject: [PATCH 3/3] simple-example: Fix without_witnesses() --- halo2_proofs/examples/simple-example.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/halo2_proofs/examples/simple-example.rs b/halo2_proofs/examples/simple-example.rs index 445bf162..2cc87a5a 100644 --- a/halo2_proofs/examples/simple-example.rs +++ b/halo2_proofs/examples/simple-example.rs @@ -238,7 +238,7 @@ impl NumericInstructions for FieldChip { /// In this struct we store the private input variables. We use `Option` because /// they won't have any value during key generation. During proving, if any of these /// were `None` we would get an error. -#[derive(Default, Copy, Clone)] +#[derive(Copy, Clone)] struct MyCircuit { constant: F, a: Value, @@ -251,7 +251,11 @@ impl Circuit for MyCircuit { type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { - Self::default() + Self { + constant: self.constant, + a: Value::default(), + b: Value::default(), + } } fn configure(meta: &mut ConstraintSystem) -> Self::Config {