Browse Source

update to last Sonobe version

main
arnaucube 4 months ago
parent
commit
5c3c0e099a
4 changed files with 75 additions and 28 deletions
  1. +1
    -1
      .gitignore
  2. +3
    -1
      Cargo.toml
  3. +1
    -3
      README.md
  4. +70
    -23
      src/lib.rs

+ 1
- 1
.gitignore

@ -9,7 +9,7 @@ package-lock.json
*.sym
# generated contracts at test time
generated
solidity
*.sol
*.calldata
*.inputs

+ 3
- 1
Cargo.toml

@ -24,7 +24,7 @@ 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"
folding-schemes = { git = "https://github.com/privacy-scaling-explorations/sonobe", package = "folding-schemes", branch="fix/circom-frontend", features=["light-test"]}
folding-schemes = { git = "https://github.com/privacy-scaling-explorations/sonobe", package = "folding-schemes", features=["light-test"]}
solidity-verifiers = { git = "https://github.com/privacy-scaling-explorations/sonobe", package = "solidity-verifiers"}
serde = "1.0.198"
serde_json = "1.0.116"
@ -38,3 +38,5 @@ rand = "0.8.5"
# 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"}
ark-circom = { git = "https://github.com/arnaucube/circom-compat" }
ark-r1cs-std = { git = "https://github.com/winderica/r1cs-std", branch="cherry-pick" }

+ 1
- 3
README.md

@ -4,8 +4,6 @@ Repo to test a more complex [Circom](https://github.com/iden3/circom) circuit wi
Proves a chain of keccak256 hashes, using the [vocdoni/keccak256-circom](https://github.com/vocdoni/keccak256-circom) circuit.
> WIP
assuming rust and circom have been installed:
Assuming rust and circom have been installed:
- `./compile-circuit.sh`
- `cargo test --release -- --nocapture`

+ 70
- 23
src/lib.rs

@ -23,6 +23,7 @@ mod tests {
use ark_std::Zero;
use std::path::PathBuf;
use std::rc::Rc;
use std::time::Instant;
use folding_schemes::{
@ -37,11 +38,10 @@ mod tests {
get_r1cs, Nova, ProverParams, VerifierParams,
},
frontend::{circom::CircomFCircuit, FCircuit},
transcript::poseidon::poseidon_test_config,
Decider, FoldingScheme,
transcript::poseidon::poseidon_canonical_config,
Decider, Error, FoldingScheme,
};
use solidity_verifiers::{
evm::{compile_solidity, Evm},
utils::get_function_selector_for_nova_cyclefold_verifier,
verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider,
NovaCycleFoldVerifierKey,
@ -59,7 +59,7 @@ mod tests {
KZGVerifierKey<Bn254>,
) {
let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_test_config::<Fr>();
let poseidon_config = poseidon_canonical_config::<Fr>();
// get the CM & CF_CM len
let (r1cs, cf_r1cs) =
@ -135,16 +135,31 @@ mod tests {
let b = f_vec_to_bits(v);
BigInteger256::from_bits_le(&b).to_bytes_le()
}
fn bytes_to_f_vec<F: PrimeField>(b: Vec<u8>) -> Result<Vec<F>, Error> {
use num_bigint::BigUint;
let bi = BigUint::from_bytes_le(&b);
let bi = BigInteger256::try_from(bi).unwrap();
let bits = bi.to_bits_le();
Ok(bits
.iter()
.map(|&e| if e == true { F::one() } else { F::zero() })
.collect())
}
// function to compute the next state of the folding via rust-native code (not Circom). Used to
// check the Circom values.
use tiny_keccak::{Hasher, Keccak};
fn rust_native_step(z_i: [u8; 32]) -> [u8; 32] {
fn rust_native_step<F: PrimeField>(
_i: usize,
z_i: Vec<F>,
_external_inputs: Vec<F>,
) -> Result<Vec<F>, Error> {
let b = f_vec_to_bytes(z_i.to_vec());
let mut h = Keccak::v256();
h.update(&z_i);
h.update(&b);
let mut z_i1 = [0u8; 32];
h.finalize(&mut z_i1);
z_i1
bytes_to_f_vec(z_i1.to_vec())
}
#[test]
@ -158,7 +173,30 @@ mod tests {
let wasm_path = PathBuf::from("./circuit/keccak-chain_js/keccak-chain.wasm");
let f_circuit_params = (r1cs_path, wasm_path, 32 * 8, 0);
let f_circuit = CircomFCircuit::<Fr>::new(f_circuit_params).unwrap();
let mut f_circuit = CircomFCircuit::<Fr>::new(f_circuit_params).unwrap();
// Note (optional): for more speed, we can set a custom rust-native logic, which will be
// used for the `step_native` method instead of extracting the values from the circom
// witness:
f_circuit.set_custom_step_native(Rc::new(rust_native_step));
// ----------------
// Sanity check
// check that the f_circuit produces valid R1CS constraints
use ark_r1cs_std::alloc::AllocVar;
use ark_r1cs_std::fields::fp::FpVar;
use ark_r1cs_std::R1CSVar;
use ark_relations::r1cs::ConstraintSystem;
let cs = ConstraintSystem::<Fr>::new_ref();
let z_0_var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_0.clone())).unwrap();
let z_1_var = f_circuit
.generate_step_constraints(cs.clone(), 1, z_0_var, vec![])
.unwrap();
// check z_1_var against the native z_1
let z_1_native = f_circuit.step_native(1, z_0.clone(), vec![]).unwrap();
assert_eq!(z_1_var.value().unwrap(), z_1_native);
// check that the constraint system is satisfied
assert!(cs.is_satisfied().unwrap());
// ----------------
let (fs_prover_params, kzg_vk, g16_pk, g16_vk) =
init_ivc_and_decider_params::<CircomFCircuit<Fr>>(f_circuit.clone());
@ -186,18 +224,16 @@ mod tests {
println!("Nova::prove_step {}: {:?}", nova.i, start.elapsed());
}
// perform the hash chain natively in rust
let z_0_bytes: [u8; 32] = [0u8; 32];
let z_1_bytes = rust_native_step(z_0_bytes);
let z_2_bytes = rust_native_step(z_1_bytes);
let z_3_bytes = rust_native_step(z_2_bytes);
// check that the value of the last folding state (nova.z_i) computed through folding, is equal to the natively
// computed hash using the rust_native_step method
let nova_z_i = f_vec_to_bytes(nova.z_i.clone());
assert_eq!(nova_z_i, z_3_bytes);
// perform the hash chain natively in rust (which uses a rust Keccak256 library)
let z_1 = rust_native_step(0, z_0.clone(), vec![]).unwrap();
let z_2 = rust_native_step(0, z_1, vec![]).unwrap();
let z_3 = rust_native_step(0, z_2, vec![]).unwrap();
// check that the value of the last folding state (nova.z_i) computed through folding, is
// equal to the natively computed hash using the rust_native_step method
assert_eq!(nova.z_i, z_3);
/*
// ----------------
// Sanity check
// The following lines contain a sanity check that checks the IVC proof (before going into
// the zkSNARK proof)
let verifier_params = VerifierParams::<G1, G2> {
@ -216,7 +252,7 @@ mod tests {
cyclefold_instance,
)
.unwrap();
*/
// ----------------
let rng = rand::rngs::OsRng;
let start = Instant::now();
@ -263,24 +299,35 @@ mod tests {
// generate the solidity code
let decider_solidity_code = get_decider_template_for_cyclefold_decider(nova_cyclefold_vk);
/*
* Note: since we're proving the Keccak256 (ie. 32 byte size, 256 bits), the number of
* inputs is too big for the contract. In a real world use case we would convert the binary
* representation into a couple of field elements which would be inputs of the Decider
* circuit, and in-circuit we would obtain the binary representation to be used for the
* final proof check.
*
* The following code is commented out for that reason.
// verify the proof against the solidity code in the EVM
use solidity_verifiers::evm::{compile_solidity, Evm};
let nova_cyclefold_verifier_bytecode =
compile_solidity(&decider_solidity_code, "NovaDecider");
let mut evm = Evm::default();
let verifier_address = evm.create(nova_cyclefold_verifier_bytecode);
let (_, output) = evm.call(verifier_address, calldata.clone());
assert_eq!(*output.last().unwrap(), 1);
*/
// save smart contract and the calldata
println!("storing nova-verifier.sol and the calldata into files");
use std::fs;
fs::create_dir_all("./solidity").unwrap();
fs::write(
"./examples/nova-verifier.sol",
"./solidity/nova-verifier.sol",
decider_solidity_code.clone(),
)
.unwrap();
fs::write("./examples/solidity-calldata.calldata", calldata.clone()).unwrap();
fs::write("./solidity/solidity-calldata.calldata", calldata.clone()).unwrap();
let s = solidity_verifiers::utils::get_formatted_calldata(calldata.clone());
fs::write("./examples/solidity-calldata.inputs", s.join(",\n")).expect("");
fs::write("./solidity/solidity-calldata.inputs", s.join(",\n")).expect("");
}
}

Loading…
Cancel
Save