vector_commit/
kzg_amortized.rs

1use std::{collections::HashMap, error::Error, fmt::Display, marker::PhantomData};
2
3use ark_ec::{pairing::Pairing, Group};
4use ark_ff::{Field, One, PrimeField, UniformRand, Zero};
5use ark_poly::{
6    univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Evaluations,
7    GeneralEvaluationDomain, Polynomial,
8};
9
10use crate::{VCPreparedData, VCUniversalParams, VectorCommitment};
11
12/// KZGKey represents the universal parameters, AKA reference string, for both
13/// committing polynomials and verifying commitments
14#[derive(Clone, Debug)]
15pub struct KZGKey<F: PrimeField, G1: Group, G2: Group> {
16    /// The max degree this reference string supports
17    degree: usize,
18
19    /// Reference string in G1 group
20    ref_string_g1: Vec<G1>,
21
22    /// For G2, we only need α*g
23    g2_secret: G2,
24
25    /// The lagrange polynomials are created and commited to.
26    lagrange_commitments: Vec<G1>,
27
28    /// domain.size / unity^i
29    //unity_neg: Vec<F>,
30    domain: GeneralEvaluationDomain<F>,
31}
32
33impl<F, G1, G2> KZGKey<F, G1, G2>
34where
35    F: PrimeField,
36    G1: Group<ScalarField = F>,
37    G2: Group<ScalarField = F>,
38{
39    fn new_from_secret(secret: F, max_degree: usize) -> Self {
40        let g1 = G1::generator();
41        let g2 = G2::generator();
42        let domain = GeneralEvaluationDomain::<F>::new(max_degree).unwrap();
43
44        let mut params: Self = Self {
45            degree: max_degree,
46            ref_string_g1: vec![],
47            g2_secret: g2 * secret,
48            lagrange_commitments: vec![], //TODO
49            domain: domain,
50        };
51        params.ref_string_g1.push(g1);
52        let mut sec_cur: F = F::one();
53        for _i in 1..max_degree {
54            sec_cur = sec_cur * secret; //α^i
55            params.ref_string_g1.push(g1 * sec_cur);
56        }
57        //params.lagrange_commitments = domain.evaluate_all_lagrange_coefficients(secret).iter().map(|l| g1 * l).collect();
58        params.lagrange_commitments = domain.ifft(&params.ref_string_g1);
59
60        params
61    }
62
63    /// Multiplies the array of coefficients with the reference string in G1
64    /// and sums the group elements.
65    fn commit_g1(&self, coeffs: &[F]) -> G1 {
66        let mut sum = G1::zero();
67        for (i, c) in coeffs.iter().enumerate() {
68            sum += self.element_at_g1(i) * c;
69        }
70
71        sum
72    }
73
74    fn commit_lagrange(&self, evaluations: &[F]) -> G1 {
75        let mut sum = G1::zero();
76        for (i, e) in evaluations.iter().enumerate() {
77            sum += self.lagrange_commitments[i] * e;
78        }
79
80        sum
81    }
82
83    fn element_at_g1(&self, index: usize) -> G1 {
84        self.ref_string_g1[index]
85    }
86
87    /// We only care about the α*g element in G2
88    fn get_g2(&self) -> G2 {
89        self.g2_secret
90    }
91
92    pub fn domain(&self) -> GeneralEvaluationDomain<F> {
93        self.domain
94    }
95
96    fn unity(&self) -> F {
97        self.domain.group_gen()
98    }
99}
100
101impl<F, G1, G2> VCUniversalParams for KZGKey<F, G1, G2>
102where
103    F: PrimeField,
104    G1: Group<ScalarField = F>,
105    G2: Group<ScalarField = F>,
106{
107    fn max_size(&self) -> usize {
108        self.degree
109    }
110}
111
112/// KZGPreparedData will create a polynomial to commit to by interpolating the dataset.
113/// Instead of the evaluations occuring at [0,1,...,n-1] they instead occur at [0,w,w^2,...w^n-1].
114/// w is the n-th root of unity
115#[derive(Clone, PartialEq)]
116pub struct KZGPreparedData<F: PrimeField> {
117    /// The evaluation domain of the dataset, aka the points that we will run polynomial evaluation at
118    evaluations: Evaluations<F, GeneralEvaluationDomain<F>>,
119
120    /// Because evaluations may be sparse, we cache which entries are filled
121    filled_index: Vec<usize>,
122
123    /// TODO: Remove
124    poly: DensePolynomial<F>,
125
126    /// The size of the data-set (not necessairly equal to n)
127    size: usize,
128}
129
130impl<F: PrimeField> KZGPreparedData<F> {
131    pub fn from_points_and_domain(mut points: Vec<F>, domain: GeneralEvaluationDomain<F>) -> Self {
132        let len = points.len();
133        if len < domain.size() {
134            points.resize(domain.size(), F::zero());
135        }
136        let evals = Evaluations::from_vec_and_domain(points, domain);
137        let poly = evals.interpolate_by_ref();
138
139        let mut filled: Vec<usize> = Vec::new();
140        for i in 0..len {
141            filled.push(i);
142        }
143
144        Self {
145            evaluations: evals,
146            filled_index: filled,
147            poly: poly,
148            size: len,
149        }
150    }
151    fn domain_size(&self) -> usize {
152        self.evaluations.domain().size()
153    }
154
155    fn evaluate(&self, index: usize) -> F {
156        self.evaluations[index]
157    }
158
159    /// Returns w^i
160    fn index_to_point(&self, index: usize) -> F {
161        self.evaluations.domain().element(index)
162    }
163
164    fn poly(&self) -> DensePolynomial<F> {
165        self.evaluations.clone().interpolate()
166    }
167}
168
169impl<F: PrimeField> VCPreparedData for KZGPreparedData<F> {
170    type Item = F;
171    type Error = KZGError;
172
173    /// FIXIME: HACKY
174    fn from_vec(data: Vec<Self::Item>) -> Self {
175        let domain = GeneralEvaluationDomain::<F>::new(data.len()).unwrap();
176        Self::from_points_and_domain(data, domain)
177    }
178
179    fn set_evaluation(&mut self, index: usize, value: Self::Item) -> Result<(), Self::Error> {
180        // TODO: Domain expansion, although in Verkle case the domain size is the arity of the tree.
181        if index > self.domain_size() {
182            return Err(KZGError::OutOfDomainBounds);
183        }
184        self.evaluations.evals[index] = value;
185        return Ok(());
186    }
187
188    fn get(&self, index: usize) -> Option<&Self::Item> {
189        self.evaluations.evals.get(index)
190    }
191
192    fn get_all(&self) -> Vec<(usize, Self::Item)> {
193        let mut res: Vec<(usize, Self::Item)> = Vec::new();
194        for i in self.filled_index.iter() {
195            res.push((*i, self.evaluations.evals[*i]));
196        }
197        res
198    }
199
200    fn max_size(&self) -> usize {
201        self.domain_size()
202    }
203}
204
205#[derive(PartialEq, Clone, Default, Debug)]
206pub struct KZGCommitment<G: Group> {
207    commit: G,
208}
209
210impl<G: Group> KZGCommitment<G> {
211    fn new(commit: G) -> Self {
212        Self { commit }
213    }
214
215    fn group_to_field(&self) -> G::ScalarField {
216        if self.commit.is_zero() {
217            <G::ScalarField as Field>::ZERO
218        } else {
219            let mut bytes: Vec<u8> = Vec::new();
220            // TODO: Check
221            self.commit.serialize_compressed(&mut bytes).unwrap();
222            <G::ScalarField as PrimeField>::from_le_bytes_mod_order(&bytes)
223        }
224    }
225}
226
227pub struct KZGProof<F: PrimeField, G: Group> {
228    commit: G,
229
230    /// index is the position in the vector, NOT the evaluation point
231    index: usize,
232
233    /// Evaluation point
234    /// TODO: pass root of unity with proof/batch proof?
235    point: F,
236
237    data: F,
238}
239
240pub struct KZGBatchProof<F: PrimeField, G: Group> {
241    //commit: G,
242
243    // For now, there is no proof compression. This is a map of index -> (eval point, output, proof)
244    proofs: HashMap<usize, (F, F, G)>,
245}
246
247impl<F: PrimeField, G: Group> Default for KZGBatchProof<F, G> {
248    fn default() -> Self {
249        Self {
250            proofs: HashMap::new(),
251        }
252    }
253}
254
255#[derive(Clone, Debug)]
256pub enum KZGError {
257    DefaultError,
258    DataExceedsMaxSize,
259    InvalidDomain,
260    OutOfDomainBounds,
261}
262
263impl KZGError {
264    fn new() -> Self {
265        Self::DefaultError
266    }
267}
268
269impl Display for KZGError {
270    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271        write!(f, "{}", "")
272    }
273}
274
275impl Error for KZGError {}
276
277/// Implementation of the Feist-Khovratovich technique of "Fast Amortized KZG proofs".
278#[derive(PartialEq, Clone)]
279pub struct KZGAmortized<E: Pairing> {
280    _engine: PhantomData<E>,
281}
282
283impl<E: Pairing> VectorCommitment for KZGAmortized<E> {
284    type UniversalParams = KZGKey<E::ScalarField, E::G1, E::G2>;
285    type PreparedData = KZGPreparedData<E::ScalarField>;
286    type Commitment = KZGCommitment<E::G1>;
287    type Proof = KZGProof<E::ScalarField, E::G1>;
288    type BatchProof = KZGBatchProof<E::ScalarField, E::G1>;
289    type Error = KZGError;
290
291    fn setup<R: rand::RngCore>(
292        max_items: usize,
293        rng: &mut R,
294    ) -> Result<Self::UniversalParams, Self::Error> {
295        let secret = <E::ScalarField as UniformRand>::rand(rng);
296        Ok(KZGKey::new_from_secret(secret, max_items))
297    }
298
299    fn commit(
300        key: &Self::UniversalParams,
301        data: &Self::PreparedData,
302    ) -> Result<Self::Commitment, Self::Error> {
303        Ok(KZGCommitment {
304            commit: key.commit_lagrange(&data.evaluations.evals),
305        })
306    }
307
308    fn prove(
309        key: &Self::UniversalParams,
310        commitment: &Self::Commitment,
311        index: usize,
312        data: &Self::PreparedData,
313    ) -> Result<Self::Proof, Self::Error> {
314        let commit = Self::single_proof(&key, &data, index)?;
315        Ok(Self::Proof {
316            commit,
317            index,
318            data: data.evaluate(index),
319            point: data.index_to_point(index),
320        })
321    }
322
323    fn prove_all(
324        key: &Self::UniversalParams,
325        commitment: &Self::Commitment,
326        data: &Self::PreparedData,
327    ) -> Result<Self::BatchProof, Self::Error> {
328        Self::get_all_proofs(&key, &data)
329    }
330
331    fn verify(
332        key: &Self::UniversalParams,
333        commitment: &Self::Commitment,
334        proof: &Self::Proof,
335    ) -> Result<bool, Self::Error> {
336        // TODO: Need to be able to access domain's root of unity to be able to evaluate properly
337        let pairing1 = E::pairing(
338            proof.commit,
339            key.get_g2() - (E::G2::generator() * proof.point),
340        );
341        let pairing2 = E::pairing(
342            commitment.commit - E::G1::generator() * proof.data,
343            E::G2::generator(),
344        );
345
346        Ok(pairing1 == pairing2)
347    }
348
349    fn verify_batch(
350        key: &Self::UniversalParams,
351        commitment: &Self::Commitment,
352        proof: &Self::BatchProof,
353    ) -> Result<bool, Self::Error> {
354        let s = key.get_g2();
355        for (index, p) in proof.proofs.iter() {
356            let pairing1 = E::pairing(p.2, s - (E::G2::generator() * p.0));
357            let pairing2 = E::pairing(
358                commitment.commit - E::G1::generator() * p.1,
359                E::G2::generator(),
360            );
361
362            if pairing1 != pairing2 {
363                return Ok(false);
364            }
365        }
366
367        Ok(true)
368    }
369
370    fn convert_commitment_to_data(
371        commit: &Self::Commitment,
372    ) -> <Self::PreparedData as VCPreparedData>::Item {
373        commit.group_to_field()
374    }
375}
376
377impl<E: Pairing> KZGAmortized<E> {
378    /// Get all the proofs for every coefficient of `data`'s polynomial representation
379    fn get_all_proofs(
380        key: &KZGKey<E::ScalarField, E::G1, E::G2>,
381        data: &KZGPreparedData<E::ScalarField>,
382    ) -> Result<KZGBatchProof<E::ScalarField, E::G1>, KZGError> {
383        let all_proofs = Self::build_witness_matrix(key, data)?;
384        let mut res = KZGBatchProof::default();
385
386        let domain = data.evaluations.domain();
387        let proofs = domain.fft(&all_proofs);
388
389        for index in 0..data.size {
390            let point = data.index_to_point(index);
391            res.proofs
392                .insert(index, (point, data.evaluate(index), proofs[index]));
393        }
394
395        Ok(res)
396    }
397
398    /// Build the group elements from the FFT of the polynomial coefficients multiplied by the reference string
399    fn build_witness_matrix(
400        key: &KZGKey<E::ScalarField, E::G1, E::G2>,
401        data: &KZGPreparedData<E::ScalarField>,
402    ) -> Result<Vec<E::G1>, KZGError> {
403        let degree = data.poly.degree();
404        let coeffs = data.poly.coeffs();
405        let domain = GeneralEvaluationDomain::<E::ScalarField>::new(degree * 2)
406            .ok_or(KZGError::InvalidDomain)?;
407        let domain_size = domain.size();
408
409        let mut c_hat: Vec<E::ScalarField> = vec![coeffs[degree]];
410        c_hat.extend(vec![E::ScalarField::zero(); degree + 1]);
411        c_hat.extend(&coeffs[0..degree]);
412
413        let mut s_hat = key.ref_string_g1[0..degree].to_vec();
414        s_hat.reverse();
415        s_hat.extend(vec![E::G1::zero(); domain_size - degree]);
416
417        let y = domain.fft(&c_hat);
418        let v = domain.fft(&s_hat);
419        let u = Self::elementwise_mul(&v, &y);
420        let h_hat = domain.ifft(&u);
421
422        Ok(h_hat[0..degree].to_vec())
423    }
424
425    fn single_proof(
426        key: &KZGKey<E::ScalarField, E::G1, E::G2>,
427        data: &KZGPreparedData<E::ScalarField>,
428        index: usize,
429    ) -> Result<E::G1, KZGError> {
430        let data_piece = data.evaluate(index);
431        let point = data.index_to_point(index);
432        let mut poly = data.poly.clone();
433        poly -= &DensePolynomial::<E::ScalarField>::from_coefficients_slice(&[data_piece]);
434
435        let divisor = DensePolynomial::<E::ScalarField>::from_coefficients_slice(&[
436            E::ScalarField::zero() - point,
437            E::ScalarField::one(),
438        ]);
439        let q = &poly / &divisor;
440
441        let commit = key.commit_g1(q.coeffs());
442
443        Ok(commit)
444    }
445
446    fn elementwise_mul<F: PrimeField, G1: Group<ScalarField = F>>(
447        g_vec: &Vec<G1>,
448        f_vec: &Vec<F>,
449    ) -> Vec<G1> {
450        let mut result: Vec<G1> = vec![];
451        for (g, f) in g_vec.iter().zip(f_vec.iter()) {
452            result.push(*g * *f);
453        }
454
455        result
456    }
457}
458
459#[cfg(test)]
460mod tests {
461    use super::*;
462    use ark_bn254::Bn254;
463
464    type F = <Bn254 as Pairing>::ScalarField;
465    type G1 = <Bn254 as Pairing>::G1;
466    type G2 = <Bn254 as Pairing>::G2;
467    type KZG = KZGAmortized<Bn254>;
468
469    const DATA_SIZE: usize = 4;
470    const MAX_CRS: usize = 32;
471
472    fn gen_data(num: usize) -> Vec<F> {
473        let mut data: Vec<F> = vec![];
474        let mut rng = rand::thread_rng();
475        for _i in 0..num {
476            data.push(F::rand(&mut rng));
477        }
478        data
479    }
480
481    fn setup(n: usize, max_degree: usize) -> (KZGPreparedData<F>, KZGKey<F, G1, G2>) {
482        let data = gen_data(n);
483        //let prep = KZGPreparedData::from_iter(data);
484        let crs = KZG::setup(max_degree, &mut rand::thread_rng()).unwrap();
485        let prep = KZGPreparedData::from_points_and_domain(data, crs.domain);
486
487        (prep, crs)
488    }
489
490    #[test]
491    fn test_single_proof() {
492        let (data, crs) = setup(DATA_SIZE, MAX_CRS);
493        let commit = KZG::commit(&crs, &data).unwrap();
494
495        for i in 0..DATA_SIZE {
496            let proof = KZG::prove(&crs, &commit, i, &data).unwrap();
497            assert!(KZG::verify(&crs, &commit, &proof).unwrap());
498        }
499    }
500
501    #[test]
502    fn test_batch_proof() {
503        let (data, crs) = setup(DATA_SIZE, MAX_CRS);
504        let commit = KZG::commit(&crs, &data).unwrap();
505
506        let proofs = KZG::prove_all(&crs, &commit, &data).unwrap();
507        assert!(KZG::verify_batch(&crs, &commit, &proofs).unwrap());
508    }
509
510    fn vec_to_str<T: Display>(v: &Vec<T>) -> String {
511        let mut s = "[\n".to_owned();
512        for e in v {
513            s.push_str(&format!("\t{}\n", e));
514        }
515        s.push_str("]");
516        s
517    }
518}