Skip to main content

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}