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 14 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
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,25 @@ 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"] }
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" }
ark-bn254 = "0.4.0"
arnaucube marked this conversation as resolved.
Show resolved Hide resolved
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"}
Expand Down
211 changes: 211 additions & 0 deletions src/frontend/circom/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
use std::{fs::File, io::BufReader, path::PathBuf, error::Error};

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

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

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)>;

// Convert the generic type of constraints from Pairing's ScalarField to PrimeField.
pub fn convert_constraints_bigint_to_scalar<E, F>(constraints: Constraints<E::ScalarField>) -> Constraints<F>
where E: Pairing,
F: PrimeField,
{
let convert_vec = |vec: ConstraintVec<E::ScalarField>| -> ConstraintVec<F> {
vec.into_iter()
.filter_map(|(index, element)| {
// Convert element into BigInt, then to BigUint, then to the destination PrimeField
let bigint_e: <<E as Pairing>::ScalarField as PrimeField>::BigInt = element.into_bigint();
let generic_biguint: num_bigint::BigUint = bigint_e.into();
let f_element: F = F::from(generic_biguint);
Some((index, f_element))
})
.collect()
};

(convert_vec(constraints.0), convert_vec(constraints.1), convert_vec(constraints.2))
}

// Extract constraints on Pairing's ScalarField from an .r1cs file
// and convert them into constraints on PrimeField
pub fn extract_constraints_from_r1cs<E, F>(
r1cs_filepath: &PathBuf
) -> Result<(Vec<Constraints<F>>, usize), Box<dyn Error>>
where
E: Pairing,
F: PrimeField,
{
// Open the .r1cs file and create a reader
let file = File::open(r1cs_filepath)?;
let reader = BufReader::new(file);

// Read the R1CS file, extract the constraints, and then convert them into new constraints on PrimeField
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 converted_constraints: Vec<Constraints<F>> = r1cs.constraints.into_iter()
.map(|c| convert_constraints_bigint_to_scalar::<E, F>(c))
.collect();

Ok((converted_constraints, pub_io_len))
}

// Convert a set of constraints from ark-circom into R1CS format of folding_schemes
pub fn convert_to_folding_schemes_r1cs<F>(constraints: Vec<Constraints<F>>, pub_io_len: 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 = a_matrix.first().map(|vec| vec.len()).unwrap_or(0);
yugocabrio marked this conversation as resolved.
Show resolved Hide resolved

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,
}
}

// Calculate the witness given the WASM filepath and inputs.
pub fn calculate_witness<I: IntoIterator<Item = (String, Vec<BigInt>)>>(wasm_filepath: &PathBuf, inputs: I) -> Result<Vec<BigInt>> {
let mut calculator = WitnessCalculator::new(wasm_filepath.clone())?;
calculator.calculate_witness(inputs, true)
}

// Convert a num_bigint's BigInt to PrimeField's BigInt
fn num_bigint_to_ark_bigint<F: PrimeField>(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())
}

// Convert 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>(
constraints: Vec<Constraints<F>>,
witness: &Vec<BigInt>,
pub_io_len: usize,
) -> Result<(R1CS<F>, Vec<F>), Box<dyn Error>>
where
F: PrimeField,
{
let folding_schemes_r1cs = convert_to_folding_schemes_r1cs(constraints, pub_io_len);

let z: Result<Vec<F>, _> = witness
.iter()
.map(|big_int| {
let ark_big_int = 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 super::*;
use num_bigint::BigInt;
use std::env;
use ark_bn254::{Bn254, Fr};

/*
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.
*/

#[test]
fn test_circom_conversion_success() {
let current_dir = env::current_dir().unwrap();
let base_path = current_dir.join("src/frontend/circom/test_folder");
let r1cs_filepath = base_path.join("test_circuit.r1cs");
let wasm_filepath = base_path.join("test_circuit.wasm");

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

let (constraints, pub_io_len) = extract_constraints_from_r1cs::<Bn254, Fr>(&r1cs_filepath).expect("Error");
assert!(!constraints.is_empty());

let converted_constraints: Vec<Constraints<Fr>> = constraints
.iter()
.map(|constraint| convert_constraints_bigint_to_scalar::<Bn254, Fr>(constraint.clone()))
.collect();
assert_eq!(constraints.len(), converted_constraints.len());

let inputs = vec![("step_in".to_string(), vec![BigInt::from(3)])];
let witness = calculate_witness(&wasm_filepath, inputs).expect("Error");
assert!(!witness.is_empty());

let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, pub_io_len)
.expect("Error");
assert!(!z.is_empty());
r1cs.check_relation(&z).expect("Error");
}

#[test]
fn test_circom_conversion_failure() {
let current_dir = env::current_dir().unwrap();
let base_path = current_dir.join("src/frontend/circom/test_folder");
let r1cs_filepath = base_path.join("test_circuit.r1cs");
let wasm_filepath = base_path.join("test_circuit.wasm");

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

let (constraints, pub_io_len) = extract_constraints_from_r1cs::<Bn254, Fr>(&r1cs_filepath).expect("Error");
assert!(!constraints.is_empty());

let converted_constraints: Vec<Constraints<Fr>> = constraints
.iter()
.map(|constraint| convert_constraints_bigint_to_scalar::<Bn254, Fr>(constraint.clone()))
.collect();
assert_eq!(constraints.len(), converted_constraints.len());

let inputs = vec![("step_in".to_string(), vec![BigInt::from(6)])];
let witness = calculate_witness(&wasm_filepath, inputs).expect("Error");
assert!(!witness.is_empty());

let (r1cs, z) = circom_to_folding_schemes_r1cs_and_z(converted_constraints, &witness, pub_io_len)
.expect("Error");
assert!(!z.is_empty());
r1cs.check_relation(&z).expect("Error");
}

}
arnaucube 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();
Binary file added src/frontend/circom/test_folder/test_circuit.r1cs
Binary file not shown.
Binary file added src/frontend/circom/test_folder/test_circuit.wasm
Binary file not shown.
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