poly_commitment/lib.rs
1// the feature `unsigned_is_multiple_of` has been stable since 1.87.0 and no longer requires an attribute to enable
2// See: https://github.com/o1-labs/mina-rust/issues/1997
3#![cfg_attr(not(feature = "std"), no_std)]
4#![deny(unsafe_code)]
5#![deny(clippy::all)]
6#![deny(clippy::pedantic)]
7#![deny(clippy::nursery)]
8
9extern crate alloc;
10
11use core::ops::Deref;
12
13use alloc::vec::Vec;
14
15pub mod collections {
16 #[cfg(not(feature = "std"))]
17 pub use alloc::collections::*;
18 #[cfg(not(feature = "std"))]
19 pub use hashbrown::{HashMap, HashSet};
20 #[cfg(feature = "std")]
21 pub use std::collections::*;
22}
23
24mod combine;
25pub mod commitment;
26pub mod error;
27#[cfg(feature = "std")]
28pub mod hash_map_cache;
29pub mod ipa;
30#[cfg(feature = "std")]
31pub mod kzg;
32#[cfg(feature = "std")]
33pub mod lagrange_basis;
34#[cfg(feature = "std")]
35pub mod precomputed_srs;
36#[cfg(feature = "std")]
37pub mod utils;
38
39// Exposing property based tests for the SRS trait
40#[cfg(feature = "std")]
41pub mod pbt_srs;
42
43pub use commitment::PolyComm;
44
45use crate::{
46 commitment::{BatchEvaluationProof, BlindedCommitment, CommitmentCurve},
47 error::CommitmentError,
48};
49use ark_poly::Radix2EvaluationDomain as D;
50use mina_poseidon::FqSponge;
51use rand_core::{CryptoRng, RngCore};
52
53#[cfg(feature = "std")]
54use {
55 crate::utils::DensePolynomialOrEvaluations,
56 ark_ec::AffineRepr,
57 ark_ff::UniformRand,
58 ark_poly::{univariate::DensePolynomial, EvaluationDomain, Evaluations},
59};
60
61pub trait SRS<G: CommitmentCurve>: Clone + Sized + Sync + Send {
62 /// The maximum polynomial degree that can be committed to
63 fn max_poly_size(&self) -> usize;
64
65 /// Get the group element used for blinding commitments
66 fn blinding_commitment(&self) -> G;
67
68 /// Same as [`SRS::mask`] except you can pass blinders manually.
69 ///
70 /// A [`BlindedCommitment`] object is returned instead of a [`PolyComm`]
71 /// object to keep the blinding factors and the commitment together. The
72 /// blinded commitment is saved in the commitment field of the output.
73 /// The output is wrapped into a [`Result`] to handle the case the blinders
74 /// are not the same length than the number of chunks commitments have.
75 ///
76 /// # Errors
77 ///
78 /// Returns [`CommitmentError::BlindersDontMatch`] if the number of
79 /// blinders does not match the number of commitment chunks.
80 fn mask_custom(
81 &self,
82 com: PolyComm<G>,
83 blinders: &PolyComm<G::ScalarField>,
84 ) -> Result<BlindedCommitment<G>, CommitmentError>;
85
86 /// Turns a non-hiding commitment into a hiding one.
87 ///
88 /// Transforms each given `<a, G>` into `(<a, G> + wH, w)` with
89 /// a random `w` per commitment.
90 /// A [`BlindedCommitment`] object is returned instead of a [`PolyComm`]
91 /// object to keep the blinding factors and the commitment together. The
92 /// blinded commitment is saved in the commitment field of the output.
93 #[cfg(feature = "std")]
94 fn mask(
95 &self,
96 comm: PolyComm<G>,
97 rng: &mut (impl RngCore + CryptoRng),
98 ) -> BlindedCommitment<G> {
99 let blinders = comm.map(|_| G::ScalarField::rand(rng));
100 self.mask_custom(comm, &blinders).unwrap()
101 }
102
103 /// This function commits a polynomial using the SRS' basis of size `n`.
104 /// - `plnm`: polynomial to commit to. The polynomial can be of any degree,
105 /// including higher than `n`.
106 /// - `num_chunks`: the minimal number of commitments to be included in the
107 /// output polynomial commitment.
108 ///
109 /// The function returns the commitments to the chunks (of size at most `n`) of
110 /// the polynomials.
111 ///
112 /// The function will also pad with zeroes if the polynomial has a degree
113 /// smaller than `n * num_chunks`.
114 ///
115 /// Note that if the polynomial has a degree higher than `n * num_chunks`,
116 /// the output will contain more than `num_chunks` commitments as it will
117 /// also contain the additional chunks.
118 ///
119 /// See the test
120 /// [`crate::pbt_srs::test_regression_commit_non_hiding_expected_number_of_chunks`]
121 /// for an example of the number of chunks returned.
122 #[cfg(feature = "std")]
123 fn commit_non_hiding(
124 &self,
125 plnm: &DensePolynomial<G::ScalarField>,
126 num_chunks: usize,
127 ) -> PolyComm<G>;
128
129 /// Commits a polynomial, potentially splitting the result.
130 ///
131 /// It is analogous to [`SRS::commit_evaluations`] but for polynomials.
132 /// A [`BlindedCommitment`] object is returned instead of a [`PolyComm`]
133 /// object to keep the blinding factors and the commitment together. The
134 /// blinded commitment is saved in the commitment field of the output.
135 #[cfg(feature = "std")]
136 fn commit(
137 &self,
138 plnm: &DensePolynomial<G::ScalarField>,
139 num_chunks: usize,
140 rng: &mut (impl RngCore + CryptoRng),
141 ) -> BlindedCommitment<G>;
142
143 /// Commit to a polynomial, with custom blinding factors.
144 ///
145 /// It is a combination of [`SRS::commit`] and [`SRS::mask_custom`].
146 /// It is analogous to [`SRS::commit_evaluations_custom`] but for
147 /// polynomials.
148 /// A [`BlindedCommitment`] object is returned instead of a [`PolyComm`]
149 /// object to keep the blinding factors and the commitment together. The
150 /// blinded commitment is saved in the commitment field of the output.
151 /// The output is wrapped into a [`Result`] to handle the case the blinders
152 /// are not the same length than the number of chunks commitments have.
153 ///
154 /// # Errors
155 ///
156 /// Returns [`CommitmentError::BlindersDontMatch`] if the number of
157 /// blinders does not match the number of commitment chunks.
158 #[cfg(feature = "std")]
159 fn commit_custom(
160 &self,
161 plnm: &DensePolynomial<G::ScalarField>,
162 num_chunks: usize,
163 blinders: &PolyComm<G::ScalarField>,
164 ) -> Result<BlindedCommitment<G>, CommitmentError>;
165
166 /// Commit to evaluations, without blinding factors.
167 ///
168 /// It is analogous to [`SRS::commit_non_hiding`] but for evaluations.
169 #[cfg(feature = "std")]
170 fn commit_evaluations_non_hiding(
171 &self,
172 domain: D<G::ScalarField>,
173 plnm: &Evaluations<G::ScalarField, D<G::ScalarField>>,
174 ) -> PolyComm<G>;
175
176 /// Commit to evaluations with blinding factors.
177 ///
178 /// Generated using the random number generator `rng`.
179 /// It is analogous to [`SRS::commit`] but for evaluations.
180 /// A [`BlindedCommitment`] object is returned instead of a [`PolyComm`]
181 /// object to keep the blinding factors and the commitment together. The
182 /// blinded commitment is saved in the commitment field of the output.
183 #[cfg(feature = "std")]
184 fn commit_evaluations(
185 &self,
186 domain: D<G::ScalarField>,
187 plnm: &Evaluations<G::ScalarField, D<G::ScalarField>>,
188 rng: &mut (impl RngCore + CryptoRng),
189 ) -> BlindedCommitment<G>;
190
191 /// Commit to evaluations with custom blinding factors.
192 ///
193 /// It is a combination of [`SRS::commit_evaluations`] and
194 /// [`SRS::mask_custom`].
195 /// It is analogous to [`SRS::commit_custom`] but for evaluations.
196 /// A [`BlindedCommitment`] object is returned instead of a [`PolyComm`]
197 /// object to keep the blinding factors and the commitment together. The
198 /// blinded commitment is saved in the commitment field of the output.
199 /// The output is wrapped into a [`Result`] to handle the case the blinders
200 /// are not the same length than the number of chunks commitments have.
201 ///
202 /// # Errors
203 ///
204 /// Returns [`CommitmentError::BlindersDontMatch`] if the number of
205 /// blinders does not match the number of commitment chunks.
206 #[cfg(feature = "std")]
207 fn commit_evaluations_custom(
208 &self,
209 domain: D<G::ScalarField>,
210 plnm: &Evaluations<G::ScalarField, D<G::ScalarField>>,
211 blinders: &PolyComm<G::ScalarField>,
212 ) -> Result<BlindedCommitment<G>, CommitmentError>;
213
214 /// Create an SRS of size `depth`.
215 ///
216 /// Warning: in the case of a trusted setup, as it is required for a
217 /// polynomial commitment scheme like KZG, a toxic waste is generated using
218 /// `rand::thread_rng()`. This is not the behavior you would expect in a
219 /// production environment.
220 /// However, we do accept this behavior for the sake of simplicity in the
221 /// interface, and this method will only be supposed to be used in tests in
222 /// this case.
223 #[cfg(feature = "std")]
224 fn create(depth: usize) -> Self;
225
226 /// Compute commitments to the lagrange basis corresponding to the given domain and
227 /// cache them in the SRS
228 fn get_lagrange_basis(
229 &self,
230 domain: D<G::ScalarField>,
231 ) -> impl Deref<Target = Vec<PolyComm<G>>> + '_;
232
233 /// Same as `get_lagrange_basis` but only using the domain size.
234 fn get_lagrange_basis_from_domain_size(
235 &self,
236 domain_size: usize,
237 ) -> impl Deref<Target = Vec<PolyComm<G>>> + '_;
238
239 #[cfg(feature = "std")]
240 fn size(&self) -> usize;
241}
242
243#[cfg(feature = "std")]
244#[allow(type_alias_bounds)]
245/// An alias to represent a polynomial (in either coefficient or
246/// evaluation form), with a set of *scalar field* elements that
247/// represent the exponent of its blinder.
248// TODO: add a string to name the polynomial
249type PolynomialsToCombine<'a, G: CommitmentCurve, D: EvaluationDomain<G::ScalarField>> = &'a [(
250 DensePolynomialOrEvaluations<'a, G::ScalarField, D>,
251 PolyComm<G::ScalarField>,
252)];
253
254pub trait OpenProof<G: CommitmentCurve, const FULL_ROUNDS: usize>: Sized + Clone {
255 type SRS: SRS<G> + core::fmt::Debug;
256
257 /// Create an opening proof for a batch of polynomials. The parameters are
258 /// the following:
259 /// - `srs`: the structured reference string used to commit
260 /// to the polynomials
261 /// - `group_map`: the group map
262 /// - `plnms`: the list of polynomials to open, with possible blinders.
263 /// The type is simply an alias to handle the polynomials in evaluations or
264 /// coefficients forms.
265 /// - `elm`: the evaluation points
266 /// - `polyscale`: a challenge to bacth the polynomials.
267 /// - `evalscale`: a challenge to bacth the evaluation points
268 /// - `sponge`: Sponge used to coin and absorb values and simulate
269 /// non-interactivity using the Fiat-Shamir transformation.
270 /// - `rng`: a pseudo random number generator used for zero-knowledge
271 #[cfg(feature = "std")]
272 #[allow(clippy::too_many_arguments)]
273 fn open<EFqSponge, RNG, D: EvaluationDomain<<G as AffineRepr>::ScalarField>>(
274 srs: &Self::SRS,
275 group_map: &<G as CommitmentCurve>::Map,
276 plnms: PolynomialsToCombine<G, D>,
277 elm: &[<G as AffineRepr>::ScalarField],
278 polyscale: <G as AffineRepr>::ScalarField,
279 evalscale: <G as AffineRepr>::ScalarField,
280 sponge: EFqSponge,
281 rng: &mut RNG,
282 ) -> Self
283 where
284 EFqSponge: Clone
285 + FqSponge<<G as AffineRepr>::BaseField, G, <G as AffineRepr>::ScalarField, FULL_ROUNDS>,
286 RNG: RngCore + CryptoRng;
287
288 /// Verify the opening proof
289 fn verify<EFqSponge, RNG>(
290 srs: &Self::SRS,
291 group_map: &G::Map,
292 batch: &mut [BatchEvaluationProof<G, EFqSponge, Self, FULL_ROUNDS>],
293 rng: &mut RNG,
294 ) -> bool
295 where
296 EFqSponge: FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
297 RNG: RngCore + CryptoRng;
298}