kimchi_stubs/
pasta_fp_plonk_index.rs

1use crate::{
2    arkworks::CamlFp, gate_vector::fp::CamlPastaFpPlonkGateVectorPtr, srs::fp::CamlFpSrs,
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::{Fp, Pallas, Vesta, VestaParameters};
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 CamlPastaFpPlonkIndex(pub Box<ProverIndex<Vesta, OpeningProof<Vesta>>>);
30pub type CamlPastaFpPlonkIndexPtr<'a> = ocaml::Pointer<'a, CamlPastaFpPlonkIndex>;
31
32extern "C" fn caml_pasta_fp_plonk_index_finalize(v: ocaml::Raw) {
33    unsafe {
34        let mut v: CamlPastaFpPlonkIndexPtr = v.as_pointer();
35        v.as_mut_ptr().drop_in_place();
36    }
37}
38
39impl ocaml::custom::Custom for CamlPastaFpPlonkIndex {
40    const NAME: &'static str = "CamlPastaFpPlonkIndex\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_fp_plonk_index_finalize),
47        ..ocaml::custom::DEFAULT_CUSTOM_OPS
48    };
49}
50
51#[ocaml_gen::func]
52#[ocaml::func]
53pub fn caml_pasta_fp_plonk_index_create(
54    gates: CamlPastaFpPlonkGateVectorPtr,
55    public: ocaml::Int,
56    lookup_tables: Vec<CamlLookupTable<CamlFp>>,
57    runtime_tables: Vec<CamlRuntimeTableCfg<CamlFp>>,
58    prev_challenges: ocaml::Int,
59    srs: CamlFpSrs,
60    lazy_mode: bool,
61) -> Result<CamlPastaFpPlonkIndex, ocaml::Error> {
62    let gates: Vec<_> = gates
63        .as_ref()
64        .0
65        .iter()
66        .map(|gate| CircuitGate::<Fp> {
67            typ: gate.typ,
68            wires: gate.wires,
69            coeffs: gate.coeffs.clone(),
70        })
71        .collect();
72
73    let runtime_tables: Vec<RuntimeTableCfg<Fp>> =
74        runtime_tables.into_iter().map(Into::into).collect();
75
76    let lookup_tables: Vec<LookupTable<Fp>> = lookup_tables.into_iter().map(Into::into).collect();
77
78    // create constraint system
79    let cs = match ConstraintSystem::<Fp>::create(gates)
80        .public(public as usize)
81        .prev_challenges(prev_challenges as usize)
82        .max_poly_size(Some(srs.0.max_poly_size()))
83        .lookup(lookup_tables)
84        .runtime(if runtime_tables.is_empty() {
85            None
86        } else {
87            Some(runtime_tables)
88        })
89        .lazy_mode(lazy_mode)
90        .build()
91    {
92        Err(e) => return Err(e.into()),
93        Ok(cs) => cs,
94    };
95
96    // endo
97    let (endo_q, _endo_r) = poly_commitment::ipa::endos::<Pallas>();
98
99    srs.0.with_lagrange_basis(cs.domain.d1);
100
101    // create index
102    let mut index =
103        ProverIndex::<Vesta, OpeningProof<Vesta>>::create(cs, endo_q, srs.clone(), lazy_mode);
104    // Compute and cache the verifier index digest
105    index.compute_verifier_index_digest::<DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>>();
106
107    Ok(CamlPastaFpPlonkIndex(Box::new(index)))
108}
109
110#[ocaml_gen::func]
111#[ocaml::func]
112pub fn caml_pasta_fp_plonk_index_max_degree(index: CamlPastaFpPlonkIndexPtr) -> ocaml::Int {
113    index.as_ref().0.srs.max_poly_size() as isize
114}
115
116#[ocaml_gen::func]
117#[ocaml::func]
118pub fn caml_pasta_fp_plonk_index_public_inputs(index: CamlPastaFpPlonkIndexPtr) -> ocaml::Int {
119    index.as_ref().0.cs.public as isize
120}
121
122#[ocaml_gen::func]
123#[ocaml::func]
124pub fn caml_pasta_fp_plonk_index_domain_d1_size(index: CamlPastaFpPlonkIndexPtr) -> ocaml::Int {
125    index.as_ref().0.cs.domain.d1.size() as isize
126}
127
128#[ocaml_gen::func]
129#[ocaml::func]
130pub fn caml_pasta_fp_plonk_index_domain_d4_size(index: CamlPastaFpPlonkIndexPtr) -> ocaml::Int {
131    index.as_ref().0.cs.domain.d4.size() as isize
132}
133
134#[ocaml_gen::func]
135#[ocaml::func]
136pub fn caml_pasta_fp_plonk_index_domain_d8_size(index: CamlPastaFpPlonkIndexPtr) -> ocaml::Int {
137    index.as_ref().0.cs.domain.d8.size() as isize
138}
139
140#[ocaml_gen::func]
141#[ocaml::func]
142pub fn caml_pasta_fp_plonk_index_read(
143    offset: Option<ocaml::Int>,
144    srs: CamlFpSrs,
145    path: String,
146) -> Result<CamlPastaFpPlonkIndex, ocaml::Error> {
147    // open the file for reading
148    let file = match File::open(path) {
149        Err(_) => {
150            return Err(
151                ocaml::Error::invalid_argument("caml_pasta_fp_plonk_index_read")
152                    .err()
153                    .unwrap(),
154            )
155        }
156        Ok(file) => file,
157    };
158    let mut r = BufReader::new(file);
159
160    // optional offset in file
161    if let Some(offset) = offset {
162        r.seek(Start(offset as u64))?;
163    }
164
165    // deserialize the index
166    let mut t = ProverIndex::<Vesta, OpeningProof<Vesta>>::deserialize(
167        &mut rmp_serde::Deserializer::new(r),
168    )?;
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(CamlPastaFpPlonkIndex(Box::new(t)))
176}
177
178#[ocaml_gen::func]
179#[ocaml::func]
180pub fn caml_pasta_fp_plonk_index_write(
181    append: Option<bool>,
182    index: CamlPastaFpPlonkIndexPtr<'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_fp_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}