#![allow(non_snake_case)]
#![doc(include = "../../docs/range-proof-protocol.md")]
extern crate alloc;
#[cfg(feature = "std")]
extern crate rand;
#[cfg(feature = "std")]
use self::rand::thread_rng;
use alloc::vec::Vec;
use core::iter;
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::{IsIdentity, VartimeMultiscalarMul};
use merlin::Transcript;
use errors::ProofError;
use generators::{BulletproofGens, PedersenGens};
use inner_product_proof::InnerProductProof;
use transcript::TranscriptProtocol;
use util;
use rand_core::{CryptoRng, RngCore};
use serde::de::Visitor;
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
pub mod dealer;
pub mod messages;
pub mod party;
#[derive(Clone, Debug)]
pub struct RangeProof {
A: CompressedRistretto,
S: CompressedRistretto,
T_1: CompressedRistretto,
T_2: CompressedRistretto,
t_x: Scalar,
t_x_blinding: Scalar,
e_blinding: Scalar,
ipp_proof: InnerProductProof,
}
impl RangeProof {
pub fn prove_single_with_rng<T: RngCore + CryptoRng>(
bp_gens: &BulletproofGens,
pc_gens: &PedersenGens,
transcript: &mut Transcript,
v: u64,
v_blinding: &Scalar,
n: usize,
rng: &mut T,
) -> Result<(RangeProof, CompressedRistretto), ProofError> {
let (p, Vs) = RangeProof::prove_multiple_with_rng(
bp_gens,
pc_gens,
transcript,
&[v],
&[*v_blinding],
n,
rng,
)?;
Ok((p, Vs[0]))
}
#[cfg(feature = "std")]
pub fn prove_single(
bp_gens: &BulletproofGens,
pc_gens: &PedersenGens,
transcript: &mut Transcript,
v: u64,
v_blinding: &Scalar,
n: usize,
) -> Result<(RangeProof, CompressedRistretto), ProofError> {
RangeProof::prove_single_with_rng(
bp_gens,
pc_gens,
transcript,
v,
v_blinding,
n,
&mut thread_rng(),
)
}
pub fn prove_multiple_with_rng<T: RngCore + CryptoRng>(
bp_gens: &BulletproofGens,
pc_gens: &PedersenGens,
transcript: &mut Transcript,
values: &[u64],
blindings: &[Scalar],
n: usize,
rng: &mut T,
) -> Result<(RangeProof, Vec<CompressedRistretto>), ProofError> {
use self::dealer::*;
use self::party::*;
if values.len() != blindings.len() {
return Err(ProofError::WrongNumBlindingFactors);
}
let dealer = Dealer::new(bp_gens, pc_gens, transcript, n, values.len())?;
let parties: Vec<_> = values
.iter()
.zip(blindings.iter())
.map(|(&v, &v_blinding)| Party::new(bp_gens, pc_gens, v, v_blinding, n))
.collect::<Result<Vec<_>, _>>()?;
let (parties, bit_commitments): (Vec<_>, Vec<_>) = parties
.into_iter()
.enumerate()
.map(|(j, p)| {
p.assign_position_with_rng(j, rng)
.expect("We already checked the parameters, so this should never happen")
})
.unzip();
let value_commitments: Vec<_> = bit_commitments.iter().map(|c| c.V_j).collect();
let (dealer, bit_challenge) = dealer.receive_bit_commitments(bit_commitments)?;
let (parties, poly_commitments): (Vec<_>, Vec<_>) = parties
.into_iter()
.map(|p| p.apply_challenge_with_rng(&bit_challenge, rng))
.unzip();
let (dealer, poly_challenge) = dealer.receive_poly_commitments(poly_commitments)?;
let proof_shares: Vec<_> = parties
.into_iter()
.map(|p| p.apply_challenge(&poly_challenge))
.collect::<Result<Vec<_>, _>>()?;
let proof = dealer.receive_trusted_shares(&proof_shares)?;
Ok((proof, value_commitments))
}
#[cfg(feature = "std")]
pub fn prove_multiple(
bp_gens: &BulletproofGens,
pc_gens: &PedersenGens,
transcript: &mut Transcript,
values: &[u64],
blindings: &[Scalar],
n: usize,
) -> Result<(RangeProof, Vec<CompressedRistretto>), ProofError> {
RangeProof::prove_multiple_with_rng(
bp_gens,
pc_gens,
transcript,
values,
blindings,
n,
&mut thread_rng(),
)
}
pub fn verify_single_with_rng<T: RngCore + CryptoRng>(
&self,
bp_gens: &BulletproofGens,
pc_gens: &PedersenGens,
transcript: &mut Transcript,
V: &CompressedRistretto,
n: usize,
rng: &mut T,
) -> Result<(), ProofError> {
self.verify_multiple_with_rng(bp_gens, pc_gens, transcript, &[*V], n, rng)
}
#[cfg(feature = "std")]
pub fn verify_single(
&self,
bp_gens: &BulletproofGens,
pc_gens: &PedersenGens,
transcript: &mut Transcript,
V: &CompressedRistretto,
n: usize,
) -> Result<(), ProofError> {
self.verify_single_with_rng(bp_gens, pc_gens, transcript, V, n, &mut thread_rng())
}
pub fn verify_multiple_with_rng<T: RngCore + CryptoRng>(
&self,
bp_gens: &BulletproofGens,
pc_gens: &PedersenGens,
transcript: &mut Transcript,
value_commitments: &[CompressedRistretto],
n: usize,
rng: &mut T,
) -> Result<(), ProofError> {
let m = value_commitments.len();
if !(n == 8 || n == 16 || n == 32 || n == 64) {
return Err(ProofError::InvalidBitsize);
}
if bp_gens.gens_capacity < n {
return Err(ProofError::InvalidGeneratorsLength);
}
if bp_gens.party_capacity < m {
return Err(ProofError::InvalidGeneratorsLength);
}
transcript.rangeproof_domain_sep(n as u64, m as u64);
for V in value_commitments.iter() {
transcript.append_point(b"V", V);
}
transcript.validate_and_append_point(b"A", &self.A)?;
transcript.validate_and_append_point(b"S", &self.S)?;
let y = transcript.challenge_scalar(b"y");
let z = transcript.challenge_scalar(b"z");
let zz = z * z;
let minus_z = -z;
transcript.validate_and_append_point(b"T_1", &self.T_1)?;
transcript.validate_and_append_point(b"T_2", &self.T_2)?;
let x = transcript.challenge_scalar(b"x");
transcript.append_scalar(b"t_x", &self.t_x);
transcript.append_scalar(b"t_x_blinding", &self.t_x_blinding);
transcript.append_scalar(b"e_blinding", &self.e_blinding);
let w = transcript.challenge_scalar(b"w");
let c = Scalar::random(rng);
let (x_sq, x_inv_sq, s) = self.ipp_proof.verification_scalars(n * m, transcript)?;
let s_inv = s.iter().rev();
let a = self.ipp_proof.a;
let b = self.ipp_proof.b;
let powers_of_2: Vec<Scalar> = util::exp_iter(Scalar::from(2u64)).take(n).collect();
let concat_z_and_2: Vec<Scalar> = util::exp_iter(z)
.take(m)
.flat_map(|exp_z| powers_of_2.iter().map(move |exp_2| exp_2 * exp_z))
.collect();
let g = s.iter().map(|s_i| minus_z - a * s_i);
let h = s_inv
.zip(util::exp_iter(y.invert()))
.zip(concat_z_and_2.iter())
.map(|((s_i_inv, exp_y_inv), z_and_2)| z + exp_y_inv * (zz * z_and_2 - b * s_i_inv));
let value_commitment_scalars = util::exp_iter(z).take(m).map(|z_exp| c * zz * z_exp);
let basepoint_scalar = w * (self.t_x - a * b) + c * (delta(n, m, &y, &z) - self.t_x);
let mega_check = RistrettoPoint::optional_multiscalar_mul(
iter::once(Scalar::one())
.chain(iter::once(x))
.chain(iter::once(c * x))
.chain(iter::once(c * x * x))
.chain(x_sq.iter().cloned())
.chain(x_inv_sq.iter().cloned())
.chain(iter::once(-self.e_blinding - c * self.t_x_blinding))
.chain(iter::once(basepoint_scalar))
.chain(g)
.chain(h)
.chain(value_commitment_scalars),
iter::once(self.A.decompress())
.chain(iter::once(self.S.decompress()))
.chain(iter::once(self.T_1.decompress()))
.chain(iter::once(self.T_2.decompress()))
.chain(self.ipp_proof.L_vec.iter().map(|L| L.decompress()))
.chain(self.ipp_proof.R_vec.iter().map(|R| R.decompress()))
.chain(iter::once(Some(pc_gens.B_blinding)))
.chain(iter::once(Some(pc_gens.B)))
.chain(bp_gens.G(n, m).map(|&x| Some(x)))
.chain(bp_gens.H(n, m).map(|&x| Some(x)))
.chain(value_commitments.iter().map(|V| V.decompress())),
)
.ok_or_else(|| ProofError::VerificationError)?;
if mega_check.is_identity() {
Ok(())
} else {
Err(ProofError::VerificationError)
}
}
#[cfg(feature = "std")]
pub fn verify_multiple(
&self,
bp_gens: &BulletproofGens,
pc_gens: &PedersenGens,
transcript: &mut Transcript,
value_commitments: &[CompressedRistretto],
n: usize,
) -> Result<(), ProofError> {
self.verify_multiple_with_rng(
bp_gens,
pc_gens,
transcript,
value_commitments,
n,
&mut thread_rng(),
)
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(7 * 32 + self.ipp_proof.serialized_size());
buf.extend_from_slice(self.A.as_bytes());
buf.extend_from_slice(self.S.as_bytes());
buf.extend_from_slice(self.T_1.as_bytes());
buf.extend_from_slice(self.T_2.as_bytes());
buf.extend_from_slice(self.t_x.as_bytes());
buf.extend_from_slice(self.t_x_blinding.as_bytes());
buf.extend_from_slice(self.e_blinding.as_bytes());
buf.extend(self.ipp_proof.to_bytes_iter());
buf
}
pub fn from_bytes(slice: &[u8]) -> Result<RangeProof, ProofError> {
if slice.len() % 32 != 0 {
return Err(ProofError::FormatError);
}
if slice.len() < 7 * 32 {
return Err(ProofError::FormatError);
}
use util::read32;
let A = CompressedRistretto(read32(&slice[0 * 32..]));
let S = CompressedRistretto(read32(&slice[1 * 32..]));
let T_1 = CompressedRistretto(read32(&slice[2 * 32..]));
let T_2 = CompressedRistretto(read32(&slice[3 * 32..]));
let t_x = Scalar::from_canonical_bytes(read32(&slice[4 * 32..]))
.ok_or(ProofError::FormatError)?;
let t_x_blinding = Scalar::from_canonical_bytes(read32(&slice[5 * 32..]))
.ok_or(ProofError::FormatError)?;
let e_blinding = Scalar::from_canonical_bytes(read32(&slice[6 * 32..]))
.ok_or(ProofError::FormatError)?;
let ipp_proof = InnerProductProof::from_bytes(&slice[7 * 32..])?;
Ok(RangeProof {
A,
S,
T_1,
T_2,
t_x,
t_x_blinding,
e_blinding,
ipp_proof,
})
}
}
impl Serialize for RangeProof {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&self.to_bytes()[..])
}
}
impl<'de> Deserialize<'de> for RangeProof {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct RangeProofVisitor;
impl<'de> Visitor<'de> for RangeProofVisitor {
type Value = RangeProof;
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
formatter.write_str("a valid RangeProof")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<RangeProof, E>
where
E: serde::de::Error,
{
RangeProof::from_bytes(v).map_err(serde::de::Error::custom)
}
}
deserializer.deserialize_bytes(RangeProofVisitor)
}
}
fn delta(n: usize, m: usize, y: &Scalar, z: &Scalar) -> Scalar {
let sum_y = util::sum_of_powers(y, n * m);
let sum_2 = util::sum_of_powers(&Scalar::from(2u64), n);
let sum_z = util::sum_of_powers(z, m);
(z - z * z) * sum_y - z * z * z * sum_2 * sum_z
}
#[cfg(test)]
mod tests {
use super::*;
use generators::PedersenGens;
#[test]
fn test_delta() {
let mut rng = rand::thread_rng();
let y = Scalar::random(&mut rng);
let z = Scalar::random(&mut rng);
let n = 256;
let z2 = z * z;
let z3 = z2 * z;
let mut power_g = Scalar::zero();
let mut exp_y = Scalar::one();
let mut exp_2 = Scalar::one();
for _ in 0..n {
power_g += (z - z2) * exp_y - z3 * exp_2;
exp_y = exp_y * y;
exp_2 = exp_2 + exp_2;
}
assert_eq!(power_g, delta(n, 1, &y, &z),);
}
fn singleparty_create_and_verify_helper(n: usize, m: usize) {
let max_bitsize = 64;
let max_parties = 8;
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(max_bitsize, max_parties);
let (proof_bytes, value_commitments) = {
use self::rand::Rng;
let mut rng = rand::thread_rng();
let (min, max) = (0u64, ((1u128 << n) - 1) as u64);
let values: Vec<u64> = (0..m).map(|_| rng.gen_range(min, max)).collect();
let blindings: Vec<Scalar> = (0..m).map(|_| Scalar::random(&mut rng)).collect();
let mut transcript = Transcript::new(b"AggregatedRangeProofTest");
let (proof, value_commitments) = RangeProof::prove_multiple(
&bp_gens,
&pc_gens,
&mut transcript,
&values,
&blindings,
n,
)
.unwrap();
(bincode::serialize(&proof).unwrap(), value_commitments)
};
{
let proof: RangeProof = bincode::deserialize(&proof_bytes).unwrap();
let mut transcript = Transcript::new(b"AggregatedRangeProofTest");
assert!(proof
.verify_multiple(&bp_gens, &pc_gens, &mut transcript, &value_commitments, n)
.is_ok());
}
}
#[test]
fn create_and_verify_n_32_m_1() {
singleparty_create_and_verify_helper(32, 1);
}
#[test]
fn create_and_verify_n_32_m_2() {
singleparty_create_and_verify_helper(32, 2);
}
#[test]
fn create_and_verify_n_32_m_4() {
singleparty_create_and_verify_helper(32, 4);
}
#[test]
fn create_and_verify_n_32_m_8() {
singleparty_create_and_verify_helper(32, 8);
}
#[test]
fn create_and_verify_n_64_m_1() {
singleparty_create_and_verify_helper(64, 1);
}
#[test]
fn create_and_verify_n_64_m_2() {
singleparty_create_and_verify_helper(64, 2);
}
#[test]
fn create_and_verify_n_64_m_4() {
singleparty_create_and_verify_helper(64, 4);
}
#[test]
fn create_and_verify_n_64_m_8() {
singleparty_create_and_verify_helper(64, 8);
}
#[test]
fn detect_dishonest_party_during_aggregation() {
use self::dealer::*;
use self::party::*;
use errors::MPCError;
let m = 4;
let n = 32;
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(n, m);
use self::rand::Rng;
let mut rng = rand::thread_rng();
let mut transcript = Transcript::new(b"AggregatedRangeProofTest");
let v0 = rng.gen::<u32>() as u64;
let v0_blinding = Scalar::random(&mut rng);
let party0 = Party::new(&bp_gens, &pc_gens, v0, v0_blinding, n).unwrap();
let v2 = rng.gen::<u32>() as u64;
let v2_blinding = Scalar::random(&mut rng);
let party2 = Party::new(&bp_gens, &pc_gens, v2, v2_blinding, n).unwrap();
let v1 = rng.gen::<u64>();
let v1_blinding = Scalar::random(&mut rng);
let party1 = Party::new(&bp_gens, &pc_gens, v1, v1_blinding, n).unwrap();
let v3 = rng.gen::<u64>();
let v3_blinding = Scalar::random(&mut rng);
let party3 = Party::new(&bp_gens, &pc_gens, v3, v3_blinding, n).unwrap();
let dealer = Dealer::new(&bp_gens, &pc_gens, &mut transcript, n, m).unwrap();
let (party0, bit_com0) = party0.assign_position(0).unwrap();
let (party1, bit_com1) = party1.assign_position(1).unwrap();
let (party2, bit_com2) = party2.assign_position(2).unwrap();
let (party3, bit_com3) = party3.assign_position(3).unwrap();
let (dealer, bit_challenge) = dealer
.receive_bit_commitments(vec![bit_com0, bit_com1, bit_com2, bit_com3])
.unwrap();
let (party0, poly_com0) = party0.apply_challenge(&bit_challenge);
let (party1, poly_com1) = party1.apply_challenge(&bit_challenge);
let (party2, poly_com2) = party2.apply_challenge(&bit_challenge);
let (party3, poly_com3) = party3.apply_challenge(&bit_challenge);
let (dealer, poly_challenge) = dealer
.receive_poly_commitments(vec![poly_com0, poly_com1, poly_com2, poly_com3])
.unwrap();
let share0 = party0.apply_challenge(&poly_challenge).unwrap();
let share1 = party1.apply_challenge(&poly_challenge).unwrap();
let share2 = party2.apply_challenge(&poly_challenge).unwrap();
let share3 = party3.apply_challenge(&poly_challenge).unwrap();
match dealer.receive_shares(&[share0, share1, share2, share3]) {
Err(MPCError::MalformedProofShares { bad_shares }) => {
assert_eq!(bad_shares, vec![1, 3]);
}
Err(_) => {
panic!("Got wrong error type from malformed shares");
}
Ok(_) => {
panic!("The proof was malformed, but it was not detected");
}
}
}
#[test]
fn detect_dishonest_dealer_during_aggregation() {
use self::dealer::*;
use self::party::*;
use errors::MPCError;
let m = 1;
let n = 32;
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(n, m);
use self::rand::Rng;
let mut rng = rand::thread_rng();
let mut transcript = Transcript::new(b"AggregatedRangeProofTest");
let v0 = rng.gen::<u32>() as u64;
let v0_blinding = Scalar::random(&mut rng);
let party0 = Party::new(&bp_gens, &pc_gens, v0, v0_blinding, n).unwrap();
let dealer = Dealer::new(&bp_gens, &pc_gens, &mut transcript, n, m).unwrap();
let (party0, bit_com0) = party0.assign_position(0).unwrap();
let (dealer, bit_challenge) = dealer.receive_bit_commitments(vec![bit_com0]).unwrap();
let (party0, poly_com0) = party0.apply_challenge(&bit_challenge);
let (_dealer, mut poly_challenge) =
dealer.receive_poly_commitments(vec![poly_com0]).unwrap();
poly_challenge.x = Scalar::zero();
let maybe_share0 = party0.apply_challenge(&poly_challenge);
assert!(maybe_share0.unwrap_err() == MPCError::MaliciousDealer);
}
}