p3_commit/
pcs.rs

1//! Traits for polynomial commitment schemes.
2
3use alloc::vec::Vec;
4use core::fmt::Debug;
5
6use p3_field::ExtensionField;
7use p3_matrix::Matrix;
8use p3_matrix::dense::RowMajorMatrix;
9use serde::Serialize;
10use serde::de::DeserializeOwned;
11
12use crate::PolynomialSpace;
13
14pub type Val<D> = <D as PolynomialSpace>::Val;
15
16/// A polynomial commitment scheme, for committing to batches of polynomials defined by their evaluations
17/// over some domain.
18///
19/// In general this does not have to be a hiding commitment scheme but it might be for some implementations.
20// TODO: Should we have a super-trait for weakly-binding PCSs, like FRI outside unique decoding radius?
21pub trait Pcs<Challenge, Challenger>
22where
23    Challenge: ExtensionField<Val<Self::Domain>>,
24{
25    /// The class of evaluation domains that this commitment scheme works over.
26    type Domain: PolynomialSpace;
27
28    /// The commitment that's sent to the verifier.
29    type Commitment: Clone + Serialize + DeserializeOwned;
30
31    /// Data that the prover stores for committed polynomials, to help the prover with opening.
32    type ProverData;
33
34    /// Type of the output of `get_evaluations_on_domain`.
35    type EvaluationsOnDomain<'a>: Matrix<Val<Self::Domain>> + 'a;
36
37    /// The opening argument.
38    type Proof: Clone + Serialize + DeserializeOwned;
39
40    /// The type of a proof verification error.
41    type Error: Debug;
42
43    /// Set to true to activate randomization and achieve zero-knowledge.
44    const ZK: bool;
45
46    /// Index of the trace commitment in the computed opened values.
47    const TRACE_IDX: usize = Self::ZK as usize;
48
49    /// Index of the quotient commitments in the computed opened values.
50    const QUOTIENT_IDX: usize = Self::TRACE_IDX + 1;
51
52    /// Index of the preprocessed trace commitment in the computed opened values.
53    const PREPROCESSED_TRACE_IDX: usize = Self::QUOTIENT_IDX + 1; // Note: not always present
54
55    /// This should return a domain such that `Domain::next_point` returns `Some`.
56    fn natural_domain_for_degree(&self, degree: usize) -> Self::Domain;
57
58    /// Given a collection of evaluation matrices, produce a binding commitment to
59    /// the polynomials defined by those evaluations. If `zk` is enabled, the evaluations are
60    /// first randomized as explained in Section 3 of https://eprint.iacr.org/2024/1037.pdf .
61    ///
62    /// Returns both the commitment which should be sent to the verifier
63    /// and the prover data which can be used to produce opening proofs.
64    #[allow(clippy::type_complexity)]
65    fn commit(
66        &self,
67        evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val<Self::Domain>>)>,
68    ) -> (Self::Commitment, Self::ProverData);
69
70    /// Same as `commit` but without randomization. This is used for preprocessed columns
71    /// which do not have to be randomized even when ZK is enabled. Note that the preprocessed columns still
72    /// need to be padded to the extended domain height.
73    ///
74    /// Returns both the commitment which should be sent to the verifier
75    /// and the prover data which can be used to produce opening proofs.
76    fn commit_preprocessing(
77        &self,
78        evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val<Self::Domain>>)>,
79    ) -> (Self::Commitment, Self::ProverData) {
80        self.commit(evaluations)
81    }
82
83    /// Commit to the quotient polynomial. We first decompose the quotient polynomial into
84    /// `num_chunks` many smaller polynomials each of degree `degree / num_chunks`.
85    /// This can have minor performance benefits, but is not strictly necessary in the non `zk` case.
86    /// When `zk` is enabled, this commitment will additionally include some randomization process
87    /// to hide the inputs.
88    ///
89    /// ### Arguments
90    /// - `quotient_domain` the domain of the quotient polynomial.
91    /// - `quotient_evaluations` the evaluations of the quotient polynomial over the domain. This should be in
92    ///   standard (not bit-reversed) order.
93    /// - `num_chunks` the number of smaller polynomials to decompose the quotient polynomial into.
94    #[allow(clippy::type_complexity)]
95    fn commit_quotient(
96        &self,
97        quotient_domain: Self::Domain,
98        quotient_evaluations: RowMajorMatrix<Val<Self::Domain>>,
99        num_chunks: usize,
100    ) -> (Self::Commitment, Self::ProverData) {
101        // Given the evaluation vector of `Q_i(x)` over a domain, split it into evaluation vectors
102        // of `q_{i0}(x), ...` over subdomains and commit to these `q`'s.
103        // TODO: Currently, split_evals involves copying the data to a new matrix.
104        //       We may be able to avoid this copy making use of bit-reversals.
105        let quotient_sub_evaluations =
106            quotient_domain.split_evals(num_chunks, quotient_evaluations);
107        let quotient_sub_domains = quotient_domain.split_domains(num_chunks);
108
109        let ldes = self.get_quotient_ldes(
110            quotient_sub_domains
111                .into_iter()
112                .zip(quotient_sub_evaluations),
113            num_chunks,
114        );
115        self.commit_ldes(ldes)
116    }
117
118    /// When committing to quotient polynomials in batch-STARK,
119    /// it is simpler to first compute the LDE evaluations before batch-committing to them.
120    ///
121    /// This corresponds to the first step of `commit_quotient`. When `zk` is enabled,
122    /// this will additionally add randomization.
123    fn get_quotient_ldes(
124        &self,
125        evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val<Self::Domain>>)>,
126        num_chunks: usize,
127    ) -> Vec<RowMajorMatrix<Val<Self::Domain>>>;
128
129    /// Commits to a collection of LDE evaluation matrices.
130    fn commit_ldes(
131        &self,
132        ldes: Vec<RowMajorMatrix<Val<Self::Domain>>>,
133    ) -> (Self::Commitment, Self::ProverData);
134
135    /// Given prover data corresponding to a commitment to a collection of evaluation matrices,
136    /// return the evaluations of those matrices on the given domain.
137    ///
138    /// This is essentially a no-op when called with a `domain` which is a subset of the evaluation domain
139    /// on which the evaluation matrices are defined.
140    fn get_evaluations_on_domain<'a>(
141        &self,
142        prover_data: &'a Self::ProverData,
143        idx: usize,
144        domain: Self::Domain,
145    ) -> Self::EvaluationsOnDomain<'a>;
146
147    /// This is the same as `get_evaluations_on_domain` but without randomization.
148    /// This is used for preprocessed columns which do not have to be randomized even when ZK is enabled.
149    fn get_evaluations_on_domain_no_random<'a>(
150        &self,
151        prover_data: &'a Self::ProverData,
152        idx: usize,
153        domain: Self::Domain,
154    ) -> Self::EvaluationsOnDomain<'a> {
155        self.get_evaluations_on_domain(prover_data, idx, domain)
156    }
157
158    /// Open a collection of polynomial commitments at a set of points. Produce the values at those points along with a proof
159    /// of correctness.
160    ///
161    /// Arguments:
162    /// - `commitment_data_with_opening_points`: A vector whose elements are a pair:
163    ///     - `data`: The prover data corresponding to a multi-matrix commitment.
164    ///     - `opening_points`: A vector containing, for each matrix committed to, a vector of opening points.
165    /// - `fiat_shamir_challenger`: The challenger that will be used to generate the proof.
166    ///
167    /// Unwrapping the arguments further, each `data` contains a vector of the committed matrices (`matrices = Vec<M>`).
168    /// If the length of `matrices` is not equal to the length of `opening_points` the function will error. Otherwise, for
169    /// each index `i`, the matrix `M = matrices[i]` will be opened at the points `opening_points[i]`.
170    ///
171    /// This means that each column of `M` will be interpreted as the evaluation vector of some polynomial
172    /// and we will compute the value of all of those polynomials at `opening_points[i]`.
173    ///
174    /// The domains on which the evaluation vectors are defined is not part of the arguments here
175    /// but should be public information known to both the prover and verifier.
176    fn open(
177        &self,
178        // For each multi-matrix commitment,
179        commitment_data_with_opening_points: Vec<(
180            // The matrices and auxiliary prover data
181            &Self::ProverData,
182            // for each matrix,
183            Vec<
184                // the points to open
185                Vec<Challenge>,
186            >,
187        )>,
188        fiat_shamir_challenger: &mut Challenger,
189    ) -> (OpenedValues<Challenge>, Self::Proof);
190
191    /// Open a collection of polynomial commitments at a set of points, when there is preprocessing data.
192    /// It is the same as `open` when `ZK` is disabled.
193    /// Produce the values at those points along with a proof of correctness.
194    ///
195    /// Arguments:
196    /// - `commitment_data_with_opening_points`: A vector whose elements are a pair:
197    ///     - `data`: The prover data corresponding to a multi-matrix commitment.
198    ///     - `opening_points`: A vector containing, for each matrix committed to, a vector of opening points.
199    /// - `fiat_shamir_challenger`: The challenger that will be used to generate the proof.
200    /// - `is_preprocessing`: If one of the committed matrices corresponds to preprocessed columns, this is the index of that matrix.
201    ///
202    /// Unwrapping the arguments further, each `data` contains a vector of the committed matrices (`matrices = Vec<M>`).
203    /// If the length of `matrices` is not equal to the length of `opening_points` the function will error. Otherwise, for
204    /// each index `i`, the matrix `M = matrices[i]` will be opened at the points `opening_points[i]`.
205    ///
206    /// This means that each column of `M` will be interpreted as the evaluation vector of some polynomial
207    /// and we will compute the value of all of those polynomials at `opening_points[i]`.
208    ///
209    /// The domains on which the evaluation vectors are defined is not part of the arguments here
210    /// but should be public information known to both the prover and verifier.
211    fn open_with_preprocessing(
212        &self,
213        // For each multi-matrix commitment,
214        commitment_data_with_opening_points: Vec<(
215            // The matrices and auxiliary prover data
216            &Self::ProverData,
217            // for each matrix,
218            Vec<
219                // the points to open
220                Vec<Challenge>,
221            >,
222        )>,
223        fiat_shamir_challenger: &mut Challenger,
224        _is_preprocessing: bool,
225    ) -> (OpenedValues<Challenge>, Self::Proof) {
226        debug_assert!(
227            !Self::ZK,
228            "open_with_preprocessing should have a different implementation when ZK is enabled"
229        );
230        self.open(commitment_data_with_opening_points, fiat_shamir_challenger)
231    }
232
233    /// Verify that a collection of opened values is correct.
234    ///
235    /// Arguments:
236    /// - `commitments_with_opening_points`: A vector whose elements are a pair:
237    ///     - `commitment`: A multi matrix commitment.
238    ///     - `opening_points`: A vector containing, for each matrix committed to, a vector of opening points and claimed evaluations.
239    /// - `proof`: A claimed proof of correctness for the opened values.
240    /// - `fiat_shamir_challenger`: The challenger that will be used to generate the proof.
241    #[allow(clippy::type_complexity)]
242    fn verify(
243        &self,
244        // For each commitment:
245        commitments_with_opening_points: Vec<(
246            // The commitment
247            Self::Commitment,
248            // for each matrix in the commitment:
249            Vec<(
250                // its domain,
251                Self::Domain,
252                // A vector of (point, claimed_evaluation) pairs
253                Vec<(
254                    // the point the matrix was opened at,
255                    Challenge,
256                    // the claimed evaluations at that point
257                    Vec<Challenge>,
258                )>,
259            )>,
260        )>,
261        // The opening proof for all claimed evaluations.
262        proof: &Self::Proof,
263        fiat_shamir_challenger: &mut Challenger,
264    ) -> Result<(), Self::Error>;
265
266    fn get_opt_randomization_poly_commitment(
267        &self,
268        _domain: impl IntoIterator<Item = Self::Domain>,
269    ) -> Option<(Self::Commitment, Self::ProverData)> {
270        None
271    }
272}
273
274pub type OpenedValues<F> = Vec<OpenedValuesForRound<F>>;
275pub type OpenedValuesForRound<F> = Vec<OpenedValuesForMatrix<F>>;
276pub type OpenedValuesForMatrix<F> = Vec<OpenedValuesForPoint<F>>;
277pub type OpenedValuesForPoint<F> = Vec<F>;