Bulletproof messages (#730)

* beginning to add bullet proof messages

* Updated core transaction creation to embed the output's value and switch commit hash as part of the rangeproof message

* formatting issue

* more formatting issues

* Removing conditional feature compliation.. just bulletproofs from now on

* ensure MAX_PROOF_SIZE uses bulletproof sizing instead of earlier version

* updated with switch commit committed to in extra data

* accidentally commented out bullet-proof-size feature
This commit is contained in:
Yeastplume 2018-02-27 21:11:55 +00:00 committed by GitHub
parent d116a434bf
commit 5d1f1af892
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 350 additions and 301 deletions

View file

@ -639,7 +639,7 @@ impl<'a> Extension<'a> {
let commit = out.commit.clone();
match self.rproof_pmmr.get(n, true) {
Some((_, Some(rp))) => out.to_output(rp).verify_proof()?,
res => {
_res => {
return Err(Error::OutputNotFound);
}
}

View file

@ -26,6 +26,7 @@ use core::{
ShortId,
SwitchCommitHash,
Proof,
ProofMessageElements,
TxKernel,
Transaction,
OutputFeatures,
@ -41,7 +42,6 @@ use ser::{self, Readable, Reader, Writeable, Writer, WriteableSorted, read_and_v
use global;
use keychain;
use keychain::BlindingFactor;
use util;
use util::kernel_sig_msg;
use util::LOGGER;
use util::{secp, static_secp_instance};
@ -818,8 +818,13 @@ impl Block {
"Block reward - Switch Commit Hash is: {:?}",
switch_commit_hash
);
let msg = util::secp::pedersen::ProofMessage::empty();
let rproof = keychain.range_proof(reward(fees), key_id, commit, msg)?;
let value = reward(fees);
let msg = (ProofMessageElements {
value: value
}).to_proof_message();
let rproof = keychain.range_proof(value, key_id, commit, Some(switch_commit_hash.as_ref().to_vec()), msg)?;
let output = Output {
features: OutputFeatures::COINBASE_OUTPUT,
@ -1059,10 +1064,7 @@ mod test {
let b = new_block(vec![], &keychain);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = match Keychain::is_using_bullet_proofs() {
true => 1_256,
false => 5_708,
};
let target_len = 1_256;
assert_eq!(
vec.len(),
target_len,
@ -1076,10 +1078,7 @@ mod test {
let b = new_block(vec![&tx1], &keychain);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = match Keychain::is_using_bullet_proofs() {
true => 2_900,
false => 16_256,
};
let target_len = 2_900;
assert_eq!(
vec.len(),
target_len,
@ -1092,10 +1091,7 @@ mod test {
let b = new_block(vec![], &keychain);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed");
let target_len = match Keychain::is_using_bullet_proofs() {
true => 1_264,
false => 5_716,
};
let target_len = 1_264;
assert_eq!(
vec.len(),
target_len,
@ -1109,10 +1105,7 @@ mod test {
let b = new_block(vec![&tx1], &keychain);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed");
let target_len = match Keychain::is_using_bullet_proofs() {
true => 1_270,
false => 5_722,
};
let target_len = 1_270;
assert_eq!(
vec.len(),
target_len,
@ -1135,10 +1128,7 @@ mod test {
);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = match Keychain::is_using_bullet_proofs() {
true => 17696,
false => 111188,
};
let target_len = 17_696;
assert_eq!(
vec.len(),
target_len,
@ -1161,10 +1151,7 @@ mod test {
);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed");
let target_len = match Keychain::is_using_bullet_proofs() {
true => 1_324,
false => 5_776,
};
let target_len = 1_324;
assert_eq!(
vec.len(),
target_len,

View file

@ -27,7 +27,7 @@
use util::{secp, kernel_sig_msg};
use core::{Transaction, TxKernel, Input, Output, OutputFeatures, SwitchCommitHash};
use core::{Transaction, TxKernel, Input, Output, OutputFeatures, ProofMessageElements, SwitchCommitHash};
use core::hash::Hash;
use keychain;
use keychain::{Keychain, BlindSum, BlindingFactor, Identifier};
@ -112,10 +112,14 @@ pub fn output(value: u64, key_id: Identifier) -> Box<Append> {
"Builder - Switch Commit Hash is: {:?}",
switch_commit_hash
);
let msg = secp::pedersen::ProofMessage::empty();
let msg = (ProofMessageElements {
value: value,
}).to_proof_message();
let rproof = build
.keychain
.range_proof(value, &key_id, commit, msg)
.range_proof(value, &key_id, commit, Some(switch_commit_hash.as_ref().to_vec()), msg)
.unwrap();
(

View file

@ -263,10 +263,7 @@ mod test {
let tx = tx2i1o();
let mut vec = Vec::new();
ser::serialize(&mut vec, &tx).expect("serialization failed");
let target_len = match Keychain::is_using_bullet_proofs() {
true => 986,
false => 5_438,
};
let target_len = 986;
assert_eq!(
vec.len(),
target_len,
@ -389,16 +386,14 @@ mod test {
assert!(h != h2);
}
#[ignore]
#[test]
fn blind_tx() {
let btx = tx2i1o();
assert!(btx.validate().is_ok());
// Ignored for bullet proofs, info doesn't exist yet and calling range_proof_info
// Ignored for bullet proofs, because calling range_proof_info
// with a bullet proof causes painful errors
if Keychain::is_using_bullet_proofs() {
return;
}
// checks that the range proof on our blind output is sufficiently hiding
let Output { proof, .. } = btx.outputs[0];

View file

@ -16,7 +16,7 @@
use blake2::blake2b::blake2b;
use util::secp::{self, Message, Signature};
use util::{static_secp_instance, kernel_sig_msg};
use util::secp::pedersen::{Commitment, RangeProof};
use util::secp::pedersen::{Commitment, RangeProof, ProofMessage};
use std::cmp::{min, max};
use std::cmp::Ordering;
@ -26,7 +26,8 @@ use core::Committed;
use core::hash::{Hash, Hashed, ZERO_HASH};
use keychain::{Identifier, Keychain, BlindingFactor};
use keychain;
use ser::{self, read_and_verify_sorted, PMMRable, Readable, Reader, Writeable, WriteableSorted, Writer};
use ser::{self, read_and_verify_sorted, PMMRable, Readable, Reader, Writeable, WriteableSorted, Writer, ser_vec};
use std::io::Cursor;
use util;
/// The size of the blake2 hash of a switch commitment (256 bits)
@ -48,24 +49,24 @@ bitflags! {
// don't seem to be able to define an Ord implementation for Hash due to
// Ord being defined on all pointers, resorting to a macro instead
macro_rules! hashable_ord {
($hashable: ident) => {
impl Ord for $hashable {
fn cmp(&self, other: &$hashable) -> Ordering {
self.hash().cmp(&other.hash())
}
}
impl PartialOrd for $hashable {
fn partial_cmp(&self, other: &$hashable) -> Option<Ordering> {
Some(self.hash().cmp(&other.hash()))
}
}
impl PartialEq for $hashable {
fn eq(&self, other: &$hashable) -> bool {
self.hash() == other.hash()
}
}
impl Eq for $hashable {}
}
($hashable: ident) => {
impl Ord for $hashable {
fn cmp(&self, other: &$hashable) -> Ordering {
self.hash().cmp(&other.hash())
}
}
impl PartialOrd for $hashable {
fn partial_cmp(&self, other: &$hashable) -> Option<Ordering> {
Some(self.hash().cmp(&other.hash()))
}
}
impl PartialEq for $hashable {
fn eq(&self, other: &$hashable) -> bool {
self.hash() == other.hash()
}
}
impl Eq for $hashable {}
}
}
/// Errors thrown by Block validation
@ -750,19 +751,20 @@ impl Output {
pub fn verify_proof(&self) -> Result<(), secp::Error> {
let secp = static_secp_instance();
let secp = secp.lock().unwrap();
match Keychain::verify_range_proof(&secp, self.commit, self.proof){
match Keychain::verify_range_proof(&secp, self.commit, self.proof, Some(self.switch_commit_hash.as_ref().to_vec())){
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
}
/// Given the original blinding factor we can recover the
/// value from the range proof and the commitment
pub fn recover_value(&self, keychain: &Keychain, key_id: &Identifier) -> Option<u64> {
match keychain.rewind_range_proof(key_id, self.commit, self.proof) {
match keychain.rewind_range_proof(key_id, self.commit, Some(self.switch_commit_hash.as_ref().to_vec()), self.proof) {
Ok(proof_info) => {
if proof_info.success {
Some(proof_info.value)
let elements = ProofMessageElements::from_proof_message(proof_info.message).unwrap();
Some(elements.value)
} else {
None
}
@ -770,6 +772,7 @@ impl Output {
Err(_) => None,
}
}
}
/// An output_identifier can be build from either an input _or_ and output and
@ -853,25 +856,25 @@ pub struct OutputStoreable {
}
impl OutputStoreable {
/// Build a StoreableOutput from an existing output.
pub fn from_output(output: &Output) -> OutputStoreable {
OutputStoreable {
features: output.features,
commit: output.commit,
switch_commit_hash: output.switch_commit_hash,
}
/// Build a StoreableOutput from an existing output.
pub fn from_output(output: &Output) -> OutputStoreable {
OutputStoreable {
features: output.features,
commit: output.commit,
switch_commit_hash: output.switch_commit_hash,
}
}
/// Return a regular output
pub fn to_output(self, rproof: RangeProof) -> Output {
Output{
features: self.features,
commit: self.commit,
switch_commit_hash: self.switch_commit_hash,
proof: rproof,
}
/// Return a regular output
pub fn to_output(self, rproof: RangeProof) -> Output {
Output{
features: self.features,
commit: self.commit,
switch_commit_hash: self.switch_commit_hash,
proof: rproof,
}
}
}
impl PMMRable for OutputStoreable {
fn len() -> usize {
@ -901,183 +904,221 @@ impl Readable for OutputStoreable {
features: features,
})
}
}
/// A structure which contains fields that are to be commited to within
/// an Output's range (bullet) proof.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ProofMessageElements {
/// The amount, stored to allow for wallet reconstruction as
/// rewinding isn't supported in bulletproofs just yet
pub value: u64,
}
impl Writeable for ProofMessageElements {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
writer.write_u64(self.value)?;
for _ in 8..64 {
let _ = writer.write_u8(0);
}
Ok(())
}
}
impl Readable for ProofMessageElements {
fn read(reader: &mut Reader) -> Result<ProofMessageElements, ser::Error> {
Ok(ProofMessageElements {
value: reader.read_u64()?,
})
}
}
impl ProofMessageElements {
/// Serialise and return a ProofMessage
pub fn to_proof_message(&self)->ProofMessage {
ProofMessage::from_bytes(&ser_vec(self).unwrap())
}
/// Deserialise and return the message elements
pub fn from_proof_message(proof_message:ProofMessage)
-> Result<ProofMessageElements, ser::Error> {
let mut c = Cursor::new(proof_message.as_bytes());
ser::deserialize::<ProofMessageElements>(&mut c)
}
}
#[cfg(test)]
mod test {
use super::*;
use core::id::{ShortId, ShortIdentifiable};
use keychain::Keychain;
use util::secp;
use super::*;
use core::id::{ShortId, ShortIdentifiable};
use keychain::Keychain;
use util::secp;
#[test]
fn test_kernel_ser_deser() {
let keychain = Keychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(5, &key_id).unwrap();
#[test]
fn test_kernel_ser_deser() {
let keychain = Keychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(5, &key_id).unwrap();
// just some bytes for testing ser/deser
let sig = secp::Signature::from_raw_data(&[0;64]).unwrap();
// just some bytes for testing ser/deser
let sig = secp::Signature::from_raw_data(&[0;64]).unwrap();
let kernel = TxKernel {
features: KernelFeatures::DEFAULT_KERNEL,
lock_height: 0,
excess: commit,
excess_sig: sig.clone(),
fee: 10,
};
let kernel = TxKernel {
features: KernelFeatures::DEFAULT_KERNEL,
lock_height: 0,
excess: commit,
excess_sig: sig.clone(),
fee: 10,
};
let mut vec = vec![];
ser::serialize(&mut vec, &kernel).expect("serialized failed");
let kernel2: TxKernel = ser::deserialize(&mut &vec[..]).unwrap();
assert_eq!(kernel2.features, KernelFeatures::DEFAULT_KERNEL);
assert_eq!(kernel2.lock_height, 0);
assert_eq!(kernel2.excess, commit);
assert_eq!(kernel2.excess_sig, sig.clone());
assert_eq!(kernel2.fee, 10);
let mut vec = vec![];
ser::serialize(&mut vec, &kernel).expect("serialized failed");
let kernel2: TxKernel = ser::deserialize(&mut &vec[..]).unwrap();
assert_eq!(kernel2.features, KernelFeatures::DEFAULT_KERNEL);
assert_eq!(kernel2.lock_height, 0);
assert_eq!(kernel2.excess, commit);
assert_eq!(kernel2.excess_sig, sig.clone());
assert_eq!(kernel2.fee, 10);
// now check a kernel with lock_height serializes/deserializes correctly
let kernel = TxKernel {
features: KernelFeatures::DEFAULT_KERNEL,
lock_height: 100,
excess: commit,
excess_sig: sig.clone(),
fee: 10,
};
// now check a kernel with lock_height serializes/deserializes correctly
let kernel = TxKernel {
features: KernelFeatures::DEFAULT_KERNEL,
lock_height: 100,
excess: commit,
excess_sig: sig.clone(),
fee: 10,
};
let mut vec = vec![];
ser::serialize(&mut vec, &kernel).expect("serialized failed");
let kernel2: TxKernel = ser::deserialize(&mut &vec[..]).unwrap();
assert_eq!(kernel2.features, KernelFeatures::DEFAULT_KERNEL);
assert_eq!(kernel2.lock_height, 100);
assert_eq!(kernel2.excess, commit);
assert_eq!(kernel2.excess_sig, sig.clone());
assert_eq!(kernel2.fee, 10);
}
#[test]
fn test_output_ser_deser() {
let keychain = Keychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(5, &key_id).unwrap();
let switch_commit = keychain.switch_commit(&key_id).unwrap();
let switch_commit_hash = SwitchCommitHash::from_switch_commit(
switch_commit,
&keychain,
&key_id,
);
let msg = secp::pedersen::ProofMessage::empty();
let proof = keychain.range_proof(5, &key_id, commit, msg).unwrap();
let out = Output {
features: OutputFeatures::DEFAULT_OUTPUT,
commit: commit,
switch_commit_hash: switch_commit_hash,
proof: proof,
};
let mut vec = vec![];
ser::serialize(&mut vec, &out).expect("serialized failed");
let dout: Output = ser::deserialize(&mut &vec[..]).unwrap();
assert_eq!(dout.features, OutputFeatures::DEFAULT_OUTPUT);
assert_eq!(dout.commit, out.commit);
assert_eq!(dout.proof, out.proof);
}
#[test]
fn test_output_value_recovery() {
let keychain = Keychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(1003, &key_id).unwrap();
let switch_commit = keychain.switch_commit(&key_id).unwrap();
let switch_commit_hash = SwitchCommitHash::from_switch_commit(
switch_commit,
&keychain,
&key_id,
);
let msg = secp::pedersen::ProofMessage::empty();
let proof = keychain.range_proof(1003, &key_id, commit, msg).unwrap();
let output = Output {
features: OutputFeatures::DEFAULT_OUTPUT,
commit: commit,
switch_commit_hash: switch_commit_hash,
proof: proof,
};
// check we can successfully recover the value with the original blinding factor
let result = output.recover_value(&keychain, &key_id);
// TODO: Remove this check once value recovery is supported within bullet proofs
if let Some(v) = result {
assert_eq!(v, 1003);
} else {
return;
}
// check we cannot recover the value without the original blinding factor
let key_id2 = keychain.derive_key_id(2).unwrap();
let not_recoverable = output.recover_value(&keychain, &key_id2);
match not_recoverable {
Some(_) => panic!("expected value to be None here"),
None => {}
}
}
#[test]
fn commit_consistency() {
let keychain = Keychain::from_seed(&[0; 32]).unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(1003, &key_id).unwrap();
let switch_commit = keychain.switch_commit(&key_id).unwrap();
println!("Switch commit: {:?}", switch_commit);
println!("commit: {:?}", commit);
let key_id = keychain.derive_key_id(1).unwrap();
let switch_commit_2 = keychain.switch_commit(&key_id).unwrap();
let commit_2 = keychain.commit(1003, &key_id).unwrap();
println!("Switch commit 2: {:?}", switch_commit_2);
println!("commit2 : {:?}", commit_2);
assert!(commit == commit_2);
assert!(switch_commit == switch_commit_2);
}
#[test]
fn input_short_id() {
let keychain = Keychain::from_seed(&[0; 32]).unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(5, &key_id).unwrap();
let input = Input {
features: OutputFeatures::DEFAULT_OUTPUT,
commit: commit,
out_block: None,
};
let block_hash = Hash::from_hex(
"3a42e66e46dd7633b57d1f921780a1ac715e6b93c19ee52ab714178eb3a9f673",
).unwrap();
let short_id = input.short_id(&block_hash);
assert_eq!(short_id, ShortId::from_hex("3e1262905b7a").unwrap());
// now generate the short_id for a *very* similar output (single feature flag different)
// and check it generates a different short_id
let input = Input {
features: OutputFeatures::COINBASE_OUTPUT,
commit: commit,
out_block: None,
};
let block_hash = Hash::from_hex(
"3a42e66e46dd7633b57d1f921780a1ac715e6b93c19ee52ab714178eb3a9f673",
).unwrap();
let short_id = input.short_id(&block_hash);
assert_eq!(short_id, ShortId::from_hex("90653c1c870a").unwrap());
}
let mut vec = vec![];
ser::serialize(&mut vec, &kernel).expect("serialized failed");
let kernel2: TxKernel = ser::deserialize(&mut &vec[..]).unwrap();
assert_eq!(kernel2.features, KernelFeatures::DEFAULT_KERNEL);
assert_eq!(kernel2.lock_height, 100);
assert_eq!(kernel2.excess, commit);
assert_eq!(kernel2.excess_sig, sig.clone());
assert_eq!(kernel2.fee, 10);
}
#[test]
fn test_output_ser_deser() {
let keychain = Keychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(5, &key_id).unwrap();
let switch_commit = keychain.switch_commit(&key_id).unwrap();
let switch_commit_hash = SwitchCommitHash::from_switch_commit(
switch_commit,
&keychain,
&key_id,
);
let msg = secp::pedersen::ProofMessage::empty();
let proof = keychain.range_proof(5, &key_id, commit, Some(switch_commit_hash.as_ref().to_vec()), msg).unwrap();
let out = Output {
features: OutputFeatures::DEFAULT_OUTPUT,
commit: commit,
switch_commit_hash: switch_commit_hash,
proof: proof,
};
let mut vec = vec![];
ser::serialize(&mut vec, &out).expect("serialized failed");
let dout: Output = ser::deserialize(&mut &vec[..]).unwrap();
assert_eq!(dout.features, OutputFeatures::DEFAULT_OUTPUT);
assert_eq!(dout.commit, out.commit);
assert_eq!(dout.proof, out.proof);
}
#[test]
fn test_output_value_recovery() {
let keychain = Keychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let value = 1003;
let commit = keychain.commit(value, &key_id).unwrap();
let switch_commit = keychain.switch_commit(&key_id).unwrap();
let switch_commit_hash = SwitchCommitHash::from_switch_commit(
switch_commit,
&keychain,
&key_id,
);
let msg = (ProofMessageElements {
value: value,
}).to_proof_message();
let proof = keychain.range_proof(value, &key_id, commit, Some(switch_commit_hash.as_ref().to_vec()), msg).unwrap();
let output = Output {
features: OutputFeatures::DEFAULT_OUTPUT,
commit: commit,
switch_commit_hash: switch_commit_hash,
proof: proof,
};
// check we can successfully recover the value with the original blinding factor
let result = output.recover_value(&keychain, &key_id);
// TODO: Remove this check once value recovery is supported within bullet proofs
if let Some(v) = result {
assert_eq!(v, 1003);
} else {
return;
}
// Bulletproofs message unwind will just be gibberish given the wrong blinding factor
}
#[test]
fn commit_consistency() {
let keychain = Keychain::from_seed(&[0; 32]).unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(1003, &key_id).unwrap();
let switch_commit = keychain.switch_commit(&key_id).unwrap();
println!("Switch commit: {:?}", switch_commit);
println!("commit: {:?}", commit);
let key_id = keychain.derive_key_id(1).unwrap();
let switch_commit_2 = keychain.switch_commit(&key_id).unwrap();
let commit_2 = keychain.commit(1003, &key_id).unwrap();
println!("Switch commit 2: {:?}", switch_commit_2);
println!("commit2 : {:?}", commit_2);
assert!(commit == commit_2);
assert!(switch_commit == switch_commit_2);
}
#[test]
fn input_short_id() {
let keychain = Keychain::from_seed(&[0; 32]).unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(5, &key_id).unwrap();
let input = Input {
features: OutputFeatures::DEFAULT_OUTPUT,
commit: commit,
out_block: None,
};
let block_hash = Hash::from_hex(
"3a42e66e46dd7633b57d1f921780a1ac715e6b93c19ee52ab714178eb3a9f673",
).unwrap();
let short_id = input.short_id(&block_hash);
assert_eq!(short_id, ShortId::from_hex("3e1262905b7a").unwrap());
// now generate the short_id for a *very* similar output (single feature flag different)
// and check it generates a different short_id
let input = Input {
features: OutputFeatures::COINBASE_OUTPUT,
commit: commit,
out_block: None,
};
let block_hash = Hash::from_hex(
"3a42e66e46dd7633b57d1f921780a1ac715e6b93c19ee52ab714178eb3a9f673",
).unwrap();
let short_id = input.short_id(&block_hash);
assert_eq!(short_id, ShortId::from_hex("90653c1c870a").unwrap());
}
}

View file

@ -37,8 +37,6 @@ use util::secp::constants::{
SECRET_KEY_SIZE,
};
const BULLET_PROOF_SIZE: usize = 674;
/// Possible errors deriving from serializing or deserializing.
#[derive(Debug)]
pub enum Error {
@ -370,7 +368,7 @@ impl Writeable for RangeProof {
impl Readable for RangeProof {
fn read(reader: &mut Reader) -> Result<RangeProof, Error> {
let p = reader.read_limited_vec(BULLET_PROOF_SIZE)?;
let p = reader.read_limited_vec(MAX_PROOF_SIZE)?;
let mut a = [0; MAX_PROOF_SIZE];
for i in 0..p.len() {
a[i] = p[i];
@ -384,7 +382,7 @@ impl Readable for RangeProof {
impl PMMRable for RangeProof {
fn len() -> usize {
BULLET_PROOF_SIZE + 8
MAX_PROOF_SIZE + 8
}
}

View file

@ -3,12 +3,6 @@ name = "grin_keychain"
version = "0.1.0"
authors = ["Antioch Peverell"]
[features]
#remove this feature to use older-style rangeproofs
#(this flag will disappear in future releases)
default = ["use-bullet-proofs"]
use-bullet-proofs = []
[dependencies]
byteorder = "^1.0"
blake2-rfc = "~0.2.17"

View file

@ -28,11 +28,6 @@ use uuid::Uuid;
use blind::{BlindSum, BlindingFactor};
use extkey::{self, Identifier};
#[cfg(feature = "use-bullet-proofs")]
pub const USE_BULLET_PROOFS:bool = true;
#[cfg(not(feature = "use-bullet-proofs"))]
pub const USE_BULLET_PROOFS:bool = false;
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Error {
ExtendedKey(extkey::Error),
@ -226,33 +221,33 @@ impl Keychain {
Ok(child_key.switch_key)
}
pub fn is_using_bullet_proofs() -> bool {
USE_BULLET_PROOFS
}
pub fn range_proof(
&self,
amount: u64,
key_id: &Identifier,
commit: Commitment,
extra_data: Option<Vec<u8>>,
msg: ProofMessage,
) -> Result<RangeProof, Error> {
let skey = self.derived_key(key_id)?;
let range_proof = match USE_BULLET_PROOFS {
true => self.secp.bullet_proof(amount, skey),
false => self.secp.range_proof(0, amount, skey, commit, msg),
};
Ok(range_proof)
if msg.len() == 0 {
return Ok(self.secp.bullet_proof(amount, skey, extra_data, None));
} else {
if msg.len() != 64 {
error!(LOGGER, "Bullet proof message must be 64 bytes.");
return Err(Error::RangeProof("Bullet proof message must be 64 bytes".to_string()));
}
}
return Ok(self.secp.bullet_proof(amount, skey, extra_data, Some(msg)));
}
pub fn verify_range_proof(
secp: &Secp256k1,
commit: Commitment,
proof: RangeProof) -> Result<(), secp::Error> {
let result = match USE_BULLET_PROOFS {
true => secp.verify_bullet_proof(commit, proof),
false => secp.verify_range_proof(commit, proof),
};
commit: Commitment,
proof: RangeProof,
extra_data: Option<Vec<u8>>)
-> Result<(), secp::Error> {
let result = secp.verify_bullet_proof(commit, proof, extra_data);
match result {
Ok(_) => Ok(()),
Err(e) => Err(e),
@ -263,14 +258,34 @@ impl Keychain {
&self,
key_id: &Identifier,
commit: Commitment,
extra_data: Option<Vec<u8>>,
proof: RangeProof,
) -> Result<ProofInfo, Error> {
let nonce = self.derived_key(key_id)?;
if USE_BULLET_PROOFS {
error!(LOGGER, "Rewinding Bullet proofs not yet supported");
return Err(Error::RangeProof("Rewinding Bullet proofs not yet supported".to_string()));
}
Ok(self.secp.rewind_range_proof(commit, proof, nonce))
let proof_message = self.secp.unwind_bullet_proof(commit, nonce, extra_data, proof);
let proof_info = match proof_message {
Ok(p) => ProofInfo {
success: true,
value: 0,
message: p,
mlen: 0,
min: 0,
max: 0,
exp: 0,
mantissa: 0,
},
Err(_) => ProofInfo {
success: false,
value: 0,
message: ProofMessage::empty(),
mlen: 0,
min: 0,
max: 0,
exp: 0,
mantissa: 0,
}
};
return Ok(proof_info);
}
pub fn blind_sum(&self, blind_sum: &BlindSum) -> Result<BlindingFactor, Error> {
@ -556,39 +571,40 @@ mod test {
let keychain = Keychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(5, &key_id).unwrap();
let msg = ProofMessage::empty();
let mut msg = ProofMessage::from_bytes(&[0u8; 64]);
let extra_data = [99u8; 64];
//TODO: Remove this check when bullet proofs can be rewound
if Keychain::is_using_bullet_proofs(){
return;
}
let proof = keychain.range_proof(5, &key_id, commit, msg).unwrap();
let proof_info = keychain.rewind_range_proof(&key_id, commit, proof).unwrap();
let proof = keychain.range_proof(5, &key_id, commit, Some(extra_data.to_vec().clone()), msg).unwrap();
let proof_info = keychain.rewind_range_proof(&key_id, commit, Some(extra_data.to_vec().clone()), proof).unwrap();
assert_eq!(proof_info.success, true);
assert_eq!(proof_info.value, 5);
// now check the recovered message is "empty" (but not truncated) i.e. all
// zeroes
//Value is in the message in this case
assert_eq!(
proof_info.message,
secp::pedersen::ProofMessage::from_bytes(&[0; secp::constants::PROOF_MSG_SIZE])
secp::pedersen::ProofMessage::from_bytes(&[0; secp::constants::BULLET_PROOF_MSG_SIZE])
);
let key_id2 = keychain.derive_key_id(2).unwrap();
// cannot rewind with a different nonce
let proof_info = keychain
.rewind_range_proof(&key_id2, commit, proof)
.rewind_range_proof(&key_id2, commit, Some(extra_data.to_vec().clone()), proof)
.unwrap();
assert_eq!(proof_info.success, false);
// With bullet proofs, if you provide the wrong nonce you'll get gibberish back as opposed
// to a failure to recover the message
assert_ne!(
proof_info.message,
secp::pedersen::ProofMessage::from_bytes(&[0; secp::constants::BULLET_PROOF_MSG_SIZE])
);
assert_eq!(proof_info.value, 0);
// cannot rewind with a commitment to the same value using a different key
let commit2 = keychain.commit(5, &key_id2).unwrap();
let proof_info = keychain
.rewind_range_proof(&key_id, commit2, proof)
.rewind_range_proof(&key_id, commit2, Some(extra_data.to_vec().clone()), proof)
.unwrap();
assert_eq!(proof_info.success, false);
assert_eq!(proof_info.value, 0);
@ -596,10 +612,20 @@ mod test {
// cannot rewind with a commitment to a different value
let commit3 = keychain.commit(4, &key_id).unwrap();
let proof_info = keychain
.rewind_range_proof(&key_id, commit3, proof)
.rewind_range_proof(&key_id, commit3, Some(extra_data.to_vec().clone()), proof)
.unwrap();
assert_eq!(proof_info.success, false);
assert_eq!(proof_info.value, 0);
// cannot rewind with wrong extra committed data
let commit3 = keychain.commit(4, &key_id).unwrap();
let wrong_extra_data = [98u8; 64];
let should_err = keychain
.rewind_range_proof(&key_id, commit3, Some(wrong_extra_data.to_vec().clone()), proof)
.unwrap();
assert_eq!(proof_info.success, false);
assert_eq!(proof_info.value, 0);
}
// We plan to "offset" the key used in the kernel commitment

View file

@ -334,7 +334,7 @@ mod tests {
commit: output_commit,
switch_commit_hash: switch_commit_hash,
proof: keychain
.range_proof(100, &key_id1, output_commit, msg)
.range_proof(100, &key_id1, output_commit, Some(switch_commit_hash.as_ref().to_vec()), msg)
.unwrap(),
};

View file

@ -1359,7 +1359,7 @@ mod tests {
&key_id,
);
let msg = secp::pedersen::ProofMessage::empty();
let proof = keychain.range_proof(value, &key_id, commit, msg).unwrap();
let proof = keychain.range_proof(value, &key_id, commit, Some(switch_commit_hash.as_ref().to_vec()), msg).unwrap();
transaction::Output {
features: transaction::OutputFeatures::DEFAULT_OUTPUT,
@ -1381,7 +1381,7 @@ mod tests {
&key_id,
);
let msg = secp::pedersen::ProofMessage::empty();
let proof = keychain.range_proof(value, &key_id, commit, msg).unwrap();
let proof = keychain.range_proof(value, &key_id, commit, Some(switch_commit_hash.as_ref().to_vec()), msg).unwrap();
transaction::Output {
features: transaction::OutputFeatures::COINBASE_OUTPUT,

View file

@ -14,7 +14,11 @@ byteorder = "^1.0"
rand = "^0.3"
serde = "~1.0.8"
serde_derive = "~1.0.8"
secp256k1zkp = { git = "https://github.com/mimblewimble/rust-secp256k1-zkp", tag="grin_integration_8" }
#secp256k1zkp = { path = "../../rust-secp256k1-zkp" }
walkdir = "^2.0.1"
zip = "^0.2.6"
[dependencies.secp256k1zkp]
git = "https://github.com/mimblewimble/rust-secp256k1-zkp"
tag="grin_integration_14"
#path = "../../rust-secp256k1-zkp"
features=["bullet-proof-sizing"]