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 #[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 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, NapiPastaFp, NapiGVesta, mina_curves::pasta::Fp, mina_curves::pasta::Vesta, NapiFpPolyComm, );
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, NapiPastaFq, NapiGPallas, mina_curves::pasta::Fq, mina_curves::pasta::Pallas, NapiFqPolyComm, );
376}