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