Update ark-circom for arkworks 0.4.0 (#43)

This commit is contained in:
Deepak Maram 2023-03-16 17:42:33 -04:00 committed by GitHub
parent 35ce5a909e
commit b892c62597
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 718 additions and 732 deletions

1133
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -12,14 +12,15 @@ num-traits = { version = "0.2.0", default-features = false }
num-bigint = { version = "0.4", default-features = false, features = ["rand"] } num-bigint = { version = "0.4", default-features = false, features = ["rand"] }
# ZKP Generation # ZKP Generation
ark-ec = { version = "0.3.0", default-features = false, features = ["parallel"] } ark-crypto-primitives = { version = "0.4.0" }
ark-ff = { version = "0.3.0", default-features = false, features = ["parallel", "asm"] } ark-ec = { version = "0.4.1", default-features = false, features = ["parallel"] }
ark-std = { version = "0.3.0", default-features = false, features = ["parallel"] } ark-ff = { version = "0.4.1", default-features = false, features = ["parallel", "asm"] }
ark-bn254 = { version = "0.3.0" } ark-std = { version = "0.4.0", default-features = false, features = ["parallel"] }
ark-groth16 = { git = "https://github.com/arkworks-rs/groth16", rev = "765817f", features = ["parallel"] } ark-bn254 = { version = "0.4.0" }
ark-poly = { version = "^0.3.0", default-features = false, features = ["parallel"] } ark-groth16 = { version = "0.4.0", features = ["parallel"] }
ark-relations = { version = "0.3.0", default-features = false } ark-poly = { version = "0.4.1", default-features = false, features = ["parallel"] }
ark-serialize = { version = "0.3.0", default-features = false } ark-relations = { version = "0.4.0", default-features = false }
ark-serialize = { version = "0.4.1", default-features = false }
# decoding of data # decoding of data
hex = "0.4.3" hex = "0.4.3"
@ -49,4 +50,4 @@ harness = false
bench-complex-all = [] bench-complex-all = []
circom-2 = [] circom-2 = []
ethereum = ["ethers-core"] ethereum = ["ethers-core"]
default = ["ethereum"] default = ["circom-2", "ethereum"]

View File

@ -35,7 +35,7 @@ let circom = builder.setup();
// Run a trusted setup // Run a trusted setup
let mut rng = thread_rng(); let mut rng = thread_rng();
let params = generate_random_parameters::<Bn254, _, _>(circom, &mut rng)?; let params = generate_random_parameters_with_reduction(circom, &mut rng)?;
// Get the populated instance of the circuit with the witness // Get the populated instance of the circuit with the witness
let circom = builder.build()?; let circom = builder.build()?;
@ -43,11 +43,11 @@ let circom = builder.build()?;
let inputs = circom.get_public_inputs().unwrap(); let inputs = circom.get_public_inputs().unwrap();
// Generate the proof // Generate the proof
let proof = prove(circom, &params, &mut rng)?; let proof = prove(&params, circom, &mut rng)?;
// Check that the proof is valid // Check that the proof is valid
let pvk = prepare_verifying_key(&params.vk); let pvk = process_vk(&params.vk)?;
let verified = verify_proof(&pvk, &proof, &inputs)?; let verified = verify_with_processed_vk(&pvk, &inputs, &proof)?;
assert!(verified); assert!(verified);
``` ```

View File

@ -1,10 +1,11 @@
use ark_crypto_primitives::snark::SNARK;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use ark_circom::{read_zkey, CircomReduction, WitnessCalculator}; use ark_circom::{read_zkey, CircomReduction, WitnessCalculator};
use ark_std::rand::thread_rng; use ark_std::rand::thread_rng;
use ark_bn254::Bn254; use ark_bn254::Bn254;
use ark_groth16::{create_proof_with_reduction_and_matrices, prepare_verifying_key, verify_proof}; use ark_groth16::Groth16;
use std::{collections::HashMap, fs::File}; use std::{collections::HashMap, fs::File};
@ -15,7 +16,7 @@ fn bench_groth(c: &mut Criterion, num_validators: u32, num_constraints: u32) {
"./test-vectors/complex-circuit/complex-circuit-{}-{}.zkey", "./test-vectors/complex-circuit/complex-circuit-{}-{}.zkey",
i, j i, j
); );
let mut file = File::open(&path).unwrap(); let mut file = File::open(path).unwrap();
let (params, matrices) = read_zkey(&mut file).unwrap(); let (params, matrices) = read_zkey(&mut file).unwrap();
let num_inputs = matrices.num_instance_variables; let num_inputs = matrices.num_instance_variables;
let num_constraints = matrices.num_constraints; let num_constraints = matrices.num_constraints;
@ -28,7 +29,7 @@ fn bench_groth(c: &mut Criterion, num_validators: u32, num_constraints: u32) {
inputs inputs
}; };
let mut wtns = WitnessCalculator::new(&format!( let mut wtns = WitnessCalculator::new(format!(
"./test-vectors/complex-circuit/complex-circuit-{}-{}.wasm", "./test-vectors/complex-circuit/complex-circuit-{}-{}.wasm",
i, j i, j
)) ))
@ -44,7 +45,7 @@ fn bench_groth(c: &mut Criterion, num_validators: u32, num_constraints: u32) {
let r = ark_bn254::Fr::rand(rng); let r = ark_bn254::Fr::rand(rng);
let s = ark_bn254::Fr::rand(rng); let s = ark_bn254::Fr::rand(rng);
let proof = create_proof_with_reduction_and_matrices::<_, CircomReduction>( let proof = Groth16::<Bn254, CircomReduction>::create_proof_with_reduction_and_matrices(
&params, &params,
r, r,
s, s,
@ -55,16 +56,16 @@ fn bench_groth(c: &mut Criterion, num_validators: u32, num_constraints: u32) {
) )
.unwrap(); .unwrap();
let pvk = prepare_verifying_key(&params.vk); let pvk = Groth16::<Bn254>::process_vk(&params.vk).unwrap();
let inputs = &full_assignment[1..num_inputs]; let inputs = &full_assignment[1..num_inputs];
let verified = verify_proof(&pvk, &proof, inputs).unwrap(); let verified = Groth16::<Bn254>::verify_with_processed_vk(&pvk, inputs, &proof).unwrap();
assert!(verified); assert!(verified);
c.bench_function(&format!("groth proof {} {}", i, j), |b| { c.bench_function(&format!("groth proof {} {}", i, j), |b| {
b.iter(|| { b.iter(|| {
black_box( black_box(
create_proof_with_reduction_and_matrices::<_, CircomReduction>( Groth16::<Bn254, CircomReduction>::create_proof_with_reduction_and_matrices(
&params, &params,
r, r,
s, s,

View File

@ -1,4 +1,4 @@
use ark_ec::PairingEngine; use ark_ec::pairing::Pairing;
use std::{fs::File, path::Path}; use std::{fs::File, path::Path};
use super::{CircomCircuit, R1CS}; use super::{CircomCircuit, R1CS};
@ -10,20 +10,20 @@ use crate::{circom::R1CSFile, witness::WitnessCalculator};
use color_eyre::Result; use color_eyre::Result;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CircomBuilder<E: PairingEngine> { pub struct CircomBuilder<E: Pairing> {
pub cfg: CircomConfig<E>, pub cfg: CircomConfig<E>,
pub inputs: HashMap<String, Vec<BigInt>>, pub inputs: HashMap<String, Vec<BigInt>>,
} }
// Add utils for creating this from files / directly from bytes // Add utils for creating this from files / directly from bytes
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CircomConfig<E: PairingEngine> { pub struct CircomConfig<E: Pairing> {
pub r1cs: R1CS<E>, pub r1cs: R1CS<E>,
pub wtns: WitnessCalculator, pub wtns: WitnessCalculator,
pub sanity_check: bool, pub sanity_check: bool,
} }
impl<E: PairingEngine> CircomConfig<E> { impl<E: Pairing> CircomConfig<E> {
pub fn new(wtns: impl AsRef<Path>, r1cs: impl AsRef<Path>) -> Result<Self> { pub fn new(wtns: impl AsRef<Path>, r1cs: impl AsRef<Path>) -> Result<Self> {
let wtns = WitnessCalculator::new(wtns).unwrap(); let wtns = WitnessCalculator::new(wtns).unwrap();
let reader = File::open(r1cs)?; let reader = File::open(r1cs)?;
@ -36,7 +36,7 @@ impl<E: PairingEngine> CircomConfig<E> {
} }
} }
impl<E: PairingEngine> CircomBuilder<E> { impl<E: Pairing> CircomBuilder<E> {
/// Instantiates a new builder using the provided WitnessGenerator and R1CS files /// Instantiates a new builder using the provided WitnessGenerator and R1CS files
/// for your circuit /// for your circuit
pub fn new(cfg: CircomConfig<E>) -> Self { pub fn new(cfg: CircomConfig<E>) -> Self {
@ -81,7 +81,7 @@ impl<E: PairingEngine> CircomBuilder<E> {
// sanity check // sanity check
debug_assert!({ debug_assert!({
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
let cs = ConstraintSystem::<E::Fr>::new_ref(); let cs = ConstraintSystem::<E::ScalarField>::new_ref();
circom.clone().generate_constraints(cs.clone()).unwrap(); circom.clone().generate_constraints(cs.clone()).unwrap();
let is_satisfied = cs.is_satisfied().unwrap(); let is_satisfied = cs.is_satisfied().unwrap();
if !is_satisfied { if !is_satisfied {

View File

@ -1,4 +1,4 @@
use ark_ec::PairingEngine; use ark_ec::pairing::Pairing;
use ark_relations::r1cs::{ use ark_relations::r1cs::{
ConstraintSynthesizer, ConstraintSystemRef, LinearCombination, SynthesisError, Variable, ConstraintSynthesizer, ConstraintSystemRef, LinearCombination, SynthesisError, Variable,
}; };
@ -8,13 +8,13 @@ use super::R1CS;
use color_eyre::Result; use color_eyre::Result;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CircomCircuit<E: PairingEngine> { pub struct CircomCircuit<E: Pairing> {
pub r1cs: R1CS<E>, pub r1cs: R1CS<E>,
pub witness: Option<Vec<E::Fr>>, pub witness: Option<Vec<E::ScalarField>>,
} }
impl<E: PairingEngine> CircomCircuit<E> { impl<E: Pairing> CircomCircuit<E> {
pub fn get_public_inputs(&self) -> Option<Vec<E::Fr>> { pub fn get_public_inputs(&self) -> Option<Vec<E::ScalarField>> {
match &self.witness { match &self.witness {
None => None, None => None,
Some(w) => match &self.r1cs.wire_mapping { Some(w) => match &self.r1cs.wire_mapping {
@ -25,8 +25,11 @@ impl<E: PairingEngine> CircomCircuit<E> {
} }
} }
impl<E: PairingEngine> ConstraintSynthesizer<E::Fr> for CircomCircuit<E> { impl<E: Pairing> ConstraintSynthesizer<E::ScalarField> for CircomCircuit<E> {
fn generate_constraints(self, cs: ConstraintSystemRef<E::Fr>) -> Result<(), SynthesisError> { fn generate_constraints(
self,
cs: ConstraintSystemRef<E::ScalarField>,
) -> Result<(), SynthesisError> {
let witness = &self.witness; let witness = &self.witness;
let wire_mapping = &self.r1cs.wire_mapping; let wire_mapping = &self.r1cs.wire_mapping;
@ -34,7 +37,7 @@ impl<E: PairingEngine> ConstraintSynthesizer<E::Fr> for CircomCircuit<E> {
for i in 1..self.r1cs.num_inputs { for i in 1..self.r1cs.num_inputs {
cs.new_input_variable(|| { cs.new_input_variable(|| {
Ok(match witness { Ok(match witness {
None => E::Fr::from(1u32), None => E::ScalarField::from(1u32),
Some(w) => match wire_mapping { Some(w) => match wire_mapping {
Some(m) => w[m[i]], Some(m) => w[m[i]],
None => w[i], None => w[i],
@ -46,7 +49,7 @@ impl<E: PairingEngine> ConstraintSynthesizer<E::Fr> for CircomCircuit<E> {
for i in 0..self.r1cs.num_aux { for i in 0..self.r1cs.num_aux {
cs.new_witness_variable(|| { cs.new_witness_variable(|| {
Ok(match witness { Ok(match witness {
None => E::Fr::from(1u32), None => E::ScalarField::from(1u32),
Some(w) => match wire_mapping { Some(w) => match wire_mapping {
Some(m) => w[m[i + self.r1cs.num_inputs]], Some(m) => w[m[i + self.r1cs.num_inputs]],
None => w[i + self.r1cs.num_inputs], None => w[i + self.r1cs.num_inputs],
@ -62,10 +65,12 @@ impl<E: PairingEngine> ConstraintSynthesizer<E::Fr> for CircomCircuit<E> {
Variable::Witness(index - self.r1cs.num_inputs) Variable::Witness(index - self.r1cs.num_inputs)
} }
}; };
let make_lc = |lc_data: &[(usize, E::Fr)]| { let make_lc = |lc_data: &[(usize, E::ScalarField)]| {
lc_data.iter().fold( lc_data.iter().fold(
LinearCombination::<E::Fr>::zero(), LinearCombination::<E::ScalarField>::zero(),
|lc: LinearCombination<E::Fr>, (index, coeff)| lc + (*coeff, make_index(*index)), |lc: LinearCombination<E::ScalarField>, (index, coeff)| {
lc + (*coeff, make_index(*index))
},
) )
}; };

View File

@ -1,4 +1,4 @@
use ark_ec::PairingEngine; use ark_ec::pairing::Pairing;
pub mod r1cs_reader; pub mod r1cs_reader;
pub use r1cs_reader::{R1CSFile, R1CS}; pub use r1cs_reader::{R1CSFile, R1CS};
@ -13,4 +13,4 @@ mod qap;
pub use qap::CircomReduction; pub use qap::CircomReduction;
pub type Constraints<E> = (ConstraintVec<E>, ConstraintVec<E>, ConstraintVec<E>); pub type Constraints<E> = (ConstraintVec<E>, ConstraintVec<E>, ConstraintVec<E>);
pub type ConstraintVec<E> = Vec<(usize, <E as PairingEngine>::Fr)>; pub type ConstraintVec<E> = Vec<(usize, <E as Pairing>::ScalarField)>;

View File

@ -1,5 +1,5 @@
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_groth16::r1cs_to_qap::{evaluate_constraint, LibsnarkReduction, R1CStoQAP}; use ark_groth16::r1cs_to_qap::{evaluate_constraint, LibsnarkReduction, R1CSToQAP};
use ark_poly::EvaluationDomain; use ark_poly::EvaluationDomain;
use ark_relations::r1cs::{ConstraintMatrices, ConstraintSystemRef, SynthesisError}; use ark_relations::r1cs::{ConstraintMatrices, ConstraintSystemRef, SynthesisError};
use ark_std::{cfg_into_iter, cfg_iter, cfg_iter_mut, vec}; use ark_std::{cfg_into_iter, cfg_iter, cfg_iter_mut, vec};
@ -11,7 +11,7 @@ use ark_std::{cfg_into_iter, cfg_iter, cfg_iter_mut, vec};
/// in that domain. This serves as HZ when computing the C proof element. /// in that domain. This serves as HZ when computing the C proof element.
pub struct CircomReduction; pub struct CircomReduction;
impl R1CStoQAP for CircomReduction { impl R1CSToQAP for CircomReduction {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn instance_map_with_evaluation<F: PrimeField, D: EvaluationDomain<F>>( fn instance_map_with_evaluation<F: PrimeField, D: EvaluationDomain<F>>(
cs: ConstraintSystemRef<F>, cs: ConstraintSystemRef<F>,

View File

@ -2,18 +2,20 @@
//! Copied from <https://github.com/poma/zkutil> //! Copied from <https://github.com/poma/zkutil>
//! Spec: <https://github.com/iden3/r1csfile/blob/master/doc/r1cs_bin_format.md> //! Spec: <https://github.com/iden3/r1csfile/blob/master/doc/r1cs_bin_format.md>
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{Error, ErrorKind, Result}; use std::io::{Error, ErrorKind};
use ark_ec::PairingEngine; use ark_ec::pairing::Pairing;
use ark_ff::FromBytes; use ark_serialize::{CanonicalDeserialize, SerializationError, SerializationError::IoError};
use ark_std::io::{Read, Seek, SeekFrom}; use ark_std::io::{Read, Seek, SeekFrom};
use std::collections::HashMap; use std::collections::HashMap;
type IoResult<T> = Result<T, SerializationError>;
use super::{ConstraintVec, Constraints}; use super::{ConstraintVec, Constraints};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct R1CS<E: PairingEngine> { pub struct R1CS<E: Pairing> {
pub num_inputs: usize, pub num_inputs: usize,
pub num_aux: usize, pub num_aux: usize,
pub num_variables: usize, pub num_variables: usize,
@ -21,7 +23,7 @@ pub struct R1CS<E: PairingEngine> {
pub wire_mapping: Option<Vec<usize>>, pub wire_mapping: Option<Vec<usize>>,
} }
impl<E: PairingEngine> From<R1CSFile<E>> for R1CS<E> { impl<E: Pairing> From<R1CSFile<E>> for R1CS<E> {
fn from(file: R1CSFile<E>) -> Self { fn from(file: R1CSFile<E>) -> Self {
let num_inputs = (1 + file.header.n_pub_in + file.header.n_pub_out) as usize; 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_variables = file.header.n_wires as usize;
@ -36,30 +38,35 @@ impl<E: PairingEngine> From<R1CSFile<E>> for R1CS<E> {
} }
} }
pub struct R1CSFile<E: PairingEngine> { pub struct R1CSFile<E: Pairing> {
pub version: u32, pub version: u32,
pub header: Header, pub header: Header,
pub constraints: Vec<Constraints<E>>, pub constraints: Vec<Constraints<E>>,
pub wire_mapping: Vec<u64>, pub wire_mapping: Vec<u64>,
} }
impl<E: PairingEngine> R1CSFile<E> { impl<E: Pairing> R1CSFile<E> {
/// reader must implement the Seek trait, for example with a Cursor /// reader must implement the Seek trait, for example with a Cursor
/// ///
/// ```rust,ignore /// ```rust,ignore
/// let reader = BufReader::new(Cursor::new(&data[..])); /// let reader = BufReader::new(Cursor::new(&data[..]));
/// ``` /// ```
pub fn new<R: Read + Seek>(mut reader: R) -> Result<R1CSFile<E>> { pub fn new<R: Read + Seek>(mut reader: R) -> IoResult<R1CSFile<E>> {
let mut magic = [0u8; 4]; let mut magic = [0u8; 4];
reader.read_exact(&mut magic)?; reader.read_exact(&mut magic)?;
if magic != [0x72, 0x31, 0x63, 0x73] { if magic != [0x72, 0x31, 0x63, 0x73] {
// magic = "r1cs" return Err(IoError(Error::new(
return Err(Error::new(ErrorKind::InvalidData, "Invalid magic number")); ErrorKind::InvalidData,
"Invalid magic number",
)));
} }
let version = reader.read_u32::<LittleEndian>()?; let version = reader.read_u32::<LittleEndian>()?;
if version != 1 { if version != 1 {
return Err(Error::new(ErrorKind::InvalidData, "Unsupported version")); return Err(IoError(Error::new(
ErrorKind::InvalidData,
"Unsupported version",
)));
} }
let num_sections = reader.read_u32::<LittleEndian>()?; let num_sections = reader.read_u32::<LittleEndian>()?;
@ -151,20 +158,20 @@ pub struct Header {
} }
impl Header { impl Header {
fn new<R: Read>(mut reader: R, size: u64) -> Result<Header> { fn new<R: Read>(mut reader: R, size: u64) -> IoResult<Header> {
let field_size = reader.read_u32::<LittleEndian>()?; let field_size = reader.read_u32::<LittleEndian>()?;
if field_size != 32 { if field_size != 32 {
return Err(Error::new( return Err(IoError(Error::new(
ErrorKind::InvalidData, ErrorKind::InvalidData,
"This parser only supports 32-byte fields", "This parser only supports 32-byte fields",
)); )));
} }
if size != 32 + field_size as u64 { if size != 32 + field_size as u64 {
return Err(Error::new( return Err(IoError(Error::new(
ErrorKind::InvalidData, ErrorKind::InvalidData,
"Invalid header section size", "Invalid header section size",
)); )));
} }
let mut prime_size = vec![0u8; field_size as usize]; let mut prime_size = vec![0u8; field_size as usize];
@ -174,10 +181,10 @@ impl Header {
!= hex::decode("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") != hex::decode("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430")
.unwrap() .unwrap()
{ {
return Err(Error::new( return Err(IoError(Error::new(
ErrorKind::InvalidData, ErrorKind::InvalidData,
"This parser only supports bn256", "This parser only supports bn256",
)); )));
} }
Ok(Header { Ok(Header {
@ -193,22 +200,22 @@ impl Header {
} }
} }
fn read_constraint_vec<R: Read, E: PairingEngine>(mut reader: R) -> Result<ConstraintVec<E>> { fn read_constraint_vec<R: Read, E: Pairing>(mut reader: R) -> IoResult<ConstraintVec<E>> {
let n_vec = reader.read_u32::<LittleEndian>()? as usize; let n_vec = reader.read_u32::<LittleEndian>()? as usize;
let mut vec = Vec::with_capacity(n_vec); let mut vec = Vec::with_capacity(n_vec);
for _ in 0..n_vec { for _ in 0..n_vec {
vec.push(( vec.push((
reader.read_u32::<LittleEndian>()? as usize, reader.read_u32::<LittleEndian>()? as usize,
E::Fr::read(&mut reader)?, E::ScalarField::deserialize_uncompressed(&mut reader)?,
)); ));
} }
Ok(vec) Ok(vec)
} }
fn read_constraints<R: Read, E: PairingEngine>( fn read_constraints<R: Read, E: Pairing>(
mut reader: R, mut reader: R,
header: &Header, header: &Header,
) -> Result<Vec<Constraints<E>>> { ) -> IoResult<Vec<Constraints<E>>> {
// todo check section size // todo check section size
let mut vec = Vec::with_capacity(header.n_constraints as usize); let mut vec = Vec::with_capacity(header.n_constraints as usize);
for _ in 0..header.n_constraints { for _ in 0..header.n_constraints {
@ -221,22 +228,22 @@ fn read_constraints<R: Read, E: PairingEngine>(
Ok(vec) Ok(vec)
} }
fn read_map<R: Read>(mut reader: R, size: u64, header: &Header) -> Result<Vec<u64>> { fn read_map<R: Read>(mut reader: R, size: u64, header: &Header) -> IoResult<Vec<u64>> {
if size != header.n_wires as u64 * 8 { if size != header.n_wires as u64 * 8 {
return Err(Error::new( return Err(IoError(Error::new(
ErrorKind::InvalidData, ErrorKind::InvalidData,
"Invalid map section size", "Invalid map section size",
)); )));
} }
let mut vec = Vec::with_capacity(header.n_wires as usize); let mut vec = Vec::with_capacity(header.n_wires as usize);
for _ in 0..header.n_wires { for _ in 0..header.n_wires {
vec.push(reader.read_u64::<LittleEndian>()?); vec.push(reader.read_u64::<LittleEndian>()?);
} }
if vec[0] != 0 { if vec[0] != 0 {
return Err(Error::new( return Err(IoError(Error::new(
ErrorKind::InvalidData, ErrorKind::InvalidData,
"Wire 0 should always be mapped to 0", "Wire 0 should always be mapped to 0",
)); )));
} }
Ok(vec) Ok(vec)
} }

View File

@ -1,10 +1,11 @@
//! Helpers for converting Arkworks types to U256-tuples as expected by the //! Helpers for converting Arkworks types to U256-tuples as expected by the
//! Solidity Groth16 Verifier smart contracts //! Solidity Groth16 Verifier smart contracts
use ark_ff::{BigInteger, FromBytes, PrimeField}; use ark_ff::{BigInteger, PrimeField};
use ethers_core::types::U256; use ethers_core::types::U256;
use num_traits::Zero; use num_traits::Zero;
use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine}; use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine};
use ark_serialize::CanonicalDeserialize;
pub struct Inputs(pub Vec<U256>); pub struct Inputs(pub Vec<U256>);
@ -26,8 +27,11 @@ impl From<G1> for G1Affine {
fn from(src: G1) -> G1Affine { fn from(src: G1) -> G1Affine {
let x: Fq = u256_to_point(src.x); let x: Fq = u256_to_point(src.x);
let y: Fq = u256_to_point(src.y); let y: Fq = u256_to_point(src.y);
let inf = x.is_zero() && y.is_zero(); if x.is_zero() && y.is_zero() {
G1Affine::new(x, y, inf) G1Affine::identity()
} else {
G1Affine::new(x, y)
}
} }
} }
@ -64,8 +68,11 @@ impl From<G2> for G2Affine {
let c1 = u256_to_point(src.y[1]); let c1 = u256_to_point(src.y[1]);
let y = Fq2::new(c0, c1); let y = Fq2::new(c0, c1);
let inf = x.is_zero() && y.is_zero(); if x.is_zero() && y.is_zero() {
G2Affine::new(x, y, inf) G2Affine::identity()
} else {
G2Affine::new(x, y)
}
} }
} }
@ -169,14 +176,14 @@ impl From<VerifyingKey> for ark_groth16::VerifyingKey<Bn254> {
fn u256_to_point<F: PrimeField>(point: U256) -> F { fn u256_to_point<F: PrimeField>(point: U256) -> F {
let mut buf = [0; 32]; let mut buf = [0; 32];
point.to_little_endian(&mut buf); point.to_little_endian(&mut buf);
let bigint = F::BigInt::read(&buf[..]).expect("always works"); let bigint = F::BigInt::deserialize_uncompressed(&buf[..]).expect("always works");
F::from_repr(bigint).expect("alwasy works") F::from_bigint(bigint).expect("always works")
} }
// Helper for converting a PrimeField to its U256 representation for Ethereum compatibility // Helper for converting a PrimeField to its U256 representation for Ethereum compatibility
// (U256 reads data as big endian) // (U256 reads data as big endian)
fn point_to_u256<F: PrimeField>(point: F) -> U256 { fn point_to_u256<F: PrimeField>(point: F) -> U256 {
let point = point.into_repr(); let point = point.into_bigint();
let point_bytes = point.to_bytes_be(); let point_bytes = point.to_bytes_be();
U256::from(&point_bytes[..]) U256::from(&point_bytes[..])
} }
@ -185,25 +192,24 @@ fn point_to_u256<F: PrimeField>(point: F) -> U256 {
mod tests { mod tests {
use super::*; use super::*;
use ark_bn254::Fq; use ark_bn254::Fq;
use ark_std::UniformRand;
fn fq() -> Fq { fn fq() -> Fq {
Fq::from(2) Fq::from(2)
} }
fn fq2() -> Fq2 {
Fq2::from(2)
}
fn fr() -> Fr { fn fr() -> Fr {
Fr::from(2) Fr::from(2)
} }
fn g1() -> G1Affine { fn g1() -> G1Affine {
G1Affine::new(fq(), fq(), false) let rng = &mut ark_std::test_rng();
G1Affine::rand(rng)
} }
fn g2() -> G2Affine { fn g2() -> G2Affine {
G2Affine::new(fq2(), fq2(), false) let rng = &mut ark_std::test_rng();
G2Affine::rand(rng)
} }
#[test] #[test]

View File

@ -1,10 +1,12 @@
//! Safe-ish interface for reading and writing specific types to the WASM runtime's memory //! Safe-ish interface for reading and writing specific types to the WASM runtime's memory
use ark_serialize::CanonicalDeserialize;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use wasmer::{Memory, MemoryView}; use wasmer::{Memory, MemoryView};
// TODO: Decide whether we want Ark here or if it should use a generic BigInt package // TODO: Decide whether we want Ark here or if it should use a generic BigInt package
use ark_bn254::FrParameters; use ark_bn254::FrConfig;
use ark_ff::{BigInteger, BigInteger256, FpParameters, FromBytes, Zero}; use ark_ff::MontConfig;
use ark_ff::{BigInteger, BigInteger256, Zero};
use num_bigint::{BigInt, BigUint}; use num_bigint::{BigInt, BigUint};
@ -38,7 +40,7 @@ impl SafeMemory {
let short_max = BigInt::from(0x8000_0000u64); let short_max = BigInt::from(0x8000_0000u64);
let short_min = BigInt::from_biguint( let short_min = BigInt::from_biguint(
num_bigint::Sign::NoSign, num_bigint::Sign::NoSign,
BigUint::try_from(FrParameters::MODULUS).unwrap(), BigUint::try_from(FrConfig::MODULUS).unwrap(),
) - &short_max; ) - &short_max;
let r_inv = BigInt::from_str( let r_inv = BigInt::from_str(
"9915499612839321149637521777990102151350674507940716049588462388200839649614", "9915499612839321149637521777990102151350674507940716049588462388200839649614",
@ -188,7 +190,7 @@ impl SafeMemory {
let buf = &buf[ptr..ptr + num_bytes * 32]; let buf = &buf[ptr..ptr + num_bytes * 32];
// TODO: Is there a better way to read big integers? // TODO: Is there a better way to read big integers?
let big = BigInteger256::read(buf).unwrap(); let big = BigInteger256::deserialize_uncompressed(buf).unwrap();
let big = BigUint::try_from(big).unwrap(); let big = BigUint::try_from(big).unwrap();
Ok(big.into()) Ok(big.into())
} }

View File

@ -255,16 +255,16 @@ impl WitnessCalculator {
} }
pub fn calculate_witness_element< pub fn calculate_witness_element<
E: ark_ec::PairingEngine, E: ark_ec::pairing::Pairing,
I: IntoIterator<Item = (String, Vec<BigInt>)>, I: IntoIterator<Item = (String, Vec<BigInt>)>,
>( >(
&mut self, &mut self,
inputs: I, inputs: I,
sanity_check: bool, sanity_check: bool,
) -> Result<Vec<E::Fr>> { ) -> Result<Vec<E::ScalarField>> {
use ark_ff::{FpParameters, PrimeField}; use ark_ff::PrimeField;
let witness = self.calculate_witness(inputs, sanity_check)?; let witness = self.calculate_witness(inputs, sanity_check)?;
let modulus = <<E::Fr as PrimeField>::Params as FpParameters>::MODULUS; let modulus = <E::ScalarField as PrimeField>::MODULUS;
// convert it to field elements // convert it to field elements
use num_traits::Signed; use num_traits::Signed;
@ -277,7 +277,7 @@ impl WitnessCalculator {
} else { } else {
w.to_biguint().unwrap() w.to_biguint().unwrap()
}; };
E::Fr::from(w) E::ScalarField::from(w)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -421,7 +421,7 @@ mod tests {
#[test] #[test]
fn safe_multipler() { fn safe_multipler() {
let witness = let witness =
std::fs::read_to_string(&root_path("test-vectors/safe-circuit-witness.json")).unwrap(); std::fs::read_to_string(root_path("test-vectors/safe-circuit-witness.json")).unwrap();
let witness: Vec<String> = serde_json::from_str(&witness).unwrap(); let witness: Vec<String> = serde_json::from_str(&witness).unwrap();
let witness = &witness.iter().map(|x| x.as_ref()).collect::<Vec<_>>(); let witness = &witness.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
run_test(TestCase { run_test(TestCase {
@ -436,7 +436,7 @@ mod tests {
#[test] #[test]
fn smt_verifier() { fn smt_verifier() {
let witness = let witness =
std::fs::read_to_string(&root_path("test-vectors/smtverifier10-witness.json")).unwrap(); std::fs::read_to_string(root_path("test-vectors/smtverifier10-witness.json")).unwrap();
let witness: Vec<String> = serde_json::from_str(&witness).unwrap(); let witness: Vec<String> = serde_json::from_str(&witness).unwrap();
let witness = &witness.iter().map(|x| x.as_ref()).collect::<Vec<_>>(); let witness = &witness.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
@ -466,8 +466,8 @@ mod tests {
wtns.memory.prime.to_str_radix(16), wtns.memory.prime.to_str_radix(16),
"30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001".to_lowercase() "30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001".to_lowercase()
); );
assert_eq!(wtns.instance.get_n_vars().unwrap() as u32, case.n_vars); assert_eq!({ wtns.instance.get_n_vars().unwrap() }, case.n_vars);
assert_eq!(wtns.n64 as u32, case.n64); assert_eq!({ wtns.n64 }, case.n64);
let inputs_str = std::fs::read_to_string(case.inputs_path).unwrap(); let inputs_str = std::fs::read_to_string(case.inputs_path).unwrap();
let inputs: std::collections::HashMap<String, serde_json::Value> = let inputs: std::collections::HashMap<String, serde_json::Value> =

View File

@ -25,7 +25,7 @@
//! PointsC(8) //! PointsC(8)
//! PointsH(9) //! PointsH(9)
//! Contributions(10) //! Contributions(10)
use ark_ff::{BigInteger256, FromBytes, PrimeField}; use ark_ff::{BigInteger256, PrimeField};
use ark_relations::r1cs::ConstraintMatrices; use ark_relations::r1cs::ConstraintMatrices;
use ark_serialize::{CanonicalDeserialize, SerializationError}; use ark_serialize::{CanonicalDeserialize, SerializationError};
use ark_std::log2; use ark_std::log2;
@ -33,13 +33,15 @@ use byteorder::{LittleEndian, ReadBytesExt};
use std::{ use std::{
collections::HashMap, collections::HashMap,
io::{Read, Result as IoResult, Seek, SeekFrom}, io::{Read, Seek, SeekFrom},
}; };
use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine}; use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine};
use ark_groth16::{ProvingKey, VerifyingKey}; use ark_groth16::{ProvingKey, VerifyingKey};
use num_traits::Zero; use num_traits::Zero;
type IoResult<T> = Result<T, SerializationError>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct Section { struct Section {
position: u64, position: u64,
@ -285,18 +287,18 @@ impl HeaderGroth {
fn read<R: Read>(mut reader: &mut R) -> IoResult<Self> { fn read<R: Read>(mut reader: &mut R) -> IoResult<Self> {
// TODO: Impl From<u32> in Arkworks // TODO: Impl From<u32> in Arkworks
let n8q: u32 = FromBytes::read(&mut reader)?; let n8q: u32 = u32::deserialize_uncompressed(&mut reader)?;
// group order r of Bn254 // group order r of Bn254
let q = BigInteger256::read(&mut reader)?; let q = BigInteger256::deserialize_uncompressed(&mut reader)?;
let n8r: u32 = FromBytes::read(&mut reader)?; let n8r: u32 = u32::deserialize_uncompressed(&mut reader)?;
// Prime field modulus // Prime field modulus
let r = BigInteger256::read(&mut reader)?; let r = BigInteger256::deserialize_uncompressed(&mut reader)?;
let n_vars = u32::read(&mut reader)? as usize; let n_vars = u32::deserialize_uncompressed(&mut reader)? as usize;
let n_public = u32::read(&mut reader)? as usize; let n_public = u32::deserialize_uncompressed(&mut reader)? as usize;
let domain_size: u32 = FromBytes::read(&mut reader)?; let domain_size: u32 = u32::deserialize_uncompressed(&mut reader)?;
let power = log2(domain_size as usize); let power = log2(domain_size as usize);
let verifying_key = ZVerifyingKey::new(&mut reader)?; let verifying_key = ZVerifyingKey::new(&mut reader)?;
@ -318,15 +320,15 @@ impl HeaderGroth {
// need to divide by R, since snarkjs outputs the zkey with coefficients // need to divide by R, since snarkjs outputs the zkey with coefficients
// multiplieid by R^2 // multiplieid by R^2
fn deserialize_field_fr<R: Read>(reader: &mut R) -> IoResult<Fr> { fn deserialize_field_fr<R: Read>(reader: &mut R) -> IoResult<Fr> {
let bigint = BigInteger256::read(reader)?; let bigint = BigInteger256::deserialize_uncompressed(reader)?;
Ok(Fr::new(Fr::new(bigint).into_repr())) Ok(Fr::new_unchecked(Fr::new_unchecked(bigint).into_bigint()))
} }
// skips the multiplication by R because Circom points are already in Montgomery form // skips the multiplication by R because Circom points are already in Montgomery form
fn deserialize_field<R: Read>(reader: &mut R) -> IoResult<Fq> { fn deserialize_field<R: Read>(reader: &mut R) -> IoResult<Fq> {
let bigint = BigInteger256::read(reader)?; let bigint = BigInteger256::deserialize_uncompressed(reader)?;
// if you use ark_ff::PrimeField::from_repr it multiplies by R // if you use Fq::new it multiplies by R
Ok(Fq::new(bigint)) Ok(Fq::new_unchecked(bigint))
} }
pub fn deserialize_field2<R: Read>(reader: &mut R) -> IoResult<Fq2> { pub fn deserialize_field2<R: Read>(reader: &mut R) -> IoResult<Fq2> {
@ -339,14 +341,22 @@ fn deserialize_g1<R: Read>(reader: &mut R) -> IoResult<G1Affine> {
let x = deserialize_field(reader)?; let x = deserialize_field(reader)?;
let y = deserialize_field(reader)?; let y = deserialize_field(reader)?;
let infinity = x.is_zero() && y.is_zero(); let infinity = x.is_zero() && y.is_zero();
Ok(G1Affine::new(x, y, infinity)) if infinity {
Ok(G1Affine::identity())
} else {
Ok(G1Affine::new(x, y))
}
} }
fn deserialize_g2<R: Read>(reader: &mut R) -> IoResult<G2Affine> { fn deserialize_g2<R: Read>(reader: &mut R) -> IoResult<G2Affine> {
let f1 = deserialize_field2(reader)?; let f1 = deserialize_field2(reader)?;
let f2 = deserialize_field2(reader)?; let f2 = deserialize_field2(reader)?;
let infinity = f1.is_zero() && f2.is_zero(); let infinity = f1.is_zero() && f2.is_zero();
Ok(G2Affine::new(f1, f2, infinity)) if infinity {
Ok(G2Affine::identity())
} else {
Ok(G2Affine::new(f1, f2))
}
} }
fn deserialize_g1_vec<R: Read>(reader: &mut R, n_vars: u32) -> IoResult<Vec<G1Affine>> { fn deserialize_g1_vec<R: Read>(reader: &mut R, n_vars: u32) -> IoResult<Vec<G1Affine>> {
@ -361,16 +371,15 @@ fn deserialize_g2_vec<R: Read>(reader: &mut R, n_vars: u32) -> IoResult<Vec<G2Af
mod tests { mod tests {
use super::*; use super::*;
use ark_bn254::{G1Projective, G2Projective}; use ark_bn254::{G1Projective, G2Projective};
use ark_crypto_primitives::snark::SNARK;
use num_bigint::BigUint; use num_bigint::BigUint;
use serde_json::Value; use serde_json::Value;
use std::fs::File; use std::fs::File;
use crate::circom::CircomReduction;
use crate::witness::WitnessCalculator; use crate::witness::WitnessCalculator;
use crate::{circom::CircomReduction, CircomBuilder, CircomConfig}; use crate::{CircomBuilder, CircomConfig};
use ark_groth16::{ use ark_groth16::Groth16;
create_proof_with_reduction_and_matrices, create_random_proof_with_reduction as prove,
prepare_verifying_key, verify_proof,
};
use ark_std::rand::thread_rng; use ark_std::rand::thread_rng;
use num_traits::{One, Zero}; use num_traits::{One, Zero};
use std::str::FromStr; use std::str::FromStr;
@ -473,8 +482,7 @@ mod tests {
let n_vars = 10; let n_vars = 10;
let buf = vec![g1_buf(); n_vars] let buf = vec![g1_buf(); n_vars]
.iter() .iter()
.cloned() .flatten().cloned()
.flatten()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let expected = vec![g1_one(); n_vars]; let expected = vec![g1_one(); n_vars];
@ -497,8 +505,7 @@ mod tests {
let n_vars = 10; let n_vars = 10;
let buf = vec![g2_buf(); n_vars] let buf = vec![g2_buf(); n_vars]
.iter() .iter()
.cloned() .flatten().cloned()
.flatten()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let expected = vec![g2_one(); n_vars]; let expected = vec![g2_one(); n_vars];
@ -853,11 +860,11 @@ mod tests {
let inputs = circom.get_public_inputs().unwrap(); let inputs = circom.get_public_inputs().unwrap();
let mut rng = thread_rng(); let mut rng = thread_rng();
let proof = prove::<_, _, _, CircomReduction>(circom, &params, &mut rng).unwrap(); let proof = Groth16::<Bn254, CircomReduction>::prove(&params, circom, &mut rng).unwrap();
let pvk = prepare_verifying_key(&params.vk); let pvk = Groth16::<Bn254>::process_vk(&params.vk).unwrap();
let verified = verify_proof(&pvk, &proof, &inputs).unwrap(); let verified = Groth16::<Bn254>::verify_with_processed_vk(&pvk, &inputs, &proof).unwrap();
assert!(verified); assert!(verified);
} }
@ -888,7 +895,7 @@ mod tests {
let full_assignment = wtns let full_assignment = wtns
.calculate_witness_element::<Bn254, _>(inputs, false) .calculate_witness_element::<Bn254, _>(inputs, false)
.unwrap(); .unwrap();
let proof = create_proof_with_reduction_and_matrices::<_, CircomReduction>( let proof = Groth16::<Bn254, CircomReduction>::create_proof_with_reduction_and_matrices(
&params, &params,
r, r,
s, s,
@ -899,9 +906,9 @@ mod tests {
) )
.unwrap(); .unwrap();
let pvk = prepare_verifying_key(&params.vk); let pvk = Groth16::<Bn254>::process_vk(&params.vk).unwrap();
let inputs = &full_assignment[1..num_inputs]; let inputs = &full_assignment[1..num_inputs];
let verified = verify_proof(&pvk, &proof, inputs).unwrap(); let verified = Groth16::<Bn254>::verify_with_processed_vk(&pvk, inputs, &proof).unwrap();
assert!(verified); assert!(verified);
} }

View File

@ -3,9 +3,10 @@ use ark_std::rand::thread_rng;
use color_eyre::Result; use color_eyre::Result;
use ark_bn254::Bn254; use ark_bn254::Bn254;
use ark_groth16::{ use ark_crypto_primitives::snark::SNARK;
create_random_proof as prove, generate_random_parameters, prepare_verifying_key, verify_proof, use ark_groth16::Groth16;
};
type GrothBn = Groth16<Bn254>;
#[test] #[test]
fn groth16_proof() -> Result<()> { fn groth16_proof() -> Result<()> {
@ -21,17 +22,17 @@ fn groth16_proof() -> Result<()> {
let circom = builder.setup(); let circom = builder.setup();
let mut rng = thread_rng(); let mut rng = thread_rng();
let params = generate_random_parameters::<Bn254, _, _>(circom, &mut rng)?; let params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng)?;
let circom = builder.build()?; let circom = builder.build()?;
let inputs = circom.get_public_inputs().unwrap(); let inputs = circom.get_public_inputs().unwrap();
let proof = prove(circom, &params, &mut rng)?; let proof = GrothBn::prove(&params, circom, &mut rng)?;
let pvk = prepare_verifying_key(&params.vk); let pvk = GrothBn::process_vk(&params.vk).unwrap();
let verified = verify_proof(&pvk, &proof, &inputs)?; let verified = GrothBn::verify_with_processed_vk(&pvk, &inputs, &proof)?;
assert!(verified); assert!(verified);
@ -47,14 +48,14 @@ fn groth16_proof_wrong_input() {
.unwrap(); .unwrap();
let mut builder = CircomBuilder::new(cfg); let mut builder = CircomBuilder::new(cfg);
builder.push_input("a", 3); builder.push_input("a", 3);
// This isn't a public input to the circuit, should faild // This isn't a public input to the circuit, should fail
builder.push_input("foo", 11); builder.push_input("foo", 11);
// create an empty instance for setting it up // create an empty instance for setting it up
let circom = builder.setup(); let circom = builder.setup();
let mut rng = thread_rng(); let mut rng = thread_rng();
let _params = generate_random_parameters::<Bn254, _, _>(circom, &mut rng).unwrap(); let _params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng).unwrap();
let _ = builder.build().unwrap_err(); let _ = builder.build().unwrap_err();
} }
@ -74,17 +75,17 @@ fn groth16_proof_circom2() -> Result<()> {
let circom = builder.setup(); let circom = builder.setup();
let mut rng = thread_rng(); let mut rng = thread_rng();
let params = generate_random_parameters::<Bn254, _, _>(circom, &mut rng)?; let params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng)?;
let circom = builder.build()?; let circom = builder.build()?;
let inputs = circom.get_public_inputs().unwrap(); let inputs = circom.get_public_inputs().unwrap();
let proof = prove(circom, &params, &mut rng)?; let proof = GrothBn::prove(&params, circom, &mut rng)?;
let pvk = prepare_verifying_key(&params.vk); let pvk = GrothBn::process_vk(&params.vk).unwrap();
let verified = verify_proof(&pvk, &proof, &inputs)?; let verified = GrothBn::verify_with_processed_vk(&pvk, &inputs, &proof)?;
assert!(verified); assert!(verified);

View File

@ -3,7 +3,8 @@ use ark_std::rand::thread_rng;
use color_eyre::Result; use color_eyre::Result;
use ark_bn254::Bn254; use ark_bn254::Bn254;
use ark_groth16::{create_random_proof as prove, generate_random_parameters}; use ark_crypto_primitives::snark::SNARK;
use ark_groth16::Groth16;
use ethers::{ use ethers::{
contract::ContractError, contract::ContractError,
@ -27,12 +28,12 @@ async fn solidity_verifier() -> Result<()> {
let circom = builder.setup(); let circom = builder.setup();
let mut rng = thread_rng(); let mut rng = thread_rng();
let params = generate_random_parameters::<Bn254, _, _>(circom, &mut rng)?; let params = Groth16::<Bn254>::generate_random_parameters_with_reduction(circom, &mut rng)?;
let circom = builder.build()?; let circom = builder.build()?;
let inputs = circom.get_public_inputs().unwrap(); let inputs = circom.get_public_inputs().unwrap();
let proof = prove(circom, &params, &mut rng)?; let proof = Groth16::<Bn254>::prove(&params, circom, &mut rng)?;
// launch the network & compile the verifier // launch the network & compile the verifier
let anvil = Anvil::new().spawn(); let anvil = Anvil::new().spawn();