use libc::size_t;
use std::cmp::min;
use std::fmt;
use std::mem;
use std::ptr;
use std::u64;
use crate::ContextFlag;
use crate::Error::{self, InvalidPublicKey, InvalidCommit};
use crate::Secp256k1;
use super::{Message, Signature};
use crate::aggsig::ZERO_256;
use crate::constants;
use crate::ffi;
use crate::key::{self, PublicKey, SecretKey};
use rand::{thread_rng, Rng};
use serde::{de, ser};
const MAX_WIDTH: usize = 1 << 20;
const SCRATCH_SPACE_SIZE: size_t = 256 * MAX_WIDTH;
const MAX_GENERATORS: size_t = 256;
static mut SHARED_BULLETGENERATORS: Option<*mut ffi::BulletproofGenerators> = None;
fn shared_generators(ctx: *mut ffi::Context) -> *mut ffi::BulletproofGenerators {
unsafe {
match SHARED_BULLETGENERATORS.clone() {
Some(s) => s,
None => {
SHARED_BULLETGENERATORS = Some(ffi::secp256k1_bulletproof_generators_create(
ctx,
constants::GENERATOR_G.as_ptr(),
MAX_GENERATORS,
));
SHARED_BULLETGENERATORS.unwrap()
}
}
}
}
pub struct CommitmentInternal(pub [u8; constants::PEDERSEN_COMMITMENT_SIZE_INTERNAL]);
impl Copy for CommitmentInternal {}
impl_array_newtype!(CommitmentInternal, u8, constants::PEDERSEN_COMMITMENT_SIZE_INTERNAL);
impl_pretty_debug!(CommitmentInternal);
impl CommitmentInternal {
pub unsafe fn blank() -> CommitmentInternal {
mem::MaybeUninit::uninit().assume_init()
}
}
pub struct Commitment(pub [u8; constants::PEDERSEN_COMMITMENT_SIZE]);
impl Copy for Commitment {}
impl_array_newtype!(Commitment, u8, constants::PEDERSEN_COMMITMENT_SIZE);
impl_pretty_debug!(Commitment);
impl Commitment {
pub fn from_vec(v: Vec<u8>) -> Commitment {
let mut h = [0; constants::PEDERSEN_COMMITMENT_SIZE];
for i in 0..min(v.len(), constants::PEDERSEN_COMMITMENT_SIZE) {
h[i] = v[i];
}
Commitment(h)
}
unsafe fn blank() -> Commitment {
mem::MaybeUninit::uninit().assume_init()
}
pub fn from_pubkey(secp: &Secp256k1, pk: &key::PublicKey) -> Result<Self, Error> {
unsafe {
let mut commit_i = [0; constants::PEDERSEN_COMMITMENT_SIZE_INTERNAL];
if ffi::secp256k1_pubkey_to_pedersen_commitment(secp.ctx, commit_i.as_mut_ptr(), &pk.0 as *const _) == 1 {
Ok(secp.commit_ser(commit_i)?)
} else {
Err(InvalidCommit)
}
}
}
pub fn to_pubkey(&self, secp: &Secp256k1) -> Result<key::PublicKey, Error> {
let mut pk = unsafe { ffi::PublicKey::blank() };
unsafe {
let commit = secp.commit_parse(self.0.clone())?;
if ffi::secp256k1_pedersen_commitment_to_pubkey(secp.ctx, &mut pk, commit.as_ptr()) == 1 {
Ok(key::PublicKey::from_secp256k1_pubkey(pk))
} else {
Err(InvalidPublicKey)
}
}
}
}
#[derive(Clone, Copy)]
pub struct RangeProof {
pub proof: [u8; constants::MAX_PROOF_SIZE],
pub plen: usize,
}
impl PartialEq for RangeProof {
fn eq(&self, other: &Self) -> bool {
self.proof.as_ref() == other.proof.as_ref()
}
}
impl ser::Serialize for RangeProof {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
(&self.proof[..self.plen]).serialize(s)
}
}
struct Visitor;
impl<'di> de::Visitor<'di> for Visitor {
type Value = RangeProof;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an array of bytes")
}
#[inline]
fn visit_seq<V>(self, mut v: V) -> Result<RangeProof, V::Error>
where
V: de::SeqAccess<'di>,
{
unsafe {
let mut ret: [u8; constants::MAX_PROOF_SIZE] = mem::MaybeUninit::uninit().assume_init();
let mut i = 0;
while let Some(val) = v.next_element()? {
ret[i] = val;
i += 1;
}
Ok(RangeProof {
proof: ret,
plen: i,
})
}
}
}
impl<'de> de::Deserialize<'de> for RangeProof {
fn deserialize<D>(d: D) -> Result<RangeProof, D::Error>
where
D: de::Deserializer<'de>,
{
d.deserialize_seq(Visitor)
}
}
impl AsRef<[u8]> for RangeProof {
fn as_ref(&self) -> &[u8] {
&self.proof[..self.plen as usize]
}
}
macro_rules! is_zero_pubkey {
(retnone => $e:expr) => {
match $e {
Some(n) => {
if (n.0).0.starts_with(&ZERO_256) {
return None;
}
n.as_mut_ptr()
}
None => ptr::null_mut(),
}
};
(ignore => $e:expr) => {
match $e {
Some(n) => n.as_mut_ptr(),
None => ptr::null_mut(),
}
};
}
impl RangeProof {
pub fn zero() -> RangeProof {
RangeProof {
proof: [0; constants::MAX_PROOF_SIZE],
plen: 0,
}
}
pub fn bytes(&self) -> &[u8] {
&self.proof[..self.plen as usize]
}
pub fn len(&self) -> usize {
self.plen
}
}
#[derive(Clone)]
pub struct ProofMessage(Vec<u8>);
impl ProofMessage {
pub fn empty() -> ProofMessage {
ProofMessage(vec![])
}
pub fn from_bytes(array: &[u8]) -> ProofMessage {
let mut msg = vec![];
for &value in array {
msg.push(value);
}
ProofMessage(msg)
}
pub fn as_bytes(&self) -> &[u8] {
self.0.iter().as_slice()
}
pub fn as_ptr(&self) -> *const u8 {
self.0.as_ptr()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn truncate(&mut self, len: usize) {
self.0.truncate(len)
}
pub fn push(&mut self, value: u8) {
self.0.push(value);
}
}
impl ::std::cmp::PartialEq for ProofMessage {
fn eq(&self, other: &ProofMessage) -> bool {
self.0[..] == other.0[..]
}
}
impl ::std::cmp::Eq for ProofMessage {}
impl ::std::fmt::Debug for ProofMessage {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}(", stringify!(ProofMessage))?;
for i in self.0.iter().cloned() {
write!(f, "{:02x}", i)?;
}
write!(f, ")")
}
}
#[derive(Debug)]
pub struct ProofRange {
pub min: u64,
pub max: u64,
}
#[derive(Debug)]
pub struct ProofInfo {
pub success: bool,
pub value: u64,
pub blinding: SecretKey,
pub message: ProofMessage,
pub mlen: usize,
pub min: u64,
pub max: u64,
pub exp: i32,
pub mantissa: i32,
}
impl ::std::fmt::Debug for RangeProof {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}(", stringify!(RangeProof))?;
for i in self.proof[..self.plen].iter().cloned() {
write!(f, "{:02x}", i)?;
}
write!(f, ")[{}]", self.plen)
}
}
impl Secp256k1 {
pub fn verify_from_commit(
&self,
msg: &Message,
sig: &Signature,
commit: &Commitment,
) -> Result<(), Error> {
if self.caps != ContextFlag::Commit {
return Err(Error::IncapableContext);
}
let pubkey = commit.to_pubkey(&self).unwrap();
let result = self.verify(msg, sig, &pubkey);
match result {
Ok(x) => Ok(x),
Err(_) => result,
}
}
fn commit_parse(&self, c_in: [u8;constants::PEDERSEN_COMMITMENT_SIZE])
-> Result<CommitmentInternal, Error> {
let c_out = unsafe {
let mut c_out = CommitmentInternal::blank();
ffi::secp256k1_pedersen_commitment_parse(
self.ctx,
c_out.as_mut_ptr(),
c_in.as_ptr(),
);
c_out
};
Ok(c_out)
}
fn commit_ser(&self, c_in: [u8;constants::PEDERSEN_COMMITMENT_SIZE_INTERNAL])
-> Result<Commitment, Error> {
let c_out = unsafe {
let mut c_out = Commitment::blank();
ffi:: secp256k1_pedersen_commitment_serialize(
self.ctx,
c_out.as_mut_ptr(),
c_in.as_ptr(),
);
c_out
};
Ok(c_out)
}
pub fn commit(&self, value: u64, blind: SecretKey) -> Result<Commitment, Error> {
if self.caps != ContextFlag::Commit {
return Err(Error::IncapableContext);
}
let mut commit_i = [0; constants::PEDERSEN_COMMITMENT_SIZE_INTERNAL];
unsafe {
ffi::secp256k1_pedersen_commit(
self.ctx,
commit_i.as_mut_ptr(),
blind.as_ptr(),
value,
constants::GENERATOR_H.as_ptr(),
constants::GENERATOR_G.as_ptr(),
)
};
Ok(self.commit_ser(commit_i)?)
}
pub fn commit_blind(&self, value: SecretKey, blind: SecretKey) -> Result<Commitment, Error> {
if self.caps != ContextFlag::Commit {
return Err(Error::IncapableContext);
}
let mut commit_i = [0; constants::PEDERSEN_COMMITMENT_SIZE_INTERNAL];
unsafe {
ffi::secp256k1_pedersen_blind_commit(
self.ctx,
commit_i.as_mut_ptr(),
blind.as_ptr(),
value.as_ptr(),
constants::GENERATOR_H.as_ptr(),
constants::GENERATOR_G.as_ptr(),
)
};
Ok(self.commit_ser(commit_i)?)
}
pub fn commit_value(&self, value: u64) -> Result<Commitment, Error> {
if self.caps != ContextFlag::Commit {
return Err(Error::IncapableContext);
}
let mut commit_i = [0; constants::PEDERSEN_COMMITMENT_SIZE_INTERNAL];
let zblind = [0u8; 32];
unsafe {
ffi::secp256k1_pedersen_commit(
self.ctx,
commit_i.as_mut_ptr(),
zblind.as_ptr(),
value,
constants::GENERATOR_H.as_ptr(),
constants::GENERATOR_G.as_ptr(),
)
};
Ok(self.commit_ser(commit_i)?)
}
pub fn verify_commit_sum(&self, positive: Vec<Commitment>, negative: Vec<Commitment>) -> bool {
let pos = map_vec!(positive, |p| { self.commit_parse(p.0).unwrap() });
let neg = map_vec!(negative, |n| self.commit_parse(n.0).unwrap());
let pos = map_vec!(pos, |p| p.0.as_ptr());
let neg = map_vec!(neg, |n| n.0.as_ptr());
unsafe {
ffi::secp256k1_pedersen_verify_tally(
self.ctx,
pos.as_ptr(),
pos.len() as size_t,
neg.as_ptr(),
neg.len() as size_t,
) == 1
}
}
pub fn commit_sum(
&self,
positive: Vec<Commitment>,
negative: Vec<Commitment>,
) -> Result<Commitment, Error> {
let pos = map_vec!(positive, |p| self.commit_parse(p.0).unwrap());
let neg = map_vec!(negative, |n| self.commit_parse(n.0).unwrap());
let pos = map_vec!(pos, |p| p.0.as_ptr());
let neg = map_vec!(neg, |n| n.0.as_ptr());
let mut ret_i = unsafe { CommitmentInternal::blank() };
let err = unsafe {
ffi::secp256k1_pedersen_commit_sum(
self.ctx,
ret_i.as_mut_ptr(),
pos.as_ptr(),
pos.len() as size_t,
neg.as_ptr(),
neg.len() as size_t,
)
};
if err == 1 {
Ok(self.commit_ser(ret_i.0)?)
} else {
Err(Error::IncorrectCommitSum)
}
}
pub fn blind_sum(
&self,
positive: Vec<SecretKey>,
negative: Vec<SecretKey>,
) -> Result<SecretKey, Error> {
let mut neg = map_vec!(negative, |n| n.as_ptr());
let mut all = map_vec!(positive, |p| p.as_ptr());
all.append(&mut neg);
let mut ret: [u8; 32] = unsafe { mem::MaybeUninit::uninit().assume_init() };
unsafe {
assert_eq!(
ffi::secp256k1_pedersen_blind_sum(
self.ctx,
ret.as_mut_ptr(),
all.as_ptr(),
all.len() as size_t,
positive.len() as size_t,
),
1
);
}
SecretKey::from_slice(self, &ret)
}
pub fn blind_switch(&self, value: u64, blind: SecretKey) -> Result<SecretKey, Error> {
if self.caps != ContextFlag::Commit {
return Err(Error::IncapableContext);
}
let mut ret: [u8; 32] = unsafe { mem::MaybeUninit::uninit().assume_init() };
unsafe {
assert_eq!(
ffi::secp256k1_blind_switch(
self.ctx,
ret.as_mut_ptr(),
blind.as_ptr(),
value,
constants::GENERATOR_H.as_ptr(),
constants::GENERATOR_G.as_ptr(),
constants::GENERATOR_PUB_J_RAW.as_ptr(),
),
1
)
}
SecretKey::from_slice(self, &ret)
}
pub fn nonce(&self) -> [u8; 32] {
thread_rng().gen::<[u8; 32]>()
}
pub fn range_proof(
&self,
min: u64,
value: u64,
blind: SecretKey,
commit: Commitment,
message: ProofMessage,
) -> RangeProof {
let mut retried = false;
let mut proof = [0; constants::MAX_PROOF_SIZE];
let mut plen = constants::MAX_PROOF_SIZE as size_t;
let nonce = blind.clone();
let extra_commit = [0u8; 33];
let commit = self.commit_parse(commit.0).unwrap();
loop {
let success = unsafe {
ffi::secp256k1_rangeproof_sign(
self.ctx,
proof.as_mut_ptr(),
&mut plen,
min,
commit.as_ptr(),
blind.as_ptr(),
nonce.as_ptr(),
0,
64,
value,
message.as_ptr(),
message.len(),
extra_commit.as_ptr(),
0 as size_t,
constants::GENERATOR_H.as_ptr(),
) == 1
};
if success || retried {
break;
} else {
retried = true;
}
}
RangeProof {
proof: proof,
plen: plen as usize,
}
}
pub fn verify_range_proof(
&self,
commit: Commitment,
proof: RangeProof,
) -> Result<ProofRange, Error> {
let mut min: u64 = 0;
let mut max: u64 = 0;
let extra_commit = [0u8; 33];
let commit = self.commit_parse(commit.0)?;
let success = unsafe {
ffi::secp256k1_rangeproof_verify(
self.ctx,
&mut min,
&mut max,
commit.as_ptr(),
proof.proof.as_ptr(),
proof.plen as size_t,
extra_commit.as_ptr(),
0 as size_t,
constants::GENERATOR_H.as_ptr(),
) == 1
};
if success {
Ok(ProofRange { min: min, max: max })
} else {
Err(Error::InvalidRangeProof)
}
}
pub fn rewind_range_proof(
&self,
commit: Commitment,
proof: RangeProof,
nonce: SecretKey,
) -> ProofInfo {
let mut value: u64 = 0;
let mut blind: [u8; 32] = unsafe { mem::MaybeUninit::uninit().assume_init() };
let mut message: [u8; constants::PROOF_MSG_SIZE] = unsafe { mem::MaybeUninit::uninit().assume_init() };
let mut mlen: usize = constants::PROOF_MSG_SIZE;
let mut min: u64 = 0;
let mut max: u64 = 0;
let extra_commit = [0u8; 33];
let commit = self.commit_parse(commit.0).unwrap();
let success = unsafe {
ffi::secp256k1_rangeproof_rewind(
self.ctx,
blind.as_mut_ptr(),
&mut value,
message.as_mut_ptr(),
&mut mlen,
nonce.as_ptr(),
&mut min,
&mut max,
commit.as_ptr(),
proof.proof.as_ptr(),
proof.plen as size_t,
extra_commit.as_ptr(),
0 as size_t,
constants::GENERATOR_H.as_ptr(),
) == 1
};
ProofInfo {
success: success,
value: value,
message: ProofMessage::from_bytes(&message),
blinding: SecretKey([0; constants::SECRET_KEY_SIZE]),
mlen: mlen,
min: min,
max: max,
exp: 0,
mantissa: 0,
}
}
pub fn range_proof_info(&self, proof: RangeProof) -> ProofInfo {
let mut exp: i32 = 0;
let mut mantissa: i32 = 0;
let mut min: u64 = 0;
let mut max: u64 = 0;
let success = unsafe {
ffi::secp256k1_rangeproof_info(
self.ctx,
&mut exp,
&mut mantissa,
&mut min,
&mut max,
proof.proof.as_ptr(),
proof.plen as size_t,
) == 1
};
ProofInfo {
success: success,
value: 0,
message: ProofMessage::empty(),
blinding: SecretKey([0; constants::SECRET_KEY_SIZE]),
mlen: 0,
min: min,
max: max,
exp: exp,
mantissa: mantissa,
}
}
pub fn bullet_proof(
&self,
value: u64,
blind: SecretKey,
rewind_nonce: SecretKey,
private_nonce: SecretKey,
extra_data_in: Option<Vec<u8>>,
message: Option<ProofMessage>,
) -> RangeProof {
let mut proof = [0; constants::MAX_PROOF_SIZE];
let mut plen = constants::MAX_PROOF_SIZE as size_t;
let blind_vec: Vec<SecretKey> = vec![blind];
let blind_vec = map_vec!(blind_vec, |p| p.0.as_ptr());
let n_bits = 64;
let (extra_data_len, extra_data) = match extra_data_in.as_ref() {
Some(d) => (d.len(), d.as_ptr()),
None => (0, ptr::null()),
};
let mut message = message;
let message_ptr = match message.as_mut() {
Some(m) => {
while m.len() < constants::BULLET_PROOF_MSG_SIZE {
m.push(0u8);
}
m.truncate(constants::BULLET_PROOF_MSG_SIZE);
m.as_ptr()
},
None => ptr::null(),
};
let tau_x = ptr::null_mut();
let t_one = ptr::null_mut();
let t_two = ptr::null_mut();
let commits = ptr::null_mut();
let _success = unsafe {
let scratch = ffi::secp256k1_scratch_space_create(self.ctx, SCRATCH_SPACE_SIZE);
let result = ffi::secp256k1_bulletproof_rangeproof_prove(
self.ctx,
scratch,
shared_generators(self.ctx),
proof.as_mut_ptr(),
&mut plen,
tau_x,
t_one,
t_two,
&value,
ptr::null(), blind_vec.as_ptr(),
commits,
1,
constants::GENERATOR_H.as_ptr(),
n_bits as size_t,
rewind_nonce.as_ptr(),
private_nonce.as_ptr(),
extra_data,
extra_data_len as size_t,
message_ptr,
);
ffi::secp256k1_scratch_space_destroy(scratch);
result == 1
};
RangeProof {
proof: proof,
plen: plen as usize,
}
}
pub fn bullet_proof_multisig(
&self,
value: u64,
blind: SecretKey,
nonce: SecretKey,
extra_data_in: Option<Vec<u8>>,
message: Option<ProofMessage>,
tau_x: Option<&mut SecretKey>,
t_one: Option<&mut PublicKey>,
t_two: Option<&mut PublicKey>,
commits: Vec<Commitment>,
private_nonce: Option<&SecretKey>,
step: u8, ) -> Option<RangeProof> {
let last_step = if 0 == step { true } else { false };
let first_step = if 1 == step { true } else { false };
let mut proof = [0; constants::MAX_PROOF_SIZE];
let mut plen = constants::MAX_PROOF_SIZE as size_t;
let blind_vec: Vec<SecretKey> = vec![blind];
let blind_vec = map_vec!(blind_vec, |p| p.0.as_ptr());
let n_bits = 64;
let (extra_data_len, extra_data) = match extra_data_in.as_ref() {
Some(d) => (d.len(), d.as_ptr()),
None => (0, ptr::null()),
};
let mut message = message;
let message_ptr = match message.as_mut() {
Some(m) => {
while m.len() < constants::BULLET_PROOF_MSG_SIZE {
m.push(0u8);
}
m.truncate(constants::BULLET_PROOF_MSG_SIZE);
m.as_ptr()
},
None => ptr::null(),
};
let tau_x = match tau_x {
Some(n) => n.0.as_mut_ptr(),
None => ptr::null_mut(),
};
let t_one_ptr;
let t_two_ptr;
if first_step {
t_one_ptr = is_zero_pubkey!(ignore => t_one);
t_two_ptr = is_zero_pubkey!(ignore => t_two);
} else {
t_one_ptr = is_zero_pubkey!(retnone => t_one);
t_two_ptr = is_zero_pubkey!(retnone => t_two);
};
let commit_vec;
let commit_ptr_vec;
let commit_ptr_vec_ptr = if commits.len() > 0 {
commit_vec = map_vec!(commits, |c| self.commit_parse(c.0).unwrap());
commit_ptr_vec = map_vec!(commit_vec, |c| c.as_ptr());
commit_ptr_vec.as_ptr()
} else {
ptr::null()
};
let private_nonce = match private_nonce {
Some(n) => n.as_ptr(),
None => ptr::null(),
};
let _success = unsafe {
let scratch = ffi::secp256k1_scratch_space_create(self.ctx, SCRATCH_SPACE_SIZE);
let result = ffi::secp256k1_bulletproof_rangeproof_prove(
self.ctx,
scratch,
shared_generators(self.ctx),
if last_step {
proof.as_mut_ptr()
} else {
ptr::null_mut()
},
if last_step {
&mut plen
} else {
ptr::null_mut()
},
tau_x,
t_one_ptr,
t_two_ptr,
&value,
ptr::null(), blind_vec.as_ptr(),
commit_ptr_vec_ptr,
1,
constants::GENERATOR_H.as_ptr(),
n_bits as size_t,
nonce.as_ptr(),
private_nonce,
extra_data,
extra_data_len as size_t,
message_ptr,
);
ffi::secp256k1_scratch_space_destroy(scratch);
result == 1
};
if last_step {
Some(RangeProof {
proof: proof,
plen: plen as usize,
})
} else {
None
}
}
pub fn verify_bullet_proof(
&self,
commit: Commitment,
proof: RangeProof,
extra_data_in: Option<Vec<u8>>,
) -> Result<ProofRange, Error> {
let n_bits = 64;
let extra_data;
let (extra_data_len, extra_data) = match extra_data_in {
Some(d) => {
extra_data = d;
(extra_data.len(), extra_data.as_ptr())
},
None => (0, ptr::null()),
};
let commit = self.commit_parse(commit.0).unwrap();
let success = unsafe {
let scratch = ffi::secp256k1_scratch_space_create(self.ctx, SCRATCH_SPACE_SIZE);
let result = ffi::secp256k1_bulletproof_rangeproof_verify(
self.ctx,
scratch,
shared_generators(self.ctx),
proof.proof.as_ptr(),
proof.plen as size_t,
ptr::null(), commit.0.as_ptr(),
1,
n_bits as size_t,
constants::GENERATOR_H.as_ptr(),
extra_data,
extra_data_len as size_t,
);
ffi::secp256k1_scratch_space_destroy(scratch);
result == 1
};
if success {
Ok(ProofRange {
min: 0,
max: u64::MAX,
})
} else {
Err(Error::InvalidRangeProof)
}
}
pub fn verify_bullet_proof_multi(
&self,
commits: Vec<Commitment>,
proofs: Vec<RangeProof>,
extra_data_in: Option<Vec<Vec<u8>>>,
) -> Result<ProofRange, Error> {
let n_bits = 64;
let proof_size = if proofs.len() > 0 {
proofs[0].plen
} else {
constants::SINGLE_BULLET_PROOF_SIZE
};
let commit_vec = map_vec!(commits, |c| self.commit_parse(c.0).unwrap());
let commit_vec = map_vec!(commit_vec, |c| c.as_ptr());
let proof_vec = map_vec!(proofs, |p| p.proof.as_ptr());
let value_gen_vec = {
let min_len = if proof_vec.len() > 0 {
proof_vec.len()
} else {
1
};
let gen_size = constants::GENERATOR_SIZE;
let mut value_gen_vec = vec![0; min_len * gen_size];
for i in 0..min_len {
value_gen_vec[i * gen_size..(i + 1) * gen_size]
.clone_from_slice(&constants::GENERATOR_H[..]);
}
value_gen_vec
};
let (extra_data_vec, extra_data_lengths) = match extra_data_in.as_ref() {
Some(ed) => {
let extra_data_vec = map_vec!(ed, |d| d.as_ptr());
let extra_data_lengths = map_vec![ed, |d| d.len()];
(extra_data_vec, extra_data_lengths)
}
None => {
let extra_data_vec = vec![ptr::null(); proof_vec.len()];
let extra_data_lengths = vec![0; proof_vec.len()];
(extra_data_vec, extra_data_lengths)
}
};
let success = unsafe {
let scratch = ffi::secp256k1_scratch_space_create(self.ctx, SCRATCH_SPACE_SIZE);
let result = ffi::secp256k1_bulletproof_rangeproof_verify_multi(
self.ctx,
scratch,
shared_generators(self.ctx),
proof_vec.as_ptr(),
proof_vec.len(),
proof_size,
ptr::null(), commit_vec.as_ptr(),
1,
n_bits as size_t,
value_gen_vec.as_ptr(),
extra_data_vec.as_ptr(),
extra_data_lengths.as_ptr(),
);
ffi::secp256k1_scratch_space_destroy(scratch);
result == 1
};
if success {
Ok(ProofRange {
min: 0,
max: u64::MAX,
})
} else {
Err(Error::InvalidRangeProof)
}
}
pub fn rewind_bullet_proof(
&self,
commit: Commitment,
nonce: SecretKey,
extra_data_in: Option<Vec<u8>>,
proof: RangeProof,
) -> Result<ProofInfo, Error> {
let (extra_data_len, extra_data) = match extra_data_in.as_ref() {
Some(d) => (d.len(), d.as_ptr()),
None => (0, ptr::null()),
};
let mut blind_out = [0u8; constants::SECRET_KEY_SIZE];
let mut value_out = 0;
let mut message_out = [0u8; 20];
let commit = self.commit_parse(commit.0)?;
let success = unsafe {
let scratch = ffi::secp256k1_scratch_space_create(self.ctx, SCRATCH_SPACE_SIZE);
let result = ffi::secp256k1_bulletproof_rangeproof_rewind(
self.ctx,
&mut value_out,
blind_out.as_mut_ptr(),
proof.proof.as_ptr(),
proof.plen as size_t,
0,
commit.as_ptr(),
constants::GENERATOR_H.as_ptr(),
nonce.as_ptr(),
extra_data,
extra_data_len as size_t,
message_out.as_mut_ptr(),
);
ffi::secp256k1_scratch_space_destroy(scratch);
result == 1
};
if success {
Ok(ProofInfo {
success: true,
value: value_out,
blinding: SecretKey(blind_out),
message: ProofMessage::from_bytes(&message_out),
mlen: 0,
min: 0,
max: u64::MAX,
exp: 0,
mantissa: 0,
})
} else {
Err(Error::InvalidRangeProof)
}
}
}
#[cfg(test)]
mod tests {
extern crate chrono;
use super::{Commitment, Error, Message, ProofMessage, ProofRange, RangeProof, Secp256k1};
use crate::key::{PublicKey, SecretKey, ONE_KEY, ZERO_KEY};
use crate::ContextFlag;
use crate::constants;
use rand::{thread_rng, Rng};
use crate::pedersen::tests::chrono::prelude::*;
#[test]
fn commit_parse_ser() {
fn commit(value: u64) -> Commitment {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = ZERO_KEY;
secp.commit(value, blinding).unwrap()
}
let two_g:[u8; 33] = [ 0x09,
0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8,
0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5
];
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let commit_i = secp.commit_parse(two_g).unwrap();
let comm = secp.commit_ser(commit_i.0).unwrap();
assert_eq!(comm, Commitment(two_g));
let c5 = commit(5);
let commit_i = secp.commit_parse(c5.0).unwrap();
let comm = secp.commit_ser(commit_i.0).unwrap();
assert_eq!(comm, c5);
}
#[test]
fn test_verify_commit_sum_zero_keys() {
fn commit(value: u64) -> Commitment {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = ZERO_KEY;
secp.commit(value, blinding).unwrap()
}
let secp = Secp256k1::with_caps(ContextFlag::Commit);
assert!(secp.verify_commit_sum(vec![], vec![],));
assert!(secp.verify_commit_sum(vec![commit(5)], vec![commit(5)],));
assert!(secp.verify_commit_sum(vec![commit(3), commit(2)], vec![commit(5)]));
assert!(secp.verify_commit_sum(vec![commit(2), commit(4)], vec![commit(1), commit(5)]));
}
#[test]
fn test_verify_commit_sum_one_keys() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
fn commit(value: u64, blinding: SecretKey) -> Commitment {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
secp.commit(value, blinding).unwrap()
}
assert!(secp.verify_commit_sum(vec![commit(5, ONE_KEY)], vec![commit(5, ONE_KEY)]));
assert_eq!(
secp.verify_commit_sum(
vec![commit(3, ONE_KEY), commit(2, ONE_KEY)],
vec![commit(5, ONE_KEY)],
),
false
);
let two_key = secp.blind_sum(vec![ONE_KEY, ONE_KEY], vec![]).unwrap();
assert!(secp.verify_commit_sum(
vec![commit(3, ONE_KEY), commit(2, ONE_KEY)],
vec![commit(5, two_key)],
));
}
#[test]
fn test_verify_commit_sum_random_keys() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
fn commit(value: u64, blinding: SecretKey) -> Commitment {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
secp.commit(value, blinding).unwrap()
}
let blind_pos = SecretKey::new(&secp, &mut thread_rng());
let blind_neg = SecretKey::new(&secp, &mut thread_rng());
let blind_sum = secp.blind_sum(vec![blind_pos.clone()], vec![blind_neg.clone()]).unwrap();
assert!(secp.verify_commit_sum(
vec![commit(101, blind_pos)],
vec![commit(75, blind_neg), commit(26, blind_sum)],
));
}
#[test]
fn test_verify_commit_sum_random_keys_switch() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
fn commit(value: u64, blinding: SecretKey) -> Commitment {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
secp.commit(value, blinding).unwrap()
}
let pos_value = 101;
let neg_value = 75;
let blind_pos = secp.blind_switch(pos_value, SecretKey::new(&secp, &mut thread_rng())).unwrap();
let blind_neg = secp.blind_switch(neg_value, SecretKey::new(&secp, &mut thread_rng())).unwrap();
let blind_sum = secp.blind_sum(vec![blind_pos.clone()], vec![blind_neg.clone()]).unwrap();
let diff = pos_value - neg_value;
assert!(secp.verify_commit_sum(
vec![commit(pos_value, blind_pos)],
vec![commit(neg_value, blind_neg), commit(diff, blind_sum)],
));
}
#[test]
fn test_to_pubkey() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = SecretKey::new(&secp, &mut thread_rng());
let commit = secp.commit(5, blinding).unwrap();
let pubkey = commit.to_pubkey(&secp);
match pubkey {
Ok(_) => {
}
Err(_) => {
panic!("this is not good");
}
}
}
#[test]
fn test_from_pubkey() {
for _ in 0..100 {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = SecretKey::new(&secp, &mut thread_rng());
let commit = secp.commit(1, blinding).unwrap();
let pubkey = commit.to_pubkey(&secp);
let p = match pubkey {
Ok(p) => {
p
}
Err(e) => {
panic!("Creating pubkey: {}", e);
}
};
let new_commit = Commitment::from_pubkey(&secp, &p);
let commit2 = match new_commit {
Ok(c) => {
c
}
Err(e) => {
panic!("Creating commit from Pubkey: {}", e);
}
};
assert_eq!(commit, commit2);
}
}
#[test]
fn test_sign_with_pubkey_from_commitment() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = SecretKey::new(&secp, &mut thread_rng());
let commit = secp.commit(0u64, blinding.clone()).unwrap();
let mut msg = [0u8; 32];
thread_rng().fill(&mut msg);
let msg = Message::from_slice(&msg).unwrap();
let sig = secp.sign(&msg, &blinding).unwrap();
let pubkey = commit.to_pubkey(&secp).unwrap();
if let Ok(_) = secp.verify(&msg, &sig, &pubkey) {
} else {
panic!("this is not good");
}
}
#[test]
fn test_commit_sum() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
fn commit(value: u64, blinding: SecretKey) -> Commitment {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
secp.commit(value, blinding).unwrap()
}
let blind_a = SecretKey::new(&secp, &mut thread_rng());
let blind_b = SecretKey::new(&secp, &mut thread_rng());
let commit_a = commit(3, blind_a.clone());
let commit_b = commit(2, blind_b.clone());
let blind_c = secp.blind_sum(vec![blind_a.clone(), blind_b.clone()], vec![]).unwrap();
let commit_c = commit(3 + 2, blind_c);
let commit_d = secp.commit_sum(vec![commit_a.clone(), commit_b.clone()], vec![]).unwrap();
assert_eq!(commit_c, commit_d);
let blind_e = secp.blind_sum(vec![blind_a.clone()], vec![blind_b.clone()]).unwrap();
let commit_e = commit(3 - 2, blind_e);
let commit_f = secp.commit_sum(vec![commit_a], vec![commit_b]).unwrap();
assert_eq!(commit_e, commit_f);
}
#[test]
fn test_blind_commit() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let rng = &mut thread_rng();
let value: u64 = 1;
let blind = SecretKey::new(&secp, rng);
let blind2 = ONE_KEY;
assert_eq!(secp.commit(value, blind.clone()).unwrap(), secp.commit_blind(blind2.clone(), blind.clone()).unwrap());
let value: u64 = 2;
let blind = SecretKey::new(&secp, rng);
assert_ne!(secp.commit(value, blind.clone()).unwrap(), secp.commit_blind(blind2, blind.clone()).unwrap());
let blind = SecretKey::new(&secp, rng);
let mut blind2 = ZERO_KEY;
blind2.0[30] = rng.gen::<u8>();
blind2.0[31] = rng.gen::<u8>();
let value: u64 = blind2[30] as u64*256 + blind2[31] as u64;
assert_eq!(secp.commit(value, blind.clone()).unwrap(), secp.commit_blind(blind2, blind.clone()).unwrap());
}
#[test]
fn test_range_proof() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = SecretKey::new(&secp, &mut thread_rng());
let commit = secp.commit(7, blinding.clone()).unwrap();
let msg = ProofMessage::empty();
let range_proof = secp.range_proof(0, 7, blinding.clone(), commit, msg.clone());
let proof_range = secp.verify_range_proof(commit, range_proof).unwrap();
assert_eq!(proof_range.min, 0);
let proof_info = secp.range_proof_info(range_proof);
assert!(proof_info.success);
assert_eq!(proof_info.min, 0);
assert_eq!(proof_info.value, 0);
let proof_info = secp.rewind_range_proof(commit, range_proof, blinding.clone());
assert!(proof_info.success);
assert_eq!(proof_info.min, 0);
assert_eq!(proof_info.value, 7);
let bad_nonce = SecretKey::new(&secp, &mut thread_rng());
let bad_info = secp.rewind_range_proof(commit, range_proof, bad_nonce);
assert_eq!(bad_info.success, false);
assert_eq!(bad_info.value, 0);
let commit = secp.commit(0, blinding.clone()).unwrap();
let range_proof = secp.range_proof(0, 0, blinding.clone(), commit, msg);
secp.verify_range_proof(commit, range_proof).unwrap();
let proof_info = secp.rewind_range_proof(commit, range_proof, blinding);
assert!(proof_info.success);
assert_eq!(proof_info.min, 0);
assert_eq!(proof_info.value, 0);
}
#[test]
fn test_bullet_proof_single() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = SecretKey::new(&secp, &mut thread_rng());
let value = 12345678;
let commit = secp.commit(value, blinding.clone()).unwrap();
let bullet_proof = secp.bullet_proof(value, blinding.clone(), blinding.clone(), blinding.clone(), None, None);
println!("Bullet proof len: {}", bullet_proof.plen);
let proof_range = secp
.verify_bullet_proof(commit, bullet_proof, None)
.unwrap();
assert_eq!(proof_range.min, 0);
let value = 12345678;
let wrong_commit = secp.commit(87654321, blinding.clone()).unwrap();
let bullet_proof = secp.bullet_proof(value, blinding.clone(), blinding.clone(), blinding.clone(), None, None);
if !secp
.verify_bullet_proof(wrong_commit, bullet_proof, None)
.is_err()
{
panic!("Bullet proof verify should have errored");
}
let value = 12345678;
let commit = secp.commit(value, blinding).unwrap();
let blinding = SecretKey::new(&secp, &mut thread_rng());
let bullet_proof = secp.bullet_proof(value, blinding.clone(), blinding.clone(), blinding.clone(), None, None);
if !secp
.verify_bullet_proof(commit, bullet_proof, None)
.is_err()
{
panic!("Bullet proof verify should have errored");
}
let extra_data = [0u8; 32].to_vec();
let blinding = SecretKey::new(&secp, &mut thread_rng());
let value = 12345678;
let commit = secp.commit(value, blinding.clone()).unwrap();
let bullet_proof =
secp.bullet_proof(value, blinding.clone(), blinding.clone(), blinding.clone(), Some(extra_data.clone()), None);
if secp
.verify_bullet_proof(commit, bullet_proof, Some(extra_data.clone()))
.is_err()
{
panic!("Bullet proof verify should NOT have errored.");
}
let mut malleated_extra_data = [0u8; 32];
malleated_extra_data[0] = 1;
let res = secp.verify_bullet_proof(
commit,
bullet_proof,
Some(malleated_extra_data.clone().to_vec()),
);
if !res.is_err() {
panic!("Bullet proof verify should have errored: {:?}", res);
}
let blinding = SecretKey::new(&secp, &mut thread_rng());
let rewind_nonce = SecretKey::new(&secp, &mut thread_rng());
let private_nonce = SecretKey::new(&secp, &mut thread_rng());
let value = 12345678;
let commit = secp.commit(value, blinding.clone()).unwrap();
let bullet_proof =
secp.bullet_proof(value, blinding.clone(), private_nonce.clone(), private_nonce.clone(), Some(extra_data.clone()), None);
let proof_info = secp
.rewind_bullet_proof(commit, private_nonce.clone(), Some(extra_data.clone()), bullet_proof)
.unwrap();
assert_eq!(proof_info.value, value);
assert_eq!(blinding, proof_info.blinding);
let proof_info = secp.rewind_bullet_proof(
commit,
blinding.clone(),
Some(extra_data.clone().to_vec()),
bullet_proof,
);
if !proof_info.is_err() {
panic!("Bullet proof verify with message should have errored.");
}
let proof_info = secp.rewind_bullet_proof(commit, private_nonce.clone(), None, bullet_proof);
if !proof_info.is_err() {
panic!("Bullet proof verify with message should have errored.");
}
let message_bytes: [u8; 20] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
let message = ProofMessage::from_bytes(&message_bytes);
let bullet_proof = secp.bullet_proof(
value,
blinding.clone(),
rewind_nonce.clone(),
private_nonce.clone(),
Some(extra_data.clone()),
Some(message.clone()),
);
let proof_info = secp
.rewind_bullet_proof(commit, rewind_nonce, Some(extra_data.clone()), bullet_proof)
.unwrap();
assert_eq!(proof_info.value, value);
assert_eq!(proof_info.message, message);
}
#[test]
fn test_bullet_proof_multisig() {
let multisig_bp =
|v, nonce: SecretKey, ca, cb, ba, bb, msg, extra| -> (RangeProof, Result<ProofRange, Error>) {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding_a: SecretKey = ba;
let value: u64 = v;
let partial_commit_a: Commitment = ca;
let blinding_b: SecretKey = bb;
let partial_commit_b: Commitment = cb;
let message: Option<ProofMessage> = msg;
let extra_data: Option<Vec<u8>> = extra;
let commit = secp
.commit_sum(vec![partial_commit_a, partial_commit_b], vec![])
.unwrap();
let mut commits = vec![];
commits.push(commit);
let common_nonce = nonce;
let private_nonce_a = SecretKey::new(&secp, &mut thread_rng());
let private_nonce_b = SecretKey::new(&secp, &mut thread_rng());
let mut t_one_a = PublicKey::new();
let mut t_two_a = PublicKey::new();
secp.bullet_proof_multisig(
value,
blinding_a.clone(),
common_nonce.clone(),
extra_data.clone(),
message.clone(),
None,
Some(&mut t_one_a),
Some(&mut t_two_a),
commits.clone(),
Some(&private_nonce_a),
1,
);
let mut t_one_b = PublicKey::new();
let mut t_two_b = PublicKey::new();
secp.bullet_proof_multisig(
value,
blinding_b.clone(),
common_nonce.clone(),
extra_data.clone(),
message.clone(),
None,
Some(&mut t_one_b),
Some(&mut t_two_b),
commits.clone(),
Some(&private_nonce_b),
1,
);
let mut pubkeys = vec![];
pubkeys.push(&t_one_a);
pubkeys.push(&t_one_b);
let mut t_one_sum = PublicKey::from_combination(&secp, pubkeys.clone()).unwrap();
pubkeys.clear();
pubkeys.push(&t_two_a);
pubkeys.push(&t_two_b);
let mut t_two_sum = PublicKey::from_combination(&secp, pubkeys.clone()).unwrap();
let mut tau_x_a = SecretKey::new(&secp, &mut thread_rng());
secp.bullet_proof_multisig(
value,
blinding_a.clone(),
common_nonce.clone(),
extra_data.clone(),
message.clone(),
Some(&mut tau_x_a),
Some(&mut t_one_sum),
Some(&mut t_two_sum),
commits.clone(),
Some(&private_nonce_a),
2,
);
let mut tau_x_b = SecretKey::new(&secp, &mut thread_rng());
secp.bullet_proof_multisig(
value,
blinding_b.clone(),
common_nonce.clone(),
extra_data.clone(),
message.clone(),
Some(&mut tau_x_b),
Some(&mut t_one_sum),
Some(&mut t_two_sum),
commits.clone(),
Some(&private_nonce_b),
2,
);
let mut tau_x_sum = tau_x_a;
tau_x_sum.add_assign(&secp, &tau_x_b).unwrap();
let bullet_proof =
secp.bullet_proof_multisig(
value,
blinding_a.clone(),
common_nonce.clone(),
extra_data.clone(),
message.clone(),
Some(&mut tau_x_sum),
Some(&mut t_one_sum),
Some(&mut t_two_sum),
commits.clone(),
Some(&private_nonce_a),
0,
).unwrap();
println!("MultiSig Bullet proof len: {:}", bullet_proof.len());
let proof_range = secp.verify_bullet_proof(commit, bullet_proof, None);
return (bullet_proof, proof_range);
};
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let value: u64 = 12345678;
let common_nonce = SecretKey::new(&secp, &mut thread_rng());
let blinding_a = SecretKey::new(&secp, &mut thread_rng());
let partial_commit_a = secp.commit(value, blinding_a.clone()).unwrap();
let blinding_b = SecretKey::new(&secp, &mut thread_rng());
let partial_commit_b = secp.commit(0, blinding_b.clone()).unwrap();
let (_, proof_range) = multisig_bp(
value,
common_nonce.clone(),
partial_commit_a,
partial_commit_b,
blinding_a.clone(),
blinding_b.clone(),
None,
None,
);
assert_eq!(proof_range.unwrap().min, 0);
let wrong_partial_commit_a = secp.commit(87654321, blinding_a.clone()).unwrap();
let (_, proof_range) = multisig_bp(
value,
common_nonce.clone(),
wrong_partial_commit_a,
partial_commit_b,
blinding_a.clone(),
blinding_b.clone(),
None,
None,
);
if !proof_range.is_err() {
panic!("Multi-Sig Bullet proof verify should have error");
}
let wrong_blinding = SecretKey::new(&secp, &mut thread_rng());
let (_, proof_range) = multisig_bp(
value,
common_nonce.clone(),
partial_commit_a,
partial_commit_b,
wrong_blinding,
blinding_b.clone(),
None,
None,
);
if !proof_range.is_err() {
panic!("Multi-Sig Bullet proof verify should have error");
}
let message_bytes: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let message = ProofMessage::from_bytes(&message_bytes);
let (_, proof_range) = multisig_bp(
value,
common_nonce,
partial_commit_a,
partial_commit_b,
blinding_a,
blinding_b,
Some(message.clone()),
None,
);
assert_eq!(proof_range.unwrap().min, 0);
}
#[test]
fn rewind_empty_message() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = SecretKey::new(&secp, &mut thread_rng());
let nonce = SecretKey::new(&secp, &mut thread_rng());
let value = <u64>::max_value() - 1;
let commit = secp.commit(value, blinding.clone()).unwrap();
let mut pm = ProofMessage::from_bytes(&[0u8;32]);
let bullet_proof = secp.bullet_proof(value, blinding.clone(), nonce.clone(), nonce.clone(), None, Some(pm.clone()));
let proof_info = secp
.rewind_bullet_proof(commit, nonce, None, bullet_proof)
.unwrap();
assert_eq!(proof_info.value, value);
assert_eq!(blinding, proof_info.blinding);
pm.truncate(constants::BULLET_PROOF_MSG_SIZE);
assert_eq!(pm, proof_info.message);
}
#[test]
fn rewind_message() {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = SecretKey::new(&secp, &mut thread_rng());
let nonce = SecretKey::new(&secp, &mut thread_rng());
let value = <u64>::max_value() - 1;
let commit = secp.commit(value, blinding.clone()).unwrap();
let bullet_proof = secp.bullet_proof(value, blinding.clone(), nonce.clone(), nonce.clone(), None, None);
let proof_info = secp
.rewind_bullet_proof(commit, nonce.clone(), None, bullet_proof)
.unwrap();
assert_eq!(proof_info.value, value);
assert_eq!(blinding, proof_info.blinding);
let private_nonce = SecretKey::new(&secp, &mut thread_rng());
let bullet_proof = secp.bullet_proof(value, blinding.clone(), nonce.clone(), private_nonce.clone(), None, None);
let proof_info = secp
.rewind_bullet_proof(commit, nonce, None, bullet_proof)
.unwrap();
assert_eq!(proof_info.value, value);
assert_ne!(blinding, proof_info.blinding);
}
#[ignore]
#[test]
fn bench_bullet_proof_single_vs_multi() {
let nano_to_millis = 1.0 / 1_000_000.0;
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = SecretKey::new(&secp, &mut thread_rng());
let value = 12345678;
let increments = vec![1, 2, 5, 10, 100, 200];
for v in increments {
let mut commits: Vec<Commitment> = vec![];
let mut proofs: Vec<RangeProof> = vec![];
for i in 0..v {
commits.push(secp.commit(value + i as u64, blinding.clone()).unwrap());
proofs.push(secp.bullet_proof(value + i as u64, blinding.clone(), blinding.clone(), blinding.clone(), None, None));
}
println!("--------");
println!("Comparing {} Proofs", v);
let start = Utc::now().timestamp_nanos();
for i in 0..v {
let proof_range = secp
.verify_bullet_proof(commits[i].clone(), proofs[i].clone(), None)
.unwrap();
assert_eq!(proof_range.min, 0);
}
let fin = Utc::now().timestamp_nanos();
let dur_ms = (fin - start) as f64 * nano_to_millis;
println!("{} proofs single validated in {}ms", v, dur_ms);
let start = Utc::now().timestamp_nanos();
let proof_range = secp.verify_bullet_proof_multi(commits.clone(), proofs.clone(), None);
assert!(!proof_range.is_err());
let fin = Utc::now().timestamp_nanos();
let dur_ms = (fin - start) as f64 * nano_to_millis;
println!("{} proofs batch validated in {}ms", v, dur_ms);
}
}
#[test]
fn test_bullet_proof_verify_multi() {
let mut commits: Vec<Commitment> = vec![];
let mut proofs: Vec<RangeProof> = vec![];
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let blinding = SecretKey::new(&secp, &mut thread_rng());
let rewind_nonce = SecretKey::new(&secp, &mut thread_rng());
let private_nonce = SecretKey::new(&secp, &mut thread_rng());
let wrong_blinding = SecretKey::new(&secp, &mut thread_rng());
let value = 12345678;
let wrong_commit = secp.commit(value, wrong_blinding).unwrap();
commits.push(secp.commit(value, blinding.clone()).unwrap());
proofs.push(secp.bullet_proof(value, blinding.clone(), rewind_nonce.clone(), private_nonce.clone(), None, None));
let proof_range = secp
.verify_bullet_proof(commits[0].clone(), proofs[0].clone(), None)
.unwrap();
assert_eq!(proof_range.min, 0);
let proof_range = secp
.verify_bullet_proof_multi(commits.clone(), proofs.clone(), None)
.unwrap();
assert_eq!(proof_range.min, 0);
commits[0] = wrong_commit.clone();
if !secp
.verify_bullet_proof_multi(commits.clone(), proofs.clone(), None)
.is_err()
{
panic!("Bullet proof multi verify should have errored.");
}
commits = vec![];
proofs = vec![];
commits.push(secp.commit(value + 1, blinding.clone()).unwrap());
commits.push(secp.commit(value - 1, blinding.clone()).unwrap());
proofs.push(secp.bullet_proof(value + 1, blinding.clone(), rewind_nonce.clone(), private_nonce.clone(), None, None));
proofs.push(secp.bullet_proof(value - 1, blinding.clone(), rewind_nonce.clone(), private_nonce.clone(), None, None));
let proof_range = secp
.verify_bullet_proof_multi(commits.clone(), proofs.clone(), None)
.unwrap();
assert_eq!(proof_range.min, 0);
let mut extra_data1 = [0u8; 64].to_vec();
extra_data1[0] = 100;
let mut extra_data2 = [0u8; 64].to_vec();
extra_data2[0] = 200;
proofs = vec![];
proofs.push(secp.bullet_proof(
value + 1,
blinding.clone(),
rewind_nonce.clone(),
private_nonce.clone(),
Some(extra_data1.clone()),
None,
));
proofs.push(secp.bullet_proof(
value - 1,
blinding.clone(),
rewind_nonce.clone(),
private_nonce.clone(),
Some(extra_data2.clone()),
None,
));
let mut extra_data = vec![];
extra_data.push(extra_data1.clone());
extra_data.push(extra_data2.clone());
let proof_range = secp
.verify_bullet_proof_multi(commits.clone(), proofs.clone(), Some(extra_data.clone()))
.unwrap();
assert_eq!(proof_range.min, 0);
let mut extra_data = vec![];
extra_data1[0] = 101; extra_data.push(extra_data1.clone());
extra_data.push(extra_data2.clone());
if !secp
.verify_bullet_proof_multi(commits.clone(), proofs.clone(), Some(extra_data.clone()))
.is_err()
{
panic!("Bullet proof multi verify should have error.");
}
commits = vec![];
proofs = vec![];
let mut errs = 0;
for i in 1..100 {
print!("\r\r\r{}", i);
commits.push(secp.commit(value + i as u64, blinding.clone()).unwrap());
proofs.push(secp.bullet_proof(value + i as u64, blinding.clone(), rewind_nonce.clone(), private_nonce.clone(), None, None));
let proof_range = secp.verify_bullet_proof_multi(commits.clone(), proofs.clone(), None); if proof_range.is_err() {
println!(" proofs batch verify failed");
errs += 1;
}
}
assert_eq!(errs, 0);
}
}