#![allow(non_snake_case)]
#![deny(missing_docs)]
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_COMPRESSED;
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::MultiscalarMul;
use digest::{ExtendableOutput, Input, XofReader};
use sha3::{Sha3XofReader, Sha3_512, Shake256};
#[derive(Copy, Clone)]
pub struct PedersenGens {
pub B: RistrettoPoint,
pub B_blinding: RistrettoPoint,
}
impl PedersenGens {
pub fn commit(&self, value: Scalar, blinding: Scalar) -> RistrettoPoint {
RistrettoPoint::multiscalar_mul(&[value, blinding], &[self.B, self.B_blinding])
}
}
impl Default for PedersenGens {
fn default() -> Self {
PedersenGens {
B: RISTRETTO_BASEPOINT_POINT,
B_blinding: RistrettoPoint::hash_from_bytes::<Sha3_512>(
RISTRETTO_BASEPOINT_COMPRESSED.as_bytes(),
),
}
}
}
struct GeneratorsChain {
reader: Sha3XofReader,
}
impl GeneratorsChain {
fn new(label: &[u8]) -> Self {
let mut shake = Shake256::default();
shake.input(b"GeneratorsChain");
shake.input(label);
GeneratorsChain {
reader: shake.xof_result(),
}
}
fn fast_forward(mut self, n: usize) -> Self {
for _ in 0..n {
let mut buf = [0u8; 64];
self.reader.read(&mut buf);
}
self
}
}
impl Default for GeneratorsChain {
fn default() -> Self {
Self::new(&[])
}
}
impl Iterator for GeneratorsChain {
type Item = RistrettoPoint;
fn next(&mut self) -> Option<Self::Item> {
let mut uniform_bytes = [0u8; 64];
self.reader.read(&mut uniform_bytes);
Some(RistrettoPoint::from_uniform_bytes(&uniform_bytes))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::max_value(), None)
}
}
#[derive(Clone)]
pub struct BulletproofGens {
pub gens_capacity: usize,
pub party_capacity: usize,
G_vec: Vec<Vec<RistrettoPoint>>,
H_vec: Vec<Vec<RistrettoPoint>>,
}
impl BulletproofGens {
pub fn new(gens_capacity: usize, party_capacity: usize) -> Self {
let mut gens = BulletproofGens {
gens_capacity: 0,
party_capacity,
G_vec: (0..party_capacity).map(|_| Vec::new()).collect(),
H_vec: (0..party_capacity).map(|_| Vec::new()).collect(),
};
gens.increase_capacity(gens_capacity);
gens
}
pub fn share(&self, j: usize) -> BulletproofGensShare {
BulletproofGensShare {
gens: &self,
share: j,
}
}
pub fn increase_capacity(&mut self, new_capacity: usize) {
use byteorder::{ByteOrder, LittleEndian};
if self.gens_capacity >= new_capacity {
return;
}
for i in 0..self.party_capacity {
let party_index = i as u32;
let mut label = [b'G', 0, 0, 0, 0];
LittleEndian::write_u32(&mut label[1..5], party_index);
self.G_vec[i].extend(
&mut GeneratorsChain::new(&label)
.fast_forward(self.gens_capacity)
.take(new_capacity - self.gens_capacity),
);
label[0] = b'H';
self.H_vec[i].extend(
&mut GeneratorsChain::new(&label)
.fast_forward(self.gens_capacity)
.take(new_capacity - self.gens_capacity),
);
}
self.gens_capacity = new_capacity;
}
pub(crate) fn G(&self, n: usize, m: usize) -> impl Iterator<Item = &RistrettoPoint> {
AggregatedGensIter {
n,
m,
array: &self.G_vec,
party_idx: 0,
gen_idx: 0,
}
}
pub(crate) fn H(&self, n: usize, m: usize) -> impl Iterator<Item = &RistrettoPoint> {
AggregatedGensIter {
n,
m,
array: &self.H_vec,
party_idx: 0,
gen_idx: 0,
}
}
}
struct AggregatedGensIter<'a> {
array: &'a Vec<Vec<RistrettoPoint>>,
n: usize,
m: usize,
party_idx: usize,
gen_idx: usize,
}
impl<'a> Iterator for AggregatedGensIter<'a> {
type Item = &'a RistrettoPoint;
fn next(&mut self) -> Option<Self::Item> {
if self.gen_idx >= self.n {
self.gen_idx = 0;
self.party_idx += 1;
}
if self.party_idx >= self.m {
None
} else {
let cur_gen = self.gen_idx;
self.gen_idx += 1;
Some(&self.array[self.party_idx][cur_gen])
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.n * self.m;
(size, Some(size))
}
}
#[derive(Copy, Clone)]
pub struct BulletproofGensShare<'a> {
gens: &'a BulletproofGens,
share: usize,
}
impl<'a> BulletproofGensShare<'a> {
pub(crate) fn G(&self, n: usize) -> impl Iterator<Item = &'a RistrettoPoint> {
self.gens.G_vec[self.share].iter().take(n)
}
pub(crate) fn H(&self, n: usize) -> impl Iterator<Item = &'a RistrettoPoint> {
self.gens.H_vec[self.share].iter().take(n)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn aggregated_gens_iter_matches_flat_map() {
let gens = BulletproofGens::new(64, 8);
let helper = |n: usize, m: usize| {
let agg_G: Vec<RistrettoPoint> = gens.G(n, m).cloned().collect();
let flat_G: Vec<RistrettoPoint> = gens
.G_vec
.iter()
.take(m)
.flat_map(move |G_j| G_j.iter().take(n))
.cloned()
.collect();
let agg_H: Vec<RistrettoPoint> = gens.H(n, m).cloned().collect();
let flat_H: Vec<RistrettoPoint> = gens
.H_vec
.iter()
.take(m)
.flat_map(move |H_j| H_j.iter().take(n))
.cloned()
.collect();
assert_eq!(agg_G, flat_G);
assert_eq!(agg_H, flat_H);
};
helper(64, 8);
helper(64, 4);
helper(64, 2);
helper(64, 1);
helper(32, 8);
helper(32, 4);
helper(32, 2);
helper(32, 1);
helper(16, 8);
helper(16, 4);
helper(16, 2);
helper(16, 1);
}
#[test]
fn resizing_small_gens_matches_creating_bigger_gens() {
let gens = BulletproofGens::new(64, 8);
let mut gen_resized = BulletproofGens::new(32, 8);
gen_resized.increase_capacity(64);
let helper = |n: usize, m: usize| {
let gens_G: Vec<RistrettoPoint> = gens.G(n, m).cloned().collect();
let gens_H: Vec<RistrettoPoint> = gens.H(n, m).cloned().collect();
let resized_G: Vec<RistrettoPoint> = gen_resized.G(n, m).cloned().collect();
let resized_H: Vec<RistrettoPoint> = gen_resized.H(n, m).cloned().collect();
assert_eq!(gens_G, resized_G);
assert_eq!(gens_H, resized_H);
};
helper(64, 8);
helper(32, 8);
helper(16, 8);
}
}