Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use precomputation on (most) fixed generators #19

Merged
merged 1 commit into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ curve25519-dalek = { package="tari-curve25519-dalek", version = "4.0.2", default
derive_more = "0.99.17"
derivative = "2.2.0"
digest = { version = "0.9.0", default-features = false }
itertools = "0.6.0"
lazy_static = "1.4.0"
merlin = { version = "2", default-features = false }
rand = "0.7"
Expand Down
91 changes: 42 additions & 49 deletions benches/range_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
#[macro_use]
extern crate criterion;

use std::convert::TryInto;

use criterion::{Criterion, SamplingMode};
use curve25519_dalek::scalar::Scalar;
use rand::{self, Rng};
Expand Down Expand Up @@ -197,74 +195,69 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
#[allow(clippy::cast_possible_truncation)]
let (value_min, value_max) = (0u64, (1u128 << (bit_length - 1)) as u64);

let max_range_proofs = BATCHED_SIZES
.to_vec()
.iter()
.fold(u32::MIN, |a, &b| a.max(b.try_into().unwrap()));
// 0. Batch data
let mut statements = vec![];
let mut proofs = vec![];
let pc_gens = ristretto::create_pedersen_gens_with_extension_degree(extension_degree);

// 1. Generators
let generators = RangeParameters::init(bit_length, 1, pc_gens).unwrap();

let mut rng = rand::thread_rng();
for _ in 0..max_range_proofs {
// 2. Create witness data
let mut openings = vec![];
let value = rng.gen_range(value_min, value_max);
let blindings = vec![Scalar::random_not_zero(&mut rng); extension_degree as usize];
openings.push(CommitmentOpening::new(value, blindings.clone()));
let witness = RangeWitness::init(openings).unwrap();

// 3. Generate the statement
let seed_nonce = Some(Scalar::random_not_zero(&mut rng));
let statement = RangeStatement::init(
generators.clone(),
vec![generators
.pc_gens()
.commit(&Scalar::from(value), blindings.as_slice())
.unwrap()],
vec![Some(value / 3)],
seed_nonce,
)
.unwrap();
statements.push(statement.clone());

// 4. Create the proof
let proof = RistrettoRangeProof::prove(transcript_label, &statement, &witness).unwrap();
proofs.push(proof);
}

for extract_masks in EXTRACT_MASKS {
for number_of_range_proofs in BATCHED_SIZES {
let label = format!(
"Batched {}-bit BP+ verify {} deg {:?} masks {:?}",
bit_length, number_of_range_proofs, extension_degree, extract_masks
);
let statements = &statements[0..number_of_range_proofs];
let proofs = &proofs[0..number_of_range_proofs];

// Generators
let pc_gens = ristretto::create_pedersen_gens_with_extension_degree(extension_degree);
let generators = RangeParameters::init(bit_length, 1, pc_gens).unwrap();

let mut rng = rand::thread_rng();

group.bench_function(&label, move |b| {
// Batch data
let mut statements = vec![];
let mut proofs = vec![];

for _ in 0..number_of_range_proofs {
// Witness data
let mut openings = vec![];
let value = rng.gen_range(value_min, value_max);
let blindings = vec![Scalar::random_not_zero(&mut rng); extension_degree as usize];
openings.push(CommitmentOpening::new(value, blindings.clone()));
let witness = RangeWitness::init(openings).unwrap();

// Statement data
let seed_nonce = Some(Scalar::random_not_zero(&mut rng));
let statement = RangeStatement::init(
generators.clone(),
vec![generators
.pc_gens()
.commit(&Scalar::from(value), blindings.as_slice())
.unwrap()],
vec![Some(value / 3)],
seed_nonce,
)
.unwrap();
statements.push(statement.clone());

// Proof
let proof = RistrettoRangeProof::prove(transcript_label, &statement, &witness).unwrap();
proofs.push(proof);
}

// Benchmark this code
b.iter(|| {
// 5. Verify the entire batch of single proofs
// Verify the entire batch of proofs
match extract_masks {
VerifyAction::VerifyOnly => {
let _masks = RangeProof::verify_batch(
transcript_label,
statements,
proofs,
&statements,
&proofs,
VerifyAction::VerifyOnly,
)
.unwrap();
},
VerifyAction::RecoverOnly => {
let _masks = RangeProof::verify_batch(
transcript_label,
statements,
proofs,
&statements,
&proofs,
VerifyAction::RecoverOnly,
)
.unwrap();
Expand Down
90 changes: 54 additions & 36 deletions src/generators/bulletproof_gens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,19 @@
// Copyright (c) 2018 Chain, Inc.
// SPDX-License-Identifier: MIT

use crate::{generators::aggregated_gens_iter::AggregatedGensIter, traits::FromUniformBytes};
use std::{
fmt::{Debug, Formatter},
sync::Arc,
};

use byteorder::{ByteOrder, LittleEndian};
use curve25519_dalek::traits::VartimePrecomputedMultiscalarMul;
use itertools::Itertools;

use crate::{
generators::{aggregated_gens_iter::AggregatedGensIter, generators_chain::GeneratorsChain},
traits::{Compressable, FromUniformBytes, Precomputable},
};

/// The `BulletproofGens` struct contains all the generators needed for aggregating up to `m` range proofs of up to `n`
/// bits each.
Expand All @@ -25,8 +37,8 @@ use crate::{generators::aggregated_gens_iter::AggregatedGensIter, traits::FromUn
/// This construction is also forward-compatible with constraint system proofs, which use a much larger slice of the
/// generator chain, and even forward-compatible to multiparty aggregation of constraint system proofs, since the
/// generators are namespaced by their party index.
#[derive(Clone, Debug)]
pub struct BulletproofGens<P> {
#[derive(Clone)]
pub struct BulletproofGens<P: Precomputable> {
/// The maximum number of usable generators for each party.
pub gens_capacity: usize,
/// Number of values or parties
Expand All @@ -35,9 +47,11 @@ pub struct BulletproofGens<P> {
pub(crate) g_vec: Vec<Vec<P>>,
/// Precomputed \\(\mathbf H\\) generators for each party.
pub(crate) h_vec: Vec<Vec<P>>,
/// Interleaved precomputed generators
pub(crate) precomp: Arc<P::Precomputation>,
}

impl<P: FromUniformBytes> BulletproofGens<P> {
impl<P: FromUniformBytes + Precomputable> BulletproofGens<P> {
/// Create a new `BulletproofGens` object.
///
/// # Inputs
Expand All @@ -48,46 +62,35 @@ impl<P: FromUniformBytes> BulletproofGens<P> {
///
/// * `party_capacity` is the maximum number of parties that can produce an aggregated proof.
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
}

/// Increases the generators' capacity to the amount specified. If less than or equal to the current capacity,
/// does nothing.
pub fn increase_capacity(&mut self, new_capacity: usize) {
use byteorder::{ByteOrder, LittleEndian};

use crate::generators::generators_chain::GeneratorsChain;

if self.gens_capacity >= new_capacity {
return;
}
let mut g_vec: Vec<Vec<P>> = (0..party_capacity).map(|_| Vec::new()).collect();
let mut h_vec: Vec<Vec<P>> = (0..party_capacity).map(|_| Vec::new()).collect();

for i in 0..self.party_capacity {
// Generate the points
for i in 0..party_capacity {
#[allow(clippy::cast_possible_truncation)]
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),
);
g_vec[i].extend(&mut GeneratorsChain::<P>::new(&label).take(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),
);
h_vec[i].extend(&mut GeneratorsChain::<P>::new(&label).take(gens_capacity));
}

// Generate a flattened interleaved iterator for the precomputation tables
let iter_g_vec = g_vec.iter().flat_map(move |g_j| g_j.iter());
let iter_h_vec = h_vec.iter().flat_map(move |h_j| h_j.iter());
let iter_interleaved = iter_g_vec.interleave(iter_h_vec);
let precomp = Arc::new(P::Precomputation::new(iter_interleaved));

BulletproofGens {
gens_capacity,
party_capacity,
g_vec,
h_vec,
precomp,
}
self.gens_capacity = new_capacity;
}

/// Return an iterator over the aggregation of the parties' G generators with given size `n`.
Expand All @@ -112,3 +115,18 @@ impl<P: FromUniformBytes> BulletproofGens<P> {
}
}
}

impl<P> Debug for BulletproofGens<P>
where
P: Compressable + Debug + Precomputable,
P::Compressed: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RangeParameters")
.field("gens_capacity", &self.gens_capacity)
.field("party_capacity", &self.party_capacity)
.field("g_vec", &self.g_vec)
.field("h_vec", &self.h_vec)
.finish()
}
}
9 changes: 0 additions & 9 deletions src/generators/generators_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,6 @@ impl<P> GeneratorsChain<P> {
_phantom: PhantomData,
}
}

/// Advances the reader n times, squeezing and discarding the result
pub(crate) fn fast_forward(mut self, n: usize) -> Self {
let mut buf = [0u8; 64];
for _ in 0..n {
self.reader.read(&mut buf);
}
self
}
}

impl<P> Default for GeneratorsChain<P> {
Expand Down
23 changes: 0 additions & 23 deletions src/generators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,27 +61,4 @@ mod tests {
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_iter(n, m).copied().collect();
let gens_h: Vec<RistrettoPoint> = gens.h_iter(n, m).copied().collect();

let resized_g: Vec<RistrettoPoint> = gen_resized.g_iter(n, m).copied().collect();
let resized_h: Vec<RistrettoPoint> = gen_resized.h_iter(n, m).copied().collect();

assert_eq!(gens_g, resized_g);
assert_eq!(gens_h, resized_h);
};

helper(64, 8);
helper(32, 8);
helper(16, 8);
}
}
26 changes: 13 additions & 13 deletions src/range_parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

//! Bulletproofs+ range parameters (generators and base points) needed for a batch of range proofs

use std::fmt::{Debug, Formatter};
use std::{
fmt::{Debug, Formatter},
sync::Arc,
};

use crate::{
errors::ProofError,
Expand All @@ -12,20 +15,20 @@ use crate::{
pedersen_gens::{ExtensionDegree, PedersenGens},
},
range_proof::MAX_RANGE_PROOF_BIT_LENGTH,
traits::{Compressable, FromUniformBytes},
traits::{Compressable, FromUniformBytes, Precomputable},
};

/// Contains all the generators and base points needed for a batch of range proofs
#[derive(Clone)]
pub struct RangeParameters<P: Compressable> {
pub struct RangeParameters<P: Compressable + Precomputable> {
/// Generators needed for aggregating up to `m` range proofs of up to `n` bits each.
bp_gens: BulletproofGens<P>,
/// The pair of base points for Pedersen commitments.
pc_gens: PedersenGens<P>,
}

impl<P> RangeParameters<P>
where P: FromUniformBytes + Compressable + Clone
where P: FromUniformBytes + Compressable + Clone + Precomputable
{
/// Initialize a new 'RangeParameters' with sanity checks
pub fn init(bit_length: usize, aggregation_factor: usize, pc_gens: PedersenGens<P>) -> Result<Self, ProofError> {
Expand Down Expand Up @@ -107,11 +110,6 @@ where P: FromUniformBytes + Compressable + Clone
self.hi_base_iter().collect()
}

/// Return the non-public value bulletproof generator references
pub fn hi_base_copied(&self) -> Vec<P> {
self.hi_base_iter().cloned().collect()
}

/// Return the non-public mask iterator to the bulletproof generators
pub fn gi_base_iter(&self) -> impl Iterator<Item = &P> {
self.bp_gens.g_iter(self.bit_length(), self.aggregation_factor())
Expand All @@ -122,15 +120,17 @@ where P: FromUniformBytes + Compressable + Clone
self.gi_base_iter().collect()
}

/// Return the non-public mask bulletproof generators
pub fn gi_base_copied(&self) -> Vec<P> {
self.gi_base_iter().cloned().collect()
/// Return the interleaved precomputation tables
pub fn precomp(&self) -> Arc<P::Precomputation> {
// We use shared ownership since precomputation evaluation is an instance method and we don't want to actually
// clone
Arc::clone(&self.bp_gens.precomp)
}
}

impl<P> Debug for RangeParameters<P>
where
P: Compressable + Debug,
P: Compressable + Debug + Precomputable,
P::Compressed: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Expand Down
Loading
Loading