From 0925ce302d1df01599a2b6b395d7415e1537f68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=AFs=20Querol?= Date: Tue, 7 Oct 2025 14:43:30 +0200 Subject: [PATCH 01/12] napi: ffi and types for srs --- plonk-napi/src/srs.rs | 284 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) diff --git a/plonk-napi/src/srs.rs b/plonk-napi/src/srs.rs index 3f66c82ddd..3a260c5e4d 100644 --- a/plonk-napi/src/srs.rs +++ b/plonk-napi/src/srs.rs @@ -28,3 +28,287 @@ pub fn caml_fq_srs_from_bytes(bytes: Uint8Array) -> NapiResult { + pub mod $mod_name { + use super::*; + + #[napi] + #[derive(Clone)] + pub struct $struct_ident { + #[napi(skip)] + pub inner: Arc>, + } + + impl $struct_ident { + fn new(inner: SRS<$group_ty>) -> Self { + Self { + inner: Arc::new(inner), + } + } + + fn from_arc(inner: Arc>) -> Self { + Self { inner } + } + } + + fn invalid_domain_error() -> Error { + Error::new(Status::InvalidArg, "invalid domain size") + } + + fn map_error(context: &str, err: impl std::fmt::Display) -> Error { + Error::new(Status::GenericFailure, format!("{}: {}", context, err)) + } + + #[napi] + impl $struct_ident { + #[napi(factory)] + pub fn create(depth: i32) -> Result { + Ok(Self::from_arc(Arc::new(SRS::<$group_ty>::create(depth as usize)))) + } + + #[napi(factory)] + pub fn create_parallel(depth: i32) -> Result { + Ok(Self::from_arc(Arc::new(SRS::<$group_ty>::create_parallel( + depth as usize, + )))) + } + + #[napi] + pub fn add_lagrange_basis(&self, log2_size: i32) -> Result<()> { + let size = 1usize << (log2_size as usize); + let domain = EvaluationDomain::<$field_ty>::new(size).ok_or_else(invalid_domain_error)?; + self.inner.get_lagrange_basis(domain); + Ok(()) + } + + #[napi] + pub fn write(&self, append: Option, path: String) -> Result<()> { + let file = OpenOptions::new() + .write(true) + .create(true) + .append(append.unwrap_or(true)) + .open(&path) + .map_err(|err| map_error("srs_write", err))?; + let mut writer = BufWriter::new(file); + self.inner + .serialize(&mut rmp_serde::Serializer::new(&mut writer)) + .map_err(|err| map_error("srs_write", err)) + } + + #[napi] + pub fn read(offset: Option, path: String) -> Result> { + let file = match File::open(&path) { + Ok(file) => file, + Err(err) => return Err(map_error("srs_read", err)), + }; + let mut reader = BufReader::new(file); + + if let Some(off) = offset { + reader + .seek(SeekFrom::Start(off as u64)) + .map_err(|err| map_error("srs_read", err))?; + } + + match SRS::<$group_ty>::deserialize(&mut rmp_serde::Deserializer::new(reader)) { + Ok(srs) => Ok(Some(Self::from_arc(Arc::new(srs)))), + Err(_) => Ok(None), + } + } + + #[napi] + pub fn get(&self) -> WasmVector<$group_wrapper> { + let mut points: Vec<$group_wrapper> = vec![self.inner.h.into()]; + points.extend(self.inner.g.iter().cloned().map(Into::into)); + points.into() + } + + #[napi] + pub fn set(points: WasmVector<$group_wrapper>) -> Result { + let mut pts: Vec<$group_ty> = points.into_iter().map(Into::into).collect(); + if pts.is_empty() { + return Err(Error::new( + Status::InvalidArg, + "expected at least one element for SRS", + )); + } + let h = pts.remove(0); + let g = pts; + Ok(Self::from_arc(Arc::new(SRS::<$group_ty> { + h, + g, + lagrange_bases: HashMapCache::new(), + }))) + } + + #[napi] + pub fn maybe_lagrange_commitment( + &self, + domain_size: i32, + index: i32, + ) -> Option<$poly_comm_wrapper> { + if !self + .inner + .lagrange_bases + .contains_key(&(domain_size as usize)) + { + return None; + } + let basis = self + .inner + .get_lagrange_basis_from_domain_size(domain_size as usize); + basis.get(index as usize).cloned().map(Into::into) + } + + #[napi] + pub fn set_lagrange_basis( + &self, + domain_size: i32, + bases: WasmVector<$poly_comm_wrapper>, + ) { + let domain = domain_size as usize; + let commitments: Vec<_> = bases.into_iter().map(Into::into).collect(); + self.inner + .lagrange_bases + .get_or_generate(domain, || commitments.clone()); + } + + #[napi] + pub fn get_lagrange_basis( + &self, + domain_size: i32, + ) -> Result> { + let domain = EvaluationDomain::<$field_ty>::new(domain_size as usize) + .ok_or_else(invalid_domain_error)?; + let basis = self.inner.get_lagrange_basis(domain); + Ok(basis.iter().cloned().map(Into::into).collect()) + } + + #[napi] + pub fn commit_evaluations( + &self, + domain_size: i32, + evaluations: Uint8Array, + ) -> Result<$poly_comm_wrapper> { + let elems: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( + evaluations.as_ref().to_vec(), + ) + .into_iter() + .map(Into::into) + .collect(); + let domain = EvaluationDomain::<$field_ty>::new(domain_size as usize) + .ok_or_else(invalid_domain_error)?; + let evals = Evaluations::from_vec_and_domain(elems, domain); + let poly = evals.interpolate(); + Ok(self.inner.commit(&poly, None).into()) + } + + #[napi] + pub fn b_poly_commitment(&self, chals: Uint8Array) -> Result<$poly_comm_wrapper> { + let elements: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( + chals.as_ref().to_vec(), + ) + .into_iter() + .map(Into::into) + .collect(); + let coeffs = b_poly_coefficients(&elements); + let poly = DensePolynomial::<$field_ty>::from_coefficients_vec(coeffs); + Ok(self.inner.commit_non_hiding(&poly, 1).into()) + } + + #[napi] + pub fn batch_accumulator_check( + &self, + commitments: WasmVector<$group_wrapper>, + chals: Uint8Array, + ) -> Result { + let comms: Vec<$group_ty> = commitments.into_iter().map(Into::into).collect(); + let chals: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( + chals.as_ref().to_vec(), + ) + .into_iter() + .map(Into::into) + .collect(); + Ok(poly_commitment::utils::batch_dlog_accumulator_check( + &self.inner, + &comms, + &chals, + )) + } + + #[napi] + pub fn batch_accumulator_generate( + &self, + count: i32, + chals: Uint8Array, + ) -> Result> { + let chals: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( + chals.as_ref().to_vec(), + ) + .into_iter() + .map(Into::into) + .collect(); + let points = poly_commitment::utils::batch_dlog_accumulator_generate::<$group_ty>( + &self.inner, + count as usize, + &chals, + ); + Ok(points.into_iter().map(Into::into).collect()) + } + + #[napi] + pub fn h(&self) -> $group_wrapper { + self.inner.h.into() + } + } + } + }; +} + +impl_srs_module!( + fp, + mina_curves::pasta::Fp, + WasmPastaFp, + mina_curves::pasta::Vesta, + WasmGVesta, + WasmFpPolyComm, + WasmFpSrs +); + +impl_srs_module!( + fq, + mina_curves::pasta::Fq, + WasmPastaFq, + mina_curves::pasta::Pallas, + WasmGPallas, + WasmFqPolyComm, + WasmFqSrs +); From b90542cc11c645ed29990de61d6a4d437c458aff Mon Sep 17 00:00:00 2001 From: querolita Date: Mon, 20 Oct 2025 16:36:04 +0200 Subject: [PATCH 02/12] napi: use macro params closer to plonk-wasms names --- plonk-napi/src/srs.rs | 85 +++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/plonk-napi/src/srs.rs b/plonk-napi/src/srs.rs index 3a260c5e4d..0138e61b39 100644 --- a/plonk-napi/src/srs.rs +++ b/plonk-napi/src/srs.rs @@ -44,39 +44,40 @@ use wasm_types::FlatVector as WasmFlatVector; use crate::{ poly_comm::{pallas::WasmFqPolyComm, vesta::WasmFpPolyComm}, wasm_vector::WasmVector, - wrappers::field::{WasmPastaFp, WasmPastaFq}, - wrappers::group::{WasmGPallas, WasmGVesta}, + wrappers::{ + field::{WasmPastaFp, WasmPastaFq}, + group::{WasmGPallas, WasmGVesta}, + }, }; -macro_rules! impl_srs_module { +macro_rules! impl_srs { ( - $mod_name:ident, + $name:ident, $field_ty:ty, - $wasm_field:ty, + $wasmF:ty, $group_ty:ty, $group_wrapper:ty, $poly_comm_wrapper:ty, - $struct_ident:ident + $field_name:ident ) => { - pub mod $mod_name { + pub mod $name { use super::*; #[napi] #[derive(Clone)] - pub struct $struct_ident { - #[napi(skip)] - pub inner: Arc>, - } + pub struct [Napi $field_name:camel Srs] ( + #[napi(skip)] pub Arc> + ); - impl $struct_ident { + impl [Napi $field_name:camel Srs] { fn new(inner: SRS<$group_ty>) -> Self { - Self { - inner: Arc::new(inner), - } + Self ( + Arc::new(inner), + ) } fn from_arc(inner: Arc>) -> Self { - Self { inner } + Self (inner) } } @@ -218,7 +219,7 @@ macro_rules! impl_srs_module { domain_size: i32, evaluations: Uint8Array, ) -> Result<$poly_comm_wrapper> { - let elems: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( + let elems: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( evaluations.as_ref().to_vec(), ) .into_iter() @@ -233,7 +234,7 @@ macro_rules! impl_srs_module { #[napi] pub fn b_poly_commitment(&self, chals: Uint8Array) -> Result<$poly_comm_wrapper> { - let elements: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( + let elements: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( chals.as_ref().to_vec(), ) .into_iter() @@ -251,7 +252,7 @@ macro_rules! impl_srs_module { chals: Uint8Array, ) -> Result { let comms: Vec<$group_ty> = commitments.into_iter().map(Into::into).collect(); - let chals: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( + let chals: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( chals.as_ref().to_vec(), ) .into_iter() @@ -270,7 +271,7 @@ macro_rules! impl_srs_module { count: i32, chals: Uint8Array, ) -> Result> { - let chals: Vec<$field_ty> = WasmFlatVector::<$wasm_field>::from_bytes( + let chals: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( chals.as_ref().to_vec(), ) .into_iter() @@ -293,22 +294,28 @@ macro_rules! impl_srs_module { }; } -impl_srs_module!( - fp, - mina_curves::pasta::Fp, - WasmPastaFp, - mina_curves::pasta::Vesta, - WasmGVesta, - WasmFpPolyComm, - WasmFpSrs -); - -impl_srs_module!( - fq, - mina_curves::pasta::Fq, - WasmPastaFq, - mina_curves::pasta::Pallas, - WasmGPallas, - WasmFqPolyComm, - WasmFqSrs -); +pub mod fp { + use super::*; + impl_srs!( + fp, + mina_curves::pasta::Fp, + WasmPastaFp, + mina_curves::pasta::Vesta, + WasmGVesta, + WasmFpPolyComm, + Fp + ); +} + +pub mod fq { + use super::*; + impl_srs!( + fq, + mina_curves::pasta::Fq, + WasmPastaFq, + mina_curves::pasta::Pallas, + WasmGPallas, + WasmFqPolyComm, + Fq + ); +} From adaf261419ce8d3b5d071912cf06ea3bf64a3e2a Mon Sep 17 00:00:00 2001 From: querolita Date: Mon, 27 Oct 2025 19:19:57 +0100 Subject: [PATCH 03/12] napi: pass to srs impls --- plonk-napi/src/lib.rs | 7 +- plonk-napi/src/srs.rs | 268 ++++++++++++++++++++-------------- plonk-napi/src/tables.rs | 13 +- plonk-wasm/src/gate_vector.rs | 18 ++- 4 files changed, 181 insertions(+), 125 deletions(-) diff --git a/plonk-napi/src/lib.rs b/plonk-napi/src/lib.rs index 84c01766ac..bc4a77b825 100644 --- a/plonk-napi/src/lib.rs +++ b/plonk-napi/src/lib.rs @@ -5,8 +5,8 @@ mod poly_comm; mod poseidon; mod srs; mod tables; -mod wrappers; mod wasm_vector; +mod wrappers; pub use pasta_fp_plonk_index::{ prover_index_fp_from_bytes, prover_index_fp_to_bytes, WasmPastaFpPlonkIndex, @@ -19,4 +19,7 @@ pub use poseidon::{caml_pasta_fp_poseidon_block_cipher, caml_pasta_fq_poseidon_b pub use srs::{caml_fp_srs_from_bytes, caml_fp_srs_to_bytes, caml_fq_srs_from_bytes}; pub use tables::{JsLookupTableFp, JsLookupTableFq, JsRuntimeTableCfgFp, JsRuntimeTableCfgFq}; pub use wasm_vector::{fp::WasmVecVecFp, fq::WasmVecVecFq}; -pub use wrappers::{field::{WasmPastaFp, WasmPastaFq}, group::{WasmGPallas, WasmGVesta}}; +pub use wrappers::{ + field::{WasmPastaFp, WasmPastaFq}, + group::{WasmGPallas, WasmGVesta}, +}; diff --git a/plonk-napi/src/srs.rs b/plonk-napi/src/srs.rs index 0138e61b39..750f89486e 100644 --- a/plonk-napi/src/srs.rs +++ b/plonk-napi/src/srs.rs @@ -1,13 +1,29 @@ -use std::sync::Arc; - +use crate::{ + poly_comm::{pallas::WasmFqPolyComm, vesta::WasmFpPolyComm}, + wasm_vector::WasmVector, + wrappers::{ + field::{WasmPastaFp, WasmPastaFq}, + group::{WasmGPallas, WasmGVesta}, + }, +}; +use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Evaluations}; +use core::ops::Deref; use mina_curves::pasta::{Pallas as GAffineOther, Vesta as GAffine}; -use napi::bindgen_prelude::{Error, External, Result as NapiResult, Status, Uint8Array}; +use napi::bindgen_prelude::{Error, External, Result, Status, Uint8Array}; use napi_derive::napi; -use plonk_wasm::srs::fp::WasmFpSrs as WasmSrsFp; -use plonk_wasm::srs::fq::WasmFqSrs as WasmSrsFq; - -use poly_commitment::ipa::SRS; +use paste::paste; +use poly_commitment::{ + commitment::b_poly_coefficients, hash_map_cache::HashMapCache, ipa::SRS, SRS as ISRS, +}; +use serde::{Deserialize, Serialize}; +use std::{ + fs::{File, OpenOptions}, + io::{BufReader, BufWriter, Seek, SeekFrom}, + sync::Arc, +}; +use wasm_types::FlatVector as WasmFlatVector; +/* #[napi] pub fn caml_fp_srs_to_bytes(srs: External) -> NapiResult { let buffer = rmp_serde::to_vec(srs.as_ref().0.as_ref()) @@ -28,27 +44,7 @@ pub fn caml_fq_srs_from_bytes(bytes: Uint8Array) -> NapiResult { - pub mod $name { - use super::*; + paste! { + + type WasmPolyComm = $poly_comm_wrapper; #[napi] #[derive(Clone)] - pub struct [Napi $field_name:camel Srs] ( + pub struct [] ( #[napi(skip)] pub Arc> ); - impl [Napi $field_name:camel Srs] { + impl Deref for [] { + type Target = Arc>; + + fn deref(&self) -> &Self::Target { &self.0 } + } + + impl From>> for [] { + fn from(x: Arc>) -> Self { + [](x) + } + } + + impl From<&Arc>> for [] { + fn from(x: &Arc>) -> Self { + [](x.clone()) + } + } + + impl From<[]> for Arc> { + fn from(x: []) -> Self { + x.0 + } + } + + impl From<&[]> for Arc> { + fn from(x: &[]) -> Self { + x.0.clone() + } + } + + impl<'a> From<&'a []> for &'a Arc> { + fn from(x: &'a []) -> Self { + &x.0 + } + } + + /* + impl [] { fn new(inner: SRS<$group_ty>) -> Self { Self ( Arc::new(inner), @@ -80,6 +114,7 @@ macro_rules! impl_srs { Self (inner) } } + */ fn invalid_domain_error() -> Error { Error::new(Status::InvalidArg, "invalid domain size") @@ -90,150 +125,163 @@ macro_rules! impl_srs { } #[napi] - impl $struct_ident { + impl [] { + + #[napi] + pub fn serialize(&self) -> Result { + let mut buf = Vec::new(); + self.0 + .serialize(&mut rmp_serde::Serializer::new(&mut buf)) + .map_err(|e| map_error("srs_serialize", e))?; + Ok(Uint8Array::from(buf)) + } + + #[napi] + pub fn deserialize(bytes: Uint8Array) -> Result { + let srs: SRS<$group_ty> = rmp_serde::from_slice(bytes.as_ref()) + .map_err(|e| map_error("srs_deserialize", e))?; + Ok(Arc::new(srs).into()) + } + #[napi(factory)] - pub fn create(depth: i32) -> Result { - Ok(Self::from_arc(Arc::new(SRS::<$group_ty>::create(depth as usize)))) + pub fn [](depth: i32) -> Result { + Ok(Arc::new(SRS::<$group_ty>::create(depth as usize)).into()) } #[napi(factory)] - pub fn create_parallel(depth: i32) -> Result { - Ok(Self::from_arc(Arc::new(SRS::<$group_ty>::create_parallel( + pub fn [](depth: i32) -> Result { + Ok(Arc::new(SRS::<$group_ty>::create_parallel( depth as usize, - )))) + )).into()) + } + + #[napi] + pub fn [](srs: &[]) -> Vec<$group_wrapper> { + let mut h_and_gs: Vec<$group_wrapper> = vec![srs.0.h.into()]; + h_and_gs.extend(srs.0.g.iter().cloned().map(Into::into)); + h_and_gs } #[napi] - pub fn add_lagrange_basis(&self, log2_size: i32) -> Result<()> { + pub fn [](srs: &[], log2_size: i32) -> Result<()> { let size = 1usize << (log2_size as usize); let domain = EvaluationDomain::<$field_ty>::new(size).ok_or_else(invalid_domain_error)?; - self.inner.get_lagrange_basis(domain); + srs.get_lagrange_basis(domain); Ok(()) } #[napi] - pub fn write(&self, append: Option, path: String) -> Result<()> { + pub fn [](append: Option, srs: &[], path: String) -> Result<()> { + let function_name = format!("caml_{0}_srs_write", stringify!($name).to_lowercase()); let file = OpenOptions::new() - .write(true) - .create(true) .append(append.unwrap_or(true)) .open(&path) - .map_err(|err| map_error("srs_write", err))?; - let mut writer = BufWriter::new(file); - self.inner - .serialize(&mut rmp_serde::Serializer::new(&mut writer)) - .map_err(|err| map_error("srs_write", err)) + .map_err(|err| map_error(&function_name, err))?; + let file = BufWriter::new(file); + srs.0.serialize(&mut rmp_serde::Serializer::new(file)) + .map_err(|err| map_error(&function_name, err)) } #[napi] - pub fn read(offset: Option, path: String) -> Result> { + pub fn [](offset: Option, path: String) -> Result> { + let function_name = format!("caml_{0}_srs_read", stringify!($name).to_lowercase()); let file = match File::open(&path) { Ok(file) => file, - Err(err) => return Err(map_error("srs_read", err)), + Err(err) => return Err(map_error(&function_name, err)), }; let mut reader = BufReader::new(file); if let Some(off) = offset { reader .seek(SeekFrom::Start(off as u64)) - .map_err(|err| map_error("srs_read", err))?; + .map_err(|err| map_error(&function_name, err))?; } match SRS::<$group_ty>::deserialize(&mut rmp_serde::Deserializer::new(reader)) { - Ok(srs) => Ok(Some(Self::from_arc(Arc::new(srs)))), + Ok(srs) => Ok(Some(Arc::new(srs).into())), Err(_) => Ok(None), } } #[napi] - pub fn get(&self) -> WasmVector<$group_wrapper> { - let mut points: Vec<$group_wrapper> = vec![self.inner.h.into()]; - points.extend(self.inner.g.iter().cloned().map(Into::into)); - points.into() + pub fn [](srs: &[]) -> Vec<$group_wrapper> { + let mut h_and_gs: Vec<$group_wrapper> = vec![srs.0.h.into()]; + h_and_gs.extend(srs.0.g.iter().cloned().map(Into::into)); + h_and_gs } #[napi] - pub fn set(points: WasmVector<$group_wrapper>) -> Result { - let mut pts: Vec<$group_ty> = points.into_iter().map(Into::into).collect(); - if pts.is_empty() { + pub fn [](h_and_gs: Vec<$group_wrapper>) -> Result { + let mut h_and_gs: Vec<$group_ty> = h_and_gs.into_iter().map(Into::into).collect(); + if h_and_gs.is_empty() { return Err(Error::new( Status::InvalidArg, "expected at least one element for SRS", )); } - let h = pts.remove(0); - let g = pts; - Ok(Self::from_arc(Arc::new(SRS::<$group_ty> { - h, - g, - lagrange_bases: HashMapCache::new(), - }))) + let h = h_and_gs.remove(0); + let g = h_and_gs; + let srs = SRS::<$group_ty> { h, g, lagrange_bases: HashMapCache::new() }; + Ok(Arc::new(srs).into()) } #[napi] - pub fn maybe_lagrange_commitment( - &self, + pub fn []( + srs: &[], domain_size: i32, - index: i32, - ) -> Option<$poly_comm_wrapper> { - if !self - .inner + i: i32, + ) -> Option { + if !srs + .0 .lagrange_bases .contains_key(&(domain_size as usize)) { return None; } - let basis = self - .inner + let basis = srs .get_lagrange_basis_from_domain_size(domain_size as usize); - basis.get(index as usize).cloned().map(Into::into) + Some(basis[i as usize].clone().into()) } #[napi] - pub fn set_lagrange_basis( - &self, + pub fn [](srs: &[], domain_size: i32, - bases: WasmVector<$poly_comm_wrapper>, + input_bases: WasmVector, ) { - let domain = domain_size as usize; - let commitments: Vec<_> = bases.into_iter().map(Into::into).collect(); - self.inner - .lagrange_bases - .get_or_generate(domain, || commitments.clone()); + srs.0.lagrange_bases + .get_or_generate(domain_size as usize, || input_bases); } #[napi] - pub fn get_lagrange_basis( - &self, + pub fn [](srs: &[], domain_size: i32, - ) -> Result> { + ) -> Result> { let domain = EvaluationDomain::<$field_ty>::new(domain_size as usize) .ok_or_else(invalid_domain_error)?; - let basis = self.inner.get_lagrange_basis(domain); + let basis = srs.0.get_lagrange_basis(domain); Ok(basis.iter().cloned().map(Into::into).collect()) } #[napi] - pub fn commit_evaluations( - &self, + pub fn [](srs: &[], domain_size: i32, - evaluations: Uint8Array, - ) -> Result<$poly_comm_wrapper> { + evals: Uint8Array, + ) -> Result { let elems: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( - evaluations.as_ref().to_vec(), + evals.as_ref().to_vec(), ) .into_iter() .map(Into::into) .collect(); - let domain = EvaluationDomain::<$field_ty>::new(domain_size as usize) + let x_domain = EvaluationDomain::<$field_ty>::new(domain_size as usize) .ok_or_else(invalid_domain_error)?; - let evals = Evaluations::from_vec_and_domain(elems, domain); - let poly = evals.interpolate(); - Ok(self.inner.commit(&poly, None).into()) + let evals = elems.into_iter().map(Into::into).collect(); + let p = Evaluations::<$field_ty>::from_vec_and_domain(evals, x_domain).interpolate(); + Ok(srs.commit_non_hiding(&p, 1).into()) } #[napi] - pub fn b_poly_commitment(&self, chals: Uint8Array) -> Result<$poly_comm_wrapper> { + pub fn b_poly_commitment(srs: &[], chals: Uint8Array) -> Result { let elements: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( chals.as_ref().to_vec(), ) @@ -241,17 +289,17 @@ macro_rules! impl_srs { .map(Into::into) .collect(); let coeffs = b_poly_coefficients(&elements); - let poly = DensePolynomial::<$field_ty>::from_coefficients_vec(coeffs); - Ok(self.inner.commit_non_hiding(&poly, 1).into()) + let p = DensePolynomial::<$field_ty>::from_coefficients_vec(coeffs); + Ok(srs.commit_non_hiding(&p, 1).into()) } #[napi] pub fn batch_accumulator_check( - &self, - commitments: WasmVector<$group_wrapper>, + srs: &[], + comms: WasmVector<$group_wrapper>, chals: Uint8Array, ) -> Result { - let comms: Vec<$group_ty> = commitments.into_iter().map(Into::into).collect(); + let comms: Vec<$group_ty> = comms.into_iter().map(Into::into).collect(); let chals: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( chals.as_ref().to_vec(), ) @@ -259,7 +307,7 @@ macro_rules! impl_srs { .map(Into::into) .collect(); Ok(poly_commitment::utils::batch_dlog_accumulator_check( - &self.inner, + &srs, &comms, &chals, )) @@ -267,8 +315,8 @@ macro_rules! impl_srs { #[napi] pub fn batch_accumulator_generate( - &self, - count: i32, + srs: &[], + comms: i32, chals: Uint8Array, ) -> Result> { let chals: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( @@ -278,20 +326,20 @@ macro_rules! impl_srs { .map(Into::into) .collect(); let points = poly_commitment::utils::batch_dlog_accumulator_generate::<$group_ty>( - &self.inner, - count as usize, + &srs, + comms as usize, &chals, ); Ok(points.into_iter().map(Into::into).collect()) } #[napi] - pub fn h(&self) -> $group_wrapper { - self.inner.h.into() + pub fn h(srs: &[]) -> $group_wrapper { + srs.h.into() } } } - }; + } } pub mod fp { diff --git a/plonk-napi/src/tables.rs b/plonk-napi/src/tables.rs index fd00cb94ae..cd9f4f718b 100644 --- a/plonk-napi/src/tables.rs +++ b/plonk-napi/src/tables.rs @@ -1,8 +1,5 @@ use arkworks::{WasmPastaFp, WasmPastaFq}; -use kimchi::circuits::lookup::{ - runtime_tables::RuntimeTableCfg, - tables::LookupTable, -}; +use kimchi::circuits::lookup::{runtime_tables::RuntimeTableCfg, tables::LookupTable}; use mina_curves::pasta::{Fp, Fq}; use napi::bindgen_prelude::Uint8Array; use napi_derive::napi; @@ -66,18 +63,14 @@ pub fn lookup_table_fq_from_js(js: JsLookupTableFq) -> napi::Result napi::Result> { +pub fn runtime_table_cfg_fp_from_js(js: JsRuntimeTableCfgFp) -> napi::Result> { Ok(RuntimeTableCfg { id: js.id, first_column: bytes_to_fp_vec(typed_array_to_vec(&js.first_column)), }) } -pub fn runtime_table_cfg_fq_from_js( - js: JsRuntimeTableCfgFq, -) -> napi::Result> { +pub fn runtime_table_cfg_fq_from_js(js: JsRuntimeTableCfgFq) -> napi::Result> { Ok(RuntimeTableCfg { id: js.id, first_column: bytes_to_fq_vec(typed_array_to_vec(&js.first_column)), diff --git a/plonk-wasm/src/gate_vector.rs b/plonk-wasm/src/gate_vector.rs index 7928845af1..d78f9d82a7 100644 --- a/plonk-wasm/src/gate_vector.rs +++ b/plonk-wasm/src/gate_vector.rs @@ -60,7 +60,12 @@ pub mod shared { Gate { typ: cg.typ, wires: GateWires::new([ - cg.wires[0], cg.wires[1], cg.wires[2], cg.wires[3], cg.wires[4], cg.wires[5], + cg.wires[0], + cg.wires[1], + cg.wires[2], + cg.wires[3], + cg.wires[4], + cg.wires[5], cg.wires[6], ]), coeffs: cg.coeffs, @@ -76,7 +81,12 @@ pub mod shared { Gate { typ: cg.typ, wires: GateWires::new([ - cg.wires[0], cg.wires[1], cg.wires[2], cg.wires[3], cg.wires[4], cg.wires[5], + cg.wires[0], + cg.wires[1], + cg.wires[2], + cg.wires[3], + cg.wires[4], + cg.wires[5], cg.wires[6], ]), coeffs: cg.coeffs.clone(), @@ -181,7 +191,9 @@ pub mod shared { } } -pub use self::shared::{Gate as CoreGate, GateVector as CoreGateVector, GateWires as CoreGateWires}; +pub use self::shared::{ + Gate as CoreGate, GateVector as CoreGateVector, GateWires as CoreGateWires, +}; #[wasm_bindgen] #[derive(Clone, Copy, Debug)] From db8bdd1965fe8a6835eae9f664550beb59e20e5d Mon Sep 17 00:00:00 2001 From: querolita Date: Mon, 27 Oct 2025 19:50:13 +0100 Subject: [PATCH 04/12] napi: implement FromNapiValue for polycomm --- plonk-napi/src/poly_comm.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plonk-napi/src/poly_comm.rs b/plonk-napi/src/poly_comm.rs index b988f3e1e7..8976a754e9 100644 --- a/plonk-napi/src/poly_comm.rs +++ b/plonk-napi/src/poly_comm.rs @@ -1,4 +1,5 @@ use crate::wasm_vector::WasmVector; +use napi::bindgen_prelude::{ClassInstance, FromNapiValue}; use napi_derive::napi; use paste::paste; use poly_commitment::commitment::PolyComm; @@ -93,6 +94,16 @@ macro_rules! impl_poly_comm { } } } + + impl FromNapiValue for [] { + unsafe fn from_napi_value( + env: napi::sys::napi_env, + napi_val: napi::sys::napi_value, + ) -> napi::Result { + let instance = ]> as FromNapiValue>::from_napi_value(env, napi_val)?; + Ok((*instance).clone()) + } + } } }; } From 5c19d073e0a4b3dd825f9991c875722aef741ffa Mon Sep 17 00:00:00 2001 From: querolita Date: Mon, 27 Oct 2025 19:50:36 +0100 Subject: [PATCH 05/12] napi: finish compilation of srs --- plonk-napi/src/lib.rs | 2 +- plonk-napi/src/srs.rs | 28 ++-------------------------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/plonk-napi/src/lib.rs b/plonk-napi/src/lib.rs index bc4a77b825..ab4ab14bf3 100644 --- a/plonk-napi/src/lib.rs +++ b/plonk-napi/src/lib.rs @@ -16,7 +16,7 @@ pub use pasta_fq_plonk_index::{ }; pub use poly_comm::{pallas::WasmFqPolyComm, vesta::WasmFpPolyComm}; pub use poseidon::{caml_pasta_fp_poseidon_block_cipher, caml_pasta_fq_poseidon_block_cipher}; -pub use srs::{caml_fp_srs_from_bytes, caml_fp_srs_to_bytes, caml_fq_srs_from_bytes}; +pub use srs::*; pub use tables::{JsLookupTableFp, JsLookupTableFq, JsRuntimeTableCfgFp, JsRuntimeTableCfgFq}; pub use wasm_vector::{fp::WasmVecVecFp, fq::WasmVecVecFq}; pub use wrappers::{ diff --git a/plonk-napi/src/srs.rs b/plonk-napi/src/srs.rs index 750f89486e..92947faee7 100644 --- a/plonk-napi/src/srs.rs +++ b/plonk-napi/src/srs.rs @@ -8,8 +8,7 @@ use crate::{ }; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Evaluations}; use core::ops::Deref; -use mina_curves::pasta::{Pallas as GAffineOther, Vesta as GAffine}; -use napi::bindgen_prelude::{Error, External, Result, Status, Uint8Array}; +use napi::bindgen_prelude::{Error, Result, Status, Uint8Array}; use napi_derive::napi; use paste::paste; use poly_commitment::{ @@ -23,29 +22,6 @@ use std::{ }; use wasm_types::FlatVector as WasmFlatVector; -/* -#[napi] -pub fn caml_fp_srs_to_bytes(srs: External) -> NapiResult { - let buffer = rmp_serde::to_vec(srs.as_ref().0.as_ref()) - .map_err(|e| Error::new(Status::GenericFailure, e.to_string()))?; - Ok(Uint8Array::from(buffer)) -} - -#[napi] -pub fn caml_fp_srs_from_bytes(bytes: Uint8Array) -> NapiResult> { - let srs: SRS = rmp_serde::from_slice(bytes.as_ref()) - .map_err(|e| Error::new(Status::InvalidArg, e.to_string()))?; - Ok(External::new(Arc::new(srs).into())) -} - -#[napi] -pub fn caml_fq_srs_from_bytes(bytes: Uint8Array) -> NapiResult> { - let srs: SRS = rmp_serde::from_slice(bytes.as_ref()) - .map_err(|e: rmp_serde::decode::Error| Error::new(Status::InvalidArg, e.to_string()))?; - Ok(External::new(Arc::new(srs).into())) -} -*/ - macro_rules! impl_srs { ( $name:ident, @@ -249,7 +225,7 @@ macro_rules! impl_srs { input_bases: WasmVector, ) { srs.0.lagrange_bases - .get_or_generate(domain_size as usize, || input_bases); + .get_or_generate(domain_size as usize, || { input_bases.into_iter().map(Into::into).collect()}); } #[napi] From 4e998c566e2943546ad0be16792c0aeaa1cfeb8b Mon Sep 17 00:00:00 2001 From: querolita Date: Tue, 28 Oct 2025 13:34:33 +0100 Subject: [PATCH 06/12] napi: replicate format in plonk-wasm and export --- plonk-napi/src/lib.rs | 2 +- plonk-napi/src/srs.rs | 184 +++++++++++++++++++----------------------- 2 files changed, 84 insertions(+), 102 deletions(-) diff --git a/plonk-napi/src/lib.rs b/plonk-napi/src/lib.rs index ab4ab14bf3..76b14e7ff8 100644 --- a/plonk-napi/src/lib.rs +++ b/plonk-napi/src/lib.rs @@ -16,7 +16,7 @@ pub use pasta_fq_plonk_index::{ }; pub use poly_comm::{pallas::WasmFqPolyComm, vesta::WasmFpPolyComm}; pub use poseidon::{caml_pasta_fp_poseidon_block_cipher, caml_pasta_fq_poseidon_block_cipher}; -pub use srs::*; +pub use srs::{fp::NapiFpSrs as WasmFpSrs, fq::NapiFqSrs as WasmFqSrs}; pub use tables::{JsLookupTableFp, JsLookupTableFq, JsRuntimeTableCfgFp, JsRuntimeTableCfgFq}; pub use wasm_vector::{fp::WasmVecVecFp, fq::WasmVecVecFq}; pub use wrappers::{ diff --git a/plonk-napi/src/srs.rs b/plonk-napi/src/srs.rs index 92947faee7..a95fbcd049 100644 --- a/plonk-napi/src/srs.rs +++ b/plonk-napi/src/srs.rs @@ -1,11 +1,4 @@ -use crate::{ - poly_comm::{pallas::WasmFqPolyComm, vesta::WasmFpPolyComm}, - wasm_vector::WasmVector, - wrappers::{ - field::{WasmPastaFp, WasmPastaFq}, - group::{WasmGPallas, WasmGVesta}, - }, -}; +use crate::wasm_vector::WasmVector; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Evaluations}; use core::ops::Deref; use napi::bindgen_prelude::{Error, Result, Status, Uint8Array}; @@ -25,73 +18,56 @@ use wasm_types::FlatVector as WasmFlatVector; macro_rules! impl_srs { ( $name:ident, - $field_ty:ty, - $wasmF:ty, - $group_ty:ty, - $group_wrapper:ty, - $poly_comm_wrapper:ty, - $field_name:ident + $WasmF:ty, + $WasmG:ty, + $F:ty, + $G:ty, + $WasmPolyComm:ty, ) => { paste! { - type WasmPolyComm = $poly_comm_wrapper; - #[napi] #[derive(Clone)] - pub struct [] ( - #[napi(skip)] pub Arc> + pub struct [] ( + #[napi(skip)] pub Arc> ); - impl Deref for [] { - type Target = Arc>; + impl Deref for [] { + type Target = Arc>; fn deref(&self) -> &Self::Target { &self.0 } } - impl From>> for [] { - fn from(x: Arc>) -> Self { - [](x) + impl From>> for [] { + fn from(x: Arc>) -> Self { + [](x) } } - impl From<&Arc>> for [] { - fn from(x: &Arc>) -> Self { - [](x.clone()) + impl From<&Arc>> for [] { + fn from(x: &Arc>) -> Self { + [](x.clone()) } } - impl From<[]> for Arc> { - fn from(x: []) -> Self { + impl From<[]> for Arc> { + fn from(x: []) -> Self { x.0 } } - impl From<&[]> for Arc> { - fn from(x: &[]) -> Self { + impl From<&[]> for Arc> { + fn from(x: &[]) -> Self { x.0.clone() } } - impl<'a> From<&'a []> for &'a Arc> { - fn from(x: &'a []) -> Self { + impl<'a> From<&'a []> for &'a Arc> { + fn from(x: &'a []) -> Self { &x.0 } } - /* - impl [] { - fn new(inner: SRS<$group_ty>) -> Self { - Self ( - Arc::new(inner), - ) - } - - fn from_arc(inner: Arc>) -> Self { - Self (inner) - } - } - */ - fn invalid_domain_error() -> Error { Error::new(Status::InvalidArg, "invalid domain size") } @@ -101,7 +77,7 @@ macro_rules! impl_srs { } #[napi] - impl [] { + impl [] { #[napi] pub fn serialize(&self) -> Result { @@ -114,40 +90,40 @@ macro_rules! impl_srs { #[napi] pub fn deserialize(bytes: Uint8Array) -> Result { - let srs: SRS<$group_ty> = rmp_serde::from_slice(bytes.as_ref()) + let srs: SRS<$G> = rmp_serde::from_slice(bytes.as_ref()) .map_err(|e| map_error("srs_deserialize", e))?; Ok(Arc::new(srs).into()) } #[napi(factory)] pub fn [](depth: i32) -> Result { - Ok(Arc::new(SRS::<$group_ty>::create(depth as usize)).into()) + Ok(Arc::new(SRS::<$G>::create(depth as usize)).into()) } #[napi(factory)] pub fn [](depth: i32) -> Result { - Ok(Arc::new(SRS::<$group_ty>::create_parallel( + Ok(Arc::new(SRS::<$G>::create_parallel( depth as usize, )).into()) } #[napi] - pub fn [](srs: &[]) -> Vec<$group_wrapper> { - let mut h_and_gs: Vec<$group_wrapper> = vec![srs.0.h.into()]; + pub fn [](srs: &[]) -> Vec<$WasmG> { + let mut h_and_gs: Vec<$WasmG> = vec![srs.0.h.into()]; h_and_gs.extend(srs.0.g.iter().cloned().map(Into::into)); h_and_gs } #[napi] - pub fn [](srs: &[], log2_size: i32) -> Result<()> { + pub fn [](srs: &[], log2_size: i32) -> Result<()> { let size = 1usize << (log2_size as usize); - let domain = EvaluationDomain::<$field_ty>::new(size).ok_or_else(invalid_domain_error)?; + let domain = EvaluationDomain::<$F>::new(size).ok_or_else(invalid_domain_error)?; srs.get_lagrange_basis(domain); Ok(()) } #[napi] - pub fn [](append: Option, srs: &[], path: String) -> Result<()> { + pub fn [](append: Option, srs: &[], path: String) -> Result<()> { let function_name = format!("caml_{0}_srs_write", stringify!($name).to_lowercase()); let file = OpenOptions::new() .append(append.unwrap_or(true)) @@ -173,22 +149,22 @@ macro_rules! impl_srs { .map_err(|err| map_error(&function_name, err))?; } - match SRS::<$group_ty>::deserialize(&mut rmp_serde::Deserializer::new(reader)) { + match SRS::<$G>::deserialize(&mut rmp_serde::Deserializer::new(reader)) { Ok(srs) => Ok(Some(Arc::new(srs).into())), Err(_) => Ok(None), } } #[napi] - pub fn [](srs: &[]) -> Vec<$group_wrapper> { - let mut h_and_gs: Vec<$group_wrapper> = vec![srs.0.h.into()]; + pub fn [](srs: &[]) -> Vec<$WasmG> { + let mut h_and_gs: Vec<$WasmG> = vec![srs.0.h.into()]; h_and_gs.extend(srs.0.g.iter().cloned().map(Into::into)); h_and_gs } #[napi] - pub fn [](h_and_gs: Vec<$group_wrapper>) -> Result { - let mut h_and_gs: Vec<$group_ty> = h_and_gs.into_iter().map(Into::into).collect(); + pub fn [](h_and_gs: Vec<$WasmG>) -> Result { + let mut h_and_gs: Vec<$G> = h_and_gs.into_iter().map(Into::into).collect(); if h_and_gs.is_empty() { return Err(Error::new( Status::InvalidArg, @@ -197,16 +173,16 @@ macro_rules! impl_srs { } let h = h_and_gs.remove(0); let g = h_and_gs; - let srs = SRS::<$group_ty> { h, g, lagrange_bases: HashMapCache::new() }; + let srs = SRS::<$G> { h, g, lagrange_bases: HashMapCache::new() }; Ok(Arc::new(srs).into()) } #[napi] pub fn []( - srs: &[], + srs: &[], domain_size: i32, i: i32, - ) -> Option { + ) -> Option<$WasmPolyComm> { if !srs .0 .lagrange_bases @@ -220,63 +196,63 @@ macro_rules! impl_srs { } #[napi] - pub fn [](srs: &[], + pub fn [](srs: &[], domain_size: i32, - input_bases: WasmVector, + input_bases: WasmVector<$WasmPolyComm>, ) { srs.0.lagrange_bases .get_or_generate(domain_size as usize, || { input_bases.into_iter().map(Into::into).collect()}); } #[napi] - pub fn [](srs: &[], + pub fn [](srs: &[], domain_size: i32, - ) -> Result> { - let domain = EvaluationDomain::<$field_ty>::new(domain_size as usize) + ) -> Result> { + let domain = EvaluationDomain::<$F>::new(domain_size as usize) .ok_or_else(invalid_domain_error)?; let basis = srs.0.get_lagrange_basis(domain); Ok(basis.iter().cloned().map(Into::into).collect()) } #[napi] - pub fn [](srs: &[], + pub fn [](srs: &[], domain_size: i32, evals: Uint8Array, - ) -> Result { - let elems: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( + ) -> Result<$WasmPolyComm> { + let elems: Vec<$F> = WasmFlatVector::<$WasmF>::from_bytes( evals.as_ref().to_vec(), ) .into_iter() .map(Into::into) .collect(); - let x_domain = EvaluationDomain::<$field_ty>::new(domain_size as usize) + let x_domain = EvaluationDomain::<$F>::new(domain_size as usize) .ok_or_else(invalid_domain_error)?; let evals = elems.into_iter().map(Into::into).collect(); - let p = Evaluations::<$field_ty>::from_vec_and_domain(evals, x_domain).interpolate(); + let p = Evaluations::<$F>::from_vec_and_domain(evals, x_domain).interpolate(); Ok(srs.commit_non_hiding(&p, 1).into()) } #[napi] - pub fn b_poly_commitment(srs: &[], chals: Uint8Array) -> Result { - let elements: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( + pub fn [](srs: &[], chals: Uint8Array) -> Result<$WasmPolyComm> { + let elements: Vec<$F> = WasmFlatVector::<$WasmF>::from_bytes( chals.as_ref().to_vec(), ) .into_iter() .map(Into::into) .collect(); let coeffs = b_poly_coefficients(&elements); - let p = DensePolynomial::<$field_ty>::from_coefficients_vec(coeffs); + let p = DensePolynomial::<$F>::from_coefficients_vec(coeffs); Ok(srs.commit_non_hiding(&p, 1).into()) } #[napi] - pub fn batch_accumulator_check( - srs: &[], - comms: WasmVector<$group_wrapper>, + pub fn []( + srs: &[], + comms: WasmVector<$WasmG>, chals: Uint8Array, ) -> Result { - let comms: Vec<$group_ty> = comms.into_iter().map(Into::into).collect(); - let chals: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( + let comms: Vec<$G> = comms.into_iter().map(Into::into).collect(); + let chals: Vec<$F> = WasmFlatVector::<$WasmF>::from_bytes( chals.as_ref().to_vec(), ) .into_iter() @@ -290,18 +266,18 @@ macro_rules! impl_srs { } #[napi] - pub fn batch_accumulator_generate( - srs: &[], + pub fn []( + srs: &[], comms: i32, chals: Uint8Array, - ) -> Result> { - let chals: Vec<$field_ty> = WasmFlatVector::<$wasmF>::from_bytes( + ) -> Result> { + let chals: Vec<$F> = WasmFlatVector::<$WasmF>::from_bytes( chals.as_ref().to_vec(), ) .into_iter() .map(Into::into) .collect(); - let points = poly_commitment::utils::batch_dlog_accumulator_generate::<$group_ty>( + let points = poly_commitment::utils::batch_dlog_accumulator_generate::<$G>( &srs, comms as usize, &chals, @@ -310,7 +286,7 @@ macro_rules! impl_srs { } #[napi] - pub fn h(srs: &[]) -> $group_wrapper { + pub fn h(srs: &[]) -> $WasmG { srs.h.into() } } @@ -320,26 +296,32 @@ macro_rules! impl_srs { pub mod fp { use super::*; + use crate::{ + poly_comm::vesta::WasmFpPolyComm, + wrappers::{field::WasmPastaFp, group::WasmGVesta}, + }; impl_srs!( - fp, - mina_curves::pasta::Fp, - WasmPastaFp, - mina_curves::pasta::Vesta, - WasmGVesta, - WasmFpPolyComm, - Fp + fp, // field name + WasmPastaFp, // Napi field wrapper + WasmGVesta, // Napi group wrapper + mina_curves::pasta::Fp, // Actual Kimchi field + mina_curves::pasta::Vesta, // Actual kimchi group + WasmFpPolyComm, // Napi poly commitment type ); } pub mod fq { use super::*; + use crate::{ + poly_comm::pallas::WasmFqPolyComm, + wrappers::{field::WasmPastaFq, group::WasmGPallas}, + }; impl_srs!( - fq, - mina_curves::pasta::Fq, - WasmPastaFq, - mina_curves::pasta::Pallas, - WasmGPallas, - WasmFqPolyComm, - Fq + fq, // Field name + WasmPastaFq, // Napi field wrapper + WasmGPallas, // Napi group wrapper + mina_curves::pasta::Fq, // Actual Kimchi field + mina_curves::pasta::Pallas, // Actual kimchi group + WasmFqPolyComm, // Napi poly commitment type ); } From a04edaeee5780c8af77a2cd8fff59610e92e2735 Mon Sep 17 00:00:00 2001 From: querolita Date: Tue, 28 Oct 2025 19:05:08 +0100 Subject: [PATCH 07/12] napi: fix naming of srs functions inside macro --- plonk-napi/src/srs.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/plonk-napi/src/srs.rs b/plonk-napi/src/srs.rs index a95fbcd049..c15b0de664 100644 --- a/plonk-napi/src/srs.rs +++ b/plonk-napi/src/srs.rs @@ -108,14 +108,7 @@ macro_rules! impl_srs { } #[napi] - pub fn [](srs: &[]) -> Vec<$WasmG> { - let mut h_and_gs: Vec<$WasmG> = vec![srs.0.h.into()]; - h_and_gs.extend(srs.0.g.iter().cloned().map(Into::into)); - h_and_gs - } - - #[napi] - pub fn [](srs: &[], log2_size: i32) -> Result<()> { + pub fn [](srs: &[], log2_size: i32) -> Result<()> { let size = 1usize << (log2_size as usize); let domain = EvaluationDomain::<$F>::new(size).ok_or_else(invalid_domain_error)?; srs.get_lagrange_basis(domain); From d205c85eaf46685260ea228bf8008e07deb22123 Mon Sep 17 00:00:00 2001 From: querolita Date: Tue, 28 Oct 2025 20:01:33 +0100 Subject: [PATCH 08/12] napi: reintroduce gate vector --- plonk-napi/src/gate_vector.rs | 223 ++++++++++++++++++++++++++++++++++ plonk-napi/src/lib.rs | 13 ++ 2 files changed, 236 insertions(+) create mode 100644 plonk-napi/src/gate_vector.rs diff --git a/plonk-napi/src/gate_vector.rs b/plonk-napi/src/gate_vector.rs new file mode 100644 index 0000000000..2a4712ee26 --- /dev/null +++ b/plonk-napi/src/gate_vector.rs @@ -0,0 +1,223 @@ +use kimchi::circuits::{ + gate::{Circuit, CircuitGate, GateType}, + wires::{GateWires, Wire as KimchiWire}, +}; +use mina_curves::pasta::{Fp, Fq}; +use napi::bindgen_prelude::*; +use napi_derive::napi; +use o1_utils::hasher::CryptoDigest; +use paste::paste; +use wasm_types::{FlatVector as WasmFlatVector, FlatVectorElem}; + +use crate::wrappers::{ + field::{WasmPastaFp, WasmPastaFq}, + wires::NapiWire, +}; + +#[napi(object)] +#[derive(Clone, Debug, Default)] +pub struct NapiGateWires { + pub w0: NapiWire, + pub w1: NapiWire, + pub w2: NapiWire, + pub w3: NapiWire, + pub w4: NapiWire, + pub w5: NapiWire, + pub w6: NapiWire, +} + +impl NapiGateWires { + fn into_inner(self) -> GateWires { + [ + KimchiWire::from(self.w0), + KimchiWire::from(self.w1), + KimchiWire::from(self.w2), + KimchiWire::from(self.w3), + KimchiWire::from(self.w4), + KimchiWire::from(self.w5), + KimchiWire::from(self.w6), + ] + } +} + +impl From<&GateWires> for NapiGateWires { + fn from(value: &GateWires) -> Self { + Self { + w0: value[0].into(), + w1: value[1].into(), + w2: value[2].into(), + w3: value[3].into(), + w4: value[4].into(), + w5: value[5].into(), + w6: value[6].into(), + } + } +} + +fn gate_type_from_i32(value: i32) -> Result { + // Ocaml/JS int are signed, so we use i32 here + if value < 0 { + return Err(Error::new( + Status::InvalidArg, + format!("invalid GateType discriminant: {}", value), + )); + } + + let variants: &[GateType] = &[ + GateType::Zero, + GateType::Generic, + GateType::Poseidon, + GateType::CompleteAdd, + GateType::VarBaseMul, + GateType::EndoMul, + GateType::EndoMulScalar, + GateType::Lookup, + GateType::CairoClaim, + GateType::CairoInstruction, + GateType::CairoFlags, + GateType::CairoTransition, + GateType::RangeCheck0, + GateType::RangeCheck1, + GateType::ForeignFieldAdd, + GateType::ForeignFieldMul, + GateType::Xor16, + GateType::Rot64, + ]; + + let index = value as usize; + variants.get(index).copied().ok_or_else(|| { + Error::new( + Status::InvalidArg, + format!("invalid GateType discriminant: {}", value), + ) + }) +} + +// For convenience to not expose the GateType enum to JS +fn gate_type_to_i32(value: GateType) -> i32 { + value as i32 +} + +macro_rules! impl_gate_support { + ($field_name:ident, $F:ty, $WasmF:ty) => { + paste! { + #[napi(object)] + #[derive(Clone, Debug, Default)] + pub struct [] { + pub typ: i32, // for convenience, we use i32 instead of GateType + pub wires: NapiGateWires, + pub coeffs: Vec, // for now, serializing fields as flat bytes, but subject to changes + } + + impl [] { + fn into_inner(self) -> Result> { + let coeffs = WasmFlatVector::<$WasmF>::from_bytes(self.coeffs) + .into_iter() + .map(Into::into) + .collect(); + + Ok(CircuitGate { + typ: gate_type_from_i32(self.typ)?, + wires: self.wires.into_inner(), + coeffs, + }) + } + + fn from_inner(value: &CircuitGate<$F>) -> Self { + let coeffs = value + .coeffs + .iter() + .cloned() + .map($WasmF::from) + .flat_map(|elem| elem.flatten()) + .collect(); + + Self { + typ: gate_type_to_i32(value.typ), + wires: (&value.wires).into(), + coeffs, + } + } + } + + #[napi] + #[derive(Clone, Default, Debug)] + pub struct []( + #[napi(skip)] pub Vec>, + ); + + #[napi] + pub fn []() -> [] { + println!("from native rust creating gate vector"); + [](Vec::new()) + } + + #[napi] + pub fn []( + vector: &mut [], + gate: [], + ) -> Result<()> { + println!("from native rust adding gate to vector"); + vector.0.push(gate.into_inner()?); + Ok(()) + } + + #[napi] + pub fn []( + vector: &[], + index: i32, + ) -> [] { + println!("from native rust getting gate from vector"); + []::from_inner(&vector.0[index as usize]) + } + + #[napi] + pub fn []( + vector: &[], + ) -> i32 { + println!("from native rust getting gate vector length"); + vector.0.len() as i32 + } + + #[napi] + pub fn []( + vector: &mut [], + target: NapiWire, + head: NapiWire, + ) { + println!("from native rust wrapping wire in gate vector"); + vector.0[target.row as usize].wires[target.col as usize] = KimchiWire::from(head); + } + + #[napi] + pub fn []( + public_input_size: i32, + vector: &[], + ) -> Uint8Array { + println!("from native rust computing gate vector digest"); + let bytes = Circuit::new(public_input_size as usize, &vector.0) + .digest() + .to_vec(); + Uint8Array::from(bytes) + } + + #[napi] + pub fn []( + public_input_size: i32, + vector: &[], + ) -> Result { + println!("from native rust serializing gate vector to json"); + let circuit = Circuit::new(public_input_size as usize, &vector.0); + serde_json::to_string(&circuit).map_err(|err| { + Error::new( + Status::GenericFailure, + format!("couldn't serialize constraints: {}", err), + ) + }) + } + } + }; +} + +impl_gate_support!(fp, Fp, WasmPastaFp); +impl_gate_support!(fq, Fq, WasmPastaFq); diff --git a/plonk-napi/src/lib.rs b/plonk-napi/src/lib.rs index 76b14e7ff8..0e17eb8215 100644 --- a/plonk-napi/src/lib.rs +++ b/plonk-napi/src/lib.rs @@ -1,4 +1,5 @@ mod circuit; +mod gate_vector; mod pasta_fp_plonk_index; mod pasta_fq_plonk_index; mod poly_comm; @@ -8,6 +9,18 @@ mod tables; mod wasm_vector; mod wrappers; +pub use circuit::prover_to_json; +pub use gate_vector::{ + caml_pasta_fp_plonk_circuit_serialize, caml_pasta_fp_plonk_gate_vector_add, + caml_pasta_fp_plonk_gate_vector_create, caml_pasta_fp_plonk_gate_vector_digest, + caml_pasta_fp_plonk_gate_vector_get, caml_pasta_fp_plonk_gate_vector_len, + caml_pasta_fp_plonk_gate_vector_wrap, caml_pasta_fq_plonk_circuit_serialize, + caml_pasta_fq_plonk_gate_vector_add, caml_pasta_fq_plonk_gate_vector_create, + caml_pasta_fq_plonk_gate_vector_digest, caml_pasta_fq_plonk_gate_vector_get, + caml_pasta_fq_plonk_gate_vector_len, caml_pasta_fq_plonk_gate_vector_wrap, + NapiFpGate as WasmFpGate, NapiFpGateVector as WasmFpGateVector, NapiFqGate as WasmFqGate, + NapiFqGateVector as WasmFqGateVector, +}; pub use pasta_fp_plonk_index::{ prover_index_fp_from_bytes, prover_index_fp_to_bytes, WasmPastaFpPlonkIndex, }; From 1b937725ef1901245c64a8391c8364882eca11ab Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 29 Oct 2025 18:32:57 +0100 Subject: [PATCH 09/12] napi: remove prints for gate vector --- plonk-napi/src/gate_vector.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/plonk-napi/src/gate_vector.rs b/plonk-napi/src/gate_vector.rs index 2a4712ee26..9c77299502 100644 --- a/plonk-napi/src/gate_vector.rs +++ b/plonk-napi/src/gate_vector.rs @@ -148,7 +148,6 @@ macro_rules! impl_gate_support { #[napi] pub fn []() -> [] { - println!("from native rust creating gate vector"); [](Vec::new()) } @@ -157,7 +156,6 @@ macro_rules! impl_gate_support { vector: &mut [], gate: [], ) -> Result<()> { - println!("from native rust adding gate to vector"); vector.0.push(gate.into_inner()?); Ok(()) } @@ -167,7 +165,6 @@ macro_rules! impl_gate_support { vector: &[], index: i32, ) -> [] { - println!("from native rust getting gate from vector"); []::from_inner(&vector.0[index as usize]) } @@ -175,7 +172,6 @@ macro_rules! impl_gate_support { pub fn []( vector: &[], ) -> i32 { - println!("from native rust getting gate vector length"); vector.0.len() as i32 } @@ -185,7 +181,6 @@ macro_rules! impl_gate_support { target: NapiWire, head: NapiWire, ) { - println!("from native rust wrapping wire in gate vector"); vector.0[target.row as usize].wires[target.col as usize] = KimchiWire::from(head); } @@ -194,7 +189,6 @@ macro_rules! impl_gate_support { public_input_size: i32, vector: &[], ) -> Uint8Array { - println!("from native rust computing gate vector digest"); let bytes = Circuit::new(public_input_size as usize, &vector.0) .digest() .to_vec(); @@ -206,7 +200,6 @@ macro_rules! impl_gate_support { public_input_size: i32, vector: &[], ) -> Result { - println!("from native rust serializing gate vector to json"); let circuit = Circuit::new(public_input_size as usize, &vector.0); serde_json::to_string(&circuit).map_err(|err| { Error::new( From 570a29aada1d09087bda961d3bc4f9e25deaaeec Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 29 Oct 2025 18:33:03 +0100 Subject: [PATCH 10/12] napi: add prints for srs --- plonk-napi/src/srs.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/plonk-napi/src/srs.rs b/plonk-napi/src/srs.rs index c15b0de664..0242a10def 100644 --- a/plonk-napi/src/srs.rs +++ b/plonk-napi/src/srs.rs @@ -97,11 +97,13 @@ macro_rules! impl_srs { #[napi(factory)] pub fn [](depth: i32) -> Result { + println!("Creating SRS with napi"); Ok(Arc::new(SRS::<$G>::create(depth as usize)).into()) } #[napi(factory)] pub fn [](depth: i32) -> Result { + println!("Creating SRS in parallel with napi"); Ok(Arc::new(SRS::<$G>::create_parallel( depth as usize, )).into()) @@ -109,6 +111,7 @@ macro_rules! impl_srs { #[napi] pub fn [](srs: &[], log2_size: i32) -> Result<()> { + println!("Adding lagrange basis with napi"); let size = 1usize << (log2_size as usize); let domain = EvaluationDomain::<$F>::new(size).ok_or_else(invalid_domain_error)?; srs.get_lagrange_basis(domain); @@ -117,6 +120,7 @@ macro_rules! impl_srs { #[napi] pub fn [](append: Option, srs: &[], path: String) -> Result<()> { + println!("Writing SRS to file with napi"); let function_name = format!("caml_{0}_srs_write", stringify!($name).to_lowercase()); let file = OpenOptions::new() .append(append.unwrap_or(true)) @@ -129,6 +133,7 @@ macro_rules! impl_srs { #[napi] pub fn [](offset: Option, path: String) -> Result> { + println!("Reading SRS from file with napi"); let function_name = format!("caml_{0}_srs_read", stringify!($name).to_lowercase()); let file = match File::open(&path) { Ok(file) => file, @@ -150,6 +155,7 @@ macro_rules! impl_srs { #[napi] pub fn [](srs: &[]) -> Vec<$WasmG> { + println!("Getting SRS with napi"); let mut h_and_gs: Vec<$WasmG> = vec![srs.0.h.into()]; h_and_gs.extend(srs.0.g.iter().cloned().map(Into::into)); h_and_gs @@ -157,6 +163,7 @@ macro_rules! impl_srs { #[napi] pub fn [](h_and_gs: Vec<$WasmG>) -> Result { + println!("Setting SRS with napi"); let mut h_and_gs: Vec<$G> = h_and_gs.into_iter().map(Into::into).collect(); if h_and_gs.is_empty() { return Err(Error::new( @@ -176,6 +183,7 @@ macro_rules! impl_srs { domain_size: i32, i: i32, ) -> Option<$WasmPolyComm> { + println!("Getting maybe lagrange commitment with napi"); if !srs .0 .lagrange_bases @@ -193,6 +201,7 @@ macro_rules! impl_srs { domain_size: i32, input_bases: WasmVector<$WasmPolyComm>, ) { + println!("Setting lagrange basis with napi"); srs.0.lagrange_bases .get_or_generate(domain_size as usize, || { input_bases.into_iter().map(Into::into).collect()}); } @@ -201,6 +210,7 @@ macro_rules! impl_srs { pub fn [](srs: &[], domain_size: i32, ) -> Result> { + println!("Getting lagrange basis with napi"); let domain = EvaluationDomain::<$F>::new(domain_size as usize) .ok_or_else(invalid_domain_error)?; let basis = srs.0.get_lagrange_basis(domain); @@ -212,6 +222,7 @@ macro_rules! impl_srs { domain_size: i32, evals: Uint8Array, ) -> Result<$WasmPolyComm> { + println!("Committing evaluations with napi"); let elems: Vec<$F> = WasmFlatVector::<$WasmF>::from_bytes( evals.as_ref().to_vec(), ) @@ -227,6 +238,7 @@ macro_rules! impl_srs { #[napi] pub fn [](srs: &[], chals: Uint8Array) -> Result<$WasmPolyComm> { + println!("Computing b poly commitment with napi"); let elements: Vec<$F> = WasmFlatVector::<$WasmF>::from_bytes( chals.as_ref().to_vec(), ) @@ -244,6 +256,7 @@ macro_rules! impl_srs { comms: WasmVector<$WasmG>, chals: Uint8Array, ) -> Result { + println!("Performing batch accumulator check with napi"); let comms: Vec<$G> = comms.into_iter().map(Into::into).collect(); let chals: Vec<$F> = WasmFlatVector::<$WasmF>::from_bytes( chals.as_ref().to_vec(), @@ -264,6 +277,7 @@ macro_rules! impl_srs { comms: i32, chals: Uint8Array, ) -> Result> { + println!("Generating batch accumulator with napi"); let chals: Vec<$F> = WasmFlatVector::<$WasmF>::from_bytes( chals.as_ref().to_vec(), ) @@ -280,6 +294,7 @@ macro_rules! impl_srs { #[napi] pub fn h(srs: &[]) -> $WasmG { + println!("Getting h point with napi"); srs.h.into() } } From e7aff41879af03bde41c71527318faaab16a98ee Mon Sep 17 00:00:00 2001 From: querolita Date: Fri, 31 Oct 2025 16:06:59 +0100 Subject: [PATCH 11/12] napi: refactor gate vector --- plonk-napi/src/gate_vector.rs | 374 ++++++++++++++++++++++--- plonk-napi/src/lib.rs | 13 +- plonk-napi/src/pasta_fp_plonk_index.rs | 6 +- plonk-napi/src/pasta_fq_plonk_index.rs | 6 +- 4 files changed, 346 insertions(+), 53 deletions(-) diff --git a/plonk-napi/src/gate_vector.rs b/plonk-napi/src/gate_vector.rs index 9c77299502..a6fda3ca12 100644 --- a/plonk-napi/src/gate_vector.rs +++ b/plonk-napi/src/gate_vector.rs @@ -1,12 +1,14 @@ +use ark_ff::PrimeField; use kimchi::circuits::{ gate::{Circuit, CircuitGate, GateType}, - wires::{GateWires, Wire as KimchiWire}, + wires::Wire, }; use mina_curves::pasta::{Fp, Fq}; use napi::bindgen_prelude::*; use napi_derive::napi; use o1_utils::hasher::CryptoDigest; use paste::paste; +use std::ops::Deref; use wasm_types::{FlatVector as WasmFlatVector, FlatVectorElem}; use crate::wrappers::{ @@ -14,8 +16,198 @@ use crate::wrappers::{ wires::NapiWire, }; +pub mod shared { + use super::*; + + /// Number of wires stored per gate. + pub const WIRE_COUNT: usize = 7; + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub struct GateWires(pub [Wire; WIRE_COUNT]); + + impl GateWires { + pub fn new(wires: [Wire; WIRE_COUNT]) -> Self { + Self(wires) + } + + pub fn as_array(&self) -> &[Wire; WIRE_COUNT] { + &self.0 + } + + pub fn into_array(self) -> [Wire; WIRE_COUNT] { + self.0 + } + } + + impl From<[Wire; WIRE_COUNT]> for GateWires { + fn from(wires: [Wire; WIRE_COUNT]) -> Self { + GateWires::new(wires) + } + } + + impl From for [Wire; WIRE_COUNT] { + fn from(gw: GateWires) -> Self { + gw.into_array() + } + } + + #[derive(Clone, Debug)] + pub struct Gate { + pub typ: GateType, + pub wires: GateWires, + pub coeffs: Vec, + } + + impl From> for Gate + where + F: PrimeField, + { + fn from(cg: CircuitGate) -> Self { + Gate { + typ: cg.typ, + wires: GateWires::new([ + cg.wires[0], + cg.wires[1], + cg.wires[2], + cg.wires[3], + cg.wires[4], + cg.wires[5], + cg.wires[6], + ]), + coeffs: cg.coeffs, + } + } + } + + impl From<&CircuitGate> for Gate + where + F: PrimeField, + { + fn from(cg: &CircuitGate) -> Self { + Gate { + typ: cg.typ, + wires: GateWires::new([ + cg.wires[0], + cg.wires[1], + cg.wires[2], + cg.wires[3], + cg.wires[4], + cg.wires[5], + cg.wires[6], + ]), + coeffs: cg.coeffs.clone(), + } + } + } + + impl From> for CircuitGate + where + F: PrimeField, + { + fn from(gate: Gate) -> Self { + CircuitGate { + typ: gate.typ, + wires: gate.wires.into_array(), + coeffs: gate.coeffs, + } + } + } + + #[derive(Clone, Debug, Default)] + pub struct GateVector { + gates: Vec>, + } + + impl GateVector + where + F: PrimeField, + { + pub fn new() -> Self { + Self { gates: Vec::new() } + } + + pub fn from_vec(gates: Vec>) -> Self { + Self { gates } + } + + pub fn into_inner(self) -> Vec> { + self.gates + } + + pub fn as_slice(&self) -> &[CircuitGate] { + &self.gates + } + + pub fn iter(&self) -> core::slice::Iter<'_, CircuitGate> { + self.gates.iter() + } + + pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, CircuitGate> { + self.gates.iter_mut() + } + + pub fn push_gate(&mut self, gate: CircuitGate) { + self.gates.push(gate); + } + + pub fn len(&self) -> usize { + self.gates.len() + } + + pub fn get_gate(&self, index: usize) -> Option> { + self.gates.get(index).map(Gate::from) + } + + pub fn wrap_wire(&mut self, target: Wire, replacement: Wire) { + if let Some(gate) = self.gates.get_mut(target.row) { + if target.col < gate.wires.len() { + gate.wires[target.col] = replacement; + } + } + } + + pub fn digest(&self, public_input_size: usize) -> Vec { + Circuit::new(public_input_size, self.as_slice()) + .digest() + .to_vec() + } + + pub fn serialize( + &self, + public_input_size: usize, + ) -> std::result::Result { + let circuit = Circuit::new(public_input_size, self.as_slice()); + serde_json::to_string(&circuit) + } + } + + impl From>> for GateVector + where + F: PrimeField, + { + fn from(gates: Vec>) -> Self { + GateVector::from_vec(gates) + } + } + + impl From> for Vec> + where + F: PrimeField, + { + fn from(vec: GateVector) -> Self { + vec.into_inner() + } + } +} + +pub use self::shared::{GateVector as CoreGateVector, GateWires as CoreGateWires}; + +fn gate_vector_error(context: &str, err: impl std::fmt::Display) -> Error { + Error::new(Status::GenericFailure, format!("{}: {}", context, err)) +} + #[napi(object)] -#[derive(Clone, Debug, Default)] +#[derive(Clone, Copy, Debug, Default)] pub struct NapiGateWires { pub w0: NapiWire, pub w1: NapiWire, @@ -26,31 +218,38 @@ pub struct NapiGateWires { pub w6: NapiWire, } -impl NapiGateWires { - fn into_inner(self) -> GateWires { - [ - KimchiWire::from(self.w0), - KimchiWire::from(self.w1), - KimchiWire::from(self.w2), - KimchiWire::from(self.w3), - KimchiWire::from(self.w4), - KimchiWire::from(self.w5), - KimchiWire::from(self.w6), - ] +impl From for NapiGateWires { + fn from(wires: CoreGateWires) -> Self { + let array = wires.into_array(); + NapiGateWires { + w0: array[0].into(), + w1: array[1].into(), + w2: array[2].into(), + w3: array[3].into(), + w4: array[4].into(), + w5: array[5].into(), + w6: array[6].into(), + } } } -impl From<&GateWires> for NapiGateWires { - fn from(value: &GateWires) -> Self { - Self { - w0: value[0].into(), - w1: value[1].into(), - w2: value[2].into(), - w3: value[3].into(), - w4: value[4].into(), - w5: value[5].into(), - w6: value[6].into(), - } +impl From for CoreGateWires { + fn from(wires: NapiGateWires) -> Self { + CoreGateWires::new(wires.into_inner()) + } +} + +impl NapiGateWires { + fn into_inner(self) -> [Wire; shared::WIRE_COUNT] { + [ + self.w0.into(), + self.w1.into(), + self.w2.into(), + self.w3.into(), + self.w4.into(), + self.w5.into(), + self.w6.into(), + ] } } @@ -132,23 +331,91 @@ macro_rules! impl_gate_support { .flat_map(|elem| elem.flatten()) .collect(); + let wires = CoreGateWires::new([ + value.wires[0], + value.wires[1], + value.wires[2], + value.wires[3], + value.wires[4], + value.wires[5], + value.wires[6], + ]); + Self { typ: gate_type_to_i32(value.typ), - wires: (&value.wires).into(), + wires: wires.into(), coeffs, } } } #[napi] - #[derive(Clone, Default, Debug)] + #[derive(Clone, Debug, Default)] pub struct []( - #[napi(skip)] pub Vec>, + #[napi(skip)] pub CoreGateVector<$F>, ); + impl Deref for [] { + type Target = CoreGateVector<$F>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl From> for [] { + fn from(inner: CoreGateVector<$F>) -> Self { + Self(inner) + } + } + + impl From<[]> for CoreGateVector<$F> { + fn from(vector: []) -> Self { + vector.0 + } + } + + #[napi] + impl [] { + #[napi(constructor)] + pub fn new() -> Self { + CoreGateVector::new().into() + } + + #[napi(js_name = "serialize")] + pub fn serialize(&self) -> Result { + let bytes = rmp_serde::to_vec(self.0.as_slice()) + .map_err(|e| gate_vector_error("gate vector serialize failed", e))?; + Ok(Uint8Array::from(bytes)) + } + + #[napi(factory, js_name = "deserialize")] + pub fn deserialize(bytes: Uint8Array) -> Result { + let gates: Vec> = rmp_serde::from_slice(bytes.as_ref()) + .map_err(|e| gate_vector_error("gate vector deserialize failed", e))?; + Ok(CoreGateVector::from_vec(gates).into()) + } + + pub(crate) fn inner(&self) -> &CoreGateVector<$F> { + &self.0 + } + + pub(crate) fn inner_mut(&mut self) -> &mut CoreGateVector<$F> { + &mut self.0 + } + + pub(crate) fn as_slice(&self) -> &[CircuitGate<$F>] { + self.0.as_slice() + } + + pub(crate) fn to_vec(&self) -> Vec> { + self.0.as_slice().to_vec() + } + } + #[napi] pub fn []() -> [] { - [](Vec::new()) + []::new() } #[napi] @@ -156,7 +423,8 @@ macro_rules! impl_gate_support { vector: &mut [], gate: [], ) -> Result<()> { - vector.0.push(gate.into_inner()?); + let gate = gate.into_inner()?; + vector.inner_mut().push_gate(gate); Ok(()) } @@ -165,14 +433,18 @@ macro_rules! impl_gate_support { vector: &[], index: i32, ) -> [] { - []::from_inner(&vector.0[index as usize]) + let gate = vector + .as_slice() + .get(index as usize) + .expect("index out of bounds"); + []::from_inner(gate) } #[napi] pub fn []( vector: &[], ) -> i32 { - vector.0.len() as i32 + vector.as_slice().len() as i32 } #[napi] @@ -181,17 +453,17 @@ macro_rules! impl_gate_support { target: NapiWire, head: NapiWire, ) { - vector.0[target.row as usize].wires[target.col as usize] = KimchiWire::from(head); - } + let target: Wire = target.into(); + let head: Wire = head.into(); + vector.inner_mut().wrap_wire(target, head); + } #[napi] pub fn []( public_input_size: i32, vector: &[], ) -> Uint8Array { - let bytes = Circuit::new(public_input_size as usize, &vector.0) - .digest() - .to_vec(); + let bytes = vector.inner().digest(public_input_size as usize); Uint8Array::from(bytes) } @@ -200,13 +472,29 @@ macro_rules! impl_gate_support { public_input_size: i32, vector: &[], ) -> Result { - let circuit = Circuit::new(public_input_size as usize, &vector.0); - serde_json::to_string(&circuit).map_err(|err| { - Error::new( - Status::GenericFailure, - format!("couldn't serialize constraints: {}", err), - ) - }) + vector + .inner() + .serialize(public_input_size as usize) + .map_err(|err| { + Error::new( + Status::GenericFailure, + format!("couldn't serialize constraints: {}", err), + ) + }) + } + + #[napi] + pub fn []( + vector: &[], + ) -> Result { + vector.serialize() + } + + #[napi] + pub fn []( + bytes: Uint8Array, + ) -> Result<[]> { + []::deserialize(bytes) } } }; diff --git a/plonk-napi/src/lib.rs b/plonk-napi/src/lib.rs index 0e17eb8215..811d6b524f 100644 --- a/plonk-napi/src/lib.rs +++ b/plonk-napi/src/lib.rs @@ -13,11 +13,13 @@ pub use circuit::prover_to_json; pub use gate_vector::{ caml_pasta_fp_plonk_circuit_serialize, caml_pasta_fp_plonk_gate_vector_add, caml_pasta_fp_plonk_gate_vector_create, caml_pasta_fp_plonk_gate_vector_digest, - caml_pasta_fp_plonk_gate_vector_get, caml_pasta_fp_plonk_gate_vector_len, + caml_pasta_fp_plonk_gate_vector_from_bytes, caml_pasta_fp_plonk_gate_vector_get, + caml_pasta_fp_plonk_gate_vector_len, caml_pasta_fp_plonk_gate_vector_to_bytes, caml_pasta_fp_plonk_gate_vector_wrap, caml_pasta_fq_plonk_circuit_serialize, caml_pasta_fq_plonk_gate_vector_add, caml_pasta_fq_plonk_gate_vector_create, - caml_pasta_fq_plonk_gate_vector_digest, caml_pasta_fq_plonk_gate_vector_get, - caml_pasta_fq_plonk_gate_vector_len, caml_pasta_fq_plonk_gate_vector_wrap, + caml_pasta_fq_plonk_gate_vector_digest, caml_pasta_fq_plonk_gate_vector_from_bytes, + caml_pasta_fq_plonk_gate_vector_get, caml_pasta_fq_plonk_gate_vector_len, + caml_pasta_fq_plonk_gate_vector_to_bytes, caml_pasta_fq_plonk_gate_vector_wrap, NapiFpGate as WasmFpGate, NapiFpGateVector as WasmFpGateVector, NapiFqGate as WasmFqGate, NapiFqGateVector as WasmFqGateVector, }; @@ -29,7 +31,10 @@ pub use pasta_fq_plonk_index::{ }; pub use poly_comm::{pallas::WasmFqPolyComm, vesta::WasmFpPolyComm}; pub use poseidon::{caml_pasta_fp_poseidon_block_cipher, caml_pasta_fq_poseidon_block_cipher}; -pub use srs::{fp::NapiFpSrs as WasmFpSrs, fq::NapiFqSrs as WasmFqSrs}; +pub use srs::{ + caml_fp_srs_from_bytes, caml_fp_srs_to_bytes, caml_fq_srs_from_bytes, + fp::NapiFpSrs as WasmFpSrs, fq::NapiFqSrs as WasmFqSrs, +}; pub use tables::{JsLookupTableFp, JsLookupTableFq, JsRuntimeTableCfgFp, JsRuntimeTableCfgFq}; pub use wasm_vector::{fp::WasmVecVecFp, fq::WasmVecVecFq}; pub use wrappers::{ diff --git a/plonk-napi/src/pasta_fp_plonk_index.rs b/plonk-napi/src/pasta_fp_plonk_index.rs index 9f3565dae6..6a7165395c 100644 --- a/plonk-napi/src/pasta_fp_plonk_index.rs +++ b/plonk-napi/src/pasta_fp_plonk_index.rs @@ -7,7 +7,7 @@ use mina_curves::pasta::{Fp, Pallas as GAffineOther, Vesta as GAffine, VestaPara use mina_poseidon::{constants::PlonkSpongeConstantsKimchi, sponge::DefaultFqSponge}; use napi::bindgen_prelude::{Error, External, Result as NapiResult, Status, Uint8Array}; use napi_derive::napi; -use plonk_wasm::gate_vector::shared::GateVector; +use crate::gate_vector::NapiFpGateVector; use poly_commitment::ipa::{OpeningProof, SRS as IPA_SRS}; use poly_commitment::SRS; use serde::{Deserialize, Serialize}; @@ -119,7 +119,7 @@ pub fn caml_pasta_fp_plonk_index_domain_d8_size(index: External>, + gates: &NapiFpGateVector, public_: i32, lookup_tables: Vec, runtime_table_cfgs: Vec, @@ -127,7 +127,7 @@ pub fn caml_pasta_fp_plonk_index_create( srs: External, lazy_mode: bool, ) -> Result, Error> { - let gates: Vec<_> = gates.as_ref().as_slice().to_vec(); + let gates: Vec<_> = gates.to_vec(); let runtime_cfgs: Vec> = runtime_table_cfgs .into_iter() diff --git a/plonk-napi/src/pasta_fq_plonk_index.rs b/plonk-napi/src/pasta_fq_plonk_index.rs index 36cfc9df9b..94537da99b 100644 --- a/plonk-napi/src/pasta_fq_plonk_index.rs +++ b/plonk-napi/src/pasta_fq_plonk_index.rs @@ -5,7 +5,7 @@ use mina_curves::pasta::{Fq, Pallas as GAffine, PallasParameters, Vesta as GAffi use mina_poseidon::{constants::PlonkSpongeConstantsKimchi, sponge::DefaultFqSponge}; use napi::bindgen_prelude::{Error, External, Result as NapiResult, Status, Uint8Array}; use napi_derive::napi; -use plonk_wasm::gate_vector::shared::GateVector; +use crate::gate_vector::NapiFqGateVector; use poly_commitment::ipa::{OpeningProof, SRS as IPA_SRS}; use poly_commitment::SRS; use serde::{Deserialize, Serialize}; @@ -113,7 +113,7 @@ pub fn caml_pasta_fq_plonk_index_domain_d8_size(index: External>, + gates: &NapiFqGateVector, public_: i32, lookup_tables: Vec, runtime_table_cfgs: Vec, @@ -123,7 +123,7 @@ pub fn caml_pasta_fq_plonk_index_create( ) -> Result, Error> { // TODO: check if and how we run rayon threads automatically in napi - let gates: Vec<_> = gates.as_ref().as_slice().to_vec(); + let gates: Vec<_> = gates.to_vec(); let runtime_cfgs = runtime_table_cfgs .into_iter() From bf6ae6f6731ed788a58ff0ba8eda134fda43a343 Mon Sep 17 00:00:00 2001 From: querolita Date: Fri, 31 Oct 2025 16:09:12 +0100 Subject: [PATCH 12/12] napi: serde napi srs type --- plonk-napi/src/srs.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/plonk-napi/src/srs.rs b/plonk-napi/src/srs.rs index 0242a10def..4f84a8920a 100644 --- a/plonk-napi/src/srs.rs +++ b/plonk-napi/src/srs.rs @@ -217,6 +217,16 @@ macro_rules! impl_srs { Ok(basis.iter().cloned().map(Into::into).collect()) } + #[napi] + pub fn [](srs: &[]) -> Result { + srs.serialize() + } + + #[napi] + pub fn [](bytes: Uint8Array) -> Result { + Self::deserialize(bytes) + } + #[napi] pub fn [](srs: &[], domain_size: i32, @@ -302,6 +312,26 @@ macro_rules! impl_srs { } } +#[napi] +pub fn caml_fp_srs_to_bytes(srs: &fp::NapiFpSrs) -> Result { + srs.serialize() +} + +#[napi] +pub fn caml_fp_srs_from_bytes(bytes: Uint8Array) -> Result { + fp::NapiFpSrs::deserialize(bytes) +} + +#[napi] +pub fn caml_fq_srs_to_bytes(srs: &fq::NapiFqSrs) -> Result { + srs.serialize() +} + +#[napi] +pub fn caml_fq_srs_from_bytes(bytes: Uint8Array) -> Result { + fq::NapiFqSrs::deserialize(bytes) +} + pub mod fp { use super::*; use crate::{