|
| 1 | +use std::{ |
| 2 | + fs::{File, OpenOptions}, |
| 3 | + io::{BufReader, BufWriter, Seek, SeekFrom}, |
| 4 | + sync::Arc, |
| 5 | +}; |
| 6 | + |
| 7 | +use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Evaluations}; |
| 8 | +use napi::bindgen_prelude::*; |
| 9 | +use napi_derive::napi; |
| 10 | +use paste::paste; |
| 11 | +use poly_commitment::{commitment::b_poly_coefficients, hash_map_cache::HashMapCache, ipa::SRS}; |
| 12 | +use wasm_types::FlatVector as WasmFlatVector; |
| 13 | + |
| 14 | +use crate::{ |
| 15 | + poly_comm::{pallas::WasmFqPolyComm, vesta::WasmFpPolyComm}, |
| 16 | + wasm_vector::WasmVector, |
| 17 | + wrappers::field::{WasmPastaFp, WasmPastaFq}, |
| 18 | + wrappers::group::{WasmGPallas, WasmGVesta}, |
| 19 | +}; |
| 20 | + |
| 21 | +macro_rules! impl_srs_module { |
| 22 | + ( |
| 23 | + $mod_name:ident, |
| 24 | + $field_ty:ty, |
| 25 | + $wasm_field:ty, |
| 26 | + $group_ty:ty, |
| 27 | + $group_wrapper:ty, |
| 28 | + $poly_comm_wrapper:ty, |
| 29 | + $struct_ident:ident |
| 30 | + ) => { |
| 31 | + pub mod $mod_name { |
| 32 | + use super::*; |
| 33 | + |
| 34 | + #[napi] |
| 35 | + #[derive(Clone)] |
| 36 | + pub struct $struct_ident { |
| 37 | + #[napi(skip)] |
| 38 | + pub inner: Arc<SRS<$group_ty>>, |
| 39 | + } |
| 40 | + |
| 41 | + impl $struct_ident { |
| 42 | + fn new(inner: SRS<$group_ty>) -> Self { |
| 43 | + Self { |
| 44 | + inner: Arc::new(inner), |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | + fn from_arc(inner: Arc<SRS<$group_ty>>) -> Self { |
| 49 | + Self { inner } |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + fn invalid_domain_error() -> Error { |
| 54 | + Error::new(Status::InvalidArg, "invalid domain size") |
| 55 | + } |
| 56 | + |
| 57 | + fn map_error(context: &str, err: impl std::fmt::Display) -> Error { |
| 58 | + Error::new(Status::GenericFailure, format!("{}: {}", context, err)) |
| 59 | + } |
| 60 | + |
| 61 | + #[napi] |
| 62 | + impl $struct_ident { |
| 63 | + #[napi(factory)] |
| 64 | + pub fn create(depth: i32) -> Result<Self> { |
| 65 | + Ok(Self::from_arc(Arc::new(SRS::<$group_ty>::create(depth as usize)))) |
| 66 | + } |
| 67 | + |
| 68 | + #[napi(factory)] |
| 69 | + pub fn create_parallel(depth: i32) -> Result<Self> { |
| 70 | + Ok(Self::from_arc(Arc::new(SRS::<$group_ty>::create_parallel( |
| 71 | + depth as usize, |
| 72 | + )))) |
| 73 | + } |
| 74 | + |
| 75 | + #[napi] |
| 76 | + pub fn add_lagrange_basis(&self, log2_size: i32) -> Result<()> { |
| 77 | + let size = 1usize << (log2_size as usize); |
| 78 | + let domain = EvaluationDomain::<$field_ty>::new(size).ok_or_else(invalid_domain_error)?; |
| 79 | + self.inner.get_lagrange_basis(domain); |
| 80 | + Ok(()) |
| 81 | + } |
| 82 | + |
| 83 | + #[napi] |
| 84 | + pub fn write(&self, append: Option<bool>, path: String) -> Result<()> { |
| 85 | + let file = OpenOptions::new() |
| 86 | + .write(true) |
| 87 | + .create(true) |
| 88 | + .append(append.unwrap_or(true)) |
| 89 | + .open(&path) |
| 90 | + .map_err(|err| map_error("srs_write", err))?; |
| 91 | + let mut writer = BufWriter::new(file); |
| 92 | + self.inner |
| 93 | + .serialize(&mut rmp_serde::Serializer::new(&mut writer)) |
| 94 | + .map_err(|err| map_error("srs_write", err)) |
| 95 | + } |
| 96 | + |
| 97 | + #[napi] |
| 98 | + pub fn read(offset: Option<i32>, path: String) -> Result<Option<Self>> { |
| 99 | + let file = match File::open(&path) { |
| 100 | + Ok(file) => file, |
| 101 | + Err(err) => return Err(map_error("srs_read", err)), |
| 102 | + }; |
| 103 | + let mut reader = BufReader::new(file); |
| 104 | + |
| 105 | + if let Some(off) = offset { |
| 106 | + reader |
| 107 | + .seek(SeekFrom::Start(off as u64)) |
| 108 | + .map_err(|err| map_error("srs_read", err))?; |
| 109 | + } |
| 110 | + |
| 111 | + match SRS::<$group_ty>::deserialize(&mut rmp_serde::Deserializer::new(reader)) { |
| 112 | + Ok(srs) => Ok(Some(Self::from_arc(Arc::new(srs)))), |
| 113 | + Err(_) => Ok(None), |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + #[napi] |
| 118 | + pub fn get(&self) -> WasmVector<$group_wrapper> { |
| 119 | + let mut points: Vec<$group_wrapper> = vec![self.inner.h.into()]; |
| 120 | + points.extend(self.inner.g.iter().cloned().map(Into::into)); |
| 121 | + points.into() |
| 122 | + } |
| 123 | + |
| 124 | + #[napi] |
| 125 | + pub fn set(points: WasmVector<$group_wrapper>) -> Result<Self> { |
| 126 | + let mut pts: Vec<$group_ty> = points.into_iter().map(Into::into).collect(); |
| 127 | + if pts.is_empty() { |
| 128 | + return Err(Error::new( |
| 129 | + Status::InvalidArg, |
| 130 | + "expected at least one element for SRS", |
| 131 | + )); |
| 132 | + } |
| 133 | + let h = pts.remove(0); |
| 134 | + let g = pts; |
| 135 | + Ok(Self::from_arc(Arc::new(SRS::<$group_ty> { |
| 136 | + h, |
| 137 | + g, |
| 138 | + lagrange_bases: HashMapCache::new(), |
| 139 | + }))) |
| 140 | + } |
| 141 | + |
| 142 | + #[napi] |
| 143 | + pub fn maybe_lagrange_commitment( |
| 144 | + &self, |
| 145 | + domain_size: i32, |
| 146 | + index: i32, |
| 147 | + ) -> Option<$poly_comm_wrapper> { |
| 148 | + if !self |
| 149 | + .inner |
| 150 | + .lagrange_bases |
| 151 | + .contains_key(&(domain_size as usize)) |
| 152 | + { |
| 153 | + return None; |
| 154 | + } |
| 155 | + let basis = self |
| 156 | + .inner |
| 157 | + .get_lagrange_basis_from_domain_size(domain_size as usize); |
| 158 | + basis.get(index as usize).cloned().map(Into::into) |
| 159 | + } |
| 160 | + |
| 161 | + #[napi] |
| 162 | + pub fn set_lagrange_basis( |
| 163 | + &self, |
| 164 | + domain_size: i32, |
| 165 | + bases: WasmVector<$poly_comm_wrapper>, |
| 166 | + ) { |
| 167 | + let domain = domain_size as usize; |
| 168 | + let commitments: Vec<_> = bases.into_iter().map(Into::into).collect(); |
| 169 | + self.inner |
| 170 | + .lagrange_bases |
| 171 | + .get_or_generate(domain, || commitments.clone()); |
| 172 | + } |
| 173 | + |
| 174 | + #[napi] |
| 175 | + pub fn get_lagrange_basis( |
| 176 | + &self, |
| 177 | + domain_size: i32, |
| 178 | + ) -> Result<WasmVector<$poly_comm_wrapper>> { |
| 179 | + let domain = EvaluationDomain::<$field_ty>::new(domain_size as usize) |
| 180 | + .ok_or_else(invalid_domain_error)?; |
| 181 | + let basis = self.inner.get_lagrange_basis(domain); |
| 182 | + Ok(basis.iter().cloned().map(Into::into).collect()) |
| 183 | + } |
| 184 | + |
| 185 | + #[napi] |
| 186 | + pub fn commit_evaluations( |
| 187 | + &self, |
| 188 | + domain_size: i32, |
| 189 | + evaluations: Uint8Array, |
| 190 | + ) -> Result<$poly_comm_wrapper> { |
| 191 | + let elems: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( |
| 192 | + evaluations.as_ref().to_vec(), |
| 193 | + ) |
| 194 | + .into_iter() |
| 195 | + .map(Into::into) |
| 196 | + .collect(); |
| 197 | + let domain = EvaluationDomain::<$field_ty>::new(domain_size as usize) |
| 198 | + .ok_or_else(invalid_domain_error)?; |
| 199 | + let evals = Evaluations::from_vec_and_domain(elems, domain); |
| 200 | + let poly = evals.interpolate(); |
| 201 | + Ok(self.inner.commit(&poly, None).into()) |
| 202 | + } |
| 203 | + |
| 204 | + #[napi] |
| 205 | + pub fn b_poly_commitment(&self, chals: Uint8Array) -> Result<$poly_comm_wrapper> { |
| 206 | + let elements: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( |
| 207 | + chals.as_ref().to_vec(), |
| 208 | + ) |
| 209 | + .into_iter() |
| 210 | + .map(Into::into) |
| 211 | + .collect(); |
| 212 | + let coeffs = b_poly_coefficients(&elements); |
| 213 | + let poly = DensePolynomial::<$field_ty>::from_coefficients_vec(coeffs); |
| 214 | + Ok(self.inner.commit_non_hiding(&poly, 1).into()) |
| 215 | + } |
| 216 | + |
| 217 | + #[napi] |
| 218 | + pub fn batch_accumulator_check( |
| 219 | + &self, |
| 220 | + commitments: WasmVector<$group_wrapper>, |
| 221 | + chals: Uint8Array, |
| 222 | + ) -> Result<bool> { |
| 223 | + let comms: Vec<$group_ty> = commitments.into_iter().map(Into::into).collect(); |
| 224 | + let chals: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( |
| 225 | + chals.as_ref().to_vec(), |
| 226 | + ) |
| 227 | + .into_iter() |
| 228 | + .map(Into::into) |
| 229 | + .collect(); |
| 230 | + Ok(poly_commitment::utils::batch_dlog_accumulator_check( |
| 231 | + &self.inner, |
| 232 | + &comms, |
| 233 | + &chals, |
| 234 | + )) |
| 235 | + } |
| 236 | + |
| 237 | + #[napi] |
| 238 | + pub fn batch_accumulator_generate( |
| 239 | + &self, |
| 240 | + count: i32, |
| 241 | + chals: Uint8Array, |
| 242 | + ) -> Result<WasmVector<$group_wrapper>> { |
| 243 | + let chals: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( |
| 244 | + chals.as_ref().to_vec(), |
| 245 | + ) |
| 246 | + .into_iter() |
| 247 | + .map(Into::into) |
| 248 | + .collect(); |
| 249 | + let points = poly_commitment::utils::batch_dlog_accumulator_generate::<$group_ty>( |
| 250 | + &self.inner, |
| 251 | + count as usize, |
| 252 | + &chals, |
| 253 | + ); |
| 254 | + Ok(points.into_iter().map(Into::into).collect()) |
| 255 | + } |
| 256 | + |
| 257 | + #[napi] |
| 258 | + pub fn h(&self) -> $group_wrapper { |
| 259 | + self.inner.h.into() |
| 260 | + } |
| 261 | + } |
| 262 | + } |
| 263 | + }; |
| 264 | +} |
| 265 | + |
| 266 | +impl_srs_module!( |
| 267 | + fp, |
| 268 | + mina_curves::pasta::Fp, |
| 269 | + WasmPastaFp, |
| 270 | + mina_curves::pasta::Vesta, |
| 271 | + WasmGVesta, |
| 272 | + WasmFpPolyComm, |
| 273 | + WasmFpSrs |
| 274 | +); |
| 275 | + |
| 276 | +impl_srs_module!( |
| 277 | + fq, |
| 278 | + mina_curves::pasta::Fq, |
| 279 | + WasmPastaFq, |
| 280 | + mina_curves::pasta::Pallas, |
| 281 | + WasmGPallas, |
| 282 | + WasmFqPolyComm, |
| 283 | + WasmFqSrs |
| 284 | +); |
0 commit comments