Skip to main content

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    /// Compact proof covering every index of one multi-opening.
24    ///
25    /// Opening `q` indices through [`Self::open_batch`] costs `q` full authentication paths.
26    /// Those paths overlap wherever two indices share an ancestor.
27    /// A multi-opening sends each shared digest once.
28    type MultiProof: Clone + Serialize + DeserializeOwned;
29    type Error: Debug;
30
31    /// Commits to a batch of matrices at once and returns both the commitment and associated prover data.
32    ///
33    /// Each matrix in the batch may have different dimensions.
34    ///
35    /// # Parameters
36    /// - `inputs`: A vector of matrices to commit to.
37    ///
38    /// # Returns
39    /// A tuple `(commitment, prover_data)` where:
40    /// - `commitment` is a compact representation of all matrix elements and will be sent to the verifier.
41    /// - `prover_data` is auxiliary data used by the prover open the commitment.
42    fn commit<M: Matrix<T>>(&self, inputs: Vec<M>) -> (Self::Commitment, Self::ProverData<M>);
43
44    /// Convenience method to commit to a single matrix.
45    ///
46    /// Internally wraps the matrix in a singleton vector and delegates to [`Self::commit`].
47    ///
48    /// # Parameters
49    /// - `input`: The matrix to commit to.
50    ///
51    /// # Returns
52    /// A tuple `(commitment, prover_data)` as in [`Self::commit`].
53    fn commit_matrix<M: Matrix<T>>(&self, input: M) -> (Self::Commitment, Self::ProverData<M>) {
54        self.commit(vec![input])
55    }
56
57    /// Convenience method to commit to a single column vector, treated as a column matrix.
58    ///
59    /// Automatically wraps the vector into a column matrix using [`RowMajorMatrix::new_col`].
60    ///
61    /// # Parameters
62    /// - `input`: A vector of field elements representing a single column.
63    ///
64    /// # Returns
65    /// A tuple `(commitment, prover_data)` for the resulting 1-column matrix.
66    fn commit_vec(&self, input: Vec<T>) -> (Self::Commitment, Self::ProverData<RowMajorMatrix<T>>)
67    where
68        T: Clone + Send + Sync,
69    {
70        self.commit_matrix(RowMajorMatrix::new_col(input))
71    }
72
73    /// Opens a specific row (identified by `index`) from each matrix in the batch.
74    ///
75    /// This function is designed to support batch opening semantics where matrices may have different heights.
76    /// The given index is interpreted relative to the maximum matrix height; smaller matrices apply a
77    /// bit-shift to extract the corresponding row.
78    ///
79    /// # Parameters
80    /// - `index`: The global row index (relative to max height).
81    /// - `prover_data`: Prover data returned from [`Self::commit`] or related methods.
82    ///
83    /// # Returns
84    /// A [`BatchOpening`] containing the opened rows and the proof of their correctness.
85    ///
86    /// # Opening Index Semantics
87    /// For each matrix `M[i]`, the row index used is:
88    /// ```text
89    /// j = index >> (log2_ceil(max_height) - log2_ceil(M[i].height))
90    /// ```
91    fn open_batch<M: Matrix<T>>(
92        &self,
93        index: usize,
94        prover_data: &Self::ProverData<M>,
95    ) -> BatchOpening<T, Self>;
96
97    /// Returns references to all matrices originally committed to in the batch.
98    ///
99    /// This allows access to the underlying data for inspection or additional logic.
100    ///
101    /// # Parameters
102    /// - `prover_data`: The prover data returned by [`Self::commit`].
103    ///
104    /// # Returns
105    /// A vector of references to the committed matrices.
106    fn get_matrices<'a, M: Matrix<T>>(&self, prover_data: &'a Self::ProverData<M>) -> Vec<&'a M>;
107
108    /// Returns the height (number of rows) of each matrix in the batch.
109    ///
110    /// This is a utility method derived from [`Self::get_matrices`].
111    ///
112    /// # Parameters
113    /// - `prover_data`: The prover data returned by [`Self::commit`].
114    ///
115    /// # Returns
116    /// A vector containing the height of each committed matrix.
117    fn get_matrix_heights<M: Matrix<T>>(&self, prover_data: &Self::ProverData<M>) -> Vec<usize> {
118        self.get_matrices(prover_data)
119            .iter()
120            .map(|matrix| matrix.height())
121            .collect()
122    }
123
124    /// Get the largest height of any committed matrix.
125    ///
126    /// # Panics
127    /// This may panic if there are no committed matrices.
128    fn get_max_height<M: Matrix<T>>(&self, prover_data: &Self::ProverData<M>) -> usize {
129        self.get_matrix_heights(prover_data)
130            .into_iter()
131            .max()
132            .unwrap_or_else(|| panic!("No committed matrices?"))
133    }
134
135    /// Verifies a batch opening at a specific row index against the original commitment.
136    ///
137    /// This is the verifier-side analogue of [`Self::open_batch`]. The verifier receives:
138    /// - The original commitment.
139    /// - Dimensions of each matrix being opened (in the same order as originally committed).
140    /// - The global index used for opening (interpreted as in [`Self::open_batch`]).
141    /// - A [`BatchOpeningRef`] containing the claimed opened values and the proof.
142    ///
143    /// # Width enforcement
144    /// - Leaf hashing may flatten all rows opened at one height into a single element stream.
145    /// - A digest match alone then does not pin where one row ends and the next begins.
146    /// - Implementations must reject any opened row whose length differs from its claimed width.
147    /// - Callers must derive each width from verifier-known data, never from the proof itself.
148    ///
149    /// # Parameters
150    /// - `commit`: The original commitment.
151    /// - `dimensions`: Dimensions of the committed matrices, in order.
152    /// - `index`: The global row index that was opened.
153    /// - `batch_opening`: A reference to the values and proof to verify.
154    ///
155    /// # Returns
156    /// `Ok(())` if the opening is valid; otherwise returns a verification error.
157    fn verify_batch(
158        &self,
159        commit: &Self::Commitment,
160        dimensions: &[Dimensions],
161        index: usize,
162        batch_opening: BatchOpeningRef<'_, T, Self>,
163    ) -> Result<(), Self::Error>;
164
165    /// Opens the given row indices from each committed matrix at once.
166    ///
167    /// Per-matrix index semantics follow [`Self::open_batch`].
168    ///
169    /// # Returns
170    ///
171    /// - `values[q][m]` — the opened row of matrix `m` at `indices[q]`.
172    /// - One [`Self::MultiProof`] authenticating all rows together.
173    fn open_multi_batch<M: Matrix<T>>(
174        &self,
175        indices: &[usize],
176        prover_data: &Self::ProverData<M>,
177    ) -> (Vec<Vec<Vec<T>>>, Self::MultiProof);
178
179    /// Verifies a multi-opening at the given indices.
180    ///
181    /// `indices` must come from verifier-side data (e.g. the transcript).
182    /// A proof opening any other index set is rejected.
183    ///
184    /// `opened_values[q][m]` is the claimed row of matrix `m` at `indices[q]`.
185    ///
186    /// Each row is any `AsRef<[T]>`.
187    /// A caller holding rows as slices verifies without copying them into owned buffers.
188    ///
189    /// Width enforcement follows [`Self::verify_batch`].
190    /// Every opened row must match its verifier-known matrix width.
191    fn verify_multi_batch<R: AsRef<[T]> + PartialEq>(
192        &self,
193        commit: &Self::Commitment,
194        dimensions: &[Dimensions],
195        indices: &[usize],
196        opened_values: &[Vec<R>],
197        proof: &Self::MultiProof,
198    ) -> Result<(), Self::Error>;
199}
200
201/// Lets a shared reference be used wherever an owned [`Mmcs`] is expected.
202impl<T: Send + Sync + Clone, M: Mmcs<T>> Mmcs<T> for &M {
203    type ProverData<Mat> = M::ProverData<Mat>;
204    type Commitment = M::Commitment;
205    type Proof = M::Proof;
206    type MultiProof = M::MultiProof;
207    type Error = M::Error;
208
209    fn commit<Mat: Matrix<T>>(
210        &self,
211        inputs: Vec<Mat>,
212    ) -> (Self::Commitment, Self::ProverData<Mat>) {
213        (**self).commit(inputs)
214    }
215
216    fn open_batch<Mat: Matrix<T>>(
217        &self,
218        index: usize,
219        prover_data: &Self::ProverData<Mat>,
220    ) -> BatchOpening<T, Self> {
221        let (opened_values, opening_proof) = (**self).open_batch(index, prover_data).unpack();
222        BatchOpening::new(opened_values, opening_proof)
223    }
224
225    fn get_matrices<'a, Mat: Matrix<T>>(
226        &self,
227        prover_data: &'a Self::ProverData<Mat>,
228    ) -> Vec<&'a Mat> {
229        (**self).get_matrices(prover_data)
230    }
231
232    fn verify_batch(
233        &self,
234        commit: &Self::Commitment,
235        dimensions: &[Dimensions],
236        index: usize,
237        batch_opening: BatchOpeningRef<'_, T, Self>,
238    ) -> Result<(), Self::Error> {
239        (**self).verify_batch(
240            commit,
241            dimensions,
242            index,
243            BatchOpeningRef::new(batch_opening.opened_values, batch_opening.opening_proof),
244        )
245    }
246
247    fn open_multi_batch<Mat: Matrix<T>>(
248        &self,
249        indices: &[usize],
250        prover_data: &Self::ProverData<Mat>,
251    ) -> (Vec<Vec<Vec<T>>>, Self::MultiProof) {
252        (**self).open_multi_batch(indices, prover_data)
253    }
254
255    fn verify_multi_batch<R: AsRef<[T]> + PartialEq>(
256        &self,
257        commit: &Self::Commitment,
258        dimensions: &[Dimensions],
259        indices: &[usize],
260        opened_values: &[Vec<R>],
261        proof: &Self::MultiProof,
262    ) -> Result<(), Self::Error> {
263        (**self).verify_multi_batch(commit, dimensions, indices, opened_values, proof)
264    }
265}
266
267/// A Batched opening proof.
268///
269/// Contains a collection of opened values at a Merkle proof for those openings.
270///
271/// Primarily used by the prover.
272#[derive(Serialize, Deserialize, Clone)]
273// Enable Serialize/Deserialize whenever T supports it.
274#[serde(bound(serialize = "T: Serialize"))]
275#[serde(bound(deserialize = "T: DeserializeOwned"))]
276pub struct BatchOpening<T: Send + Sync + Clone, InputMmcs: Mmcs<T>> {
277    /// The opened row values from each matrix in the batch.
278    /// Each inner vector corresponds to one matrix.
279    pub opened_values: Vec<Vec<T>>,
280    /// The proof showing the values are valid openings.
281    pub opening_proof: InputMmcs::Proof,
282}
283
284impl<T: Send + Sync + Clone, InputMmcs: Mmcs<T>> BatchOpening<T, InputMmcs> {
285    /// Creates a new batch opening proof.
286    #[inline]
287    pub const fn new(opened_values: Vec<Vec<T>>, opening_proof: InputMmcs::Proof) -> Self {
288        Self {
289            opened_values,
290            opening_proof,
291        }
292    }
293
294    /// Unpacks the batch opening proof into its components.
295    #[inline]
296    pub fn unpack(self) -> (Vec<Vec<T>>, InputMmcs::Proof) {
297        (self.opened_values, self.opening_proof)
298    }
299}
300
301/// A reference to a batched opening proof.
302///
303/// Contains references to a collection of claimed opening values and a Merkle proof for those values.
304///
305/// Primarily used by the verifier.
306#[derive(Copy, Clone)]
307pub struct BatchOpeningRef<'a, T: Send + Sync + Clone, InputMmcs: Mmcs<T>> {
308    /// Reference to the opened row values, as slices of base elements.
309    pub opened_values: &'a [Vec<T>],
310    /// Reference to the proof object used for verification.
311    pub opening_proof: &'a InputMmcs::Proof,
312}
313
314impl<'a, T: Send + Sync + Clone, InputMmcs: Mmcs<T>> BatchOpeningRef<'a, T, InputMmcs> {
315    /// Creates a new batch opening proof.
316    #[inline]
317    pub const fn new(opened_values: &'a [Vec<T>], opening_proof: &'a InputMmcs::Proof) -> Self {
318        Self {
319            opened_values,
320            opening_proof,
321        }
322    }
323
324    /// Unpacks the batch opening proof into its components.
325    #[inline]
326    pub const fn unpack(&self) -> (&'a [Vec<T>], &'a InputMmcs::Proof) {
327        (self.opened_values, self.opening_proof)
328    }
329}
330
331impl<'a, T: Send + Sync + Clone, InputMmcs: Mmcs<T>> From<&'a BatchOpening<T, InputMmcs>>
332    for BatchOpeningRef<'a, T, InputMmcs>
333{
334    #[inline]
335    fn from(batch_opening: &'a BatchOpening<T, InputMmcs>) -> Self {
336        Self::new(&batch_opening.opened_values, &batch_opening.opening_proof)
337    }
338}