plonk_wasm/
srs.rs

1use crate::{wasm_flat_vector::WasmFlatVector, wasm_vector::WasmVector};
2use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Evaluations};
3use core::ops::Deref;
4use paste::paste;
5use poly_commitment::{
6    commitment::b_poly_coefficients, hash_map_cache::HashMapCache, ipa::SRS, SRS as ISRS,
7};
8use serde::{Deserialize, Serialize};
9use std::{
10    fs::{File, OpenOptions},
11    io::{BufReader, BufWriter, Seek, SeekFrom::Start},
12    sync::Arc,
13};
14use wasm_bindgen::prelude::*;
15
16macro_rules! impl_srs {
17    ($name: ident,
18     $WasmF: ty,
19     $WasmG: ty,
20     $F: ty,
21     $G: ty,
22     $WasmPolyComm: ty,
23     $field_name: ident) => {
24        paste! {
25            #[wasm_bindgen]
26            #[derive(Clone)]
27            pub struct [<Wasm $field_name:camel Srs>](
28                #[wasm_bindgen(skip)]
29                pub Arc<SRS<$G>>);
30
31            impl Deref for [<Wasm $field_name:camel Srs>] {
32                type Target = Arc<SRS<$G>>;
33
34                fn deref(&self) -> &Self::Target { &self.0 }
35            }
36
37            impl From<Arc<SRS<$G>>> for [<Wasm $field_name:camel Srs>] {
38                fn from(x: Arc<SRS<$G>>) -> Self {
39                    [<Wasm $field_name:camel Srs>](x)
40                }
41            }
42
43            impl From<&Arc<SRS<$G>>> for [<Wasm $field_name:camel Srs>] {
44                fn from(x: &Arc<SRS<$G>>) -> Self {
45                    [<Wasm $field_name:camel Srs>](x.clone())
46                }
47            }
48
49            impl From<[<Wasm $field_name:camel Srs>]> for Arc<SRS<$G>> {
50                fn from(x: [<Wasm $field_name:camel Srs>]) -> Self {
51                    x.0
52                }
53            }
54
55            impl From<&[<Wasm $field_name:camel Srs>]> for Arc<SRS<$G>> {
56                fn from(x: &[<Wasm $field_name:camel Srs>]) -> Self {
57                    x.0.clone()
58                }
59            }
60
61            impl<'a> From<&'a [<Wasm $field_name:camel Srs>]> for &'a Arc<SRS<$G>> {
62                fn from(x: &'a [<Wasm $field_name:camel Srs>]) -> Self {
63                    &x.0
64                }
65            }
66
67            #[wasm_bindgen]
68            pub fn [<$name:snake _create>](depth: i32) -> [<Wasm $field_name:camel Srs>] {
69                Arc::new(SRS::create(depth as usize)).into()
70            }
71
72            #[wasm_bindgen]
73            pub fn [<$name:snake _add_lagrange_basis>](
74                srs: &[<Wasm $field_name:camel Srs>],
75                log2_size: i32,
76            ) {
77                crate::rayon::run_in_pool(|| {
78                    let domain = EvaluationDomain::<$F>::new(1 << (log2_size as usize)).expect("invalid domain size");
79                    srs.get_lagrange_basis(domain);
80                });
81            }
82
83            #[wasm_bindgen]
84            pub fn [<$name:snake _write>](
85                append: Option<bool>,
86                srs: &[<Wasm $field_name:camel Srs>],
87                path: String,
88            ) -> Result<(), JsValue> {
89                let file = OpenOptions::new()
90                    .append(append.unwrap_or(true))
91                    .open(path)
92                    .map_err(|err| {
93                        JsValue::from_str(format!("caml_pasta_fp_urs_write: {}", err).as_str())
94                    })?;
95                let file = BufWriter::new(file);
96
97                srs.0.serialize(&mut rmp_serde::Serializer::new(file))
98                .map_err(|e| JsValue::from_str(format!("caml_pasta_fp_urs_write: {}", e).as_str()))
99            }
100
101            #[wasm_bindgen]
102            pub fn [<$name:snake _read>](
103                offset: Option<i32>,
104                path: String,
105            ) -> Result<Option<[<Wasm $field_name:camel Srs>]>, JsValue> {
106                let file = File::open(path).map_err(|err| {
107                    JsValue::from_str(format!("caml_pasta_fp_urs_read: {}", err).as_str())
108                })?;
109                let mut reader = BufReader::new(file);
110
111                if let Some(offset) = offset {
112                    reader.seek(Start(offset as u64)).map_err(|err| {
113                        JsValue::from_str(format!("caml_pasta_fp_urs_read: {}", err).as_str())
114                    })?;
115                }
116
117                // TODO: shouldn't we just error instead of returning None?
118                let srs = match SRS::<$G>::deserialize(&mut rmp_serde::Deserializer::new(reader)) {
119                    Ok(srs) => srs,
120                    Err(_) => return Ok(None),
121                };
122
123                Ok(Some(Arc::new(srs).into()))
124            }
125
126            #[wasm_bindgen]
127            pub fn [<$name:snake _lagrange_commitments_whole_domain_ptr>](
128                srs: &[<Wasm $field_name:camel Srs>],
129                domain_size: i32,
130            ) -> *mut WasmVector<$WasmPolyComm> {
131                // this is the best workaround we have, for now
132                // returns a pointer to the commitment
133                // later, we read the commitment from the pointer
134                let comm = srs
135                    .get_lagrange_basis_from_domain_size(domain_size as usize)
136                    .clone()
137                    .into_iter()
138                    .map(|x| x.into())
139                    .collect();
140                let boxed_comm = Box::<WasmVector<WasmPolyComm>>::new(comm);
141                Box::into_raw(boxed_comm)
142            }
143
144            /// Reads the lagrange commitments from a raw pointer.
145            ///
146            /// # Safety
147            ///
148            /// This function is unsafe because it might dereference a
149            /// raw pointer.
150            #[wasm_bindgen]
151            pub unsafe fn [<$name:snake _lagrange_commitments_whole_domain_read_from_ptr>](
152                ptr: *mut WasmVector<$WasmPolyComm>,
153            ) -> WasmVector<$WasmPolyComm> {
154                // read the commitment at the pointers address, hack for the web
155                // worker implementation (see o1js web worker impl for details)
156                let b = unsafe { Box::from_raw(ptr) };
157                b.as_ref().clone()
158            }
159
160            #[wasm_bindgen]
161            pub fn [<$name:snake _lagrange_commitment>](
162                srs: &[<Wasm $field_name:camel Srs>],
163                domain_size: i32,
164                i: i32,
165            ) -> Result<$WasmPolyComm, JsValue> {
166                let x_domain = EvaluationDomain::<$F>::new(domain_size as usize).ok_or_else(|| {
167                    JsValue::from_str("caml_pasta_fp_urs_lagrange_commitment")
168                })?;
169                let basis =
170                    crate::rayon::run_in_pool(|| {
171                        srs.get_lagrange_basis(x_domain)
172                    });
173
174                Ok(basis[i as usize].clone().into())
175            }
176
177            #[wasm_bindgen]
178            pub fn [<$name:snake _commit_evaluations>](
179                srs: &[<Wasm $field_name:camel Srs>],
180                domain_size: i32,
181                evals: WasmFlatVector<$WasmF>,
182            ) -> Result<$WasmPolyComm, JsValue> {
183                let x_domain = EvaluationDomain::<$F>::new(domain_size as usize).ok_or_else(|| {
184                    JsValue::from_str("caml_pasta_fp_urs_commit_evaluations")
185                })?;
186
187                let evals = evals.into_iter().map(Into::into).collect();
188                let p = Evaluations::<$F>::from_vec_and_domain(evals, x_domain).interpolate();
189
190                Ok(srs.commit_non_hiding(&p, 1).into())
191            }
192
193            #[wasm_bindgen]
194            pub fn [<$name:snake _b_poly_commitment>](
195                srs: &[<Wasm $field_name:camel Srs>],
196                chals: WasmFlatVector<$WasmF>,
197            ) -> Result<$WasmPolyComm, JsValue> {
198                let result = crate::rayon::run_in_pool(|| {
199                    let chals: Vec<$F> = chals.into_iter().map(Into::into).collect();
200                    let coeffs = b_poly_coefficients(&chals);
201                    let p = DensePolynomial::<$F>::from_coefficients_vec(coeffs);
202                    srs.commit_non_hiding(&p, 1)
203                });
204                Ok(result.into())
205            }
206
207            #[wasm_bindgen]
208            pub fn [<$name:snake _batch_accumulator_check>](
209                srs: &[<Wasm $field_name:camel Srs>],
210                comms: WasmVector<$WasmG>,
211                chals: WasmFlatVector<$WasmF>,
212            ) -> bool {
213                crate::rayon::run_in_pool(|| {
214                    let comms: Vec<_> = comms.into_iter().map(Into::into).collect();
215                    let chals: Vec<_> = chals.into_iter().map(Into::into).collect();
216                    poly_commitment::utils::batch_dlog_accumulator_check(&srs, &comms, &chals)
217                })
218            }
219
220            #[wasm_bindgen]
221            pub fn [<$name:snake _batch_accumulator_generate>](
222                srs: &[<Wasm $field_name:camel Srs>],
223                comms: i32,
224                chals: WasmFlatVector<$WasmF>,
225            ) -> WasmVector<$WasmG> {
226                poly_commitment::utils::batch_dlog_accumulator_generate::<$G>(
227                    &srs,
228                    comms as usize,
229                    &chals.into_iter().map(From::from).collect(),
230                ).into_iter().map(Into::into).collect()
231            }
232
233            #[wasm_bindgen]
234            pub fn [<$name:snake _h>](srs: &[<Wasm $field_name:camel Srs>]) -> $WasmG {
235                srs.h.into()
236            }
237        }
238    }
239}
240
241//
242// Fp
243//
244
245pub mod fp {
246    use super::*;
247    use crate::{
248        arkworks::{WasmGVesta as WasmG, WasmPastaFp},
249        poly_comm::vesta::WasmFpPolyComm as WasmPolyComm,
250    };
251    use mina_curves::pasta::{Fp, Vesta as G};
252
253    impl_srs!(caml_fp_srs, WasmPastaFp, WasmG, Fp, G, WasmPolyComm, Fp);
254    #[wasm_bindgen]
255    pub fn caml_fp_srs_create_parallel(depth: i32) -> WasmFpSrs {
256        crate::rayon::run_in_pool(|| Arc::new(SRS::<G>::create_parallel(depth as usize)).into())
257    }
258
259    // return the cloned srs in a form that we can store on the js side
260    #[wasm_bindgen]
261    pub fn caml_fp_srs_get(srs: &WasmFpSrs) -> WasmVector<WasmG> {
262        // return a vector which consists of h, then all the gs
263        let mut h_and_gs: Vec<WasmG> = vec![srs.0.h.into()];
264        h_and_gs.extend(srs.0.g.iter().map(|x: &G| WasmG::from(*x)));
265        h_and_gs.into()
266    }
267
268    // set the srs from a vector of h and gs
269    #[wasm_bindgen]
270    pub fn caml_fp_srs_set(h_and_gs: WasmVector<WasmG>) -> WasmFpSrs {
271        // return a vector which consists of h, then all the gs
272        let mut h_and_gs: Vec<G> = h_and_gs.into_iter().map(|x| x.into()).collect();
273        let h = h_and_gs.remove(0);
274        let g = h_and_gs;
275        let srs = SRS::<G> {
276            h,
277            g,
278            lagrange_bases: HashMapCache::new(),
279        };
280        Arc::new(srs).into()
281    }
282
283    // maybe get lagrange commitment
284    #[wasm_bindgen]
285    pub fn caml_fp_srs_maybe_lagrange_commitment(
286        srs: &WasmFpSrs,
287        domain_size: i32,
288        i: i32,
289    ) -> Option<WasmPolyComm> {
290        if !(srs.0.lagrange_bases.contains_key(&(domain_size as usize))) {
291            return None;
292        }
293        let basis = srs.get_lagrange_basis_from_domain_size(domain_size as usize);
294        Some(basis[i as usize].clone().into())
295    }
296
297    // set entire lagrange basis from input
298    #[wasm_bindgen]
299    pub fn caml_fp_srs_set_lagrange_basis(
300        srs: &WasmFpSrs,
301        domain_size: i32,
302        input_bases: WasmVector<WasmPolyComm>,
303    ) {
304        srs.lagrange_bases
305            .get_or_generate(domain_size as usize, || {
306                input_bases.into_iter().map(Into::into).collect()
307            });
308    }
309
310    // compute & add lagrange basis internally, return the entire basis
311    #[wasm_bindgen]
312    pub fn caml_fp_srs_get_lagrange_basis(
313        srs: &WasmFpSrs,
314        domain_size: i32,
315    ) -> WasmVector<WasmPolyComm> {
316        // compute lagrange basis
317        let basis = crate::rayon::run_in_pool(|| {
318            let domain =
319                EvaluationDomain::<Fp>::new(domain_size as usize).expect("invalid domain size");
320            srs.get_lagrange_basis(domain)
321        });
322        basis.iter().map(Into::into).collect()
323    }
324}
325
326pub mod fq {
327    use super::*;
328    use crate::{
329        arkworks::{WasmGPallas as WasmG, WasmPastaFq},
330        poly_comm::pallas::WasmFqPolyComm as WasmPolyComm,
331    };
332    use mina_curves::pasta::{Fq, Pallas as G};
333
334    impl_srs!(caml_fq_srs, WasmPastaFq, WasmG, Fq, G, WasmPolyComm, Fq);
335
336    #[wasm_bindgen]
337    pub fn caml_fq_srs_create_parallel(depth: i32) -> WasmFqSrs {
338        crate::rayon::run_in_pool(|| Arc::new(SRS::<G>::create_parallel(depth as usize)).into())
339    }
340
341    // return the cloned srs in a form that we can store on the js side
342    #[wasm_bindgen]
343    pub fn caml_fq_srs_get(srs: &WasmFqSrs) -> WasmVector<WasmG> {
344        // return a vector which consists of h, then all the gs
345        let mut h_and_gs: Vec<WasmG> = vec![srs.0.h.into()];
346        h_and_gs.extend(srs.0.g.iter().map(|x: &G| WasmG::from(*x)));
347        h_and_gs.into()
348    }
349
350    // set the srs from a vector of h and gs
351    #[wasm_bindgen]
352    pub fn caml_fq_srs_set(h_and_gs: WasmVector<WasmG>) -> WasmFqSrs {
353        // return a vector which consists of h, then all the gs
354        let mut h_and_gs: Vec<G> = h_and_gs.into_iter().map(|x| x.into()).collect();
355        let h = h_and_gs.remove(0);
356        let g = h_and_gs;
357        let srs = SRS::<G> {
358            h,
359            g,
360            lagrange_bases: HashMapCache::new(),
361        };
362        Arc::new(srs).into()
363    }
364
365    // maybe get lagrange commitment
366    #[wasm_bindgen]
367    pub fn caml_fq_srs_maybe_lagrange_commitment(
368        srs: &WasmFqSrs,
369        domain_size: i32,
370        i: i32,
371    ) -> Option<WasmPolyComm> {
372        if !(srs.0.lagrange_bases.contains_key(&(domain_size as usize))) {
373            return None;
374        }
375        let basis = srs.get_lagrange_basis_from_domain_size(domain_size as usize);
376        Some(basis[i as usize].clone().into())
377    }
378
379    // set entire lagrange basis from input
380    #[wasm_bindgen]
381    pub fn caml_fq_srs_set_lagrange_basis(
382        srs: &WasmFqSrs,
383        domain_size: i32,
384        input_bases: WasmVector<WasmPolyComm>,
385    ) {
386        srs.lagrange_bases
387            .get_or_generate(domain_size as usize, || {
388                input_bases.into_iter().map(Into::into).collect()
389            });
390    }
391
392    // compute & add lagrange basis internally, return the entire basis
393    #[wasm_bindgen]
394    pub fn caml_fq_srs_get_lagrange_basis(
395        srs: &WasmFqSrs,
396        domain_size: i32,
397    ) -> WasmVector<WasmPolyComm> {
398        // compute lagrange basis
399        let basis = crate::rayon::run_in_pool(|| {
400            let domain =
401                EvaluationDomain::<Fq>::new(domain_size as usize).expect("invalid domain size");
402            srs.get_lagrange_basis(domain)
403        });
404        basis.iter().map(Into::into).collect()
405    }
406}