diff --git a/CHANGELOG.md b/CHANGELOG.md index ab855e57d..3565cfcdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ and follow [semantic versioning](https://semver.org/) for our releases. - [#251](https://github.com/EspressoSystems/jellyfish/pull/251) add sign_key_ref api for BLSKeyPair - [#297](https://github.com/EspressoSystems/jellyfish/pull/297) Updated `tagged-base64` dependency to the `crates.io` package - [#299](https://github.com/EspressoSystems/jellyfish/pull/299) For Merkle tree, `DigestAlgorithm` now returns a `Result` type. +- [#302](https://github.com/EspressoSystems/jellyfish/pull/302) Followup APIs for non-native ECC circuit support. ### Removed diff --git a/primitives/src/rescue/sponge.rs b/primitives/src/rescue/sponge.rs index f4b22ed29..aed4a9983 100644 --- a/primitives/src/rescue/sponge.rs +++ b/primitives/src/rescue/sponge.rs @@ -27,7 +27,7 @@ struct RescueSponge { /// CRHF #[derive(Debug, Clone)] -pub(crate) struct RescueCRHF { +pub struct RescueCRHF { sponge: RescueSponge, } @@ -47,7 +47,7 @@ impl RescueCRHF { /// multiple of RATE /// /// [padding]: https://en.wikipedia.org/wiki/Padding_(cryptography)#Bit_padding - pub(crate) fn sponge_with_bit_padding(input: &[F], num_outputs: usize) -> Vec { + pub fn sponge_with_bit_padding(input: &[F], num_outputs: usize) -> Vec { let mut padded = input.to_vec(); padded.push(F::one()); pad_with_zeros(&mut padded, CRHF_RATE); @@ -60,7 +60,7 @@ impl RescueCRHF { /// overall length to be a multiple of RATE. /// /// [padding]: https://en.wikipedia.org/wiki/Padding_(cryptography)#Zero_padding - pub(crate) fn sponge_with_zero_padding(input: &[F], num_outputs: usize) -> Vec { + pub fn sponge_with_zero_padding(input: &[F], num_outputs: usize) -> Vec { let mut padded = input.to_vec(); pad_with_zeros(&mut padded, CRHF_RATE); Self::sponge_no_padding(padded.as_slice(), num_outputs) @@ -70,7 +70,7 @@ impl RescueCRHF { /// Sponge hashing based on rescue permutation for RATE 3 and CAPACITY 1. It /// allows inputs with length that is a multiple of `CRHF_RATE` and /// returns a vector of `num_outputs` elements. - pub(crate) fn sponge_no_padding(input: &[F], num_output: usize) -> Result, RescueError> { + pub fn sponge_no_padding(input: &[F], num_output: usize) -> Result, RescueError> { if input.len() % CRHF_RATE != 0 { return Err(RescueError::ParameterError( "Rescue sponge Error : input to sponge hashing function is not multiple of RATE." diff --git a/relation/src/gadgets/ecc/non_native.rs b/relation/src/gadgets/ecc/non_native.rs index 2aff54f1e..73bf5858e 100644 --- a/relation/src/gadgets/ecc/non_native.rs +++ b/relation/src/gadgets/ecc/non_native.rs @@ -10,11 +10,12 @@ use super::Point; use crate::{ errors::CircuitError, gadgets::{EmulatedVariable, EmulationConfig}, - PlonkCircuit, + BoolVar, PlonkCircuit, }; use ark_ff::PrimeField; /// The variable represents an EC point in the emulated field. +#[derive(Debug, Clone)] pub struct EmulatedPointVariable(pub EmulatedVariable, pub EmulatedVariable); impl PlonkCircuit { @@ -110,6 +111,34 @@ impl PlonkCircuit { self.emulated_ecc_add_gate(a, b, &c, d)?; Ok(c) } + + /// Obtain an emulated point variable of the conditional selection from 2 + /// emulated point variables. `b` is a boolean variable that indicates + /// selection of P_b from (P0, P1). + /// Return error if invalid input parameters are provided. + pub fn binary_emulated_point_vars_select>( + &mut self, + b: BoolVar, + point0: &EmulatedPointVariable, + point1: &EmulatedPointVariable, + ) -> Result, CircuitError> { + let select_x = self.conditional_select_emulated(b, &point0.0, &point1.0)?; + let select_y = self.conditional_select_emulated(b, &point0.1, &point1.1)?; + + Ok(EmulatedPointVariable::(select_x, select_y)) + } + + /// Constrain two emulated point variables to be the same. + /// Return error if the input point variables are invalid. + pub fn enforce_emulated_point_equal>( + &mut self, + point0: &EmulatedPointVariable, + point1: &EmulatedPointVariable, + ) -> Result<(), CircuitError> { + self.enforce_emulated_var_equal(&point0.0, &point1.0)?; + self.enforce_emulated_var_equal(&point0.1, &point1.1)?; + Ok(()) + } } #[cfg(test)] @@ -164,4 +193,67 @@ mod tests { .unwrap(); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); } + + #[test] + fn test_emulated_point_select() { + test_emulated_point_select_helper::(); + } + + fn test_emulated_point_select_helper() + where + E: EmulationConfig + SWToTEConParam, + F: PrimeField, + P: SWCurveConfig, + { + let mut rng = jf_utils::test_rng(); + let p1 = Projective::

::rand(&mut rng).into_affine(); + let p2 = Projective::

::rand(&mut rng).into_affine(); + let p1: Point = (&p1).into(); + let p2: Point = (&p2).into(); + + let mut circuit = PlonkCircuit::::new_turbo_plonk(); + + let var_p1 = circuit.create_emulated_point_variable(p1).unwrap(); + let var_p2 = circuit.create_emulated_point_variable(p2).unwrap(); + let b = circuit.create_boolean_variable(true).unwrap(); + let var_p3 = circuit + .binary_emulated_point_vars_select(b, &var_p1, &var_p2) + .unwrap(); + assert_eq!(circuit.emulated_point_witness(&var_p3).unwrap(), p2); + assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); + *circuit.witness_mut(var_p3.0 .0[0]) = F::zero(); + assert!(circuit.check_circuit_satisfiability(&[]).is_err()); + } + + #[test] + fn test_enforce_emulated_point_eq() { + test_enforce_emulated_point_eq_helper::(); + } + + fn test_enforce_emulated_point_eq_helper() + where + E: EmulationConfig + SWToTEConParam, + F: PrimeField, + P: SWCurveConfig, + { + let mut rng = jf_utils::test_rng(); + let p1 = Projective::

::rand(&mut rng).into_affine(); + let p2 = (p1 + Projective::

::generator()).into_affine(); + let p1: Point = (&p1).into(); + let p2: Point = (&p2).into(); + + let mut circuit = PlonkCircuit::::new_turbo_plonk(); + + let var_p1 = circuit.create_emulated_point_variable(p1).unwrap(); + let var_p2 = circuit.create_emulated_point_variable(p2).unwrap(); + let var_p3 = circuit.create_emulated_point_variable(p1).unwrap(); + circuit + .enforce_emulated_point_equal(&var_p1, &var_p3) + .unwrap(); + assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); + circuit + .enforce_emulated_point_equal(&var_p1, &var_p2) + .unwrap(); + assert!(circuit.check_circuit_satisfiability(&[]).is_err()); + } } diff --git a/relation/src/gadgets/emulated.rs b/relation/src/gadgets/emulated.rs index a14ad0d72..9482a4530 100644 --- a/relation/src/gadgets/emulated.rs +++ b/relation/src/gadgets/emulated.rs @@ -12,7 +12,7 @@ //! componenet, with modulus 2^T, will be divided into limbs each with B bits //! where 2^{2B} < p. -use crate::{errors::CircuitError, Circuit, PlonkCircuit, Variable}; +use crate::{errors::CircuitError, BoolVar, Circuit, PlonkCircuit, Variable}; use ark_ff::PrimeField; use ark_std::{string::ToString, vec, vec::Vec, One, Zero}; use core::marker::PhantomData; @@ -71,8 +71,16 @@ where } /// The variable represents an element in the emulated field. +#[derive(Debug, Clone)] pub struct EmulatedVariable(pub(crate) Vec, PhantomData); +impl EmulatedVariable { + /// Return the list of variables that simulate the field element + pub fn to_vec(&self) -> Vec { + self.0.clone() + } +} + impl PlonkCircuit { /// Return the witness point for the circuit pub fn emulated_witness>( @@ -542,6 +550,44 @@ impl PlonkCircuit { Ok(c) } + /// Obtain an emulated variable of the conditional selection from 2 emulated + /// variables. `b` is a boolean variable that indicates selection of P_b + /// from (P0, P1). + /// Return error if invalid input parameters are provided. + pub fn conditional_select_emulated>( + &mut self, + b: BoolVar, + p0: &EmulatedVariable, + p1: &EmulatedVariable, + ) -> Result, CircuitError> { + self.check_var_bound(b.into())?; + self.check_vars_bound(&p0.0[..])?; + self.check_vars_bound(&p1.0[..])?; + + let mut vals = vec![]; + for (&x_0, &x_1) in p0.0.iter().zip(p1.0.iter()) { + let selected = self.conditional_select(b, x_0, x_1)?; + vals.push(selected); + } + + Ok(EmulatedVariable::(vals, PhantomData::)) + } + + /// Constrain two emulated variables to be the same. + /// Return error if the input variables are invalid. + pub fn enforce_emulated_var_equal>( + &mut self, + p0: &EmulatedVariable, + p1: &EmulatedVariable, + ) -> Result<(), CircuitError> { + self.check_vars_bound(&p0.0[..])?; + self.check_vars_bound(&p1.0[..])?; + for (&a, &b) in p0.0.iter().zip(p1.0.iter()) { + self.enforce_equal(a, b)?; + } + Ok(()) + } + /// Given an emulated field element `a`, return `a mod F::MODULUS` in the /// native field. fn mod_to_native_field>( @@ -709,4 +755,51 @@ mod tests { .check_circuit_satisfiability(&from_emulated_field(x)) .is_err()); } + + #[test] + fn test_select() { + test_select_helper::(); + test_select_helper::(); + } + + fn test_select_helper() + where + E: EmulationConfig, + F: PrimeField, + { + let mut circuit = PlonkCircuit::::new_turbo_plonk(); + let var_x = circuit.create_emulated_variable(E::one()).unwrap(); + let overflow = E::from(E::MODULUS.into() - 1u64); + let var_y = circuit.create_emulated_variable(overflow).unwrap(); + let b = circuit.create_boolean_variable(true).unwrap(); + let var_z = circuit + .conditional_select_emulated(b, &var_x, &var_y) + .unwrap(); + assert_eq!(circuit.emulated_witness(&var_z).unwrap(), overflow); + assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); + *circuit.witness_mut(var_z.0[0]) = F::zero(); + assert!(circuit.check_circuit_satisfiability(&[]).is_err()); + } + + #[test] + fn test_enforce_equal() { + test_enforce_equal_helper::(); + test_enforce_equal_helper::(); + } + + fn test_enforce_equal_helper() + where + E: EmulationConfig, + F: PrimeField, + { + let mut circuit = PlonkCircuit::::new_turbo_plonk(); + let var_x = circuit.create_emulated_variable(E::one()).unwrap(); + let overflow = E::from(E::MODULUS.into() - 1u64); + let var_y = circuit.create_emulated_variable(overflow).unwrap(); + let var_z = circuit.create_emulated_variable(overflow).unwrap(); + circuit.enforce_emulated_var_equal(&var_y, &var_z).unwrap(); + assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); + circuit.enforce_emulated_var_equal(&var_x, &var_y).unwrap(); + assert!(circuit.check_circuit_satisfiability(&[]).is_err()); + } }