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
175    let ivc_proof_bytes_comp = lz4_flex::block::compress_prepend_size(&ivc_proof_bytes);
176
177    dbg(format!(
178        "ivc_proof size (uncompressed): {} mb",
179        ivc_proof_bytes.len() / (1024 * 1024)
180    ));
181    dbg(format!(
182        "ivc_proof size (compressed): {} mb",
183        ivc_proof_bytes_comp.len() / (1024 * 1024)
184    ));
185
186    b64.encode(ivc_proof_bytes_comp)
187}
188
189#[wasm_bindgen]
190pub fn verify_proof(verifier_params: String, ivc_proof_b64: String) -> String {
191    let poseidon_config = poseidon_canonical_config::<Fr>();
192
193    let vp = FS::vp_deserialize_with_mode(
194        &mut b64.decode(verifier_params.clone()).unwrap().as_slice(),
195        ark_serialize::Compress::Yes,
196        ark_serialize::Validate::Yes,
197        poseidon_config.clone(), // fcircuit_params
198    )
199    .unwrap();
200    let ivc_proof_bytes_comp = b64.decode(ivc_proof_b64).unwrap();
201    let ivc_proof_bytes =
202        lz4_flex::block::decompress_size_prepended(&ivc_proof_bytes_comp).unwrap();
203    let proof = <Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>, false> as FoldingScheme<
204        G1,
205        G2,
206        FC,
207    >>::IVCProof::deserialize_compressed(ivc_proof_bytes.as_slice())
208    .unwrap();
209
210    FS::verify(
211        vp, // Nova's verifier params
212        proof,
213    )
214    .unwrap();
215    "verified".to_string()
216}
217
218#[cfg(test)]
219mod tests {
220    use ark_bn254::{Fr, G1Projective as G1};
221    use ark_ec::AffineRepr;
222    use ark_grumpkin::Projective as G2;
223    use ark_serialize::CanonicalSerialize;
224    use ark_std::Zero;
225    use rand::rngs::OsRng;
226
227    use arkeddsa::ed_on_bn254_twist::{constraints::EdwardsVar, EdwardsProjective};
228
229    use folding_schemes::{
230        commitment::pedersen::Pedersen,
231        folding::nova::{Nova, PreprocessorParam},
232        frontend::FCircuit,
233        transcript::poseidon::poseidon_canonical_config,
234        FoldingScheme,
235    };
236
237    use crate::{
238        fcircuit::EthDosCircuit,
239        signature::gen_signatures,
240        utils::{dbg, elapsed, get_time},
241    };
242
243    // test showing a full-execution example.
244    #[test]
245    fn test_full_flow() {
246        // set how many steps of folding we want to compute
247        const N_STEPS: usize = 10;
248        dbg(format!(
249            "running Nova folding scheme on EthDosCircuit, with N_STEPS={}",
250            N_STEPS
251        ));
252
253        let mut rng = OsRng;
254        let poseidon_config = poseidon_canonical_config::<Fr>();
255
256        let pks_sigs =
257            gen_signatures::<OsRng, EdwardsProjective>(&mut rng, &poseidon_config, N_STEPS);
258
259        // set the initial state
260        let xy = pks_sigs[0].pk.0.xy().unwrap();
261        let pk0 = vec![xy.0, xy.1];
262        let z_0: Vec<Fr> = [pk0.clone(), pk0, vec![Fr::zero()]].concat();
263
264        type FC = EthDosCircuit<Fr, EdwardsProjective, EdwardsVar>;
265        let f_circuit = FC::new(poseidon_config.clone()).unwrap();
266
267        // define type aliases for the FoldingScheme (FS) and Decider (D), to avoid writing the
268        // whole type each time
269        pub type FS = Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>, false>;
270
271        // prepare the Nova prover & verifier params
272        let nova_preprocess_params =
273            PreprocessorParam::new(poseidon_config.clone(), f_circuit.clone());
274        let start = get_time();
275        let nova_params = FS::preprocess(&mut rng, &nova_preprocess_params).unwrap();
276        dbg(format!("Nova params generated: {:?}", elapsed(start)));
277
278        // initialize the folding scheme engine, in our case we use Nova
279        let mut nova = FS::init(&nova_params, f_circuit, z_0.clone()).unwrap();
280
281        // run n steps of the folding iteration
282        let start_full = get_time();
283        #[allow(clippy::needless_range_loop)]
284        for i in 0..N_STEPS {
285            let start = get_time();
286            nova.prove_step(rng, pks_sigs[i], None).unwrap();
287            dbg(format!("Nova::prove_step {}: {:?}", nova.i, elapsed(start)));
288        }
289        dbg(format!(
290            "Nova's all {} steps time: {:?}",
291            N_STEPS,
292            elapsed(start_full)
293        ));
294
295        // verify the last IVC proof
296        let ivc_proof = nova.ivc_proof();
297        dbg!(&ivc_proof.z_i);
298        FS::verify(
299            nova_params.1.clone(), // Nova's verifier params
300            ivc_proof.clone(),
301        )
302        .unwrap();
303
304        // print IVCProof size (uncompressed & compressed)
305        let mut ivc_proof_bytes = vec![];
306        ivc_proof
307            .serialize_compressed(&mut ivc_proof_bytes)
308            .unwrap();
309        let ivc_proof_bytes_comp = lz4_flex::block::compress_prepend_size(&ivc_proof_bytes);
310        dbg(format!(
311            "ivc_proof size (uncompressed): {} mb",
312            ivc_proof_bytes.len() / (1024 * 1024)
313        ));
314        dbg(format!(
315            "ivc_proof size (compressed): {} mb",
316            ivc_proof_bytes_comp.len() / (1024 * 1024)
317        ));
318    }
319}