Skip to main content

kimchi_napi/
srs.rs

1use crate::vector::NapiVector;
2use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Evaluations};
3use core::ops::Deref;
4use napi::bindgen_prelude::{sys, ClassInstance, Error, FromNapiValue, Result, Status, Uint8Array};
5use napi_derive::napi;
6use paste::paste;
7use poly_commitment::{commitment::b_poly_coefficients, ipa::SRS, SRS as ISRS};
8use serde::{Deserialize, Serialize};
9use std::{
10    fs::{File, OpenOptions},
11    io::{BufReader, BufWriter, Seek, SeekFrom},
12    sync::Arc,
13};
14use wasm_types::FlatVector as WasmFlatVector;
15
16macro_rules! impl_srs {
17    (
18        $name:ident,
19        $NapiF:ty,
20        $NapiG:ty,
21        $F:ty,
22        $G:ty,
23        $NapiPolyComm:ty,
24    ) => {
25        paste! {
26
27            #[napi(js_name = [<"Wasm" $name:camel "Srs">])]
28            #[derive(Clone, Debug, Default)]
29            pub struct [<Napi $name:camel Srs>] (
30                 #[napi(skip)] pub Arc<SRS<$G>>
31            );
32
33            impl FromNapiValue for [<Napi $name:camel Srs>] {
34                unsafe fn from_napi_value(
35                    env: sys::napi_env,
36                    napi_val: sys::napi_value,
37                ) -> Result<Self> {
38                    let instance = <ClassInstance<[<Napi $name:camel Srs>]> as FromNapiValue>::from_napi_value(env, napi_val)?;
39                    Ok((*instance).clone())
40                }
41            }
42
43            impl Deref for [<Napi $name:camel Srs>] {
44                type Target = Arc<SRS<$G>>;
45
46                fn deref(&self) -> &Self::Target { &self.0 }
47            }
48
49            impl From<Arc<SRS<$G>>> for [<Napi $name:camel Srs>] {
50                fn from(x: Arc<SRS<$G>>) -> Self {
51                    [<Napi $name:camel Srs>](x)
52                }
53            }
54
55            impl From<&Arc<SRS<$G>>> for [<Napi $name:camel Srs>] {
56                fn from(x: &Arc<SRS<$G>>) -> Self {
57                    [<Napi $name:camel Srs>](x.clone())
58                }
59            }
60
61            impl From<[<Napi $name:camel Srs>]> for Arc<SRS<$G>> {
62                fn from(x: [<Napi $name:camel Srs>]) -> Self {
63                    x.0
64                }
65            }
66
67            impl From<&[<Napi $name:camel Srs>]> for Arc<SRS<$G>> {
68                fn from(x: &[<Napi $name:camel Srs>]) -> Self {
69                    x.0.clone()
70                }
71            }
72
73            impl<'a> From<&'a [<Napi $name:camel Srs>]> for &'a Arc<SRS<$G>> {
74                fn from(x: &'a [<Napi $name:camel Srs>]) -> Self {
75                    &x.0
76                }
77            }
78
79            fn invalid_domain_error() -> Error {
80                Error::new(Status::InvalidArg, "invalid domain size")
81            }
82
83            fn map_error(context: &str, err: impl std::fmt::Display) -> Error {
84                Error::new(Status::GenericFailure, format!("{}: {}", context, err))
85            }
86
87            #[napi]
88            impl [<Napi $name:camel Srs>] {
89
90                #[napi]
91                pub fn serialize(&self) -> Result<Uint8Array> {
92                    let mut buf = Vec::new();
93                    self.0
94                        .serialize(&mut rmp_serde::Serializer::new(&mut buf))
95                        .map_err(|e| map_error("srs_serialize", e))?;
96                    Ok(Uint8Array::from(buf))
97                }
98
99                #[napi]
100                pub fn deserialize(bytes: Uint8Array) -> Result<Self> {
101                    let srs: SRS<$G> = rmp_serde::from_slice(bytes.as_ref())
102                        .map_err(|e| map_error("srs_deserialize", e))?;
103                    Ok(Arc::new(srs).into())
104                }
105              }
106
107              #[napi(js_name = [<"caml_" $name:snake "_srs_create">])]
108              pub fn [<caml_ $name:snake _srs_create>](depth: i32) -> [<Napi $name:camel Srs>]  {
109                  Arc::new(SRS::<$G>::create(depth as usize)).into()
110              }
111
112              #[napi(js_name = [<"caml_" $name:snake "_srs_create_parallel">])]
113              pub fn [<caml_ $name:snake _srs_create_parallel>](depth: i32) -> [<Napi $name:camel Srs>] {
114                  Arc::new(SRS::<$G>::create_parallel(depth as usize)).into()
115              }
116
117              #[napi(js_name = [<"caml_" $name:snake "_srs_add_lagrange_basis">])]
118              pub fn [<caml_ $name:snake _srs_add_lagrange_basis>](srs: &[<Napi $name:camel Srs>], log2_size: i32) -> Result<()> {
119                  let size = 1usize << (log2_size as usize);
120                  let domain = EvaluationDomain::<$F>::new(size).ok_or_else(invalid_domain_error)?;
121                  srs.get_lagrange_basis(domain);
122                  Ok(())
123              }
124
125              #[napi(js_name = [<"caml_" $name:snake "_srs_write">])]
126              pub fn [<caml_ $name:snake _srs_write>](append: Option<bool>, srs: &[<Napi $name:camel Srs>], path: String) -> Result<()> {
127                  let function_name = format!("caml_{0}_srs_write", stringify!($name).to_lowercase());
128                  let file = OpenOptions::new()
129                      .append(append.unwrap_or(true))
130                      .open(&path)
131                      .map_err(|err| map_error(&function_name, err))?;
132                  let file = BufWriter::new(file);
133                  srs.0.serialize(&mut rmp_serde::Serializer::new(file))
134                      .map_err(|err| map_error(&function_name, err))
135              }
136
137              #[napi(js_name = [<"caml_" $name:snake "_srs_read">])]
138              pub fn [<caml_ $name:snake _srs_read>](offset: Option<i32>, path: String) -> Result<Option<[<Napi $name:camel Srs>]>> {
139                  let function_name = format!("caml_{0}_srs_read", stringify!($name).to_lowercase());
140                  let file = match File::open(&path) {
141                      Ok(file) => file,
142                      Err(err) => return Err(map_error(&function_name, err)),
143                  };
144                  let mut reader = BufReader::new(file);
145
146                  if let Some(off) = offset {
147                      reader
148                          .seek(SeekFrom::Start(off as u64))
149                          .map_err(|err| map_error(&function_name, err))?;
150                  }
151
152                  match SRS::<$G>::deserialize(&mut rmp_serde::Deserializer::new(reader)) {
153                      Ok(srs) => Ok(Some(Arc::new(srs).into())),
154                      Err(_) => Ok(None),
155                  }
156              }
157
158            #[napi(js_name = [<"caml_" $name:snake "_srs_lagrange_commitment">])]
159            pub fn [<caml_ $name:snake _srs_lagrange_commitment>](
160                srs: &[<Napi $name:camel Srs>],
161                domain_size: i32,
162                i: i32,
163            ) -> Result<[<$NapiPolyComm>]> {
164                let x_domain = EvaluationDomain::<$F>::new(domain_size as usize)
165                    .ok_or_else(invalid_domain_error)?;
166                let basis = srs.get_lagrange_basis(x_domain);
167                Ok(basis[i as usize].clone().into())
168            }
169
170            // Fake overwrite of the kimchi_wasm equivalent, but without pointers.
171            // In the srs bindings, the same symbol will be used to either provide
172            // the pointer for wasm, or the actual data for napi
173            #[napi(js_name = [<"caml_" $name:snake "_srs_lagrange_commitments_whole_domain_ptr">])]
174            pub fn [<caml_ $name:snake _srs_lagrange_commitments_whole_domain_ptr>](
175                srs: &[<Napi $name:camel Srs>],
176                domain_size: i32,
177            ) -> Result<NapiVector<$NapiPolyComm>> {
178                let domain = ark_poly::Radix2EvaluationDomain::<$F>::new(domain_size as usize)
179                    .ok_or_else(invalid_domain_error)?;
180                if srs.0.g.is_empty() {
181                    return Err(Error::new(
182                        Status::InvalidArg,
183                        "SRS is empty; regenerate or clear cache",
184                    ));
185                }
186                // Chunkeing is supported so domain can be larger than SRS size.
187                let basis = srs.get_lagrange_basis(domain);
188
189                Ok(basis.iter().cloned().map(Into::into).collect())
190            }
191
192              #[napi(js_name = [<"caml_" $name:snake "_srs_get">])]
193              pub fn [<caml_ $name:snake _srs_get>](srs: &[<Napi $name:camel Srs>]) -> Vec<$NapiG> {
194                  let mut h_and_gs: Vec<$NapiG> = vec![srs.0.h.into()];
195                  h_and_gs.extend(srs.0.g.iter().cloned().map(Into::into));
196                  h_and_gs
197              }
198
199              #[napi(js_name = [<"caml_" $name:snake "_srs_set">])]
200              pub fn [<caml_ $name:snake _srs_set>](h_and_gs: Vec<$NapiG>) -> [<Napi $name:camel Srs>] {
201                  let mut h_and_gs: Vec<$G> = h_and_gs.into_iter().map(Into::into).collect();
202
203                  let h = h_and_gs.remove(0);
204                  let g = h_and_gs;
205                  let srs = SRS::<$G>::new(g, h);
206                  Arc::new(srs).into()
207              }
208
209              #[napi(js_name = [<"caml_" $name:snake "_srs_maybe_lagrange_commitment">])]
210              pub fn [<caml_ $name:snake _srs_maybe_lagrange_commitment>](
211                  srs: &[<Napi $name:camel Srs>],
212                  domain_size: i32,
213                  i: i32,
214              ) -> Option<$NapiPolyComm> {
215                  if !srs
216                      .0
217                      .lagrange_bases()
218                      .contains_key(&(domain_size as usize))
219                  {
220                      return None;
221                  }
222                  let basis = srs
223                      .get_lagrange_basis_from_domain_size(domain_size as usize);
224                    basis.get(i as usize).map(Into::into)
225              }
226
227              #[napi(js_name = [<"caml_" $name:snake "_srs_set_lagrange_basis">])]
228              pub fn [<caml_ $name:snake _srs_set_lagrange_basis>](srs: &[<Napi $name:camel Srs>],
229                  domain_size: i32,
230                  input_bases: NapiVector<$NapiPolyComm>,
231              ) {
232                  srs.0.lagrange_bases()
233                      .set_once(domain_size as usize, input_bases.into_iter().map(Into::into).collect());
234              }
235
236              #[napi(js_name = [<"caml_" $name:snake "_srs_get_lagrange_basis">])]
237              pub fn [<caml_ $name:snake _srs_get_lagrange_basis>](srs: &[<Napi $name:camel Srs>],
238                  domain_size: i32,
239              ) -> Result<NapiVector<$NapiPolyComm>> {
240                  let domain = EvaluationDomain::<$F>::new(domain_size as usize)
241                      .ok_or_else(invalid_domain_error)?;
242                  let basis = srs.0.get_lagrange_basis(domain);
243                  Ok(basis.iter().cloned().map(Into::into).collect())
244              }
245
246
247              #[napi(js_name = [<"caml_" $name:snake "_srs_commit_evaluations">])]
248              pub fn [<caml_ $name:snake _srs_commit_evaluations>](srs: &[<Napi $name:camel Srs>],
249                  domain_size: i32,
250                  evals: Uint8Array,
251              ) -> Result<$NapiPolyComm> {
252                  let elems: Vec<$F> = WasmFlatVector::<$NapiF>::from_bytes(
253                      evals.as_ref().to_vec(),
254                  )
255                  .into_iter()
256                  .map(Into::into)
257                  .collect();
258                  let x_domain = EvaluationDomain::<$F>::new(domain_size as usize)
259                      .ok_or_else(invalid_domain_error)?;
260                  let evals = elems.into_iter().map(Into::into).collect();
261                  let p = Evaluations::<$F>::from_vec_and_domain(evals, x_domain).interpolate();
262                  Ok(srs.commit_non_hiding(&p, 1).into())
263              }
264
265              #[napi(js_name = [<"caml_" $name:snake "_srs_b_poly_commitment">])]
266              pub fn [<caml_ $name:snake _srs_b_poly_commitment>](srs: &[<Napi $name:camel Srs>], chals: Uint8Array) -> Result<$NapiPolyComm> {
267                  let elements: Vec<$F> = WasmFlatVector::<$NapiF>::from_bytes(
268                      chals.as_ref().to_vec(),
269                  )
270                  .into_iter()
271                  .map(Into::into)
272                  .collect();
273                  let coeffs = b_poly_coefficients(&elements);
274                  let p = DensePolynomial::<$F>::from_coefficients_vec(coeffs);
275                  Ok(srs.commit_non_hiding(&p, 1).into())
276              }
277
278              #[napi(js_name = [<"caml_" $name:snake "_srs_batch_accumulator_check">])]
279              pub fn [<caml_ $name:snake _srs_batch_accumulator_check>](
280                  srs: &[<Napi $name:camel Srs>],
281                  comms: NapiVector<$NapiG>,
282                  chals: Uint8Array,
283              ) -> Result<bool> {
284                  let comms: Vec<$G> = comms.into_iter().map(Into::into).collect();
285                  let chals: Vec<$F> = WasmFlatVector::<$NapiF>::from_bytes(
286                      chals.as_ref().to_vec(),
287                  )
288                  .into_iter()
289                  .map(Into::into)
290                  .collect();
291                  Ok(poly_commitment::utils::batch_dlog_accumulator_check(
292                      &srs,
293                      &comms,
294                      &chals,
295                  ))
296              }
297
298              #[napi(js_name = [<"caml_" $name:snake "_srs_batch_accumulator_generate">])]
299              pub fn [<caml_ $name:snake _srs_batch_accumulator_generate>](
300                  srs: &[<Napi $name:camel Srs>],
301                  comms: i32,
302                  chals: Uint8Array,
303              ) -> Result<NapiVector<$NapiG>> {
304                  let chals: Vec<$F> = WasmFlatVector::<$NapiF>::from_bytes(
305                      chals.as_ref().to_vec(),
306                  )
307                  .into_iter()
308                  .map(Into::into)
309                  .collect();
310                  let points = poly_commitment::utils::batch_dlog_accumulator_generate::<$G>(
311                      &srs,
312                      comms as usize,
313                      &chals,
314                  );
315                  Ok(points.into_iter().map(Into::into).collect())
316              }
317
318              #[napi(js_name = [<"caml_" $name:snake "_srs_h">])]
319              pub fn [<caml_ $name:snake _srs_h>](srs: &[<Napi $name:camel Srs>]) -> $NapiG {
320                  srs.h.into()
321              }
322        }
323    }
324}
325
326#[napi(js_name = "caml_fp_srs_to_bytes")]
327pub fn caml_fp_srs_to_bytes(srs: &fp::NapiFpSrs) -> Result<Uint8Array> {
328    srs.serialize()
329}
330
331#[napi(js_name = "caml_fp_srs_from_bytes")]
332pub fn caml_fp_srs_from_bytes(bytes: Uint8Array) -> Result<fp::NapiFpSrs> {
333    fp::NapiFpSrs::deserialize(bytes)
334}
335
336#[napi(js_name = "caml_fq_srs_to_bytes")]
337pub fn caml_fq_srs_to_bytes(srs: &fq::NapiFqSrs) -> Result<Uint8Array> {
338    srs.serialize()
339}
340
341#[napi(js_name = "caml_fq_srs_from_bytes")]
342pub fn caml_fq_srs_from_bytes(bytes: Uint8Array) -> Result<fq::NapiFqSrs> {
343    fq::NapiFqSrs::deserialize(bytes)
344}
345
346pub mod fp {
347    use super::*;
348    use crate::{
349        poly_comm::vesta::NapiFpPolyComm,
350        wrappers::{field::NapiPastaFp, group::NapiGVesta},
351    };
352    impl_srs!(
353        fp,                        // field name
354        NapiPastaFp,               // Napi field wrapper
355        NapiGVesta,                // Napi group wrapper
356        mina_curves::pasta::Fp,    // Actual Kimchi field
357        mina_curves::pasta::Vesta, // Actual kimchi group
358        NapiFpPolyComm,            // Napi poly commitment type
359    );
360}
361
362pub mod fq {
363    use super::*;
364    use crate::{
365        poly_comm::pallas::NapiFqPolyComm,
366        wrappers::{field::NapiPastaFq, group::NapiGPallas},
367    };
368    impl_srs!(
369        fq,                         // Field name
370        NapiPastaFq,                // Napi field wrapper
371        NapiGPallas,                // Napi group wrapper
372        mina_curves::pasta::Fq,     // Actual Kimchi field
373        mina_curves::pasta::Pallas, // Actual kimchi group
374        NapiFqPolyComm,             // Napi poly commitment type
375    );
376}