#![allow(non_snake_case)]
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::thread_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)?;
Ok(())
}
}
fn kshuffle_helper(k: usize) {
use rand::Rng;
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new((2 * k).next_power_of_two(), 1);
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"ShuffleProofTest");
ShuffleProof::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output).unwrap()
};
{
let mut verifier_transcript = Transcript::new(b"ShuffleProofTest");
assert!(proof
.verify(
&pc_gens,
&bp_gens,
&mut verifier_transcript,
&input_commitments,
&output_commitments
)
.is_ok());
}
}
#[test]
fn shuffle_gadget_test_1() {
kshuffle_helper(1);
}
#[test]
fn shuffle_gadget_test_2() {
kshuffle_helper(2);
}
#[test]
fn shuffle_gadget_test_3() {
kshuffle_helper(3);
}
#[test]
fn shuffle_gadget_test_4() {
kshuffle_helper(4);
}
#[test]
fn shuffle_gadget_test_5() {
kshuffle_helper(5);
}
#[test]
fn shuffle_gadget_test_6() {
kshuffle_helper(6);
}
#[test]
fn shuffle_gadget_test_7() {
kshuffle_helper(7);
}
#[test]
fn shuffle_gadget_test_24() {
kshuffle_helper(24);
}
#[test]
fn shuffle_gadget_test_42() {
kshuffle_helper(42);
}
fn example_gadget<CS: ConstraintSystem>(
cs: &mut CS,
a1: LinearCombination,
a2: LinearCombination,
b1: LinearCombination,
b2: LinearCombination,
c1: LinearCombination,
c2: LinearCombination,
) {
let (_, _, c_var) = cs.multiply(a1 + a2, b1 + b2);
cs.constrain(c1 + c2 - c_var);
}
fn example_gadget_proof(
pc_gens: &PedersenGens,
bp_gens: &BulletproofGens,
a1: u64,
a2: u64,
b1: u64,
b2: u64,
c1: u64,
c2: u64,
) -> Result<(R1CSProof, Vec<CompressedRistretto>), R1CSError> {
let mut transcript = Transcript::new(b"R1CSExampleGadget");
let mut prover = Prover::new(pc_gens, &mut transcript);
let (commitments, vars): (Vec<_>, Vec<_>) = [a1, a2, b1, b2, c1]
.into_iter()
.map(|x| prover.commit(Scalar::from(*x), Scalar::random(&mut thread_rng())))
.unzip();
example_gadget(
&mut prover,
vars[0].into(),
vars[1].into(),
vars[2].into(),
vars[3].into(),
vars[4].into(),
Scalar::from(c2).into(),
);
let proof = prover.prove(bp_gens)?;
Ok((proof, commitments))
}
fn example_gadget_verify(
pc_gens: &PedersenGens,
bp_gens: &BulletproofGens,
c2: u64,
proof: R1CSProof,
commitments: Vec<CompressedRistretto>,
) -> Result<(), R1CSError> {
let mut transcript = Transcript::new(b"R1CSExampleGadget");
let mut verifier = Verifier::new(&mut transcript);
let vars: Vec<_> = commitments.iter().map(|V| verifier.commit(*V)).collect();
example_gadget(
&mut verifier,
vars[0].into(),
vars[1].into(),
vars[2].into(),
vars[3].into(),
vars[4].into(),
Scalar::from(c2).into(),
);
verifier
.verify(&proof, &pc_gens, &bp_gens)
.map_err(|_| R1CSError::VerificationError)
}
fn example_gadget_roundtrip_helper(
a1: u64,
a2: u64,
b1: u64,
b2: u64,
c1: u64,
c2: u64,
) -> Result<(), R1CSError> {
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(128, 1);
let (proof, commitments) = example_gadget_proof(&pc_gens, &bp_gens, a1, a2, b1, b2, c1, c2)?;
example_gadget_verify(&pc_gens, &bp_gens, c2, proof, commitments)
}
fn example_gadget_roundtrip_serialization_helper(
a1: u64,
a2: u64,
b1: u64,
b2: u64,
c1: u64,
c2: u64,
) -> Result<(), R1CSError> {
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(128, 1);
let (proof, commitments) = example_gadget_proof(&pc_gens, &bp_gens, a1, a2, b1, b2, c1, c2)?;
let proof = proof.to_bytes();
let proof = R1CSProof::from_bytes(&proof)?;
example_gadget_verify(&pc_gens, &bp_gens, c2, proof, commitments)
}
#[test]
fn example_gadget_test() {
assert!(example_gadget_roundtrip_helper(3, 4, 6, 1, 40, 9).is_ok());
assert!(example_gadget_roundtrip_helper(3, 4, 6, 1, 40, 10).is_err());
}
#[test]
fn example_gadget_serialization_test() {
assert!(example_gadget_roundtrip_serialization_helper(3, 4, 6, 1, 40, 9).is_ok());
assert!(example_gadget_roundtrip_serialization_helper(3, 4, 6, 1, 40, 10).is_err());
}
pub fn range_proof<CS: ConstraintSystem>(
cs: &mut CS,
mut v: LinearCombination,
v_assignment: Option<u64>,
n: usize,
) -> Result<(), R1CSError> {
let mut exp_2 = Scalar::one();
for i in 0..n {
let (a, b, o) = cs.allocate_multiplier(v_assignment.map(|q| {
let bit: u64 = (q >> i) & 1;
((1 - bit).into(), bit.into())
}))?;
cs.constrain(o.into());
cs.constrain(a + (b - 1u64));
v = v - b * exp_2;
exp_2 = exp_2 + exp_2;
}
cs.constrain(v);
Ok(())
}
#[test]
fn range_proof_gadget() {
use rand::thread_rng;
use rand::Rng;
let mut rng = thread_rng();
let m = 3;
for n in [2, 10, 32, 63].iter() {
let (min, max) = (0u64, ((1u128 << n) - 1) as u64);
let values: Vec<u64> = (0..m).map(|_| rng.gen_range(min, max)).collect();
for v in values {
assert!(range_proof_helper(v.into(), *n).is_ok());
}
assert!(range_proof_helper((max + 1).into(), *n).is_err());
}
}
fn range_proof_helper(v_val: u64, n: usize) -> Result<(), R1CSError> {
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(128, 1);
let (proof, commitment) = {
let mut prover_transcript = Transcript::new(b"RangeProofTest");
let mut rng = rand::thread_rng();
let mut prover = Prover::new(&pc_gens, &mut prover_transcript);
let (com, var) = prover.commit(v_val.into(), Scalar::random(&mut rng));
assert!(range_proof(&mut prover, var.into(), Some(v_val), n).is_ok());
let proof = prover.prove(&bp_gens)?;
(proof, com)
};
let mut verifier_transcript = Transcript::new(b"RangeProofTest");
let mut verifier = Verifier::new(&mut verifier_transcript);
let var = verifier.commit(commitment);
assert!(range_proof(&mut verifier, var.into(), None, n).is_ok());
verifier.verify(&proof, &pc_gens, &bp_gens)
}