diff --git a/CHANGELOG.md b/CHANGELOG.md index 7edbe28c..36e1d1eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ Entries are listed in reverse chronological order. +## 3.0.0 + +* Update `curve25519-dalek`, `sha3`, `digest` and `merlin` versions. +* Add `scalar_range_proof` feature, which allow `RangeProof` to be used with `Scalar`. +* Allow range proof with bitsize n = 128. +* Use `u128` instead of `u64` in range proof. + ## 2.0.0 * Switch from `failure` to `std`-compatible errors via `thiserror`. diff --git a/Cargo.toml b/Cargo.toml index 07867f8d..9d52c832 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ name = "bulletproofs" # - update html_root_url # - ensure yoloproofs was disabled in an atomic (revertable) commit # - update CHANGELOG -version = "2.0.0" +version = "3.0.0" authors = ["Cathie Yun ", "Henry de Valence ", "Oleg Andreev "] @@ -15,20 +15,20 @@ repository = "https://github.com/dalek-cryptography/bulletproofs" categories = ["cryptography"] keywords = ["cryptography", "crypto", "ristretto", "zero-knowledge", "bulletproofs"] description = "A pure-Rust implementation of Bulletproofs using Ristretto" -edition = "2018" +edition = "2021" [dependencies] -curve25519-dalek = { version = "2", default-features = false, features = ["u64_backend", "nightly", "serde", "alloc"] } +curve25519-dalek = { version = "3.2.0", default-features = false, features = ["u64_backend", "nightly", "serde", "alloc"] } subtle = { version = "2", default-features = false } -sha3 = { version = "0.8", default-features = false } -digest = { version = "0.8", default-features = false } +sha3 = { version = "0.9", default-features = false } +digest = { version = "0.9", default-features = false } rand_core = { version = "0.5", default-features = false, features = ["alloc"] } rand = { version = "0.7", default-features = false, optional = true } byteorder = { version = "1", default-features = false } serde = { version = "1", default-features = false, features = ["alloc"] } serde_derive = { version = "1", default-features = false } thiserror = { version = "1", optional = true } -merlin = { version = "2", default-features = false } +merlin = { version = "3", default-features = false } clear_on_drop = { version = "0.2", default-features = false, features = ["nightly"] } [dev-dependencies] @@ -42,10 +42,15 @@ default = ["std", "avx2_backend"] avx2_backend = ["curve25519-dalek/avx2_backend"] # yoloproofs = [] std = ["rand", "rand/std", "thiserror"] +scalar_range_proof = [] [[test]] name = "range_proof" +[[test]] +name = "scalar_range_proof" +required-features = ["scalar_range_proof"] + [[test]] name = "r1cs" required-features = ["yoloproofs"] @@ -67,3 +72,7 @@ required-features = ["yoloproofs"] name = "linear_proof" harness = false +[[bench]] +name = "scalar_range_proof" +harness = false +required-features = ["scalar_range_proof"] diff --git a/README.md b/README.md index a16fbc8d..806f8312 100644 --- a/README.md +++ b/README.md @@ -112,12 +112,15 @@ The following example shows how to create and verify a 32-bit rangeproof. // independently of the Bulletproofs generators. let pc_gens = PedersenGens::default(); -// Generators for Bulletproofs, valid for proofs up to bitsize 64 +// Generators for Bulletproofs, valid for proofs up to bitsize 128 // and aggregation size up to 1. -let bp_gens = BulletproofGens::new(64, 1); +let bp_gens = BulletproofGens::new(128, 1); // A secret value we want to prove lies in the range [0, 2^32) -let secret_value = 1037578891u64; +# #[cfg(not(feature = "scalar_range_proof"))] +let secret_value = 1037578891u128; +# #[cfg(feature = "scalar_range_proof")] +# let secret_value = &Scalar::from(1037578891u128); // The API takes a blinding factor for the commitment. let blinding = Scalar::random(&mut thread_rng()); diff --git a/benches/range_proof.rs b/benches/range_proof.rs index 5ab246ec..5b0250ca 100644 --- a/benches/range_proof.rs +++ b/benches/range_proof.rs @@ -16,7 +16,10 @@ use bulletproofs::{BulletproofGens, PedersenGens}; static AGGREGATION_SIZES: [usize; 6] = [1, 2, 4, 8, 16, 32]; fn create_aggregated_rangeproof_helper(n: usize, c: &mut Criterion) { - let label = format!("Aggregated {}-bit rangeproof creation", n); + #[cfg(not(feature = "scalar_range_proof"))] + let label = format!("Aggregated {}-bit rangeproof verification", n); + #[cfg(feature = "scalar_range_proof")] + let label = format!("Aggregated {}-bit rangeproof verification using Scalar", n); c.bench_function_over_inputs( &label, @@ -25,8 +28,13 @@ fn create_aggregated_rangeproof_helper(n: usize, c: &mut Criterion) { let bp_gens = BulletproofGens::new(n, m); let mut rng = rand::thread_rng(); - let (min, max) = (0u64, ((1u128 << n) - 1) as u64); - let values: Vec = (0..m).map(|_| rng.gen_range(min, max)).collect(); + let (min, max) = (0u128, u128::MAX >> (u128::BITS as usize - n)); + + #[cfg(not(feature = "scalar_range_proof"))] + let values: Vec = (0..m).map(|_| rng.gen_range(min, max)).collect(); + #[cfg(feature = "scalar_range_proof")] + let values: Vec = (0..m).map(|_| rng.gen_range(min, max).into()).collect(); + let blindings: Vec = (0..m).map(|_| Scalar::random(&mut rng)).collect(); b.iter(|| { @@ -63,8 +71,15 @@ fn create_aggregated_rangeproof_n_64(c: &mut Criterion) { create_aggregated_rangeproof_helper(64, c); } +fn create_aggregated_rangeproof_n_128(c: &mut Criterion) { + create_aggregated_rangeproof_helper(128, c); +} + fn verify_aggregated_rangeproof_helper(n: usize, c: &mut Criterion) { + #[cfg(not(feature = "scalar_range_proof"))] let label = format!("Aggregated {}-bit rangeproof verification", n); + #[cfg(feature = "scalar_range_proof")] + let label = format!("Aggregated {}-bit rangeproof verification using Scalar", n); c.bench_function_over_inputs( &label, @@ -73,8 +88,13 @@ fn verify_aggregated_rangeproof_helper(n: usize, c: &mut Criterion) { let bp_gens = BulletproofGens::new(n, m); let mut rng = rand::thread_rng(); - let (min, max) = (0u64, ((1u128 << n) - 1) as u64); - let values: Vec = (0..m).map(|_| rng.gen_range(min, max)).collect(); + let (min, max) = (0u128, u128::MAX >> (u128::BITS as usize - n)); + + #[cfg(not(feature = "scalar_range_proof"))] + let values: Vec = (0..m).map(|_| rng.gen_range(min, max)).collect(); + #[cfg(feature = "scalar_range_proof")] + let values: Vec = (0..m).map(|_| rng.gen_range(min, max).into()).collect(); + let blindings: Vec = (0..m).map(|_| Scalar::random(&mut rng)).collect(); let mut transcript = Transcript::new(b"AggregateRangeProofBenchmark"); @@ -115,6 +135,10 @@ fn verify_aggregated_rangeproof_n_64(c: &mut Criterion) { verify_aggregated_rangeproof_helper(64, c); } +fn verify_aggregated_rangeproof_n_128(c: &mut Criterion) { + verify_aggregated_rangeproof_helper(128, c); +} + criterion_group! { name = create_rp; config = Criterion::default().sample_size(10); @@ -123,6 +147,7 @@ criterion_group! { create_aggregated_rangeproof_n_16, create_aggregated_rangeproof_n_32, create_aggregated_rangeproof_n_64, + create_aggregated_rangeproof_n_128, } criterion_group! { @@ -133,6 +158,7 @@ criterion_group! { verify_aggregated_rangeproof_n_16, verify_aggregated_rangeproof_n_32, verify_aggregated_rangeproof_n_64, + verify_aggregated_rangeproof_n_128, } criterion_main!(create_rp, verify_rp); diff --git a/benches/scalar_range_proof.rs b/benches/scalar_range_proof.rs new file mode 100644 index 00000000..3580a043 --- /dev/null +++ b/benches/scalar_range_proof.rs @@ -0,0 +1,148 @@ +#![allow(non_snake_case)] +#[macro_use] +extern crate criterion; +use criterion::Criterion; + +use rand; +use rand::Rng; + +use curve25519_dalek::scalar::Scalar; + +use merlin::Transcript; + +use bulletproofs::RangeProof; +use bulletproofs::{BulletproofGens, PedersenGens}; + +static AGGREGATION_SIZES: [usize; 6] = [1, 2, 4, 8, 16, 32]; + +fn create_aggregated_rangeproof_helper(n: usize, c: &mut Criterion) { + let label = format!("Aggregated {}-bit rangeproof verification using Scalar", n); + + c.bench_function_over_inputs( + &label, + move |b, &&m| { + let pc_gens = PedersenGens::default(); + let bp_gens = BulletproofGens::new(n, m); + let mut rng = rand::thread_rng(); + + let (min, max) = (0u128, u128::MAX >> (u128::BITS as usize - n)); + let values: Vec = (0..m).map(|_| rng.gen_range(min, max).into()).collect(); + let blindings: Vec = (0..m).map(|_| Scalar::random(&mut rng)).collect(); + + b.iter(|| { + // Each proof creation requires a clean transcript. + let mut transcript = Transcript::new(b"AggregateRangeProofBenchmark"); + + RangeProof::prove_multiple( + &bp_gens, + &pc_gens, + &mut transcript, + &values, + &blindings, + n, + ) + }) + }, + &AGGREGATION_SIZES, + ); +} + +fn create_aggregated_rangeproof_n_8(c: &mut Criterion) { + create_aggregated_rangeproof_helper(8, c); +} + +fn create_aggregated_rangeproof_n_16(c: &mut Criterion) { + create_aggregated_rangeproof_helper(16, c); +} + +fn create_aggregated_rangeproof_n_32(c: &mut Criterion) { + create_aggregated_rangeproof_helper(32, c); +} + +fn create_aggregated_rangeproof_n_64(c: &mut Criterion) { + create_aggregated_rangeproof_helper(64, c); +} + +fn create_aggregated_rangeproof_n_128(c: &mut Criterion) { + create_aggregated_rangeproof_helper(128, c); +} + +fn verify_aggregated_rangeproof_helper(n: usize, c: &mut Criterion) { + let label = format!("Aggregated {}-bit rangeproof verification using Scalar", n); + + c.bench_function_over_inputs( + &label, + move |b, &&m| { + let pc_gens = PedersenGens::default(); + let bp_gens = BulletproofGens::new(n, m); + let mut rng = rand::thread_rng(); + + let (min, max) = (0u128, u128::MAX >> (u128::BITS as usize - n)); + let values: Vec = (0..m).map(|_| rng.gen_range(min, max).into()).collect(); + let blindings: Vec = (0..m).map(|_| Scalar::random(&mut rng)).collect(); + + let mut transcript = Transcript::new(b"AggregateRangeProofBenchmark"); + let (proof, value_commitments) = RangeProof::prove_multiple( + &bp_gens, + &pc_gens, + &mut transcript, + &values, + &blindings, + n, + ) + .unwrap(); + + b.iter(|| { + // Each proof creation requires a clean transcript. + let mut transcript = Transcript::new(b"AggregateRangeProofBenchmark"); + + proof.verify_multiple(&bp_gens, &pc_gens, &mut transcript, &value_commitments, n) + }); + }, + &AGGREGATION_SIZES, + ); +} + +fn verify_aggregated_rangeproof_n_8(c: &mut Criterion) { + verify_aggregated_rangeproof_helper(8, c); +} + +fn verify_aggregated_rangeproof_n_16(c: &mut Criterion) { + verify_aggregated_rangeproof_helper(16, c); +} + +fn verify_aggregated_rangeproof_n_32(c: &mut Criterion) { + verify_aggregated_rangeproof_helper(32, c); +} + +fn verify_aggregated_rangeproof_n_64(c: &mut Criterion) { + verify_aggregated_rangeproof_helper(64, c); +} + +fn verify_aggregated_rangeproof_n_128(c: &mut Criterion) { + verify_aggregated_rangeproof_helper(128, c); +} + +criterion_group! { + name = create_rp; + config = Criterion::default().sample_size(10); + targets = + create_aggregated_rangeproof_n_8, + create_aggregated_rangeproof_n_16, + create_aggregated_rangeproof_n_32, + create_aggregated_rangeproof_n_64, + create_aggregated_rangeproof_n_128, +} + +criterion_group! { + name = verify_rp; + config = Criterion::default(); + targets = + verify_aggregated_rangeproof_n_8, + verify_aggregated_rangeproof_n_16, + verify_aggregated_rangeproof_n_32, + verify_aggregated_rangeproof_n_64, + verify_aggregated_rangeproof_n_128, +} + +criterion_main!(create_rp, verify_rp); diff --git a/rust-toolchain b/rust-toolchain index ed6f0c18..8ee72f48 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2019-07-31 +nightly-2023-01-01 diff --git a/src/errors.rs b/src/errors.rs index 776915d1..18662165 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -21,8 +21,11 @@ pub enum ProofError { #[cfg_attr(feature = "std", error("Wrong number of blinding factors supplied."))] WrongNumBlindingFactors, /// This error occurs when attempting to create a proof with - /// bitsize other than \\(8\\), \\(16\\), \\(32\\), or \\(64\\). - #[cfg_attr(feature = "std", error("Invalid bitsize, must have n = 8,16,32,64."))] + /// bitsize other than \\(8\\), \\(16\\), \\(32\\), \\(64\\) or \\(128\\). + #[cfg_attr( + feature = "std", + error("Invalid bitsize, must have n = 8,16,32,64,128.") + )] InvalidBitsize, /// This error occurs when attempting to create an aggregated /// proof with non-power-of-two aggregation size. @@ -73,8 +76,11 @@ pub enum MPCError { #[cfg_attr(feature = "std", error("Dealer gave a malicious challenge value."))] MaliciousDealer, /// This error occurs when attempting to create a proof with - /// bitsize other than \\(8\\), \\(16\\), \\(32\\), or \\(64\\). - #[cfg_attr(feature = "std", error("Invalid bitsize, must have n = 8,16,32,64"))] + /// bitsize other than \\(8\\), \\(16\\), \\(32\\), \\(64\\) or \\(128\\). + #[cfg_attr( + feature = "std", + error("Invalid bitsize, must have n = 8,16,32,64,128") + )] InvalidBitsize, /// This error occurs when attempting to create an aggregated /// proof with non-power-of-two aggregation size. diff --git a/src/generators.rs b/src/generators.rs index 3759350a..1718e74c 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -12,7 +12,7 @@ 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 digest::{ExtendableOutput, Update, XofReader}; use sha3::{Sha3XofReader, Sha3_512, Shake256}; /// Represents a pair of base points for Pedersen commitments. @@ -63,11 +63,11 @@ impl GeneratorsChain { /// Creates a chain of generators, determined by the hash of `label`. fn new(label: &[u8]) -> Self { let mut shake = Shake256::default(); - shake.input(b"GeneratorsChain"); - shake.input(label); + shake.update(b"GeneratorsChain"); + shake.update(label); GeneratorsChain { - reader: shake.xof_result(), + reader: shake.finalize_xof(), } } @@ -169,7 +169,7 @@ impl BulletproofGens { /// slice of vectors G and H for the j-th range proof. pub fn share(&self, j: usize) -> BulletproofGensShare<'_> { BulletproofGensShare { - gens: &self, + gens: self, share: j, } } diff --git a/src/inner_product_proof.rs b/src/inner_product_proof.rs index 5d1eb594..55656bf8 100644 --- a/src/inner_product_proof.rs +++ b/src/inner_product_proof.rs @@ -1,5 +1,5 @@ #![allow(non_snake_case)] -#![doc(include = "../docs/inner-product-protocol.md")] +#![doc = include_str!("../docs/inner-product-protocol.md")] extern crate alloc; @@ -15,7 +15,8 @@ use merlin::Transcript; use crate::errors::ProofError; use crate::transcript::TranscriptProtocol; -#[derive(Clone, Debug)] +/// Inner-product proof. +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct InnerProductProof { pub(crate) L_vec: Vec, pub(crate) R_vec: Vec, @@ -75,22 +76,22 @@ impl InnerProductProof { // If it's the first iteration, unroll the Hprime = H*y_inv scalar mults // into multiscalar muls, for performance. if n != 1 { - n = n / 2; + n /= 2; let (a_L, a_R) = a.split_at_mut(n); let (b_L, b_R) = b.split_at_mut(n); let (G_L, G_R) = G.split_at_mut(n); let (H_L, H_R) = H.split_at_mut(n); - let c_L = inner_product(&a_L, &b_R); - let c_R = inner_product(&a_R, &b_L); + let c_L = inner_product(a_L, b_R); + let c_R = inner_product(a_R, b_L); let L = RistrettoPoint::vartime_multiscalar_mul( a_L.iter() - .zip(G_factors[n..2 * n].into_iter()) + .zip(G_factors[n..2 * n].iter()) .map(|(a_L_i, g)| a_L_i * g) .chain( b_R.iter() - .zip(H_factors[0..n].into_iter()) + .zip(H_factors[0..n].iter()) .map(|(b_R_i, h)| b_R_i * h), ) .chain(iter::once(c_L)), @@ -100,11 +101,11 @@ impl InnerProductProof { let R = RistrettoPoint::vartime_multiscalar_mul( a_R.iter() - .zip(G_factors[0..n].into_iter()) + .zip(G_factors[0..n].iter()) .map(|(a_R_i, g)| a_R_i * g) .chain( b_L.iter() - .zip(H_factors[n..2 * n].into_iter()) + .zip(H_factors[n..2 * n].iter()) .map(|(b_L_i, h)| b_L_i * h), ) .chain(iter::once(c_R)), @@ -141,14 +142,14 @@ impl InnerProductProof { } while n != 1 { - n = n / 2; + n /= 2; let (a_L, a_R) = a.split_at_mut(n); let (b_L, b_R) = b.split_at_mut(n); let (G_L, G_R) = G.split_at_mut(n); let (H_L, H_R) = H.split_at_mut(n); - let c_L = inner_product(&a_L, &b_R); - let c_R = inner_product(&a_R, &b_L); + let c_L = inner_product(a_L, b_R); + let c_R = inner_product(a_R, b_L); let L = RistrettoPoint::vartime_multiscalar_mul( a_L.iter().chain(b_R.iter()).chain(iter::once(&c_L)), @@ -185,8 +186,8 @@ impl InnerProductProof { } InnerProductProof { - L_vec: L_vec, - R_vec: R_vec, + L_vec, + R_vec, a: a[0], b: b[0], } diff --git a/src/lib.rs b/src/lib.rs index f65d9337..6289fd3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,8 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![feature(nll)] -#![feature(external_doc)] -#![feature(try_trait)] #![deny(missing_docs)] -#![doc(include = "../README.md")] +#![doc = include_str!("../README.md")] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] -#![doc(html_root_url = "https://docs.rs/bulletproofs/2.0.0")] +#![doc(html_root_url = "https://docs.rs/bulletproofs/3.0.0")] extern crate alloc; @@ -14,13 +11,13 @@ extern crate serde_derive; mod util; -#[doc(include = "../docs/notes-intro.md")] +#[doc = include_str!("../docs/notes-intro.md")] mod notes { - #[doc(include = "../docs/notes-ipp.md")] + #[doc = include_str!("../docs/notes-ipp.md")] mod inner_product_proof {} - #[doc(include = "../docs/notes-rp.md")] + #[doc = include_str!("../docs/notes-rp.md")] mod range_proof {} - #[doc(include = "../docs/notes-r1cs.md")] + #[doc = include_str!("../docs/notes-r1cs.md")] mod r1cs_proof {} } @@ -33,10 +30,11 @@ mod transcript; pub use crate::errors::ProofError; pub use crate::generators::{BulletproofGens, BulletproofGensShare, PedersenGens}; +pub use crate::inner_product_proof::{inner_product, InnerProductProof}; pub use crate::linear_proof::LinearProof; pub use crate::range_proof::RangeProof; -#[doc(include = "../docs/aggregation-api.md")] +#[doc = include_str!("../docs/aggregation-api.md")] pub mod range_proof_mpc { pub use crate::errors::MPCError; pub use crate::range_proof::dealer; diff --git a/src/linear_proof.rs b/src/linear_proof.rs index 84fec0b1..bc6c979a 100644 --- a/src/linear_proof.rs +++ b/src/linear_proof.rs @@ -73,20 +73,20 @@ impl LinearProof { assert!(n.is_power_of_two()); transcript.innerproduct_domain_sep(n as u64); - transcript.append_point(b"C", &C); + transcript.append_point(b"C", C); let lg_n = n.next_power_of_two().trailing_zeros() as usize; let mut L_vec = Vec::with_capacity(lg_n); let mut R_vec = Vec::with_capacity(lg_n); while n != 1 { - n = n / 2; + n /= 2; let (a_L, a_R) = a.split_at_mut(n); let (b_L, b_R) = b.split_at_mut(n); let (G_L, G_R) = G.split_at_mut(n); - let c_L = inner_product(&a_L, &b_R); - let c_R = inner_product(&a_R, &b_L); + let c_L = inner_product(a_L, b_R); + let c_R = inner_product(a_R, b_L); let s_j = Scalar::random(rng); let t_j = Scalar::random(rng); @@ -116,9 +116,9 @@ impl LinearProof { for i in 0..n { // a_L = a_L + x_j^{-1} * a_R - a_L[i] = a_L[i] + x_j_inv * a_R[i]; + a_L[i] += x_j_inv * a_R[i]; // b_L = b_L + x_j * b_R - b_L[i] = b_L[i] + x_j * b_R[i]; + b_L[i] += x_j * b_R[i]; // G_L = G_L + x_j * G_R G_L[i] = RistrettoPoint::vartime_multiscalar_mul( &[Scalar::one(), x_j], @@ -149,6 +149,7 @@ impl LinearProof { } } + /// Verify a linear proof pub fn verify( &self, n: usize, @@ -165,7 +166,7 @@ impl LinearProof { b_vec: Vec, ) -> Result<(), ProofError> { transcript.innerproduct_domain_sep(n as u64); - transcript.append_point(b"C", &C); + transcript.append_point(b"C", C); let (x_vec, x_inv_vec, b_0) = self.verification_scalars(n, transcript, b_vec)?; transcript.append_point(b"S", &self.S); @@ -249,10 +250,10 @@ impl LinearProof { transcript.validate_and_append_point(b"R", R)?; let x_j = transcript.challenge_scalar(b"x_j"); challenges.push(x_j); - n_mut = n_mut / 2; + n_mut /= 2; let (b_L, b_R) = b.split_at_mut(n_mut); for i in 0..n_mut { - b_L[i] = b_L[i] + x_j * b_R[i]; + b_L[i] += x_j * b_R[i]; } b = b_L; } diff --git a/src/range_proof/dealer.rs b/src/range_proof/dealer.rs index 00c6691e..63a8acb4 100644 --- a/src/range_proof/dealer.rs +++ b/src/range_proof/dealer.rs @@ -41,7 +41,7 @@ impl Dealer { n: usize, m: usize, ) -> Result, MPCError> { - if !(n == 8 || n == 16 || n == 32 || n == 64) { + if !(n == 8 || n == 16 || n == 32 || n == 64 || n == 128) { return Err(MPCError::InvalidBitsize); } if !m.is_power_of_two() { @@ -232,13 +232,13 @@ impl<'a, 'b> DealerAwaitingProofShares<'a, 'b> { let mut bad_shares = Vec::::new(); // no allocations until we append for (j, share) in proof_shares.iter().enumerate() { share - .check_size(self.n, &self.bp_gens, j) + .check_size(self.n, self.bp_gens, j) .unwrap_or_else(|_| { bad_shares.push(j); }); } - if bad_shares.len() > 0 { + if !bad_shares.is_empty() { return Err(MPCError::MalformedProofShares { bad_shares }); } @@ -335,10 +335,10 @@ impl<'a, 'b> DealerAwaitingProofShares<'a, 'b> { } else { // Proof verification failed. Now audit the parties: let mut bad_shares = Vec::new(); - for j in 0..self.m { - match proof_shares[j].audit_share( - &self.bp_gens, - &self.pc_gens, + for (j, proof_share) in proof_shares.iter().enumerate().take(self.m) { + match proof_share.audit_share( + self.bp_gens, + self.pc_gens, j, &self.bit_commitments[j], &self.bit_challenge, diff --git a/src/range_proof/messages.rs b/src/range_proof/messages.rs index 8a563fb0..a4505839 100644 --- a/src/range_proof/messages.rs +++ b/src/range_proof/messages.rs @@ -143,7 +143,7 @@ impl ProofShare { let V_j = bit_commitment.V_j.decompress().ok_or(())?; - let sum_of_powers_y = util::sum_of_powers(&y, n); + let sum_of_powers_y = util::sum_of_powers(y, n); let sum_of_powers_2 = util::sum_of_powers(&Scalar::from(2u64), n); let delta = (z - zz) * sum_of_powers_y * y_jn - z * zz * sum_of_powers_2 * z_j; let t_check = RistrettoPoint::vartime_multiscalar_mul( diff --git a/src/range_proof/mod.rs b/src/range_proof/mod.rs index 6eee8d31..30c1486f 100644 --- a/src/range_proof/mod.rs +++ b/src/range_proof/mod.rs @@ -1,5 +1,5 @@ #![allow(non_snake_case)] -#![doc(include = "../../docs/range-proof-protocol.md")] +#![doc = include_str!("../../docs/range-proof-protocol.md")] extern crate alloc; #[cfg(feature = "std")] @@ -10,6 +10,8 @@ use self::rand::thread_rng; use alloc::vec::Vec; use core::iter; +#[cfg(feature = "scalar_range_proof")] +use core::slice; use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; @@ -44,7 +46,7 @@ pub mod party; /// the verifier. /// /// This implementation requires that both the bitsize `n` and the -/// aggregation size `m` be powers of two, so that `n = 8, 16, 32, 64` +/// aggregation size `m` be powers of two, so that `n = 8, 16, 32, 64, 128` /// and `m = 1, 2, 4, 8, 16, ...`. Note that the aggregation size is /// not given as an explicit parameter, but is determined by the /// number of values or commitments passed to the prover or verifier. @@ -104,7 +106,85 @@ impl RangeProof { /// let bp_gens = BulletproofGens::new(64, 1); /// /// // A secret value we want to prove lies in the range [0, 2^32) - /// let secret_value = 1037578891u64; + /// let secret_value = Scalar::from(1037578891u64); + /// + /// // The API takes a blinding factor for the commitment. + /// let blinding = Scalar::random(&mut thread_rng()); + /// + /// // The proof can be chained to an existing transcript. + /// // Here we create a transcript with a doctest domain separator. + /// let mut prover_transcript = Transcript::new(b"doctest example"); + /// + /// // Create a 32-bit rangeproof. + /// let (proof, committed_value) = RangeProof::prove_single( + /// &bp_gens, + /// &pc_gens, + /// &mut prover_transcript, + /// &secret_value, + /// &blinding, + /// 32, + /// ).expect("A real program could handle errors"); + /// + /// // Verification requires a transcript with identical initial state: + /// let mut verifier_transcript = Transcript::new(b"doctest example"); + /// assert!( + /// proof + /// .verify_single(&bp_gens, &pc_gens, &mut verifier_transcript, &committed_value, 32) + /// .is_ok() + /// ); + /// # } + /// ``` + #[cfg(feature = "scalar_range_proof")] + pub fn prove_single_with_rng( + bp_gens: &BulletproofGens, + pc_gens: &PedersenGens, + transcript: &mut Transcript, + v: &Scalar, + 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, + slice::from_ref(v), + slice::from_ref(v_blinding), + n, + rng, + )?; + Ok((p, Vs[0])) + } + + /// Create a rangeproof for a given pair of value `v` and + /// blinding scalar `v_blinding`. + /// This is a convenience wrapper around [`RangeProof::prove_multiple`]. + /// + /// # Example + /// ``` + /// extern crate rand; + /// use rand::thread_rng; + /// + /// extern crate curve25519_dalek; + /// use curve25519_dalek::scalar::Scalar; + /// + /// extern crate merlin; + /// use merlin::Transcript; + /// + /// extern crate bulletproofs; + /// use bulletproofs::{BulletproofGens, PedersenGens, RangeProof}; + /// + /// # fn main() { + /// // Generators for Pedersen commitments. These can be selected + /// // independently of the Bulletproofs generators. + /// let pc_gens = PedersenGens::default(); + /// + /// // Generators for Bulletproofs, valid for proofs up to bitsize 128 + /// // and aggregation size up to 1. + /// let bp_gens = BulletproofGens::new(128, 1); + /// + /// // A secret value we want to prove lies in the range [0, 2^32) + /// let secret_value = 1037578891u128; /// /// // The API takes a blinding factor for the commitment. /// let blinding = Scalar::random(&mut thread_rng()); @@ -132,11 +212,12 @@ impl RangeProof { /// ); /// # } /// ``` + #[cfg(not(feature = "scalar_range_proof"))] pub fn prove_single_with_rng( bp_gens: &BulletproofGens, pc_gens: &PedersenGens, transcript: &mut Transcript, - v: u64, + v: u128, v_blinding: &Scalar, n: usize, rng: &mut T, @@ -157,12 +238,36 @@ impl RangeProof { /// blinding scalar `v_blinding`. /// This is a convenience wrapper around [`RangeProof::prove_single_with_rng`], /// passing in a threadsafe RNG. - #[cfg(feature = "std")] + #[cfg(all(feature = "std", feature = "scalar_range_proof"))] pub fn prove_single( bp_gens: &BulletproofGens, pc_gens: &PedersenGens, transcript: &mut Transcript, - v: u64, + v: &Scalar, + 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(), + ) + } + + /// Create a rangeproof for a given pair of value `v` and + /// blinding scalar `v_blinding`. + /// This is a convenience wrapper around [`RangeProof::prove_single_with_rng`], + /// passing in a threadsafe RNG. + #[cfg(all(feature = "std", not(feature = "scalar_range_proof")))] + pub fn prove_single( + bp_gens: &BulletproofGens, + pc_gens: &PedersenGens, + transcript: &mut Transcript, + v: u128, v_blinding: &Scalar, n: usize, ) -> Result<(RangeProof, CompressedRistretto), ProofError> { @@ -203,7 +308,100 @@ impl RangeProof { /// let bp_gens = BulletproofGens::new(64, 16); /// /// // Four secret values we want to prove lie in the range [0, 2^32) - /// let secrets = [4242344947u64, 3718732727u64, 2255562556u64, 2526146994u64]; + /// let secrets = [ + /// Scalar::from(4242344947u64), + /// Scalar::from(3718732727u64), + /// Scalar::from(2255562556u64), + /// Scalar::from(2526146994u64), + /// ]; + /// + /// // The API takes blinding factors for the commitments. + /// let blindings: Vec<_> = (0..4).map(|_| Scalar::random(&mut thread_rng())).collect(); + /// + /// // The proof can be chained to an existing transcript. + /// // Here we create a transcript with a doctest domain separator. + /// let mut prover_transcript = Transcript::new(b"doctest example"); + /// + /// // Create an aggregated 32-bit rangeproof and corresponding commitments. + /// let (proof, commitments) = RangeProof::prove_multiple( + /// &bp_gens, + /// &pc_gens, + /// &mut prover_transcript, + /// &secrets, + /// &blindings, + /// 32, + /// ).expect("A real program could handle errors"); + /// + /// // Verification requires a transcript with identical initial state: + /// let mut verifier_transcript = Transcript::new(b"doctest example"); + /// assert!( + /// proof + /// .verify_multiple(&bp_gens, &pc_gens, &mut verifier_transcript, &commitments, 32) + /// .is_ok() + /// ); + /// # } + /// ``` + #[cfg(feature = "scalar_range_proof")] + pub fn prove_multiple_with_rng( + bp_gens: &BulletproofGens, + pc_gens: &PedersenGens, + transcript: &mut Transcript, + values: &[Scalar], + blindings: &[Scalar], + n: usize, + rng: &mut T, + ) -> Result<(RangeProof, Vec), 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 the iterator of Results into a Result, then unwrap it + .collect::, _>>()?; + + RangeProof::prove_multiple_with_rng_internal(parties, dealer, rng) + } + + /// Create a rangeproof for a set of values. + /// + /// # Example + /// ``` + /// extern crate rand; + /// use rand::thread_rng; + /// + /// extern crate curve25519_dalek; + /// use curve25519_dalek::scalar::Scalar; + /// + /// extern crate merlin; + /// use merlin::Transcript; + /// + /// extern crate bulletproofs; + /// use bulletproofs::{BulletproofGens, PedersenGens, RangeProof}; + /// + /// # fn main() { + /// // Generators for Pedersen commitments. These can be selected + /// // independently of the Bulletproofs generators. + /// let pc_gens = PedersenGens::default(); + /// + /// // Generators for Bulletproofs, valid for proofs up to bitsize 64 + /// // and aggregation size up to 16. + /// let bp_gens = BulletproofGens::new(128, 16); + /// + /// // Four secret values we want to prove lie in the range [0, 2^32) + /// let secrets = [ + /// 4242344947u128, + /// 3718732727u128, + /// 2255562556u128, + /// 2526146994u128, + /// ]; /// /// // The API takes blinding factors for the commitments. /// let blindings: Vec<_> = (0..4).map(|_| Scalar::random(&mut thread_rng())).collect(); @@ -231,11 +429,12 @@ impl RangeProof { /// ); /// # } /// ``` + #[cfg(not(feature = "scalar_range_proof"))] pub fn prove_multiple_with_rng( bp_gens: &BulletproofGens, pc_gens: &PedersenGens, transcript: &mut Transcript, - values: &[u64], + values: &[u128], blindings: &[Scalar], n: usize, rng: &mut T, @@ -255,7 +454,14 @@ impl RangeProof { .map(|(&v, &v_blinding)| Party::new(bp_gens, pc_gens, v, v_blinding, n)) // Collect the iterator of Results into a Result, then unwrap it .collect::, _>>()?; + RangeProof::prove_multiple_with_rng_internal(parties, dealer, rng) + } + fn prove_multiple_with_rng_internal( + parties: Vec, + dealer: dealer::DealerAwaitingBitCommitments, + rng: &mut T, + ) -> Result<(RangeProof, Vec), ProofError> { let (parties, bit_commitments): (Vec<_>, Vec<_>) = parties .into_iter() .enumerate() @@ -290,12 +496,35 @@ impl RangeProof { /// Create a rangeproof for a set of values. /// This is a convenience wrapper around [`RangeProof::prove_multiple_with_rng`], /// passing in a threadsafe RNG. - #[cfg(feature = "std")] + #[cfg(all(feature = "std", feature = "scalar_range_proof"))] pub fn prove_multiple( bp_gens: &BulletproofGens, pc_gens: &PedersenGens, transcript: &mut Transcript, - values: &[u64], + values: &[Scalar], + blindings: &[Scalar], + n: usize, + ) -> Result<(RangeProof, Vec), ProofError> { + RangeProof::prove_multiple_with_rng( + bp_gens, + pc_gens, + transcript, + values, + blindings, + n, + &mut thread_rng(), + ) + } + + /// Create a rangeproof for a set of values. + /// This is a convenience wrapper around [`RangeProof::prove_multiple_with_rng`], + /// passing in a threadsafe RNG. + #[cfg(all(feature = "std", not(feature = "scalar_range_proof")))] + pub fn prove_multiple( + bp_gens: &BulletproofGens, + pc_gens: &PedersenGens, + transcript: &mut Transcript, + values: &[u128], blindings: &[Scalar], n: usize, ) -> Result<(RangeProof, Vec), ProofError> { @@ -355,7 +584,7 @@ impl RangeProof { // First, replay the "interactive" protocol using the proof // data to recompute all challenges. - if !(n == 8 || n == 16 || n == 32 || n == 64) { + if !(n == 8 || n == 16 || n == 32 || n == 64 || n == 128) { return Err(ProofError::InvalidBitsize); } if bp_gens.gens_capacity < n { @@ -442,7 +671,7 @@ impl RangeProof { .chain(bp_gens.H(n, m).map(|&x| Some(x))) .chain(value_commitments.iter().map(|V| V.decompress())), ) - .ok_or_else(|| ProofError::VerificationError)?; + .ok_or(ProofError::VerificationError)?; if mega_check.is_identity() { Ok(()) @@ -511,8 +740,8 @@ impl RangeProof { use crate::util::read32; - let A = CompressedRistretto(read32(&slice[0 * 32..])); - let S = CompressedRistretto(read32(&slice[1 * 32..])); + let A = CompressedRistretto(read32(&slice[0..])); + let S = CompressedRistretto(read32(&slice[32..])); let T_1 = CompressedRistretto(read32(&slice[2 * 32..])); let T_2 = CompressedRistretto(read32(&slice[3 * 32..])); @@ -638,7 +867,7 @@ mod tests { //use bincode; // already present in lib.rs // Both prover and verifier have access to the generators and the proof - let max_bitsize = 64; + let max_bitsize = 128; let max_parties = 8; let pc_gens = PedersenGens::default(); let bp_gens = BulletproofGens::new(max_bitsize, max_parties); @@ -649,8 +878,13 @@ mod tests { let mut rng = rand::thread_rng(); // 0. Create witness data - let (min, max) = (0u64, ((1u128 << n) - 1) as u64); - let values: Vec = (0..m).map(|_| rng.gen_range(min, max)).collect(); + let (min, max) = (0u128, u128::MAX >> (u128::BITS as usize - n)); + + #[cfg(feature = "scalar_range_proof")] + let values: Vec = (0..m).map(|_| rng.gen_range(min, max).into()).collect(); + #[cfg(not(feature = "scalar_range_proof"))] + let values: Vec = (0..m).map(|_| rng.gen_range(min, max)).collect(); + let blindings: Vec = (0..m).map(|_| Scalar::random(&mut rng)).collect(); // 1. Create the proof @@ -723,6 +957,26 @@ mod tests { singleparty_create_and_verify_helper(64, 8); } + #[test] + fn create_and_verify_n_128_m_1() { + singleparty_create_and_verify_helper(128, 1); + } + + #[test] + fn create_and_verify_n_128_m_2() { + singleparty_create_and_verify_helper(128, 2); + } + + #[test] + fn create_and_verify_n_128_m_4() { + singleparty_create_and_verify_helper(128, 4); + } + + #[test] + fn create_and_verify_n_128_m_8() { + singleparty_create_and_verify_helper(128, 8); + } + #[test] fn detect_dishonest_party_during_aggregation() { use self::dealer::*; @@ -730,9 +984,9 @@ mod tests { use crate::errors::MPCError; - // Simulate four parties, two of which will be dishonest and use a 64-bit value. + // Simulate four parties, two of which will be dishonest and use a 128-bit value. let m = 4; - let n = 32; + let n = 64; let pc_gens = PedersenGens::default(); let bp_gens = BulletproofGens::new(n, m); @@ -742,20 +996,33 @@ mod tests { let mut transcript = Transcript::new(b"AggregatedRangeProofTest"); // Parties 0, 2 are honest and use a 32-bit value - let v0 = rng.gen::() as u64; + + #[cfg(feature = "scalar_range_proof")] + let v0 = Scalar::from(rng.gen::()); + #[cfg(not(feature = "scalar_range_proof"))] + let v0 = rng.gen::() as u128; let v0_blinding = Scalar::random(&mut rng); let party0 = Party::new(&bp_gens, &pc_gens, v0, v0_blinding, n).unwrap(); - let v2 = rng.gen::() as u64; + #[cfg(feature = "scalar_range_proof")] + let v2 = Scalar::from(rng.gen::()); + #[cfg(not(feature = "scalar_range_proof"))] + let v2 = rng.gen::() as u128; let v2_blinding = Scalar::random(&mut rng); let party2 = Party::new(&bp_gens, &pc_gens, v2, v2_blinding, n).unwrap(); // Parties 1, 3 are dishonest and use a 64-bit value - let v1 = rng.gen::(); + #[cfg(feature = "scalar_range_proof")] + let v1 = Scalar::from(rng.gen::()); + #[cfg(not(feature = "scalar_range_proof"))] + let v1 = rng.gen::(); let v1_blinding = Scalar::random(&mut rng); let party1 = Party::new(&bp_gens, &pc_gens, v1, v1_blinding, n).unwrap(); - let v3 = rng.gen::(); + #[cfg(feature = "scalar_range_proof")] + let v3 = Scalar::from(rng.gen::()); + #[cfg(not(feature = "scalar_range_proof"))] + let v3 = rng.gen::(); let v3_blinding = Scalar::random(&mut rng); let party3 = Party::new(&bp_gens, &pc_gens, v3, v3_blinding, n).unwrap(); @@ -814,7 +1081,10 @@ mod tests { let mut rng = rand::thread_rng(); let mut transcript = Transcript::new(b"AggregatedRangeProofTest"); - let v0 = rng.gen::() as u64; + #[cfg(feature = "scalar_range_proof")] + let v0 = Scalar::from(rng.gen::()); + #[cfg(not(feature = "scalar_range_proof"))] + let v0 = rng.gen::() as u128; let v0_blinding = Scalar::random(&mut rng); let party0 = Party::new(&bp_gens, &pc_gens, v0, v0_blinding, n).unwrap(); diff --git a/src/range_proof/party.rs b/src/range_proof/party.rs index ebb232cc..afcc4800 100644 --- a/src/range_proof/party.rs +++ b/src/range_proof/party.rs @@ -34,14 +34,43 @@ pub struct Party {} impl Party { /// Constructs a `PartyAwaitingPosition` with the given rangeproof parameters. + #[cfg(feature = "scalar_range_proof")] pub fn new<'a>( bp_gens: &'a BulletproofGens, pc_gens: &'a PedersenGens, - v: u64, + v: Scalar, v_blinding: Scalar, n: usize, ) -> Result, MPCError> { - if !(n == 8 || n == 16 || n == 32 || n == 64) { + if !(n == 8 || n == 16 || n == 32 || n == 64 || n == 128) { + return Err(MPCError::InvalidBitsize); + } + if bp_gens.gens_capacity < n { + return Err(MPCError::InvalidGeneratorsLength); + } + + let V = pc_gens.commit(v, v_blinding).compress(); + + Ok(PartyAwaitingPosition { + bp_gens, + pc_gens, + n, + v, + v_blinding, + V, + }) + } + + /// Constructs a `PartyAwaitingPosition` with the given rangeproof parameters. + #[cfg(not(feature = "scalar_range_proof"))] + pub fn new<'a>( + bp_gens: &'a BulletproofGens, + pc_gens: &'a PedersenGens, + v: u128, + v_blinding: Scalar, + n: usize, + ) -> Result, MPCError> { + if !(n == 8 || n == 16 || n == 32 || n == 64 || n == 128) { return Err(MPCError::InvalidBitsize); } if bp_gens.gens_capacity < n { @@ -62,11 +91,23 @@ impl Party { } /// A party waiting for the dealer to assign their position in the aggregation. +#[cfg(feature = "scalar_range_proof")] +pub struct PartyAwaitingPosition<'a> { + bp_gens: &'a BulletproofGens, + pc_gens: &'a PedersenGens, + n: usize, + v: Scalar, + v_blinding: Scalar, + V: CompressedRistretto, +} + +/// A party waiting for the dealer to assign their position in the aggregation. +#[cfg(not(feature = "scalar_range_proof"))] pub struct PartyAwaitingPosition<'a> { bp_gens: &'a BulletproofGens, pc_gens: &'a PedersenGens, n: usize, - v: u64, + v: u128, v_blinding: Scalar, V: CompressedRistretto, } @@ -100,15 +141,17 @@ impl<'a> PartyAwaitingPosition<'a> { let mut A = self.pc_gens.B_blinding * a_blinding; use subtle::{Choice, ConditionallySelectable}; - let mut i = 0; - for (G_i, H_i) in bp_share.G(self.n).zip(bp_share.H(self.n)) { + for (i, (G_i, H_i)) in bp_share.G(self.n).zip(bp_share.H(self.n)).enumerate() { // If v_i = 0, we add a_L[i] * G[i] + a_R[i] * H[i] = - H[i] // If v_i = 1, we add a_L[i] * G[i] + a_R[i] * H[i] = G[i] + #[cfg(feature = "scalar_range_proof")] + let v_i = Choice::from(extract_scalar_bit(&self.v, i)); + #[cfg(not(feature = "scalar_range_proof"))] let v_i = Choice::from(((self.v >> i) & 1) as u8); + let mut point = -H_i; point.conditional_assign(G_i, v_i); A += point; - i += 1; } let s_blinding = Scalar::random(rng); @@ -154,9 +197,25 @@ impl<'a> Drop for PartyAwaitingPosition<'a> { /// A party which has committed to the bits of its value /// and is waiting for the aggregated value challenge from the dealer. +#[cfg(feature = "scalar_range_proof")] pub struct PartyAwaitingBitChallenge<'a> { n: usize, // bitsize of the range - v: u64, + v: Scalar, + v_blinding: Scalar, + j: usize, + pc_gens: &'a PedersenGens, + a_blinding: Scalar, + s_blinding: Scalar, + s_L: Vec, + s_R: Vec, +} + +/// A party which has committed to the bits of its value +/// and is waiting for the aggregated value challenge from the dealer. +#[cfg(not(feature = "scalar_range_proof"))] +pub struct PartyAwaitingBitChallenge<'a> { + n: usize, // bitsize of the range + v: u128, v_blinding: Scalar, j: usize, pc_gens: &'a PedersenGens, @@ -196,7 +255,11 @@ impl<'a> PartyAwaitingBitChallenge<'a> { let mut exp_y = offset_y; // start at y^j let mut exp_2 = Scalar::one(); // start at 2^0 = 1 for i in 0..n { + #[cfg(not(feature = "scalar_range_proof"))] let a_L_i = Scalar::from((self.v >> i) & 1); + #[cfg(feature = "scalar_range_proof")] + let a_L_i = Scalar::from(extract_scalar_bit(&self.v, i)); + let a_R_i = a_L_i - Scalar::one(); l_poly.0[i] = a_L_i - vc.z; @@ -291,7 +354,7 @@ impl PartyAwaitingPolyChallenge { let t_x = self.t_poly.eval(pc.x); let t_x_blinding = t_blinding_poly.eval(pc.x); - let e_blinding = self.a_blinding + self.s_blinding * &pc.x; + let e_blinding = self.a_blinding + self.s_blinding * pc.x; let l_vec = self.l_poly.eval(pc.x); let r_vec = self.r_poly.eval(pc.x); @@ -318,3 +381,11 @@ impl Drop for PartyAwaitingPolyChallenge { // are cleared within their own Drop impls. } } + +#[cfg(feature = "scalar_range_proof")] +fn extract_scalar_bit(scalar: &Scalar, index: usize) -> u8 { + let u8_bits = u8::BITS as usize; + let byte_index = index / u8_bits; + let bit_index = index % u8_bits; + (scalar[byte_index] >> bit_index) & 1 +} diff --git a/src/transcript.rs b/src/transcript.rs index 639c7cf1..005da0f3 100644 --- a/src/transcript.rs +++ b/src/transcript.rs @@ -82,7 +82,8 @@ impl TranscriptProtocol for Transcript { if point.is_identity() { Err(ProofError::VerificationError) } else { - Ok(self.append_message(label, point.as_bytes())) + self.append_message(label, point.as_bytes()); + Ok(()) } } diff --git a/src/util.rs b/src/util.rs index dd7ce2fe..d841c479 100644 --- a/src/util.rs +++ b/src/util.rs @@ -102,8 +102,8 @@ impl VecPoly1 { pub fn eval(&self, x: Scalar) -> Vec { let n = self.0.len(); let mut out = vec![Scalar::zero(); n]; - for i in 0..n { - out[i] = self.0[i] + self.1[i] * x; + for (i, item) in out.iter_mut().enumerate().take(n) { + *item = self.0[i] + self.1[i] * x; } out } @@ -225,9 +225,9 @@ pub fn scalar_exp_vartime(x: &Scalar, mut n: u64) -> Scalar { while n > 0 { let bit = n & 1; if bit == 1 { - result = result * aux; + result *= aux; } - n = n >> 1; + n >>= 1; aux = aux * aux; // FIXME: one unnecessary mult at the last step here! } result @@ -250,7 +250,7 @@ pub fn sum_of_powers(x: &Scalar, n: usize) -> Scalar { while m > 2 { factor = factor * factor; result = result + factor * result; - m = m / 2; + m /= 2; } result } diff --git a/tests/range_proof.rs b/tests/range_proof.rs index 57b0f653..385a7ca2 100644 --- a/tests/range_proof.rs +++ b/tests/range_proof.rs @@ -1,8 +1,10 @@ -use rand_core::SeedableRng; - +#[cfg(not(feature = "scalar_range_proof"))] use rand_chacha::ChaChaRng; +#[cfg(not(feature = "scalar_range_proof"))] +use rand_core::SeedableRng; use curve25519_dalek::ristretto::CompressedRistretto; +#[cfg(not(feature = "scalar_range_proof"))] use curve25519_dalek::scalar::Scalar; use merlin::Transcript; @@ -98,21 +100,22 @@ fn deserialize_and_verify() { // It can be run by uncommenting the #[test] annotation. // We allow(dead_code) to ensure that it continues to compile. //#[test] +#[cfg(not(feature = "scalar_range_proof"))] #[allow(dead_code)] fn generate_test_vectors() { let pc_gens = PedersenGens::default(); - let bp_gens = BulletproofGens::new(64, 8); + let bp_gens = BulletproofGens::new(128, 8); // Use a deterministic RNG for proving, so the test vectors can be // generated reproducibly. let mut test_rng = ChaChaRng::from_seed([24u8; 32]); - let values = vec![0u64, 1, 2, 3, 4, 5, 6, 7]; + let values = vec![0u128, 1, 2, 3, 4, 5, 6, 7]; let blindings = (0..8) .map(|_| Scalar::random(&mut test_rng)) .collect::>(); - for n in &[8, 16, 32, 64] { + for n in &[8, 16, 32, 64, 128] { for m in &[1, 2, 4, 8] { let mut transcript = Transcript::new(b"Deserialize-And-Verify Test"); let (proof, value_commitments) = RangeProof::prove_multiple( diff --git a/tests/scalar_range_proof.rs b/tests/scalar_range_proof.rs new file mode 100644 index 00000000..019bf4b7 --- /dev/null +++ b/tests/scalar_range_proof.rs @@ -0,0 +1,58 @@ +#[cfg(feature = "scalar_range_proof")] +use bulletproofs::{BulletproofGens, PedersenGens, RangeProof}; +#[cfg(feature = "scalar_range_proof")] +use curve25519_dalek::scalar::Scalar; +#[cfg(feature = "scalar_range_proof")] +use hex; +#[cfg(feature = "scalar_range_proof")] +use merlin::Transcript; +#[cfg(feature = "scalar_range_proof")] +use rand_chacha::ChaChaRng; +#[cfg(feature = "scalar_range_proof")] +use rand_core::SeedableRng; + +// This function generates test vectors and dumps them to stdout. +// It can be run by uncommenting the #[test] annotation. +// We allow(dead_code) to ensure that it continues to compile. +//#[test] +#[cfg(feature = "scalar_range_proof")] +#[allow(dead_code)] +fn generate_test_vectors() { + let pc_gens = PedersenGens::default(); + let bp_gens = BulletproofGens::new(128, 8); + + // Use a deterministic RNG for proving, so the test vectors can be + // generated reproducibly. + let mut test_rng = ChaChaRng::from_seed([24u8; 32]); + + let value_range = 0u64..8; + let values = value_range.clone().map(Scalar::from).collect::>(); + let blindings = value_range + .map(|_| Scalar::random(&mut test_rng)) + .collect::>(); + + for n in &[8, 16, 32, 64, 128] { + for m in &[1, 2, 4, 8] { + let mut transcript = Transcript::new(b"Deserialize-And-Verify Test"); + let (proof, value_commitments) = RangeProof::prove_multiple( + &bp_gens, + &pc_gens, + &mut transcript, + &values[0..*m], + &blindings[0..*m], + *n, + ) + .unwrap(); + + println!("n,m = {}, {}", n, m); + println!("proof = \"{}\"", hex::encode(proof.to_bytes())); + println!("vc = ["); + for com in &value_commitments { + println!(" \"{}\"", hex::encode(com.as_bytes())); + } + println!("]\n"); + } + } + + panic!(); +}