diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fbc233d8..3968a2483 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and follow [semantic versioning](https://semver.org/) for our releases. - [#271](https://github.com/EspressoSystems/jellyfish/pull/271) Serde support for Aggregateable signatures - [#291](https://github.com/EspressoSystems/jellyfish/pull/291) Non-native field operations and elliptic curve addition - [#309](https://github.com/EspressoSystems/jellyfish/pull/309) Reed-Solomon decoder accept FFT domain +- [#320](https://github.com/EspressoSystems/jellyfish/pull/320) Non-native elliptic curve addition in short Weierstrass form ### Changed diff --git a/plonk/examples/proof_of_exp.rs b/plonk/examples/proof_of_exp.rs index aac039490..526c4cc71 100644 --- a/plonk/examples/proof_of_exp.rs +++ b/plonk/examples/proof_of_exp.rs @@ -25,7 +25,7 @@ use jf_plonk::{ proof_system::{PlonkKzgSnark, UniversalSNARK}, transcript::StandardTranscript, }; -use jf_relation::{gadgets::ecc::Point, Arithmetization, Circuit, PlonkCircuit}; +use jf_relation::{gadgets::ecc::TEPoint, Arithmetization, Circuit, PlonkCircuit}; use jf_utils::fr_to_fq; use rand_chacha::ChaCha20Rng; @@ -124,11 +124,11 @@ where // The next variable is a public constant: generator `G`. // We need to convert the point to Jellyfish's own `Point` struct. - let G_jf: Point = G.into(); + let G_jf: TEPoint = G.into(); let G_var = circuit.create_constant_point_variable(G_jf)?; // The last variable is a public variable `X`. - let X_jf: Point = X.into(); + let X_jf: TEPoint = X.into(); let X_var = circuit.create_public_point_variable(X_jf)?; // Step 3: diff --git a/plonk/src/circuit/plonk_verifier/mod.rs b/plonk/src/circuit/plonk_verifier/mod.rs index 8ceb054f3..44d7199f8 100644 --- a/plonk/src/circuit/plonk_verifier/mod.rs +++ b/plonk/src/circuit/plonk_verifier/mod.rs @@ -17,7 +17,7 @@ use jf_primitives::rescue::RescueParameter; use jf_relation::{ errors::{CircuitError, CircuitError::ParameterError}, gadgets::{ - ecc::{MultiScalarMultiplicationCircuit, Point, PointVariable, SWToTEConParam}, + ecc::{MultiScalarMultiplicationCircuit, PointVariable, SWToTEConParam, TEPoint}, ultraplonk::mod_arith::{FpElem, FpElemVar}, }, Circuit, PlonkCircuit, Variable, @@ -65,12 +65,12 @@ impl VerifyingKeyVar { let sigma_comms = verify_key .sigma_comms .iter() - .map(|comm| circuit.create_point_variable(Point::from(&comm.0))) + .map(|comm| circuit.create_point_variable(TEPoint::from(comm.0))) .collect::, CircuitError>>()?; let selector_comms = verify_key .selector_comms .iter() - .map(|comm| circuit.create_point_variable(Point::from(&comm.0))) + .map(|comm| circuit.create_point_variable(TEPoint::from(comm.0))) .collect::, CircuitError>>()?; Ok(Self { sigma_comms, @@ -149,8 +149,8 @@ impl VerifyingKeyVar { /// The public inputs are already in the form of FpElemVars. pub fn partial_verify_circuit( circuit: &mut PlonkCircuit, - beta_g: &Point, - generator_g: &Point, + beta_g: &TEPoint, + generator_g: &TEPoint, merged_vks: &[Self], shared_public_input_vars: &[FpElemVar], batch_proof: &BatchProofVar, @@ -334,7 +334,7 @@ mod test { use ark_std::{vec, UniformRand}; use jf_primitives::rescue::RescueParameter; use jf_relation::{ - gadgets::{ecc::Point, test_utils::test_variable_independence_for_circuit}, + gadgets::{ecc::TEPoint, test_utils::test_variable_independence_for_circuit}, Circuit, MergeableCircuitType, }; use jf_utils::{field_switching, test_rng}; @@ -445,11 +445,11 @@ mod test { P: SWParam, { for (comm_var, comm) in vk_var.sigma_comms.iter().zip(vk.sigma_comms.iter()) { - let expected_comm = Point::from(&comm.0); + let expected_comm = TEPoint::from(comm.0); assert_eq!(circuit.point_witness(comm_var).unwrap(), expected_comm); } for (comm_var, comm) in vk_var.selector_comms.iter().zip(vk.selector_comms.iter()) { - let expected_comm = Point::from(&comm.0); + let expected_comm = TEPoint::from(comm.0); assert_eq!(circuit.point_witness(comm_var).unwrap(), expected_comm); } assert_eq!(vk_var.is_merged, vk.is_merged); @@ -555,11 +555,11 @@ mod test { ); assert_eq!( circuit.point_witness(&partial_verify_points.0)?, - Point::::from(&inner1.into_affine()) + TEPoint::::from(inner1.into_affine()) ); assert_eq!( circuit.point_witness(&partial_verify_points.1)?, - Point::::from(&inner2.into_affine()) + TEPoint::::from(inner2.into_affine()) ); // ark_std::println!("#variables: {}", circuit.num_vars()); @@ -650,11 +650,11 @@ mod test { ); assert_ne!( circuit.point_witness(&partial_verify_points.0)?, - Point::::from(&inner1.into_affine()) + TEPoint::::from(inner1.into_affine()) ); assert_ne!( circuit.point_witness(&partial_verify_points.1)?, - Point::::from(&inner2.into_affine()) + TEPoint::::from(inner2.into_affine()) ); // wrong shared input and circuit input @@ -673,11 +673,11 @@ mod test { ); assert_ne!( circuit.point_witness(&partial_verify_points.0)?, - Point::::from(&inner1.into_affine()) + TEPoint::::from(inner1.into_affine()) ); assert_ne!( circuit.point_witness(&partial_verify_points.1)?, - Point::::from(&inner2.into_affine()) + TEPoint::::from(inner2.into_affine()) ); } } @@ -719,8 +719,8 @@ mod test { // proof let batch_proof_vars = (*batch_proof).create_variables(&mut circuit, m, two_power_m)?; - let beta_g: Point = beta_g_ref.into(); - let generator_g = &generator_g.into(); + let beta_g: TEPoint = (*beta_g_ref).into(); + let generator_g = &(*generator_g).into(); let blinding_factor_var = circuit.create_variable(field_switching(blinding_factor))?; let partial_verify_points = VerifyingKeyVar::partial_verify_circuit( diff --git a/plonk/src/circuit/transcript.rs b/plonk/src/circuit/transcript.rs index ddb3116f6..ef17cb52d 100644 --- a/plonk/src/circuit/transcript.rs +++ b/plonk/src/circuit/transcript.rs @@ -232,7 +232,7 @@ mod tests { }; use ark_std::{format, UniformRand}; use jf_primitives::pcs::prelude::{Commitment, UnivariateVerifierParam}; - use jf_relation::gadgets::ecc::Point; + use jf_relation::gadgets::ecc::TEPoint; use jf_utils::{bytes_to_field_elements, field_switching, test_rng}; const RANGE_BIT_LEN_FOR_TEST: usize = 16; @@ -349,7 +349,7 @@ mod tests { let mut sigma_comms_vars: Vec = Vec::new(); for e in sigma_comms.iter() { // convert point into TE form - let p: Point = (&e.0).into(); + let p: TEPoint = e.0.into(); sigma_comms_vars.push(circuit.create_point_variable(p).unwrap()); } @@ -360,7 +360,7 @@ mod tests { let mut selector_comms_vars: Vec = Vec::new(); for e in selector_comms.iter() { // convert point into TE form - let p: Point = (&e.0).into(); + let p: TEPoint = e.0.into(); selector_comms_vars.push(circuit.create_point_variable(p).unwrap()); } diff --git a/plonk/src/proof_system/structs.rs b/plonk/src/proof_system/structs.rs index c6e69a101..01ad8e9bc 100644 --- a/plonk/src/proof_system/structs.rs +++ b/plonk/src/proof_system/structs.rs @@ -39,7 +39,7 @@ use jf_primitives::{ use jf_relation::{ constants::{compute_coset_representatives, GATE_WIDTH, N_TURBO_PLONK_SELECTORS}, gadgets::{ - ecc::{Point, SWToTEConParam}, + ecc::{SWToTEConParam, TEPoint}, ultraplonk::mod_arith::FpElemVar, }, PlonkCircuit, @@ -358,14 +358,14 @@ impl BatchProof { for e in self.wires_poly_comms_vec.iter() { let mut tmp = Vec::new(); for f in e.iter() { - let p: Point = (&f.0).into(); + let p: TEPoint = f.0.into(); tmp.push(circuit.create_point_variable(p)?); } wires_poly_comms_vec.push(tmp); } let mut prod_perm_poly_comms_vec = Vec::new(); for e in self.prod_perm_poly_comms_vec.iter() { - let p: Point = (&e.0).into(); + let p: TEPoint = e.0.into(); prod_perm_poly_comms_vec.push(circuit.create_point_variable(p)?); } @@ -377,14 +377,14 @@ impl BatchProof { let mut split_quot_poly_comms = Vec::new(); for e in self.split_quot_poly_comms.iter() { - let p: Point = (&e.0).into(); + let p: TEPoint = e.0.into(); split_quot_poly_comms.push(circuit.create_point_variable(p)?); } - let p: Point = (&self.opening_proof.0).into(); + let p: TEPoint = self.opening_proof.0.into(); let opening_proof = circuit.create_point_variable(p)?; - let p: Point = (&self.shifted_opening_proof.0).into(); + let p: TEPoint = self.shifted_opening_proof.0.into(); let shifted_opening_proof = circuit.create_point_variable(p)?; Ok(BatchProofVar { @@ -728,12 +728,12 @@ where pub fn convert_te_coordinates_to_scalars(&self) -> Vec { let mut res = vec![]; for sigma_comm in self.sigma_comms.iter() { - let point: Point = (&sigma_comm.0).into(); + let point: TEPoint = sigma_comm.0.into(); res.push(point.get_x()); res.push(point.get_y()); } for selector_comm in self.selector_comms.iter() { - let point: Point = (&selector_comm.0).into(); + let point: TEPoint = selector_comm.0.into(); res.push(point.get_x()); res.push(point.get_y()); } diff --git a/plonk/src/transcript/rescue.rs b/plonk/src/transcript/rescue.rs index 5e8491623..c5fad2024 100644 --- a/plonk/src/transcript/rescue.rs +++ b/plonk/src/transcript/rescue.rs @@ -20,7 +20,7 @@ use jf_primitives::{ pcs::prelude::Commitment, rescue::{RescueParameter, STATE_SIZE}, }; -use jf_relation::gadgets::ecc::{Point, SWToTEConParam}; +use jf_relation::gadgets::ecc::{SWToTEConParam, TEPoint}; use jf_utils::{bytes_to_field_elements, field_switching, fq_to_fr_with_mask}; /// Transcript with rescue hash function. @@ -75,14 +75,14 @@ where // selector commitments for com in vk.selector_comms.iter() { // convert the SW form commitments into TE form - let te_point: Point = (&com.0).into(); + let te_point: TEPoint = com.0.into(); self.transcript.push(te_point.get_x()); self.transcript.push(te_point.get_y()); } // sigma commitments for com in vk.sigma_comms.iter() { // convert the SW form commitments into TE form - let te_point: Point = (&com.0).into(); + let te_point: TEPoint = com.0.into(); self.transcript.push(te_point.get_x()); self.transcript.push(te_point.get_y()); } @@ -116,7 +116,7 @@ where P: SWParam, { // convert the SW form commitments into TE form - let te_point: Point = (&comm.0).into(); + let te_point: TEPoint = comm.0.into(); // push the x and y coordinate of comm (in twisted // edwards form) to the transcript diff --git a/primitives/src/circuit/elgamal.rs b/primitives/src/circuit/elgamal.rs index bdeb311d7..49f807eaf 100644 --- a/primitives/src/circuit/elgamal.rs +++ b/primitives/src/circuit/elgamal.rs @@ -19,7 +19,7 @@ use ark_ff::PrimeField; use ark_std::{vec, vec::Vec}; use jf_relation::{ errors::CircuitError, - gadgets::ecc::{Point, PointVariable}, + gadgets::ecc::{PointVariable, TEPoint}, Circuit, PlonkCircuit, Variable, }; use jf_utils::compute_len_to_next_multiple; @@ -224,7 +224,7 @@ where } fn create_enc_key_variable(&mut self, pk: &EncKey

) -> Result { - let point = Point::from(pk.key.into_affine()); + let point = TEPoint::from(pk.key.into_affine()); let point_variable = self.create_point_variable(point)?; Ok(EncKeyVars(point_variable)) } @@ -234,7 +234,7 @@ where ctxts: &Ciphertext

, ) -> Result { let ephemeral = - self.create_point_variable(Point::from(ctxts.ephemeral.key.into_affine()))?; + self.create_point_variable(TEPoint::from(ctxts.ephemeral.key.into_affine()))?; let symm_ctxts = ctxts .data .iter() @@ -264,7 +264,7 @@ mod tests { use ark_ed_on_bn254::{EdwardsConfig as ParamEd254, Fq as FqEd254}; use ark_ff::UniformRand; use ark_std::{vec, vec::Vec}; - use jf_relation::{gadgets::ecc::Point, Circuit, PlonkCircuit, Variable}; + use jf_relation::{gadgets::ecc::TEPoint, Circuit, PlonkCircuit, Variable}; use jf_utils::fr_to_fq; #[test] @@ -379,7 +379,7 @@ mod tests { // Check ciphertexts assert_eq!( - Point::from(ctxts.ephemeral.key.into_affine()), + TEPoint::from(ctxts.ephemeral.key.into_affine()), circuit.point_witness(&ctxts_vars.ephemeral).unwrap() ); @@ -433,7 +433,7 @@ mod tests { let ctxts_var = circuit.create_ciphertext_variable(&ctxts).unwrap(); // Check ciphertexts assert_eq!( - Point::from(ctxts.ephemeral.key.into_affine()), + TEPoint::from(ctxts.ephemeral.key.into_affine()), circuit.point_witness(&ctxts_var.ephemeral).unwrap() ); for (ctxt, ctxt_var) in ctxts.data.iter().zip(ctxts_var.symm_ctxts) { diff --git a/primitives/src/circuit/signature/schnorr.rs b/primitives/src/circuit/signature/schnorr.rs index d6ec96a0e..8fddbeee5 100644 --- a/primitives/src/circuit/signature/schnorr.rs +++ b/primitives/src/circuit/signature/schnorr.rs @@ -21,7 +21,7 @@ use ark_ff::PrimeField; use ark_std::{vec, vec::Vec}; use jf_relation::{ errors::CircuitError, - gadgets::ecc::{Point, PointVariable}, + gadgets::ecc::{PointVariable, TEPoint}, BoolVar, Circuit, PlonkCircuit, Variable, }; use jf_utils::fr_to_fq; @@ -123,13 +123,13 @@ where ) -> Result { let sig_var = SignatureVar { s: self.create_variable(fr_to_fq::(&sig.s))?, - R: self.create_point_variable(Point::from(sig.R))?, + R: self.create_point_variable(TEPoint::from(sig.R))?, }; Ok(sig_var) } fn create_signature_vk_variable(&mut self, vk: &VerKey

) -> Result { - let vk_var = VerKeyVar(self.create_point_variable(Point::from(vk.0))?); + let vk_var = VerKeyVar(self.create_point_variable(TEPoint::from(vk.0))?); Ok(vk_var) } diff --git a/relation/src/constraint_system.rs b/relation/src/constraint_system.rs index 091840bd1..4e8796209 100644 --- a/relation/src/constraint_system.rs +++ b/relation/src/constraint_system.rs @@ -109,6 +109,14 @@ pub trait Circuit { /// Add a public input variable; return the index of the variable. fn create_public_variable(&mut self, val: F) -> Result; + /// Add a public bool variable to the circuit; return the index of the + /// variable. + fn create_public_boolean_variable(&mut self, val: bool) -> Result { + let val_scalar = if val { F::one() } else { F::zero() }; + let var = self.create_public_variable(val_scalar)?; + Ok(BoolVar(var)) + } + /// Set a variable to a public variable fn set_variable_public(&mut self, var: Variable) -> Result<(), CircuitError>; diff --git a/relation/src/gadgets/arithmetic.rs b/relation/src/gadgets/arithmetic.rs index 843a440f3..6e00ed165 100644 --- a/relation/src/gadgets/arithmetic.rs +++ b/relation/src/gadgets/arithmetic.rs @@ -11,8 +11,8 @@ use crate::{ constants::{GATE_WIDTH, N_MUL_SELECTORS}, errors::CircuitError, gates::{ - ArithmeticGate, ConstantAdditionGate, ConstantMultiplicationGate, FifthRootGate, - LinCombGate, MulAddGate, QuadPolyGate, + ConstantAdditionGate, ConstantMultiplicationGate, FifthRootGate, LinCombGate, MulAddGate, + QuadPolyGate, }, Circuit, PlonkCircuit, Variable, }; @@ -498,62 +498,6 @@ impl PlonkCircuit { Ok(()) } - - /// Constrain a general arithmetic gate: - /// q1 * a + q2 * b + q3 * c + q4 * d + mul1 * a * b + mul2 * c * d + - /// constant = y - pub fn general_arithmetic_gate( - &mut self, - wires: &[Variable; GATE_WIDTH + 1], - lc_coeffs: &[F; GATE_WIDTH], - mul_coeffs: &[F; N_MUL_SELECTORS], - constant: F, - ) -> Result<(), CircuitError> { - self.check_vars_bound(wires)?; - - self.insert_gate( - wires, - Box::new(ArithmeticGate { - lc_coeffs: *lc_coeffs, - mul_coeffs: *mul_coeffs, - constant, - }), - )?; - Ok(()) - } - - /// Obtain a variable representing a general arithmetic formula. - /// Return error if variables are invalid. - pub fn general_arithmetic( - &mut self, - wires_in: &[Variable; GATE_WIDTH], - lc_coeffs: &[F; GATE_WIDTH], - mul_coeffs: &[F; N_MUL_SELECTORS], - constant: F, - ) -> Result { - self.check_vars_bound(wires_in)?; - - let vals_in: Vec = wires_in - .iter() - .map(|&var| self.witness(var)) - .collect::, CircuitError>>()?; - - // calculate y as the linear combination of coeffs and vals_in - let mut y_val = vals_in - .iter() - .zip(lc_coeffs.iter()) - .map(|(&val, &coeff)| val * coeff) - .sum(); - y_val += mul_coeffs[0] * vals_in[0] * vals_in[1] - + mul_coeffs[1] * vals_in[2] * vals_in[3] - + constant; - - let y = self.create_variable(y_val)?; - - let wires = [wires_in[0], wires_in[1], wires_in[2], wires_in[3], y]; - self.general_arithmetic_gate(&wires, lc_coeffs, mul_coeffs, constant)?; - Ok(y) - } } #[cfg(test)] diff --git a/relation/src/gadgets/ecc/conversion.rs b/relation/src/gadgets/ecc/conversion.rs index 0ef9208a8..b480bdd83 100644 --- a/relation/src/gadgets/ecc/conversion.rs +++ b/relation/src/gadgets/ecc/conversion.rs @@ -15,16 +15,16 @@ //! the other form is public. In practice a prover will convert all of the //! points to the TE form and work on the TE form inside the circuits. -use super::Point; +use super::TEPoint; use ark_ec::short_weierstrass::{Affine as SWAffine, SWCurveConfig as SWParam}; use ark_ff::{BigInteger256, BigInteger384, BigInteger768, PrimeField}; -impl From<&SWAffine

> for Point +impl From> for TEPoint where F: PrimeField + SWToTEConParam, P: SWParam, { - fn from(p: &SWAffine

) -> Self { + fn from(p: SWAffine

) -> Self { // this function is only correct for BLS12-377 // (other curves does not impl an SW form) @@ -53,7 +53,7 @@ where let edwards_x = beta * montgomery_x / montgomery_y; let edwards_y = (montgomery_x - F::one()) / (montgomery_x + F::one()); - Point(edwards_x, edwards_y) + TEPoint(edwards_x, edwards_y) } } @@ -141,7 +141,7 @@ mod test { // a helper function to check if a point is on the ed curve // of bls12-377 G1 - fn is_on_bls12_377_ed_curve(p: &Point) -> bool { + fn is_on_bls12_377_ed_curve(p: &TEPoint) -> bool { // Twisted Edwards curve 2: a * x² + y² = 1 + d * x² * y² let a = MontFp!("-1"); let d = MontFp!("122268283598675559488486339158635529096981886914877139579534153582033676785385790730042363341236035746924960903179"); @@ -161,20 +161,20 @@ mod test { let mut rng = test_rng(); // test generator - let g1 = &G1Affine::generator(); - let p: Point = g1.into(); + let g1 = G1Affine::generator(); + let p: TEPoint = g1.into(); assert!(is_on_bls12_377_ed_curve(&p)); // test zero point - let g1 = &G1Affine::zero(); - let p: Point = g1.into(); + let g1 = G1Affine::zero(); + let p: TEPoint = g1.into(); assert_eq!(p.0, Fq377::zero()); assert_eq!(p.1, Fq377::one()); assert!(is_on_bls12_377_ed_curve(&p)); // test a random group element - let g1 = &G1Projective::rand(&mut rng).into_affine(); - let p: Point = g1.into(); + let g1 = G1Projective::rand(&mut rng).into_affine(); + let p: TEPoint = g1.into(); assert!(is_on_bls12_377_ed_curve(&p)); } } diff --git a/relation/src/gadgets/ecc/emulated/mod.rs b/relation/src/gadgets/ecc/emulated/mod.rs new file mode 100644 index 000000000..5755d7661 --- /dev/null +++ b/relation/src/gadgets/ecc/emulated/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2022 Espresso Systems (espressosys.com) +// This file is part of the Jellyfish library. + +// You should have received a copy of the MIT License +// along with the Jellyfish library. If not, see . + +//! Elliptic curve related gates and gadgets for non-native fields + +mod short_weierstrass; +mod twisted_edwards; + +pub use short_weierstrass::*; +pub use twisted_edwards::*; diff --git a/relation/src/gadgets/ecc/emulated/short_weierstrass.rs b/relation/src/gadgets/ecc/emulated/short_weierstrass.rs new file mode 100644 index 000000000..793a0c398 --- /dev/null +++ b/relation/src/gadgets/ecc/emulated/short_weierstrass.rs @@ -0,0 +1,422 @@ +// Copyright (c) 2022 Espresso Systems (espressosys.com) +// This file is part of the Jellyfish library. + +// You should have received a copy of the MIT License +// along with the Jellyfish library. If not, see . + +//! Short Weierstrass curve point addition + +use crate::{ + errors::CircuitError, + gadgets::{from_emulated_field, EmulatedVariable, EmulationConfig, SerializableEmulatedStruct}, + BoolVar, Circuit, PlonkCircuit, +}; +use ark_ec::short_weierstrass::{Affine, SWCurveConfig}; +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{vec, vec::Vec}; + +/// An elliptic curve point in short Weierstrass affine form (x, y, infinity). +#[derive(Debug, Eq, PartialEq, Copy, Clone, CanonicalSerialize, CanonicalDeserialize)] +pub struct SWPoint(pub F, pub F, pub bool); + +impl From> for SWPoint +where + F: PrimeField, + P: SWCurveConfig, +{ + fn from(p: Affine

) -> Self { + SWPoint(p.x, p.y, p.infinity) + } +} + +impl SerializableEmulatedStruct for SWPoint +where + E: EmulationConfig, + F: PrimeField, +{ + fn serialize_to_native_elements(&self) -> Vec { + let mut result = from_emulated_field(self.0); + result.extend(from_emulated_field(self.1)); + result.push(if self.2 { F::one() } else { F::zero() }); + result + } +} + +/// The variable represents an SW point in the emulated field. +#[derive(Debug, Clone)] +pub struct EmulatedSWPointVariable( + pub EmulatedVariable, + pub EmulatedVariable, + pub BoolVar, +); + +impl PlonkCircuit { + /// Return the witness point + pub fn emulated_sw_point_witness>( + &self, + point_var: &EmulatedSWPointVariable, + ) -> Result, CircuitError> { + let x = self.emulated_witness(&point_var.0)?; + let y = self.emulated_witness(&point_var.1)?; + let infinity = self.witness(point_var.2 .0)? == F::one(); + Ok(SWPoint(x, y, infinity)) + } + + /// Add a new emulated EC point (as witness) + pub fn create_emulated_sw_point_variable>( + &mut self, + point: SWPoint, + ) -> Result, CircuitError> { + let x = self.create_emulated_variable(point.0)?; + let y = self.create_emulated_variable(point.1)?; + let infinity = self.create_boolean_variable(point.2)?; + Ok(EmulatedSWPointVariable(x, y, infinity)) + } + + /// Add a new constant emulated EC point + pub fn create_constant_emulated_sw_point_variable>( + &mut self, + point: SWPoint, + ) -> Result, CircuitError> { + let x = self.create_constant_emulated_variable(point.0)?; + let y = self.create_constant_emulated_variable(point.1)?; + let infinity = BoolVar(if point.2 { self.one() } else { self.zero() }); + Ok(EmulatedSWPointVariable(x, y, infinity)) + } + + /// Add a new public emulated EC point + pub fn create_public_emulated_sw_point_variable>( + &mut self, + point: SWPoint, + ) -> Result, CircuitError> { + let x = self.create_public_emulated_variable(point.0)?; + let y = self.create_public_emulated_variable(point.1)?; + let infinity = self.create_public_boolean_variable(point.2)?; + Ok(EmulatedSWPointVariable(x, y, infinity)) + } + + /// 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_sw_point_vars_select>( + &mut self, + b: BoolVar, + p0: &EmulatedSWPointVariable, + p1: &EmulatedSWPointVariable, + ) -> Result, CircuitError> { + let select_x = self.conditional_select_emulated(b, &p0.0, &p1.0)?; + let select_y = self.conditional_select_emulated(b, &p0.1, &p1.1)?; + let select_infinity = BoolVar(self.conditional_select(b, p0.2 .0, p1.2 .0)?); + + Ok(EmulatedSWPointVariable::( + select_x, + select_y, + select_infinity, + )) + } + + /// Constrain two emulated point variables to be the same. + /// Return error if the input point variables are invalid. + pub fn enforce_emulated_sw_point_equal>( + &mut self, + p0: &EmulatedSWPointVariable, + p1: &EmulatedSWPointVariable, + ) -> Result<(), CircuitError> { + self.enforce_emulated_var_equal(&p0.0, &p1.0)?; + self.enforce_emulated_var_equal(&p0.1, &p1.1)?; + self.enforce_equal(p0.2 .0, p1.2 .0)?; + Ok(()) + } + + /// Obtain a bool variable representing whether two input emulated point + /// variables are equal. Return error if variables are invalid. + pub fn is_emulated_sw_point_equal>( + &mut self, + p0: &EmulatedSWPointVariable, + p1: &EmulatedSWPointVariable, + ) -> Result { + let mut r0 = self.is_emulated_var_equal(&p0.0, &p1.0)?; + let r1 = self.is_emulated_var_equal(&p0.1, &p1.1)?; + let r2 = self.is_equal(p0.2 .0, p1.2 .0)?; + r0.0 = self.mul(r0.0, r1.0)?; + r0.0 = self.mul(r0.0, r2.0)?; + Ok(r0) + } + + /// Constrain variable `p2` to be the point addition of `p0` and + /// `p1` over an elliptic curve. + /// Let p0 = (x0, y0, inf0), p1 = (x1, y1, inf1), p2 = (x2, y2, inf2) + /// The addition formula for affine points of sw curve is + /// If either p0 or p1 is infinity, then p2 equals to another point. + /// 1. if p0 == p1 + /// - if y0 == 0 then inf2 = 1 + /// - Calculate s = (3 * x0^2 + a) / (2 * y0) + /// - x2 = s^2 - x0 - x1 + /// - y2 = s(x0 - x2) - y0 + /// 2. Otherwise + /// - if x0 == x1 then inf2 = 1 + /// - Calculate s = (y0 - y1) / (x0 - x1) + /// - x2 = s^2 - x0 - x1 + /// - y2 = s(x0 - x2) - y0 + /// The first case is equivalent to the following: + /// - inf0 == 1 || inf1 == 1 || x0 != x1 || y0 != y1 || y0 != 0 || inf2 == 0 + /// - (x0 + x1 + x2) * (y0 + y0)^2 == (3 * x0^2 + a)^2 + /// - (y2 + y0) * (y0 + y0) == (3 * x0^2 + a) (x0 - x2) + /// The second case is equivalent to the following: + /// - inf0 == 1 || inf1 == 1 || x0 != x1 || y0 == y1 || inf2 == 0 + /// - (x0 - x1)^2 (x0 + x1 + x2) == (y0 - y1)^2 + /// - (x0 - x2) (y0 - y1) == (y0 + y2) (x0 - x1) + /// First check in both cases can be combined into the following: + /// inf0 == 1 || inf1 == 1 || inf2 == 0 || x0 != x1 || (y0 == y1 && y0 != 0) + /// For the rest equality checks, + /// - Both LHS and RHS must be multiplied with an indicator variable + /// (!inf0 && !inf1). So that if either p0 or p1 is infinity, those + /// checks will trivially pass. + /// - For the first case (point doubling), both LHS and RHS must be + /// multiplied with an indicator variable (y0 != 0 && x0 == x1 && y1 == + /// y0). So that when y0 == 0 || x0 != x1 || y0 != y1, these checks will + /// trivially pass. + /// - For the second case, both LHS and RHS must be multiplied with (x0 - + /// x1). So that when x0 == x1, these checks will trivially pass. + pub fn emulated_sw_ecc_add_gate>( + &mut self, + p0: &EmulatedSWPointVariable, + p1: &EmulatedSWPointVariable, + p2: &EmulatedSWPointVariable, + a: E, + ) -> Result<(), CircuitError> { + let eq_p1_p2 = self.is_emulated_sw_point_equal(p1, p2)?; + let eq_p0_p2 = self.is_emulated_sw_point_equal(p0, p2)?; + // Case 1: either p0 or p1 is infinity + self.enforce_equal(p0.2 .0, eq_p1_p2.0)?; + self.enforce_equal(p1.2 .0, eq_p0_p2.0)?; + + // infinity_mark is 1 iff either p0 or p1 is infinity + let infinity_mark = self.logic_or(p0.2, p1.2)?; + // is 1 iff both p0 and p1 are not infinity + let non_infinity_mark = self.logic_neg(infinity_mark)?; + + // Case 2: p2 is infinity, while p0 and p1 are not. + // inf0 == 1 || inf1 == 1 || inf2 == 0 || x0 != x1 || (y0 == y1 && y0 != 0) + let non_inf_p2 = self.logic_neg(p2.2)?; + let eq_x0_x1 = self.is_emulated_var_equal(&p0.0, &p1.0)?; + let neq_x0_x1 = self.logic_neg(eq_x0_x1)?; + let eq_y0_y1 = self.is_emulated_var_equal(&p0.1, &p1.1)?; + let is_y0_zero = self.is_emulated_var_zero(&p0.1)?; + let not_y0_zero = self.logic_neg(is_y0_zero)?; + let t = self.logic_and(eq_y0_y1, not_y0_zero)?; + let t = self.logic_or(neq_x0_x1, t)?; + let t = self.logic_or(non_inf_p2, t)?; + self.logic_or_gate(infinity_mark, t)?; + + // Case 3: point doubling + // doubling mark is 1 iff x0 == x1 and y0 == y1 + let doubling_mark = self.mul(eq_x0_x1.0, eq_y0_y1.0)?; + let doubling_coef = self.mul(doubling_mark, non_infinity_mark.0)?; + let doubling_coef = self.mul(doubling_coef, not_y0_zero.0)?; + // forcefully convert Variable into EmulatedVariable + // safe because it's boolean + let mut v = vec![self.zero(); E::NUM_LIMBS]; + v[0] = doubling_coef; + let doubling_coef = EmulatedVariable::(v, core::marker::PhantomData); + + // first equality (x0 + x1 + x2) * (y0 + y0)^2 == (3 * x1^2 + a)^2 + let y0_times_2 = self.emulated_add(&p0.1, &p0.1)?; + let x0_plus_x1 = self.emulated_add(&p0.0, &p1.0)?; + let x0_plus_x1_plus_x2 = self.emulated_add(&p2.0, &x0_plus_x1)?; + let lhs = self.emulated_mul(&x0_plus_x1_plus_x2, &y0_times_2)?; + let lhs = self.emulated_mul(&lhs, &y0_times_2)?; + // s = 3 * x1^2 + a + let s = self.emulated_mul(&p0.0, &p0.0)?; + let s = self.emulated_mul_constant(&s, E::from(3u64))?; + let s = self.emulated_add_constant(&s, a)?; + let rhs = self.emulated_mul(&s, &s)?; + + let lhs = self.emulated_mul(&lhs, &doubling_coef)?; + let rhs = self.emulated_mul(&rhs, &doubling_coef)?; + self.enforce_emulated_var_equal(&lhs, &rhs)?; + + // second equality (y2 + y0) * (y0 + y0) == (3 * x1^2 + a) (x0 - x2) + let y2_plus_y0 = self.emulated_add(&p2.1, &p0.1)?; + let lhs = self.emulated_mul(&y2_plus_y0, &y0_times_2)?; + let x0_minus_x2 = self.emulated_sub(&p0.0, &p2.0)?; + let rhs = self.emulated_mul(&s, &x0_minus_x2)?; + + let lhs = self.emulated_mul(&lhs, &doubling_coef)?; + let rhs = self.emulated_mul(&rhs, &doubling_coef)?; + self.enforce_emulated_var_equal(&lhs, &rhs)?; + + // Case 4: point addition + let coef = self.mul(non_infinity_mark.0, neq_x0_x1.0)?; + // forcefully convert Variable into EmulatedVariable + // safe because it's boolean + let mut v = vec![self.zero(); E::NUM_LIMBS]; + v[0] = coef; + let add_coef = EmulatedVariable::(v, core::marker::PhantomData); + + // first equality (x0 - x1)^2 (x0 + x1 + x2) == (y0 - y1)^2 + let x0_minus_x1 = self.emulated_sub(&p0.0, &p1.0)?; + let lhs = self.emulated_mul(&x0_minus_x1, &x0_minus_x1)?; + let lhs = self.emulated_mul(&lhs, &x0_plus_x1_plus_x2)?; + let y0_minus_y1 = self.emulated_sub(&p0.1, &p1.1)?; + let rhs = self.emulated_mul(&y0_minus_y1, &y0_minus_y1)?; + + let lhs = self.emulated_mul(&lhs, &add_coef)?; + let rhs = self.emulated_mul(&rhs, &add_coef)?; + self.enforce_emulated_var_equal(&lhs, &rhs)?; + + // second equality (x0 - x2) (y0 - y1) == (y0 + y2) (x0 - x1) + let lhs = self.emulated_mul(&x0_minus_x2, &y0_minus_y1)?; + let y0_plus_y2 = self.emulated_add(&p0.1, &p2.1)?; + let rhs = self.emulated_mul(&y0_plus_y2, &x0_minus_x1)?; + + let lhs = self.emulated_mul(&lhs, &add_coef)?; + let rhs = self.emulated_mul(&rhs, &add_coef)?; + self.enforce_emulated_var_equal(&lhs, &rhs)?; + + Ok(()) + } + + /// Obtain a variable to the point addition result of `p0` + `p1` + pub fn emulated_sw_ecc_add>( + &mut self, + p0: &EmulatedSWPointVariable, + p1: &EmulatedSWPointVariable, + a: E, + ) -> Result, CircuitError> { + let x0 = self.emulated_witness(&p0.0)?; + let y0 = self.emulated_witness(&p0.1)?; + let infinity0 = self.witness(p0.2 .0)? == F::one(); + let x1 = self.emulated_witness(&p1.0)?; + let y1 = self.emulated_witness(&p1.1)?; + let infinity1 = self.witness(p1.2 .0)? == F::one(); + let p2 = if infinity0 { + SWPoint(x1, y1, infinity1) + } else if infinity1 { + SWPoint(x0, y0, infinity0) + } else if x0 == x1 && y0 == y1 { + // point doubling + if y0.is_zero() { + SWPoint(E::zero(), E::zero(), true) + } else { + let s = (x0 * x0 * E::from(3u64) + a) / (y0 + y0); + let x2 = s * s - x0 - x1; + let y2 = s * (x0 - x2) - y0; + SWPoint(x2, y2, false) + } + } else { + // point addition + if x0 == x1 { + SWPoint(E::zero(), E::zero(), true) + } else { + let s = (y0 - y1) / (x0 - x1); + let x2 = s * s - x0 - x1; + let y2 = s * (x0 - x2) - y0; + SWPoint(x2, y2, false) + } + }; + let p2 = self.create_emulated_sw_point_variable(p2)?; + self.emulated_sw_ecc_add_gate(p0, p1, &p2, a)?; + Ok(p2) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + errors::CircuitError, + gadgets::{ecc::conversion::*, EmulationConfig, SerializableEmulatedStruct}, + Circuit, PlonkCircuit, + }; + use ark_bls12_377::{g1::Config as Param377, Fq as Fq377}; + use ark_bn254::{g1::Config as Param254, Fq as Fq254, Fr as Fr254}; + use ark_ec::{ + short_weierstrass::{Projective, SWCurveConfig}, + CurveGroup, Group, + }; + use ark_ff::{MontFp, PrimeField}; + use ark_std::{UniformRand, Zero}; + + use super::{EmulatedSWPointVariable, SWPoint}; + + #[test] + fn test_emulated_sw_point_addition() { + let a: Fq377 = MontFp!("0"); + test_emulated_sw_point_addition_helper::(a); + let a: Fq254 = MontFp!("0"); + test_emulated_sw_point_addition_helper::(a); + } + + fn ecc_add_and_check( + circuit: &mut PlonkCircuit, + p0: &EmulatedSWPointVariable, + p1: &EmulatedSWPointVariable, + a: E, + expected: &SWPoint, + ) -> Result, CircuitError> + where + E: EmulationConfig + SWToTEConParam, + F: PrimeField, + { + let result = circuit.emulated_sw_ecc_add(p0, p1, a)?; + assert_eq!(circuit.emulated_sw_point_witness(&result)?, *expected); + Ok(result) + } + + fn test_emulated_sw_point_addition_helper(a: E) + where + E: EmulationConfig + SWToTEConParam, + F: PrimeField, + P: SWCurveConfig, + { + let mut rng = jf_utils::test_rng(); + let neutral = Projective::

::zero().into_affine(); + let p1 = Projective::

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

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

::generator()) + .into_affine() + .into(); + + let mut circuit = PlonkCircuit::::new_ultra_plonk(20); + + let var_p1 = circuit + .create_public_emulated_sw_point_variable(p1.into()) + .unwrap(); + let var_p2 = circuit + .create_emulated_sw_point_variable(p2.into()) + .unwrap(); + let var_neutral = circuit + .create_emulated_sw_point_variable(neutral.into()) + .unwrap(); + ecc_add_and_check::(&mut circuit, &var_p1, &var_p2, a, &expected).unwrap(); + ecc_add_and_check::(&mut circuit, &var_p1, &var_neutral, a, &p1.into()).unwrap(); + ecc_add_and_check::(&mut circuit, &var_neutral, &var_p2, a, &p2.into()).unwrap(); + + // test point doubling + let double_p1 = (p1 + p1).into_affine().into(); + ecc_add_and_check::(&mut circuit, &var_p1, &var_p1, a, &double_p1).unwrap(); + ecc_add_and_check::(&mut circuit, &var_neutral, &var_neutral, a, &neutral.into()) + .unwrap(); + + let public_inputs: SWPoint = p1.into(); + let public_inputs = public_inputs.serialize_to_native_elements(); + let wrong_inputs: SWPoint = neutral.into(); + let wrong_inputs = wrong_inputs.serialize_to_native_elements(); + assert!(circuit.check_circuit_satisfiability(&public_inputs).is_ok()); + assert!(circuit.check_circuit_satisfiability(&wrong_inputs).is_err()); + + // fail path + let var_wrong_result = circuit + .create_emulated_sw_point_variable(wrong_result) + .unwrap(); + circuit + .emulated_sw_ecc_add_gate(&var_p1, &var_p2, &var_wrong_result, a) + .unwrap(); + assert!(circuit + .check_circuit_satisfiability(&public_inputs) + .is_err()); + } +} diff --git a/relation/src/gadgets/ecc/emulated/twisted_edwards.rs b/relation/src/gadgets/ecc/emulated/twisted_edwards.rs new file mode 100644 index 000000000..0766c9b22 --- /dev/null +++ b/relation/src/gadgets/ecc/emulated/twisted_edwards.rs @@ -0,0 +1,309 @@ +// Copyright (c) 2022 Espresso Systems (espressosys.com) +// This file is part of the Jellyfish library. + +// You should have received a copy of the MIT License +// along with the Jellyfish library. If not, see . + +//! Twisted Edwards curve point addition + +use crate::{ + errors::CircuitError, + gadgets::{ecc::TEPoint, EmulatedVariable, EmulationConfig}, + BoolVar, Circuit, PlonkCircuit, +}; +use ark_ff::PrimeField; + +/// The variable represents an TE point in the emulated field. +#[derive(Debug, Clone)] +pub struct EmulatedTEPointVariable(pub EmulatedVariable, pub EmulatedVariable); + +impl PlonkCircuit { + /// Return the witness point + pub fn emulated_te_point_witness>( + &self, + point_var: &EmulatedTEPointVariable, + ) -> Result, CircuitError> { + let x = self.emulated_witness(&point_var.0)?; + let y = self.emulated_witness(&point_var.1)?; + Ok(TEPoint(x, y)) + } + + /// Add a new emulated EC point (as witness) + pub fn create_emulated_te_point_variable>( + &mut self, + p: TEPoint, + ) -> Result, CircuitError> { + let x = self.create_emulated_variable(p.0)?; + let y = self.create_emulated_variable(p.1)?; + Ok(EmulatedTEPointVariable(x, y)) + } + + /// Add a new constant emulated EC point + pub fn create_constant_emulated_te_point_variable>( + &mut self, + p: TEPoint, + ) -> Result, CircuitError> { + let x = self.create_constant_emulated_variable(p.0)?; + let y = self.create_constant_emulated_variable(p.1)?; + Ok(EmulatedTEPointVariable(x, y)) + } + + /// Add a new public emulated EC point + pub fn create_public_emulated_te_point_variable>( + &mut self, + p: TEPoint, + ) -> Result, CircuitError> { + let x = self.create_public_emulated_variable(p.0)?; + let y = self.create_public_emulated_variable(p.1)?; + Ok(EmulatedTEPointVariable(x, y)) + } + + /// 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_te_point_vars_select>( + &mut self, + b: BoolVar, + p0: &EmulatedTEPointVariable, + p1: &EmulatedTEPointVariable, + ) -> Result, CircuitError> { + let select_x = self.conditional_select_emulated(b, &p0.0, &p1.0)?; + let select_y = self.conditional_select_emulated(b, &p0.1, &p1.1)?; + + Ok(EmulatedTEPointVariable::(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_te_point_equal>( + &mut self, + p0: &EmulatedTEPointVariable, + p1: &EmulatedTEPointVariable, + ) -> Result<(), CircuitError> { + self.enforce_emulated_var_equal(&p0.0, &p1.0)?; + self.enforce_emulated_var_equal(&p0.1, &p1.1)?; + Ok(()) + } + + /// Obtain a bool variable representing whether two input emulated point + /// variables are equal. Return error if variables are invalid. + pub fn is_emulated_te_point_equal>( + &mut self, + p0: &EmulatedTEPointVariable, + p1: &EmulatedTEPointVariable, + ) -> Result { + let mut r0 = self.is_emulated_var_equal(&p0.0, &p1.0)?; + let r1 = self.is_emulated_var_equal(&p0.1, &p1.1)?; + r0.0 = self.mul(r0.0, r1.0)?; + Ok(r0) + } + + /// Constrain variable `p2` to be the point addition of `p0` and + /// `p1` over an elliptic curve. + pub fn emulated_te_ecc_add_gate>( + &mut self, + p0: &EmulatedTEPointVariable, + p1: &EmulatedTEPointVariable, + p2: &EmulatedTEPointVariable, + d: E, + ) -> Result<(), CircuitError> { + let x0y1 = self.emulated_mul(&p0.0, &p1.1)?; + let x1y0 = self.emulated_mul(&p1.0, &p0.1)?; + let x0x1 = self.emulated_mul(&p0.0, &p1.0)?; + let y0y1 = self.emulated_mul(&p0.1, &p1.1)?; + let x0x1y0y1 = self.emulated_mul(&x0x1, &y0y1)?; + let dx0x1y0y1 = self.emulated_mul_constant(&x0x1y0y1, d)?; + + // checking that x2 = x0y1 + x1y0 - dx0y0x1y1x2 + // t1 = x0y1 + x1y0 + let t1 = self.emulated_add(&x0y1, &x1y0)?; + // t2 = d x0 y0 x1 y1 x2 + let t2 = self.emulated_mul(&dx0x1y0y1, &p2.0)?; + self.emulated_add_gate(&p2.0, &t2, &t1)?; + + // checking that y2 = x0x1 + y0y1 + dx0y0x1y1y2 + // t1 = x0x1 + y0y1 + let t1 = self.emulated_add(&x0x1, &y0y1)?; + let t2 = self.emulated_mul(&dx0x1y0y1, &p2.1)?; + self.emulated_add_gate(&t1, &t2, &p2.1) + } + + /// Obtain a variable to the point addition result of `a` + `b` + pub fn emulated_te_ecc_add>( + &mut self, + p0: &EmulatedTEPointVariable, + p1: &EmulatedTEPointVariable, + d: E, + ) -> Result, CircuitError> { + let x0 = self.emulated_witness(&p0.0)?; + let y0 = self.emulated_witness(&p0.1)?; + let x1 = self.emulated_witness(&p1.0)?; + let y1 = self.emulated_witness(&p1.1)?; + + let t1 = x0 * y1; + let t2 = x1 * y0; + let dx0x1y0y1 = d * t1 * t2; + + let x2 = (t1 + t2) / (E::one() + dx0x1y0y1); + let y2 = (x0 * x1 + y0 * y1) / (E::one() - dx0x1y0y1); + let p2 = self.create_emulated_te_point_variable(TEPoint(x2, y2))?; + self.emulated_te_ecc_add_gate(p0, p1, &p2, d)?; + Ok(p2) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + gadgets::{ + ecc::{conversion::*, TEPoint}, + EmulationConfig, + }, + Circuit, PlonkCircuit, + }; + use ark_bls12_377::{g1::Config as Param377, Fq as Fq377}; + use ark_bn254::Fr as Fr254; + use ark_ec::{ + short_weierstrass::{Projective, SWCurveConfig}, + CurveGroup, Group, + }; + use ark_ff::{MontFp, PrimeField}; + use ark_std::{UniformRand, Zero}; + + #[test] + fn test_emulated_te_point_addition() { + let d : Fq377 = MontFp!("122268283598675559488486339158635529096981886914877139579534153582033676785385790730042363341236035746924960903179"); + test_emulated_te_point_addition_helper::(d); + } + + fn test_emulated_te_point_addition_helper(d: E) + where + E: EmulationConfig + SWToTEConParam, + F: PrimeField, + P: SWCurveConfig, + { + let mut rng = jf_utils::test_rng(); + let neutral = Projective::

::zero().into_affine(); + let p1 = Projective::

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

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

::generator()) + .into_affine() + .into(); + let p1: TEPoint = p1.into(); + let p2: TEPoint = p2.into(); + + let mut circuit = PlonkCircuit::::new_turbo_plonk(); + + let var_p1 = circuit.create_emulated_te_point_variable(p1).unwrap(); + let var_p2 = circuit.create_emulated_te_point_variable(p2).unwrap(); + let var_result = circuit.emulated_te_ecc_add(&var_p1, &var_p2, d).unwrap(); + assert_eq!( + circuit.emulated_te_point_witness(&var_result).unwrap(), + expected + ); + let var_neutral = circuit + .create_emulated_te_point_variable(neutral.into()) + .unwrap(); + let var_neutral_result = circuit + .emulated_te_ecc_add(&var_p1, &var_neutral, d) + .unwrap(); + assert_eq!( + circuit + .emulated_te_point_witness(&var_neutral_result) + .unwrap(), + p1 + ); + let var_neutral_result = circuit + .emulated_te_ecc_add(&var_neutral, &var_p1, d) + .unwrap(); + assert_eq!( + circuit + .emulated_te_point_witness(&var_neutral_result) + .unwrap(), + p1 + ); + assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); + + let var_wrong_result = circuit + .create_emulated_te_point_variable(wrong_result) + .unwrap(); + circuit + .emulated_te_ecc_add_gate(&var_p1, &var_p2, &var_wrong_result, d) + .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 mut circuit = PlonkCircuit::::new_turbo_plonk(); + + let var_p1 = circuit + .create_emulated_te_point_variable(p1.into()) + .unwrap(); + let var_p2 = circuit + .create_emulated_te_point_variable(p2.into()) + .unwrap(); + let b = circuit.create_boolean_variable(true).unwrap(); + let var_p3 = circuit + .binary_emulated_te_point_vars_select(b, &var_p1, &var_p2) + .unwrap(); + assert_eq!( + circuit.emulated_te_point_witness(&var_p3).unwrap(), + p2.into() + ); + 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 mut circuit = PlonkCircuit::::new_turbo_plonk(); + + let var_p1 = circuit + .create_emulated_te_point_variable(p1.into()) + .unwrap(); + let var_p2 = circuit + .create_emulated_te_point_variable(p2.into()) + .unwrap(); + let var_p3 = circuit + .create_emulated_te_point_variable(p1.into()) + .unwrap(); + circuit + .enforce_emulated_te_point_equal(&var_p1, &var_p3) + .unwrap(); + assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); + circuit + .enforce_emulated_te_point_equal(&var_p1, &var_p2) + .unwrap(); + assert!(circuit.check_circuit_satisfiability(&[]).is_err()); + } +} diff --git a/relation/src/gadgets/ecc/glv.rs b/relation/src/gadgets/ecc/glv.rs index 1fa7fe1f3..57615fd30 100644 --- a/relation/src/gadgets/ecc/glv.rs +++ b/relation/src/gadgets/ecc/glv.rs @@ -17,7 +17,7 @@ use ark_ff::{PrimeField, Zero}; use jf_utils::field_switching; use num_bigint::{BigInt, BigUint}; -use super::Point; +use super::TEPoint; // phi(P) = lambda*P for all P // constants that are used to calculate phi(P) @@ -128,7 +128,7 @@ where } /// Mapping a point G to phi(G):= lambda G where phi is the endomorphism -fn endomorphism(base: &Point) -> Point +fn endomorphism(base: &TEPoint) -> TEPoint where F: PrimeField, P: TECurveConfig, @@ -571,7 +571,7 @@ fn get_bits(a: &[bool]) -> u16 { #[cfg(test)] mod tests { use super::*; - use crate::{errors::CircuitError, gadgets::ecc::Point, Circuit, PlonkCircuit}; + use crate::{errors::CircuitError, gadgets::ecc::TEPoint, Circuit, PlonkCircuit}; use ark_ec::{ twisted_edwards::{Affine, TECurveConfig as Config}, CurveConfig, @@ -599,10 +599,10 @@ mod tests { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); let s_var = circuit.create_variable(fr_to_fq::(&s))?; - let base_var = circuit.create_point_variable(Point::from(base))?; + let base_var = circuit.create_point_variable(TEPoint::from(base))?; base = (base * s).into(); let result = circuit.variable_base_scalar_mul::

(s_var, &base_var)?; - assert_eq!(Point::from(base), circuit.point_witness(&result)?); + assert_eq!(TEPoint::from(base), circuit.point_witness(&result)?); // ark_std::println!("Turbo Plonk: {} constraints", circuit.num_gates()); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); @@ -613,10 +613,10 @@ mod tests { let mut circuit: PlonkCircuit = PlonkCircuit::new_ultra_plonk(16); let s_var = circuit.create_variable(fr_to_fq::(&s))?; - let base_var = circuit.create_point_variable(Point::from(base))?; + let base_var = circuit.create_point_variable(TEPoint::from(base))?; base = (base * s).into(); let result = circuit.variable_base_scalar_mul::

(s_var, &base_var)?; - assert_eq!(Point::from(base), circuit.point_witness(&result)?); + assert_eq!(TEPoint::from(base), circuit.point_witness(&result)?); // ark_std::println!("Ultra Plonk: {} constraints", circuit.num_gates()); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); @@ -628,10 +628,10 @@ mod tests { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); let s_var = circuit.create_variable(fr_to_fq::(&s))?; - let base_var = circuit.create_point_variable(Point::from(base))?; + let base_var = circuit.create_point_variable(TEPoint::from(base))?; base = (base * s).into(); let result = circuit.glv_mul::

(s_var, &base_var)?; - assert_eq!(Point::from(base), circuit.point_witness(&result)?); + assert_eq!(TEPoint::from(base), circuit.point_witness(&result)?); // ark_std::println!("Turbo Plonk GLV: {} constraints", circuit.num_gates()); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); @@ -643,10 +643,10 @@ mod tests { let mut circuit: PlonkCircuit = PlonkCircuit::new_ultra_plonk(16); let s_var = circuit.create_variable(fr_to_fq::(&s))?; - let base_var = circuit.create_point_variable(Point::from(base))?; + let base_var = circuit.create_point_variable(TEPoint::from(base))?; base = (base * s).into(); let result = circuit.glv_mul::

(s_var, &base_var)?; - assert_eq!(Point::from(base), circuit.point_witness(&result)?); + assert_eq!(TEPoint::from(base), circuit.point_witness(&result)?); // ark_std::println!("Ultra Plonk GLV: {} constraints", circuit.num_gates()); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); @@ -671,8 +671,8 @@ mod tests { "33370049900732270411777328808452912493896532385897059012214433666611661340894" ), ); - let base_point: Point = base_point.into(); - let endo_point: Point = endo_point.into(); + let base_point: TEPoint = base_point.into(); + let endo_point: TEPoint = endo_point.into(); let t = endomorphism::<_, EdwardsConfig>(&base_point); assert_eq!(t, endo_point); diff --git a/relation/src/gadgets/ecc/mod.rs b/relation/src/gadgets/ecc/mod.rs index 1b7c8ee24..fbf1e0031 100644 --- a/relation/src/gadgets/ecc/mod.rs +++ b/relation/src/gadgets/ecc/mod.rs @@ -7,9 +7,9 @@ //! Elliptic curve related gates and gadgets. Including both native and //! non-native fields. +use super::{from_emulated_field, EmulationConfig, SerializableEmulatedStruct}; use crate::{errors::CircuitError, gates::*, BoolVar, Circuit, PlonkCircuit, Variable}; use ark_ec::{ - short_weierstrass::{Affine as SWGroupAffine, SWCurveConfig}, twisted_edwards::{Affine, Projective, TECurveConfig as Config}, AffineRepr, CurveConfig, CurveGroup, ScalarMul, }; @@ -18,17 +18,17 @@ use ark_std::{borrow::ToOwned, boxed::Box, string::ToString, vec, vec::Vec}; use core::marker::PhantomData; mod conversion; +pub mod emulated; mod glv; mod msm; -pub mod non_native; pub use conversion::*; pub use msm::*; #[derive(Debug, Copy, Clone, Eq, PartialEq)] /// An elliptic curve point in twisted Edwards affine form (x, y). -pub struct Point(F, F); +pub struct TEPoint(F, F); -impl From> for Point +impl From> for TEPoint where F: PrimeField, P: Config, @@ -40,29 +40,14 @@ where // `Point(0, 0)` which might lead to problems as seen in precedence: // https://cryptosubtlety.medium.com/00-8d4adcf4d255 let inf = Affine::

::zero(); - Point(inf.x, inf.y) + TEPoint(inf.x, inf.y) } else { - Point(p.x, p.y) + TEPoint(p.x, p.y) } } } -impl From> for Point -where - F: PrimeField, - P: SWCurveConfig, -{ - fn from(p: SWGroupAffine

) -> Self { - if p.is_zero() { - let inf = SWGroupAffine::

::zero(); - Point(inf.x, inf.y) - } else { - Point(p.x, p.y) - } - } -} - -impl Point { +impl TEPoint { /// Get the x coordinate of the point. pub fn get_x(&self) -> F { self.0 @@ -79,38 +64,50 @@ impl Point { } } -impl From> for Point +impl From> for TEPoint where F: PrimeField, P: Config, { fn from(p: Projective

) -> Self { let affine_repr = p.into_affine(); - Point(affine_repr.x, affine_repr.y) + TEPoint(affine_repr.x, affine_repr.y) } } -impl From> for Affine

+impl From> for Affine

where F: PrimeField, P: Config, { - fn from(p: Point) -> Self { + fn from(p: TEPoint) -> Self { Self::new(p.0, p.1) } } -impl From> for Projective

+impl From> for Projective

where F: PrimeField, P: Config, { - fn from(p: Point) -> Self { + fn from(p: TEPoint) -> Self { let affine_point: Affine

= p.into(); affine_point.into_group() } } +impl SerializableEmulatedStruct for TEPoint +where + E: EmulationConfig, + F: PrimeField, +{ + fn serialize_to_native_elements(&self) -> Vec { + let mut result = from_emulated_field(self.0); + result.extend(from_emulated_field(self.1)); + result + } +} + #[derive(Debug, Copy, Clone, Eq, PartialEq)] /// Represent variable of an EC point. pub struct PointVariable(Variable, Variable); @@ -130,17 +127,17 @@ impl PointVariable { // ECC related gates impl PlonkCircuit { /// Return the witness point for the circuit - pub fn point_witness(&self, point_var: &PointVariable) -> Result, CircuitError> { + pub fn point_witness(&self, point_var: &PointVariable) -> Result, CircuitError> { self.check_point_var_bound(point_var)?; let x = self.witness(point_var.0)?; let y = self.witness(point_var.1)?; - Ok(Point(x, y)) + Ok(TEPoint(x, y)) } /// Add a new EC point (as witness) to the circuit pub fn create_point_variable( &mut self, - point: Point, + point: TEPoint, ) -> Result { let x_var = self.create_variable(point.0)?; let y_var = self.create_variable(point.1)?; @@ -150,7 +147,7 @@ impl PlonkCircuit { /// Add a new EC point (as a constant) to the circuit pub fn create_constant_point_variable( &mut self, - point: Point, + point: TEPoint, ) -> Result { let x_var = self.create_constant_variable(point.0)?; let y_var = self.create_constant_variable(point.1)?; @@ -160,7 +157,7 @@ impl PlonkCircuit { /// Add a new EC point (as public input) to the circuit pub fn create_public_point_variable( &mut self, - point: Point, + point: TEPoint, ) -> Result { let x_var = self.create_public_variable(point.0)?; let y_var = self.create_public_variable(point.1)?; @@ -177,9 +174,9 @@ impl PlonkCircuit { &mut self, b0: BoolVar, b1: BoolVar, - point1: &Point, - point2: &Point, - point3: &Point, + point1: &TEPoint, + point2: &TEPoint, + point3: &TEPoint, ) -> Result { self.check_var_bound(b0.into())?; self.check_var_bound(b1.into())?; @@ -189,7 +186,7 @@ impl PlonkCircuit { self.witness(b0.into())? == F::one(), self.witness(b1.into())? == F::one(), ) { - (false, false) => Point::from(Affine::

::zero()), + (false, false) => TEPoint::from(Affine::

::zero()), (true, false) => point1.to_owned(), (false, true) => point2.to_owned(), (true, true) => point3.to_owned(), @@ -318,7 +315,7 @@ impl PlonkCircuit { self.check_point_var_bound(point_var)?; let b = { - if self.point_witness(point_var)? == Point::from(Affine::

::zero()) { + if self.point_witness(point_var)? == TEPoint::from(Affine::

::zero()) { self.create_boolean_variable(true)? } else { self.create_boolean_variable(false)? @@ -407,7 +404,7 @@ impl PlonkCircuit { let z = d * x_1 * y_1 * x_2 * y_2; // temporary intermediate value let x_3 = (x_1 * y_2 + x_2 * y_1) / (F::one() + z); let y_3 = (-P::COEFF_A * x_1 * x_2 + y_2 * y_1) / (F::one() - z); - let point_c = self.create_point_variable(Point(x_3, y_3))?; + let point_c = self.create_point_variable(TEPoint(x_3, y_3))?; self.ecc_add_gate::

(point_a, point_b, &point_c)?; Ok(point_c) @@ -451,9 +448,9 @@ impl PlonkCircuit { let selected = self.quaternary_point_select::

( *b0, *b1, - &Point::from(*p1), - &Point::from(*p2), - &Point::from(*p3), + &TEPoint::from(*p1), + &TEPoint::from(*p2), + &TEPoint::from(*p3), )?; accum = self.ecc_add::

(&accum, &selected)?; } @@ -619,8 +616,8 @@ mod test { P: Config, { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let p1 = circuit.create_point_variable(Point(F::zero(), F::one()))?; - let p2 = circuit.create_point_variable(Point(F::from(2353u32), F::one()))?; + let p1 = circuit.create_point_variable(TEPoint(F::zero(), F::one()))?; + let p2 = circuit.create_point_variable(TEPoint(F::from(2353u32), F::one()))?; let p1_check = circuit.is_neutral_point::

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

(&p2)?; @@ -634,14 +631,14 @@ mod test { .is_neutral_point::

(&PointVariable(circuit.num_vars(), circuit.num_vars() - 1)) .is_err()); - let circuit_1 = build_is_neutral_circuit::(Point(F::zero(), F::one()))?; - let circuit_2 = build_is_neutral_circuit::(Point(F::one(), F::zero()))?; + let circuit_1 = build_is_neutral_circuit::(TEPoint(F::zero(), F::one()))?; + let circuit_2 = build_is_neutral_circuit::(TEPoint(F::one(), F::zero()))?; test_variable_independence_for_circuit(circuit_1, circuit_2)?; Ok(()) } - fn build_is_neutral_circuit(point: Point) -> Result, CircuitError> + fn build_is_neutral_circuit(point: TEPoint) -> Result, CircuitError> where F: PrimeField, P: Config, @@ -656,13 +653,13 @@ mod test { macro_rules! test_enforce_on_curve { ($fq:tt, $param:tt, $pt:tt) => { let mut circuit: PlonkCircuit<$fq> = PlonkCircuit::new_turbo_plonk(); - let p1 = circuit.create_point_variable(Point($fq::zero(), $fq::one()))?; + let p1 = circuit.create_point_variable(TEPoint($fq::zero(), $fq::one()))?; circuit.enforce_on_curve::<$param>(&p1)?; let p2 = circuit.create_point_variable($pt)?; circuit.enforce_on_curve::<$param>(&p2)?; assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); - let p3 = circuit.create_point_variable(Point($fq::one(), $fq::one()))?; + let p3 = circuit.create_point_variable(TEPoint($fq::one(), $fq::one()))?; circuit.enforce_on_curve::<$param>(&p3)?; assert!(circuit.check_circuit_satisfiability(&[]).is_err()); // Check variable out of bound error. @@ -674,8 +671,8 @@ mod test { .is_err()); let circuit_1 = - build_enforce_on_curve_circuit::<_, $param>(Point($fq::zero(), $fq::one()))?; - let circuit_2 = build_enforce_on_curve_circuit::<_, $param>(Point( + build_enforce_on_curve_circuit::<_, $param>(TEPoint($fq::zero(), $fq::one()))?; + let circuit_2 = build_enforce_on_curve_circuit::<_, $param>(TEPoint( $fq::from(5u32), $fq::from(89u32), ))?; @@ -686,7 +683,7 @@ mod test { #[test] fn test_enforce_on_curve() -> Result<(), CircuitError> { // generator for ed_on_bn254 curve - let ed_on_254_gen = Point( + let ed_on_254_gen = TEPoint( FqEd354::from_str( "19698561148652590122159747500897617769866003486955115824547446575314762165298", ) @@ -697,7 +694,7 @@ mod test { .unwrap(), ); // generator for ed_on_bls377 curve - let ed_on_377_gen = Point( + let ed_on_377_gen = TEPoint( FqEd377::from_str( "4497879464030519973909970603271755437257548612157028181994697785683032656389", ) @@ -708,7 +705,7 @@ mod test { .unwrap(), ); // generator for ed_on_bls381 curve - let ed_on_381_gen = Point( + let ed_on_381_gen = TEPoint( FqEd381::from_str( "8076246640662884909881801758704306714034609987455869804520522091855516602923", ) @@ -719,7 +716,7 @@ mod test { .unwrap(), ); // generator for ed_on_bls381_bandersnatch curve - let ed_on_381b_gen = Point( + let ed_on_381b_gen = TEPoint( FqEd381::from_str( "18886178867200960497001835917649091219057080094937609519140440539760939937304", ) @@ -730,7 +727,7 @@ mod test { .unwrap(), ); // generator for bls377 G1 curve - let bls377_gen = Point( + let bls377_gen = TEPoint( Fq377::from_str( "71222569531709137229370268896323705690285216175189308202338047559628438110820800641278662592954630774340654489393", ) @@ -750,7 +747,7 @@ mod test { } fn build_enforce_on_curve_circuit( - point: Point, + point: TEPoint, ) -> Result, CircuitError> where F: PrimeField, @@ -783,8 +780,8 @@ mod test { let p3 = (p1 + p2).into_affine(); let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let p1_var = circuit.create_point_variable(Point::from(p1))?; - let p2_var = circuit.create_point_variable(Point::from(p2))?; + let p1_var = circuit.create_point_variable(TEPoint::from(p1))?; + let p2_var = circuit.create_point_variable(TEPoint::from(p2))?; let p3_var = circuit.ecc_add::

(&p1_var, &p2_var)?; assert_eq!(circuit.witness(p3_var.0)?, p3.x); @@ -802,17 +799,17 @@ mod test { let p3 = Affine::

::rand(&mut rng); let p4 = Affine::

::rand(&mut rng); let circuit_1 = - build_curve_point_addition_circuit::(Point::from(p1), Point::from(p2))?; + build_curve_point_addition_circuit::(TEPoint::from(p1), TEPoint::from(p2))?; let circuit_2 = - build_curve_point_addition_circuit::(Point::from(p3), Point::from(p4))?; + build_curve_point_addition_circuit::(TEPoint::from(p3), TEPoint::from(p4))?; test_variable_independence_for_circuit(circuit_1, circuit_2)?; Ok(()) } fn build_curve_point_addition_circuit( - p1: Point, - p2: Point, + p1: TEPoint, + p2: TEPoint, ) -> Result, CircuitError> where F: PrimeField, @@ -852,38 +849,38 @@ mod test { let select_p0 = circuit.quaternary_point_select::

( false_var, false_var, - &Point::from(p1), - &Point::from(p2), - &Point::from(p3), + &TEPoint::from(p1), + &TEPoint::from(p2), + &TEPoint::from(p3), )?; assert_eq!( - Point(F::zero(), F::one()), - Point(circuit.witness(select_p0.0)?, circuit.witness(select_p0.1)?) + TEPoint(F::zero(), F::one()), + TEPoint(circuit.witness(select_p0.0)?, circuit.witness(select_p0.1)?) ); let select_p1 = circuit.quaternary_point_select::

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

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

( true_var, true_var, - &Point::from(p1), - &Point::from(p2), - &Point::from(p3), + &TEPoint::from(p1), + &TEPoint::from(p2), + &TEPoint::from(p3), )?; - assert_eq!(Point::from(p3), circuit.point_witness(&select_p3)?); + assert_eq!(TEPoint::from(p3), circuit.point_witness(&select_p3)?); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); @@ -916,9 +913,9 @@ mod test { circuit.quaternary_point_select::

( b0_var, b1_var, - &Point::from(p1), - &Point::from(p2), - &Point::from(p3), + &TEPoint::from(p1), + &TEPoint::from(p2), + &TEPoint::from(p3), )?; circuit.finalize_for_arithmetization()?; Ok(circuit) @@ -942,8 +939,8 @@ mod test { let p = Affine::

::rand(&mut rng); let mut circuit = PlonkCircuit::::new_turbo_plonk(); - let p1_var = circuit.create_point_variable(Point::from(p))?; - let p2_var = circuit.create_point_variable(Point::from(p))?; + let p1_var = circuit.create_point_variable(TEPoint::from(p))?; + let p2_var = circuit.create_point_variable(TEPoint::from(p))?; circuit.enforce_point_equal(&p1_var, &p2_var)?; assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); @@ -955,16 +952,16 @@ mod test { .is_err()); let new_p = Affine::

::rand(&mut rng); - let circuit_1 = build_point_equal_circuit(Point::from(p), Point::from(p))?; - let circuit_2 = build_point_equal_circuit(Point::from(new_p), Point::from(new_p))?; + let circuit_1 = build_point_equal_circuit(TEPoint::from(p), TEPoint::from(p))?; + let circuit_2 = build_point_equal_circuit(TEPoint::from(new_p), TEPoint::from(new_p))?; test_variable_independence_for_circuit(circuit_1, circuit_2)?; Ok(()) } fn build_point_equal_circuit( - p1: Point, - p2: Point, + p1: TEPoint, + p2: TEPoint, ) -> Result, CircuitError> { let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); let p1_var = circuit.create_point_variable(p1)?; @@ -994,9 +991,9 @@ mod test { let p3 = Affine::

::rand(&mut rng); let mut circuit: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); - let p1_var = circuit.create_point_variable(Point::from(p1))?; - let p2_var = circuit.create_point_variable(Point::from(p2))?; - let p3_var = circuit.create_point_variable(Point::from(p3))?; + let p1_var = circuit.create_point_variable(TEPoint::from(p1))?; + let p2_var = circuit.create_point_variable(TEPoint::from(p2))?; + let p3_var = circuit.create_point_variable(TEPoint::from(p3))?; let p1_p2_eq = circuit.is_point_equal(&p1_var, &p2_var)?; let p1_p3_eq = circuit.is_point_equal(&p1_var, &p3_var)?; @@ -1011,18 +1008,18 @@ mod test { .is_err()); let circuit_1 = - build_is_equal_point_circuit(Point::from(p1), Point::from(p2), Point::from(p3))?; + build_is_equal_point_circuit(TEPoint::from(p1), TEPoint::from(p2), TEPoint::from(p3))?; let circuit_2 = - build_is_equal_point_circuit(Point::from(p3), Point::from(p3), Point::from(p1))?; + build_is_equal_point_circuit(TEPoint::from(p3), TEPoint::from(p3), TEPoint::from(p1))?; test_variable_independence_for_circuit(circuit_1, circuit_2)?; Ok(()) } fn build_is_equal_point_circuit( - p1: Point, - p2: Point, - p3: Point, + p1: TEPoint, + p2: TEPoint, + p3: TEPoint, ) -> Result, CircuitError> { let mut circuit = PlonkCircuit::new_turbo_plonk(); let p1_var = circuit.create_point_variable(p1)?; @@ -1114,7 +1111,7 @@ mod test { let scalar = circuit.create_variable(fr_to_fq::(&s))?; let result = circuit.fixed_base_scalar_mul(scalar, &base)?; base = (base * s).into(); - assert_eq!(Point::from(base), circuit.point_witness(&result)?); + assert_eq!(TEPoint::from(base), circuit.point_witness(&result)?); } assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); @@ -1167,15 +1164,15 @@ mod test { let p2 = Affine::

::rand(&mut rng); 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 p0_var = circuit.create_point_variable(TEPoint::from(p0))?; + let p1_var = circuit.create_point_variable(TEPoint::from(p1))?; 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)); + assert_eq!(circuit.point_witness(&select_p0)?, TEPoint::from(p0)); 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_eq!(circuit.point_witness(&select_p1)?, TEPoint::from(p1)); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); // wrong witness should fail @@ -1189,10 +1186,16 @@ mod test { ) .is_err()); - 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::(false, Point::from(p1), Point::from(p2))?; + let circuit_1 = build_binary_point_vars_select_circuit::( + true, + TEPoint::from(p0), + TEPoint::from(p1), + )?; + let circuit_2 = build_binary_point_vars_select_circuit::( + false, + TEPoint::from(p1), + TEPoint::from(p2), + )?; test_variable_independence_for_circuit(circuit_1, circuit_2)?; Ok(()) @@ -1200,8 +1203,8 @@ mod test { fn build_binary_point_vars_select_circuit( b: bool, - p0: Point, - p1: Point, + p0: TEPoint, + p1: TEPoint, ) -> Result, CircuitError> where F: PrimeField, @@ -1235,17 +1238,17 @@ mod test { let mut base = Affine::

::rand(&mut rng); let s = P::ScalarField::rand(&mut rng); let s_var = circuit.create_variable(fr_to_fq::(&s))?; - let base_var = circuit.create_point_variable(Point::from(base))?; + let base_var = circuit.create_point_variable(TEPoint::from(base))?; base = (base * s).into(); let result = circuit.variable_base_scalar_mul::

(s_var, &base_var)?; - assert_eq!(Point::from(base), circuit.point_witness(&result)?); + assert_eq!(TEPoint::from(base), circuit.point_witness(&result)?); } assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); let base = Affine::

::rand(&mut rng); let s = P::ScalarField::rand(&mut rng); let s_var = circuit.create_variable(fr_to_fq::(&s))?; - let base_var = circuit.create_point_variable(Point::from(base))?; + let base_var = circuit.create_point_variable(TEPoint::from(base))?; // wrong witness should fail *circuit.witness_mut(2) = F::rand(&mut rng); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); @@ -1261,10 +1264,10 @@ mod test { .is_err()); let circuit_1 = - build_variable_base_scalar_mul_circuit::(F::zero(), Point::from(base))?; + build_variable_base_scalar_mul_circuit::(F::zero(), TEPoint::from(base))?; let circuit_2 = build_variable_base_scalar_mul_circuit::( F::from(314u32), - Point::from(Affine::

::rand(&mut rng)), + TEPoint::from(Affine::

::rand(&mut rng)), )?; test_variable_independence_for_circuit(circuit_1, circuit_2)?; @@ -1273,7 +1276,7 @@ mod test { fn build_variable_base_scalar_mul_circuit( scalar: F, - base: Point, + base: TEPoint, ) -> Result, CircuitError> where F: PrimeField, diff --git a/relation/src/gadgets/ecc/msm.rs b/relation/src/gadgets/ecc/msm.rs index 1f839ba62..fd9cb997a 100644 --- a/relation/src/gadgets/ecc/msm.rs +++ b/relation/src/gadgets/ecc/msm.rs @@ -6,7 +6,7 @@ //! This module implements multi-scalar-multiplication circuits. -use super::{Point, PointVariable}; +use super::{PointVariable, TEPoint}; use crate::{errors::CircuitError, Circuit, PlonkCircuit, Variable}; use ark_ec::{ twisted_edwards::{Projective, TECurveConfig as Config}, @@ -344,7 +344,7 @@ fn compute_scalar_mul_value( circuit: &PlonkCircuit, scalar_var: Variable, base_var: &PointVariable, -) -> Result, CircuitError> +) -> Result, CircuitError> where F: PrimeField, P: Config, @@ -368,7 +368,7 @@ fn ln_without_floats(a: usize) -> usize { mod tests { use super::*; - use crate::{gadgets::ecc::Point, Circuit, PlonkType}; + use crate::{gadgets::ecc::TEPoint, Circuit, PlonkType}; use ark_bls12_377::{g1::Config as Param377, Fq as Fq377}; use ark_ec::{ scalar_mul::variable_base::VariableBaseMSM, @@ -422,10 +422,10 @@ mod tests { let scalar_reprs: Vec<::BigInt> = scalars.iter().map(|x| x.into_bigint()).collect(); let res = Projective::

::msm_bigint(&bases, &scalar_reprs); - let res_point: Point = res.into(); + let res_point: TEPoint = res.into(); // corresponding wires - let bases_point: Vec> = bases.iter().map(|x| (*x).into()).collect(); + let bases_point: Vec> = bases.iter().map(|x| (*x).into()).collect(); let bases_vars: Vec = bases_point .iter() .map(|x| circuit.create_point_variable(*x)) diff --git a/relation/src/gadgets/ecc/non_native.rs b/relation/src/gadgets/ecc/non_native.rs deleted file mode 100644 index 73bf5858e..000000000 --- a/relation/src/gadgets/ecc/non_native.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) 2022 Espresso Systems (espressosys.com) -// This file is part of the Jellyfish library. - -// You should have received a copy of the MIT License -// along with the Jellyfish library. If not, see . - -//! Elliptic curve related gates and gadgets for non-native fields - -use super::Point; -use crate::{ - errors::CircuitError, - gadgets::{EmulatedVariable, EmulationConfig}, - 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 { - /// Return the witness point - pub fn emulated_point_witness>( - &self, - point_var: &EmulatedPointVariable, - ) -> Result, CircuitError> { - let x = self.emulated_witness(&point_var.0)?; - let y = self.emulated_witness(&point_var.1)?; - Ok(Point(x, y)) - } - - /// Add a new emulated EC point (as witness) - pub fn create_emulated_point_variable>( - &mut self, - point: Point, - ) -> Result, CircuitError> { - let x = self.create_emulated_variable(point.0)?; - let y = self.create_emulated_variable(point.1)?; - Ok(EmulatedPointVariable(x, y)) - } - - /// Add a new constant emulated EC point - pub fn create_constant_emulated_point_variable>( - &mut self, - point: Point, - ) -> Result, CircuitError> { - let x = self.create_constant_emulated_variable(point.0)?; - let y = self.create_constant_emulated_variable(point.1)?; - Ok(EmulatedPointVariable(x, y)) - } - - /// Add a new public emulated EC point - pub fn create_public_emulated_point_variable>( - &mut self, - point: Point, - ) -> Result, CircuitError> { - let x = self.create_public_emulated_variable(point.0)?; - let y = self.create_public_emulated_variable(point.1)?; - Ok(EmulatedPointVariable(x, y)) - } - - /// Constrain variable `c` to be the point addition of `a` and - /// `b` over an elliptic curve. - pub fn emulated_ecc_add_gate>( - &mut self, - a: &EmulatedPointVariable, - b: &EmulatedPointVariable, - c: &EmulatedPointVariable, - d: E, - ) -> Result<(), CircuitError> { - let x1y2 = self.emulated_mul(&a.0, &b.1)?; - let x2y1 = self.emulated_mul(&b.0, &a.1)?; - let x1x2 = self.emulated_mul(&a.0, &b.0)?; - let y1y2 = self.emulated_mul(&a.1, &b.1)?; - let x1x2y1y2 = self.emulated_mul(&x1x2, &y1y2)?; - let dx1x2y1y2 = self.emulated_mul_constant(&x1x2y1y2, d)?; - - // checking that x3 = x1y2 + x2y1 - dx1y1x2y2x3 - // t1 = x1y2 + x2y1 - let t1 = self.emulated_add(&x1y2, &x2y1)?; - // t2 = d x1 y1 x2 y2 x3 - let t2 = self.emulated_mul(&dx1x2y1y2, &c.0)?; - self.emulated_add_gate(&c.0, &t2, &t1)?; - - // checking that y3 = x1x2 + y1y2 + dx1y1x2y2y3 - // t1 = x1x2 + y1y2 - let t1 = self.emulated_add(&x1x2, &y1y2)?; - let t2 = self.emulated_mul(&dx1x2y1y2, &c.1)?; - self.emulated_add_gate(&t1, &t2, &c.1) - } - - /// Obtain a variable to the point addition result of `a` + `b` - pub fn emulated_ecc_add>( - &mut self, - a: &EmulatedPointVariable, - b: &EmulatedPointVariable, - d: E, - ) -> Result, CircuitError> { - let x1 = self.emulated_witness(&a.0)?; - let y1 = self.emulated_witness(&a.1)?; - let x2 = self.emulated_witness(&b.0)?; - let y2 = self.emulated_witness(&b.1)?; - - let t1 = x1 * y2; - let t2 = x2 * y1; - let dx1x2y1y2 = d * t1 * t2; - - let x3 = (t1 + t2) / (E::one() + dx1x2y1y2); - let y3 = (x1 * x2 + y1 * y2) / (E::one() - dx1x2y1y2); - let c = self.create_emulated_point_variable(Point(x3, y3))?; - 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)] -mod tests { - use crate::{ - gadgets::{ - ecc::{conversion::*, Point}, - EmulationConfig, - }, - Circuit, PlonkCircuit, - }; - use ark_bls12_377::{g1::Config as Param377, Fq as Fq377}; - use ark_bn254::Fr as Fr254; - use ark_ec::{ - short_weierstrass::{Projective, SWCurveConfig}, - CurveGroup, Group, - }; - use ark_ff::{MontFp, PrimeField}; - use ark_std::UniformRand; - - #[test] - fn test_emulated_point_addition() { - let d : Fq377 = MontFp!("122268283598675559488486339158635529096981886914877139579534153582033676785385790730042363341236035746924960903179"); - test_emulated_point_addition_helper::(d); - } - - fn test_emulated_point_addition_helper(d: E) - 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 p3: Point = (&(p1 + p2).into_affine()).into(); - let fail_p3: Point = (&(p1 + p2 + Projective::

::generator()).into_affine()).into(); - 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.emulated_ecc_add(&var_p1, &var_p2, d).unwrap(); - assert_eq!(circuit.emulated_point_witness(&var_p3).unwrap(), p3); - assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); - - let var_fail_p3 = circuit.create_emulated_point_variable(fail_p3).unwrap(); - circuit - .emulated_ecc_add_gate(&var_p1, &var_p2, &var_fail_p3, d) - .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 32b993441..64631057e 100644 --- a/relation/src/gadgets/emulated.rs +++ b/relation/src/gadgets/emulated.rs @@ -29,6 +29,12 @@ pub trait EmulationConfig: PrimeField { const NUM_LIMBS: usize; } +/// A struct that can be serialized into `Vec` of field elements. +pub trait SerializableEmulatedStruct { + /// Serialize into a `Vec` of field elements. + fn serialize_to_native_elements(&self) -> Vec; +} + fn biguint_to_limbs(val: &BigUint, b: usize, num_limbs: usize) -> Vec { let mut result = vec![]; let b_pow = BigUint::one() << b; @@ -72,7 +78,7 @@ where /// The variable represents an element in the emulated field. #[derive(Debug, Clone)] -pub struct EmulatedVariable(pub(crate) Vec, PhantomData); +pub struct EmulatedVariable(pub(crate) Vec, pub PhantomData); impl EmulatedVariable { /// Return the list of variables that simulate the field element @@ -188,10 +194,11 @@ impl PlonkCircuit { // checking that the carry_out has at most [`E::B`] + 1 bits self.enforce_in_range(carry_out, E::B + 1)?; // enforcing that a0 * b0 - k0 * modulus[0] - carry_out * 2^E::B = c0 - self.general_arithmetic_gate( + self.quad_poly_gate( &[a.0[0], b.0[0], k.0[0], carry_out, c.0[0]], &[F::zero(), F::zero(), neg_modulus[0], -b_pow], &[F::one(), F::zero()], + F::one(), F::zero(), )?; @@ -235,7 +242,7 @@ impl PlonkCircuit { if i % 2 == 0 { let t1 = stack.pop().unwrap(); let t2 = stack.pop().unwrap(); - let t = self.general_arithmetic( + let t = self.gen_quad_poly( &[a.0[i], b.0[0], t1.0, t2.0], &[F::zero(), F::zero(), t1.1, t2.1], &[F::one(), F::zero()], @@ -271,10 +278,11 @@ impl PlonkCircuit { let k_mod = self.mod_to_native_field(&k)?; let c_mod = self.mod_to_native_field(c)?; let e_mod_f = F::from(E::MODULUS.into()); - self.general_arithmetic_gate( + self.quad_poly_gate( &[a_mod, b_mod, k_mod, self.zero(), c_mod], &[F::zero(), F::zero(), -e_mod_f, F::zero()], &[F::one(), F::zero()], + F::one(), F::zero(), )?; @@ -482,6 +490,18 @@ impl PlonkCircuit { Ok(c) } + /// Return an [`EmulatedVariable`] which equals to a-b. + pub fn emulated_sub>( + &mut self, + a: &EmulatedVariable, + b: &EmulatedVariable, + ) -> Result, CircuitError> { + let c = self.emulated_witness(a)? - self.emulated_witness(b)?; + let c = self.create_emulated_variable(c)?; + self.emulated_add_gate(&c, b, a)?; + Ok(c) + } + /// Constrain that a+b=c in the emulated field. /// This function doesn't perform emulated variable validaty check on the /// input a and c. We assume that they are already performed elsewhere. @@ -541,7 +561,8 @@ impl PlonkCircuit { Ok(()) } - /// Return an [`EmulatedVariable`] which equals to a+b. + /// Return an [`EmulatedVariable`] which equals to a + b where b is a + /// constant. pub fn emulated_add_constant>( &mut self, a: &EmulatedVariable, @@ -553,6 +574,18 @@ impl PlonkCircuit { Ok(c) } + /// Return an [`EmulatedVariable`] which equals to a - b where b is a + /// constant. + pub fn emulated_sub_constant>( + &mut self, + a: &EmulatedVariable, + b: E, + ) -> Result, CircuitError> { + let c = self.emulated_witness(a)? - b; + let c = self.create_emulated_variable(c)?; + self.emulated_add_constant_gate(&c, b, a)?; + 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). @@ -580,17 +613,48 @@ impl PlonkCircuit { /// Return error if the input variables are invalid. pub fn enforce_emulated_var_equal>( &mut self, - p0: &EmulatedVariable, - p1: &EmulatedVariable, + a: &EmulatedVariable, + b: &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.check_vars_bound(&a.0[..])?; + self.check_vars_bound(&b.0[..])?; + for (&a, &b) in a.0.iter().zip(b.0.iter()) { self.enforce_equal(a, b)?; } Ok(()) } + /// Obtain a bool variable representing whether two input emulated variables + /// are equal. Return error if variables are invalid. + pub fn is_emulated_var_equal>( + &mut self, + a: &EmulatedVariable, + b: &EmulatedVariable, + ) -> Result { + self.check_vars_bound(&a.0[..])?; + self.check_vars_bound(&b.0[..])?; + let c = + a.0.iter() + .zip(b.0.iter()) + .map(|(&a, &b)| self.is_equal(a, b)) + .collect::, _>>()?; + self.logic_and_all(&c) + } + + /// Obtain a bool variable representing whether the input emulated variable + /// is zero. Return error if variables are invalid. + pub fn is_emulated_var_zero>( + &mut self, + a: &EmulatedVariable, + ) -> Result { + self.check_vars_bound(&a.0[..])?; + let c = + a.0.iter() + .map(|&a| self.is_zero(a)) + .collect::, _>>()?; + self.logic_and_all(&c) + } + /// Given an emulated field element `a`, return `a mod F::MODULUS` in the /// native field. fn mod_to_native_field>( @@ -630,17 +694,13 @@ impl PlonkCircuit { impl EmulationConfig for ark_bls12_377::Fq { const T: usize = 500; - - const B: usize = 125; - - const NUM_LIMBS: usize = 4; + const B: usize = 100; + const NUM_LIMBS: usize = 5; } impl EmulationConfig for ark_bn254::Fq { - const T: usize = 261; - - const B: usize = 87; - + const T: usize = 300; + const B: usize = 100; const NUM_LIMBS: usize = 3; } @@ -723,7 +783,7 @@ mod tests { E: EmulationConfig, F: PrimeField, { - let mut circuit = PlonkCircuit::::new_turbo_plonk(); + let mut circuit = PlonkCircuit::::new_ultra_plonk(20); let x = E::from(6732u64); let y = E::from(E::MODULUS.into() - 12387u64); let expected = x * y; diff --git a/relation/src/gates/arithmetic.rs b/relation/src/gates/arithmetic.rs index 776f1d4df..05123a0c2 100644 --- a/relation/src/gates/arithmetic.rs +++ b/relation/src/gates/arithmetic.rs @@ -292,31 +292,3 @@ where F::one() } } - -/// A multiplication-then-addition gate -#[derive(Clone)] -pub struct ArithmeticGate { - pub(crate) lc_coeffs: [F; GATE_WIDTH], - pub(crate) mul_coeffs: [F; N_MUL_SELECTORS], - pub(crate) constant: F, -} -impl Gate for ArithmeticGate -where - F: Field, -{ - fn name(&self) -> &'static str { - "Multiplication-then-addition Gate" - } - fn q_lc(&self) -> [F; GATE_WIDTH] { - self.lc_coeffs - } - fn q_mul(&self) -> [F; N_MUL_SELECTORS] { - self.mul_coeffs - } - fn q_c(&self) -> F { - self.constant - } - fn q_o(&self) -> F { - F::one() - } -}