Skip to content

Commit cd1bbf3

Browse files
committed
Update R1CS benchmark code & fmt
1 parent 29fad78 commit cd1bbf3

File tree

3 files changed

+122
-127
lines changed

3 files changed

+122
-127
lines changed

benches/r1cs.rs

Lines changed: 105 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,46 @@
1-
extern crate bulletproofs;
2-
use bulletproofs::r1cs::{
3-
ConstraintSystem, Prover, R1CSError, R1CSProof, RandomizedConstraintSystem, Variable, Verifier,
4-
};
5-
use bulletproofs::{BulletproofGens, PedersenGens};
1+
#![allow(non_snake_case)]
62

73
#[macro_use]
84
extern crate criterion;
95
use criterion::Criterion;
106

11-
extern crate curve25519_dalek;
12-
use curve25519_dalek::ristretto::CompressedRistretto;
13-
use curve25519_dalek::scalar::Scalar;
7+
// Code below copied from ../tests/r1cs.rs
8+
//
9+
// Ideally we wouldn't duplicate it, but AFAIK criterion requires a
10+
// seperate benchmark harness, while the test code uses a different
11+
// test harness, so I (hdevalence) just copied the code over. It
12+
// should not be edited here. In the future it would be good if
13+
// someone wants to figure a way to use #[path] attributes or
14+
// something to avoid the duplication.
1415

16+
extern crate bulletproofs;
17+
extern crate curve25519_dalek;
1518
extern crate merlin;
16-
use merlin::Transcript;
17-
1819
extern crate rand;
19-
use rand::Rng;
20-
21-
/*
22-
K-SHUFFLE GADGET SPECIFICATION:
23-
24-
Represents a permutation of a list of `k` scalars `{x_i}` into a list of `k` scalars `{y_i}`.
25-
26-
Algebraically it can be expressed as a statement that for a free variable `z`,
27-
the roots of the two polynomials in terms of `z` are the same up to a permutation:
2820

29-
∏(x_i - z) == ∏(y_i - z)
30-
31-
Prover can commit to blinded scalars `x_i` and `y_i` then receive a random challenge `z`,
32-
and build a proof that the above relation holds.
33-
34-
K-shuffle requires `2*(K-1)` multipliers.
35-
36-
For K > 1:
37-
38-
(x_0 - z)---⊗------⊗---(y_0 - z) // mulx[0], muly[0]
39-
| |
40-
(x_1 - z)---⊗ ⊗---(y_1 - z) // mulx[1], muly[1]
41-
| |
42-
... ...
43-
| |
44-
(x_{k-2} - z)---⊗ ⊗---(y_{k-2} - z) // mulx[k-2], muly[k-2]
45-
/ \
46-
(x_{k-1} - z)_/ \_(y_{k-1} - z)
47-
48-
// Connect left and right sides of the shuffle statement
49-
mulx_out[0] = muly_out[0]
50-
51-
// For i == [0, k-3]:
52-
mulx_left[i] = x_i - z
53-
mulx_right[i] = mulx_out[i+1]
54-
muly_left[i] = y_i - z
55-
muly_right[i] = muly_out[i+1]
56-
57-
// last multipliers connect two last variables (on each side)
58-
mulx_left[k-2] = x_{k-2} - z
59-
mulx_right[k-2] = x_{k-1} - z
60-
muly_left[k-2] = y_{k-2} - z
61-
muly_right[k-2] = y_{k-1} - z
62-
63-
For K = 1:
64-
65-
(x_0 - z)--------------(y_0 - z)
21+
use bulletproofs::r1cs::*;
22+
use bulletproofs::{BulletproofGens, PedersenGens};
23+
use curve25519_dalek::ristretto::CompressedRistretto;
24+
use curve25519_dalek::scalar::Scalar;
25+
use merlin::Transcript;
26+
use rand::{thread_rng, Rng};
6627

67-
// Connect x to y directly, omitting the challenge entirely as it cancels out
68-
x_0 = y_0
69-
*/
28+
// Shuffle gadget (documented in markdown file)
7029

71-
// Make a gadget that adds constraints to a ConstraintSystem, such that the
72-
// y variables are constrained to be a valid shuffle of the x variables.
73-
struct KShuffleGadget {}
30+
/// A proof-of-shuffle.
31+
struct ShuffleProof(R1CSProof);
7432

75-
impl KShuffleGadget {
76-
fn fill_cs<CS: RandomizableConstraintSystem>(
33+
impl ShuffleProof {
34+
fn gadget<CS: RandomizableConstraintSystem>(
7735
cs: &mut CS,
7836
x: Vec<Variable>,
7937
y: Vec<Variable>,
8038
) -> Result<(), R1CSError> {
81-
let one = Scalar::one();
82-
8339
assert_eq!(x.len(), y.len());
84-
8540
let k = x.len();
41+
8642
if k == 1 {
87-
cs.constrain([(x[0], -one), (y[0], one)].iter().collect());
43+
cs.constrain(y[0] - x[0]);
8844
return Ok(());
8945
}
9046

@@ -110,16 +66,17 @@ impl KShuffleGadget {
11066
});
11167

11268
// Constrain last x mul output and last y mul output to be equal
113-
cs.constrain(
114-
[(first_muly_out, -one), (first_mulx_out, one)]
115-
.iter()
116-
.collect(),
117-
);
69+
cs.constrain(first_mulx_out - first_muly_out);
11870

11971
Ok(())
12072
})
12173
}
74+
}
12275

76+
impl ShuffleProof {
77+
/// Attempt to construct a proof that `output` is a permutation of `input`.
78+
///
79+
/// Returns a tuple `(proof, input_commitments || output_commitments)`.
12380
pub fn prove<'a, 'b>(
12481
pc_gens: &'b PedersenGens,
12582
bp_gens: &'b BulletproofGens,
@@ -128,16 +85,17 @@ impl KShuffleGadget {
12885
output: &[Scalar],
12986
) -> Result<
13087
(
131-
R1CSProof,
88+
ShuffleProof,
13289
Vec<CompressedRistretto>,
13390
Vec<CompressedRistretto>,
13491
),
13592
R1CSError,
13693
> {
13794
// Apply a domain separator with the shuffle parameters to the transcript
95+
// XXX should this be part of the gadget?
13896
let k = input.len();
139-
transcript.commit_bytes(b"dom-sep", b"ShuffleProof");
140-
transcript.commit_bytes(b"k", Scalar::from(k as u64).as_bytes());
97+
transcript.append_message(b"dom-sep", b"ShuffleProof");
98+
transcript.append_u64(b"k", k as u64);
14199

142100
let mut prover = Prover::new(&pc_gens, transcript);
143101

@@ -155,43 +113,60 @@ impl KShuffleGadget {
155113
.map(|v| prover.commit(*v, Scalar::random(&mut blinding_rng)))
156114
.unzip();
157115

158-
Self::fill_cs(&mut prover, input_vars, output_vars)?;
116+
ShuffleProof::gadget(&mut prover, input_vars, output_vars)?;
117+
159118
let proof = prover.prove(&bp_gens)?;
160119

161-
Ok((proof, input_commitments, output_commitments))
120+
Ok((ShuffleProof(proof), input_commitments, output_commitments))
162121
}
122+
}
163123

124+
impl ShuffleProof {
125+
/// Attempt to verify a `ShuffleProof`.
164126
pub fn verify<'a, 'b>(
127+
&self,
165128
pc_gens: &'b PedersenGens,
166129
bp_gens: &'b BulletproofGens,
167130
transcript: &'a mut Transcript,
168-
proof: &R1CSProof,
169131
input_commitments: &Vec<CompressedRistretto>,
170132
output_commitments: &Vec<CompressedRistretto>,
171133
) -> Result<(), R1CSError> {
172134
// Apply a domain separator with the shuffle parameters to the transcript
135+
// XXX should this be part of the gadget?
173136
let k = input_commitments.len();
174-
transcript.commit_bytes(b"dom-sep", b"ShuffleProof");
175-
transcript.commit_bytes(b"k", Scalar::from(k as u64).as_bytes());
137+
transcript.append_message(b"dom-sep", b"ShuffleProof");
138+
transcript.append_u64(b"k", k as u64);
176139

177140
let mut verifier = Verifier::new(transcript);
178141

179142
let input_vars: Vec<_> = input_commitments
180143
.iter()
181-
.map(|commitment| verifier.commit(*commitment))
144+
.map(|V| verifier.commit(*V))
182145
.collect();
183146

184147
let output_vars: Vec<_> = output_commitments
185148
.iter()
186-
.map(|commitment| verifier.commit(*commitment))
149+
.map(|V| verifier.commit(*V))
187150
.collect();
188151

189-
Self::fill_cs(&mut verifier, input_vars, output_vars)?;
190-
verifier.verify(proof, &pc_gens, &bp_gens)
152+
ShuffleProof::gadget(&mut verifier, input_vars, output_vars)?;
153+
154+
verifier.verify(&self.0, &pc_gens, &bp_gens)
191155
}
192156
}
193157

158+
// End of copied code.
159+
160+
/// Binary logarithm of maximum shuffle size.
161+
const LG_MAX_SHUFFLE_SIZE: usize = 10;
162+
/// Maximum shuffle size to benchmark.
163+
const MAX_SHUFFLE_SIZE: usize = 1 << LG_MAX_SHUFFLE_SIZE;
164+
194165
fn bench_kshuffle_prove(c: &mut Criterion) {
166+
// Construct Bulletproof generators externally
167+
let pc_gens = PedersenGens::default();
168+
let bp_gens = BulletproofGens::new(2 * MAX_SHUFFLE_SIZE, 1);
169+
195170
c.bench_function_over_inputs(
196171
"k-shuffle proof creation",
197172
move |b, k| {
@@ -205,67 +180,78 @@ fn bench_kshuffle_prove(c: &mut Criterion) {
205180
rand::thread_rng().shuffle(&mut output);
206181

207182
// Make kshuffle proof
208-
let pc_gens = PedersenGens::default();
209-
let bp_gens = BulletproofGens::new(128, 1);
210183
b.iter(|| {
211-
let mut prover_transcript = Transcript::new(b"ShuffleTest");
212-
KShuffleGadget::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output)
184+
let mut prover_transcript = Transcript::new(b"ShuffleBenchmark");
185+
ShuffleProof::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output)
213186
.unwrap();
214187
})
215188
},
216-
vec![8, 16, 32, 64, 17],
189+
(1..=LG_MAX_SHUFFLE_SIZE)
190+
.map(|i| 1 << i)
191+
.collect::<Vec<_>>(),
217192
);
218193
}
219194

220195
criterion_group! {
221196
name = kshuffle_prove;
222-
config = Criterion::default();
197+
// Lower the sample size to run faster; larger shuffle sizes are
198+
// long so we're not microbenchmarking anyways.
199+
config = Criterion::default().sample_size(10);
223200
targets =
224201
bench_kshuffle_prove,
225202
}
226203

227204
fn bench_kshuffle_verify(c: &mut Criterion) {
205+
// Construct Bulletproof generators externally
206+
let pc_gens = PedersenGens::default();
207+
let bp_gens = BulletproofGens::new(2 * MAX_SHUFFLE_SIZE, 1);
208+
228209
c.bench_function_over_inputs(
229210
"k-shuffle proof verification",
230211
move |b, k| {
231-
// Generate inputs and outputs to kshuffle
232-
let mut rng = rand::thread_rng();
233-
let (min, max) = (0u64, std::u64::MAX);
234-
let input: Vec<Scalar> = (0..*k)
235-
.map(|_| Scalar::from(rng.gen_range(min, max)))
236-
.collect();
237-
let mut output = input.clone();
238-
rand::thread_rng().shuffle(&mut output);
239-
240-
// Make kshuffle proof
241-
let pc_gens = PedersenGens::default();
242-
let bp_gens = BulletproofGens::new(128, 1);
243-
let mut prover_transcript = Transcript::new(b"ShuffleTest");
244-
let (proof, in_commitments, out_commitments) =
245-
KShuffleGadget::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output)
246-
.unwrap();
212+
// Generate the proof in its own scope to prevent reuse of
213+
// prover variables by the verifier
214+
let (proof, input_commitments, output_commitments) = {
215+
// Generate inputs and outputs to kshuffle
216+
let mut rng = rand::thread_rng();
217+
let (min, max) = (0u64, std::u64::MAX);
218+
let input: Vec<Scalar> = (0..*k)
219+
.map(|_| Scalar::from(rng.gen_range(min, max)))
220+
.collect();
221+
let mut output = input.clone();
222+
rand::thread_rng().shuffle(&mut output);
223+
224+
let mut prover_transcript = Transcript::new(b"ShuffleBenchmark");
225+
226+
ShuffleProof::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output)
227+
.unwrap()
228+
};
247229

248230
// Verify kshuffle proof
249231
b.iter(|| {
250-
let mut verifier_transcript = Transcript::new(b"ShuffleTest");
251-
KShuffleGadget::verify(
252-
&pc_gens,
253-
&bp_gens,
254-
&mut verifier_transcript,
255-
&proof,
256-
&in_commitments,
257-
&out_commitments,
258-
)
259-
.unwrap();
232+
let mut verifier_transcript = Transcript::new(b"ShuffleBenchmark");
233+
proof
234+
.verify(
235+
&pc_gens,
236+
&bp_gens,
237+
&mut verifier_transcript,
238+
&input_commitments,
239+
&output_commitments,
240+
)
241+
.unwrap();
260242
})
261243
},
262-
vec![8, 16, 32, 64, 17],
244+
(1..=LG_MAX_SHUFFLE_SIZE)
245+
.map(|i| 1 << i)
246+
.collect::<Vec<_>>(),
263247
);
264248
}
265249

266250
criterion_group! {
267251
name = kshuffle_verify;
268-
config = Criterion::default();
252+
// Lower the sample size to run faster; larger shuffle sizes are
253+
// long so we're not microbenchmarking anyways.
254+
config = Criterion::default().sample_size(10);
269255
targets =
270256
bench_kshuffle_verify,
271257
}

src/r1cs/verifier.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -331,9 +331,12 @@ impl<'t> Verifier<'t> {
331331
self.transcript.append_u64(b"m", self.V.len() as u64);
332332

333333
let n1 = self.num_vars;
334-
self.transcript.validate_and_append_point(b"A_I1", &proof.A_I1)?;
335-
self.transcript.validate_and_append_point(b"A_O1", &proof.A_O1)?;
336-
self.transcript.validate_and_append_point(b"S1", &proof.S1)?;
334+
self.transcript
335+
.validate_and_append_point(b"A_I1", &proof.A_I1)?;
336+
self.transcript
337+
.validate_and_append_point(b"A_O1", &proof.A_O1)?;
338+
self.transcript
339+
.validate_and_append_point(b"S1", &proof.S1)?;
337340

338341
// Process the remaining constraints.
339342
self = self.create_randomized_constraints()?;
@@ -362,11 +365,16 @@ impl<'t> Verifier<'t> {
362365
let y = self.transcript.challenge_scalar(b"y");
363366
let z = self.transcript.challenge_scalar(b"z");
364367

365-
self.transcript.validate_and_append_point(b"T_1", &proof.T_1)?;
366-
self.transcript.validate_and_append_point(b"T_3", &proof.T_3)?;
367-
self.transcript.validate_and_append_point(b"T_4", &proof.T_4)?;
368-
self.transcript.validate_and_append_point(b"T_5", &proof.T_5)?;
369-
self.transcript.validate_and_append_point(b"T_6", &proof.T_6)?;
368+
self.transcript
369+
.validate_and_append_point(b"T_1", &proof.T_1)?;
370+
self.transcript
371+
.validate_and_append_point(b"T_3", &proof.T_3)?;
372+
self.transcript
373+
.validate_and_append_point(b"T_4", &proof.T_4)?;
374+
self.transcript
375+
.validate_and_append_point(b"T_5", &proof.T_5)?;
376+
self.transcript
377+
.validate_and_append_point(b"T_6", &proof.T_6)?;
370378

371379
let u = self.transcript.challenge_scalar(b"u");
372380
let x = self.transcript.challenge_scalar(b"x");

tests/r1cs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![allow(non_snake_case)]
2+
23
extern crate bulletproofs;
34
extern crate curve25519_dalek;
45
extern crate merlin;

0 commit comments

Comments
 (0)