From 9dfc6ac3798fc56fa090f81b9fbee0bec9be74e0 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 22 Aug 2020 15:09:47 -0600 Subject: [PATCH 01/15] Add first pieces of the API. --- src/plonk.rs | 13 ++++++- src/plonk/circuit.rs | 82 ++++++++++++++++++++++++++++++++++++++++++-- src/plonk/prover.rs | 8 +++-- src/plonk/srs.rs | 8 +++-- 4 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index 66f3e32..97eba66 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -103,12 +103,23 @@ fn test_proving() { // Initialize the polynomial commitment parameters let params: Params = Params::new::>(K); + struct MyConfig {} struct MyCircuit { a: Option, } impl Circuit for MyCircuit { - fn synthesize(&self, cs: &mut impl ConstraintSystem) -> Result<(), Error> { + type Config = MyConfig; + + fn configure(meta: &mut MetaCircuit) -> MyConfig { + MyConfig {} + } + + fn synthesize( + &self, + cs: &mut impl ConstraintSystem, + config: MyConfig, + ) -> Result<(), Error> { for _ in 0..10 { let (_, _, _, _) = cs.multiply(|| { let a = self.a.ok_or(Error::SynthesisError)?; diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index c0168d2..48da226 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -1,10 +1,12 @@ -use super::Error; +use core::cmp::max; +use core::ops::{Add, Mul}; +use super::Error; use crate::arithmetic::Field; /// This represents a PLONK wire, which could be a fixed (selector) wire or an /// advice wire. -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum Wire { /// A wires A(usize), @@ -59,8 +61,82 @@ pub trait ConstraintSystem { /// backend prover can ask the circuit to synthesize using some given /// [`ConstraintSystem`] implementation. pub trait Circuit { + /// This is a configuration object that stores things like wires. + type Config; + + /// The circuit is given an opportunity to describe the exact gate + /// arrangement, wire arrangement, etc. + fn configure(meta: &mut MetaCircuit) -> Self::Config; + /// Given the provided `cs`, synthesize the circuit. The concrete type of /// the caller will be different depending on the context, and they may or /// may not expect to have a witness present. - fn synthesize(&self, cs: &mut impl ConstraintSystem) -> Result<(), Error>; + fn synthesize( + &self, + cs: &mut impl ConstraintSystem, + config: Self::Config, + ) -> Result<(), Error>; +} + +/// Low-degree polynomial representing an identity that must hold over the committed wires. +#[derive(Clone, Debug)] +pub enum Polynomial { + /// This is a wire queried at a certain relative location + Wire(Wire, isize), + /// This is the sum of two polynomials + Sum(Box>, Box>), + /// This is the product of two polynomials + Product(Box>, Box>), + /// This is a scaled polynomial + Scaled(Box>, F), +} + +impl Polynomial { + fn degree(&self) -> usize { + match self { + Polynomial::Wire(_, _) => 1, + Polynomial::Sum(ref a, ref b) => max(a.degree(), b.degree()), + Polynomial::Product(ref a, ref b) => a.degree() + b.degree(), + Polynomial::Scaled(ref poly, _) => poly.degree(), + } + } +} + +impl Add for Polynomial { + type Output = Polynomial; + fn add(self, rhs: Polynomial) -> Polynomial { + Polynomial::Sum(Box::new(self), Box::new(rhs)) + } +} + +impl Mul for Polynomial { + type Output = Polynomial; + fn mul(self, rhs: Polynomial) -> Polynomial { + Polynomial::Product(Box::new(self), Box::new(rhs)) + } +} + +impl Mul for Polynomial { + type Output = Polynomial; + fn mul(self, rhs: F) -> Polynomial { + Polynomial::Scaled(Box::new(self), rhs) + } +} + +/// This is a description of the circuit environment, such as the gate, wire and +/// permutation arrangements. +#[derive(Debug, Clone)] +pub struct MetaCircuit { + // num_fixed_wires: usize, +// num_advice_wires: usize, +// permutations: Vec>, +// gates: Vec, +// queries: HashSet<(Wire, usize)>, +// num_queries: usize, +} + +impl Default for MetaCircuit { + fn default() -> MetaCircuit { + MetaCircuit {} + } } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index d06119d..b0b66bf 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -1,5 +1,5 @@ use super::{ - circuit::{Circuit, ConstraintSystem, Wire}, + circuit::{Circuit, ConstraintSystem, MetaCircuit, Wire}, hash_point, Error, Proof, SRS, }; use crate::arithmetic::{ @@ -78,8 +78,12 @@ impl Proof { sm: vec![], }; + let mut meta = MetaCircuit::default(); + + let config = ConcreteCircuit::configure(&mut meta); + // Synthesize the circuit to obtain the witness and other information. - circuit.synthesize(&mut witness)?; + circuit.synthesize(&mut witness, config)?; // Create a transcript for obtaining Fiat-Shamir challenges. let mut transcript = HBase::init(C::Base::one()); diff --git a/src/plonk/srs.rs b/src/plonk/srs.rs index 0a9acdb..7d0487d 100644 --- a/src/plonk/srs.rs +++ b/src/plonk/srs.rs @@ -1,5 +1,5 @@ use super::{ - circuit::{Circuit, ConstraintSystem, Wire}, + circuit::{Circuit, ConstraintSystem, MetaCircuit, Wire}, domain::EvaluationDomain, Error, GATE_DEGREE, SRS, }; @@ -54,8 +54,12 @@ impl SRS { sm: vec![], }; + let mut meta = MetaCircuit::default(); + + let config = ConcreteCircuit::configure(&mut meta); + // Synthesize the circuit to obtain SRS - circuit.synthesize(&mut assembly)?; + circuit.synthesize(&mut assembly, config)?; assembly.sa.resize(params.n as usize, C::Scalar::zero()); assembly.sb.resize(params.n as usize, C::Scalar::zero()); From c16141be9a150934e2a35604459cb6373553b702 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 22 Aug 2020 15:15:39 -0600 Subject: [PATCH 02/15] Introduce `Variable` type --- src/plonk/circuit.rs | 28 ++++++++++++++++------------ src/plonk/prover.rs | 12 ++++++------ src/plonk/srs.rs | 12 ++++++------ 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 48da226..f016b6b 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -9,15 +9,19 @@ use crate::arithmetic::Field; #[derive(Clone, Debug)] pub enum Wire { /// A wires - A(usize), + A, /// B wires - B(usize), + B, /// C wires - C(usize), + C, /// D wires - D(usize), + D, } +/// Represents a pointer to a value in the constraint system. +#[derive(Clone, Debug)] +pub struct Variable(pub(crate) Wire, pub(crate) usize); + /// This trait allows a [`Circuit`] to direct some backend to assign a witness /// for a constraint system. pub trait ConstraintSystem { @@ -30,13 +34,13 @@ pub trait ConstraintSystem { sd: F, sm: F, f: impl Fn() -> Result<(F, F, F, F), Error>, - ) -> Result<(Wire, Wire, Wire, Wire), Error>; + ) -> Result<(Variable, Variable, Variable, Variable), Error>; /// a * b - c = 0 fn multiply( &mut self, f: impl Fn() -> Result<(F, F, F), Error>, - ) -> Result<(Wire, Wire, Wire, Wire), Error> { + ) -> Result<(Variable, Variable, Variable, Variable), Error> { self.create_gate(F::zero(), F::zero(), F::one(), F::zero(), F::one(), || { let (a, b, c) = f()?; Ok((a, b, c, F::zero())) @@ -47,7 +51,7 @@ pub trait ConstraintSystem { fn add( &mut self, f: impl Fn() -> Result<(F, F, F), Error>, - ) -> Result<(Wire, Wire, Wire, Wire), Error> { + ) -> Result<(Variable, Variable, Variable, Variable), Error> { self.create_gate(F::one(), F::one(), F::one(), F::zero(), F::zero(), || { let (a, b, c) = f()?; Ok((a, b, c, F::zero())) @@ -128,11 +132,11 @@ impl Mul for Polynomial { #[derive(Debug, Clone)] pub struct MetaCircuit { // num_fixed_wires: usize, -// num_advice_wires: usize, -// permutations: Vec>, -// gates: Vec, -// queries: HashSet<(Wire, usize)>, -// num_queries: usize, + // num_advice_wires: usize, + // permutations: Vec>, + // gates: Vec, + // queries: HashSet<(Wire, usize)>, + // num_queries: usize, } impl Default for MetaCircuit { diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index b0b66bf..f6016cf 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -1,5 +1,5 @@ use super::{ - circuit::{Circuit, ConstraintSystem, MetaCircuit, Wire}, + circuit::{Circuit, ConstraintSystem, MetaCircuit, Wire, Variable}, hash_point, Error, Proof, SRS, }; use crate::arithmetic::{ @@ -42,13 +42,13 @@ impl Proof { sd: F, sm: F, f: impl Fn() -> Result<(F, F, F, F), Error>, - ) -> Result<(Wire, Wire, Wire, Wire), Error> { + ) -> Result<(Variable, Variable, Variable, Variable), Error> { let (a, b, c, d) = f()?; let tmp = Ok(( - Wire::A(self.a.len()), - Wire::B(self.a.len()), - Wire::C(self.a.len()), - Wire::D(self.a.len()), + Variable(Wire::A, self.a.len()), + Variable(Wire::B, self.a.len()), + Variable(Wire::C, self.a.len()), + Variable(Wire::D, self.a.len()), )); self.a.push(a); self.b.push(b); diff --git a/src/plonk/srs.rs b/src/plonk/srs.rs index 7d0487d..c61f8cb 100644 --- a/src/plonk/srs.rs +++ b/src/plonk/srs.rs @@ -1,5 +1,5 @@ use super::{ - circuit::{Circuit, ConstraintSystem, MetaCircuit, Wire}, + circuit::{Circuit, ConstraintSystem, MetaCircuit, Wire, Variable}, domain::EvaluationDomain, Error, GATE_DEGREE, SRS, }; @@ -30,12 +30,12 @@ impl SRS { sd: F, sm: F, _: impl Fn() -> Result<(F, F, F, F), Error>, - ) -> Result<(Wire, Wire, Wire, Wire), Error> { + ) -> Result<(Variable, Variable, Variable, Variable), Error> { let tmp = Ok(( - Wire::A(self.sa.len()), - Wire::B(self.sa.len()), - Wire::C(self.sa.len()), - Wire::D(self.sa.len()), + Variable(Wire::A, self.sa.len()), + Variable(Wire::B, self.sa.len()), + Variable(Wire::C, self.sa.len()), + Variable(Wire::D, self.sa.len()), )); self.sa.push(sa); self.sb.push(sb); From 7edffe0197e0c1d81b39ec46193328de9381658e Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 22 Aug 2020 16:10:27 -0600 Subject: [PATCH 03/15] Allow commitments to generic advice wire polynomials --- src/plonk.rs | 74 +++++++++++++++++++++++++++++++++++++++++-- src/plonk/circuit.rs | 36 ++++++++++++++++++--- src/plonk/prover.rs | 48 +++++++++++++++++++++++++--- src/plonk/srs.rs | 45 +++++++++++++++++++++++--- src/plonk/verifier.rs | 5 +++ 5 files changed, 191 insertions(+), 17 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index 97eba66..9385609 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -42,15 +42,21 @@ pub struct SRS { sd_commitment: C, sm_commitment: C, domain: EvaluationDomain, + + fixed_commitments: Vec, + fixed_polys: Vec<(Vec, Vec)>, + meta: MetaCircuit, } /// This is an object which represents a (Turbo)PLONK proof. +// This structure must never allow points at infinity. #[derive(Debug, Clone)] pub struct Proof { a_commitment: C, b_commitment: C, c_commitment: C, d_commitment: C, + advice_commitments: Vec, h_commitments: Vec, a_eval_x: C::Scalar, b_eval_x: C::Scalar, @@ -77,6 +83,8 @@ pub enum Error { IncompatibleParams, /// The constraint system is not satisfied. ConstraintSystemFailure, + /// Out of bounds index passed to a backend + BoundsFailure, } fn hash_point>( @@ -103,7 +111,16 @@ fn test_proving() { // Initialize the polynomial commitment parameters let params: Params = Params::new::>(K); - struct MyConfig {} + struct MyConfig { + a: Wire, + b: Wire, + c: Wire, + + sa: Wire, + sb: Wire, + sc: Wire, + sm: Wire, + } struct MyCircuit { a: Option, } @@ -112,7 +129,24 @@ fn test_proving() { type Config = MyConfig; fn configure(meta: &mut MetaCircuit) -> MyConfig { - MyConfig {} + let a = meta.advice_wire(); + let b = meta.advice_wire(); + let c = meta.advice_wire(); + + let sa = meta.fixed_wire(); + let sb = meta.fixed_wire(); + let sc = meta.fixed_wire(); + let sm = meta.fixed_wire(); + + MyConfig { + a, + b, + c, + sa, + sb, + sc, + sm, + } } fn synthesize( @@ -137,6 +171,42 @@ fn test_proving() { //cs.copy(c, e); } + // Similar to the above... + let mut row = 0; + for _ in 0..10 { + cs.assign(Variable(config.a, row), || { + self.a.ok_or(Error::SynthesisError) + })?; + cs.assign(Variable(config.b, row), || { + self.a.ok_or(Error::SynthesisError) + })?; + let a_squared = self.a.map(|a| a.square()); + cs.assign(Variable(config.c, row), || { + self.a.ok_or(Error::SynthesisError) + })?; + // Multiplication gate + cs.assign(Variable(config.sa, row), || Ok(Field::zero()))?; + cs.assign(Variable(config.sb, row), || Ok(Field::zero()))?; + cs.assign(Variable(config.sc, row), || Ok(Field::one()))?; + cs.assign(Variable(config.sm, row), || Ok(Field::one()))?; + row += 1; + + cs.assign(Variable(config.a, row), || { + self.a.ok_or(Error::SynthesisError) + })?; + cs.assign(Variable(config.b, row), || { + a_squared.ok_or(Error::SynthesisError) + })?; + let fin = a_squared.and_then(|a_squared| self.a.map(|a| a + a_squared)); + cs.assign(Variable(config.c, row), || fin.ok_or(Error::SynthesisError))?; + // Addition gate + cs.assign(Variable(config.sa, row), || Ok(Field::one()))?; + cs.assign(Variable(config.sb, row), || Ok(Field::one()))?; + cs.assign(Variable(config.sc, row), || Ok(Field::one()))?; + cs.assign(Variable(config.sm, row), || Ok(Field::zero()))?; + row += 1; + } + Ok(()) } } diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index f016b6b..3f4f872 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -6,7 +6,7 @@ use crate::arithmetic::Field; /// This represents a PLONK wire, which could be a fixed (selector) wire or an /// advice wire. -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub enum Wire { /// A wires A, @@ -16,15 +16,23 @@ pub enum Wire { C, /// D wires D, + /// Fixed wires + Fixed(usize), + /// Advice wires + Advice(usize), } /// Represents a pointer to a value in the constraint system. #[derive(Clone, Debug)] -pub struct Variable(pub(crate) Wire, pub(crate) usize); +pub struct Variable(pub Wire, pub usize); /// This trait allows a [`Circuit`] to direct some backend to assign a witness /// for a constraint system. pub trait ConstraintSystem { + /// Assign a wire value + fn assign(&mut self, var: Variable, to: impl FnOnce() -> Result) + -> Result<(), Error>; + /// Creates a gate. fn create_gate( &mut self, @@ -131,8 +139,8 @@ impl Mul for Polynomial { /// permutation arrangements. #[derive(Debug, Clone)] pub struct MetaCircuit { - // num_fixed_wires: usize, - // num_advice_wires: usize, + pub(crate) num_fixed_wires: usize, + pub(crate) num_advice_wires: usize, // permutations: Vec>, // gates: Vec, // queries: HashSet<(Wire, usize)>, @@ -141,6 +149,24 @@ pub struct MetaCircuit { impl Default for MetaCircuit { fn default() -> MetaCircuit { - MetaCircuit {} + MetaCircuit { + num_fixed_wires: 0, + num_advice_wires: 0, + } + } +} + +impl MetaCircuit { + /// Allocate a new fixed wire + pub fn fixed_wire(&mut self) -> Wire { + let tmp = Wire::Fixed(self.num_fixed_wires); + self.num_fixed_wires += 1; + tmp + } + /// Allocate a new advice wire + pub fn advice_wire(&mut self) -> Wire { + let tmp = Wire::Advice(self.num_advice_wires); + self.num_advice_wires += 1; + tmp } } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index f6016cf..b99e015 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -1,5 +1,5 @@ use super::{ - circuit::{Circuit, ConstraintSystem, MetaCircuit, Wire, Variable}, + circuit::{Circuit, ConstraintSystem, MetaCircuit, Variable, Wire}, hash_point, Error, Proof, SRS, }; use crate::arithmetic::{ @@ -31,9 +31,29 @@ impl Proof { sc: Vec, sd: Vec, sm: Vec, + advice: Vec>, } impl ConstraintSystem for WitnessCollection { + fn assign( + &mut self, + var: Variable, + to: impl FnOnce() -> Result, + ) -> Result<(), Error> { + // We only care about advice wires here. + match var.0 { + Wire::Advice(index) => { + *self + .advice + .get_mut(index) + .and_then(|v| v.get_mut(var.1)) + .ok_or(Error::BoundsFailure)? = to()?; + } + _ => {} + } + Ok(()) + } + fn create_gate( &mut self, sa: F, @@ -66,6 +86,9 @@ impl Proof { // } } + let mut meta = MetaCircuit::default(); + let config = ConcreteCircuit::configure(&mut meta); + let mut witness = WitnessCollection { a: vec![], b: vec![], @@ -76,12 +99,9 @@ impl Proof { sc: vec![], sd: vec![], sm: vec![], + advice: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_advice_wires], }; - let mut meta = MetaCircuit::default(); - - let config = ConcreteCircuit::configure(&mut meta); - // Synthesize the circuit to obtain the witness and other information. circuit.synthesize(&mut witness, config)?; @@ -115,10 +135,21 @@ impl Proof { let c_commitment = params.commit_lagrange(&witness.c, c_blind).to_affine(); let d_commitment = params.commit_lagrange(&witness.d, d_blind).to_affine(); + let advice_blinds = vec![C::Scalar::one(); witness.advice.len()]; // TODO: not random + let advice_commitments = witness + .advice + .iter() + .zip(advice_blinds.iter()) + .map(|(poly, blind)| params.commit_lagrange(poly, *blind).to_affine()) + .collect(); + hash_point(&mut transcript, &a_commitment)?; hash_point(&mut transcript, &b_commitment)?; hash_point(&mut transcript, &c_commitment)?; hash_point(&mut transcript, &d_commitment)?; + for commitment in &advice_commitments { + hash_point(&mut transcript, commitment)?; + } let domain = &srs.domain; @@ -127,6 +158,12 @@ impl Proof { let (c_coset, c_poly) = domain.obtain_coset(witness.c); let (d_coset, d_poly) = domain.obtain_coset(witness.d); + let advice_polys: Vec<_> = witness + .advice + .into_iter() + .map(|poly| domain.obtain_coset(poly)) + .collect(); + // (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc) let mut h_poly = Vec::with_capacity(a_coset.len()); for ((((((((a, b), c), d), sa), sb), sc), sd), sm) in a_coset @@ -253,6 +290,7 @@ impl Proof { b_commitment, c_commitment, d_commitment, + advice_commitments, h_commitments, a_eval_x, b_eval_x, diff --git a/src/plonk/srs.rs b/src/plonk/srs.rs index c61f8cb..1c2e3b6 100644 --- a/src/plonk/srs.rs +++ b/src/plonk/srs.rs @@ -1,5 +1,5 @@ use super::{ - circuit::{Circuit, ConstraintSystem, MetaCircuit, Wire, Variable}, + circuit::{Circuit, ConstraintSystem, MetaCircuit, Variable, Wire}, domain::EvaluationDomain, Error, GATE_DEGREE, SRS, }; @@ -19,9 +19,28 @@ impl SRS { sc: Vec, sd: Vec, sm: Vec, + fixed: Vec>, } impl ConstraintSystem for Assembly { + fn assign( + &mut self, + var: Variable, + to: impl FnOnce() -> Result, + ) -> Result<(), Error> { + // We only care about fixed wires here. + match var.0 { + Wire::Fixed(index) => { + *self + .fixed + .get_mut(index) + .and_then(|v| v.get_mut(var.1)) + .ok_or(Error::BoundsFailure)? = to()?; + } + _ => {} + } + Ok(()) + } fn create_gate( &mut self, sa: F, @@ -46,18 +65,18 @@ impl SRS { } } + let mut meta = MetaCircuit::default(); + let config = ConcreteCircuit::configure(&mut meta); + let mut assembly: Assembly = Assembly { sa: vec![], sb: vec![], sc: vec![], sd: vec![], sm: vec![], + fixed: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_fixed_wires], }; - let mut meta = MetaCircuit::default(); - - let config = ConcreteCircuit::configure(&mut meta); - // Synthesize the circuit to obtain SRS circuit.synthesize(&mut assembly, config)?; @@ -84,6 +103,12 @@ impl SRS { .commit_lagrange(&assembly.sm, C::Scalar::one()) .to_affine(); + let fixed_commitments = assembly + .fixed + .iter() + .map(|poly| params.commit_lagrange(poly, C::Scalar::one()).to_affine()) + .collect(); + let domain = EvaluationDomain::new(GATE_DEGREE, params.k); let sa = domain.obtain_coset(assembly.sa); @@ -92,6 +117,12 @@ impl SRS { let sd = domain.obtain_coset(assembly.sd); let sm = domain.obtain_coset(assembly.sm); + let fixed_polys = assembly + .fixed + .into_iter() + .map(|poly| domain.obtain_coset(poly)) + .collect(); + Ok(SRS { sa, sb, @@ -104,6 +135,10 @@ impl SRS { sd_commitment, sm_commitment, domain, + + fixed_commitments, + fixed_polys, + meta, }) } } diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 4759fd4..e155cb1 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -22,6 +22,11 @@ impl Proof { hash_point(&mut transcript, &self.d_commitment) .expect("proof cannot contain points at infinity"); + for commitment in &self.advice_commitments { + hash_point(&mut transcript, commitment) + .expect("proof cannot contain points at infinity"); + } + for c in &self.h_commitments { hash_point(&mut transcript, c).expect("proof cannot contain points at infinity"); } From c20f3fdf1aaa87c0c366d57f60fa3d4a68817464 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 23 Aug 2020 13:26:04 -0600 Subject: [PATCH 04/15] Give fixed and advice wires separate types --- src/plonk.rs | 52 ++++++++++++++++++------------------------- src/plonk/circuit.rs | 53 ++++++++++++++++++++++++++++++-------------- src/plonk/prover.rs | 35 +++++++++++++++++------------ src/plonk/srs.rs | 37 ++++++++++++++++++------------- 4 files changed, 100 insertions(+), 77 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index 9385609..ef6e997 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -112,14 +112,14 @@ fn test_proving() { let params: Params = Params::new::>(K); struct MyConfig { - a: Wire, - b: Wire, - c: Wire, + a: AdviceWire, + b: AdviceWire, + c: AdviceWire, - sa: Wire, - sb: Wire, - sc: Wire, - sm: Wire, + sa: FixedWire, + sb: FixedWire, + sc: FixedWire, + sm: FixedWire, } struct MyCircuit { a: Option, @@ -174,36 +174,26 @@ fn test_proving() { // Similar to the above... let mut row = 0; for _ in 0..10 { - cs.assign(Variable(config.a, row), || { - self.a.ok_or(Error::SynthesisError) - })?; - cs.assign(Variable(config.b, row), || { - self.a.ok_or(Error::SynthesisError) - })?; + cs.assign_advice(config.a, row, || self.a.ok_or(Error::SynthesisError))?; + cs.assign_advice(config.b, row, || self.a.ok_or(Error::SynthesisError))?; let a_squared = self.a.map(|a| a.square()); - cs.assign(Variable(config.c, row), || { - self.a.ok_or(Error::SynthesisError) - })?; + cs.assign_advice(config.c, row, || self.a.ok_or(Error::SynthesisError))?; // Multiplication gate - cs.assign(Variable(config.sa, row), || Ok(Field::zero()))?; - cs.assign(Variable(config.sb, row), || Ok(Field::zero()))?; - cs.assign(Variable(config.sc, row), || Ok(Field::one()))?; - cs.assign(Variable(config.sm, row), || Ok(Field::one()))?; + cs.assign_fixed(config.sa, row, || Ok(Field::zero()))?; + cs.assign_fixed(config.sb, row, || Ok(Field::zero()))?; + cs.assign_fixed(config.sc, row, || Ok(Field::one()))?; + cs.assign_fixed(config.sm, row, || Ok(Field::one()))?; row += 1; - cs.assign(Variable(config.a, row), || { - self.a.ok_or(Error::SynthesisError) - })?; - cs.assign(Variable(config.b, row), || { - a_squared.ok_or(Error::SynthesisError) - })?; + cs.assign_advice(config.a, row, || self.a.ok_or(Error::SynthesisError))?; + cs.assign_advice(config.b, row, || a_squared.ok_or(Error::SynthesisError))?; let fin = a_squared.and_then(|a_squared| self.a.map(|a| a + a_squared)); - cs.assign(Variable(config.c, row), || fin.ok_or(Error::SynthesisError))?; + cs.assign_advice(config.c, row, || fin.ok_or(Error::SynthesisError))?; // Addition gate - cs.assign(Variable(config.sa, row), || Ok(Field::one()))?; - cs.assign(Variable(config.sb, row), || Ok(Field::one()))?; - cs.assign(Variable(config.sc, row), || Ok(Field::one()))?; - cs.assign(Variable(config.sm, row), || Ok(Field::zero()))?; + cs.assign_fixed(config.sa, row, || Ok(Field::one()))?; + cs.assign_fixed(config.sb, row, || Ok(Field::one()))?; + cs.assign_fixed(config.sc, row, || Ok(Field::one()))?; + cs.assign_fixed(config.sm, row, || Ok(Field::zero()))?; row += 1; } diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 3f4f872..6f3d165 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -16,12 +16,16 @@ pub enum Wire { C, /// D wires D, - /// Fixed wires - Fixed(usize), - /// Advice wires - Advice(usize), } +/// This represents a wire which has a fixed (permanent) value +#[derive(Copy, Clone, Debug)] +pub struct FixedWire(pub usize); + +/// This represents a wire which has a witness-specific value +#[derive(Copy, Clone, Debug)] +pub struct AdviceWire(pub usize); + /// Represents a pointer to a value in the constraint system. #[derive(Clone, Debug)] pub struct Variable(pub Wire, pub usize); @@ -29,9 +33,21 @@ pub struct Variable(pub Wire, pub usize); /// This trait allows a [`Circuit`] to direct some backend to assign a witness /// for a constraint system. pub trait ConstraintSystem { - /// Assign a wire value - fn assign(&mut self, var: Variable, to: impl FnOnce() -> Result) - -> Result<(), Error>; + /// Assign an advice wire value (witness) + fn assign_advice( + &mut self, + wire: AdviceWire, + row: usize, + to: impl FnOnce() -> Result, + ) -> Result<(), Error>; + + /// Assign a fixed value + fn assign_fixed( + &mut self, + wire: FixedWire, + row: usize, + to: impl FnOnce() -> Result, + ) -> Result<(), Error>; /// Creates a gate. fn create_gate( @@ -93,8 +109,10 @@ pub trait Circuit { /// Low-degree polynomial representing an identity that must hold over the committed wires. #[derive(Clone, Debug)] pub enum Polynomial { - /// This is a wire queried at a certain relative location - Wire(Wire, isize), + /// This is a fixed wire queried at a certain relative location + Fixed(FixedWire, isize), + /// This is an advice (witness) wire queried at a certain relative location + Advice(AdviceWire, isize), /// This is the sum of two polynomials Sum(Box>, Box>), /// This is the product of two polynomials @@ -106,10 +124,11 @@ pub enum Polynomial { impl Polynomial { fn degree(&self) -> usize { match self { - Polynomial::Wire(_, _) => 1, - Polynomial::Sum(ref a, ref b) => max(a.degree(), b.degree()), - Polynomial::Product(ref a, ref b) => a.degree() + b.degree(), - Polynomial::Scaled(ref poly, _) => poly.degree(), + Polynomial::Fixed(_, _) => 1, + Polynomial::Advice(_, _) => 1, + Polynomial::Sum(a, b) => max(a.degree(), b.degree()), + Polynomial::Product(a, b) => a.degree() + b.degree(), + Polynomial::Scaled(poly, _) => poly.degree(), } } } @@ -158,14 +177,14 @@ impl Default for MetaCircuit { impl MetaCircuit { /// Allocate a new fixed wire - pub fn fixed_wire(&mut self) -> Wire { - let tmp = Wire::Fixed(self.num_fixed_wires); + pub fn fixed_wire(&mut self) -> FixedWire { + let tmp = FixedWire(self.num_fixed_wires); self.num_fixed_wires += 1; tmp } /// Allocate a new advice wire - pub fn advice_wire(&mut self) -> Wire { - let tmp = Wire::Advice(self.num_advice_wires); + pub fn advice_wire(&mut self) -> AdviceWire { + let tmp = AdviceWire(self.num_advice_wires); self.num_advice_wires += 1; tmp } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index b99e015..4f3c854 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -1,5 +1,5 @@ use super::{ - circuit::{Circuit, ConstraintSystem, MetaCircuit, Variable, Wire}, + circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit, Variable, Wire}, hash_point, Error, Proof, SRS, }; use crate::arithmetic::{ @@ -35,22 +35,29 @@ impl Proof { } impl ConstraintSystem for WitnessCollection { - fn assign( + fn assign_advice( &mut self, - var: Variable, + wire: AdviceWire, + row: usize, to: impl FnOnce() -> Result, ) -> Result<(), Error> { - // We only care about advice wires here. - match var.0 { - Wire::Advice(index) => { - *self - .advice - .get_mut(index) - .and_then(|v| v.get_mut(var.1)) - .ok_or(Error::BoundsFailure)? = to()?; - } - _ => {} - } + *self + .advice + .get_mut(wire.0) + .and_then(|v| v.get_mut(row)) + .ok_or(Error::BoundsFailure)? = to()?; + + Ok(()) + } + + fn assign_fixed( + &mut self, + _: FixedWire, + _: usize, + _: impl FnOnce() -> Result, + ) -> Result<(), Error> { + // We only care about advice wires here + Ok(()) } diff --git a/src/plonk/srs.rs b/src/plonk/srs.rs index 1c2e3b6..5a94d97 100644 --- a/src/plonk/srs.rs +++ b/src/plonk/srs.rs @@ -1,5 +1,5 @@ use super::{ - circuit::{Circuit, ConstraintSystem, MetaCircuit, Variable, Wire}, + circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit, Variable, Wire}, domain::EvaluationDomain, Error, GATE_DEGREE, SRS, }; @@ -23,24 +23,31 @@ impl SRS { } impl ConstraintSystem for Assembly { - fn assign( + fn assign_advice( &mut self, - var: Variable, - to: impl FnOnce() -> Result, + _: AdviceWire, + _: usize, + _: impl FnOnce() -> Result, ) -> Result<(), Error> { - // We only care about fixed wires here. - match var.0 { - Wire::Fixed(index) => { - *self - .fixed - .get_mut(index) - .and_then(|v| v.get_mut(var.1)) - .ok_or(Error::BoundsFailure)? = to()?; - } - _ => {} - } + // We only care about fixed wires here Ok(()) } + + fn assign_fixed( + &mut self, + wire: FixedWire, + row: usize, + to: impl FnOnce() -> Result, + ) -> Result<(), Error> { + *self + .fixed + .get_mut(wire.0) + .and_then(|v| v.get_mut(row)) + .ok_or(Error::BoundsFailure)? = to()?; + + Ok(()) + } + fn create_gate( &mut self, sa: F, From 36f9e87056ddc49976283443e6394f205fe4f4db Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 24 Aug 2020 08:28:42 -0600 Subject: [PATCH 05/15] Implementation of gate/query API --- src/plonk.rs | 17 ++++++++-- src/plonk/circuit.rs | 75 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index ef6e997..d6d9188 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -45,7 +45,7 @@ pub struct SRS { fixed_commitments: Vec, fixed_polys: Vec<(Vec, Vec)>, - meta: MetaCircuit, + meta: MetaCircuit, } /// This is an object which represents a (Turbo)PLONK proof. @@ -128,7 +128,7 @@ fn test_proving() { impl Circuit for MyCircuit { type Config = MyConfig; - fn configure(meta: &mut MetaCircuit) -> MyConfig { + fn configure(meta: &mut MetaCircuit) -> MyConfig { let a = meta.advice_wire(); let b = meta.advice_wire(); let c = meta.advice_wire(); @@ -138,6 +138,19 @@ fn test_proving() { let sc = meta.fixed_wire(); let sm = meta.fixed_wire(); + meta.create_gate(|meta| { + let a = meta.query_advice(a, 0); + let b = meta.query_advice(b, 0); + let c = meta.query_advice(c, 0); + + let sa = meta.query_fixed(sa, 0); + let sb = meta.query_fixed(sb, 0); + let sc = meta.query_fixed(sc, 0); + let sm = meta.query_fixed(sm, 0); + + a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one())) + }); + MyConfig { a, b, diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 6f3d165..c04ec7e 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -1,5 +1,6 @@ use core::cmp::max; use core::ops::{Add, Mul}; +use std::collections::HashMap; use super::Error; use crate::arithmetic::Field; @@ -19,11 +20,11 @@ pub enum Wire { } /// This represents a wire which has a fixed (permanent) value -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct FixedWire(pub usize); /// This represents a wire which has a witness-specific value -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct AdviceWire(pub usize); /// Represents a pointer to a value in the constraint system. @@ -94,7 +95,7 @@ pub trait Circuit { /// The circuit is given an opportunity to describe the exact gate /// arrangement, wire arrangement, etc. - fn configure(meta: &mut MetaCircuit) -> Self::Config; + fn configure(meta: &mut MetaCircuit) -> Self::Config; /// Given the provided `cs`, synthesize the circuit. The concrete type of /// the caller will be different depending on the context, and they may or @@ -121,6 +122,36 @@ pub enum Polynomial { Scaled(Box>, F), } +impl Polynomial { + fn evaluate( + &self, + fixed_wire: &impl Fn(FixedWire, isize) -> T, + advice_wire: &impl Fn(AdviceWire, isize) -> T, + sum: &impl Fn(T, T) -> T, + product: &impl Fn(T, T) -> T, + scaled: &impl Fn(T, F) -> T, + ) -> T { + match self { + Polynomial::Fixed(a, location) => fixed_wire(*a, *location), + Polynomial::Advice(a, location) => advice_wire(*a, *location), + Polynomial::Sum(a, b) => { + let a = a.evaluate(fixed_wire, advice_wire, sum, product, scaled); + let b = b.evaluate(fixed_wire, advice_wire, sum, product, scaled); + sum(a, b) + } + Polynomial::Product(a, b) => { + let a = a.evaluate(fixed_wire, advice_wire, sum, product, scaled); + let b = b.evaluate(fixed_wire, advice_wire, sum, product, scaled); + product(a, b) + } + Polynomial::Scaled(a, f) => { + let a = a.evaluate(fixed_wire, advice_wire, sum, product, scaled); + scaled(a, *f) + } + } + } +} + impl Polynomial { fn degree(&self) -> usize { match self { @@ -157,25 +188,51 @@ impl Mul for Polynomial { /// This is a description of the circuit environment, such as the gate, wire and /// permutation arrangements. #[derive(Debug, Clone)] -pub struct MetaCircuit { +pub struct MetaCircuit { pub(crate) num_fixed_wires: usize, pub(crate) num_advice_wires: usize, // permutations: Vec>, - // gates: Vec, - // queries: HashSet<(Wire, usize)>, + gates: Vec>, + advice_queries: HashMap<(AdviceWire, isize), usize>, + fixed_queries: HashMap<(FixedWire, isize), usize>, // num_queries: usize, } -impl Default for MetaCircuit { - fn default() -> MetaCircuit { +impl Default for MetaCircuit { + fn default() -> MetaCircuit { MetaCircuit { num_fixed_wires: 0, num_advice_wires: 0, + gates: vec![], + fixed_queries: HashMap::new(), + advice_queries: HashMap::new(), } } } -impl MetaCircuit { +impl MetaCircuit { + /// Query a fixed wire at a relative position + pub fn query_fixed(&mut self, wire: FixedWire, at: isize) -> Polynomial { + let len = self.fixed_queries.len(); + self.fixed_queries.entry((wire, at)).or_insert_with(|| len); + + Polynomial::Fixed(wire, at) + } + + /// Query an advice wire at a relative position + pub fn query_advice(&mut self, wire: AdviceWire, at: isize) -> Polynomial { + let len = self.advice_queries.len(); + self.advice_queries.entry((wire, at)).or_insert_with(|| len); + + Polynomial::Advice(wire, at) + } + + /// Create a new gate + pub fn create_gate(&mut self, f: impl FnOnce(&mut Self) -> Polynomial) { + let poly = f(self); + self.gates.push(poly); + } + /// Allocate a new fixed wire pub fn fixed_wire(&mut self) -> FixedWire { let tmp = FixedWire(self.num_fixed_wires); From 24b7e6cc7c8fb7344e6568783824927f2453f1ec Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 24 Aug 2020 08:36:41 -0600 Subject: [PATCH 06/15] Run SRS synthesis on an empty circuit in test. --- src/plonk.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plonk.rs b/src/plonk.rs index d6d9188..a10e168 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -218,8 +218,10 @@ fn test_proving() { a: Some((-Fp::from_u64(2) + Fp::ROOT_OF_UNITY).pow(&[100, 0, 0, 0])), }; + let empty_circuit: MyCircuit = MyCircuit { a: None }; + // Initialize the SRS - let srs = SRS::generate(¶ms, &circuit).expect("SRS generation should not fail"); + let srs = SRS::generate(¶ms, &empty_circuit).expect("SRS generation should not fail"); // Create a proof let proof = Proof::create::, DummyHash, _>(¶ms, &srs, &circuit) From 6051814c4bbe535e0c1957798acfd343c964e061 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 24 Aug 2020 13:50:52 -0600 Subject: [PATCH 07/15] Split coset step up so that we can query wires at multiple spots. --- src/plonk/circuit.rs | 16 +++++++------- src/plonk/domain.rs | 52 +++++++++++++++++++++++++++++++------------- src/plonk/prover.rs | 19 +++++++++++----- src/plonk/srs.rs | 31 ++++++++++++++++---------- 4 files changed, 79 insertions(+), 39 deletions(-) diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index c04ec7e..066b48d 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -111,9 +111,9 @@ pub trait Circuit { #[derive(Clone, Debug)] pub enum Polynomial { /// This is a fixed wire queried at a certain relative location - Fixed(FixedWire, isize), + Fixed(FixedWire, i32), /// This is an advice (witness) wire queried at a certain relative location - Advice(AdviceWire, isize), + Advice(AdviceWire, i32), /// This is the sum of two polynomials Sum(Box>, Box>), /// This is the product of two polynomials @@ -125,8 +125,8 @@ pub enum Polynomial { impl Polynomial { fn evaluate( &self, - fixed_wire: &impl Fn(FixedWire, isize) -> T, - advice_wire: &impl Fn(AdviceWire, isize) -> T, + fixed_wire: &impl Fn(FixedWire, i32) -> T, + advice_wire: &impl Fn(AdviceWire, i32) -> T, sum: &impl Fn(T, T) -> T, product: &impl Fn(T, T) -> T, scaled: &impl Fn(T, F) -> T, @@ -193,8 +193,8 @@ pub struct MetaCircuit { pub(crate) num_advice_wires: usize, // permutations: Vec>, gates: Vec>, - advice_queries: HashMap<(AdviceWire, isize), usize>, - fixed_queries: HashMap<(FixedWire, isize), usize>, + advice_queries: HashMap<(AdviceWire, i32), usize>, + fixed_queries: HashMap<(FixedWire, i32), usize>, // num_queries: usize, } @@ -212,7 +212,7 @@ impl Default for MetaCircuit { impl MetaCircuit { /// Query a fixed wire at a relative position - pub fn query_fixed(&mut self, wire: FixedWire, at: isize) -> Polynomial { + pub fn query_fixed(&mut self, wire: FixedWire, at: i32) -> Polynomial { let len = self.fixed_queries.len(); self.fixed_queries.entry((wire, at)).or_insert_with(|| len); @@ -220,7 +220,7 @@ impl MetaCircuit { } /// Query an advice wire at a relative position - pub fn query_advice(&mut self, wire: AdviceWire, at: isize) -> Polynomial { + pub fn query_advice(&mut self, wire: AdviceWire, at: i32) -> Polynomial { let len = self.advice_queries.len(); self.advice_queries.entry((wire, at)).or_insert_with(|| len); diff --git a/src/plonk/domain.rs b/src/plonk/domain.rs index 1aa70fa..cf1fde7 100644 --- a/src/plonk/domain.rs +++ b/src/plonk/domain.rs @@ -8,6 +8,7 @@ pub struct EvaluationDomain { n: u64, k: u32, extended_k: u32, + omega: G::Scalar, omega_inv: G::Scalar, extended_omega: G::Scalar, extended_omega_inv: G::Scalar, @@ -91,6 +92,7 @@ impl EvaluationDomain { n, k, extended_k, + omega, omega_inv, extended_omega, extended_omega_inv, @@ -103,32 +105,42 @@ impl EvaluationDomain { } } - /// This takes us from an n-length vector into the coset evaluation domain. - /// Also returns the polynomial. + /// This takes us from an n-length vector into the coefficient form. /// /// This function will panic if the provided vector is not the correct /// length. - pub fn obtain_coset(&self, mut a: Vec) -> (Vec, Vec) { + pub fn obtain_poly(&self, mut a: Vec) -> Vec { assert_eq!(a.len(), 1 << self.k); // Perform inverse FFT to obtain the polynomial in coefficient form Self::ifft(&mut a, self.omega_inv, self.k, self.ifft_divisor); - // Keep this polynomial around; we'll need to evaluate it at arbitrary - // points later. - let old = a.clone(); + a + } - // Distributes powers so that an FFT will move us into the coset - // evaluation domain. - Self::distribute_powers(&mut a, self.g_coset); + /// This takes us from an n-length coefficient vector into the coset + /// evaluation domain. + /// + /// This function will panic if the provided vector is not the correct + /// length. + pub fn obtain_coset(&self, mut a: Vec, index: i32) -> Vec { + assert_eq!(a.len(), 1 << self.k); - // Resize to account for the quotient polynomial's size + assert!(index != i32::MIN); + if index == 0 { + Self::distribute_powers_zeta(&mut a, self.g_coset); + } else { + let mut g = G::Scalar::ZETA; + if index > 0 { + g *= &self.omega.pow_vartime(&[index as u64, 0, 0, 0]); + } else { + g *= &self.omega_inv.pow_vartime(&[index.abs() as u64, 0, 0, 0]); + } + Self::distribute_powers(&mut a, g); + } a.resize(1 << self.extended_k, G::group_zero()); - - // Move into coset evaluation domain best_fft(&mut a, self.extended_omega, self.extended_k); - - (a, old) + a } /// This takes us from the coset evaluation domain and gets us the quotient @@ -176,7 +188,7 @@ impl EvaluationDomain { h_poly } - fn distribute_powers(mut a: &mut [G], g: G::Scalar) { + fn distribute_powers_zeta(mut a: &mut [G], g: G::Scalar) { let coset_powers = [g, g.square()]; parallelize(&mut a, |a, mut index| { for a in a { @@ -190,6 +202,16 @@ impl EvaluationDomain { }); } + fn distribute_powers(mut a: &mut [G], g: G::Scalar) { + parallelize(&mut a, |a, index| { + let mut cur = g.pow_vartime(&[index as u64, 0, 0, 0]); + for a in a { + a.group_scale(&cur); + cur *= &g; + } + }); + } + fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) { best_fft(a, omega_inv, log_n); parallelize(a, |a, _| { diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 4f3c854..012887e 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -160,15 +160,24 @@ impl Proof { let domain = &srs.domain; - let (a_coset, a_poly) = domain.obtain_coset(witness.a); - let (b_coset, b_poly) = domain.obtain_coset(witness.b); - let (c_coset, c_poly) = domain.obtain_coset(witness.c); - let (d_coset, d_poly) = domain.obtain_coset(witness.d); + let a_poly = domain.obtain_poly(witness.a); + let b_poly = domain.obtain_poly(witness.b); + let c_poly = domain.obtain_poly(witness.c); + let d_poly = domain.obtain_poly(witness.d); + + let a_coset = domain.obtain_coset(a_poly.clone(), 0); + let b_coset = domain.obtain_coset(b_poly.clone(), 0); + let c_coset = domain.obtain_coset(c_poly.clone(), 0); + let d_coset = domain.obtain_coset(d_poly.clone(), 0); let advice_polys: Vec<_> = witness .advice .into_iter() - .map(|poly| domain.obtain_coset(poly)) + .map(|poly| { + let poly = domain.obtain_poly(poly); + let coset = domain.obtain_coset(poly.clone(), 0); + (poly, coset) + }) .collect(); // (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc) diff --git a/src/plonk/srs.rs b/src/plonk/srs.rs index 5a94d97..8ce141b 100644 --- a/src/plonk/srs.rs +++ b/src/plonk/srs.rs @@ -118,24 +118,33 @@ impl SRS { let domain = EvaluationDomain::new(GATE_DEGREE, params.k); - let sa = domain.obtain_coset(assembly.sa); - let sb = domain.obtain_coset(assembly.sb); - let sc = domain.obtain_coset(assembly.sc); - let sd = domain.obtain_coset(assembly.sd); - let sm = domain.obtain_coset(assembly.sm); + let sa_poly = domain.obtain_poly(assembly.sa); + let sb_poly = domain.obtain_poly(assembly.sb); + let sc_poly = domain.obtain_poly(assembly.sc); + let sd_poly = domain.obtain_poly(assembly.sd); + let sm_poly = domain.obtain_poly(assembly.sm); + let sa_coset = domain.obtain_coset(sa_poly.clone(), 0); + let sb_coset = domain.obtain_coset(sb_poly.clone(), 0); + let sc_coset = domain.obtain_coset(sc_poly.clone(), 0); + let sd_coset = domain.obtain_coset(sd_poly.clone(), 0); + let sm_coset = domain.obtain_coset(sm_poly.clone(), 0); let fixed_polys = assembly .fixed .into_iter() - .map(|poly| domain.obtain_coset(poly)) + .map(|poly| { + let coeffs = domain.obtain_poly(poly); + let coset = domain.obtain_coset(coeffs.clone(), 0); + (coeffs, coset) + }) .collect(); Ok(SRS { - sa, - sb, - sc, - sd, - sm, + sa: (sa_coset, sa_poly), + sb: (sb_coset, sb_poly), + sc: (sc_coset, sc_poly), + sd: (sd_coset, sd_poly), + sm: (sm_coset, sm_poly), sa_commitment, sb_commitment, sc_commitment, From ad106f111962df7782fc2f310fe588f170a41c71 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 27 Aug 2020 10:10:55 -0600 Subject: [PATCH 08/15] (WIP) broken implementation of generalized PLONK --- src/arithmetic.rs | 23 +++ src/plonk.rs | 53 +----- src/plonk/circuit.rs | 117 +++++------- src/plonk/domain.rs | 18 +- src/plonk/prover.rs | 426 +++++++++++++++++++++++++----------------- src/plonk/srs.rs | 103 ++-------- src/plonk/verifier.rs | 182 +++++++++++++++--- 7 files changed, 509 insertions(+), 413 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 39d3920..49f13c8 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -313,6 +313,29 @@ pub fn compute_inner_product(a: &[F], b: &[F]) -> F { acc } +/// Divides polynomial `a` in `X` by `X - b` with +/// no remainder. +pub fn kate_division<'a, F: Field, I: IntoIterator>(a: I, mut b: F) -> Vec +where + I::IntoIter: DoubleEndedIterator + ExactSizeIterator, +{ + b = -b; + let a = a.into_iter(); + + let mut q = vec![F::zero(); a.len() - 1]; + + let mut tmp = F::zero(); + for (q, r) in q.iter_mut().rev().zip(a.rev()) { + let mut lead_coeff = *r; + lead_coeff.sub_assign(&tmp); + *q = lead_coeff; + tmp = lead_coeff; + tmp.mul_assign(&b); + } + + q +} + /// This simple utility function will parallelize an operation that is to be /// performed over a mutable slice. pub fn parallelize(v: &mut [T], f: F) { diff --git a/src/plonk.rs b/src/plonk.rs index a10e168..d2217da 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -23,28 +23,15 @@ pub use verifier::*; use domain::EvaluationDomain; -// TODO: remove this -const GATE_DEGREE: u32 = 3; - /// This is a structured reference string (SRS) that is (deterministically) /// computed from a specific circuit and parameters for the polynomial /// commitment scheme. #[derive(Debug)] pub struct SRS { - sa: (Vec, Vec), - sb: (Vec, Vec), - sc: (Vec, Vec), - sd: (Vec, Vec), - sm: (Vec, Vec), - sa_commitment: C, - sb_commitment: C, - sc_commitment: C, - sd_commitment: C, - sm_commitment: C, domain: EvaluationDomain, - fixed_commitments: Vec, - fixed_polys: Vec<(Vec, Vec)>, + fixed_polys: Vec>, + fixed_cosets: Vec>, meta: MetaCircuit, } @@ -52,22 +39,13 @@ pub struct SRS { // This structure must never allow points at infinity. #[derive(Debug, Clone)] pub struct Proof { - a_commitment: C, - b_commitment: C, - c_commitment: C, - d_commitment: C, advice_commitments: Vec, h_commitments: Vec, - a_eval_x: C::Scalar, - b_eval_x: C::Scalar, - c_eval_x: C::Scalar, - d_eval_x: C::Scalar, - sa_eval_x: C::Scalar, - sb_eval_x: C::Scalar, - sc_eval_x: C::Scalar, - sd_eval_x: C::Scalar, - sm_eval_x: C::Scalar, + advice_evals_x: Vec, + fixed_evals_x: Vec, h_evals_x: Vec, + f_commitment: C, + q_evals: Vec, opening: OpeningProof, } @@ -167,30 +145,13 @@ fn test_proving() { cs: &mut impl ConstraintSystem, config: MyConfig, ) -> Result<(), Error> { - for _ in 0..10 { - let (_, _, _, _) = cs.multiply(|| { - let a = self.a.ok_or(Error::SynthesisError)?; - let a2 = a.square(); - Ok((a, a, a2)) - })?; - //cs.copy(a, b); - let (_, _, _, _) = cs.add(|| { - let a = self.a.ok_or(Error::SynthesisError)?; - let a2 = a.square(); - let a3 = a + a2; - Ok((a, a2, a3)) - })?; - //cs.copy(a, d); - //cs.copy(c, e); - } - // Similar to the above... let mut row = 0; for _ in 0..10 { cs.assign_advice(config.a, row, || self.a.ok_or(Error::SynthesisError))?; cs.assign_advice(config.b, row, || self.a.ok_or(Error::SynthesisError))?; let a_squared = self.a.map(|a| a.square()); - cs.assign_advice(config.c, row, || self.a.ok_or(Error::SynthesisError))?; + cs.assign_advice(config.c, row, || a_squared.ok_or(Error::SynthesisError))?; // Multiplication gate cs.assign_fixed(config.sa, row, || Ok(Field::zero()))?; cs.assign_fixed(config.sb, row, || Ok(Field::zero()))?; diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 066b48d..5466260 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -5,20 +5,6 @@ use std::collections::HashMap; use super::Error; use crate::arithmetic::Field; -/// This represents a PLONK wire, which could be a fixed (selector) wire or an -/// advice wire. -#[derive(Copy, Clone, Debug)] -pub enum Wire { - /// A wires - A, - /// B wires - B, - /// C wires - C, - /// D wires - D, -} - /// This represents a wire which has a fixed (permanent) value #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct FixedWire(pub usize); @@ -27,10 +13,6 @@ pub struct FixedWire(pub usize); #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct AdviceWire(pub usize); -/// Represents a pointer to a value in the constraint system. -#[derive(Clone, Debug)] -pub struct Variable(pub Wire, pub usize); - /// This trait allows a [`Circuit`] to direct some backend to assign a witness /// for a constraint system. pub trait ConstraintSystem { @@ -50,39 +32,6 @@ pub trait ConstraintSystem { to: impl FnOnce() -> Result, ) -> Result<(), Error>; - /// Creates a gate. - fn create_gate( - &mut self, - sa: F, - sb: F, - sc: F, - sd: F, - sm: F, - f: impl Fn() -> Result<(F, F, F, F), Error>, - ) -> Result<(Variable, Variable, Variable, Variable), Error>; - - /// a * b - c = 0 - fn multiply( - &mut self, - f: impl Fn() -> Result<(F, F, F), Error>, - ) -> Result<(Variable, Variable, Variable, Variable), Error> { - self.create_gate(F::zero(), F::zero(), F::one(), F::zero(), F::one(), || { - let (a, b, c) = f()?; - Ok((a, b, c, F::zero())) - }) - } - - /// a + b - c = 0 - fn add( - &mut self, - f: impl Fn() -> Result<(F, F, F), Error>, - ) -> Result<(Variable, Variable, Variable, Variable), Error> { - self.create_gate(F::one(), F::one(), F::one(), F::zero(), F::zero(), || { - let (a, b, c) = f()?; - Ok((a, b, c, F::zero())) - }) - } - // fn copy(&mut self, left: Wire, right: Wire); } @@ -111,9 +60,9 @@ pub trait Circuit { #[derive(Clone, Debug)] pub enum Polynomial { /// This is a fixed wire queried at a certain relative location - Fixed(FixedWire, i32), + Fixed(usize), /// This is an advice (witness) wire queried at a certain relative location - Advice(AdviceWire, i32), + Advice(usize), /// This is the sum of two polynomials Sum(Box>, Box>), /// This is the product of two polynomials @@ -123,17 +72,19 @@ pub enum Polynomial { } impl Polynomial { - fn evaluate( + /// Evaluate the polynomial using the provided closures to perform the + /// operations. + pub fn evaluate( &self, - fixed_wire: &impl Fn(FixedWire, i32) -> T, - advice_wire: &impl Fn(AdviceWire, i32) -> T, + fixed_wire: &impl Fn(usize) -> T, + advice_wire: &impl Fn(usize) -> T, sum: &impl Fn(T, T) -> T, product: &impl Fn(T, T) -> T, scaled: &impl Fn(T, F) -> T, ) -> T { match self { - Polynomial::Fixed(a, location) => fixed_wire(*a, *location), - Polynomial::Advice(a, location) => advice_wire(*a, *location), + Polynomial::Fixed(index) => fixed_wire(*index), + Polynomial::Advice(index) => advice_wire(*index), Polynomial::Sum(a, b) => { let a = a.evaluate(fixed_wire, advice_wire, sum, product, scaled); let b = b.evaluate(fixed_wire, advice_wire, sum, product, scaled); @@ -150,13 +101,12 @@ impl Polynomial { } } } -} -impl Polynomial { - fn degree(&self) -> usize { + /// Compute the degree of this polynomial + pub fn degree(&self) -> usize { match self { - Polynomial::Fixed(_, _) => 1, - Polynomial::Advice(_, _) => 1, + Polynomial::Fixed(_) => 1, + Polynomial::Advice(_) => 1, Polynomial::Sum(a, b) => max(a.degree(), b.degree()), Polynomial::Product(a, b) => a.degree() + b.degree(), Polynomial::Scaled(poly, _) => poly.degree(), @@ -192,20 +142,24 @@ pub struct MetaCircuit { pub(crate) num_fixed_wires: usize, pub(crate) num_advice_wires: usize, // permutations: Vec>, - gates: Vec>, - advice_queries: HashMap<(AdviceWire, i32), usize>, - fixed_queries: HashMap<(FixedWire, i32), usize>, - // num_queries: usize, + pub(crate) gates: Vec>, + pub(crate) advice_queries: Vec<(AdviceWire, i32)>, + pub(crate) fixed_queries: Vec<(FixedWire, i32)>, + pub(crate) query_rows: HashMap, } impl Default for MetaCircuit { fn default() -> MetaCircuit { + let mut query_rows = HashMap::new(); + query_rows.insert(0, 0); + MetaCircuit { num_fixed_wires: 0, num_advice_wires: 0, gates: vec![], - fixed_queries: HashMap::new(), - advice_queries: HashMap::new(), + fixed_queries: Vec::new(), + advice_queries: Vec::new(), + query_rows, } } } @@ -213,18 +167,30 @@ impl Default for MetaCircuit { impl MetaCircuit { /// Query a fixed wire at a relative position pub fn query_fixed(&mut self, wire: FixedWire, at: i32) -> Polynomial { - let len = self.fixed_queries.len(); - self.fixed_queries.entry((wire, at)).or_insert_with(|| len); + { + let len = self.query_rows.len(); + self.query_rows.entry(at).or_insert(len); + } - Polynomial::Fixed(wire, at) + // TODO: check for existing query so we don't make redundant queries + let index = self.fixed_queries.len(); + self.fixed_queries.push((wire, at)); + + Polynomial::Fixed(index) } /// Query an advice wire at a relative position pub fn query_advice(&mut self, wire: AdviceWire, at: i32) -> Polynomial { - let len = self.advice_queries.len(); - self.advice_queries.entry((wire, at)).or_insert_with(|| len); + { + let len = self.query_rows.len(); + self.query_rows.entry(at).or_insert(len); + } - Polynomial::Advice(wire, at) + // TODO: check for existing query so we don't make redundant queries + let index = self.advice_queries.len(); + self.advice_queries.push((wire, at)); + + Polynomial::Advice(index) } /// Create a new gate @@ -239,6 +205,7 @@ impl MetaCircuit { self.num_fixed_wires += 1; tmp } + /// Allocate a new advice wire pub fn advice_wire(&mut self) -> AdviceWire { let tmp = AdviceWire(self.num_advice_wires); diff --git a/src/plonk/domain.rs b/src/plonk/domain.rs index cf1fde7..07ac13a 100644 --- a/src/plonk/domain.rs +++ b/src/plonk/domain.rs @@ -138,7 +138,7 @@ impl EvaluationDomain { } Self::distribute_powers(&mut a, g); } - a.resize(1 << self.extended_k, G::group_zero()); + a.resize(self.coset_len(), G::group_zero()); best_fft(&mut a, self.extended_omega, self.extended_k); a } @@ -149,7 +149,7 @@ impl EvaluationDomain { /// This function will panic if the provided vector is not the correct /// length. pub fn from_coset(&self, mut a: Vec) -> Vec { - assert_eq!(a.len(), 1 << self.extended_k); + assert_eq!(a.len(), self.coset_len()); // Inverse FFT Self::ifft( @@ -174,7 +174,7 @@ impl EvaluationDomain { /// This divides the polynomial (in the coset domain) by the vanishing /// polynomial. pub fn divide_by_vanishing_poly(&self, mut h_poly: Vec) -> Vec { - assert_eq!(h_poly.len(), 1 << self.extended_k); + assert_eq!(h_poly.len(), self.coset_len()); // Divide to obtain the quotient polynomial in the coset evaluation // domain. @@ -221,4 +221,16 @@ impl EvaluationDomain { } }); } + + pub fn coset_len(&self) -> usize { + 1 << self.extended_k + } + + pub fn get_omega(&self) -> G::Scalar { + self.omega + } + + pub fn get_omega_inv(&self) -> G::Scalar { + self.omega_inv + } } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 012887e..2a356d9 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -1,9 +1,10 @@ use super::{ - circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit, Variable, Wire}, + circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit}, hash_point, Error, Proof, SRS, }; use crate::arithmetic::{ - eval_polynomial, get_challenge_scalar, Challenge, Curve, CurveAffine, Field, + eval_polynomial, get_challenge_scalar, kate_division, parallelize, Challenge, Curve, + CurveAffine, Field, }; use crate::polycommit::Params; use crate::transcript::Hasher; @@ -22,15 +23,6 @@ impl Proof { circuit: &ConcreteCircuit, ) -> Result { struct WitnessCollection { - a: Vec, - b: Vec, - c: Vec, - d: Vec, - sa: Vec, - sb: Vec, - sc: Vec, - sd: Vec, - sm: Vec, advice: Vec>, } @@ -60,52 +52,12 @@ impl Proof { Ok(()) } - - fn create_gate( - &mut self, - sa: F, - sb: F, - sc: F, - sd: F, - sm: F, - f: impl Fn() -> Result<(F, F, F, F), Error>, - ) -> Result<(Variable, Variable, Variable, Variable), Error> { - let (a, b, c, d) = f()?; - let tmp = Ok(( - Variable(Wire::A, self.a.len()), - Variable(Wire::B, self.a.len()), - Variable(Wire::C, self.a.len()), - Variable(Wire::D, self.a.len()), - )); - self.a.push(a); - self.b.push(b); - self.c.push(c); - self.d.push(d); - self.sa.push(sa); - self.sb.push(sb); - self.sc.push(sc); - self.sd.push(sd); - self.sm.push(sm); - tmp - } - // fn copy(&mut self, left: Wire, right: Wire) { - // unimplemented!() - // } } let mut meta = MetaCircuit::default(); let config = ConcreteCircuit::configure(&mut meta); let mut witness = WitnessCollection { - a: vec![], - b: vec![], - c: vec![], - d: vec![], - sa: vec![], - sb: vec![], - sc: vec![], - sd: vec![], - sm: vec![], advice: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_advice_wires], }; @@ -115,34 +67,8 @@ impl Proof { // Create a transcript for obtaining Fiat-Shamir challenges. let mut transcript = HBase::init(C::Base::one()); - if witness.a.len() > params.n as usize { - // The polynomial commitment does not support a high enough degree - // polynomial to commit to our wires because this circuit has too - // many gates. - return Err(Error::IncompatibleParams); - } - - witness.a.resize(params.n as usize, C::Scalar::zero()); - witness.b.resize(params.n as usize, C::Scalar::zero()); - witness.c.resize(params.n as usize, C::Scalar::zero()); - witness.d.resize(params.n as usize, C::Scalar::zero()); - witness.sa.resize(params.n as usize, C::Scalar::zero()); - witness.sb.resize(params.n as usize, C::Scalar::zero()); - witness.sc.resize(params.n as usize, C::Scalar::zero()); - witness.sd.resize(params.n as usize, C::Scalar::zero()); - witness.sm.resize(params.n as usize, C::Scalar::zero()); - - // Compute commitments to the various wire values - let a_blind = C::Scalar::one(); // TODO: not random - let b_blind = C::Scalar::one(); // TODO: not random - let c_blind = C::Scalar::one(); // TODO: not random - let d_blind = C::Scalar::one(); // TODO: not random - let a_commitment = params.commit_lagrange(&witness.a, a_blind).to_affine(); - let b_commitment = params.commit_lagrange(&witness.b, b_blind).to_affine(); - let c_commitment = params.commit_lagrange(&witness.c, c_blind).to_affine(); - let d_commitment = params.commit_lagrange(&witness.d, d_blind).to_affine(); - - let advice_blinds = vec![C::Scalar::one(); witness.advice.len()]; // TODO: not random + // Compute commitments to advice wire polynomials + let advice_blinds: Vec<_> = witness.advice.iter().map(|_| C::Scalar::random()).collect(); let advice_commitments = witness .advice .iter() @@ -150,50 +76,77 @@ impl Proof { .map(|(poly, blind)| params.commit_lagrange(poly, *blind).to_affine()) .collect(); - hash_point(&mut transcript, &a_commitment)?; - hash_point(&mut transcript, &b_commitment)?; - hash_point(&mut transcript, &c_commitment)?; - hash_point(&mut transcript, &d_commitment)?; for commitment in &advice_commitments { hash_point(&mut transcript, commitment)?; } let domain = &srs.domain; - let a_poly = domain.obtain_poly(witness.a); - let b_poly = domain.obtain_poly(witness.b); - let c_poly = domain.obtain_poly(witness.c); - let d_poly = domain.obtain_poly(witness.d); - - let a_coset = domain.obtain_coset(a_poly.clone(), 0); - let b_coset = domain.obtain_coset(b_poly.clone(), 0); - let c_coset = domain.obtain_coset(c_poly.clone(), 0); - let d_coset = domain.obtain_coset(d_poly.clone(), 0); - let advice_polys: Vec<_> = witness .advice .into_iter() - .map(|poly| { - let poly = domain.obtain_poly(poly); - let coset = domain.obtain_coset(poly.clone(), 0); - (poly, coset) + .map(|poly| domain.obtain_poly(poly)) + .collect(); + + let advice_cosets: Vec<_> = meta + .advice_queries + .iter() + .map(|&(wire, at)| { + let poly = advice_polys[wire.0].clone(); + domain.obtain_coset(poly, at) }) .collect(); - // (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc) - let mut h_poly = Vec::with_capacity(a_coset.len()); - for ((((((((a, b), c), d), sa), sb), sc), sd), sm) in a_coset - .iter() - .zip(b_coset.iter()) - .zip(c_coset.iter()) - .zip(d_coset.iter()) - .zip(srs.sa.0.iter()) - .zip(srs.sb.0.iter()) - .zip(srs.sc.0.iter()) - .zip(srs.sd.0.iter()) - .zip(srs.sm.0.iter()) - { - h_poly.push((*a) * sa + &((*b) * sb) + &((*a) * sm * b) + &((*d) * sd) - &((*c) * sc)); + // Obtain challenge for keeping all separate gates linearly independent + let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + + // Evaluate the circuit using the custom gates provided + let mut h_poly = vec![C::Scalar::zero(); domain.coset_len()]; + for (i, poly) in meta.gates.iter().enumerate() { + if i != 0 { + for h in h_poly.iter_mut() { + *h *= &x_2; + } + } + + let evaluation: Vec = poly.evaluate( + &|index| srs.fixed_cosets[index].clone(), + &|index| advice_cosets[index].clone(), + &|mut a, b| { + parallelize(&mut a, |a, start| { + for (a, b) in a.into_iter().zip(b[start..].iter()) { + *a += b; + } + }); + a + }, + &|mut a, b| { + parallelize(&mut a, |a, start| { + for (a, b) in a.into_iter().zip(b[start..].iter()) { + *a *= b; + } + }); + a + }, + &|mut a, scalar| { + parallelize(&mut a, |a, _| { + for a in a { + *a *= &scalar; + } + }); + a + }, + ); + + assert_eq!(h_poly.len(), evaluation.len()); + + if i == 0 { + h_poly = evaluation; + } else { + for (h, e) in h_poly.iter_mut().zip(evaluation.into_iter()) { + *h += &e; + } + } } // Divide by t(X) = X^{params.n} - 1. @@ -208,7 +161,7 @@ impl Proof { .map(|v| v.to_vec()) .collect::>(); drop(h_poly); - let h_blinds = vec![C::Scalar::one(); h_pieces.len()]; // TODO: not random + let h_blinds: Vec<_> = h_pieces.iter().map(|_| C::Scalar::random()).collect(); // Compute commitments to each h(X) piece let h_commitments: Vec<_> = h_pieces @@ -222,38 +175,59 @@ impl Proof { hash_point(&mut transcript, c)?; } - let x: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - // Evaluate polynomials at x - let a_eval_x = eval_polynomial(&a_poly, x); - let b_eval_x = eval_polynomial(&b_poly, x); - let c_eval_x = eval_polynomial(&c_poly, x); - let d_eval_x = eval_polynomial(&d_poly, x); - let sa_eval_x = eval_polynomial(&srs.sa.1, x); - let sb_eval_x = eval_polynomial(&srs.sb.1, x); - let sc_eval_x = eval_polynomial(&srs.sc.1, x); - let sd_eval_x = eval_polynomial(&srs.sd.1, x); - let sm_eval_x = eval_polynomial(&srs.sm.1, x); + // Evaluate polynomials at omega^i x_3 + let advice_evals_x: Vec<_> = meta + .advice_queries + .iter() + .map(|&(wire, at)| { + let mut point = x_3; + if at >= 0 { + point *= &domain.get_omega().pow(&[at as u64, 0, 0, 0]); + } else { + point *= &domain.get_omega_inv().pow(&[at.abs() as u64, 0, 0, 0]); + } + + eval_polynomial(&advice_polys[wire.0], point) + }) + .collect(); + + let fixed_evals_x: Vec<_> = meta + .fixed_queries + .iter() + .map(|&(wire, at)| { + let mut point = x_3; + if at >= 0 { + point *= &domain.get_omega().pow(&[at as u64, 0, 0, 0]); + } else { + point *= &domain.get_omega_inv().pow(&[at.abs() as u64, 0, 0, 0]); + } + + eval_polynomial(&srs.fixed_polys[wire.0], point) + }) + .collect(); let h_evals_x: Vec<_> = h_pieces .iter() - .map(|poly| eval_polynomial(poly, x)) + .map(|poly| eval_polynomial(poly, x_3)) .collect(); // We set up a second transcript on the scalar field to hash in openings of // our polynomial commitments. let mut transcript_scalar = HScalar::init(C::Scalar::one()); - transcript_scalar.absorb(a_eval_x); - transcript_scalar.absorb(b_eval_x); - transcript_scalar.absorb(c_eval_x); - transcript_scalar.absorb(d_eval_x); - transcript_scalar.absorb(sa_eval_x); - transcript_scalar.absorb(sb_eval_x); - transcript_scalar.absorb(sc_eval_x); - transcript_scalar.absorb(sd_eval_x); - transcript_scalar.absorb(sm_eval_x); - // Hash each h(x) piece + // Hash each advice evaluation + for eval in advice_evals_x.iter() { + transcript_scalar.absorb(*eval); + } + + // Hash each fixed evaluation + for eval in fixed_evals_x.iter() { + transcript_scalar.absorb(*eval); + } + + // Hash each h(x) piece evaluation for eval in h_evals_x.iter() { transcript_scalar.absorb(*eval); } @@ -262,62 +236,164 @@ impl Proof { C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap(); transcript.absorb(transcript_scalar_point); - let y: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - let mut q_commitment = h_commitments[0].clone().to_projective(); - let mut q_poly = h_pieces[0].clone(); - let mut q_blind = h_blinds[0]; + // Collapse openings at same points together into single openings using + // x_4 challenge. + let mut q_polys: Vec>> = vec![None; meta.query_rows.len()]; + let mut q_blinds = vec![C::Scalar::zero(); meta.query_rows.len()]; + let mut q_evals: Vec<_> = vec![C::Scalar::zero(); meta.query_rows.len()]; { - let mut accumulate = |poly: &[_], blind: &C::Scalar, commitment: C| { - for (a, q) in poly.iter().zip(q_poly.iter_mut()) { - *q = (*q * &y) + a; - } - q_commitment = (q_commitment * y) + &commitment.to_projective(); - q_blind = (q_blind * &y) + blind; - }; + for (i, &(wire, ref at)) in meta.advice_queries.iter().enumerate() { + let query_row = *meta.query_rows.get(at).unwrap(); - for ((poly, blind), commitment) in h_pieces - .iter() - .zip(h_blinds.iter()) - .zip(h_commitments.iter()) - .skip(1) - { - accumulate(&poly, blind, *commitment); + if q_polys[query_row].is_none() { + q_polys[query_row] = Some(advice_polys[wire.0].clone()); + q_blinds[query_row] = advice_blinds[wire.0]; + q_evals[query_row] = advice_evals_x[i]; + } else { + parallelize(q_polys[query_row].as_mut().unwrap(), |q, start| { + for (q, a) in q.iter_mut().zip(advice_polys[wire.0][start..].iter()) { + *q *= &x_4; + *q += a; + } + }); + q_blinds[query_row] *= &x_4; + q_blinds[query_row] += &advice_blinds[wire.0]; + q_evals[query_row] *= &x_4; + q_evals[query_row] += &advice_evals_x[i]; + } } - accumulate(&a_poly, &a_blind, a_commitment); - accumulate(&b_poly, &b_blind, b_commitment); - accumulate(&c_poly, &c_blind, c_commitment); - accumulate(&d_poly, &d_blind, d_commitment); - accumulate(&srs.sa.1, &Field::one(), srs.sa_commitment); - accumulate(&srs.sb.1, &Field::one(), srs.sb_commitment); - accumulate(&srs.sc.1, &Field::one(), srs.sc_commitment); - accumulate(&srs.sd.1, &Field::one(), srs.sd_commitment); - accumulate(&srs.sm.1, &Field::one(), srs.sm_commitment); + for (i, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() { + let query_row = *meta.query_rows.get(at).unwrap(); + + if q_polys[query_row].is_none() { + q_polys[query_row] = Some(srs.fixed_polys[wire.0].clone()); + q_blinds[query_row] = C::Scalar::one(); + q_evals[query_row] = fixed_evals_x[i]; + } else { + parallelize(q_polys[query_row].as_mut().unwrap(), |q, start| { + for (q, a) in q.iter_mut().zip(srs.fixed_polys[wire.0][start..].iter()) { + *q *= &x_4; + *q += a; + } + }); + q_blinds[query_row] *= &x_4; + q_blinds[query_row] += &C::Scalar::one(); + q_evals[query_row] *= &x_4; + q_evals[query_row] += &fixed_evals_x[i]; + } + } + + for ((h_poly, h_blind), h_eval) in h_pieces + .into_iter() + .zip(h_blinds.iter()) + .zip(h_evals_x.iter()) + { + // We query the h(X) polynomial at x_3 + let cur_row = *meta.query_rows.get(&0).unwrap(); + + if q_polys[cur_row].is_none() { + q_polys[cur_row] = Some(h_poly); + q_blinds[cur_row] = *h_blind; + q_evals[cur_row] = *h_eval; + } else { + parallelize(q_polys[cur_row].as_mut().unwrap(), |q, start| { + for (q, a) in q.iter_mut().zip(h_poly[start..].iter()) { + *q *= &x_4; + *q += a; + } + }); + q_blinds[cur_row] *= &x_4; + q_blinds[cur_row] += h_blind; + q_evals[cur_row] *= &x_4; + q_evals[cur_row] += h_eval; + } + } + } + + let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + + let mut f_poly = None; + for (&row, &col) in meta.query_rows.iter() { + let mut poly = q_polys[col].as_ref().unwrap().clone(); + let mut point = x_3; + if row >= 0 { + point *= &domain.get_omega().pow_vartime(&[row as u64, 0, 0, 0]); + } else { + point *= &domain + .get_omega_inv() + .pow_vartime(&[row.abs() as u64, 0, 0, 0]); + } + poly[0] -= &q_evals[col]; + let mut poly = kate_division(&poly, point); + poly.push(C::Scalar::zero()); + + if f_poly.is_none() { + f_poly = Some(poly); + } else { + parallelize(f_poly.as_mut().unwrap(), |q, start| { + for (q, a) in q.iter_mut().zip(poly[start..].iter()) { + *q *= &x_5; + *q += a; + } + }); + } + } + let mut f_poly = f_poly.unwrap(); + let mut f_blind = C::Scalar::random(); + + let f_commitment = params.commit(&f_poly, f_blind).to_affine(); + + hash_point(&mut transcript, &f_commitment)?; + + let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + + let mut q_evals = vec![]; + + for (_, &col) in meta.query_rows.iter() { + q_evals.push(eval_polynomial(&q_polys[col].as_ref().unwrap(), x_6)); + } + + for eval in q_evals.iter() { + transcript_scalar.absorb(*eval); + } + + let transcript_scalar_point = + C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap(); + transcript.absorb(transcript_scalar_point); + + let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + + for (_, &col) in meta.query_rows.iter() { + f_blind *= &x_7; + f_blind += &q_blinds[col]; + + parallelize(&mut f_poly, |f, start| { + for (f, a) in f + .iter_mut() + .zip(q_polys[col].as_ref().unwrap()[start..].iter()) + { + *f *= &x_7; + *f += a; + } + }); } // Let's prove that the q_commitment opens at x to the expected value. let opening = params - .create_proof(&mut transcript, &q_poly, q_blind, x) + .create_proof(&mut transcript, &f_poly, f_blind, x_6) .map_err(|_| Error::ConstraintSystemFailure)?; Ok(Proof { - a_commitment, - b_commitment, - c_commitment, - d_commitment, advice_commitments, h_commitments, - a_eval_x, - b_eval_x, - c_eval_x, - d_eval_x, - sa_eval_x, - sb_eval_x, - sc_eval_x, - sd_eval_x, - sm_eval_x, + advice_evals_x, + fixed_evals_x, h_evals_x, + f_commitment, + q_evals, opening, }) } diff --git a/src/plonk/srs.rs b/src/plonk/srs.rs index 8ce141b..98bdc13 100644 --- a/src/plonk/srs.rs +++ b/src/plonk/srs.rs @@ -1,7 +1,7 @@ use super::{ - circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit, Variable, Wire}, + circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit}, domain::EvaluationDomain, - Error, GATE_DEGREE, SRS, + Error, SRS, }; use crate::arithmetic::{Curve, CurveAffine, Field}; use crate::polycommit::Params; @@ -14,11 +14,6 @@ impl SRS { circuit: &ConcreteCircuit, ) -> Result { struct Assembly { - sa: Vec, - sb: Vec, - sc: Vec, - sd: Vec, - sm: Vec, fixed: Vec>, } @@ -47,113 +42,51 @@ impl SRS { Ok(()) } - - fn create_gate( - &mut self, - sa: F, - sb: F, - sc: F, - sd: F, - sm: F, - _: impl Fn() -> Result<(F, F, F, F), Error>, - ) -> Result<(Variable, Variable, Variable, Variable), Error> { - let tmp = Ok(( - Variable(Wire::A, self.sa.len()), - Variable(Wire::B, self.sa.len()), - Variable(Wire::C, self.sa.len()), - Variable(Wire::D, self.sa.len()), - )); - self.sa.push(sa); - self.sb.push(sb); - self.sc.push(sc); - self.sd.push(sd); - self.sm.push(sm); - tmp - } } let mut meta = MetaCircuit::default(); let config = ConcreteCircuit::configure(&mut meta); let mut assembly: Assembly = Assembly { - sa: vec![], - sb: vec![], - sc: vec![], - sd: vec![], - sm: vec![], fixed: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_fixed_wires], }; // Synthesize the circuit to obtain SRS circuit.synthesize(&mut assembly, config)?; - assembly.sa.resize(params.n as usize, C::Scalar::zero()); - assembly.sb.resize(params.n as usize, C::Scalar::zero()); - assembly.sc.resize(params.n as usize, C::Scalar::zero()); - assembly.sd.resize(params.n as usize, C::Scalar::zero()); - assembly.sm.resize(params.n as usize, C::Scalar::zero()); - - // Compute commitments to the fixed wire values - let sa_commitment = params - .commit_lagrange(&assembly.sa, C::Scalar::one()) - .to_affine(); - let sb_commitment = params - .commit_lagrange(&assembly.sb, C::Scalar::one()) - .to_affine(); - let sc_commitment = params - .commit_lagrange(&assembly.sc, C::Scalar::one()) - .to_affine(); - let sd_commitment = params - .commit_lagrange(&assembly.sd, C::Scalar::one()) - .to_affine(); - let sm_commitment = params - .commit_lagrange(&assembly.sm, C::Scalar::one()) - .to_affine(); - let fixed_commitments = assembly .fixed .iter() .map(|poly| params.commit_lagrange(poly, C::Scalar::one()).to_affine()) .collect(); - let domain = EvaluationDomain::new(GATE_DEGREE, params.k); + let mut degree = 1; + for poly in meta.gates.iter() { + degree = std::cmp::max(degree, poly.degree()); + } - let sa_poly = domain.obtain_poly(assembly.sa); - let sb_poly = domain.obtain_poly(assembly.sb); - let sc_poly = domain.obtain_poly(assembly.sc); - let sd_poly = domain.obtain_poly(assembly.sd); - let sm_poly = domain.obtain_poly(assembly.sm); - let sa_coset = domain.obtain_coset(sa_poly.clone(), 0); - let sb_coset = domain.obtain_coset(sb_poly.clone(), 0); - let sc_coset = domain.obtain_coset(sc_poly.clone(), 0); - let sd_coset = domain.obtain_coset(sd_poly.clone(), 0); - let sm_coset = domain.obtain_coset(sm_poly.clone(), 0); + let domain = EvaluationDomain::new(degree as u32, params.k); - let fixed_polys = assembly + let fixed_polys: Vec<_> = assembly .fixed .into_iter() - .map(|poly| { - let coeffs = domain.obtain_poly(poly); - let coset = domain.obtain_coset(coeffs.clone(), 0); - (coeffs, coset) + .map(|poly| domain.obtain_poly(poly)) + .collect(); + + let fixed_cosets = meta + .fixed_queries + .iter() + .map(|&(wire, at)| { + let poly = fixed_polys[wire.0].clone(); + domain.obtain_coset(poly, at) }) .collect(); Ok(SRS { - sa: (sa_coset, sa_poly), - sb: (sb_coset, sb_poly), - sc: (sc_coset, sc_poly), - sd: (sd_coset, sd_poly), - sm: (sm_coset, sm_poly), - sa_commitment, - sb_commitment, - sc_commitment, - sd_commitment, - sm_commitment, domain, - fixed_commitments, fixed_polys, + fixed_cosets, meta, }) } diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index e155cb1..10777e9 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -13,48 +13,178 @@ impl Proof { // Create a transcript for obtaining Fiat-Shamir challenges. let mut transcript = HBase::init(C::Base::one()); - hash_point(&mut transcript, &self.a_commitment) - .expect("proof cannot contain points at infinity"); - hash_point(&mut transcript, &self.b_commitment) - .expect("proof cannot contain points at infinity"); - hash_point(&mut transcript, &self.c_commitment) - .expect("proof cannot contain points at infinity"); - hash_point(&mut transcript, &self.d_commitment) - .expect("proof cannot contain points at infinity"); - for commitment in &self.advice_commitments { hash_point(&mut transcript, commitment) .expect("proof cannot contain points at infinity"); } + let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + for c in &self.h_commitments { hash_point(&mut transcript, c).expect("proof cannot contain points at infinity"); } - let x: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - // We set up a second transcript on the scalar field to hash in openings of - // our polynomial commitments. let mut transcript_scalar = HScalar::init(C::Scalar::one()); - transcript_scalar.absorb(self.a_eval_x); - transcript_scalar.absorb(self.b_eval_x); - transcript_scalar.absorb(self.c_eval_x); - transcript_scalar.absorb(self.d_eval_x); - transcript_scalar.absorb(self.sa_eval_x); - transcript_scalar.absorb(self.sb_eval_x); - transcript_scalar.absorb(self.sc_eval_x); - transcript_scalar.absorb(self.sd_eval_x); - transcript_scalar.absorb(self.sm_eval_x); + + for eval in self.advice_evals_x.iter() { + transcript_scalar.absorb(*eval); + } + + for eval in self.fixed_evals_x.iter() { + transcript_scalar.absorb(*eval); + } for eval in &self.h_evals_x { transcript_scalar.absorb(*eval); } + // Evaluate the circuit using the custom gates provided + let mut h_eval = C::Scalar::zero(); + for poly in srs.meta.gates.iter() { + h_eval *= &x_2; + + let evaluation: C::Scalar = poly.evaluate( + &|index| self.fixed_evals_x[index], + &|index| self.advice_evals_x[index], + &|a, b| a + &b, + &|a, b| a * &b, + &|a, scalar| a * &scalar, + ); + + h_eval += &evaluation; + } + let xn = x_3.pow(&[params.n as u64, 0, 0, 0]); + h_eval *= &(xn - &C::Scalar::one()); + + // Compute the expected h(x) value + let mut expected_h_eval = C::Scalar::zero(); + let mut cur = C::Scalar::one(); + for eval in &self.h_evals_x { + expected_h_eval += &(cur * eval); + cur *= &xn; + } + + if h_eval != expected_h_eval { + return false; + } + + let transcript_scalar_point = + C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap(); + transcript.absorb(transcript_scalar_point); + + let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + + let mut q_commitments: Vec<_> = vec![None; srs.meta.query_rows.len()]; + let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.query_rows.len()]; + + { + for (i, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() { + let query_row = *srs.meta.query_rows.get(at).unwrap(); + + if q_commitments[query_row].is_none() { + q_commitments[query_row] = + Some(self.advice_commitments[wire.0].to_projective()); + q_evals[query_row] = self.advice_evals_x[i]; + } else { + q_commitments[query_row].as_mut().map(|commitment| { + *commitment *= x_4; + *commitment += self.advice_commitments[wire.0]; + }); + q_evals[query_row] *= &x_4; + q_evals[query_row] += &self.advice_evals_x[i]; + } + } + + for (i, &(wire, ref at)) in srs.meta.fixed_queries.iter().enumerate() { + let query_row = *srs.meta.query_rows.get(at).unwrap(); + + if q_commitments[query_row].is_none() { + q_commitments[query_row] = Some(srs.fixed_commitments[wire.0].to_projective()); + q_evals[query_row] = self.fixed_evals_x[i]; + } else { + q_commitments[query_row].as_mut().map(|commitment| { + *commitment *= x_4; + *commitment += srs.fixed_commitments[wire.0]; + }); + q_evals[query_row] *= &x_4; + q_evals[query_row] += &self.fixed_evals_x[i]; + } + } + + for (h_commitment, h_eval) in self.h_commitments.iter().zip(self.h_evals_x.iter()) { + // We query the h(X) polynomial at x_3 + let cur_row = *srs.meta.query_rows.get(&0).unwrap(); + + if q_commitments[cur_row].is_none() { + q_commitments[cur_row] = Some(h_commitment.to_projective()); + q_evals[cur_row] = *h_eval; + } else { + q_commitments[cur_row].as_mut().map(|commitment| { + *commitment *= x_4; + *commitment += *h_commitment; + }); + q_evals[cur_row] *= &x_4; + q_evals[cur_row] += h_eval; + } + } + } + + let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + + hash_point(&mut transcript, &self.f_commitment) + .expect("proof cannot contain points at infinity"); + + let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + + // We can compute the expected f_eval from x_5 + let mut f_eval = C::Scalar::zero(); + for (&row, &col) in srs.meta.query_rows.iter() { + let mut eval: C::Scalar = self.q_evals[col].clone(); + let mut point = x_3; + if row >= 0 { + point *= &srs.domain.get_omega().pow_vartime(&[row as u64, 0, 0, 0]); + } else { + point *= &srs + .domain + .get_omega_inv() + .pow_vartime(&[row.abs() as u64, 0, 0, 0]); + } + eval = eval - &q_evals[col]; + eval = eval * &(x_6 - &point).invert().unwrap(); + + f_eval *= &x_5; + f_eval += &eval; + } + + for eval in self.q_evals.iter() { + transcript_scalar.absorb(*eval); + } + let transcript_scalar_point = C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap(); transcript.absorb(transcript_scalar_point); - let y: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + + let mut f_commitment: C::Projective = self.f_commitment.to_projective(); + for (_, &col) in srs.meta.query_rows.iter() { + f_commitment *= x_7; + f_commitment = f_commitment + &q_commitments[col].as_ref().unwrap(); + f_eval *= &x_7; + f_eval += &self.q_evals[col]; + } + + params.verify_proof( + &self.opening, + &mut transcript, + x_6, + &f_commitment.to_affine(), + f_eval, + ) + + /* let mut q_commitment = self.h_commitments[0].clone().to_projective(); let mut expected_opening = self.h_evals_x[0]; @@ -102,12 +232,6 @@ impl Proof { return false; } - params.verify_proof( - &self.opening, - &mut transcript, - x, - &q_commitment, - expected_opening, - ) + */ } } From 9099e9d9ba1f05790c96045878932079d89ca80d Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 27 Aug 2020 10:16:42 -0600 Subject: [PATCH 09/15] Properly invert when computing expected opening. --- src/plonk/verifier.rs | 52 +------------------------------------------ 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 10777e9..e339893 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -56,7 +56,7 @@ impl Proof { h_eval += &evaluation; } let xn = x_3.pow(&[params.n as u64, 0, 0, 0]); - h_eval *= &(xn - &C::Scalar::one()); + h_eval *= &(xn - &C::Scalar::one()).invert().unwrap(); // Compute the expected h(x) value let mut expected_h_eval = C::Scalar::zero(); @@ -183,55 +183,5 @@ impl Proof { &f_commitment.to_affine(), f_eval, ) - - /* - - let mut q_commitment = self.h_commitments[0].clone().to_projective(); - let mut expected_opening = self.h_evals_x[0]; - { - let mut accumulate = |commitment: C, opening: C::Scalar| { - q_commitment = commitment.to_projective() + &(q_commitment * y); - expected_opening = opening + &(expected_opening * &y); - }; - - for (commitment, eval) in self.h_commitments.iter().zip(self.h_evals_x.iter()).skip(1) { - accumulate(*commitment, *eval); - } - - accumulate(self.a_commitment, self.a_eval_x); - accumulate(self.b_commitment, self.b_eval_x); - accumulate(self.c_commitment, self.c_eval_x); - accumulate(self.d_commitment, self.d_eval_x); - accumulate(srs.sa_commitment, self.sa_eval_x); - accumulate(srs.sb_commitment, self.sb_eval_x); - accumulate(srs.sc_commitment, self.sc_eval_x); - accumulate(srs.sd_commitment, self.sd_eval_x); - accumulate(srs.sm_commitment, self.sm_eval_x); - } - let q_commitment = q_commitment.to_affine(); - - let xn = x.pow(&[params.n as u64, 0, 0, 0]); - - // Compute the expected h(x) value - let mut h_eval_x = C::Scalar::zero(); - let mut cur = C::Scalar::one(); - for eval in &self.h_evals_x { - h_eval_x += &(cur * eval); - cur *= &xn; - } - - // Check that the circuit is satisfied. - // (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc) - if self.a_eval_x * &self.sa_eval_x - + &(self.b_eval_x * &self.sb_eval_x) - + &(self.a_eval_x * &self.sm_eval_x * &self.b_eval_x) - + &(self.d_eval_x * &self.sd_eval_x) - - &(self.c_eval_x * &self.sc_eval_x) - != h_eval_x * &(xn - &C::Scalar::one()) - { - return false; - } - - */ } } From 1b6c0e9a8b2677a8f8f42b7ea2865ce9bec7605d Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 27 Aug 2020 10:25:36 -0600 Subject: [PATCH 10/15] Remove _x suffix from variable names --- src/plonk.rs | 6 +++--- src/plonk/prover.rs | 28 ++++++++++++++-------------- src/plonk/verifier.rs | 22 +++++++++++----------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index d2217da..0c7098f 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -41,9 +41,9 @@ pub struct SRS { pub struct Proof { advice_commitments: Vec, h_commitments: Vec, - advice_evals_x: Vec, - fixed_evals_x: Vec, - h_evals_x: Vec, + advice_evals: Vec, + fixed_evals: Vec, + h_evals: Vec, f_commitment: C, q_evals: Vec, opening: OpeningProof, diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 2a356d9..95238e1 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -178,7 +178,7 @@ impl Proof { let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); // Evaluate polynomials at omega^i x_3 - let advice_evals_x: Vec<_> = meta + let advice_evals: Vec<_> = meta .advice_queries .iter() .map(|&(wire, at)| { @@ -193,7 +193,7 @@ impl Proof { }) .collect(); - let fixed_evals_x: Vec<_> = meta + let fixed_evals: Vec<_> = meta .fixed_queries .iter() .map(|&(wire, at)| { @@ -208,7 +208,7 @@ impl Proof { }) .collect(); - let h_evals_x: Vec<_> = h_pieces + let h_evals: Vec<_> = h_pieces .iter() .map(|poly| eval_polynomial(poly, x_3)) .collect(); @@ -218,17 +218,17 @@ impl Proof { let mut transcript_scalar = HScalar::init(C::Scalar::one()); // Hash each advice evaluation - for eval in advice_evals_x.iter() { + for eval in advice_evals.iter() { transcript_scalar.absorb(*eval); } // Hash each fixed evaluation - for eval in fixed_evals_x.iter() { + for eval in fixed_evals.iter() { transcript_scalar.absorb(*eval); } // Hash each h(x) piece evaluation - for eval in h_evals_x.iter() { + for eval in h_evals.iter() { transcript_scalar.absorb(*eval); } @@ -250,7 +250,7 @@ impl Proof { if q_polys[query_row].is_none() { q_polys[query_row] = Some(advice_polys[wire.0].clone()); q_blinds[query_row] = advice_blinds[wire.0]; - q_evals[query_row] = advice_evals_x[i]; + q_evals[query_row] = advice_evals[i]; } else { parallelize(q_polys[query_row].as_mut().unwrap(), |q, start| { for (q, a) in q.iter_mut().zip(advice_polys[wire.0][start..].iter()) { @@ -261,7 +261,7 @@ impl Proof { q_blinds[query_row] *= &x_4; q_blinds[query_row] += &advice_blinds[wire.0]; q_evals[query_row] *= &x_4; - q_evals[query_row] += &advice_evals_x[i]; + q_evals[query_row] += &advice_evals[i]; } } @@ -271,7 +271,7 @@ impl Proof { if q_polys[query_row].is_none() { q_polys[query_row] = Some(srs.fixed_polys[wire.0].clone()); q_blinds[query_row] = C::Scalar::one(); - q_evals[query_row] = fixed_evals_x[i]; + q_evals[query_row] = fixed_evals[i]; } else { parallelize(q_polys[query_row].as_mut().unwrap(), |q, start| { for (q, a) in q.iter_mut().zip(srs.fixed_polys[wire.0][start..].iter()) { @@ -282,14 +282,14 @@ impl Proof { q_blinds[query_row] *= &x_4; q_blinds[query_row] += &C::Scalar::one(); q_evals[query_row] *= &x_4; - q_evals[query_row] += &fixed_evals_x[i]; + q_evals[query_row] += &fixed_evals[i]; } } for ((h_poly, h_blind), h_eval) in h_pieces .into_iter() .zip(h_blinds.iter()) - .zip(h_evals_x.iter()) + .zip(h_evals.iter()) { // We query the h(X) polynomial at x_3 let cur_row = *meta.query_rows.get(&0).unwrap(); @@ -389,9 +389,9 @@ impl Proof { Ok(Proof { advice_commitments, h_commitments, - advice_evals_x, - fixed_evals_x, - h_evals_x, + advice_evals, + fixed_evals, + h_evals, f_commitment, q_evals, opening, diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index e339893..33c0f2c 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -28,15 +28,15 @@ impl Proof { let mut transcript_scalar = HScalar::init(C::Scalar::one()); - for eval in self.advice_evals_x.iter() { + for eval in self.advice_evals.iter() { transcript_scalar.absorb(*eval); } - for eval in self.fixed_evals_x.iter() { + for eval in self.fixed_evals.iter() { transcript_scalar.absorb(*eval); } - for eval in &self.h_evals_x { + for eval in &self.h_evals { transcript_scalar.absorb(*eval); } @@ -46,8 +46,8 @@ impl Proof { h_eval *= &x_2; let evaluation: C::Scalar = poly.evaluate( - &|index| self.fixed_evals_x[index], - &|index| self.advice_evals_x[index], + &|index| self.fixed_evals[index], + &|index| self.advice_evals[index], &|a, b| a + &b, &|a, b| a * &b, &|a, scalar| a * &scalar, @@ -61,7 +61,7 @@ impl Proof { // Compute the expected h(x) value let mut expected_h_eval = C::Scalar::zero(); let mut cur = C::Scalar::one(); - for eval in &self.h_evals_x { + for eval in &self.h_evals { expected_h_eval += &(cur * eval); cur *= &xn; } @@ -86,14 +86,14 @@ impl Proof { if q_commitments[query_row].is_none() { q_commitments[query_row] = Some(self.advice_commitments[wire.0].to_projective()); - q_evals[query_row] = self.advice_evals_x[i]; + q_evals[query_row] = self.advice_evals[i]; } else { q_commitments[query_row].as_mut().map(|commitment| { *commitment *= x_4; *commitment += self.advice_commitments[wire.0]; }); q_evals[query_row] *= &x_4; - q_evals[query_row] += &self.advice_evals_x[i]; + q_evals[query_row] += &self.advice_evals[i]; } } @@ -102,18 +102,18 @@ impl Proof { if q_commitments[query_row].is_none() { q_commitments[query_row] = Some(srs.fixed_commitments[wire.0].to_projective()); - q_evals[query_row] = self.fixed_evals_x[i]; + q_evals[query_row] = self.fixed_evals[i]; } else { q_commitments[query_row].as_mut().map(|commitment| { *commitment *= x_4; *commitment += srs.fixed_commitments[wire.0]; }); q_evals[query_row] *= &x_4; - q_evals[query_row] += &self.fixed_evals_x[i]; + q_evals[query_row] += &self.fixed_evals[i]; } } - for (h_commitment, h_eval) in self.h_commitments.iter().zip(self.h_evals_x.iter()) { + for (h_commitment, h_eval) in self.h_commitments.iter().zip(self.h_evals.iter()) { // We query the h(X) polynomial at x_3 let cur_row = *srs.meta.query_rows.get(&0).unwrap(); From 9852913a32877937af11ea3ee4e5992c14272aa1 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 27 Aug 2020 10:46:54 -0600 Subject: [PATCH 11/15] Add some comments and documentation. --- src/plonk/verifier.rs | 48 +++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 33c0f2c..cc28d7d 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -13,19 +13,26 @@ impl Proof { // Create a transcript for obtaining Fiat-Shamir challenges. let mut transcript = HBase::init(C::Base::one()); + // Hash the prover's advice commitments into the transcript for commitment in &self.advice_commitments { hash_point(&mut transcript, commitment) .expect("proof cannot contain points at infinity"); } + // Sample x_2 challenge, which keeps the gates linearly independent. let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1 for c in &self.h_commitments { hash_point(&mut transcript, c).expect("proof cannot contain points at infinity"); } + // Sample x_3 challenge, which is used to ensure the circuit is + // satisfied with high probability. let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // Hash together all the openings provided by the prover into a new + // transcript on the scalar field. let mut transcript_scalar = HScalar::init(C::Scalar::one()); for eval in self.advice_evals.iter() { @@ -40,6 +47,10 @@ impl Proof { transcript_scalar.absorb(*eval); } + let transcript_scalar_point = + C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap(); + transcript.absorb(transcript_scalar_point); + // Evaluate the circuit using the custom gates provided let mut h_eval = C::Scalar::zero(); for poly in srs.meta.gates.iter() { @@ -70,15 +81,16 @@ impl Proof { return false; } - let transcript_scalar_point = - C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap(); - transcript.absorb(transcript_scalar_point); + // We are now convinced the circuit is satisfied so long as the + // polynomial commitments open to the correct values. + // Sample x_4 for compressing openings at the same points together let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // Compress the commitments and expected evaluations at x_3 together + // using the challenge x_4 let mut q_commitments: Vec<_> = vec![None; srs.meta.query_rows.len()]; let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.query_rows.len()]; - { for (i, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() { let query_row = *srs.meta.query_rows.get(at).unwrap(); @@ -131,14 +143,28 @@ impl Proof { } } + // Sample a challenge x_5 for keeping the multi-point quotient + // polynomial terms linearly independent. let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // Obtain the commitment to the multi-point quotient polynomial f(X). hash_point(&mut transcript, &self.f_commitment) .expect("proof cannot contain points at infinity"); + // Sample a challenge x_6 for checking that f(X) was committed to + // correctly. let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - // We can compute the expected f_eval from x_5 + for eval in self.q_evals.iter() { + transcript_scalar.absorb(*eval); + } + + let transcript_scalar_point = + C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap(); + transcript.absorb(transcript_scalar_point); + + // We can compute the expected f_eval at x_6 using the q_evals provided + // by the prover and from x_5 let mut f_eval = C::Scalar::zero(); for (&row, &col) in srs.meta.query_rows.iter() { let mut eval: C::Scalar = self.q_evals[col].clone(); @@ -158,16 +184,11 @@ impl Proof { f_eval += &eval; } - for eval in self.q_evals.iter() { - transcript_scalar.absorb(*eval); - } - - let transcript_scalar_point = - C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap(); - transcript.absorb(transcript_scalar_point); - + // Sample a challenge x_7 that we will use to collapse the openings of + // the various remaining polynomials at x_6 together. let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // Compute the final commitment that has to be opened let mut f_commitment: C::Projective = self.f_commitment.to_projective(); for (_, &col) in srs.meta.query_rows.iter() { f_commitment *= x_7; @@ -176,6 +197,7 @@ impl Proof { f_eval += &self.q_evals[col]; } + // Verify the opening proof params.verify_proof( &self.opening, &mut transcript, From 378c56b9528732f699d0364517cd1e2049421ebd Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 27 Aug 2020 11:43:08 -0600 Subject: [PATCH 12/15] Sample of abstraction for writing PLONK circuits --- src/plonk.rs | 153 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 124 insertions(+), 29 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index 0c7098f..7de9c61 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -84,12 +84,13 @@ fn test_proving() { use crate::arithmetic::{EqAffine, Field, Fp, Fq}; use crate::polycommit::Params; use crate::transcript::DummyHash; + use std::marker::PhantomData; const K: u32 = 5; // Initialize the polynomial commitment parameters let params: Params = Params::new::>(K); - struct MyConfig { + struct PLONKConfig { a: AdviceWire, b: AdviceWire, c: AdviceWire, @@ -99,14 +100,112 @@ fn test_proving() { sc: FixedWire, sm: FixedWire, } + + #[derive(Copy, Clone)] + struct Variable(AdviceWire, usize); + + trait StandardCS { + fn raw_multiply(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>; + fn raw_add(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>; + } + struct MyCircuit { a: Option, } - impl Circuit for MyCircuit { - type Config = MyConfig; + struct StandardPLONK<'a, F: Field, CS: ConstraintSystem + 'a> { + cs: &'a mut CS, + config: PLONKConfig, + current_gate: usize, + _marker: PhantomData, + } - fn configure(meta: &mut MetaCircuit) -> MyConfig { + impl<'a, FF: Field, CS: ConstraintSystem> StandardPLONK<'a, FF, CS> { + fn new(cs: &'a mut CS, config: PLONKConfig) -> Self { + StandardPLONK { + cs, + config, + current_gate: 0, + _marker: PhantomData, + } + } + } + + impl<'a, FF: Field, CS: ConstraintSystem> StandardCS for StandardPLONK<'a, FF, CS> { + fn raw_multiply(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>, + { + let index = self.current_gate; + self.current_gate += 1; + let mut value = None; + self.cs.assign_advice(self.config.a, index, || { + value = Some(f()?); + Ok(value.ok_or(Error::SynthesisError)?.0) + })?; + self.cs.assign_advice(self.config.b, index, || { + Ok(value.ok_or(Error::SynthesisError)?.1) + })?; + self.cs.assign_advice(self.config.c, index, || { + Ok(value.ok_or(Error::SynthesisError)?.2) + })?; + + self.cs + .assign_fixed(self.config.sa, index, || Ok(FF::zero()))?; + self.cs + .assign_fixed(self.config.sb, index, || Ok(FF::zero()))?; + self.cs + .assign_fixed(self.config.sc, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sm, index, || Ok(FF::one()))?; + Ok(( + Variable(self.config.a, index), + Variable(self.config.b, index), + Variable(self.config.c, index), + )) + } + fn raw_add(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>, + { + let index = self.current_gate; + self.current_gate += 1; + let mut value = None; + self.cs.assign_advice(self.config.a, index, || { + value = Some(f()?); + Ok(value.ok_or(Error::SynthesisError)?.0) + })?; + self.cs.assign_advice(self.config.b, index, || { + Ok(value.ok_or(Error::SynthesisError)?.1) + })?; + self.cs.assign_advice(self.config.c, index, || { + Ok(value.ok_or(Error::SynthesisError)?.2) + })?; + + self.cs + .assign_fixed(self.config.sa, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sb, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sc, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sm, index, || Ok(FF::zero()))?; + Ok(( + Variable(self.config.a, index), + Variable(self.config.b, index), + Variable(self.config.c, index), + )) + } + } + + impl Circuit for MyCircuit { + type Config = PLONKConfig; + + fn configure(meta: &mut MetaCircuit) -> PLONKConfig { let a = meta.advice_wire(); let b = meta.advice_wire(); let c = meta.advice_wire(); @@ -129,7 +228,7 @@ fn test_proving() { a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one())) }); - MyConfig { + PLONKConfig { a, b, c, @@ -143,32 +242,28 @@ fn test_proving() { fn synthesize( &self, cs: &mut impl ConstraintSystem, - config: MyConfig, + config: PLONKConfig, ) -> Result<(), Error> { - // Similar to the above... - let mut row = 0; - for _ in 0..10 { - cs.assign_advice(config.a, row, || self.a.ok_or(Error::SynthesisError))?; - cs.assign_advice(config.b, row, || self.a.ok_or(Error::SynthesisError))?; - let a_squared = self.a.map(|a| a.square()); - cs.assign_advice(config.c, row, || a_squared.ok_or(Error::SynthesisError))?; - // Multiplication gate - cs.assign_fixed(config.sa, row, || Ok(Field::zero()))?; - cs.assign_fixed(config.sb, row, || Ok(Field::zero()))?; - cs.assign_fixed(config.sc, row, || Ok(Field::one()))?; - cs.assign_fixed(config.sm, row, || Ok(Field::one()))?; - row += 1; + let mut cs = StandardPLONK::new(cs, config); - cs.assign_advice(config.a, row, || self.a.ok_or(Error::SynthesisError))?; - cs.assign_advice(config.b, row, || a_squared.ok_or(Error::SynthesisError))?; - let fin = a_squared.and_then(|a_squared| self.a.map(|a| a + a_squared)); - cs.assign_advice(config.c, row, || fin.ok_or(Error::SynthesisError))?; - // Addition gate - cs.assign_fixed(config.sa, row, || Ok(Field::one()))?; - cs.assign_fixed(config.sb, row, || Ok(Field::one()))?; - cs.assign_fixed(config.sc, row, || Ok(Field::one()))?; - cs.assign_fixed(config.sm, row, || Ok(Field::zero()))?; - row += 1; + for _ in 0..10 { + let mut a_squared = None; + let (_, _, _) = cs.raw_multiply(|| { + a_squared = self.a.map(|a| a.square()); + Ok(( + self.a.ok_or(Error::SynthesisError)?, + self.a.ok_or(Error::SynthesisError)?, + a_squared.ok_or(Error::SynthesisError)?, + )) + })?; + let (_, _, _) = cs.raw_add(|| { + let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2)); + Ok(( + self.a.ok_or(Error::SynthesisError)?, + a_squared.ok_or(Error::SynthesisError)?, + fin.ok_or(Error::SynthesisError)?, + )) + })?; } Ok(()) From 35c4bd4dd96ea8325197436b6998d4c52d9ec1df Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 27 Aug 2020 13:27:24 -0600 Subject: [PATCH 13/15] Improve naming of offsets/indexes and mappings. --- src/plonk/circuit.rs | 30 +++++++---- src/plonk/domain.rs | 41 +++++++++++--- src/plonk/prover.rs | 121 +++++++++++++++++------------------------- src/plonk/verifier.rs | 76 ++++++++++++-------------- 4 files changed, 139 insertions(+), 129 deletions(-) diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 5466260..d1923b6 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use super::Error; use crate::arithmetic::Field; +use super::domain::Rotation; /// This represents a wire which has a fixed (permanent) value #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct FixedWire(pub usize); @@ -135,6 +136,11 @@ impl Mul for Polynomial { } } +/// Represents an index into a vector where each entry corresponds to a distinct +/// point that polynomials are queried at. +#[derive(Copy, Clone, Debug)] +pub struct PointIndex(pub usize); + /// This is a description of the circuit environment, such as the gate, wire and /// permutation arrangements. #[derive(Debug, Clone)] @@ -143,15 +149,17 @@ pub struct MetaCircuit { pub(crate) num_advice_wires: usize, // permutations: Vec>, pub(crate) gates: Vec>, - pub(crate) advice_queries: Vec<(AdviceWire, i32)>, - pub(crate) fixed_queries: Vec<(FixedWire, i32)>, - pub(crate) query_rows: HashMap, + pub(crate) advice_queries: Vec<(AdviceWire, Rotation)>, + pub(crate) fixed_queries: Vec<(FixedWire, Rotation)>, + + // Mapping from a witness vector rotation to the index in the point vector. + pub(crate) rotations: HashMap, } impl Default for MetaCircuit { fn default() -> MetaCircuit { - let mut query_rows = HashMap::new(); - query_rows.insert(0, 0); + let mut rotations = HashMap::new(); + rotations.insert(Rotation::default(), PointIndex(0)); MetaCircuit { num_fixed_wires: 0, @@ -159,7 +167,7 @@ impl Default for MetaCircuit { gates: vec![], fixed_queries: Vec::new(), advice_queries: Vec::new(), - query_rows, + rotations, } } } @@ -167,9 +175,10 @@ impl Default for MetaCircuit { impl MetaCircuit { /// Query a fixed wire at a relative position pub fn query_fixed(&mut self, wire: FixedWire, at: i32) -> Polynomial { + let at = Rotation(at); { - let len = self.query_rows.len(); - self.query_rows.entry(at).or_insert(len); + let len = self.rotations.len(); + self.rotations.entry(at).or_insert(PointIndex(len)); } // TODO: check for existing query so we don't make redundant queries @@ -181,9 +190,10 @@ impl MetaCircuit { /// Query an advice wire at a relative position pub fn query_advice(&mut self, wire: AdviceWire, at: i32) -> Polynomial { + let at = Rotation(at); { - let len = self.query_rows.len(); - self.query_rows.entry(at).or_insert(len); + let len = self.rotations.len(); + self.rotations.entry(at).or_insert(PointIndex(len)); } // TODO: check for existing query so we don't make redundant queries diff --git a/src/plonk/domain.rs b/src/plonk/domain.rs index 07ac13a..6ebfb5c 100644 --- a/src/plonk/domain.rs +++ b/src/plonk/domain.rs @@ -1,5 +1,16 @@ use crate::arithmetic::{best_fft, parallelize, Field, Group}; +/// Describes a relative location in the evaluation domain; applying a rotation +/// by i will rotate the vector in the evaluation domain by i. +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct Rotation(pub i32); + +impl Default for Rotation { + fn default() -> Rotation { + Rotation(0) + } +} + /// This structure contains precomputed constants and other details needed for /// performing operations on an evaluation domain of size $2^k$ in the context /// of PLONK. @@ -119,22 +130,26 @@ impl EvaluationDomain { } /// This takes us from an n-length coefficient vector into the coset - /// evaluation domain. + /// evaluation domain, rotating by `rotation` if desired. /// /// This function will panic if the provided vector is not the correct /// length. - pub fn obtain_coset(&self, mut a: Vec, index: i32) -> Vec { + pub fn obtain_coset(&self, mut a: Vec, rotation: Rotation) -> Vec { assert_eq!(a.len(), 1 << self.k); - assert!(index != i32::MIN); - if index == 0 { + assert!(rotation.0 != i32::MIN); + if rotation.0 == 0 { + // In this special case, the powers of zeta repeat so we do not need + // to compute them. Self::distribute_powers_zeta(&mut a, self.g_coset); } else { let mut g = G::Scalar::ZETA; - if index > 0 { - g *= &self.omega.pow_vartime(&[index as u64, 0, 0, 0]); + if rotation.0 > 0 { + g *= &self.omega.pow_vartime(&[rotation.0 as u64, 0, 0, 0]); } else { - g *= &self.omega_inv.pow_vartime(&[index.abs() as u64, 0, 0, 0]); + g *= &self + .omega_inv + .pow_vartime(&[rotation.0.abs() as u64, 0, 0, 0]); } Self::distribute_powers(&mut a, g); } @@ -233,4 +248,16 @@ impl EvaluationDomain { pub fn get_omega_inv(&self) -> G::Scalar { self.omega_inv } + + pub fn rotate_omega(&self, constant: G::Scalar, rotation: Rotation) -> G::Scalar { + let mut point = constant; + if rotation.0 >= 0 { + point *= &self.get_omega().pow(&[rotation.0 as u64, 0, 0, 0]); + } else { + point *= &self + .get_omega_inv() + .pow(&[rotation.0.abs() as u64, 0, 0, 0]); + } + point + } } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 95238e1..41310e3 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -1,5 +1,6 @@ use super::{ circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit}, + domain::Rotation, hash_point, Error, Proof, SRS, }; use crate::arithmetic::{ @@ -181,31 +182,13 @@ impl Proof { let advice_evals: Vec<_> = meta .advice_queries .iter() - .map(|&(wire, at)| { - let mut point = x_3; - if at >= 0 { - point *= &domain.get_omega().pow(&[at as u64, 0, 0, 0]); - } else { - point *= &domain.get_omega_inv().pow(&[at.abs() as u64, 0, 0, 0]); - } - - eval_polynomial(&advice_polys[wire.0], point) - }) + .map(|&(wire, at)| eval_polynomial(&advice_polys[wire.0], domain.rotate_omega(x_3, at))) .collect(); let fixed_evals: Vec<_> = meta .fixed_queries .iter() - .map(|&(wire, at)| { - let mut point = x_3; - if at >= 0 { - point *= &domain.get_omega().pow(&[at as u64, 0, 0, 0]); - } else { - point *= &domain.get_omega_inv().pow(&[at.abs() as u64, 0, 0, 0]); - } - - eval_polynomial(&srs.fixed_polys[wire.0], point) - }) + .map(|&(wire, at)| eval_polynomial(&srs.fixed_polys[wire.0], domain.rotate_omega(x_3, at))) .collect(); let h_evals: Vec<_> = h_pieces @@ -240,49 +223,49 @@ impl Proof { // Collapse openings at same points together into single openings using // x_4 challenge. - let mut q_polys: Vec>> = vec![None; meta.query_rows.len()]; - let mut q_blinds = vec![C::Scalar::zero(); meta.query_rows.len()]; - let mut q_evals: Vec<_> = vec![C::Scalar::zero(); meta.query_rows.len()]; + let mut q_polys: Vec>> = vec![None; meta.rotations.len()]; + let mut q_blinds = vec![C::Scalar::zero(); meta.rotations.len()]; + let mut q_evals: Vec<_> = vec![C::Scalar::zero(); meta.rotations.len()]; { - for (i, &(wire, ref at)) in meta.advice_queries.iter().enumerate() { - let query_row = *meta.query_rows.get(at).unwrap(); + for (query_index, &(wire, ref at)) in meta.advice_queries.iter().enumerate() { + let point_index = (*meta.rotations.get(at).unwrap()).0; - if q_polys[query_row].is_none() { - q_polys[query_row] = Some(advice_polys[wire.0].clone()); - q_blinds[query_row] = advice_blinds[wire.0]; - q_evals[query_row] = advice_evals[i]; + if q_polys[point_index].is_none() { + q_polys[point_index] = Some(advice_polys[wire.0].clone()); + q_blinds[point_index] = advice_blinds[wire.0]; + q_evals[point_index] = advice_evals[query_index]; } else { - parallelize(q_polys[query_row].as_mut().unwrap(), |q, start| { + parallelize(q_polys[point_index].as_mut().unwrap(), |q, start| { for (q, a) in q.iter_mut().zip(advice_polys[wire.0][start..].iter()) { *q *= &x_4; *q += a; } }); - q_blinds[query_row] *= &x_4; - q_blinds[query_row] += &advice_blinds[wire.0]; - q_evals[query_row] *= &x_4; - q_evals[query_row] += &advice_evals[i]; + q_blinds[point_index] *= &x_4; + q_blinds[point_index] += &advice_blinds[wire.0]; + q_evals[point_index] *= &x_4; + q_evals[point_index] += &advice_evals[query_index]; } } - for (i, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() { - let query_row = *meta.query_rows.get(at).unwrap(); + for (query_index, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() { + let point_index = (*meta.rotations.get(at).unwrap()).0; - if q_polys[query_row].is_none() { - q_polys[query_row] = Some(srs.fixed_polys[wire.0].clone()); - q_blinds[query_row] = C::Scalar::one(); - q_evals[query_row] = fixed_evals[i]; + if q_polys[point_index].is_none() { + q_polys[point_index] = Some(srs.fixed_polys[wire.0].clone()); + q_blinds[point_index] = C::Scalar::one(); + q_evals[point_index] = fixed_evals[query_index]; } else { - parallelize(q_polys[query_row].as_mut().unwrap(), |q, start| { + parallelize(q_polys[point_index].as_mut().unwrap(), |q, start| { for (q, a) in q.iter_mut().zip(srs.fixed_polys[wire.0][start..].iter()) { *q *= &x_4; *q += a; } }); - q_blinds[query_row] *= &x_4; - q_blinds[query_row] += &C::Scalar::one(); - q_evals[query_row] *= &x_4; - q_evals[query_row] += &fixed_evals[i]; + q_blinds[point_index] *= &x_4; + q_blinds[point_index] += &C::Scalar::one(); + q_evals[point_index] *= &x_4; + q_evals[point_index] += &fixed_evals[query_index]; } } @@ -292,23 +275,23 @@ impl Proof { .zip(h_evals.iter()) { // We query the h(X) polynomial at x_3 - let cur_row = *meta.query_rows.get(&0).unwrap(); + let point_index = (*meta.rotations.get(&Rotation::default()).unwrap()).0; - if q_polys[cur_row].is_none() { - q_polys[cur_row] = Some(h_poly); - q_blinds[cur_row] = *h_blind; - q_evals[cur_row] = *h_eval; + if q_polys[point_index].is_none() { + q_polys[point_index] = Some(h_poly); + q_blinds[point_index] = *h_blind; + q_evals[point_index] = *h_eval; } else { - parallelize(q_polys[cur_row].as_mut().unwrap(), |q, start| { + parallelize(q_polys[point_index].as_mut().unwrap(), |q, start| { for (q, a) in q.iter_mut().zip(h_poly[start..].iter()) { *q *= &x_4; *q += a; } }); - q_blinds[cur_row] *= &x_4; - q_blinds[cur_row] += h_blind; - q_evals[cur_row] *= &x_4; - q_evals[cur_row] += h_eval; + q_blinds[point_index] *= &x_4; + q_blinds[point_index] += h_blind; + q_evals[point_index] *= &x_4; + q_evals[point_index] += h_eval; } } } @@ -316,17 +299,10 @@ impl Proof { let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); let mut f_poly = None; - for (&row, &col) in meta.query_rows.iter() { - let mut poly = q_polys[col].as_ref().unwrap().clone(); - let mut point = x_3; - if row >= 0 { - point *= &domain.get_omega().pow_vartime(&[row as u64, 0, 0, 0]); - } else { - point *= &domain - .get_omega_inv() - .pow_vartime(&[row.abs() as u64, 0, 0, 0]); - } - poly[0] -= &q_evals[col]; + for (&row, &point_index) in meta.rotations.iter() { + let mut poly = q_polys[point_index.0].as_ref().unwrap().clone(); + let point = domain.rotate_omega(x_3, row); + poly[0] -= &q_evals[point_index.0]; let mut poly = kate_division(&poly, point); poly.push(C::Scalar::zero()); @@ -352,8 +328,11 @@ impl Proof { let mut q_evals = vec![]; - for (_, &col) in meta.query_rows.iter() { - q_evals.push(eval_polynomial(&q_polys[col].as_ref().unwrap(), x_6)); + for (_, &point_index) in meta.rotations.iter() { + q_evals.push(eval_polynomial( + &q_polys[point_index.0].as_ref().unwrap(), + x_6, + )); } for eval in q_evals.iter() { @@ -366,14 +345,14 @@ impl Proof { let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - for (_, &col) in meta.query_rows.iter() { + for (_, &point_index) in meta.rotations.iter() { f_blind *= &x_7; - f_blind += &q_blinds[col]; + f_blind += &q_blinds[point_index.0]; parallelize(&mut f_poly, |f, start| { for (f, a) in f .iter_mut() - .zip(q_polys[col].as_ref().unwrap()[start..].iter()) + .zip(q_polys[point_index.0].as_ref().unwrap()[start..].iter()) { *f *= &x_7; *f += a; diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index cc28d7d..fbffbc1 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -1,4 +1,4 @@ -use super::{hash_point, Proof, SRS}; +use super::{domain::Rotation, hash_point, Proof, SRS}; use crate::arithmetic::{get_challenge_scalar, Challenge, Curve, CurveAffine, Field}; use crate::polycommit::Params; use crate::transcript::Hasher; @@ -89,56 +89,57 @@ impl Proof { // Compress the commitments and expected evaluations at x_3 together // using the challenge x_4 - let mut q_commitments: Vec<_> = vec![None; srs.meta.query_rows.len()]; - let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.query_rows.len()]; + let mut q_commitments: Vec<_> = vec![None; srs.meta.rotations.len()]; + let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.rotations.len()]; { - for (i, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() { - let query_row = *srs.meta.query_rows.get(at).unwrap(); + for (query_index, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() { + let point_index = (*srs.meta.rotations.get(at).unwrap()).0; - if q_commitments[query_row].is_none() { - q_commitments[query_row] = + if q_commitments[point_index].is_none() { + q_commitments[point_index] = Some(self.advice_commitments[wire.0].to_projective()); - q_evals[query_row] = self.advice_evals[i]; + q_evals[point_index] = self.advice_evals[query_index]; } else { - q_commitments[query_row].as_mut().map(|commitment| { + q_commitments[point_index].as_mut().map(|commitment| { *commitment *= x_4; *commitment += self.advice_commitments[wire.0]; }); - q_evals[query_row] *= &x_4; - q_evals[query_row] += &self.advice_evals[i]; + q_evals[point_index] *= &x_4; + q_evals[point_index] += &self.advice_evals[query_index]; } } - for (i, &(wire, ref at)) in srs.meta.fixed_queries.iter().enumerate() { - let query_row = *srs.meta.query_rows.get(at).unwrap(); + for (query_index, &(wire, ref at)) in srs.meta.fixed_queries.iter().enumerate() { + let point_index = (*srs.meta.rotations.get(at).unwrap()).0; - if q_commitments[query_row].is_none() { - q_commitments[query_row] = Some(srs.fixed_commitments[wire.0].to_projective()); - q_evals[query_row] = self.fixed_evals[i]; + if q_commitments[point_index].is_none() { + q_commitments[point_index] = + Some(srs.fixed_commitments[wire.0].to_projective()); + q_evals[point_index] = self.fixed_evals[query_index]; } else { - q_commitments[query_row].as_mut().map(|commitment| { + q_commitments[point_index].as_mut().map(|commitment| { *commitment *= x_4; *commitment += srs.fixed_commitments[wire.0]; }); - q_evals[query_row] *= &x_4; - q_evals[query_row] += &self.fixed_evals[i]; + q_evals[point_index] *= &x_4; + q_evals[point_index] += &self.fixed_evals[query_index]; } } for (h_commitment, h_eval) in self.h_commitments.iter().zip(self.h_evals.iter()) { // We query the h(X) polynomial at x_3 - let cur_row = *srs.meta.query_rows.get(&0).unwrap(); + let point_index = (*srs.meta.rotations.get(&Rotation::default()).unwrap()).0; - if q_commitments[cur_row].is_none() { - q_commitments[cur_row] = Some(h_commitment.to_projective()); - q_evals[cur_row] = *h_eval; + if q_commitments[point_index].is_none() { + q_commitments[point_index] = Some(h_commitment.to_projective()); + q_evals[point_index] = *h_eval; } else { - q_commitments[cur_row].as_mut().map(|commitment| { + q_commitments[point_index].as_mut().map(|commitment| { *commitment *= x_4; *commitment += *h_commitment; }); - q_evals[cur_row] *= &x_4; - q_evals[cur_row] += h_eval; + q_evals[point_index] *= &x_4; + q_evals[point_index] += h_eval; } } } @@ -166,18 +167,11 @@ impl Proof { // We can compute the expected f_eval at x_6 using the q_evals provided // by the prover and from x_5 let mut f_eval = C::Scalar::zero(); - for (&row, &col) in srs.meta.query_rows.iter() { - let mut eval: C::Scalar = self.q_evals[col].clone(); - let mut point = x_3; - if row >= 0 { - point *= &srs.domain.get_omega().pow_vartime(&[row as u64, 0, 0, 0]); - } else { - point *= &srs - .domain - .get_omega_inv() - .pow_vartime(&[row.abs() as u64, 0, 0, 0]); - } - eval = eval - &q_evals[col]; + for (&row, &point_index) in srs.meta.rotations.iter() { + let mut eval: C::Scalar = self.q_evals[point_index.0].clone(); + + let point = srs.domain.rotate_omega(x_3, row); + eval = eval - &q_evals[point_index.0]; eval = eval * &(x_6 - &point).invert().unwrap(); f_eval *= &x_5; @@ -190,11 +184,11 @@ impl Proof { // Compute the final commitment that has to be opened let mut f_commitment: C::Projective = self.f_commitment.to_projective(); - for (_, &col) in srs.meta.query_rows.iter() { + for (_, &point_index) in srs.meta.rotations.iter() { f_commitment *= x_7; - f_commitment = f_commitment + &q_commitments[col].as_ref().unwrap(); + f_commitment = f_commitment + &q_commitments[point_index.0].as_ref().unwrap(); f_eval *= &x_7; - f_eval += &self.q_evals[col]; + f_eval += &self.q_evals[point_index.0]; } // Verify the opening proof From 154568c3879acdb52629c8859dc48f5a5bf13da7 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 27 Aug 2020 13:52:55 -0600 Subject: [PATCH 14/15] Clean up verification implementation --- src/plonk/verifier.rs | 71 +++++++++++-------------------------------- 1 file changed, 18 insertions(+), 53 deletions(-) diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index fbffbc1..ad6478f 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -35,15 +35,7 @@ impl Proof { // transcript on the scalar field. let mut transcript_scalar = HScalar::init(C::Scalar::one()); - for eval in self.advice_evals.iter() { - transcript_scalar.absorb(*eval); - } - - for eval in self.fixed_evals.iter() { - transcript_scalar.absorb(*eval); - } - - for eval in &self.h_evals { + for eval in self.advice_evals.iter().chain(self.fixed_evals.iter()).chain(self.h_evals.iter()) { transcript_scalar.absorb(*eval); } @@ -67,7 +59,6 @@ impl Proof { h_eval += &evaluation; } let xn = x_3.pow(&[params.n as u64, 0, 0, 0]); - h_eval *= &(xn - &C::Scalar::one()).invert().unwrap(); // Compute the expected h(x) value let mut expected_h_eval = C::Scalar::zero(); @@ -77,7 +68,7 @@ impl Proof { cur *= &xn; } - if h_eval != expected_h_eval { + if h_eval != (expected_h_eval * &(xn - &C::Scalar::one())) { return false; } @@ -89,58 +80,32 @@ impl Proof { // Compress the commitments and expected evaluations at x_3 together // using the challenge x_4 - let mut q_commitments: Vec<_> = vec![None; srs.meta.rotations.len()]; + let mut q_commitments: Vec> = vec![None; srs.meta.rotations.len()]; let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.rotations.len()]; { + let mut accumulate = |point_index: usize, new_commitment, eval| { + q_commitments[point_index] = q_commitments[point_index].map(|mut commitment| { + commitment *= x_4; + commitment += new_commitment; + commitment + }).or_else(|| Some(new_commitment.to_projective())); + q_evals[point_index] *= &x_4; + q_evals[point_index] += &eval; + }; + for (query_index, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() { let point_index = (*srs.meta.rotations.get(at).unwrap()).0; - - if q_commitments[point_index].is_none() { - q_commitments[point_index] = - Some(self.advice_commitments[wire.0].to_projective()); - q_evals[point_index] = self.advice_evals[query_index]; - } else { - q_commitments[point_index].as_mut().map(|commitment| { - *commitment *= x_4; - *commitment += self.advice_commitments[wire.0]; - }); - q_evals[point_index] *= &x_4; - q_evals[point_index] += &self.advice_evals[query_index]; - } + accumulate(point_index, self.advice_commitments[wire.0], self.advice_evals[query_index]); } for (query_index, &(wire, ref at)) in srs.meta.fixed_queries.iter().enumerate() { let point_index = (*srs.meta.rotations.get(at).unwrap()).0; - - if q_commitments[point_index].is_none() { - q_commitments[point_index] = - Some(srs.fixed_commitments[wire.0].to_projective()); - q_evals[point_index] = self.fixed_evals[query_index]; - } else { - q_commitments[point_index].as_mut().map(|commitment| { - *commitment *= x_4; - *commitment += srs.fixed_commitments[wire.0]; - }); - q_evals[point_index] *= &x_4; - q_evals[point_index] += &self.fixed_evals[query_index]; - } + accumulate(point_index, srs.fixed_commitments[wire.0], self.fixed_evals[query_index]); } + let current_index = (*srs.meta.rotations.get(&Rotation::default()).unwrap()).0; for (h_commitment, h_eval) in self.h_commitments.iter().zip(self.h_evals.iter()) { - // We query the h(X) polynomial at x_3 - let point_index = (*srs.meta.rotations.get(&Rotation::default()).unwrap()).0; - - if q_commitments[point_index].is_none() { - q_commitments[point_index] = Some(h_commitment.to_projective()); - q_evals[point_index] = *h_eval; - } else { - q_commitments[point_index].as_mut().map(|commitment| { - *commitment *= x_4; - *commitment += *h_commitment; - }); - q_evals[point_index] *= &x_4; - q_evals[point_index] += h_eval; - } + accumulate(current_index, *h_commitment, *h_eval); } } @@ -168,7 +133,7 @@ impl Proof { // by the prover and from x_5 let mut f_eval = C::Scalar::zero(); for (&row, &point_index) in srs.meta.rotations.iter() { - let mut eval: C::Scalar = self.q_evals[point_index.0].clone(); + let mut eval = self.q_evals[point_index.0]; let point = srs.domain.rotate_omega(x_3, row); eval = eval - &q_evals[point_index.0]; From b453b845b8e37f92b4fec6945a10e3ae47178dd9 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 27 Aug 2020 14:03:43 -0600 Subject: [PATCH 15/15] Clean up prover implementation --- src/plonk/prover.rs | 118 +++++++++++++++++++----------------------- src/plonk/verifier.rs | 31 ++++++++--- 2 files changed, 76 insertions(+), 73 deletions(-) diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 41310e3..70f006c 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -115,7 +115,7 @@ impl Proof { &|index| advice_cosets[index].clone(), &|mut a, b| { parallelize(&mut a, |a, start| { - for (a, b) in a.into_iter().zip(b[start..].iter()) { + for (a, b) in a.iter_mut().zip(b[start..].iter()) { *a += b; } }); @@ -123,7 +123,7 @@ impl Proof { }, &|mut a, b| { parallelize(&mut a, |a, start| { - for (a, b) in a.into_iter().zip(b[start..].iter()) { + for (a, b) in a.iter_mut().zip(b[start..].iter()) { *a *= b; } }); @@ -188,7 +188,9 @@ impl Proof { let fixed_evals: Vec<_> = meta .fixed_queries .iter() - .map(|&(wire, at)| eval_polynomial(&srs.fixed_polys[wire.0], domain.rotate_omega(x_3, at))) + .map(|&(wire, at)| { + eval_polynomial(&srs.fixed_polys[wire.0], domain.rotate_omega(x_3, at)) + }) .collect(); let h_evals: Vec<_> = h_pieces @@ -227,78 +229,63 @@ impl Proof { let mut q_blinds = vec![C::Scalar::zero(); meta.rotations.len()]; let mut q_evals: Vec<_> = vec![C::Scalar::zero(); meta.rotations.len()]; { + let mut accumulate = |point_index: usize, new_poly: &Vec<_>, blind, eval| { + q_polys[point_index] + .as_mut() + .map(|poly| { + parallelize(poly, |q, start| { + for (q, a) in q.iter_mut().zip(new_poly[start..].iter()) { + *q *= &x_4; + *q += a; + } + }); + }) + .or_else(|| { + q_polys[point_index] = Some(new_poly.clone()); + Some(()) + }); + q_blinds[point_index] *= &x_4; + q_blinds[point_index] += &blind; + q_evals[point_index] *= &x_4; + q_evals[point_index] += &eval; + }; + for (query_index, &(wire, ref at)) in meta.advice_queries.iter().enumerate() { let point_index = (*meta.rotations.get(at).unwrap()).0; - if q_polys[point_index].is_none() { - q_polys[point_index] = Some(advice_polys[wire.0].clone()); - q_blinds[point_index] = advice_blinds[wire.0]; - q_evals[point_index] = advice_evals[query_index]; - } else { - parallelize(q_polys[point_index].as_mut().unwrap(), |q, start| { - for (q, a) in q.iter_mut().zip(advice_polys[wire.0][start..].iter()) { - *q *= &x_4; - *q += a; - } - }); - q_blinds[point_index] *= &x_4; - q_blinds[point_index] += &advice_blinds[wire.0]; - q_evals[point_index] *= &x_4; - q_evals[point_index] += &advice_evals[query_index]; - } + accumulate( + point_index, + &advice_polys[wire.0], + advice_blinds[wire.0], + advice_evals[query_index], + ); } for (query_index, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() { let point_index = (*meta.rotations.get(at).unwrap()).0; - if q_polys[point_index].is_none() { - q_polys[point_index] = Some(srs.fixed_polys[wire.0].clone()); - q_blinds[point_index] = C::Scalar::one(); - q_evals[point_index] = fixed_evals[query_index]; - } else { - parallelize(q_polys[point_index].as_mut().unwrap(), |q, start| { - for (q, a) in q.iter_mut().zip(srs.fixed_polys[wire.0][start..].iter()) { - *q *= &x_4; - *q += a; - } - }); - q_blinds[point_index] *= &x_4; - q_blinds[point_index] += &C::Scalar::one(); - q_evals[point_index] *= &x_4; - q_evals[point_index] += &fixed_evals[query_index]; - } + accumulate( + point_index, + &srs.fixed_polys[wire.0], + C::Scalar::one(), + fixed_evals[query_index], + ); } + // We query the h(X) polynomial at x_3 + let current_index = (*meta.rotations.get(&Rotation::default()).unwrap()).0; for ((h_poly, h_blind), h_eval) in h_pieces .into_iter() .zip(h_blinds.iter()) .zip(h_evals.iter()) { - // We query the h(X) polynomial at x_3 - let point_index = (*meta.rotations.get(&Rotation::default()).unwrap()).0; - - if q_polys[point_index].is_none() { - q_polys[point_index] = Some(h_poly); - q_blinds[point_index] = *h_blind; - q_evals[point_index] = *h_eval; - } else { - parallelize(q_polys[point_index].as_mut().unwrap(), |q, start| { - for (q, a) in q.iter_mut().zip(h_poly[start..].iter()) { - *q *= &x_4; - *q += a; - } - }); - q_blinds[point_index] *= &x_4; - q_blinds[point_index] += h_blind; - q_evals[point_index] *= &x_4; - q_evals[point_index] += h_eval; - } + accumulate(current_index, &h_poly, *h_blind, *h_eval); } } let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - let mut f_poly = None; + let mut f_poly: Option> = None; for (&row, &point_index) in meta.rotations.iter() { let mut poly = q_polys[point_index.0].as_ref().unwrap().clone(); let point = domain.rotate_omega(x_3, row); @@ -306,16 +293,17 @@ impl Proof { let mut poly = kate_division(&poly, point); poly.push(C::Scalar::zero()); - if f_poly.is_none() { - f_poly = Some(poly); - } else { - parallelize(f_poly.as_mut().unwrap(), |q, start| { - for (q, a) in q.iter_mut().zip(poly[start..].iter()) { - *q *= &x_5; - *q += a; - } - }); - } + f_poly = f_poly + .map(|mut f_poly| { + parallelize(&mut f_poly, |q, start| { + for (q, a) in q.iter_mut().zip(poly[start..].iter()) { + *q *= &x_5; + *q += a; + } + }); + f_poly + }) + .or_else(|| Some(poly)); } let mut f_poly = f_poly.unwrap(); let mut f_blind = C::Scalar::random(); diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index ad6478f..d8560d1 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -35,7 +35,12 @@ impl Proof { // transcript on the scalar field. let mut transcript_scalar = HScalar::init(C::Scalar::one()); - for eval in self.advice_evals.iter().chain(self.fixed_evals.iter()).chain(self.h_evals.iter()) { + for eval in self + .advice_evals + .iter() + .chain(self.fixed_evals.iter()) + .chain(self.h_evals.iter()) + { transcript_scalar.absorb(*eval); } @@ -84,23 +89,33 @@ impl Proof { let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.rotations.len()]; { let mut accumulate = |point_index: usize, new_commitment, eval| { - q_commitments[point_index] = q_commitments[point_index].map(|mut commitment| { - commitment *= x_4; - commitment += new_commitment; - commitment - }).or_else(|| Some(new_commitment.to_projective())); + q_commitments[point_index] = q_commitments[point_index] + .map(|mut commitment| { + commitment *= x_4; + commitment += new_commitment; + commitment + }) + .or_else(|| Some(new_commitment.to_projective())); q_evals[point_index] *= &x_4; q_evals[point_index] += &eval; }; for (query_index, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() { let point_index = (*srs.meta.rotations.get(at).unwrap()).0; - accumulate(point_index, self.advice_commitments[wire.0], self.advice_evals[query_index]); + accumulate( + point_index, + self.advice_commitments[wire.0], + self.advice_evals[query_index], + ); } for (query_index, &(wire, ref at)) in srs.meta.fixed_queries.iter().enumerate() { let point_index = (*srs.meta.rotations.get(at).unwrap()).0; - accumulate(point_index, srs.fixed_commitments[wire.0], self.fixed_evals[query_index]); + accumulate( + point_index, + srs.fixed_commitments[wire.0], + self.fixed_evals[query_index], + ); } let current_index = (*srs.meta.rotations.get(&Rotation::default()).unwrap()).0;