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