Skip to content

Commit 54a0611

Browse files
hdevalencecathieyun
andcommitted
Add a notion of capacity to Generators
This lets the `Generators` struct hold more generators than are used in each proof. Each party uses the first `n` generators of an arbitrarily-long stream of generators. Co-authored-by: Cathie Yun <[email protected]>
1 parent ba4c168 commit 54a0611

File tree

6 files changed

+191
-107
lines changed

6 files changed

+191
-107
lines changed

src/generators.rs

Lines changed: 167 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,36 @@ use curve25519_dalek::traits::MultiscalarMul;
1111
use digest::{ExtendableOutput, Input, XofReader};
1212
use sha3::{Sha3XofReader, Shake256};
1313

14+
/// Represents a pair of base points for Pedersen commitments.
15+
#[derive(Copy, Clone)]
16+
pub struct PedersenGenerators {
17+
/// Base for the committed value
18+
pub B: RistrettoPoint,
19+
20+
/// Base for the blinding factor
21+
pub B_blinding: RistrettoPoint,
22+
}
23+
24+
impl PedersenGenerators {
25+
/// Creates a Pedersen commitment using the value scalar and a blinding factor.
26+
pub fn commit(&self, value: Scalar, blinding: Scalar) -> RistrettoPoint {
27+
RistrettoPoint::multiscalar_mul(&[value, blinding], &[self.B, self.B_blinding])
28+
}
29+
}
30+
31+
impl Default for PedersenGenerators {
32+
fn default() -> Self {
33+
PedersenGenerators {
34+
B: GeneratorsChain::new(b"Bulletproofs.Generators.B")
35+
.next()
36+
.unwrap(),
37+
B_blinding: GeneratorsChain::new(b"Bulletproofs.Generators.B_blinding")
38+
.next()
39+
.unwrap(),
40+
}
41+
}
42+
}
43+
1444
/// The `GeneratorsChain` creates an arbitrary-long sequence of orthogonal generators.
1545
/// The sequence can be deterministically produced starting with an arbitrary point.
1646
struct GeneratorsChain {
@@ -55,107 +85,147 @@ impl Iterator for GeneratorsChain {
5585
/// aggregating `m` range proofs of `n` bits each.
5686
#[derive(Clone)]
5787
pub struct Generators {
58-
/// Number of bits in a rangeproof
59-
pub n: usize,
60-
/// Number of values or parties
61-
pub m: usize,
6288
/// Bases for Pedersen commitments
6389
pub pedersen_gens: PedersenGenerators,
90+
/// The maximum number of usable generators for each party.
91+
capacity: usize,
92+
/// Number of values or parties
93+
parties: usize,
6494
/// Per-bit generators for the bit values
65-
pub G: Vec<RistrettoPoint>,
66-
/// Per-bit generators for the bit blinding factors
67-
pub H: Vec<RistrettoPoint>,
68-
}
69-
70-
/// The `GeneratorsView` is produced by `Generators::share()`.
71-
///
72-
/// The `Generators` struct represents generators for an aggregated
73-
/// range proof `m` proofs of `n` bits each; the `GeneratorsView`
74-
/// represents the generators for one of the `m` parties' shares.
75-
#[derive(Copy, Clone)]
76-
pub struct GeneratorsView<'a> {
77-
/// Bases for Pedersen commitments
78-
pub pedersen_gens: &'a PedersenGenerators,
79-
/// Per-bit generators for the bit values
80-
pub G: &'a [RistrettoPoint],
95+
G_vec: Vec<Vec<RistrettoPoint>>,
8196
/// Per-bit generators for the bit blinding factors
82-
pub H: &'a [RistrettoPoint],
83-
}
84-
85-
/// Represents a pair of base points for Pedersen commitments.
86-
#[derive(Copy, Clone)]
87-
pub struct PedersenGenerators {
88-
/// Base for the committed value
89-
pub B: RistrettoPoint,
90-
91-
/// Base for the blinding factor
92-
pub B_blinding: RistrettoPoint,
93-
}
94-
95-
impl PedersenGenerators {
96-
/// Creates a Pedersen commitment using the value scalar and a blinding factor.
97-
pub fn commit(&self, value: Scalar, blinding: Scalar) -> RistrettoPoint {
98-
RistrettoPoint::multiscalar_mul(&[value, blinding], &[self.B, self.B_blinding])
99-
}
100-
}
101-
102-
impl Default for PedersenGenerators {
103-
fn default() -> Self {
104-
PedersenGenerators {
105-
B: GeneratorsChain::new(b"Bulletproofs.Generators.B")
106-
.next()
107-
.unwrap(),
108-
B_blinding: GeneratorsChain::new(b"Bulletproofs.Generators.B_blinding")
109-
.next()
110-
.unwrap(),
111-
}
112-
}
97+
H_vec: Vec<Vec<RistrettoPoint>>,
11398
}
11499

115100
impl Generators {
116-
/// Creates generators for `m` range proofs of `n` bits each.
117-
pub fn new(pedersen_gens: PedersenGenerators, n: usize, m: usize) -> Self {
101+
/// Creates `capacity` generators for the given number of `parties`.
102+
pub fn new(pedersen_gens: PedersenGenerators, capacity: usize, parties: usize) -> Self {
118103
use byteorder::{ByteOrder, LittleEndian};
119104

120-
let G = (0..m)
121-
.flat_map(|i| {
105+
let G_vec = (0..parties)
106+
.map(|i| {
122107
let party_index = i as u32;
123108
let mut label = [b'G', 0, 0, 0, 0];
124109
LittleEndian::write_u32(&mut label[1..5], party_index);
125110

126-
GeneratorsChain::new(&label).take(n)
111+
GeneratorsChain::new(&label)
112+
.take(capacity)
113+
.collect::<Vec<_>>()
127114
})
128115
.collect();
129116

130-
let H = (0..m)
131-
.flat_map(|i| {
117+
let H_vec = (0..parties)
118+
.map(|i| {
132119
let party_index = i as u32;
133120
let mut label = [b'H', 0, 0, 0, 0];
134121
LittleEndian::write_u32(&mut label[1..5], party_index);
135122

136-
GeneratorsChain::new(&label).take(n)
123+
GeneratorsChain::new(&label)
124+
.take(capacity)
125+
.collect::<Vec<_>>()
137126
})
138127
.collect();
139128

140129
Generators {
141130
pedersen_gens,
142-
n,
143-
m,
144-
G,
145-
H,
131+
capacity,
132+
parties,
133+
G_vec,
134+
H_vec,
146135
}
147136
}
148137

149138
/// Returns j-th share of generators, with an appropriate
150139
/// slice of vectors G and H for the j-th range proof.
151140
pub fn share(&self, j: usize) -> GeneratorsView {
152-
let lower = self.n * j;
153-
let upper = self.n * (j + 1);
154141
GeneratorsView {
155142
pedersen_gens: &self.pedersen_gens,
156-
G: &self.G[lower..upper],
157-
H: &self.H[lower..upper],
143+
gens: &self,
144+
share: j,
145+
}
146+
}
147+
148+
/// Get the maximum number of generators that can be used in a proof.
149+
pub fn capacity(&self) -> usize {
150+
self.capacity
151+
}
152+
153+
/// Return an iterator over the aggregation of the parties' G generators with given size `n`.
154+
pub(crate) fn G(&self, n: usize) -> impl Iterator<Item = &RistrettoPoint> {
155+
AggregatedGensIter {
156+
n,
157+
array: &self.G_vec,
158+
party_idx: 0,
159+
gen_idx: 0,
160+
}
161+
}
162+
163+
/// Return an iterator over the aggregation of the parties' H generators with given size `n`.
164+
pub(crate) fn H(&self, n: usize) -> impl Iterator<Item = &RistrettoPoint> {
165+
AggregatedGensIter {
166+
n,
167+
array: &self.H_vec,
168+
party_idx: 0,
169+
gen_idx: 0,
170+
}
171+
}
172+
}
173+
174+
struct AggregatedGensIter<'a> {
175+
array: &'a Vec<Vec<RistrettoPoint>>,
176+
n: usize,
177+
party_idx: usize,
178+
gen_idx: usize,
179+
}
180+
181+
impl<'a> Iterator for AggregatedGensIter<'a> {
182+
type Item = &'a RistrettoPoint;
183+
184+
fn next(&mut self) -> Option<Self::Item> {
185+
if self.gen_idx >= self.n {
186+
self.gen_idx = 0;
187+
self.party_idx += 1;
158188
}
189+
190+
if self.party_idx >= self.array.len() {
191+
None
192+
} else {
193+
let cur_gen = self.gen_idx;
194+
self.gen_idx += 1;
195+
Some(&self.array[self.party_idx][cur_gen])
196+
}
197+
}
198+
199+
fn size_hint(&self) -> (usize, Option<usize>) {
200+
let size = self.n * self.array.len();
201+
(size, Some(size))
202+
}
203+
}
204+
205+
/// The `GeneratorsView` is produced by `Generators::share()`.
206+
///
207+
/// The `Generators` struct represents generators for an aggregated
208+
/// range proof `m` proofs of `n` bits each; the `GeneratorsView`
209+
/// represents the generators for one of the `m` parties' shares.
210+
#[derive(Copy, Clone)]
211+
pub struct GeneratorsView<'a> {
212+
/// Bases for Pedersen commitments
213+
pub pedersen_gens: &'a PedersenGenerators,
214+
/// The parent object that this is a view into
215+
gens: &'a Generators,
216+
/// Which share we are
217+
share: usize,
218+
}
219+
220+
impl<'a> GeneratorsView<'a> {
221+
/// Return an iterator over this party's G generators with given size `n`.
222+
pub(crate) fn G(&self, n: usize) -> impl Iterator<Item = &'a RistrettoPoint> {
223+
self.gens.G_vec[self.share].iter().take(n)
224+
}
225+
226+
/// Return an iterator over this party's H generators with given size `n`.
227+
pub(crate) fn H(&self, n: usize) -> impl Iterator<Item = &'a RistrettoPoint> {
228+
self.gens.H_vec[self.share].iter().take(n)
159229
}
160230
}
161231

@@ -164,24 +234,36 @@ mod tests {
164234
extern crate hex;
165235
use super::*;
166236

237+
// XXX write tests
238+
167239
#[test]
168-
fn rangeproof_generators() {
169-
let n = 2;
170-
let m = 3;
171-
let gens = Generators::new(PedersenGenerators::default(), n, m);
172-
173-
// The concatenation of shares must be the full generator set
174-
assert_eq!(
175-
[gens.G[..n].to_vec(), gens.H[..n].to_vec()],
176-
[gens.share(0).G[..].to_vec(), gens.share(0).H[..].to_vec()]
177-
);
178-
assert_eq!(
179-
[gens.G[n..][..n].to_vec(), gens.H[n..][..n].to_vec()],
180-
[gens.share(1).G[..].to_vec(), gens.share(1).H[..].to_vec()]
181-
);
182-
assert_eq!(
183-
[gens.G[2 * n..][..n].to_vec(), gens.H[2 * n..][..n].to_vec()],
184-
[gens.share(2).G[..].to_vec(), gens.share(2).H[..].to_vec()]
185-
);
240+
fn aggregated_gens_iter_matches_flat_map() {
241+
let gens = Generators::new(PedersenGenerators::default(), 64, 8);
242+
243+
let helper = |n: usize| {
244+
let agg_G: Vec<RistrettoPoint> = gens.G(n).cloned().collect();
245+
let flat_G: Vec<RistrettoPoint> = gens
246+
.G_vec
247+
.iter()
248+
.flat_map(move |G_j| G_j.iter().take(n))
249+
.cloned()
250+
.collect();
251+
252+
let agg_H: Vec<RistrettoPoint> = gens.H(n).cloned().collect();
253+
let flat_H: Vec<RistrettoPoint> = gens
254+
.H_vec
255+
.iter()
256+
.flat_map(move |H_j| H_j.iter().take(n))
257+
.cloned()
258+
.collect();
259+
260+
assert_eq!(agg_G, flat_G);
261+
assert_eq!(agg_H, flat_H);
262+
};
263+
264+
helper(64);
265+
helper(32);
266+
helper(16);
267+
helper(8);
186268
}
187269
}

src/inner_product_proof.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,8 @@ mod tests {
337337

338338
use generators::{Generators, PedersenGenerators};
339339
let gens = Generators::new(PedersenGenerators::default(), n, 1);
340-
let G = gens.share(0).G.to_vec();
341-
let H = gens.share(0).H.to_vec();
340+
let G: Vec<RistrettoPoint> = gens.share(0).G(n).cloned().collect();
341+
let H: Vec<RistrettoPoint> = gens.share(0).H(n).cloned().collect();
342342

343343
// Q would be determined upstream in the protocol, so we pick a random one.
344344
let Q = RistrettoPoint::hash_from_bytes::<Sha3_512>(b"test point");

src/range_proof/dealer.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,10 @@ impl<'a, 'b> DealerAwaitingProofShares<'a, 'b> {
226226
self.transcript,
227227
&Q,
228228
util::exp_iter(self.value_challenge.y.invert()),
229-
self.gens.G.to_vec(),
230-
self.gens.H.to_vec(),
231-
l_vec.clone(),
232-
r_vec.clone(),
229+
self.gens.G(self.n).cloned().collect(),
230+
self.gens.H(self.n).cloned().collect(),
231+
l_vec,
232+
r_vec,
233233
);
234234

235235
Ok(RangeProof {

src/range_proof/messages.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl ProofShare {
6363
use inner_product_proof::inner_product;
6464
use util;
6565

66-
let n = gens.n;
66+
let n = self.l_vec.len();
6767
let (y, z) = (&value_challenge.y, &value_challenge.z);
6868
let x = &poly_challenge.x;
6969

@@ -98,8 +98,8 @@ impl ProofShare {
9898
iter::once(&value_commitment.A_j)
9999
.chain(iter::once(&value_commitment.S_j))
100100
.chain(iter::once(&gens.pedersen_gens.B_blinding))
101-
.chain(gens.share(j).G.iter())
102-
.chain(gens.share(j).H.iter()),
101+
.chain(gens.share(j).G(n))
102+
.chain(gens.share(j).H(n)),
103103
);
104104
if !P_check.is_identity() {
105105
return Err(());

src/range_proof/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,12 @@ impl RangeProof {
7979
if values.len() != blindings.len() {
8080
return Err(ProofError::WrongNumBlindingFactors);
8181
}
82-
if generators.n != n {
83-
return Err(ProofError::InvalidGeneratorsLength);
84-
}
8582
if !(n == 8 || n == 16 || n == 32 || n == 64) {
8683
return Err(ProofError::InvalidBitsize);
8784
}
85+
if generators.capacity() < n {
86+
return Err(ProofError::InvalidGeneratorsLength);
87+
}
8888

8989
let dealer = Dealer::new(generators, n, values.len(), transcript)?;
9090

@@ -152,12 +152,12 @@ impl RangeProof {
152152
) -> Result<(), ProofError> {
153153
// First, replay the "interactive" protocol using the proof
154154
// data to recompute all challenges.
155-
if gens.n != n {
156-
return Err(ProofError::InvalidGeneratorsLength);
157-
}
158155
if !(n == 8 || n == 16 || n == 32 || n == 64) {
159156
return Err(ProofError::InvalidBitsize);
160157
}
158+
if gens.capacity() < n {
159+
return Err(ProofError::InvalidGeneratorsLength);
160+
}
161161

162162
let m = value_commitments.len();
163163

@@ -235,8 +235,8 @@ impl RangeProof {
235235
.chain(self.ipp_proof.R_vec.iter().map(|R| R.decompress()))
236236
.chain(iter::once(Some(gens.pedersen_gens.B_blinding)))
237237
.chain(iter::once(Some(gens.pedersen_gens.B)))
238-
.chain(gens.G.iter().map(|&x| Some(x)))
239-
.chain(gens.H.iter().map(|&x| Some(x)))
238+
.chain(gens.G(n).map(|&x| Some(x)))
239+
.chain(gens.H(n).map(|&x| Some(x)))
240240
.chain(value_commitments.iter().map(|&x| Some(x))),
241241
).ok_or_else(|| ProofError::VerificationError)?;
242242

0 commit comments

Comments
 (0)