|
| 1 | +extern crate bulletproofs; |
| 2 | +use bulletproofs::r1cs::{ConstraintSystem, ProverCS, R1CSError, R1CSProof, Variable, VerifierCS}; |
| 3 | +use bulletproofs::{BulletproofGens, PedersenGens}; |
| 4 | + |
| 5 | +#[macro_use] |
| 6 | +extern crate criterion; |
| 7 | +use criterion::Criterion; |
| 8 | + |
| 9 | +extern crate curve25519_dalek; |
| 10 | +use curve25519_dalek::ristretto::CompressedRistretto; |
| 11 | +use curve25519_dalek::scalar::Scalar; |
| 12 | + |
| 13 | +extern crate merlin; |
| 14 | +use merlin::Transcript; |
| 15 | + |
| 16 | +extern crate rand; |
| 17 | +use rand::Rng; |
| 18 | + |
| 19 | +/* |
| 20 | +K-SHUFFLE GADGET SPECIFICATION: |
| 21 | +
|
| 22 | +Represents a permutation of a list of `k` scalars `{x_i}` into a list of `k` scalars `{y_i}`. |
| 23 | +
|
| 24 | +Algebraically it can be expressed as a statement that for a free variable `z`, |
| 25 | +the roots of the two polynomials in terms of `z` are the same up to a permutation: |
| 26 | +
|
| 27 | + ∏(x_i - z) == ∏(y_i - z) |
| 28 | +
|
| 29 | +Prover can commit to blinded scalars `x_i` and `y_i` then receive a random challenge `z`, |
| 30 | +and build a proof that the above relation holds. |
| 31 | +
|
| 32 | +K-shuffle requires `2*(K-1)` multipliers. |
| 33 | +
|
| 34 | +For K > 1: |
| 35 | +
|
| 36 | + (x_0 - z)---⊗------⊗---(y_0 - z) // mulx[0], muly[0] |
| 37 | + | | |
| 38 | + (x_1 - z)---⊗ ⊗---(y_1 - z) // mulx[1], muly[1] |
| 39 | + | | |
| 40 | + ... ... |
| 41 | + | | |
| 42 | + (x_{k-2} - z)---⊗ ⊗---(y_{k-2} - z) // mulx[k-2], muly[k-2] |
| 43 | + / \ |
| 44 | + (x_{k-1} - z)_/ \_(y_{k-1} - z) |
| 45 | +
|
| 46 | + // Connect left and right sides of the shuffle statement |
| 47 | + mulx_out[0] = muly_out[0] |
| 48 | +
|
| 49 | + // For i == [0, k-3]: |
| 50 | + mulx_left[i] = x_i - z |
| 51 | + mulx_right[i] = mulx_out[i+1] |
| 52 | + muly_left[i] = y_i - z |
| 53 | + muly_right[i] = muly_out[i+1] |
| 54 | +
|
| 55 | + // last multipliers connect two last variables (on each side) |
| 56 | + mulx_left[k-2] = x_{k-2} - z |
| 57 | + mulx_right[k-2] = x_{k-1} - z |
| 58 | + muly_left[k-2] = y_{k-2} - z |
| 59 | + muly_right[k-2] = y_{k-1} - z |
| 60 | +
|
| 61 | +For K = 1: |
| 62 | +
|
| 63 | + (x_0 - z)--------------(y_0 - z) |
| 64 | +
|
| 65 | + // Connect x to y directly, omitting the challenge entirely as it cancels out |
| 66 | + x_0 = y_0 |
| 67 | +*/ |
| 68 | + |
| 69 | +// Make a gadget that adds constraints to a ConstraintSystem, such that the |
| 70 | +// y variables are constrained to be a valid shuffle of the x variables. |
| 71 | +struct KShuffleGadget {} |
| 72 | + |
| 73 | +impl KShuffleGadget { |
| 74 | + fn fill_cs<CS: ConstraintSystem>(cs: &mut CS, x: &[Variable], y: &[Variable]) { |
| 75 | + let one = Scalar::one(); |
| 76 | + let z = cs.challenge_scalar(b"k-scalar shuffle challenge"); |
| 77 | + |
| 78 | + assert_eq!(x.len(), y.len()); |
| 79 | + |
| 80 | + let k = x.len(); |
| 81 | + if k == 1 { |
| 82 | + cs.add_auxiliary_constraint([(x[0], -one), (y[0], one)].iter().collect()); |
| 83 | + return; |
| 84 | + } |
| 85 | + |
| 86 | + // Make last x multiplier for i = k-1 and k-2 |
| 87 | + let (_, _, last_mulx_out) = cs.add_partial_constraint(x[k - 1] - z, x[k - 2] - z); |
| 88 | + |
| 89 | + // Make multipliers for x from i == [0, k-3] |
| 90 | + let first_mulx_out = (0..k - 2).rev().fold(last_mulx_out, |prev_out, i| { |
| 91 | + let (_, _, o) = cs.add_partial_constraint(prev_out.into(), x[i] - z); |
| 92 | + o |
| 93 | + }); |
| 94 | + |
| 95 | + // Make last y multiplier for i = k-1 and k-2 |
| 96 | + let (_, _, last_muly_out) = cs.add_partial_constraint(y[k - 1] - z, y[k - 2] - z); |
| 97 | + |
| 98 | + // Make multipliers for y from i == [0, k-3] |
| 99 | + let first_muly_out = (0..k - 2).rev().fold(last_muly_out, |prev_out, i| { |
| 100 | + let (_, _, o) = cs.add_partial_constraint(prev_out.into(), y[i] - z); |
| 101 | + o |
| 102 | + }); |
| 103 | + |
| 104 | + // Check equality between last x mul output and last y mul output |
| 105 | + cs.add_auxiliary_constraint( |
| 106 | + [(first_muly_out, -one), (first_mulx_out, one)] |
| 107 | + .iter() |
| 108 | + .collect(), |
| 109 | + ); |
| 110 | + } |
| 111 | + |
| 112 | + pub fn prove<'a, 'b>( |
| 113 | + pc_gens: &'b PedersenGens, |
| 114 | + bp_gens: &'b BulletproofGens, |
| 115 | + transcript: &'a mut Transcript, |
| 116 | + input: &[Scalar], |
| 117 | + output: &[Scalar], |
| 118 | + ) -> Result<(R1CSProof, Vec<CompressedRistretto>), R1CSError> { |
| 119 | + let k = input.len(); |
| 120 | + |
| 121 | + // Prover makes a `ConstraintSystem` instance representing a shuffle gadget |
| 122 | + // Make v vector |
| 123 | + let mut v = Vec::with_capacity(2 * k); |
| 124 | + v.extend_from_slice(input); |
| 125 | + v.extend_from_slice(output); |
| 126 | + |
| 127 | + // Make v_blinding vector using RNG from transcript |
| 128 | + let mut rng = { |
| 129 | + let mut builder = transcript.build_rng(); |
| 130 | + // commit the secret values |
| 131 | + for &v_i in &v { |
| 132 | + builder = builder.commit_witness_bytes(b"v_i", v_i.as_bytes()); |
| 133 | + } |
| 134 | + use rand::thread_rng; |
| 135 | + builder.finalize(&mut thread_rng()) |
| 136 | + }; |
| 137 | + let v_blinding: Vec<Scalar> = (0..2 * k).map(|_| Scalar::random(&mut rng)).collect(); |
| 138 | + let (mut prover_cs, variables, commitments) = |
| 139 | + ProverCS::new(&bp_gens, &pc_gens, transcript, v, v_blinding.clone()); |
| 140 | + |
| 141 | + // Prover allocates variables and adds constraints to the constraint system |
| 142 | + let (input_vars, output_vars) = variables.split_at(k); |
| 143 | + KShuffleGadget::fill_cs(&mut prover_cs, input_vars, output_vars); |
| 144 | + |
| 145 | + // Prover generates proof |
| 146 | + let proof = prover_cs.prove()?; |
| 147 | + Ok((proof, commitments)) |
| 148 | + } |
| 149 | + |
| 150 | + pub fn verify<'a, 'b>( |
| 151 | + pc_gens: &'b PedersenGens, |
| 152 | + bp_gens: &'b BulletproofGens, |
| 153 | + transcript: &'a mut Transcript, |
| 154 | + proof: &R1CSProof, |
| 155 | + commitments: &Vec<CompressedRistretto>, |
| 156 | + ) -> Result<(), R1CSError> { |
| 157 | + let k = commitments.len() / 2; |
| 158 | + |
| 159 | + // Verifier makes a `ConstraintSystem` instance representing a shuffle gadget |
| 160 | + let (mut verifier_cs, variables) = |
| 161 | + VerifierCS::new(&bp_gens, &pc_gens, transcript, commitments.to_vec()); |
| 162 | + |
| 163 | + // Verifier allocates variables and adds constraints to the constraint system |
| 164 | + let (input_vars, output_vars) = variables.split_at(k); |
| 165 | + KShuffleGadget::fill_cs(&mut verifier_cs, input_vars, output_vars); |
| 166 | + |
| 167 | + // Verifier verifies proof |
| 168 | + verifier_cs.verify(&proof) |
| 169 | + } |
| 170 | +} |
| 171 | + |
| 172 | +fn kshuffle_prove_helper(k: usize, c: &mut Criterion) { |
| 173 | + let label = format!("{}-shuffle proof creation", k); |
| 174 | + |
| 175 | + c.bench_function(&label, move |b| { |
| 176 | + // Generate inputs and outputs to kshuffle |
| 177 | + let mut rng = rand::thread_rng(); |
| 178 | + let (min, max) = (0u64, std::u64::MAX); |
| 179 | + let input: Vec<Scalar> = (0..k) |
| 180 | + .map(|_| Scalar::from(rng.gen_range(min, max))) |
| 181 | + .collect(); |
| 182 | + let mut output = input.clone(); |
| 183 | + rand::thread_rng().shuffle(&mut output); |
| 184 | + |
| 185 | + // Make kshuffle proof |
| 186 | + let pc_gens = PedersenGens::default(); |
| 187 | + let bp_gens = BulletproofGens::new(128, 1); |
| 188 | + b.iter(|| { |
| 189 | + let mut prover_transcript = Transcript::new(b"ShuffleTest"); |
| 190 | + KShuffleGadget::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output) |
| 191 | + .unwrap(); |
| 192 | + }) |
| 193 | + }); |
| 194 | +} |
| 195 | + |
| 196 | +fn kshuffle_prove_8(c: &mut Criterion) { |
| 197 | + kshuffle_prove_helper(8, c); |
| 198 | +} |
| 199 | +fn kshuffle_prove_16(c: &mut Criterion) { |
| 200 | + kshuffle_prove_helper(16, c); |
| 201 | +} |
| 202 | +fn kshuffle_prove_32(c: &mut Criterion) { |
| 203 | + kshuffle_prove_helper(32, c); |
| 204 | +} |
| 205 | +fn kshuffle_prove_64(c: &mut Criterion) { |
| 206 | + kshuffle_prove_helper(64, c); |
| 207 | +} |
| 208 | +fn kshuffle_prove_17(c: &mut Criterion) { |
| 209 | + kshuffle_prove_helper(17, c); |
| 210 | +} |
| 211 | + |
| 212 | +criterion_group!{ |
| 213 | + name = kshuffle_prove; |
| 214 | + config = Criterion::default(); |
| 215 | + targets = |
| 216 | + kshuffle_prove_8, |
| 217 | + kshuffle_prove_16, |
| 218 | + kshuffle_prove_32, |
| 219 | + kshuffle_prove_64, |
| 220 | + kshuffle_prove_17, |
| 221 | +} |
| 222 | + |
| 223 | +fn kshuffle_verify_helper(k: usize, c: &mut Criterion) { |
| 224 | + let label = format!("{}-shuffle proof verification", k); |
| 225 | + |
| 226 | + c.bench_function(&label, move |b| { |
| 227 | + // Generate inputs and outputs to kshuffle |
| 228 | + let mut rng = rand::thread_rng(); |
| 229 | + let (min, max) = (0u64, std::u64::MAX); |
| 230 | + let input: Vec<Scalar> = (0..k) |
| 231 | + .map(|_| Scalar::from(rng.gen_range(min, max))) |
| 232 | + .collect(); |
| 233 | + let mut output = input.clone(); |
| 234 | + rand::thread_rng().shuffle(&mut output); |
| 235 | + |
| 236 | + // Make kshuffle proof |
| 237 | + let pc_gens = PedersenGens::default(); |
| 238 | + let bp_gens = BulletproofGens::new(128, 1); |
| 239 | + let mut prover_transcript = Transcript::new(b"ShuffleTest"); |
| 240 | + let (proof, commitments) = |
| 241 | + KShuffleGadget::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output) |
| 242 | + .unwrap(); |
| 243 | + |
| 244 | + // Verify kshuffle proof |
| 245 | + b.iter(|| { |
| 246 | + let mut verifier_transcript = Transcript::new(b"ShuffleTest"); |
| 247 | + KShuffleGadget::verify( |
| 248 | + &pc_gens, |
| 249 | + &bp_gens, |
| 250 | + &mut verifier_transcript, |
| 251 | + &proof, |
| 252 | + &commitments, |
| 253 | + ) |
| 254 | + .unwrap(); |
| 255 | + }) |
| 256 | + }); |
| 257 | +} |
| 258 | + |
| 259 | +fn kshuffle_verify_8(c: &mut Criterion) { |
| 260 | + kshuffle_verify_helper(8, c); |
| 261 | +} |
| 262 | +fn kshuffle_verify_16(c: &mut Criterion) { |
| 263 | + kshuffle_verify_helper(16, c); |
| 264 | +} |
| 265 | +fn kshuffle_verify_32(c: &mut Criterion) { |
| 266 | + kshuffle_verify_helper(32, c); |
| 267 | +} |
| 268 | +fn kshuffle_verify_64(c: &mut Criterion) { |
| 269 | + kshuffle_verify_helper(64, c); |
| 270 | +} |
| 271 | +fn kshuffle_verify_17(c: &mut Criterion) { |
| 272 | + kshuffle_verify_helper(17, c); |
| 273 | +} |
| 274 | + |
| 275 | +criterion_group!{ |
| 276 | + name = kshuffle_verify; |
| 277 | + config = Criterion::default(); |
| 278 | + targets = |
| 279 | + kshuffle_verify_8, |
| 280 | + kshuffle_verify_16, |
| 281 | + kshuffle_verify_32, |
| 282 | + kshuffle_verify_64, |
| 283 | + kshuffle_verify_17, |
| 284 | +} |
| 285 | + |
| 286 | +criterion_main!(kshuffle_prove, kshuffle_verify); |
0 commit comments