mirror of
https://github.com/arnaucube/grapevine-sonobe.git
synced 2026-01-11 08:21:30 +01:00
port to last Sonobe version (which includes external_inputs), update from pasta curves to bn254,grumpkin in rust to match circom usage"
This commit is contained in:
16
Cargo.toml
16
Cargo.toml
@@ -6,12 +6,9 @@ edition = "2021"
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[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-ff = "0.4.1"
|
||||
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"
|
||||
color-eyre = "0.6.2"
|
||||
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_json = "1.0.116"
|
||||
|
||||
[dev-dependencies]
|
||||
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"}
|
||||
|
||||
@@ -17,10 +17,18 @@ template grapevine(num_felts) {
|
||||
signal input ivc_input[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
|
||||
signal degrees_of_separation <== ivc_input[0];
|
||||
@@ -94,4 +102,4 @@ template grapevine(num_felts) {
|
||||
ivc_output <== chaff_mux.out;
|
||||
}
|
||||
|
||||
component main { public [ivc_input] } = grapevine(6);
|
||||
component main { public [ivc_input] } = grapevine(6);
|
||||
|
||||
178
src/circom.rs
178
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)]
|
||||
mod test {
|
||||
use super::*;
|
||||
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_vesta::{constraints::GVar as GVar2, Projective as Projective2};
|
||||
use lazy_static::lazy_static;
|
||||
use num_bigint::BigInt;
|
||||
use sonobe::{
|
||||
commitment::pedersen::Pedersen, folding::nova::Nova,
|
||||
commitment::pedersen::Pedersen, folding::nova::Nova, frontend::circom::CircomFCircuit,
|
||||
transcript::poseidon::poseidon_test_config, Error, FoldingScheme,
|
||||
};
|
||||
use sonobe::{frontend::FCircuit, Error as SonobeError};
|
||||
use std::env::current_dir;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::errors::GrapevineError;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref R1CS_PATH: PathBuf = PathBuf::from("./circom/artifacts/grapevine.r1cs");
|
||||
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())],
|
||||
chaff: false,
|
||||
};
|
||||
let z_0 = get_z0();
|
||||
let external_inputs = prepare_external_inputs::<Fr>(&step_0_inputs);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_step_constraints() {
|
||||
// initialize new Grapevine function circuit
|
||||
let mut f_circuit = GrapevineFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone()));
|
||||
|
||||
// define inputs
|
||||
let step_0_inputs = CircomPrivateInput {
|
||||
phrase: Some(String::from(&*PHRASE)),
|
||||
@@ -190,22 +72,34 @@ mod test {
|
||||
auth_secrets: [None, Some(AUTH_SECRETS[0].clone())],
|
||||
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
|
||||
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
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
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();
|
||||
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]
|
||||
fn test_multiple_steps_native() {
|
||||
// initialize new Grapevine function circuit
|
||||
@@ -283,6 +177,7 @@ mod test {
|
||||
assert_eq!(z_i[0], Fr::from(3));
|
||||
assert_eq!(z_i[3], Fr::from(0));
|
||||
}
|
||||
*/
|
||||
|
||||
// #[test]
|
||||
// fn test_multiple_steps_constraints() {
|
||||
@@ -332,6 +227,7 @@ mod test {
|
||||
//
|
||||
// }
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn test_full_one_step() {
|
||||
// initialize new Grapevine function circuit
|
||||
@@ -340,7 +236,6 @@ mod test {
|
||||
// Get test params
|
||||
let (prover_params, verifier_params) =
|
||||
test_nova_setup::<GrapevineFCircuit<Fr>>(f_circuit.clone());
|
||||
|
||||
|
||||
// define inputs
|
||||
// define inputs
|
||||
@@ -406,4 +301,5 @@ mod test {
|
||||
.unwrap();
|
||||
println!("Verified: {:?}", start.elapsed());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
22
src/nova.rs
22
src/nova.rs
@@ -1,3 +1,4 @@
|
||||
use ark_bn254::{constraints::GVar, Fr, G1Projective as Projective};
|
||||
use ark_crypto_primitives::{
|
||||
crh::{
|
||||
poseidon::constraints::{CRHGadget, CRHParametersVar},
|
||||
@@ -7,25 +8,22 @@ use ark_crypto_primitives::{
|
||||
sponge::{poseidon::PoseidonConfig, Absorb},
|
||||
};
|
||||
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_std::Zero;
|
||||
use ark_vesta::{constraints::GVar as Gvar2, Projective as Projective2};
|
||||
use core::marker::PhantomData;
|
||||
use std::time::Instant;
|
||||
use sonobe::{
|
||||
commitment::{pedersen::Pedersen, CommitmentScheme},
|
||||
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)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@@ -35,7 +33,8 @@ mod test {
|
||||
fn test_generate_params() {
|
||||
let r1cs_path = PathBuf::from("./circom/artifacts/circuit.r1cs");
|
||||
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 (prover_params, verifier_params) = test_nova_setup::<GrapevineFCircuit<Fr>>(f_circuit);
|
||||
// let prover_params_str = serde_json::to_string(&prover_params).unwrap();
|
||||
@@ -44,3 +43,4 @@ mod test {
|
||||
println!("Time to generate params: {:?}", pre.elapsed());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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::{
|
||||
commitment::{pedersen::Pedersen, CommitmentScheme},
|
||||
folding::nova::{get_r1cs, ProverParams, VerifierParams},
|
||||
frontend::FCircuit,
|
||||
transcript::poseidon::poseidon_test_config
|
||||
transcript::poseidon::poseidon_test_config,
|
||||
};
|
||||
|
||||
pub fn test_nova_setup<FC: FCircuit<Fr>>(
|
||||
f_circuit: FC
|
||||
f_circuit: FC,
|
||||
) -> (
|
||||
ProverParams<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>>,
|
||||
VerifierParams<Projective, Projective2>
|
||||
VerifierParams<Projective, Projective2>,
|
||||
) {
|
||||
let mut rng = ark_std::test_rng();
|
||||
let poseidon_config = poseidon_test_config::<Fr>();
|
||||
|
||||
// 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_cf_len = cf_r1cs.A.n_rows;
|
||||
|
||||
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 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(),
|
||||
r1cs,
|
||||
cf_r1cs
|
||||
cf_r1cs,
|
||||
};
|
||||
|
||||
(prover_params, verifier_params)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 num_bigint::{BigInt, RandBigInt, Sign::Plus};
|
||||
use std::error::Error;
|
||||
use num_bigint::{BigInt, Sign::Plus, RandBigInt};
|
||||
|
||||
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 */
|
||||
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 */
|
||||
@@ -25,9 +60,7 @@ pub fn random_f_bigint<F: PrimeField>() -> BigInt {
|
||||
* @param phrase - the string entered by user to compute hash for (will be length checked)
|
||||
* @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
|
||||
if phrase.len() > MAX_SECRET_LENGTH {
|
||||
return Err("Phrase must be <= 180 characters".into());
|
||||
@@ -69,3 +102,51 @@ pub fn serialize_username(username: &String) -> Result<BigInt, Box<dyn Error>> {
|
||||
// convert to bigint
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use ark_pallas::Fr;
|
||||
use ark_bn254::Fr;
|
||||
use ark_ff::UniformRand;
|
||||
use ark_std::rand::rngs::OsRng;
|
||||
|
||||
pub mod wrapper;
|
||||
pub mod inputs;
|
||||
|
||||
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;
|
||||
|
||||
/** Get a random field element */
|
||||
pub fn random_fr() -> ark_pallas::Fr {
|
||||
pub fn random_fr() -> ark_bn254::Fr {
|
||||
Fr::rand(&mut OsRng)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user