Skip to main content

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}