plonk_wasm/
pasta_fp_plonk_index.rs

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