Skip to main content

kimchi_napi/
proof.rs

1use crate::vector::{fp::NapiVecVecFp, fq::NapiVecVecFq, NapiFlatVector, NapiVector};
2use ark_ec::AffineRepr;
3use ark_ff::One;
4use core::array;
5use kimchi::{
6    circuits::{
7        lookup::runtime_tables::RuntimeTable,
8        wires::{COLUMNS, PERMUTS},
9    },
10    groupmap::GroupMap,
11    proof::{
12        LookupCommitments, PointEvaluations, ProofEvaluations, ProverCommitments, ProverProof,
13        RecursionChallenge,
14    },
15    prover_index::ProverIndex,
16    verifier::{batch_verify, Context},
17};
18use mina_poseidon::{
19    constants::PlonkSpongeConstantsKimchi,
20    pasta::FULL_ROUNDS,
21    sponge::{DefaultFqSponge, DefaultFrSponge},
22};
23use napi::{
24    bindgen_prelude::{sys, ClassInstance, External, FromNapiValue, Result},
25    Error as NapiError, Status,
26};
27use napi_derive::napi;
28use paste::paste;
29use poly_commitment::commitment::CommitmentCurve; // Import CommitmentCurve trait
30use poly_commitment::{
31    ipa::{OpeningProof, SRS as IPA_SRS},
32    PolyComm, SRS as _,
33};
34
35macro_rules! impl_proof {
36    (
37     $NapiG: ty,
38     $G: ty,
39     $NapiF: ty,
40     $F: ty,
41     $NapiPolyComm: ty,
42     $NapiSrs: ty,
43     $NapiIndex: ty,
44     $NapiVerifierIndex: ty,
45     $NapiVecVec: ty,
46     $field_name: ident,
47     $NapiRuntimeTable: ty,
48     ) => {
49        paste! {
50            // type NapiVecVecF = [<NapiVecVec $field_name:camel>];
51
52            #[napi(object, js_name = [<Wasm $field_name:camel PointEvaluations>])]
53            #[derive(Clone)]
54            pub struct [<Napi $field_name:camel PointEvaluations>] {
55                pub zeta: NapiVector<$NapiF>,
56                pub zeta_omega: NapiVector<$NapiF>,
57            }
58
59            type NapiPointEvaluations = [<Napi $field_name:camel PointEvaluations>];
60
61            impl From<&PointEvaluations<Vec<$F>>> for NapiPointEvaluations {
62                fn from(x: &PointEvaluations<Vec<$F>>) -> Self {
63                    let zeta: Vec<$NapiF> = x.zeta.iter().cloned().map(Into::into).collect();
64                    let zeta_omega: Vec<$NapiF> = x
65                        .zeta_omega
66                        .iter()
67                        .cloned()
68                        .map(Into::into)
69                        .collect();
70                    Self {
71                        zeta: zeta.into(),
72                        zeta_omega: zeta_omega.into(),
73                    }
74                }
75            }
76
77            #[napi(object, js_name = [<Wasm $field_name:camel ProofEvaluationsObject>])]
78            #[derive(Clone)]
79            pub struct [<Napi $field_name:camel ProofEvaluationsObject>] {
80                pub public: Option<NapiPointEvaluations>,
81                pub w: NapiVector<NapiPointEvaluations>,
82                pub z: NapiPointEvaluations,
83                pub s: NapiVector<NapiPointEvaluations>,
84                pub coefficients: NapiVector<NapiPointEvaluations>,
85                pub generic_selector: NapiPointEvaluations,
86                pub poseidon_selector: NapiPointEvaluations,
87                pub complete_add_selector: NapiPointEvaluations,
88                pub mul_selector: NapiPointEvaluations,
89                pub emul_selector: NapiPointEvaluations,
90                pub endomul_scalar_selector: NapiPointEvaluations,
91                pub range_check0_selector: Option<NapiPointEvaluations>,
92                pub range_check1_selector: Option<NapiPointEvaluations>,
93                pub foreign_field_add_selector: Option<NapiPointEvaluations>,
94                pub foreign_field_mul_selector: Option<NapiPointEvaluations>,
95                pub xor_selector: Option<NapiPointEvaluations>,
96                pub rot_selector: Option<NapiPointEvaluations>,
97                pub lookup_aggregation: Option<NapiPointEvaluations>,
98                pub lookup_table: Option<NapiPointEvaluations>,
99                pub lookup_sorted: NapiVector<Option<NapiPointEvaluations>>,
100                pub runtime_lookup_table: Option<NapiPointEvaluations>,
101                pub runtime_lookup_table_selector: Option<NapiPointEvaluations>,
102                pub xor_lookup_selector: Option<NapiPointEvaluations>,
103                pub lookup_gate_lookup_selector: Option<NapiPointEvaluations>,
104                pub range_check_lookup_selector: Option<NapiPointEvaluations>,
105                pub foreign_field_mul_lookup_selector: Option<NapiPointEvaluations>,
106            }
107
108            // Use the object representation as the JS-exposed type.
109            pub type [<Napi $field_name:camel ProofEvaluations>] = [<Napi $field_name:camel ProofEvaluationsObject>];
110            type NapiProofEvaluations = [<Napi $field_name:camel ProofEvaluations>];
111
112            // Field-specific helpers to avoid name clashes between fp/fq instantiations.
113            fn [<point_evals_from_napi_ $field_name:snake>](
114                evals: NapiPointEvaluations,
115            ) -> PointEvaluations<Vec<$F>> {
116                PointEvaluations {
117                    zeta: evals.zeta.into_iter().map(Into::into).collect(),
118                    zeta_omega: evals.zeta_omega.into_iter().map(Into::into).collect(),
119                }
120            }
121
122            fn [<point_evals_into_napi_ $field_name:snake>](
123                evals: &PointEvaluations<Vec<$F>>,
124            ) -> NapiPointEvaluations {
125                evals.into()
126            }
127
128            fn [<proof_evals_from_napi_object_ $field_name:snake>](
129                x: NapiProofEvaluations,
130            ) -> std::result::Result<ProofEvaluations<PointEvaluations<Vec<$F>>>, NapiError> {
131                fn invalid_len(name: &str, expected: usize, got: usize) -> NapiError {
132                    NapiError::new(
133                        Status::InvalidArg,
134                        format!("{name}: expected length {expected}, got {got}"),
135                    )
136                }
137
138                let w_vec: Vec<_> = x
139                    .w
140                    .into_iter()
141                    .map([<point_evals_from_napi_ $field_name:snake>])
142                    .collect();
143                let w_len = w_vec.len();
144                let w: [PointEvaluations<Vec<$F>>; COLUMNS] = w_vec
145                    .try_into()
146                    .map_err(|_| invalid_len("evals.w", COLUMNS, w_len))?;
147
148                let s_expected = PERMUTS - 1;
149                let s_vec: Vec<_> = x
150                    .s
151                    .into_iter()
152                    .map([<point_evals_from_napi_ $field_name:snake>])
153                    .collect();
154                let s_len = s_vec.len();
155                let s: [PointEvaluations<Vec<$F>>; PERMUTS - 1] = s_vec
156                    .try_into()
157                    .map_err(|_| invalid_len("evals.s", s_expected, s_len))?;
158
159                let coeffs_vec: Vec<_> = x
160                    .coefficients
161                    .into_iter()
162                    .map([<point_evals_from_napi_ $field_name:snake>])
163                    .collect();
164                let coeffs_len = coeffs_vec.len();
165                let coefficients: [PointEvaluations<Vec<$F>>; COLUMNS] = coeffs_vec
166                    .try_into()
167                    .map_err(|_| invalid_len("evals.coefficients", COLUMNS, coeffs_len))?;
168
169                let lookup_sorted_vec: Vec<_> = x
170                    .lookup_sorted
171                    .into_iter()
172                    .map(|v| v.map([<point_evals_from_napi_ $field_name:snake>]))
173                    .collect();
174                let lookup_sorted_len = lookup_sorted_vec.len();
175                let lookup_sorted: [Option<PointEvaluations<Vec<$F>>>; 5] = lookup_sorted_vec
176                    .try_into()
177                    .map_err(|_| invalid_len("evals.lookup_sorted", 5, lookup_sorted_len))?;
178
179                Ok(ProofEvaluations {
180                    public: x.public.map([<point_evals_from_napi_ $field_name:snake>]),
181                    w,
182                    z: [<point_evals_from_napi_ $field_name:snake>](x.z),
183                    s,
184                    coefficients,
185                    generic_selector: [<point_evals_from_napi_ $field_name:snake>](x.generic_selector),
186                    poseidon_selector: [<point_evals_from_napi_ $field_name:snake>](x.poseidon_selector),
187                    complete_add_selector: [<point_evals_from_napi_ $field_name:snake>](x.complete_add_selector),
188                    mul_selector: [<point_evals_from_napi_ $field_name:snake>](x.mul_selector),
189                    emul_selector: [<point_evals_from_napi_ $field_name:snake>](x.emul_selector),
190                    endomul_scalar_selector: [<point_evals_from_napi_ $field_name:snake>](x.endomul_scalar_selector),
191                    range_check0_selector: x.range_check0_selector.map([<point_evals_from_napi_ $field_name:snake>]),
192                    range_check1_selector: x.range_check1_selector.map([<point_evals_from_napi_ $field_name:snake>]),
193                    foreign_field_add_selector: x.foreign_field_add_selector.map([<point_evals_from_napi_ $field_name:snake>]),
194                    foreign_field_mul_selector: x.foreign_field_mul_selector.map([<point_evals_from_napi_ $field_name:snake>]),
195                    xor_selector: x.xor_selector.map([<point_evals_from_napi_ $field_name:snake>]),
196                    rot_selector: x.rot_selector.map([<point_evals_from_napi_ $field_name:snake>]),
197                    lookup_aggregation: x.lookup_aggregation.map([<point_evals_from_napi_ $field_name:snake>]),
198                    lookup_table: x.lookup_table.map([<point_evals_from_napi_ $field_name:snake>]),
199                    lookup_sorted,
200                    runtime_lookup_table: x.runtime_lookup_table.map([<point_evals_from_napi_ $field_name:snake>]),
201                    runtime_lookup_table_selector: x.runtime_lookup_table_selector.map([<point_evals_from_napi_ $field_name:snake>]),
202                    xor_lookup_selector: x.xor_lookup_selector.map([<point_evals_from_napi_ $field_name:snake>]),
203                    lookup_gate_lookup_selector: x.lookup_gate_lookup_selector.map([<point_evals_from_napi_ $field_name:snake>]),
204                    range_check_lookup_selector: x.range_check_lookup_selector.map([<point_evals_from_napi_ $field_name:snake>]),
205                    foreign_field_mul_lookup_selector: x.foreign_field_mul_lookup_selector.map([<point_evals_from_napi_ $field_name:snake>]),
206                })
207            }
208
209            impl From<NapiProofEvaluations> for ProofEvaluations<PointEvaluations<Vec<$F>>> {
210                fn from(x: NapiProofEvaluations) -> Self {
211                    [<proof_evals_from_napi_object_ $field_name:snake>](x)
212                        .expect("invalid proof evaluations shape")
213                }
214            }
215
216            impl From<ProofEvaluations<PointEvaluations<Vec<$F>>>> for NapiProofEvaluations {
217                fn from(x: ProofEvaluations<PointEvaluations<Vec<$F>>>) -> Self {
218                    [<Napi $field_name:camel ProofEvaluationsObject>] {
219                        public: x.public.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
220                        w: x.w.iter().map([<point_evals_into_napi_ $field_name:snake>]).collect(),
221                        z: (&x.z).into(),
222                        s: x.s.iter().map([<point_evals_into_napi_ $field_name:snake>]).collect(),
223                        coefficients: x.coefficients.iter().map([<point_evals_into_napi_ $field_name:snake>]).collect(),
224                        generic_selector: (&x.generic_selector).into(),
225                        poseidon_selector: (&x.poseidon_selector).into(),
226                        complete_add_selector: (&x.complete_add_selector).into(),
227                        mul_selector: (&x.mul_selector).into(),
228                        emul_selector: (&x.emul_selector).into(),
229                        endomul_scalar_selector: (&x.endomul_scalar_selector).into(),
230                        range_check0_selector: x.range_check0_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
231                        range_check1_selector: x.range_check1_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
232                        foreign_field_add_selector: x.foreign_field_add_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
233                        foreign_field_mul_selector: x.foreign_field_mul_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
234                        xor_selector: x.xor_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
235                        rot_selector: x.rot_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
236                        lookup_aggregation: x.lookup_aggregation.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
237                        lookup_table: x.lookup_table.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
238                        lookup_sorted: x
239                            .lookup_sorted
240                            .iter()
241                            .map(|v| v.as_ref().map([<point_evals_into_napi_ $field_name:snake>]))
242                            .collect(),
243                        runtime_lookup_table: x.runtime_lookup_table.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
244                        runtime_lookup_table_selector: x.runtime_lookup_table_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
245                        xor_lookup_selector: x.xor_lookup_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
246                        lookup_gate_lookup_selector: x.lookup_gate_lookup_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
247                        range_check_lookup_selector: x.range_check_lookup_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
248                        foreign_field_mul_lookup_selector: x.foreign_field_mul_lookup_selector.as_ref().map([<point_evals_into_napi_ $field_name:snake>]),
249                    }
250                }
251            }
252
253            #[napi(js_name = [<Wasm $field_name:camel LookupCommitments>])]
254            #[derive(Clone)]
255            pub struct [<Napi $field_name:camel LookupCommitments>]
256            {
257                #[napi(skip)]
258                pub sorted: NapiVector<$NapiPolyComm>,
259                #[napi(skip)]
260                pub aggreg: $NapiPolyComm,
261                #[napi(skip)]
262                pub runtime: Option<$NapiPolyComm>,
263            }
264
265            type NapiLookupCommitments = [<Napi $field_name:camel LookupCommitments>];
266
267            #[napi]
268            impl [<Napi $field_name:camel LookupCommitments>] {
269                #[napi(constructor)]
270                pub fn new(
271                    sorted: NapiVector<$NapiPolyComm>,
272                    aggreg: $NapiPolyComm,
273                    runtime: Option<$NapiPolyComm>) -> Self {
274                    NapiLookupCommitments { sorted, aggreg, runtime }
275                }
276
277                #[napi(getter)]
278                pub fn sorted(&self) -> NapiVector<$NapiPolyComm> {
279                    self.sorted.clone()
280                }
281
282                #[napi(getter)]
283                pub fn aggreg(&self) -> $NapiPolyComm {
284                    self.aggreg.clone()
285                }
286
287                #[napi(getter)]
288                pub fn runtime(&self) -> Option<$NapiPolyComm> {
289                    self.runtime.clone()
290                }
291
292                #[napi(setter, js_name="set_sorted")]
293                pub fn set_sorted(&mut self, s: NapiVector<$NapiPolyComm>) {
294                    self.sorted = s
295                }
296
297                #[napi(setter, js_name="set_aggreg")]
298                pub fn set_aggreg(&mut self, a: $NapiPolyComm) {
299                    self.aggreg = a
300                }
301
302                #[napi(setter, js_name="set_runtime")]
303                pub fn set_runtime(&mut self, r: Option<$NapiPolyComm>) {
304                    self.runtime = r
305                }
306            }
307
308            impl From<LookupCommitments<$G>> for NapiLookupCommitments {
309                fn from(x: LookupCommitments<$G>) -> Self {
310                    NapiLookupCommitments {
311                        sorted: x.sorted.into_iter().map(Into::into).collect(),
312                        aggreg: x.aggreg.into(),
313                        runtime: x.runtime.map(Into::into)
314                    }
315                }
316            }
317
318            impl From<NapiLookupCommitments> for LookupCommitments<$G> {
319                fn from(x: NapiLookupCommitments) -> Self {
320                    LookupCommitments {
321                        sorted: x.sorted.into_iter().map(Into::into).collect(),
322                        aggreg: x.aggreg.into(),
323                        runtime: x.runtime.map(Into::into)
324                    }
325                }
326            }
327
328            impl FromNapiValue for [<Napi $field_name:camel LookupCommitments>] {
329                unsafe fn from_napi_value(
330                    env: sys::napi_env,
331                    napi_val: sys::napi_value,
332                ) -> Result<Self> {
333                    let instance = <ClassInstance<[<Napi $field_name:camel LookupCommitments>]> as FromNapiValue>::from_napi_value(env, napi_val)?;
334                    Ok((*instance).clone())
335                }
336            }
337
338            #[napi(js_name = [<Wasm $field_name:camel ProverCommitments>])]
339            #[derive(Clone)]
340            pub struct [<Napi $field_name:camel ProverCommitments>]
341            {
342                #[napi(skip)]
343                pub w_comm: NapiVector<$NapiPolyComm>,
344                #[napi(skip)]
345                pub z_comm: $NapiPolyComm,
346                #[napi(skip)]
347                pub t_comm: $NapiPolyComm,
348                #[napi(skip)]
349                pub lookup: Option<NapiLookupCommitments>,
350            }
351
352            type NapiProverCommitments = [<Napi $field_name:camel ProverCommitments>];
353
354            #[napi]
355            impl [<Napi $field_name:camel ProverCommitments>] {
356                #[napi(constructor)]
357                pub fn new(
358                    w_comm: NapiVector<$NapiPolyComm>,
359                    z_comm: $NapiPolyComm,
360                    t_comm: $NapiPolyComm,
361                    lookup: Option<NapiLookupCommitments>
362                ) -> Self {
363                    NapiProverCommitments { w_comm, z_comm, t_comm, lookup }
364                }
365
366                #[napi(getter, js_name="w_comm")]
367                pub fn w_comm(&self) -> NapiVector<$NapiPolyComm> {
368                    self.w_comm.clone()
369                }
370                #[napi(getter, js_name="z_comm")]
371                pub fn z_comm(&self) -> $NapiPolyComm {
372                    self.z_comm.clone()
373                }
374                #[napi(getter, js_name="t_comm")]
375                pub fn t_comm(&self) -> $NapiPolyComm {
376                    self.t_comm.clone()
377                }
378
379                #[napi(getter)]
380                pub fn lookup(&self) -> Option<NapiLookupCommitments> {
381                    self.lookup.clone()
382                }
383
384                #[napi(setter, js_name="set_w_comm")]
385                pub fn set_w_comm(&mut self, x: NapiVector<$NapiPolyComm>) {
386                    self.w_comm = x
387                }
388                #[napi(setter, js_name="set_z_comm")]
389                pub fn set_z_comm(&mut self, x: $NapiPolyComm) {
390                    self.z_comm = x
391                }
392                #[napi(setter, js_name="set_t_comm")]
393                pub fn set_t_comm(&mut self, x: $NapiPolyComm) {
394                    self.t_comm = x
395                }
396
397                #[napi(setter, js_name="set_lookup")]
398                pub fn set_lookup(&mut self, l: Option<NapiLookupCommitments>) {
399                    self.lookup = l
400                }
401            }
402
403            impl From<ProverCommitments<$G>> for NapiProverCommitments {
404                fn from(x: ProverCommitments<$G>) -> Self {
405                    NapiProverCommitments {
406                        w_comm: x.w_comm.iter().map(Into::into).collect(),
407                        z_comm: x.z_comm.into(),
408                        t_comm: x.t_comm.into(),
409                        lookup: x.lookup.map(Into::into),
410                    }
411                }
412            }
413
414            impl From<NapiProverCommitments> for ProverCommitments<$G> {
415                fn from(x: NapiProverCommitments) -> Self {
416                    ProverCommitments {
417                        w_comm: core::array::from_fn(|i| (&x.w_comm[i]).into()),
418                        z_comm: x.z_comm.into(),
419                        t_comm: x.t_comm.into(),
420                        lookup: x.lookup.map(Into::into),
421                    }
422                }
423            }
424
425            impl FromNapiValue for [<Napi $field_name:camel ProverCommitments>] {
426                unsafe fn from_napi_value(
427                    env: sys::napi_env,
428                    napi_val: sys::napi_value,
429                ) -> Result<Self> {
430                    let instance = <ClassInstance<[<Napi $field_name:camel ProverCommitments>]> as FromNapiValue>::from_napi_value(env, napi_val)?;
431                    Ok((*instance).clone())
432                }
433            }
434
435            #[napi(js_name = [<Wasm $field_name:camel OpeningProof>] )]
436            #[derive(Clone, Debug)]
437            pub struct [<Napi $field_name:camel OpeningProof>] {
438                #[napi(skip)]
439                pub lr_0: NapiVector<$NapiG>, // vector of rounds of L commitments
440                #[napi(skip)]
441                pub lr_1: NapiVector<$NapiG>, // vector of rounds of R commitments
442                #[napi(skip)]
443                pub delta: $NapiG,
444                pub z1: $NapiF,
445                pub z2: $NapiF,
446                #[napi(skip)]
447                pub sg: $NapiG,
448            }
449
450            type NapiOpeningProof = [<Napi $field_name:camel OpeningProof>];
451
452            #[napi]
453            impl [<Napi $field_name:camel OpeningProof>] {
454                #[napi(constructor)]
455                pub fn new(
456                    lr_0: NapiVector<$NapiG>,
457                    lr_1: NapiVector<$NapiG>,
458                    delta: $NapiG,
459                    z1: $NapiF,
460                    z2: $NapiF,
461                    sg: $NapiG) -> Self {
462                    NapiOpeningProof { lr_0, lr_1, delta, z1, z2, sg }
463                }
464
465                #[napi(getter, js_name="lr_0")]
466                pub fn lr_0(&self) -> NapiVector<$NapiG> {
467                    self.lr_0.clone()
468                }
469                #[napi(getter, js_name="lr_1")]
470                pub fn lr_1(&self) -> NapiVector<$NapiG> {
471                    self.lr_1.clone()
472                }
473                #[napi(getter)]
474                pub fn delta(&self) -> $NapiG {
475                    self.delta.clone()
476                }
477                #[napi(getter)]
478                pub fn sg(&self) -> $NapiG {
479                    self.sg.clone()
480                }
481
482                #[napi(setter, js_name="set_lr_0")]
483                pub fn set_lr_0(&mut self, lr_0: NapiVector<$NapiG>) {
484                    self.lr_0 = lr_0
485                }
486                #[napi(setter, js_name="set_lr_1")]
487                pub fn set_lr_1(&mut self, lr_1: NapiVector<$NapiG>) {
488                    self.lr_1 = lr_1
489                }
490                #[napi(setter, js_name="set_delta")]
491                pub fn set_delta(&mut self, delta: $NapiG) {
492                    self.delta = delta
493                }
494                #[napi(setter, js_name="set_sg")]
495                pub fn set_sg(&mut self, sg: $NapiG) {
496                    self.sg = sg
497                }
498            }
499
500            impl From<NapiOpeningProof> for KimchiOpeningProof {
501                fn from(x: NapiOpeningProof) -> Self {
502                    let NapiOpeningProof {lr_0, lr_1, delta, z1, z2, sg} = x;
503                    OpeningProof {
504                        lr: lr_0.into_iter().zip(lr_1.into_iter()).map(|(x, y)| (x.into(), y.into())).collect(),
505                        delta: delta.into(),
506                        z1: z1.into(),
507                        z2: z2.into(),
508                        sg: sg.into(),
509                    }
510                }
511            }
512
513            impl From<KimchiOpeningProof> for NapiOpeningProof {
514                fn from(x: KimchiOpeningProof) -> Self {
515                    let (lr_0, lr_1): (NapiVector<$NapiG>, NapiVector<$NapiG>) = x
516                        .lr
517                        .clone()
518                        .into_iter()
519                        .map(|(x, y)| (x.into(), y.into()))
520                        .unzip();
521                    NapiOpeningProof {
522                        lr_0,
523                        lr_1,
524                        delta: x.delta.clone().into(),
525                        z1: x.z1.into(),
526                        z2: x.z2.into(),
527                        sg: x.sg.clone().into(),
528                    }
529                }
530            }
531
532            impl FromNapiValue for [<Napi $field_name:camel ProverProof>] {
533                unsafe fn from_napi_value(
534                    env: sys::napi_env,
535                    napi_val: sys::napi_value,
536                ) -> Result<Self> {
537                    let instance = <ClassInstance<[<Napi $field_name:camel ProverProof>]> as FromNapiValue>::from_napi_value(env, napi_val)?;
538                    Ok((*instance).clone())
539                }
540            }
541
542            #[napi(js_name = [<Wasm $field_name:camel ProverProof>])]
543            #[derive(Clone)]
544            pub struct [<Napi $field_name:camel ProverProof>] {
545                #[napi(skip)]
546                pub commitments: NapiProverCommitments,
547                #[napi(skip)]
548                pub proof: NapiOpeningProof,
549                // OCaml doesn't have sized arrays, so we have to convert to a tuple..
550                #[napi(skip)]
551                pub evals: NapiProofEvaluations,
552                #[napi(skip)]
553                pub ft_eval1: $NapiF,
554                #[napi(skip)]
555                pub public: NapiFlatVector<$NapiF>,
556                #[napi(skip)]
557                pub prev_challenges_scalars: $NapiVecVec,
558                #[napi(skip)]
559                pub prev_challenges_comms: NapiVector<$NapiPolyComm>,
560            }
561
562            type NapiProverProof = [<Napi $field_name:camel ProverProof>];
563            type KimchiOpeningProof = OpeningProof<$G, FULL_ROUNDS>;
564            type KimchiProverProof = ProverProof<$G, KimchiOpeningProof, FULL_ROUNDS>;
565            type KimchiProofWithPublic = (KimchiProverProof, Vec<$F>);
566            type KimchiIndex = ProverIndex<FULL_ROUNDS, $G, IPA_SRS<$G>>;
567
568            impl From<&NapiProverProof> for KimchiProofWithPublic {
569                fn from(x: &NapiProverProof) -> Self {
570                    let proof = ProverProof {
571                        commitments: x.commitments.clone().into(),
572                        proof: x.proof.clone().into(),
573                        evals: x.evals.clone().into(),
574                        prev_challenges: x.prev_challenges_scalars.0
575                            .iter().cloned()
576                            .zip(x.prev_challenges_comms.clone().into_iter())
577                            .map(|(chals, comm)| RecursionChallenge { chals, comm: comm.into() })
578                            .collect(),
579                        ft_eval1: x.ft_eval1.clone().into()
580                    };
581                    let public = x.public.clone().into_iter().map(Into::into).collect();
582                    (proof, public)
583                }
584            }
585
586            impl From<NapiProverProof> for KimchiProofWithPublic {
587                fn from(x: NapiProverProof) -> Self {
588                    let proof = ProverProof {
589                        commitments: x.commitments.into(),
590                        proof: x.proof.into(),
591                        evals: x.evals.into(),
592                        prev_challenges:
593                            x.prev_challenges_scalars.0
594                                .iter()
595                                .zip((x.prev_challenges_comms).clone().into_iter())
596                                .map(|(chals, comm)| {
597                                    RecursionChallenge {
598                                        chals: chals.clone(),
599                                        comm: comm.into(),
600                                    }
601                                })
602                                .collect(),
603                        ft_eval1: x.ft_eval1.into()
604                    };
605                    let public = x.public.into_iter().map(Into::into).collect();
606                    (proof, public)
607                }
608            }
609
610            // Map native proof + public input into a NapiProverProof wrapper so it can
611            // be returned directly to JS without an External.
612            impl From<(&KimchiProverProof, &Vec<$F>)> for NapiProverProof {
613                fn from((proof, public): (&KimchiProverProof, &Vec<$F>)) -> Self {
614                    let (scalars, comms): (Vec<Vec<$F>>, Vec<$NapiPolyComm>) = proof
615                        .prev_challenges
616                        .iter()
617                        .map(|RecursionChallenge { chals, comm }| (chals.clone(), comm.clone().into()))
618                        .unzip();
619
620                    NapiProverProof {
621                        commitments: proof.commitments.clone().into(),
622                        proof: proof.proof.clone().into(),
623                        evals: proof.evals.clone().into(),
624                        ft_eval1: proof.ft_eval1.clone().into(),
625                        public: public.clone().into_iter().map(Into::into).collect(),
626                        prev_challenges_scalars: scalars.into(),
627                        prev_challenges_comms: comms.into(), // NapiVector<$NapiPolyComm>
628                    }
629                }
630            }
631
632            impl From<KimchiProofWithPublic> for NapiProverProof {
633                fn from((proof, public): KimchiProofWithPublic) -> Self {
634                    let (scalars, comms): (Vec<Vec<$F>>, Vec<$NapiPolyComm>) = proof
635                        .prev_challenges
636                        .into_iter()
637                        .map(|RecursionChallenge { chals, comm }| (chals, comm.into()))
638                        .unzip();
639
640                    NapiProverProof {
641                        commitments: proof.commitments.into(),
642                        proof: proof.proof.into(),
643                        evals: proof.evals.into(),
644                        ft_eval1: proof.ft_eval1.into(),
645                        public: public.into_iter().map(Into::into).collect(),
646                        prev_challenges_scalars: scalars.into(),
647                        prev_challenges_comms: comms.into(),
648                    }
649                }
650            }
651
652            impl FromNapiValue for [<Napi $field_name:camel OpeningProof>] {
653                unsafe fn from_napi_value(
654                    env: sys::napi_env,
655                    napi_val: sys::napi_value,
656                ) -> Result<Self> {
657                    let instance = <ClassInstance<[<Napi $field_name:camel OpeningProof>]> as FromNapiValue>::from_napi_value(env, napi_val)?;
658                    Ok((*instance).clone())
659                }
660            }
661
662            #[napi]
663            impl [<Napi $field_name:camel ProverProof>] {
664                #[napi(constructor)]
665                pub fn new(
666                    commitments: NapiProverCommitments, // maybe remove FromNapiValue trait implementation and wrap it in External instead
667                    proof: NapiOpeningProof,
668                    evals: NapiProofEvaluations, // maybe remove FromNapiValue trait implementation and wrap it in External instead
669                    ft_eval1: $NapiF,
670                    public_: NapiFlatVector<$NapiF>,
671                    prev_challenges_scalars: $NapiVecVec,
672                    prev_challenges_comms: NapiVector<$NapiPolyComm>) -> Self {
673                    NapiProverProof {
674                        commitments,
675                        proof,
676                        evals,
677                        ft_eval1,
678                        public: public_,
679                        prev_challenges_scalars,
680                        prev_challenges_comms,
681                    }
682                }
683
684                #[napi(getter)]
685                pub fn commitments(&self) -> NapiProverCommitments {
686                    self.commitments.clone()
687                }
688                #[napi(getter)]
689                pub fn proof(&self) -> NapiOpeningProof {
690                    self.proof.clone()
691                }
692                #[napi(getter)]
693                pub fn evals(&self) -> NapiProofEvaluations {
694                    self.evals.clone()
695                }
696                #[napi(getter, js_name = "ft_eval1")]
697                pub fn ft_eval1(&self) -> $NapiF {
698                    self.ft_eval1.clone()
699                }
700                #[napi(getter, js_name="public_")]
701                pub fn public_(&self) -> NapiFlatVector<$NapiF> {
702                    self.public.clone()
703                }
704                #[napi(getter, js_name="prev_challenges_scalars")]
705                pub fn prev_challenges_scalars(&self) -> $NapiVecVec {
706                    self.prev_challenges_scalars.clone()
707                }
708                #[napi(getter, js_name="prev_challenges_comms")]
709                pub fn prev_challenges_comms(&self) -> NapiVector<$NapiPolyComm> {
710                    self.prev_challenges_comms.clone()
711                }
712
713                #[napi(setter, js_name="set_commitments")]
714                pub fn set_commitments(&mut self, commitments: NapiProverCommitments) {
715                    self.commitments = commitments
716                }
717                #[napi(setter, js_name="set_proof")]
718                pub fn set_proof(&mut self, proof: NapiOpeningProof) {
719                    self.proof = proof
720                }
721                #[napi(setter, js_name="set_evals")]
722                pub fn set_evals(&mut self, evals: NapiProofEvaluations) {
723                    self.evals = evals
724                }
725                #[napi(setter, js_name = "set_ft_eval1")]
726                pub fn set_ft_eval1(&mut self, ft_eval1: $NapiF) {
727                    self.ft_eval1 = ft_eval1
728                }
729                #[napi(setter, js_name="set_public_")]
730                pub fn set_public_(&mut self, public_: NapiFlatVector<$NapiF>) {
731                    self.public = public_
732                }
733                #[napi(setter, js_name="set_prev_challenges_scalars")]
734                pub fn set_prev_challenges_scalars(&mut self, prev_challenges_scalars: $NapiVecVec) {
735                    self.prev_challenges_scalars = prev_challenges_scalars;
736                }
737                #[napi(setter, js_name="set_prev_challenges_comms")]
738                pub fn set_prev_challenges_comms(&mut self, prev_challenges_comms: NapiVector<$NapiPolyComm>) {
739                    self.prev_challenges_comms = prev_challenges_comms
740                }
741
742                #[napi]
743                #[allow(deprecated)]
744                pub fn serialize(&self) -> String {
745                    let (proof, _public_input) = self.into();
746                    let serialized = rmp_serde::to_vec(&proof).unwrap();
747                    // Deprecated used on purpose: updating this leads to a bug in o1js
748                    base64::encode(serialized)
749                }
750            }
751
752            // Re-use the prover proof wrapper as the returned type (matching wasm binding).
753            type NapiProofF = NapiProverProof;
754
755            #[napi(js_name = [<"caml_pasta_" $field_name:snake "_plonk_proof_create">])]
756            pub fn [<caml_pasta_ $field_name:snake _plonk_proof_create>](
757                index: &External<$NapiIndex>,
758                witness: $NapiVecVec,
759                runtime_tables: NapiVector<$NapiRuntimeTable>,
760                prev_challenges: NapiFlatVector<$NapiF>,
761                prev_sgs: NapiVector<$NapiG>,
762            ) -> Result<NapiProofF> {
763                let (maybe_proof, public_input) = {
764                    index
765                        .0
766                        .srs
767                        .as_ref()
768                        .get_lagrange_basis(index.0.as_ref().cs.domain.d1);
769                    let prev: Vec<RecursionChallenge<$G>> = {
770                        if prev_challenges.is_empty() {
771                            Vec::new()
772                        } else {
773                            let challenges_per_sg = prev_challenges.len() / prev_sgs.len();
774                            let d = prev_sgs
775                                .into_iter()
776                                .map(Into::<$G>::into)
777                                .enumerate()
778                                .map(|(i, sg)| {
779                                    let chals = prev_challenges
780                                        [(i * challenges_per_sg)..(i + 1) * challenges_per_sg]
781                                        .iter()
782                                        .cloned()
783                                        .map(Into::into)
784                                        .collect();
785                                    let comm = PolyComm::<$G> { chunks: vec![sg] };
786                                    RecursionChallenge { chals, comm }
787                                })
788                                .collect();
789                            d
790                        }
791                    };
792                    let rust_runtime_tables: Vec<RuntimeTable<$F>> = runtime_tables.into_iter().map(Into::into).collect();
793
794                    let witness: [Vec<_>; COLUMNS] = witness
795                        .0
796                        .try_into()
797                        .expect("the witness should be a column of 15 vectors");
798
799                    let index: &KimchiIndex = &index.0.as_ref();
800
801                    let public_input = witness[0][0..index.cs.public].to_vec();
802
803                    // Release the runtime lock so that other threads can run using it while we generate the proof.
804                    let group_map = GroupMap::<_>::setup();
805                    let maybe_proof = ProverProof::create_recursive::<
806                        DefaultFqSponge<_, PlonkSpongeConstantsKimchi, FULL_ROUNDS>,
807                        DefaultFrSponge<_, PlonkSpongeConstantsKimchi, FULL_ROUNDS>,
808                        _,
809                    >(
810                        &group_map,
811                        witness,
812                        &rust_runtime_tables,
813                        index,
814                        prev,
815                        None,
816                        &mut rand::rngs::OsRng,
817                    );
818                    (maybe_proof, public_input)
819                };
820
821                match maybe_proof {
822                    Ok(proof) => Ok((proof, public_input).into()),
823                    Err(err) => Err(NapiError::new(Status::GenericFailure, err.to_string())),
824                }
825            }
826
827            #[napi(js_name = [<"caml_pasta_" $field_name:snake "_plonk_proof_verify">])]
828            pub fn [<caml_pasta_ $field_name:snake _plonk_proof_verify>](
829                index: $NapiVerifierIndex,
830                proof: NapiProofF,
831            ) -> bool {
832                    let group_map = <$G as CommitmentCurve>::Map::setup();
833                    let verifier_index = &index.into();
834                    let proof_with_public: KimchiProofWithPublic =
835                        proof.into();
836                    let (proof, public_input) = (&proof_with_public.0, &proof_with_public.1);
837                    batch_verify::<
838                        FULL_ROUNDS,
839                        $G,
840                        DefaultFqSponge<_, PlonkSpongeConstantsKimchi, FULL_ROUNDS>,
841                        DefaultFrSponge<_, PlonkSpongeConstantsKimchi, FULL_ROUNDS>,
842                        KimchiOpeningProof
843                    >(
844                        &group_map,
845                        &[Context { verifier_index, proof, public_input }]
846                    ).is_ok()
847            }
848
849
850        #[napi(js_name = [<"caml_pasta_" $field_name:snake "_plonk_proof_batch_verify">])]
851        pub fn [<caml_pasta_ $field_name:snake _plonk_proof_batch_verify>](
852                indexes: NapiVector<$NapiVerifierIndex>,
853                proofs: NapiVector<NapiProofF>,
854            ) -> bool {
855                let indexes: Vec<_> = indexes.into_iter().map(Into::into).collect();
856                let proofs_native: Vec<KimchiProofWithPublic> =
857                    proofs.into_iter().map(Into::into).collect();
858
859                if indexes.len() != proofs_native.len() {
860                    return false;
861                }
862
863                let contexts: Vec<_> = indexes
864                    .iter()
865                    .zip(proofs_native.iter())
866                    .map(|(index, proof_with_public)| Context {
867                        verifier_index: index,
868                        proof: &proof_with_public.0,
869                        public_input: &proof_with_public.1,
870                    })
871                    .collect();
872
873                let group_map = GroupMap::<_>::setup();
874
875                batch_verify::<
876                    FULL_ROUNDS,
877                    $G,
878                    DefaultFqSponge<_, PlonkSpongeConstantsKimchi, FULL_ROUNDS>,
879                    DefaultFrSponge<_, PlonkSpongeConstantsKimchi, FULL_ROUNDS>,
880                    KimchiOpeningProof
881                >(&group_map, &contexts)
882                .is_ok()
883            }
884
885            #[napi(js_name = [<"caml_pasta_" $field_name:snake "_plonk_proof_dummy">])]
886            pub fn [<caml_pasta_ $field_name:snake _plonk_proof_dummy>]() -> NapiProofF {
887                fn comm() -> PolyComm<$G> {
888                    let g = $G::generator();
889                    PolyComm {
890                        chunks: vec![g, g, g],
891                    }
892                }
893
894                let prev = RecursionChallenge {
895                    chals: vec![$F::one(), $F::one()],
896                    comm: comm(),
897                };
898                let prev_challenges = vec![prev.clone(), prev.clone(), prev.clone()];
899
900                let g = $G::generator();
901                let proof = OpeningProof {
902                    lr: vec![(g, g), (g, g), (g, g)],
903                    z1: $F::one(),
904                    z2: $F::one(),
905                    delta: g,
906                    sg: g,
907                };
908                let eval = || PointEvaluations {
909                    zeta: vec![$F::one()],
910                    zeta_omega: vec![$F::one()],
911                };
912                let evals = ProofEvaluations {
913                    w: core::array::from_fn(|_| eval()),
914                    coefficients: core::array::from_fn(|_| eval()),
915                    z: eval(),
916                    s: core::array::from_fn(|_| eval()),
917                    generic_selector: eval(),
918                    poseidon_selector: eval(),
919                    complete_add_selector: eval(),
920                    mul_selector: eval(),
921                    emul_selector: eval(),
922                    endomul_scalar_selector: eval(),
923                    range_check0_selector: None,
924                    range_check1_selector: None,
925                    foreign_field_add_selector: None,
926                    foreign_field_mul_selector: None,
927                    xor_selector: None,
928                    rot_selector: None,
929                    lookup_aggregation: None,
930                    lookup_table: None,
931                    lookup_sorted: array::from_fn(|_| None),
932                    runtime_lookup_table: None,
933                    runtime_lookup_table_selector: None,
934                    xor_lookup_selector: None,
935                    lookup_gate_lookup_selector: None,
936                    range_check_lookup_selector: None,
937                    foreign_field_mul_lookup_selector: None,
938                    public: None,
939                };
940
941                let dlogproof = ProverProof {
942                    commitments: ProverCommitments {
943                        w_comm: core::array::from_fn(|_| comm()),
944                        z_comm: comm(),
945                        t_comm: comm(),
946                        lookup: None,
947                    },
948                    proof,
949                    evals,
950                    ft_eval1: $F::one(),
951                    prev_challenges,
952                };
953
954                let public = vec![$F::one(), $F::one()];
955                (dlogproof, public).into()
956            }
957
958            #[napi(js_name = [<"caml_pasta_" $field_name:snake "_plonk_proof_deep_copy">])]
959            pub fn [<caml_pasta_ $field_name:snake "_plonk_proof_deep_copy">](
960                x: NapiProofF
961            ) -> NapiProofF {
962                x.clone()
963            }
964        }
965    };
966}
967
968pub mod fp {
969    use super::*;
970    use crate::{
971        pasta_fp_plonk_index::WasmPastaFpPlonkIndex as NapiPastaFpPlonkIndex,
972        plonk_verifier_index::fp::NapiFpPlonkVerifierIndex,
973        poly_comm::vesta::NapiFpPolyComm,
974        wrappers::{field::NapiPastaFp, group::NapiGVesta, lookups::NapiFpRuntimeTable},
975    };
976    use mina_curves::pasta::{Fp, Vesta};
977
978    impl_proof!(
979        NapiGVesta,
980        Vesta,
981        NapiPastaFp,
982        Fp,
983        NapiFpPolyComm,
984        NapiSrs,
985        NapiPastaFpPlonkIndex,
986        NapiFpPlonkVerifierIndex,
987        NapiVecVecFp,
988        Fp,
989        NapiFpRuntimeTable,
990    );
991}
992
993pub mod fq {
994    use super::*;
995    use crate::{
996        pasta_fq_plonk_index::WasmPastaFqPlonkIndex as NapiPastaFqPlonkIndex,
997        plonk_verifier_index::fq::NapiFqPlonkVerifierIndex,
998        poly_comm::pallas::NapiFqPolyComm,
999        wrappers::{field::NapiPastaFq, group::NapiGPallas, lookups::NapiFqRuntimeTable},
1000    };
1001    use mina_curves::pasta::{Fq, Pallas};
1002
1003    impl_proof!(
1004        NapiGPallas,
1005        Pallas,
1006        NapiPastaFq,
1007        Fq,
1008        NapiFqPolyComm,
1009        NapiSrs,
1010        NapiPastaFqPlonkIndex,
1011        NapiFqPlonkVerifierIndex,
1012        NapiVecVecFq,
1013        Fq,
1014        NapiFqRuntimeTable,
1015    );
1016}