kimchi_stubs/
pasta_fq_plonk_index.rs

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