Use updated bulletproof API (#1194)

This commit is contained in:
Yeastplume 2018-06-25 12:28:56 +01:00 committed by GitHub
parent dc827ebe93
commit 38a7936521
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 43 additions and 266 deletions

6
Cargo.lock generated
View file

@ -749,7 +749,7 @@ dependencies = [
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"secp256k1zkp 0.7.1 (git+https://github.com/mimblewimble/rust-secp256k1-zkp?tag=grin_integration_19)",
"secp256k1zkp 0.7.1 (git+https://github.com/mimblewimble/rust-secp256k1-zkp?branch=testnet3)",
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
"slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1617,7 +1617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "secp256k1zkp"
version = "0.7.1"
source = "git+https://github.com/mimblewimble/rust-secp256k1-zkp?tag=grin_integration_19#800e9b3ea4a8b2df7b999980ae78b224a6ad07ce"
source = "git+https://github.com/mimblewimble/rust-secp256k1-zkp?branch=testnet3#748296c61341461e46e7b2c05db494d60f96ac44"
dependencies = [
"arrayvec 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2429,7 +2429,7 @@ dependencies = [
"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637"
"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum secp256k1zkp 0.7.1 (git+https://github.com/mimblewimble/rust-secp256k1-zkp?tag=grin_integration_19)" = "<none>"
"checksum secp256k1zkp 0.7.1 (git+https://github.com/mimblewimble/rust-secp256k1-zkp?branch=testnet3)" = "<none>"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum sequence_trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c915714ca833b1d4d6b8f6a9d72a3ff632fe45b40a8d184ef79c81bec6327eed"

View file

@ -917,114 +917,6 @@ impl Readable for OutputIdentifier {
}
}
/// A structure which contains fields that are to be committed 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
/// This is going to be written 3 times, to facilitate checking
/// values on rewind
/// Note that rewinding with only the nonce will give you back
/// the first 32 bytes of the message. To get the second
/// 32 bytes, you need to provide the correct blinding factor as well
value: u64,
/// another copy of the value, to check on rewind
value_copy_1: u64,
/// another copy of the value
value_copy_2: u64,
/// the first 8 bytes of the blinding factor, used to avoid having to grind
/// through a proof each time you want to check against key possibilities
bf_first_8: Vec<u8>,
/// unused portion of message, used to test whether we have both nonce
/// and blinding correct
zeroes: Vec<u8>,
}
impl Writeable for ProofMessageElements {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
writer.write_u64(self.value)?;
writer.write_u64(self.value_copy_1)?;
writer.write_u64(self.value_copy_2)?;
writer.write_fixed_bytes(&self.bf_first_8)?;
for i in 0..32 {
let _ = writer.write_u8(self.zeroes[i]);
}
Ok(())
}
}
impl Readable for ProofMessageElements {
fn read(reader: &mut Reader) -> Result<ProofMessageElements, ser::Error> {
// if the value isn't repeated 3 times, it's most likely not the value,
// so reject
Ok(ProofMessageElements {
value: reader.read_u64()?,
value_copy_1: reader.read_u64()?,
value_copy_2: reader.read_u64()?,
bf_first_8: reader.read_fixed_bytes(8)?,
zeroes: reader.read_fixed_bytes(32)?,
})
}
}
impl ProofMessageElements {
/// Create a new proof message
pub fn new(value: u64, blinding: &keychain::Identifier) -> ProofMessageElements {
ProofMessageElements {
value: value,
value_copy_1: value,
value_copy_2: value,
bf_first_8: blinding.to_bytes()[0..8].to_vec(),
zeroes: [0u8; 32].to_vec(),
}
}
/// Return the value if it's valid, an error otherwise
pub fn value(&self) -> Result<u64, Error> {
if self.value == self.value_copy_1 && self.value == self.value_copy_2 {
Ok(self.value)
} else {
Err(Error::InvalidProofMessage)
}
}
/// Compare given identifier with first 8 bytes of what's stored
pub fn compare_bf_first_8(&self, in_id: &keychain::Identifier) -> bool {
let in_id_vec = in_id.to_bytes()[0..8].to_vec();
for i in 0..8 {
if in_id_vec[i] != self.bf_first_8[i] {
return false;
}
}
true
}
/// Whether our remainder is zero (as it should be if the BF and nonce used
/// to unwind are correct
pub fn zeroes_correct(&self) -> bool {
for i in 0..self.zeroes.len() {
if self.zeroes[i] != 0 {
return false;
}
}
true
}
/// Serialize and return a ProofMessage
pub fn to_proof_message(&self) -> ProofMessage {
ProofMessage::from_bytes(&ser_vec(self).unwrap())
}
/// Deserialize 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::*;

View file

@ -233,7 +233,7 @@ fn empty_block_serialized_size() {
let b = new_block(vec![], &keychain, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = 1_265;
let target_len = 1_266;
assert_eq!(vec.len(), target_len,);
}
@ -246,7 +246,7 @@ fn block_single_tx_serialized_size() {
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = 2_845;
let target_len = 2_848;
assert_eq!(vec.len(), target_len);
}
@ -258,7 +258,7 @@ fn empty_compact_block_serialized_size() {
let b = new_block(vec![], &keychain, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed");
let target_len = 1_273;
let target_len = 1_274;
assert_eq!(vec.len(), target_len,);
}
@ -271,7 +271,7 @@ fn compact_block_single_tx_serialized_size() {
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed");
let target_len = 1_279;
let target_len = 1_280;
assert_eq!(vec.len(), target_len,);
}
@ -290,7 +290,7 @@ fn block_10_tx_serialized_size() {
let b = new_block(txs.iter().collect(), &keychain, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = 17_065;
let target_len = 17_086;
assert_eq!(vec.len(), target_len,);
}
@ -308,7 +308,7 @@ fn compact_block_10_tx_serialized_size() {
let b = new_block(txs.iter().collect(), &keychain, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed");
let target_len = 1_333;
let target_len = 1_334;
assert_eq!(vec.len(), target_len,);
}

View file

@ -36,7 +36,7 @@ fn simple_tx_ser() {
let tx = tx2i1o();
let mut vec = Vec::new();
ser::serialize(&mut vec, &tx).expect("serialization failed");
let target_len = 954;
let target_len = 955;
assert_eq!(vec.len(), target_len,);
}

View file

@ -23,7 +23,6 @@ pub mod common;
use grin_core::core::{Output, OutputFeatures};
use grin_core::ser;
use keychain::{ExtKeychain, Keychain};
use util::secp;
use wallet::libtx::proof;
#[test]
@ -31,8 +30,7 @@ fn test_output_ser_deser() {
let keychain = ExtKeychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let commit = keychain.commit(5, &key_id).unwrap();
let msg = secp::pedersen::ProofMessage::empty();
let proof = proof::create(&keychain, 5, &key_id, commit, None, msg).unwrap();
let proof = proof::create(&keychain, 5, &key_id, commit, None).unwrap();
let out = Output {
features: OutputFeatures::DEFAULT_OUTPUT,

View file

@ -20,6 +20,6 @@ zip = "0.4"
[dependencies.secp256k1zkp]
git = "https://github.com/mimblewimble/rust-secp256k1-zkp"
tag = "grin_integration_19"
branch = "testnet3"
#path = "../../rust-secp256k1-zkp"
features = ["bullet-proof-sizing"]

View file

@ -29,7 +29,7 @@ use util::{kernel_sig_msg, secp};
use core::core::hash::Hash;
use core::core::merkle_proof::MerkleProof;
use core::core::{Input, Output, OutputFeatures, ProofMessageElements, Transaction, TxKernel};
use core::core::{Input, Output, OutputFeatures, Transaction, TxKernel};
use keychain::{self, BlindSum, BlindingFactor, Identifier, Keychain};
use libtx::{aggsig, proof};
use util::LOGGER;
@ -118,15 +118,12 @@ where
let commit = build.keychain.commit(value, &key_id).unwrap();
trace!(LOGGER, "Builder - Pedersen Commit is: {:?}", commit,);
let msg = ProofMessageElements::new(value, &key_id);
let rproof = proof::create(
build.keychain,
value,
&key_id,
commit,
None,
msg.to_proof_message(),
).unwrap();
(

View file

@ -17,7 +17,6 @@
use blake2;
use keychain::{Identifier, Keychain};
use libtx::error::{Error, ErrorKind};
use util::logger::LOGGER;
use util::secp::key::SecretKey;
use util::secp::pedersen::{Commitment, ProofInfo, ProofMessage, RangeProof};
use util::secp::{self, Secp256k1};
@ -42,16 +41,13 @@ where
}
}
/// So we want this to take an opaque structure that can be called
/// back to get the sensitive data
/// Create a bulletproof
pub fn create<K>(
k: &K,
amount: u64,
key_id: &Identifier,
_commit: Commitment,
extra_data: Option<Vec<u8>>,
msg: ProofMessage,
) -> Result<RangeProof, Error>
where
K: Keychain,
@ -59,18 +55,7 @@ where
let commit = k.commit(amount, key_id)?;
let skey = k.derived_key(key_id)?;
let nonce = create_nonce(k, &commit)?;
if msg.len() == 0 {
return Ok(k.secp().bullet_proof(amount, skey, nonce, extra_data, None));
} else {
if msg.len() != 64 {
error!(LOGGER, "Bullet proof message must be 64 bytes.");
return Err(ErrorKind::RangeProof(
"Bullet proof message must be 64 bytes".to_string(),
))?;
}
}
return Ok(k.secp()
.bullet_proof(amount, skey, nonce, extra_data, Some(msg)));
Ok(k.secp().bullet_proof(amount, skey, nonce, extra_data))
}
/// Verify a proof
@ -90,7 +75,6 @@ pub fn verify(
/// Rewind a rangeproof to retrieve the amount
pub fn rewind<K>(
k: &K,
key_id: &Identifier,
commit: Commitment,
extra_data: Option<Vec<u8>>,
proof: RangeProof,
@ -98,25 +82,16 @@ pub fn rewind<K>(
where
K: Keychain,
{
let skey = k.derived_key(key_id)?;
let nonce = create_nonce(k, &commit)?;
let proof_message = k.secp()
.unwind_bullet_proof(commit, skey, nonce, extra_data, proof);
.rewind_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,
},
Ok(p) => p,
Err(_) => ProofInfo {
success: false,
value: 0,
message: ProofMessage::empty(),
blinding: SecretKey([0; secp::constants::SECRET_KEY_SIZE]),
mlen: 0,
min: 0,
max: 0,

View file

@ -18,7 +18,7 @@ use keychain::{Identifier, Keychain};
use core::consensus::reward;
use core::core::KernelFeatures;
use core::core::{Output, OutputFeatures, ProofMessageElements, TxKernel};
use core::core::{Output, OutputFeatures, TxKernel};
use libtx::error::Error;
use libtx::{aggsig, proof};
use util::{kernel_sig_msg, secp, static_secp_instance, LOGGER};
@ -35,7 +35,6 @@ where
{
let value = reward(fees);
let commit = keychain.commit(value, key_id)?;
let msg = ProofMessageElements::new(value, key_id);
trace!(LOGGER, "Block reward - Pedersen Commit is: {:?}", commit,);
@ -45,7 +44,6 @@ where
key_id,
commit,
None,
msg.to_proof_message(),
)?;
let output = Output {

View file

@ -16,10 +16,9 @@
/// TODO: Remove api
use api;
use byteorder::{BigEndian, ByteOrder};
use core::core::transaction::ProofMessageElements;
use core::global;
use error::{Error, ErrorKind};
use failure::{Fail, ResultExt};
use failure::Fail;
use keychain::{Identifier, Keychain};
use libtx::proof;
use libwallet::types::*;
@ -77,8 +76,7 @@ where
// TODO - wrap the many return values in a struct
fn find_outputs_with_key<T, K>(
wallet: &mut T,
outputs: Vec<api::OutputPrintable>,
found_key_index: &mut Vec<u32>,
outputs: Vec<api::OutputPrintable>
) -> Vec<(
pedersen::Commitment,
Identifier,
@ -109,76 +107,41 @@ where
info!(LOGGER, "Scanning {} outputs", outputs.len(),);
let current_chain_height = wallet.get_chain_height(wallet.node_url()).unwrap();
// skey doesn't matter in this case
let skey = wallet.keychain().derive_key_id(1).unwrap();
for output in outputs.iter().filter(|x| !x.spent) {
// attempt to unwind message from the RP and get a value.. note
// this will only return okay if the value is included in the
// message 3 times, indicating a strong match. Also, sec_key provided
// to unwind in this case will be meaningless. With only the nonce known
// only the first 32 bytes of the recovered message will be accurate
// attempt to unwind message from the RP and get a value
// will fail if it's not ours
let info = proof::rewind(
wallet.keychain(),
&skey,
output.commit,
None,
output.range_proof().unwrap(),
).unwrap();
let message = ProofMessageElements::from_proof_message(&info.message).unwrap();
let value = message.value();
if value.is_err() {
if !info.success {
continue;
}
// we have a match, now check through our key iterations to find a partial match
let mut found = false;
// we have a match, now check through our key iterations to find out which one it was
let mut found = false;
let mut start_index = 1;
// TODO: This assumption only holds with current wallet software assuming
// wallet doesn't go back and re-use gaps in its key index, ie. every
// new key index produced is always greater than the previous max key index
if let Some(m) = found_key_index.iter().max() {
start_index = *m as usize + 1;
}
for i in start_index..max_derivations {
// much faster than calling EC functions for each found key
// Shouldn't be needed if assumption about wallet key 'gaps' above
// holds.. otherwise this is a good optimization.. perhaps
// provide a command line switch
/*if found_key_index.contains(&(i as u32)) {
continue;
}*/
let key_id = &wallet.keychain().derive_key_id(i as u32).unwrap();
if !message.compare_bf_first_8(key_id) {
let b = wallet.keychain().derived_key(key_id).unwrap();
if info.blinding != b {
continue;
}
found = true;
// we have a partial match, let's just confirm
let info = proof::rewind(
wallet.keychain(),
key_id,
output.commit,
None,
output.range_proof().unwrap(),
).unwrap();
let message = ProofMessageElements::from_proof_message(&info.message).unwrap();
let value = message.value();
if value.is_err() || !message.zeroes_correct() {
continue;
}
let value = value.unwrap();
info!(
LOGGER,
"Output found: {:?}, key_index: {:?}", output.commit, i,
);
found_key_index.push(i as u32);
// add it to result set here
let commit_id = output.commit.0;
let is_coinbase = coinbase_status(output);
info!(LOGGER, "Amount: {}", value);
info!(LOGGER, "Amount: {}", info.value);
let commit = wallet
.keychain()
@ -204,7 +167,7 @@ where
commit,
key_id.clone(),
i as u32,
value,
info.value,
height,
lock_height,
is_coinbase,
@ -218,7 +181,7 @@ where
LOGGER,
"Very probable matching output found with amount: {} \
but didn't match key child key up to {}",
message.value().unwrap(),
info.value,
max_derivations,
);
}
@ -248,9 +211,6 @@ where
let batch_size = 1000;
let mut start_index = 1;
// Keep a set of keys we've already claimed (cause it's far faster than
// deriving a key for each one)
let mut found_key_index: Vec<u32> = vec![];
// this will start here, then lower as outputs are found, moving backwards on
// the chain
loop {
@ -265,10 +225,10 @@ where
let root_key_id = wallet.keychain().root_key_id();
let result_vec =
find_outputs_with_key(wallet, output_listing.outputs.clone(), &mut found_key_index);
find_outputs_with_key(wallet, output_listing.outputs.clone());
let mut batch = wallet.batch()?;
for output in result_vec {
batch.save(OutputData {
let _ = batch.save(OutputData {
root_key_id: root_key_id.clone(),
key_id: output.1.clone(),
n_child: output.2,

View file

@ -22,7 +22,6 @@ extern crate uuid;
use keychain::{BlindSum, BlindingFactor, ExtKeychain, Keychain};
use util::secp::key::{PublicKey, SecretKey};
use util::secp::pedersen::ProofMessage;
use util::{kernel_sig_msg, secp};
use wallet::libtx::{aggsig, proof};
use wallet::libwallet::internal::sigcontext;
@ -408,8 +407,8 @@ fn aggsig_sender_receiver_interaction_offset() {
fn test_rewind_range_proof() {
let keychain = ExtKeychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let key_id2 = keychain.derive_key_id(2).unwrap();
let commit = keychain.commit(5, &key_id).unwrap();
let msg = ProofMessage::from_bytes(&[0u8; 64]);
let extra_data = [99u8; 64];
let proof = proof::create(
@ -418,65 +417,24 @@ fn test_rewind_range_proof() {
&key_id,
commit,
Some(extra_data.to_vec().clone()),
msg,
).unwrap();
let proof_info = proof::rewind(
&keychain,
&key_id,
commit,
Some(extra_data.to_vec().clone()),
proof,
).unwrap();
let proof_info =
proof::rewind(&keychain, 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::BULLET_PROOF_MSG_SIZE])
);
let key_id2 = keychain.derive_key_id(2).unwrap();
// cannot rewind with a different nonce
let proof_info = proof::rewind(
&keychain,
&key_id2,
commit,
Some(extra_data.to_vec().clone()),
proof,
).unwrap();
// 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
// cannot rewind with a different commit
let commit2 = keychain.commit(5, &key_id2).unwrap();
let proof_info = proof::rewind(
&keychain,
&key_id,
commit2,
Some(extra_data.to_vec().clone()),
proof,
).unwrap();
let proof_info =
proof::rewind(&keychain, commit2, Some(extra_data.to_vec().clone()), proof).unwrap();
assert_eq!(proof_info.success, false);
assert_eq!(proof_info.value, 0);
// cannot rewind with a commitment to a different value
let commit3 = keychain.commit(4, &key_id).unwrap();
let proof_info = proof::rewind(
&keychain,
&key_id,
commit3,
Some(extra_data.to_vec().clone()),
proof,
).unwrap();
let proof_info =
proof::rewind(&keychain, commit3, Some(extra_data.to_vec().clone()), proof).unwrap();
assert_eq!(proof_info.success, false);
assert_eq!(proof_info.value, 0);
@ -485,7 +443,6 @@ fn test_rewind_range_proof() {
let wrong_extra_data = [98u8; 64];
let _should_err = proof::rewind(
&keychain,
&key_id,
commit3,
Some(wrong_extra_data.to_vec().clone()),
proof,