ivc/
verifier.rs

1use crate::{
2    expr_eval::GenericEvalEnv,
3    plonkish_lang::{PlonkishChallenge, PlonkishWitnessGeneric},
4    prover::Proof,
5};
6use ark_ff::Field;
7use ark_poly::{
8    univariate::DensePolynomial, EvaluationDomain, Evaluations, Polynomial,
9    Radix2EvaluationDomain as R2D,
10};
11use folding::{
12    eval_leaf::EvalLeaf, instance_witness::ExtendedWitness, FoldingCompatibleExpr, FoldingConfig,
13};
14use kimchi::{
15    self, circuits::domains::EvaluationDomains, curve::KimchiCurve, groupmap::GroupMap,
16    plonk_sponge::FrSponge, proof::PointEvaluations,
17};
18use kimchi_msm::columns::Column as GenericColumn;
19use mina_poseidon::{sponge::ScalarChallenge, FqSponge};
20use poly_commitment::{
21    commitment::{
22        absorb_commitment, combined_inner_product, BatchEvaluationProof, CommitmentCurve,
23        Evaluation, PolyComm,
24    },
25    kzg::{KZGProof, PairingSRS},
26    OpenProof, SRS,
27};
28use rand::thread_rng;
29use rayon::iter::{IntoParallelIterator, ParallelIterator};
30
31pub type Pairing = kimchi_msm::BN254;
32/// The curve we commit into
33pub type G = kimchi_msm::BN254G1Affine;
34/// Scalar field of the curve.
35pub type Fp = kimchi_msm::Fp;
36/// The base field of the curve
37/// Used to encode the polynomial commitments
38pub type Fq = ark_bn254::Fq;
39
40pub fn verify<
41    EFqSponge: Clone + FqSponge<Fq, G, Fp>,
42    EFrSponge: FrSponge<Fp>,
43    FC: FoldingConfig<Column = GenericColumn<usize>, Curve = G, Challenge = PlonkishChallenge>,
44    const N_WIT: usize,
45    const N_REL: usize,
46    const N_DSEL: usize,
47    const N_FSEL: usize,
48    const NPUB: usize,
49>(
50    domain: EvaluationDomains<Fp>,
51    srs: &PairingSRS<Pairing>,
52    combined_expr: &FoldingCompatibleExpr<FC>,
53    fixed_selectors: Box<[Evaluations<Fp, R2D<Fp>>; N_FSEL]>,
54    proof: &Proof<N_WIT, N_REL, N_DSEL, N_FSEL, G, KZGProof<Pairing>>,
55) -> bool {
56    assert!(N_WIT == N_REL + N_DSEL);
57
58    let Proof {
59        proof_comms,
60        proof_evals,
61        opening_proof,
62        ..
63    } = proof;
64
65    ////////////////////////////////////////////////////////////////////////////
66    // Re-evaluating public inputs
67    ////////////////////////////////////////////////////////////////////////////
68
69    let fixed_selectors_evals_d1: Box<[Evaluations<Fp, R2D<Fp>>; N_FSEL]> = fixed_selectors;
70
71    let fixed_selectors_polys: Box<[DensePolynomial<Fp>; N_FSEL]> = {
72        o1_utils::array::vec_to_boxed_array(
73            fixed_selectors_evals_d1
74                .into_par_iter()
75                .map(|evals| evals.interpolate())
76                .collect(),
77        )
78    };
79
80    let fixed_selectors_comms: Box<[PolyComm<G>; N_FSEL]> = {
81        let comm = |poly: &DensePolynomial<Fp>| srs.commit_non_hiding(poly, 1);
82        o1_utils::array::vec_to_boxed_array(
83            fixed_selectors_polys
84                .as_ref()
85                .into_par_iter()
86                .map(comm)
87                .collect(),
88        )
89    };
90
91    ////////////////////////////////////////////////////////////////////////////
92    // Absorbing all the commitments to the columns
93    ////////////////////////////////////////////////////////////////////////////
94
95    let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params());
96
97    fixed_selectors_comms
98        .as_ref()
99        .iter()
100        .chain(&proof_comms.witness_comms)
101        .for_each(|comm| absorb_commitment(&mut fq_sponge, comm));
102
103    ////////////////////////////////////////////////////////////////////////////
104    // Quotient polynomial
105    ////////////////////////////////////////////////////////////////////////////
106
107    absorb_commitment(&mut fq_sponge, &proof_comms.t_comm);
108
109    // -- Preparing for opening proof verification
110    let zeta_chal = ScalarChallenge(fq_sponge.challenge());
111    let (_, endo_r) = G::endos();
112    let zeta: Fp = zeta_chal.to_field(endo_r);
113    let omega = domain.d1.group_gen;
114    let zeta_omega = zeta * omega;
115
116    let mut coms_and_evaluations: Vec<Evaluation<_>> = vec![];
117
118    coms_and_evaluations.extend(
119        (&proof_comms.witness_comms)
120            .into_iter()
121            .zip(&proof_evals.witness_evals)
122            .map(|(commitment, point_eval)| Evaluation {
123                commitment: commitment.clone(),
124                evaluations: vec![vec![point_eval.zeta], vec![point_eval.zeta_omega]],
125            }),
126    );
127
128    coms_and_evaluations.extend(
129        (fixed_selectors_comms)
130            .into_iter()
131            .zip(proof_evals.fixed_selectors_evals.iter())
132            .map(|(commitment, point_eval)| Evaluation {
133                commitment,
134                evaluations: vec![vec![point_eval.zeta], vec![point_eval.zeta_omega]],
135            }),
136    );
137
138    // -- Absorb all coms_and_evaluations
139    let fq_sponge_before_coms_and_evaluations = fq_sponge.clone();
140    let mut fr_sponge = EFrSponge::new(G::sponge_params());
141    fr_sponge.absorb(&fq_sponge.digest());
142
143    for PointEvaluations { zeta, zeta_omega } in (&proof_evals.witness_evals).into_iter() {
144        fr_sponge.absorb(zeta);
145        fr_sponge.absorb(zeta_omega);
146    }
147
148    for PointEvaluations { zeta, zeta_omega } in proof_evals.fixed_selectors_evals.as_ref().iter() {
149        fr_sponge.absorb(zeta);
150        fr_sponge.absorb(zeta_omega);
151    }
152
153    // Compute [ft(X)] = \
154    //   (1 - ζ^n) \
155    //    ([t_0(X)] + ζ^n [t_1(X)] + ... + ζ^{kn} [t_{k}(X)])
156    let ft_comm = {
157        let evaluation_point_to_domain_size = zeta.pow([domain.d1.size]);
158        let chunked_t_comm = proof_comms
159            .t_comm
160            .chunk_commitment(evaluation_point_to_domain_size);
161
162        // (1 - ζ^n)
163        let minus_vanishing_poly_at_zeta: Fp = -domain.d1.vanishing_polynomial().evaluate(&zeta);
164        chunked_t_comm.scale(minus_vanishing_poly_at_zeta)
165    };
166
167    let ft_eval0 = {
168        let witness_evals_vecs = (&proof_evals.witness_evals)
169            .into_iter()
170            .map(|x| vec![x.zeta])
171            .collect::<Vec<_>>()
172            .try_into()
173            .unwrap();
174        let fixed_selectors_evals_vecs = proof_evals
175            .fixed_selectors_evals
176            .into_iter()
177            .map(|x| vec![x.zeta])
178            .collect::<Vec<_>>()
179            .try_into()
180            .unwrap();
181        let error_vec = vec![proof_evals.error_vec.zeta];
182
183        let alphas = proof.alphas.clone();
184        let challenges = proof.challenges;
185        let u = proof.u;
186
187        let eval_env: GenericEvalEnv<G, N_WIT, N_FSEL, Vec<Fp>> = {
188            let ext_witness = ExtendedWitness {
189                witness: PlonkishWitnessGeneric {
190                    witness: witness_evals_vecs,
191                    fixed_selectors: fixed_selectors_evals_vecs,
192                    phantom: core::marker::PhantomData,
193                },
194                extended: Default::default(),
195            };
196
197            GenericEvalEnv {
198                ext_witness,
199                alphas,
200                challenges,
201                error_vec,
202                u,
203            }
204        };
205
206        let eval_res: Vec<_> = match eval_env.eval_naive_fcompat(combined_expr) {
207            EvalLeaf::Result(x) => x,
208            EvalLeaf::Col(x) => x.to_vec(),
209            _ => panic!("eval_leaf is not Result"),
210        };
211
212        // Note the minus! ft polynomial at zeta (ft_eval0) is minus evaluation of the expression.
213        -eval_res[0]
214    };
215
216    coms_and_evaluations.push(Evaluation {
217        commitment: ft_comm,
218        evaluations: vec![vec![ft_eval0], vec![proof_evals.ft_eval1]],
219    });
220
221    fr_sponge.absorb(&proof_evals.ft_eval1);
222    // -- End absorb all coms_and_evaluations
223
224    let v_chal = fr_sponge.challenge();
225    let v = v_chal.to_field(endo_r);
226    let u_chal = fr_sponge.challenge();
227    let u = u_chal.to_field(endo_r);
228
229    let combined_inner_product = {
230        let es: Vec<_> = coms_and_evaluations
231            .iter()
232            .map(|Evaluation { evaluations, .. }| evaluations.clone())
233            .collect();
234
235        combined_inner_product(&v, &u, es.as_slice())
236    };
237
238    let batch = BatchEvaluationProof {
239        sponge: fq_sponge_before_coms_and_evaluations,
240        evaluations: coms_and_evaluations,
241        evaluation_points: vec![zeta, zeta_omega],
242        polyscale: v,
243        evalscale: u,
244        opening: opening_proof,
245        combined_inner_product,
246    };
247
248    let group_map = <G as CommitmentCurve>::Map::setup();
249    OpenProof::verify(srs, &group_map, &mut [batch], &mut thread_rng())
250}