From 5d1f1af89289a5c3331fce5c9a174bda985de186 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Tue, 27 Feb 2018 21:11:55 +0000 Subject: [PATCH] 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 --- chain/src/sumtree.rs | 2 +- core/src/core/block.rs | 41 ++-- core/src/core/build.rs | 10 +- core/src/core/mod.rs | 11 +- core/src/core/transaction.rs | 457 +++++++++++++++++++---------------- core/src/ser.rs | 6 +- keychain/Cargo.toml | 6 - keychain/src/keychain.rs | 104 +++++--- pool/src/graph.rs | 2 +- pool/src/pool.rs | 4 +- util/Cargo.toml | 8 +- 11 files changed, 350 insertions(+), 301 deletions(-) diff --git a/chain/src/sumtree.rs b/chain/src/sumtree.rs index 9872bbaf2..37e781d6f 100644 --- a/chain/src/sumtree.rs +++ b/chain/src/sumtree.rs @@ -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); } } diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 7c8057532..a07b997fc 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -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, diff --git a/core/src/core/build.rs b/core/src/core/build.rs index 252872297..42cdd6d7d 100644 --- a/core/src/core/build.rs +++ b/core/src/core/build.rs @@ -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 { "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(); ( diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index 8d1c72660..6d4a2b094 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -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]; diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 523d00be6..d7a3d09fd 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -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 { - 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 { + 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 { - 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(&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 { + 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 { + let mut c = Cursor::new(proof_message.as_bytes()); + ser::deserialize::(&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()); +} } diff --git a/core/src/ser.rs b/core/src/ser.rs index 836ab7517..36990342a 100644 --- a/core/src/ser.rs +++ b/core/src/ser.rs @@ -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 { - 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 } } diff --git a/keychain/Cargo.toml b/keychain/Cargo.toml index b3eeefa19..649be6cdb 100644 --- a/keychain/Cargo.toml +++ b/keychain/Cargo.toml @@ -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" diff --git a/keychain/src/keychain.rs b/keychain/src/keychain.rs index 39a5e622e..ae485d837 100644 --- a/keychain/src/keychain.rs +++ b/keychain/src/keychain.rs @@ -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>, msg: ProofMessage, ) -> Result { 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>) + -> 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>, proof: RangeProof, ) -> Result { 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 { @@ -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 diff --git a/pool/src/graph.rs b/pool/src/graph.rs index 3e567cc00..0f38a3c44 100644 --- a/pool/src/graph.rs +++ b/pool/src/graph.rs @@ -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(), }; diff --git a/pool/src/pool.rs b/pool/src/pool.rs index 4a415b72d..014b63b11 100644 --- a/pool/src/pool.rs +++ b/pool/src/pool.rs @@ -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, diff --git a/util/Cargo.toml b/util/Cargo.toml index 2e43840d2..20eda26d5 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -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"]