diff --git a/src/plonk.rs b/src/plonk.rs index 345d39e..f71ecf7 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -32,6 +32,9 @@ pub struct SRS { fixed_commitments: Vec, fixed_polys: Vec>, fixed_cosets: Vec>, + permutation_commitments: Vec>, + permutation_polys: Vec>>, + permutation_cosets: Vec>>, meta: MetaCircuit, } @@ -87,6 +90,10 @@ fn test_proving() { use std::marker::PhantomData; const K: u32 = 5; + /// This represents an advice wire at a certain row in the MetaCircuit + #[derive(Copy, Clone, Debug)] + pub struct Variable(AdviceWire, usize); + // Initialize the polynomial commitment parameters let params: Params = Params::new::>(K); @@ -99,6 +106,8 @@ fn test_proving() { sb: FixedWire, sc: FixedWire, sm: FixedWire, + + perm: usize, } trait StandardCS { @@ -108,6 +117,7 @@ fn test_proving() { fn raw_add(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> where F: FnOnce() -> Result<(FF, FF, FF), Error>; + fn copy(&mut self, a: Variable, b: Variable) -> Result<(), Error>; } struct MyCircuit { @@ -160,9 +170,9 @@ fn test_proving() { self.cs .assign_fixed(self.config.sm, index, || Ok(FF::one()))?; Ok(( - Variable::new(self.config.a, index), - Variable::new(self.config.b, index), - Variable::new(self.config.c, index), + 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> @@ -192,11 +202,28 @@ fn test_proving() { self.cs .assign_fixed(self.config.sm, index, || Ok(FF::zero()))?; Ok(( - Variable::new(self.config.a, index), - Variable::new(self.config.b, index), - Variable::new(self.config.c, index), + Variable(self.config.a, index), + Variable(self.config.b, index), + Variable(self.config.c, index), )) } + fn copy(&mut self, a: Variable, b: Variable) -> Result<(), Error> { + let left_wire = match a.0 { + x if x == self.config.a => 0, + x if x == self.config.b => 1, + x if x == self.config.c => 2, + _ => unreachable!(), + }; + let right_wire = match b.0 { + x if x == self.config.a => 0, + x if x == self.config.b => 1, + x if x == self.config.c => 2, + _ => unreachable!(), + }; + + self.cs + .copy(self.config.perm, left_wire, a.1, right_wire, b.1) + } } impl Circuit for MyCircuit { @@ -207,6 +234,8 @@ fn test_proving() { let b = meta.advice_wire(); let c = meta.advice_wire(); + let perm = meta.permutation(&[a, b, c]); + let sa = meta.fixed_wire(); let sb = meta.fixed_wire(); let sc = meta.fixed_wire(); @@ -233,6 +262,7 @@ fn test_proving() { sb, sc, sm, + perm, } } @@ -253,7 +283,7 @@ fn test_proving() { a_squared.ok_or(Error::SynthesisError)?, )) })?; - let (a1, _, _) = cs.raw_add(|| { + let (_, b1, _) = cs.raw_add(|| { let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2)); Ok(( self.a.ok_or(Error::SynthesisError)?, @@ -261,7 +291,7 @@ fn test_proving() { fin.ok_or(Error::SynthesisError)?, )) })?; - cs.cs.assign_copy(a1, c0)?; + cs.copy(b1, c0)?; } Ok(()) diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index b190d29..26aca04 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -14,17 +14,6 @@ pub struct FixedWire(pub usize); #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct AdviceWire(pub usize); -/// This represents an advice wire at a certain row in the MetaCircuit -#[derive(Copy, Clone, Debug)] -pub struct Variable(pub AdviceWire, pub usize); - -impl Variable { - /// Construct a Variable - pub fn new(wire: AdviceWire, index: usize) -> Variable { - Variable(wire, index) - } -} - /// This trait allows a [`Circuit`] to direct some backend to assign a witness /// for a constraint system. pub trait ConstraintSystem { @@ -45,7 +34,14 @@ pub trait ConstraintSystem { ) -> Result<(), Error>; /// Assign two advice wires to have the same value - fn assign_copy(&mut self, left: Variable, right: Variable) -> Result<(), Error>; + fn copy( + &mut self, + permutation: usize, + left_wire: usize, + left_row: usize, + right_wire: usize, + right_row: usize, + ) -> Result<(), Error>; } /// This is a trait that circuits provide implementations for so that the @@ -165,6 +161,14 @@ pub struct MetaCircuit { // Mapping from a witness vector rotation to the index in the point vector. pub(crate) rotations: HashMap, + + // Vector of permutation arguments, where each corresponds to a set of wires + // that are involved in a permutation argument. As an example, we could have + // a permutation argument between wires (A, B, C) which allows copy + // constraints to be enforced between advice wire values in A, B and C, and + // another permutation between wires (B, C, D) which allows the same with D + // instead of A. + pub(crate) permutations: Vec>, } impl Default for MetaCircuit { @@ -179,11 +183,19 @@ impl Default for MetaCircuit { fixed_queries: Vec::new(), advice_queries: Vec::new(), rotations, + permutations: Vec::new(), } } } impl MetaCircuit { + /// Add a permutation argument for some advice wires + pub fn permutation(&mut self, wires: &[AdviceWire]) -> usize { + let index = self.permutations.len(); + self.permutations.push(wires.to_vec()); + index + } + /// Query a fixed wire at a relative position pub fn query_fixed(&mut self, wire: FixedWire, at: i32) -> Polynomial { let at = Rotation(at); diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 8cc1e72..8ac627e 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -1,5 +1,5 @@ use super::{ - circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit, Variable}, + circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit}, domain::Rotation, hash_point, Error, Proof, SRS, }; @@ -54,7 +54,14 @@ impl Proof { Ok(()) } - fn assign_copy(&mut self, _: Variable, _: Variable) -> Result<(), Error> { + fn copy( + &mut self, + _: usize, + _: usize, + _: usize, + _: usize, + _: usize, + ) -> Result<(), Error> { // We only care about advice wires here Ok(()) diff --git a/src/plonk/srs.rs b/src/plonk/srs.rs index 82ca2c9..13c7992 100644 --- a/src/plonk/srs.rs +++ b/src/plonk/srs.rs @@ -1,6 +1,6 @@ use super::{ - circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit, Variable}, - domain::EvaluationDomain, + circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit}, + domain::{EvaluationDomain, Rotation}, Error, SRS, }; use crate::arithmetic::{Curve, CurveAffine, Field}; @@ -15,7 +15,7 @@ impl SRS { ) -> Result { struct Assembly { fixed: Vec>, - copy: Vec>, + copy: Vec>>, } impl ConstraintSystem for Assembly { @@ -44,12 +44,38 @@ impl SRS { Ok(()) } - fn assign_copy(&mut self, left: Variable, right: Variable) -> Result<(), Error> { - *self - .copy - .get_mut((left.0).0) - .and_then(|v| v.get_mut(left.1)) - .ok_or(Error::BoundsFailure)? = right; + fn copy( + &mut self, + permutation: usize, + left_wire: usize, + left_row: usize, + right_wire: usize, + right_row: usize, + ) -> Result<(), Error> { + let left: (usize, usize) = *self.copy[permutation] + .get_mut(left_wire) + .and_then(|wire| wire.get_mut(left_row)) + .ok_or(Error::BoundsFailure)?; + + let right: (usize, usize) = *self.copy[permutation] + .get_mut(right_wire) + .and_then(|wire| wire.get_mut(right_row)) + .ok_or(Error::BoundsFailure)?; + + if left == (left_wire, left_row) || right == (right_wire, right_row) { + // Don't perform the copy constraint because it will undo + // the effect of the permutation. + } else { + *self.copy[permutation] + .get_mut(left_wire) + .and_then(|wire| wire.get_mut(left_row)) + .ok_or(Error::BoundsFailure)? = right; + + *self.copy[permutation] + .get_mut(right_wire) + .and_then(|wire| wire.get_mut(right_row)) + .ok_or(Error::BoundsFailure)? = left; + } Ok(()) } @@ -58,30 +84,96 @@ impl SRS { let mut meta = MetaCircuit::default(); let config = ConcreteCircuit::configure(&mut meta); + let mut degree = 1; + for poly in meta.gates.iter() { + degree = std::cmp::max(degree, poly.degree()); + } + for permutation in &meta.permutations { + degree = std::cmp::max(degree, permutation.len() + 1); + } + + let domain = EvaluationDomain::new(degree as u32, params.k); + + let mut largest_permutation_length = 0; + for permutation in &meta.permutations { + largest_permutation_length = + std::cmp::max(permutation.len(), largest_permutation_length); + } + + let mut omega_powers = Vec::with_capacity(params.n as usize); + { + let mut cur = C::Scalar::one(); + for _ in 0..params.n { + omega_powers.push(cur); + cur *= &domain.get_omega(); + } + } + + let mut deltaomega = Vec::with_capacity(largest_permutation_length); + { + let mut cur = C::Scalar::one(); + for _ in 0..largest_permutation_length { + let mut omega_powers = omega_powers.clone(); + for o in &mut omega_powers { + *o *= &cur; + } + + deltaomega.push(omega_powers); + + cur *= &C::Scalar::DELTA; + } + } + let mut assembly: Assembly = Assembly { fixed: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_fixed_wires], - copy: vec![ - vec![Variable::new(AdviceWire(0), 0); params.n as usize]; - meta.num_advice_wires - ], + copy: vec![], }; + for permutation in &meta.permutations { + let mut wires = vec![]; + for (i, _) in permutation.iter().enumerate() { + wires.push((0..params.n).map(|j| (i, j as usize)).collect()); + } + assembly.copy.push(wires); + } + // Synthesize the circuit to obtain SRS circuit.synthesize(&mut assembly, config)?; + // Compute permutation polynomials + let mut permutation_commitments = vec![]; + let mut permutation_polys = vec![]; + let mut permutation_cosets = vec![]; + for (permutation_index, permutation) in meta.permutations.iter().enumerate() { + let mut commitments = vec![]; + let mut polys = vec![]; + let mut cosets = vec![]; + for (i, _) in permutation.iter().enumerate() { + let permutation_poly: Vec<_> = (0..params.n as usize) + .map(|j| { + let (permuted_i, permuted_j) = assembly.copy[permutation_index][i][j]; + deltaomega[permuted_i][permuted_j] + }) + .collect(); + commitments.push( + params + .commit_lagrange(&permutation_poly, C::Scalar::one()) + .to_affine(), + ); + polys.push(permutation_poly.clone()); + cosets.push(domain.obtain_coset(permutation_poly, Rotation::default())); + } + permutation_commitments.push(commitments); + permutation_polys.push(polys); + permutation_cosets.push(cosets); + } + let fixed_commitments = assembly .fixed .iter() .map(|poly| params.commit_lagrange(poly, C::Scalar::one()).to_affine()) .collect(); - let mut degree = 1; - for poly in meta.gates.iter() { - degree = std::cmp::max(degree, poly.degree()); - } - - let domain = EvaluationDomain::new(degree as u32, params.k); - let fixed_polys: Vec<_> = assembly .fixed .into_iter() @@ -102,6 +194,9 @@ impl SRS { fixed_commitments, fixed_polys, fixed_cosets, + permutation_commitments, + permutation_polys, + permutation_cosets, meta, }) }