fullcodec_plonk/commitment_scheme/kzg10/
key.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7//! Key module contains the utilities and data structures
8//! that support the generation and usage of Commit and
9//! Opening keys.
10use super::{proof::Proof, Commitment};
11use crate::{
12    error::Error, fft::Polynomial, transcript::TranscriptProtocol, util,
13};
14use dusk_bls12_381::{
15    multiscalar_mul::msm_variable_base, BlsScalar, G1Affine, G1Projective,
16    G2Affine, G2Prepared,
17};
18use dusk_bytes::{DeserializableSlice, Serializable};
19use merlin::Transcript;
20use parity_scale_codec::{Decode, Encode};
21use serde::{Deserialize, Deserializer, Serialize, Serializer};
22use sp_std::vec;
23use sp_std::vec::Vec;
24
25/// CommitKey is used to commit to a polynomial which is bounded by the
26/// max_degree.
27#[derive(Debug, Clone, PartialEq, Encode, Decode, Serialize, Deserialize)]
28pub struct CommitKey {
29    /// Group elements of the form `{ \beta^i G }`, where `i` ranges from 0 to
30    /// `degree`.
31    #[serde(
32        serialize_with = "serialize_powers_of_g",
33        deserialize_with = "deserialize_powers_of_g"
34    )]
35    pub(crate) powers_of_g: Vec<G1Affine>,
36}
37
38fn serialize_powers_of_g<S>(
39    powers_of_g: &Vec<G1Affine>,
40    se: S,
41) -> Result<S::Ok, S::Error>
42where
43    S: Serializer,
44{
45    use serde::ser::SerializeSeq;
46    let mut seq = se.serialize_seq(Some(powers_of_g.len()))?;
47    for coeff in powers_of_g {
48        seq.serialize_element(coeff)?;
49    }
50    seq.end()
51}
52
53fn deserialize_powers_of_g<'de, D>(de: D) -> Result<Vec<G1Affine>, D::Error>
54where
55    D: Deserializer<'de>,
56{
57    let s = Deserialize::deserialize(de).unwrap();
58    Ok(vec![s])
59}
60
61impl CommitKey {
62    /// Serialize the [`CommitKey`] into bytes.
63    ///
64    /// This operation is designed to store the raw representation of the
65    /// contents of the CommitKey. Therefore, the size of the bytes outputed
66    /// by this function is expected to be the double than the one that
67    /// `CommitKey::to_bytes`.
68    ///
69    /// # Note
70    /// This function should be used when we want to serialize the CommitKey
71    /// allowing a really fast deserialization later.
72    /// This functions output should not be used by the regular
73    /// `CommitKey::from_bytes` fn.
74    pub fn to_raw_var_bytes(&self) -> Vec<u8> {
75        let mut bytes = Vec::with_capacity(
76            u64::SIZE + self.powers_of_g.len() * G1Affine::RAW_SIZE,
77        );
78
79        let len = self.powers_of_g.len() as u64;
80        let len = len.to_le_bytes();
81        bytes.extend_from_slice(&len);
82
83        self.powers_of_g
84            .iter()
85            .for_each(|g| bytes.extend_from_slice(&g.to_raw_bytes()));
86
87        bytes
88    }
89
90    /// Deserialize [`CommitKey`] from a set of bytes created by
91    /// [`CommitKey::to_raw_var_bytes`].
92    ///
93    /// The bytes source is expected to be trusted and no check will be
94    /// performed reggarding the points security
95    ///
96    /// # Safety
97    /// This function will not produce any memory errors but can deal to the
98    /// generation of invalid or unsafe points/keys. To make sure this does not
99    /// happen, the inputed bytes must match the ones that were generated by
100    /// the encoding functions of this lib.
101    pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self {
102        let mut len = [0u8; u64::SIZE];
103        len.copy_from_slice(&bytes[..u64::SIZE]);
104        let len = u64::from_le_bytes(len);
105
106        let powers_of_g = bytes[u64::SIZE..]
107            .chunks_exact(G1Affine::RAW_SIZE)
108            .zip(0..len)
109            .map(|(c, _)| G1Affine::from_slice_unchecked(c))
110            .collect();
111
112        Self { powers_of_g }
113    }
114
115    /// Serialises the [`CommitKey`] into a byte slice.
116    pub fn to_var_bytes(&self) -> Vec<u8> {
117        self.powers_of_g
118            .iter()
119            .flat_map(|item| item.to_bytes().to_vec())
120            .collect()
121    }
122
123    /// Deserialise a slice of bytes into a [`CommitKey`] struct performing
124    /// security and consistency checks for each point that the bytes
125    /// contain.
126    ///
127    /// # Note
128    /// This function can be really slow if the [`CommitKey`] has a certain
129    /// degree/size. If the bytes come from a trusted source such as a local
130    /// file, we recommend to use [`CommitKey::from_slice_unchecked`] and
131    /// [`CommitKey::to_raw_var_bytes`].
132    pub fn from_slice(bytes: &[u8]) -> Result<CommitKey, Error> {
133        let powers_of_g = bytes
134            .chunks(G1Affine::SIZE)
135            .map(|chunk| G1Affine::from_slice(chunk))
136            .collect::<Result<Vec<G1Affine>, dusk_bytes::Error>>()?;
137
138        Ok(CommitKey { powers_of_g })
139    }
140
141    /// Returns the maximum degree polynomial that you can commit to.
142    pub(crate) fn max_degree(&self) -> usize {
143        self.powers_of_g.len() - 1
144    }
145
146    /// Truncates the commit key to a lower max degree.
147    /// Returns an error if the truncated degree is zero or if the truncated
148    /// degree is larger than the max degree of the commit key.
149    pub(crate) fn truncate(
150        &self,
151        mut truncated_degree: usize,
152    ) -> Result<CommitKey, Error> {
153        match truncated_degree {
154            // Check that the truncated degree is not zero
155            0 => Err(Error::TruncatedDegreeIsZero),
156            // Check that max degree is less than truncated degree
157            i if i > self.max_degree() => Err(Error::TruncatedDegreeTooLarge),
158            i => {
159                if i == 1 {
160                    truncated_degree += 1
161                };
162                let truncated_powers = Self {
163                    powers_of_g: self.powers_of_g[..=truncated_degree].to_vec(),
164                };
165                Ok(truncated_powers)
166            }
167        }
168    }
169
170    /// Checks whether the polynomial we are committing to:
171    /// - Has zero degree
172    /// - Has a degree which is more than the max supported degree
173    ///
174    /// Returns an error if any of the above conditions are true.
175    fn check_commit_degree_is_within_bounds(
176        &self,
177        poly_degree: usize,
178    ) -> Result<(), Error> {
179        match (poly_degree == 0, poly_degree > self.max_degree()) {
180            (true, _) => Err(Error::PolynomialDegreeIsZero),
181            (false, true) => Err(Error::PolynomialDegreeTooLarge),
182            (false, false) => Ok(()),
183        }
184    }
185
186    /// Commits to a [`Polynomial`] returning the corresponding [`Commitment`].
187    ///
188    /// Returns an error if the polynomial's degree is more than the max degree
189    /// of the commit key.
190    pub(crate) fn commit(
191        &self,
192        polynomial: &Polynomial,
193    ) -> Result<Commitment, Error> {
194        // Check whether we can safely commit to this polynomial
195        self.check_commit_degree_is_within_bounds(polynomial.degree())?;
196
197        // Compute commitment
198        Ok(Commitment::from(msm_variable_base(
199            &self.powers_of_g,
200            &polynomial.coeffs,
201        )))
202    }
203
204    /// Computes a single witness for multiple polynomials at the same point, by
205    /// taking a random linear combination of the individual witnesses.
206    /// We apply the same optimisation mentioned in when computing each witness;
207    /// removing f(z).
208    pub(crate) fn compute_aggregate_witness(
209        &self,
210        polynomials: &[Polynomial],
211        point: &BlsScalar,
212        transcript: &mut Transcript,
213    ) -> Polynomial {
214        let challenge = transcript.challenge_scalar(b"aggregate_witness");
215        let powers = util::powers_of(&challenge, polynomials.len() - 1);
216
217        assert_eq!(powers.len(), polynomials.len());
218
219        let numerator: Polynomial = polynomials
220            .iter()
221            .zip(powers.iter())
222            .map(|(poly, challenge)| poly * challenge)
223            .sum();
224        numerator.ruffini(*point)
225    }
226}
227
228/// Opening Key is used to verify opening proofs made about a committed
229/// polynomial.
230#[derive(Clone, Debug, Encode, Decode, Serialize, Deserialize, PartialEq)]
231pub struct OpeningKey {
232    /// The generator of G1.
233    pub(crate) g: G1Affine,
234    /// The generator of G2.
235    pub(crate) h: G2Affine,
236    /// \beta times the above generator of G2.
237    pub(crate) beta_h: G2Affine,
238    /// The generator of G2, prepared for use in pairings.
239    pub(crate) prepared_h: G2Prepared,
240    /// \beta times the above generator of G2, prepared for use in pairings.
241    pub(crate) prepared_beta_h: G2Prepared,
242}
243
244impl Serializable<{ G1Affine::SIZE + G2Affine::SIZE * 2 }> for OpeningKey {
245    type Error = dusk_bytes::Error;
246    #[allow(unused_must_use)]
247    fn to_bytes(&self) -> [u8; Self::SIZE] {
248        use dusk_bytes::Write;
249        let mut buf = [0u8; Self::SIZE];
250        let mut writer = &mut buf[..];
251        // This can't fail therefore we don't care about the Result nor use it.
252        writer.write(&self.g.to_bytes());
253        writer.write(&self.h.to_bytes());
254        writer.write(&self.beta_h.to_bytes());
255
256        buf
257    }
258
259    fn from_bytes(buf: &[u8; Self::SIZE]) -> Result<Self, Self::Error> {
260        let mut buffer = &buf[..];
261        let g = G1Affine::from_reader(&mut buffer)?;
262        let h = G2Affine::from_reader(&mut buffer)?;
263        let beta_h = G2Affine::from_reader(&mut buffer)?;
264
265        Ok(Self::new(g, h, beta_h))
266    }
267}
268
269impl OpeningKey {
270    pub(crate) fn new(
271        g: G1Affine,
272        h: G2Affine,
273        beta_h: G2Affine,
274    ) -> OpeningKey {
275        let prepared_h = G2Prepared::from(h);
276        let prepared_beta_h = G2Prepared::from(beta_h);
277        OpeningKey {
278            g,
279            h,
280            beta_h,
281            prepared_h,
282            prepared_beta_h,
283        }
284    }
285
286    /// Checks whether a batch of polynomials evaluated at different points,
287    /// returned their specified value.
288    pub(crate) fn batch_check(
289        &self,
290        points: &[BlsScalar],
291        proofs: &[Proof],
292        transcript: &mut Transcript,
293    ) -> Result<(), Error> {
294        let mut total_c = G1Projective::identity();
295        let mut total_w = G1Projective::identity();
296
297        let challenge = transcript.challenge_scalar(b"batch"); // XXX: Verifier can add their own randomness at this point
298        let powers = util::powers_of(&challenge, proofs.len() - 1);
299        // Instead of multiplying g and gamma_g in each turn, we simply
300        // accumulate their coefficients and perform a final
301        // multiplication at the end.
302        let mut g_multiplier = BlsScalar::zero();
303
304        for ((proof, challenge), point) in proofs.iter().zip(powers).zip(points)
305        {
306            let mut c = G1Projective::from(proof.commitment_to_polynomial.0);
307            let w = proof.commitment_to_witness.0;
308            c += w * point;
309            g_multiplier += challenge * proof.evaluated_point;
310
311            total_c += c * challenge;
312            total_w += w * challenge;
313        }
314        total_c -= self.g * g_multiplier;
315
316        let affine_total_w = G1Affine::from(-total_w);
317        let affine_total_c = G1Affine::from(total_c);
318
319        let pairing = dusk_bls12_381::multi_miller_loop(&[
320            (&affine_total_w, &self.prepared_beta_h),
321            (&affine_total_c, &self.prepared_h),
322        ])
323        .final_exponentiation();
324
325        if pairing != dusk_bls12_381::Gt::identity() {
326            return Err(Error::PairingCheckFailure);
327        };
328        Ok(())
329    }
330}
331
332#[cfg(feature = "std")]
333#[cfg(test)]
334mod test {
335    use super::*;
336    use crate::commitment_scheme::{AggregateProof, PublicParameters};
337    use crate::fft::Polynomial;
338    use dusk_bls12_381::BlsScalar;
339    use dusk_bytes::Serializable;
340    use merlin::Transcript;
341    use rand_core::OsRng;
342
343    // Checks that a polynomial `p` was evaluated at a point `z` and returned
344    // the value specified `v`. ie. v = p(z).
345    fn check(op_key: &OpeningKey, point: BlsScalar, proof: Proof) -> bool {
346        let inner_a: G1Affine = (proof.commitment_to_polynomial.0
347            - (op_key.g * proof.evaluated_point))
348            .into();
349
350        let inner_b: G2Affine = (op_key.beta_h - (op_key.h * point)).into();
351        let prepared_inner_b = G2Prepared::from(-inner_b);
352
353        let pairing = dusk_bls12_381::multi_miller_loop(&[
354            (&inner_a, &op_key.prepared_h),
355            (&proof.commitment_to_witness.0, &prepared_inner_b),
356        ])
357        .final_exponentiation();
358
359        pairing == dusk_bls12_381::Gt::identity()
360    }
361
362    // Creates an opening proof that a polynomial `p` was correctly evaluated at
363    // p(z) and produced the value `v`. ie v = p(z).
364    // Returns an error if the polynomials degree is too large.
365    fn open_single(
366        ck: &CommitKey,
367        polynomial: &Polynomial,
368        value: &BlsScalar,
369        point: &BlsScalar,
370    ) -> Result<Proof, Error> {
371        let witness_poly = compute_single_witness(polynomial, point);
372        Ok(Proof {
373            commitment_to_witness: ck.commit(&witness_poly)?,
374            evaluated_point: *value,
375            commitment_to_polynomial: ck.commit(polynomial)?,
376        })
377    }
378
379    // Creates an opening proof that multiple polynomials were evaluated at the
380    // same point and that each evaluation produced the correct evaluation
381    // point. Returns an error if any of the polynomial's degrees are too
382    // large.
383    fn open_multiple(
384        ck: &CommitKey,
385        polynomials: &[Polynomial],
386        evaluations: Vec<BlsScalar>,
387        point: &BlsScalar,
388        transcript: &mut Transcript,
389    ) -> Result<AggregateProof, Error> {
390        // Commit to polynomials
391        let mut polynomial_commitments = Vec::with_capacity(polynomials.len());
392        for poly in polynomials.iter() {
393            polynomial_commitments.push(ck.commit(poly)?)
394        }
395
396        // Compute the aggregate witness for polynomials
397        let witness_poly =
398            ck.compute_aggregate_witness(polynomials, point, transcript);
399
400        // Commit to witness polynomial
401        let witness_commitment = ck.commit(&witness_poly)?;
402
403        let aggregate_proof = AggregateProof {
404            commitment_to_witness: witness_commitment,
405            evaluated_points: evaluations,
406            commitments_to_polynomials: polynomial_commitments,
407        };
408        Ok(aggregate_proof)
409    }
410
411    // For a given polynomial `p` and a point `z`, compute the witness
412    // for p(z) using Ruffini's method for simplicity.
413    // The Witness is the quotient of f(x) - f(z) / x-z.
414    // However we note that the quotient polynomial is invariant under the value
415    // f(z) ie. only the remainder changes. We can therefore compute the
416    // witness as f(x) / x - z and only use the remainder term f(z) during
417    // verification.
418    fn compute_single_witness(
419        polynomial: &Polynomial,
420        point: &BlsScalar,
421    ) -> Polynomial {
422        // Computes `f(x) / x-z`, returning it as the witness poly
423        polynomial.ruffini(*point)
424    }
425
426    // Creates a proving key and verifier key based on a specified degree
427    fn setup_test(degree: usize) -> Result<(CommitKey, OpeningKey), Error> {
428        let srs = PublicParameters::setup(degree, &mut OsRng)?;
429        srs.trim(degree)
430    }
431    #[test]
432    fn test_basic_commit() -> Result<(), Error> {
433        let degree = 25;
434        let (ck, opening_key) = setup_test(degree)?;
435        let point = BlsScalar::from(10);
436
437        let poly = Polynomial::rand(degree, &mut OsRng);
438        let value = poly.evaluate(&point);
439
440        let proof = open_single(&ck, &poly, &value, &point)?;
441
442        let ok = check(&opening_key, point, proof);
443        assert!(ok);
444        Ok(())
445    }
446    #[test]
447    fn test_batch_verification() -> Result<(), Error> {
448        let degree = 25;
449        let (ck, vk) = setup_test(degree)?;
450
451        let point_a = BlsScalar::from(10);
452        let point_b = BlsScalar::from(11);
453
454        // Compute secret polynomial a
455        let poly_a = Polynomial::rand(degree, &mut OsRng);
456        let value_a = poly_a.evaluate(&point_a);
457        let proof_a = open_single(&ck, &poly_a, &value_a, &point_a)?;
458        assert!(check(&vk, point_a, proof_a));
459
460        // Compute secret polynomial b
461        let poly_b = Polynomial::rand(degree, &mut OsRng);
462        let value_b = poly_b.evaluate(&point_b);
463        let proof_b = open_single(&ck, &poly_b, &value_b, &point_b)?;
464        assert!(check(&vk, point_b, proof_b));
465
466        vk.batch_check(
467            &[point_a, point_b],
468            &[proof_a, proof_b],
469            &mut Transcript::new(b""),
470        )
471    }
472    #[test]
473    fn test_aggregate_witness() -> Result<(), Error> {
474        let max_degree = 27;
475        let (ck, opening_key) = setup_test(max_degree)?;
476        let point = BlsScalar::from(10);
477
478        // Committer's View
479        let aggregated_proof = {
480            // Compute secret polynomials and their evaluations
481            let poly_a = Polynomial::rand(25, &mut OsRng);
482            let poly_a_eval = poly_a.evaluate(&point);
483
484            let poly_b = Polynomial::rand(26 + 1, &mut OsRng);
485            let poly_b_eval = poly_b.evaluate(&point);
486
487            let poly_c = Polynomial::rand(27, &mut OsRng);
488            let poly_c_eval = poly_c.evaluate(&point);
489
490            open_multiple(
491                &ck,
492                &[poly_a, poly_b, poly_c],
493                vec![poly_a_eval, poly_b_eval, poly_c_eval],
494                &point,
495                &mut Transcript::new(b"agg_flatten"),
496            )?
497        };
498
499        // Verifier's View
500        let ok = {
501            let flattened_proof =
502                aggregated_proof.flatten(&mut Transcript::new(b"agg_flatten"));
503            check(&opening_key, point, flattened_proof)
504        };
505
506        assert!(ok);
507        Ok(())
508    }
509
510    #[test]
511    fn test_batch_with_aggregation() -> Result<(), Error> {
512        let max_degree = 28;
513        let (ck, opening_key) = setup_test(max_degree)?;
514        let point_a = BlsScalar::from(10);
515        let point_b = BlsScalar::from(11);
516
517        // Committer's View
518        let (aggregated_proof, single_proof) = {
519            // Compute secret polynomial and their evaluations
520            let poly_a = Polynomial::rand(25, &mut OsRng);
521            let poly_a_eval = poly_a.evaluate(&point_a);
522
523            let poly_b = Polynomial::rand(26, &mut OsRng);
524            let poly_b_eval = poly_b.evaluate(&point_a);
525
526            let poly_c = Polynomial::rand(27, &mut OsRng);
527            let poly_c_eval = poly_c.evaluate(&point_a);
528
529            let poly_d = Polynomial::rand(28, &mut OsRng);
530            let poly_d_eval = poly_d.evaluate(&point_b);
531
532            let aggregated_proof = open_multiple(
533                &ck,
534                &[poly_a, poly_b, poly_c],
535                vec![poly_a_eval, poly_b_eval, poly_c_eval],
536                &point_a,
537                &mut Transcript::new(b"agg_batch"),
538            )?;
539
540            let single_proof =
541                open_single(&ck, &poly_d, &poly_d_eval, &point_b)?;
542
543            (aggregated_proof, single_proof)
544        };
545
546        // Verifier's View
547
548        let mut transcript = Transcript::new(b"agg_batch");
549        let flattened_proof = aggregated_proof.flatten(&mut transcript);
550
551        opening_key.batch_check(
552            &[point_a, point_b],
553            &[flattened_proof, single_proof],
554            &mut transcript,
555        )
556    }
557
558    #[test]
559    fn commit_key_serde() -> Result<(), Error> {
560        let (commit_key, _) = setup_test(11)?;
561        let ck_bytes = commit_key.to_var_bytes();
562        let ck_bytes_safe = CommitKey::from_slice(&ck_bytes)?;
563
564        assert_eq!(commit_key.powers_of_g, ck_bytes_safe.powers_of_g);
565        Ok(())
566    }
567
568    #[test]
569    fn opening_key_dusk_bytes() -> Result<(), Error> {
570        let (_, opening_key) = setup_test(7)?;
571        let ok_bytes = opening_key.to_bytes();
572        let obtained_key = OpeningKey::from_bytes(&ok_bytes)?;
573
574        assert_eq!(opening_key.to_bytes(), obtained_key.to_bytes());
575        Ok(())
576    }
577
578    #[test]
579    fn commit_key_bytes_unchecked() -> Result<(), Error> {
580        let (ck, _) = setup_test(7)?;
581
582        let ck_p = unsafe {
583            let bytes = ck.to_raw_var_bytes();
584            CommitKey::from_slice_unchecked(&bytes)
585        };
586
587        assert_eq!(ck, ck_p);
588        Ok(())
589    }
590}