kimchi_stubs/
pasta_fp_plonk_index.rs

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