Skip to main content

kimchi_napi/
pasta_fq_plonk_index.rs

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// TODO: remove incl all dependencies when no longer needed and we only pass napi objects around
87#[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// TODO: remove incl all dependencies when no longer needed and we only pass napi objects around
98#[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    // TODO: check if and how we run rayon threads automatically in napi
143
144    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    // read from file
256    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    // optional offset in file
268    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    // deserialize the index
278    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    //
293    Ok(External::new(WasmPastaFqPlonkIndex(Box::new(t))))
294}