From e15f2cbd512395dbc214b1aefe8401b917311451 Mon Sep 17 00:00:00 2001 From: Alex Xiong Date: Mon, 8 Aug 2022 21:18:03 +0800 Subject: [PATCH 1/6] update nix and rust --- nix/nixpkgs.json | 4 ++-- nix/oxalica_rust_overlay.json | 6 +++--- shell.nix | 18 +++++++----------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/nix/nixpkgs.json b/nix/nixpkgs.json index cc5cf5156..3cd4c582f 100644 --- a/nix/nixpkgs.json +++ b/nix/nixpkgs.json @@ -1,4 +1,4 @@ { - "url": "https://github.com/nixos/nixpkgs/archive/db8ab32efd3a4ad59044848d889480954e458f25.tar.gz", - "sha256": "1i7ayivjm3rx62qq263jjj55m0nzhn4b99wax25kw6a8zhhwcwjb" + "url": "https://github.com/nixos/nixpkgs/archive/6f38b43c8c84c800f93465b2241156419fd4fd52.tar.gz", + "sha256": "0xw3y3jx1bcnwsc0imacbp5m8f51b66s9h8kk8qnfbckwv67dhgd" } diff --git a/nix/oxalica_rust_overlay.json b/nix/oxalica_rust_overlay.json index 98b023744..3081ab7c1 100644 --- a/nix/oxalica_rust_overlay.json +++ b/nix/oxalica_rust_overlay.json @@ -1,7 +1,7 @@ { "owner": "oxalica", "repo": "rust-overlay", - "rev": "9d7c777625640b70a4d211f62711fa316bca7176", - "sha256": "025bw59nl12jqf4nrvbn0a8xn03aj9bz54nvf1rb25zl2l1nkrnd", + "rev": "53f8467a9ef7a49c8729b28660bb83d1a75da508", + "sha256": "aeno3vAJm5ReC2ZCOQoZHDoNsBsqM64HgSWgqhzF6yk=", "fetchSubmodules": true -} +} \ No newline at end of file diff --git a/shell.nix b/shell.nix index 993c4be7a..6a99444c6 100644 --- a/shell.nix +++ b/shell.nix @@ -1,22 +1,20 @@ let basePkgs = import ./nix/nixpkgs.nix { }; - rust_overlay = with basePkgs; import (fetchFromGitHub - (lib.importJSON ./nix/oxalica_rust_overlay.json)); + rust_overlay = with basePkgs; + import (fetchFromGitHub (lib.importJSON ./nix/oxalica_rust_overlay.json)); pkgs = import ./nix/nixpkgs.nix { overlays = [ rust_overlay ]; }; - nightlyToolchain = pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal.override { - extensions = [ "rustfmt" ]; - }); + nightlyToolchain = pkgs.rust-bin.selectLatestNightlyWith + (toolchain: toolchain.minimal.override { extensions = [ "rustfmt" ]; }); - stableToolchain = pkgs.rust-bin.stable."1.56.1".minimal.override { + stableToolchain = pkgs.rust-bin.stable.latest.minimal.override { extensions = [ "clippy" "llvm-tools-preview" "rust-src" ]; }; pre-commit-check = pkgs.callPackage ./nix/pre-commit.nix { }; -in -with pkgs; +in with pkgs; mkShell { buildInputs = [ @@ -28,9 +26,7 @@ mkShell { stableToolchain nightlyToolchain - ] ++ lib.optionals stdenv.isDarwin [ - darwin.apple_sdk.frameworks.Security - ]; + ] ++ lib.optionals stdenv.isDarwin [ darwin.apple_sdk.frameworks.Security ]; shellHook = '' export RUST_BACKTRACE=full From 4ea0522a8278852adf51cfa58cf71295061693bc Mon Sep 17 00:00:00 2001 From: Alex Xiong Date: Mon, 8 Aug 2022 22:38:50 +0800 Subject: [PATCH 2/6] introduce BoolVar, first pass --- plonk/src/circuit/basic.rs | 17 -- plonk/src/circuit/customized/ecc/glv.rs | 27 ++- plonk/src/circuit/customized/ecc/mod.rs | 164 +++++------------- plonk/src/circuit/customized/ecc/msm.rs | 2 +- plonk/src/circuit/customized/mod.rs | 62 ++++--- .../circuit/customized/ultraplonk/range.rs | 2 +- plonk/src/circuit/mod.rs | 15 +- primitives/src/circuit/merkle_tree.rs | 64 +++---- primitives/src/circuit/signature/schnorr.rs | 6 +- 9 files changed, 131 insertions(+), 228 deletions(-) diff --git a/plonk/src/circuit/basic.rs b/plonk/src/circuit/basic.rs index 771632532..58f1d9b02 100644 --- a/plonk/src/circuit/basic.rs +++ b/plonk/src/circuit/basic.rs @@ -715,23 +715,6 @@ impl PlonkCircuit { Ok(()) } - // Check whether the variable `var` is a boolean value - // this is used to return error to invalid parameter early in the circuit - // building development lifecycle, it should NOT be used as a circuit constraint - // for which you should use bool_gate() instead - #[inline] - pub(crate) fn check_bool(&self, var: Variable) -> Result<(), PlonkError> { - let val = self.witness(var)?; - if val != F::zero() && val != F::one() { - Err(ParameterError( - "Expecting a boolean value, something is wrong with your circuit logic".to_string(), - ) - .into()) - } else { - Ok(()) - } - } - // Return the variable that maps to a wire `(i, j)` where i is the wire type and // j is the gate index. If gate `j` is a padded dummy gate, return zero // variable. diff --git a/plonk/src/circuit/customized/ecc/glv.rs b/plonk/src/circuit/customized/ecc/glv.rs index fe245681e..07f03d63e 100644 --- a/plonk/src/circuit/customized/ecc/glv.rs +++ b/plonk/src/circuit/customized/ecc/glv.rs @@ -7,7 +7,7 @@ use crate::{ circuit::{ customized::ecc::{MultiScalarMultiplicationCircuit, PointVariable}, - Circuit, PlonkCircuit, Variable, + BoolVar, Circuit, PlonkCircuit, Variable, }, errors::PlonkError, }; @@ -93,17 +93,10 @@ where self.check_var_bound(scalar)?; self.check_point_var_bound(base)?; - let k_vars = scalar_decomposition_gate::<_, P, _>(self, &scalar)?; + let (s1_var, s2_var, s2_sign_var) = scalar_decomposition_gate::<_, P, _>(self, &scalar)?; let endo_base_var = endomorphism_circuit::<_, P>(self, base)?; - multi_scalar_mul_circuit::<_, P>( - self, - base, - k_vars[0], - &endo_base_var, - k_vars[1], - k_vars[2], - ) + multi_scalar_mul_circuit::<_, P>(self, base, s1_var, &endo_base_var, s2_var, s2_sign_var) } } @@ -114,7 +107,7 @@ fn multi_scalar_mul_circuit( scalar_1: Variable, endo_base: &PointVariable, scalar_2: Variable, - scalar_2_sign_var: Variable, + scalar_2_sign_var: BoolVar, ) -> Result where F: PrimeField, @@ -273,7 +266,7 @@ macro_rules! int_to_fq { fn scalar_decomposition_gate( circuit: &mut PlonkCircuit, s_var: &Variable, -) -> Result<([Variable; 3]), PlonkError> +) -> Result<(Variable, Variable, BoolVar), PlonkError> where F: PrimeField, P: TEModelParameters + Clone, @@ -555,7 +548,7 @@ where circuit.enforce_true(sat)?; // extract the output - Ok([k1_var, k2_var, k2_sign_var]) + Ok((k1_var, k2_var, k2_sign_var)) } #[cfg(test)] @@ -702,15 +695,15 @@ mod tests { let mut circuit: PlonkCircuit = PlonkCircuit::new_ultra_plonk(16); let scalar_var = circuit.create_variable(field_switching(&scalar)).unwrap(); - let res = + let (k1_var, k2_var, k2_sign_var) = scalar_decomposition_gate::<_, EdwardsParameters, _>(&mut circuit, &scalar_var) .unwrap(); - let k1_rec = circuit.witness(res[0]).unwrap(); + let k1_rec = circuit.witness(k1_var).unwrap(); assert_eq!(field_switching::<_, Fq>(&k1), k1_rec); - let k2_rec = circuit.witness(res[1]).unwrap(); - let k2_sign = circuit.witness(res[2]).unwrap(); + let k2_rec = circuit.witness(k2_var).unwrap(); + let k2_sign = circuit.witness(k2_sign_var.into()).unwrap(); let k2_with_sign_rec = if k2_sign == Fq::one() { field_switching::<_, Fr>(&k2_rec) } else { diff --git a/plonk/src/circuit/customized/ecc/mod.rs b/plonk/src/circuit/customized/ecc/mod.rs index e57ff2c23..0a063d8fd 100644 --- a/plonk/src/circuit/customized/ecc/mod.rs +++ b/plonk/src/circuit/customized/ecc/mod.rs @@ -9,7 +9,7 @@ use super::gates::*; use crate::{ - circuit::{gates::Gate, Circuit, PlonkCircuit, Variable}, + circuit::{gates::Gate, BoolVar, Circuit, PlonkCircuit, Variable}, errors::{CircuitError, PlonkError}, }; use ark_ec::{ @@ -180,19 +180,20 @@ where /// variables, that would ultimately failed to build a correct circuit. fn quaternary_point_select + Clone>( &mut self, - b0: Variable, - b1: Variable, + b0: BoolVar, + b1: BoolVar, point1: &Point, point2: &Point, point3: &Point, ) -> Result { - self.check_var_bound(b0)?; - self.check_var_bound(b1)?; - self.check_bool(b0)?; - self.check_bool(b1)?; + self.check_var_bound(b0.into())?; + self.check_var_bound(b1.into())?; let selected_point = { - let selected = match (self.witness(b0)? == F::one(), self.witness(b1)? == F::one()) { + let selected = match ( + self.witness(b0.into())? == F::one(), + self.witness(b1.into())? == F::one(), + ) { (false, false) => Point::from(GroupAffine::

::zero()), (true, false) => point1.to_owned(), (false, true) => point2.to_owned(), @@ -201,7 +202,7 @@ where // create new point with the same (x, y) coordinates self.create_point_variable(selected)? }; - let wire_vars_x = [b0, b1, 0, 0, selected_point.0]; + let wire_vars_x = [b0.into(), b1.into(), 0, 0, selected_point.0]; self.insert_gate( &wire_vars_x, Box::new(QuaternaryPointSelectXGate { @@ -210,7 +211,7 @@ where x3: point3.0, }), )?; - let wire_vars_y = [b0, b1, 0, 0, selected_point.1]; + let wire_vars_y = [b0.into(), b1.into(), 0, 0, selected_point.1]; self.insert_gate( &wire_vars_y, Box::new(QuaternaryPointSelectYGate { @@ -229,14 +230,13 @@ where /// Return error if invalid input parameters are provided. fn binary_point_vars_select( &mut self, - b: Variable, + b: BoolVar, point0: &PointVariable, point1: &PointVariable, ) -> Result { - self.check_var_bound(b)?; + self.check_var_bound(b.into())?; self.check_point_var_bound(point0)?; self.check_point_var_bound(point1)?; - self.check_bool(b)?; let selected_x = self.conditional_select(b, point0.0, point1.0)?; let selected_y = self.conditional_select(b, point0.1, point1.1)?; @@ -497,12 +497,11 @@ where /// Currently only supports GroupAffine::

. pub fn variable_base_binary_scalar_mul + Clone>( &mut self, - scalar_bits_le: &[Variable], + scalar_bits_le: &[BoolVar], base: &PointVariable, ) -> Result { for &bit in scalar_bits_le { - self.check_var_bound(bit)?; - self.check_bool(bit)?; + self.check_var_bound(bit.into())?; } self.check_point_var_bound(base)?; @@ -852,10 +851,12 @@ mod test { let p3 = GroupAffine::

::rand(&mut rng); let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); + let false_var = circuit.create_bool_variable(false)?; + let true_var = circuit.create_bool_variable(true)?; let select_p0 = circuit.quaternary_point_select::

( - circuit.zero(), - circuit.zero(), + false_var, + false_var, &Point::from(p1), &Point::from(p2), &Point::from(p3), @@ -865,24 +866,24 @@ mod test { Point(circuit.witness(select_p0.0)?, circuit.witness(select_p0.1)?) ); let select_p1 = circuit.quaternary_point_select::

( - circuit.one(), - circuit.zero(), + true_var, + false_var, &Point::from(p1), &Point::from(p2), &Point::from(p3), )?; assert_eq!(Point::from(p1), circuit.point_witness(&select_p1)?); let select_p2 = circuit.quaternary_point_select::

( - circuit.zero(), - circuit.one(), + false_var, + true_var, &Point::from(p1), &Point::from(p2), &Point::from(p3), )?; assert_eq!(Point::from(p2), circuit.point_witness(&select_p2)?); let select_p3 = circuit.quaternary_point_select::

( - circuit.one(), - circuit.one(), + true_var, + true_var, &Point::from(p1), &Point::from(p2), &Point::from(p3), @@ -891,55 +892,24 @@ mod test { assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); - // non binary b0, b1 should fail - let two = circuit.create_variable(F::from(2u32))?; - assert!(circuit - .quaternary_point_select::

( - two, - 1, - &Point::from(p1), - &Point::from(p2), - &Point::from(p3) - ) - .is_err()); - assert!(circuit - .quaternary_point_select::

( - 0, - two, - &Point::from(p1), - &Point::from(p2), - &Point::from(p3) - ) - .is_err()); - *circuit.witness_mut(select_p3.0) = p2.x; *circuit.witness_mut(select_p3.1) = p2.y; assert!(circuit.check_circuit_satisfiability(&[]).is_err()); - // Check variable out of bound error. - assert!(circuit - .quaternary_point_select::

( - 0, - circuit.num_vars(), - &Point::from(p1), - &Point::from(p2), - &Point::from(p3) - ) - .is_err()); - let circuit_1 = build_quaternary_select_gate::(F::zero(), F::zero())?; - let circuit_2 = build_quaternary_select_gate::(F::one(), F::one())?; + let circuit_1 = build_quaternary_select_gate::(false, false)?; + let circuit_2 = build_quaternary_select_gate::(true, true)?; customized::test::test_variable_independence_for_circuit(circuit_1, circuit_2)?; Ok(()) } - fn build_quaternary_select_gate(b0: F, b1: F) -> Result, PlonkError> + fn build_quaternary_select_gate(b0: bool, b1: bool) -> Result, PlonkError> where F: PrimeField, P: Parameters + Clone, { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let b0_var = circuit.create_variable(b0)?; - let b1_var = circuit.create_variable(b1)?; + let b0_var = circuit.create_bool_variable(b0)?; + let b1_var = circuit.create_bool_variable(b1)?; let mut rng = ark_std::test_rng(); let p1 = GroupAffine::

::rand(&mut rng); @@ -1198,43 +1168,30 @@ mod test { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); let p0_var = circuit.create_point_variable(Point::from(p0))?; let p1_var = circuit.create_point_variable(Point::from(p1))?; - let select_p0 = circuit.binary_point_vars_select(circuit.zero(), &p0_var, &p1_var)?; + let true_var = circuit.create_bool_variable(true)?; + let false_var = circuit.create_bool_variable(false)?; + + let select_p0 = circuit.binary_point_vars_select(false_var, &p0_var, &p1_var)?; assert_eq!(circuit.point_witness(&select_p0)?, Point::from(p0)); - let select_p1 = circuit.binary_point_vars_select(circuit.one(), &p0_var, &p1_var)?; + let select_p1 = circuit.binary_point_vars_select(true_var, &p0_var, &p1_var)?; assert_eq!(circuit.point_witness(&select_p1)?, Point::from(p1)); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); - // non boolean selection variable should fail - let two = circuit.create_variable(F::from(2u32))?; - assert!(circuit - .binary_point_vars_select(two, &p0_var, &p1_var) - .is_err()); // wrong witness should fail *circuit.witness_mut(p1_var.0) = F::rand(&mut rng); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); - // Check variable out of bound error. assert!(circuit .binary_point_vars_select( - circuit.zero(), - &PointVariable(circuit.num_vars(), p0_var.1), - &p1_var - ) - .is_err()); - assert!(circuit - .binary_point_vars_select( - circuit.zero(), + false_var, &p0_var, &PointVariable(p1_var.0, circuit.num_vars()), ) .is_err()); - let circuit_1 = build_binary_point_vars_select_circuit::( - F::one(), - Point::from(p0), - Point::from(p1), - )?; + let circuit_1 = + build_binary_point_vars_select_circuit::(true, Point::from(p0), Point::from(p1))?; let circuit_2 = build_binary_point_vars_select_circuit::( - F::zero(), + false, Point::from(p1), Point::from(p2), )?; @@ -1244,7 +1201,7 @@ mod test { } fn build_binary_point_vars_select_circuit( - b: F, + b: bool, p0: Point, p1: Point, ) -> Result, PlonkError> @@ -1253,7 +1210,7 @@ mod test { P: Parameters + Clone, { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let b_var = circuit.create_variable(b)?; + let b_var = circuit.create_bool_variable(b)?; let p0_var = circuit.create_point_variable(p0)?; let p1_var = circuit.create_point_variable(p1)?; circuit.binary_point_vars_select(b_var, &p0_var, &p1_var)?; @@ -1317,45 +1274,6 @@ mod test { Ok(()) } - // Given `test_variable_base_scalar_mul`, we don't need to further test - // `variable_base_binary_scalar_mul`'s good paths. - #[test] - fn test_variable_base_binary_scalar_mul_errors() -> Result<(), PlonkError> { - test_variable_base_binary_scalar_mul_errors_helper::()?; - test_variable_base_binary_scalar_mul_errors_helper::()?; - test_variable_base_binary_scalar_mul_errors_helper::()?; - test_variable_base_binary_scalar_mul_errors_helper::()?; - test_variable_base_binary_scalar_mul_errors_helper::() - } - fn test_variable_base_binary_scalar_mul_errors_helper() -> Result<(), PlonkError> - where - F: PrimeField, - P: Parameters + Clone, - { - let mut rng = ark_std::test_rng(); - let mut circuit = PlonkCircuit::::new_turbo_plonk(); - let non_bit_var = circuit.create_variable(F::from(2u8))?; - let base = GroupAffine::

::rand(&mut rng); - let base_var = circuit.create_point_variable(Point::from(base))?; - // Binary scalar variables out of bound - assert!(circuit - .variable_base_binary_scalar_mul::

(&[circuit.one(), circuit.num_vars()], &base_var) - .is_err()); - // Base point out of bound - assert!(circuit - .variable_base_binary_scalar_mul::

( - &[circuit.zero(), circuit.one()], - &PointVariable(circuit.num_vars(), circuit.num_vars()) - ) - .is_err()); - // Non-binary scalar variables - assert!(circuit - .variable_base_binary_scalar_mul::

(&[circuit.one(), non_bit_var], &base_var) - .is_err()); - - Ok(()) - } - fn build_variable_base_scalar_mul_circuit( scalar: F, base: Point, diff --git a/plonk/src/circuit/customized/ecc/msm.rs b/plonk/src/circuit/customized/ecc/msm.rs index 0238237eb..9ecd5da0c 100644 --- a/plonk/src/circuit/customized/ecc/msm.rs +++ b/plonk/src/circuit/customized/ecc/msm.rs @@ -335,7 +335,7 @@ where // create circuit let range_size = F::from((1 << c) as u32); - circuit.decompose_vars_gate(decomposed_scalar_vars.clone(), scalar_var, range_size)?; + circuit.decomposition_gate(decomposed_scalar_vars.clone(), scalar_var, range_size)?; Ok(decomposed_scalar_vars) } diff --git a/plonk/src/circuit/customized/mod.rs b/plonk/src/circuit/customized/mod.rs index 32468452a..dde4959bd 100644 --- a/plonk/src/circuit/customized/mod.rs +++ b/plonk/src/circuit/customized/mod.rs @@ -8,7 +8,7 @@ //! related, rescue-based transcript and lookup table etc. use self::gates::*; -use super::{Circuit, PlonkCircuit, PlonkError, Variable}; +use super::{BoolVar, Circuit, PlonkCircuit, PlonkError, Variable}; use crate::{ circuit::gates::{ConstantAdditionGate, ConstantMultiplicationGate, FifthRootGate}, constants::{GATE_WIDTH, N_MUL_SELECTORS}, @@ -235,18 +235,18 @@ where /// one. Return error if variables are invalid. pub fn conditional_select( &mut self, - b: Variable, + b: BoolVar, x_0: Variable, x_1: Variable, ) -> Result { - self.check_var_bound(b)?; + self.check_var_bound(b.into())?; self.check_var_bound(x_0)?; self.check_var_bound(x_1)?; // y = x_bit - let y = if self.witness(b)? == F::zero() { + let y = if self.witness(b.into())? == F::zero() { self.create_variable(self.witness(x_0)?)? - } else if self.witness(b)? == F::one() { + } else if self.witness(b.into())? == F::one() { self.create_variable(self.witness(x_1)?)? } else { return Err(CircuitError::ParameterError( @@ -254,7 +254,7 @@ where ) .into()); }; - let wire_vars = [b, x_0, b, x_1, y]; + let wire_vars = [b.into(), x_0, b.into(), x_1, y]; self.insert_gate(&wire_vars, Box::new(CondSelectGate))?; Ok(y) } @@ -655,7 +655,8 @@ impl PlonkCircuit { /// range [0, 2^`bit_len`). Return error if the variable is invalid. /// TODO: optimize the gate for UltraPlonk. pub fn check_in_range(&mut self, a: Variable, bit_len: usize) -> Result { - let a_bit_le = self.unpack(a, F::size_in_bits())?; + let a_bit_le: Vec = self.unpack(a, F::size_in_bits())?; + let a_bit_le: Vec = a_bit_le.into_iter().map(|b| b.into()).collect(); // a is in range if and only if the bits in `a_bit_le[bit_len..]` are all // zeroes. let higher_bit_sum = self.sum(&a_bit_le[bit_len..])?; @@ -666,7 +667,7 @@ impl PlonkCircuit { /// Return a list of variables [b0, ..., b_`bit_len`] which is the binary /// representation of `a`. /// Return error if the `a` is not the range of [0, 2^`bit_len`). - pub fn unpack(&mut self, a: Variable, bit_len: usize) -> Result, PlonkError> { + pub fn unpack(&mut self, a: Variable, bit_len: usize) -> Result, PlonkError> { if bit_len < F::size_in_bits() && self.witness(a)? >= F::from(2u32).pow([bit_len as u64]) { return Err(CircuitError::ParameterError( "Failed to unpack variable to a range of smaller than 2^bit_len".to_string(), @@ -681,7 +682,7 @@ impl PlonkCircuit { &mut self, a: Variable, bit_len: usize, - ) -> Result, PlonkError> { + ) -> Result, PlonkError> { self.check_var_bound(a)?; if bit_len == 0 { return Err(CircuitError::ParameterError( @@ -701,7 +702,7 @@ impl PlonkCircuit { } // convert to variable in the circuit from the vector of boolean as binary // representation - let a_bits_le: Vec = a_bits_le + let a_bits_le: Vec = a_bits_le .iter() .take(bit_len) // since little-endian, truncate would remove MSBs .map(|&b| { @@ -709,18 +710,33 @@ impl PlonkCircuit { }) .collect::, PlonkError>>()?; - self.decompose_vars_gate(a_bits_le.clone(), a, F::from(2u8))?; + self.binary_decomposition_gate(a_bits_le.clone(), a)?; Ok(a_bits_le) } - pub(crate) fn decompose_vars_gate( + fn binary_decomposition_gate( &mut self, - mut padded: Vec, + a_bits_le: Vec, + a: Variable, + ) -> Result<(), PlonkError> { + let a_chunks_le: Vec = a_bits_le.into_iter().map(|b| b.into()).collect(); + self.decomposition_gate(a_chunks_le, a, 2u8.into())?; + Ok(()) + } + + /// a general decomposition gate (not necessarily binary decomposition) + /// where `a` are enforced to decomposed to `a_chunks_le` which consists + /// of chunks (multiple bits) in little-endian order and + /// each chunk \in [0, `range_size`) + pub(crate) fn decomposition_gate( + &mut self, + a_chunks_le: Vec, a: Variable, range_size: F, ) -> Result<(), PlonkError> { // ensure (padded_len - 1) % 3 = 0 + let mut padded = a_chunks_le; let len = padded.len(); let rate = GATE_WIDTH - 1; // rate at which lc add each round let padded_len = next_multiple(len - 1, rate)? + 1; @@ -1179,8 +1195,8 @@ pub(crate) mod test { fn test_conditional_select_helper() -> Result<(), PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let bit_true = circuit.create_variable(F::one())?; - let bit_false = circuit.create_variable(F::zero())?; + let bit_true = circuit.create_bool_variable(true)?; + let bit_false = circuit.create_bool_variable(false)?; let x_0 = circuit.create_variable(F::from(23u32))?; let x_1 = circuit.create_variable(F::from(24u32))?; let select_true = circuit.conditional_select(bit_true, x_0, x_1)?; @@ -1190,11 +1206,8 @@ pub(crate) mod test { assert_eq!(circuit.witness(select_false)?, circuit.witness(x_0)?); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); - // if bit is NOT a boolean variable, should fail - let non_bool = circuit.create_variable(F::from(2u32))?; - assert!(circuit.conditional_select(non_bool, x_0, x_1).is_err()); // if mess up the wire value, should fail - *circuit.witness_mut(bit_false) = F::one(); + *circuit.witness_mut(bit_false.into()) = F::one(); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); // Check variable out of bound error. assert!(circuit @@ -1204,20 +1217,19 @@ pub(crate) mod test { // build two fixed circuits with different variable assignments, checking that // the arithmetized extended permutation polynomial is variable // independent - let circuit_1 = build_conditional_select_circuit(F::one(), F::from(23u32), F::from(24u32))?; - let circuit_2 = - build_conditional_select_circuit(F::zero(), F::from(99u32), F::from(98u32))?; + let circuit_1 = build_conditional_select_circuit(true, F::from(23u32), F::from(24u32))?; + let circuit_2 = build_conditional_select_circuit(false, F::from(99u32), F::from(98u32))?; test_variable_independence_for_circuit(circuit_1, circuit_2)?; Ok(()) } fn build_conditional_select_circuit( - bit: F, + bit: bool, x_0: F, x_1: F, ) -> Result, PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let bit_var = circuit.create_variable(bit)?; + let bit_var = circuit.create_bool_variable(bit)?; let x_0_var = circuit.create_variable(x_0)?; let x_1_var = circuit.create_variable(x_1)?; circuit.conditional_select(bit_var, x_0_var, x_1_var)?; @@ -1430,7 +1442,7 @@ pub(crate) mod test { circuit.lc(&wire_in.try_into().unwrap(), &coeffs)?; // conditional select gate - let bit_true = circuit.create_variable(F::one())?; + let bit_true = circuit.create_bool_variable(true)?; let x_0 = circuit.create_variable(F::from(23u32))?; let x_1 = circuit.create_variable(F::from(24u32))?; circuit.conditional_select(bit_true, x_0, x_1)?; diff --git a/plonk/src/circuit/customized/ultraplonk/range.rs b/plonk/src/circuit/customized/ultraplonk/range.rs index 2ffb047fa..ee6a6d2d0 100644 --- a/plonk/src/circuit/customized/ultraplonk/range.rs +++ b/plonk/src/circuit/customized/ultraplonk/range.rs @@ -48,7 +48,7 @@ impl PlonkCircuit { } // add linear combination gates - self.decompose_vars_gate(reprs_le_vars, a, F::from(range_size as u64))?; + self.decomposition_gate(reprs_le_vars, a, F::from(range_size as u64))?; Ok(()) } diff --git a/plonk/src/circuit/mod.rs b/plonk/src/circuit/mod.rs index e32326efb..f0110bbfc 100644 --- a/plonk/src/circuit/mod.rs +++ b/plonk/src/circuit/mod.rs @@ -18,6 +18,16 @@ pub use basic::PlonkCircuit; /// An index to one of the witness values. pub type Variable = usize; +/// An index to a witness value of boolean type. +#[derive(Debug, Clone, Copy)] +pub struct BoolVar(usize); + +impl From for Variable { + fn from(bv: BoolVar) -> Self { + bv.0 + } +} + /// An index to a gate in circuit. pub type GateId = usize; /// An index to the type of gate wires. @@ -56,11 +66,12 @@ pub trait Circuit { fn create_variable(&mut self, val: F) -> Result; /// Add a bool variable to the circuit; return the index of the variable. - fn create_bool_variable(&mut self, val: bool) -> Result { + fn create_bool_variable(&mut self, val: bool) -> Result { let val_scalar = if val { F::one() } else { F::zero() }; let var = self.create_variable(val_scalar)?; + // FIXME: (alex) should I enforce this in `BoolVar::create()` instead? self.bool_gate(var)?; - Ok(var) + Ok(BoolVar(var)) } /// Add a public input variable; return the index of the variable. diff --git a/primitives/src/circuit/merkle_tree.rs b/primitives/src/circuit/merkle_tree.rs index fd4ca669d..827f12e7e 100644 --- a/primitives/src/circuit/merkle_tree.rs +++ b/primitives/src/circuit/merkle_tree.rs @@ -13,7 +13,7 @@ use ark_ec::TEModelParameters as Parameters; use ark_ff::PrimeField; use ark_std::{vec, vec::Vec}; use jf_plonk::{ - circuit::{customized::rescue::RescueGadget, Circuit, PlonkCircuit, Variable}, + circuit::{customized::rescue::RescueGadget, BoolVar, Circuit, PlonkCircuit, Variable}, errors::PlonkError, }; use jf_rescue::RescueParameter; @@ -22,16 +22,16 @@ use jf_rescue::RescueParameter; struct MerkleNodeBooleanEncoding { sibling1: NodeValue, sibling2: NodeValue, - is_left_child: u8, - is_right_child: u8, + is_left_child: bool, + is_right_child: bool, } impl MerkleNodeBooleanEncoding { fn new( sibling1: NodeValue, sibling2: NodeValue, - is_left_child: u8, - is_right_child: u8, + is_left_child: bool, + is_right_child: bool, ) -> Self { MerkleNodeBooleanEncoding { sibling1, @@ -60,13 +60,13 @@ impl From<&MerklePath> for MerklePathBooleanEncoding { for node in path.nodes.iter() { let circuit_node = match node.pos { NodePos::Left => { - MerkleNodeBooleanEncoding::new(node.sibling1, node.sibling2, 1_u8, 0_u8) + MerkleNodeBooleanEncoding::new(node.sibling1, node.sibling2, true, false) }, NodePos::Middle => { - MerkleNodeBooleanEncoding::new(node.sibling1, node.sibling2, 0_u8, 0_u8) + MerkleNodeBooleanEncoding::new(node.sibling1, node.sibling2, false, false) }, NodePos::Right => { - MerkleNodeBooleanEncoding::new(node.sibling1, node.sibling2, 0_u8, 1_u8) + MerkleNodeBooleanEncoding::new(node.sibling1, node.sibling2, false, true) }, }; nodes.push(circuit_node); @@ -81,8 +81,8 @@ impl From<&MerklePath> for MerklePathBooleanEncoding { pub struct MerkleNodeVars { pub sibling1: Variable, pub sibling2: Variable, - pub is_left_child: Variable, - pub is_right_child: Variable, + pub is_left_child: BoolVar, + pub is_right_child: BoolVar, } #[derive(Debug)] @@ -134,8 +134,8 @@ trait MerkleTreeHelperGadget { node: Variable, sib1: Variable, sib2: Variable, - node_is_left: Variable, - node_is_right: Variable, + node_is_left: BoolVar, + node_is_right: BoolVar, ) -> Result<[Variable; 3], PlonkError>; /// Ensure that the position of each node of the path is correctly encoded @@ -221,8 +221,8 @@ where node: Variable, sib1: Variable, sib2: Variable, - node_is_left: Variable, - node_is_right: Variable, + node_is_left: BoolVar, + node_is_right: BoolVar, ) -> Result<[Variable; 3], PlonkError> { let one = F::one(); let left_node = self.conditional_select(node_is_left, sib1, node)?; @@ -248,8 +248,8 @@ where Ok(MerkleNodeVars { sibling1: self.create_variable(node.sibling1.0)?, sibling2: self.create_variable(node.sibling2.0)?, - is_left_child: self.create_variable(F::from(node.is_left_child as u32))?, - is_right_child: self.create_variable(F::from(node.is_right_child as u32))?, + is_left_child: self.create_bool_variable(node.is_left_child)?, + is_right_child: self.create_bool_variable(node.is_right_child)?, }) }) .collect::, PlonkError>>()?; @@ -257,11 +257,10 @@ where // `is_left_child`, `is_right_child` and `is_left_child+is_right_child` are // boolean for node in nodes.iter() { - self.bool_gate(node.is_left_child)?; - self.bool_gate(node.is_right_child)?; // Boolean constrain `is_left_child + is_right_child` because a node // can either be the left or the right child of its parent - let left_plus_right = self.add(node.is_left_child, node.is_right_child)?; + let left_plus_right = + self.add(node.is_left_child.into(), node.is_right_child.into())?; self.bool_gate(left_plus_right)?; } @@ -307,7 +306,7 @@ mod test { use jf_plonk::circuit::{Circuit, PlonkCircuit, Variable}; use jf_rescue::RescueParameter; - fn check_merkle_path(is_left_child: u8, is_right_child: u8, accept: bool) { + fn check_merkle_path(is_left_child: bool, is_right_child: bool, accept: bool) { let mut circuit = PlonkCircuit::::new_turbo_plonk(); let zero = F::zero(); let one = F::one(); @@ -338,18 +337,13 @@ mod test { // Happy path: // `is_left_child`,`is_right_child` and `is_left_child + is_right_child` are // boolean - check_merkle_path::(1, 0, true); - check_merkle_path::(0, 1, true); - check_merkle_path::(0, 0, true); - - // Circuit cannot be satisfied when `is_left_child` (or `is_right_child`) is not - // boolean - check_merkle_path::(2, 0, false); - check_merkle_path::(0, 2, false); + check_merkle_path::(true, false, true); + check_merkle_path::(false, true, true); + check_merkle_path::(false, false, true); // Circuit cannot be satisfied when `is_left_child + is_right_child` is not // boolean - check_merkle_path::(1, 1, false); + check_merkle_path::(true, true, false); } fn check_permute( @@ -360,17 +354,9 @@ mod test { expected_output_vars: &[Variable], ) { let zero = F::zero(); - let one = F::one(); - let node_is_left = match is_left { - false => circuit.create_variable(zero).unwrap(), - true => circuit.create_variable(one).unwrap(), - }; - - let node_is_right = match is_right { - false => circuit.create_variable(zero).unwrap(), - true => circuit.create_variable(one).unwrap(), - }; + let node_is_left = circuit.create_bool_variable(is_left).unwrap(); + let node_is_right = circuit.create_bool_variable(is_right).unwrap(); let node = input_vars[0]; let sib1 = input_vars[1]; diff --git a/primitives/src/circuit/signature/schnorr.rs b/primitives/src/circuit/signature/schnorr.rs index 1041dbb20..c518b423c 100644 --- a/primitives/src/circuit/signature/schnorr.rs +++ b/primitives/src/circuit/signature/schnorr.rs @@ -20,7 +20,7 @@ use jf_plonk::{ ecc::{Point, PointVariable}, rescue::RescueGadget, }, - Circuit, PlonkCircuit, Variable, + BoolVar, Circuit, PlonkCircuit, Variable, }, errors::PlonkError, }; @@ -159,7 +159,7 @@ where vk: &VerKeyVar, sig_point: &PointVariable, msg: &[Variable], - ) -> Result, PlonkError>; + ) -> Result, PlonkError>; } impl SignatureHelperGadget for PlonkCircuit @@ -172,7 +172,7 @@ where vk: &VerKeyVar, sig_point: &PointVariable, msg: &[Variable], - ) -> Result, PlonkError> { + ) -> Result, PlonkError> { let instance_description = F::from_be_bytes_mod_order(CS_ID_SCHNORR.as_ref()); // TODO: create `inst_desc_var` and the constant gate *only once* during the // entire circuit construction. From cad9a160fa647be056a0bb2a11795aa9f7495062 Mon Sep 17 00:00:00 2001 From: Alex Xiong Date: Tue, 9 Aug 2022 17:38:07 +0800 Subject: [PATCH 3/6] update more APIs --- plonk/src/circuit/basic.rs | 11 +- plonk/src/circuit/customized/ecc/glv.rs | 6 +- plonk/src/circuit/customized/ecc/mod.rs | 30 ++-- plonk/src/circuit/customized/mod.rs | 155 +++++++++++--------- plonk/src/circuit/mod.rs | 21 ++- primitives/src/circuit/signature/schnorr.rs | 6 +- 6 files changed, 137 insertions(+), 92 deletions(-) diff --git a/plonk/src/circuit/basic.rs b/plonk/src/circuit/basic.rs index 58f1d9b02..43408c7a8 100644 --- a/plonk/src/circuit/basic.rs +++ b/plonk/src/circuit/basic.rs @@ -5,7 +5,7 @@ // along with the Jellyfish library. If not, see . //! Basic instantiations of Plonk-based constraint systems -use super::{Arithmetization, Circuit, GateId, Variable, WireId}; +use super::{Arithmetization, BoolVar, Circuit, GateId, Variable, WireId}; use crate::{ circuit::{gates::*, SortedLookupVecAndPolys}, constants::{compute_coset_representatives, GATE_WIDTH, N_MUL_SELECTORS}, @@ -264,6 +264,15 @@ impl PlonkCircuit { pub fn range_size(&self) -> Result { Ok(1 << self.range_bit_len()?) } + + /// creating a `BoolVar` without checking if `v` is a boolean value! + /// You should absolutely sure about what you are doing. + /// You should normally only use this API if you already enforce `v` to be a + /// boolean value using other constraints. + pub(crate) fn create_bool_variable_unchecked(&mut self, a: F) -> Result { + let var = self.create_variable(a)?; + Ok(BoolVar::new_unchecked(var)) + } } impl Circuit for PlonkCircuit { diff --git a/plonk/src/circuit/customized/ecc/glv.rs b/plonk/src/circuit/customized/ecc/glv.rs index 07f03d63e..3f8dfba7c 100644 --- a/plonk/src/circuit/customized/ecc/glv.rs +++ b/plonk/src/circuit/customized/ecc/glv.rs @@ -514,7 +514,8 @@ where }; // (f.3) either f.1 or f.2 is satisfied - let sat = circuit.conditional_select(k2_sign_var, k2_is_neg_sat, k2_is_pos_sat)?; + let sat = + circuit.conditional_select(k2_sign_var, k2_is_neg_sat.into(), k2_is_pos_sat.into())?; circuit.enforce_true(sat)?; // (g) tmp2 + lambda_2 * k2_sign * k2 + s2 = t * t_sign * r2 @@ -544,7 +545,8 @@ where }; // (g.3) either g.1 or g.2 is satisfied - let sat = circuit.conditional_select(k2_sign_var, k2_is_neg_sat, k2_is_pos_sat)?; + let sat = + circuit.conditional_select(k2_sign_var, k2_is_neg_sat.into(), k2_is_pos_sat.into())?; circuit.enforce_true(sat)?; // extract the output diff --git a/plonk/src/circuit/customized/ecc/mod.rs b/plonk/src/circuit/customized/ecc/mod.rs index 0a063d8fd..ae3156dba 100644 --- a/plonk/src/circuit/customized/ecc/mod.rs +++ b/plonk/src/circuit/customized/ecc/mod.rs @@ -263,12 +263,18 @@ where &mut self, point0: &PointVariable, point1: &PointVariable, - ) -> Result { + ) -> Result { self.check_point_var_bound(point0)?; self.check_point_var_bound(point1)?; let x_eq = self.check_equal(point0.0, point1.0)?; let y_eq = self.check_equal(point0.1, point1.1)?; - self.mul(x_eq, y_eq) + + let res = self.create_bool_variable_unchecked( + self.witness(x_eq.into())? * self.witness(y_eq.into())?, + )?; + self.mul_gate(x_eq.into(), y_eq.into(), res.into())?; + + Ok(res) } } @@ -299,24 +305,24 @@ where fn neutral_point_gate( &mut self, point_var: &PointVariable, - expected_neutral: Variable, + expected_neutral: BoolVar, ) -> Result<(), PlonkError> { self.check_point_var_bound(point_var)?; - self.check_var_bound(expected_neutral)?; + self.check_var_bound(expected_neutral.into())?; // constraint 1: b_x = is_equal(x, 0); let b_x = self.check_equal(point_var.0, self.zero())?; // constraint 2: b_y = is_equal(y, 1); let b_y = self.check_equal(point_var.1, self.one())?; // constraint 3: b = b_x * b_y; - self.mul_gate(b_x, b_y, expected_neutral)?; + self.mul_gate(b_x.into(), b_y.into(), expected_neutral.into())?; Ok(()) } /// Obtain a boolean variable indicating whether a point is the neutral /// point (0, 1) Return variable with value 1 if it is, or 0 otherwise /// Return error if input variables are invalid - pub fn is_neutral_point

(&mut self, point_var: &PointVariable) -> Result + pub fn is_neutral_point

(&mut self, point_var: &PointVariable) -> Result where P: Parameters + Clone, { @@ -324,9 +330,9 @@ where let b = { if self.point_witness(point_var)? == Point::from(GroupAffine::

::zero()) { - self.create_variable(F::one())? + self.create_bool_variable(true)? } else { - self.create_variable(F::zero())? + self.create_bool_variable(false)? } }; @@ -636,8 +642,8 @@ mod test { let p1_check = circuit.is_neutral_point::

(&p1)?; let p2_check = circuit.is_neutral_point::

(&p2)?; - assert_eq!(circuit.witness(p1_check)?, F::one()); - assert_eq!(circuit.witness(p2_check)?, F::zero()); + assert_eq!(circuit.witness(p1_check.into())?, F::one()); + assert_eq!(circuit.witness(p2_check.into())?, F::zero()); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); *circuit.witness_mut(p1.0) = F::one(); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); @@ -1002,8 +1008,8 @@ mod test { let p1_p2_eq = circuit.check_equal_point(&p1_var, &p2_var)?; let p1_p3_eq = circuit.check_equal_point(&p1_var, &p3_var)?; - assert_eq!(circuit.witness(p1_p2_eq)?, F::one()); - assert_eq!(circuit.witness(p1_p3_eq)?, F::zero()); + assert_eq!(circuit.witness(p1_p2_eq.into())?, F::one()); + assert_eq!(circuit.witness(p1_p3_eq.into())?, F::zero()); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); *circuit.witness_mut(p2_var.0) = F::zero(); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); diff --git a/plonk/src/circuit/customized/mod.rs b/plonk/src/circuit/customized/mod.rs index dde4959bd..3737c296a 100644 --- a/plonk/src/circuit/customized/mod.rs +++ b/plonk/src/circuit/customized/mod.rs @@ -313,17 +313,17 @@ where /// Constrain that `a` is true or `b` is true. /// Return error if variables are invalid. - pub fn logic_or_gate(&mut self, a: Variable, b: Variable) -> Result<(), PlonkError> { - self.check_var_bound(a)?; - self.check_var_bound(b)?; - let wire_vars = &[a, b, 0, 0, 0]; + pub fn logic_or_gate(&mut self, a: BoolVar, b: BoolVar) -> Result<(), PlonkError> { + self.check_var_bound(a.into())?; + self.check_var_bound(b.into())?; + let wire_vars = &[a.into(), b.into(), 0, 0, 0]; self.insert_gate(wire_vars, Box::new(LogicOrGate))?; Ok(()) } /// Obtain a bool variable representing whether two input variables are /// equal. Return error if variables are invalid. - pub fn check_equal(&mut self, a: Variable, b: Variable) -> Result { + pub fn check_equal(&mut self, a: Variable, b: Variable) -> Result { self.check_var_bound(a)?; self.check_var_bound(b)?; let delta = self.sub(a, b)?; @@ -332,7 +332,7 @@ where /// Obtain a bool variable representing whether input variable is zero. /// Return error if the input variable is invalid. - pub fn check_is_zero(&mut self, a: Variable) -> Result { + pub fn check_is_zero(&mut self, a: Variable) -> Result { self.check_var_bound(a)?; // y is the bit indicating if a == zero @@ -348,16 +348,16 @@ where })?, ) }; - let y = self.create_variable(y)?; + let y = self.create_bool_variable_unchecked(y)?; let a_inv = self.create_variable(a_inv)?; // constraint 1: 1 - a * a^(-1) = y, i.e., a * a^(-1) + 1 * y = 1 self.mul_add_gate( - &[a, a_inv, self.one(), y, self.one()], + &[a, a_inv, self.one(), y.into(), self.one()], &[F::one(), F::one()], )?; // constraint 2: multiplication y * a = 0 - self.mul_gate(y, a, self.zero())?; + self.mul_gate(y.into(), a, self.zero())?; Ok(y) } @@ -370,26 +370,27 @@ where self.mul_gate(var, inv_var, one_var) } - /// Assuming value represented by `a` is boolean, obtain a - /// variable representing the result of a logic negation gate. Return the - /// index of the variable. Return error if the input variable is invalid. - pub fn logic_neg(&mut self, a: Variable) -> Result { - self.check_is_zero(a) + /// Obtain a variable representing the result of a logic negation gate. + /// Return the index of the variable. Return error if the input variable + /// is invalid. + pub fn logic_neg(&mut self, a: BoolVar) -> Result { + self.check_is_zero(a.into()) } - /// Assuming values represented by `a` and `b` are boolean, obtain a - /// variable representing the result of a logic AND gate. Return the - /// index of the variable. Return error if the input variables are + /// Obtain a variable representing the result of a logic AND gate. Return + /// the index of the variable. Return error if the input variables are /// invalid. - pub fn logic_and(&mut self, a: Variable, b: Variable) -> Result { - self.mul(a, b) + pub fn logic_and(&mut self, a: BoolVar, b: BoolVar) -> Result { + let c = + self.create_bool_variable_unchecked(self.witness(a.into())? * self.witness(b.into())?)?; + self.mul_gate(a.into(), b.into(), c.into())?; + Ok(c) } - /// Given a list of boolean variables, obtain a - /// variable representing the result of a logic AND gate. Return the - /// index of the variable. Return error if the input variables are - /// invalid. - pub fn logic_and_all(&mut self, vars: &[Variable]) -> Result { + /// Given a list of boolean variables, obtain a variable representing the + /// result of a logic AND gate. Return the index of the variable. Return + /// error if the input variables are invalid. + pub fn logic_and_all(&mut self, vars: &[BoolVar]) -> Result { if vars.is_empty() { return Err(PlonkError::InvalidParameters( "logic_and_all: empty variable list".to_string(), @@ -402,19 +403,21 @@ where Ok(res) } - /// Assuming values represented by `a` and `b` are boolean, obtain a - /// variable representing the result of a logic OR gate. Return the + /// Obtain a variable representing the result of a logic OR gate. Return the /// index of the variable. Return error if the input variables are /// invalid. - pub fn logic_or(&mut self, a: Variable, b: Variable) -> Result { - self.check_var_bound(a)?; - self.check_var_bound(b)?; - let a_val = self.witness(a)?; - let b_val = self.witness(b)?; + pub fn logic_or(&mut self, a: BoolVar, b: BoolVar) -> Result { + self.check_var_bound(a.into())?; + self.check_var_bound(b.into())?; + + let a_val = self.witness(a.into())?; + let b_val = self.witness(b.into())?; let c_val = a_val + b_val - a_val * b_val; - let c = self.create_variable(c_val)?; - let wire_vars = &[a, b, 0, 0, c]; + + let c = self.create_bool_variable_unchecked(c_val)?; + let wire_vars = &[a.into(), b.into(), 0, 0, c.into()]; self.insert_gate(wire_vars, Box::new(LogicOrValueGate))?; + Ok(c) } @@ -654,7 +657,7 @@ impl PlonkCircuit { /// Return a boolean variable indicating whether variable `a` is in the /// range [0, 2^`bit_len`). Return error if the variable is invalid. /// TODO: optimize the gate for UltraPlonk. - pub fn check_in_range(&mut self, a: Variable, bit_len: usize) -> Result { + pub fn check_in_range(&mut self, a: Variable, bit_len: usize) -> Result { let a_bit_le: Vec = self.unpack(a, F::size_in_bits())?; let a_bit_le: Vec = a_bit_le.into_iter().map(|b| b.into()).collect(); // a is in range if and only if the bits in `a_bit_le[bit_len..]` are all @@ -831,28 +834,31 @@ pub(crate) mod test { fn test_logic_or_helper() -> Result<(), PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let zero_var = circuit.zero(); - let one_var = circuit.one(); + let false_var = circuit.create_bool_variable(false)?; + let true_var = circuit.create_bool_variable(true)?; // Good path - circuit.logic_or_gate(zero_var, one_var)?; - circuit.logic_or_gate(one_var, zero_var)?; - circuit.logic_or_gate(one_var, one_var)?; + circuit.logic_or_gate(false_var, true_var)?; + circuit.logic_or_gate(true_var, false_var)?; + circuit.logic_or_gate(true_var, true_var)?; assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); // Error path - circuit.logic_or_gate(zero_var, zero_var)?; + circuit.logic_or_gate(false_var, false_var)?; assert!(circuit.check_circuit_satisfiability(&[]).is_err()); - let circuit_1 = build_logic_or_circuit(F::one(), F::one())?; - let circuit_2 = build_logic_or_circuit(F::zero(), F::one())?; + let circuit_1 = build_logic_or_circuit(true, true)?; + let circuit_2 = build_logic_or_circuit(false, true)?; test_variable_independence_for_circuit::(circuit_1, circuit_2)?; Ok(()) } - fn build_logic_or_circuit(a: F, b: F) -> Result, PlonkError> { + fn build_logic_or_circuit( + a: bool, + b: bool, + ) -> Result, PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let a = circuit.create_variable(a)?; - let b = circuit.create_variable(b)?; + let a = circuit.create_bool_variable(a)?; + let b = circuit.create_bool_variable(b)?; circuit.logic_or_gate(a, b)?; circuit.finalize_for_arithmetization()?; Ok(circuit) @@ -868,37 +874,40 @@ pub(crate) mod test { fn test_logic_and_helper() -> Result<(), PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let zero_var = circuit.zero(); - let one_var = circuit.one(); + let false_var = circuit.false_var(); + let true_var = circuit.true_var(); // Good path - let a = circuit.logic_and(zero_var, one_var)?; - assert_eq!(F::zero(), circuit.witness(a)?); - let b = circuit.logic_and(one_var, zero_var)?; - assert_eq!(F::zero(), circuit.witness(b)?); - let c = circuit.logic_and(one_var, one_var)?; - assert_eq!(F::one(), circuit.witness(c)?); - let d = circuit.logic_and_all(&[zero_var, one_var, one_var])?; - assert_eq!(F::zero(), circuit.witness(d)?); - let e = circuit.logic_and_all(&[one_var, one_var, one_var])?; - assert_eq!(F::one(), circuit.witness(e)?); + let a = circuit.logic_and(false_var, true_var)?; + assert_eq!(F::zero(), circuit.witness(a.into())?); + let b = circuit.logic_and(true_var, false_var)?; + assert_eq!(F::zero(), circuit.witness(b.into())?); + let c = circuit.logic_and(true_var, true_var)?; + assert_eq!(F::one(), circuit.witness(c.into())?); + let d = circuit.logic_and_all(&[false_var, true_var, true_var])?; + assert_eq!(F::zero(), circuit.witness(d.into())?); + let e = circuit.logic_and_all(&[true_var, true_var, true_var])?; + assert_eq!(F::one(), circuit.witness(e.into())?); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); // Error path - *circuit.witness_mut(e) = F::zero(); + *circuit.witness_mut(e.into()) = F::zero(); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); - *circuit.witness_mut(e) = F::one(); + *circuit.witness_mut(e.into()) = F::one(); assert!(circuit.logic_and_all(&[]).is_err()); - let circuit_1 = build_logic_and_circuit(F::one(), F::one())?; - let circuit_2 = build_logic_and_circuit(F::zero(), F::one())?; + let circuit_1 = build_logic_and_circuit(true, true)?; + let circuit_2 = build_logic_and_circuit(false, true)?; test_variable_independence_for_circuit::(circuit_1, circuit_2)?; Ok(()) } - fn build_logic_and_circuit(a: F, b: F) -> Result, PlonkError> { + fn build_logic_and_circuit( + a: bool, + b: bool, + ) -> Result, PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let a = circuit.create_variable(a)?; - let b = circuit.create_variable(b)?; + let a = circuit.create_bool_variable(a)?; + let b = circuit.create_bool_variable(b)?; circuit.logic_and(a, b)?; circuit.finalize_for_arithmetization()?; Ok(circuit) @@ -920,8 +929,8 @@ pub(crate) mod test { let a_zero_eq = circuit.check_equal(a, circuit.zero())?; // check circuit - assert_eq!(circuit.witness(a_b_eq)?, F::one()); - assert_eq!(circuit.witness(a_zero_eq)?, F::zero()); + assert_eq!(circuit.witness(a_b_eq.into())?, F::one()); + assert_eq!(circuit.witness(a_zero_eq.into())?, F::zero()); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); *circuit.witness_mut(b) = val + F::one(); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); @@ -959,12 +968,12 @@ pub(crate) mod test { let zero_zero_eq = circuit.check_is_zero(circuit.zero())?; // check circuit - assert_eq!(circuit.witness(a_zero_eq)?, F::zero()); - assert_eq!(circuit.witness(zero_zero_eq)?, F::one()); + assert_eq!(circuit.witness(a_zero_eq.into())?, F::zero()); + assert_eq!(circuit.witness(zero_zero_eq.into())?, F::one()); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); - *circuit.witness_mut(zero_zero_eq) = F::zero(); + *circuit.witness_mut(zero_zero_eq.into()) = F::zero(); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); - *circuit.witness_mut(zero_zero_eq) = F::one(); + *circuit.witness_mut(zero_zero_eq.into()) = F::one(); *circuit.witness_mut(a) = F::zero(); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); // Check variable out of bound error. @@ -1382,9 +1391,9 @@ pub(crate) mod test { let b1 = circuit.check_in_range(a, 5)?; let b2 = circuit.check_in_range(a, 10)?; let b3 = circuit.check_in_range(a, 0)?; - assert_eq!(circuit.witness(b1)?, F::zero()); - assert_eq!(circuit.witness(b2)?, F::one()); - assert_eq!(circuit.witness(b3)?, F::zero()); + assert_eq!(circuit.witness(b1.into())?, F::zero()); + assert_eq!(circuit.witness(b2.into())?, F::one()); + assert_eq!(circuit.witness(b3.into())?, F::zero()); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); // if mess up the wire value, should fail diff --git a/plonk/src/circuit/mod.rs b/plonk/src/circuit/mod.rs index f0110bbfc..08585d0e2 100644 --- a/plonk/src/circuit/mod.rs +++ b/plonk/src/circuit/mod.rs @@ -28,6 +28,16 @@ impl From for Variable { } } +impl BoolVar { + /// Create a `BoolVar` without any check. Be careful! + /// This is an internal API, shouldn't be used unless you know what you are + /// doing. Normally you should only construct `BoolVar` through + /// `Circuit::create_bool_variable()`. + pub(crate) fn new_unchecked(inner: usize) -> Self { + Self(inner) + } +} + /// An index to a gate in circuit. pub type GateId = usize; /// An index to the type of gate wires. @@ -69,7 +79,6 @@ pub trait Circuit { fn create_bool_variable(&mut self, val: bool) -> Result { let val_scalar = if val { F::one() } else { F::zero() }; let var = self.create_variable(val_scalar)?; - // FIXME: (alex) should I enforce this in `BoolVar::create()` instead? self.bool_gate(var)?; Ok(BoolVar(var)) } @@ -86,6 +95,16 @@ pub trait Circuit { /// Return a default variable with value one. fn one(&self) -> Variable; + /// Return a default variable with value `false` (namely zero). + fn false_var(&self) -> BoolVar { + BoolVar::new_unchecked(self.zero()) + } + + /// Return a default variable with value `true` (namely one). + fn true_var(&self) -> BoolVar { + BoolVar::new_unchecked(self.one()) + } + /// Return the witness value of variable `idx`. /// Return error if the input variable is invalid. fn witness(&self, idx: Variable) -> Result; diff --git a/primitives/src/circuit/signature/schnorr.rs b/primitives/src/circuit/signature/schnorr.rs index c518b423c..3be49ab58 100644 --- a/primitives/src/circuit/signature/schnorr.rs +++ b/primitives/src/circuit/signature/schnorr.rs @@ -70,7 +70,7 @@ where vk: &VerKeyVar, msg: &[Variable], sig: &SignatureVar, - ) -> Result; + ) -> Result; /// Create a signature variable from a signature `sig`. fn create_signature_variable(&mut self, sig: &Signature

) @@ -111,7 +111,7 @@ where vk: &VerKeyVar, msg: &[Variable], sig: &SignatureVar, - ) -> Result { + ) -> Result { let (p1, p2) = >::verify_sig_core(self, vk, msg, sig)?; self.check_equal_point(&p1, &p2) } @@ -316,6 +316,6 @@ mod tests { &msg_var, &sig_var, )?; - Ok((circuit, bit)) + Ok((circuit, bit.into())) } } From 6a1926505fbfa3f2b0e065afe3d853708d9c997c Mon Sep 17 00:00:00 2001 From: Alex Xiong Date: Tue, 9 Aug 2022 17:43:21 +0800 Subject: [PATCH 4/6] use true_var() and false_var() instead --- plonk/src/circuit/customized/ecc/mod.rs | 8 ++++---- plonk/src/circuit/customized/mod.rs | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/plonk/src/circuit/customized/ecc/mod.rs b/plonk/src/circuit/customized/ecc/mod.rs index ae3156dba..762bfe9a3 100644 --- a/plonk/src/circuit/customized/ecc/mod.rs +++ b/plonk/src/circuit/customized/ecc/mod.rs @@ -857,8 +857,8 @@ mod test { let p3 = GroupAffine::

::rand(&mut rng); let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let false_var = circuit.create_bool_variable(false)?; - let true_var = circuit.create_bool_variable(true)?; + let false_var = circuit.false_var(); + let true_var = circuit.true_var(); let select_p0 = circuit.quaternary_point_select::

( false_var, @@ -1174,8 +1174,8 @@ mod test { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); let p0_var = circuit.create_point_variable(Point::from(p0))?; let p1_var = circuit.create_point_variable(Point::from(p1))?; - let true_var = circuit.create_bool_variable(true)?; - let false_var = circuit.create_bool_variable(false)?; + let true_var = circuit.true_var(); + let false_var = circuit.false_var(); let select_p0 = circuit.binary_point_vars_select(false_var, &p0_var, &p1_var)?; assert_eq!(circuit.point_witness(&select_p0)?, Point::from(p0)); diff --git a/plonk/src/circuit/customized/mod.rs b/plonk/src/circuit/customized/mod.rs index 3737c296a..8a125f083 100644 --- a/plonk/src/circuit/customized/mod.rs +++ b/plonk/src/circuit/customized/mod.rs @@ -834,8 +834,8 @@ pub(crate) mod test { fn test_logic_or_helper() -> Result<(), PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let false_var = circuit.create_bool_variable(false)?; - let true_var = circuit.create_bool_variable(true)?; + let false_var = circuit.false_var(); + let true_var = circuit.true_var(); // Good path circuit.logic_or_gate(false_var, true_var)?; circuit.logic_or_gate(true_var, false_var)?; @@ -1204,8 +1204,9 @@ pub(crate) mod test { fn test_conditional_select_helper() -> Result<(), PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let bit_true = circuit.create_bool_variable(true)?; - let bit_false = circuit.create_bool_variable(false)?; + let bit_true = circuit.true_var(); + let bit_false = circuit.false_var(); + let x_0 = circuit.create_variable(F::from(23u32))?; let x_1 = circuit.create_variable(F::from(24u32))?; let select_true = circuit.conditional_select(bit_true, x_0, x_1)?; From b8c0f350065957826187e4b7d3e49db1fe6b33ed Mon Sep 17 00:00:00 2001 From: Alex Xiong Date: Tue, 9 Aug 2022 17:46:48 +0800 Subject: [PATCH 5/6] update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea36dd10f..325093a54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Refactored `UniversalSNARK` trait (#80, #87) - Restore `no_std` compliance (#85, #87) - Use [blst](https://github.com/supranational/blst) library for BLS signature/VRF (#89) +- Introduce `struct BoolVar` whenever necessary and possible (#91) ## v0.1.2 From 5f0cd663417b46145cedbd3f620aa8963808ba00 Mon Sep 17 00:00:00 2001 From: Alex Xiong Date: Tue, 9 Aug 2022 22:48:08 +0800 Subject: [PATCH 6/6] address comments --- plonk/src/circuit/basic.rs | 5 ++++- plonk/src/circuit/customized/ecc/glv.rs | 2 +- plonk/src/circuit/customized/ecc/mod.rs | 18 ++++++------------ plonk/src/circuit/customized/mod.rs | 22 +++++++++++----------- plonk/src/circuit/mod.rs | 4 ++-- primitives/src/circuit/merkle_tree.rs | 8 ++++---- 6 files changed, 28 insertions(+), 31 deletions(-) diff --git a/plonk/src/circuit/basic.rs b/plonk/src/circuit/basic.rs index 43408c7a8..7640149df 100644 --- a/plonk/src/circuit/basic.rs +++ b/plonk/src/circuit/basic.rs @@ -269,7 +269,10 @@ impl PlonkCircuit { /// You should absolutely sure about what you are doing. /// You should normally only use this API if you already enforce `v` to be a /// boolean value using other constraints. - pub(crate) fn create_bool_variable_unchecked(&mut self, a: F) -> Result { + pub(crate) fn create_boolean_variable_unchecked( + &mut self, + a: F, + ) -> Result { let var = self.create_variable(a)?; Ok(BoolVar::new_unchecked(var)) } diff --git a/plonk/src/circuit/customized/ecc/glv.rs b/plonk/src/circuit/customized/ecc/glv.rs index 3f8dfba7c..b8c0db60c 100644 --- a/plonk/src/circuit/customized/ecc/glv.rs +++ b/plonk/src/circuit/customized/ecc/glv.rs @@ -459,7 +459,7 @@ where let k1_var = circuit.create_variable(int_to_fq!(k1_int))?; let k2_var = circuit.create_variable(int_to_fq!(k2_int))?; - let k2_sign_var = circuit.create_bool_variable(is_k2_positive)?; + let k2_sign_var = circuit.create_boolean_variable(is_k2_positive)?; let t_var = circuit.create_variable(int_to_fq!(t_int))?; diff --git a/plonk/src/circuit/customized/ecc/mod.rs b/plonk/src/circuit/customized/ecc/mod.rs index 762bfe9a3..2c6bcd850 100644 --- a/plonk/src/circuit/customized/ecc/mod.rs +++ b/plonk/src/circuit/customized/ecc/mod.rs @@ -268,13 +268,7 @@ where self.check_point_var_bound(point1)?; let x_eq = self.check_equal(point0.0, point1.0)?; let y_eq = self.check_equal(point0.1, point1.1)?; - - let res = self.create_bool_variable_unchecked( - self.witness(x_eq.into())? * self.witness(y_eq.into())?, - )?; - self.mul_gate(x_eq.into(), y_eq.into(), res.into())?; - - Ok(res) + self.logic_and(x_eq, y_eq) } } @@ -330,9 +324,9 @@ where let b = { if self.point_witness(point_var)? == Point::from(GroupAffine::

::zero()) { - self.create_bool_variable(true)? + self.create_boolean_variable(true)? } else { - self.create_bool_variable(false)? + self.create_boolean_variable(false)? } }; @@ -914,8 +908,8 @@ mod test { P: Parameters + Clone, { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let b0_var = circuit.create_bool_variable(b0)?; - let b1_var = circuit.create_bool_variable(b1)?; + let b0_var = circuit.create_boolean_variable(b0)?; + let b1_var = circuit.create_boolean_variable(b1)?; let mut rng = ark_std::test_rng(); let p1 = GroupAffine::

::rand(&mut rng); @@ -1216,7 +1210,7 @@ mod test { P: Parameters + Clone, { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let b_var = circuit.create_bool_variable(b)?; + let b_var = circuit.create_boolean_variable(b)?; let p0_var = circuit.create_point_variable(p0)?; let p1_var = circuit.create_point_variable(p1)?; circuit.binary_point_vars_select(b_var, &p0_var, &p1_var)?; diff --git a/plonk/src/circuit/customized/mod.rs b/plonk/src/circuit/customized/mod.rs index 8a125f083..9ce778ae7 100644 --- a/plonk/src/circuit/customized/mod.rs +++ b/plonk/src/circuit/customized/mod.rs @@ -348,7 +348,7 @@ where })?, ) }; - let y = self.create_bool_variable_unchecked(y)?; + let y = self.create_boolean_variable_unchecked(y)?; let a_inv = self.create_variable(a_inv)?; // constraint 1: 1 - a * a^(-1) = y, i.e., a * a^(-1) + 1 * y = 1 @@ -381,8 +381,8 @@ where /// the index of the variable. Return error if the input variables are /// invalid. pub fn logic_and(&mut self, a: BoolVar, b: BoolVar) -> Result { - let c = - self.create_bool_variable_unchecked(self.witness(a.into())? * self.witness(b.into())?)?; + let c = self + .create_boolean_variable_unchecked(self.witness(a.into())? * self.witness(b.into())?)?; self.mul_gate(a.into(), b.into(), c.into())?; Ok(c) } @@ -414,7 +414,7 @@ where let b_val = self.witness(b.into())?; let c_val = a_val + b_val - a_val * b_val; - let c = self.create_bool_variable_unchecked(c_val)?; + let c = self.create_boolean_variable_unchecked(c_val)?; let wire_vars = &[a.into(), b.into(), 0, 0, c.into()]; self.insert_gate(wire_vars, Box::new(LogicOrValueGate))?; @@ -709,7 +709,7 @@ impl PlonkCircuit { .iter() .take(bit_len) // since little-endian, truncate would remove MSBs .map(|&b| { - self.create_bool_variable(b) + self.create_boolean_variable(b) }) .collect::, PlonkError>>()?; @@ -857,8 +857,8 @@ pub(crate) mod test { b: bool, ) -> Result, PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let a = circuit.create_bool_variable(a)?; - let b = circuit.create_bool_variable(b)?; + let a = circuit.create_boolean_variable(a)?; + let b = circuit.create_boolean_variable(b)?; circuit.logic_or_gate(a, b)?; circuit.finalize_for_arithmetization()?; Ok(circuit) @@ -906,8 +906,8 @@ pub(crate) mod test { b: bool, ) -> Result, PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let a = circuit.create_bool_variable(a)?; - let b = circuit.create_bool_variable(b)?; + let a = circuit.create_boolean_variable(a)?; + let b = circuit.create_boolean_variable(b)?; circuit.logic_and(a, b)?; circuit.finalize_for_arithmetization()?; Ok(circuit) @@ -1239,7 +1239,7 @@ pub(crate) mod test { x_1: F, ) -> Result, PlonkError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let bit_var = circuit.create_bool_variable(bit)?; + let bit_var = circuit.create_boolean_variable(bit)?; let x_0_var = circuit.create_variable(x_0)?; let x_1_var = circuit.create_variable(x_1)?; circuit.conditional_select(bit_var, x_0_var, x_1_var)?; @@ -1452,7 +1452,7 @@ pub(crate) mod test { circuit.lc(&wire_in.try_into().unwrap(), &coeffs)?; // conditional select gate - let bit_true = circuit.create_bool_variable(true)?; + let bit_true = circuit.create_boolean_variable(true)?; let x_0 = circuit.create_variable(F::from(23u32))?; let x_1 = circuit.create_variable(F::from(24u32))?; circuit.conditional_select(bit_true, x_0, x_1)?; diff --git a/plonk/src/circuit/mod.rs b/plonk/src/circuit/mod.rs index 08585d0e2..344e3649c 100644 --- a/plonk/src/circuit/mod.rs +++ b/plonk/src/circuit/mod.rs @@ -32,7 +32,7 @@ impl BoolVar { /// Create a `BoolVar` without any check. Be careful! /// This is an internal API, shouldn't be used unless you know what you are /// doing. Normally you should only construct `BoolVar` through - /// `Circuit::create_bool_variable()`. + /// `Circuit::create_boolean_variable()`. pub(crate) fn new_unchecked(inner: usize) -> Self { Self(inner) } @@ -76,7 +76,7 @@ pub trait Circuit { fn create_variable(&mut self, val: F) -> Result; /// Add a bool variable to the circuit; return the index of the variable. - fn create_bool_variable(&mut self, val: bool) -> Result { + fn create_boolean_variable(&mut self, val: bool) -> Result { let val_scalar = if val { F::one() } else { F::zero() }; let var = self.create_variable(val_scalar)?; self.bool_gate(var)?; diff --git a/primitives/src/circuit/merkle_tree.rs b/primitives/src/circuit/merkle_tree.rs index 827f12e7e..df038c7ce 100644 --- a/primitives/src/circuit/merkle_tree.rs +++ b/primitives/src/circuit/merkle_tree.rs @@ -248,8 +248,8 @@ where Ok(MerkleNodeVars { sibling1: self.create_variable(node.sibling1.0)?, sibling2: self.create_variable(node.sibling2.0)?, - is_left_child: self.create_bool_variable(node.is_left_child)?, - is_right_child: self.create_bool_variable(node.is_right_child)?, + is_left_child: self.create_boolean_variable(node.is_left_child)?, + is_right_child: self.create_boolean_variable(node.is_right_child)?, }) }) .collect::, PlonkError>>()?; @@ -355,8 +355,8 @@ mod test { ) { let zero = F::zero(); - let node_is_left = circuit.create_bool_variable(is_left).unwrap(); - let node_is_right = circuit.create_bool_variable(is_right).unwrap(); + let node_is_left = circuit.create_boolean_variable(is_left).unwrap(); + let node_is_right = circuit.create_boolean_variable(is_right).unwrap(); let node = input_vars[0]; let sib1 = input_vars[1];