saffron/
commitment.rs

1use crate::{diff::Diff, utils, Sponge};
2use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM};
3use ark_poly::univariate::DensePolynomial;
4use kimchi::curve::KimchiCurve;
5use poly_commitment::{ipa::SRS, SRS as _};
6use rayon::prelude::*;
7use tracing::instrument;
8
9fn get_lagrange_basis<G: KimchiCurve>(srs: &SRS<G>) -> Vec<G> {
10    srs.get_lagrange_basis_from_domain_size(crate::SRS_SIZE)
11        .iter()
12        .map(|x| x.chunks[0])
13        .collect()
14}
15
16/// Compute the commitment to `data` ; if the length of `data` is greater than
17/// `SRS_SIZE`, the data is splitted in chunks of at most `SRS_SIZE` length.
18#[instrument(skip_all, level = "debug")]
19pub fn commit_to_field_elems<G: KimchiCurve>(srs: &SRS<G>, data: &[G::ScalarField]) -> Vec<G>
20where
21    <G as AffineRepr>::Group: VariableBaseMSM,
22{
23    let basis = get_lagrange_basis(srs);
24
25    let commitments_projective = (0..data.len() / crate::SRS_SIZE)
26        .into_par_iter()
27        .map(|idx| {
28            G::Group::msm(
29                &basis,
30                &data[crate::SRS_SIZE * idx..crate::SRS_SIZE * (idx + 1)],
31            )
32            .unwrap()
33        })
34        .collect::<Vec<_>>();
35
36    let commitments = G::Group::normalize_batch(commitments_projective.as_slice());
37
38    commitments
39}
40
41/// Returns the non-hiding commitment to the provided polynomial
42pub fn commit_poly<G: KimchiCurve>(srs: &SRS<G>, poly: &DensePolynomial<G::ScalarField>) -> G {
43    srs.commit_non_hiding(poly, 1).chunks[0]
44}
45
46/// Compute the commitment to the polynomial `P` of same degree as `srs`
47/// such that `P(ω_i) = sparse_data[i]` for all `ì` in `indexes` and `P(ω_i) = 0`
48/// for any other `ω_i` of the domain of the same size as `srs`.
49/// This commitment is computed in a sparse way through the sum of `sparse_data[i] × [L_i]`
50/// for all i in `indexes` and `[L_i]` the commitment to the i-th Lagrange polynomial
51/// of same degree as `srs`.
52pub fn commit_sparse<G: KimchiCurve>(
53    srs: &SRS<G>,
54    sparse_data: &[G::ScalarField],
55    indexes: &[u64],
56) -> G {
57    if sparse_data.len() != indexes.len() {
58        panic!(
59            "commitment::commit_sparse: size mismatch (sparse_data: {}, indexes: {})",
60            sparse_data.len(),
61            indexes.len()
62        )
63    };
64    let basis = get_lagrange_basis(srs);
65    let basis: Vec<G> = indexes.iter().map(|&i| basis[i as usize]).collect();
66    G::Group::msm(&basis, sparse_data).unwrap().into()
67}
68
69/// Takes commitments C_i, computes α = hash(C_0 || C_1 || ... || C_n),
70/// returns ∑ α^i C_i.
71#[instrument(skip_all, level = "debug")]
72pub fn combine_commitments<G: AffineRepr, Spng: Sponge<G::BaseField, G, G::ScalarField>>(
73    sponge: &mut Spng,
74    commitments: &[G],
75) -> (G, G::ScalarField) {
76    for commitment in commitments.iter() {
77        sponge.absorb_g(std::slice::from_ref(commitment))
78    }
79    let alpha = sponge.challenge();
80    let combined_data_commitment = utils::aggregate_commitments(alpha, commitments);
81    (combined_data_commitment, alpha)
82}
83
84/// A commitment that represent a whole data
85/// TODO: for now, we consider 1 commitment = 1 contract = 1 data
86/// This type may be redundant with other types in Proof-systems
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct Commitment<G: KimchiCurve> {
89    pub cm: G,
90}
91
92impl<G: KimchiCurve> From<G> for Commitment<G> {
93    fn from(cm: G) -> Self {
94        Self { cm }
95    }
96}
97
98impl<G: KimchiCurve> Commitment<G> {
99    /// Commit a `data` of length smaller than `SRS_SIZE`
100    /// If greater data is provided, anything above `SRS_SIZE` is ignored
101    pub fn from_data(srs: &SRS<G>, data: &[G::ScalarField]) -> Commitment<G> {
102        Commitment {
103            cm: commit_to_field_elems::<G>(srs, data)[0],
104        }
105    }
106
107    /// TODO: This only handle the single commitment version for now
108    /// This function update the given commitment based on the given diff. The
109    /// returned commitment correspond to the data for the given commitment updated
110    /// according to the diff.
111    /// This function is tested in storage.rs
112    pub fn update(&self, srs: &SRS<G>, diff: Diff<G::ScalarField>) -> Commitment<G> {
113        // TODO: precompute this, or cache it & compute it in a lazy way ; it feels like it’s already cached but I’m not sure
114        let cm_diff = commit_sparse(srs, &diff.diff_values, &diff.addresses);
115        Commitment {
116            cm: self.cm.add(cm_diff).into(),
117        }
118    }
119}