From 9d650514baee50b619804bd83834ece1f04f8a39 Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Thu, 14 Oct 2021 15:50:30 +0200 Subject: [PATCH] Update `Constraint` to take witness value Constraints contains also witness indexes. The `gate` functions are supposed to take plain `Constraint`s while `component` functions can receive witnesses and build internal constraints. Resolves #587 --- README.md | 26 +-- benches/plonk.rs | 9 +- src/circuit.rs | 26 +-- src/constraint_system.rs | 2 +- src/constraint_system/arithmetic.rs | 157 ++++++++++------- src/constraint_system/boolean.rs | 31 ++-- src/constraint_system/composer.rs | 162 ++++++++++-------- src/constraint_system/constraint.rs | 97 ++++++++--- src/constraint_system/ecc.rs | 24 ++- .../ecc/curve_addition/variable_base_gate.rs | 87 +++++----- .../ecc/scalar_mul/fixed_base.rs | 11 +- .../ecc/scalar_mul/variable_base.rs | 37 ---- src/constraint_system/helper.rs | 12 +- src/constraint_system/logic.rs | 20 ++- src/constraint_system/range.rs | 8 +- src/permutation.rs | 31 +++- src/proof_system/preprocess.rs | 2 +- tests/circuit.rs | 22 +-- 18 files changed, 412 insertions(+), 352 deletions(-) diff --git a/README.md b/README.md index 9e70772e5..d6a5114b6 100644 --- a/README.md +++ b/README.md @@ -43,15 +43,11 @@ impl Circuit for TestCircuit { let constraint = Constraint::new() .left(1) .right(1) - .public(-self.c); + .public(-self.c) + .a(a) + .b(b); - composer.append_gate( - a, - b, - composer.constant_zero(), - composer.constant_zero(), - constraint, - ); + composer.append_gate(constraint); // Check that a and b are in range composer.component_range(a, 1 << 6); @@ -61,15 +57,11 @@ impl Circuit for TestCircuit { let constraint = Constraint::new() .mul(1) .output(1) - .public(-self.d); - - composer.append_gate( - a, - b, - composer.constant_zero(), - composer.constant_zero(), - constraint, - ); + .public(-self.d) + .a(a) + .b(b); + + composer.append_gate(constraint); let e = composer.append_witness(self.e); let scalar_mul_result = composer diff --git a/benches/plonk.rs b/benches/plonk.rs index 8759cc7a9..c95475b88 100644 --- a/benches/plonk.rs +++ b/benches/plonk.rs @@ -33,7 +33,7 @@ impl Circuit for BenchCircuit { let mut b = BlsScalar::from(3u64); let mut c; - let zero = composer.constant_zero(); + let zero = TurboComposer::constant_zero(); while composer.gates() < self.padded_gates() { a += BlsScalar::one(); @@ -49,9 +49,12 @@ impl Circuit for BenchCircuit { .left(1) .right(1) .output(-BlsScalar::one()) - .constant(1); + .constant(1) + .a(x) + .b(y) + .o(z); - composer.append_gate(x, y, z, zero, constraint); + composer.append_gate(constraint); } Ok(()) diff --git a/src/circuit.rs b/src/circuit.rs index 44a3b8d54..ade1dfa12 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -152,7 +152,7 @@ impl VerifierData { /// composer: &mut TurboComposer, /// ) -> Result<(), Error> { /// // Add fixed witness zero -/// let zero = composer.constant_zero(); +/// let zero = TurboComposer::constant_zero(); /// let a = composer.append_witness(self.a); /// let b = composer.append_witness(self.b); /// @@ -160,15 +160,11 @@ impl VerifierData { /// let constraint = Constraint::new() /// .left(1) /// .right(1) -/// .public(-self.c); +/// .public(-self.c) +/// .a(a) +/// .b(b); /// -/// composer.append_gate( -/// a, -/// b, -/// zero, -/// zero, -/// constraint, -/// ); +/// composer.append_gate(constraint); /// /// // Check that a and b are in range /// composer.component_range(a, 1 << 6); @@ -178,15 +174,11 @@ impl VerifierData { /// let constraint = Constraint::new() /// .mul(1) /// .output(1) -/// .public(-self.d); +/// .public(-self.d) +/// .a(a) +/// .b(b); /// -/// composer.append_gate( -/// a, -/// b, -/// zero, -/// zero, -/// constraint, -/// ); +/// composer.append_gate(constraint); /// /// let e = composer.append_witness(self.e); /// let scalar_mul_result = diff --git a/src/constraint_system.rs b/src/constraint_system.rs index b6f30a6f8..375619f67 100644 --- a/src/constraint_system.rs +++ b/src/constraint_system.rs @@ -16,7 +16,7 @@ pub(crate) mod logic; pub(crate) mod range; pub(crate) mod witness; -pub(crate) use constraint::Selector; +pub(crate) use constraint::{Selector, WiredWitness}; pub(crate) use witness::WireData; mod arithmetic; diff --git a/src/constraint_system/arithmetic.rs b/src/constraint_system/arithmetic.rs index 6b88d9189..9c88eebe8 100644 --- a/src/constraint_system/arithmetic.rs +++ b/src/constraint_system/arithmetic.rs @@ -4,42 +4,46 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use crate::constraint_system::{Constraint, TurboComposer, Witness}; +use crate::constraint_system::{ + Constraint, TurboComposer, WiredWitness, Witness, +}; use dusk_bls12_381::BlsScalar; impl TurboComposer { /// Evaluate and return `o` by appending a new constraint into the circuit. /// + /// The output of the constraint will be overwritten with /// `o := q_l · a + q_r · b + q_4 · d + q_c + PI` - pub fn gate_add( - &mut self, - a: Witness, - b: Witness, - d: Witness, - s: Constraint, - ) -> Witness { + pub fn gate_add(&mut self, s: Constraint) -> Witness { let s = Constraint::arithmetic(&s).output(-BlsScalar::one()); + + let a = s.witness(WiredWitness::A); + let b = s.witness(WiredWitness::B); + let d = s.witness(WiredWitness::D); + let o = self.append_output_witness(a, b, d, s); + let s = s.o(o); - self.append_gate(a, b, o, d, s); + self.append_gate(s); o } /// Evaluate and return `o` by appending a new constraint into the circuit. /// + /// The output of the constraint will be overwritten with /// `o := q_m · a · b + q_4 · d + q_c + PI` - pub fn gate_mul( - &mut self, - a: Witness, - b: Witness, - d: Witness, - s: Constraint, - ) -> Witness { + pub fn gate_mul(&mut self, s: Constraint) -> Witness { let s = Constraint::arithmetic(&s).output(-BlsScalar::one()); + + let a = s.witness(WiredWitness::A); + let b = s.witness(WiredWitness::B); + let d = s.witness(WiredWitness::D); + let o = self.append_output_witness(a, b, d, s); + let s = s.o(o); - self.append_gate(a, b, o, d, s); + self.append_gate(s); o } @@ -48,22 +52,20 @@ impl TurboComposer { #[cfg(feature = "std")] #[cfg(test)] mod tests { - use crate::constraint_system::helper::*; - use crate::constraint_system::Constraint; + use crate::constraint_system::{helper, Constraint}; use dusk_bls12_381::BlsScalar; #[test] fn test_public_inputs() { - gadget_tester( + helper::gadget_tester( |composer| { let one = composer.append_witness(BlsScalar::one()); - let zero = composer.constant_zero(); composer.append_dummy_gates(); - let constraint = Constraint::new().left(1).right(1).public(1); - let should_be_three = - composer.gate_add(one, one, zero, constraint); + let constraint = + Constraint::new().left(1).right(1).public(1).a(one).b(one); + let should_be_three = composer.gate_add(constraint); composer.assert_equal_constant( should_be_three, @@ -71,9 +73,9 @@ mod tests { None, ); - let constraint = Constraint::new().left(1).right(1).public(2); - let should_be_four = - composer.gate_add(one, one, zero, constraint); + let constraint = + Constraint::new().left(1).right(1).public(2).a(one).b(one); + let should_be_four = composer.gate_add(constraint); composer.assert_equal_constant( should_be_four, @@ -88,19 +90,31 @@ mod tests { #[test] fn test_correct_add_mul_gate() { - let res = gadget_tester( + let res = helper::gadget_tester( |composer| { // Verify that (4+5+5) * (6+7+7) = 280 let four = composer.append_witness(BlsScalar::from(4)); let five = composer.append_witness(BlsScalar::from(5)); let six = composer.append_witness(BlsScalar::from(6)); let seven = composer.append_witness(BlsScalar::from(7)); - let zero = composer.constant_zero(); - - let constraint = Constraint::new().left(1).right(1).fourth(1); - let fourteen = composer.gate_add(four, five, five, constraint); - let twenty = composer.gate_add(six, seven, seven, constraint); + let constraint = Constraint::new() + .left(1) + .right(1) + .fourth(1) + .a(four) + .b(five) + .d(five); + let fourteen = composer.gate_add(constraint); + + let constraint = Constraint::new() + .left(1) + .right(1) + .fourth(1) + .a(six) + .b(seven) + .d(seven); + let twenty = composer.gate_add(constraint); // There are quite a few ways to check the equation is correct, // depending on your circumstance If we already @@ -109,9 +123,8 @@ mod tests { // can compute it using the `mul` If the output // is public, we can also constrain the output wire of the mul // gate to it. This is what this test does - let constraint = Constraint::new().mul(1); - let output = - composer.gate_mul(fourteen, twenty, zero, constraint); + let constraint = Constraint::new().mul(1).a(fourteen).b(twenty); + let output = composer.gate_mul(constraint); composer.assert_equal_constant( output, @@ -126,24 +139,23 @@ mod tests { #[test] fn test_correct_add_gate() { - let res = gadget_tester( + helper::gadget_tester( |composer| { - let zero = composer.constant_zero(); let one = composer.append_witness(BlsScalar::one()); - let constraint = Constraint::new().left(1).constant(2); - let c = composer.gate_add(one, zero, zero, constraint); + let constraint = Constraint::new().left(1).constant(2).a(one); + let c = composer.gate_add(constraint); composer.assert_equal_constant(c, BlsScalar::from(3), None); }, 32, - ); - assert!(res.is_ok()) + ) + .expect("Circuit consistency failed"); } #[test] fn test_correct_big_add_mul_gate() { - let res = gadget_tester( + let res = helper::gadget_tester( |composer| { // Verify that (4+5+5) * (6+7+7) + (8*9) = 352 let four = composer.append_witness(BlsScalar::from(4)); @@ -152,14 +164,31 @@ mod tests { let seven = composer.append_witness(BlsScalar::from(7)); let nine = composer.append_witness(BlsScalar::from(9)); - let constraint = Constraint::new().left(1).right(1).fourth(1); - - let fourteen = composer.gate_add(four, five, five, constraint); - let twenty = composer.gate_add(six, seven, seven, constraint); - - let constraint = Constraint::new().mul(1).fourth(8); - let output = - composer.gate_mul(fourteen, twenty, nine, constraint); + let constraint = Constraint::new() + .left(1) + .right(1) + .fourth(1) + .a(four) + .b(five) + .d(five); + let fourteen = composer.gate_add(constraint); + + let constraint = Constraint::new() + .left(1) + .right(1) + .fourth(1) + .a(six) + .b(seven) + .d(seven); + let twenty = composer.gate_add(constraint); + + let constraint = Constraint::new() + .mul(1) + .fourth(8) + .a(fourteen) + .b(twenty) + .d(nine); + let output = composer.gate_mul(constraint); composer.assert_equal_constant( output, @@ -174,28 +203,26 @@ mod tests { #[test] fn test_incorrect_add_mul_gate() { - let res = gadget_tester( + let res = helper::gadget_tester( |composer| { // Verify that (5+5) * (6+7) != 117 let five = composer.append_witness(BlsScalar::from(5)); let six = composer.append_witness(BlsScalar::from(6)); let seven = composer.append_witness(BlsScalar::from(7)); - let zero = composer.constant_zero(); - let constraint = Constraint::new().left(1).right(1); - let five_plus_five = - composer.gate_add(five, five, zero, constraint); + let constraint = + Constraint::new().left(1).right(1).a(five).b(five); + let five_plus_five = composer.gate_add(constraint); - let six_plus_seven = - composer.gate_add(six, seven, zero, constraint); + let constraint = + Constraint::new().left(1).right(1).a(six).b(seven); + let six_plus_seven = composer.gate_add(constraint); - let constraint = Constraint::new().mul(1); - let output = composer.gate_mul( - five_plus_five, - six_plus_seven, - zero, - constraint, - ); + let constraint = Constraint::new() + .mul(1) + .a(five_plus_five) + .b(six_plus_seven); + let output = composer.gate_mul(constraint); composer.assert_equal_constant( output, diff --git a/src/constraint_system/boolean.rs b/src/constraint_system/boolean.rs index 4efea5af3..c90e2d5a1 100644 --- a/src/constraint_system/boolean.rs +++ b/src/constraint_system/boolean.rs @@ -15,28 +15,35 @@ impl TurboComposer { /// Note that using this constraint with whatever [`Witness`] that /// is not representing a value equalling 0 or 1, will always force the /// equation to fail. - pub fn gate_boolean(&mut self, a: Witness) { - let zero = self.constant_zero(); - let constraint = Constraint::new().mul(1).output(-BlsScalar::one()); + pub fn component_boolean(&mut self, a: Witness) { + let zero = Self::constant_zero(); + let constraint = Constraint::new() + .mul(1) + .output(-BlsScalar::one()) + .a(a) + .b(a) + .o(a) + .d(zero); - self.append_gate(a, a, a, zero, constraint); + self.append_gate(constraint); } } #[cfg(feature = "std")] #[cfg(test)] mod tests { - use super::super::helper::*; + use crate::constraint_system::{helper, TurboComposer}; use dusk_bls12_381::BlsScalar; + #[test] fn test_correct_bool_gate() { - let res = gadget_tester( + let res = helper::gadget_tester( |composer| { - let zero = composer.constant_zero(); + let zero = TurboComposer::constant_zero(); let one = composer.append_witness(BlsScalar::one()); - composer.gate_boolean(zero); - composer.gate_boolean(one); + composer.component_boolean(zero); + composer.component_boolean(one); }, 32, ); @@ -45,13 +52,13 @@ mod tests { #[test] fn test_incorrect_bool_gate() { - let res = gadget_tester( + let res = helper::gadget_tester( |composer| { let zero = composer.append_witness(BlsScalar::from(5)); let one = composer.append_witness(BlsScalar::one()); - composer.gate_boolean(zero); - composer.gate_boolean(one); + composer.component_boolean(zero); + composer.component_boolean(one); }, 32, ); diff --git a/src/constraint_system/composer.rs b/src/constraint_system/composer.rs index 506b83def..c5df86cc1 100644 --- a/src/constraint_system/composer.rs +++ b/src/constraint_system/composer.rs @@ -18,7 +18,7 @@ // it is intended to be like this in order to provide // maximum performance and minimum circuit sizes. -use crate::constraint_system::{Constraint, Selector, Witness}; +use crate::constraint_system::{Constraint, Selector, WiredWitness, Witness}; use crate::permutation::Permutation; use crate::plonkup::PlonkupTable4Arity; use alloc::collections::BTreeMap; @@ -98,12 +98,6 @@ pub struct TurboComposer { /// Public lookup table pub(crate) lookup_table: PlonkupTable4Arity, - /// A zero Witness that is a part of the circuit description. - /// We reserve a variable to be zero in the system - /// This is so that when a gate only uses three wires, we set the fourth - /// wire to be the variable that references zero - pub(crate) constant_zero: Witness, - /// These are the actual variable values. pub(crate) witnesses: HashMap, @@ -117,8 +111,8 @@ impl TurboComposer { /// Every [`TurboComposer`] is initialized with a circuit description /// containing a representation of zero. This function will return the /// index of that representation. - pub const fn constant_zero(&self) -> Witness { - self.constant_zero + pub const fn constant_zero() -> Witness { + Witness::new(0) } /// Return the number of gates in the circuit @@ -220,15 +214,13 @@ impl TurboComposer { lookup_table: PlonkupTable4Arity::new(), - constant_zero: Witness::new(0), - witnesses: HashMap::with_capacity(size), perm: Permutation::new(), }; - // Reserve the first variable to be zero - composer.constant_zero = composer.append_constant(BlsScalar::zero()); + // Reserve the first witness to be zero + composer.append_constant(BlsScalar::zero()); // Add dummy gates composer.append_dummy_gates(); @@ -254,14 +246,12 @@ impl TurboComposer { /// /// The final constraint added will enforce the following: /// `q_m · a · b + q_l · a + q_r · b + q_o · o + q_4 · d + q_c + PI = 0`. - pub fn append_gate( - &mut self, - a: Witness, - b: Witness, - o: Witness, - d: Witness, - s: Constraint, - ) { + pub fn append_gate(&mut self, s: Constraint) { + let a = s.witness(WiredWitness::A); + let b = s.witness(WiredWitness::B); + let o = s.witness(WiredWitness::O); + let d = s.witness(WiredWitness::D); + let s = Constraint::arithmetic(&s); let q_m = *s.coeff(Selector::Multiplication); @@ -317,8 +307,7 @@ impl TurboComposer { constant: BlsScalar, pi: Option, ) { - let zero = self.constant_zero(); - let constraint = Constraint::new().left(1).constant(-constant); + let constraint = Constraint::new().left(1).constant(-constant).a(a); // TODO maybe accept `Constraint` instead of `Option`? let constraint = match pi { @@ -326,15 +315,15 @@ impl TurboComposer { None => constraint, }; - self.append_gate(a, zero, zero, zero, constraint); + self.append_gate(constraint); } /// Asserts `a == b` by appending a gate pub fn assert_equal(&mut self, a: Witness, b: Witness) { - let zero = self.constant_zero(); - let constraint = Constraint::new().left(1).right(-BlsScalar::one()); + let constraint = + Constraint::new().left(1).right(-BlsScalar::one()).a(a).b(b); - self.append_gate(a, b, zero, zero, constraint); + self.append_gate(constraint); } /// Conditionally selects a [`Witness`] based on an input bit. @@ -342,31 +331,39 @@ impl TurboComposer { /// bit == 1 => a, /// bit == 0 => b, /// - /// `bit` is expected to be constrained by [`TurboComposer::gate_boolean`] + /// `bit` is expected to be constrained by + /// [`TurboComposer::component_boolean`] pub fn component_select( &mut self, bit: Witness, a: Witness, b: Witness, ) -> Witness { - let zero = self.constant_zero(); + debug_assert!( + self.witnesses[&bit] == BlsScalar::one() + || self.witnesses[&bit] == BlsScalar::zero() + ); // bit * a - let constraint = Constraint::new().mul(1); - let bit_times_a = - self.gate_mul(bit, a, self.constant_zero(), constraint); + let constraint = Constraint::new().mul(1).a(bit).b(a); + let bit_times_a = self.gate_mul(constraint); // 1 - bit - let constraint = Constraint::new().left(-BlsScalar::one()).constant(1); - let one_min_bit = self.gate_add(bit, zero, zero, constraint); + let constraint = + Constraint::new().left(-BlsScalar::one()).constant(1).a(bit); + let one_min_bit = self.gate_add(constraint); // (1 - bit) * b - let constraint = Constraint::new().mul(1); - let one_min_bit_b = self.gate_mul(one_min_bit, b, zero, constraint); + let constraint = Constraint::new().mul(1).a(one_min_bit).b(b); + let one_min_bit_b = self.gate_mul(constraint); // [ (1 - bit) * b ] + [ bit * a ] - let constraint = Constraint::new().left(1).right(1); - self.gate_add(one_min_bit_b, bit_times_a, zero, constraint) + let constraint = Constraint::new() + .left(1) + .right(1) + .a(one_min_bit_b) + .b(bit_times_a); + self.gate_add(constraint) } /// Conditionally selects a [`Witness`] based on an input bit. @@ -374,19 +371,21 @@ impl TurboComposer { /// bit == 1 => value, /// bit == 0 => 0, /// - /// `bit` is expected to be constrained by [`TurboComposer::gate_boolean`] - pub fn gate_select_zero( + /// `bit` is expected to be constrained by + /// [`TurboComposer::component_boolean`] + pub fn component_select_zero( &mut self, bit: Witness, value: Witness, ) -> Witness { - // returns bit * value - self.gate_mul( - bit, - value, - self.constant_zero(), - Constraint::new().mul(1), - ) + debug_assert!( + self.witnesses[&bit] == BlsScalar::one() + || self.witnesses[&bit] == BlsScalar::zero() + ); + + let constraint = Constraint::new().mul(1).a(bit).b(value); + + self.gate_mul(constraint) } /// Conditionally selects a [`Witness`] based on an input bit. @@ -394,11 +393,20 @@ impl TurboComposer { /// bit == 1 => value, /// bit == 0 => 1, /// - /// `bit` is expected to be constrained by [`TurboComposer::gate_boolean`] - pub fn gate_select_one(&mut self, bit: Witness, value: Witness) -> Witness { + /// `bit` is expected to be constrained by + /// [`TurboComposer::component_boolean`] + pub fn component_select_one( + &mut self, + bit: Witness, + value: Witness, + ) -> Witness { + debug_assert!( + self.witnesses[&bit] == BlsScalar::one() + || self.witnesses[&bit] == BlsScalar::zero() + ); + let b = self.witnesses[&bit]; let v = self.witnesses[&value]; - let zero = self.constant_zero(); let f_x = BlsScalar::one() - b + (b * v); let f_x = self.append_witness(f_x); @@ -407,8 +415,12 @@ impl TurboComposer { .mul(1) .left(-BlsScalar::one()) .output(-BlsScalar::one()) - .constant(1); - self.append_gate(bit, value, f_x, zero, constraint); + .constant(1) + .a(bit) + .b(value) + .o(f_x); + + self.append_gate(constraint); f_x } @@ -425,9 +437,9 @@ impl TurboComposer { // Static assertion assert!(0 < N && N <= 256); - let mut decomposition = [self.constant_zero(); N]; + let mut decomposition = [Self::constant_zero(); N]; - let acc = self.constant_zero(); + let acc = Self::constant_zero(); let acc = self.witnesses[&scalar] .to_bits() .iter() @@ -436,12 +448,15 @@ impl TurboComposer { .fold(acc, |acc, ((i, w), d)| { *d = self.append_witness(BlsScalar::from(*w as u64)); - self.gate_boolean(*d); + self.component_boolean(*d); let constraint = Constraint::new() .left(BlsScalar::pow_of_2(i as u64)) - .right(1); - self.gate_add(*d, acc, self.constant_zero(), constraint) + .right(1) + .a(*d) + .b(acc); + + self.gate_add(constraint) }); self.assert_equal(acc, scalar); @@ -499,12 +514,12 @@ impl TurboComposer { self.w_l.push(var_min_twenty); self.w_r.push(var_six); self.w_o.push(var_seven); - self.w_4.push(self.constant_zero()); + self.w_4.push(Self::constant_zero()); self.perm.add_variables_to_map( var_min_twenty, var_six, var_seven, - self.constant_zero(), + Self::constant_zero(), self.n, ); @@ -550,14 +565,18 @@ impl TurboComposer { d: Witness, s: Constraint, ) -> Witness { - let x = s.coeff(Selector::Multiplication) - * self.witnesses[&a] - * self.witnesses[&b] - + s.coeff(Selector::Left) * self.witnesses[&a] - + s.coeff(Selector::Right) * self.witnesses[&b] - + s.coeff(Selector::Fourth) * self.witnesses[&d] - + s.coeff(Selector::Constant) - + s.coeff(Selector::PublicInput); + let a = self.witnesses[&a]; + let b = self.witnesses[&b]; + let d = self.witnesses[&d]; + + let qm = s.coeff(Selector::Multiplication); + let ql = s.coeff(Selector::Left); + let qr = s.coeff(Selector::Right); + let qd = s.coeff(Selector::Fourth); + let qc = s.coeff(Selector::Constant); + let pi = s.coeff(Selector::PublicInput); + + let x = qm * a * b + ql * a + qr * b + qd * d + qc + pi; let y = s.coeff(Selector::Output); let y: Option = y.invert().into(); @@ -808,7 +827,7 @@ mod tests { let res = gadget_tester( |composer| { let bit_1 = composer.append_witness(BlsScalar::one()); - let bit_0 = composer.constant_zero(); + let bit_0 = TurboComposer::constant_zero(); let choice_a = composer.append_witness(BlsScalar::from(10u64)); let choice_b = composer.append_witness(BlsScalar::from(20u64)); @@ -850,7 +869,7 @@ mod tests { let res = gadget_plonkup_tester( |composer| { let bit_1 = composer.append_witness(BlsScalar::one()); - let bit_0 = composer.constant_zero(); + let bit_0 = TurboComposer::constant_zero(); let choice_a = composer.append_witness(BlsScalar::from(10u64)); let choice_b = composer.append_witness(BlsScalar::from(20u64)); @@ -964,7 +983,6 @@ mod tests { let three = prover.cs.append_constant(BlsScalar::from(3)); let result = prover.cs.append_constant(output.unwrap()); let one = prover.cs.append_constant(BlsScalar::one()); - let zero = prover.cs.constant_zero(); prover.cs.append_plonkup_gate(two, three, result, one, None); prover.cs.append_plonkup_gate(two, three, result, one, None); @@ -972,8 +990,8 @@ mod tests { prover.cs.append_plonkup_gate(two, three, result, one, None); prover.cs.append_plonkup_gate(two, three, result, one, None); - let constraint = Constraint::new().left(1).right(1); - prover.cs.gate_add(two, three, zero, constraint); + let constraint = Constraint::new().left(1).right(1).a(two).b(three); + prover.cs.gate_add(constraint); // Commit Key let (ck, _) = public_parameters.trim(2 * 70).unwrap(); diff --git a/src/constraint_system/constraint.rs b/src/constraint_system/constraint.rs index e2a8ea3a6..aa25a917a 100644 --- a/src/constraint_system/constraint.rs +++ b/src/constraint_system/constraint.rs @@ -4,6 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +use crate::constraint_system::{TurboComposer, Witness}; use dusk_bls12_381::BlsScalar; /// Index the coefficients in a polynomial description of the circuit @@ -38,11 +39,25 @@ pub(crate) enum Selector { Lookup = 0x0c, } +/// Index the coefficients in a polynomial description of the circuit +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum WiredWitness { + /// `A` witness + A = 0x00, + /// `B` witness + B = 0x01, + /// `O` witness + O = 0x02, + /// `D` witness + D = 0x03, +} + /// Constraint representation containing the coefficients of a polynomial /// evaluation -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Constraint { coefficients: [BlsScalar; 13], + witnesses: [Witness; 4], // TODO Workaround solution to keep the sparse public input indexes in the // composer @@ -66,6 +81,12 @@ pub struct Constraint { has_public_input: bool, } +impl Default for Constraint { + fn default() -> Self { + Self::new() + } +} + impl AsRef<[BlsScalar]> for Constraint { fn as_ref(&self) -> &[BlsScalar] { &self.coefficients @@ -77,6 +98,7 @@ impl Constraint { pub const fn new() -> Self { Self { coefficients: [BlsScalar::zero(); 13], + witnesses: [TurboComposer::constant_zero(); 4], has_public_input: false, } } @@ -87,16 +109,20 @@ impl Constraint { self } - fn copy_public_selectors(mut self, rhs: &Self) -> Self { + fn from_external(constraint: &Self) -> Self { const EXTERNAL: usize = Selector::Arithmetic as usize; - let src = &rhs.coefficients[..EXTERNAL]; - let dst = &mut self.coefficients[..EXTERNAL]; + let mut s = Self::default(); + + let src = &constraint.coefficients[..EXTERNAL]; + let dst = &mut s.coefficients[..EXTERNAL]; dst.copy_from_slice(src); - self.has_public_input = rhs.has_public_input(); - self + s.has_public_input = constraint.has_public_input(); + s.witnesses.copy_from_slice(&constraint.witnesses); + + s } /// Return a reference to the specified selector of a circuit constraint. @@ -104,6 +130,11 @@ impl Constraint { &self.coefficients[r as usize] } + /// Return the wired witness in the constraint + pub(crate) const fn witness(&self, w: WiredWitness) -> Witness { + self.witnesses[w as usize] + } + /// Set `s` as the polynomial selector for the multiplication coefficient /// index. #[allow(clippy::should_implement_trait)] @@ -144,31 +175,54 @@ impl Constraint { self.set(Selector::PublicInput, s) } + /// Set witness `a` wired to `qM` and `qL` + pub fn a(mut self, w: Witness) -> Self { + self.witnesses[WiredWitness::A as usize] = w; + + self + } + + /// Set witness `b` wired to `qM` and `qR` + pub fn b(mut self, w: Witness) -> Self { + self.witnesses[WiredWitness::B as usize] = w; + + self + } + + /// Set witness `o` wired to `qO` + pub fn o(mut self, w: Witness) -> Self { + self.witnesses[WiredWitness::O as usize] = w; + + self + } + + /// Set witness `d` wired to the fourth/advice `q4` coefficient + pub fn d(mut self, w: Witness) -> Self { + self.witnesses[WiredWitness::D as usize] = w; + + self + } + pub(crate) const fn has_public_input(&self) -> bool { self.has_public_input } pub(crate) fn arithmetic(s: &Self) -> Self { - Self::default() - .copy_public_selectors(s) - .set(Selector::Arithmetic, 1) + Self::from_external(s).set(Selector::Arithmetic, 1) } #[allow(dead_code)] // TODO to be used when `TurboComposer` replaces internal selectors with // this struct pub(crate) fn range(s: &Self) -> Self { - Self::default() - .copy_public_selectors(s) - .set(Selector::Range, 1) + Self::from_external(s).set(Selector::Range, 1) } #[allow(dead_code)] // TODO to be used when `TurboComposer` replaces internal selectors with // this struct pub(crate) fn logic(s: &Self) -> Self { - Self::default() - .copy_public_selectors(s) + Self::from_external(s) .set(Selector::Constant, 1) .set(Selector::Logic, 1) } @@ -177,8 +231,7 @@ impl Constraint { // TODO to be used when `TurboComposer` replaces internal selectors with // this struct pub(crate) fn logic_xor(s: &Self) -> Self { - Self::default() - .copy_public_selectors(s) + Self::from_external(s) .set(Selector::Constant, -BlsScalar::one()) .set(Selector::Logic, -BlsScalar::one()) } @@ -187,26 +240,20 @@ impl Constraint { // TODO to be used when `TurboComposer` replaces internal selectors with // this struct pub(crate) fn group_add_fixed_base(s: &Self) -> Self { - Self::default() - .copy_public_selectors(s) - .set(Selector::GroupAddFixedBase, 1) + Self::from_external(s).set(Selector::GroupAddFixedBase, 1) } #[allow(dead_code)] // TODO to be used when `TurboComposer` replaces internal selectors with // this struct pub(crate) fn group_add_variable_base(s: &Self) -> Self { - Self::default() - .copy_public_selectors(s) - .set(Selector::GroupAddVariableBase, 1) + Self::from_external(s).set(Selector::GroupAddVariableBase, 1) } #[allow(dead_code)] // TODO to be used when `TurboComposer` replaces internal selectors with // this struct pub(crate) fn lookup(s: &Self) -> Self { - Self::default() - .copy_public_selectors(s) - .set(Selector::Lookup, 1) + Self::from_external(s).set(Selector::Lookup, 1) } } diff --git a/src/constraint_system/ecc.rs b/src/constraint_system/ecc.rs index 91897842e..9359d0783 100644 --- a/src/constraint_system/ecc.rs +++ b/src/constraint_system/ecc.rs @@ -88,7 +88,7 @@ impl TurboComposer { /// Create an identity [`WitnessPoint`] constrained by the circuit /// description pub fn append_constant_identity(&mut self) -> WitnessPoint { - let x = self.constant_zero(); + let x = Self::constant_zero(); let y = self.append_constant(BlsScalar::one()); WitnessPoint { x, y } @@ -128,13 +128,19 @@ impl TurboComposer { /// bit == 1 => a, /// bit == 0 => b, /// - /// `bit` is expected to be constrained by [`TurboComposer::gate_boolean`] + /// `bit` is expected to be constrained by + /// [`TurboComposer::component_boolean`] pub fn component_select_point( &mut self, a: WitnessPoint, b: WitnessPoint, bit: Witness, ) -> WitnessPoint { + debug_assert!( + self.witnesses[&bit] == BlsScalar::one() + || self.witnesses[&bit] == BlsScalar::zero() + ); + let x = self.component_select(bit, *a.x(), *b.x()); let y = self.component_select(bit, *a.y(), *b.y()); @@ -147,14 +153,20 @@ impl TurboComposer { /// bit == 1 => a, /// bit == 0 => identity, /// - /// `bit` is expected to be constrained by [`TurboComposer::gate_boolean`] + /// `bit` is expected to be constrained by + /// [`TurboComposer::component_boolean`] pub fn component_select_identity( &mut self, bit: Witness, a: WitnessPoint, ) -> WitnessPoint { - let x = self.gate_select_zero(bit, *a.x()); - let y = self.gate_select_one(bit, *a.y()); + debug_assert!( + self.witnesses[&bit] == BlsScalar::one() + || self.witnesses[&bit] == BlsScalar::zero() + ); + + let x = self.component_select_zero(bit, *a.x()); + let y = self.component_select_one(bit, *a.y()); WitnessPoint { x, y } } @@ -171,7 +183,7 @@ mod tests { let res = gadget_tester( |composer| { let bit_1 = composer.append_witness(BlsScalar::one()); - let bit_0 = composer.constant_zero(); + let bit_0 = TurboComposer::constant_zero(); let point_a = composer.append_constant_identity(); let point_b = WitnessPoint { diff --git a/src/constraint_system/ecc/curve_addition/variable_base_gate.rs b/src/constraint_system/ecc/curve_addition/variable_base_gate.rs index 0cc14aa3e..29e9a509c 100644 --- a/src/constraint_system/ecc/curve_addition/variable_base_gate.rs +++ b/src/constraint_system/ecc/curve_addition/variable_base_gate.rs @@ -53,7 +53,7 @@ impl TurboComposer { self.w_l.extend(&[x_1, x_3]); self.w_r.extend(&[y_1, y_3]); - self.w_o.extend(&[x_2, self.constant_zero()]); + self.w_o.extend(&[x_2, Self::constant_zero()]); self.w_4.extend(&[y_2, x_1_y_2]); let zeros = [BlsScalar::zero(), BlsScalar::zero()]; @@ -78,7 +78,7 @@ impl TurboComposer { self.perm.add_variables_to_map( x_3, y_3, - self.constant_zero(), + Self::constant_zero(), x_1_y_2, self.n, ); @@ -105,42 +105,43 @@ mod test { a: WitnessPoint, b: WitnessPoint, ) -> WitnessPoint { - let zero = composer.constant_zero(); - let x1 = a.x; let y1 = a.y; let x2 = b.x; let y2 = b.y; - let constraint = Constraint::new().mul(1); - // x1 * y2 + let constraint = Constraint::new().mul(1).a(x1).b(y2); + let x1_y2 = composer.gate_mul(constraint); + // y1 * x2 + let constraint = Constraint::new().mul(1).a(y1).b(x2); + let y1_x2 = composer.gate_mul(constraint); + // y1 * y2 - // x1 * x2 + let constraint = Constraint::new().mul(1).a(y1).b(y2); + let y1_y2 = composer.gate_mul(constraint); - let x1_y2 = composer.gate_mul(x1, y2, zero, constraint); - let y1_x2 = composer.gate_mul(y1, x2, zero, constraint); - let y1_y2 = composer.gate_mul(y1, y2, zero, constraint); - let x1_x2 = composer.gate_mul(x1, x2, zero, constraint); + // x1 * x2 + let constraint = Constraint::new().mul(1).a(x1).b(x2); + let x1_x2 = composer.gate_mul(constraint); // d x1x2 * y1y2 - let constraint = Constraint::new().mul(EDWARDS_D); - let d_x1_x2_y1_y2 = composer.gate_mul(x1_x2, y1_y2, zero, constraint); - - let constraint = Constraint::new().left(1).right(1); + let constraint = Constraint::new().mul(EDWARDS_D).a(x1_x2).b(y1_y2); + let d_x1_x2_y1_y2 = composer.gate_mul(constraint); // x1y2 + y1x2 - // y1y2 - a * x1x2 (a=-1) => y1y2 + x1x2 + let constraint = Constraint::new().left(1).right(1).a(x1_y2).b(y1_x2); + let x_numerator = composer.gate_add(constraint); - let x_numerator = composer.gate_add(x1_y2, y1_x2, zero, constraint); - let y_numerator = composer.gate_add(y1_y2, x1_x2, zero, constraint); + // y1y2 - a * x1x2 (a=-1) => y1y2 + x1x2 + let constraint = Constraint::new().left(1).right(1).a(y1_y2).b(x1_x2); + let y_numerator = composer.gate_add(constraint); // 1 + dx1x2y1y2 - let constraint = Constraint::new().left(1).constant(1); - let x_denominator = - composer.gate_add(d_x1_x2_y1_y2, zero, zero, constraint); + let constraint = Constraint::new().left(1).constant(1).a(d_x1_x2_y1_y2); + let x_denominator = composer.gate_add(constraint); // Compute the inverse let inv_x_denom = unsafe { @@ -148,22 +149,21 @@ mod test { }; let inv_x_denom = composer.append_witness(inv_x_denom); - let constraint = Constraint::new().mul(1).constant(-BlsScalar::one()); - // Assert that we actually have the inverse // inv_x * x = 1 - composer.append_gate( - x_denominator, - inv_x_denom, - zero, - zero, - constraint, - ); + let constraint = Constraint::new() + .mul(1) + .constant(-BlsScalar::one()) + .a(x_denominator) + .b(inv_x_denom); + composer.append_gate(constraint); // 1 - dx1x2y1y2 - let constraint = Constraint::new().left(-BlsScalar::one()).constant(1); - let y_denominator = - composer.gate_add(d_x1_x2_y1_y2, zero, zero, constraint); + let constraint = Constraint::new() + .left(-BlsScalar::one()) + .constant(1) + .a(d_x1_x2_y1_y2); + let y_denominator = composer.gate_add(constraint); let inv_y_denom = unsafe { composer.evaluate_witness(&y_denominator).invert().unwrap() @@ -172,20 +172,19 @@ mod test { // Assert that we actually have the inverse // inv_y * y = 1 - let constraint = Constraint::new().mul(1).constant(-BlsScalar::one()); - composer.append_gate( - y_denominator, - inv_y_denom, - zero, - zero, - constraint, - ); + let constraint = Constraint::new() + .mul(1) + .constant(-BlsScalar::one()) + .a(y_denominator) + .b(inv_y_denom); + composer.append_gate(constraint); // We can now use the inverses - let constraint = Constraint::new().mul(1); + let constraint = Constraint::new().mul(1).a(inv_x_denom).b(x_numerator); + let x_3 = composer.gate_mul(constraint); - let x_3 = composer.gate_mul(inv_x_denom, x_numerator, zero, constraint); - let y_3 = composer.gate_mul(inv_y_denom, y_numerator, zero, constraint); + let constraint = Constraint::new().mul(1).a(inv_y_denom).b(y_numerator); + let y_3 = composer.gate_mul(constraint); WitnessPoint { x: x_3, y: y_3 } } diff --git a/src/constraint_system/ecc/scalar_mul/fixed_base.rs b/src/constraint_system/ecc/scalar_mul/fixed_base.rs index 60c897438..95ba84a84 100644 --- a/src/constraint_system/ecc/scalar_mul/fixed_base.rs +++ b/src/constraint_system/ecc/scalar_mul/fixed_base.rs @@ -140,14 +140,9 @@ impl TurboComposer { // FIXME this gate isn't verifying anything because all the selectors // are zeroed. Validate what was the intent - let constraint = Constraint::new(); - self.append_gate( - acc_x, - acc_y, - self.constant_zero(), - last_accumulated_bit, - constraint, - ); + let constraint = + Constraint::new().a(acc_x).b(acc_y).d(last_accumulated_bit); + self.append_gate(constraint); // Constrain the last element in the accumulator to be equal to the // input jubjub scalar diff --git a/src/constraint_system/ecc/scalar_mul/variable_base.rs b/src/constraint_system/ecc/scalar_mul/variable_base.rs index ff00a6a09..9deee8aa2 100644 --- a/src/constraint_system/ecc/scalar_mul/variable_base.rs +++ b/src/constraint_system/ecc/scalar_mul/variable_base.rs @@ -28,43 +28,6 @@ impl TurboComposer { result } - - /* - fn scalar_decomposition(&mut self, witness: Witness) -> Vec { - // Decompose the bits - let scalar_bits = self.gate_decomposition(witness); - - // Take the first 252 bits - let scalar_bits_witness = scalar_bits[..252].to_vec(); - - // Now ensure that the bits correctly accumulate to the witness given - let mut accumulator_witness = self.constant_zero(); - let mut accumulator_scalar = BlsScalar::zero(); - - for (power, bit) in scalar_bits_witness.iter().enumerate() { - self.gate_boolean(*bit); - - let two_pow = BlsScalar::pow_of_2(power as u64); - - accumulator_witness = self.gate_add( - *bit, - accumulator_witness, - self.constant_zero(), - two_pow, - BlsScalar::one(), - BlsScalar::zero(), - BlsScalar::zero(), - None, - ); - - accumulator_scalar += two_pow * self.witnesses[&scalar_bits[power]]; - } - - self.assert_equal(accumulator_witness, witness); - - scalar_bits_witness - } - */ } #[cfg(feature = "std")] diff --git a/src/constraint_system/helper.rs b/src/constraint_system/helper.rs index 845fbc7d8..9fc9e4153 100644 --- a/src/constraint_system/helper.rs +++ b/src/constraint_system/helper.rs @@ -17,12 +17,12 @@ use rand_core::OsRng; pub(crate) fn dummy_gadget(n: usize, composer: &mut TurboComposer) { let one = BlsScalar::one(); let one = composer.append_witness(one); - let zero = composer.constant_zero(); for _ in 0..n { // FIXME dummy gates with zeroed selectors doesn't make sense - let constraint = Constraint::new().left(1).right(1); - composer.gate_add(one, one, zero, constraint); + let constraint = Constraint::new().left(1).right(1).a(one).b(one); + + composer.gate_add(constraint); } } @@ -31,11 +31,11 @@ pub(crate) fn dummy_gadget_plonkup(n: usize, composer: &mut TurboComposer) { // FIXME duplicate of `dummy_gadget` for no clear reason let one = BlsScalar::one(); let one = composer.append_witness(one); - let zero = composer.constant_zero(); for _ in 0..n { - let constraint = Constraint::new().left(1).right(1); - composer.gate_add(one, one, zero, constraint); + let constraint = Constraint::new().left(1).right(1).a(one).b(one); + + composer.gate_add(constraint); } } diff --git a/src/constraint_system/logic.rs b/src/constraint_system/logic.rs index ce6c31790..7c88145a8 100644 --- a/src/constraint_system/logic.rs +++ b/src/constraint_system/logic.rs @@ -74,16 +74,18 @@ impl TurboComposer { // Now we can add the first row as: `| 0 | 0 | -- | 0 |`. // Note that `w_1` will be set on the first loop iteration. self.perm - .add_variable_to_map(self.constant_zero(), WireData::Left(self.n)); - self.perm - .add_variable_to_map(self.constant_zero(), WireData::Right(self.n)); + .add_variable_to_map(Self::constant_zero(), WireData::Left(self.n)); + self.perm.add_variable_to_map( + Self::constant_zero(), + WireData::Right(self.n), + ); self.perm.add_variable_to_map( - self.constant_zero(), + Self::constant_zero(), WireData::Fourth(self.n), ); - self.w_l.push(self.constant_zero()); - self.w_r.push(self.constant_zero()); - self.w_4.push(self.constant_zero()); + self.w_l.push(Self::constant_zero()); + self.w_r.push(Self::constant_zero()); + self.w_4.push(Self::constant_zero()); // Increase the gate index so we can add the following rows in the // correct order. self.n += 1; @@ -221,10 +223,10 @@ impl TurboComposer { // the program memory will look like this: // | an | bn | --- | cn | self.perm.add_variable_to_map( - self.constant_zero(), + Self::constant_zero(), WireData::Output(self.n - 1), ); - self.w_o.push(self.constant_zero()); + self.w_o.push(Self::constant_zero()); // Now the wire values are set for each gate, indexed and mapped in the // `variable_map` inside of the `Permutation` struct. diff --git a/src/constraint_system/range.rs b/src/constraint_system/range.rs index bdd6259d1..3ab64aeae 100644 --- a/src/constraint_system/range.rs +++ b/src/constraint_system/range.rs @@ -139,7 +139,7 @@ impl TurboComposer { // First we pad our gates by the necessary amount for i in 0..pad { - add_wire(self, i, self.constant_zero()); + add_wire(self, i, Self::constant_zero()); } for i in pad..=num_quads { @@ -182,9 +182,9 @@ impl TurboComposer { // wire, which will be used in the gate before it // Furthermore, we set the left, right and output wires to zero *self.q_range.last_mut().unwrap() = BlsScalar::zero(); - self.w_l.push(self.constant_zero()); - self.w_r.push(self.constant_zero()); - self.w_o.push(self.constant_zero()); + self.w_l.push(Self::constant_zero()); + self.w_r.push(Self::constant_zero()); + self.w_o.push(Self::constant_zero()); // Lastly, we must link the last accumulator value to the initial // witness This last constraint will pass as long as diff --git a/src/permutation.rs b/src/permutation.rs index abfe6db12..d325dde6a 100644 --- a/src/permutation.rs +++ b/src/permutation.rs @@ -815,23 +815,36 @@ mod test { let one = BlsScalar::one(); let two = BlsScalar::from_raw([2, 0, 0, 0]); - let z = cs.constant_zero(); // x1 * x4 = x2 - let constraint = Constraint::new().mul(1).output(-one); - cs.append_gate(x1, x4, x2, z, constraint); + let constraint = + Constraint::new().mul(1).output(-one).a(x1).b(x4).o(x2); + cs.append_gate(constraint); // x1 + x3 = x2 - let constraint = Constraint::new().left(1).right(1).output(-one); - cs.append_gate(x1, x3, x2, z, constraint); + let constraint = Constraint::new() + .left(1) + .right(1) + .output(-one) + .a(x1) + .b(x3) + .o(x2); + cs.append_gate(constraint); // x1 + x2 = 2*x3 - let constraint = Constraint::new().left(1).right(1).output(-two); - cs.append_gate(x1, x2, x3, z, constraint); + let constraint = Constraint::new() + .left(1) + .right(1) + .output(-two) + .a(x1) + .b(x2) + .o(x3); + cs.append_gate(constraint); // x3 * x4 = 2*x2 - let constraint = Constraint::new().mul(1).output(-two); - cs.append_gate(x3, x4, x2, z, constraint); + let constraint = + Constraint::new().mul(1).output(-two).a(x3).b(x4).o(x2); + cs.append_gate(constraint); let domain = EvaluationDomain::new(cs.gates()).unwrap(); let pad = vec![BlsScalar::zero(); domain.size() - cs.w_l.len()]; diff --git a/src/proof_system/preprocess.rs b/src/proof_system/preprocess.rs index caadc1037..ca424dbd1 100644 --- a/src/proof_system/preprocess.rs +++ b/src/proof_system/preprocess.rs @@ -46,7 +46,7 @@ impl TurboComposer { fn pad(&mut self, diff: usize) { // Add a zero variable to circuit let zero_scalar = BlsScalar::zero(); - let zero_var = self.constant_zero(); + let zero_var = Self::constant_zero(); let zeroes_scalar = vec![zero_scalar; diff]; let zeroes_var = vec![zero_var; diff]; diff --git a/tests/circuit.rs b/tests/circuit.rs index 0b2ef10b3..a52720c70 100644 --- a/tests/circuit.rs +++ b/tests/circuit.rs @@ -36,28 +36,18 @@ impl Circuit for TestCircuit { let b = composer.append_witness(self.b); // Make first constraint a + b = c - let constraint = Constraint::new().left(1).right(1).public(-self.c); - composer.append_gate( - a, - b, - composer.constant_zero(), - composer.constant_zero(), - constraint, - ); + let constraint = + Constraint::new().left(1).right(1).public(-self.c).a(a).b(b); + composer.append_gate(constraint); // Check that a and b are in range composer.component_range(a, 1 << 6); composer.component_range(b, 1 << 5); // Make second constraint a * b = d - let constraint = Constraint::new().mul(1).output(1).public(-self.d); - composer.append_gate( - a, - b, - composer.constant_zero(), - composer.constant_zero(), - constraint, - ); + let constraint = + Constraint::new().mul(1).output(1).public(-self.d).a(a).b(b); + composer.append_gate(constraint); let e = composer.append_witness(self.e); let scalar_mul_result = composer