plonk_wasm/
pasta_fp_plonk_index.rs

1use crate::{
2    gate_vector::fp::WasmGateVector,
3    srs::fp::WasmFpSrs as WasmSrs,
4    wasm_vector::{fp::*, WasmVector},
5};
6use ark_poly::EvaluationDomain;
7use arkworks::WasmPastaFp;
8use kimchi::{
9    circuits::{
10        constraints::ConstraintSystem,
11        gate::CircuitGate,
12        lookup::{runtime_tables::RuntimeTableCfg, tables::LookupTable},
13    },
14    linearization::expr_linearization,
15    poly_commitment::{ipa::OpeningProof, SRS as _},
16    prover_index::ProverIndex,
17};
18use mina_curves::pasta::{Fp, Pallas as GAffineOther, Vesta as GAffine, VestaParameters};
19use mina_poseidon::{constants::PlonkSpongeConstantsKimchi, sponge::DefaultFqSponge};
20use serde::{Deserialize, Serialize};
21use std::{
22    fs::{File, OpenOptions},
23    io::{BufReader, BufWriter, Seek, SeekFrom::Start},
24};
25use wasm_bindgen::prelude::*;
26use wasm_types::FlatVector as WasmFlatVector;
27
28//
29// CamlPastaFpPlonkIndex (custom type)
30//
31
32/// Boxed so that we don't store large proving indexes in the OCaml heap.
33#[wasm_bindgen]
34pub struct WasmPastaFpPlonkIndex(
35    #[wasm_bindgen(skip)] pub Box<ProverIndex<GAffine, OpeningProof<GAffine>>>,
36);
37
38// This should mimic LookupTable structure
39#[wasm_bindgen]
40pub struct WasmPastaFpLookupTable {
41    #[wasm_bindgen(skip)]
42    pub id: i32,
43    #[wasm_bindgen(skip)]
44    pub data: WasmVecVecFp,
45}
46
47// Converter from WasmPastaFpLookupTable to LookupTable, used by the binding
48// below.
49impl From<WasmPastaFpLookupTable> for LookupTable<Fp> {
50    fn from(wasm_lt: WasmPastaFpLookupTable) -> LookupTable<Fp> {
51        LookupTable {
52            id: wasm_lt.id,
53            data: wasm_lt.data.0,
54        }
55    }
56}
57
58// JS constructor for js/bindings.js
59#[wasm_bindgen]
60impl WasmPastaFpLookupTable {
61    #[wasm_bindgen(constructor)]
62    pub fn new(id: i32, data: WasmVecVecFp) -> WasmPastaFpLookupTable {
63        WasmPastaFpLookupTable { id, data }
64    }
65}
66
67// Runtime table config
68
69#[wasm_bindgen]
70pub struct WasmPastaFpRuntimeTableCfg {
71    #[wasm_bindgen(skip)]
72    pub id: i32,
73    #[wasm_bindgen(skip)]
74    pub first_column: WasmFlatVector<WasmPastaFp>,
75}
76
77// JS constructor for js/bindings.js
78#[wasm_bindgen]
79impl WasmPastaFpRuntimeTableCfg {
80    #[wasm_bindgen(constructor)]
81    pub fn new(id: i32, first_column: WasmFlatVector<WasmPastaFp>) -> Self {
82        Self { id, first_column }
83    }
84}
85
86impl From<WasmPastaFpRuntimeTableCfg> for RuntimeTableCfg<Fp> {
87    fn from(wasm_rt_table_cfg: WasmPastaFpRuntimeTableCfg) -> Self {
88        Self {
89            id: wasm_rt_table_cfg.id,
90            first_column: wasm_rt_table_cfg
91                .first_column
92                .into_iter()
93                .map(Into::into)
94                .collect(),
95        }
96    }
97}
98
99// CamlPastaFpPlonkIndex methods
100//
101
102// Change js/web/worker-spec.js accordingly
103#[wasm_bindgen]
104pub fn caml_pasta_fp_plonk_index_create(
105    gates: &WasmGateVector,
106    public_: i32,
107    lookup_tables: WasmVector<WasmPastaFpLookupTable>,
108    runtime_table_cfgs: WasmVector<WasmPastaFpRuntimeTableCfg>,
109    prev_challenges: i32,
110    srs: &WasmSrs,
111    lazy_mode: bool,
112) -> Result<WasmPastaFpPlonkIndex, JsError> {
113    console_error_panic_hook::set_once();
114    let index = crate::rayon::run_in_pool(|| {
115        // flatten the permutation information (because OCaml has a different way of keeping track of permutations)
116        let gates: Vec<_> = gates
117            .0
118            .iter()
119            .map(|gate| CircuitGate::<Fp> {
120                typ: gate.typ,
121                wires: gate.wires,
122                coeffs: gate.coeffs.clone(),
123            })
124            .collect();
125
126        let rust_runtime_table_cfgs: Vec<RuntimeTableCfg<Fp>> =
127            runtime_table_cfgs.into_iter().map(Into::into).collect();
128
129        let rust_lookup_tables: Vec<LookupTable<Fp>> =
130            lookup_tables.into_iter().map(Into::into).collect();
131
132        // create constraint system
133        let cs = match ConstraintSystem::<Fp>::create(gates)
134            .public(public_ as usize)
135            .prev_challenges(prev_challenges as usize)
136            .lookup(rust_lookup_tables)
137            .max_poly_size(Some(srs.0.max_poly_size()))
138            .runtime(if rust_runtime_table_cfgs.is_empty() {
139                None
140            } else {
141                Some(rust_runtime_table_cfgs)
142            })
143            .lazy_mode(lazy_mode)
144            .build()
145        {
146            Err(_) => {
147                return Err("caml_pasta_fp_plonk_index_create: could not create constraint system");
148            }
149            Ok(cs) => cs,
150        };
151
152        // endo
153        let (endo_q, _endo_r) = poly_commitment::ipa::endos::<GAffineOther>();
154
155        srs.0.get_lagrange_basis(cs.domain.d1);
156
157        let mut index = ProverIndex::<GAffine, OpeningProof<GAffine>>::create(
158            cs,
159            endo_q,
160            srs.0.clone(),
161            lazy_mode,
162        );
163        // Compute and cache the verifier index digest
164        index.compute_verifier_index_digest::<DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>>();
165        Ok(index)
166    });
167
168    // create index
169    match index {
170        Ok(index) => Ok(WasmPastaFpPlonkIndex(Box::new(index))),
171        Err(str) => Err(JsError::new(str)),
172    }
173}
174
175#[wasm_bindgen]
176pub fn caml_pasta_fp_plonk_index_max_degree(index: &WasmPastaFpPlonkIndex) -> i32 {
177    index.0.srs.max_poly_size() as i32
178}
179
180#[wasm_bindgen]
181pub fn caml_pasta_fp_plonk_index_public_inputs(index: &WasmPastaFpPlonkIndex) -> i32 {
182    index.0.cs.public as i32
183}
184
185#[wasm_bindgen]
186pub fn caml_pasta_fp_plonk_index_domain_d1_size(index: &WasmPastaFpPlonkIndex) -> i32 {
187    index.0.cs.domain.d1.size() as i32
188}
189
190#[wasm_bindgen]
191pub fn caml_pasta_fp_plonk_index_domain_d4_size(index: &WasmPastaFpPlonkIndex) -> i32 {
192    index.0.cs.domain.d4.size() as i32
193}
194
195#[wasm_bindgen]
196pub fn caml_pasta_fp_plonk_index_domain_d8_size(index: &WasmPastaFpPlonkIndex) -> i32 {
197    index.0.cs.domain.d8.size() as i32
198}
199
200#[wasm_bindgen]
201pub fn caml_pasta_fp_plonk_index_decode(
202    bytes: &[u8],
203    srs: &WasmSrs,
204) -> Result<WasmPastaFpPlonkIndex, JsError> {
205    let mut deserializer = rmp_serde::Deserializer::new(bytes);
206    let mut index =
207        ProverIndex::<GAffine, OpeningProof<GAffine>>::deserialize(&mut deserializer)
208            .map_err(|e| JsError::new(&format!("caml_pasta_fp_plonk_index_decode: {}", e)))?;
209
210    index.srs = srs.0.clone();
211    let (linearization, powers_of_alpha) = expr_linearization(Some(&index.cs.feature_flags), true);
212    index.linearization = linearization;
213    index.powers_of_alpha = powers_of_alpha;
214
215    Ok(WasmPastaFpPlonkIndex(Box::new(index)))
216}
217
218#[wasm_bindgen]
219pub fn caml_pasta_fp_plonk_index_encode(index: &WasmPastaFpPlonkIndex) -> Result<Vec<u8>, JsError> {
220    let mut buffer = Vec::new();
221    let mut serializer = rmp_serde::Serializer::new(&mut buffer);
222    index
223        .0
224        .serialize(&mut serializer)
225        .map_err(|e| JsError::new(&format!("caml_pasta_fp_plonk_index_encode: {}", e)))?;
226    Ok(buffer)
227}
228
229#[wasm_bindgen]
230pub fn caml_pasta_fp_plonk_index_read(
231    offset: Option<i32>,
232    srs: &WasmSrs,
233    path: String,
234) -> Result<WasmPastaFpPlonkIndex, JsValue> {
235    // read from file
236    let file = match File::open(path) {
237        Err(_) => return Err(JsValue::from_str("caml_pasta_fp_plonk_index_read")),
238        Ok(file) => file,
239    };
240    let mut r = BufReader::new(file);
241
242    // optional offset in file
243    if let Some(offset) = offset {
244        r.seek(Start(offset as u64))
245            .map_err(|err| JsValue::from_str(&format!("caml_pasta_fp_plonk_index_read: {err}")))?;
246    }
247
248    // deserialize the index
249    let mut t = ProverIndex::<GAffine, OpeningProof<GAffine>>::deserialize(
250        &mut rmp_serde::Deserializer::new(r),
251    )
252    .map_err(|err| JsValue::from_str(&format!("caml_pasta_fp_plonk_index_read: {err}")))?;
253    t.srs = srs.0.clone();
254    let (linearization, powers_of_alpha) = expr_linearization(Some(&t.cs.feature_flags), true);
255    t.linearization = linearization;
256    t.powers_of_alpha = powers_of_alpha;
257
258    //
259    Ok(WasmPastaFpPlonkIndex(Box::new(t)))
260}
261
262#[wasm_bindgen]
263pub fn caml_pasta_fp_plonk_index_write(
264    append: Option<bool>,
265    index: &WasmPastaFpPlonkIndex,
266    path: String,
267) -> Result<(), JsValue> {
268    let file = OpenOptions::new()
269        .append(append.unwrap_or(true))
270        .open(path)
271        .map_err(|_| JsValue::from_str("caml_pasta_fp_plonk_index_write"))?;
272    let w = BufWriter::new(file);
273    index
274        .0
275        .serialize(&mut rmp_serde::Serializer::new(w))
276        .map_err(|e| JsValue::from_str(&format!("caml_pasta_fp_plonk_index_read: {e}")))
277}
278
279#[allow(deprecated)]
280#[wasm_bindgen]
281pub fn caml_pasta_fp_plonk_index_serialize(index: &WasmPastaFpPlonkIndex) -> String {
282    let serialized = rmp_serde::to_vec(&index.0).unwrap();
283    // Deprecated used on purpose: updating this leads to a bug in o1js
284    base64::encode(serialized)
285}
286
287// helpers
288
289fn format_field(f: &Fp) -> String {
290    // TODO this could be much nicer, should end up as "1", "-1", "0" etc
291    format!("{f}")
292}
293
294pub fn format_circuit_gate(i: usize, gate: &CircuitGate<Fp>) -> String {
295    let coeffs = gate
296        .coeffs
297        .iter()
298        .map(format_field)
299        .collect::<Vec<_>>()
300        .join("\n");
301    let wires = gate
302        .wires
303        .iter()
304        .enumerate()
305        .filter(|(j, wire)| wire.row != i || wire.col != *j)
306        .map(|(j, wire)| format!("({}, {}) --> ({}, {})", i, j, wire.row, wire.col))
307        .collect::<Vec<_>>()
308        .join("\n");
309    format!(
310        "c[{}][{:?}]:\nconstraints\n{}\nwires\n{}\n",
311        i, gate.typ, coeffs, wires
312    )
313}