plonk_wasm/
srs.rs

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