@ -0,0 +1,16 @@ |
|||||
|
# fold-babyjubjubs |
||||
|
|
||||
|
Repo folding BabyJubJub EdDSA signatures using [arkeddsa](https://github.com/kilic/arkeddsa), showcasing usage of [Sonobe](https://github.com/privacy-scaling-explorations/sonobe) with [Arkworks](https://github.com/arkworks-rs). |
||||
|
|
||||
|
The main idea is to prove $z_n = F(F(...~F(F(F(z_0)))))$, where $n$ is the number of BabyJubJub EdDSA signature verifications ($F$) that we compute. Proving this in a 'normal' R1CS circuit for a large $n$ would be too costly, but with folding we can manage to prove it in a reasonable time span. |
||||
|
|
||||
|
For more info about Sonobe, check out [Sonobe's docs](https://privacy-scaling-explorations.github.io/sonobe-docs). |
||||
|
|
||||
|
<p align="center"> |
||||
|
<img src="https://privacy-scaling-explorations.github.io/sonobe-docs/imgs/folding-main-idea-diagram.png" style="width:70%;" /> |
||||
|
</p> |
||||
|
|
||||
|
|
||||
|
### Usage |
||||
|
|
||||
|
- `cargo test --release -- --nocapture` |
@ -0,0 +1,93 @@ |
|||||
|
#[cfg(test)]
|
||||
|
mod tests {
|
||||
|
use ark_bn254::{constraints::GVar, Fr, G1Projective as G1};
|
||||
|
use ark_grumpkin::{constraints::GVar as GVar2, Projective as G2};
|
||||
|
use std::time::Instant;
|
||||
|
|
||||
|
use arkeddsa::ed_on_bn254_twist::{constraints::EdwardsVar, EdwardsProjective};
|
||||
|
|
||||
|
use folding_schemes::{
|
||||
|
commitment::pedersen::Pedersen,
|
||||
|
folding::nova::{Nova, PreprocessorParam},
|
||||
|
frontend::FCircuit,
|
||||
|
transcript::poseidon::poseidon_canonical_config,
|
||||
|
FoldingScheme,
|
||||
|
};
|
||||
|
|
||||
|
use crate::fcircuit::{tests::gen_signatures, FoldSigsStepCircuit};
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_full_flow() {
|
||||
|
// 5 recursive steps, 10 signature verifications per step
|
||||
|
full_flow::<5, 10>();
|
||||
|
// 5 recursive steps, 50 signature verifications per step
|
||||
|
full_flow::<5, 50>();
|
||||
|
}
|
||||
|
|
||||
|
fn full_flow<const N_STEPS: usize, const SIGS_PER_STEP: usize>() {
|
||||
|
println!("\nrunning Nova folding scheme on FoldSigsStepCircuit, with N_STEPS={}, SIGS_PER_STEP={}. Total sigs = {}", N_STEPS, SIGS_PER_STEP, N_STEPS* SIGS_PER_STEP);
|
||||
|
|
||||
|
let mut rng = rand::rngs::OsRng;
|
||||
|
let poseidon_config = poseidon_canonical_config::<Fr>();
|
||||
|
|
||||
|
let pks_sigs =
|
||||
|
gen_signatures::<rand::rngs::OsRng, SIGS_PER_STEP>(&mut rng, &poseidon_config, N_STEPS);
|
||||
|
|
||||
|
// set the initial state
|
||||
|
let z_0: Vec<Fr> = vec![0_u8; 1]
|
||||
|
.iter()
|
||||
|
.map(|v| Fr::from(*v))
|
||||
|
.collect::<Vec<Fr>>();
|
||||
|
|
||||
|
type FC<const S: usize> = FoldSigsStepCircuit<Fr, EdwardsProjective, EdwardsVar, S>;
|
||||
|
let f_circuit = FC::<SIGS_PER_STEP>::new(poseidon_config.clone()).unwrap();
|
||||
|
|
||||
|
// define type aliases for the FoldingScheme (FS) and Decider (D), to avoid writting the
|
||||
|
// whole type each time
|
||||
|
pub type FS<const S: usize> =
|
||||
|
Nova<G1, GVar, G2, GVar2, FC<S>, Pedersen<G1>, Pedersen<G2>, false>;
|
||||
|
|
||||
|
// prepare the Nova prover & verifier params
|
||||
|
let nova_preprocess_params =
|
||||
|
PreprocessorParam::new(poseidon_config.clone(), f_circuit.clone());
|
||||
|
let start = Instant::now();
|
||||
|
let nova_params =
|
||||
|
FS::<SIGS_PER_STEP>::preprocess(&mut rng, &nova_preprocess_params).unwrap();
|
||||
|
println!("Nova params generated: {:?}", start.elapsed());
|
||||
|
|
||||
|
// initialize the folding scheme engine, in our case we use Nova
|
||||
|
let mut nova = FS::<SIGS_PER_STEP>::init(&nova_params, f_circuit, z_0.clone()).unwrap();
|
||||
|
|
||||
|
// run n steps of the folding iteration
|
||||
|
let start_full = Instant::now();
|
||||
|
for i in 0..N_STEPS {
|
||||
|
let start = Instant::now();
|
||||
|
nova.prove_step(rng, pks_sigs[i].clone(), None).unwrap();
|
||||
|
println!("Nova::prove_step {}: {:?}", nova.i, start.elapsed());
|
||||
|
}
|
||||
|
let t = start_full.elapsed();
|
||||
|
println!("Nova's all {} steps time: {:?}", N_STEPS, t);
|
||||
|
println!(
|
||||
|
"N_STEPS={}, SIGS_PER_STEP={}. Total sigs = {}",
|
||||
|
N_STEPS,
|
||||
|
SIGS_PER_STEP,
|
||||
|
N_STEPS * SIGS_PER_STEP
|
||||
|
);
|
||||
|
println!(
|
||||
|
"SIGS PER SECOND: {:?}",
|
||||
|
(N_STEPS * SIGS_PER_STEP) as f64 / t.as_secs_f64()
|
||||
|
);
|
||||
|
println!(
|
||||
|
"TIME FOR EACH SIG: {:?} ms",
|
||||
|
t / (N_STEPS * SIGS_PER_STEP) as u32
|
||||
|
);
|
||||
|
|
||||
|
// verify the last IVC proof
|
||||
|
let ivc_proof = nova.ivc_proof();
|
||||
|
FS::<SIGS_PER_STEP>::verify(
|
||||
|
nova_params.1.clone(), // Nova's verifier params
|
||||
|
ivc_proof,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,7 @@ |
|||||
|
#![allow(non_snake_case)]
|
||||
|
#![allow(dead_code)]
|
||||
|
#![allow(non_camel_case_types)]
|
||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||
|
|
||||
|
mod fcircuit;
|
||||
|
mod fold_babyjubjubs;
|