1use crate::{gate_vector::NapiFqGateVector, WasmFqSrs};
2use ark_poly::EvaluationDomain;
3use kimchi::{
4 circuits::constraints::ConstraintSystem, linearization::expr_linearization,
5 prover_index::ProverIndex,
6};
7use mina_curves::pasta::{Fq, Pallas as GAffine, PallasParameters, Vesta as GAffineOther};
8use mina_poseidon::{
9 constants::PlonkSpongeConstantsKimchi, pasta::FULL_ROUNDS, sponge::DefaultFqSponge,
10};
11use napi::bindgen_prelude::{Error, External, Status, Uint8Array};
12use napi_derive::napi;
13use poly_commitment::{
14 ipa::{OpeningProof, SRS as IPA_SRS},
15 OpenProof, SRS,
16};
17use serde::{Deserialize, Serialize};
18use std::{
19 fs::{File, OpenOptions},
20 io::{BufReader, BufWriter, Cursor, Seek, SeekFrom::Start},
21 sync::Arc,
22};
23
24use crate::tables::{
25 lookup_table_fq_from_js, runtime_table_cfg_fq_from_js, JsLookupTableFq, JsRuntimeTableCfgFq,
26};
27
28type ProverIndexType = ProverIndex<
29 FULL_ROUNDS,
30 GAffine,
31 <OpeningProof<GAffine, FULL_ROUNDS> as OpenProof<GAffine, FULL_ROUNDS>>::SRS,
32>;
33#[napi(js_name = "WasmPastaFqPlonkIndex")]
34pub struct WasmPastaFqPlonkIndex(#[napi(skip)] pub Box<ProverIndexType>);
35
36#[derive(Serialize, Deserialize)]
37struct SerializedProverIndex {
38 prover_index: Vec<u8>,
39 srs: Vec<u8>,
40}
41
42impl WasmPastaFqPlonkIndex {
43 fn serialize_inner(&self) -> Result<Vec<u8>, String> {
44 let prover_index = rmp_serde::to_vec(self.0.as_ref()).map_err(|e| e.to_string())?;
45
46 let mut srs = Vec::new();
47 self.0
48 .srs
49 .serialize(&mut rmp_serde::Serializer::new(&mut srs))
50 .map_err(|e| e.to_string())?;
51
52 let serialized = SerializedProverIndex { prover_index, srs };
53
54 rmp_serde::to_vec(&serialized).map_err(|e| e.to_string())
55 }
56
57 fn deserialize_inner(bytes: &[u8]) -> Result<Self, String> {
58 let serialized: SerializedProverIndex =
59 rmp_serde::from_slice(bytes).map_err(|e| e.to_string())?;
60
61 let mut index: ProverIndexType = ProverIndex::deserialize(
62 &mut rmp_serde::Deserializer::new(Cursor::new(serialized.prover_index)),
63 )
64 .map_err(|e| e.to_string())?;
65
66 let srs = IPA_SRS::<GAffine>::deserialize(&mut rmp_serde::Deserializer::new(Cursor::new(
67 serialized.srs,
68 )))
69 .map_err(|e| e.to_string())?;
70
71 index.srs = Arc::new(srs);
72
73 let (linearization, powers_of_alpha) =
74 expr_linearization(Some(&index.cs.feature_flags), true);
75 index.linearization = linearization;
76 index.powers_of_alpha = powers_of_alpha;
77
78 index.compute_verifier_index_digest::<
79 DefaultFqSponge<PallasParameters, PlonkSpongeConstantsKimchi, FULL_ROUNDS>,
80 >();
81
82 Ok(WasmPastaFqPlonkIndex(Box::new(index)))
83 }
84}
85
86#[napi(js_name = "prover_index_fq_serialize")]
88pub fn prover_index_fq_serialize(
89 index: &External<WasmPastaFqPlonkIndex>,
90) -> napi::bindgen_prelude::Result<Uint8Array> {
91 let bytes = index
92 .serialize_inner()
93 .map_err(|e| Error::new(Status::GenericFailure, e))?;
94 Ok(Uint8Array::from(bytes))
95}
96
97#[napi(js_name = "prover_index_fq_deserialize")]
99pub fn prover_index_fq_deserialize(
100 bytes: Uint8Array,
101) -> napi::bindgen_prelude::Result<External<WasmPastaFqPlonkIndex>> {
102 let index = WasmPastaFqPlonkIndex::deserialize_inner(bytes.as_ref())
103 .map_err(|e| Error::new(Status::InvalidArg, e))?;
104 Ok(External::new(index))
105}
106
107#[napi(js_name = "caml_pasta_fq_plonk_index_max_degree")]
108pub fn caml_pasta_fq_plonk_index_max_degree(index: &External<WasmPastaFqPlonkIndex>) -> i32 {
109 index.0.srs.max_poly_size() as i32
110}
111
112#[napi(js_name = "caml_pasta_fq_plonk_index_public_inputs")]
113pub fn caml_pasta_fq_plonk_index_public_inputs(index: &External<WasmPastaFqPlonkIndex>) -> i32 {
114 index.0.cs.public as i32
115}
116
117#[napi(js_name = "caml_pasta_fq_plonk_index_domain_d1_size")]
118pub fn caml_pasta_fq_plonk_index_domain_d1_size(index: &External<WasmPastaFqPlonkIndex>) -> i32 {
119 index.0.cs.domain.d1.size() as i32
120}
121
122#[napi(js_name = "caml_pasta_fq_plonk_index_domain_d4_size")]
123pub fn caml_pasta_fq_plonk_index_domain_d4_size(index: &External<WasmPastaFqPlonkIndex>) -> i32 {
124 index.0.cs.domain.d4.size() as i32
125}
126
127#[napi(js_name = "caml_pasta_fq_plonk_index_domain_d8_size")]
128pub fn caml_pasta_fq_plonk_index_domain_d8_size(index: &External<WasmPastaFqPlonkIndex>) -> i32 {
129 index.0.cs.domain.d8.size() as i32
130}
131
132#[napi(js_name = "caml_pasta_fq_plonk_index_create")]
133pub fn caml_pasta_fq_plonk_index_create(
134 gates: &NapiFqGateVector,
135 public_: i32,
136 lookup_tables: Vec<JsLookupTableFq>,
137 runtime_table_cfgs: Vec<JsRuntimeTableCfgFq>,
138 prev_challenges: i32,
139 srs: &WasmFqSrs,
140 lazy_mode: bool,
141) -> Result<External<WasmPastaFqPlonkIndex>, Error> {
142 let gates: Vec<_> = gates.to_vec();
145
146 let runtime_cfgs = runtime_table_cfgs
147 .into_iter()
148 .map(runtime_table_cfg_fq_from_js)
149 .collect::<Result<Vec<_>, _>>()?;
150
151 let lookup_tables = lookup_tables
152 .into_iter()
153 .map(lookup_table_fq_from_js)
154 .collect::<Result<Vec<_>, _>>()?;
155
156 let cs = ConstraintSystem::<Fq>::create(gates)
157 .public(public_ as usize)
158 .prev_challenges(prev_challenges as usize)
159 .lookup(lookup_tables)
160 .max_poly_size(Some(srs.0.max_poly_size()))
161 .runtime(if runtime_cfgs.is_empty() {
162 None
163 } else {
164 Some(runtime_cfgs)
165 })
166 .lazy_mode(lazy_mode)
167 .build()
168 .map_err(|_| {
169 Error::new(
170 Status::InvalidArg,
171 "caml_pasta_fq_plonk_index_create: could not create constraint system",
172 )
173 })?;
174
175 let (endo_q, _endo_r) = poly_commitment::ipa::endos::<GAffineOther>();
176
177 srs.0.get_lagrange_basis(cs.domain.d1);
178
179 let mut index = ProverIndex::<FULL_ROUNDS, GAffine, IPA_SRS<GAffine>>::create(
180 cs,
181 endo_q,
182 srs.0.clone(),
183 lazy_mode,
184 );
185 index.compute_verifier_index_digest::<DefaultFqSponge<PallasParameters, PlonkSpongeConstantsKimchi, FULL_ROUNDS>>();
186
187 Ok(External::new(WasmPastaFqPlonkIndex(Box::new(index))))
188}
189
190#[napi(js_name = "caml_pasta_fq_plonk_index_decode")]
191pub fn caml_pasta_fq_plonk_index_decode(
192 bytes: &[u8],
193 srs: &WasmFqSrs,
194) -> Result<External<WasmPastaFqPlonkIndex>, Error> {
195 let mut deserializer = rmp_serde::Deserializer::new(bytes);
196 let mut index =
197 ProverIndex::<FULL_ROUNDS, GAffine, IPA_SRS<GAffine>>::deserialize(&mut deserializer)
198 .map_err(|e| {
199 Error::new(
200 Status::InvalidArg,
201 format!("caml_pasta_fq_plonk_index_decode: {}", e),
202 )
203 })?;
204 index.srs = srs.0.clone();
205 let (linearization, powers_of_alpha) = expr_linearization(Some(&index.cs.feature_flags), true);
206 index.linearization = linearization;
207 index.powers_of_alpha = powers_of_alpha;
208
209 Ok(External::new(WasmPastaFqPlonkIndex(Box::new(index))))
210}
211
212#[napi(js_name = "caml_pasta_fq_plonk_index_encode")]
213pub fn caml_pasta_fq_plonk_index_encode(
214 index: &External<WasmPastaFqPlonkIndex>,
215) -> Result<Uint8Array, Error> {
216 let mut buffer = Vec::new();
217 let mut serializer = rmp_serde::Serializer::new(&mut buffer);
218 index.0.serialize(&mut serializer).map_err(|e| {
219 Error::new(
220 Status::InvalidArg,
221 format!("caml_pasta_fq_plonk_index_encode: {}", e),
222 )
223 })?;
224 Ok(Uint8Array::from(buffer))
225}
226
227#[napi(js_name = "caml_pasta_fq_plonk_index_write")]
228pub fn caml_pasta_fq_plonk_index_write(
229 append: Option<bool>,
230 index: &External<WasmPastaFqPlonkIndex>,
231 path: String,
232) -> Result<(), Error> {
233 let file = OpenOptions::new()
234 .append(append.unwrap_or(true))
235 .open(path)
236 .map_err(|_| Error::new(Status::InvalidArg, "caml_pasta_fq_plonk_index_write"))?;
237 let w = BufWriter::new(file);
238 index
239 .0
240 .serialize(&mut rmp_serde::Serializer::new(w))
241 .map_err(|e| {
242 Error::new(
243 Status::InvalidArg,
244 format!("caml_pasta_fq_plonk_index_write: {e}"),
245 )
246 })
247}
248
249#[napi(js_name = "caml_pasta_fq_plonk_index_read")]
250pub fn caml_pasta_fq_plonk_index_read(
251 offset: Option<i32>,
252 srs: &WasmFqSrs,
253 path: String,
254) -> Result<External<WasmPastaFqPlonkIndex>, Error> {
255 let file = match File::open(path) {
257 Err(_) => {
258 return Err(Error::new(
259 Status::InvalidArg,
260 "caml_pasta_fq_plonk_index_read",
261 ))
262 }
263 Ok(file) => file,
264 };
265 let mut r = BufReader::new(file);
266
267 if let Some(offset) = offset {
269 r.seek(Start(offset as u64)).map_err(|err| {
270 Error::new(
271 Status::InvalidArg,
272 format!("caml_pasta_fq_plonk_index_read: {err}"),
273 )
274 })?;
275 }
276
277 let mut t = ProverIndex::<FULL_ROUNDS, GAffine, IPA_SRS<GAffine>>::deserialize(
279 &mut rmp_serde::Deserializer::new(r),
280 )
281 .map_err(|err| {
282 Error::new(
283 Status::InvalidArg,
284 format!("caml_pasta_fq_plonk_index_read: {err}"),
285 )
286 })?;
287 t.srs = srs.0.clone();
288 let (linearization, powers_of_alpha) = expr_linearization(Some(&t.cs.feature_flags), true);
289 t.linearization = linearization;
290 t.powers_of_alpha = powers_of_alpha;
291
292 Ok(External::new(WasmPastaFqPlonkIndex(Box::new(t))))
294}