noah_bulletproofs/r1cs/
prover.rs

1#![allow(non_snake_case)]
2
3use alloc::{boxed::Box, vec, vec::Vec};
4use clear_on_drop::clear::Clear;
5use core::borrow::BorrowMut;
6use core::mem;
7use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
8use curve25519_dalek::scalar::Scalar;
9use curve25519_dalek::traits::{Identity, MultiscalarMul};
10use merlin::Transcript;
11use rand_core::{CryptoRng, RngCore};
12
13use super::{
14    ConstraintSystem, LinearCombination, R1CSProof, RandomizableConstraintSystem,
15    RandomizedConstraintSystem, Variable,
16};
17
18use crate::errors::R1CSError;
19use crate::generators::{BulletproofGens, PedersenGens};
20use crate::inner_product_proof::InnerProductProof;
21use crate::transcript::TranscriptProtocol;
22
23/// A [`ConstraintSystem`] implementation for use by the prover.
24///
25/// The prover commits high-level variables and their blinding factors `(v, v_blinding)`,
26/// allocates low-level variables and creates constraints in terms of these
27/// high-level variables and low-level variables.
28///
29/// When all constraints are added, the proving code calls `prove`
30/// which consumes the `Prover` instance, samples random challenges
31/// that instantiate the randomized constraints, and creates a complete proof.
32pub struct Prover<'g, T: BorrowMut<Transcript>> {
33    transcript: T,
34    pc_gens: &'g PedersenGens,
35    /// The constraints accumulated so far.
36    constraints: Vec<LinearCombination>,
37    /// Secret data
38    secrets: Secrets,
39
40    /// This list holds closures that will be called in the second phase of the protocol,
41    /// when non-randomized variables are committed.
42    deferred_constraints: Vec<Box<dyn Fn(&mut RandomizingProver<'g, T>) -> Result<(), R1CSError>>>,
43
44    /// Index of a pending multiplier that's not fully assigned yet.
45    pending_multiplier: Option<usize>,
46}
47
48/// Separate struct to implement Drop trait for (for zeroing),
49/// so that compiler does not prohibit us from moving the Transcript out of `prove()`.
50struct Secrets {
51    /// Stores assignments to the "left" of multiplication gates
52    a_L: Vec<Scalar>,
53    /// Stores assignments to the "right" of multiplication gates
54    a_R: Vec<Scalar>,
55    /// Stores assignments to the "output" of multiplication gates
56    a_O: Vec<Scalar>,
57    /// High-level witness data (value openings to V commitments)
58    v: Vec<Scalar>,
59    /// High-level witness data (blinding openings to V commitments)
60    v_blinding: Vec<Scalar>,
61}
62
63/// Prover in the randomizing phase.
64///
65/// Note: this type is exported because it is used to specify the associated type
66/// in the public impl of a trait `ConstraintSystem`, which boils down to allowing compiler to
67/// monomorphize the closures for the proving and verifying code.
68/// However, this type cannot be instantiated by the user and therefore can only be used within
69/// the callback provided to `specify_randomized_constraints`.
70pub struct RandomizingProver<'g, T: BorrowMut<Transcript>> {
71    prover: Prover<'g, T>,
72}
73
74/// Overwrite secrets with null bytes when they go out of scope.
75impl Drop for Secrets {
76    fn drop(&mut self) {
77        self.v.clear();
78        self.v_blinding.clear();
79
80        // Important: due to how ClearOnDrop auto-implements InitializableFromZeroed
81        // for T: Default, calling .clear() on Vec compiles, but does not
82        // clear the content. Instead, it only clears the Vec's header.
83        // Clearing the underlying buffer item-by-item will do the job, but will
84        // keep the header as-is, which is fine since the header does not contain secrets.
85        for e in self.a_L.iter_mut() {
86            e.clear();
87        }
88        for e in self.a_R.iter_mut() {
89            e.clear();
90        }
91        for e in self.a_O.iter_mut() {
92            e.clear();
93        }
94        // XXX use ClearOnDrop instead of doing the above
95    }
96}
97
98impl<'g, T: BorrowMut<Transcript>> ConstraintSystem for Prover<'g, T> {
99    fn transcript(&mut self) -> &mut Transcript {
100        self.transcript.borrow_mut()
101    }
102
103    fn multiply(
104        &mut self,
105        mut left: LinearCombination,
106        mut right: LinearCombination,
107    ) -> (Variable, Variable, Variable) {
108        // Synthesize the assignments for l,r,o
109        let l = self.eval(&left);
110        let r = self.eval(&right);
111        let o = l * r;
112
113        // Create variables for l,r,o ...
114        let l_var = Variable::MultiplierLeft(self.secrets.a_L.len());
115        let r_var = Variable::MultiplierRight(self.secrets.a_R.len());
116        let o_var = Variable::MultiplierOutput(self.secrets.a_O.len());
117        // ... and assign them
118        self.secrets.a_L.push(l);
119        self.secrets.a_R.push(r);
120        self.secrets.a_O.push(o);
121
122        // Constrain l,r,o:
123        left.terms.push((l_var, -Scalar::one()));
124        right.terms.push((r_var, -Scalar::one()));
125        self.constrain(left);
126        self.constrain(right);
127
128        (l_var, r_var, o_var)
129    }
130
131    fn allocate(&mut self, assignment: Option<Scalar>) -> Result<Variable, R1CSError> {
132        let scalar = assignment.ok_or(R1CSError::MissingAssignment)?;
133
134        match self.pending_multiplier {
135            None => {
136                let i = self.secrets.a_L.len();
137                self.pending_multiplier = Some(i);
138                self.secrets.a_L.push(scalar);
139                self.secrets.a_R.push(Scalar::zero());
140                self.secrets.a_O.push(Scalar::zero());
141                Ok(Variable::MultiplierLeft(i))
142            }
143            Some(i) => {
144                self.pending_multiplier = None;
145                self.secrets.a_R[i] = scalar;
146                self.secrets.a_O[i] = self.secrets.a_L[i] * self.secrets.a_R[i];
147                Ok(Variable::MultiplierRight(i))
148            }
149        }
150    }
151
152    fn allocate_multiplier(
153        &mut self,
154        input_assignments: Option<(Scalar, Scalar)>,
155    ) -> Result<(Variable, Variable, Variable), R1CSError> {
156        let (l, r) = input_assignments.ok_or(R1CSError::MissingAssignment)?;
157        let o = l * r;
158
159        // Create variables for l,r,o ...
160        let l_var = Variable::MultiplierLeft(self.secrets.a_L.len());
161        let r_var = Variable::MultiplierRight(self.secrets.a_R.len());
162        let o_var = Variable::MultiplierOutput(self.secrets.a_O.len());
163        // ... and assign them
164        self.secrets.a_L.push(l);
165        self.secrets.a_R.push(r);
166        self.secrets.a_O.push(o);
167
168        Ok((l_var, r_var, o_var))
169    }
170
171    fn multipliers_len(&self) -> usize {
172        self.secrets.a_L.len()
173    }
174
175    fn constrain(&mut self, lc: LinearCombination) {
176        // TODO: check that the linear combinations are valid
177        // (e.g. that variables are valid, that the linear combination evals to 0 for prover, etc).
178        self.constraints.push(lc);
179    }
180}
181
182impl<'g, T: BorrowMut<Transcript>> RandomizableConstraintSystem for Prover<'g, T> {
183    type RandomizedCS = RandomizingProver<'g, T>;
184
185    fn specify_randomized_constraints<F>(&mut self, callback: F) -> Result<(), R1CSError>
186    where
187        F: 'static + Fn(&mut Self::RandomizedCS) -> Result<(), R1CSError>,
188    {
189        self.deferred_constraints.push(Box::new(callback));
190        Ok(())
191    }
192}
193
194impl<'g, T: BorrowMut<Transcript>> ConstraintSystem for RandomizingProver<'g, T> {
195    fn transcript(&mut self) -> &mut Transcript {
196        self.prover.transcript.borrow_mut()
197    }
198
199    fn multiply(
200        &mut self,
201        left: LinearCombination,
202        right: LinearCombination,
203    ) -> (Variable, Variable, Variable) {
204        self.prover.multiply(left, right)
205    }
206
207    fn allocate(&mut self, assignment: Option<Scalar>) -> Result<Variable, R1CSError> {
208        self.prover.allocate(assignment)
209    }
210
211    fn allocate_multiplier(
212        &mut self,
213        input_assignments: Option<(Scalar, Scalar)>,
214    ) -> Result<(Variable, Variable, Variable), R1CSError> {
215        self.prover.allocate_multiplier(input_assignments)
216    }
217
218    fn multipliers_len(&self) -> usize {
219        self.prover.multipliers_len()
220    }
221
222    fn constrain(&mut self, lc: LinearCombination) {
223        self.prover.constrain(lc)
224    }
225}
226
227impl<'g, T: BorrowMut<Transcript>> RandomizedConstraintSystem for RandomizingProver<'g, T> {
228    fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar {
229        self.prover.transcript.borrow_mut().challenge_scalar(label)
230    }
231}
232
233impl<'g, T: BorrowMut<Transcript>> Prover<'g, T> {
234    /// Construct an empty constraint system with specified external
235    /// input variables.
236    ///
237    /// # Inputs
238    ///
239    /// The `bp_gens` and `pc_gens` are generators for Bulletproofs
240    /// and for the Pedersen commitments, respectively.  The
241    /// [`BulletproofGens`] should have `gens_capacity` greater than
242    /// the number of multiplication constraints that will eventually
243    /// be added into the constraint system.
244    ///
245    /// The `transcript` parameter is a Merlin proof transcript.  The
246    /// `ProverCS` holds onto the `&mut Transcript` until it consumes
247    /// itself during [`ProverCS::prove`], releasing its borrow of the
248    /// transcript.  This ensures that the transcript cannot be
249    /// altered except by the `ProverCS` before proving is complete.
250    ///
251    /// # Returns
252    ///
253    /// Returns a new `Prover` instance.
254    pub fn new(pc_gens: &'g PedersenGens, mut transcript: T) -> Self {
255        transcript.borrow_mut().r1cs_domain_sep();
256
257        Prover {
258            pc_gens,
259            transcript,
260            secrets: Secrets {
261                v: Vec::new(),
262                v_blinding: Vec::new(),
263                a_L: Vec::new(),
264                a_R: Vec::new(),
265                a_O: Vec::new(),
266            },
267            constraints: Vec::new(),
268            deferred_constraints: Vec::new(),
269            pending_multiplier: None,
270        }
271    }
272
273    /// Creates commitment to a high-level variable and adds it to the transcript.
274    ///
275    /// # Inputs
276    ///
277    /// The `v` and `v_blinding` parameters are openings to the
278    /// commitment to the external variable for the constraint
279    /// system.  Passing the opening (the value together with the
280    /// blinding factor) makes it possible to reference pre-existing
281    /// commitments in the constraint system.  All external variables
282    /// must be passed up-front, so that challenges produced by
283    /// [`ConstraintSystem::challenge_scalar`] are bound to the
284    /// external variables.
285    ///
286    /// # Returns
287    ///
288    /// Returns a pair of a Pedersen commitment (as a compressed Ristretto point),
289    /// and a [`Variable`] corresponding to it, which can be used to form constraints.
290    pub fn commit(&mut self, v: Scalar, v_blinding: Scalar) -> (CompressedRistretto, Variable) {
291        let i = self.secrets.v.len();
292        self.secrets.v.push(v);
293        self.secrets.v_blinding.push(v_blinding);
294
295        // Add the commitment to the transcript.
296        let V = self.pc_gens.commit(v, v_blinding).compress();
297        self.transcript.borrow_mut().append_point(b"V", &V);
298
299        (V, Variable::Committed(i))
300    }
301
302    /// Use a challenge, `z`, to flatten the constraints in the
303    /// constraint system into vectors used for proving and
304    /// verification.
305    ///
306    /// # Output
307    ///
308    /// Returns a tuple of
309    /// ```text
310    /// (wL, wR, wO, wV)
311    /// ```
312    /// where `w{L,R,O}` is \\( z \cdot z^Q \cdot W_{L,R,O} \\).
313    fn flattened_constraints(
314        &mut self,
315        z: &Scalar,
316    ) -> (Vec<Scalar>, Vec<Scalar>, Vec<Scalar>, Vec<Scalar>) {
317        let n = self.secrets.a_L.len();
318        let m = self.secrets.v.len();
319
320        let mut wL = vec![Scalar::zero(); n];
321        let mut wR = vec![Scalar::zero(); n];
322        let mut wO = vec![Scalar::zero(); n];
323        let mut wV = vec![Scalar::zero(); m];
324
325        let mut exp_z = *z;
326        for lc in self.constraints.iter() {
327            for (var, coeff) in &lc.terms {
328                match var {
329                    Variable::MultiplierLeft(i) => {
330                        wL[*i] += exp_z * coeff;
331                    }
332                    Variable::MultiplierRight(i) => {
333                        wR[*i] += exp_z * coeff;
334                    }
335                    Variable::MultiplierOutput(i) => {
336                        wO[*i] += exp_z * coeff;
337                    }
338                    Variable::Committed(i) => {
339                        wV[*i] -= exp_z * coeff;
340                    }
341                    Variable::One() => {
342                        // The prover doesn't need to handle constant terms
343                    }
344                }
345            }
346            exp_z *= z;
347        }
348
349        (wL, wR, wO, wV)
350    }
351
352    fn eval(&self, lc: &LinearCombination) -> Scalar {
353        lc.terms
354            .iter()
355            .map(|(var, coeff)| {
356                coeff
357                    * match var {
358                        Variable::MultiplierLeft(i) => self.secrets.a_L[*i],
359                        Variable::MultiplierRight(i) => self.secrets.a_R[*i],
360                        Variable::MultiplierOutput(i) => self.secrets.a_O[*i],
361                        Variable::Committed(i) => self.secrets.v[*i],
362                        Variable::One() => Scalar::one(),
363                    }
364            })
365            .sum()
366    }
367
368    /// Calls all remembered callbacks with an API that
369    /// allows generating challenge scalars.
370    fn create_randomized_constraints(mut self) -> Result<Self, R1CSError> {
371        // Clear the pending multiplier (if any) because it was committed into A_L/A_R/S.
372        self.pending_multiplier = None;
373
374        if self.deferred_constraints.len() == 0 {
375            self.transcript.borrow_mut().r1cs_1phase_domain_sep();
376            Ok(self)
377        } else {
378            self.transcript.borrow_mut().r1cs_2phase_domain_sep();
379            // Note: the wrapper could've used &mut instead of ownership,
380            // but specifying lifetimes for boxed closures is not going to be nice,
381            // so we move the self into wrapper and then move it back out afterwards.
382            let mut callbacks = mem::replace(&mut self.deferred_constraints, Vec::new());
383            let mut wrapped_self = RandomizingProver { prover: self };
384            for callback in callbacks.drain(..) {
385                callback(&mut wrapped_self)?;
386            }
387            Ok(wrapped_self.prover)
388        }
389    }
390
391    /// Consume this `ConstraintSystem` to produce a proof.
392    pub fn prove<R: RngCore + CryptoRng>(
393        self,
394        rng: &mut R,
395        bp_gens: &BulletproofGens,
396    ) -> Result<R1CSProof, R1CSError> {
397        self.prove_and_return_transcript(rng, bp_gens)
398            .map(|(proof, _transcript)| proof)
399    }
400
401    /// Consume this `ConstraintSystem` to produce a proof. Returns the proof and the transcript passed in `Prover::new`.
402    pub fn prove_and_return_transcript<R: RngCore + CryptoRng>(
403        mut self,
404        rng: &mut R,
405        bp_gens: &BulletproofGens,
406    ) -> Result<(R1CSProof, T), R1CSError> {
407        use crate::util;
408        use core::iter;
409
410        // Commit a length _suffix_ for the number of high-level variables.
411        // We cannot do this in advance because user can commit variables one-by-one,
412        // but this suffix provides safe disambiguation because each variable
413        // is prefixed with a separate label.
414        self.transcript
415            .borrow_mut()
416            .append_u64(b"m", self.secrets.v.len() as u64);
417
418        // Create a `TranscriptRng` from the high-level witness data
419        //
420        // The prover wants to rekey the RNG with its witness data.
421        //
422        // This consists of the high level witness data (the v's and
423        // v_blinding's), as well as the low-level witness data (a_L,
424        // a_R, a_O).  Since the low-level data should (hopefully) be
425        // determined by the high-level data, it doesn't give any
426        // extra entropy for reseeding the RNG.
427        //
428        // Since the v_blindings should be random scalars (in order to
429        // protect the v's in the commitments), we don't gain much by
430        // committing the v's as well as the v_blinding's.
431        let mut rng = {
432            let mut builder = self.transcript.borrow_mut().build_rng();
433
434            // Commit the blinding factors for the input wires
435            for v_b in &self.secrets.v_blinding {
436                builder = builder.rekey_with_witness_bytes(b"v_blinding", v_b.as_bytes());
437            }
438
439            builder.finalize(rng)
440        };
441
442        // Commit to the first-phase low-level witness variables.
443        let n1 = self.secrets.a_L.len();
444
445        if bp_gens.gens_capacity < n1 {
446            return Err(R1CSError::InvalidGeneratorsLength);
447        }
448
449        // We are performing a single-party circuit proof, so party index is 0.
450        let gens = bp_gens.share(0);
451
452        let i_blinding1 = Scalar::random(&mut rng);
453        let o_blinding1 = Scalar::random(&mut rng);
454        let s_blinding1 = Scalar::random(&mut rng);
455
456        let mut s_L1: Vec<Scalar> = (0..n1).map(|_| Scalar::random(&mut rng)).collect();
457        let mut s_R1: Vec<Scalar> = (0..n1).map(|_| Scalar::random(&mut rng)).collect();
458
459        // A_I = <a_L, G> + <a_R, H> + i_blinding * B_blinding
460        let A_I1 = RistrettoPoint::multiscalar_mul(
461            iter::once(&i_blinding1)
462                .chain(self.secrets.a_L.iter())
463                .chain(self.secrets.a_R.iter()),
464            iter::once(&self.pc_gens.B_blinding)
465                .chain(gens.G(n1))
466                .chain(gens.H(n1)),
467        )
468        .compress();
469
470        // A_O = <a_O, G> + o_blinding * B_blinding
471        let A_O1 = RistrettoPoint::multiscalar_mul(
472            iter::once(&o_blinding1).chain(self.secrets.a_O.iter()),
473            iter::once(&self.pc_gens.B_blinding).chain(gens.G(n1)),
474        )
475        .compress();
476
477        // S = <s_L, G> + <s_R, H> + s_blinding * B_blinding
478        let S1 = RistrettoPoint::multiscalar_mul(
479            iter::once(&s_blinding1)
480                .chain(s_L1.iter())
481                .chain(s_R1.iter()),
482            iter::once(&self.pc_gens.B_blinding)
483                .chain(gens.G(n1))
484                .chain(gens.H(n1)),
485        )
486        .compress();
487
488        let transcript = self.transcript.borrow_mut();
489        transcript.append_point(b"A_I1", &A_I1);
490        transcript.append_point(b"A_O1", &A_O1);
491        transcript.append_point(b"S1", &S1);
492
493        // Process the remaining constraints.
494        self = self.create_randomized_constraints()?;
495
496        // Pad zeros to the next power of two (or do that implicitly when creating vectors)
497
498        // If the number of multiplications is not 0 or a power of 2, then pad the circuit.
499        let n = self.secrets.a_L.len();
500        let n2 = n - n1;
501        let padded_n = self.secrets.a_L.len().next_power_of_two();
502        let pad = padded_n - n;
503
504        if bp_gens.gens_capacity < padded_n {
505            return Err(R1CSError::InvalidGeneratorsLength);
506        }
507
508        // Commit to the second-phase low-level witness variables
509
510        let has_2nd_phase_commitments = n2 > 0;
511
512        let (i_blinding2, o_blinding2, s_blinding2) = if has_2nd_phase_commitments {
513            (
514                Scalar::random(&mut rng),
515                Scalar::random(&mut rng),
516                Scalar::random(&mut rng),
517            )
518        } else {
519            (Scalar::zero(), Scalar::zero(), Scalar::zero())
520        };
521
522        let mut s_L2: Vec<Scalar> = (0..n2).map(|_| Scalar::random(&mut rng)).collect();
523        let mut s_R2: Vec<Scalar> = (0..n2).map(|_| Scalar::random(&mut rng)).collect();
524
525        let (A_I2, A_O2, S2) = if has_2nd_phase_commitments {
526            (
527                // A_I = <a_L, G> + <a_R, H> + i_blinding * B_blinding
528                RistrettoPoint::multiscalar_mul(
529                    iter::once(&i_blinding2)
530                        .chain(self.secrets.a_L.iter().skip(n1))
531                        .chain(self.secrets.a_R.iter().skip(n1)),
532                    iter::once(&self.pc_gens.B_blinding)
533                        .chain(gens.G(n).skip(n1))
534                        .chain(gens.H(n).skip(n1)),
535                )
536                .compress(),
537                // A_O = <a_O, G> + o_blinding * B_blinding
538                RistrettoPoint::multiscalar_mul(
539                    iter::once(&o_blinding2).chain(self.secrets.a_O.iter().skip(n1)),
540                    iter::once(&self.pc_gens.B_blinding).chain(gens.G(n).skip(n1)),
541                )
542                .compress(),
543                // S = <s_L, G> + <s_R, H> + s_blinding * B_blinding
544                RistrettoPoint::multiscalar_mul(
545                    iter::once(&s_blinding2)
546                        .chain(s_L2.iter())
547                        .chain(s_R2.iter()),
548                    iter::once(&self.pc_gens.B_blinding)
549                        .chain(gens.G(n).skip(n1))
550                        .chain(gens.H(n).skip(n1)),
551                )
552                .compress(),
553            )
554        } else {
555            // Since we are using zero blinding factors and
556            // there are no variables to commit,
557            // the commitments _must_ be identity points,
558            // so we can hardcode them saving 3 mults+compressions.
559            (
560                CompressedRistretto::identity(),
561                CompressedRistretto::identity(),
562                CompressedRistretto::identity(),
563            )
564        };
565
566        let transcript = self.transcript.borrow_mut();
567        transcript.append_point(b"A_I2", &A_I2);
568        transcript.append_point(b"A_O2", &A_O2);
569        transcript.append_point(b"S2", &S2);
570
571        // 4. Compute blinded vector polynomials l(x) and r(x)
572
573        let y = transcript.challenge_scalar(b"y");
574        let z = transcript.challenge_scalar(b"z");
575
576        let (wL, wR, wO, wV) = self.flattened_constraints(&z);
577
578        let mut l_poly = util::VecPoly3::zero(n);
579        let mut r_poly = util::VecPoly3::zero(n);
580
581        let mut exp_y = Scalar::one(); // y^n starting at n=0
582        let y_inv = y.invert();
583        let exp_y_inv = util::exp_iter(y_inv).take(padded_n).collect::<Vec<_>>();
584
585        let sLsR = s_L1
586            .iter()
587            .chain(s_L2.iter())
588            .zip(s_R1.iter().chain(s_R2.iter()));
589        for (i, (sl, sr)) in sLsR.enumerate() {
590            // l_poly.0 = 0
591            // l_poly.1 = a_L + y^-n * (z * z^Q * W_R)
592            l_poly.1[i] = self.secrets.a_L[i] + exp_y_inv[i] * wR[i];
593            // l_poly.2 = a_O
594            l_poly.2[i] = self.secrets.a_O[i];
595            // l_poly.3 = s_L
596            l_poly.3[i] = *sl;
597            // r_poly.0 = (z * z^Q * W_O) - y^n
598            r_poly.0[i] = wO[i] - exp_y;
599            // r_poly.1 = y^n * a_R + (z * z^Q * W_L)
600            r_poly.1[i] = exp_y * self.secrets.a_R[i] + wL[i];
601            // r_poly.2 = 0
602            // r_poly.3 = y^n * s_R
603            r_poly.3[i] = exp_y * sr;
604
605            exp_y = exp_y * y; // y^i -> y^(i+1)
606        }
607
608        let t_poly = util::VecPoly3::special_inner_product(&l_poly, &r_poly);
609
610        let t_1_blinding = Scalar::random(&mut rng);
611        let t_3_blinding = Scalar::random(&mut rng);
612        let t_4_blinding = Scalar::random(&mut rng);
613        let t_5_blinding = Scalar::random(&mut rng);
614        let t_6_blinding = Scalar::random(&mut rng);
615
616        let T_1 = self.pc_gens.commit(t_poly.t1, t_1_blinding).compress();
617        let T_3 = self.pc_gens.commit(t_poly.t3, t_3_blinding).compress();
618        let T_4 = self.pc_gens.commit(t_poly.t4, t_4_blinding).compress();
619        let T_5 = self.pc_gens.commit(t_poly.t5, t_5_blinding).compress();
620        let T_6 = self.pc_gens.commit(t_poly.t6, t_6_blinding).compress();
621
622        let transcript = self.transcript.borrow_mut();
623        transcript.append_point(b"T_1", &T_1);
624        transcript.append_point(b"T_3", &T_3);
625        transcript.append_point(b"T_4", &T_4);
626        transcript.append_point(b"T_5", &T_5);
627        transcript.append_point(b"T_6", &T_6);
628
629        let u = transcript.challenge_scalar(b"u");
630        let x = transcript.challenge_scalar(b"x");
631
632        // t_2_blinding = <z*z^Q, W_V * v_blinding>
633        // in the t_x_blinding calculations, line 76.
634        let t_2_blinding = wV
635            .iter()
636            .zip(self.secrets.v_blinding.iter())
637            .map(|(c, v_blinding)| c * v_blinding)
638            .sum();
639
640        let t_blinding_poly = util::Poly6 {
641            t1: t_1_blinding,
642            t2: t_2_blinding,
643            t3: t_3_blinding,
644            t4: t_4_blinding,
645            t5: t_5_blinding,
646            t6: t_6_blinding,
647        };
648
649        let t_x = t_poly.eval(x);
650        let t_x_blinding = t_blinding_poly.eval(x);
651        let mut l_vec = l_poly.eval(x);
652        l_vec.append(&mut vec![Scalar::zero(); pad]);
653
654        let mut r_vec = r_poly.eval(x);
655        r_vec.append(&mut vec![Scalar::zero(); pad]);
656
657        // XXX this should refer to the notes to explain why this is correct
658        for i in n..padded_n {
659            r_vec[i] = -exp_y;
660            exp_y = exp_y * y; // y^i -> y^(i+1)
661        }
662
663        let i_blinding = i_blinding1 + u * i_blinding2;
664        let o_blinding = o_blinding1 + u * o_blinding2;
665        let s_blinding = s_blinding1 + u * s_blinding2;
666
667        let e_blinding = x * (i_blinding + x * (o_blinding + x * s_blinding));
668
669        transcript.append_scalar(b"t_x", &t_x);
670        transcript.append_scalar(b"t_x_blinding", &t_x_blinding);
671        transcript.append_scalar(b"e_blinding", &e_blinding);
672
673        // Get a challenge value to combine statements for the IPP
674        let w = transcript.challenge_scalar(b"w");
675        let Q = w * self.pc_gens.B;
676
677        let G_factors = iter::repeat(Scalar::one())
678            .take(n1)
679            .chain(iter::repeat(u).take(n2 + pad))
680            .collect::<Vec<_>>();
681        let H_factors = exp_y_inv
682            .into_iter()
683            .zip(G_factors.iter())
684            .map(|(y, u_or_1)| y * u_or_1)
685            .collect::<Vec<_>>();
686
687        let ipp_proof = InnerProductProof::create(
688            transcript,
689            &Q,
690            &G_factors,
691            &H_factors,
692            gens.G(padded_n).cloned().collect(),
693            gens.H(padded_n).cloned().collect(),
694            l_vec,
695            r_vec,
696        );
697
698        // We do not yet have a ClearOnDrop wrapper for Vec<Scalar>.
699        // When PR 202 [1] is merged, we can simply wrap s_L and s_R at the point of creation.
700        // [1] https://github.com/dalek-cryptography/curve25519-dalek/pull/202
701        for scalar in s_L1
702            .iter_mut()
703            .chain(s_L2.iter_mut())
704            .chain(s_R1.iter_mut())
705            .chain(s_R2.iter_mut())
706        {
707            scalar.clear();
708        }
709        let proof = R1CSProof {
710            A_I1,
711            A_O1,
712            S1,
713            A_I2,
714            A_O2,
715            S2,
716            T_1,
717            T_3,
718            T_4,
719            T_5,
720            T_6,
721            t_x,
722            t_x_blinding,
723            e_blinding,
724            ipp_proof,
725        };
726        Ok((proof, self.transcript))
727    }
728}