Skip to main content

kimchi_napi/
pasta_fp_plonk_index.rs

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