Skip to content

Commit

Permalink
feat: support no-std environments (#107)
Browse files Browse the repository at this point in the history
This PR makes the library friendly to `no-std` environments with supporting tests.
  • Loading branch information
hansieodendaal authored Jan 12, 2024
2 parents 33abac4 + c9a03b1 commit bce16c4
Show file tree
Hide file tree
Showing 23 changed files with 212 additions and 106 deletions.
23 changes: 21 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@ on: [push, pull_request]
name: Test

jobs:
wasm:
name: cargo build (WASM)
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
targets: wasm32-unknown-unknown
- name: build (WASM)
run: cargo +nightly build --target wasm32-unknown-unknown --no-default-features -Zavoid-dev-deps

test:
name: cargo test
runs-on: ubuntu-latest
Expand All @@ -18,13 +32,18 @@ jobs:
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: wasm32-unknown-unknown
- name: check (no features)
run: cargo +${{ matrix.rust }} check --no-default-features
- name: check (all features)
run: cargo +${{ matrix.rust }} check --all-features --all-targets
- name: test/debug
- name: test/debug (all features)
run: cargo +${{ matrix.rust }} test --all-features
- name: test/release
- name: test/debug (no features)
run: cargo +${{ matrix.rust }} test --no-default-features
- name: test/release (all features)
run: cargo +${{ matrix.rust }} test --release --all-features
- name: test/release (no features)
run: cargo +${{ matrix.rust }} test --release --no-default-features
- name: Build documentation
run: RUSTDOCFLAGS="-D warnings" cargo +${{ matrix.rust }} doc --no-deps
16 changes: 9 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,28 @@ description = "A smaller faster implementation of Bulletproofs"
[dependencies]
blake2 = { version = "0.10", default-features = false }
byteorder = { version = "1", default-features = false }
curve25519-dalek = { package = "tari-curve25519-dalek", version = "4.0.3", features = ["serde", "rand_core"] }
digest = { version = "0.10", default-features = false }
curve25519-dalek = { package = "tari-curve25519-dalek", version = "4.0.3", default-features = false, features = ["alloc", "rand_core", "serde", "zeroize"] }
digest = { version = "0.10", default-features = false, features = ["alloc"] }
itertools = { version = "0.12", default-features = false, features = ["use_alloc"] }
merlin = { version = "3", default-features = false }
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
rand = { version = "0.8", optional = true }
once_cell = { version = "1", default-features = false, features = ["alloc", "critical-section"] }
rand = { version = "0.8", optional = true, default-features = false }
rand_core = { version = "0.6", default-features = false, features = ["alloc"] }
serde = { version = "1.0", default-features = false, features = ["alloc"] }
sha3 = { version = "0.10", default-features = false }
thiserror-no-std = { version = "2", default-features = false }
zeroize = { version = "1", default-features = false, features = ["alloc", "derive"] }
rand_core = { version = "0.6", default-features = false, features = ["alloc"] }

[dev-dependencies]
bincode = "1"
criterion = "0.5"
quickcheck = "1"
rand_chacha = "0.3.1"

[features]
default = ["rand"]
rand = ["dep:rand"]
default = ["rand", "std"]
std = ["blake2/std", "byteorder/std", "digest/std", "itertools/use_std", "merlin/std", "once_cell/std", "rand?/std", "rand_core/std", "serde/std", "sha3/std", "zeroize/std"]
rand = ["dep:rand", "rand/alloc", "rand/getrandom"]

[[bench]]
name = "range_proof"
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ As always, your mileage may vary.

This library underwent a code audit by [Quarkslab](https://www.quarkslab.com/) at a [specific point](https://github.com/tari-project/bulletproofs-plus/releases/tag/pre-audit-commit) in the repository history. You can read the [report and issue responses](docs/quarkslab-audit/README.md) in this repository.

## Features

The library is `#![no_std]`-friendly when default features are disabled.

The (default) `rand` feature adds prover and verifier functionality using the `OsRng` random number generator.
If it is not enabled, you must supply your own cryptographically-secure random number generator.

The (default) `std` feature enables corresponding functionality in dependencies.

## Testing

Unit tests are available via `cargo test`. Basic fuzz testing can be run (on a nightly toolchain) via `cargo fuzz`.
Expand Down
55 changes: 33 additions & 22 deletions benches/range_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ extern crate criterion;

use criterion::{Criterion, SamplingMode};
use curve25519_dalek::scalar::Scalar;
use rand::{thread_rng, Rng};
use rand_chacha::ChaCha12Rng;
use rand_core::{CryptoRngCore, SeedableRng};
use tari_bulletproofs_plus::{
commitment_opening::CommitmentOpening,
generators::pedersen_gens::ExtensionDegree,
Expand Down Expand Up @@ -41,16 +42,18 @@ fn create_aggregated_rangeproof_helper(bit_length: usize, extension_degree: Exte
let mut group = c.benchmark_group("range_proof_creation");
group.sampling_mode(SamplingMode::Flat);

let mut rng = ChaCha12Rng::seed_from_u64(8675309); // for testing only!

let transcript_label: &'static str = "BatchedRangeProofTest";
#[allow(clippy::cast_possible_truncation)]
let (value_min, value_max) = (0u64, (1u128 << (bit_length - 1)) as u64);
let value_max = (1u128 << (bit_length - 1)) as u64;

for aggregation_factor in AGGREGATION_SIZES {
let label = format!(
"Agg {}-bit BP+ create agg factor {} degree {:?}",
bit_length, aggregation_factor, extension_degree
);
group.bench_function(&label, move |b| {
group.bench_function(&label, |b| {
// 1. Generators
let generators = RangeParameters::init(
bit_length,
Expand All @@ -63,9 +66,8 @@ fn create_aggregated_rangeproof_helper(bit_length: usize, extension_degree: Exte
let mut commitments = vec![];
let mut minimum_values = vec![];
let mut openings = vec![];
let mut rng = thread_rng();
for _ in 0..aggregation_factor {
let value = rng.gen_range(value_min..=value_max);
let value = rng.as_rngcore().next_u64() % value_max; // introduces bias, but that's fine for testing
minimum_values.push(Some(value / 3));
let blindings = vec![Scalar::random_not_zero(&mut rng); extension_degree as usize];
commitments.push(
Expand All @@ -90,7 +92,7 @@ fn create_aggregated_rangeproof_helper(bit_length: usize, extension_degree: Exte
// Benchmark this code
b.iter(|| {
// 4. Create the aggregated proof
let _proof = RistrettoRangeProof::prove(transcript_label, &statement, &witness);
let _proof = RistrettoRangeProof::prove_with_rng(transcript_label, &statement, &witness, &mut rng);
})
});
}
Expand All @@ -113,17 +115,19 @@ fn verify_aggregated_rangeproof_helper(bit_length: usize, extension_degree: Exte
let mut group = c.benchmark_group("range_proof_verification");
group.sampling_mode(SamplingMode::Flat);

let mut rng = ChaCha12Rng::seed_from_u64(8675309); // for testing only!

let transcript_label: &'static str = "BatchedRangeProofTest";
#[allow(clippy::cast_possible_truncation)]
let (value_min, value_max) = (0u64, (1u128 << (bit_length - 1)) as u64);
let value_max = (1u128 << (bit_length - 1)) as u64;

for aggregation_factor in AGGREGATION_SIZES {
let pederson_gens = ristretto::create_pedersen_gens_with_extension_degree(extension_degree);
let label = format!(
"Agg {}-bit BP+ verify agg factor {} degree {:?}",
bit_length, aggregation_factor, extension_degree
);
group.bench_function(&label, move |b| {
group.bench_function(&label, |b| {
// 0. Batch data
let mut statements = vec![];
let mut proofs = vec![];
Expand All @@ -136,9 +140,8 @@ fn verify_aggregated_rangeproof_helper(bit_length: usize, extension_degree: Exte
let mut commitments = vec![];
let mut minimum_values = vec![];
let mut openings = vec![];
let mut rng = thread_rng();
for _ in 0..aggregation_factor {
let value = rng.gen_range(value_min..=value_max);
let value = rng.as_rngcore().next_u64() % value_max; // introduces bias, but that's fine for testing
minimum_values.push(Some(value / 3));
let blindings = vec![Scalar::random_not_zero(&mut rng); extension_degree as usize];
commitments.push(
Expand All @@ -163,15 +166,20 @@ fn verify_aggregated_rangeproof_helper(bit_length: usize, extension_degree: Exte
transcript_labels.push(transcript_label);

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

// Benchmark this code
b.iter(|| {
// 5. Verify the aggregated proof
let _masks =
RangeProof::verify_batch(&transcript_labels, &statements, &proofs, VerifyAction::VerifyOnly)
.unwrap();
let _masks = RangeProof::verify_batch_with_rng(
&transcript_labels,
&statements,
&proofs,
VerifyAction::VerifyOnly,
&mut rng,
)
.unwrap();
});
});
}
Expand All @@ -194,9 +202,11 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
let mut group = c.benchmark_group("batched_range_proof_verification");
group.sampling_mode(SamplingMode::Flat);

let mut rng = ChaCha12Rng::seed_from_u64(8675309); // for testing only!

let transcript_label: &'static str = "BatchedRangeProofTest";
#[allow(clippy::cast_possible_truncation)]
let (value_min, value_max) = (0u64, (1u128 << (bit_length - 1)) as u64);
let value_max = (1u128 << (bit_length - 1)) as u64;

for extract_masks in EXTRACT_MASKS {
for number_of_range_proofs in BATCHED_SIZES {
Expand All @@ -209,9 +219,7 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
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 = thread_rng();

group.bench_function(&label, move |b| {
group.bench_function(&label, |b| {
// Batch data
let mut statements = vec![];
let mut proofs = vec![];
Expand All @@ -220,7 +228,7 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
for _ in 0..number_of_range_proofs {
// Witness data
let mut openings = vec![];
let value = rng.gen_range(value_min..=value_max);
let value = rng.as_rngcore().next_u64() % value_max; // introduces bias, but that's fine for testing
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();
Expand All @@ -241,7 +249,8 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
transcript_labels.push(transcript_label);

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

Expand All @@ -250,20 +259,22 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
// Verify the entire batch of proofs
match extract_masks {
VerifyAction::VerifyOnly => {
let _masks = RangeProof::verify_batch(
let _masks = RangeProof::verify_batch_with_rng(
&transcript_labels,
&statements,
&proofs,
VerifyAction::VerifyOnly,
&mut rng,
)
.unwrap();
},
VerifyAction::RecoverOnly => {
let _masks = RangeProof::verify_batch(
let _masks = RangeProof::verify_batch_with_rng(
&transcript_labels,
&statements,
&proofs,
VerifyAction::RecoverOnly,
&mut rng,
)
.unwrap();
},
Expand Down
2 changes: 2 additions & 0 deletions src/commitment_opening.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

//! Bulletproofs+ commitment opening struct

use alloc::{string::ToString, vec::Vec};

use curve25519_dalek::scalar::Scalar;
use zeroize::{Zeroize, ZeroizeOnDrop};

Expand Down
2 changes: 2 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

//! Bulletproofs+ error definitions

use alloc::string::String;

use thiserror_no_std::Error;

/// Represents an error in proof creation, verification, or parsing.
Expand Down
5 changes: 4 additions & 1 deletion src/extended_mask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

//! Bulletproofs+ embedded extended mask

use alloc::{string::ToString, vec::Vec};

use curve25519_dalek::scalar::Scalar;
use zeroize::{Zeroize, ZeroizeOnDrop};

Expand Down Expand Up @@ -40,7 +42,8 @@ impl ExtendedMask {

#[cfg(test)]
mod test {
use std::convert::TryFrom;
use alloc::vec;
use core::convert::TryFrom;

use super::*;

Expand Down
2 changes: 2 additions & 0 deletions src/generators/aggregated_gens_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// Copyright (c) 2018 Chain, Inc.
// SPDX-License-Identifier: MIT

use alloc::vec::Vec;

/// A convenience iterator struct for the generators
pub struct AggregatedGensIter<'a, P> {
pub(super) array: &'a Vec<Vec<P>>,
Expand Down
6 changes: 3 additions & 3 deletions src/generators/bulletproof_gens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
// Copyright (c) 2018 Chain, Inc.
// SPDX-License-Identifier: MIT

use std::{
use alloc::{sync::Arc, vec::Vec};
use core::{
convert::TryFrom,
fmt::{Debug, Formatter},
sync::Arc,
};

use byteorder::{ByteOrder, LittleEndian};
Expand Down Expand Up @@ -139,7 +139,7 @@ where
P: Compressable + Debug + Precomputable,
P::Compressed: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RangeParameters")
.field("gens_capacity", &self.gens_capacity)
.field("party_capacity", &self.party_capacity)
Expand Down
2 changes: 1 addition & 1 deletion src/generators/generators_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Copyright (c) 2018 Chain, Inc.
// SPDX-License-Identifier: MIT

use std::marker::PhantomData;
use core::marker::PhantomData;

use digest::{core_api::XofReaderCoreWrapper, ExtendableOutput, Update, XofReader};
use sha3::{Shake256, Shake256ReaderCore};
Expand Down
2 changes: 2 additions & 0 deletions src/generators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub mod pedersen_gens;

#[cfg(test)]
mod tests {
use alloc::vec::Vec;

use curve25519_dalek::ristretto::RistrettoPoint;

use crate::generators::bulletproof_gens::BulletproofGens;
Expand Down
5 changes: 3 additions & 2 deletions src/generators/pedersen_gens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
// Copyright (c) 2018 Chain, Inc.
// SPDX-License-Identifier: MIT

use std::{borrow::Borrow, convert::TryFrom, iter::once};
use alloc::{string::ToString, vec::Vec};
use core::{borrow::Borrow, convert::TryFrom, iter::once};

use curve25519_dalek::{scalar::Scalar, traits::MultiscalarMul};
use zeroize::Zeroize;
Expand Down Expand Up @@ -124,7 +125,7 @@ where P: Compressable + MultiscalarMul<Point = P> + Clone

#[cfg(test)]
mod test {
use std::convert::TryFrom;
use core::convert::TryFrom;

use super::ExtensionDegree;

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

//! Bulletproofs+

extern crate alloc;

/// Bulletproofs+ commitment opening
pub mod commitment_opening;
/// Bulletproofs+ error definitions
Expand Down
2 changes: 1 addition & 1 deletion src/protocols/curve_point_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

//! Bulletproofs+ `CurvePointProtocol` trait provides the required interface for curves using BP+.

use std::{
use core::{
borrow::Borrow,
ops::{Add, AddAssign},
};
Expand Down
Loading

0 comments on commit bce16c4

Please sign in to comment.