mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
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:
parent
d116a434bf
commit
5d1f1af892
11 changed files with 350 additions and 301 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
(
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"]
|
||||
|
|
Loading…
Reference in a new issue