kimchi/
prover_index.rs

1//! This module implements the prover index as [`ProverIndex`].
2
3use crate::{
4    alphas::Alphas,
5    circuits::{
6        berkeley_columns::{BerkeleyChallengeTerm, Column},
7        constraints::{ColumnEvaluations, ConstraintSystem},
8        expr::{Linearization, PolishToken},
9    },
10    curve::KimchiCurve,
11    linearization::expr_linearization,
12    o1_utils::lazy_cache::LazyCache,
13    verifier_index::VerifierIndex,
14};
15use ark_ff::PrimeField;
16use mina_poseidon::FqSponge;
17use poly_commitment::SRS;
18use serde::{de::DeserializeOwned, Deserialize, Serialize};
19use serde_with::serde_as;
20use std::sync::Arc;
21
22/// The index used by the prover
23#[serde_as]
24#[derive(Serialize, Deserialize, Clone, Debug)]
25//~spec:startcode
26pub struct ProverIndex<const FULL_ROUNDS: usize, G: KimchiCurve<FULL_ROUNDS>, Srs> {
27    /// constraints system polynomials
28    #[serde(bound = "ConstraintSystem<G::ScalarField>: Serialize + DeserializeOwned")]
29    pub cs: Arc<ConstraintSystem<G::ScalarField>>,
30
31    /// The symbolic linearization of our circuit, which can compile to concrete types once certain values are learned in the protocol.
32    #[serde(skip)]
33    pub linearization:
34        Linearization<Vec<PolishToken<G::ScalarField, Column, BerkeleyChallengeTerm>>, Column>,
35
36    /// The mapping between powers of alpha and constraints
37    #[serde(skip)]
38    pub powers_of_alpha: Alphas<G::ScalarField>,
39
40    /// polynomial commitment keys
41    #[serde(skip)]
42    pub srs: Arc<Srs>,
43
44    /// maximal size of polynomial section
45    pub max_poly_size: usize,
46
47    #[serde(bound = "ColumnEvaluations<G::ScalarField>: Serialize + DeserializeOwned")]
48    pub column_evaluations: Arc<LazyCache<ColumnEvaluations<G::ScalarField>>>,
49
50    /// The verifier index corresponding to this prover index
51    #[serde(skip)]
52    pub verifier_index: Option<VerifierIndex<FULL_ROUNDS, G, Srs>>,
53
54    /// The verifier index digest corresponding to this prover index
55    #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
56    pub verifier_index_digest: Option<G::BaseField>,
57}
58//~spec:endcode
59
60impl<const FULL_ROUNDS: usize, G: KimchiCurve<FULL_ROUNDS>, Srs: SRS<G>>
61    ProverIndex<FULL_ROUNDS, G, Srs>
62where
63    G::BaseField: PrimeField,
64{
65    /// this function compiles the index from constraints
66    pub fn create(
67        mut cs: ConstraintSystem<G::ScalarField>,
68        endo_q: G::ScalarField,
69        srs: Arc<Srs>,
70        lazy_mode: bool,
71    ) -> Self {
72        let max_poly_size = srs.max_poly_size();
73        cs.endo = endo_q;
74
75        // pre-compute the linearization
76        let (linearization, powers_of_alpha) = expr_linearization(Some(&cs.feature_flags), true);
77
78        let evaluated_column_coefficients = cs.evaluated_column_coefficients();
79
80        let cs = Arc::new(cs);
81        let cs_clone = Arc::clone(&cs);
82        let column_evaluations =
83            LazyCache::new(move || cs_clone.column_evaluations(&evaluated_column_coefficients));
84        if !lazy_mode {
85            // precompute the values
86            column_evaluations.get();
87        };
88
89        ProverIndex {
90            cs,
91            linearization,
92            powers_of_alpha,
93            srs,
94            max_poly_size,
95            column_evaluations: Arc::new(column_evaluations),
96            verifier_index: None,
97            verifier_index_digest: None,
98        }
99    }
100
101    /// Retrieve or compute the digest for the corresponding verifier index.
102    /// If the digest is not already cached inside the index, store it.
103    pub fn compute_verifier_index_digest<
104        EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
105    >(
106        &mut self,
107    ) -> G::BaseField
108    where
109        VerifierIndex<FULL_ROUNDS, G, Srs>: Clone,
110    {
111        if let Some(verifier_index_digest) = self.verifier_index_digest {
112            return verifier_index_digest;
113        }
114
115        if self.verifier_index.is_none() {
116            self.verifier_index = Some(self.verifier_index());
117        }
118
119        let verifier_index_digest = self.verifier_index_digest::<EFqSponge>();
120        self.verifier_index_digest = Some(verifier_index_digest);
121        verifier_index_digest
122    }
123
124    /// Retrieve or compute the digest for the corresponding verifier index.
125    pub fn verifier_index_digest<
126        EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
127    >(
128        &self,
129    ) -> G::BaseField
130    where
131        VerifierIndex<FULL_ROUNDS, G, Srs>: Clone,
132    {
133        if let Some(verifier_index_digest) = self.verifier_index_digest {
134            return verifier_index_digest;
135        }
136
137        match &self.verifier_index {
138            None => {
139                let verifier_index = self.verifier_index();
140                verifier_index.digest::<EFqSponge>()
141            }
142            Some(verifier_index) => verifier_index.digest::<EFqSponge>(),
143        }
144    }
145}
146
147pub mod testing {
148    use super::*;
149    use crate::circuits::{
150        gate::CircuitGate,
151        lookup::{runtime_tables::RuntimeTableCfg, tables::LookupTable},
152    };
153    use ark_ff::PrimeField;
154    use ark_poly::{EvaluationDomain, Radix2EvaluationDomain as D};
155    use poly_commitment::{ipa::OpeningProof, precomputed_srs, OpenProof, SRS};
156
157    #[allow(clippy::too_many_arguments)]
158    pub fn new_index_for_test_with_lookups_and_custom_srs<const FULL_ROUNDS: usize, G, Srs, F>(
159        gates: Vec<CircuitGate<G::ScalarField>>,
160        public: usize,
161        prev_challenges: usize,
162        lookup_tables: Vec<LookupTable<G::ScalarField>>,
163        runtime_tables: Option<Vec<RuntimeTableCfg<G::ScalarField>>>,
164        disable_gates_checks: bool,
165        override_srs_size: Option<usize>,
166        mut get_srs: F,
167        lazy_mode: bool,
168    ) -> ProverIndex<FULL_ROUNDS, G, Srs>
169    where
170        G: KimchiCurve<FULL_ROUNDS>,
171        Srs: SRS<G>,
172        F: FnMut(D<G::ScalarField>, usize) -> Srs,
173        G::BaseField: PrimeField,
174        G::ScalarField: PrimeField,
175    {
176        // not sure if theres a smarter way instead of the double unwrap, but should be fine in the test
177        let cs = ConstraintSystem::<G::ScalarField>::create(gates)
178            .lookup(lookup_tables)
179            .runtime(runtime_tables)
180            .public(public)
181            .prev_challenges(prev_challenges)
182            .disable_gates_checks(disable_gates_checks)
183            .max_poly_size(override_srs_size)
184            .lazy_mode(lazy_mode)
185            .build()
186            .unwrap();
187
188        let srs_size = override_srs_size.unwrap_or_else(|| cs.domain.d1.size());
189        let srs = get_srs(cs.domain.d1, srs_size);
190        let srs = Arc::new(srs);
191
192        let &endo_q = G::other_curve_endo();
193        ProverIndex::create(cs, endo_q, srs, lazy_mode)
194    }
195
196    /// Create new index for lookups.
197    ///
198    /// # Panics
199    ///
200    /// Will panic if `constraint system` is not built with `gates` input.
201    pub fn new_index_for_test_with_lookups<const FULL_ROUNDS: usize, G: KimchiCurve<FULL_ROUNDS>>(
202        gates: Vec<CircuitGate<G::ScalarField>>,
203        public: usize,
204        prev_challenges: usize,
205        lookup_tables: Vec<LookupTable<G::ScalarField>>,
206        runtime_tables: Option<Vec<RuntimeTableCfg<G::ScalarField>>>,
207        disable_gates_checks: bool,
208        override_srs_size: Option<usize>,
209        lazy_mode: bool,
210    ) -> ProverIndex<FULL_ROUNDS, G, <OpeningProof<G, FULL_ROUNDS> as OpenProof<G, FULL_ROUNDS>>::SRS>
211    where
212        G::BaseField: PrimeField,
213        G::ScalarField: PrimeField,
214    {
215        new_index_for_test_with_lookups_and_custom_srs::<FULL_ROUNDS, _, _, _>(
216            gates,
217            public,
218            prev_challenges,
219            lookup_tables,
220            runtime_tables,
221            disable_gates_checks,
222            override_srs_size,
223            |d1: D<G::ScalarField>, size: usize| {
224                let log2_size = size.ilog2();
225                let srs = if log2_size <= precomputed_srs::SERIALIZED_SRS_SIZE {
226                    // TODO: we should trim it if it's smaller
227                    precomputed_srs::get_srs_test()
228                } else {
229                    // TODO: we should resume the SRS generation starting from the serialized one
230                    SRS::<G>::create(size)
231                };
232
233                srs.get_lagrange_basis(d1);
234                srs
235            },
236            lazy_mode,
237        )
238    }
239
240    pub fn new_index_for_test<const FULL_ROUNDS: usize, G: KimchiCurve<FULL_ROUNDS>>(
241        gates: Vec<CircuitGate<G::ScalarField>>,
242        public: usize,
243    ) -> ProverIndex<FULL_ROUNDS, G, poly_commitment::ipa::SRS<G>>
244    where
245        G::BaseField: PrimeField,
246        G::ScalarField: PrimeField,
247    {
248        new_index_for_test_with_lookups::<FULL_ROUNDS, G>(
249            gates,
250            public,
251            0,
252            vec![],
253            None,
254            false,
255            None,
256            false,
257        )
258    }
259}