monero_bulletproofs/
batch_verifier.rs

1use std_shims::vec::Vec;
2
3use curve25519_dalek::{
4  constants::ED25519_BASEPOINT_POINT,
5  traits::{IsIdentity, VartimeMultiscalarMul},
6  scalar::Scalar,
7  edwards::EdwardsPoint,
8};
9
10use monero_generators::{H as MONERO_H, Generators};
11
12use crate::{original, plus};
13
14#[derive(Default)]
15pub(crate) struct InternalBatchVerifier {
16  pub(crate) g: Scalar,
17  pub(crate) h: Scalar,
18  pub(crate) g_bold: Vec<Scalar>,
19  pub(crate) h_bold: Vec<Scalar>,
20  pub(crate) other: Vec<(Scalar, EdwardsPoint)>,
21}
22
23impl InternalBatchVerifier {
24  #[must_use]
25  fn verify(self, G: EdwardsPoint, H: EdwardsPoint, generators: &Generators) -> bool {
26    /*
27      Technically, this following line can overflow, and joining these `Vec`s _may_ panic if
28      they're individually acceptable lengths yet their sum isn't. This is so negligible, due to
29      the amount of memory required, it's dismissed.
30    */
31    let capacity = 2 + self.g_bold.len() + self.h_bold.len() + self.other.len();
32    let mut scalars = Vec::with_capacity(capacity);
33    let mut points = Vec::with_capacity(capacity);
34
35    scalars.push(self.g);
36    points.push(G);
37
38    scalars.push(self.h);
39    points.push(H);
40
41    for (i, g_bold) in self.g_bold.into_iter().enumerate() {
42      scalars.push(g_bold);
43      points.push(generators.G[i]);
44    }
45
46    for (i, h_bold) in self.h_bold.into_iter().enumerate() {
47      scalars.push(h_bold);
48      points.push(generators.H[i]);
49    }
50
51    for (scalar, point) in self.other {
52      scalars.push(scalar);
53      points.push(point);
54    }
55
56    EdwardsPoint::vartime_multiscalar_mul(scalars, points).is_identity()
57  }
58}
59
60#[derive(Default)]
61pub(crate) struct BulletproofsBatchVerifier(pub(crate) InternalBatchVerifier);
62impl BulletproofsBatchVerifier {
63  #[must_use]
64  pub(crate) fn verify(self) -> bool {
65    self.0.verify(ED25519_BASEPOINT_POINT, *MONERO_H, &original::GENERATORS)
66  }
67}
68
69#[derive(Default)]
70pub(crate) struct BulletproofsPlusBatchVerifier(pub(crate) InternalBatchVerifier);
71impl BulletproofsPlusBatchVerifier {
72  #[must_use]
73  pub(crate) fn verify(self) -> bool {
74    // Bulletproofs+ is written as per the paper, with G for the value and H for the mask
75    // Monero uses H for the value and G for the mask
76    self.0.verify(*MONERO_H, ED25519_BASEPOINT_POINT, &plus::GENERATORS)
77  }
78}
79
80/// A batch verifier for Bulletproofs(+).
81///
82/// This uses a fixed layout such that all fixed points only incur a single point scaling,
83/// regardless of the amounts of proofs verified. For all variable points (commitments), they're
84/// accumulated with the fixed points into a single multiscalar multiplication.
85#[derive(Default)]
86pub struct BatchVerifier {
87  pub(crate) original: BulletproofsBatchVerifier,
88  pub(crate) plus: BulletproofsPlusBatchVerifier,
89}
90impl BatchVerifier {
91  /// Create a new batch verifier.
92  pub fn new() -> Self {
93    Self {
94      original: BulletproofsBatchVerifier(InternalBatchVerifier::default()),
95      plus: BulletproofsPlusBatchVerifier(InternalBatchVerifier::default()),
96    }
97  }
98
99  /// Verify all of the proofs queued within this batch verifier.
100  ///
101  /// This uses a variable-time multiscalar multiplication internally.
102  #[must_use]
103  pub fn verify(self) -> bool {
104    self.original.verify() && self.plus.verify()
105  }
106}