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]
84extern crate criterion;
95use 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;
1518extern crate merlin;
16- use merlin:: Transcript ;
17-
1819extern 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+
194165fn 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
220195criterion_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
227204fn 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
266250criterion_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}
0 commit comments