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

Circom wrapper's helper function #26

Merged
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2451f0b
r1cs_parser
yugocabrio Sep 17, 2023
90bbb33
z vector calculation
yugocabrio Sep 17, 2023
da1851f
test function done
yugocabrio Sep 17, 2023
1c2e949
improved
yugocabrio Sep 17, 2023
84b66b9
Brushuped
yugocabrio Sep 17, 2023
c9c2fdf
add comment
yugocabrio Sep 17, 2023
1e36b65
Added description of test_circuit
yugocabrio Sep 17, 2023
6fdac1d
found mistake
yugocabrio Sep 18, 2023
c5070e7
fixed cargo.toml
yugocabrio Sep 20, 2023
99f07f5
Imported ark-circom as crate
yugocabrio Sep 20, 2023
95576a2
improved l in R1CS as the number of public I/O
yugocabrio Sep 21, 2023
104d07b
separate test functions into success/failure and unify variable to pu…
yugocabrio Sep 25, 2023
1d70036
removed bn254 & abstracted to PrimeField, but still some work
yugocabrio Oct 1, 2023
877ee39
add comments and clean up code
yugocabrio Oct 1, 2023
1c41806
move ark-bn254 in dev-dependencies
yugocabrio Oct 5, 2023
10e0986
abstracted test function
yugocabrio Oct 5, 2023
a3fe2e4
fixed github action's error
yugocabrio Oct 5, 2023
25b9000
cargo fmt
yugocabrio Oct 5, 2023
f7e6194
remove convert_constraints_bigint_to_scalar function
yugocabrio Oct 10, 2023
c84c6a0
fixed n_cols
yugocabrio Oct 10, 2023
68bcc3d
fixed n_cols
yugocabrio Oct 10, 2023
c9ed92f
Add functionality to compile Circom files in tests
yugocabrio Oct 11, 2023
687d440
Remove test_circuit.r1cs
yugocabrio Oct 11, 2023
7b064e3
Introduce CircomFrontend trait and simplify with CircomWrapper struct
yugocabrio Oct 11, 2023
f25b987
deleted the CircomFrontend
yugocabrio Oct 12, 2023
233c0db
improved
yugocabrio Oct 12, 2023
23fe0f0
Merge branch 'main' into arkcircom_parser
CPerezz Oct 16, 2023
938b723
fixed clippy lint checks of github actions
yugocabrio Oct 16, 2023
e49c510
probably fixed github actions error by changing the github yaml
yugocabrio Oct 16, 2023
b5e61f4
fixed github yaml, fmt, and clippy
yugocabrio Oct 17, 2023
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
/target
Cargo.lock
Cargo.lock
# Circom generated files
/src/frontend/circom/test_folder/test_circuit.r1cs
/src/frontend/circom/test_folder/test_circuit_js/
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,28 @@ version = "0.1.0"
edition = "2021"

[dependencies]
ark-ec = "0.4.2"
ark-ec = "^0.4.0"
ark-ff = "^0.4.0"
ark-poly = "^0.4.0"
ark-std = "^0.4.0"
ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["r1cs", "sponge", "crh"] }
ark-relations = { version = "^0.4.0", default-features = false }
ark-r1cs-std = { version = "^0.4.0", default-features = false }
ark-circom = { git = "https://github.com/gakonst/ark-circom.git" }
thiserror = "1.0"
rayon = "1.7.0"
num-bigint = "0.4"
color-eyre = "=0.6.2"

# tmp imports for espresso's sumcheck
ark-serialize = "0.4.2"
ark-serialize = "^0.4.0"
espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", package="subroutines"}
espresso_transcript = {git="https://github.com/EspressoSystems/hyperplonk", package="transcript"}


[dev-dependencies]
ark-pallas = {version="0.4.0", features=["r1cs"]}
ark-vesta = {version="0.4.0"}
ark-bn254 = "0.4.0"

[features]
default = ["parallel", "nova", "hypernova"]
Expand Down
234 changes: 234 additions & 0 deletions src/frontend/circom/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
use std::{error::Error, fs::File, io::BufReader, marker::PhantomData, path::PathBuf};

use color_eyre::Result;
use num_bigint::BigInt;

use ark_circom::{circom::r1cs_reader, WitnessCalculator};
use ark_ec::pairing::Pairing;
use ark_ff::PrimeField;

use crate::ccs::r1cs::R1CS;
use crate::utils::vec::SparseMatrix;

// Define the sparse matrices on PrimeFiled.
pub type Constraints<F> = (ConstraintVec<F>, ConstraintVec<F>, ConstraintVec<F>);
pub type ConstraintVec<F> = Vec<(usize, F)>;
pub type ExtractedConstraintsResult<F> =
Result<(Vec<Constraints<F>>, usize, usize), Box<dyn Error>>;

// A struct that wraps Circom functionalities, allowing for extraction of R1CS and witnesses
// based on file paths to Circom's .r1cs and .wasm.
pub struct CircomWrapper<E: Pairing> {
r1cs_filepath: PathBuf,
wasm_filepath: PathBuf,
_marker: PhantomData<E>,
}

impl<E: Pairing> CircomWrapper<E> {
// 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,
}
}

// Aggregates multiple functions to obtain R1CS and Z as defined in folding-schemes from Circom.
pub fn extract_r1cs_and_z(
&self,
inputs: &[(String, Vec<BigInt>)],
) -> Result<(R1CS<E::ScalarField>, Vec<E::ScalarField>), Box<dyn Error>> {
let (constraints, pub_io_len, num_variables) = self.extract_constraints_from_r1cs()?;
let witness = self.calculate_witness(inputs)?;
self.circom_to_folding_schemes_r1cs_and_z(constraints, &witness, pub_io_len, num_variables)
}

// Extracts constraints from the r1cs file.
pub fn extract_constraints_from_r1cs(&self) -> ExtractedConstraintsResult<E::ScalarField>
where
E: Pairing,
{
// Opens the .r1cs file and create a reader.
let file = File::open(&self.r1cs_filepath)?;
let reader = BufReader::new(file);

// Reads the R1CS file and extract the constraints directly.
let r1cs_file = r1cs_reader::R1CSFile::<E>::new(reader)?;
let pub_io_len = (r1cs_file.header.n_pub_in + r1cs_file.header.n_pub_out) as usize;
let r1cs = r1cs_reader::R1CS::<E>::from(r1cs_file);
let num_variables = r1cs.num_variables;
let constraints: Vec<Constraints<E::ScalarField>> = r1cs.constraints;

Ok((constraints, pub_io_len, num_variables))
}

// Converts a set of constraints from ark-circom into R1CS format of folding-schemes.
pub fn convert_to_folding_schemes_r1cs<F>(
&self,
constraints: Vec<Constraints<F>>,
pub_io_len: usize,
num_variables: usize,
) -> R1CS<F>
where
F: PrimeField,
{
let mut a_matrix: Vec<Vec<(F, usize)>> = Vec::new();
let mut b_matrix: Vec<Vec<(F, usize)>> = Vec::new();
let mut c_matrix: Vec<Vec<(F, usize)>> = Vec::new();

let n_rows = constraints.len();

for (ai, bi, ci) in constraints {
a_matrix.push(
ai.into_iter()
.map(|(index, scalar)| (scalar, index))
.collect(),
);
b_matrix.push(
bi.into_iter()
.map(|(index, scalar)| (scalar, index))
.collect(),
);
c_matrix.push(
ci.into_iter()
.map(|(index, scalar)| (scalar, index))
.collect(),
);
}

let l = pub_io_len;
let n_cols = num_variables;

let A = SparseMatrix {
n_rows,
n_cols,
coeffs: a_matrix,
};
let B = SparseMatrix {
n_rows,
n_cols,
coeffs: b_matrix,
};
let C = SparseMatrix {
n_rows,
n_cols,
coeffs: c_matrix,
};

R1CS::<F> { l, A, B, C }
}

// Calculates the witness given the Wasm filepath and inputs.
pub fn calculate_witness(&self, inputs: &[(String, Vec<BigInt>)]) -> Result<Vec<BigInt>> {
let mut calculator = WitnessCalculator::new(&self.wasm_filepath)?;
calculator.calculate_witness(inputs.to_vec().into_iter(), true)
}

// Converts a num_bigint input to `PrimeField`'s BigInt.
pub fn num_bigint_to_ark_bigint<F: PrimeField>(
&self,
value: &BigInt,
) -> Result<F::BigInt, Box<dyn Error>> {
let big_uint = value
.to_biguint()
.ok_or_else(|| "BigInt is negative".to_string())?;
F::BigInt::try_from(big_uint).map_err(|_| "BigInt conversion failed".to_string().into())
}

// Converts R1CS constraints and witness from ark-circom format
// into folding-schemes R1CS and z format.
pub fn circom_to_folding_schemes_r1cs_and_z<F>(
&self,
constraints: Vec<Constraints<F>>,
witness: &[BigInt],
pub_io_len: usize,
num_variables: usize,
) -> Result<(R1CS<F>, Vec<F>), Box<dyn Error>>
where
F: PrimeField,
{
let folding_schemes_r1cs =
self.convert_to_folding_schemes_r1cs(constraints, pub_io_len, num_variables);

let z: Result<Vec<F>, _> = witness
.iter()
.map(|big_int| {
let ark_big_int = self.num_bigint_to_ark_bigint::<F>(big_int)?;
F::from_bigint(ark_big_int).ok_or_else(|| {
"Failed to convert bigint to field element"
.to_string()
.into()
})
})
.collect();

z.map(|z| (folding_schemes_r1cs, z))
}
}

#[cfg(test)]
mod tests {
use crate::frontend::circom::CircomWrapper;
use ark_bn254::Bn254;
use num_bigint::BigInt;
use std::{env, process::Command};

// Compiles the .circom file to generate the .r1cs and .wasm files.
fn compile_circom_to_r1cs_and_wasm(base_path: &std::path::Path) {
let script_path = base_path.join("compile.sh");

let status = Command::new("bash")
.arg(script_path)
.status()
.expect("Failed to execute circom compilation script.");

assert!(status.success(), "Circom compilation script failed.");
}

/*
test_circuit represents 35 = x^3 + x + 5 in test_folder/test_circuit.circom.
In the test_circom_conversion_success function, x is assigned the value 3, which satisfies the R1CS correctly.
In the test_circom_conversion_failure function, x is assigned the value 6, which doesn't satisfy the R1CS.
*/

fn test_circom_conversion_logic(expect_success: bool, inputs: Vec<(String, Vec<BigInt>)>) {
let current_dir = env::current_dir().unwrap();
let base_path = current_dir.join("src/frontend/circom/test_folder");

compile_circom_to_r1cs_and_wasm(&base_path);

let r1cs_filepath = base_path.join("test_circuit.r1cs");
let wasm_filepath = base_path.join("test_circuit_js/test_circuit.wasm");

assert!(r1cs_filepath.exists());
assert!(wasm_filepath.exists());

let circom_wrapper = CircomWrapper::<Bn254>::new(r1cs_filepath, wasm_filepath);

let (r1cs, z) = circom_wrapper
.extract_r1cs_and_z(&inputs)
.expect("Error processing input");

// Checks the relationship of R1CS.
let check_result = std::panic::catch_unwind(|| {
r1cs.check_relation(&z).unwrap();
});

match (expect_success, check_result) {
(true, Ok(_)) => {}
(false, Err(_)) => {}
(true, Err(_)) => panic!("Expected success but got a failure."),
(false, Ok(_)) => panic!("Expected a failure but got success."),
}
}

#[test]
fn test_circom_conversion() {
// expect it to pass for correct inputs.
test_circom_conversion_logic(true, vec![("step_in".to_string(), vec![BigInt::from(3)])]);

// expect it to fail for incorrect inputs.
test_circom_conversion_logic(false, vec![("step_in".to_string(), vec![BigInt::from(7)])]);
}
}
2 changes: 2 additions & 0 deletions src/frontend/circom/test_folder/compile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
circom ./src/frontend/circom/test_folder/test_circuit.circom --r1cs --wasm --prime bn128 --output ./src/frontend/circom/test_folder/
yugocabrio marked this conversation as resolved.
Show resolved Hide resolved
13 changes: 13 additions & 0 deletions src/frontend/circom/test_folder/test_circuit.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pragma circom 2.0.3;

template Example () {
signal input step_in;
signal output step_out;
signal temp;

temp <== step_in * step_in;
step_out <== temp * step_in + step_in + 5;
step_out === 35;
}

component main = Example();
1 change: 1 addition & 0 deletions src/frontend/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod arkworks;
pub mod circom;
Loading