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}