arnaucube
arnaucube/ethdos-foldPublic Self-hosted repository
ethdos-fold/src/lib.rs
src/lib.rsRaw
  1//! This file contains the WASM bindings, and at the bottom a test running the full flow.
  2//!
  3#![allow(non_snake_case)]
  4#![allow(dead_code)]
  5#![allow(non_camel_case_types)]
  6#![allow(clippy::upper_case_acronyms)]
  7
  8use ark_bn254::{Fr, G1Projective as G1};
  9use ark_ec::AffineRepr;
 10use ark_grumpkin::Projective as G2;
 11use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
 12use ark_std::Zero;
 13use base64::{engine::general_purpose::STANDARD as b64, Engine as _};
 14use rand::rngs::OsRng;
 15
 16use arkeddsa::ed_on_bn254_twist::{constraints::EdwardsVar, EdwardsProjective};
 17
 18use folding_schemes::{
 19    commitment::pedersen::Pedersen,
 20    folding::nova::{Nova, PreprocessorParam},
 21    frontend::FCircuit,
 22    transcript::poseidon::poseidon_canonical_config,
 23    FoldingScheme,
 24};
 25
 26use crate::fcircuit::EthDosCircuit;
 27use crate::signature::{gen_signatures, SigPk};
 28use crate::utils::{dbg, elapsed, get_time};
 29
 30mod fcircuit;
 31mod signature;
 32mod utils;
 33
 34use wasm_bindgen::prelude::*;
 35
 36// define type aliases for the FCircuit (FC) and the FoldingScheme (FS), to avoid writing the whole
 37// type each time.
 38type FC = EthDosCircuit<Fr, EdwardsProjective, EdwardsVar>;
 39type FS = Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>>;
 40
 41#[wasm_bindgen]
 42extern "C" {
 43    fn alert(s: &str);
 44}
 45
 46#[wasm_bindgen]
 47pub fn gen_params() -> Vec<String> {
 48    let mut rng = OsRng;
 49    let poseidon_config = poseidon_canonical_config::<Fr>();
 50
 51    let f_circuit = FC::new(poseidon_config.clone()).unwrap();
 52
 53    let nova_preprocess_params = PreprocessorParam::new(poseidon_config.clone(), f_circuit.clone());
 54    let start = get_time();
 55    let nova_params = FS::preprocess(&mut rng, &nova_preprocess_params).unwrap();
 56    dbg(format!("Nova params generated: {:?}ms", elapsed(start)));
 57
 58    // serialize
 59    let start = get_time();
 60    let mut prover_params_serialized = vec![];
 61    nova_params
 62        .0
 63        .serialize_compressed(&mut prover_params_serialized)
 64        .unwrap();
 65    dbg(format!(
 66        "Nova prover params serialized: {:?}ms",
 67        elapsed(start)
 68    ));
 69
 70    let start = get_time();
 71    let mut verifier_params_serialized = vec![];
 72    nova_params
 73        .1
 74        .serialize_compressed(&mut verifier_params_serialized)
 75        .unwrap();
 76    dbg(format!(
 77        "Nova prover params serialized: {:?}ms",
 78        elapsed(start)
 79    ));
 80
 81    dbg(format!(
 82        "prover_params size: {} mb",
 83        prover_params_serialized.len() / (1024 * 1024)
 84    ));
 85    dbg(format!(
 86        "verifier_params size: {} mb",
 87        verifier_params_serialized.len() / (1024 * 1024)
 88    ));
 89
 90    vec![
 91        b64.encode(&prover_params_serialized),
 92        b64.encode(&prover_params_serialized),
 93    ]
 94}
 95
 96#[wasm_bindgen]
 97pub fn gen_sigs(n_steps: usize) -> Vec<String> {
 98    let mut rng = OsRng;
 99    let poseidon_config = poseidon_canonical_config::<Fr>();
100
101    let sigs: Vec<SigPk<EdwardsProjective>> = gen_signatures(&mut rng, &poseidon_config, n_steps);
102    let b: Vec<Vec<u8>> = sigs.iter().map(|&s| s.to_bytes()).collect();
103    b.iter().map(|s| b64.encode(s)).collect::<Vec<String>>()
104}
105
106#[wasm_bindgen]
107pub fn fold_sigs(params: Vec<String>, sigs_pks: Vec<String>) -> String {
108    dbg("starting fold_sigs (rust)".to_string());
109
110    let poseidon_config = poseidon_canonical_config::<Fr>();
111
112    // parse sigs_pks
113    let b: Vec<Vec<u8>> = sigs_pks.iter().map(|s| b64.decode(s).unwrap()).collect();
114    let pks_sigs: Vec<SigPk<EdwardsProjective>> =
115        b.iter().map(|s| SigPk::from_bytes(s.clone())).collect();
116
117    // parse params
118    let start = get_time();
119    let pp = FS::pp_deserialize_with_mode(
120        &mut b64.decode(params[0].clone()).unwrap().as_slice(),
121        ark_serialize::Compress::Yes,
122        ark_serialize::Validate::Yes,
123        poseidon_config.clone(), // fcircuit_params
124    )
125    .unwrap();
126    let vp = FS::vp_deserialize_with_mode(
127        &mut b64.decode(params[1].clone()).unwrap().as_slice(),
128        ark_serialize::Compress::Yes,
129        ark_serialize::Validate::Yes,
130        poseidon_config.clone(), // fcircuit_params
131    )
132    .unwrap();
133    let fs_params = (pp, vp);
134    dbg(format!(
135        "params (prover & verifier) deserialization: {:?}ms",
136        elapsed(start)
137    ));
138
139    // set the initial state
140    let xy = pks_sigs[0].pk.0.xy().unwrap();
141    let pk0 = vec![xy.0, xy.1];
142    let z_0: Vec<Fr> = [pk0.clone(), pk0, vec![Fr::zero()]].concat();
143
144    type FC = EthDosCircuit<Fr, EdwardsProjective, EdwardsVar>;
145    let f_circuit = FC::new(poseidon_config.clone()).unwrap();
146
147    // initialize the folding scheme engine, in our case we use Nova
148    let mut nova = FS::init(&fs_params, f_circuit, z_0.clone()).unwrap();
149    let rng = OsRng;
150    let n_steps = sigs_pks.len();
151
152    let start_full = get_time();
153    #[allow(clippy::needless_range_loop)]
154    for i in 0..n_steps {
155        let start = get_time();
156        nova.prove_step(rng, pks_sigs[i], None).unwrap();
157        dbg(format!(
158            "Nova::prove_step {}: {:?}ms",
159            nova.i,
160            elapsed(start)
161        ));
162    }
163    dbg(format!(
164        "Nova's all {} steps time: {:?}ms",
165        n_steps,
166        elapsed(start_full)
167    ));
168
169    let ivc_proof = nova.ivc_proof();
170    let mut ivc_proof_bytes = vec![];
171    ivc_proof
172        .serialize_compressed(&mut ivc_proof_bytes)
173        .unwrap();
174    dbg(format!(
175        "ivc_proof size: {} mb",
176        ivc_proof_bytes.len() / (1024 * 1024)
177    ));
178
179    b64.encode(ivc_proof_bytes)
180}
181
182#[wasm_bindgen]
183pub fn verify_proof(verifier_params: String, ivc_proof: String) -> String {
184    let poseidon_config = poseidon_canonical_config::<Fr>();
185
186    let vp = FS::vp_deserialize_with_mode(
187        &mut b64.decode(verifier_params.clone()).unwrap().as_slice(),
188        ark_serialize::Compress::Yes,
189        ark_serialize::Validate::Yes,
190        poseidon_config.clone(), // fcircuit_params
191    )
192    .unwrap();
193    // let proof =
194    //     FS::IVCProof::deserialize_compressed(b64.decode(ivc_proof).unwrap().as_slice()).unwrap();
195    let proof = <Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>, false> as FoldingScheme<
196        G1,
197        G2,
198        FC,
199    >>::IVCProof::deserialize_compressed(b64.decode(ivc_proof).unwrap().as_slice())
200    .unwrap();
201
202    FS::verify(
203        vp, // Nova's verifier params
204        proof,
205    )
206    .unwrap();
207    "verified".to_string()
208}
209
210#[cfg(test)]
211mod tests {
212    use ark_bn254::{Fr, G1Projective as G1};
213    use ark_ec::AffineRepr;
214    use ark_grumpkin::Projective as G2;
215    use ark_std::Zero;
216    use rand::rngs::OsRng;
217
218    use arkeddsa::ed_on_bn254_twist::{constraints::EdwardsVar, EdwardsProjective};
219
220    use folding_schemes::{
221        commitment::pedersen::Pedersen,
222        folding::nova::{Nova, PreprocessorParam},
223        frontend::FCircuit,
224        transcript::poseidon::poseidon_canonical_config,
225        FoldingScheme,
226    };
227
228    use crate::{
229        fcircuit::EthDosCircuit,
230        signature::gen_signatures,
231        utils::{dbg, elapsed, get_time},
232    };
233
234    // test showing a full-execution example.
235    #[test]
236    fn test_full_flow() {
237        // set how many steps of folding we want to compute
238        const N_STEPS: usize = 10;
239        dbg(format!(
240            "running Nova folding scheme on EthDosCircuit, with N_STEPS={}",
241            N_STEPS
242        ));
243
244        let mut rng = OsRng;
245        let poseidon_config = poseidon_canonical_config::<Fr>();
246
247        let pks_sigs =
248            gen_signatures::<OsRng, EdwardsProjective>(&mut rng, &poseidon_config, N_STEPS);
249
250        // set the initial state
251        let xy = pks_sigs[0].pk.0.xy().unwrap();
252        let pk0 = vec![xy.0, xy.1];
253        let z_0: Vec<Fr> = vec![pk0.clone(), pk0, vec![Fr::zero()]].concat();
254
255        type FC = EthDosCircuit<Fr, EdwardsProjective, EdwardsVar>;
256        let f_circuit = FC::new(poseidon_config.clone()).unwrap();
257
258        // define type aliases for the FoldingScheme (FS) and Decider (D), to avoid writing the
259        // whole type each time
260        pub type FS = Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>, false>;
261
262        // prepare the Nova prover & verifier params
263        let nova_preprocess_params =
264            PreprocessorParam::new(poseidon_config.clone(), f_circuit.clone());
265        let start = get_time();
266        let nova_params = FS::preprocess(&mut rng, &nova_preprocess_params).unwrap();
267        dbg(format!("Nova params generated: {:?}", elapsed(start)));
268
269        // initialize the folding scheme engine, in our case we use Nova
270        let mut nova = FS::init(&nova_params, f_circuit, z_0.clone()).unwrap();
271
272        // run n steps of the folding iteration
273        let start_full = get_time();
274        for i in 0..N_STEPS {
275            let start = get_time();
276            nova.prove_step(rng, pks_sigs[i].clone(), None).unwrap();
277            dbg(format!("Nova::prove_step {}: {:?}", nova.i, elapsed(start)));
278        }
279        dbg(format!(
280            "Nova's all {} steps time: {:?}",
281            N_STEPS,
282            elapsed(start_full)
283        ));
284
285        // verify the last IVC proof
286        let ivc_proof = nova.ivc_proof();
287        dbg!(&ivc_proof.z_i);
288        FS::verify(
289            nova_params.1.clone(), // Nova's verifier params
290            ivc_proof,
291        )
292        .unwrap();
293    }
294}