p3_commit/mmcs.rs
1use alloc::vec;
2use alloc::vec::Vec;
3use core::fmt::Debug;
4
5use p3_matrix::dense::RowMajorMatrix;
6use p3_matrix::{Dimensions, Matrix};
7use serde::de::DeserializeOwned;
8use serde::{Deserialize, Serialize};
9
10/// A "Mixed Matrix Commitment Scheme" (MMCS) is a generalization of a vector commitment scheme.
11///
12/// It supports committing to matrices and then opening rows. It is also batch-oriented; one can commit
13/// to a batch of matrices at once even if their widths and heights differ.
14///
15/// When a particular row index is opened, it is interpreted directly as a row index for matrices
16/// with the largest height. For matrices with smaller heights, some bits of the row index are
17/// removed (from the least-significant side) to get the effective row index. These semantics are
18/// useful in the FRI protocol. See the documentation for `open_batch` for more details.
19pub trait Mmcs<T: Send + Sync + Clone>: Clone {
20 type ProverData<M>;
21 type Commitment: Clone + Serialize + DeserializeOwned;
22 type Proof: Clone + Serialize + DeserializeOwned;
23 type Error: Debug;
24
25 /// Commits to a batch of matrices at once and returns both the commitment and associated prover data.
26 ///
27 /// Each matrix in the batch may have different dimensions.
28 ///
29 /// # Parameters
30 /// - `inputs`: A vector of matrices to commit to.
31 ///
32 /// # Returns
33 /// A tuple `(commitment, prover_data)` where:
34 /// - `commitment` is a compact representation of all matrix elements and will be sent to the verifier.
35 /// - `prover_data` is auxiliary data used by the prover open the commitment.
36 fn commit<M: Matrix<T>>(&self, inputs: Vec<M>) -> (Self::Commitment, Self::ProverData<M>);
37
38 /// Convenience method to commit to a single matrix.
39 ///
40 /// Internally wraps the matrix in a singleton vector and delegates to [`Self::commit`].
41 ///
42 /// # Parameters
43 /// - `input`: The matrix to commit to.
44 ///
45 /// # Returns
46 /// A tuple `(commitment, prover_data)` as in [`Self::commit`].
47 fn commit_matrix<M: Matrix<T>>(&self, input: M) -> (Self::Commitment, Self::ProverData<M>) {
48 self.commit(vec![input])
49 }
50
51 /// Convenience method to commit to a single column vector, treated as a column matrix.
52 ///
53 /// Automatically wraps the vector into a column matrix using [`RowMajorMatrix::new_col`].
54 ///
55 /// # Parameters
56 /// - `input`: A vector of field elements representing a single column.
57 ///
58 /// # Returns
59 /// A tuple `(commitment, prover_data)` for the resulting 1-column matrix.
60 fn commit_vec(&self, input: Vec<T>) -> (Self::Commitment, Self::ProverData<RowMajorMatrix<T>>)
61 where
62 T: Clone + Send + Sync,
63 {
64 self.commit_matrix(RowMajorMatrix::new_col(input))
65 }
66
67 /// Opens a specific row (identified by `index`) from each matrix in the batch.
68 ///
69 /// This function is designed to support batch opening semantics where matrices may have different heights.
70 /// The given index is interpreted relative to the maximum matrix height; smaller matrices apply a
71 /// bit-shift to extract the corresponding row.
72 ///
73 /// # Parameters
74 /// - `index`: The global row index (relative to max height).
75 /// - `prover_data`: Prover data returned from [`Self::commit`] or related methods.
76 ///
77 /// # Returns
78 /// A [`BatchOpening`] containing the opened rows and the proof of their correctness.
79 ///
80 /// # Opening Index Semantics
81 /// For each matrix `M[i]`, the row index used is:
82 /// ```text
83 /// j = index >> (log2_ceil(max_height) - log2_ceil(M[i].height))
84 /// ```
85 fn open_batch<M: Matrix<T>>(
86 &self,
87 index: usize,
88 prover_data: &Self::ProverData<M>,
89 ) -> BatchOpening<T, Self>;
90
91 /// Returns references to all matrices originally committed to in the batch.
92 ///
93 /// This allows access to the underlying data for inspection or additional logic.
94 ///
95 /// # Parameters
96 /// - `prover_data`: The prover data returned by [`Self::commit`].
97 ///
98 /// # Returns
99 /// A vector of references to the committed matrices.
100 fn get_matrices<'a, M: Matrix<T>>(&self, prover_data: &'a Self::ProverData<M>) -> Vec<&'a M>;
101
102 /// Returns the height (number of rows) of each matrix in the batch.
103 ///
104 /// This is a utility method derived from [`Self::get_matrices`].
105 ///
106 /// # Parameters
107 /// - `prover_data`: The prover data returned by [`Self::commit`].
108 ///
109 /// # Returns
110 /// A vector containing the height of each committed matrix.
111 fn get_matrix_heights<M: Matrix<T>>(&self, prover_data: &Self::ProverData<M>) -> Vec<usize> {
112 self.get_matrices(prover_data)
113 .iter()
114 .map(|matrix| matrix.height())
115 .collect()
116 }
117
118 /// Get the largest height of any committed matrix.
119 ///
120 /// # Panics
121 /// This may panic if there are no committed matrices.
122 fn get_max_height<M: Matrix<T>>(&self, prover_data: &Self::ProverData<M>) -> usize {
123 self.get_matrix_heights(prover_data)
124 .into_iter()
125 .max()
126 .unwrap_or_else(|| panic!("No committed matrices?"))
127 }
128
129 /// Verifies a batch opening at a specific row index against the original commitment.
130 ///
131 /// This is the verifier-side analogue of [`Self::open_batch`]. The verifier receives:
132 /// - The original commitment.
133 /// - Dimensions of each matrix being opened (in the same order as originally committed).
134 /// - The global index used for opening (interpreted as in [`Self::open_batch`]).
135 /// - A [`BatchOpeningRef`] containing the claimed opened values and the proof.
136 ///
137 /// # Width enforcement
138 /// - Leaf hashing may flatten all rows opened at one height into a single element stream.
139 /// - A digest match alone then does not pin where one row ends and the next begins.
140 /// - Implementations must reject any opened row whose length differs from its claimed width.
141 /// - Callers must derive each width from verifier-known data, never from the proof itself.
142 ///
143 /// # Parameters
144 /// - `commit`: The original commitment.
145 /// - `dimensions`: Dimensions of the committed matrices, in order.
146 /// - `index`: The global row index that was opened.
147 /// - `batch_opening`: A reference to the values and proof to verify.
148 ///
149 /// # Returns
150 /// `Ok(())` if the opening is valid; otherwise returns a verification error.
151 fn verify_batch(
152 &self,
153 commit: &Self::Commitment,
154 dimensions: &[Dimensions],
155 index: usize,
156 batch_opening: BatchOpeningRef<'_, T, Self>,
157 ) -> Result<(), Self::Error>;
158}
159
160/// Lets a shared reference be used wherever an owned [`Mmcs`] is expected.
161impl<T: Send + Sync + Clone, M: Mmcs<T>> Mmcs<T> for &M {
162 type ProverData<Mat> = M::ProverData<Mat>;
163 type Commitment = M::Commitment;
164 type Proof = M::Proof;
165 type Error = M::Error;
166
167 fn commit<Mat: Matrix<T>>(
168 &self,
169 inputs: Vec<Mat>,
170 ) -> (Self::Commitment, Self::ProverData<Mat>) {
171 (**self).commit(inputs)
172 }
173
174 fn open_batch<Mat: Matrix<T>>(
175 &self,
176 index: usize,
177 prover_data: &Self::ProverData<Mat>,
178 ) -> BatchOpening<T, Self> {
179 let (opened_values, opening_proof) = (**self).open_batch(index, prover_data).unpack();
180 BatchOpening::new(opened_values, opening_proof)
181 }
182
183 fn get_matrices<'a, Mat: Matrix<T>>(
184 &self,
185 prover_data: &'a Self::ProverData<Mat>,
186 ) -> Vec<&'a Mat> {
187 (**self).get_matrices(prover_data)
188 }
189
190 fn verify_batch(
191 &self,
192 commit: &Self::Commitment,
193 dimensions: &[Dimensions],
194 index: usize,
195 batch_opening: BatchOpeningRef<'_, T, Self>,
196 ) -> Result<(), Self::Error> {
197 (**self).verify_batch(
198 commit,
199 dimensions,
200 index,
201 BatchOpeningRef::new(batch_opening.opened_values, batch_opening.opening_proof),
202 )
203 }
204}
205
206/// A Batched opening proof.
207///
208/// Contains a collection of opened values at a Merkle proof for those openings.
209///
210/// Primarily used by the prover.
211#[derive(Serialize, Deserialize, Clone)]
212// Enable Serialize/Deserialize whenever T supports it.
213#[serde(bound(serialize = "T: Serialize"))]
214#[serde(bound(deserialize = "T: DeserializeOwned"))]
215pub struct BatchOpening<T: Send + Sync + Clone, InputMmcs: Mmcs<T>> {
216 /// The opened row values from each matrix in the batch.
217 /// Each inner vector corresponds to one matrix.
218 pub opened_values: Vec<Vec<T>>,
219 /// The proof showing the values are valid openings.
220 pub opening_proof: InputMmcs::Proof,
221}
222
223impl<T: Send + Sync + Clone, InputMmcs: Mmcs<T>> BatchOpening<T, InputMmcs> {
224 /// Creates a new batch opening proof.
225 #[inline]
226 pub const fn new(opened_values: Vec<Vec<T>>, opening_proof: InputMmcs::Proof) -> Self {
227 Self {
228 opened_values,
229 opening_proof,
230 }
231 }
232
233 /// Unpacks the batch opening proof into its components.
234 #[inline]
235 pub fn unpack(self) -> (Vec<Vec<T>>, InputMmcs::Proof) {
236 (self.opened_values, self.opening_proof)
237 }
238}
239
240/// A reference to a batched opening proof.
241///
242/// Contains references to a collection of claimed opening values and a Merkle proof for those values.
243///
244/// Primarily used by the verifier.
245#[derive(Copy, Clone)]
246pub struct BatchOpeningRef<'a, T: Send + Sync + Clone, InputMmcs: Mmcs<T>> {
247 /// Reference to the opened row values, as slices of base elements.
248 pub opened_values: &'a [Vec<T>],
249 /// Reference to the proof object used for verification.
250 pub opening_proof: &'a InputMmcs::Proof,
251}
252
253impl<'a, T: Send + Sync + Clone, InputMmcs: Mmcs<T>> BatchOpeningRef<'a, T, InputMmcs> {
254 /// Creates a new batch opening proof.
255 #[inline]
256 pub const fn new(opened_values: &'a [Vec<T>], opening_proof: &'a InputMmcs::Proof) -> Self {
257 Self {
258 opened_values,
259 opening_proof,
260 }
261 }
262
263 /// Unpacks the batch opening proof into its components.
264 #[inline]
265 pub const fn unpack(&self) -> (&'a [Vec<T>], &'a InputMmcs::Proof) {
266 (self.opened_values, self.opening_proof)
267 }
268}
269
270impl<'a, T: Send + Sync + Clone, InputMmcs: Mmcs<T>> From<&'a BatchOpening<T, InputMmcs>>
271 for BatchOpeningRef<'a, T, InputMmcs>
272{
273 #[inline]
274 fn from(batch_opening: &'a BatchOpening<T, InputMmcs>) -> Self {
275 Self::new(&batch_opening.opened_values, &batch_opening.opening_proof)
276 }
277}