From ec0632bf16b8f70c28e6a65c404585922c96995c Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 1 Mar 2021 12:35:02 -0800 Subject: [PATCH] Degree-3 GMiMC gate --- src/constraint_polynomial.rs | 83 ++---------------------------------- src/gates/gmimc.rs | 80 +++++++++++++++++++++++++--------- src/gates/output_graph.rs | 32 +++++++++++--- 3 files changed, 90 insertions(+), 105 deletions(-) diff --git a/src/constraint_polynomial.rs b/src/constraint_polynomial.rs index 12a44436..45bf7003 100644 --- a/src/constraint_polynomial.rs +++ b/src/constraint_polynomial.rs @@ -1,17 +1,17 @@ +use std::{fmt, ptr}; +use std::borrow::Borrow; use std::collections::{HashMap, HashSet}; +use std::fmt::{Debug, Display, Formatter}; use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use std::{ptr, fmt}; use std::rc::Rc; use num::{BigUint, FromPrimitive, One, Zero}; use crate::field::field::Field; -use crate::wire::Wire; use crate::gates::output_graph::GateOutputLocation; -use std::borrow::Borrow; -use std::fmt::{Display, Formatter, Debug}; +use crate::wire::Wire; pub(crate) struct EvaluationVars<'a, F: Field> { pub(crate) local_constants: &'a [F], @@ -121,41 +121,6 @@ impl ConstraintPolynomial { self.0.degree() } - pub(crate) fn populate_degree_map(&self, degrees: &mut HashMap) { - if degrees.contains_key(self) { - // Already visited this node in the polynomial graph. - return; - } - - match self.0.as_ref() { - ConstraintPolynomialInner::Constant(_) => - degrees.insert(self.clone(), BigUint::zero()), - ConstraintPolynomialInner::LocalConstant(_) => - degrees.insert(self.clone(), BigUint::one()), - ConstraintPolynomialInner::NextConstant(_) => - degrees.insert(self.clone(), BigUint::one()), - ConstraintPolynomialInner::LocalWireValue(_) => - degrees.insert(self.clone(), BigUint::one()), - ConstraintPolynomialInner::NextWireValue(_) => - degrees.insert(self.clone(), BigUint::one()), - ConstraintPolynomialInner::Sum { lhs, rhs } => { - lhs.populate_degree_map(degrees); - rhs.populate_degree_map(degrees); - degrees.insert(self.clone(), (°rees[lhs]).max(°rees[rhs]).clone()) - } - ConstraintPolynomialInner::Product { lhs, rhs } => { - lhs.populate_degree_map(degrees); - rhs.populate_degree_map(degrees); - degrees.insert(self.clone(), °rees[lhs] + °rees[rhs]) - } - ConstraintPolynomialInner::Exponentiation { base, exponent } => { - base.populate_degree_map(degrees); - degrees.insert(self.clone(), - °rees[base] * BigUint::from_usize(*exponent).unwrap()) - } - }; - } - /// Returns the set of wires that this constraint would depend on if it were applied at a /// certain gate index. pub(crate) fn dependencies(&self, gate: usize) -> Vec { @@ -271,13 +236,6 @@ impl ConstraintPolynomial { fn from_inner(inner: ConstraintPolynomialInner) -> Self { Self(Rc::new(inner)) } - - /// The number of polynomials in this graph. - fn graph_size(&self) -> usize { - let mut degrees = HashMap::new(); - self.populate_degree_map(&mut degrees); - degrees.len() - } } impl Display for ConstraintPolynomial { @@ -611,37 +569,4 @@ mod tests { // ConstraintPolynomialInner. assert_eq!(wire0.clone(), wire0); } - - #[test] - fn replace_all() { - type F = CrandallField; - let wire0 = ConstraintPolynomial::::local_wire_value(0); - let wire1 = ConstraintPolynomial::::local_wire_value(1); - let wire2 = ConstraintPolynomial::::local_wire_value(2); - let wire3 = ConstraintPolynomial::::local_wire_value(3); - let wire4 = ConstraintPolynomial::::local_wire_value(4); - let sum01 = &wire0 + &wire1; - let sum12 = &wire1 + &wire2; - let sum23 = &wire2 + &wire3; - let product = &sum01 * &sum12 * &sum23; - - assert_eq!( - wire0.replace_all(wire0.clone(), wire1.clone()), - wire1); - - assert_eq!( - wire0.replace_all(wire1.clone(), wire2.clone()), - wire0); - - // This should be a no-op, since wire 4 is not present in the product. - assert_eq!( - product.replace_all(wire4.clone(), wire3.clone()).graph_size(), - product.graph_size()); - - // This shouldn't change the graph structure at all, since the replacement wire 4 was not - // previously present. - assert_eq!( - product.replace_all(wire3.clone(), wire4.clone()).graph_size(), - product.graph_size()); - } } diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index 76611958..23e90380 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -1,6 +1,8 @@ use std::convert::TryInto; use std::sync::Arc; +use num::{BigUint, One}; + use crate::circuit_data::CircuitConfig; use crate::constraint_polynomial::ConstraintPolynomial; use crate::field::field::Field; @@ -34,17 +36,31 @@ impl GMiMCGate { /// If this is set to 1, the first four inputs will be swapped with the next four inputs. This /// is useful for ordering hashes in Merkle proofs. Otherwise, this should be set to 0. // TODO: Assert binary. - pub const WIRE_SWITCH: usize = 0; + pub const WIRE_SWITCH: usize = W; /// The wire index for the i'th input to the permutation. pub fn wire_input(i: usize) -> usize { - i + 1 + i } /// The wire index for the i'th output to the permutation. /// Note that outputs are written to the next gate's wires. pub fn wire_output(i: usize) -> usize { - i + 1 + i + } + + /// Adds a local wire output to this output graph. Returns a `ConstraintPolynomial` which + /// references the newly-created wire. + /// + /// This may seem like it belongs in `OutputGraph`, but it is not that general, since it uses + /// a notion of "next available" local wire indices specific to this gate. + fn add_local( + outputs: &mut OutputGraph, + poly: ConstraintPolynomial, + ) -> ConstraintPolynomial { + let index = outputs.max_local_output_index().map_or(W + 1, |i| i + 1); + outputs.add(GateOutputLocation::LocalWire(index), poly); + ConstraintPolynomial::local_wire_value(index) } } @@ -59,6 +75,8 @@ impl DeterministicGate for GMiMCGat .map(|i| ConstraintPolynomial::local_wire_value(Self::wire_input(i))) .collect::>(); + let mut outputs = OutputGraph::new(); + // Conditionally switch inputs based on the (boolean) switch wire. let switch = ConstraintPolynomial::local_wire_value(Self::WIRE_SWITCH); let mut state = Vec::new(); @@ -83,21 +101,20 @@ impl DeterministicGate for GMiMCGat for r in 0..R { let active = r % W; let round_constant = ConstraintPolynomial::constant(self.constants[r]); - let f = (&state[active] + &addition_buffer + round_constant).cube(); - addition_buffer += &f; - state[active] -= f; + let mut f_input = &state[active] + &addition_buffer + round_constant; + if f_input.degree() > BigUint::one() { + f_input = Self::add_local(&mut outputs, f_input); + } + let f_output = f_input.cube(); + addition_buffer += &f_output; + state[active] -= f_output; } for i in 0..W { - state[i] += &addition_buffer; + outputs.add(GateOutputLocation::NextWire(i), &state[i] + &addition_buffer); } - let outputs = state.into_iter() - .enumerate() - .map(|(i, out)| (GateOutputLocation::NextWire(Self::wire_output(i)), out)) - .collect(); - - OutputGraph { outputs } + outputs } fn additional_constraints(&self, _config: CircuitConfig) -> Vec> { @@ -111,25 +128,48 @@ impl DeterministicGate for GMiMCGat mod tests { use std::sync::Arc; - use crate::field::crandall_field::CrandallField; - use crate::gates::gmimc::GMiMCGate; - use crate::field::field::Field; use crate::circuit_data::CircuitConfig; + use crate::field::crandall_field::CrandallField; + use crate::field::field::Field; use crate::gates::deterministic_gate::DeterministicGate; + use crate::gates::gmimc::GMiMCGate; + use crate::circuit_builder::CircuitBuilder2; #[test] - #[ignore] fn degree() { type F = CrandallField; const W: usize = 12; - const R: usize = 20; + const R: usize = 101; let gate = GMiMCGate:: { constants: Arc::new([F::TWO; R]) }; let config = CircuitConfig { num_wires: 200, num_routed_wires: 200, - security_bits: 128 + security_bits: 128, }; let outs = gate.outputs(config); - assert_eq!(outs.max_wire_input_index(), Some(50)); + + assert_eq!(outs.degree(), 3); + assert_eq!(outs.max_local_output_index(), Some(57)); + panic!(); + } + + #[test] + fn generated_output() { + type F = CrandallField; + const W: usize = 12; + const R: usize = 101; + let constants = Arc::new([F::TWO; R]); + let gate = GMiMCGate::::with_constants(constants); + + let config = CircuitConfig { + num_wires: 200, + num_routed_wires: 200, + security_bits: 128, + }; + let mut builder = CircuitBuilder2::new(config); + builder.add_gate(gate, Vec::new()); + // let circuit = builder.build(); + + // TODO: generate witness & compare output to normal GMiMC function. } } diff --git a/src/gates/output_graph.rs b/src/gates/output_graph.rs index 9e6c6651..a07f6b95 100644 --- a/src/gates/output_graph.rs +++ b/src/gates/output_graph.rs @@ -1,11 +1,11 @@ +use std::{fmt, iter}; use std::collections::HashMap; -use std::{iter, fmt}; +use std::fmt::{Display, Formatter}; use num::{BigUint, FromPrimitive, One, ToPrimitive}; -use crate::constraint_polynomial::ConstraintPolynomial; +use crate::constraint_polynomial::{ConstraintPolynomial, EvaluationVars}; use crate::field::field::Field; -use std::fmt::{Display, Formatter}; /// Represents a set of deterministic gate outputs, expressed as polynomials over witness /// values. @@ -15,15 +15,35 @@ pub struct OutputGraph { } impl OutputGraph { + /// Creates a new output graph with no outputs. + pub fn new() -> Self { + Self { outputs: Vec::new() } + } + /// Creates an output graph with a single output. pub fn single_output(loc: GateOutputLocation, out: ConstraintPolynomial) -> Self { Self { outputs: vec![(loc, out)] } } - /// The largest local wire index in this entire graph. - pub(crate) fn max_wire_input_index(&self) -> Option { + pub fn add(&mut self, location: GateOutputLocation, poly: ConstraintPolynomial) { + self.outputs.push((location, poly)) + } + + /// The largest polynomial degree among all polynomials in this graph. + pub fn degree(&self) -> usize { self.outputs.iter() - .flat_map(|(loc, out)| out.max_wire_input_index()) + .map(|(loc, out)| out.degree().to_usize().unwrap()) + .max() + .unwrap_or(0) + } + + /// The largest local wire index among this graph's output locations. + pub fn max_local_output_index(&self) -> Option { + self.outputs.iter() + .filter_map(|(loc, out)| match loc { + GateOutputLocation::LocalWire(i) => Some(*i), + GateOutputLocation::NextWire(_) => None + }) .max() } }