#![allow(non_snake_case)]
#[macro_use]
extern crate criterion;
use criterion::Criterion;
extern crate bulletproofs;
extern crate curve25519_dalek;
extern crate merlin;
extern crate rand;
use bulletproofs::r1cs::*;
use bulletproofs::{BulletproofGens, PedersenGens};
use curve25519_dalek::ristretto::CompressedRistretto;
use curve25519_dalek::scalar::Scalar;
use merlin::Transcript;
use rand::seq::SliceRandom;
use rand::Rng;
struct ShuffleProof(R1CSProof);
impl ShuffleProof {
fn gadget<CS: RandomizableConstraintSystem>(
cs: &mut CS,
x: Vec<Variable>,
y: Vec<Variable>,
) -> Result<(), R1CSError> {
assert_eq!(x.len(), y.len());
let k = x.len();
if k == 1 {
cs.constrain(y[0] - x[0]);
return Ok(());
}
cs.specify_randomized_constraints(move |cs| {
let z = cs.challenge_scalar(b"shuffle challenge");
let (_, _, last_mulx_out) = cs.multiply(x[k - 1] - z, x[k - 2] - z);
let first_mulx_out = (0..k - 2).rev().fold(last_mulx_out, |prev_out, i| {
let (_, _, o) = cs.multiply(prev_out.into(), x[i] - z);
o
});
let (_, _, last_muly_out) = cs.multiply(y[k - 1] - z, y[k - 2] - z);
let first_muly_out = (0..k - 2).rev().fold(last_muly_out, |prev_out, i| {
let (_, _, o) = cs.multiply(prev_out.into(), y[i] - z);
o
});
cs.constrain(first_mulx_out - first_muly_out);
Ok(())
})
}
}
impl ShuffleProof {
pub fn prove<'a, 'b>(
pc_gens: &'b PedersenGens,
bp_gens: &'b BulletproofGens,
transcript: &'a mut Transcript,
input: &[Scalar],
output: &[Scalar],
) -> Result<
(
ShuffleProof,
Vec<CompressedRistretto>,
Vec<CompressedRistretto>,
),
R1CSError,
> {
let k = input.len();
transcript.append_message(b"dom-sep", b"ShuffleProof");
transcript.append_u64(b"k", k as u64);
let mut prover = Prover::new(&pc_gens, transcript);
let mut blinding_rng = rand::thread_rng();
let (input_commitments, input_vars): (Vec<_>, Vec<_>) = input
.into_iter()
.map(|v| prover.commit(*v, Scalar::random(&mut blinding_rng)))
.unzip();
let (output_commitments, output_vars): (Vec<_>, Vec<_>) = output
.into_iter()
.map(|v| prover.commit(*v, Scalar::random(&mut blinding_rng)))
.unzip();
ShuffleProof::gadget(&mut prover, input_vars, output_vars)?;
let proof = prover.prove(&bp_gens)?;
Ok((ShuffleProof(proof), input_commitments, output_commitments))
}
}
impl ShuffleProof {
pub fn verify<'a, 'b>(
&self,
pc_gens: &'b PedersenGens,
bp_gens: &'b BulletproofGens,
transcript: &'a mut Transcript,
input_commitments: &Vec<CompressedRistretto>,
output_commitments: &Vec<CompressedRistretto>,
) -> Result<(), R1CSError> {
let k = input_commitments.len();
transcript.append_message(b"dom-sep", b"ShuffleProof");
transcript.append_u64(b"k", k as u64);
let mut verifier = Verifier::new(transcript);
let input_vars: Vec<_> = input_commitments
.iter()
.map(|V| verifier.commit(*V))
.collect();
let output_vars: Vec<_> = output_commitments
.iter()
.map(|V| verifier.commit(*V))
.collect();
ShuffleProof::gadget(&mut verifier, input_vars, output_vars)?;
verifier.verify(&self.0, &pc_gens, &bp_gens)
}
}
const LG_MAX_SHUFFLE_SIZE: usize = 10;
const MAX_SHUFFLE_SIZE: usize = 1 << LG_MAX_SHUFFLE_SIZE;
fn bench_kshuffle_prove(c: &mut Criterion) {
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(2 * MAX_SHUFFLE_SIZE, 1);
c.bench_function_over_inputs(
"k-shuffle proof creation",
move |b, k| {
let mut rng = rand::thread_rng();
let (min, max) = (0u64, std::u64::MAX);
let input: Vec<Scalar> = (0..*k)
.map(|_| Scalar::from(rng.gen_range(min, max)))
.collect();
let mut output = input.clone();
output.shuffle(&mut rand::thread_rng());
b.iter(|| {
let mut prover_transcript = Transcript::new(b"ShuffleBenchmark");
ShuffleProof::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output)
.unwrap();
})
},
(1..=LG_MAX_SHUFFLE_SIZE)
.map(|i| 1 << i)
.collect::<Vec<_>>(),
);
}
criterion_group! {
name = kshuffle_prove;
config = Criterion::default().sample_size(10);
targets =
bench_kshuffle_prove,
}
fn bench_kshuffle_verify(c: &mut Criterion) {
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(2 * MAX_SHUFFLE_SIZE, 1);
c.bench_function_over_inputs(
"k-shuffle proof verification",
move |b, k| {
let (proof, input_commitments, output_commitments) = {
let mut rng = rand::thread_rng();
let (min, max) = (0u64, std::u64::MAX);
let input: Vec<Scalar> = (0..*k)
.map(|_| Scalar::from(rng.gen_range(min, max)))
.collect();
let mut output = input.clone();
output.shuffle(&mut rand::thread_rng());
let mut prover_transcript = Transcript::new(b"ShuffleBenchmark");
ShuffleProof::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output)
.unwrap()
};
b.iter(|| {
let mut verifier_transcript = Transcript::new(b"ShuffleBenchmark");
proof
.verify(
&pc_gens,
&bp_gens,
&mut verifier_transcript,
&input_commitments,
&output_commitments,
)
.unwrap();
})
},
(1..=LG_MAX_SHUFFLE_SIZE)
.map(|i| 1 << i)
.collect::<Vec<_>>(),
);
}
criterion_group! {
name = kshuffle_verify;
config = Criterion::default().sample_size(10);
targets =
bench_kshuffle_verify,
}
criterion_main!(kshuffle_prove, kshuffle_verify);