Skip to main content

ark_bulletproofs/r1cs/
verifier.rs

1#![allow(non_snake_case)]
2
3use ark_ec::{AffineRepr, VariableBaseMSM};
4use ark_ff::{Field, UniformRand};
5use ark_std::{
6    borrow::BorrowMut,
7    boxed::Box,
8    iter, mem,
9    rand::{CryptoRng, RngCore},
10    vec,
11    vec::Vec,
12    One, Zero,
13};
14use merlin::Transcript;
15
16use super::{
17    ConstraintSystem, LinearCombination, R1CSProof, RandomizableConstraintSystem,
18    RandomizedConstraintSystem, Variable,
19};
20
21use crate::errors::R1CSError;
22use crate::generators::{BulletproofGens, PedersenGens};
23use crate::transcript::TranscriptProtocol;
24
25/// A [`ConstraintSystem`] implementation for use by the verifier.
26///
27/// The verifier adds high-level variable commitments to the transcript,
28/// allocates low-level variables and creates constraints in terms of these
29/// high-level variables and low-level variables.
30///
31/// When all constraints are added, the verifying code calls `verify`
32/// which consumes the `Verifier` instance, samples random challenges
33/// that instantiate the randomized constraints, and verifies the proof.
34pub struct Verifier<G: AffineRepr, T: BorrowMut<Transcript>> {
35    transcript: T,
36    constraints: Vec<LinearCombination<G::ScalarField>>,
37
38    /// Records the number of low-level variables allocated in the
39    /// constraint system.
40    ///
41    /// Because the `VerifierCS` only keeps the constraints
42    /// themselves, it doesn't record the assignments (they're all
43    /// `Missing`), so the `num_vars` isn't kept implicitly in the
44    /// variable assignments.
45    num_vars: usize,
46    V: Vec<G>,
47
48    /// This list holds closures that will be called in the second phase of the protocol,
49    /// when non-randomized variables are committed.
50    /// After that, the option will flip to None and additional calls to `randomize_constraints`
51    /// will invoke closures immediately.
52    deferred_constraints: Vec<Box<dyn Fn(&mut RandomizingVerifier<G, T>) -> Result<(), R1CSError>>>,
53
54    /// Index of a pending multiplier that's not fully assigned yet.
55    pending_multiplier: Option<usize>,
56}
57
58/// Verifier in the randomizing phase.
59///
60/// Note: this type is exported because it is used to specify the associated type
61/// in the public impl of a trait `ConstraintSystem`, which boils down to allowing compiler to
62/// monomorphize the closures for the proving and verifying code.
63/// However, this type cannot be instantiated by the user and therefore can only be used within
64/// the callback provided to `specify_randomized_constraints`.
65pub struct RandomizingVerifier<G: AffineRepr, T: BorrowMut<Transcript>> {
66    verifier: Verifier<G, T>,
67}
68
69impl<T: BorrowMut<Transcript>, G: AffineRepr> ConstraintSystem<G::ScalarField> for Verifier<G, T> {
70    fn transcript(&mut self) -> &mut Transcript {
71        self.transcript.borrow_mut()
72    }
73
74    fn multiply(
75        &mut self,
76        mut left: LinearCombination<G::ScalarField>,
77        mut right: LinearCombination<G::ScalarField>,
78    ) -> (
79        Variable<G::ScalarField>,
80        Variable<G::ScalarField>,
81        Variable<G::ScalarField>,
82    ) {
83        let var = self.num_vars;
84        self.num_vars += 1;
85
86        // Create variables for l,r,o
87        let l_var = Variable::MultiplierLeft(var);
88        let r_var = Variable::MultiplierRight(var);
89        let o_var = Variable::MultiplierOutput(var);
90
91        // Constrain l,r,o:
92        left.terms.push((l_var, -G::ScalarField::one()));
93        right.terms.push((r_var, -G::ScalarField::one()));
94        self.constrain(left);
95        self.constrain(right);
96
97        (l_var, r_var, o_var)
98    }
99
100    fn allocate(
101        &mut self,
102        _: Option<G::ScalarField>,
103    ) -> Result<Variable<G::ScalarField>, R1CSError> {
104        match self.pending_multiplier {
105            None => {
106                let i = self.num_vars;
107                self.num_vars += 1;
108                self.pending_multiplier = Some(i);
109                Ok(Variable::MultiplierLeft(i))
110            }
111            Some(i) => {
112                self.pending_multiplier = None;
113                Ok(Variable::MultiplierRight(i))
114            }
115        }
116    }
117
118    fn allocate_multiplier(
119        &mut self,
120        _: Option<(G::ScalarField, G::ScalarField)>,
121    ) -> Result<
122        (
123            Variable<G::ScalarField>,
124            Variable<G::ScalarField>,
125            Variable<G::ScalarField>,
126        ),
127        R1CSError,
128    > {
129        let var = self.num_vars;
130        self.num_vars += 1;
131
132        // Create variables for l,r,o
133        let l_var = Variable::MultiplierLeft(var);
134        let r_var = Variable::MultiplierRight(var);
135        let o_var = Variable::MultiplierOutput(var);
136
137        Ok((l_var, r_var, o_var))
138    }
139
140    fn multipliers_len(&self) -> usize {
141        self.num_vars
142    }
143
144    fn constrain(&mut self, lc: LinearCombination<G::ScalarField>) {
145        // TODO: check that the linear combinations are valid
146        // (e.g. that variables are valid, that the linear combination
147        // evals to 0 for prover, etc).
148        self.constraints.push(lc);
149    }
150}
151
152impl<T: BorrowMut<Transcript>, G: AffineRepr> RandomizableConstraintSystem<G::ScalarField>
153    for Verifier<G, T>
154{
155    type RandomizedCS = RandomizingVerifier<G, T>;
156
157    fn specify_randomized_constraints<F>(&mut self, callback: F) -> Result<(), R1CSError>
158    where
159        F: 'static + Fn(&mut Self::RandomizedCS) -> Result<(), R1CSError>,
160    {
161        self.deferred_constraints.push(Box::new(callback));
162        Ok(())
163    }
164}
165
166impl<T: BorrowMut<Transcript>, G: AffineRepr> ConstraintSystem<G::ScalarField>
167    for RandomizingVerifier<G, T>
168{
169    fn transcript(&mut self) -> &mut Transcript {
170        self.verifier.transcript.borrow_mut()
171    }
172
173    fn multiply(
174        &mut self,
175        left: LinearCombination<G::ScalarField>,
176        right: LinearCombination<G::ScalarField>,
177    ) -> (
178        Variable<G::ScalarField>,
179        Variable<G::ScalarField>,
180        Variable<G::ScalarField>,
181    ) {
182        self.verifier.multiply(left, right)
183    }
184
185    fn allocate(
186        &mut self,
187        assignment: Option<G::ScalarField>,
188    ) -> Result<Variable<G::ScalarField>, R1CSError> {
189        self.verifier.allocate(assignment)
190    }
191
192    fn allocate_multiplier(
193        &mut self,
194        input_assignments: Option<(G::ScalarField, G::ScalarField)>,
195    ) -> Result<
196        (
197            Variable<G::ScalarField>,
198            Variable<G::ScalarField>,
199            Variable<G::ScalarField>,
200        ),
201        R1CSError,
202    > {
203        self.verifier.allocate_multiplier(input_assignments)
204    }
205
206    fn multipliers_len(&self) -> usize {
207        self.verifier.multipliers_len()
208    }
209
210    fn constrain(&mut self, lc: LinearCombination<G::ScalarField>) {
211        self.verifier.constrain(lc)
212    }
213}
214
215impl<T: BorrowMut<Transcript>, G: AffineRepr> RandomizedConstraintSystem<G::ScalarField>
216    for RandomizingVerifier<G, T>
217{
218    fn challenge_scalar(&mut self, label: &'static [u8]) -> G::ScalarField {
219        <Transcript as TranscriptProtocol<G>>::challenge_scalar(
220            self.verifier.transcript.borrow_mut(),
221            label,
222        )
223    }
224}
225
226impl<G: AffineRepr, T: BorrowMut<Transcript>> Verifier<G, T> {
227    /// Construct an empty constraint system with specified external
228    /// input variables.
229    ///
230    /// # Inputs
231    ///
232    /// The `transcript` parameter is a Merlin proof transcript.  The
233    /// `VerifierCS` holds onto the `&mut Transcript` until it consumes
234    /// itself during [`VerifierCS::verify`], releasing its borrow of the
235    /// transcript.  This ensures that the transcript cannot be
236    /// altered except by the `VerifierCS` before proving is complete.
237    ///
238    /// The `commitments` parameter is a list of Pedersen commitments
239    /// to the external variables for the constraint system.  All
240    /// external variables must be passed up-front, so that challenges
241    /// produced by [`ConstraintSystem::challenge_scalar`] are bound
242    /// to the external variables.
243    ///
244    /// # Returns
245    ///
246    /// Returns a tuple `(cs, vars)`.
247    ///
248    /// The first element is the newly constructed constraint system.
249    ///
250    /// The second element is a list of [`Variable`]s corresponding to
251    /// the external inputs, which can be used to form constraints.
252    pub fn new(mut transcript: T) -> Self {
253        <Transcript as TranscriptProtocol<G>>::r1cs_domain_sep(transcript.borrow_mut());
254
255        Verifier {
256            transcript,
257            num_vars: 0,
258            V: Vec::new(),
259            constraints: Vec::new(),
260            deferred_constraints: Vec::new(),
261            pending_multiplier: None,
262        }
263    }
264
265    /// Creates commitment to a high-level variable and adds it to the transcript.
266    ///
267    /// # Inputs
268    ///
269    /// The `commitment` parameter is a Pedersen commitment
270    /// to the external variable for the constraint system.  All
271    /// external variables must be passed up-front, so that challenges
272    /// produced by [`ConstraintSystem::challenge_scalar`] are bound
273    /// to the external variables.
274    ///
275    /// # Returns
276    ///
277    /// Returns a pair of a Pedersen commitment (as a compressed Ristretto point),
278    /// and a [`Variable`] corresponding to it, which can be used to form constraints.
279    pub fn commit(&mut self, commitment: G) -> Variable<G::ScalarField> {
280        let i = self.V.len();
281        self.V.push(commitment);
282
283        // Add the commitment to the transcript.
284        self.transcript.borrow_mut().append_point(b"V", &commitment);
285
286        Variable::Committed(i)
287    }
288
289    /// Use a challenge, `z`, to flatten the constraints in the
290    /// constraint system into vectors used for proving and
291    /// verification.
292    ///
293    /// # Output
294    ///
295    /// Returns a tuple of
296    /// ```text
297    /// (wL, wR, wO, wV, wc)
298    /// ```
299    /// where `w{L,R,O}` is \\( z \cdot z^Q \cdot W_{L,R,O} \\).
300    ///
301    /// This has the same logic as `ProverCS::flattened_constraints()`
302    /// but also computes the constant terms (which the prover skips
303    /// because they're not needed to construct the proof).
304    fn flattened_constraints(
305        &mut self,
306        z: &G::ScalarField,
307    ) -> (
308        Vec<G::ScalarField>,
309        Vec<G::ScalarField>,
310        Vec<G::ScalarField>,
311        Vec<G::ScalarField>,
312        G::ScalarField,
313    ) {
314        let n = self.num_vars;
315        let m = self.V.len();
316
317        let mut wL = vec![G::ScalarField::zero(); n];
318        let mut wR = vec![G::ScalarField::zero(); n];
319        let mut wO = vec![G::ScalarField::zero(); n];
320        let mut wV = vec![G::ScalarField::zero(); m];
321        let mut wc = G::ScalarField::zero();
322
323        let mut exp_z = *z;
324        for lc in self.constraints.iter() {
325            for (var, coeff) in &lc.terms {
326                match var {
327                    Variable::MultiplierLeft(i) => {
328                        wL[*i] += exp_z * coeff;
329                    }
330                    Variable::MultiplierRight(i) => {
331                        wR[*i] += exp_z * coeff;
332                    }
333                    Variable::MultiplierOutput(i) => {
334                        wO[*i] += exp_z * coeff;
335                    }
336                    Variable::Committed(i) => {
337                        wV[*i] -= exp_z * coeff;
338                    }
339                    Variable::One() => {
340                        wc -= exp_z * coeff;
341                    }
342                    _ => {}
343                }
344            }
345            exp_z *= z;
346        }
347
348        (wL, wR, wO, wV, wc)
349    }
350
351    /// Calls all remembered callbacks with an API that
352    /// allows generating challenge scalars.
353    fn create_randomized_constraints(mut self) -> Result<Self, R1CSError> {
354        // Clear the pending multiplier (if any) because it was committed into A_L/A_R/S.
355        self.pending_multiplier = None;
356
357        if self.deferred_constraints.len() == 0 {
358            <Transcript as TranscriptProtocol<G>>::r1cs_1phase_domain_sep(
359                self.transcript.borrow_mut(),
360            );
361            Ok(self)
362        } else {
363            <Transcript as TranscriptProtocol<G>>::r1cs_2phase_domain_sep(
364                self.transcript.borrow_mut(),
365            );
366            // Note: the wrapper could've used &mut instead of ownership,
367            // but specifying lifetimes for boxed closures is not going to be nice,
368            // so we move the self into wrapper and then move it back out afterwards.
369            let mut callbacks = mem::replace(&mut self.deferred_constraints, Vec::new());
370            let mut wrapped_self = RandomizingVerifier { verifier: self };
371            for callback in callbacks.drain(..) {
372                callback(&mut wrapped_self)?;
373            }
374            Ok(wrapped_self.verifier)
375        }
376    }
377
378    // Get scalars for single multiexponentiation verification
379    // Order is
380    // pc_gens.B
381    // pc_gens.B_blinding
382    // gens.G_vec
383    // gens.H_vec
384    // proof.A_I1
385    // proof.A_O1
386    // proof.S1
387    // proof.A_I2
388    // proof.A_O2
389    // proof.S2
390    // self.V
391    // T_1, T3, T4, T5, T6
392    // proof.ipp_proof.L_vec
393    // proof.ipp_proof.R_vec
394    pub(super) fn verification_scalars(
395        mut self,
396        proof: &R1CSProof<G>,
397        bp_gens: &BulletproofGens<G>,
398    ) -> Result<(Self, Vec<G::ScalarField>), R1CSError> {
399        // Commit a length _suffix_ for the number of high-level variables.
400        // We cannot do this in advance because user can commit variables one-by-one,
401        // but this suffix provides safe disambiguation because each variable
402        // is prefixed with a separate label.
403        let transcript = self.transcript.borrow_mut();
404        transcript.append_u64(b"m", self.V.len() as u64);
405
406        let n1 = self.num_vars;
407        transcript.validate_and_append_point(b"A_I1", &proof.A_I1)?;
408        transcript.validate_and_append_point(b"A_O1", &proof.A_O1)?;
409        transcript.validate_and_append_point(b"S1", &proof.S1)?;
410
411        // Process the remaining constraints.
412        self = self.create_randomized_constraints()?;
413
414        let transcript = self.transcript.borrow_mut();
415
416        // If the number of multiplications is not 0 or a power of 2, then pad the circuit.
417        let n = self.num_vars;
418        let n2 = n - n1;
419        let padded_n = self.num_vars.next_power_of_two();
420        let pad = padded_n - n;
421
422        use crate::inner_product_proof::inner_product;
423        use crate::util;
424
425        if bp_gens.gens_capacity < padded_n {
426            return Err(R1CSError::InvalidGeneratorsLength);
427        }
428
429        // These points are the identity in the 1-phase unrandomized case.
430        transcript.append_point(b"A_I2", &proof.A_I2);
431        transcript.append_point(b"A_O2", &proof.A_O2);
432        transcript.append_point(b"S2", &proof.S2);
433
434        let y: G::ScalarField =
435            <Transcript as TranscriptProtocol<G>>::challenge_scalar(transcript, b"y");
436        let z = <Transcript as TranscriptProtocol<G>>::challenge_scalar(transcript, b"z");
437
438        transcript.validate_and_append_point(b"T_1", &proof.T_1)?;
439        transcript.validate_and_append_point(b"T_3", &proof.T_3)?;
440        transcript.validate_and_append_point(b"T_4", &proof.T_4)?;
441        transcript.validate_and_append_point(b"T_5", &proof.T_5)?;
442        transcript.validate_and_append_point(b"T_6", &proof.T_6)?;
443
444        let u = <Transcript as TranscriptProtocol<G>>::challenge_scalar(transcript, b"u");
445        let x = <Transcript as TranscriptProtocol<G>>::challenge_scalar(transcript, b"x");
446
447        <Transcript as TranscriptProtocol<G>>::append_scalar(transcript, b"t_x", &proof.t_x);
448        <Transcript as TranscriptProtocol<G>>::append_scalar(
449            transcript,
450            b"t_x_blinding",
451            &proof.t_x_blinding,
452        );
453        <Transcript as TranscriptProtocol<G>>::append_scalar(
454            transcript,
455            b"e_blinding",
456            &proof.e_blinding,
457        );
458
459        let w: G::ScalarField =
460            <Transcript as TranscriptProtocol<G>>::challenge_scalar(transcript, b"w");
461
462        let (wL, wR, wO, wV, wc) = self.flattened_constraints(&z);
463
464        // Get IPP variables
465        let (u_sq, u_inv_sq, s) = proof
466            .ipp_proof
467            .verification_scalars(padded_n, self.transcript.borrow_mut())
468            .map_err(|_| R1CSError::VerificationError)?;
469
470        let a = proof.ipp_proof.a;
471        let b = proof.ipp_proof.b;
472
473        let y_inv = y.inverse().unwrap();
474        let y_inv_vec = util::exp_iter::<G>(y_inv)
475            .take(padded_n)
476            .collect::<Vec<G::ScalarField>>();
477        let yneg_wR = wR
478            .into_iter()
479            .zip(y_inv_vec.iter())
480            .map(|(wRi, exp_y_inv)| wRi * exp_y_inv)
481            .chain(iter::repeat(G::ScalarField::zero()).take(pad))
482            .collect::<Vec<G::ScalarField>>();
483
484        let delta = inner_product(&yneg_wR[0..n], &wL);
485
486        let u_for_g = iter::repeat(G::ScalarField::one())
487            .take(n1)
488            .chain(iter::repeat(u).take(n2 + pad));
489        let u_for_h = u_for_g.clone();
490
491        // define parameters for P check
492        let g_scalars: Vec<_> = yneg_wR
493            .iter()
494            .zip(u_for_g)
495            .zip(s.iter().take(padded_n))
496            .map(|((yneg_wRi, u_or_1), s_i)| u_or_1 * (x * yneg_wRi - a * s_i))
497            .collect();
498
499        let h_scalars: Vec<_> = y_inv_vec
500            .iter()
501            .zip(u_for_h)
502            .zip(s.iter().rev().take(padded_n))
503            .zip(
504                wL.into_iter()
505                    .chain(iter::repeat(G::ScalarField::zero()).take(pad)),
506            )
507            .zip(
508                wO.into_iter()
509                    .chain(iter::repeat(G::ScalarField::zero()).take(pad)),
510            )
511            .map(|((((y_inv_i, u_or_1), s_i_inv), wLi), wOi)| {
512                u_or_1 * (*y_inv_i * (x * wLi + wOi - b * s_i_inv) - G::ScalarField::one())
513            })
514            .collect();
515
516        let r: G::ScalarField = <Transcript as TranscriptProtocol<G>>::challenge_scalar(
517            &mut self.transcript.borrow_mut().clone(),
518            b"r",
519        );
520
521        let xx = x * x;
522        let rxx = r * xx;
523        let xxx = x * xx;
524
525        // group the T_scalars and T_points together
526        let T_scalars = [r * x, rxx * x, rxx * xx, rxx * xxx, rxx * xx * xx];
527
528        let mut scalars: Vec<G::ScalarField> = vec![];
529        scalars.push(w * (proof.t_x - a * b) + r * (xx * (wc + delta) - proof.t_x));
530        scalars.push(-proof.e_blinding - r * proof.t_x_blinding);
531        scalars.extend_from_slice(&g_scalars);
532        scalars.extend_from_slice(&h_scalars);
533        scalars.extend_from_slice(&[x, xx, xxx, u * x, u * xx, u * xxx]);
534        for wVi in wV.iter() {
535            scalars.push(*wVi * rxx);
536        }
537        scalars.extend_from_slice(&T_scalars);
538        scalars.extend_from_slice(&u_sq);
539        scalars.extend_from_slice(&u_inv_sq);
540        Ok((self, scalars))
541    }
542
543    /// Consume this `VerifierCS` and attempt to verify the supplied `proof`.
544    /// The `pc_gens` and `bp_gens` are generators for Pedersen commitments and
545    /// Bulletproofs vector commitments, respectively.  The
546    /// [`BulletproofGens`] should have `gens_capacity` greater than
547    /// the number of multiplication constraints that will eventually
548    /// be added into the constraint system.
549    pub fn verify(
550        self,
551        proof: &R1CSProof<G>,
552        pc_gens: &PedersenGens<G>,
553        bp_gens: &BulletproofGens<G>,
554    ) -> Result<(), R1CSError> {
555        self.verify_and_return_transcript(proof, pc_gens, bp_gens)
556            .map(|_| ())
557    }
558    /// Same as `verify`, but also returns the transcript back to the user.
559    pub fn verify_and_return_transcript(
560        mut self,
561        proof: &R1CSProof<G>,
562        pc_gens: &PedersenGens<G>,
563        bp_gens: &BulletproofGens<G>,
564    ) -> Result<T, R1CSError> {
565        let (verifier, scalars) = self.verification_scalars(proof, bp_gens)?;
566        self = verifier;
567        let T_points = [proof.T_1, proof.T_3, proof.T_4, proof.T_5, proof.T_6];
568
569        // We are performing a single-party circuit proof, so party index is 0.
570        let gens = bp_gens.share(0);
571
572        let padded_n = self.num_vars.next_power_of_two();
573
574        let mega_check = G::Group::msm(
575            &iter::once(&pc_gens.B)
576                .chain(iter::once(&pc_gens.B_blinding))
577                .chain(gens.G(padded_n))
578                .chain(gens.H(padded_n))
579                .chain(iter::once(&proof.A_I1))
580                .chain(iter::once(&proof.A_O1))
581                .chain(iter::once(&proof.S1))
582                .chain(iter::once(&proof.A_I2))
583                .chain(iter::once(&proof.A_O2))
584                .chain(iter::once(&proof.S2))
585                .chain(self.V.iter())
586                .chain(T_points.iter())
587                .chain(proof.ipp_proof.L_vec.iter())
588                .chain(proof.ipp_proof.R_vec.iter())
589                .map(|f| f.clone())
590                .collect::<Vec<G>>(),
591            &scalars,
592        )
593        .unwrap();
594
595        if !mega_check.is_zero() {
596            return Err(R1CSError::VerificationError);
597        }
598
599        Ok(self.transcript)
600    }
601}
602
603/// Batch verification of R1CS proofs
604pub fn batch_verify<'a, G: AffineRepr, I, R: CryptoRng + RngCore>(
605    prng: &mut R,
606    instances: I,
607    pc_gens: &PedersenGens<G>,
608    bp_gens: &BulletproofGens<G>,
609) -> Result<(), R1CSError>
610where
611    I: IntoIterator<Item = (Verifier<G, &'a mut Transcript>, &'a R1CSProof<G>)>,
612{
613    let mut max_n_padded = 0;
614    let mut verifiers: Vec<Verifier<G, _>> = vec![];
615    let mut proofs: Vec<&R1CSProof<G>> = vec![];
616    let mut verification_scalars = vec![];
617    for (verifier, proof) in instances.into_iter() {
618        // verification_scalars method is mutable, need to run before obtaining verifier.num_vars
619        let (verifier, scalars) = verifier.verification_scalars(proof, bp_gens)?;
620        let n = verifier.num_vars.next_power_of_two();
621        if n > max_n_padded {
622            max_n_padded = n;
623        }
624        verification_scalars.push(scalars);
625        verifiers.push(verifier);
626        proofs.push(proof);
627    }
628    let mut all_scalars = vec![];
629    let mut all_elems = vec![];
630
631    for _ in 0..(2 * max_n_padded + 2) {
632        all_scalars.push(G::ScalarField::zero());
633    }
634    all_elems.push(pc_gens.B);
635    all_elems.push(pc_gens.B_blinding);
636    let gens = bp_gens.share(0);
637    for G in gens.G(max_n_padded) {
638        all_elems.push(*G);
639    }
640    for H in gens.H(max_n_padded) {
641        all_elems.push(*H);
642    }
643
644    for ((verifier, proof), scalars) in verifiers
645        .into_iter()
646        .zip(proofs.iter())
647        .zip(verification_scalars.iter())
648    {
649        let alpha = G::ScalarField::rand(prng);
650        let scaled_scalars: Vec<G::ScalarField> = scalars.into_iter().map(|s| alpha * s).collect();
651        let padded_n = verifier.num_vars.next_power_of_two();
652        all_scalars[0] += scaled_scalars[0]; // B
653        all_scalars[1] += scaled_scalars[1]; // B_blinding
654                                             // g values
655        for (i, s) in (&scaled_scalars[2..2 + padded_n]).iter().enumerate() {
656            all_scalars[i + 2] += *s;
657        }
658        // h values
659        for (i, s) in (&scaled_scalars[2 + padded_n..2 + 2 * padded_n])
660            .iter()
661            .enumerate()
662        {
663            all_scalars[2 + max_n_padded + i] += *s;
664        }
665
666        for s in (&scaled_scalars[2 + 2 * padded_n..]).iter() {
667            all_scalars.push(*s);
668        }
669        all_elems.push(proof.A_I1);
670        all_elems.push(proof.A_O1);
671        all_elems.push(proof.S1);
672        all_elems.push(proof.A_I2);
673        all_elems.push(proof.A_O2);
674        all_elems.push(proof.S2);
675        all_elems.extend_from_slice(verifier.V.as_slice());
676        all_elems.push(proof.T_1);
677        all_elems.push(proof.T_3);
678        all_elems.push(proof.T_4);
679        all_elems.push(proof.T_5);
680        all_elems.push(proof.T_6);
681        all_elems.extend_from_slice(&proof.ipp_proof.L_vec);
682        all_elems.extend_from_slice(&proof.ipp_proof.R_vec);
683    }
684
685    let multi_exp = G::Group::msm(&all_elems, &all_scalars).unwrap();
686    if !multi_exp.is_zero() {
687        Err(R1CSError::VerificationError)
688    } else {
689        Ok(())
690    }
691}