mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
Support new Bulletproof rewind scheme (#2848)
* Update keychain with new rewind scheme * Refactor: proof builder trait * Update tests, cleanup * rustfmt * Move conversion of SwitchCommitmentType * Add proof build trait to tx builders * Cache hashes in proof builders * Proof builder tests * Add ViewKey struct * Fix some warnings * Zeroize proof builder secrets on drop
This commit is contained in:
parent
6429580b0c
commit
e3f3064414
30 changed files with 1399 additions and 208 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -777,6 +777,7 @@ dependencies = [
|
|||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -844,16 +845,17 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "grin_secp256k1zkp"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayvec 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -914,7 +916,7 @@ dependencies = [
|
|||
"backtrace 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"grin_secp256k1zkp 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"grin_secp256k1zkp 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2739,7 +2741,7 @@ dependencies = [
|
|||
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
|
||||
"checksum git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "591f8be1674b421644b6c030969520bc3fa12114d2eb467471982ed3e9584e71"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum grin_secp256k1zkp 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "75e9a265f3eeea4c204470f7262e2c6fe18f3d8ddf5fb24340cb550ac4f909c5"
|
||||
"checksum grin_secp256k1zkp 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0e96161c7d923bf094e7f4f583e680a03746b692523f2211bff59f642e05aa85"
|
||||
"checksum h2 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "85ab6286db06040ddefb71641b50017c06874614001a134b423783e2db2920bd"
|
||||
"checksum hashbrown 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "570178d5e4952010d138b0f1d581271ff3a02406d990f887d1e87e3d6e43b0ac"
|
||||
"checksum hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "733e1b3ac906631ca01ebb577e9bb0f5e37a454032b9036b5eaea4013ed6f99a"
|
||||
|
|
|
@ -76,7 +76,14 @@ fn data_files() {
|
|||
let prev = chain.head_header().unwrap();
|
||||
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
|
||||
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
|
||||
let reward = libtx::reward::output(&keychain, &pk, 0, false).unwrap();
|
||||
let reward = libtx::reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&pk,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let mut b =
|
||||
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
|
||||
.unwrap();
|
||||
|
@ -154,7 +161,8 @@ fn _prepare_block_nosum(
|
|||
let key_id = ExtKeychainPath::new(1, diff as u32, 0, 0, 0).to_identifier();
|
||||
|
||||
let fees = txs.iter().map(|tx| tx.fee()).sum();
|
||||
let reward = libtx::reward::output(kc, &key_id, fees, false).unwrap();
|
||||
let reward =
|
||||
libtx::reward::output(kc, &libtx::ProofBuilder::new(kc), &key_id, fees, false).unwrap();
|
||||
let mut b = match core::core::Block::new(
|
||||
prev,
|
||||
txs.into_iter().cloned().collect(),
|
||||
|
|
|
@ -19,7 +19,7 @@ use self::core::core::verifier_cache::LruVerifierCache;
|
|||
use self::core::core::{Block, BlockHeader, OutputIdentifier, Transaction};
|
||||
use self::core::genesis;
|
||||
use self::core::global::ChainTypes;
|
||||
use self::core::libtx::{self, build, reward};
|
||||
use self::core::libtx::{self, build, reward, ProofBuilder};
|
||||
use self::core::pow::Difficulty;
|
||||
use self::core::{consensus, global, pow};
|
||||
use self::keychain::{ExtKeychain, ExtKeychainPath, Keychain};
|
||||
|
@ -106,7 +106,14 @@ fn mine_genesis_reward_chain() {
|
|||
let mut genesis = genesis::genesis_dev();
|
||||
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
||||
let key_id = keychain::ExtKeychain::derive_key_id(0, 1, 0, 0, 0);
|
||||
let reward = reward::output(&keychain, &key_id, 0, false).unwrap();
|
||||
let reward = reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&key_id,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
genesis = genesis.with_reward(reward.0, reward.1);
|
||||
|
||||
let tmp_chain_dir = ".grin.tmp";
|
||||
|
@ -143,7 +150,9 @@ where
|
|||
let prev = chain.head_header().unwrap();
|
||||
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
|
||||
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
|
||||
let reward = libtx::reward::output(keychain, &pk, 0, false).unwrap();
|
||||
let reward =
|
||||
libtx::reward::output(keychain, &libtx::ProofBuilder::new(keychain), &pk, 0, false)
|
||||
.unwrap();
|
||||
let mut b =
|
||||
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
|
||||
.unwrap();
|
||||
|
@ -401,6 +410,7 @@ fn spend_in_fork_and_compact() {
|
|||
let chain = setup(".grin6", pow::mine_genesis_block().unwrap());
|
||||
let prev = chain.head_header().unwrap();
|
||||
let kc = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let pb = ProofBuilder::new(&kc);
|
||||
|
||||
let mut fork_head = prev;
|
||||
|
||||
|
@ -434,6 +444,7 @@ fn spend_in_fork_and_compact() {
|
|||
build::with_fee(20000),
|
||||
],
|
||||
&kc,
|
||||
&pb,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -451,6 +462,7 @@ fn spend_in_fork_and_compact() {
|
|||
build::with_fee(20000),
|
||||
],
|
||||
&kc,
|
||||
&pb,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -540,7 +552,14 @@ fn output_header_mappings() {
|
|||
let prev = chain.head_header().unwrap();
|
||||
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
|
||||
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
|
||||
let reward = libtx::reward::output(&keychain, &pk, 0, false).unwrap();
|
||||
let reward = libtx::reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&pk,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
reward_outputs.push(reward.0.clone());
|
||||
let mut b =
|
||||
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
|
||||
|
@ -643,7 +662,8 @@ where
|
|||
let key_id = ExtKeychainPath::new(1, diff as u32, 0, 0, 0).to_identifier();
|
||||
|
||||
let fees = txs.iter().map(|tx| tx.fee()).sum();
|
||||
let reward = libtx::reward::output(kc, &key_id, fees, false).unwrap();
|
||||
let reward =
|
||||
libtx::reward::output(kc, &libtx::ProofBuilder::new(kc), &key_id, fees, false).unwrap();
|
||||
let mut b = match core::core::Block::new(
|
||||
prev,
|
||||
txs.into_iter().cloned().collect(),
|
||||
|
|
|
@ -60,7 +60,14 @@ fn test_various_store_indices() {
|
|||
|
||||
setup_chain(&genesis, chain_store.clone()).unwrap();
|
||||
|
||||
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
|
||||
let reward = libtx::reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&key_id,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let block = Block::new(&genesis.header, vec![], Difficulty::min(), reward).unwrap();
|
||||
let block_hash = block.hash();
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use self::chain::types::NoopAdapter;
|
|||
use self::chain::ErrorKind;
|
||||
use self::core::core::verifier_cache::LruVerifierCache;
|
||||
use self::core::global::{self, ChainTypes};
|
||||
use self::core::libtx::{self, build};
|
||||
use self::core::libtx::{self, build, ProofBuilder};
|
||||
use self::core::pow::Difficulty;
|
||||
use self::core::{consensus, pow};
|
||||
use self::keychain::{ExtKeychain, ExtKeychainPath, Keychain};
|
||||
|
@ -59,13 +59,14 @@ fn test_coinbase_maturity() {
|
|||
let prev = chain.head_header().unwrap();
|
||||
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
|
||||
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
|
||||
let key_id3 = ExtKeychainPath::new(1, 3, 0, 0, 0).to_identifier();
|
||||
let key_id4 = ExtKeychainPath::new(1, 4, 0, 0, 0).to_identifier();
|
||||
|
||||
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
|
||||
let reward = libtx::reward::output(&keychain, &key_id1, 0, false).unwrap();
|
||||
let reward = libtx::reward::output(&keychain, &builder, &key_id1, 0, false).unwrap();
|
||||
let mut block = core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
|
||||
block.header.timestamp = prev.timestamp + Duration::seconds(60);
|
||||
block.header.pow.secondary_scaling = next_header_info.secondary_scaling;
|
||||
|
@ -104,12 +105,13 @@ fn test_coinbase_maturity() {
|
|||
build::with_fee(2),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let txs = vec![coinbase_txn.clone()];
|
||||
let fees = txs.iter().map(|tx| tx.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id3, fees, false).unwrap();
|
||||
let reward = libtx::reward::output(&keychain, &builder, &key_id3, fees, false).unwrap();
|
||||
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
|
||||
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
|
||||
block.header.timestamp = prev.timestamp + Duration::seconds(60);
|
||||
|
@ -141,10 +143,11 @@ fn test_coinbase_maturity() {
|
|||
let prev = chain.head_header().unwrap();
|
||||
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
|
||||
|
||||
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
|
||||
let reward = libtx::reward::output(&keychain, &key_id1, 0, false).unwrap();
|
||||
let reward = libtx::reward::output(&keychain, &builder, &key_id1, 0, false).unwrap();
|
||||
let mut block =
|
||||
core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
|
||||
|
||||
|
@ -185,12 +188,13 @@ fn test_coinbase_maturity() {
|
|||
build::with_fee(2),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let txs = vec![coinbase_txn.clone()];
|
||||
let fees = txs.iter().map(|tx| tx.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id3, fees, false).unwrap();
|
||||
let reward = libtx::reward::output(&keychain, &builder, &key_id3, fees, false).unwrap();
|
||||
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
|
||||
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
|
||||
block.header.timestamp = prev.timestamp + Duration::seconds(60);
|
||||
|
@ -222,9 +226,10 @@ fn test_coinbase_maturity() {
|
|||
let prev = chain.head_header().unwrap();
|
||||
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let pk = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
|
||||
|
||||
let reward = libtx::reward::output(&keychain, &pk, 0, false).unwrap();
|
||||
let reward = libtx::reward::output(&keychain, &builder, &pk, 0, false).unwrap();
|
||||
let mut block =
|
||||
core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
|
||||
let next_header_info =
|
||||
|
@ -254,7 +259,7 @@ fn test_coinbase_maturity() {
|
|||
let txs = vec![coinbase_txn];
|
||||
let fees = txs.iter().map(|tx| tx.fee()).sum();
|
||||
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
|
||||
let reward = libtx::reward::output(&keychain, &key_id4, fees, false).unwrap();
|
||||
let reward = libtx::reward::output(&keychain, &builder, &key_id4, fees, false).unwrap();
|
||||
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
|
||||
|
||||
block.header.timestamp = prev.timestamp + Duration::seconds(60);
|
||||
|
|
|
@ -27,6 +27,7 @@ siphasher = "0.2"
|
|||
uuid = { version = "0.6", features = ["serde", "v4"] }
|
||||
log = "0.4"
|
||||
chrono = { version = "0.4.4", features = ["serde"] }
|
||||
zeroize = "0.8"
|
||||
|
||||
grin_keychain = { path = "../keychain", version = "2.0.0-beta.1" }
|
||||
grin_util = { path = "../util", version = "2.0.0-beta.1" }
|
||||
|
|
|
@ -1499,14 +1499,16 @@ mod test {
|
|||
use super::*;
|
||||
use crate::core::hash::Hash;
|
||||
use crate::core::id::{ShortId, ShortIdentifiable};
|
||||
use crate::keychain::{ExtKeychain, Keychain};
|
||||
use crate::keychain::{ExtKeychain, Keychain, SwitchCommitmentType};
|
||||
use crate::util::secp;
|
||||
|
||||
#[test]
|
||||
fn test_kernel_ser_deser() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let commit = keychain.commit(5, &key_id).unwrap();
|
||||
let commit = keychain
|
||||
.commit(5, &key_id, &SwitchCommitmentType::Regular)
|
||||
.unwrap();
|
||||
|
||||
// just some bytes for testing ser/deser
|
||||
let sig = secp::Signature::from_raw_data(&[0; 64]).unwrap();
|
||||
|
@ -1552,10 +1554,14 @@ mod test {
|
|||
let keychain = ExtKeychain::from_seed(&[0; 32], false).unwrap();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
|
||||
let commit = keychain.commit(1003, &key_id).unwrap();
|
||||
let commit = keychain
|
||||
.commit(1003, &key_id, &SwitchCommitmentType::Regular)
|
||||
.unwrap();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
|
||||
let commit_2 = keychain.commit(1003, &key_id).unwrap();
|
||||
let commit_2 = keychain
|
||||
.commit(1003, &key_id, &SwitchCommitmentType::Regular)
|
||||
.unwrap();
|
||||
|
||||
assert!(commit == commit_2);
|
||||
}
|
||||
|
@ -1564,7 +1570,9 @@ mod test {
|
|||
fn input_short_id() {
|
||||
let keychain = ExtKeychain::from_seed(&[0; 32], false).unwrap();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let commit = keychain.commit(5, &key_id).unwrap();
|
||||
let commit = keychain
|
||||
.commit(5, &key_id, &SwitchCommitmentType::Regular)
|
||||
.unwrap();
|
||||
|
||||
let input = Input {
|
||||
features: OutputFeatures::Plain,
|
||||
|
|
|
@ -36,6 +36,7 @@ extern crate log;
|
|||
use failure;
|
||||
#[macro_use]
|
||||
extern crate failure_derive;
|
||||
extern crate zeroize;
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ use crate::libtx::error::{Error, ErrorKind};
|
|||
use crate::util::secp::key::{PublicKey, SecretKey};
|
||||
use crate::util::secp::pedersen::Commitment;
|
||||
use crate::util::secp::{self, aggsig, Message, Secp256k1, Signature};
|
||||
use grin_keychain::SwitchCommitmentType;
|
||||
|
||||
/// Creates a new secure nonce (as a SecretKey), guaranteed to be usable during
|
||||
/// aggsig creation.
|
||||
|
@ -231,15 +232,17 @@ pub fn verify_partial_sig(
|
|||
/// use core::libtx::{aggsig, proof};
|
||||
/// use core::core::transaction::{kernel_sig_msg, KernelFeatures};
|
||||
/// use core::core::{Output, OutputFeatures};
|
||||
/// use keychain::{Keychain, ExtKeychain};
|
||||
/// use keychain::{Keychain, ExtKeychain, SwitchCommitmentType};
|
||||
///
|
||||
/// let secp = Secp256k1::with_caps(ContextFlag::Commit);
|
||||
/// let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
/// let fees = 10_000;
|
||||
/// let value = reward(fees);
|
||||
/// let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
/// let commit = keychain.commit(value, &key_id).unwrap();
|
||||
/// let rproof = proof::create(&keychain, value, &key_id, commit, None).unwrap();
|
||||
/// let switch = &SwitchCommitmentType::Regular;
|
||||
/// let commit = keychain.commit(value, &key_id, switch).unwrap();
|
||||
/// let builder = proof::ProofBuilder::new(&keychain);
|
||||
/// let rproof = proof::create(&keychain, &builder, value, &key_id, switch, commit, None).unwrap();
|
||||
/// let output = Output {
|
||||
/// features: OutputFeatures::Coinbase,
|
||||
/// commit: commit,
|
||||
|
@ -266,7 +269,7 @@ pub fn sign_from_key_id<K>(
|
|||
where
|
||||
K: Keychain,
|
||||
{
|
||||
let skey = k.derive_key(value, key_id)?;
|
||||
let skey = k.derive_key(value, key_id, &SwitchCommitmentType::Regular)?; // TODO: proper support for different switch commitment schemes
|
||||
let sig = aggsig::sign_single(secp, &msg, &skey, s_nonce, None, None, blind_sum, None)?;
|
||||
Ok(sig)
|
||||
}
|
||||
|
@ -296,7 +299,7 @@ where
|
|||
/// use util::secp::{ContextFlag, Secp256k1};
|
||||
/// use core::core::transaction::{kernel_sig_msg, KernelFeatures};
|
||||
/// use core::core::{Output, OutputFeatures};
|
||||
/// use keychain::{Keychain, ExtKeychain};
|
||||
/// use keychain::{Keychain, ExtKeychain, SwitchCommitmentType};
|
||||
///
|
||||
/// // Create signature
|
||||
/// let secp = Secp256k1::with_caps(ContextFlag::Commit);
|
||||
|
@ -304,8 +307,10 @@ where
|
|||
/// let fees = 10_000;
|
||||
/// let value = reward(fees);
|
||||
/// let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
/// let commit = keychain.commit(value, &key_id).unwrap();
|
||||
/// let rproof = proof::create(&keychain, value, &key_id, commit, None).unwrap();
|
||||
/// let switch = &SwitchCommitmentType::Regular;
|
||||
/// let commit = keychain.commit(value, &key_id, switch).unwrap();
|
||||
/// let builder = proof::ProofBuilder::new(&keychain);
|
||||
/// let rproof = proof::create(&keychain, &builder, value, &key_id, switch, commit, None).unwrap();
|
||||
/// let output = Output {
|
||||
/// features: OutputFeatures::Coinbase,
|
||||
/// commit: commit,
|
||||
|
|
|
@ -27,33 +27,42 @@
|
|||
|
||||
use crate::core::{Input, Output, OutputFeatures, Transaction, TxKernel};
|
||||
use crate::keychain::{BlindSum, BlindingFactor, Identifier, Keychain};
|
||||
use crate::libtx::{aggsig, proof, Error};
|
||||
use crate::libtx::proof::{self, ProofBuild};
|
||||
use crate::libtx::{aggsig, Error};
|
||||
use grin_keychain::SwitchCommitmentType;
|
||||
|
||||
/// Context information available to transaction combinators.
|
||||
pub struct Context<'a, K>
|
||||
pub struct Context<'a, K, B>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
/// The keychain used for key derivation
|
||||
pub keychain: &'a K,
|
||||
/// The bulletproof builder
|
||||
pub builder: &'a B,
|
||||
}
|
||||
|
||||
/// Function type returned by the transaction combinators. Transforms a
|
||||
/// (Transaction, BlindSum) pair into another, provided some context.
|
||||
pub type Append<K> = dyn for<'a> Fn(
|
||||
&'a mut Context<'_, K>,
|
||||
pub type Append<K, B> = dyn for<'a> Fn(
|
||||
&'a mut Context<'_, K, B>,
|
||||
(Transaction, TxKernel, BlindSum),
|
||||
) -> (Transaction, TxKernel, BlindSum);
|
||||
|
||||
/// Adds an input with the provided value and blinding key to the transaction
|
||||
/// being built.
|
||||
fn build_input<K>(value: u64, features: OutputFeatures, key_id: Identifier) -> Box<Append<K>>
|
||||
fn build_input<K, B>(value: u64, features: OutputFeatures, key_id: Identifier) -> Box<Append<K, B>>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
Box::new(
|
||||
move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
|
||||
let commit = build.keychain.commit(value, &key_id).unwrap();
|
||||
let commit = build
|
||||
.keychain
|
||||
.commit(value, &key_id, &SwitchCommitmentType::Regular)
|
||||
.unwrap(); // TODO: proper support for different switch commitment schemes
|
||||
let input = Input::new(features, commit);
|
||||
(
|
||||
tx.with_input(input),
|
||||
|
@ -66,9 +75,10 @@ where
|
|||
|
||||
/// Adds an input with the provided value and blinding key to the transaction
|
||||
/// being built.
|
||||
pub fn input<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
|
||||
pub fn input<K, B>(value: u64, key_id: Identifier) -> Box<Append<K, B>>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
debug!(
|
||||
"Building input (spending regular output): {}, {}",
|
||||
|
@ -78,9 +88,10 @@ where
|
|||
}
|
||||
|
||||
/// Adds a coinbase input spending a coinbase output.
|
||||
pub fn coinbase_input<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
|
||||
pub fn coinbase_input<K, B>(value: u64, key_id: Identifier) -> Box<Append<K, B>>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
debug!("Building input (spending coinbase): {}, {}", value, key_id);
|
||||
build_input(value, OutputFeatures::Coinbase, key_id)
|
||||
|
@ -88,17 +99,30 @@ where
|
|||
|
||||
/// Adds an output with the provided value and key identifier from the
|
||||
/// keychain.
|
||||
pub fn output<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
|
||||
pub fn output<K, B>(value: u64, key_id: Identifier) -> Box<Append<K, B>>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
Box::new(
|
||||
move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
|
||||
let commit = build.keychain.commit(value, &key_id).unwrap();
|
||||
// TODO: proper support for different switch commitment schemes
|
||||
let switch = &SwitchCommitmentType::Regular;
|
||||
|
||||
let commit = build.keychain.commit(value, &key_id, switch).unwrap();
|
||||
|
||||
debug!("Building output: {}, {:?}", value, commit);
|
||||
|
||||
let rproof = proof::create(build.keychain, value, &key_id, commit, None).unwrap();
|
||||
let rproof = proof::create(
|
||||
build.keychain,
|
||||
build.builder,
|
||||
value,
|
||||
&key_id,
|
||||
switch,
|
||||
commit,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(
|
||||
tx.with_output(Output {
|
||||
|
@ -114,9 +138,10 @@ where
|
|||
}
|
||||
|
||||
/// Sets the fee on the transaction being built.
|
||||
pub fn with_fee<K>(fee: u64) -> Box<Append<K>>
|
||||
pub fn with_fee<K, B>(fee: u64) -> Box<Append<K, B>>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
Box::new(
|
||||
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
|
||||
|
@ -126,9 +151,10 @@ where
|
|||
}
|
||||
|
||||
/// Sets the lock_height on the transaction being built.
|
||||
pub fn with_lock_height<K>(lock_height: u64) -> Box<Append<K>>
|
||||
pub fn with_lock_height<K, B>(lock_height: u64) -> Box<Append<K, B>>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
Box::new(
|
||||
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
|
||||
|
@ -140,9 +166,10 @@ where
|
|||
/// Adds a known excess value on the transaction being built. Usually used in
|
||||
/// combination with the initial_tx function when a new transaction is built
|
||||
/// by adding to a pre-existing one.
|
||||
pub fn with_excess<K>(excess: BlindingFactor) -> Box<Append<K>>
|
||||
pub fn with_excess<K, B>(excess: BlindingFactor) -> Box<Append<K, B>>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
Box::new(
|
||||
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
|
||||
|
@ -152,9 +179,10 @@ where
|
|||
}
|
||||
|
||||
/// Sets a known tx "offset". Used in final step of tx construction.
|
||||
pub fn with_offset<K>(offset: BlindingFactor) -> Box<Append<K>>
|
||||
pub fn with_offset<K, B>(offset: BlindingFactor) -> Box<Append<K, B>>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
Box::new(
|
||||
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
|
||||
|
@ -166,9 +194,10 @@ where
|
|||
/// Sets an initial transaction to add to when building a new transaction.
|
||||
/// We currently only support building a tx with a single kernel with
|
||||
/// build::transaction()
|
||||
pub fn initial_tx<K>(mut tx: Transaction) -> Box<Append<K>>
|
||||
pub fn initial_tx<K, B>(mut tx: Transaction) -> Box<Append<K, B>>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
assert_eq!(tx.kernels().len(), 1);
|
||||
let kern = tx.kernels_mut().remove(0);
|
||||
|
@ -189,14 +218,16 @@ where
|
|||
/// let (tx2, _) = build::transaction(vec![initial_tx(tx1), with_excess(sum),
|
||||
/// output_rand(2)], keychain).unwrap();
|
||||
///
|
||||
pub fn partial_transaction<K>(
|
||||
elems: Vec<Box<Append<K>>>,
|
||||
pub fn partial_transaction<K, B>(
|
||||
elems: Vec<Box<Append<K, B>>>,
|
||||
keychain: &K,
|
||||
builder: &B,
|
||||
) -> Result<(Transaction, BlindingFactor), Error>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
let mut ctx = Context { keychain };
|
||||
let mut ctx = Context { keychain, builder };
|
||||
let (tx, kern, sum) = elems.iter().fold(
|
||||
(Transaction::empty(), TxKernel::empty(), BlindSum::new()),
|
||||
|acc, elem| elem(&mut ctx, acc),
|
||||
|
@ -212,11 +243,16 @@ where
|
|||
}
|
||||
|
||||
/// Builds a complete transaction.
|
||||
pub fn transaction<K>(elems: Vec<Box<Append<K>>>, keychain: &K) -> Result<Transaction, Error>
|
||||
pub fn transaction<K, B>(
|
||||
elems: Vec<Box<Append<K, B>>>,
|
||||
keychain: &K,
|
||||
builder: &B,
|
||||
) -> Result<Transaction, Error>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
let mut ctx = Context { keychain };
|
||||
let mut ctx = Context { keychain, builder };
|
||||
let (mut tx, mut kern, sum) = elems.iter().fold(
|
||||
(Transaction::empty(), TxKernel::empty(), BlindSum::new()),
|
||||
|acc, elem| elem(&mut ctx, acc),
|
||||
|
@ -260,6 +296,7 @@ mod test {
|
|||
use crate::core::transaction::Weighting;
|
||||
use crate::core::verifier_cache::{LruVerifierCache, VerifierCache};
|
||||
use crate::keychain::{ExtKeychain, ExtKeychainPath};
|
||||
use crate::libtx::ProofBuilder;
|
||||
|
||||
fn verifier_cache() -> Arc<RwLock<dyn VerifierCache>> {
|
||||
Arc::new(RwLock::new(LruVerifierCache::new()))
|
||||
|
@ -268,6 +305,7 @@ mod test {
|
|||
#[test]
|
||||
fn blind_simple_tx() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
|
||||
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
|
||||
let key_id3 = ExtKeychainPath::new(1, 3, 0, 0, 0).to_identifier();
|
||||
|
@ -282,6 +320,7 @@ mod test {
|
|||
with_fee(2),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -291,6 +330,7 @@ mod test {
|
|||
#[test]
|
||||
fn blind_simple_tx_with_offset() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
|
||||
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
|
||||
let key_id3 = ExtKeychainPath::new(1, 3, 0, 0, 0).to_identifier();
|
||||
|
@ -305,6 +345,7 @@ mod test {
|
|||
with_fee(2),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -314,6 +355,7 @@ mod test {
|
|||
#[test]
|
||||
fn blind_simpler_tx() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
|
||||
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
|
||||
|
||||
|
@ -322,6 +364,7 @@ mod test {
|
|||
let tx = transaction(
|
||||
vec![input(6, key_id1), output(2, key_id2), with_fee(4)],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ pub mod secp_ser;
|
|||
use crate::consensus;
|
||||
use crate::core::Transaction;
|
||||
|
||||
pub use self::proof::ProofBuilder;
|
||||
pub use crate::libtx::error::{Error, ErrorKind};
|
||||
|
||||
const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN;
|
||||
|
|
|
@ -14,31 +14,47 @@
|
|||
|
||||
//! Rangeproof library functions
|
||||
|
||||
use crate::keychain::{Identifier, Keychain};
|
||||
use crate::blake2::blake2b::blake2b;
|
||||
use crate::keychain::extkey_bip32::BIP32GrinHasher;
|
||||
use crate::keychain::{Identifier, Keychain, SwitchCommitmentType, ViewKey};
|
||||
use crate::libtx::error::{Error, ErrorKind};
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::secp::pedersen::{Commitment, ProofInfo, ProofMessage, RangeProof};
|
||||
use crate::util::secp::pedersen::{Commitment, ProofMessage, RangeProof};
|
||||
use crate::util::secp::{self, Secp256k1};
|
||||
use crate::zeroize::Zeroize;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// Create a bulletproof
|
||||
pub fn create<K>(
|
||||
pub fn create<K, B>(
|
||||
k: &K,
|
||||
b: &B,
|
||||
amount: u64,
|
||||
key_id: &Identifier,
|
||||
switch: &SwitchCommitmentType,
|
||||
_commit: Commitment,
|
||||
extra_data: Option<Vec<u8>>,
|
||||
) -> Result<RangeProof, Error>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
let commit = k.commit(amount, key_id)?;
|
||||
let skey = k.derive_key(amount, key_id)?;
|
||||
let nonce = k
|
||||
.create_nonce(&commit)
|
||||
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
|
||||
let message = ProofMessage::from_bytes(&key_id.serialize_path());
|
||||
Ok(k.secp()
|
||||
.bullet_proof(amount, skey, nonce, extra_data, Some(message)))
|
||||
// TODO: proper support for different switch commitment schemes
|
||||
// The new bulletproof scheme encodes and decodes it, but
|
||||
// it is not supported at the wallet level (yet).
|
||||
let secp = k.secp();
|
||||
let commit = k.commit(amount, key_id, switch)?;
|
||||
let skey = k.derive_key(amount, key_id, switch)?;
|
||||
let rewind_nonce = b.rewind_nonce(secp, &commit)?;
|
||||
let private_nonce = b.private_nonce(secp, &commit)?;
|
||||
let message = b.proof_message(secp, key_id, switch)?;
|
||||
Ok(secp.bullet_proof(
|
||||
amount,
|
||||
skey,
|
||||
rewind_nonce,
|
||||
private_nonce,
|
||||
extra_data,
|
||||
Some(message),
|
||||
))
|
||||
}
|
||||
|
||||
/// Verify a proof
|
||||
|
@ -55,35 +71,689 @@ pub fn verify(
|
|||
}
|
||||
}
|
||||
|
||||
/// Rewind a rangeproof to retrieve the amount
|
||||
pub fn rewind<K>(
|
||||
k: &K,
|
||||
/// Rewind a rangeproof to retrieve the amount, derivation path and switch commitment type
|
||||
pub fn rewind<B>(
|
||||
secp: &Secp256k1,
|
||||
b: &B,
|
||||
commit: Commitment,
|
||||
extra_data: Option<Vec<u8>>,
|
||||
proof: RangeProof,
|
||||
) -> Result<ProofInfo, Error>
|
||||
) -> Result<Option<(u64, Identifier, SwitchCommitmentType)>, Error>
|
||||
where
|
||||
B: ProofBuild,
|
||||
{
|
||||
let nonce = b
|
||||
.rewind_nonce(secp, &commit)
|
||||
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
|
||||
let info = secp.rewind_bullet_proof(commit, nonce, extra_data, proof);
|
||||
if info.is_err() {
|
||||
return Ok(None);
|
||||
}
|
||||
let info = info.unwrap();
|
||||
|
||||
let amount = info.value;
|
||||
let check = b
|
||||
.check_output(secp, &commit, amount, info.message)
|
||||
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
|
||||
|
||||
Ok(check.map(|(id, switch)| (amount, id, switch)))
|
||||
}
|
||||
|
||||
/// Used for building proofs and checking if the output belongs to the wallet
|
||||
pub trait ProofBuild {
|
||||
/// Create a BP nonce that will allow to rewind the derivation path and flags
|
||||
fn rewind_nonce(&self, secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error>;
|
||||
|
||||
/// Create a BP nonce that blinds the private key
|
||||
fn private_nonce(&self, secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error>;
|
||||
|
||||
/// Create a BP message
|
||||
fn proof_message(
|
||||
&self,
|
||||
secp: &Secp256k1,
|
||||
id: &Identifier,
|
||||
switch: &SwitchCommitmentType,
|
||||
) -> Result<ProofMessage, Error>;
|
||||
|
||||
/// Check if the output belongs to this keychain
|
||||
fn check_output(
|
||||
&self,
|
||||
secp: &Secp256k1,
|
||||
commit: &Commitment,
|
||||
amount: u64,
|
||||
message: ProofMessage,
|
||||
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error>;
|
||||
}
|
||||
|
||||
/// The new, more flexible proof builder
|
||||
pub struct ProofBuilder<'a, K>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
let nonce = k
|
||||
.create_nonce(&commit)
|
||||
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
|
||||
let proof_message = k
|
||||
.secp()
|
||||
.rewind_bullet_proof(commit, nonce, extra_data, proof);
|
||||
let proof_info = match proof_message {
|
||||
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,
|
||||
exp: 0,
|
||||
mantissa: 0,
|
||||
},
|
||||
};
|
||||
return Ok(proof_info);
|
||||
keychain: &'a K,
|
||||
rewind_hash: Vec<u8>,
|
||||
private_hash: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a, K> ProofBuilder<'a, K>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
/// Creates a new instance of this proof builder
|
||||
pub fn new(keychain: &'a K) -> Self {
|
||||
let private_root_key = keychain
|
||||
.derive_key(0, &K::root_key_id(), &SwitchCommitmentType::None)
|
||||
.unwrap();
|
||||
|
||||
let private_hash = blake2b(32, &[], &private_root_key.0).as_bytes().to_vec();
|
||||
|
||||
let public_root_key = keychain
|
||||
.public_root_key()
|
||||
.serialize_vec(keychain.secp(), true);
|
||||
let rewind_hash = blake2b(32, &[], &public_root_key[..]).as_bytes().to_vec();
|
||||
|
||||
Self {
|
||||
keychain,
|
||||
rewind_hash,
|
||||
private_hash,
|
||||
}
|
||||
}
|
||||
|
||||
fn nonce(&self, commit: &Commitment, private: bool) -> Result<SecretKey, Error> {
|
||||
let hash = if private {
|
||||
&self.private_hash
|
||||
} else {
|
||||
&self.rewind_hash
|
||||
};
|
||||
let res = blake2b(32, &commit.0, hash);
|
||||
SecretKey::from_slice(self.keychain.secp(), res.as_bytes()).map_err(|e| {
|
||||
ErrorKind::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()).into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K> ProofBuild for ProofBuilder<'a, K>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
fn rewind_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
|
||||
self.nonce(commit, false)
|
||||
}
|
||||
|
||||
fn private_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
|
||||
self.nonce(commit, true)
|
||||
}
|
||||
|
||||
/// Message bytes:
|
||||
/// 0: reserved for future use
|
||||
/// 1: wallet type (0 for standard)
|
||||
/// 2: switch commitment type
|
||||
/// 3: path depth
|
||||
/// 4-19: derivation path
|
||||
fn proof_message(
|
||||
&self,
|
||||
_secp: &Secp256k1,
|
||||
id: &Identifier,
|
||||
switch: &SwitchCommitmentType,
|
||||
) -> Result<ProofMessage, Error> {
|
||||
let mut msg = [0; 20];
|
||||
msg[2] = u8::from(switch);
|
||||
let id_bytes = id.to_bytes();
|
||||
for i in 0..17 {
|
||||
msg[i + 3] = id_bytes[i];
|
||||
}
|
||||
Ok(ProofMessage::from_bytes(&msg))
|
||||
}
|
||||
|
||||
fn check_output(
|
||||
&self,
|
||||
_secp: &Secp256k1,
|
||||
commit: &Commitment,
|
||||
amount: u64,
|
||||
message: ProofMessage,
|
||||
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error> {
|
||||
if message.len() != 20 {
|
||||
return Ok(None);
|
||||
}
|
||||
let msg = message.as_bytes();
|
||||
let exp: [u8; 2] = [0; 2];
|
||||
if msg[..2] != exp {
|
||||
return Ok(None);
|
||||
}
|
||||
let switch = match SwitchCommitmentType::try_from(msg[2]) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return Ok(None),
|
||||
};
|
||||
let depth = u8::min(msg[3], 4);
|
||||
let id = Identifier::from_serialized_path(depth, &msg[4..]);
|
||||
|
||||
let commit_exp = self.keychain.commit(amount, &id, &switch)?;
|
||||
match commit == &commit_exp {
|
||||
true => Ok(Some((id, switch))),
|
||||
false => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K> Zeroize for ProofBuilder<'a, K>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
fn zeroize(&mut self) {
|
||||
self.rewind_hash.zeroize();
|
||||
self.private_hash.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K> Drop for ProofBuilder<'a, K>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
/// The legacy proof builder, used before the first hard fork
|
||||
pub struct LegacyProofBuilder<'a, K>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
keychain: &'a K,
|
||||
root_hash: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a, K> LegacyProofBuilder<'a, K>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
/// Creates a new instance of this proof builder
|
||||
pub fn new(keychain: &'a K) -> Self {
|
||||
Self {
|
||||
keychain,
|
||||
root_hash: keychain
|
||||
.derive_key(0, &K::root_key_id(), &SwitchCommitmentType::Regular)
|
||||
.unwrap()
|
||||
.0
|
||||
.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
fn nonce(&self, commit: &Commitment) -> Result<SecretKey, Error> {
|
||||
let res = blake2b(32, &commit.0, &self.root_hash);
|
||||
SecretKey::from_slice(self.keychain.secp(), res.as_bytes()).map_err(|e| {
|
||||
ErrorKind::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()).into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K> ProofBuild for LegacyProofBuilder<'a, K>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
fn rewind_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
|
||||
self.nonce(commit)
|
||||
}
|
||||
|
||||
fn private_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
|
||||
self.nonce(commit)
|
||||
}
|
||||
|
||||
/// Message bytes:
|
||||
/// 0-3: 0
|
||||
/// 4-19: derivation path
|
||||
/// All outputs with this scheme are assumed to use regular switch commitments
|
||||
fn proof_message(
|
||||
&self,
|
||||
_secp: &Secp256k1,
|
||||
id: &Identifier,
|
||||
_switch: &SwitchCommitmentType,
|
||||
) -> Result<ProofMessage, Error> {
|
||||
let mut msg = [0; 20];
|
||||
let id_ser = id.serialize_path();
|
||||
for i in 0..16 {
|
||||
msg[i + 4] = id_ser[i];
|
||||
}
|
||||
Ok(ProofMessage::from_bytes(&msg))
|
||||
}
|
||||
|
||||
fn check_output(
|
||||
&self,
|
||||
_secp: &Secp256k1,
|
||||
commit: &Commitment,
|
||||
amount: u64,
|
||||
message: ProofMessage,
|
||||
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error> {
|
||||
if message.len() != 20 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let msg = message.as_bytes();
|
||||
let id = Identifier::from_serialized_path(3, &msg[4..]);
|
||||
let exp: [u8; 4] = [0; 4];
|
||||
if msg[..4] != exp {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let commit_exp = self
|
||||
.keychain
|
||||
.commit(amount, &id, &SwitchCommitmentType::Regular)?;
|
||||
match commit == &commit_exp {
|
||||
true => Ok(Some((id, SwitchCommitmentType::Regular))),
|
||||
false => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K> Zeroize for LegacyProofBuilder<'a, K>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
fn zeroize(&mut self) {
|
||||
self.root_hash.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K> Drop for LegacyProofBuilder<'a, K>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl ProofBuild for ViewKey {
|
||||
fn rewind_nonce(&self, secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
|
||||
let res = blake2b(32, &commit.0, &self.rewind_hash);
|
||||
SecretKey::from_slice(secp, res.as_bytes()).map_err(|e| {
|
||||
ErrorKind::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()).into()
|
||||
})
|
||||
}
|
||||
|
||||
fn private_nonce(&self, _secp: &Secp256k1, _commit: &Commitment) -> Result<SecretKey, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn proof_message(
|
||||
&self,
|
||||
_secp: &Secp256k1,
|
||||
_id: &Identifier,
|
||||
_switch: &SwitchCommitmentType,
|
||||
) -> Result<ProofMessage, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn check_output(
|
||||
&self,
|
||||
secp: &Secp256k1,
|
||||
commit: &Commitment,
|
||||
amount: u64,
|
||||
message: ProofMessage,
|
||||
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error> {
|
||||
if message.len() != 20 {
|
||||
return Ok(None);
|
||||
}
|
||||
let msg = message.as_bytes();
|
||||
let exp: [u8; 2] = [0; 2];
|
||||
if msg[..2] != exp {
|
||||
return Ok(None);
|
||||
}
|
||||
let switch = match SwitchCommitmentType::try_from(msg[2]) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return Ok(None),
|
||||
};
|
||||
let depth = u8::min(msg[3], 4);
|
||||
let id = Identifier::from_serialized_path(depth, &msg[4..]);
|
||||
|
||||
let path = id.to_path();
|
||||
if self.depth > path.depth {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// For non-root key, check child number of current depth
|
||||
if self.depth > 0
|
||||
&& path.depth > 0
|
||||
&& self.child_number != path.path[self.depth as usize - 1]
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut key = self.clone();
|
||||
let mut hasher = BIP32GrinHasher::new(self.is_floo);
|
||||
for i in self.depth..path.depth {
|
||||
let child_number = path.path[i as usize];
|
||||
if child_number.is_hardened() {
|
||||
return Ok(None);
|
||||
}
|
||||
key = key.ckd_pub(&secp, &mut hasher, child_number)?;
|
||||
}
|
||||
let pub_key = key.commit(secp, amount, &switch)?;
|
||||
if commit.to_pubkey(&secp)? == pub_key {
|
||||
Ok(Some((id, switch)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::keychain::ExtKeychain;
|
||||
use grin_keychain::ChildNumber;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
#[test]
|
||||
fn legacy_builder() {
|
||||
let rng = &mut thread_rng();
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = LegacyProofBuilder::new(&keychain);
|
||||
let amount = rng.gen();
|
||||
let id = ExtKeychain::derive_key_id(3, rng.gen(), rng.gen(), rng.gen(), 0);
|
||||
let switch = SwitchCommitmentType::Regular;
|
||||
let commit = keychain.commit(amount, &id, &switch).unwrap();
|
||||
let proof = create(
|
||||
&keychain,
|
||||
&builder,
|
||||
amount,
|
||||
&id,
|
||||
&switch,
|
||||
commit.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(verify(&keychain.secp(), commit.clone(), proof.clone(), None).is_ok());
|
||||
let rewind = rewind(keychain.secp(), &builder, commit, None, proof).unwrap();
|
||||
assert!(rewind.is_some());
|
||||
let (r_amount, r_id, r_switch) = rewind.unwrap();
|
||||
assert_eq!(r_amount, amount);
|
||||
assert_eq!(r_id, id);
|
||||
assert_eq!(r_switch, switch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder() {
|
||||
let rng = &mut thread_rng();
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let amount = rng.gen();
|
||||
let id = ExtKeychain::derive_key_id(3, rng.gen(), rng.gen(), rng.gen(), 0);
|
||||
// With switch commitment
|
||||
let commit_a = {
|
||||
let switch = SwitchCommitmentType::Regular;
|
||||
let commit = keychain.commit(amount, &id, &switch).unwrap();
|
||||
let proof = create(
|
||||
&keychain,
|
||||
&builder,
|
||||
amount,
|
||||
&id,
|
||||
&switch,
|
||||
commit.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(verify(&keychain.secp(), commit.clone(), proof.clone(), None).is_ok());
|
||||
let rewind = rewind(keychain.secp(), &builder, commit.clone(), None, proof).unwrap();
|
||||
assert!(rewind.is_some());
|
||||
let (r_amount, r_id, r_switch) = rewind.unwrap();
|
||||
assert_eq!(r_amount, amount);
|
||||
assert_eq!(r_id, id);
|
||||
assert_eq!(r_switch, switch);
|
||||
commit
|
||||
};
|
||||
// Without switch commitment
|
||||
let commit_b = {
|
||||
let switch = SwitchCommitmentType::None;
|
||||
let commit = keychain.commit(amount, &id, &switch).unwrap();
|
||||
let proof = create(
|
||||
&keychain,
|
||||
&builder,
|
||||
amount,
|
||||
&id,
|
||||
&switch,
|
||||
commit.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(verify(&keychain.secp(), commit.clone(), proof.clone(), None).is_ok());
|
||||
let rewind = rewind(keychain.secp(), &builder, commit.clone(), None, proof).unwrap();
|
||||
assert!(rewind.is_some());
|
||||
let (r_amount, r_id, r_switch) = rewind.unwrap();
|
||||
assert_eq!(r_amount, amount);
|
||||
assert_eq!(r_id, id);
|
||||
assert_eq!(r_switch, switch);
|
||||
commit
|
||||
};
|
||||
// The resulting pedersen commitments should be different
|
||||
assert_ne!(commit_a, commit_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_key() {
|
||||
// TODO
|
||||
/*let rng = &mut thread_rng();
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let mut hasher = keychain.hasher();
|
||||
let view_key = ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
|
||||
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
|
||||
|
||||
let amount = rng.gen();
|
||||
//let id = ExtKeychain::derive_key_id(3, rng.gen::<u16>() as u32, rng.gen::<u16>() as u32, rng.gen::<u16>() as u32, 0);
|
||||
let id = ExtKeychain::derive_key_id(0, 0, 0, 0, 0);
|
||||
let switch = SwitchCommitmentType::Regular;
|
||||
println!("commit_0 = {:?}", keychain.commit(amount, &id, &SwitchCommitmentType::None).unwrap().0.to_vec());
|
||||
let commit = keychain.commit(amount, &id, &switch).unwrap();
|
||||
|
||||
// Generate proof with ProofBuilder..
|
||||
let proof = create(&keychain, &builder, amount, &id, &switch, commit.clone(), None).unwrap();
|
||||
// ..and rewind with ViewKey
|
||||
let rewind = rewind(keychain.secp(), &view_key, commit.clone(), None, proof);
|
||||
|
||||
assert!(rewind.is_ok());
|
||||
let rewind = rewind.unwrap();
|
||||
assert!(rewind.is_some());
|
||||
let (r_amount, r_id, r_switch) = rewind.unwrap();
|
||||
assert_eq!(r_amount, amount);
|
||||
assert_eq!(r_id, id);
|
||||
assert_eq!(r_switch, switch);*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_key_no_switch() {
|
||||
let rng = &mut thread_rng();
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let mut hasher = keychain.hasher();
|
||||
let view_key =
|
||||
ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
|
||||
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
|
||||
|
||||
let amount = rng.gen();
|
||||
let id = ExtKeychain::derive_key_id(
|
||||
3,
|
||||
rng.gen::<u16>() as u32,
|
||||
rng.gen::<u16>() as u32,
|
||||
rng.gen::<u16>() as u32,
|
||||
0,
|
||||
);
|
||||
let switch = SwitchCommitmentType::None;
|
||||
let commit = keychain.commit(amount, &id, &switch).unwrap();
|
||||
|
||||
// Generate proof with ProofBuilder..
|
||||
let proof = create(
|
||||
&keychain,
|
||||
&builder,
|
||||
amount,
|
||||
&id,
|
||||
&switch,
|
||||
commit.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
// ..and rewind with ViewKey
|
||||
let rewind = rewind(keychain.secp(), &view_key, commit.clone(), None, proof);
|
||||
|
||||
assert!(rewind.is_ok());
|
||||
let rewind = rewind.unwrap();
|
||||
assert!(rewind.is_some());
|
||||
let (r_amount, r_id, r_switch) = rewind.unwrap();
|
||||
assert_eq!(r_amount, amount);
|
||||
assert_eq!(r_id, id);
|
||||
assert_eq!(r_switch, switch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_key_hardened() {
|
||||
let rng = &mut thread_rng();
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let mut hasher = keychain.hasher();
|
||||
let view_key =
|
||||
ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
|
||||
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
|
||||
|
||||
let amount = rng.gen();
|
||||
let id = ExtKeychain::derive_key_id(
|
||||
3,
|
||||
rng.gen::<u16>() as u32,
|
||||
u32::max_value() - 2,
|
||||
rng.gen::<u16>() as u32,
|
||||
0,
|
||||
);
|
||||
let switch = SwitchCommitmentType::None;
|
||||
let commit = keychain.commit(amount, &id, &switch).unwrap();
|
||||
|
||||
// Generate proof with ProofBuilder..
|
||||
let proof = create(
|
||||
&keychain,
|
||||
&builder,
|
||||
amount,
|
||||
&id,
|
||||
&switch,
|
||||
commit.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
// ..and rewind with ViewKey
|
||||
let rewind = rewind(keychain.secp(), &view_key, commit.clone(), None, proof);
|
||||
|
||||
assert!(rewind.is_ok());
|
||||
let rewind = rewind.unwrap();
|
||||
assert!(rewind.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_key_child() {
|
||||
let rng = &mut thread_rng();
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let mut hasher = keychain.hasher();
|
||||
let view_key =
|
||||
ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
|
||||
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
|
||||
|
||||
// Same child
|
||||
{
|
||||
let child_view_key = view_key
|
||||
.ckd_pub(
|
||||
keychain.secp(),
|
||||
&mut hasher,
|
||||
ChildNumber::from_normal_idx(10),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(child_view_key.depth, 1);
|
||||
|
||||
let amount = rng.gen();
|
||||
let id = ExtKeychain::derive_key_id(
|
||||
3,
|
||||
10,
|
||||
rng.gen::<u16>() as u32,
|
||||
rng.gen::<u16>() as u32,
|
||||
0,
|
||||
);
|
||||
let switch = SwitchCommitmentType::None;
|
||||
let commit = keychain.commit(amount, &id, &switch).unwrap();
|
||||
|
||||
// Generate proof with ProofBuilder..
|
||||
let proof = create(
|
||||
&keychain,
|
||||
&builder,
|
||||
amount,
|
||||
&id,
|
||||
&switch,
|
||||
commit.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
// ..and rewind with child ViewKey
|
||||
let rewind = rewind(
|
||||
keychain.secp(),
|
||||
&child_view_key,
|
||||
commit.clone(),
|
||||
None,
|
||||
proof,
|
||||
);
|
||||
|
||||
assert!(rewind.is_ok());
|
||||
let rewind = rewind.unwrap();
|
||||
assert!(rewind.is_some());
|
||||
let (r_amount, r_id, r_switch) = rewind.unwrap();
|
||||
assert_eq!(r_amount, amount);
|
||||
assert_eq!(r_id, id);
|
||||
assert_eq!(r_switch, switch);
|
||||
}
|
||||
|
||||
// Different child
|
||||
{
|
||||
let child_view_key = view_key
|
||||
.ckd_pub(
|
||||
keychain.secp(),
|
||||
&mut hasher,
|
||||
ChildNumber::from_normal_idx(11),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(child_view_key.depth, 1);
|
||||
|
||||
let amount = rng.gen();
|
||||
let id = ExtKeychain::derive_key_id(
|
||||
3,
|
||||
10,
|
||||
rng.gen::<u16>() as u32,
|
||||
rng.gen::<u16>() as u32,
|
||||
0,
|
||||
);
|
||||
let switch = SwitchCommitmentType::None;
|
||||
let commit = keychain.commit(amount, &id, &switch).unwrap();
|
||||
|
||||
// Generate proof with ProofBuilder..
|
||||
let proof = create(
|
||||
&keychain,
|
||||
&builder,
|
||||
amount,
|
||||
&id,
|
||||
&switch,
|
||||
commit.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
// ..and rewind with child ViewKey
|
||||
let rewind = rewind(
|
||||
keychain.secp(),
|
||||
&child_view_key,
|
||||
commit.clone(),
|
||||
None,
|
||||
proof,
|
||||
);
|
||||
|
||||
assert!(rewind.is_ok());
|
||||
let rewind = rewind.unwrap();
|
||||
assert!(rewind.is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,25 +19,33 @@ use crate::core::transaction::kernel_sig_msg;
|
|||
use crate::core::{KernelFeatures, Output, OutputFeatures, TxKernel};
|
||||
use crate::keychain::{Identifier, Keychain};
|
||||
use crate::libtx::error::Error;
|
||||
use crate::libtx::{aggsig, proof};
|
||||
use crate::libtx::{
|
||||
aggsig,
|
||||
proof::{self, ProofBuild},
|
||||
};
|
||||
use crate::util::{secp, static_secp_instance};
|
||||
use grin_keychain::SwitchCommitmentType;
|
||||
|
||||
/// output a reward output
|
||||
pub fn output<K>(
|
||||
pub fn output<K, B>(
|
||||
keychain: &K,
|
||||
builder: &B,
|
||||
key_id: &Identifier,
|
||||
fees: u64,
|
||||
test_mode: bool,
|
||||
) -> Result<(Output, TxKernel), Error>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
let value = reward(fees);
|
||||
let commit = keychain.commit(value, key_id)?;
|
||||
// TODO: proper support for different switch commitment schemes
|
||||
let switch = &SwitchCommitmentType::Regular;
|
||||
let commit = keychain.commit(value, key_id, switch)?;
|
||||
|
||||
trace!("Block reward - Pedersen Commit is: {:?}", commit,);
|
||||
|
||||
let rproof = proof::create(keychain, value, key_id, commit, None)?;
|
||||
let rproof = proof::create(keychain, builder, value, key_id, switch, commit, None)?;
|
||||
|
||||
let output = Output {
|
||||
features: OutputFeatures::Coinbase,
|
||||
|
|
|
@ -25,6 +25,7 @@ use crate::core::core::{
|
|||
Block, BlockHeader, CompactBlock, HeaderVersion, KernelFeatures, OutputFeatures,
|
||||
};
|
||||
use crate::core::libtx::build::{self, input, output, with_fee};
|
||||
use crate::core::libtx::ProofBuilder;
|
||||
use crate::core::{global, ser};
|
||||
use crate::keychain::{BlindingFactor, ExtKeychain, Keychain};
|
||||
use crate::util::secp;
|
||||
|
@ -45,6 +46,7 @@ fn verifier_cache() -> Arc<RwLock<dyn VerifierCache>> {
|
|||
#[allow(dead_code)]
|
||||
fn too_large_block() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let max_out = global::max_block_weight() / BLOCK_OUTPUT_WEIGHT;
|
||||
|
||||
let mut pks = vec![];
|
||||
|
@ -59,12 +61,12 @@ fn too_large_block() {
|
|||
|
||||
let now = Instant::now();
|
||||
parts.append(&mut vec![input(500000, pks.pop().unwrap()), with_fee(2)]);
|
||||
let tx = build::transaction(parts, &keychain).unwrap();
|
||||
let tx = build::transaction(parts, &keychain, &builder).unwrap();
|
||||
println!("Build tx: {}", now.elapsed().as_secs());
|
||||
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![&tx], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![&tx], &keychain, &builder, &prev, &key_id);
|
||||
assert!(b
|
||||
.validate(&BlindingFactor::zero(), verifier_cache())
|
||||
.is_err());
|
||||
|
@ -86,6 +88,7 @@ fn very_empty_block() {
|
|||
// builds a block with a tx spending another and check that cut_through occurred
|
||||
fn block_with_cut_through() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
|
@ -94,17 +97,19 @@ fn block_with_cut_through() {
|
|||
let mut btx2 = build::transaction(
|
||||
vec![input(7, key_id1), output(5, key_id2.clone()), with_fee(2)],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// spending tx2 - reuse key_id2
|
||||
|
||||
let mut btx3 = txspend1i1o(5, &keychain, key_id2.clone(), key_id3);
|
||||
let mut btx3 = txspend1i1o(5, &keychain, &builder, key_id2.clone(), key_id3);
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(
|
||||
vec![&mut btx1, &mut btx2, &mut btx3],
|
||||
&keychain,
|
||||
&builder,
|
||||
&prev,
|
||||
&key_id,
|
||||
);
|
||||
|
@ -120,9 +125,10 @@ fn block_with_cut_through() {
|
|||
#[test]
|
||||
fn empty_block_with_coinbase_is_valid() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
|
||||
|
||||
assert_eq!(b.inputs().len(), 0);
|
||||
assert_eq!(b.outputs().len(), 1);
|
||||
|
@ -157,9 +163,10 @@ fn empty_block_with_coinbase_is_valid() {
|
|||
// additionally verifying the merkle_inputs_outputs also fails
|
||||
fn remove_coinbase_output_flag() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let mut b = new_block(vec![], &keychain, &prev, &key_id);
|
||||
let mut b = new_block(vec![], &keychain, &builder, &prev, &key_id);
|
||||
|
||||
assert!(b.outputs()[0].is_coinbase());
|
||||
b.outputs_mut()[0].features = OutputFeatures::Plain;
|
||||
|
@ -179,9 +186,10 @@ fn remove_coinbase_output_flag() {
|
|||
// invalidates the block and specifically it causes verify_coinbase to fail
|
||||
fn remove_coinbase_kernel_flag() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let mut b = new_block(vec![], &keychain, &prev, &key_id);
|
||||
let mut b = new_block(vec![], &keychain, &builder, &prev, &key_id);
|
||||
|
||||
assert!(b.kernels()[0].is_coinbase());
|
||||
b.kernels_mut()[0].features = KernelFeatures::Plain;
|
||||
|
@ -220,9 +228,10 @@ fn serialize_deserialize_header_version() {
|
|||
#[test]
|
||||
fn serialize_deserialize_block_header() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
|
||||
let header1 = b.header;
|
||||
|
||||
let mut vec = Vec::new();
|
||||
|
@ -237,9 +246,10 @@ fn serialize_deserialize_block_header() {
|
|||
fn serialize_deserialize_block() {
|
||||
let tx1 = tx1i2o();
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
|
||||
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &b).expect("serialization failed");
|
||||
|
@ -255,9 +265,10 @@ fn serialize_deserialize_block() {
|
|||
#[test]
|
||||
fn empty_block_serialized_size() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &b).expect("serialization failed");
|
||||
let target_len = 1_265;
|
||||
|
@ -267,10 +278,11 @@ fn empty_block_serialized_size() {
|
|||
#[test]
|
||||
fn block_single_tx_serialized_size() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let tx1 = tx1i2o();
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &b).expect("serialization failed");
|
||||
let target_len = 2_847;
|
||||
|
@ -280,9 +292,10 @@ fn block_single_tx_serialized_size() {
|
|||
#[test]
|
||||
fn empty_compact_block_serialized_size() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
|
||||
let cb: CompactBlock = b.into();
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &cb).expect("serialization failed");
|
||||
|
@ -293,10 +306,11 @@ fn empty_compact_block_serialized_size() {
|
|||
#[test]
|
||||
fn compact_block_single_tx_serialized_size() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let tx1 = tx1i2o();
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
|
||||
let cb: CompactBlock = b.into();
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &cb).expect("serialization failed");
|
||||
|
@ -307,6 +321,7 @@ fn compact_block_single_tx_serialized_size() {
|
|||
#[test]
|
||||
fn block_10_tx_serialized_size() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
global::set_mining_mode(global::ChainTypes::Mainnet);
|
||||
|
||||
let mut txs = vec![];
|
||||
|
@ -316,7 +331,7 @@ fn block_10_tx_serialized_size() {
|
|||
}
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(txs.iter().collect(), &keychain, &prev, &key_id);
|
||||
let b = new_block(txs.iter().collect(), &keychain, &builder, &prev, &key_id);
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &b).expect("serialization failed");
|
||||
let target_len = 17_085;
|
||||
|
@ -326,6 +341,7 @@ fn block_10_tx_serialized_size() {
|
|||
#[test]
|
||||
fn compact_block_10_tx_serialized_size() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
|
||||
let mut txs = vec![];
|
||||
for _ in 0..10 {
|
||||
|
@ -334,7 +350,7 @@ fn compact_block_10_tx_serialized_size() {
|
|||
}
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(txs.iter().collect(), &keychain, &prev, &key_id);
|
||||
let b = new_block(txs.iter().collect(), &keychain, &builder, &prev, &key_id);
|
||||
let cb: CompactBlock = b.into();
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &cb).expect("serialization failed");
|
||||
|
@ -345,10 +361,11 @@ fn compact_block_10_tx_serialized_size() {
|
|||
#[test]
|
||||
fn compact_block_hash_with_nonce() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let tx = tx1i2o();
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![&tx], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![&tx], &keychain, &builder, &prev, &key_id);
|
||||
let cb1: CompactBlock = b.clone().into();
|
||||
let cb2: CompactBlock = b.clone().into();
|
||||
|
||||
|
@ -375,10 +392,11 @@ fn compact_block_hash_with_nonce() {
|
|||
#[test]
|
||||
fn convert_block_to_compact_block() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let tx1 = tx1i2o();
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
|
||||
let cb: CompactBlock = b.clone().into();
|
||||
|
||||
assert_eq!(cb.out_full().len(), 1);
|
||||
|
@ -398,9 +416,10 @@ fn convert_block_to_compact_block() {
|
|||
#[test]
|
||||
fn hydrate_empty_compact_block() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
|
||||
let cb: CompactBlock = b.clone().into();
|
||||
let hb = Block::hydrate_from(cb, vec![]).unwrap();
|
||||
assert_eq!(hb.header, b.header);
|
||||
|
@ -411,10 +430,11 @@ fn hydrate_empty_compact_block() {
|
|||
#[test]
|
||||
fn serialize_deserialize_compact_block() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let tx1 = tx1i2o();
|
||||
let prev = BlockHeader::default();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
|
||||
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
|
||||
|
||||
let mut cb1: CompactBlock = b.into();
|
||||
|
||||
|
@ -437,6 +457,7 @@ fn serialize_deserialize_compact_block() {
|
|||
#[test]
|
||||
fn same_amount_outputs_copy_range_proof() {
|
||||
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
|
@ -449,6 +470,7 @@ fn same_amount_outputs_copy_range_proof() {
|
|||
with_fee(1),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -468,6 +490,7 @@ fn same_amount_outputs_copy_range_proof() {
|
|||
kernels.clone(),
|
||||
)],
|
||||
&keychain,
|
||||
&builder,
|
||||
&prev,
|
||||
&key_id,
|
||||
);
|
||||
|
@ -484,6 +507,7 @@ fn same_amount_outputs_copy_range_proof() {
|
|||
#[test]
|
||||
fn wrong_amount_range_proof() {
|
||||
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
|
@ -496,6 +520,7 @@ fn wrong_amount_range_proof() {
|
|||
with_fee(1),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
let tx2 = build::transaction(
|
||||
|
@ -506,6 +531,7 @@ fn wrong_amount_range_proof() {
|
|||
with_fee(1),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -525,6 +551,7 @@ fn wrong_amount_range_proof() {
|
|||
kernels.clone(),
|
||||
)],
|
||||
&keychain,
|
||||
&builder,
|
||||
&prev,
|
||||
&key_id,
|
||||
);
|
||||
|
|
|
@ -21,6 +21,7 @@ use grin_core::core::{
|
|||
};
|
||||
use grin_core::libtx::{
|
||||
build::{self, input, output, with_fee},
|
||||
proof::{ProofBuild, ProofBuilder},
|
||||
reward,
|
||||
};
|
||||
use grin_core::pow::Difficulty;
|
||||
|
@ -29,6 +30,7 @@ use grin_keychain as keychain;
|
|||
// utility producing a transaction with 2 inputs and a single outputs
|
||||
pub fn tx2i1o() -> Transaction {
|
||||
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
|
@ -41,6 +43,7 @@ pub fn tx2i1o() -> Transaction {
|
|||
with_fee(2),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
@ -48,12 +51,14 @@ pub fn tx2i1o() -> Transaction {
|
|||
// utility producing a transaction with a single input and output
|
||||
pub fn tx1i1o() -> Transaction {
|
||||
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
|
||||
build::transaction(
|
||||
vec![input(5, key_id1), output(3, key_id2), with_fee(2)],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
@ -63,6 +68,7 @@ pub fn tx1i1o() -> Transaction {
|
|||
// Note: this tx has an "offset" kernel
|
||||
pub fn tx1i2o() -> Transaction {
|
||||
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
|
@ -75,23 +81,26 @@ pub fn tx1i2o() -> Transaction {
|
|||
with_fee(2),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
// utility to create a block without worrying about the key or previous
|
||||
// header
|
||||
pub fn new_block<K>(
|
||||
pub fn new_block<K, B>(
|
||||
txs: Vec<&Transaction>,
|
||||
keychain: &K,
|
||||
builder: &B,
|
||||
previous_header: &BlockHeader,
|
||||
key_id: &Identifier,
|
||||
) -> Block
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
let fees = txs.iter().map(|tx| tx.fee()).sum();
|
||||
let reward_output = reward::output(keychain, &key_id, fees, false).unwrap();
|
||||
let reward_output = reward::output(keychain, builder, &key_id, fees, false).unwrap();
|
||||
Block::new(
|
||||
&previous_header,
|
||||
txs.into_iter().cloned().collect(),
|
||||
|
@ -103,13 +112,21 @@ where
|
|||
|
||||
// utility producing a transaction that spends an output with the provided
|
||||
// value and blinding key
|
||||
pub fn txspend1i1o<K>(v: u64, keychain: &K, key_id1: Identifier, key_id2: Identifier) -> Transaction
|
||||
pub fn txspend1i1o<K, B>(
|
||||
v: u64,
|
||||
keychain: &K,
|
||||
builder: &B,
|
||||
key_id1: Identifier,
|
||||
key_id2: Identifier,
|
||||
) -> Transaction
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
build::transaction(
|
||||
vec![input(v, key_id1), output(3, key_id2), with_fee(2)],
|
||||
keychain,
|
||||
builder,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ use self::core::core::{aggregate, deaggregate, KernelFeatures, Output, Transacti
|
|||
use self::core::libtx::build::{
|
||||
self, initial_tx, input, output, with_excess, with_fee, with_lock_height,
|
||||
};
|
||||
use self::core::libtx::ProofBuilder;
|
||||
use self::core::ser;
|
||||
use self::keychain::{BlindingFactor, ExtKeychain, Keychain};
|
||||
use self::util::static_secp_instance;
|
||||
|
@ -75,18 +76,15 @@ fn tx_double_ser_deser() {
|
|||
#[test]
|
||||
#[should_panic(expected = "Keychain Error")]
|
||||
fn test_zero_commit_fails() {
|
||||
let mut keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
keychain.set_use_switch_commits(false);
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
|
||||
// blinding should fail as signing with a zero r*G shouldn't work
|
||||
build::transaction(
|
||||
vec![
|
||||
input(10, key_id1.clone()),
|
||||
output(9, key_id1.clone()),
|
||||
with_fee(1),
|
||||
],
|
||||
vec![input(10, key_id1.clone()), output(10, key_id1.clone())],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -98,6 +96,7 @@ fn verifier_cache() -> Arc<RwLock<dyn VerifierCache>> {
|
|||
#[test]
|
||||
fn build_tx_kernel() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
|
@ -111,6 +110,7 @@ fn build_tx_kernel() {
|
|||
with_fee(2),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -350,6 +350,7 @@ fn basic_transaction_deaggregation() {
|
|||
#[test]
|
||||
fn hash_output() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
|
@ -362,6 +363,7 @@ fn hash_output() {
|
|||
with_fee(1),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
let h = tx.outputs()[0].hash();
|
||||
|
@ -407,6 +409,7 @@ fn tx_hash_diff() {
|
|||
#[test]
|
||||
fn tx_build_exchange() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
|
@ -419,9 +422,12 @@ fn tx_build_exchange() {
|
|||
|
||||
// Alice builds her transaction, with change, which also produces the sum
|
||||
// of blinding factors before they're obscured.
|
||||
let (tx, sum) =
|
||||
build::partial_transaction(vec![in1, in2, output(1, key_id3), with_fee(2)], &keychain)
|
||||
.unwrap();
|
||||
let (tx, sum) = build::partial_transaction(
|
||||
vec![in1, in2, output(1, key_id3), with_fee(2)],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(tx, sum)
|
||||
};
|
||||
|
@ -436,6 +442,7 @@ fn tx_build_exchange() {
|
|||
output(4, key_id4),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -447,11 +454,12 @@ fn tx_build_exchange() {
|
|||
#[test]
|
||||
fn reward_empty_block() {
|
||||
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
|
||||
let previous_header = BlockHeader::default();
|
||||
|
||||
let b = new_block(vec![], &keychain, &previous_header, &key_id);
|
||||
let b = new_block(vec![], &keychain, &builder, &previous_header, &key_id);
|
||||
|
||||
b.cut_through()
|
||||
.unwrap()
|
||||
|
@ -462,6 +470,7 @@ fn reward_empty_block() {
|
|||
#[test]
|
||||
fn reward_with_tx_block() {
|
||||
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
|
||||
let vc = verifier_cache();
|
||||
|
@ -471,7 +480,13 @@ fn reward_with_tx_block() {
|
|||
|
||||
let previous_header = BlockHeader::default();
|
||||
|
||||
let block = new_block(vec![&mut tx1], &keychain, &previous_header, &key_id);
|
||||
let block = new_block(
|
||||
vec![&mut tx1],
|
||||
&keychain,
|
||||
&builder,
|
||||
&previous_header,
|
||||
&key_id,
|
||||
);
|
||||
block
|
||||
.cut_through()
|
||||
.unwrap()
|
||||
|
@ -482,6 +497,7 @@ fn reward_with_tx_block() {
|
|||
#[test]
|
||||
fn simple_block() {
|
||||
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
|
||||
let vc = verifier_cache();
|
||||
|
@ -493,6 +509,7 @@ fn simple_block() {
|
|||
let b = new_block(
|
||||
vec![&mut tx1, &mut tx2],
|
||||
&keychain,
|
||||
&builder,
|
||||
&previous_header,
|
||||
&key_id,
|
||||
);
|
||||
|
@ -503,7 +520,7 @@ fn simple_block() {
|
|||
#[test]
|
||||
fn test_block_with_timelocked_tx() {
|
||||
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
||||
|
||||
let builder = ProofBuilder::new(&keychain);
|
||||
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
|
@ -520,12 +537,19 @@ fn test_block_with_timelocked_tx() {
|
|||
with_lock_height(1),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let previous_header = BlockHeader::default();
|
||||
|
||||
let b = new_block(vec![&tx1], &keychain, &previous_header, &key_id3.clone());
|
||||
let b = new_block(
|
||||
vec![&tx1],
|
||||
&keychain,
|
||||
&builder,
|
||||
&previous_header,
|
||||
&key_id3.clone(),
|
||||
);
|
||||
b.validate(&BlindingFactor::zero(), vc.clone()).unwrap();
|
||||
|
||||
// now try adding a timelocked tx where lock height is greater than current
|
||||
|
@ -538,11 +562,18 @@ fn test_block_with_timelocked_tx() {
|
|||
with_lock_height(2),
|
||||
],
|
||||
&keychain,
|
||||
&builder,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let previous_header = BlockHeader::default();
|
||||
let b = new_block(vec![&tx1], &keychain, &previous_header, &key_id3.clone());
|
||||
let b = new_block(
|
||||
vec![&tx1],
|
||||
&keychain,
|
||||
&builder,
|
||||
&previous_header,
|
||||
&key_id3.clone(),
|
||||
);
|
||||
|
||||
match b.validate(&BlindingFactor::zero(), vc.clone()) {
|
||||
Err(KernelLockHeight(height)) => {
|
||||
|
|
|
@ -27,8 +27,10 @@ use grin_keychain as keychain;
|
|||
fn test_output_ser_deser() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let commit = keychain.commit(5, &key_id).unwrap();
|
||||
let proof = proof::create(&keychain, 5, &key_id, commit, None).unwrap();
|
||||
let switch = &keychain::SwitchCommitmentType::Regular;
|
||||
let commit = keychain.commit(5, &key_id, switch).unwrap();
|
||||
let builder = proof::ProofBuilder::new(&keychain);
|
||||
let proof = proof::create(&keychain, &builder, 5, &key_id, switch, commit, None).unwrap();
|
||||
|
||||
let out = Output {
|
||||
features: OutputFeatures::Plain,
|
||||
|
|
|
@ -17,7 +17,7 @@ pub mod common;
|
|||
use self::core::core::verifier_cache::{LruVerifierCache, VerifierCache};
|
||||
use self::core::core::{Output, OutputFeatures};
|
||||
use self::core::libtx::proof;
|
||||
use self::keychain::{ExtKeychain, Keychain};
|
||||
use self::keychain::{ExtKeychain, Keychain, SwitchCommitmentType};
|
||||
use self::util::RwLock;
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
|
@ -34,8 +34,10 @@ fn test_verifier_cache_rangeproofs() {
|
|||
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let commit = keychain.commit(5, &key_id).unwrap();
|
||||
let proof = proof::create(&keychain, 5, &key_id, commit, None).unwrap();
|
||||
let switch = &SwitchCommitmentType::Regular;
|
||||
let commit = keychain.commit(5, &key_id, switch).unwrap();
|
||||
let builder = proof::ProofBuilder::new(&keychain);
|
||||
let proof = proof::create(&keychain, &builder, 5, &key_id, switch, commit, None).unwrap();
|
||||
|
||||
let out = Output {
|
||||
features: OutputFeatures::Plain,
|
||||
|
|
|
@ -149,7 +149,7 @@ impl BIP32Hasher for BIP32GrinHasher {
|
|||
}
|
||||
|
||||
/// Extended private key
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct ExtendedPrivKey {
|
||||
/// The network this key is to be used on
|
||||
pub network: [u8; 4],
|
||||
|
@ -399,7 +399,7 @@ impl ExtendedPrivKey {
|
|||
where
|
||||
H: BIP32Hasher,
|
||||
{
|
||||
let mut sk: ExtendedPrivKey = *self;
|
||||
let mut sk: ExtendedPrivKey = self.clone();
|
||||
for cnum in cnums {
|
||||
sk = sk.ckd_priv(secp, hasher, *cnum)?;
|
||||
}
|
||||
|
|
|
@ -17,22 +17,33 @@
|
|||
use rand::distributions::Alphanumeric;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::blake2;
|
||||
use crate::blake2::blake2b::blake2b;
|
||||
|
||||
use crate::extkey_bip32::{BIP32GrinHasher, ExtendedPrivKey};
|
||||
use crate::types::{BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain};
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::extkey_bip32::{BIP32GrinHasher, ExtendedPrivKey, ExtendedPubKey};
|
||||
use crate::types::{
|
||||
BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain, SwitchCommitmentType,
|
||||
};
|
||||
use crate::util::secp::key::{PublicKey, SecretKey};
|
||||
use crate::util::secp::pedersen::Commitment;
|
||||
use crate::util::secp::{self, Message, Secp256k1, Signature};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExtKeychain {
|
||||
secp: Secp256k1,
|
||||
master: ExtendedPrivKey,
|
||||
use_switch_commits: bool,
|
||||
pub master: ExtendedPrivKey,
|
||||
hasher: BIP32GrinHasher,
|
||||
}
|
||||
|
||||
impl ExtKeychain {
|
||||
pub fn pub_root_key(&mut self) -> ExtendedPubKey {
|
||||
ExtendedPubKey::from_private(&self.secp, &self.master, &mut self.hasher)
|
||||
}
|
||||
|
||||
pub fn hasher(&self) -> BIP32GrinHasher {
|
||||
self.hasher.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Keychain for ExtKeychain {
|
||||
fn from_seed(seed: &[u8], is_floo: bool) -> Result<ExtKeychain, Error> {
|
||||
let mut h = BIP32GrinHasher::new(is_floo);
|
||||
|
@ -41,7 +52,6 @@ impl Keychain for ExtKeychain {
|
|||
let keychain = ExtKeychain {
|
||||
secp: secp,
|
||||
master: master,
|
||||
use_switch_commits: true,
|
||||
hasher: h,
|
||||
};
|
||||
Ok(keychain)
|
||||
|
@ -54,7 +64,6 @@ impl Keychain for ExtKeychain {
|
|||
let keychain = ExtKeychain {
|
||||
secp: secp,
|
||||
master: master,
|
||||
use_switch_commits: true,
|
||||
hasher: h,
|
||||
};
|
||||
Ok(keychain)
|
||||
|
@ -63,7 +72,7 @@ impl Keychain for ExtKeychain {
|
|||
/// For testing - probably not a good idea to use outside of tests.
|
||||
fn from_random_seed(is_floo: bool) -> Result<ExtKeychain, Error> {
|
||||
let seed: String = thread_rng().sample_iter(&Alphanumeric).take(16).collect();
|
||||
let seed = blake2::blake2b::blake2b(32, &[], seed.as_bytes());
|
||||
let seed = blake2b(32, &[], seed.as_bytes());
|
||||
ExtKeychain::from_seed(seed.as_bytes(), is_floo)
|
||||
}
|
||||
|
||||
|
@ -75,22 +84,39 @@ impl Keychain for ExtKeychain {
|
|||
ExtKeychainPath::new(depth, d1, d2, d3, d4).to_identifier()
|
||||
}
|
||||
|
||||
fn derive_key(&self, amount: u64, id: &Identifier) -> Result<SecretKey, Error> {
|
||||
fn public_root_key(&self) -> PublicKey {
|
||||
let mut hasher = self.hasher.clone();
|
||||
ExtendedPubKey::from_private(&self.secp, &self.master, &mut hasher).public_key
|
||||
}
|
||||
|
||||
fn derive_key(
|
||||
&self,
|
||||
amount: u64,
|
||||
id: &Identifier,
|
||||
switch: &SwitchCommitmentType,
|
||||
) -> Result<SecretKey, Error> {
|
||||
let mut h = self.hasher.clone();
|
||||
let p = id.to_path();
|
||||
let mut ext_key = self.master;
|
||||
let mut ext_key = self.master.clone();
|
||||
for i in 0..p.depth {
|
||||
ext_key = ext_key.ckd_priv(&self.secp, &mut h, p.path[i as usize])?;
|
||||
}
|
||||
|
||||
match self.use_switch_commits {
|
||||
true => Ok(self.secp.blind_switch(amount, ext_key.secret_key)?),
|
||||
false => Ok(ext_key.secret_key),
|
||||
match *switch {
|
||||
SwitchCommitmentType::Regular => {
|
||||
Ok(self.secp.blind_switch(amount, ext_key.secret_key)?)
|
||||
}
|
||||
SwitchCommitmentType::None => Ok(ext_key.secret_key),
|
||||
}
|
||||
}
|
||||
|
||||
fn commit(&self, amount: u64, id: &Identifier) -> Result<Commitment, Error> {
|
||||
let key = self.derive_key(amount, id)?;
|
||||
fn commit(
|
||||
&self,
|
||||
amount: u64,
|
||||
id: &Identifier,
|
||||
switch: &SwitchCommitmentType,
|
||||
) -> Result<Commitment, Error> {
|
||||
let key = self.derive_key(amount, id, switch)?;
|
||||
let commit = self.secp.commit(amount, key)?;
|
||||
Ok(commit)
|
||||
}
|
||||
|
@ -100,7 +126,11 @@ impl Keychain for ExtKeychain {
|
|||
.positive_key_ids
|
||||
.iter()
|
||||
.filter_map(|k| {
|
||||
let res = self.derive_key(k.value, &Identifier::from_path(&k.ext_keychain_path));
|
||||
let res = self.derive_key(
|
||||
k.value,
|
||||
&Identifier::from_path(&k.ext_keychain_path),
|
||||
&k.switch,
|
||||
);
|
||||
if let Ok(s) = res {
|
||||
Some(s)
|
||||
} else {
|
||||
|
@ -113,7 +143,11 @@ impl Keychain for ExtKeychain {
|
|||
.negative_key_ids
|
||||
.iter()
|
||||
.filter_map(|k| {
|
||||
let res = self.derive_key(k.value, &Identifier::from_path(&k.ext_keychain_path));
|
||||
let res = self.derive_key(
|
||||
k.value,
|
||||
&Identifier::from_path(&k.ext_keychain_path),
|
||||
&k.switch,
|
||||
);
|
||||
if let Ok(s) = res {
|
||||
Some(s)
|
||||
} else {
|
||||
|
@ -122,37 +156,32 @@ impl Keychain for ExtKeychain {
|
|||
})
|
||||
.collect();
|
||||
|
||||
pos_keys.extend(
|
||||
&blind_sum
|
||||
.positive_blinding_factors
|
||||
.iter()
|
||||
.filter_map(|b| b.secret_key(&self.secp).ok())
|
||||
.collect::<Vec<SecretKey>>(),
|
||||
);
|
||||
let keys = blind_sum
|
||||
.positive_blinding_factors
|
||||
.iter()
|
||||
.filter_map(|b| b.secret_key(&self.secp).ok().clone())
|
||||
.collect::<Vec<SecretKey>>();
|
||||
pos_keys.extend(keys);
|
||||
|
||||
neg_keys.extend(
|
||||
&blind_sum
|
||||
.negative_blinding_factors
|
||||
.iter()
|
||||
.filter_map(|b| b.secret_key(&self.secp).ok())
|
||||
.collect::<Vec<SecretKey>>(),
|
||||
);
|
||||
let keys = blind_sum
|
||||
.negative_blinding_factors
|
||||
.iter()
|
||||
.filter_map(|b| b.secret_key(&self.secp).ok().clone())
|
||||
.collect::<Vec<SecretKey>>();
|
||||
neg_keys.extend(keys);
|
||||
|
||||
let sum = self.secp.blind_sum(pos_keys, neg_keys)?;
|
||||
Ok(BlindingFactor::from_secret_key(sum))
|
||||
}
|
||||
|
||||
fn create_nonce(&self, commit: &Commitment) -> Result<SecretKey, Error> {
|
||||
// hash(commit|wallet root secret key (m)) as nonce
|
||||
let root_key = self.derive_key(0, &Self::root_key_id())?;
|
||||
let res = blake2::blake2b::blake2b(32, &commit.0, &root_key.0[..]);
|
||||
let res = res.as_bytes();
|
||||
SecretKey::from_slice(&self.secp, &res)
|
||||
.map_err(|e| Error::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()))
|
||||
}
|
||||
|
||||
fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result<Signature, Error> {
|
||||
let skey = self.derive_key(amount, id)?;
|
||||
fn sign(
|
||||
&self,
|
||||
msg: &Message,
|
||||
amount: u64,
|
||||
id: &Identifier,
|
||||
switch: &SwitchCommitmentType,
|
||||
) -> Result<Signature, Error> {
|
||||
let skey = self.derive_key(amount, id, switch)?;
|
||||
let sig = self.secp.sign(msg, &skey)?;
|
||||
Ok(sig)
|
||||
}
|
||||
|
@ -167,10 +196,6 @@ impl Keychain for ExtKeychain {
|
|||
Ok(sig)
|
||||
}
|
||||
|
||||
fn set_use_switch_commits(&mut self, value: bool) {
|
||||
self.use_switch_commits = value;
|
||||
}
|
||||
|
||||
fn secp(&self) -> &Secp256k1 {
|
||||
&self.secp
|
||||
}
|
||||
|
@ -182,11 +207,13 @@ mod test {
|
|||
use crate::types::{BlindSum, BlindingFactor, ExtKeychainPath, Keychain};
|
||||
use crate::util::secp;
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::SwitchCommitmentType;
|
||||
|
||||
#[test]
|
||||
fn test_key_derivation() {
|
||||
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let secp = keychain.secp();
|
||||
let switch = &SwitchCommitmentType::None;
|
||||
|
||||
let path = ExtKeychainPath::new(1, 1, 0, 0, 0);
|
||||
let key_id = path.to_identifier();
|
||||
|
@ -196,10 +223,10 @@ mod test {
|
|||
|
||||
// now create a zero commitment using the key on the keychain associated with
|
||||
// the key_id
|
||||
let commit = keychain.commit(0, &key_id).unwrap();
|
||||
let commit = keychain.commit(0, &key_id, switch).unwrap();
|
||||
|
||||
// now check we can use our key to verify a signature from this zero commitment
|
||||
let sig = keychain.sign(&msg, 0, &key_id).unwrap();
|
||||
let sig = keychain.sign(&msg, 0, &key_id, switch).unwrap();
|
||||
secp.verify_from_commit(&msg, &sig, &commit).unwrap();
|
||||
}
|
||||
|
||||
|
@ -235,9 +262,9 @@ mod test {
|
|||
|
||||
// create commitments for secret keys 1, 2 and 3
|
||||
// all committing to the value 0 (which is what we do for tx_kernels)
|
||||
let commit_1 = keychain.secp.commit(0, skey1).unwrap();
|
||||
let commit_2 = keychain.secp.commit(0, skey2).unwrap();
|
||||
let commit_3 = keychain.secp.commit(0, skey3).unwrap();
|
||||
let commit_1 = keychain.secp.commit(0, skey1.clone()).unwrap();
|
||||
let commit_2 = keychain.secp.commit(0, skey2.clone()).unwrap();
|
||||
let commit_3 = keychain.secp.commit(0, skey3.clone()).unwrap();
|
||||
|
||||
// now sum commitments for keys 1 and 2
|
||||
let sum = keychain
|
||||
|
|
|
@ -25,14 +25,19 @@ extern crate serde_derive;
|
|||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
extern crate sha2;
|
||||
|
||||
mod base58;
|
||||
pub mod extkey_bip32;
|
||||
pub mod mnemonic;
|
||||
mod types;
|
||||
pub mod view_key;
|
||||
|
||||
pub mod keychain;
|
||||
pub use crate::extkey_bip32::ChildNumber;
|
||||
pub use crate::keychain::ExtKeychain;
|
||||
pub use crate::types::{
|
||||
BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain, IDENTIFIER_SIZE,
|
||||
BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain, SwitchCommitmentType,
|
||||
IDENTIFIER_SIZE,
|
||||
};
|
||||
pub use crate::view_key::ViewKey;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
use rand::thread_rng;
|
||||
use std::cmp::min;
|
||||
use std::convert::TryFrom;
|
||||
use std::io::Cursor;
|
||||
use std::ops::Add;
|
||||
/// Keychain trait and its main supporting types. The Identifier is a
|
||||
|
@ -129,9 +130,12 @@ impl Identifier {
|
|||
}
|
||||
|
||||
pub fn to_value_path(&self, value: u64) -> ValueExtKeychainPath {
|
||||
// TODO: proper support for different switch commitment schemes
|
||||
// For now it is assumed all outputs are using the regular switch commitment scheme
|
||||
ValueExtKeychainPath {
|
||||
value,
|
||||
ext_keychain_path: self.to_path(),
|
||||
switch: SwitchCommitmentType::Regular,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,7 +322,7 @@ impl BlindingFactor {
|
|||
|
||||
// use blind_sum to subtract skey_1 from our key (to give k = k1 + k2)
|
||||
let skey = self.secret_key(secp)?;
|
||||
let skey_2 = secp.blind_sum(vec![skey], vec![skey_1])?;
|
||||
let skey_2 = secp.blind_sum(vec![skey], vec![skey_1.clone()])?;
|
||||
|
||||
let blind_1 = BlindingFactor::from_secret_key(skey_1);
|
||||
let blind_2 = BlindingFactor::from_secret_key(skey_2);
|
||||
|
@ -443,11 +447,12 @@ impl ExtKeychainPath {
|
|||
}
|
||||
}
|
||||
|
||||
/// Wrapper for amount + path
|
||||
/// Wrapper for amount + switch + path
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize)]
|
||||
pub struct ValueExtKeychainPath {
|
||||
pub value: u64,
|
||||
pub ext_keychain_path: ExtKeychainPath,
|
||||
pub switch: SwitchCommitmentType,
|
||||
}
|
||||
|
||||
pub trait Keychain: Sync + Send + Clone {
|
||||
|
@ -467,16 +472,61 @@ pub trait Keychain: Sync + Send + Clone {
|
|||
/// Derives a key id from the depth of the keychain and the values at each
|
||||
/// depth level. See `KeychainPath` for more information.
|
||||
fn derive_key_id(depth: u8, d1: u32, d2: u32, d3: u32, d4: u32) -> Identifier;
|
||||
fn derive_key(&self, amount: u64, id: &Identifier) -> Result<SecretKey, Error>;
|
||||
fn commit(&self, amount: u64, id: &Identifier) -> Result<Commitment, Error>;
|
||||
|
||||
/// The public root key
|
||||
fn public_root_key(&self) -> PublicKey;
|
||||
|
||||
fn derive_key(
|
||||
&self,
|
||||
amount: u64,
|
||||
id: &Identifier,
|
||||
switch: &SwitchCommitmentType,
|
||||
) -> Result<SecretKey, Error>;
|
||||
fn commit(
|
||||
&self,
|
||||
amount: u64,
|
||||
id: &Identifier,
|
||||
switch: &SwitchCommitmentType,
|
||||
) -> Result<Commitment, Error>;
|
||||
fn blind_sum(&self, blind_sum: &BlindSum) -> Result<BlindingFactor, Error>;
|
||||
fn create_nonce(&self, commit: &Commitment) -> Result<SecretKey, Error>;
|
||||
fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result<Signature, Error>;
|
||||
fn sign(
|
||||
&self,
|
||||
msg: &Message,
|
||||
amount: u64,
|
||||
id: &Identifier,
|
||||
switch: &SwitchCommitmentType,
|
||||
) -> Result<Signature, Error>;
|
||||
fn sign_with_blinding(&self, _: &Message, _: &BlindingFactor) -> Result<Signature, Error>;
|
||||
fn set_use_switch_commits(&mut self, value: bool);
|
||||
fn secp(&self) -> &Secp256k1;
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum SwitchCommitmentType {
|
||||
None,
|
||||
Regular,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for SwitchCommitmentType {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(SwitchCommitmentType::None),
|
||||
1 => Ok(SwitchCommitmentType::Regular),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SwitchCommitmentType> for u8 {
|
||||
fn from(switch: &SwitchCommitmentType) -> Self {
|
||||
match *switch {
|
||||
SwitchCommitmentType::None => 0,
|
||||
SwitchCommitmentType::Regular => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rand::thread_rng;
|
||||
|
@ -519,7 +569,7 @@ mod test {
|
|||
fn split_blinding_factor() {
|
||||
let secp = Secp256k1::new();
|
||||
let skey_in = SecretKey::new(&secp, &mut thread_rng());
|
||||
let blind = BlindingFactor::from_secret_key(skey_in);
|
||||
let blind = BlindingFactor::from_secret_key(skey_in.clone());
|
||||
let split = blind.split(&secp).unwrap();
|
||||
|
||||
// split a key, sum the split keys and confirm the sum matches the original key
|
||||
|
|
195
keychain/src/view_key.rs
Normal file
195
keychain/src/view_key.rs
Normal file
|
@ -0,0 +1,195 @@
|
|||
use crate::blake2::blake2b::blake2b;
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
//use crate::sha2::{Digest, Sha256};
|
||||
use super::extkey_bip32::{
|
||||
BIP32Hasher, ChainCode, ChildNumber, Error as BIP32Error, ExtendedPrivKey, ExtendedPubKey,
|
||||
Fingerprint,
|
||||
};
|
||||
use super::types::{Error, Keychain};
|
||||
use crate::util::secp::constants::GENERATOR_PUB_J_RAW;
|
||||
use crate::util::secp::ffi;
|
||||
use crate::util::secp::key::{PublicKey, SecretKey};
|
||||
use crate::util::secp::Secp256k1;
|
||||
use crate::SwitchCommitmentType;
|
||||
|
||||
/*const VERSION_FLOO_NS: [u8;4] = [0x03, 0x27, 0x3E, 0x4B];
|
||||
const VERSION_FLOO: [u8;4] = [0x03, 0x27, 0x3E, 0x4B];
|
||||
const VERSION_MAIN_NS: [u8;4] = [0x03, 0x3C, 0x08, 0xDF];
|
||||
const VERSION_MAIN: [u8;4] = [0x03, 0x3C, 0x08, 0xDF];*/
|
||||
|
||||
/// Key that can be used to scan the chain for owned outputs
|
||||
/// This is a public key, meaning it cannot be used to spend those outputs
|
||||
/// At the moment only depth 0 keys can be used
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct ViewKey {
|
||||
/// Whether this view key is meant for floonet or not
|
||||
pub is_floo: bool,
|
||||
/// How many derivations this key is from the master (which is 0)
|
||||
pub depth: u8,
|
||||
/// Fingerprint of the parent key
|
||||
parent_fingerprint: Fingerprint,
|
||||
/// Child number of the key used to derive from parent (0 for master)
|
||||
pub child_number: ChildNumber,
|
||||
/// Public key
|
||||
public_key: PublicKey,
|
||||
/// Switch public key, required to view outputs that use switch commitment
|
||||
switch_public_key: Option<PublicKey>,
|
||||
/// Chain code
|
||||
chain_code: ChainCode,
|
||||
/// Hash used to generate rewind nonce
|
||||
pub rewind_hash: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ViewKey {
|
||||
pub fn create<K, H>(
|
||||
keychain: &K,
|
||||
ext_key: ExtendedPrivKey,
|
||||
hasher: &mut H,
|
||||
is_floo: bool,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
K: Keychain,
|
||||
H: BIP32Hasher,
|
||||
{
|
||||
let secp = keychain.secp();
|
||||
|
||||
let ExtendedPubKey {
|
||||
network: _,
|
||||
depth,
|
||||
parent_fingerprint,
|
||||
child_number,
|
||||
public_key,
|
||||
chain_code,
|
||||
} = ExtendedPubKey::from_private(secp, &ext_key, hasher);
|
||||
|
||||
let mut switch_public_key = PublicKey(ffi::PublicKey(GENERATOR_PUB_J_RAW));
|
||||
switch_public_key.mul_assign(secp, &ext_key.secret_key)?;
|
||||
let switch_public_key = Some(switch_public_key);
|
||||
|
||||
let rewind_hash = Self::rewind_hash(secp, keychain.public_root_key());
|
||||
|
||||
Ok(Self {
|
||||
is_floo,
|
||||
depth,
|
||||
parent_fingerprint,
|
||||
child_number,
|
||||
public_key,
|
||||
switch_public_key,
|
||||
chain_code,
|
||||
rewind_hash,
|
||||
})
|
||||
}
|
||||
|
||||
fn rewind_hash(secp: &Secp256k1, public_root_key: PublicKey) -> Vec<u8> {
|
||||
let ser = public_root_key.serialize_vec(secp, true);
|
||||
blake2b(32, &[], &ser[..]).as_bytes().to_vec()
|
||||
}
|
||||
|
||||
fn ckd_pub_tweak<H>(
|
||||
&self,
|
||||
secp: &Secp256k1,
|
||||
hasher: &mut H,
|
||||
i: ChildNumber,
|
||||
) -> Result<(SecretKey, ChainCode), Error>
|
||||
where
|
||||
H: BIP32Hasher,
|
||||
{
|
||||
match i {
|
||||
ChildNumber::Hardened { .. } => Err(BIP32Error::CannotDeriveFromHardenedKey.into()),
|
||||
ChildNumber::Normal { index: n } => {
|
||||
hasher.init_sha512(&self.chain_code[..]);
|
||||
hasher.append_sha512(&self.public_key.serialize_vec(secp, true)[..]);
|
||||
let mut be_n = [0; 4];
|
||||
BigEndian::write_u32(&mut be_n, n);
|
||||
hasher.append_sha512(&be_n);
|
||||
|
||||
let result = hasher.result_sha512();
|
||||
|
||||
let secret_key = SecretKey::from_slice(secp, &result[..32])?;
|
||||
let chain_code = ChainCode::from(&result[32..]);
|
||||
Ok((secret_key, chain_code))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ckd_pub<H>(
|
||||
&self,
|
||||
secp: &Secp256k1,
|
||||
hasher: &mut H,
|
||||
i: ChildNumber,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
H: BIP32Hasher,
|
||||
{
|
||||
let (secret_key, chain_code) = self.ckd_pub_tweak(secp, hasher, i)?;
|
||||
|
||||
let mut public_key = self.public_key.clone();
|
||||
public_key.add_exp_assign(secp, &secret_key)?;
|
||||
|
||||
let switch_public_key = match &self.switch_public_key {
|
||||
Some(p) => {
|
||||
let mut j = PublicKey(ffi::PublicKey(GENERATOR_PUB_J_RAW));
|
||||
j.mul_assign(secp, &secret_key)?;
|
||||
Some(PublicKey::from_combination(secp, vec![p, &j])?)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
is_floo: self.is_floo,
|
||||
depth: self.depth + 1,
|
||||
parent_fingerprint: self.fingerprint(secp, hasher),
|
||||
child_number: i,
|
||||
public_key,
|
||||
switch_public_key,
|
||||
chain_code,
|
||||
rewind_hash: self.rewind_hash.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn commit(
|
||||
&self,
|
||||
secp: &Secp256k1,
|
||||
amount: u64,
|
||||
switch: &SwitchCommitmentType,
|
||||
) -> Result<PublicKey, Error> {
|
||||
let value_key = secp.commit_value(amount)?.to_pubkey(secp)?;
|
||||
let pub_key = PublicKey::from_combination(secp, vec![&self.public_key, &value_key])?;
|
||||
match *switch {
|
||||
SwitchCommitmentType::None => Ok(pub_key),
|
||||
SwitchCommitmentType::Regular => {
|
||||
// TODO: replace this whole block by a libsecp function
|
||||
/*let switch_pub = self.switch_public_key.ok_or(Error::SwitchCommitment)?;
|
||||
let switch_ser: Vec<u8> = switch_pub.serialize_vec(secp, true)[..].to_vec();
|
||||
|
||||
let mut commit_ser: Vec<u8> = pub_key.serialize_vec(secp, true)[..].to_vec();
|
||||
commit_ser[0] += 6; // This only works sometimes
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.input(&commit_ser);
|
||||
hasher.input(&switch_ser);
|
||||
let blind = SecretKey::from_slice(secp, &hasher.result()[..])?;
|
||||
let mut pub_key = pub_key;
|
||||
pub_key.add_exp_assign(secp, &blind)?;
|
||||
|
||||
Ok(pub_key)*/
|
||||
Err(Error::SwitchCommitment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn identifier<H>(&self, secp: &Secp256k1, hasher: &mut H) -> [u8; 20]
|
||||
where
|
||||
H: BIP32Hasher,
|
||||
{
|
||||
let sha2_res = hasher.sha_256(&self.public_key.serialize_vec(secp, true)[..]);
|
||||
hasher.ripemd_160(&sha2_res)
|
||||
}
|
||||
|
||||
fn fingerprint<H>(&self, secp: &Secp256k1, hasher: &mut H) -> Fingerprint
|
||||
where
|
||||
H: BIP32Hasher,
|
||||
{
|
||||
Fingerprint::from(&self.identifier(secp, hasher)[0..4])
|
||||
}
|
||||
}
|
|
@ -47,7 +47,14 @@ fn test_transaction_pool_block_building() {
|
|||
let height = prev_header.height + 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let fee = txs.iter().map(|x| x.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fee, false).unwrap();
|
||||
let reward = libtx::reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&key_id,
|
||||
fee,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
|
|
|
@ -51,7 +51,14 @@ fn test_block_building_max_weight() {
|
|||
let height = prev_header.height + 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let fee = txs.iter().map(|x| x.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fee, false).unwrap();
|
||||
let reward = libtx::reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&key_id,
|
||||
fee,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
|
|
|
@ -45,7 +45,14 @@ fn test_transaction_pool_block_reconciliation() {
|
|||
let header = {
|
||||
let height = 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
|
||||
let reward = libtx::reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&key_id,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let genesis = BlockHeader::default();
|
||||
let mut block = Block::new(&genesis, vec![], Difficulty::min(), reward).unwrap();
|
||||
|
||||
|
@ -65,7 +72,14 @@ fn test_transaction_pool_block_reconciliation() {
|
|||
let block = {
|
||||
let key_id = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let fees = initial_tx.fee();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fees, false).unwrap();
|
||||
let reward = libtx::reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&key_id,
|
||||
fees,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let mut block =
|
||||
Block::new(&header, vec![initial_tx], Difficulty::min(), reward).unwrap();
|
||||
|
||||
|
@ -159,7 +173,14 @@ fn test_transaction_pool_block_reconciliation() {
|
|||
let block = {
|
||||
let key_id = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
let fees = block_txs.iter().map(|tx| tx.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fees, false).unwrap();
|
||||
let reward = libtx::reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&key_id,
|
||||
fees,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let mut block = Block::new(&header, block_txs, Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
|
|
|
@ -195,7 +195,7 @@ where
|
|||
|
||||
tx_elements.push(libtx::build::with_fee(fees as u64));
|
||||
|
||||
libtx::build::transaction(tx_elements, keychain).unwrap()
|
||||
libtx::build::transaction(tx_elements, keychain, &libtx::ProofBuilder::new(keychain)).unwrap()
|
||||
}
|
||||
|
||||
pub fn test_transaction<K>(
|
||||
|
@ -225,7 +225,7 @@ where
|
|||
}
|
||||
tx_elements.push(libtx::build::with_fee(fees as u64));
|
||||
|
||||
libtx::build::transaction(tx_elements, keychain).unwrap()
|
||||
libtx::build::transaction(tx_elements, keychain, &libtx::ProofBuilder::new(keychain)).unwrap()
|
||||
}
|
||||
|
||||
pub fn test_source() -> TxSource {
|
||||
|
|
|
@ -44,7 +44,14 @@ fn test_the_transaction_pool() {
|
|||
let header = {
|
||||
let height = 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
|
||||
let reward = libtx::reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&key_id,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let block = Block::new(&BlockHeader::default(), vec![], Difficulty::min(), reward).unwrap();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
|
@ -246,7 +253,14 @@ fn test_the_transaction_pool() {
|
|||
let header = {
|
||||
let height = 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
|
||||
let reward = libtx::reward::output(
|
||||
&keychain,
|
||||
&libtx::ProofBuilder::new(&keychain),
|
||||
&key_id,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let block =
|
||||
Block::new(&BlockHeader::default(), vec![], Difficulty::min(), reward).unwrap();
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ use crate::common::types::Error;
|
|||
use crate::core::core::verifier_cache::VerifierCache;
|
||||
use crate::core::core::{Output, TxKernel};
|
||||
use crate::core::libtx::secp_ser;
|
||||
use crate::core::libtx::ProofBuilder;
|
||||
use crate::core::{consensus, core, global};
|
||||
use crate::keychain::{ExtKeychain, Identifier, Keychain};
|
||||
use crate::pool;
|
||||
|
@ -223,8 +224,14 @@ fn burn_reward(block_fees: BlockFees) -> Result<(core::Output, core::TxKernel, B
|
|||
warn!("Burning block fees: {:?}", block_fees);
|
||||
let keychain = ExtKeychain::from_random_seed(global::is_floonet())?;
|
||||
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let (out, kernel) =
|
||||
crate::core::libtx::reward::output(&keychain, &key_id, block_fees.fees, false).unwrap();
|
||||
let (out, kernel) = crate::core::libtx::reward::output(
|
||||
&keychain,
|
||||
&ProofBuilder::new(&keychain),
|
||||
&key_id,
|
||||
block_fees.fees,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
Ok((out, kernel, block_fees))
|
||||
}
|
||||
|
||||
|
|
|
@ -28,5 +28,5 @@ zeroize = "0.5.2"
|
|||
#git = "https://github.com/mimblewimble/rust-secp256k1-zkp"
|
||||
#tag = "grin_integration_29"
|
||||
#path = "../../rust-secp256k1-zkp"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
features = ["bullet-proof-sizing"]
|
||||
|
|
Loading…
Reference in a new issue