kimchi_stubs/
pasta_fq_plonk_index.rs

1use crate::{arkworks::CamlFq, gate_vector::fq::CamlPastaFqPlonkGateVectorPtr, srs::fq::CamlFqSrs};
2use ark_poly::EvaluationDomain;
3use kimchi::{
4    circuits::{
5        constraints::ConstraintSystem,
6        gate::CircuitGate,
7        lookup::{
8            runtime_tables::{caml::CamlRuntimeTableCfg, RuntimeTableCfg},
9            tables::{caml::CamlLookupTable, LookupTable},
10        },
11    },
12    linearization::expr_linearization,
13    prover_index::ProverIndex,
14};
15use mina_curves::pasta::{Fq, Pallas, PallasParameters, Vesta};
16use mina_poseidon::{
17    constants::PlonkSpongeConstantsKimchi, pasta::FULL_ROUNDS, sponge::DefaultFqSponge,
18};
19use poly_commitment::{ipa::OpeningProof, lagrange_basis::WithLagrangeBasis, SRS as _};
20use serde::{Deserialize, Serialize};
21use std::{
22    fs::{File, OpenOptions},
23    io::{BufReader, BufWriter, Seek, SeekFrom::Start},
24};
25
26type Srs =
27    <OpeningProof<Pallas, FULL_ROUNDS> as poly_commitment::OpenProof<Pallas, FULL_ROUNDS>>::SRS;
28
29/// Boxed so that we don't store large proving indexes in the OCaml heap.
30#[derive(ocaml_gen::CustomType)]
31pub struct CamlPastaFqPlonkIndex(pub Box<ProverIndex<FULL_ROUNDS, Pallas, Srs>>);
32pub type CamlPastaFqPlonkIndexPtr<'a> = ocaml::Pointer<'a, CamlPastaFqPlonkIndex>;
33
34extern "C" fn caml_pasta_fq_plonk_index_finalize(v: ocaml::Raw) {
35    unsafe {
36        let mut v: CamlPastaFqPlonkIndexPtr = v.as_pointer();
37        v.as_mut_ptr().drop_in_place();
38    }
39}
40
41impl ocaml::custom::Custom for CamlPastaFqPlonkIndex {
42    const NAME: &'static str = "CamlPastaFqPlonkIndex\0";
43    const USED: usize = 1;
44    /// Encourage the GC to free when there are > 12 in memory
45    const MAX: usize = 12;
46    const OPS: ocaml::custom::CustomOps = ocaml::custom::CustomOps {
47        identifier: Self::NAME.as_ptr() as *const ocaml::sys::Char,
48        finalize: Some(caml_pasta_fq_plonk_index_finalize),
49        ..ocaml::custom::DEFAULT_CUSTOM_OPS
50    };
51}
52
53#[ocaml_gen::func]
54#[ocaml::func]
55pub fn caml_pasta_fq_plonk_index_create(
56    gates: CamlPastaFqPlonkGateVectorPtr,
57    public: ocaml::Int,
58    lookup_tables: Vec<CamlLookupTable<CamlFq>>,
59    runtime_tables: Vec<CamlRuntimeTableCfg<CamlFq>>,
60    prev_challenges: ocaml::Int,
61    srs: CamlFqSrs,
62    lazy_mode: bool,
63) -> Result<CamlPastaFqPlonkIndex, ocaml::Error> {
64    let gates: Vec<_> = gates
65        .as_ref()
66        .0
67        .iter()
68        .map(|gate| CircuitGate::<Fq> {
69            typ: gate.typ,
70            wires: gate.wires,
71            coeffs: gate.coeffs.clone(),
72        })
73        .collect();
74
75    let runtime_tables: Vec<RuntimeTableCfg<Fq>> =
76        runtime_tables.into_iter().map(Into::into).collect();
77
78    let lookup_tables: Vec<LookupTable<Fq>> = lookup_tables.into_iter().map(Into::into).collect();
79
80    // create constraint system
81    let cs = ConstraintSystem::<Fq>::create(gates)
82        .public(public as usize)
83        .prev_challenges(prev_challenges as usize)
84        .lookup(lookup_tables)
85        .runtime(if runtime_tables.is_empty() {
86            None
87        } else {
88            Some(runtime_tables)
89        })
90        .lazy_mode(lazy_mode)
91        .build()?;
92
93    // endo
94    let (endo_q, _endo_r) = poly_commitment::ipa::endos::<Vesta>();
95
96    srs.0.with_lagrange_basis(cs.domain.d1);
97
98    // create index
99    let mut index =
100        ProverIndex::<FULL_ROUNDS, Pallas, Srs>::create(cs, endo_q, srs.clone(), lazy_mode);
101    // Compute and cache the verifier index digest
102    index.compute_verifier_index_digest::<DefaultFqSponge<
103        PallasParameters,
104        PlonkSpongeConstantsKimchi,
105        FULL_ROUNDS,
106    >>();
107
108    Ok(CamlPastaFqPlonkIndex(Box::new(index)))
109}
110
111#[ocaml_gen::func]
112#[ocaml::func]
113pub fn caml_pasta_fq_plonk_index_max_degree(index: CamlPastaFqPlonkIndexPtr) -> ocaml::Int {
114    index.as_ref().0.srs.max_poly_size() as isize
115}
116
117#[ocaml_gen::func]
118#[ocaml::func]
119pub fn caml_pasta_fq_plonk_index_public_inputs(index: CamlPastaFqPlonkIndexPtr) -> ocaml::Int {
120    index.as_ref().0.cs.public as isize
121}
122
123#[ocaml_gen::func]
124#[ocaml::func]
125pub fn caml_pasta_fq_plonk_index_domain_d1_size(index: CamlPastaFqPlonkIndexPtr) -> ocaml::Int {
126    index.as_ref().0.cs.domain.d1.size() as isize
127}
128
129#[ocaml_gen::func]
130#[ocaml::func]
131pub fn caml_pasta_fq_plonk_index_domain_d4_size(index: CamlPastaFqPlonkIndexPtr) -> ocaml::Int {
132    index.as_ref().0.cs.domain.d4.size() as isize
133}
134
135#[ocaml_gen::func]
136#[ocaml::func]
137pub fn caml_pasta_fq_plonk_index_domain_d8_size(index: CamlPastaFqPlonkIndexPtr) -> ocaml::Int {
138    index.as_ref().0.cs.domain.d8.size() as isize
139}
140
141#[ocaml_gen::func]
142#[ocaml::func]
143pub fn caml_pasta_fq_plonk_index_read(
144    offset: Option<ocaml::Int>,
145    srs: CamlFqSrs,
146    path: String,
147) -> Result<CamlPastaFqPlonkIndex, ocaml::Error> {
148    // open the file for reading
149    let file = match File::open(path) {
150        Err(_) => {
151            return Err(
152                ocaml::Error::invalid_argument("caml_pasta_fp_plonk_index_read")
153                    .err()
154                    .unwrap(),
155            )
156        }
157        Ok(file) => file,
158    };
159    let mut r = BufReader::new(file);
160
161    // optional offset in file
162    if let Some(offset) = offset {
163        r.seek(Start(offset as u64))?;
164    }
165
166    // deserialize the index
167    let mut t =
168        ProverIndex::<FULL_ROUNDS, Pallas, Srs>::deserialize(&mut rmp_serde::Deserializer::new(r))?;
169    t.srs = srs.clone();
170
171    let (linearization, powers_of_alpha) = expr_linearization(Some(&t.cs.feature_flags), true);
172    t.linearization = linearization;
173    t.powers_of_alpha = powers_of_alpha;
174
175    Ok(CamlPastaFqPlonkIndex(Box::new(t)))
176}
177
178#[ocaml_gen::func]
179#[ocaml::func]
180pub fn caml_pasta_fq_plonk_index_write(
181    append: Option<bool>,
182    index: CamlPastaFqPlonkIndexPtr<'static>,
183    path: String,
184) -> Result<(), ocaml::Error> {
185    let file = OpenOptions::new()
186        .append(append.unwrap_or(true))
187        .open(path)
188        .map_err(|_| {
189            ocaml::Error::invalid_argument("caml_pasta_fq_plonk_index_write")
190                .err()
191                .unwrap()
192        })?;
193    let w = BufWriter::new(file);
194    index
195        .as_ref()
196        .0
197        .serialize(&mut rmp_serde::Serializer::new(w))
198        .map_err(|e| e.into())
199}