Skip to content

Commit

Permalink
feat(circom): wip working on evaluation values
Browse files Browse the repository at this point in the history
  • Loading branch information
tchataigner committed Feb 23, 2024
1 parent e0e4db4 commit fe798a8
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 80 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ criterion = "0.5"
expect-test = "1.4.1"
hex = "0.4.3"
statrs = "0.16.0"
tiny-keccak = { version = "2.0.2", features = ["keccak"] }
tap = "1.0.1"
tempfile = { workspace = true }

Expand Down
174 changes: 139 additions & 35 deletions examples/circom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,27 @@ use lurk::circuit::gadgets::pointer::AllocatedPtr;
#[cfg(not(target_arch = "wasm32"))]
use lurk::coprocessor::circom::non_wasm::CircomCoprocessor;

use halo2curves::bn256::Fr as Bn;
use lurk::eval::lang::Lang;
use lurk::field::LurkField;
use lurk::lem::{pointers::Ptr, store::Store};
use lurk::proof::{nova::NovaProver, Prover, RecursiveSNARKTrait};
use lurk::proof::RecursiveSNARKTrait;
use lurk::public_parameters::{
instance::{Instance, Kind},
public_params,
supernova_public_params,
};
use lurk::Symbol;
use lurk_macros::Coproc;
use pasta_curves::pallas::{Scalar as Fr, Scalar};

use anyhow::Result;
use circom_scotia::r1cs::CircomInput;
use ff::PrimeField;
use lurk::lem::eval::{
evaluate, make_cprocs_funcs_from_lang, make_eval_step_from_config, EvalConfig,
};
use lurk::proof::supernova::SuperNovaProver;
use lurk::state::user_sym;
use sha2::{Digest, Sha256};

const REDUCTION_COUNT: usize = 1;

Expand All @@ -72,24 +79,97 @@ impl<F: LurkField> CircomSha256<F> {
}
}

pub fn from_vec_u32<F: PrimeField>(arr: Vec<u32>) -> F {
let mut res = F::ZERO;
let radix = F::from(0x0001_0000_0000_u64);
for &val in &arr {
res = res * radix + F::from(u64::from(val));
}
res
}

/// Helper function to convert a vector of [`u32`] values to a [`PrimeField`] element. Assumes little endian representation.
/// Compatible with Circom version 2.
pub fn to_vec_u32<F: PrimeField>(f: F) -> Result<Vec<u32>> {
let repr = F::to_repr(&f);
let repr = repr.as_ref();

let (pre, res, suf) = unsafe { repr.align_to::<u32>() };

if !pre.is_empty() || !suf.is_empty() {
panic!("nope")
}

Ok(res.into())
}

impl<F: LurkField> CircomGadget<F> for CircomSha256<F> {
fn reference(&self) -> &CircomGadgetReference {
&self.reference
}

fn into_circom_input(self, input: &[AllocatedPtr<F>]) -> Vec<CircomInput<F>> {
dbg!(input.len());
dbg!(input.get(0).unwrap().hash().get_value());
dbg!(input.get(0).unwrap().hash().get_variable().get_unchecked());
dbg!(input.get(1).unwrap().hash().get_value());
dbg!(input.get(1).unwrap().hash().get_variable().get_unchecked());
// TODO: actually use the lurk inputs
let a = CircomInput::new("a".into(), vec![F::ZERO]);
let b = CircomInput::new("b".into(), vec![F::ZERO]);
dbg!("------------------------------INTO CIRCOM INPUT--------------------------------");
dbg!(input
.get(0)
.unwrap()
.hash()
.get_value()
.or_else(|| Some(F::ZERO))
.unwrap()
.to_bytes());
dbg!(input
.get(1)
.unwrap()
.hash()
.get_value()
.or_else(|| Some(F::ZERO))
.unwrap()
.to_bytes());
let a = CircomInput::new(
"a".into(),
vec![input
.get(0)
.unwrap()
.hash()
.get_value()
.or_else(|| Some(F::ZERO))
.unwrap()],
);
let b = CircomInput::new(
"b".into(),
vec![input
.get(1)
.unwrap()
.hash()
.get_value()
.or_else(|| Some(F::ZERO))
.unwrap()],
);
vec![a, b]
}

fn evaluate_simple(&self, s: &Store<F>, _args: &[Ptr]) -> Ptr {
fn evaluate_simple(&self, s: &Store<F>, args: &[Ptr]) -> Ptr {
dbg!("------------------------------EVALUATE SIMPLE--------------------------------");

let a_ptr = &args[0];
let b_ptr = &args[1];
let a_scalar = *s.hash_ptr(a_ptr).value();
let b_scalar = *s.hash_ptr(b_ptr).value();

dbg!(s.fetch_string(a_ptr));
dbg!(s.fetch_string(b_ptr));

// Create a Sha256 object
let mut hasher = Sha256::new();

// Write input message
hasher.update([a_scalar.to_bytes(), b_scalar.to_bytes()].concat());

// Read hash digest and consume hasher
let result: Vec<u8> = hasher.finalize().to_vec();
dbg!(F::from_bytes(&result));
// TODO hash to get proper ptr
// TODO: actually use the lurk inputs
s.num(
F::from_str_vartime(
Expand All @@ -105,7 +185,7 @@ impl<F: LurkField> CircomGadget<F> for CircomSha256<F> {
}

#[derive(Clone, Debug, Coproc)]
enum Sha256Coproc<F: LurkField> {
enum CircomCoproc<F: LurkField> {
SC(CircomCoprocessor<F, CircomSha256<F>>),
}

Expand All @@ -114,50 +194,74 @@ enum Sha256Coproc<F: LurkField> {
/// `cargo run --release --example circom`
fn main() {
let store = &Store::default();
let sym_str = Symbol::new(&[".circom_sha256_2"], false); // two inputs
let circom_sym = user_sym(&format!("circom_sha256_2"));

let circom_sha256: CircomSha256<Bn> = CircomSha256::new(0).unwrap();
let mut lang = Lang::<Bn, Sha256Coproc<Bn>>::new();
let expr = "(circom_sha256_2 \"a\" \"b\")".to_string();
let ptr = store.read_with_default_state(&expr).unwrap();

lang.add_coprocessor(sym_str, CircomCoprocessor::new(circom_sha256));
let lang_rc = Arc::new(lang);
let circom_sha256: CircomSha256<Fr> = CircomSha256::new(0).unwrap();
let mut lang = Lang::<Fr, CircomCoproc<Fr>>::new();

let expr = "(.circom_sha256_2 \"b\" \"a\")".to_string();
let ptr = store.read_with_default_state(&expr).unwrap();
lang.add_coprocessor(circom_sym, CircomCoprocessor::new(circom_sha256));
let lang_rc = Arc::new(lang.clone());

let nova_prover = NovaProver::<Bn, Sha256Coproc<Bn>>::new(REDUCTION_COUNT, lang_rc.clone());
let lurk_step = make_eval_step_from_config(&EvalConfig::new_nivc(&lang));
let cprocs = make_cprocs_funcs_from_lang(&lang);
let frames = evaluate(Some((&lurk_step, &cprocs, &lang)), ptr, store, 1000).unwrap();

println!("Setting up public parameters...");
// TODO is reduction count alright
let supernova_prover = SuperNovaProver::<Fr, CircomCoproc<Fr>>::new(10, lang_rc.clone());

let pp_start = Instant::now();
let instance = Instance::new(REDUCTION_COUNT, lang_rc, true, Kind::NovaPublicParams);
let pp = public_params(&instance).unwrap();
let pp_end = pp_start.elapsed();

println!("Public parameters took {pp_end:?}");
println!("Setting up running claim parameters (rc = 10)...");

println!("Beginning proof step...");
let instance_primary = Instance::new(10, lang_rc, true, Kind::SuperNovaAuxParams);
let pp = supernova_public_params(&instance_primary).unwrap();

let pp_end = pp_start.elapsed();
println!("Running claim parameters took {:?}", pp_end);

println!("Beginning proof step...");
let proof_start = Instant::now();
let (proof, z0, zi, _num_steps) = nova_prover
.evaluate_and_prove(&pp, ptr, store.intern_empty_env(), store, 10000)
.unwrap();
let (proof, z0, zi, _num_steps) = tracing_texray::examine(tracing::info_span!("bang!"))
.in_scope(|| {
supernova_prover
.prove_from_frames(&pp, &frames, store)
.unwrap()
});
let proof_end = proof_start.elapsed();

println!("Proofs took {proof_end:?}");
println!("Proofs took {:?}", proof_end);

println!("Verifying proof...");

let verify_start = Instant::now();
let res = proof.verify(&pp, &z0, &zi).unwrap();
assert!(proof.verify(&pp, &z0, &zi).unwrap());
let verify_end = verify_start.elapsed();

println!("Verify took {verify_end:?}");
println!("Verify took {:?}", verify_end);

println!("Compressing proof..");
let compress_start = Instant::now();
let compressed_proof = proof.compress(&pp).unwrap();
let compress_end = compress_start.elapsed();

println!("Compression took {:?}", compress_end);

let buf = bincode::serialize(&compressed_proof).unwrap();
println!("proof size : {:}B", buf.len());

let compressed_verify_start = Instant::now();
let res = compressed_proof.verify(&pp, &z0, &zi).unwrap();
let compressed_verify_end = compressed_verify_start.elapsed();

println!("Final verification took {:?}", compressed_verify_end);

if res {
println!(
"Congratulations! You proved and verified a CIRCOM-SHA256 hash calculation in {:?} time!",
pp_end + proof_end + verify_end
"Congratulations! You proved, verified, compressed, and verified (again!) an NIVC SHA256 hash calculation in {:?} time!",
verify_end + proof_end + verify_end + compress_end
);
}
}
Loading

0 comments on commit fe798a8

Please sign in to comment.