jf_plonk/proof_system/
structs.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the Jellyfish library.
3
4// You should have received a copy of the MIT License
5// along with the Jellyfish library. If not, see <https://mit-license.org/>.
6
7//! Data structures used in Plonk proof systems
8use crate::{
9    circuit::plonk_verifier::{BatchProofVar, ProofEvaluationsVar},
10    errors::{
11        PlonkError,
12        SnarkError::{self, ParameterError, SnarkLookupUnsupported},
13    },
14};
15use ark_ec::{
16    pairing::Pairing,
17    scalar_mul::variable_base::VariableBaseMSM,
18    short_weierstrass::{Affine, SWCurveConfig},
19    CurveGroup,
20};
21use ark_ff::{FftField, Field, Fp2, Fp2Config, PrimeField, Zero};
22use ark_poly::univariate::DensePolynomial;
23use ark_serialize::*;
24use ark_std::{format, string::ToString, vec, vec::Vec};
25use espresso_systems_common::jellyfish::tag;
26use hashbrown::HashMap;
27use jf_pcs::prelude::{
28    Commitment, UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam,
29};
30use jf_relation::{
31    constants::{compute_coset_representatives, GATE_WIDTH, N_TURBO_PLONK_SELECTORS},
32    gadgets::{
33        ecc::{SWToTEConParam, TEPoint},
34        ultraplonk::mod_arith::FpElemVar,
35    },
36    PlonkCircuit,
37};
38use jf_rescue::RescueParameter;
39use jf_utils::{field_switching, fq_to_fr, fr_to_fq};
40use tagged_base64::tagged;
41
42/// Universal StructuredReferenceString
43pub type UniversalSrs<E> = UnivariateUniversalParams<E>;
44/// Commitment key
45pub type CommitKey<E> = UnivariateProverParam<E>;
46/// Key for verifying PCS opening proof.
47pub type OpenKey<E> = UnivariateVerifierParam<E>;
48
49/// A Plonk SNARK proof.
50#[tagged(tag::PROOF)]
51#[derive(Debug, Clone, Eq, CanonicalSerialize, CanonicalDeserialize, Derivative)]
52#[derivative(PartialEq, Hash(bound = "E:Pairing"))]
53pub struct Proof<E: Pairing> {
54    /// Wire witness polynomials commitments.
55    pub wires_poly_comms: Vec<Commitment<E>>,
56
57    /// The polynomial commitment for the wire permutation argument.
58    pub prod_perm_poly_comm: Commitment<E>,
59
60    /// Split quotient polynomial commitments.
61    pub split_quot_poly_comms: Vec<Commitment<E>>,
62
63    /// (Aggregated) proof of evaluations at challenge point `zeta`.
64    pub opening_proof: Commitment<E>,
65
66    /// (Aggregated) proof of evaluation at challenge point `zeta * g` where `g`
67    /// is the root of unity.
68    pub shifted_opening_proof: Commitment<E>,
69
70    /// Polynomial evaluations.
71    pub poly_evals: ProofEvaluations<E::ScalarField>,
72
73    /// The partial proof for Plookup argument
74    pub plookup_proof: Option<PlookupProof<E>>,
75}
76
77impl<E, P> TryFrom<Vec<E::BaseField>> for Proof<E>
78where
79    E: Pairing<G1Affine = Affine<P>>,
80    P: SWCurveConfig<BaseField = E::BaseField, ScalarField = E::ScalarField>,
81{
82    type Error = SnarkError;
83
84    fn try_from(value: Vec<E::BaseField>) -> Result<Self, Self::Error> {
85        // both wires_poly_comms and split_quot_poly_comms are (GATE_WIDTH +1)
86        // // Commitments, each point takes two base fields elements;
87        // 3 individual commitment points;
88        // (GATE_WIDTH + 1) * 2 scalar fields in poly_evals are  converted to base
89        // fields.
90        const TURBO_PLONK_LEN: usize = (GATE_WIDTH + 1) * 2 * 2 + 2 * 3 + (GATE_WIDTH + 1) * 2;
91        if value.len() == TURBO_PLONK_LEN {
92            // NOTE: for convenience, we slightly reordered our fields in Proof.
93            let mut ptr = 0;
94            let wires_poly_comms: Vec<Commitment<E>> = value[ptr..ptr + (GATE_WIDTH + 1) * 2]
95                .chunks_exact(2)
96                .map(|chunk| {
97                    if chunk.len() == 2 {
98                        Commitment(Affine::new(chunk[0], chunk[1]))
99                    } else {
100                        unreachable!("Internal error");
101                    }
102                })
103                .collect();
104            ptr += (GATE_WIDTH + 1) * 2;
105
106            let split_quot_poly_comms = value[ptr..ptr + (GATE_WIDTH + 1) * 2]
107                .chunks_exact(2)
108                .map(|chunk| {
109                    if chunk.len() == 2 {
110                        Commitment(Affine::new(chunk[0], chunk[1]))
111                    } else {
112                        unreachable!("Internal error");
113                    }
114                })
115                .collect();
116            ptr += (GATE_WIDTH + 1) * 2;
117
118            let prod_perm_poly_comm = Commitment(Affine::new(value[ptr], value[ptr + 1]));
119            ptr += 2;
120
121            let opening_proof = Commitment(Affine::new(value[ptr], value[ptr + 1]));
122            ptr += 2;
123
124            let shifted_opening_proof = Commitment(Affine::new(value[ptr], value[ptr + 1]));
125            ptr += 2;
126
127            let poly_evals_scalars: Vec<E::ScalarField> = value[ptr..]
128                .iter()
129                .map(|f| fq_to_fr::<E::BaseField, P>(f))
130                .collect();
131            let poly_evals = poly_evals_scalars.try_into()?;
132
133            Ok(Self {
134                wires_poly_comms,
135                prod_perm_poly_comm,
136                split_quot_poly_comms,
137                opening_proof,
138                shifted_opening_proof,
139                poly_evals,
140                plookup_proof: None,
141            })
142        } else {
143            Err(SnarkError::ParameterError(
144                "Wrong number of scalars for proof, only support TurboPlonk for now".to_string(),
145            ))
146        }
147    }
148}
149
150// helper function to convert a G1Affine or G2Affine into two base fields
151fn group1_to_fields<E, P>(p: Affine<P>) -> Vec<E::BaseField>
152where
153    E: Pairing<G1Affine = Affine<P>>,
154    P: SWCurveConfig<BaseField = E::BaseField>,
155{
156    // contains x, y, infinity_flag, only need the first 2 field elements
157    vec![p.x, p.y]
158}
159
160fn group2_to_fields<E, F, P>(p: Affine<P>) -> Vec<E::BaseField>
161where
162    E: Pairing<G2Affine = Affine<P>>,
163    F: Fp2Config<Fp = E::BaseField>,
164    P: SWCurveConfig<BaseField = Fp2<F>>,
165{
166    // contains x, y, infinity_flag, only need the first 2 field elements
167    vec![p.x.c0, p.x.c1, p.y.c0, p.y.c1]
168}
169
170impl<E, P> From<Proof<E>> for Vec<E::BaseField>
171where
172    E: Pairing<G1Affine = Affine<P>>,
173    P: SWCurveConfig<BaseField = E::BaseField, ScalarField = E::ScalarField>,
174{
175    fn from(proof: Proof<E>) -> Self {
176        if proof.plookup_proof.is_some() {
177            panic!("Only support TurboPlonk for now.");
178        }
179        let poly_evals_scalars: Vec<E::ScalarField> = proof.poly_evals.into();
180
181        // NOTE: order of these fields must match deserialization
182        [
183            proof
184                .wires_poly_comms
185                .iter()
186                .map(|cm| group1_to_fields::<E, _>(cm.0))
187                .collect::<Vec<_>>()
188                .concat(),
189            proof
190                .split_quot_poly_comms
191                .iter()
192                .map(|cm| group1_to_fields::<E, _>(cm.0))
193                .collect::<Vec<_>>()
194                .concat(),
195            group1_to_fields::<E, _>(proof.prod_perm_poly_comm.0),
196            group1_to_fields::<E, _>(proof.opening_proof.0),
197            group1_to_fields::<E, _>(proof.shifted_opening_proof.0),
198            poly_evals_scalars
199                .iter()
200                .map(|s| fr_to_fq::<E::BaseField, P>(s))
201                .collect::<Vec<_>>(),
202        ]
203        .concat()
204    }
205}
206
207/// A Plookup argument proof.
208#[derive(Debug, Clone, Eq, CanonicalSerialize, CanonicalDeserialize, Derivative)]
209#[derivative(PartialEq, Hash(bound = "E:Pairing"))]
210pub struct PlookupProof<E: Pairing> {
211    /// The commitments for the polynomials that interpolate the sorted
212    /// concatenation of the lookup table and the witnesses in the lookup gates.
213    pub(crate) h_poly_comms: Vec<Commitment<E>>,
214
215    /// The product accumulation polynomial commitment for the Plookup argument
216    pub(crate) prod_lookup_poly_comm: Commitment<E>,
217
218    /// Polynomial evaluations.
219    pub(crate) poly_evals: PlookupEvaluations<E::ScalarField>,
220}
221
222/// An aggregated SNARK proof that batchly proving multiple instances.
223#[tagged(tag::BATCHPROOF)]
224#[derive(Debug, Clone, Eq, CanonicalSerialize, CanonicalDeserialize, Derivative)]
225#[derivative(PartialEq, Hash(bound = "E:Pairing"))]
226pub struct BatchProof<E: Pairing> {
227    /// The list of wire witness polynomials commitments.
228    pub(crate) wires_poly_comms_vec: Vec<Vec<Commitment<E>>>,
229
230    /// The list of polynomial commitment for the wire permutation argument.
231    pub(crate) prod_perm_poly_comms_vec: Vec<Commitment<E>>,
232
233    /// The list of polynomial evaluations.
234    pub(crate) poly_evals_vec: Vec<ProofEvaluations<E::ScalarField>>,
235
236    /// The list of partial proofs for Plookup argument
237    pub(crate) plookup_proofs_vec: Vec<Option<PlookupProof<E>>>,
238
239    /// Split quotient polynomial commitments.
240    pub(crate) split_quot_poly_comms: Vec<Commitment<E>>,
241
242    /// (Aggregated) proof of evaluations at challenge point `zeta`.
243    pub(crate) opening_proof: Commitment<E>,
244
245    /// (Aggregated) proof of evaluation at challenge point `zeta * g` where `g`
246    /// is the root of unity.
247    pub(crate) shifted_opening_proof: Commitment<E>,
248}
249
250impl<E: Pairing> BatchProof<E> {
251    /// The number of instances being proved in a batch proof.
252    pub fn len(&self) -> usize {
253        self.prod_perm_poly_comms_vec.len()
254    }
255    /// Check whether a BatchProof proves nothing.
256    pub fn is_empty(&self) -> bool {
257        self.prod_perm_poly_comms_vec.is_empty()
258    }
259    /// Create a dummy batch proof over `n` TurboPlonk instances.
260    pub fn dummy(n: usize) -> Self {
261        let num_wire_types = GATE_WIDTH + 1;
262        Self {
263            wires_poly_comms_vec: vec![vec![Commitment::default(); num_wire_types]; n],
264            prod_perm_poly_comms_vec: vec![Commitment::default(); n],
265            poly_evals_vec: vec![ProofEvaluations::default(); n],
266            plookup_proofs_vec: vec![None; n],
267            split_quot_poly_comms: vec![Commitment::default(); num_wire_types],
268            opening_proof: Commitment::default(),
269            shifted_opening_proof: Commitment::default(),
270        }
271    }
272}
273
274impl<E: Pairing> From<Proof<E>> for BatchProof<E> {
275    fn from(proof: Proof<E>) -> Self {
276        Self {
277            wires_poly_comms_vec: vec![proof.wires_poly_comms],
278            prod_perm_poly_comms_vec: vec![proof.prod_perm_poly_comm],
279            poly_evals_vec: vec![proof.poly_evals],
280            plookup_proofs_vec: vec![proof.plookup_proof],
281            split_quot_poly_comms: proof.split_quot_poly_comms,
282            opening_proof: proof.opening_proof,
283            shifted_opening_proof: proof.shifted_opening_proof,
284        }
285    }
286}
287
288impl<T: PrimeField> ProofEvaluations<T> {
289    /// create variables for the ProofEvaluations who's field
290    /// is smaller than plonk circuit field.
291    /// The output wires are in the FpElemVar form.
292    pub(crate) fn create_variables<F>(
293        &self,
294        circuit: &mut PlonkCircuit<F>,
295        m: usize,
296        two_power_m: Option<F>,
297    ) -> Result<ProofEvaluationsVar<F>, PlonkError>
298    where
299        F: RescueParameter + SWToTEConParam,
300    {
301        if T::MODULUS_BIT_SIZE >= F::MODULUS_BIT_SIZE {
302            return Err(PlonkError::InvalidParameters(format!(
303                "circuit field size {} is not greater than Plookup Evaluation field size {}",
304                F::MODULUS_BIT_SIZE,
305                T::MODULUS_BIT_SIZE
306            )));
307        }
308        let wires_evals = self
309            .wires_evals
310            .iter()
311            .map(|x| {
312                FpElemVar::new_from_field_element(circuit, &field_switching(x), m, two_power_m)
313            })
314            .collect::<Result<Vec<_>, _>>()?;
315        let wire_sigma_evals = self
316            .wire_sigma_evals
317            .iter()
318            .map(|x| {
319                FpElemVar::new_from_field_element(circuit, &field_switching(x), m, two_power_m)
320            })
321            .collect::<Result<Vec<_>, _>>()?;
322        Ok(ProofEvaluationsVar {
323            wires_evals,
324            wire_sigma_evals,
325            perm_next_eval: FpElemVar::new_from_field_element(
326                circuit,
327                &field_switching(&self.perm_next_eval),
328                m,
329                two_power_m,
330            )?,
331        })
332    }
333}
334
335impl<E: Pairing> BatchProof<E> {
336    /// Create a `BatchProofVar` variable from a `BatchProof`.
337    pub fn create_variables<F, P>(
338        &self,
339        circuit: &mut PlonkCircuit<F>,
340        m: usize,
341        two_power_m: Option<F>,
342    ) -> Result<BatchProofVar<F>, PlonkError>
343    where
344        E: Pairing<BaseField = F, G1Affine = Affine<P>>,
345        F: RescueParameter + SWToTEConParam,
346        P: SWCurveConfig<BaseField = F>,
347    {
348        let mut wires_poly_comms_vec = Vec::new();
349        for e in self.wires_poly_comms_vec.iter() {
350            let mut tmp = Vec::new();
351            for f in e.iter() {
352                let p: TEPoint<F> = f.0.into();
353                tmp.push(circuit.create_point_variable(p)?);
354            }
355            wires_poly_comms_vec.push(tmp);
356        }
357        let mut prod_perm_poly_comms_vec = Vec::new();
358        for e in self.prod_perm_poly_comms_vec.iter() {
359            let p: TEPoint<F> = e.0.into();
360            prod_perm_poly_comms_vec.push(circuit.create_point_variable(p)?);
361        }
362
363        let poly_evals_vec = self
364            .poly_evals_vec
365            .iter()
366            .map(|x| x.create_variables(circuit, m, two_power_m))
367            .collect::<Result<Vec<_>, _>>()?;
368
369        let mut split_quot_poly_comms = Vec::new();
370        for e in self.split_quot_poly_comms.iter() {
371            let p: TEPoint<F> = e.0.into();
372            split_quot_poly_comms.push(circuit.create_point_variable(p)?);
373        }
374
375        let p: TEPoint<F> = self.opening_proof.0.into();
376        let opening_proof = circuit.create_point_variable(p)?;
377
378        let p: TEPoint<F> = self.shifted_opening_proof.0.into();
379        let shifted_opening_proof = circuit.create_point_variable(p)?;
380
381        Ok(BatchProofVar {
382            wires_poly_comms_vec,
383            prod_perm_poly_comms_vec,
384            poly_evals_vec,
385            split_quot_poly_comms,
386            opening_proof,
387            shifted_opening_proof,
388        })
389    }
390}
391
392/// A struct that stores the polynomial evaluations in a Plonk proof.
393#[derive(Debug, Clone, PartialEq, Eq, Hash, CanonicalSerialize, CanonicalDeserialize)]
394pub struct ProofEvaluations<F: Field> {
395    /// Wire witness polynomials evaluations at point `zeta`.
396    pub wires_evals: Vec<F>,
397
398    /// Extended permutation (sigma) polynomials evaluations at point `zeta`.
399    /// We do not include the last sigma polynomial evaluation.
400    pub wire_sigma_evals: Vec<F>,
401
402    /// Permutation product polynomial evaluation at point `zeta * g`.
403    pub perm_next_eval: F,
404}
405
406impl<F: Field> TryFrom<Vec<F>> for ProofEvaluations<F> {
407    type Error = SnarkError;
408
409    fn try_from(value: Vec<F>) -> Result<Self, Self::Error> {
410        // | wires_evals | = | wire_sigma_evals | + 1
411        // = GATE_WIDTH + 1 + 0/1 (0 for TurboPlonk and 1 for UltraPlonk)
412        // thanks to Maller optimization.
413        const TURBO_PLONK_EVAL_LEN: usize = (GATE_WIDTH + 1) * 2;
414        const ULTRA_PLONK_EVAL_LEN: usize = (GATE_WIDTH + 2) * 2;
415
416        if value.len() == TURBO_PLONK_EVAL_LEN || value.len() == ULTRA_PLONK_EVAL_LEN {
417            let l = value.len();
418            let wires_evals = value[..l / 2].to_vec();
419            let wire_sigma_evals = value[l / 2..l - 1].to_vec();
420            let perm_next_eval = value[l - 1];
421            Ok(Self {
422                wires_evals,
423                wire_sigma_evals,
424                perm_next_eval,
425            })
426        } else {
427            Err(SnarkError::ParameterError(
428                "Wrong number of scalars for proof evals.".to_string(),
429            ))
430        }
431    }
432}
433
434impl<F: Field> From<ProofEvaluations<F>> for Vec<F> {
435    fn from(evals: ProofEvaluations<F>) -> Self {
436        [
437            evals.wires_evals,
438            evals.wire_sigma_evals,
439            vec![evals.perm_next_eval],
440        ]
441        .concat()
442    }
443}
444
445impl<F> Default for ProofEvaluations<F>
446where
447    F: Field,
448{
449    fn default() -> Self {
450        let num_wire_types = GATE_WIDTH + 1;
451        Self {
452            wires_evals: vec![F::zero(); num_wire_types],
453            wire_sigma_evals: vec![F::zero(); num_wire_types - 1],
454            perm_next_eval: F::zero(),
455        }
456    }
457}
458
459/// A struct that stores the polynomial evaluations in a Plookup argument proof.
460#[derive(Debug, Clone, PartialEq, Eq, Hash, CanonicalSerialize, CanonicalDeserialize)]
461pub struct PlookupEvaluations<F: Field> {
462    /// Range table polynomial evaluation at point `zeta`.
463    pub(crate) range_table_eval: F,
464
465    /// Key table polynomial evaluation at point `zeta`.
466    pub(crate) key_table_eval: F,
467
468    /// Table domain separation polynomial evaluation at point `zeta`.
469    pub(crate) table_dom_sep_eval: F,
470
471    /// Domain separation selector polynomial evaluation at point `zeta`.
472    pub(crate) q_dom_sep_eval: F,
473
474    /// The first sorted vector polynomial evaluation at point `zeta`.
475    pub(crate) h_1_eval: F,
476
477    /// The lookup selector polynomial evaluation at point `zeta`.
478    pub(crate) q_lookup_eval: F,
479
480    /// Lookup product polynomial evaluation at point `zeta * g`.
481    pub(crate) prod_next_eval: F,
482
483    /// Range table polynomial evaluation at point `zeta * g`.
484    pub(crate) range_table_next_eval: F,
485
486    /// Key table polynomial evaluation at point `zeta * g`.
487    pub(crate) key_table_next_eval: F,
488
489    /// Table domain separation polynomial evaluation at point `zeta * g`.
490    pub(crate) table_dom_sep_next_eval: F,
491
492    /// The first sorted vector polynomial evaluation at point `zeta * g`.
493    pub(crate) h_1_next_eval: F,
494
495    /// The second sorted vector polynomial evaluation at point `zeta * g`.
496    pub(crate) h_2_next_eval: F,
497
498    /// The lookup selector polynomial evaluation at point `zeta * g`.
499    pub(crate) q_lookup_next_eval: F,
500
501    /// The 4th witness polynomial evaluation at point `zeta * g`.
502    pub(crate) w_3_next_eval: F,
503
504    /// The 5th witness polynomial evaluation at point `zeta * g`.
505    pub(crate) w_4_next_eval: F,
506}
507
508impl<F: Field> PlookupEvaluations<F> {
509    /// Return the list of evaluations at point `zeta`.
510    pub(crate) fn evals_vec(&self) -> Vec<F> {
511        vec![
512            self.range_table_eval,
513            self.key_table_eval,
514            self.h_1_eval,
515            self.q_lookup_eval,
516            self.table_dom_sep_eval,
517            self.q_dom_sep_eval,
518        ]
519    }
520
521    /// Return the list of evaluations at point `zeta * g`.
522    pub(crate) fn next_evals_vec(&self) -> Vec<F> {
523        vec![
524            self.prod_next_eval,
525            self.range_table_next_eval,
526            self.key_table_next_eval,
527            self.h_1_next_eval,
528            self.h_2_next_eval,
529            self.q_lookup_next_eval,
530            self.w_3_next_eval,
531            self.w_4_next_eval,
532            self.table_dom_sep_next_eval,
533        ]
534    }
535}
536
537/// Preprocessed prover parameters used to compute Plonk proofs for a certain
538/// circuit.
539#[derive(Debug, Clone, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)]
540pub struct ProvingKey<E: Pairing> {
541    /// Extended permutation (sigma) polynomials.
542    pub(crate) sigmas: Vec<DensePolynomial<E::ScalarField>>,
543
544    /// Selector polynomials.
545    pub(crate) selectors: Vec<DensePolynomial<E::ScalarField>>,
546
547    // KZG PCS committing key.
548    pub(crate) commit_key: CommitKey<E>,
549
550    /// The verifying key. It is used by prover to initialize transcripts.
551    pub vk: VerifyingKey<E>,
552
553    /// Proving key for Plookup, None if not support lookup.
554    pub(crate) plookup_pk: Option<PlookupProvingKey<E>>,
555}
556
557/// Preprocessed prover parameters used to compute Plookup proofs for a certain
558/// circuit.
559#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
560pub struct PlookupProvingKey<E: Pairing> {
561    /// Range table polynomial.
562    pub(crate) range_table_poly: DensePolynomial<E::ScalarField>,
563
564    /// Key table polynomial.
565    pub(crate) key_table_poly: DensePolynomial<E::ScalarField>,
566
567    /// Table domain separation polynomial.
568    pub(crate) table_dom_sep_poly: DensePolynomial<E::ScalarField>,
569
570    /// Lookup domain separation selector polynomial.
571    pub(crate) q_dom_sep_poly: DensePolynomial<E::ScalarField>,
572}
573
574impl<E: Pairing> ProvingKey<E> {
575    /// The size of the evaluation domain. Should be a power of two.
576    pub(crate) fn domain_size(&self) -> usize {
577        self.vk.domain_size
578    }
579    /// The number of public inputs.
580    #[allow(dead_code)]
581    pub(crate) fn num_inputs(&self) -> usize {
582        self.vk.num_inputs
583    }
584    /// The constants K0, ..., K4 that ensure wire subsets are disjoint.
585    pub(crate) fn k(&self) -> &[E::ScalarField] {
586        &self.vk.k
587    }
588
589    /// The lookup selector polynomial
590    pub(crate) fn q_lookup_poly(&self) -> Result<&DensePolynomial<E::ScalarField>, PlonkError> {
591        if self.plookup_pk.is_none() {
592            return Err(SnarkLookupUnsupported.into());
593        }
594        Ok(self.selectors.last().unwrap())
595    }
596
597    /// Merge with another TurboPlonk proving key to obtain a new TurboPlonk
598    /// proving key. Return error if any of the following holds:
599    /// 1. the other proving key has a different domain size;
600    /// 2. the circuit underlying the other key has different number of inputs;
601    /// 3. the key or the other key is not a TurboPlonk key.
602    #[allow(dead_code)]
603    pub(crate) fn merge(&self, other_pk: &Self) -> Result<Self, PlonkError> {
604        if self.domain_size() != other_pk.domain_size() {
605            return Err(ParameterError(format!(
606                "mismatched domain size ({} vs {}) when merging proving keys",
607                self.domain_size(),
608                other_pk.domain_size()
609            ))
610            .into());
611        }
612        if self.num_inputs() != other_pk.num_inputs() {
613            return Err(ParameterError(
614                "mismatched number of public inputs when merging proving keys".to_string(),
615            )
616            .into());
617        }
618        if self.plookup_pk.is_some() || other_pk.plookup_pk.is_some() {
619            return Err(ParameterError("cannot merge UltraPlonk proving keys".to_string()).into());
620        }
621        let sigmas: Vec<DensePolynomial<E::ScalarField>> = self
622            .sigmas
623            .iter()
624            .zip(other_pk.sigmas.iter())
625            .map(|(poly1, poly2)| poly1 + poly2)
626            .collect();
627        let selectors: Vec<DensePolynomial<E::ScalarField>> = self
628            .selectors
629            .iter()
630            .zip(other_pk.selectors.iter())
631            .map(|(poly1, poly2)| poly1 + poly2)
632            .collect();
633
634        Ok(Self {
635            sigmas,
636            selectors,
637            commit_key: self.commit_key.clone(),
638            vk: self.vk.merge(&other_pk.vk)?,
639            plookup_pk: None,
640        })
641    }
642}
643
644/// Preprocessed verifier parameters used to verify Plonk proofs for a certain
645/// circuit.
646#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
647pub struct VerifyingKey<E: Pairing> {
648    /// The size of the evaluation domain. Should be a power of two.
649    pub domain_size: usize,
650
651    /// The number of public inputs.
652    pub num_inputs: usize,
653
654    /// The permutation polynomial commitments. The commitments are not hiding.
655    pub sigma_comms: Vec<Commitment<E>>,
656
657    /// The selector polynomial commitments. The commitments are not hiding.
658    pub selector_comms: Vec<Commitment<E>>,
659
660    /// The constants K0, ..., K_num_wire_types that ensure wire subsets are
661    /// disjoint.
662    pub k: Vec<E::ScalarField>,
663
664    /// KZG PCS opening key.
665    pub open_key: OpenKey<E>,
666
667    /// A flag indicating whether the key is a merged key.
668    pub is_merged: bool,
669
670    /// Plookup verifying key, None if not support lookup.
671    pub plookup_vk: Option<PlookupVerifyingKey<E>>,
672}
673
674impl<E, F, P1, P2> From<VerifyingKey<E>> for Vec<E::BaseField>
675where
676    E: Pairing<G1Affine = Affine<P1>, G2Affine = Affine<P2>, TargetField = Fp2<F>>,
677    F: Fp2Config<Fp = E::BaseField>,
678    P1: SWCurveConfig<BaseField = E::BaseField, ScalarField = E::ScalarField>,
679    P2: SWCurveConfig<BaseField = E::TargetField, ScalarField = E::ScalarField>,
680{
681    fn from(vk: VerifyingKey<E>) -> Self {
682        if vk.plookup_vk.is_some() {
683            panic!("Only support TurboPlonk VerifyingKey for now.");
684        }
685
686        [
687            vec![E::BaseField::from(vk.domain_size as u64)],
688            vec![E::BaseField::from(vk.num_inputs as u64)],
689            vk.sigma_comms
690                .iter()
691                .map(|cm| group1_to_fields::<E, _>(cm.0))
692                .collect::<Vec<_>>()
693                .concat(),
694            vk.selector_comms
695                .iter()
696                .map(|cm| group1_to_fields::<E, _>(cm.0))
697                .collect::<Vec<_>>()
698                .concat(),
699            vk.k.iter()
700                .map(|fr| fr_to_fq::<E::BaseField, P1>(fr))
701                .collect(),
702            // NOTE: only adding g, h, beta_h since only these are used.
703            group1_to_fields::<E, P1>(vk.open_key.g),
704            group2_to_fields::<E, F, P2>(vk.open_key.h),
705            group2_to_fields::<E, F, P2>(vk.open_key.beta_h),
706        ]
707        .concat()
708    }
709}
710
711impl<E, F, P> VerifyingKey<E>
712where
713    E: Pairing<BaseField = F, G1Affine = Affine<P>>,
714    F: SWToTEConParam,
715    P: SWCurveConfig<BaseField = F>,
716{
717    /// Convert the group elements to a list of scalars that represent the
718    /// Twisted Edwards coordinates.
719    pub fn convert_te_coordinates_to_scalars(&self) -> Vec<F> {
720        let mut res = vec![];
721        for sigma_comm in self.sigma_comms.iter() {
722            let point: TEPoint<F> = sigma_comm.0.into();
723            res.push(point.get_x());
724            res.push(point.get_y());
725        }
726        for selector_comm in self.selector_comms.iter() {
727            let point: TEPoint<F> = selector_comm.0.into();
728            res.push(point.get_x());
729            res.push(point.get_y());
730        }
731        res
732    }
733}
734
735/// Preprocessed verifier parameters used to verify Plookup proofs for a certain
736/// circuit.
737#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
738pub struct PlookupVerifyingKey<E: Pairing> {
739    /// Range table polynomial commitment. The commitment is not hiding.
740    pub(crate) range_table_comm: Commitment<E>,
741
742    /// Key table polynomial commitment. The commitment is not hiding.
743    pub(crate) key_table_comm: Commitment<E>,
744
745    /// Table domain separation polynomial commitment. The commitment is not
746    /// hiding.
747    pub(crate) table_dom_sep_comm: Commitment<E>,
748
749    /// Lookup domain separation selector polynomial commitment. The commitment
750    /// is not hiding.
751    pub(crate) q_dom_sep_comm: Commitment<E>,
752}
753
754impl<E: Pairing> VerifyingKey<E> {
755    /// Create a dummy TurboPlonk verification key for a circuit with
756    /// `num_inputs` public inputs and domain size `domain_size`.
757    pub fn dummy(num_inputs: usize, domain_size: usize) -> Self {
758        let num_wire_types = GATE_WIDTH + 1;
759        Self {
760            domain_size,
761            num_inputs,
762            sigma_comms: vec![Commitment::default(); num_wire_types],
763            selector_comms: vec![Commitment::default(); N_TURBO_PLONK_SELECTORS],
764            k: compute_coset_representatives(num_wire_types, Some(domain_size)),
765            open_key: OpenKey::default(),
766            is_merged: false,
767            plookup_vk: None,
768        }
769    }
770    /// Merge with another TurboPlonk verifying key to obtain a new TurboPlonk
771    /// verifying key. Return error if any of the following holds:
772    /// 1. the other verifying key has a different domain size;
773    /// 2. the circuit underlying the other key has different number of inputs.
774    /// 3. the key or the other key is not a TurboPlonk key.
775    pub(crate) fn merge(&self, other_vk: &Self) -> Result<Self, PlonkError> {
776        if self.is_merged || other_vk.is_merged {
777            return Err(ParameterError("cannot merge a merged key again".to_string()).into());
778        }
779        if self.domain_size != other_vk.domain_size {
780            return Err(ParameterError(
781                "mismatched domain size when merging verifying keys".to_string(),
782            )
783            .into());
784        }
785        if self.num_inputs != other_vk.num_inputs {
786            return Err(ParameterError(
787                "mismatched number of public inputs when merging verifying keys".to_string(),
788            )
789            .into());
790        }
791        if self.plookup_vk.is_some() || other_vk.plookup_vk.is_some() {
792            return Err(
793                ParameterError("cannot merge UltraPlonk verifying keys".to_string()).into(),
794            );
795        }
796        let sigma_comms: Vec<Commitment<E>> = self
797            .sigma_comms
798            .iter()
799            .zip(other_vk.sigma_comms.iter())
800            .map(|(com1, com2)| Commitment((com1.0 + com2.0).into_affine()))
801            .collect();
802        let selector_comms: Vec<Commitment<E>> = self
803            .selector_comms
804            .iter()
805            .zip(other_vk.selector_comms.iter())
806            .map(|(com1, com2)| Commitment((com1.0 + com2.0).into_affine()))
807            .collect();
808
809        Ok(Self {
810            domain_size: self.domain_size,
811            num_inputs: self.num_inputs + other_vk.num_inputs,
812            sigma_comms,
813            selector_comms,
814            k: self.k.clone(),
815            open_key: self.open_key.clone(),
816            plookup_vk: None,
817            is_merged: true,
818        })
819    }
820
821    /// The lookup selector polynomial commitment
822    pub(crate) fn q_lookup_comm(&self) -> Result<&Commitment<E>, PlonkError> {
823        if self.plookup_vk.is_none() {
824            return Err(SnarkLookupUnsupported.into());
825        }
826        Ok(self.selector_comms.last().unwrap())
827    }
828}
829
830/// Plonk IOP verifier challenges.
831#[derive(Debug, Default)]
832pub(crate) struct Challenges<F: Field> {
833    pub(crate) tau: Option<F>,
834    pub(crate) alpha: F,
835    pub(crate) beta: F,
836    pub(crate) gamma: F,
837    pub(crate) zeta: F,
838    pub(crate) v: F,
839    pub(crate) u: F,
840}
841
842/// Plonk IOP online polynomial oracles.
843#[derive(Debug, Default, Clone)]
844pub(crate) struct Oracles<F: FftField> {
845    pub(crate) wire_polys: Vec<DensePolynomial<F>>,
846    pub(crate) pub_inp_poly: DensePolynomial<F>,
847    pub(crate) prod_perm_poly: DensePolynomial<F>,
848    pub(crate) plookup_oracles: PlookupOracles<F>,
849}
850
851/// Plookup IOP online polynomial oracles.
852#[derive(Debug, Default, Clone)]
853pub(crate) struct PlookupOracles<F: FftField> {
854    pub(crate) h_polys: Vec<DensePolynomial<F>>,
855    pub(crate) prod_lookup_poly: DensePolynomial<F>,
856}
857
858/// The vector representation of bases and corresponding scalars.
859#[derive(Debug)]
860pub(crate) struct ScalarsAndBases<E: Pairing> {
861    pub(crate) base_scalar_map: HashMap<E::G1Affine, E::ScalarField>,
862}
863
864impl<E: Pairing> ScalarsAndBases<E> {
865    pub(crate) fn new() -> Self {
866        Self {
867            base_scalar_map: HashMap::new(),
868        }
869    }
870    /// Insert a base point and the corresponding scalar.
871    pub(crate) fn push(&mut self, scalar: E::ScalarField, base: E::G1Affine) {
872        let entry_scalar = self
873            .base_scalar_map
874            .entry(base)
875            .or_insert_with(E::ScalarField::zero);
876        *entry_scalar += scalar;
877    }
878
879    /// Add a list of scalars and bases into self, where each scalar is
880    /// multiplied by a constant c.
881    pub(crate) fn merge(&mut self, c: E::ScalarField, scalars_and_bases: &Self) {
882        for (base, scalar) in &scalars_and_bases.base_scalar_map {
883            self.push(c * scalar, *base);
884        }
885    }
886    /// Compute the multi-scalar multiplication.
887    pub(crate) fn multi_scalar_mul(&self) -> E::G1 {
888        let mut bases = vec![];
889        let mut scalars = vec![];
890        for (base, scalar) in &self.base_scalar_map {
891            bases.push(*base);
892            scalars.push(scalar.into_bigint());
893        }
894        VariableBaseMSM::msm_bigint(&bases, &scalars)
895    }
896}
897
898// Utility function for computing merged table evaluations.
899#[inline]
900pub(crate) fn eval_merged_table<E: Pairing>(
901    tau: E::ScalarField,
902    range_eval: E::ScalarField,
903    key_eval: E::ScalarField,
904    q_lookup_eval: E::ScalarField,
905    w3_eval: E::ScalarField,
906    w4_eval: E::ScalarField,
907    table_dom_sep_eval: E::ScalarField,
908) -> E::ScalarField {
909    range_eval
910        + q_lookup_eval
911            * tau
912            * (table_dom_sep_eval + tau * (key_eval + tau * (w3_eval + tau * w4_eval)))
913}
914
915// Utility function for computing merged lookup witness evaluations.
916#[inline]
917pub(crate) fn eval_merged_lookup_witness<E: Pairing>(
918    tau: E::ScalarField,
919    w_range_eval: E::ScalarField,
920    w_0_eval: E::ScalarField,
921    w_1_eval: E::ScalarField,
922    w_2_eval: E::ScalarField,
923    q_lookup_eval: E::ScalarField,
924    q_dom_sep_eval: E::ScalarField,
925) -> E::ScalarField {
926    w_range_eval
927        + q_lookup_eval
928            * tau
929            * (q_dom_sep_eval + tau * (w_0_eval + tau * (w_1_eval + tau * w_2_eval)))
930}
931
932#[cfg(test)]
933mod test {
934    use super::*;
935    use ark_bn254::{g1::Config, Bn254, Fq};
936    use ark_ec::AffineRepr;
937
938    #[test]
939    fn test_group_to_field() {
940        let g1 = <Bn254 as Pairing>::G1Affine::generator();
941        let f1: Vec<Fq> = group1_to_fields::<Bn254, Config>(g1);
942        assert_eq!(f1.len(), 2);
943        let g2 = <Bn254 as Pairing>::G2Affine::generator();
944        let f2: Vec<Fq> = group2_to_fields::<Bn254, _, _>(g2);
945        assert_eq!(f2.len(), 4);
946    }
947}