mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-07 14:31:31 +01:00
feat: Minimal in-browser compatibility for the crate (#149)
* change: CircomWrapper constructor to use raw bytes * chore: tmp update to latest circom-compat * feat: Introduce PathOrBin to support in-browser CircomWrapper usage This changes the associated type `Params` of the `CircomFCircuit` to use the newly created `PathOrBin` type. This allows the user of the lib to directly send the binary of the files already read or instead, provide a path to it and let `sonobe` do the work. With this, Circom should be already usable from the browser if we allow JS to take care of reading the `.wasm` and `.r1cs` files. * feat: Introduce PathOrBin to support in-browser NoirFCircuit usage This commit temporarilly stands on top of https://github.com/dmpierre/arkworks_backend/pull/1 referenced as `rev`. This changes the associated type `Params` of the `CircomFCircuit` to use the newly created `PathOrBin` type. This allows the user of the lib to directly send the binary of the files already read or instead, provide a path to it and let `sonobe` do the work. With this, Noir should be already usable from the browser if we allow JS to take care of reading the `circuit.json` files * chore: Update deps to branch instead of `rev` * fix: use PathOrBin in examples * fix: clippy * fix: read file length for initializing vec size --------- Co-authored-by: dmpierre <pdaixmoreux@gmail.com>
This commit is contained in:
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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};
|
||||
|
||||
@@ -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 {
|
||||
@@ -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]
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
|
||||
@@ -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)];
|
||||
@@ -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)];
|
||||
@@ -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();
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::Error;
|
||||
use crate::{utils::PathOrBin, Error};
|
||||
|
||||
use super::FCircuit;
|
||||
use acvm::{
|
||||
@@ -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> {
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user