fullcodec_plonk/proof_system/
proof.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//! A Proof stores the commitments to all of the elements that
8//! are needed to univocally identify a prove of some statement.
9//!
10//! This module contains the implementation of the `TurboComposer`s
11//! `Proof` structure and it's methods.
12
13use super::linearisation_poly::ProofEvaluations;
14use crate::commitment_scheme::Commitment;
15use dusk_bytes::{DeserializableSlice, Serializable};
16use parity_scale_codec::{Decode, Encode};
17
18/// A Proof is a composition of `Commitment`s to the Witness, Permutation,
19/// Quotient, Shifted and Opening polynomials as well as the
20/// `ProofEvaluations`.
21///
22/// It's main goal is to allow the `Verifier` to
23/// formally verify that the secret witnesses used to generate the [`Proof`]
24/// satisfy a circuit that both [`Prover`](super::Prover) and
25/// [`Verifier`](super::Verifier) have in common succintly and without any
26/// capabilities of adquiring any kind of knowledge about the witness used to
27/// construct the Proof.
28#[derive(Debug, Eq, PartialEq, Clone, Default, Decode, Encode)]
29pub struct Proof {
30    /// Commitment to the witness polynomial for the left wires.
31    pub(crate) a_comm: Commitment,
32    /// Commitment to the witness polynomial for the right wires.
33    pub(crate) b_comm: Commitment,
34    /// Commitment to the witness polynomial for the output wires.
35    pub(crate) c_comm: Commitment,
36    /// Commitment to the witness polynomial for the fourth wires.
37    pub(crate) d_comm: Commitment,
38
39    /// Commitment to the lookup query polynomial.
40    pub(crate) f_comm: Commitment,
41
42    /// Commitment to first half of sorted polynomial
43    pub(crate) h_1_comm: Commitment,
44
45    /// Commitment to second half of sorted polynomial
46    pub(crate) h_2_comm: Commitment,
47
48    /// Commitment to the permutation polynomial.
49    pub(crate) z_comm: Commitment,
50
51    /// Commitment to the plonkup permutation polynomial.
52    pub(crate) p_comm: Commitment,
53
54    /// Commitment to the quotient polynomial.
55    pub(crate) t_1_comm: Commitment,
56    /// Commitment to the quotient polynomial.
57    pub(crate) t_2_comm: Commitment,
58    /// Commitment to the quotient polynomial.
59    pub(crate) t_3_comm: Commitment,
60    /// Commitment to the quotient polynomial.
61    pub(crate) t_4_comm: Commitment,
62
63    /// Commitment to the opening polynomial.
64    pub(crate) w_z_comm: Commitment,
65    /// Commitment to the shifted opening polynomial.
66    pub(crate) w_zw_comm: Commitment,
67    /// Subset of all of the evaluations added to the proof.
68    pub(crate) evaluations: ProofEvaluations,
69}
70
71impl Serializable<{ 15 * Commitment::SIZE + ProofEvaluations::SIZE }>
72    for Proof
73{
74    type Error = dusk_bytes::Error;
75
76    #[allow(unused_must_use)]
77    fn to_bytes(&self) -> [u8; Self::SIZE] {
78        use dusk_bytes::Write;
79
80        let mut buf = [0u8; Self::SIZE];
81        let mut writer = &mut buf[..];
82        writer.write(&self.a_comm.to_bytes());
83        writer.write(&self.b_comm.to_bytes());
84        writer.write(&self.c_comm.to_bytes());
85        writer.write(&self.d_comm.to_bytes());
86        writer.write(&self.f_comm.to_bytes());
87        writer.write(&self.h_1_comm.to_bytes());
88        writer.write(&self.h_2_comm.to_bytes());
89        writer.write(&self.z_comm.to_bytes());
90        writer.write(&self.p_comm.to_bytes());
91        writer.write(&self.t_1_comm.to_bytes());
92        writer.write(&self.t_2_comm.to_bytes());
93        writer.write(&self.t_3_comm.to_bytes());
94        writer.write(&self.t_4_comm.to_bytes());
95        writer.write(&self.w_z_comm.to_bytes());
96        writer.write(&self.w_zw_comm.to_bytes());
97        writer.write(&self.evaluations.to_bytes());
98
99        buf
100    }
101
102    fn from_bytes(buf: &[u8; Self::SIZE]) -> Result<Self, Self::Error> {
103        let mut buffer = &buf[..];
104
105        let a_comm = Commitment::from_reader(&mut buffer)?;
106        let b_comm = Commitment::from_reader(&mut buffer)?;
107        let c_comm = Commitment::from_reader(&mut buffer)?;
108        let d_comm = Commitment::from_reader(&mut buffer)?;
109        let f_comm = Commitment::from_reader(&mut buffer)?;
110        let h_1_comm = Commitment::from_reader(&mut buffer)?;
111        let h_2_comm = Commitment::from_reader(&mut buffer)?;
112        let z_comm = Commitment::from_reader(&mut buffer)?;
113        let p_comm = Commitment::from_reader(&mut buffer)?;
114        let t_1_comm = Commitment::from_reader(&mut buffer)?;
115        let t_2_comm = Commitment::from_reader(&mut buffer)?;
116        let t_3_comm = Commitment::from_reader(&mut buffer)?;
117        let t_4_comm = Commitment::from_reader(&mut buffer)?;
118        let w_z_comm = Commitment::from_reader(&mut buffer)?;
119        let w_zw_comm = Commitment::from_reader(&mut buffer)?;
120        let evaluations = ProofEvaluations::from_reader(&mut buffer)?;
121
122        Ok(Proof {
123            a_comm,
124            b_comm,
125            c_comm,
126            d_comm,
127            f_comm,
128            h_1_comm,
129            h_2_comm,
130            z_comm,
131            p_comm,
132            t_1_comm,
133            t_2_comm,
134            t_3_comm,
135            t_4_comm,
136            w_z_comm,
137            w_zw_comm,
138            evaluations,
139        })
140    }
141}
142
143use crate::{
144    commitment_scheme::{AggregateProof, OpeningKey},
145    error::Error,
146    fft::EvaluationDomain,
147    proof_system::widget::VerifierKey,
148    transcript::TranscriptProtocol,
149    util::batch_inversion,
150};
151use dusk_bls12_381::{multiscalar_mul::msm_variable_base, BlsScalar, G1Affine};
152use merlin::Transcript;
153use sp_std::vec::Vec;
154
155impl Proof {
156    /// Performs the verification of a [`Proof`] returning a boolean result.
157    pub(crate) fn verify(
158        &self,
159        verifier_key: &VerifierKey,
160        transcript: &mut Transcript,
161        opening_key: &OpeningKey,
162        pub_inputs: &[BlsScalar],
163    ) -> Result<(), Error> {
164        let domain = EvaluationDomain::new(verifier_key.n as usize)?;
165
166        // Subgroup checks are done when the proof is deserialised.
167
168        // In order for the Verifier and Prover to have the same view in the
169        // non-interactive setting Both parties must commit the same
170        // elements into the transcript Below the verifier will simulate
171        // an interaction with the prover by adding the same elements
172        // that the prover added into the transcript, hence generating the
173        // same challenges
174        //
175        // Add commitment to witness polynomials to transcript
176        transcript.append_commitment(b"w_l", &self.a_comm);
177        transcript.append_commitment(b"w_r", &self.b_comm);
178        transcript.append_commitment(b"w_o", &self.c_comm);
179        transcript.append_commitment(b"w_4", &self.d_comm);
180
181        // Compute zeta compression challenge
182        let zeta = transcript.challenge_scalar(b"zeta");
183
184        // Add f_poly commitment to transcript
185        transcript.append_commitment(b"f", &self.f_comm);
186
187        // Compute beta and gamma challenges
188        let beta = transcript.challenge_scalar(b"beta");
189        transcript.append_scalar(b"beta", &beta);
190        let gamma = transcript.challenge_scalar(b"gamma");
191        // Compute delta and epsilon challenges
192        let delta = transcript.challenge_scalar(b"delta");
193        let epsilon = transcript.challenge_scalar(b"epsilon");
194
195        // Add commitment to permutation polynomial to transcript
196        transcript.append_commitment(b"z", &self.z_comm);
197
198        // Compute evaluation challenge
199        let z_challenge = transcript.challenge_scalar(b"z_challenge");
200
201        // Add h polynomials to transcript
202        transcript.append_commitment(b"h1", &self.h_1_comm);
203        transcript.append_commitment(b"h2", &self.h_2_comm);
204
205        // Add permutation polynomial commitment to transcript
206        transcript.append_commitment(b"p", &self.p_comm);
207
208        // Compute quotient challenge
209        let alpha = transcript.challenge_scalar(b"alpha");
210        let range_sep_challenge =
211            transcript.challenge_scalar(b"range separation challenge");
212        let logic_sep_challenge =
213            transcript.challenge_scalar(b"logic separation challenge");
214        let fixed_base_sep_challenge =
215            transcript.challenge_scalar(b"fixed base separation challenge");
216        let var_base_sep_challenge =
217            transcript.challenge_scalar(b"variable base separation challenge");
218        let lookup_sep_challenge =
219            transcript.challenge_scalar(b"lookup challenge");
220
221        // Add commitment to quotient polynomial to transcript
222        transcript.append_commitment(b"t_1", &self.t_1_comm);
223        transcript.append_commitment(b"t_2", &self.t_2_comm);
224        transcript.append_commitment(b"t_3", &self.t_3_comm);
225        transcript.append_commitment(b"t_4", &self.t_4_comm);
226
227        // Compute zero polynomial evaluated at `z_challenge`
228        let z_h_eval = domain.evaluate_vanishing_polynomial(&z_challenge);
229
230        // Compute first lagrange polynomial evaluated at `z_challenge`
231        let l1_eval =
232            compute_first_lagrange_evaluation(&domain, &z_h_eval, &z_challenge);
233
234        let table_comm = Commitment(G1Affine::from(
235            verifier_key.lookup.table_1.0
236                + verifier_key.lookup.table_2.0 * zeta
237                + verifier_key.lookup.table_3.0 * zeta * zeta
238                + verifier_key.lookup.table_4.0 * zeta * zeta * zeta,
239        ));
240
241        // Compute quotient polynomial evaluated at `z_challenge`
242        let t_eval = self.compute_quotient_evaluation(
243            &domain,
244            pub_inputs,
245            &alpha,
246            &beta,
247            &gamma,
248            &delta,
249            &epsilon,
250            &z_challenge,
251            &z_h_eval,
252            &l1_eval,
253            &self.evaluations.perm_eval,
254            &lookup_sep_challenge,
255        );
256
257        // Compute commitment to quotient polynomial
258        // This method is necessary as we pass the `un-splitted` variation
259        // to our commitment scheme
260        let t_comm =
261            self.compute_quotient_commitment(&z_challenge, domain.size());
262
263        // Add evaluations to transcript
264        transcript.append_scalar(b"a_eval", &self.evaluations.a_eval);
265        transcript.append_scalar(b"b_eval", &self.evaluations.b_eval);
266        transcript.append_scalar(b"c_eval", &self.evaluations.c_eval);
267        transcript.append_scalar(b"d_eval", &self.evaluations.d_eval);
268        transcript.append_scalar(b"a_next_eval", &self.evaluations.a_next_eval);
269        transcript.append_scalar(b"b_next_eval", &self.evaluations.b_next_eval);
270        transcript.append_scalar(b"d_next_eval", &self.evaluations.d_next_eval);
271        transcript
272            .append_scalar(b"left_sig_eval", &self.evaluations.left_sigma_eval);
273        transcript.append_scalar(
274            b"right_sig_eval",
275            &self.evaluations.right_sigma_eval,
276        );
277        transcript
278            .append_scalar(b"out_sig_eval", &self.evaluations.out_sigma_eval);
279        transcript
280            .append_scalar(b"q_arith_eval", &self.evaluations.q_arith_eval);
281        transcript.append_scalar(b"q_c_eval", &self.evaluations.q_c_eval);
282        transcript.append_scalar(b"q_l_eval", &self.evaluations.q_l_eval);
283        transcript.append_scalar(b"q_r_eval", &self.evaluations.q_r_eval);
284        transcript
285            .append_scalar(b"q_lookup_eval", &self.evaluations.q_lookup_eval);
286        transcript.append_scalar(b"perm_eval", &self.evaluations.perm_eval);
287        transcript.append_scalar(
288            b"lookup_perm_eval",
289            &self.evaluations.lookup_perm_eval,
290        );
291        transcript.append_scalar(b"h_1_eval", &self.evaluations.h_1_eval);
292        transcript
293            .append_scalar(b"h_1_next_eval", &self.evaluations.h_1_next_eval);
294        transcript.append_scalar(b"h_2_eval", &self.evaluations.h_2_eval);
295        transcript.append_scalar(b"t_eval", &t_eval);
296        transcript.append_scalar(b"r_eval", &self.evaluations.lin_poly_eval);
297
298        // Compute linearisation commitment
299        let r_comm = self.compute_linearisation_commitment(
300            &alpha,
301            &beta,
302            &gamma,
303            &delta,
304            &epsilon,
305            &zeta,
306            (
307                &range_sep_challenge,
308                &logic_sep_challenge,
309                &fixed_base_sep_challenge,
310                &var_base_sep_challenge,
311                &lookup_sep_challenge,
312            ),
313            &z_challenge,
314            l1_eval,
315            self.evaluations.table_eval,
316            self.evaluations.table_next_eval,
317            verifier_key,
318        );
319
320        // Commitment Scheme
321        // Now we delegate computation to the commitment scheme by batch
322        // checking two proofs The `AggregateProof`, which is a
323        // proof that all the necessary polynomials evaluated at
324        // `z_challenge` are correct and a `SingleProof` which
325        // is proof that the permutation polynomial evaluated at the shifted
326        // root of unity is correct
327
328        // Compose the Aggregated Proof
329        //
330        let mut aggregate_proof = AggregateProof::with_witness(self.w_z_comm);
331        aggregate_proof.add_part((t_eval, t_comm));
332        aggregate_proof.add_part((self.evaluations.lin_poly_eval, r_comm));
333        aggregate_proof.add_part((self.evaluations.a_eval, self.a_comm));
334        aggregate_proof.add_part((self.evaluations.b_eval, self.b_comm));
335        aggregate_proof.add_part((self.evaluations.c_eval, self.c_comm));
336        aggregate_proof.add_part((self.evaluations.d_eval, self.d_comm));
337        aggregate_proof.add_part((
338            self.evaluations.left_sigma_eval,
339            verifier_key.permutation.left_sigma,
340        ));
341        aggregate_proof.add_part((
342            self.evaluations.right_sigma_eval,
343            verifier_key.permutation.right_sigma,
344        ));
345        aggregate_proof.add_part((
346            self.evaluations.out_sigma_eval,
347            verifier_key.permutation.out_sigma,
348        ));
349        aggregate_proof.add_part((self.evaluations.f_eval, self.f_comm));
350        aggregate_proof.add_part((self.evaluations.h_1_eval, self.h_1_comm));
351        aggregate_proof.add_part((self.evaluations.h_2_eval, self.h_2_comm));
352        aggregate_proof.add_part((self.evaluations.table_eval, table_comm));
353        // Flatten proof with opening challenge
354        let flattened_proof_a = aggregate_proof.flatten(transcript);
355
356        // Compose the shifted aggregate proof
357        let mut shifted_aggregate_proof =
358            AggregateProof::with_witness(self.w_zw_comm);
359        shifted_aggregate_proof
360            .add_part((self.evaluations.perm_eval, self.z_comm));
361        shifted_aggregate_proof
362            .add_part((self.evaluations.a_next_eval, self.a_comm));
363        shifted_aggregate_proof
364            .add_part((self.evaluations.b_next_eval, self.b_comm));
365        shifted_aggregate_proof
366            .add_part((self.evaluations.d_next_eval, self.d_comm));
367        shifted_aggregate_proof
368            .add_part((self.evaluations.h_1_next_eval, self.h_1_comm));
369        shifted_aggregate_proof
370            .add_part((self.evaluations.lookup_perm_eval, self.p_comm));
371        shifted_aggregate_proof
372            .add_part((self.evaluations.table_next_eval, table_comm));
373        let flattened_proof_b = shifted_aggregate_proof.flatten(transcript);
374        // Add commitment to openings to transcript
375        transcript.append_commitment(b"w_z", &self.w_z_comm);
376        transcript.append_commitment(b"w_z_w", &self.w_zw_comm);
377        // Batch check
378        if opening_key
379            .batch_check(
380                &[z_challenge, (z_challenge * domain.group_gen)],
381                &[flattened_proof_a, flattened_proof_b],
382                transcript,
383            )
384            .is_err()
385        {
386            return Err(Error::ProofVerificationError);
387        }
388
389        Ok(())
390    }
391
392    #[allow(clippy::too_many_arguments)]
393    fn compute_quotient_evaluation(
394        &self,
395        domain: &EvaluationDomain,
396        pub_inputs: &[BlsScalar],
397        alpha: &BlsScalar,
398        beta: &BlsScalar,
399        gamma: &BlsScalar,
400        delta: &BlsScalar,
401        epsilon: &BlsScalar,
402        z_challenge: &BlsScalar,
403        z_h_eval: &BlsScalar,
404        l1_eval: &BlsScalar,
405        z_hat_eval: &BlsScalar,
406        lookup_sep_challenge: &BlsScalar,
407    ) -> BlsScalar {
408        // Compute the public input polynomial evaluated at `z_challenge`
409        let pi_eval = compute_barycentric_eval(pub_inputs, z_challenge, domain);
410
411        // Compute powers of alpha_0
412        let alpha_sq = alpha.square();
413
414        // Compute powers of alpha_1
415        let l_sep_2 = lookup_sep_challenge.square();
416        let l_sep_3 = lookup_sep_challenge * l_sep_2;
417
418        // Compute common term
419        let epsilon_one_plus_delta = epsilon * (BlsScalar::one() + delta);
420
421        // r + PI(z)
422        let a = self.evaluations.lin_poly_eval + pi_eval;
423
424        // a + beta * sigma_1 + gamma
425        let beta_sig1 = beta * self.evaluations.left_sigma_eval;
426        let b_0 = self.evaluations.a_eval + beta_sig1 + gamma;
427
428        // b+ beta * sigma_2 + gamma
429        let beta_sig2 = beta * self.evaluations.right_sigma_eval;
430        let b_1 = self.evaluations.b_eval + beta_sig2 + gamma;
431
432        // c+ beta * sigma_3 + gamma
433        let beta_sig3 = beta * self.evaluations.out_sigma_eval;
434        let b_2 = self.evaluations.c_eval + beta_sig3 + gamma;
435
436        // ((d + gamma) * z_hat) * alpha_0
437        let b_3 = (self.evaluations.d_eval + gamma) * z_hat_eval * alpha;
438
439        let b = b_0 * b_1 * b_2 * b_3;
440
441        // l_1(z) * alpha_0^2
442        let c = l1_eval * alpha_sq;
443
444        // l_1(z) * alpha_1^2
445        let e = l1_eval * l_sep_2;
446
447        // p_eval * (epsilon( 1+ delta) + h_1_eval + delta *
448        // h_2_eval)(epsilon( 1+ delta) + delta * h_1_next_eval) * alpha_1^3
449        let f_0 = epsilon_one_plus_delta
450            + self.evaluations.h_1_eval
451            + (delta * self.evaluations.h_2_eval);
452        let f_1 =
453            epsilon_one_plus_delta + (delta * self.evaluations.h_1_next_eval);
454        let f = self.evaluations.lookup_perm_eval * f_0 * f_1 * l_sep_3;
455
456        // Return t_eval
457        (a - b - c //+ d
458                 - e - f)
459            * z_h_eval.invert().unwrap()
460    }
461
462    fn compute_quotient_commitment(
463        &self,
464        z_challenge: &BlsScalar,
465        n: usize,
466    ) -> Commitment {
467        let z_n = z_challenge.pow(&[n as u64, 0, 0, 0]);
468        let z_two_n = z_challenge.pow(&[2 * n as u64, 0, 0, 0]);
469        let z_three_n = z_challenge.pow(&[3 * n as u64, 0, 0, 0]);
470        let t_comm = self.t_1_comm.0
471            + self.t_2_comm.0 * z_n
472            + self.t_3_comm.0 * z_two_n
473            + self.t_4_comm.0 * z_three_n;
474        Commitment::from(t_comm)
475    }
476
477    // Commitment to [r]_1
478    #[allow(clippy::too_many_arguments)]
479    fn compute_linearisation_commitment(
480        &self,
481        alpha: &BlsScalar,
482        beta: &BlsScalar,
483        gamma: &BlsScalar,
484        delta: &BlsScalar,
485        epsilon: &BlsScalar,
486        zeta: &BlsScalar,
487        (
488            range_sep_challenge,
489            logic_sep_challenge,
490            fixed_base_sep_challenge,
491            var_base_sep_challenge,
492            lookup_sep_challenge,
493        ): (&BlsScalar, &BlsScalar, &BlsScalar, &BlsScalar, &BlsScalar),
494        z_challenge: &BlsScalar,
495        l1_eval: BlsScalar,
496        t_eval: BlsScalar,
497        t_next_eval: BlsScalar,
498        verifier_key: &VerifierKey,
499    ) -> Commitment {
500        let mut scalars: Vec<_> = Vec::with_capacity(6);
501        let mut points: Vec<G1Affine> = Vec::with_capacity(6);
502
503        verifier_key.arithmetic.compute_linearisation_commitment(
504            &mut scalars,
505            &mut points,
506            &self.evaluations,
507        );
508
509        verifier_key.range.compute_linearisation_commitment(
510            range_sep_challenge,
511            &mut scalars,
512            &mut points,
513            &self.evaluations,
514        );
515
516        verifier_key.logic.compute_linearisation_commitment(
517            logic_sep_challenge,
518            &mut scalars,
519            &mut points,
520            &self.evaluations,
521        );
522
523        verifier_key.fixed_base.compute_linearisation_commitment(
524            fixed_base_sep_challenge,
525            &mut scalars,
526            &mut points,
527            &self.evaluations,
528        );
529
530        verifier_key.variable_base.compute_linearisation_commitment(
531            var_base_sep_challenge,
532            &mut scalars,
533            &mut points,
534            &self.evaluations,
535        );
536
537        verifier_key.lookup.compute_linearisation_commitment(
538            lookup_sep_challenge,
539            &mut scalars,
540            &mut points,
541            &self.evaluations,
542            (delta, epsilon),
543            zeta,
544            &l1_eval,
545            &t_eval,
546            &t_next_eval,
547            self.h_2_comm.0,
548            self.p_comm.0,
549        );
550
551        verifier_key.permutation.compute_linearisation_commitment(
552            &mut scalars,
553            &mut points,
554            &self.evaluations,
555            z_challenge,
556            (alpha, beta, gamma),
557            &l1_eval,
558            self.z_comm.0,
559        );
560
561        Commitment::from(msm_variable_base(&points, &scalars))
562    }
563}
564
565fn compute_first_lagrange_evaluation(
566    domain: &EvaluationDomain,
567    z_h_eval: &BlsScalar,
568    z_challenge: &BlsScalar,
569) -> BlsScalar {
570    let n_fr = BlsScalar::from(domain.size() as u64);
571    let denom = n_fr * (z_challenge - BlsScalar::one());
572    z_h_eval * denom.invert().unwrap()
573}
574
575fn compute_barycentric_eval(
576    evaluations: &[BlsScalar],
577    point: &BlsScalar,
578    domain: &EvaluationDomain,
579) -> BlsScalar {
580    let numerator = (point.pow(&[domain.size() as u64, 0, 0, 0])
581        - BlsScalar::one())
582        * domain.size_inv;
583
584    // Indices with non-zero evaluations
585    #[cfg(not(feature = "std"))]
586    let range = (0..evaluations.len()).into_iter();
587
588    #[cfg(feature = "std")]
589    let range = (0..evaluations.len()).into_iter();
590
591    let non_zero_evaluations: Vec<usize> = range
592        .filter(|&i| {
593            let evaluation = &evaluations[i];
594            evaluation != &BlsScalar::zero()
595        })
596        .collect();
597
598    // Only compute the denominators with non-zero evaluations
599    #[cfg(not(feature = "std"))]
600    let range = (0..non_zero_evaluations.len()).into_iter();
601
602    #[cfg(feature = "std")]
603    let range = (0..non_zero_evaluations.len()).into_iter();
604
605    let mut denominators: Vec<BlsScalar> = range
606        .clone()
607        .map(|i| {
608            // index of non-zero evaluation
609            let index = non_zero_evaluations[i];
610
611            (domain.group_gen_inv.pow(&[index as u64, 0, 0, 0]) * point)
612                - BlsScalar::one()
613        })
614        .collect();
615    batch_inversion(&mut denominators);
616
617    let result: BlsScalar = range
618        .map(|i| {
619            let eval_index = non_zero_evaluations[i];
620            let eval = evaluations[eval_index];
621
622            denominators[i] * eval
623        })
624        .sum();
625
626    result * numerator
627}
628
629#[cfg(test)]
630mod proof_tests {
631    use super::*;
632    use dusk_bls12_381::BlsScalar;
633    use rand::SeedableRng;
634    use rand_xorshift::XorShiftRng;
635
636    #[test]
637    fn test_dusk_bytes_serde_proof() {
638        // Initialize the polynomial commitment parameters
639        let rng = XorShiftRng::from_seed([
640            0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37,
641            0x32, 0x54, 0x06, 0xbc, 0xe5,
642        ]);
643        let proof = Proof {
644            a_comm: Commitment::default(),
645            b_comm: Commitment::default(),
646            c_comm: Commitment::default(),
647            d_comm: Commitment::default(),
648            f_comm: Commitment::default(),
649            h_1_comm: Commitment::default(),
650            h_2_comm: Commitment::default(),
651            z_comm: Commitment::default(),
652            p_comm: Commitment::default(),
653            t_1_comm: Commitment::default(),
654            t_2_comm: Commitment::default(),
655            t_3_comm: Commitment::default(),
656            t_4_comm: Commitment::default(),
657            w_z_comm: Commitment::default(),
658            w_zw_comm: Commitment::default(),
659            evaluations: ProofEvaluations {
660                a_eval: BlsScalar::random(rng.clone()),
661                b_eval: BlsScalar::random(rng.clone()),
662                c_eval: BlsScalar::random(rng.clone()),
663                d_eval: BlsScalar::random(rng.clone()),
664                a_next_eval: BlsScalar::random(rng.clone()),
665                b_next_eval: BlsScalar::random(rng.clone()),
666                d_next_eval: BlsScalar::random(rng.clone()),
667                q_arith_eval: BlsScalar::random(rng.clone()),
668                q_c_eval: BlsScalar::random(rng.clone()),
669                q_l_eval: BlsScalar::random(rng.clone()),
670                q_r_eval: BlsScalar::random(rng.clone()),
671                q_lookup_eval: BlsScalar::random(rng.clone()),
672                left_sigma_eval: BlsScalar::random(rng.clone()),
673                right_sigma_eval: BlsScalar::random(rng.clone()),
674                out_sigma_eval: BlsScalar::random(rng.clone()),
675                lin_poly_eval: BlsScalar::random(rng.clone()),
676                perm_eval: BlsScalar::random(rng.clone()),
677                lookup_perm_eval: BlsScalar::random(rng.clone()),
678                h_1_eval: BlsScalar::random(rng.clone()),
679                h_1_next_eval: BlsScalar::random(rng.clone()),
680                h_2_eval: BlsScalar::random(rng.clone()),
681                f_eval: BlsScalar::random(rng.clone()),
682                table_eval: BlsScalar::random(rng.clone()),
683                table_next_eval: BlsScalar::random(rng.clone()),
684            },
685        };
686
687        let proof_bytes = proof.to_bytes();
688        let got_proof = Proof::from_bytes(&proof_bytes).unwrap();
689        assert_eq!(got_proof, proof);
690    }
691}