Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Minimal in-browser compatibility for the crate #149

Merged
merged 8 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/circom_full_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ fn main() {
"./folding-schemes/src/frontend/circom/test_folder/with_external_inputs_js/with_external_inputs.wasm",
);

let f_circuit_params = (r1cs_path, wasm_path, 1, 2);
let f_circuit_params = (r1cs_path.into(), wasm_path.into(), 1, 2);
let f_circuit = CircomFCircuit::<Fr>::new(f_circuit_params).unwrap();

pub type N =
Expand Down
4 changes: 2 additions & 2 deletions folding-schemes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ num-bigint = "0.4"
num-integer = "0.1"
color-eyre = "=0.6.2"
sha3 = "0.10"
ark-noname = { git = "https://github.com/dmpierre/ark-noname", branch="feat/sonobe-integration" }
ark-noname = { git = "https://github.com/dmpierre/ark-noname", branch = "feat/sonobe-integration" }
noname = { git = "https://github.com/dmpierre/noname" }
serde_json = "1.0.85" # to (de)serialize JSON
serde = "1.0.203"
acvm = { git = "https://github.com/noir-lang/noir", rev="2b4853e", default-features = false }
arkworks_backend = { git = "https://github.com/dmpierre/arkworks_backend", branch="feat/sonobe-integration" }
arkworks_backend = { git = "https://github.com/dmpierre/arkworks_backend", branch = "feat/sonobe-integration" }
log = "0.4"

# tmp import for espresso's sumcheck
Expand Down
25 changes: 16 additions & 9 deletions folding-schemes/src/frontend/circom/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::frontend::FCircuit;
use crate::frontend::FpVar::Var;
use crate::utils::PathOrBin;
use crate::Error;
use ark_circom::circom::{CircomCircuit, R1CS as CircomR1CS};
use ark_ff::PrimeField;
Expand All @@ -9,7 +10,6 @@ use ark_r1cs_std::R1CSVar;
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
use ark_std::fmt::Debug;
use num_bigint::BigInt;
use std::path::PathBuf;
use std::rc::Rc;
use std::{fmt, usize};

Expand Down Expand Up @@ -93,11 +93,11 @@ impl<F: PrimeField> CircomFCircuit<F> {

impl<F: PrimeField> FCircuit<F> for CircomFCircuit<F> {
/// (r1cs_path, wasm_path, state_len, external_inputs_len)
type Params = (PathBuf, PathBuf, usize, usize);
type Params = (PathOrBin, PathOrBin, usize, usize);

fn new(params: Self::Params) -> Result<Self, Error> {
let (r1cs_path, wasm_path, state_len, external_inputs_len) = params;
let circom_wrapper = CircomWrapper::new(r1cs_path, wasm_path);
let circom_wrapper = CircomWrapper::new(r1cs_path, wasm_path)?;

let r1cs = circom_wrapper.extract_r1cs()?;
Ok(Self {
Expand Down Expand Up @@ -208,6 +208,7 @@ pub mod tests {
use super::*;
use ark_bn254::Fr;
use ark_relations::r1cs::ConstraintSystem;
use std::path::PathBuf;

// Tests the step_native function of CircomFCircuit.
#[test]
Expand All @@ -216,7 +217,8 @@ pub mod tests {
let wasm_path =
PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");

let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 1, 0)).unwrap(); // state_len:1, external_inputs_len:0
let circom_fcircuit =
CircomFCircuit::<Fr>::new((r1cs_path.into(), wasm_path.into(), 1, 0)).unwrap(); // state_len:1, external_inputs_len:0

let z_i = vec![Fr::from(3u32)];
let z_i1 = circom_fcircuit.step_native(1, z_i, vec![]).unwrap();
Expand All @@ -230,7 +232,8 @@ pub mod tests {
let wasm_path =
PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");

let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 1, 0)).unwrap(); // state_len:1, external_inputs_len:0
let circom_fcircuit =
CircomFCircuit::<Fr>::new((r1cs_path.into(), wasm_path.into(), 1, 0)).unwrap(); // state_len:1, external_inputs_len:0

let cs = ConstraintSystem::<Fr>::new_ref();

Expand All @@ -250,7 +253,8 @@ pub mod tests {
let wasm_path =
PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");

let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 1, 0)).unwrap(); // state_len:1, external_inputs_len:0
let circom_fcircuit =
CircomFCircuit::<Fr>::new((r1cs_path.into(), wasm_path.into(), 1, 0)).unwrap(); // state_len:1, external_inputs_len:0

// Allocates z_i1 by using step_native function.
let z_i = vec![Fr::from(3_u32)];
Expand All @@ -276,7 +280,8 @@ pub mod tests {
let wasm_path = PathBuf::from(
"./src/frontend/circom/test_folder/with_external_inputs_js/with_external_inputs.wasm",
);
let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 1, 2)).unwrap(); // state_len:1, external_inputs_len:2
let circom_fcircuit =
CircomFCircuit::<Fr>::new((r1cs_path.into(), wasm_path.into(), 1, 2)).unwrap(); // state_len:1, external_inputs_len:2
let cs = ConstraintSystem::<Fr>::new_ref();
let z_i = vec![Fr::from(3u32)];
let external_inputs = vec![Fr::from(6u32), Fr::from(7u32)];
Expand Down Expand Up @@ -319,7 +324,8 @@ pub mod tests {
let wasm_path = PathBuf::from(
"./src/frontend/circom/test_folder/no_external_inputs_js/no_external_inputs.wasm",
);
let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 3, 0)).unwrap();
let circom_fcircuit =
CircomFCircuit::<Fr>::new((r1cs_path.into(), wasm_path.into(), 3, 0)).unwrap();
let cs = ConstraintSystem::<Fr>::new_ref();
let z_i = vec![Fr::from(3u32), Fr::from(4u32), Fr::from(5u32)];
let z_i_var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_i.clone())).unwrap();
Expand Down Expand Up @@ -351,7 +357,8 @@ pub mod tests {
let wasm_path =
PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");

let mut circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 1, 0)).unwrap(); // state_len:1, external_inputs_len:0
let mut circom_fcircuit =
CircomFCircuit::<Fr>::new((r1cs_path.into(), wasm_path.into(), 1, 0)).unwrap(); // state_len:1, external_inputs_len:0

circom_fcircuit.set_custom_step_native(Rc::new(|_i, z_i, _external| {
let z = z_i[0];
Expand Down
56 changes: 39 additions & 17 deletions folding-schemes/src/frontend/circom/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,64 @@ use ark_circom::{
WitnessCalculator,
};
use ark_ff::{BigInteger, PrimeField};
use ark_serialize::Read;
use color_eyre::Result;
use num_bigint::{BigInt, Sign};
use std::{fs::File, io::BufReader, marker::PhantomData, path::PathBuf};
use std::{fs::File, io::Cursor, marker::PhantomData, path::PathBuf};

use crate::Error;
use crate::{utils::PathOrBin, Error};

// A struct that wraps Circom functionalities, allowing for extraction of R1CS and witnesses
// based on file paths to Circom's .r1cs and .wasm.
#[derive(Clone, Debug)]
pub struct CircomWrapper<F: PrimeField> {
r1cs_filepath: PathBuf,
wasm_filepath: PathBuf,
r1csfile_bytes: Vec<u8>,
wasmfile_bytes: Vec<u8>,
_marker: PhantomData<F>,
}

impl<F: PrimeField> CircomWrapper<F> {
// Creates a new instance of the CircomWrapper with the file paths.
pub fn new(r1cs_filepath: PathBuf, wasm_filepath: PathBuf) -> Self {
CircomWrapper {
r1cs_filepath,
wasm_filepath,
_marker: PhantomData,
pub fn new(r1cs: PathOrBin, wasm: PathOrBin) -> Result<Self, Error> {
match (r1cs, wasm) {
(PathOrBin::Path(r1cs_path), PathOrBin::Path(wasm_path)) => {
Self::new_from_path(r1cs_path, wasm_path)
}
(PathOrBin::Bin(r1cs_bin), PathOrBin::Bin(wasm_bin)) => Ok(Self {
r1csfile_bytes: r1cs_bin,
wasmfile_bytes: wasm_bin,
_marker: PhantomData,
}),
_ => unreachable!("You should pass the same enum branch for both inputs"),
}
}

// Creates a new instance of the CircomWrapper with the file paths.
fn new_from_path(r1cs_file_path: PathBuf, wasm_file_path: PathBuf) -> Result<Self, Error> {
let mut file = File::open(r1cs_file_path)?;
let metadata = File::metadata(&file)?;
let mut r1csfile_bytes = vec![0; metadata.len() as usize];
file.read_exact(&mut r1csfile_bytes)?;

let mut file = File::open(wasm_file_path)?;
let metadata = File::metadata(&file)?;
let mut wasmfile_bytes = vec![0; metadata.len() as usize];
file.read_exact(&mut wasmfile_bytes)?;

Ok(CircomWrapper {
r1csfile_bytes,
wasmfile_bytes,
_marker: PhantomData,
})
}

// Aggregated function to obtain R1CS and witness from Circom.
pub fn extract_r1cs_and_witness(
&self,
inputs: &[(String, Vec<BigInt>)],
) -> Result<(R1CS<F>, Option<Vec<F>>), Error> {
// Extracts the R1CS
let file = File::open(&self.r1cs_filepath)?;
let reader = BufReader::new(file);
let r1cs_file = r1cs_reader::R1CSFile::<F>::new(reader)?;
let r1cs_file = r1cs_reader::R1CSFile::<F>::new(Cursor::new(&self.r1csfile_bytes))?;
let r1cs = r1cs_reader::R1CS::<F>::from(r1cs_file);

// Extracts the witness vector
Expand All @@ -46,9 +70,7 @@ impl<F: PrimeField> CircomWrapper<F> {
}

pub fn extract_r1cs(&self) -> Result<R1CS<F>, Error> {
let file = File::open(&self.r1cs_filepath)?;
let reader = BufReader::new(file);
let r1cs_file = r1cs_reader::R1CSFile::<F>::new(reader)?;
let r1cs_file = r1cs_reader::R1CSFile::<F>::new(Cursor::new(&self.r1csfile_bytes))?;
let mut r1cs = r1cs_reader::R1CS::<F>::from(r1cs_file);
r1cs.wire_mapping = None;
Ok(r1cs)
Expand All @@ -75,7 +97,7 @@ impl<F: PrimeField> CircomWrapper<F> {
&self,
inputs: &[(String, Vec<BigInt>)],
) -> Result<Vec<BigInt>, Error> {
let mut calculator = WitnessCalculator::new(&self.wasm_filepath).map_err(|e| {
let mut calculator = WitnessCalculator::from_binary(&self.wasmfile_bytes).map_err(|e| {
Error::WitnessCalculationError(format!("Failed to create WitnessCalculator: {}", e))
})?;
calculator
Expand Down Expand Up @@ -139,7 +161,7 @@ mod tests {
PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");

let inputs = vec![("ivc_input".to_string(), vec![BigInt::from(3)])];
let wrapper = CircomWrapper::<Fr>::new(r1cs_path, wasm_path);
let wrapper = CircomWrapper::<Fr>::new(r1cs_path.into(), wasm_path.into()).unwrap();

let (r1cs, witness) = wrapper.extract_r1cs_and_witness(&inputs).unwrap();

Expand Down
17 changes: 11 additions & 6 deletions folding-schemes/src/frontend/noir/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;

use crate::Error;
use crate::{utils::PathOrBin, Error};

use super::FCircuit;
use acvm::{
Expand All @@ -16,7 +16,9 @@ use ark_ff::PrimeField;
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
use ark_relations::r1cs::ConstraintSynthesizer;
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
use arkworks_backend::{read_program_from_file, sonobe_bridge::AcirCircuitSonobe};
use arkworks_backend::{
read_program_from_binary, read_program_from_file, sonobe_bridge::AcirCircuitSonobe,
};

#[derive(Clone, Debug)]
pub struct NoirFCircuit<F: PrimeField> {
Expand All @@ -26,12 +28,15 @@ pub struct NoirFCircuit<F: PrimeField> {
}

impl<F: PrimeField> FCircuit<F> for NoirFCircuit<F> {
type Params = (String, usize, usize);
type Params = (PathOrBin, usize, usize);

fn new(params: Self::Params) -> Result<Self, crate::Error> {
let (path, state_len, external_inputs_len) = params;
let program =
read_program_from_file(path).map_err(|ee| Error::Other(format!("{:?}", ee)))?;
let (source, state_len, external_inputs_len) = params;
let program = match source {
PathOrBin::Path(path) => read_program_from_file(path),
PathOrBin::Bin(bytes) => read_program_from_binary(&bytes),
}
.map_err(|ee| Error::Other(format!("{:?}", ee)))?;
let circuit: Circuit<GenericFieldElement<F>> = program.functions[0].clone();
let ivc_input_length = circuit.public_parameters.0.len();
let ivc_return_length = circuit.return_values.0.len();
Expand Down
31 changes: 31 additions & 0 deletions folding-schemes/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::path::Path;
use std::path::PathBuf;

use ark_crypto_primitives::sponge::poseidon::PoseidonConfig;
use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::PrimeField;
Expand Down Expand Up @@ -100,3 +103,31 @@ where
&public_params_hash,
))
}

/// Tiny utility enum that allows to import circuits and wasm modules from files by passing their path
/// or passing their content already read.
///
/// This enum implements the [`From`] trait for both [`Path`], [`PathBuf`] and [`Vec<u8>`].
#[derive(Debug, Clone)]
pub enum PathOrBin {
Path(PathBuf),
Bin(Vec<u8>),
}

impl From<&Path> for PathOrBin {
fn from(value: &Path) -> Self {
PathOrBin::Path(value.into())
}
}

impl From<PathBuf> for PathOrBin {
fn from(value: PathBuf) -> Self {
PathOrBin::Path(value)
}
}

impl From<Vec<u8>> for PathOrBin {
fn from(value: Vec<u8>) -> Self {
PathOrBin::Bin(value)
}
}
Loading