plonk_wasm/
pasta_fq_plonk_index.rs

1use crate::{
2    gate_vector::fq::WasmGateVector,
3    srs::fq::WasmFqSrs as WasmSrs,
4    wasm_vector::{fq::*, WasmVector},
5};
6use ark_poly::EvaluationDomain;
7use arkworks::WasmPastaFq;
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::{Fq, Pallas as GAffine, PallasParameters, Vesta as GAffineOther};
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// CamlPastaFqPlonkIndex (custom type)
30//
31
32/// Boxed so that we don't store large proving indexes in the OCaml heap.
33#[wasm_bindgen]
34pub struct WasmPastaFqPlonkIndex(
35    #[wasm_bindgen(skip)] pub Box<ProverIndex<GAffine, OpeningProof<GAffine>>>,
36);
37
38#[wasm_bindgen]
39pub struct WasmPastaFqLookupTable {
40    #[wasm_bindgen(skip)]
41    pub id: i32,
42    #[wasm_bindgen(skip)]
43    pub data: WasmVecVecFq,
44}
45
46impl From<WasmPastaFqLookupTable> for LookupTable<Fq> {
47    fn from(wasm_lt: WasmPastaFqLookupTable) -> LookupTable<Fq> {
48        LookupTable {
49            id: wasm_lt.id,
50            data: wasm_lt.data.0,
51        }
52    }
53}
54
55// JS constructor for js/bindings.js
56#[wasm_bindgen]
57impl WasmPastaFqLookupTable {
58    #[wasm_bindgen(constructor)]
59    pub fn new(id: i32, data: WasmVecVecFq) -> WasmPastaFqLookupTable {
60        WasmPastaFqLookupTable { id, data }
61    }
62}
63
64// Runtime table config
65
66#[wasm_bindgen]
67pub struct WasmPastaFqRuntimeTableCfg {
68    #[wasm_bindgen(skip)]
69    pub id: i32,
70    #[wasm_bindgen(skip)]
71    pub first_column: WasmFlatVector<WasmPastaFq>,
72}
73
74impl From<WasmPastaFqRuntimeTableCfg> for RuntimeTableCfg<Fq> {
75    fn from(wasm_rt_cfg: WasmPastaFqRuntimeTableCfg) -> Self {
76        Self {
77            id: wasm_rt_cfg.id,
78            first_column: wasm_rt_cfg
79                .first_column
80                .into_iter()
81                .map(Into::into)
82                .collect(),
83        }
84    }
85}
86
87// JS constructor for js/bindings.js
88#[wasm_bindgen]
89impl WasmPastaFqRuntimeTableCfg {
90    #[wasm_bindgen(constructor)]
91    pub fn new(id: i32, first_column: WasmFlatVector<WasmPastaFq>) -> Self {
92        Self { id, first_column }
93    }
94}
95
96//
97// CamlPastaFqPlonkIndex methods
98//
99
100// Change js/web/worker-spec.js accordingly
101#[wasm_bindgen]
102pub fn caml_pasta_fq_plonk_index_create(
103    gates: &WasmGateVector,
104    public_: i32,
105    lookup_tables: WasmVector<WasmPastaFqLookupTable>,
106    runtime_table_cfgs: WasmVector<WasmPastaFqRuntimeTableCfg>,
107    prev_challenges: i32,
108    srs: &WasmSrs,
109    lazy_mode: bool,
110) -> Result<WasmPastaFqPlonkIndex, JsError> {
111    console_error_panic_hook::set_once();
112    let index = crate::rayon::run_in_pool(|| {
113        // flatten the permutation information (because OCaml has a different way of keeping track of permutations)
114        let gates: Vec<_> = gates
115            .0
116            .iter()
117            .map(|gate| CircuitGate::<Fq> {
118                typ: gate.typ,
119                wires: gate.wires,
120                coeffs: gate.coeffs.clone(),
121            })
122            .collect();
123
124        let rust_runtime_table_cfgs: Vec<RuntimeTableCfg<Fq>> =
125            runtime_table_cfgs.into_iter().map(Into::into).collect();
126
127        let rust_lookup_tables: Vec<LookupTable<Fq>> =
128            lookup_tables.into_iter().map(Into::into).collect();
129
130        // create constraint system
131        let cs = match ConstraintSystem::<Fq>::create(gates)
132            .public(public_ as usize)
133            .prev_challenges(prev_challenges as usize)
134            .lookup(rust_lookup_tables)
135            .runtime(if rust_runtime_table_cfgs.is_empty() {
136                None
137            } else {
138                Some(rust_runtime_table_cfgs)
139            })
140            .lazy_mode(lazy_mode)
141            .build()
142        {
143            Err(_) => {
144                return Err("caml_pasta_fq_plonk_index_create: could not create constraint system");
145            }
146            Ok(cs) => cs,
147        };
148
149        // endo
150        let (endo_q, _endo_r) = poly_commitment::ipa::endos::<GAffineOther>();
151
152        srs.0.get_lagrange_basis(cs.domain.d1);
153
154        let mut index = ProverIndex::<GAffine, OpeningProof<GAffine>>::create(
155            cs,
156            endo_q,
157            srs.0.clone(),
158            lazy_mode,
159        );
160        // Compute and cache the verifier index digest
161        index.compute_verifier_index_digest::<DefaultFqSponge<PallasParameters, PlonkSpongeConstantsKimchi>>();
162
163        Ok(index)
164    });
165
166    // create index
167    match index {
168        Ok(index) => Ok(WasmPastaFqPlonkIndex(Box::new(index))),
169        Err(str) => Err(JsError::new(str)),
170    }
171}
172
173#[wasm_bindgen]
174pub fn caml_pasta_fq_plonk_index_max_degree(index: &WasmPastaFqPlonkIndex) -> i32 {
175    index.0.srs.max_poly_size() as i32
176}
177
178#[wasm_bindgen]
179pub fn caml_pasta_fq_plonk_index_public_inputs(index: &WasmPastaFqPlonkIndex) -> i32 {
180    index.0.cs.public as i32
181}
182
183#[wasm_bindgen]
184pub fn caml_pasta_fq_plonk_index_domain_d1_size(index: &WasmPastaFqPlonkIndex) -> i32 {
185    index.0.cs.domain.d1.size() as i32
186}
187
188#[wasm_bindgen]
189pub fn caml_pasta_fq_plonk_index_domain_d4_size(index: &WasmPastaFqPlonkIndex) -> i32 {
190    index.0.cs.domain.d4.size() as i32
191}
192
193#[wasm_bindgen]
194pub fn caml_pasta_fq_plonk_index_domain_d8_size(index: &WasmPastaFqPlonkIndex) -> i32 {
195    index.0.cs.domain.d8.size() as i32
196}
197
198#[wasm_bindgen]
199pub fn caml_pasta_fq_plonk_index_decode(
200    bytes: &[u8],
201    srs: &WasmSrs,
202) -> Result<WasmPastaFqPlonkIndex, JsError> {
203    let mut deserializer = rmp_serde::Deserializer::new(bytes);
204    let mut index =
205        ProverIndex::<GAffine, OpeningProof<GAffine>>::deserialize(&mut deserializer)
206            .map_err(|e| JsError::new(&format!("caml_pasta_fq_plonk_index_decode: {}", e)))?;
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(WasmPastaFqPlonkIndex(Box::new(index)))
214}
215
216#[wasm_bindgen]
217pub fn caml_pasta_fq_plonk_index_encode(index: &WasmPastaFqPlonkIndex) -> Result<Vec<u8>, JsError> {
218    let mut buffer = Vec::new();
219    let mut serializer = rmp_serde::Serializer::new(&mut buffer);
220    index
221        .0
222        .serialize(&mut serializer)
223        .map_err(|e| JsError::new(&format!("caml_pasta_fq_plonk_index_encode: {}", e)))?;
224    Ok(buffer)
225}
226
227#[wasm_bindgen]
228pub fn caml_pasta_fq_plonk_index_read(
229    offset: Option<i32>,
230    srs: &WasmSrs,
231    path: String,
232) -> Result<WasmPastaFqPlonkIndex, JsValue> {
233    // read from file
234    let file = match File::open(path) {
235        Err(_) => return Err(JsValue::from_str("caml_pasta_fq_plonk_index_read")),
236        Ok(file) => file,
237    };
238    let mut r = BufReader::new(file);
239
240    // optional offset in file
241    if let Some(offset) = offset {
242        r.seek(Start(offset as u64))
243            .map_err(|err| JsValue::from_str(&format!("caml_pasta_fq_plonk_index_read: {err}")))?;
244    }
245
246    // deserialize the index
247    let mut t = ProverIndex::<GAffine, OpeningProof<GAffine>>::deserialize(
248        &mut rmp_serde::Deserializer::new(r),
249    )
250    .map_err(|err| JsValue::from_str(&format!("caml_pasta_fq_plonk_index_read: {err}")))?;
251    t.srs = srs.0.clone();
252    let (linearization, powers_of_alpha) = expr_linearization(Some(&t.cs.feature_flags), true);
253    t.linearization = linearization;
254    t.powers_of_alpha = powers_of_alpha;
255
256    //
257    Ok(WasmPastaFqPlonkIndex(Box::new(t)))
258}
259
260#[wasm_bindgen]
261pub fn caml_pasta_fq_plonk_index_write(
262    append: Option<bool>,
263    index: &WasmPastaFqPlonkIndex,
264    path: String,
265) -> Result<(), JsValue> {
266    let file = OpenOptions::new()
267        .append(append.unwrap_or(true))
268        .open(path)
269        .map_err(|_| JsValue::from_str("caml_pasta_fq_plonk_index_write"))?;
270    let w = BufWriter::new(file);
271    index
272        .0
273        .serialize(&mut rmp_serde::Serializer::new(w))
274        .map_err(|e| JsValue::from_str(&format!("caml_pasta_fq_plonk_index_read: {e}")))
275}
276
277#[allow(deprecated)]
278#[wasm_bindgen]
279pub fn caml_pasta_fq_plonk_index_serialize(index: &WasmPastaFqPlonkIndex) -> String {
280    let serialized = rmp_serde::to_vec(&index.0).unwrap();
281    // Deprecated used on purpose: updating this leads to a bug in o1js
282    base64::encode(serialized)
283}