lib_q_stark_commit/pcs.rs
1//! Traits for polynomial commitment schemes.
2
3use alloc::vec::Vec;
4use core::fmt::Debug;
5
6use lib_q_stark_field::ExtensionField;
7use lib_q_stark_matrix::Matrix;
8use lib_q_stark_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 /// Commit to the quotient polynomial. We first decompose the quotient polynomial into
71 /// `num_chunks` many smaller polynomials each of degree `degree / num_chunks`.
72 /// This can have minor performance benefits, but is not strictly necessary in the non `zk` case.
73 /// When `zk` is enabled, this commitment will additionally include some randomization process
74 /// to hide the inputs.
75 ///
76 /// ### Arguments
77 /// - `quotient_domain` the domain of the quotient polynomial.
78 /// - `quotient_evaluations` the evaluations of the quotient polynomial over the domain. This should be in
79 /// standard (not bit-reversed) order.
80 /// - `num_chunks` the number of smaller polynomials to decompose the quotient polynomial into.
81 #[allow(clippy::type_complexity)]
82 fn commit_quotient(
83 &self,
84 quotient_domain: Self::Domain,
85 quotient_evaluations: RowMajorMatrix<Val<Self::Domain>>,
86 num_chunks: usize,
87 ) -> (Self::Commitment, Self::ProverData) {
88 let quotient_sub_evaluations =
89 quotient_domain.split_evals(num_chunks, quotient_evaluations);
90 let quotient_sub_domains = quotient_domain.split_domains(num_chunks);
91 let ldes = self.get_quotient_ldes(
92 quotient_sub_domains
93 .into_iter()
94 .zip(quotient_sub_evaluations),
95 num_chunks,
96 );
97 self.commit_ldes(ldes)
98 }
99
100 /// When committing to quotient polynomials in batch-STARK, it is simpler to first compute
101 /// the LDE evaluations before batch-committing. When `zk` is enabled, this may add randomization.
102 fn get_quotient_ldes(
103 &self,
104 evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val<Self::Domain>>)>,
105 num_chunks: usize,
106 ) -> Vec<RowMajorMatrix<Val<Self::Domain>>>;
107
108 /// Commits to a collection of LDE evaluation matrices.
109 fn commit_ldes(
110 &self,
111 ldes: Vec<RowMajorMatrix<Val<Self::Domain>>>,
112 ) -> (Self::Commitment, Self::ProverData);
113
114 /// Same as `commit`; used when the committed data is preprocessing (e.g. fixed trace).
115 fn commit_preprocessing(
116 &self,
117 evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val<Self::Domain>>)>,
118 ) -> (Self::Commitment, Self::ProverData) {
119 self.commit(evaluations)
120 }
121
122 /// Given prover data corresponding to a commitment to a collection of evaluation matrices,
123 /// return the evaluations of those matrices on the given domain.
124 ///
125 /// This is essentially a no-op when called with a `domain` which is a subset of the evaluation domain
126 /// on which the evaluation matrices are defined.
127 fn get_evaluations_on_domain<'a>(
128 &self,
129 prover_data: &'a Self::ProverData,
130 idx: usize,
131 domain: Self::Domain,
132 ) -> Self::EvaluationsOnDomain<'a>;
133
134 /// Like `get_evaluations_on_domain` but without applying ZK randomization (e.g. for quotient domain).
135 fn get_evaluations_on_domain_no_random<'a>(
136 &self,
137 prover_data: &'a Self::ProverData,
138 idx: usize,
139 domain: Self::Domain,
140 ) -> Self::EvaluationsOnDomain<'a> {
141 self.get_evaluations_on_domain(prover_data, idx, domain)
142 }
143
144 /// Open a collection of polynomial commitments at a set of points. Produce the values at those points along with a proof
145 /// of correctness.
146 ///
147 /// Arguments:
148 /// - `commitment_data_with_opening_points`: A vector whose elements are a pair:
149 /// - `data`: The prover data corresponding to a multi-matrix commitment.
150 /// - `opening_points`: A vector containing, for each matrix committed to, a vector of opening points.
151 /// - `fiat_shamir_challenger`: The challenger that will be used to generate the proof.
152 ///
153 /// Unwrapping the arguments further, each `data` contains a vector of the committed matrices (`matrices = Vec<M>`).
154 /// If the length of `matrices` is not equal to the length of `opening_points` the function will error. Otherwise, for
155 /// each index `i`, the matrix `M = matrices[i]` will be opened at the points `opening_points[i]`.
156 ///
157 /// This means that each column of `M` will be interpreted as the evaluation vector of some polynomial
158 /// and we will compute the value of all of those polynomials at `opening_points[i]`.
159 ///
160 /// The domains on which the evaluation vectors are defined is not part of the arguments here
161 /// but should be public information known to both the prover and verifier.
162 fn open(
163 &self,
164 // For each multi-matrix commitment,
165 commitment_data_with_opening_points: Vec<(
166 // The matrices and auxiliary prover data
167 &Self::ProverData,
168 // for each matrix,
169 Vec<
170 // the points to open
171 Vec<Challenge>,
172 >,
173 )>,
174 fiat_shamir_challenger: &mut Challenger,
175 ) -> (OpenedValues<Challenge>, Self::Proof);
176
177 /// Like `open` but allows the implementation to treat some rounds as preprocessing (e.g. for ZK).
178 #[allow(clippy::type_complexity)]
179 fn open_with_preprocessing(
180 &self,
181 rounds: Vec<(&Self::ProverData, Vec<Vec<Challenge>>)>,
182 challenger: &mut Challenger,
183 _is_preprocessing: bool,
184 ) -> (OpenedValues<Challenge>, Self::Proof) {
185 self.open(rounds, challenger)
186 }
187
188 /// Verify that a collection of opened values is correct.
189 ///
190 /// Arguments:
191 /// - `commitments_with_opening_points`: A vector whose elements are a pair:
192 /// - `commitment`: A multi matrix commitment.
193 /// - `opening_points`: A vector containing, for each matrix committed to, a vector of opening points and claimed evaluations.
194 /// - `proof`: A claimed proof of correctness for the opened values.
195 /// - `fiat_shamir_challenger`: The challenger that will be used to generate the proof.
196 #[allow(clippy::type_complexity)]
197 fn verify(
198 &self,
199 // For each commitment:
200 commitments_with_opening_points: Vec<(
201 // The commitment
202 Self::Commitment,
203 // for each matrix in the commitment:
204 Vec<(
205 // its domain,
206 Self::Domain,
207 // A vector of (point, claimed_evaluation) pairs
208 Vec<(
209 // the point the matrix was opened at,
210 Challenge,
211 // the claimed evaluations at that point
212 Vec<Challenge>,
213 )>,
214 )>,
215 )>,
216 // The opening proof for all claimed evaluations.
217 proof: &Self::Proof,
218 fiat_shamir_challenger: &mut Challenger,
219 ) -> Result<(), Self::Error>;
220
221 fn get_opt_randomization_poly_commitment(
222 &self,
223 _domains: impl IntoIterator<Item = Self::Domain>,
224 ) -> Option<(Self::Commitment, Self::ProverData)> {
225 None
226 }
227}
228
229pub type OpenedValues<F> = Vec<OpenedValuesForRound<F>>;
230pub type OpenedValuesForRound<F> = Vec<OpenedValuesForMatrix<F>>;
231pub type OpenedValuesForMatrix<F> = Vec<OpenedValuesForPoint<F>>;
232pub type OpenedValuesForPoint<F> = Vec<F>;