From 06552eec44754ed5d9384efcd944251ed175da7a Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 23 Dec 2020 13:03:31 -0700 Subject: [PATCH] Update the PLONK implementation to adapt to the new transcript API. --- benches/arithmetic.rs | 6 +- benches/plonk.rs | 16 +- examples/performance_model.rs | 17 +- src/plonk.rs | 40 +- src/plonk/lookup.rs | 13 - src/plonk/lookup/prover.rs | 76 ++-- src/plonk/lookup/verifier.rs | 139 +++++-- src/plonk/permutation.rs | 8 - src/plonk/permutation/prover.rs | 58 ++- src/plonk/permutation/verifier.rs | 87 +++-- src/plonk/prover.rs | 622 +++++++++++++++--------------- src/plonk/vanishing.rs | 6 - src/plonk/vanishing/prover.rs | 46 +-- src/plonk/vanishing/verifier.rs | 71 ++-- src/plonk/verifier.rs | 360 ++++++++--------- src/poly/multiopen/verifier.rs | 4 +- src/transcript.rs | 22 ++ 17 files changed, 780 insertions(+), 811 deletions(-) diff --git a/benches/arithmetic.rs b/benches/arithmetic.rs index 400725b..aeb3f5e 100644 --- a/benches/arithmetic.rs +++ b/benches/arithmetic.rs @@ -3,9 +3,9 @@ extern crate criterion; extern crate halo2; use crate::arithmetic::{small_multiexp, FieldExt}; -use crate::pasta::{EqAffine, Fp, Fq}; +use crate::pasta::{EqAffine, Fp}; use crate::poly::commitment::Params; -use crate::transcript::DummyHash; +use crate::transcript::DummyHashWriter; use halo2::*; use criterion::{black_box, Criterion}; @@ -13,7 +13,7 @@ use criterion::{black_box, Criterion}; fn criterion_benchmark(c: &mut Criterion) { // small multiexp { - let params: Params = Params::new::>(5); + let params: Params = Params::new::>(5); let g = &mut params.get_g(); let len = g.len() / 2; let (g_lo, g_hi) = g.split_at_mut(len); diff --git a/benches/plonk.rs b/benches/plonk.rs index 70a4f4e..84eace6 100644 --- a/benches/plonk.rs +++ b/benches/plonk.rs @@ -6,7 +6,7 @@ use halo2::arithmetic::FieldExt; use halo2::pasta::{EqAffine, Fp, Fq}; use halo2::plonk::*; use halo2::poly::commitment::Params; -use halo2::transcript::DummyHash; +use halo2::transcript::{DummyHashReader, DummyHashWriter, TranscriptRead, TranscriptWrite}; use std::marker::PhantomData; @@ -18,7 +18,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) { pub struct Variable(Column, usize); // Initialize the polynomial commitment parameters - let params: Params = Params::new::>(k); + let params: Params = Params::new::>(k); struct PLONKConfig { a: Column, @@ -239,7 +239,8 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) { }; // Create a proof - Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[]) + let mut transcript = DummyHashWriter::init(vec![], Fq::one()); + create_proof(¶ms, &pk, &circuit, &[], &mut transcript) .expect("proof generation should not fail") }); }); @@ -250,15 +251,16 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) { }; // Create a proof - let proof = Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[]) + let mut transcript = DummyHashWriter::init(vec![], Fq::one()); + create_proof(¶ms, &pk, &circuit, &[], &mut transcript) .expect("proof generation should not fail"); + let proof = transcript.finalize(); c.bench_function(&verifier_name, |b| { b.iter(|| { let msm = params.empty_msm(); - let guard = proof - .verify::, DummyHash>(¶ms, pk.get_vk(), msm, &[]) - .unwrap(); + let mut transcript = DummyHashReader::init(&proof[..], Fq::one()); + let guard = verify_proof(¶ms, pk.get_vk(), msm, &[], &mut transcript).unwrap(); let msm = guard.clone().use_challenges(); assert!(msm.eval()); }); diff --git a/examples/performance_model.rs b/examples/performance_model.rs index cd38cd0..ddca377 100644 --- a/examples/performance_model.rs +++ b/examples/performance_model.rs @@ -4,9 +4,10 @@ use halo2::{ pasta::{EqAffine, Fp, Fq}, plonk::*, poly::commitment::{Blind, Params}, - transcript::DummyHash, + transcript::{DummyHashReader, DummyHashWriter, TranscriptRead, TranscriptWrite}, }; +use std::io; use std::marker::PhantomData; /// This represents an advice column at a certain row in the ConstraintSystem @@ -249,7 +250,7 @@ fn main() { let k = 11; // Initialize the polynomial commitment parameters - let params: Params = Params::new::>(k); + let params: Params = Params::new::>(k); let empty_circuit: MyCircuit = MyCircuit { a: None, k }; @@ -273,18 +274,18 @@ fn main() { }; // Create a proof - let proof = - Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[pubinputs]) - .expect("proof generation should not fail"); + let mut transcript = DummyHashWriter::init(vec![], Fq::one()); + create_proof(¶ms, &pk, &circuit, &[pubinputs], &mut transcript) + .expect("proof generation should not fail"); + let proof: Vec = transcript.finalize(); println!("[Prover] {}", recorder); recorder.clear(); let pubinput_slice = &[pubinput]; let msm = params.empty_msm(); - let guard = proof - .verify::, DummyHash>(¶ms, pk.get_vk(), msm, pubinput_slice) - .unwrap(); + let mut transcript = DummyHashReader::init(&proof[..], Fq::one()); + let guard = verify_proof(¶ms, pk.get_vk(), msm, pubinput_slice, &mut transcript).unwrap(); let msm = guard.clone().use_challenges(); assert!(msm.eval()); diff --git a/src/plonk.rs b/src/plonk.rs index 01c2b7b..cdf304c 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -6,9 +6,7 @@ //! [plonk]: https://eprint.iacr.org/2019/953 use crate::arithmetic::CurveAffine; -use crate::poly::{ - multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, -}; +use crate::poly::{Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial}; use crate::transcript::ChallengeScalar; mod circuit; @@ -48,20 +46,6 @@ pub struct ProvingKey { permutations: Vec>, } -/// This is an object which represents a (Turbo)PLONK proof. -// This structure must never allow points at infinity. -#[derive(Debug, Clone)] -pub struct Proof { - advice_commitments: Vec, - permutations: Vec>, - lookups: Vec>, - advice_evals: Vec, - aux_evals: Vec, - fixed_evals: Vec, - vanishing: vanishing::Proof, - multiopening: multiopen::Proof, -} - /// This is an error that could occur during proving or circuit synthesis. // TODO: these errors need to be cleaned up #[derive(Debug)] @@ -121,8 +105,9 @@ fn test_proving() { use crate::arithmetic::{Curve, FieldExt}; use crate::pasta::{EqAffine, Fp, Fq}; use crate::poly::commitment::{Blind, Params}; - use crate::transcript::DummyHash; + use crate::transcript::{DummyHashReader, DummyHashWriter, TranscriptRead, TranscriptWrite}; use circuit::{Advice, Column, Fixed}; + use std::io; use std::marker::PhantomData; const K: u32 = 5; @@ -131,7 +116,7 @@ fn test_proving() { pub struct Variable(Column, usize); // Initialize the polynomial commitment parameters - let params: Params = Params::new::>(K); + let params: Params = Params::new::>(K); struct PLONKConfig { a: Column, @@ -463,20 +448,23 @@ fn test_proving() { .to_affine(); for _ in 0..100 { + let mut transcript = DummyHashWriter::init(vec![], Fq::one()); // Create a proof - let proof = Proof::create::, DummyHash, _>( + create_proof( ¶ms, &pk, &circuit, &[pubinputs.clone()], + &mut transcript, ) .expect("proof generation should not fail"); + let proof: Vec = transcript.finalize(); let pubinput_slice = &[pubinput]; let msm = params.empty_msm(); - let guard = proof - .verify::, DummyHash>(¶ms, pk.get_vk(), msm, pubinput_slice) - .unwrap(); + let mut transcript = DummyHashReader::init(&proof[..], Fq::one()); + let guard = + verify_proof(¶ms, pk.get_vk(), msm, pubinput_slice, &mut transcript).unwrap(); { let msm = guard.clone().use_challenges(); assert!(msm.eval()); @@ -488,9 +476,9 @@ fn test_proving() { } let msm = guard.clone().use_challenges(); assert!(msm.clone().eval()); - let guard = proof - .verify::, DummyHash>(¶ms, pk.get_vk(), msm, pubinput_slice) - .unwrap(); + let mut transcript = DummyHashReader::init(&proof[..], Fq::one()); + let guard = + verify_proof(¶ms, pk.get_vk(), msm, pubinput_slice, &mut transcript).unwrap(); { let msm = guard.clone().use_challenges(); assert!(msm.eval()); diff --git a/src/plonk/lookup.rs b/src/plonk/lookup.rs index 7d588d0..1f05569 100644 --- a/src/plonk/lookup.rs +++ b/src/plonk/lookup.rs @@ -1,5 +1,4 @@ use super::circuit::{Any, Column}; -use crate::arithmetic::CurveAffine; mod prover; mod verifier; @@ -37,15 +36,3 @@ impl Argument { 3 } } - -#[derive(Clone, Debug)] -pub(crate) struct Proof { - product_commitment: C, - product_eval: C::Scalar, - product_inv_eval: C::Scalar, - permuted_input_commitment: C, - permuted_table_commitment: C, - permuted_input_eval: C::Scalar, - permuted_input_inv_eval: C::Scalar, - permuted_table_eval: C::Scalar, -} diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 5ee9a82..3e452ac 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -2,7 +2,7 @@ use super::super::{ circuit::{Any, Column}, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error, ProvingKey, }; -use super::{Argument, Proof}; +use super::Argument; use crate::{ arithmetic::{eval_polynomial, parallelize, BatchInvert, Curve, CurveAffine, FieldExt}, poly::{ @@ -10,9 +10,10 @@ use crate::{ multiopen::ProverQuery, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation, }, - transcript::{Hasher, Transcript}, + transcript::TranscriptWrite, }; use ff::Field; +use std::io::Write; use std::{collections::BTreeMap, iter}; #[derive(Debug)] @@ -47,13 +48,10 @@ pub(in crate::plonk) struct Committed<'a, C: CurveAffine> { pub(in crate::plonk) struct Constructed { permuted_input_poly: Polynomial, permuted_input_blind: Blind, - permuted_input_commitment: C, permuted_table_poly: Polynomial, permuted_table_blind: Blind, - permuted_table_commitment: C, product_poly: Polynomial, product_blind: Blind, - product_commitment: C, } pub(in crate::plonk) struct Evaluated { @@ -78,21 +76,21 @@ impl Argument { pub(in crate::plonk) fn commit_permuted< 'a, C: CurveAffine, - HBase: Hasher, - HScalar: Hasher, + W: Write, + T: TranscriptWrite, >( &self, pk: &ProvingKey, params: &Params, domain: &EvaluationDomain, - theta: ChallengeTheta, + theta: ChallengeTheta, advice_values: &'a [Polynomial], fixed_values: &'a [Polynomial], aux_values: &'a [Polynomial], advice_cosets: &'a [Polynomial], fixed_cosets: &'a [Polynomial], aux_cosets: &'a [Polynomial], - transcript: &mut Transcript, + transcript: &mut T, ) -> Result, Error> { // Closure to get values of columns and compress them let compress_columns = |columns: &[Column]| { @@ -150,12 +148,12 @@ impl Argument { // Hash permuted input commitment transcript - .absorb_point(&permuted_input_commitment) + .write_point(permuted_input_commitment) .map_err(|_| Error::TranscriptError)?; // Hash permuted table commitment transcript - .absorb_point(&permuted_table_commitment) + .write_point(permuted_table_commitment) .map_err(|_| Error::TranscriptError)?; let permuted_input_coset = pk @@ -197,14 +195,14 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { /// grand product polynomial over the lookup. The grand product polynomial /// is used to populate the Product struct. The Product struct is /// added to the Lookup and finally returned by the method. - pub(in crate::plonk) fn commit_product, HScalar: Hasher>( + pub(in crate::plonk) fn commit_product>( self, pk: &ProvingKey, params: &Params, - theta: ChallengeTheta, - beta: ChallengeBeta, - gamma: ChallengeGamma, - transcript: &mut Transcript, + theta: ChallengeTheta, + beta: ChallengeBeta, + gamma: ChallengeGamma, + transcript: &mut T, ) -> Result, Error> { // Goal is to compute the products of fractions // @@ -331,7 +329,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { // Hash product commitment transcript - .absorb_point(&product_commitment) + .write_point(product_commitment) .map_err(|_| Error::TranscriptError)?; Ok(Committed::<'a, C> { @@ -354,9 +352,9 @@ impl<'a, C: CurveAffine> Committed<'a, C> { pub(in crate::plonk) fn construct( self, pk: &'a ProvingKey, - theta: ChallengeTheta, - beta: ChallengeBeta, - gamma: ChallengeGamma, + theta: ChallengeTheta, + beta: ChallengeBeta, + gamma: ChallengeGamma, ) -> Result< ( Constructed, @@ -434,13 +432,10 @@ impl<'a, C: CurveAffine> Committed<'a, C> { Constructed { permuted_input_poly: permuted.permuted_input_poly, permuted_input_blind: permuted.permuted_input_blind, - permuted_input_commitment: permuted.permuted_input_commitment, permuted_table_poly: permuted.permuted_table_poly, permuted_table_blind: permuted.permuted_table_blind, - permuted_table_commitment: permuted.permuted_table_commitment, product_poly: self.product_poly, product_blind: self.product_blind, - product_commitment: self.product_commitment, }, expressions, )) @@ -448,12 +443,12 @@ impl<'a, C: CurveAffine> Committed<'a, C> { } impl Constructed { - pub(in crate::plonk) fn evaluate, HScalar: Hasher>( + pub(in crate::plonk) fn evaluate>( self, pk: &ProvingKey, - x: ChallengeX, - transcript: &mut Transcript, - ) -> Evaluated { + x: ChallengeX, + transcript: &mut T, + ) -> Result, Error> { let domain = &pk.vk.domain; let x_inv = domain.rotate_omega(*x, Rotation(-1)); @@ -471,17 +466,19 @@ impl Constructed { .chain(Some(permuted_input_inv_eval)) .chain(Some(permuted_table_eval)) { - transcript.absorb_scalar(eval); + transcript + .write_scalar(eval) + .map_err(|_| Error::TranscriptError)?; } - Evaluated { + Ok(Evaluated { constructed: self, product_eval, product_inv_eval, permuted_input_eval, permuted_input_inv_eval, permuted_table_eval, - } + }) } } @@ -489,7 +486,7 @@ impl Evaluated { pub(in crate::plonk) fn open<'a>( &'a self, pk: &'a ProvingKey, - x: ChallengeX, + x: ChallengeX, ) -> impl Iterator> + Clone { let x_inv = pk.vk.domain.rotate_omega(*x, Rotation(-1)); @@ -520,29 +517,16 @@ impl Evaluated { point: x_inv, poly: &self.constructed.permuted_input_poly, blind: self.constructed.permuted_input_blind, - eval: self.permuted_input_eval, + eval: self.permuted_input_inv_eval, })) // Open lookup product commitments at x_inv .chain(Some(ProverQuery { point: x_inv, poly: &self.constructed.product_poly, blind: self.constructed.product_blind, - eval: self.product_eval, + eval: self.product_inv_eval, })) } - - pub(crate) fn build(self) -> Proof { - Proof { - product_commitment: self.constructed.product_commitment, - product_eval: self.product_eval, - product_inv_eval: self.product_inv_eval, - permuted_input_commitment: self.constructed.permuted_input_commitment, - permuted_table_commitment: self.constructed.permuted_table_commitment, - permuted_input_eval: self.permuted_input_eval, - permuted_input_inv_eval: self.permuted_input_inv_eval, - permuted_table_eval: self.permuted_table_eval, - } - } } /// Given a column of input values A and a column of table values S, diff --git a/src/plonk/lookup/verifier.rs b/src/plonk/lookup/verifier.rs index 4118600..23a5c68 100644 --- a/src/plonk/lookup/verifier.rs +++ b/src/plonk/lookup/verifier.rs @@ -1,51 +1,117 @@ use std::iter; use super::super::circuit::{Any, Column}; -use super::{Argument, Proof}; +use super::Argument; use crate::{ arithmetic::CurveAffine, plonk::{ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error, VerifyingKey}, poly::{multiopen::VerifierQuery, Rotation}, - transcript::{Hasher, Transcript}, + transcript::TranscriptRead, }; use ff::Field; +use std::io::Read; -impl Proof { +pub struct PermutationCommitments { + permuted_input_commitment: C, + permuted_table_commitment: C, +} + +pub struct Committed { + permuted_input_commitment: C, + permuted_table_commitment: C, + product_commitment: C, +} + +pub struct Evaluated { + committed: Committed, + product_eval: C::Scalar, + product_inv_eval: C::Scalar, + permuted_input_eval: C::Scalar, + permuted_input_inv_eval: C::Scalar, + permuted_table_eval: C::Scalar, +} + +impl Argument { pub(in crate::plonk) fn absorb_permuted_commitments< - HBase: Hasher, - HScalar: Hasher, + C: CurveAffine, + R: Read, + T: TranscriptRead, >( &self, - transcript: &mut Transcript, - ) -> Result<(), Error> { - transcript - .absorb_point(&self.permuted_input_commitment) + transcript: &mut T, + ) -> Result, Error> { + let permuted_input_commitment = transcript + .read_point() + .map_err(|_| Error::TranscriptError)?; + let permuted_table_commitment = transcript + .read_point() .map_err(|_| Error::TranscriptError)?; - transcript - .absorb_point(&self.permuted_table_commitment) - .map_err(|_| Error::TranscriptError) - } - pub(in crate::plonk) fn absorb_product_commitment< - HBase: Hasher, - HScalar: Hasher, - >( - &self, - transcript: &mut Transcript, - ) -> Result<(), Error> { - transcript - .absorb_point(&self.product_commitment) - .map_err(|_| Error::TranscriptError) + Ok(PermutationCommitments { + permuted_input_commitment, + permuted_table_commitment, + }) } +} +impl PermutationCommitments { + pub(in crate::plonk) fn absorb_product_commitment>( + self, + transcript: &mut T, + ) -> Result, Error> { + let product_commitment = transcript + .read_point() + .map_err(|_| Error::TranscriptError)?; + + Ok(Committed { + permuted_input_commitment: self.permuted_input_commitment, + permuted_table_commitment: self.permuted_table_commitment, + product_commitment, + }) + } +} + +impl Committed { + pub(crate) fn evaluate>( + self, + transcript: &mut T, + ) -> Result, Error> { + let product_eval = transcript + .read_scalar() + .map_err(|_| Error::TranscriptError)?; + let product_inv_eval = transcript + .read_scalar() + .map_err(|_| Error::TranscriptError)?; + let permuted_input_eval = transcript + .read_scalar() + .map_err(|_| Error::TranscriptError)?; + let permuted_input_inv_eval = transcript + .read_scalar() + .map_err(|_| Error::TranscriptError)?; + let permuted_table_eval = transcript + .read_scalar() + .map_err(|_| Error::TranscriptError)?; + + Ok(Evaluated { + committed: self, + product_eval, + product_inv_eval, + permuted_input_eval, + permuted_input_inv_eval, + permuted_table_eval, + }) + } +} + +impl Evaluated { pub(in crate::plonk) fn expressions<'a>( &'a self, vk: &'a VerifyingKey, l_0: C::Scalar, argument: &'a Argument, - theta: ChallengeTheta, - beta: ChallengeBeta, - gamma: ChallengeGamma, + theta: ChallengeTheta, + beta: ChallengeBeta, + gamma: ChallengeGamma, advice_evals: &[C::Scalar], fixed_evals: &[C::Scalar], aux_evals: &[C::Scalar], @@ -98,19 +164,10 @@ impl Proof { )) } - pub(in crate::plonk) fn evals(&self) -> impl Iterator { - iter::empty() - .chain(Some(&self.product_eval)) - .chain(Some(&self.product_inv_eval)) - .chain(Some(&self.permuted_input_eval)) - .chain(Some(&self.permuted_input_inv_eval)) - .chain(Some(&self.permuted_table_eval)) - } - pub(in crate::plonk) fn queries<'a>( &'a self, vk: &'a VerifyingKey, - x: ChallengeX, + x: ChallengeX, ) -> impl Iterator> + Clone { let x_inv = vk.domain.rotate_omega(*x, Rotation(-1)); @@ -118,31 +175,31 @@ impl Proof { // Open lookup product commitments at x .chain(Some(VerifierQuery { point: *x, - commitment: &self.product_commitment, + commitment: &self.committed.product_commitment, eval: self.product_eval, })) // Open lookup input commitments at x .chain(Some(VerifierQuery { point: *x, - commitment: &self.permuted_input_commitment, + commitment: &self.committed.permuted_input_commitment, eval: self.permuted_input_eval, })) // Open lookup table commitments at x .chain(Some(VerifierQuery { point: *x, - commitment: &self.permuted_table_commitment, + commitment: &self.committed.permuted_table_commitment, eval: self.permuted_table_eval, })) // Open lookup input commitments at \omega^{-1} x .chain(Some(VerifierQuery { point: x_inv, - commitment: &self.permuted_input_commitment, + commitment: &self.committed.permuted_input_commitment, eval: self.permuted_input_inv_eval, })) // Open lookup product commitments at \omega^{-1} x .chain(Some(VerifierQuery { point: x_inv, - commitment: &self.product_commitment, + commitment: &self.committed.product_commitment, eval: self.product_inv_eval, })) } diff --git a/src/plonk/permutation.rs b/src/plonk/permutation.rs index 3bd1eef..051177e 100644 --- a/src/plonk/permutation.rs +++ b/src/plonk/permutation.rs @@ -52,11 +52,3 @@ pub(crate) struct ProvingKey { polys: Vec>, cosets: Vec>, } - -#[derive(Debug, Clone)] -pub(crate) struct Proof { - permutation_product_commitment: C, - permutation_product_eval: C::Scalar, - permutation_product_inv_eval: C::Scalar, - permutation_evals: Vec, -} diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index 8a07732..2c218fa 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -1,7 +1,8 @@ use ff::Field; +use std::io::Write; use std::iter; -use super::{Argument, Proof, ProvingKey}; +use super::{Argument, ProvingKey}; use crate::{ arithmetic::{eval_polynomial, parallelize, BatchInvert, Curve, CurveAffine, FieldExt}, plonk::{self, ChallengeBeta, ChallengeGamma, ChallengeX, Error}, @@ -10,7 +11,7 @@ use crate::{ multiopen::ProverQuery, Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation, }, - transcript::{Hasher, Transcript}, + transcript::TranscriptWrite, }; pub(crate) struct Committed { @@ -18,13 +19,11 @@ pub(crate) struct Committed { permutation_product_coset: Polynomial, permutation_product_coset_inv: Polynomial, permutation_product_blind: Blind, - permutation_product_commitment: C, } pub(crate) struct Constructed { permutation_product_poly: Polynomial, permutation_product_blind: Blind, - permutation_product_commitment: C, } pub(crate) struct Evaluated { @@ -35,19 +34,15 @@ pub(crate) struct Evaluated { } impl Argument { - pub(in crate::plonk) fn commit< - C: CurveAffine, - HBase: Hasher, - HScalar: Hasher, - >( + pub(in crate::plonk) fn commit>( &self, params: &Params, pk: &plonk::ProvingKey, pkey: &ProvingKey, advice: &[Polynomial], - beta: ChallengeBeta, - gamma: ChallengeGamma, - transcript: &mut Transcript, + beta: ChallengeBeta, + gamma: ChallengeGamma, + transcript: &mut T, ) -> Result, Error> { let domain = &pk.vk.domain; @@ -129,7 +124,7 @@ impl Argument { // Hash the permutation product commitment transcript - .absorb_point(&permutation_product_commitment) + .write_point(permutation_product_commitment) .map_err(|_| Error::TranscriptError)?; Ok(Committed { @@ -137,7 +132,6 @@ impl Argument { permutation_product_coset, permutation_product_coset_inv, permutation_product_blind, - permutation_product_commitment, }) } } @@ -149,8 +143,8 @@ impl Committed { p: &'a Argument, pkey: &'a ProvingKey, advice_cosets: &'a [Polynomial], - beta: ChallengeBeta, - gamma: ChallengeGamma, + beta: ChallengeBeta, + gamma: ChallengeGamma, ) -> Result< ( Constructed, @@ -211,7 +205,6 @@ impl Committed { Constructed { permutation_product_poly: self.permutation_product_poly, permutation_product_blind: self.permutation_product_blind, - permutation_product_commitment: self.permutation_product_commitment, }, expressions, )) @@ -219,7 +212,7 @@ impl Committed { } impl super::ProvingKey { - fn evaluate(&self, x: ChallengeX) -> Vec { + fn evaluate(&self, x: ChallengeX) -> Vec { self.polys .iter() .map(|poly| eval_polynomial(poly, *x)) @@ -229,7 +222,7 @@ impl super::ProvingKey { fn open<'a>( &'a self, evals: &'a [C::Scalar], - x: ChallengeX, + x: ChallengeX, ) -> impl Iterator> + Clone { self.polys .iter() @@ -244,13 +237,13 @@ impl super::ProvingKey { } impl Constructed { - pub(in crate::plonk) fn evaluate, HScalar: Hasher>( + pub(in crate::plonk) fn evaluate>( self, pk: &plonk::ProvingKey, pkey: &ProvingKey, - x: ChallengeX, - transcript: &mut Transcript, - ) -> Evaluated { + x: ChallengeX, + transcript: &mut T, + ) -> Result, Error> { let domain = &pk.vk.domain; let permutation_product_eval = eval_polynomial(&self.permutation_product_poly, *x); @@ -268,15 +261,17 @@ impl Constructed { .chain(Some(&permutation_product_inv_eval)) .chain(permutation_evals.iter()) { - transcript.absorb_scalar(*eval); + transcript + .write_scalar(*eval) + .map_err(|_| Error::TranscriptError)?; } - Evaluated { + Ok(Evaluated { constructed: self, permutation_product_eval, permutation_product_inv_eval, permutation_evals, - } + }) } } @@ -285,7 +280,7 @@ impl Evaluated { &'a self, pk: &'a plonk::ProvingKey, pkey: &'a ProvingKey, - x: ChallengeX, + x: ChallengeX, ) -> impl Iterator> + Clone { let x_inv = pk.vk.domain.rotate_omega(*x, Rotation(-1)); @@ -306,13 +301,4 @@ impl Evaluated { // Open permutation polynomial commitments at x .chain(pkey.open(&self.permutation_evals, x)) } - - pub(crate) fn build(self) -> Proof { - Proof { - permutation_product_commitment: self.constructed.permutation_product_commitment, - permutation_product_eval: self.permutation_product_eval, - permutation_product_inv_eval: self.permutation_product_inv_eval, - permutation_evals: self.permutation_evals, - } - } } diff --git a/src/plonk/permutation/verifier.rs b/src/plonk/permutation/verifier.rs index 207dee1..0bfe7b5 100644 --- a/src/plonk/permutation/verifier.rs +++ b/src/plonk/permutation/verifier.rs @@ -1,41 +1,81 @@ use ff::Field; +use std::io::Read; use std::iter; -use super::{Argument, Proof, VerifyingKey}; +use super::{Argument, VerifyingKey}; use crate::{ arithmetic::{CurveAffine, FieldExt}, plonk::{self, ChallengeBeta, ChallengeGamma, ChallengeX, Error}, poly::{multiopen::VerifierQuery, Rotation}, - transcript::{Hasher, Transcript}, + transcript::TranscriptRead, }; -impl Proof { - pub(crate) fn check_lengths(&self, p: &Argument) -> Result<(), Error> { - if self.permutation_evals.len() != p.columns.len() { - return Err(Error::IncompatibleParams); +pub struct Committed { + permutation_product_commitment: C, +} + +pub struct Evaluated { + permutation_product_commitment: C, + permutation_product_eval: C::Scalar, + permutation_product_inv_eval: C::Scalar, + permutation_evals: Vec, +} + +impl Argument { + pub(crate) fn absorb_product_commitment>( + &self, + transcript: &mut T, + ) -> Result, Error> { + let permutation_product_commitment = transcript + .read_point() + .map_err(|_| Error::TranscriptError)?; + + Ok(Committed { + permutation_product_commitment, + }) + } +} + +impl Committed { + pub(crate) fn evaluate>( + self, + vkey: &VerifyingKey, + transcript: &mut T, + ) -> Result, Error> { + let permutation_product_eval = transcript + .read_scalar() + .map_err(|_| Error::TranscriptError)?; + let permutation_product_inv_eval = transcript + .read_scalar() + .map_err(|_| Error::TranscriptError)?; + let mut permutation_evals = Vec::with_capacity(vkey.commitments.len()); + for _ in 0..vkey.commitments.len() { + permutation_evals.push( + transcript + .read_scalar() + .map_err(|_| Error::TranscriptError)?, + ); } - Ok(()) - } - - pub(crate) fn absorb_commitments, HScalar: Hasher>( - &self, - transcript: &mut Transcript, - ) -> Result<(), Error> { - transcript - .absorb_point(&self.permutation_product_commitment) - .map_err(|_| Error::TranscriptError) + Ok(Evaluated { + permutation_product_commitment: self.permutation_product_commitment, + permutation_product_eval, + permutation_product_inv_eval, + permutation_evals, + }) } +} +impl Evaluated { pub(in crate::plonk) fn expressions<'a>( &'a self, vk: &'a plonk::VerifyingKey, p: &'a Argument, advice_evals: &'a [C::Scalar], l_0: C::Scalar, - beta: ChallengeBeta, - gamma: ChallengeGamma, - x: ChallengeX, + beta: ChallengeBeta, + gamma: ChallengeGamma, + x: ChallengeX, ) -> impl Iterator + 'a { iter::empty() // l_0(X) * (1 - z(X)) = 0 @@ -70,18 +110,11 @@ impl Proof { })) } - pub(crate) fn evals(&self) -> impl Iterator { - iter::empty() - .chain(Some(&self.permutation_product_eval)) - .chain(Some(&self.permutation_product_inv_eval)) - .chain(self.permutation_evals.iter()) - } - pub(in crate::plonk) fn queries<'a>( &'a self, vk: &'a plonk::VerifyingKey, vkey: &'a VerifyingKey, - x: ChallengeX, + x: ChallengeX, ) -> impl Iterator> + Clone { let x_inv = vk.domain.rotate_omega(*x, Rotation(-1)); diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 41a003c..5f5429f 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -1,9 +1,10 @@ use ff::Field; +use std::io::Write; use std::iter; use super::{ circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, - vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, Proof, + vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, ProvingKey, }; use crate::arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}; @@ -12,371 +13,350 @@ use crate::poly::{ multiopen::{self, ProverQuery}, LagrangeCoeff, Polynomial, }; -use crate::transcript::{Hasher, Transcript}; +use crate::transcript::TranscriptWrite; -impl Proof { - /// This creates a proof for the provided `circuit` when given the public - /// parameters `params` and the proving key [`ProvingKey`] that was - /// generated previously for the same circuit. - pub fn create< - HBase: Hasher, - HScalar: Hasher, - ConcreteCircuit: Circuit, - >( - params: &Params, - pk: &ProvingKey, - circuit: &ConcreteCircuit, - aux: &[Polynomial], - ) -> Result { - if aux.len() != pk.vk.cs.num_aux_columns { - return Err(Error::IncompatibleParams); +/// This creates a proof for the provided `circuit` when given the public +/// parameters `params` and the proving key [`ProvingKey`] that was +/// generated previously for the same circuit. +pub fn create_proof< + C: CurveAffine, + W: Write, + T: TranscriptWrite, + ConcreteCircuit: Circuit, +>( + params: &Params, + pk: &ProvingKey, + circuit: &ConcreteCircuit, + aux: &[Polynomial], + transcript: &mut T, +) -> Result<(), Error> { + if aux.len() != pk.vk.cs.num_aux_columns { + return Err(Error::IncompatibleParams); + } + + struct WitnessCollection { + advice: Vec>, + _marker: std::marker::PhantomData, + } + + impl Assignment for WitnessCollection { + fn assign_advice( + &mut self, + column: Column, + row: usize, + to: impl FnOnce() -> Result, + ) -> Result<(), Error> { + *self + .advice + .get_mut(column.index()) + .and_then(|v| v.get_mut(row)) + .ok_or(Error::BoundsFailure)? = to()?; + + Ok(()) } - struct WitnessCollection { - advice: Vec>, - _marker: std::marker::PhantomData, + fn assign_fixed( + &mut self, + _: Column, + _: usize, + _: impl FnOnce() -> Result, + ) -> Result<(), Error> { + // We only care about advice columns here + + Ok(()) } - impl Assignment for WitnessCollection { - fn assign_advice( - &mut self, - column: Column, - row: usize, - to: impl FnOnce() -> Result, - ) -> Result<(), Error> { - *self - .advice - .get_mut(column.index()) - .and_then(|v| v.get_mut(row)) - .ok_or(Error::BoundsFailure)? = to()?; + fn copy(&mut self, _: usize, _: usize, _: usize, _: usize, _: usize) -> Result<(), Error> { + // We only care about advice columns here - Ok(()) - } - - fn assign_fixed( - &mut self, - _: Column, - _: usize, - _: impl FnOnce() -> Result, - ) -> Result<(), Error> { - // We only care about advice columns here - - Ok(()) - } - - fn copy( - &mut self, - _: usize, - _: usize, - _: usize, - _: usize, - _: usize, - ) -> Result<(), Error> { - // We only care about advice columns here - - Ok(()) - } + Ok(()) } + } - let domain = &pk.vk.domain; - let mut meta = ConstraintSystem::default(); - let config = ConcreteCircuit::configure(&mut meta); + let domain = &pk.vk.domain; + let mut meta = ConstraintSystem::default(); + let config = ConcreteCircuit::configure(&mut meta); - let mut witness = WitnessCollection { - advice: vec![domain.empty_lagrange(); meta.num_advice_columns], - _marker: std::marker::PhantomData, - }; + let mut witness = WitnessCollection { + advice: vec![domain.empty_lagrange(); meta.num_advice_columns], + _marker: std::marker::PhantomData, + }; - // Synthesize the circuit to obtain the witness and other information. - circuit.synthesize(&mut witness, config)?; + // Synthesize the circuit to obtain the witness and other information. + circuit.synthesize(&mut witness, config)?; - let witness = witness; + let witness = witness; - // Create a transcript for obtaining Fiat-Shamir challenges. - let mut transcript = Transcript::::new(); + // Compute commitments to aux column polynomials + let aux_commitments_projective: Vec<_> = aux + .iter() + .map(|poly| params.commit_lagrange(poly, Blind::default())) + .collect(); + let mut aux_commitments = vec![C::zero(); aux_commitments_projective.len()]; + C::Projective::batch_to_affine(&aux_commitments_projective, &mut aux_commitments); + let aux_commitments = aux_commitments; + drop(aux_commitments_projective); + metrics::counter!("aux_commitments", aux_commitments.len() as u64); - // Compute commitments to aux column polynomials - let aux_commitments_projective: Vec<_> = aux - .iter() - .map(|poly| params.commit_lagrange(poly, Blind::default())) - .collect(); - let mut aux_commitments = vec![C::zero(); aux_commitments_projective.len()]; - C::Projective::batch_to_affine(&aux_commitments_projective, &mut aux_commitments); - let aux_commitments = aux_commitments; - drop(aux_commitments_projective); - metrics::counter!("aux_commitments", aux_commitments.len() as u64); + for commitment in &aux_commitments { + transcript + .common_point(*commitment) + .map_err(|_| Error::TranscriptError)?; + } - for commitment in &aux_commitments { - transcript - .absorb_point(commitment) - .map_err(|_| Error::TranscriptError)?; - } + let aux_polys: Vec<_> = aux + .iter() + .map(|poly| { + let lagrange_vec = domain.lagrange_from_vec(poly.to_vec()); + domain.lagrange_to_coeff(lagrange_vec) + }) + .collect(); - let aux_polys: Vec<_> = aux - .iter() - .map(|poly| { - let lagrange_vec = domain.lagrange_from_vec(poly.to_vec()); - domain.lagrange_to_coeff(lagrange_vec) - }) - .collect(); + let aux_cosets: Vec<_> = meta + .aux_queries + .iter() + .map(|&(column, at)| { + let poly = aux_polys[column.index()].clone(); + domain.coeff_to_extended(poly, at) + }) + .collect(); - let aux_cosets: Vec<_> = meta - .aux_queries - .iter() - .map(|&(column, at)| { - let poly = aux_polys[column.index()].clone(); - domain.coeff_to_extended(poly, at) - }) - .collect(); + // Compute commitments to advice column polynomials + let advice_blinds: Vec<_> = witness + .advice + .iter() + .map(|_| Blind(C::Scalar::rand())) + .collect(); + let advice_commitments_projective: Vec<_> = witness + .advice + .iter() + .zip(advice_blinds.iter()) + .map(|(poly, blind)| params.commit_lagrange(poly, *blind)) + .collect(); + let mut advice_commitments = vec![C::zero(); advice_commitments_projective.len()]; + C::Projective::batch_to_affine(&advice_commitments_projective, &mut advice_commitments); + let advice_commitments = advice_commitments; + drop(advice_commitments_projective); + metrics::counter!("advice_commitments", advice_commitments.len() as u64); - // Compute commitments to advice column polynomials - let advice_blinds: Vec<_> = witness - .advice - .iter() - .map(|_| Blind(C::Scalar::rand())) - .collect(); - let advice_commitments_projective: Vec<_> = witness - .advice - .iter() - .zip(advice_blinds.iter()) - .map(|(poly, blind)| params.commit_lagrange(poly, *blind)) - .collect(); - let mut advice_commitments = vec![C::zero(); advice_commitments_projective.len()]; - C::Projective::batch_to_affine(&advice_commitments_projective, &mut advice_commitments); - let advice_commitments = advice_commitments; - drop(advice_commitments_projective); - metrics::counter!("advice_commitments", advice_commitments.len() as u64); + for commitment in &advice_commitments { + transcript + .write_point(*commitment) + .map_err(|_| Error::TranscriptError)?; + } - for commitment in &advice_commitments { - transcript - .absorb_point(commitment) - .map_err(|_| Error::TranscriptError)?; - } + let advice_polys: Vec<_> = witness + .advice + .clone() + .into_iter() + .map(|poly| domain.lagrange_to_coeff(poly)) + .collect(); - let advice_polys: Vec<_> = witness - .advice - .clone() + let advice_cosets: Vec<_> = meta + .advice_queries + .iter() + .map(|&(column, at)| { + let poly = advice_polys[column.index()].clone(); + domain.coeff_to_extended(poly, at) + }) + .collect(); + + // Sample theta challenge for keeping lookup columns linearly independent + let theta = ChallengeTheta::get(transcript); + + // Construct and commit to permuted values for each lookup + let lookups = pk + .vk + .cs + .lookups + .iter() + .map(|lookup| { + lookup.commit_permuted( + &pk, + ¶ms, + &domain, + theta, + &witness.advice, + &pk.fixed_values, + &aux, + &advice_cosets, + &pk.fixed_cosets, + &aux_cosets, + transcript, + ) + }) + .collect::, _>>()?; + + // Sample beta challenge + let beta = ChallengeBeta::get(transcript); + + // Sample gamma challenge + let gamma = ChallengeGamma::get(transcript); + + // Commit to permutations, if any. + let permutations = pk + .vk + .cs + .permutations + .iter() + .zip(pk.permutations.iter()) + .map(|(p, pkey)| p.commit(params, pk, pkey, &witness.advice, beta, gamma, transcript)) + .collect::, _>>()?; + + // Construct and commit to products for each lookup + let lookups = lookups + .into_iter() + .map(|lookup| lookup.commit_product(&pk, ¶ms, theta, beta, gamma, transcript)) + .collect::, _>>()?; + + // Obtain challenge for keeping all separate gates linearly independent + let y = ChallengeY::get(transcript); + + // Evaluate the h(X) polynomial's constraint system expressions for the permutation constraints, if any. + let (permutations, permutation_expressions): (Vec<_>, Vec<_>) = { + let tmp = permutations .into_iter() - .map(|poly| domain.lagrange_to_coeff(poly)) - .collect(); - - let advice_cosets: Vec<_> = meta - .advice_queries - .iter() - .map(|&(column, at)| { - let poly = advice_polys[column.index()].clone(); - domain.coeff_to_extended(poly, at) - }) - .collect(); - - // Sample theta challenge for keeping lookup columns linearly independent - let theta = ChallengeTheta::get(&mut transcript); - - // Construct and commit to permuted values for each lookup - let lookups = pk - .vk - .cs - .lookups - .iter() - .map(|lookup| { - lookup.commit_permuted( - &pk, - ¶ms, - &domain, - theta, - &witness.advice, - &pk.fixed_values, - &aux, - &advice_cosets, - &pk.fixed_cosets, - &aux_cosets, - &mut transcript, - ) - }) - .collect::, _>>()?; - - // Sample beta challenge - let beta = ChallengeBeta::get(&mut transcript); - - // Sample gamma challenge - let gamma = ChallengeGamma::get(&mut transcript); - - // Commit to permutations, if any. - let permutations = pk - .vk - .cs - .permutations - .iter() + .zip(pk.vk.cs.permutations.iter()) .zip(pk.permutations.iter()) - .map(|(p, pkey)| { - p.commit( - params, - pk, - pkey, - &witness.advice, - beta, - gamma, - &mut transcript, - ) + .map(|((p, argument), pkey)| { + p.construct(pk, argument, pkey, &advice_cosets, beta, gamma) }) .collect::, _>>()?; - // Construct and commit to products for each lookup - let lookups = lookups + tmp.into_iter().unzip() + }; + + // Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any. + let (lookups, lookup_expressions): (Vec<_>, Vec<_>) = { + let tmp = lookups .into_iter() - .map(|lookup| lookup.commit_product(&pk, ¶ms, theta, beta, gamma, &mut transcript)) + .map(|p| p.construct(pk, theta, beta, gamma)) .collect::, _>>()?; - // Obtain challenge for keeping all separate gates linearly independent - let y = ChallengeY::get(&mut transcript); + tmp.into_iter().unzip() + }; - // Evaluate the h(X) polynomial's constraint system expressions for the permutation constraints, if any. - let (permutations, permutation_expressions): (Vec<_>, Vec<_>) = { - let tmp = permutations - .into_iter() - .zip(pk.vk.cs.permutations.iter()) - .zip(pk.permutations.iter()) - .map(|((p, argument), pkey)| { - p.construct(pk, argument, pkey, &advice_cosets, beta, gamma) - }) - .collect::, _>>()?; + // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided + let expressions = iter::empty() + // Custom constraints + .chain(meta.gates.iter().map(|poly| { + poly.evaluate( + &|index| pk.fixed_cosets[index].clone(), + &|index| advice_cosets[index].clone(), + &|index| aux_cosets[index].clone(), + &|a, b| a + &b, + &|a, b| a * &b, + &|a, scalar| a * scalar, + ) + })) + // Permutation constraints, if any. + .chain(permutation_expressions.into_iter().flatten()) + // Lookup constraints, if any. + .chain(lookup_expressions.into_iter().flatten()); - tmp.into_iter().unzip() - }; + // Construct the vanishing argument + let vanishing = vanishing::Argument::construct(params, domain, expressions, y, transcript)?; - // Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any. - let (lookups, lookup_expressions): (Vec<_>, Vec<_>) = { - let tmp = lookups - .into_iter() - .map(|p| p.construct(pk, theta, beta, gamma)) - .collect::, _>>()?; + let x = ChallengeX::get(transcript); - tmp.into_iter().unzip() - }; + // Evaluate polynomials at omega^i x + let advice_evals: Vec<_> = meta + .advice_queries + .iter() + .map(|&(column, at)| { + eval_polynomial(&advice_polys[column.index()], domain.rotate_omega(*x, at)) + }) + .collect(); - // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided - let expressions = iter::empty() - // Custom constraints - .chain(meta.gates.iter().map(|poly| { - poly.evaluate( - &|index| pk.fixed_cosets[index].clone(), - &|index| advice_cosets[index].clone(), - &|index| aux_cosets[index].clone(), - &|a, b| a + &b, - &|a, b| a * &b, - &|a, scalar| a * scalar, - ) - })) - // Permutation constraints, if any. - .chain(permutation_expressions.into_iter().flatten()) - // Lookup constraints, if any. - .chain(lookup_expressions.into_iter().flatten()); + let aux_evals: Vec<_> = meta + .aux_queries + .iter() + .map(|&(column, at)| { + eval_polynomial(&aux_polys[column.index()], domain.rotate_omega(*x, at)) + }) + .collect(); - // Construct the vanishing argument - let vanishing = - vanishing::Argument::construct(params, domain, expressions, y, &mut transcript)?; + let fixed_evals: Vec<_> = meta + .fixed_queries + .iter() + .map(|&(column, at)| { + eval_polynomial(&pk.fixed_polys[column.index()], domain.rotate_omega(*x, at)) + }) + .collect(); - let x = ChallengeX::get(&mut transcript); + // Hash each column evaluation + for eval in advice_evals + .iter() + .chain(aux_evals.iter()) + .chain(fixed_evals.iter()) + { + transcript + .write_scalar(*eval) + .map_err(|_| Error::TranscriptError)?; + } - // Evaluate polynomials at omega^i x - let advice_evals: Vec<_> = meta - .advice_queries - .iter() - .map(|&(column, at)| { - eval_polynomial(&advice_polys[column.index()], domain.rotate_omega(*x, at)) - }) - .collect(); + let vanishing = vanishing.evaluate(x, transcript)?; - let aux_evals: Vec<_> = meta - .aux_queries - .iter() - .map(|&(column, at)| { - eval_polynomial(&aux_polys[column.index()], domain.rotate_omega(*x, at)) - }) - .collect(); + // Evaluate the permutations, if any, at omega^i x. + let permutations = permutations + .into_iter() + .zip(pk.permutations.iter()) + .map(|(p, pkey)| p.evaluate(pk, pkey, x, transcript)) + .collect::, _>>()?; - let fixed_evals: Vec<_> = meta - .fixed_queries - .iter() - .map(|&(column, at)| { - eval_polynomial(&pk.fixed_polys[column.index()], domain.rotate_omega(*x, at)) - }) - .collect(); + // Evaluate the lookups, if any, at omega^i x. + let lookups = lookups + .into_iter() + .map(|p| p.evaluate(pk, x, transcript)) + .collect::, _>>()?; - // Hash each column evaluation - for eval in advice_evals - .iter() - .chain(aux_evals.iter()) - .chain(fixed_evals.iter()) - { - transcript.absorb_scalar(*eval); - } - - let vanishing = vanishing.evaluate(x, &mut transcript); - - // Evaluate the permutations, if any, at omega^i x. - let permutations = permutations - .into_iter() - .zip(pk.permutations.iter()) - .map(|(p, pkey)| p.evaluate(pk, pkey, x, &mut transcript)) - .collect::>(); - - // Evaluate the lookups, if any, at omega^i x. - let lookups = lookups - .into_iter() - .map(|p| p.evaluate(pk, x, &mut transcript)) - .collect::>(); - - let instances = - iter::empty() - .chain(pk.vk.cs.advice_queries.iter().enumerate().map( - |(query_index, &(column, at))| ProverQuery { - point: domain.rotate_omega(*x, at), - poly: &advice_polys[column.index()], - blind: advice_blinds[column.index()], - eval: advice_evals[query_index], - }, - )) - .chain(pk.vk.cs.aux_queries.iter().enumerate().map( - |(query_index, &(column, at))| ProverQuery { + let instances = + iter::empty() + .chain(pk.vk.cs.advice_queries.iter().enumerate().map( + |(query_index, &(column, at))| ProverQuery { + point: domain.rotate_omega(*x, at), + poly: &advice_polys[column.index()], + blind: advice_blinds[column.index()], + eval: advice_evals[query_index], + }, + )) + .chain( + pk.vk + .cs + .aux_queries + .iter() + .enumerate() + .map(|(query_index, &(column, at))| ProverQuery { point: domain.rotate_omega(*x, at), poly: &aux_polys[column.index()], blind: Blind::default(), eval: aux_evals[query_index], - }, - )) - .chain(pk.vk.cs.fixed_queries.iter().enumerate().map( - |(query_index, &(column, at))| ProverQuery { + }), + ) + .chain( + pk.vk + .cs + .fixed_queries + .iter() + .enumerate() + .map(|(query_index, &(column, at))| ProverQuery { point: domain.rotate_omega(*x, at), poly: &pk.fixed_polys[column.index()], blind: Blind::default(), eval: fixed_evals[query_index], - }, - )) - // We query the h(X) polynomial at x - .chain(vanishing.open(x)) - .chain( - permutations - .iter() - .zip(pk.permutations.iter()) - .map(|(p, pkey)| p.open(pk, pkey, x)) - .into_iter() - .flatten(), - ) - .chain(lookups.iter().map(|p| p.open(pk, x)).into_iter().flatten()); + }), + ) + // We query the h(X) polynomial at x + .chain(vanishing.open(x)) + .chain( + permutations + .iter() + .zip(pk.permutations.iter()) + .map(|(p, pkey)| p.open(pk, pkey, x)) + .into_iter() + .flatten(), + ) + .chain(lookups.iter().map(|p| p.open(pk, x)).into_iter().flatten()); - let multiopening = multiopen::Proof::create(params, &mut transcript, instances) - .map_err(|_| Error::OpeningError)?; - - Ok(Proof { - advice_commitments, - permutations: permutations.into_iter().map(|p| p.build()).collect(), - lookups: lookups.into_iter().map(|p| p.build()).collect(), - advice_evals, - fixed_evals, - aux_evals, - vanishing: vanishing.build(), - multiopening, - }) - } + multiopen::create_proof(params, transcript, instances).map_err(|_| Error::OpeningError) } diff --git a/src/plonk/vanishing.rs b/src/plonk/vanishing.rs index 62695ba..81f86b0 100644 --- a/src/plonk/vanishing.rs +++ b/src/plonk/vanishing.rs @@ -9,9 +9,3 @@ mod verifier; pub(crate) struct Argument { _marker: PhantomData, } - -#[derive(Debug, Clone)] -pub(crate) struct Proof { - h_commitments: Vec, - h_evals: Vec, -} diff --git a/src/plonk/vanishing/prover.rs b/src/plonk/vanishing/prover.rs index df6a00a..b50176f 100644 --- a/src/plonk/vanishing/prover.rs +++ b/src/plonk/vanishing/prover.rs @@ -1,4 +1,6 @@ -use super::{Argument, Proof}; +use std::io::Write; + +use super::Argument; use crate::{ arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}, plonk::{ChallengeX, ChallengeY, Error}, @@ -7,13 +9,12 @@ use crate::{ multiopen::ProverQuery, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, Polynomial, }, - transcript::{Hasher, Transcript}, + transcript::TranscriptWrite, }; pub(in crate::plonk) struct Constructed { h_pieces: Vec>, h_blinds: Vec>, - h_commitments: Vec, } pub(in crate::plonk) struct Evaluated { @@ -22,12 +23,12 @@ pub(in crate::plonk) struct Evaluated { } impl Argument { - pub(in crate::plonk) fn construct, HScalar: Hasher>( + pub(in crate::plonk) fn construct>( params: &Params, domain: &EvaluationDomain, expressions: impl Iterator>, - y: ChallengeY, - transcript: &mut Transcript, + y: ChallengeY, + transcript: &mut T, ) -> Result, Error> { // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided let h_poly = expressions.fold(domain.empty_extended(), |h_poly, v| h_poly * *y + &v); @@ -59,24 +60,20 @@ impl Argument { // Hash each h(X) piece for c in h_commitments.iter() { transcript - .absorb_point(c) + .write_point(*c) .map_err(|_| Error::TranscriptError)?; } - Ok(Constructed { - h_pieces, - h_blinds, - h_commitments, - }) + Ok(Constructed { h_pieces, h_blinds }) } } impl Constructed { - pub(in crate::plonk) fn evaluate, HScalar: Hasher>( + pub(in crate::plonk) fn evaluate>( self, - x: ChallengeX, - transcript: &mut Transcript, - ) -> Evaluated { + x: ChallengeX, + transcript: &mut T, + ) -> Result, Error> { let h_evals: Vec<_> = self .h_pieces .iter() @@ -85,20 +82,22 @@ impl Constructed { // Hash each advice evaluation for eval in &h_evals { - transcript.absorb_scalar(*eval); + transcript + .write_scalar(*eval) + .map_err(|_| Error::TranscriptError)?; } - Evaluated { + Ok(Evaluated { constructed: self, h_evals, - } + }) } } impl Evaluated { pub(in crate::plonk) fn open<'a>( &'a self, - x: ChallengeX, + x: ChallengeX, ) -> impl Iterator> + Clone { self.constructed .h_pieces @@ -112,11 +111,4 @@ impl Evaluated { eval: *h_eval, }) } - - pub(in crate::plonk) fn build(self) -> Proof { - Proof { - h_commitments: self.constructed.h_commitments, - h_evals: self.h_evals, - } - } } diff --git a/src/plonk/vanishing/verifier.rs b/src/plonk/vanishing/verifier.rs index fd59104..90cfb9e 100644 --- a/src/plonk/vanishing/verifier.rs +++ b/src/plonk/vanishing/verifier.rs @@ -1,46 +1,57 @@ use ff::Field; +use std::io::Read; -use super::Proof; use crate::{ arithmetic::CurveAffine, plonk::{ChallengeX, ChallengeY, Error, VerifyingKey}, poly::multiopen::VerifierQuery, - transcript::{Hasher, Transcript}, + transcript::{read_n_points, read_n_scalars, TranscriptRead}, }; -impl Proof { - pub(in crate::plonk) fn check_lengths(&self, vk: &VerifyingKey) -> Result<(), Error> { - if self.h_commitments.len() != self.h_evals.len() { - return Err(Error::IncompatibleParams); - } +use super::Argument; - if self.h_commitments.len() != vk.domain.get_quotient_poly_degree() { - return Err(Error::IncompatibleParams); - } +pub struct Committed { + h_commitments: Vec, +} - Ok(()) - } +pub struct Evaluated { + h_commitments: Vec, + h_evals: Vec, +} - pub(in crate::plonk) fn absorb_commitments< - HBase: Hasher, - HScalar: Hasher, - >( - &self, - transcript: &mut Transcript, - ) -> Result<(), Error> { +impl Argument { + pub(in crate::plonk) fn absorb_commitments>( + vk: &VerifyingKey, + transcript: &mut T, + ) -> Result, Error> { // Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1 - for c in &self.h_commitments { - transcript - .absorb_point(c) - .map_err(|_| Error::TranscriptError)?; - } - Ok(()) - } + let h_commitments = read_n_points(transcript, vk.domain.get_quotient_poly_degree()) + .map_err(|_| Error::TranscriptError)?; + Ok(Committed { h_commitments }) + } +} + +impl Committed { + pub(in crate::plonk) fn evaluate>( + self, + transcript: &mut T, + ) -> Result, Error> { + let h_evals = read_n_scalars(transcript, self.h_commitments.len()) + .map_err(|_| Error::TranscriptError)?; + + Ok(Evaluated { + h_commitments: self.h_commitments, + h_evals, + }) + } +} + +impl Evaluated { pub(in crate::plonk) fn verify( &self, expressions: impl Iterator, - y: ChallengeY, + y: ChallengeY, xn: C::Scalar, ) -> Result<(), Error> { let expected_h_eval = expressions.fold(C::Scalar::zero(), |h_eval, v| h_eval * &*y + &v); @@ -60,13 +71,9 @@ impl Proof { Ok(()) } - pub(in crate::plonk) fn evals(&self) -> impl Iterator { - self.h_evals.iter() - } - pub(in crate::plonk) fn queries<'a>( &'a self, - x: ChallengeX, + x: ChallengeX, ) -> impl Iterator> + Clone { self.h_commitments .iter() diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 3ece2a9..b1b64d9 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -1,216 +1,105 @@ use ff::Field; +use std::io::Read; use std::iter; use super::{ - ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, Proof, + vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, VerifyingKey, }; use crate::arithmetic::{CurveAffine, FieldExt}; use crate::poly::{ commitment::{Guard, Params, MSM}, - multiopen::VerifierQuery, + multiopen::{self, VerifierQuery}, }; -use crate::transcript::{Hasher, Transcript}; +use crate::transcript::{read_n_points, read_n_scalars, TranscriptRead}; -impl<'a, C: CurveAffine> Proof { - /// Returns a boolean indicating whether or not the proof is valid - pub fn verify, HScalar: Hasher>( - &'a self, - params: &'a Params, - vk: &'a VerifyingKey, - msm: MSM<'a, C>, - aux_commitments: &'a [C], - ) -> Result, Error> { - self.check_lengths(vk, aux_commitments)?; - - // Check that aux_commitments matches the expected number of aux_columns - // and self.aux_evals - if aux_commitments.len() != vk.cs.num_aux_columns - || self.aux_evals.len() != vk.cs.num_aux_columns - { - return Err(Error::IncompatibleParams); - } - - // Create a transcript for obtaining Fiat-Shamir challenges. - let mut transcript = Transcript::::new(); - - // Hash the aux (external) commitments into the transcript - for commitment in aux_commitments { - transcript - .absorb_point(commitment) - .map_err(|_| Error::TranscriptError)?; - } - - // Hash the prover's advice commitments into the transcript - for commitment in &self.advice_commitments { - transcript - .absorb_point(commitment) - .map_err(|_| Error::TranscriptError)?; - } - - // Sample theta challenge for keeping lookup columns linearly independent - let theta = ChallengeTheta::get(&mut transcript); - - // Hash each lookup permuted commitment - for lookup in &self.lookups { - lookup.absorb_permuted_commitments(&mut transcript)?; - } - - // Sample beta challenge - let beta = ChallengeBeta::get(&mut transcript); - - // Sample gamma challenge - let gamma = ChallengeGamma::get(&mut transcript); - - // Hash each permutation product commitment - for permutation in &self.permutations { - permutation.absorb_commitments(&mut transcript)?; - } - - // Hash each lookup product commitment - for lookup in &self.lookups { - lookup.absorb_product_commitment(&mut transcript)?; - } - - // Sample y challenge, which keeps the gates linearly independent. - let y = ChallengeY::get(&mut transcript); - - self.vanishing.absorb_commitments(&mut transcript)?; - - // Sample x challenge, which is used to ensure the circuit is - // satisfied with high probability. - let x = ChallengeX::get(&mut transcript); - - // This check ensures the circuit is satisfied so long as the polynomial - // commitments open to the correct values. - self.check_hx(params, vk, theta, beta, gamma, y, x)?; - - for eval in self - .advice_evals - .iter() - .chain(self.aux_evals.iter()) - .chain(self.fixed_evals.iter()) - .chain(self.vanishing.evals()) - .chain( - self.permutations - .iter() - .map(|p| p.evals()) - .into_iter() - .flatten(), - ) - .chain(self.lookups.iter().map(|p| p.evals()).into_iter().flatten()) - { - transcript.absorb_scalar(*eval); - } - - let queries = - iter::empty() - .chain(vk.cs.advice_queries.iter().enumerate().map( - |(query_index, &(column, at))| VerifierQuery { - point: vk.domain.rotate_omega(*x, at), - commitment: &self.advice_commitments[column.index()], - eval: self.advice_evals[query_index], - }, - )) - .chain( - vk.cs - .aux_queries - .iter() - .enumerate() - .map(|(query_index, &(column, at))| VerifierQuery { - point: vk.domain.rotate_omega(*x, at), - commitment: &aux_commitments[column.index()], - eval: self.aux_evals[query_index], - }), - ) - .chain(vk.cs.fixed_queries.iter().enumerate().map( - |(query_index, &(column, at))| VerifierQuery { - point: vk.domain.rotate_omega(*x, at), - commitment: &vk.fixed_commitments[column.index()], - eval: self.fixed_evals[query_index], - }, - )) - .chain(self.vanishing.queries(x)); - - // We are now convinced the circuit is satisfied so long as the - // polynomial commitments open to the correct values. - self.multiopening - .verify( - params, - &mut transcript, - queries - .chain( - self.permutations - .iter() - .zip(vk.permutations.iter()) - .map(|(p, vkey)| p.queries(vk, vkey, x)) - .into_iter() - .flatten(), - ) - .chain( - self.lookups - .iter() - .map(|p| p.queries(vk, x)) - .into_iter() - .flatten(), - ), - msm, - ) - .map_err(|_| Error::OpeningError) +/// Returns a boolean indicating whether or not the proof is valid +pub fn verify_proof<'a, C: CurveAffine, R: Read, T: TranscriptRead>( + params: &'a Params, + vk: &'a VerifyingKey, + msm: MSM<'a, C>, + aux_commitments: &'a [C], + transcript: &mut T, +) -> Result, Error> { + // Check that aux_commitments matches the expected number of aux columns + if aux_commitments.len() != vk.cs.num_aux_columns { + return Err(Error::IncompatibleParams); } - /// Checks that the lengths of vectors are consistent with the constraint - /// system - fn check_lengths(&self, vk: &VerifyingKey, aux_commitments: &[C]) -> Result<(), Error> { - // Check that aux_commitments matches the expected number of aux_columns - // and self.aux_evals - if aux_commitments.len() != vk.cs.num_aux_columns - || self.aux_evals.len() != vk.cs.num_aux_columns - { - return Err(Error::IncompatibleParams); - } - - if self.fixed_evals.len() != vk.cs.fixed_queries.len() { - return Err(Error::IncompatibleParams); - } - - if self.advice_evals.len() != vk.cs.advice_queries.len() { - return Err(Error::IncompatibleParams); - } - - if self.permutations.len() != vk.cs.permutations.len() { - return Err(Error::IncompatibleParams); - } - - for (permutation, p) in self.permutations.iter().zip(vk.cs.permutations.iter()) { - permutation.check_lengths(p)?; - } - - self.vanishing.check_lengths(vk)?; - - if self.lookups.len() != vk.cs.lookups.len() { - return Err(Error::IncompatibleParams); - } - - if self.advice_commitments.len() != vk.cs.num_advice_columns { - return Err(Error::IncompatibleParams); - } - - Ok(()) + // Hash the aux (external) commitments into the transcript + for commitment in aux_commitments { + transcript + .common_point(*commitment) + .map_err(|_| Error::TranscriptError)? } - /// Checks that this proof's h_evals are correct, and thus that all of the - /// rules are satisfied. - fn check_hx( - &self, - params: &'a Params, - vk: &VerifyingKey, - theta: ChallengeTheta, - beta: ChallengeBeta, - gamma: ChallengeGamma, - y: ChallengeY, - x: ChallengeX, - ) -> Result<(), Error> { + // Hash the prover's advice commitments into the transcript + let advice_commitments = + read_n_points(transcript, vk.cs.num_advice_columns).map_err(|_| Error::TranscriptError)?; + + // Sample theta challenge for keeping lookup columns linearly independent + let theta = ChallengeTheta::get(transcript); + + // Hash each lookup permuted commitment + let lookups = vk + .cs + .lookups + .iter() + .map(|argument| argument.absorb_permuted_commitments(transcript)) + .collect::, _>>()?; + + // Sample beta challenge + let beta = ChallengeBeta::get(transcript); + + // Sample gamma challenge + let gamma = ChallengeGamma::get(transcript); + + // Hash each permutation product commitment + let permutations = vk + .cs + .permutations + .iter() + .map(|argument| argument.absorb_product_commitment(transcript)) + .collect::, _>>()?; + + // Hash each lookup product commitment + let lookups = lookups + .into_iter() + .map(|lookup| lookup.absorb_product_commitment(transcript)) + .collect::, _>>()?; + + // Sample y challenge, which keeps the gates linearly independent. + let y = ChallengeY::get(transcript); + + let vanishing = vanishing::Argument::absorb_commitments(vk, transcript)?; + + // Sample x challenge, which is used to ensure the circuit is + // satisfied with high probability. + let x = ChallengeX::get(transcript); + + let advice_evals = read_n_scalars(transcript, vk.cs.advice_queries.len()) + .map_err(|_| Error::TranscriptError)?; + let aux_evals = + read_n_scalars(transcript, vk.cs.aux_queries.len()).map_err(|_| Error::TranscriptError)?; + let fixed_evals = read_n_scalars(transcript, vk.cs.fixed_queries.len()) + .map_err(|_| Error::TranscriptError)?; + + let vanishing = vanishing.evaluate(transcript)?; + + let permutations = permutations + .into_iter() + .zip(vk.permutations.iter()) + .map(|(permutation, vkey)| permutation.evaluate(vkey, transcript)) + .collect::, _>>()?; + + let lookups = lookups + .into_iter() + .map(|lookup| lookup.evaluate(transcript)) + .collect::, _>>()?; + + // This check ensures the circuit is satisfied so long as the polynomial + // commitments open to the correct values. + { // x^n let xn = x.pow(&[params.n as u64, 0, 0, 0]); @@ -225,26 +114,26 @@ impl<'a, C: CurveAffine> Proof { // Evaluate the circuit using the custom gates provided .chain(vk.cs.gates.iter().map(|poly| { poly.evaluate( - &|index| self.fixed_evals[index], - &|index| self.advice_evals[index], - &|index| self.aux_evals[index], + &|index| fixed_evals[index], + &|index| advice_evals[index], + &|index| aux_evals[index], &|a, b| a + &b, &|a, b| a * &b, &|a, scalar| a * &scalar, ) })) .chain( - self.permutations + permutations .iter() .zip(vk.cs.permutations.iter()) .map(|(p, argument)| { - p.expressions(vk, argument, &self.advice_evals, l_0, beta, gamma, x) + p.expressions(vk, argument, &advice_evals, l_0, beta, gamma, x) }) .into_iter() .flatten(), ) .chain( - self.lookups + lookups .iter() .zip(vk.cs.lookups.iter()) .map(|(p, argument)| { @@ -255,15 +144,70 @@ impl<'a, C: CurveAffine> Proof { theta, beta, gamma, - &self.advice_evals, - &self.fixed_evals, - &self.aux_evals, + &advice_evals, + &fixed_evals, + &aux_evals, ) }) .into_iter() .flatten(), ); - self.vanishing.verify(expressions, y, xn) + vanishing.verify(expressions, y, xn)?; } + + let queries = iter::empty() + .chain( + vk.cs + .advice_queries + .iter() + .enumerate() + .map(|(query_index, &(column, at))| VerifierQuery { + point: vk.domain.rotate_omega(*x, at), + commitment: &advice_commitments[column.index()], + eval: advice_evals[query_index], + }), + ) + .chain( + vk.cs + .aux_queries + .iter() + .enumerate() + .map(|(query_index, &(column, at))| VerifierQuery { + point: vk.domain.rotate_omega(*x, at), + commitment: &aux_commitments[column.index()], + eval: aux_evals[query_index], + }), + ) + .chain( + vk.cs + .fixed_queries + .iter() + .enumerate() + .map(|(query_index, &(column, at))| VerifierQuery { + point: vk.domain.rotate_omega(*x, at), + commitment: &vk.fixed_commitments[column.index()], + eval: fixed_evals[query_index], + }), + ) + .chain(vanishing.queries(x)) + .chain( + permutations + .iter() + .zip(vk.permutations.iter()) + .map(|(p, vkey)| p.queries(vk, vkey, x)) + .into_iter() + .flatten(), + ) + .chain( + lookups + .iter() + .map(|p| p.queries(vk, x)) + .into_iter() + .flatten(), + ); + + // We are now convinced the circuit is satisfied so long as the + // polynomial commitments open to the correct values. + multiopen::verify_proof(params, transcript, queries, msm).map_err(|_| Error::OpeningError) } diff --git a/src/poly/multiopen/verifier.rs b/src/poly/multiopen/verifier.rs index dc2e8b9..55609f7 100644 --- a/src/poly/multiopen/verifier.rs +++ b/src/poly/multiopen/verifier.rs @@ -21,14 +21,14 @@ struct CommitmentData { } /// Verify a multi-opening proof -pub fn verify_proof<'a, I, C: CurveAffine, R: Read, T: TranscriptRead>( +pub fn verify_proof<'b, 'a: 'b, I, C: CurveAffine, R: Read, T: TranscriptRead>( params: &'a Params, transcript: &mut T, queries: I, mut msm: MSM<'a, C>, ) -> Result, Error> where - I: IntoIterator> + Clone, + I: IntoIterator> + Clone, { // Scale the MSM by a random factor to ensure that if the existing MSM // has is_zero() == false then this argument won't be able to interfere diff --git a/src/transcript.rs b/src/transcript.rs index 79849ab..b18119d 100644 --- a/src/transcript.rs +++ b/src/transcript.rs @@ -284,3 +284,25 @@ impl Deref for ChallengeScalar { &self.inner } } + +pub(crate) fn read_n_points>( + transcript: &mut T, + n: usize, +) -> io::Result> { + let mut v = Vec::with_capacity(n); + for _ in 0..n { + v.push(transcript.read_point()?); + } + Ok(v) +} + +pub(crate) fn read_n_scalars>( + transcript: &mut T, + n: usize, +) -> io::Result> { + let mut v = Vec::with_capacity(n); + for _ in 0..n { + v.push(transcript.read_scalar()?); + } + Ok(v) +}