Browse Source

port to last Sonobe version (which includes external_inputs), update from pasta curves to bn254,grumpkin in rust to match circom usage"

main
arnaucube 7 months ago
parent
commit
69812e791c
8 changed files with 178 additions and 369 deletions
  1. +10
    -6
      Cargo.toml
  2. +13
    -5
      circom/grapevine.circom
  3. +37
    -141
      src/circom.rs
  4. +11
    -11
      src/nova.rs
  5. +16
    -18
      src/params.rs
  6. +88
    -7
      src/utils/inputs.rs
  7. +3
    -4
      src/utils/mod.rs
  8. +0
    -177
      src/utils/wrapper.rs

+ 10
- 6
Cargo.toml

@ -6,12 +6,9 @@ edition = "2021"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ark-pallas = { version = "0.4.0", features = ["r1cs"] }
ark-vesta = {version="0.4.0", features=["r1cs"]}
ark-circom = { git = "https://github.com/arnaucube/circom-compat.git" }
ark-bn254 = { version = "0.4.0", features = ["r1cs"] }
ark-grumpkin = {version="0.4.0", features=["r1cs"]}
ark-ec = "0.4.1" ark-ec = "0.4.1"
ark-ff = "0.4.1" ark-ff = "0.4.1"
ark-r1cs-std = { version = "0.4.0", default-features = false } ark-r1cs-std = { version = "0.4.0", default-features = false }
@ -24,9 +21,16 @@ ark-crypto-primitives = { version = "^0.4.0", default-features = false, features
ark-std = "0.4.0" ark-std = "0.4.0"
color-eyre = "0.6.2" color-eyre = "0.6.2"
num-bigint = "0.4.3" num-bigint = "0.4.3"
sonobe = { git = "https://github.com/privacy-scaling-explorations/sonobe", package = "folding-schemes", branch = "main" }
sonobe = { git = "https://github.com/privacy-scaling-explorations/sonobe", package = "folding-schemes", branch = "circom-external-inputs" }
serde = "1.0.198" serde = "1.0.198"
serde_json = "1.0.116" serde_json = "1.0.116"
[dev-dependencies] [dev-dependencies]
lazy_static = "1.4.0" lazy_static = "1.4.0"
[patch.crates-io]
# patch ark_curves to use a cherry-picked version which contains
# bn254::constraints & grumpkin for v0.4.0 (once arkworks v0.5.0 is released
# this will no longer be needed)
ark-bn254 = { git = "https://github.com/arnaucube/ark-curves-cherry-picked", branch="cherry-pick"}
ark-grumpkin = { git = "https://github.com/arnaucube/ark-curves-cherry-picked", branch="cherry-pick"}

+ 13
- 5
circom/grapevine.circom

@ -17,10 +17,18 @@ template grapevine(num_felts) {
signal input ivc_input[4]; signal input ivc_input[4];
signal output ivc_output[4]; signal output ivc_output[4];
// private inputs
signal input phrase[num_felts]; // secret phrase, if first iteration
signal input usernames[2]; // prev username, current username
signal input auth_secrets[2]; // prev degree's user secret, current degree's user secret
// external inputs at each folding step
signal input external_inputs[num_felts+2+2];
signal phrase[num_felts]; // secret phrase, if first iteration
for (var i=0; i<num_felts; i++) {
phrase[i] <== external_inputs[i];
}
signal usernames[2]; // prev username, current username
usernames[0]<==external_inputs[num_felts];
usernames[1]<==external_inputs[num_felts+1];
signal auth_secrets[2]; // prev degree's user secret, current degree's user secret
auth_secrets[0]<==external_inputs[num_felts+2];
auth_secrets[1]<==external_inputs[num_felts+3];
// name inputs from step_in // name inputs from step_in
signal degrees_of_separation <== ivc_input[0]; signal degrees_of_separation <== ivc_input[0];
@ -94,4 +102,4 @@ template grapevine(num_felts) {
ivc_output <== chaff_mux.out; ivc_output <== chaff_mux.out;
} }
component main { public [ivc_input] } = grapevine(6);
component main { public [ivc_input] } = grapevine(6);

+ 37
- 141
src/circom.rs

@ -1,146 +1,29 @@
use ark_circom::circom::CircomCircuit;
use ark_ff::PrimeField;
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
use ark_std::fmt::Debug;
use num_bigint::BigInt;
use sonobe::{frontend::FCircuit, Error as SonobeError};
use std::path::PathBuf;
use crate::errors::GrapevineError;
use crate::utils::wrapper::{CircomPrivateInput, CircomWrapper};
// Define Circom FCircuit
#[derive(Clone, Debug)]
pub struct GrapevineFCircuit<F: PrimeField> {
circom_wrapper: CircomWrapper<F>,
private_input: CircomPrivateInput,
}
impl<F: PrimeField> GrapevineFCircuit<F> {
pub fn set_private_input(&mut self, input: CircomPrivateInput) {
self.private_input = input;
}
}
impl<F: PrimeField> FCircuit<F> for GrapevineFCircuit<F> {
type Params = (PathBuf, PathBuf);
fn new(params: Self::Params) -> Self {
let (r1cs_path, wasm_path) = params;
let circom_wrapper = CircomWrapper::new(r1cs_path, wasm_path);
Self {
circom_wrapper,
private_input: CircomPrivateInput::empty(false),
}
}
fn state_len(&self) -> usize {
4
}
fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, SonobeError> {
// convert ivc_input from ark ff to BigInt
let ivc_input = z_i
.iter()
.map(|val| CircomWrapper::ark_primefield_to_num_bigint(*val))
.collect::<Vec<BigInt>>();
let mut inputs = vec![("ivc_input".to_string(), ivc_input)];
// set the private inputs
if self.private_input.uninitialized() {
return Err(SonobeError::Other("Private input not set".to_string()));
}
let private_input = CircomWrapper::<F>::marshal_private_inputs(&self.private_input);
inputs.extend(private_input);
// calculate witness
let witness = self.circom_wrapper.extract_witness(&inputs).map_err(|e| {
SonobeError::WitnessCalculationError(format!("Failed to calculate witness: {}", e))
})?;
// extract the z_i1 (next state) from witvec
let z_i1 = witness[1..1 + self.state_len()].to_vec();
Ok(z_i1)
}
fn generate_step_constraints(
&self,
cs: ConstraintSystemRef<F>,
_i: usize,
z_i: Vec<FpVar<F>>,
) -> Result<Vec<FpVar<F>>, SynthesisError> {
// convert ivc input from FpVar to ark ff to BigInt
let ivc_input: Vec<BigInt> = z_i
.iter()
.map(|val| CircomWrapper::ark_primefield_to_num_bigint(val.value().unwrap()))
.collect();
let mut inputs = vec![("ivc_input".to_string(), ivc_input)];
// set the private inputs
if self.private_input.uninitialized() {
return Err(SynthesisError::AssignmentMissing);
}
let private_input = CircomWrapper::<F>::marshal_private_inputs(&self.private_input);
inputs.extend(private_input);
println!("Inputs: {:?}", inputs);
// extract r1cs and witness
let (r1cs, witness) = self
.circom_wrapper
.extract_r1cs_and_witness(&inputs)
.map_err(|_| SynthesisError::AssignmentMissing)?;
println!("Wire map len: {:?}", r1cs.clone().wire_mapping.unwrap().len());
println!("Constraints len: {:?}", r1cs.clone().constraints.len());
println!("Witness len: {:?}", witness.clone().unwrap().len());
// Initialize CircomCircuit
let circom_circuit = CircomCircuit {
r1cs,
witness: witness.clone(),
inputs_already_computed: false,
};
circom_circuit
.generate_constraints(cs.clone())
.map_err(|_| SynthesisError::Unsatisfiable)?;
if !cs.is_satisfied().unwrap() {
return Err(SynthesisError::Unsatisfiable);
};
let w = witness.ok_or(SynthesisError::Unsatisfiable)?;
let z_i1: Vec<FpVar<F>> =
Vec::<FpVar<F>>::new_witness(cs.clone(), || Ok(w[1..1 + self.state_len()].to_vec()))?;
Ok(z_i1)
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::params::test_nova_setup; use crate::params::test_nova_setup;
use crate::utils::{
inputs::{get_z0, random_f_bigint},
wrapper::CircomPrivateInput,
use crate::utils::inputs::{
get_z0, prepare_external_inputs, random_f_bigint, CircomPrivateInput,
}; };
use ark_pallas::{constraints::GVar, Fr, Projective};
use ark_r1cs_std::alloc::AllocVar;
use ark_bn254::{constraints::GVar, Fr, G1Projective as Projective};
// use ark_circom::circom::CircomCircuit;
use ark_ff::PrimeField;
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use num_bigint::BigInt; use num_bigint::BigInt;
use sonobe::{ use sonobe::{
commitment::pedersen::Pedersen, folding::nova::Nova,
commitment::pedersen::Pedersen, folding::nova::Nova, frontend::circom::CircomFCircuit,
transcript::poseidon::poseidon_test_config, Error, FoldingScheme, transcript::poseidon::poseidon_test_config, Error, FoldingScheme,
}; };
use sonobe::{frontend::FCircuit, Error as SonobeError};
use std::env::current_dir; use std::env::current_dir;
use std::path::PathBuf;
use std::time::Instant; use std::time::Instant;
use crate::errors::GrapevineError;
lazy_static! { lazy_static! {
pub static ref R1CS_PATH: PathBuf = PathBuf::from("./circom/artifacts/grapevine.r1cs"); pub static ref R1CS_PATH: PathBuf = PathBuf::from("./circom/artifacts/grapevine.r1cs");
pub static ref WASM_PATH: PathBuf = PathBuf::from("./circom/artifacts/grapevine.wasm"); pub static ref WASM_PATH: PathBuf = PathBuf::from("./circom/artifacts/grapevine.wasm");
@ -168,21 +51,20 @@ mod test {
auth_secrets: [None, Some(AUTH_SECRETS[0].clone())], auth_secrets: [None, Some(AUTH_SECRETS[0].clone())],
chaff: false, chaff: false,
}; };
let z_0 = get_z0();
let external_inputs = prepare_external_inputs::<Fr>(&step_0_inputs);
// initialize new Grapevine function circuit // initialize new Grapevine function circuit
let mut f_circuit = GrapevineFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone()));
f_circuit.set_private_input(step_0_inputs);
let f_circuit = CircomFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone(), 4, 6 + 4)); // 4=ivc_input.lenght, 6+4=external_inputs
let z_1 = f_circuit.step_native(0, z_0.to_vec()).unwrap();
let z_0 = get_z0();
let z_1 = f_circuit
.step_native(0, z_0.to_vec(), external_inputs)
.unwrap();
println!("z_1: {:?}", z_1); println!("z_1: {:?}", z_1);
} }
#[test] #[test]
fn test_step_constraints() { fn test_step_constraints() {
// initialize new Grapevine function circuit
let mut f_circuit = GrapevineFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone()));
// define inputs // define inputs
let step_0_inputs = CircomPrivateInput { let step_0_inputs = CircomPrivateInput {
phrase: Some(String::from(&*PHRASE)), phrase: Some(String::from(&*PHRASE)),
@ -190,22 +72,34 @@ mod test {
auth_secrets: [None, Some(AUTH_SECRETS[0].clone())], auth_secrets: [None, Some(AUTH_SECRETS[0].clone())],
chaff: false, chaff: false,
}; };
f_circuit.set_private_input(step_0_inputs);
let external_inputs = prepare_external_inputs::<Fr>(&step_0_inputs);
// initialize new Grapevine function circuit
let f_circuit = CircomFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone(), 4, 6 + 4)); // 4=ivc_input.lenght, 6+4=external_inputs
let z_0 = get_z0();
let z_1 = f_circuit
.step_native(0, z_0.to_vec(), external_inputs.clone())
.unwrap();
// assign z0 // assign z0
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
let z_0_var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(get_z0())).unwrap();
let z_0_var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_0)).unwrap();
let external_inputs_var =
Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(external_inputs)).unwrap();
// compute constraints for step 0 // compute constraints for step 0
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
let z_1_var = f_circuit let z_1_var = f_circuit
.generate_step_constraints(cs.clone(), 1, z_0_var)
.generate_step_constraints(cs.clone(), 1, z_0_var, external_inputs_var)
.unwrap(); .unwrap();
println!("z_1: {:?}", z_1_var); println!("z_1: {:?}", z_1_var);
// assert_eq!(z_i1_var.value().unwrap(), vec![Fr::from(38), Fr::from(1)]);
assert_eq!(z_1_var.value().unwrap(), z_1);
} }
// WIP
/*
#[test] #[test]
fn test_multiple_steps_native() { fn test_multiple_steps_native() {
// initialize new Grapevine function circuit // initialize new Grapevine function circuit
@ -283,6 +177,7 @@ mod test {
assert_eq!(z_i[0], Fr::from(3)); assert_eq!(z_i[0], Fr::from(3));
assert_eq!(z_i[3], Fr::from(0)); assert_eq!(z_i[3], Fr::from(0));
} }
*/
// #[test] // #[test]
// fn test_multiple_steps_constraints() { // fn test_multiple_steps_constraints() {
@ -332,6 +227,7 @@ mod test {
// //
// } // }
/*
#[test] #[test]
fn test_full_one_step() { fn test_full_one_step() {
// initialize new Grapevine function circuit // initialize new Grapevine function circuit
@ -340,7 +236,6 @@ mod test {
// Get test params // Get test params
let (prover_params, verifier_params) = let (prover_params, verifier_params) =
test_nova_setup::<GrapevineFCircuit<Fr>>(f_circuit.clone()); test_nova_setup::<GrapevineFCircuit<Fr>>(f_circuit.clone());
// define inputs // define inputs
// define inputs // define inputs
@ -406,4 +301,5 @@ mod test {
.unwrap(); .unwrap();
println!("Verified: {:?}", start.elapsed()); println!("Verified: {:?}", start.elapsed());
} }
*/
} }

+ 11
- 11
src/nova.rs

@ -1,3 +1,4 @@
use ark_bn254::{constraints::GVar, Fr, G1Projective as Projective};
use ark_crypto_primitives::{ use ark_crypto_primitives::{
crh::{ crh::{
poseidon::constraints::{CRHGadget, CRHParametersVar}, poseidon::constraints::{CRHGadget, CRHParametersVar},
@ -7,25 +8,22 @@ use ark_crypto_primitives::{
sponge::{poseidon::PoseidonConfig, Absorb}, sponge::{poseidon::PoseidonConfig, Absorb},
}; };
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_pallas::{constraints::GVar, Fr, Projective};
use ark_r1cs_std::{alloc::AllocVar, fields::FieldVar, fields::fp::FpVar};
use ark_grumpkin::{constraints::GVar as Gvar2, Projective as Projective2};
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, fields::FieldVar};
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
use ark_std::Zero; use ark_std::Zero;
use ark_vesta::{constraints::GVar as Gvar2, Projective as Projective2};
use core::marker::PhantomData; use core::marker::PhantomData;
use std::time::Instant;
use sonobe::{ use sonobe::{
commitment::{pedersen::Pedersen, CommitmentScheme}, commitment::{pedersen::Pedersen, CommitmentScheme},
folding::nova::{get_r1cs, ProverParams, VerifierParams}, folding::nova::{get_r1cs, ProverParams, VerifierParams},
frontend::FCircuit,
transcript::poseidon::poseidon_test_config
frontend::{circom::CircomFCircuit, FCircuit},
transcript::poseidon::poseidon_test_config,
}; };
use std::time::Instant;
use crate::{
params::test_nova_setup,
circom::GrapevineFCircuit,
};
use crate::params::test_nova_setup;
/* WIP
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -35,7 +33,8 @@ mod test {
fn test_generate_params() { fn test_generate_params() {
let r1cs_path = PathBuf::from("./circom/artifacts/circuit.r1cs"); let r1cs_path = PathBuf::from("./circom/artifacts/circuit.r1cs");
let wasm_path = PathBuf::from("./circom/artifacts/circuit.wasm"); let wasm_path = PathBuf::from("./circom/artifacts/circuit.wasm");
let f_circuit = GrapevineFCircuit::<Fr>::new((r1cs_path, wasm_path));
let f_circuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path, 4, 6 + 4)); // 4=ivc_input.lenght, 6+4=external_inputs
let pre = Instant::now(); let pre = Instant::now();
let (prover_params, verifier_params) = test_nova_setup::<GrapevineFCircuit<Fr>>(f_circuit); let (prover_params, verifier_params) = test_nova_setup::<GrapevineFCircuit<Fr>>(f_circuit);
// let prover_params_str = serde_json::to_string(&prover_params).unwrap(); // let prover_params_str = serde_json::to_string(&prover_params).unwrap();
@ -44,3 +43,4 @@ mod test {
println!("Time to generate params: {:?}", pre.elapsed()); println!("Time to generate params: {:?}", pre.elapsed());
} }
} }
*/

+ 16
- 18
src/params.rs

@ -1,45 +1,43 @@
use ark_pallas::{constraints::GVar, Fr, Projective};
use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
use ark_bn254::{constraints::GVar, Fr, G1Projective as Projective};
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
use sonobe::{ use sonobe::{
commitment::{pedersen::Pedersen, CommitmentScheme}, commitment::{pedersen::Pedersen, CommitmentScheme},
folding::nova::{get_r1cs, ProverParams, VerifierParams}, folding::nova::{get_r1cs, ProverParams, VerifierParams},
frontend::FCircuit, frontend::FCircuit,
transcript::poseidon::poseidon_test_config
transcript::poseidon::poseidon_test_config,
}; };
pub fn test_nova_setup<FC: FCircuit<Fr>>( pub fn test_nova_setup<FC: FCircuit<Fr>>(
f_circuit: FC
f_circuit: FC,
) -> ( ) -> (
ProverParams<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>>, ProverParams<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>>,
VerifierParams<Projective, Projective2>
VerifierParams<Projective, Projective2>,
) { ) {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_test_config::<Fr>(); let poseidon_config = poseidon_test_config::<Fr>();
// get CM & CF_CM len // get CM & CF_CM len
let (r1cs, cf_r1cs) = get_r1cs::<Projective, GVar, Projective2, GVar2, FC>(
&poseidon_config,
f_circuit,
).unwrap();
let (r1cs, cf_r1cs) =
get_r1cs::<Projective, GVar, Projective2, GVar2, FC>(&poseidon_config, f_circuit).unwrap();
let cf_len = r1cs.A.n_rows; let cf_len = r1cs.A.n_rows;
let cf_cf_len = cf_r1cs.A.n_rows; let cf_cf_len = cf_r1cs.A.n_rows;
let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, cf_len).unwrap(); let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, cf_len).unwrap();
let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cf_len).unwrap(); let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cf_len).unwrap();
let prover_params =
ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>>{
poseidon_config: poseidon_config.clone(),
cs_params: pedersen_params,
cf_cs_params: cf_pedersen_params
let prover_params =
ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> {
poseidon_config: poseidon_config.clone(),
cs_params: pedersen_params,
cf_cs_params: cf_pedersen_params,
}; };
let verifier_params = VerifierParams::<Projective, Projective2>{
let verifier_params = VerifierParams::<Projective, Projective2> {
poseidon_config: poseidon_config.clone(), poseidon_config: poseidon_config.clone(),
r1cs, r1cs,
cf_r1cs
cf_r1cs,
}; };
(prover_params, verifier_params) (prover_params, verifier_params)
}
}

+ 88
- 7
src/utils/inputs.rs

@ -1,14 +1,49 @@
use ark_ff::{PrimeField, BigInteger};
use ark_pallas::Fr;
use ark_bn254::Fr;
use ark_ff::{BigInteger, PrimeField};
use ark_std::rand::rngs::OsRng; use ark_std::rand::rngs::OsRng;
use num_bigint::{BigInt, RandBigInt, Sign::Plus};
use std::error::Error; use std::error::Error;
use num_bigint::{BigInt, Sign::Plus, RandBigInt};
use super::{MAX_SECRET_LENGTH, MAX_USERNAME_LENGTH, SECRET_FIELD_LENGTH}; use super::{MAX_SECRET_LENGTH, MAX_USERNAME_LENGTH, SECRET_FIELD_LENGTH};
#[derive(Clone, Debug)]
pub struct CircomPrivateInput {
pub phrase: Option<String>,
pub usernames: [Option<String>; 2],
pub auth_secrets: [Option<BigInt>; 2],
pub chaff: bool,
}
impl CircomPrivateInput {
/**
* Creates empty inputs
*
* @param chaff - if true, should compute random vars for chaff in circuit
*/
pub fn empty(chaff: bool) -> Self {
Self {
phrase: None,
usernames: [None, None],
auth_secrets: [None, None],
chaff,
}
}
pub fn uninitialized(&self) -> bool {
let not_chaff = self.phrase.is_none()
&& self.usernames.iter().all(|u| u.is_none())
&& self.auth_secrets.iter().all(|a| a.is_none());
not_chaff && !self.chaff
}
}
/** Get the starting ivc inputs (z0) for the grapevine circuit */ /** Get the starting ivc inputs (z0) for the grapevine circuit */
pub fn get_z0<F: PrimeField>() -> [F; 4] { pub fn get_z0<F: PrimeField>() -> [F; 4] {
(0..4).map(|_| F::zero()).collect::<Vec<F>>().try_into().unwrap()
(0..4)
.map(|_| F::zero())
.collect::<Vec<F>>()
.try_into()
.unwrap()
} }
/** Generates a random field element for given field as bigint */ /** Generates a random field element for given field as bigint */
@ -25,9 +60,7 @@ pub fn random_f_bigint() -> BigInt {
* @param phrase - the string entered by user to compute hash for (will be length checked) * @param phrase - the string entered by user to compute hash for (will be length checked)
* @return - array of 6 Fr elements * @return - array of 6 Fr elements
*/ */
pub fn serialize_phrase(
phrase: &String,
) -> Result<[BigInt; SECRET_FIELD_LENGTH], Box<dyn Error>> {
pub fn serialize_phrase(phrase: &String) -> Result<[BigInt; SECRET_FIELD_LENGTH], Box<dyn Error>> {
// check length // check length
if phrase.len() > MAX_SECRET_LENGTH { if phrase.len() > MAX_SECRET_LENGTH {
return Err("Phrase must be <= 180 characters".into()); return Err("Phrase must be <= 180 characters".into());
@ -69,3 +102,51 @@ pub fn serialize_username(username: &String) -> Result> {
// convert to bigint // convert to bigint
Ok(BigInt::from_bytes_be(Plus, &bytes)) Ok(BigInt::from_bytes_be(Plus, &bytes))
} }
pub fn prepare_external_inputs<F: PrimeField>(inputs: &CircomPrivateInput) -> Vec<F> {
// handle phrase presence (if not present infer chaff)
let phrase = match &inputs.phrase {
Some(phrase) => serialize_phrase(&phrase).unwrap().to_vec(),
None => (0..6)
.map(|_| random_f_bigint::<F>())
.collect::<Vec<BigInt>>(),
};
// determine inputs: first step ([0] = None), Nth step ([1] = Some), and chaff ([2] = None)
// marshal usernames
let usernames = match inputs.usernames[0] {
Some(_) => inputs
.usernames
.iter()
.map(|u| serialize_username(&u.clone().unwrap()).unwrap())
.collect::<Vec<BigInt>>(),
None => match &inputs.usernames[1] {
Some(username) => vec![BigInt::from(0), serialize_username(&username).unwrap()],
None => vec![random_f_bigint::<F>(), random_f_bigint::<F>()],
},
};
// marshal auth secrets
let auth_sec = match inputs.auth_secrets[0] {
Some(_) => inputs
.auth_secrets
.iter()
.map(|a| a.clone().unwrap())
.collect::<Vec<BigInt>>(),
None => match &inputs.auth_secrets[1] {
Some(auth_secret) => vec![BigInt::from(0), auth_secret.clone()],
None => vec![random_f_bigint::<F>(), random_f_bigint::<F>()],
},
};
// NOTE: probably wold be better that the inputs are prepared already as F instead of
// BigInt (at the methods serialize_phrase, serialize_username).
let inp: Vec<BigInt> = [phrase, usernames, auth_sec].concat();
inp.iter()
.map(|v| {
let (_, b) = v.to_bytes_le();
F::from_le_bytes_mod_order(&b)
})
.collect()
}

+ 3
- 4
src/utils/mod.rs

@ -1,8 +1,7 @@
use ark_pallas::Fr;
use ark_bn254::Fr;
use ark_ff::UniformRand; use ark_ff::UniformRand;
use ark_std::rand::rngs::OsRng; use ark_std::rand::rngs::OsRng;
pub mod wrapper;
pub mod inputs; pub mod inputs;
pub const SECRET_FIELD_LENGTH: usize = 6; pub const SECRET_FIELD_LENGTH: usize = 6;
@ -10,6 +9,6 @@ pub const MAX_SECRET_LENGTH: usize = 180;
pub const MAX_USERNAME_LENGTH: usize = 30; pub const MAX_USERNAME_LENGTH: usize = 30;
/** Get a random field element */ /** Get a random field element */
pub fn random_fr() -> ark_pallas::Fr {
pub fn random_fr() -> ark_bn254::Fr {
Fr::rand(&mut OsRng) Fr::rand(&mut OsRng)
}
}

+ 0
- 177
src/utils/wrapper.rs

@ -1,177 +0,0 @@
use crate::utils::inputs::{random_f_bigint, serialize_phrase, serialize_username};
use ark_circom::{
circom::{r1cs_reader, R1CS},
WitnessCalculator,
};
use ark_ff::{BigInteger, PrimeField};
use color_eyre::Result;
use num_bigint::{BigInt, Sign};
use sonobe::Error as SonobeError;
use std::{fs::File, io::BufReader, marker::PhantomData, path::PathBuf};
#[derive(Clone, Debug)]
pub struct CircomPrivateInput {
pub phrase: Option<String>,
pub usernames: [Option<String>; 2],
pub auth_secrets: [Option<BigInt>; 2],
pub chaff: bool,
}
impl CircomPrivateInput {
/**
* Creates empty inputs
*
* @param chaff - if true, should compute random vars for chaff in circuit
*/
pub fn empty(chaff: bool) -> Self {
Self {
phrase: None,
usernames: [None, None],
auth_secrets: [None, None],
chaff,
}
}
pub fn uninitialized(&self) -> bool {
let not_chaff =self.phrase.is_none()
&& self.usernames.iter().all(|u| u.is_none())
&& self.auth_secrets.iter().all(|a| a.is_none());
not_chaff && !self.chaff
}
}
// Wrapper for circom functionalities (extract R1CS and witness)
#[derive(Clone, Debug)]
pub struct CircomWrapper<F: PrimeField> {
r1cs_path: PathBuf,
wc_path: PathBuf,
_marker: PhantomData<F>,
}
impl<F: PrimeField> CircomWrapper<F> {
// creates a new instance of the wrapper with filepaths
pub fn new(r1cs_path: PathBuf, wc_path: PathBuf) -> Self {
Self {
r1cs_path,
wc_path,
_marker: PhantomData,
}
}
/**
* Marshals the private inputs into the format expected by circom
*
* @param inputs - the private inputs
* @return - the marshalled inputs
*/
pub fn marshal_private_inputs(inputs: &CircomPrivateInput) -> [(String, Vec<BigInt>); 3] {
// handle phrase presence (if not present infer chaff)
let phrase = match &inputs.phrase {
Some(phrase) => serialize_phrase(&phrase).unwrap().to_vec(),
None => (0..6)
.map(|_| random_f_bigint::<F>())
.collect::<Vec<BigInt>>(),
};
// determine inputs: first step ([0] = None), Nth step ([1] = Some), and chaff ([2] = None)
// marshal usernames
let usernames = match inputs.usernames[0] {
Some(_) => inputs
.usernames
.iter()
.map(|u| serialize_username(&u.clone().unwrap()).unwrap())
.collect::<Vec<BigInt>>(),
None => match &inputs.usernames[1] {
Some(username) => vec![BigInt::from(0), serialize_username(&username).unwrap()],
None => vec![random_f_bigint::<F>(), random_f_bigint::<F>()],
},
};
// marshal auth secrets
let auth_sec = match inputs.auth_secrets[0] {
Some(_) => inputs
.auth_secrets
.iter()
.map(|a| a.clone().unwrap())
.collect::<Vec<BigInt>>(),
None => match &inputs.auth_secrets[1] {
Some(auth_secret) => vec![BigInt::from(0), auth_secret.clone()],
None => vec![random_f_bigint::<F>(), random_f_bigint::<F>()],
},
};
// label the inputs for circom
[
("phrase".to_string(), phrase),
("usernames".to_string(), usernames),
("auth_secrets".to_string(), auth_sec),
]
}
// 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>>), SonobeError> {
// extract R1CS
let file = File::open(&self.r1cs_path)?;
let reader = BufReader::new(file);
let r1cs_file = r1cs_reader::R1CSFile::<F>::new(reader)?;
let r1cs = r1cs_reader::R1CS::<F>::from(r1cs_file);
// extract witness vector
let witness_vec = self.extract_witness(inputs)?;
Ok((r1cs, Some(witness_vec)))
}
pub fn extract_witness(&self, inputs: &[(String, Vec<BigInt>)]) -> Result<Vec<F>, SonobeError> {
let witness_bigint = self.calculate_witness(inputs)?;
witness_bigint
.iter()
.map(|bigint| {
Self::num_bigint_to_ark_bigint(bigint)
.and_then(|ark_bigint| {
F::from_bigint(ark_bigint).ok_or_else(|| {
SonobeError::Other("Could not get F from bigint".to_string())
})
})
})
.collect()
}
pub fn calculate_witness(
&self,
inputs: &[(String, Vec<BigInt>)],
) -> Result<Vec<BigInt>, SonobeError> {
let mut calculator = WitnessCalculator::new(&self.wc_path).map_err(|e| {
SonobeError::WitnessCalculationError(format!(
"Failed to create WitnessCalculator: {}",
e
))
})?;
calculator
.calculate_witness(inputs.iter().cloned(), true)
.map_err(|e| {
SonobeError::WitnessCalculationError(format!("Failed to calculate witness: {}", e))
})
}
pub fn num_bigint_to_ark_bigint(value: &BigInt) -> Result<F::BigInt, SonobeError> {
let big_uint = value
.to_biguint()
.ok_or_else(|| SonobeError::BigIntConversionError("BigInt is negative".to_string()))?;
F::BigInt::try_from(big_uint).map_err(|_| {
SonobeError::BigIntConversionError(
"Failed to convert to Primefield::BigInt".to_string(),
)
})
}
pub fn ark_primefield_to_num_bigint(value: F) -> BigInt {
let primefield_bigint: F::BigInt = value.into_bigint();
let bytes = primefield_bigint.to_bytes_be();
BigInt::from_bytes_be(Sign::Plus, &bytes)
}
}

Loading…
Cancel
Save