From 5f8cfd49be8fb9e2e60ed284ad6735bd2f120853 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Thu, 22 Feb 2024 11:15:49 +0100 Subject: [PATCH] Migrate usage from E:Pairing to F:PrimeField when Pairing is not needed The motivation to do so, is so we can use the witness generation with other curves that don't have pairings (and hence the Pairing trait implemented). --- src/circom/builder.rs | 24 ++++++------- src/circom/circuit.rs | 35 ++++++++---------- src/circom/mod.rs | 6 ++-- src/circom/r1cs_reader.rs | 59 ++++++++++++++++--------------- src/witness/witness_calculator.rs | 10 +++--- src/zkey.rs | 4 +-- tests/groth16.rs | 10 +++--- tests/solidity.rs | 4 +-- 8 files changed, 73 insertions(+), 79 deletions(-) diff --git a/src/circom/builder.rs b/src/circom/builder.rs index 670e22b..63fab22 100644 --- a/src/circom/builder.rs +++ b/src/circom/builder.rs @@ -1,4 +1,4 @@ -use ark_ec::pairing::Pairing; +use ark_ff::PrimeField; use std::{fs::File, path::Path}; use super::{CircomCircuit, R1CS}; @@ -10,20 +10,20 @@ use crate::{circom::R1CSFile, witness::WitnessCalculator}; use color_eyre::Result; #[derive(Clone, Debug)] -pub struct CircomBuilder { - pub cfg: CircomConfig, +pub struct CircomBuilder { + pub cfg: CircomConfig, pub inputs: HashMap>, } // Add utils for creating this from files / directly from bytes #[derive(Clone, Debug)] -pub struct CircomConfig { - pub r1cs: R1CS, +pub struct CircomConfig { + pub r1cs: R1CS, pub wtns: WitnessCalculator, pub sanity_check: bool, } -impl CircomConfig { +impl CircomConfig { pub fn new(wtns: impl AsRef, r1cs: impl AsRef) -> Result { let wtns = WitnessCalculator::new(wtns).unwrap(); let reader = File::open(r1cs)?; @@ -36,10 +36,10 @@ impl CircomConfig { } } -impl CircomBuilder { +impl CircomBuilder { /// Instantiates a new builder using the provided WitnessGenerator and R1CS files /// for your circuit - pub fn new(cfg: CircomConfig) -> Self { + pub fn new(cfg: CircomConfig) -> Self { Self { cfg, inputs: HashMap::new(), @@ -54,7 +54,7 @@ impl CircomBuilder { /// Generates an empty circom circuit with no witness set, to be used for /// generation of the trusted setup parameters - pub fn setup(&self) -> CircomCircuit { + pub fn setup(&self) -> CircomCircuit { let mut circom = CircomCircuit { r1cs: self.cfg.r1cs.clone(), witness: None, @@ -68,20 +68,20 @@ impl CircomBuilder { /// Creates the circuit populated with the witness corresponding to the previously /// provided inputs - pub fn build(mut self) -> Result> { + pub fn build(mut self) -> Result> { let mut circom = self.setup(); // calculate the witness let witness = self .cfg .wtns - .calculate_witness_element::(self.inputs, self.cfg.sanity_check)?; + .calculate_witness_element::(self.inputs, self.cfg.sanity_check)?; circom.witness = Some(witness); // sanity check debug_assert!({ use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; - let cs = ConstraintSystem::::new_ref(); + let cs = ConstraintSystem::::new_ref(); circom.clone().generate_constraints(cs.clone()).unwrap(); let is_satisfied = cs.is_satisfied().unwrap(); if !is_satisfied { diff --git a/src/circom/circuit.rs b/src/circom/circuit.rs index 12540e9..7895712 100644 --- a/src/circom/circuit.rs +++ b/src/circom/circuit.rs @@ -1,4 +1,4 @@ -use ark_ec::pairing::Pairing; +use ark_ff::PrimeField; use ark_relations::r1cs::{ ConstraintSynthesizer, ConstraintSystemRef, LinearCombination, SynthesisError, Variable, }; @@ -8,13 +8,13 @@ use super::R1CS; use color_eyre::Result; #[derive(Clone, Debug)] -pub struct CircomCircuit { - pub r1cs: R1CS, - pub witness: Option>, +pub struct CircomCircuit { + pub r1cs: R1CS, + pub witness: Option>, } -impl CircomCircuit { - pub fn get_public_inputs(&self) -> Option> { +impl CircomCircuit { + pub fn get_public_inputs(&self) -> Option> { match &self.witness { None => None, Some(w) => match &self.r1cs.wire_mapping { @@ -25,11 +25,8 @@ impl CircomCircuit { } } -impl ConstraintSynthesizer for CircomCircuit { - fn generate_constraints( - self, - cs: ConstraintSystemRef, - ) -> Result<(), SynthesisError> { +impl ConstraintSynthesizer for CircomCircuit { + fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { let witness = &self.witness; let wire_mapping = &self.r1cs.wire_mapping; @@ -37,7 +34,7 @@ impl ConstraintSynthesizer for CircomCircuit { for i in 1..self.r1cs.num_inputs { cs.new_input_variable(|| { Ok(match witness { - None => E::ScalarField::from(1u32), + None => F::from(1u32), Some(w) => match wire_mapping { Some(m) => w[m[i]], None => w[i], @@ -49,7 +46,7 @@ impl ConstraintSynthesizer for CircomCircuit { for i in 0..self.r1cs.num_aux { cs.new_witness_variable(|| { Ok(match witness { - None => E::ScalarField::from(1u32), + None => F::from(1u32), Some(w) => match wire_mapping { Some(m) => w[m[i + self.r1cs.num_inputs]], None => w[i + self.r1cs.num_inputs], @@ -65,12 +62,10 @@ impl ConstraintSynthesizer for CircomCircuit { Variable::Witness(index - self.r1cs.num_inputs) } }; - let make_lc = |lc_data: &[(usize, E::ScalarField)]| { + let make_lc = |lc_data: &[(usize, F)]| { lc_data.iter().fold( - LinearCombination::::zero(), - |lc: LinearCombination, (index, coeff)| { - lc + (*coeff, make_index(*index)) - }, + LinearCombination::::zero(), + |lc: LinearCombination, (index, coeff)| lc + (*coeff, make_index(*index)), ) }; @@ -90,12 +85,12 @@ impl ConstraintSynthesizer for CircomCircuit { mod tests { use super::*; use crate::{CircomBuilder, CircomConfig}; - use ark_bn254::{Bn254, Fr}; + use ark_bn254::Fr; use ark_relations::r1cs::ConstraintSystem; #[test] fn satisfied() { - let cfg = CircomConfig::::new( + let cfg = CircomConfig::::new( "./test-vectors/mycircuit.wasm", "./test-vectors/mycircuit.r1cs", ) diff --git a/src/circom/mod.rs b/src/circom/mod.rs index 6ad7fd0..080eb2d 100644 --- a/src/circom/mod.rs +++ b/src/circom/mod.rs @@ -1,5 +1,3 @@ -use ark_ec::pairing::Pairing; - pub mod r1cs_reader; pub use r1cs_reader::{R1CSFile, R1CS}; @@ -12,5 +10,5 @@ pub use builder::{CircomBuilder, CircomConfig}; mod qap; pub use qap::CircomReduction; -pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); -pub type ConstraintVec = Vec<(usize, ::ScalarField)>; +pub type Constraints = (ConstraintVec, ConstraintVec, ConstraintVec); +pub type ConstraintVec = Vec<(usize, F)>; diff --git a/src/circom/r1cs_reader.rs b/src/circom/r1cs_reader.rs index b5db104..eb0ea43 100644 --- a/src/circom/r1cs_reader.rs +++ b/src/circom/r1cs_reader.rs @@ -4,8 +4,8 @@ use byteorder::{LittleEndian, ReadBytesExt}; use std::io::{Error, ErrorKind}; -use ark_ec::pairing::Pairing; -use ark_serialize::{CanonicalDeserialize, SerializationError, SerializationError::IoError}; +use ark_ff::PrimeField; +use ark_serialize::{SerializationError, SerializationError::IoError}; use ark_std::io::{Read, Seek, SeekFrom}; use std::collections::HashMap; @@ -15,16 +15,16 @@ type IoResult = Result; use super::{ConstraintVec, Constraints}; #[derive(Clone, Debug)] -pub struct R1CS { +pub struct R1CS { pub num_inputs: usize, pub num_aux: usize, pub num_variables: usize, - pub constraints: Vec>, + pub constraints: Vec>, pub wire_mapping: Option>, } -impl From> for R1CS { - fn from(file: R1CSFile) -> Self { +impl From> for R1CS { + fn from(file: R1CSFile) -> Self { let num_inputs = (1 + file.header.n_pub_in + file.header.n_pub_out) as usize; let num_variables = file.header.n_wires as usize; let num_aux = num_variables - num_inputs; @@ -38,20 +38,20 @@ impl From> for R1CS { } } -pub struct R1CSFile { +pub struct R1CSFile { pub version: u32, pub header: Header, - pub constraints: Vec>, + pub constraints: Vec>, pub wire_mapping: Vec, } -impl R1CSFile { +impl R1CSFile { /// reader must implement the Seek trait, for example with a Cursor /// /// ```rust,ignore /// let reader = BufReader::new(Cursor::new(&data[..])); /// ``` - pub fn new(mut reader: R) -> IoResult> { + pub fn new(mut reader: R) -> IoResult> { let mut magic = [0u8; 4]; reader.read_exact(&mut magic)?; if magic != [0x72, 0x31, 0x63, 0x73] { @@ -117,7 +117,7 @@ impl R1CSFile { reader.seek(SeekFrom::Start(*constraint_offset?))?; - let constraints = read_constraints::<&mut R, E>(&mut reader, &header)?; + let constraints = read_constraints::<&mut R, F>(&mut reader, &header)?; let wire2label_offset = sec_offsets.get(&wire2label_type).ok_or_else(|| { Error::new( @@ -177,15 +177,16 @@ impl Header { let mut prime_size = vec![0u8; field_size as usize]; reader.read_exact(&mut prime_size)?; - if prime_size - != hex::decode("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") - .unwrap() - { - return Err(IoError(Error::new( - ErrorKind::InvalidData, - "This parser only supports bn256", - ))); - } + // TODO WIP + // if prime_size + // != hex::decode("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") + // .unwrap() + // { + // return Err(IoError(Error::new( + // ErrorKind::InvalidData, + // "This parser only supports bn256", + // ))); + // } Ok(Header { field_size, @@ -200,29 +201,29 @@ impl Header { } } -fn read_constraint_vec(mut reader: R) -> IoResult> { +fn read_constraint_vec(mut reader: R) -> IoResult> { let n_vec = reader.read_u32::()? as usize; let mut vec = Vec::with_capacity(n_vec); for _ in 0..n_vec { vec.push(( reader.read_u32::()? as usize, - E::ScalarField::deserialize_uncompressed(&mut reader)?, + F::deserialize_uncompressed(&mut reader)?, )); } Ok(vec) } -fn read_constraints( +fn read_constraints( mut reader: R, header: &Header, -) -> IoResult>> { +) -> IoResult>> { // todo check section size let mut vec = Vec::with_capacity(header.n_constraints as usize); for _ in 0..header.n_constraints { vec.push(( - read_constraint_vec::<&mut R, E>(&mut reader)?, - read_constraint_vec::<&mut R, E>(&mut reader)?, - read_constraint_vec::<&mut R, E>(&mut reader)?, + read_constraint_vec::<&mut R, F>(&mut reader)?, + read_constraint_vec::<&mut R, F>(&mut reader)?, + read_constraint_vec::<&mut R, F>(&mut reader)?, )); } Ok(vec) @@ -251,7 +252,7 @@ fn read_map(mut reader: R, size: u64, header: &Header) -> IoResult::new(reader).unwrap(); + let file = R1CSFile::::new(reader).unwrap(); assert_eq!(file.version, 1); assert_eq!(file.header.field_size, 32); diff --git a/src/witness/witness_calculator.rs b/src/witness/witness_calculator.rs index 49582b9..a859394 100644 --- a/src/witness/witness_calculator.rs +++ b/src/witness/witness_calculator.rs @@ -1,4 +1,5 @@ use super::{fnv, CircomBase, SafeMemory, Wasm}; +use ark_ff::PrimeField; use color_eyre::Result; use num_bigint::BigInt; use num_traits::Zero; @@ -255,16 +256,15 @@ impl WitnessCalculator { } pub fn calculate_witness_element< - E: ark_ec::pairing::Pairing, + F: PrimeField, I: IntoIterator)>, >( &mut self, inputs: I, sanity_check: bool, - ) -> Result> { - use ark_ff::PrimeField; + ) -> Result> { let witness = self.calculate_witness(inputs, sanity_check)?; - let modulus = ::MODULUS; + let modulus = ::MODULUS; // convert it to field elements use num_traits::Signed; @@ -277,7 +277,7 @@ impl WitnessCalculator { } else { w.to_biguint().unwrap() }; - E::ScalarField::from(w) + F::from(w) }) .collect::>(); diff --git a/src/zkey.rs b/src/zkey.rs index 8dd6422..ab9c0f8 100644 --- a/src/zkey.rs +++ b/src/zkey.rs @@ -848,7 +848,7 @@ mod tests { let mut file = File::open(path).unwrap(); let (params, _matrices) = read_zkey(&mut file).unwrap(); // binfile.proving_key().unwrap(); - let cfg = CircomConfig::::new( + let cfg = CircomConfig::::new( "./test-vectors/mycircuit.wasm", "./test-vectors/mycircuit.r1cs", ) @@ -895,7 +895,7 @@ mod tests { let s = ark_bn254::Fr::rand(rng); let full_assignment = wtns - .calculate_witness_element::(inputs, false) + .calculate_witness_element::(inputs, false) .unwrap(); let proof = Groth16::::create_proof_with_reduction_and_matrices( ¶ms, diff --git a/tests/groth16.rs b/tests/groth16.rs index 8b29ba3..a561a84 100644 --- a/tests/groth16.rs +++ b/tests/groth16.rs @@ -2,7 +2,7 @@ use ark_circom::{CircomBuilder, CircomConfig}; use ark_std::rand::thread_rng; use color_eyre::Result; -use ark_bn254::Bn254; +use ark_bn254::{Bn254, Fr}; use ark_crypto_primitives::snark::SNARK; use ark_groth16::Groth16; @@ -10,7 +10,7 @@ type GrothBn = Groth16; #[test] fn groth16_proof() -> Result<()> { - let cfg = CircomConfig::::new( + let cfg = CircomConfig::::new( "./test-vectors/mycircuit.wasm", "./test-vectors/mycircuit.r1cs", )?; @@ -41,7 +41,7 @@ fn groth16_proof() -> Result<()> { #[test] fn groth16_proof_wrong_input() { - let cfg = CircomConfig::::new( + let cfg = CircomConfig::::new( "./test-vectors/mycircuit.wasm", "./test-vectors/mycircuit.r1cs", ) @@ -63,7 +63,7 @@ fn groth16_proof_wrong_input() { #[test] #[cfg(feature = "circom-2")] fn groth16_proof_circom2() -> Result<()> { - let cfg = CircomConfig::::new( + let cfg = CircomConfig::::new( "./test-vectors/circom2_multiplier2.wasm", "./test-vectors/circom2_multiplier2.r1cs", )?; @@ -95,7 +95,7 @@ fn groth16_proof_circom2() -> Result<()> { #[test] #[cfg(feature = "circom-2")] fn witness_generation_circom2() -> Result<()> { - let cfg = CircomConfig::::new( + let cfg = CircomConfig::::new( "./test-vectors/circom2_multiplier2.wasm", "./test-vectors/circom2_multiplier2.r1cs", )?; diff --git a/tests/solidity.rs b/tests/solidity.rs index c183709..a805c3b 100644 --- a/tests/solidity.rs +++ b/tests/solidity.rs @@ -2,7 +2,7 @@ use ark_circom::{ethereum, CircomBuilder, CircomConfig}; use ark_std::rand::thread_rng; use color_eyre::Result; -use ark_bn254::Bn254; +use ark_bn254::{Bn254, Fr}; use ark_crypto_primitives::snark::SNARK; use ark_groth16::Groth16; @@ -16,7 +16,7 @@ use std::{convert::TryFrom, sync::Arc}; #[tokio::test] async fn solidity_verifier() -> Result<()> { - let cfg = CircomConfig::::new( + let cfg = CircomConfig::::new( "./test-vectors/mycircuit.wasm", "./test-vectors/mycircuit.r1cs", )?;