From af178f82f85549e1710ff3bf2d71977936e197f0 Mon Sep 17 00:00:00 2001 From: Ignotus Peverell Date: Fri, 8 Jun 2018 06:21:54 +0100 Subject: [PATCH] Refactor the Keychain to be based on a trait (#1146) * First pass at restructuring the keychain crate and introducing a Keychain trait * Parameterized everything that had to. Stuff compiles. * More stuff compiles, fix most tests * Big merge, pushing down opening the keychain forced adding factory methods on trait * Test fixes for pool and servers crate --- chain/src/chain.rs | 4 +- chain/tests/data_file_integrity.rs | 18 +- chain/tests/mine_simple_chain.rs | 52 +++-- chain/tests/store_indices.rs | 8 +- chain/tests/test_coinbase_maturity.rs | 8 +- chain/tests/test_txhashset_raw_txs.rs | 6 +- core/src/core/transaction.rs | 12 +- core/tests/block.rs | 34 +-- core/tests/common/mod.rs | 29 +-- core/tests/core.rs | 18 +- core/tests/transaction.rs | 4 +- keychain/src/extkey.rs | 166 +------------- keychain/src/keychain.rs | 233 +++++++++----------- keychain/src/lib.rs | 7 +- keychain/src/{blind.rs => types.rs} | 176 ++++++++++++++- p2p/tests/peer_handshake.rs | 2 +- pool/tests/block_building.rs | 6 +- pool/tests/block_reconciliation.rs | 6 +- pool/tests/coinbase_maturity.rs | 4 +- pool/tests/common/mod.rs | 26 ++- pool/tests/transaction_pool.rs | 6 +- servers/src/mining/mine_block.rs | 4 +- servers/tests/framework/mod.rs | 17 +- wallet/src/display.rs | 1 + wallet/src/file_wallet.rs | 29 ++- wallet/src/libtx/aggsig.rs | 13 +- wallet/src/libtx/build.rs | 101 ++++++--- wallet/src/libtx/error.rs | 13 +- wallet/src/libtx/proof.rs | 30 ++- wallet/src/libtx/reward.rs | 13 +- wallet/src/libtx/slate.rs | 68 ++++-- wallet/src/libwallet/api.rs | 39 +++- wallet/src/libwallet/controller.rs | 108 +++++---- wallet/src/libwallet/internal/keys.rs | 17 +- wallet/src/libwallet/internal/restore.rs | 21 +- wallet/src/libwallet/internal/selection.rs | 37 ++-- wallet/src/libwallet/internal/sigcontext.rs | 2 +- wallet/src/libwallet/internal/tx.rs | 39 +++- wallet/src/libwallet/internal/updater.rs | 72 +++--- wallet/src/libwallet/types.rs | 7 +- wallet/tests/common/mod.rs | 36 +-- wallet/tests/libwallet.rs | 20 +- wallet/tests/transaction.rs | 6 +- 43 files changed, 840 insertions(+), 678 deletions(-) rename keychain/src/{blind.rs => types.rs} (51%) diff --git a/chain/src/chain.rs b/chain/src/chain.rs index 929d4541a..a01a4b941 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -20,10 +20,10 @@ use std::fs::File; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; +use core::core::Committed; use core::core::hash::{Hash, Hashed}; use core::core::pmmr::MerkleProof; use core::core::target::Difficulty; -use core::core::Committed; use core::core::{Block, BlockHeader, Output, OutputIdentifier, Transaction, TxKernel}; use core::global; use grin_store::Error::NotFoundErr; @@ -31,8 +31,8 @@ use pipe; use store; use txhashset; use types::*; -use util::secp::pedersen::{Commitment, RangeProof}; use util::LOGGER; +use util::secp::pedersen::{Commitment, RangeProof}; /// Orphan pool size is limited by MAX_ORPHAN_SIZE pub const MAX_ORPHAN_SIZE: usize = 200; diff --git a/chain/tests/data_file_integrity.rs b/chain/tests/data_file_integrity.rs index 9f9f24a8c..cdf8c1893 100644 --- a/chain/tests/data_file_integrity.rs +++ b/chain/tests/data_file_integrity.rs @@ -24,15 +24,15 @@ extern crate time; use std::fs; use std::sync::Arc; -use chain::types::*; use chain::Chain; +use chain::types::*; use core::core::target::Difficulty; use core::core::{Block, BlockHeader, Transaction}; use core::global; use core::global::ChainTypes; use core::{consensus, genesis}; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use wallet::libtx; use core::pow; @@ -69,7 +69,7 @@ fn data_files() { //new block so chain references should be freed { let chain = setup(chain_dir); - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); for n in 1..4 { let prev = chain.head_header().unwrap(); @@ -114,14 +114,14 @@ fn data_files() { } } -fn _prepare_block(kc: &Keychain, prev: &BlockHeader, chain: &Chain, diff: u64) -> Block { +fn _prepare_block(kc: &ExtKeychain, prev: &BlockHeader, chain: &Chain, diff: u64) -> Block { let mut b = _prepare_block_nosum(kc, prev, diff, vec![]); chain.set_txhashset_roots(&mut b, false).unwrap(); b } fn _prepare_block_tx( - kc: &Keychain, + kc: &ExtKeychain, prev: &BlockHeader, chain: &Chain, diff: u64, @@ -132,14 +132,14 @@ fn _prepare_block_tx( b } -fn _prepare_fork_block(kc: &Keychain, prev: &BlockHeader, chain: &Chain, diff: u64) -> Block { +fn _prepare_fork_block(kc: &ExtKeychain, prev: &BlockHeader, chain: &Chain, diff: u64) -> Block { let mut b = _prepare_block_nosum(kc, prev, diff, vec![]); chain.set_txhashset_roots(&mut b, true).unwrap(); b } fn _prepare_fork_block_tx( - kc: &Keychain, + kc: &ExtKeychain, prev: &BlockHeader, chain: &Chain, diff: u64, @@ -151,7 +151,7 @@ fn _prepare_fork_block_tx( } fn _prepare_block_nosum( - kc: &Keychain, + kc: &ExtKeychain, prev: &BlockHeader, diff: u64, txs: Vec<&Transaction>, @@ -159,7 +159,7 @@ fn _prepare_block_nosum( let key_id = kc.derive_key_id(diff as u32).unwrap(); let fees = txs.iter().map(|tx| tx.fee()).sum(); - let reward = libtx::reward::output(&kc, &key_id, fees, prev.height).unwrap(); + let reward = libtx::reward::output(kc, &key_id, fees, prev.height).unwrap(); let mut b = match core::core::Block::new( prev, txs.into_iter().cloned().collect(), diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 893f1b916..191b03a89 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -34,7 +34,7 @@ use core::global; use core::global::ChainTypes; use wallet::libtx::{self, build}; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use core::pow; @@ -58,7 +58,7 @@ fn setup(dir_name: &str) -> Chain { #[test] fn mine_empty_chain() { let chain = setup(".grin"); - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); for n in 1..4 { let prev = chain.head_header().unwrap(); @@ -107,7 +107,7 @@ fn mine_empty_chain() { #[test] fn mine_forks() { let chain = setup(".grin2"); - let kc = Keychain::from_random_seed().unwrap(); + let kc = ExtKeychain::from_random_seed().unwrap(); // add a first block to not fork genesis let prev = chain.head_header().unwrap(); @@ -148,7 +148,7 @@ fn mine_forks() { #[test] fn mine_losing_fork() { - let kc = Keychain::from_random_seed().unwrap(); + let kc = ExtKeychain::from_random_seed().unwrap(); let chain = setup(".grin3"); // add a first block we'll be forking from @@ -179,7 +179,7 @@ fn mine_losing_fork() { #[test] fn longer_fork() { - let kc = Keychain::from_random_seed().unwrap(); + let kc = ExtKeychain::from_random_seed().unwrap(); // to make it easier to compute the txhashset roots in the test, we // prepare 2 chains, the 2nd will be have the forked blocks we can // then send back on the 1st @@ -231,7 +231,7 @@ fn spend_in_fork_and_compact() { util::init_test_logger(); let chain = setup(".grin6"); let prev = chain.head_header().unwrap(); - let kc = Keychain::from_random_seed().unwrap(); + let kc = ExtKeychain::from_random_seed().unwrap(); let mut fork_head = prev; @@ -359,53 +359,63 @@ fn spend_in_fork_and_compact() { chain.validate(false).unwrap(); } -fn prepare_block(kc: &Keychain, prev: &BlockHeader, chain: &Chain, diff: u64) -> Block { +fn prepare_block(kc: &K, prev: &BlockHeader, chain: &Chain, diff: u64) -> Block +where + K: Keychain, +{ let mut b = prepare_block_nosum(kc, prev, diff, vec![]); chain.set_txhashset_roots(&mut b, false).unwrap(); b } -fn prepare_block_tx( - kc: &Keychain, +fn prepare_block_tx( + kc: &K, prev: &BlockHeader, chain: &Chain, diff: u64, txs: Vec<&Transaction>, -) -> Block { +) -> Block +where + K: Keychain, +{ let mut b = prepare_block_nosum(kc, prev, diff, txs); chain.set_txhashset_roots(&mut b, false).unwrap(); b } -fn prepare_fork_block(kc: &Keychain, prev: &BlockHeader, chain: &Chain, diff: u64) -> Block { +fn prepare_fork_block(kc: &K, prev: &BlockHeader, chain: &Chain, diff: u64) -> Block +where + K: Keychain, +{ let mut b = prepare_block_nosum(kc, prev, diff, vec![]); chain.set_txhashset_roots(&mut b, true).unwrap(); b } -fn prepare_fork_block_tx( - kc: &Keychain, +fn prepare_fork_block_tx( + kc: &K, prev: &BlockHeader, chain: &Chain, diff: u64, txs: Vec<&Transaction>, -) -> Block { +) -> Block +where + K: Keychain, +{ let mut b = prepare_block_nosum(kc, prev, diff, txs); chain.set_txhashset_roots(&mut b, true).unwrap(); b } -fn prepare_block_nosum( - kc: &Keychain, - prev: &BlockHeader, - diff: u64, - txs: Vec<&Transaction>, -) -> Block { +fn prepare_block_nosum(kc: &K, prev: &BlockHeader, diff: u64, txs: Vec<&Transaction>) -> Block +where + K: Keychain, +{ let proof_size = global::proofsize(); let key_id = kc.derive_key_id(diff as u32).unwrap(); let fees = txs.iter().map(|tx| tx.fee()).sum(); - let reward = libtx::reward::output(&kc, &key_id, fees, prev.height).unwrap(); + let reward = libtx::reward::output(kc, &key_id, fees, prev.height).unwrap(); let mut b = match core::core::Block::new( prev, txs.into_iter().cloned().collect(), diff --git a/chain/tests/store_indices.rs b/chain/tests/store_indices.rs index e854e3b2b..a3a5ba947 100644 --- a/chain/tests/store_indices.rs +++ b/chain/tests/store_indices.rs @@ -22,14 +22,14 @@ extern crate rand; use std::fs; use chain::{ChainStore, Tip}; -use core::core::hash::Hashed; -use core::core::target::Difficulty; use core::core::Block; use core::core::BlockHeader; +use core::core::hash::Hashed; +use core::core::target::Difficulty; use core::global; use core::global::ChainTypes; use core::pow; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use wallet::libtx; @@ -43,7 +43,7 @@ fn test_various_store_indices() { let chain_dir = ".grin_idx_1"; clean_output_dir(chain_dir); - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let chain_store = diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index 779aa1b3f..8d14abc5a 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -25,14 +25,14 @@ use std::sync::Arc; use chain::types::*; use core::consensus; +use core::core::OutputIdentifier; use core::core::target::Difficulty; use core::core::transaction; -use core::core::OutputIdentifier; use core::global; use core::global::ChainTypes; use wallet::libtx::build; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use wallet::libtx; use core::pow; @@ -58,7 +58,7 @@ fn test_coinbase_maturity() { let prev = chain.head_header().unwrap(); - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); @@ -145,7 +145,7 @@ fn test_coinbase_maturity() { for _ in 0..3 { let prev = chain.head_header().unwrap(); - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let pk = keychain.derive_key_id(1).unwrap(); let reward = libtx::reward::output(&keychain, &pk, 0, prev.height).unwrap(); diff --git a/chain/tests/test_txhashset_raw_txs.rs b/chain/tests/test_txhashset_raw_txs.rs index ad0d80fe9..3ceddd6aa 100644 --- a/chain/tests/test_txhashset_raw_txs.rs +++ b/chain/tests/test_txhashset_raw_txs.rs @@ -20,15 +20,15 @@ extern crate grin_wallet as wallet; use std::fs; use std::sync::Arc; +use chain::ChainStore; use chain::store::ChainKVStore; use chain::txhashset; use chain::txhashset::TxHashSet; use chain::types::Tip; -use chain::ChainStore; use core::core::pmmr::MerkleProof; use core::core::target::Difficulty; use core::core::{Block, BlockHeader}; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use wallet::libtx::{build, reward}; fn clean_output_dir(dir_name: &str) { @@ -43,7 +43,7 @@ fn test_some_raw_txs() { let store = Arc::new(ChainKVStore::new(db_root.clone()).unwrap()); let mut txhashset = TxHashSet::open(db_root.clone(), store.clone()).unwrap(); - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index f2b25ede2..dfe774cee 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -14,8 +14,8 @@ //! Transactions -use std::cmp::max; use std::cmp::Ordering; +use std::cmp::max; use std::collections::HashSet; use std::io::Cursor; use std::{error, fmt}; @@ -26,12 +26,12 @@ use util::{kernel_sig_msg, static_secp_instance}; use consensus; use consensus::VerifySortOrder; +use core::BlockHeader; use core::committed; use core::committed::Committed; use core::global; use core::hash::{Hash, Hashed, ZERO_HASH}; use core::pmmr::MerkleProof; -use core::BlockHeader; use keychain; use keychain::BlindingFactor; use ser::{self, read_and_verify_sorted, ser_vec, PMMRable, Readable, Reader, Writeable, @@ -1074,12 +1074,12 @@ impl ProofMessageElements { mod test { use super::*; use core::id::{ShortId, ShortIdentifiable}; - use keychain::Keychain; + use keychain::{ExtKeychain, Keychain}; use util::secp; #[test] fn test_kernel_ser_deser() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let commit = keychain.commit(5, &key_id).unwrap(); @@ -1124,7 +1124,7 @@ mod test { #[test] fn commit_consistency() { - let keychain = Keychain::from_seed(&[0; 32]).unwrap(); + let keychain = ExtKeychain::from_seed(&[0; 32]).unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let commit = keychain.commit(1003, &key_id).unwrap(); @@ -1137,7 +1137,7 @@ mod test { #[test] fn input_short_id() { - let keychain = Keychain::from_seed(&[0; 32]).unwrap(); + let keychain = ExtKeychain::from_seed(&[0; 32]).unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let commit = keychain.commit(5, &key_id).unwrap(); diff --git a/core/tests/block.rs b/core/tests/block.rs index c1b8c2d4a..423ab30af 100644 --- a/core/tests/block.rs +++ b/core/tests/block.rs @@ -21,14 +21,14 @@ pub mod common; use common::{new_block, tx1i2o, tx2i1o, txspend1i1o}; use grin_core::consensus::{BLOCK_OUTPUT_WEIGHT, MAX_BLOCK_WEIGHT}; +use grin_core::core::Committed; use grin_core::core::block::Error; use grin_core::core::hash::Hashed; use grin_core::core::id::{ShortId, ShortIdentifiable}; -use grin_core::core::Committed; use grin_core::core::{Block, BlockHeader, CompactBlock, KernelFeatures, OutputFeatures}; use grin_core::global; use grin_core::ser; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use std::time::Instant; use wallet::libtx::build::{self, input, output, with_fee}; @@ -38,7 +38,7 @@ use util::{secp, secp_static}; // TODO: make this fast enough or add similar but faster test? #[allow(dead_code)] fn too_large_block() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let max_out = MAX_BLOCK_WEIGHT / BLOCK_OUTPUT_WEIGHT; let zero_commit = secp_static::commit_to_zero_value(); @@ -84,7 +84,7 @@ fn very_empty_block() { #[test] // builds a block with a tx spending another and check that cut_through occurred fn block_with_cut_through() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); @@ -118,7 +118,7 @@ fn block_with_cut_through() { #[test] fn empty_block_with_coinbase_is_valid() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let zero_commit = secp_static::commit_to_zero_value(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); @@ -152,7 +152,7 @@ fn empty_block_with_coinbase_is_valid() { // invalidates the block and specifically it causes verify_coinbase to fail // additionally verifying the merkle_inputs_outputs also fails fn remove_coinbase_output_flag() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let zero_commit = secp_static::commit_to_zero_value(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); @@ -184,7 +184,7 @@ fn remove_coinbase_output_flag() { // test that flipping the COINBASE_KERNEL flag on the kernel features // invalidates the block and specifically it causes verify_coinbase to fail fn remove_coinbase_kernel_flag() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let zero_commit = secp_static::commit_to_zero_value(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); @@ -212,7 +212,7 @@ fn remove_coinbase_kernel_flag() { #[test] fn serialize_deserialize_block() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); let b = new_block(vec![], &keychain, &prev, &key_id); @@ -229,7 +229,7 @@ fn serialize_deserialize_block() { #[test] fn empty_block_serialized_size() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); let b = new_block(vec![], &keychain, &prev, &key_id); @@ -241,7 +241,7 @@ fn empty_block_serialized_size() { #[test] fn block_single_tx_serialized_size() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let tx1 = tx1i2o(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); @@ -254,7 +254,7 @@ fn block_single_tx_serialized_size() { #[test] fn empty_compact_block_serialized_size() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); let b = new_block(vec![], &keychain, &prev, &key_id); @@ -266,7 +266,7 @@ fn empty_compact_block_serialized_size() { #[test] fn compact_block_single_tx_serialized_size() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let tx1 = tx1i2o(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); @@ -279,7 +279,7 @@ fn compact_block_single_tx_serialized_size() { #[test] fn block_10_tx_serialized_size() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); global::set_mining_mode(global::ChainTypes::Mainnet); let mut txs = vec![]; @@ -298,7 +298,7 @@ fn block_10_tx_serialized_size() { #[test] fn compact_block_10_tx_serialized_size() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let mut txs = vec![]; for _ in 0..10 { @@ -316,7 +316,7 @@ fn compact_block_10_tx_serialized_size() { #[test] fn compact_block_hash_with_nonce() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let tx = tx1i2o(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); @@ -346,7 +346,7 @@ fn compact_block_hash_with_nonce() { #[test] fn convert_block_to_compact_block() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let tx1 = tx1i2o(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); @@ -369,7 +369,7 @@ fn convert_block_to_compact_block() { #[test] fn hydrate_empty_compact_block() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); let b = new_block(vec![], &keychain, &prev, &key_id); diff --git a/core/tests/common/mod.rs b/core/tests/common/mod.rs index 6936bf799..c6f328399 100644 --- a/core/tests/common/mod.rs +++ b/core/tests/common/mod.rs @@ -19,16 +19,16 @@ extern crate grin_keychain as keychain; extern crate grin_util as util; extern crate grin_wallet as wallet; +use grin_core::core::Transaction; use grin_core::core::block::{Block, BlockHeader}; use grin_core::core::target::Difficulty; -use grin_core::core::Transaction; use keychain::{Identifier, Keychain}; use wallet::libtx::build::{self, input, output, with_fee}; use wallet::libtx::reward; // utility producing a transaction with 2 inputs and a single outputs pub fn tx2i1o() -> Transaction { - let keychain = keychain::Keychain::from_random_seed().unwrap(); + let keychain = keychain::ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); @@ -46,7 +46,7 @@ pub fn tx2i1o() -> Transaction { // utility producing a transaction with a single input and output pub fn tx1i1o() -> Transaction { - let keychain = keychain::Keychain::from_random_seed().unwrap(); + let keychain = keychain::ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); @@ -60,7 +60,7 @@ pub fn tx1i1o() -> Transaction { // and two outputs (one change output) // Note: this tx has an "offset" kernel pub fn tx1i2o() -> Transaction { - let keychain = keychain::Keychain::from_random_seed().unwrap(); + let keychain = keychain::ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); @@ -78,12 +78,15 @@ pub fn tx1i2o() -> Transaction { // utility to create a block without worrying about the key or previous // header -pub fn new_block( +pub fn new_block( txs: Vec<&Transaction>, - keychain: &Keychain, + keychain: &K, previous_header: &BlockHeader, key_id: &Identifier, -) -> Block { +) -> Block +where + K: Keychain, +{ let fees = txs.iter().map(|tx| tx.fee()).sum(); let reward_output = reward::output(keychain, &key_id, fees, previous_header.height).unwrap(); Block::new( @@ -96,14 +99,12 @@ pub fn new_block( // utility producing a transaction that spends an output with the provided // value and blinding key -pub fn txspend1i1o( - v: u64, - keychain: &Keychain, - key_id1: Identifier, - key_id2: Identifier, -) -> Transaction { +pub fn txspend1i1o(v: u64, keychain: &K, key_id1: Identifier, key_id2: Identifier) -> Transaction +where + K: Keychain, +{ build::transaction( vec![input(v, key_id1), output(3, key_id2), with_fee(2)], - &keychain, + keychain, ).unwrap() } diff --git a/core/tests/core.rs b/core/tests/core.rs index a05204e7d..ac41ef523 100644 --- a/core/tests/core.rs +++ b/core/tests/core.rs @@ -26,7 +26,7 @@ use grin_core::core::block::Error::KernelLockHeight; use grin_core::core::hash::{Hashed, ZERO_HASH}; use grin_core::core::{aggregate, deaggregate, KernelFeatures, Output, Transaction}; use grin_core::ser; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use util::{secp_static, static_secp_instance}; use wallet::libtx::build::{self, initial_tx, input, output, with_excess, with_fee, with_lock_height}; @@ -72,7 +72,7 @@ fn tx_double_ser_deser() { #[test] #[should_panic(expected = "InvalidSecretKey")] fn test_zero_commit_fails() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); // blinding should fail as signing with a zero r*G shouldn't work @@ -88,7 +88,7 @@ fn test_zero_commit_fails() { #[test] fn build_tx_kernel() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); @@ -295,7 +295,7 @@ fn basic_transaction_deaggregation() { #[test] fn hash_output() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); @@ -349,7 +349,7 @@ fn tx_hash_diff() { /// 2 inputs, 2 outputs transaction. #[test] fn tx_build_exchange() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); @@ -386,7 +386,7 @@ fn tx_build_exchange() { #[test] fn reward_empty_block() { - let keychain = keychain::Keychain::from_random_seed().unwrap(); + let keychain = keychain::ExtKeychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let zero_commit = secp_static::commit_to_zero_value(); @@ -402,7 +402,7 @@ fn reward_empty_block() { #[test] fn reward_with_tx_block() { - let keychain = keychain::Keychain::from_random_seed().unwrap(); + let keychain = keychain::ExtKeychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let zero_commit = secp_static::commit_to_zero_value(); @@ -421,7 +421,7 @@ fn reward_with_tx_block() { #[test] fn simple_block() { - let keychain = keychain::Keychain::from_random_seed().unwrap(); + let keychain = keychain::ExtKeychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let zero_commit = secp_static::commit_to_zero_value(); @@ -442,7 +442,7 @@ fn simple_block() { #[test] fn test_block_with_timelocked_tx() { - let keychain = keychain::Keychain::from_random_seed().unwrap(); + let keychain = keychain::ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index 6e83e3a9a..cbad8b965 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -22,13 +22,13 @@ pub mod common; use grin_core::core::{Output, OutputFeatures}; use grin_core::ser; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use util::secp; use wallet::libtx::proof; #[test] fn test_output_ser_deser() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let commit = keychain.commit(5, &key_id).unwrap(); let msg = secp::pedersen::ProofMessage::empty(); diff --git a/keychain/src/extkey.rs b/keychain/src/extkey.rs index 1d2d5b709..f01afab9b 100644 --- a/keychain/src/extkey.rs +++ b/keychain/src/extkey.rs @@ -12,167 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::cmp::min; -use std::{error, fmt, num}; - -use serde::{de, ser}; - use blake2::blake2b::blake2b; use byteorder::{BigEndian, ByteOrder}; -use util; -use util::secp; +use types::{Error, Identifier}; use util::secp::Secp256k1; -use util::secp::key::{PublicKey, SecretKey}; - -// Size of an identifier in bytes -pub const IDENTIFIER_SIZE: usize = 10; - -/// An ExtKey error -#[derive(PartialEq, Eq, Clone, Debug)] -pub enum Error { - /// The size of the seed is invalid - InvalidSeedSize, - InvalidSliceSize, - InvalidExtendedKey, - Secp(secp::Error), - ParseIntError(num::ParseIntError), -} - -impl From for Error { - fn from(e: secp::Error) -> Error { - Error::Secp(e) - } -} - -impl From for Error { - fn from(e: num::ParseIntError) -> Error { - Error::ParseIntError(e) - } -} - -// Passthrough Debug to Display, since errors should be user-visible -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - f.write_str(error::Error::description(self)) - } -} - -impl error::Error for Error { - fn cause(&self) -> Option<&error::Error> { - None - } - - fn description(&self) -> &str { - match *self { - Error::InvalidSeedSize => "keychain: seed isn't of size 128, 256 or 512", - // TODO change when ser. ext. size is fixed - Error::InvalidSliceSize => "keychain: serialized extended key must be of size 73", - Error::InvalidExtendedKey => "keychain: the given serialized extended key is invalid", - Error::Secp(_) => "keychain: secp error", - Error::ParseIntError(_) => "keychain: error parsing int", - } - } -} - -#[derive(Clone, PartialEq, Eq, Ord, Hash, PartialOrd)] -pub struct Identifier([u8; IDENTIFIER_SIZE]); - -impl ser::Serialize for Identifier { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_str(&self.to_hex()) - } -} - -impl<'de> de::Deserialize<'de> for Identifier { - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(IdentifierVisitor) - } -} - -struct IdentifierVisitor; - -impl<'de> de::Visitor<'de> for IdentifierVisitor { - type Value = Identifier; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an identifier") - } - - fn visit_str(self, s: &str) -> Result - where - E: de::Error, - { - let identifier = Identifier::from_hex(s).unwrap(); - Ok(identifier) - } -} - -impl Identifier { - pub fn zero() -> Identifier { - Identifier::from_bytes(&[0; IDENTIFIER_SIZE]) - } - - pub fn from_bytes(bytes: &[u8]) -> Identifier { - let mut identifier = [0; IDENTIFIER_SIZE]; - for i in 0..min(IDENTIFIER_SIZE, bytes.len()) { - identifier[i] = bytes[i]; - } - Identifier(identifier) - } - - pub fn to_bytes(&self) -> [u8; IDENTIFIER_SIZE] { - self.0.clone() - } - - pub fn from_pubkey(secp: &Secp256k1, pubkey: &PublicKey) -> Identifier { - let bytes = pubkey.serialize_vec(secp, true); - let identifier = blake2b(IDENTIFIER_SIZE, &[], &bytes[..]); - Identifier::from_bytes(&identifier.as_bytes()) - } - - /// Return the identifier of the secret key - /// which is the blake2b (10 byte) digest of the PublicKey - /// corresponding to the secret key provided. - fn from_secret_key(secp: &Secp256k1, key: &SecretKey) -> Result { - let key_id = PublicKey::from_secret_key(secp, key)?; - Ok(Identifier::from_pubkey(secp, &key_id)) - } - - fn from_hex(hex: &str) -> Result { - let bytes = util::from_hex(hex.to_string()).unwrap(); - Ok(Identifier::from_bytes(&bytes)) - } - - pub fn to_hex(&self) -> String { - util::to_hex(self.0.to_vec()) - } -} - -impl AsRef<[u8]> for Identifier { - fn as_ref(&self) -> &[u8] { - &self.0.as_ref() - } -} - -impl ::std::fmt::Debug for Identifier { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{}(", stringify!(Identifier))?; - write!(f, "{}", self.to_hex())?; - write!(f, ")") - } -} - -impl fmt::Display for Identifier { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_hex()) - } -} +use util::secp::key::SecretKey; #[derive(Debug, Clone)] pub struct ChildKey { @@ -210,7 +54,11 @@ impl ExtendedKey { pub fn from_seed(secp: &Secp256k1, seed: &[u8]) -> Result { match seed.len() { 16 | 32 | 64 => (), - _ => return Err(Error::InvalidSeedSize), + _ => { + return Err(Error::KeyDerivation( + "seed size must be 128, 256 or 512".to_owned(), + )) + } } let derived = blake2b(64, b"Grin/MW Seed", seed); diff --git a/keychain/src/keychain.rs b/keychain/src/keychain.rs index 5bf4567a3..3e03d3231 100644 --- a/keychain/src/keychain.rs +++ b/keychain/src/keychain.rs @@ -12,87 +12,34 @@ // See the License for the specific language governing permissions and // limitations under the License. +/// Implementation of the Keychain trait based on an extended key derivation +/// scheme. use rand::{thread_rng, Rng}; use std::collections::HashMap; use std::sync::{Arc, RwLock}; -use std::{error, fmt}; -use util::secp; -use util::secp::{Message, Secp256k1, Signature}; +use blake2; + +use extkey; +use types::{BlindSum, BlindingFactor, Error, Identifier, Keychain}; +use util::logger::LOGGER; use util::secp::key::SecretKey; use util::secp::pedersen::Commitment; -use util::logger::LOGGER; -use blake2; -use blind::{BlindSum, BlindingFactor}; -use extkey::{self, Identifier}; - -#[derive(PartialEq, Eq, Clone, Debug)] -pub enum Error { - ExtendedKey(extkey::Error), - Secp(secp::Error), - KeyDerivation(String), - Transaction(String), - RangeProof(String), -} - -impl From for Error { - fn from(e: secp::Error) -> Error { - Error::Secp(e) - } -} - -impl From for Error { - fn from(e: extkey::Error) -> Error { - Error::ExtendedKey(e) - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - _ => "some kind of keychain error", - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - _ => write!(f, "some kind of keychain error"), - } - } -} +use util::secp::{self, Message, Secp256k1, Signature}; #[derive(Clone, Debug)] -pub struct Keychain { +pub struct ExtKeychain { secp: Secp256k1, extkey: extkey::ExtendedKey, key_overrides: HashMap, key_derivation_cache: Arc>>, } -impl Keychain { - pub fn root_key_id(&self) -> Identifier { - self.extkey.root_key_id.clone() - } - - // For tests and burn only, associate a key identifier with a known secret key. - pub fn burn_enabled(keychain: &Keychain, burn_key_id: &Identifier) -> Keychain { - let mut key_overrides = HashMap::new(); - key_overrides.insert( - burn_key_id.clone(), - SecretKey::from_slice(&keychain.secp, &[1; 32]).unwrap(), - ); - Keychain { - key_overrides: key_overrides, - ..keychain.clone() - } - } - - pub fn from_seed(seed: &[u8]) -> Result { +impl Keychain for ExtKeychain { + fn from_seed(seed: &[u8]) -> Result { let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); let extkey = extkey::ExtendedKey::from_seed(&secp, seed)?; - let keychain = Keychain { + let keychain = ExtKeychain { secp: secp, extkey: extkey, key_overrides: HashMap::new(), @@ -102,18 +49,22 @@ impl Keychain { } /// For testing - probably not a good idea to use outside of tests. - pub fn from_random_seed() -> Result { + fn from_random_seed() -> Result { let seed: String = thread_rng().gen_ascii_chars().take(16).collect(); let seed = blake2::blake2b::blake2b(32, &[], seed.as_bytes()); - Keychain::from_seed(seed.as_bytes()) + ExtKeychain::from_seed(seed.as_bytes()) } - pub fn derive_key_id(&self, derivation: u32) -> Result { + fn root_key_id(&self) -> Identifier { + self.extkey.root_key_id.clone() + } + + fn derive_key_id(&self, derivation: u32) -> Result { let child_key = self.extkey.derive(&self.secp, derivation)?; Ok(child_key.key_id) } - pub fn derived_key(&self, key_id: &Identifier) -> Result { + fn derived_key(&self, key_id: &Identifier) -> Result { // first check our overrides and just return the key if we have one in there if let Some(key) = self.key_overrides.get(key_id) { trace!( @@ -128,6 +79,82 @@ impl Keychain { Ok(child_key.key) } + fn commit(&self, amount: u64, key_id: &Identifier) -> Result { + let skey = self.derived_key(key_id)?; + let commit = self.secp.commit(amount, skey)?; + Ok(commit) + } + + fn commit_with_key_index(&self, amount: u64, derivation: u32) -> Result { + let child_key = self.derived_key_from_index(derivation)?; + let commit = self.secp.commit(amount, child_key.key)?; + Ok(commit) + } + + fn blind_sum(&self, blind_sum: &BlindSum) -> Result { + let mut pos_keys: Vec = blind_sum + .positive_key_ids + .iter() + .filter_map(|k| self.derived_key(&k).ok()) + .collect(); + + let mut neg_keys: Vec = blind_sum + .negative_key_ids + .iter() + .filter_map(|k| self.derived_key(&k).ok()) + .collect(); + + pos_keys.extend(&blind_sum + .positive_blinding_factors + .iter() + .filter_map(|b| b.secret_key(&self.secp).ok()) + .collect::>()); + + neg_keys.extend(&blind_sum + .negative_blinding_factors + .iter() + .filter_map(|b| b.secret_key(&self.secp).ok()) + .collect::>()); + + let sum = self.secp.blind_sum(pos_keys, neg_keys)?; + Ok(BlindingFactor::from_secret_key(sum)) + } + + fn sign(&self, msg: &Message, key_id: &Identifier) -> Result { + let skey = self.derived_key(key_id)?; + let sig = self.secp.sign(msg, &skey)?; + Ok(sig) + } + + fn sign_with_blinding( + &self, + msg: &Message, + blinding: &BlindingFactor, + ) -> Result { + let skey = &blinding.secret_key(&self.secp)?; + let sig = self.secp.sign(msg, &skey)?; + Ok(sig) + } + + fn secp(&self) -> &Secp256k1 { + &self.secp + } +} + +impl ExtKeychain { + // For tests and burn only, associate a key identifier with a known secret key. + pub fn burn_enabled(keychain: &ExtKeychain, burn_key_id: &Identifier) -> ExtKeychain { + let mut key_overrides = HashMap::new(); + key_overrides.insert( + burn_key_id.clone(), + SecretKey::from_slice(&keychain.secp, &[1; 32]).unwrap(), + ); + ExtKeychain { + key_overrides: key_overrides, + ..keychain.clone() + } + } + fn derived_child_key(&self, key_id: &Identifier) -> Result { trace!(LOGGER, "Derived Key by key_id: {}", key_id); @@ -184,78 +211,18 @@ impl Keychain { let child_key = self.extkey.derive(&self.secp, derivation)?; return Ok(child_key); } - - pub fn commit(&self, amount: u64, key_id: &Identifier) -> Result { - let skey = self.derived_key(key_id)?; - let commit = self.secp.commit(amount, skey)?; - Ok(commit) - } - - pub fn commit_with_key_index(&self, amount: u64, derivation: u32) -> Result { - let child_key = self.derived_key_from_index(derivation)?; - let commit = self.secp.commit(amount, child_key.key)?; - Ok(commit) - } - - pub fn blind_sum(&self, blind_sum: &BlindSum) -> Result { - let mut pos_keys: Vec = blind_sum - .positive_key_ids - .iter() - .filter_map(|k| self.derived_key(&k).ok()) - .collect(); - - let mut neg_keys: Vec = blind_sum - .negative_key_ids - .iter() - .filter_map(|k| self.derived_key(&k).ok()) - .collect(); - - pos_keys.extend(&blind_sum - .positive_blinding_factors - .iter() - .filter_map(|b| b.secret_key(&self.secp).ok()) - .collect::>()); - - neg_keys.extend(&blind_sum - .negative_blinding_factors - .iter() - .filter_map(|b| b.secret_key(&self.secp).ok()) - .collect::>()); - - let sum = self.secp.blind_sum(pos_keys, neg_keys)?; - Ok(BlindingFactor::from_secret_key(sum)) - } - - pub fn sign(&self, msg: &Message, key_id: &Identifier) -> Result { - let skey = self.derived_key(key_id)?; - let sig = self.secp.sign(msg, &skey)?; - Ok(sig) - } - - pub fn sign_with_blinding( - &self, - msg: &Message, - blinding: &BlindingFactor, - ) -> Result { - let skey = &blinding.secret_key(&self.secp)?; - let sig = self.secp.sign(msg, &skey)?; - Ok(sig) - } - - pub fn secp(&self) -> &Secp256k1 { - &self.secp - } } #[cfg(test)] mod test { - use keychain::{BlindSum, BlindingFactor, Keychain}; + use keychain::ExtKeychain; + use types::{BlindSum, BlindingFactor, Keychain}; use util::secp; use util::secp::key::SecretKey; #[test] fn test_key_derivation() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let secp = keychain.secp(); // use the keychain to derive a "key_id" based on the underlying seed @@ -279,7 +246,7 @@ mod test { // and summing the keys used to commit to 0 have the same result. #[test] fn secret_key_addition() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let skey1 = SecretKey::from_slice( &keychain.secp, diff --git a/keychain/src/lib.rs b/keychain/src/lib.rs index 9024bd036..92a986442 100644 --- a/keychain/src/lib.rs +++ b/keychain/src/lib.rs @@ -26,10 +26,9 @@ extern crate serde_json; extern crate slog; extern crate uuid; -pub mod blind; pub mod extkey; +mod types; -pub use blind::{BlindSum, BlindingFactor}; -pub use extkey::{ExtendedKey, Identifier, IDENTIFIER_SIZE}; pub mod keychain; -pub use keychain::{Error, Keychain}; +pub use keychain::ExtKeychain; +pub use types::{BlindSum, BlindingFactor, Error, Identifier, Keychain, IDENTIFIER_SIZE}; diff --git a/keychain/src/blind.rs b/keychain/src/types.rs similarity index 51% rename from keychain/src/blind.rs rename to keychain/src/types.rs index b4d195e02..2bf8e72b2 100644 --- a/keychain/src/blind.rs +++ b/keychain/src/types.rs @@ -11,16 +11,156 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -/// Encapsulate a secret key for the blind_sum operation -use std::cmp::min; use rand::thread_rng; +use std::cmp::min; +/// Keychain trait and its main supporting types. The Identifier is a +/// semi-opaque structure (just bytes) to track keys within the Keychain. +/// BlindingFactor is a useful wrapper around a private key to help with +/// commitment generation. +use std::{error, fmt}; + +use blake2::blake2b::blake2b; +use serde::{de, ser}; -use extkey::Identifier; -use keychain::Error; use util; -use util::secp::{self, Secp256k1}; use util::secp::constants::SECRET_KEY_SIZE; +use util::secp::key::{PublicKey, SecretKey}; +use util::secp::pedersen::Commitment; +use util::secp::{self, Message, Secp256k1, Signature}; + +// Size of an identifier in bytes +pub const IDENTIFIER_SIZE: usize = 10; + +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum Error { + Secp(secp::Error), + KeyDerivation(String), + Transaction(String), + RangeProof(String), +} + +impl From for Error { + fn from(e: secp::Error) -> Error { + Error::Secp(e) + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + match *self { + _ => "some kind of keychain error", + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + _ => write!(f, "some kind of keychain error"), + } + } +} + +#[derive(Clone, PartialEq, Eq, Ord, Hash, PartialOrd)] +pub struct Identifier([u8; IDENTIFIER_SIZE]); + +impl ser::Serialize for Identifier { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_hex()) + } +} + +impl<'de> de::Deserialize<'de> for Identifier { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(IdentifierVisitor) + } +} + +struct IdentifierVisitor; + +impl<'de> de::Visitor<'de> for IdentifierVisitor { + type Value = Identifier; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an identifier") + } + + fn visit_str(self, s: &str) -> Result + where + E: de::Error, + { + let identifier = Identifier::from_hex(s).unwrap(); + Ok(identifier) + } +} + +impl Identifier { + pub fn zero() -> Identifier { + Identifier::from_bytes(&[0; IDENTIFIER_SIZE]) + } + + pub fn from_bytes(bytes: &[u8]) -> Identifier { + let mut identifier = [0; IDENTIFIER_SIZE]; + for i in 0..min(IDENTIFIER_SIZE, bytes.len()) { + identifier[i] = bytes[i]; + } + Identifier(identifier) + } + + pub fn to_bytes(&self) -> [u8; IDENTIFIER_SIZE] { + self.0.clone() + } + + pub fn from_pubkey(secp: &Secp256k1, pubkey: &PublicKey) -> Identifier { + let bytes = pubkey.serialize_vec(secp, true); + let identifier = blake2b(IDENTIFIER_SIZE, &[], &bytes[..]); + Identifier::from_bytes(&identifier.as_bytes()) + } + + /// Return the identifier of the secret key + /// which is the blake2b (10 byte) digest of the PublicKey + /// corresponding to the secret key provided. + pub fn from_secret_key(secp: &Secp256k1, key: &SecretKey) -> Result { + let key_id = PublicKey::from_secret_key(secp, key)?; + Ok(Identifier::from_pubkey(secp, &key_id)) + } + + pub fn from_hex(hex: &str) -> Result { + let bytes = util::from_hex(hex.to_string()).unwrap(); + Ok(Identifier::from_bytes(&bytes)) + } + + pub fn to_hex(&self) -> String { + util::to_hex(self.0.to_vec()) + } +} + +impl AsRef<[u8]> for Identifier { + fn as_ref(&self) -> &[u8] { + &self.0.as_ref() + } +} + +impl ::std::fmt::Debug for Identifier { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + try!(write!(f, "{}(", stringify!(Identifier))); + try!(write!(f, "{}", self.to_hex())); + write!(f, ")") + } +} + +impl fmt::Display for Identifier { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_hex()) + } +} #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct BlindingFactor([u8; SECRET_KEY_SIZE]); @@ -68,12 +208,12 @@ impl BlindingFactor { } } - /// Split a blinding_factor (aka secret_key) into a pair of blinding_factors. - /// We use one of these (k1) to sign the tx_kernel (k1G) + /// Split a blinding_factor (aka secret_key) into a pair of + /// blinding_factors. We use one of these (k1) to sign the tx_kernel (k1G) /// and the other gets aggregated in the block_header as the "offset". - /// This prevents an actor from being able to sum a set of inputs, outputs and kernels - /// from a block to identify and reconstruct a particular tx from a block. - /// You would need both k1, k2 to do this. + /// This prevents an actor from being able to sum a set of inputs, outputs + /// and kernels from a block to identify and reconstruct a particular tx + /// from a block. You would need both k1, k2 to do this. pub fn split(&self, secp: &Secp256k1) -> Result { let skey_1 = secp::key::SecretKey::new(secp, &mut thread_rng()); @@ -138,11 +278,25 @@ impl BlindSum { } } +pub trait Keychain: Sync + Send + Clone { + fn from_seed(seed: &[u8]) -> Result; + fn from_random_seed() -> Result; + fn root_key_id(&self) -> Identifier; + fn derive_key_id(&self, derivation: u32) -> Result; + fn derived_key(&self, key_id: &Identifier) -> Result; + fn commit(&self, amount: u64, key_id: &Identifier) -> Result; + fn commit_with_key_index(&self, amount: u64, derivation: u32) -> Result; + fn blind_sum(&self, blind_sum: &BlindSum) -> Result; + fn sign(&self, msg: &Message, key_id: &Identifier) -> Result; + fn sign_with_blinding(&self, &Message, &BlindingFactor) -> Result; + fn secp(&self) -> &Secp256k1; +} + #[cfg(test)] mod test { use rand::thread_rng; - use blind::BlindingFactor; + use types::BlindingFactor; use util::secp::Secp256k1; use util::secp::key::{SecretKey, ZERO_KEY}; diff --git a/p2p/tests/peer_handshake.rs b/p2p/tests/peer_handshake.rs index 065348b71..45353b901 100644 --- a/p2p/tests/peer_handshake.rs +++ b/p2p/tests/peer_handshake.rs @@ -17,8 +17,8 @@ extern crate grin_p2p as p2p; extern crate grin_util as util; use std::net::{SocketAddr, TcpListener, TcpStream}; -use std::sync::atomic::AtomicBool; use std::sync::Arc; +use std::sync::atomic::AtomicBool; use std::thread; use std::time; diff --git a/pool/tests/block_building.rs b/pool/tests/block_building.rs index caa455ec4..ece57cff5 100644 --- a/pool/tests/block_building.rs +++ b/pool/tests/block_building.rs @@ -29,19 +29,19 @@ use std::sync::{Arc, RwLock}; use core::core::{Block, BlockHeader}; +use chain::ChainStore; use chain::txhashset; use chain::types::Tip; -use chain::ChainStore; use core::core::target::Difficulty; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use wallet::libtx; use common::*; #[test] fn test_transaction_pool_block_building() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain: ExtKeychain = Keychain::from_random_seed().unwrap(); let db_root = ".grin_block_building".to_string(); clean_output_dir(db_root.clone()); diff --git a/pool/tests/block_reconciliation.rs b/pool/tests/block_reconciliation.rs index 8525ea198..69285f75a 100644 --- a/pool/tests/block_reconciliation.rs +++ b/pool/tests/block_reconciliation.rs @@ -29,19 +29,19 @@ use std::sync::{Arc, RwLock}; use core::core::{Block, BlockHeader}; +use chain::ChainStore; use chain::txhashset; use chain::types::Tip; -use chain::ChainStore; use core::core::target::Difficulty; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use wallet::libtx; use common::*; #[test] fn test_transaction_pool_block_reconciliation() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain: ExtKeychain = Keychain::from_random_seed().unwrap(); let db_root = ".grin_block_reconcilliation".to_string(); clean_output_dir(db_root.clone()); diff --git a/pool/tests/coinbase_maturity.rs b/pool/tests/coinbase_maturity.rs index 972656d18..3c57481ea 100644 --- a/pool/tests/coinbase_maturity.rs +++ b/pool/tests/coinbase_maturity.rs @@ -29,7 +29,7 @@ use std::sync::{Arc, RwLock}; use core::core::Transaction; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use pool::TransactionPool; use pool::types::*; @@ -82,7 +82,7 @@ impl BlockChain for CoinbaseMaturityErrorChainAdapter { /// Test we correctly verify coinbase maturity when adding txs to the pool. #[test] fn test_coinbase_maturity() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain: ExtKeychain = Keychain::from_random_seed().unwrap(); // Mocking this up with an adapter that will raise an error for coinbase // maturity. diff --git a/pool/tests/common/mod.rs b/pool/tests/common/mod.rs index 8c72827c4..a48f2b704 100644 --- a/pool/tests/common/mod.rs +++ b/pool/tests/common/mod.rs @@ -30,10 +30,10 @@ use std::sync::{Arc, RwLock}; use core::core::{BlockHeader, Transaction}; +use chain::ChainStore; use chain::store::ChainKVStore; use chain::txhashset; use chain::txhashset::TxHashSet; -use chain::ChainStore; use core::core::hash::Hashed; use core::core::pmmr::MerkleProof; use pool::*; @@ -41,8 +41,8 @@ use pool::*; use keychain::Keychain; use wallet::libtx; -use pool::types::*; use pool::TransactionPool; +use pool::types::*; #[derive(Clone)] pub struct ChainAdapter { @@ -105,11 +105,14 @@ pub fn test_setup(chain: &Arc) -> TransactionPool { ) } -pub fn test_transaction_spending_coinbase( - keychain: &Keychain, +pub fn test_transaction_spending_coinbase( + keychain: &K, header: &BlockHeader, output_values: Vec, -) -> Transaction { +) -> Transaction +where + K: Keychain, +{ let output_sum = output_values.iter().sum::() as i64; let coinbase_reward: u64 = 60_000_000_000; @@ -137,14 +140,17 @@ pub fn test_transaction_spending_coinbase( tx_elements.push(libtx::build::with_fee(fees as u64)); - libtx::build::transaction(tx_elements, &keychain).unwrap() + libtx::build::transaction(tx_elements, keychain).unwrap() } -pub fn test_transaction( - keychain: &Keychain, +pub fn test_transaction( + keychain: &K, input_values: Vec, output_values: Vec, -) -> Transaction { +) -> Transaction +where + K: Keychain, +{ let input_sum = input_values.iter().sum::() as i64; let output_sum = output_values.iter().sum::() as i64; @@ -164,7 +170,7 @@ pub fn test_transaction( } tx_elements.push(libtx::build::with_fee(fees as u64)); - libtx::build::transaction(tx_elements, &keychain).unwrap() + libtx::build::transaction(tx_elements, keychain).unwrap() } pub fn test_source() -> TxSource { diff --git a/pool/tests/transaction_pool.rs b/pool/tests/transaction_pool.rs index adf5ae4be..d28b577d0 100644 --- a/pool/tests/transaction_pool.rs +++ b/pool/tests/transaction_pool.rs @@ -29,13 +29,13 @@ use std::sync::{Arc, RwLock}; use core::core::{Block, BlockHeader}; +use chain::ChainStore; use chain::txhashset; use chain::types::Tip; -use chain::ChainStore; use core::core::target::Difficulty; use core::core::transaction; -use keychain::Keychain; +use keychain::{ExtKeychain, Keychain}; use wallet::libtx; use common::*; @@ -43,7 +43,7 @@ use common::*; /// Test we can add some txs to the pool (both stempool and txpool). #[test] fn test_the_transaction_pool() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain: ExtKeychain = Keychain::from_random_seed().unwrap(); let db_root = ".grin_transaction_pool".to_string(); clean_output_dir(db_root.clone()); diff --git a/servers/src/mining/mine_block.rs b/servers/src/mining/mine_block.rs index 9463fa59b..f9109d567 100644 --- a/servers/src/mining/mine_block.rs +++ b/servers/src/mining/mine_block.rs @@ -31,7 +31,7 @@ use core::core; use core::core::hash::Hashed; use core::ser; use core::ser::AsFixedBytes; -use keychain::{Identifier, Keychain}; +use keychain::{ExtKeychain, Identifier, Keychain}; use pool; use util; use util::LOGGER; @@ -219,7 +219,7 @@ fn build_block( /// fn burn_reward(block_fees: BlockFees) -> Result<(core::Output, core::TxKernel, BlockFees), Error> { warn!(LOGGER, "Burning block fees: {:?}", block_fees); - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let (out, kernel) = wallet::libtx::reward::output(&keychain, &key_id, block_fees.fees, block_fees.height) diff --git a/servers/tests/framework/mod.rs b/servers/tests/framework/mod.rs index 9fdd8d19f..61505d0d2 100644 --- a/servers/tests/framework/mod.rs +++ b/servers/tests/framework/mod.rs @@ -268,12 +268,13 @@ impl LocalServerContainer { //panic!("Error initting wallet seed: {}", e); } - let wallet = FileWallet::new(self.wallet_config.clone(), "").unwrap_or_else(|e| { - panic!( - "Error creating wallet: {:?} Config: {:?}", - e, self.wallet_config - ) - }); + let wallet: FileWallet = + FileWallet::new(self.wallet_config.clone(), "").unwrap_or_else(|e| { + panic!( + "Error creating wallet: {:?} Config: {:?}", + e, self.wallet_config + ) + }); wallet::controller::foreign_listener(wallet, &self.wallet_config.api_listen_addr()) .unwrap_or_else(|e| { @@ -298,7 +299,7 @@ impl LocalServerContainer { config: &WalletConfig, wallet_seed: &wallet::WalletSeed, ) -> wallet::WalletInfo { - let keychain = wallet_seed + let keychain: keychain::ExtKeychain = wallet_seed .derive_keychain("") .expect("Failed to derive keychain from seed file and passphrase."); let mut wallet = FileWallet::new(config.clone(), "") @@ -321,7 +322,7 @@ impl LocalServerContainer { let wallet_seed = wallet::WalletSeed::from_file(config).expect("Failed to read wallet seed file."); - let keychain = wallet_seed + let keychain: keychain::ExtKeychain = wallet_seed .derive_keychain("") .expect("Failed to derive keychain from seed file and passphrase."); let max_outputs = 500; diff --git a/wallet/src/display.rs b/wallet/src/display.rs index d07ce4718..9ca77d0a9 100644 --- a/wallet/src/display.rs +++ b/wallet/src/display.rs @@ -14,6 +14,7 @@ use core::core; use core::core::amount_to_hr_string; +use keychain::Keychain; use libwallet::Error; use libwallet::types::{OutputData, WalletInfo}; use prettytable; diff --git a/wallet/src/file_wallet.rs b/wallet/src/file_wallet.rs index 8e520cb3d..b810e753c 100644 --- a/wallet/src/file_wallet.rs +++ b/wallet/src/file_wallet.rs @@ -103,9 +103,9 @@ impl WalletSeed { util::to_hex(self.0.to_vec()) } - pub fn derive_keychain(&self, password: &str) -> Result { + pub fn derive_keychain(&self, password: &str) -> Result { let seed = blake2::blake2b::blake2b(64, &password.as_bytes(), &self.0); - let result = keychain::Keychain::from_seed(seed.as_bytes())?; + let result = K::from_seed(seed.as_bytes())?; Ok(result) } @@ -168,9 +168,9 @@ impl WalletSeed { /// Wallet information tracking all our outputs. Based on HD derivation and /// avoids storing any key data, only storing output amounts and child index. #[derive(Debug, Clone)] -pub struct FileWallet { +pub struct FileWallet { /// Keychain - pub keychain: Option, + pub keychain: Option, /// Configuration pub config: WalletConfig, /// passphrase: TODO better ways of dealing with this other than storing @@ -185,14 +185,18 @@ pub struct FileWallet { pub lock_file_path: String, } -impl WalletBackend for FileWallet { +impl WalletBackend for FileWallet +where + K: Keychain, +{ /// Initialise with whatever stored credentials we have fn open_with_credentials(&mut self) -> Result<(), libwallet::Error> { let wallet_seed = WalletSeed::from_file(&self.config) .context(libwallet::ErrorKind::CallbackImpl("Error opening wallet"))?; - self.keychain = Some(wallet_seed.derive_keychain(&self.passphrase).context( - libwallet::ErrorKind::CallbackImpl("Error deriving keychain"), - )?); + let keychain = wallet_seed.derive_keychain(&self.passphrase); + self.keychain = Some(keychain.context(libwallet::ErrorKind::CallbackImpl( + "Error deriving keychain", + ))?); // Just blow up password for now after it's been used self.passphrase = String::from(""); Ok(()) @@ -205,7 +209,7 @@ impl WalletBackend for FileWallet { } /// Return the keychain being used - fn keychain(&mut self) -> &mut Keychain { + fn keychain(&mut self) -> &mut K { self.keychain.as_mut().unwrap() } @@ -398,7 +402,7 @@ impl WalletBackend for FileWallet { } } -impl WalletClient for FileWallet { +impl WalletClient for FileWallet { /// Return URL for check node fn node_url(&self) -> &str { &self.config.check_node_api_http_addr @@ -473,7 +477,10 @@ impl WalletClient for FileWallet { } } -impl FileWallet { +impl FileWallet +where + K: Keychain, +{ /// Create a new FileWallet instance pub fn new(config: WalletConfig, passphrase: &str) -> Result { let mut retval = FileWallet { diff --git a/wallet/src/libtx/aggsig.rs b/wallet/src/libtx/aggsig.rs index d7a60da0b..df43eec62 100644 --- a/wallet/src/libtx/aggsig.rs +++ b/wallet/src/libtx/aggsig.rs @@ -13,9 +13,7 @@ // limitations under the License. //! Aggsig helper functions used in transaction creation.. should be only //! interface into the underlying secp library -use keychain::Keychain; -use keychain::blind::BlindingFactor; -use keychain::extkey::Identifier; +use keychain::{BlindingFactor, Identifier, Keychain}; use libtx::error::{Error, ErrorKind}; use util::kernel_sig_msg; use util::secp::key::{PublicKey, SecretKey}; @@ -72,12 +70,15 @@ pub fn verify_partial_sig( } /// Just a simple sig, creates its own nonce, etc -pub fn sign_from_key_id( +pub fn sign_from_key_id( secp: &Secp256k1, - k: &Keychain, + k: &K, msg: &Message, key_id: &Identifier, -) -> Result { +) -> Result +where + K: Keychain, +{ let skey = k.derived_key(key_id)?; let sig = aggsig::sign_single(secp, &msg, &skey, None, None, None)?; Ok(sig) diff --git a/wallet/src/libtx/build.rs b/wallet/src/libtx/build.rs index c92e096ee..0c9cd9f37 100644 --- a/wallet/src/libtx/build.rs +++ b/wallet/src/libtx/build.rs @@ -30,30 +30,35 @@ use util::{kernel_sig_msg, secp}; use core::core::hash::Hash; use core::core::pmmr::MerkleProof; use core::core::{Input, Output, OutputFeatures, ProofMessageElements, Transaction, TxKernel}; -use keychain; -use keychain::{BlindSum, BlindingFactor, Identifier, Keychain}; +use keychain::{self, BlindSum, BlindingFactor, Identifier, Keychain}; use libtx::{aggsig, proof}; use util::LOGGER; /// Context information available to transaction combinators. -pub struct Context<'a> { - keychain: &'a Keychain, +pub struct Context<'a, K: 'a> +where + K: Keychain, +{ + keychain: &'a K, } /// Function type returned by the transaction combinators. Transforms a /// (Transaction, BlindSum) pair into another, provided some context. -pub type Append = for<'a> Fn(&'a mut Context, (Transaction, TxKernel, BlindSum)) +pub type Append = for<'a> Fn(&'a mut Context, (Transaction, TxKernel, BlindSum)) -> (Transaction, TxKernel, BlindSum); /// Adds an input with the provided value and blinding key to the transaction /// being built. -fn build_input( +fn build_input( value: u64, features: OutputFeatures, block_hash: Option, merkle_proof: Option, key_id: Identifier, -) -> Box { +) -> Box> +where + K: Keychain, +{ Box::new( move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) { let commit = build.keychain.commit(value, &key_id).unwrap(); @@ -65,7 +70,10 @@ fn build_input( /// Adds an input with the provided value and blinding key to the transaction /// being built. -pub fn input(value: u64, key_id: Identifier) -> Box { +pub fn input(value: u64, key_id: Identifier) -> Box> +where + K: Keychain, +{ debug!( LOGGER, "Building input (spending regular output): {}, {}", value, key_id @@ -75,12 +83,15 @@ pub fn input(value: u64, key_id: Identifier) -> Box { /// Adds a coinbase input spending a coinbase output. /// We will use the block hash to verify coinbase maturity. -pub fn coinbase_input( +pub fn coinbase_input( value: u64, block_hash: Hash, merkle_proof: MerkleProof, key_id: Identifier, -) -> Box { +) -> Box> +where + K: Keychain, +{ debug!( LOGGER, "Building input (spending coinbase): {}, {}", value, key_id @@ -96,7 +107,10 @@ pub fn coinbase_input( /// Adds an output with the provided value and key identifier from the /// keychain. -pub fn output(value: u64, key_id: Identifier) -> Box { +pub fn output(value: u64, key_id: Identifier) -> Box> +where + K: Keychain, +{ Box::new( move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) { debug!(LOGGER, "Building an output: {}, {}", value, key_id,); @@ -129,7 +143,10 @@ pub fn output(value: u64, key_id: Identifier) -> Box { } /// Sets the fee on the transaction being built. -pub fn with_fee(fee: u64) -> Box { +pub fn with_fee(fee: u64) -> Box> +where + K: Keychain, +{ Box::new( move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) { (tx, kern.with_fee(fee), sum) @@ -138,7 +155,10 @@ pub fn with_fee(fee: u64) -> Box { } /// Sets the lock_height on the transaction being built. -pub fn with_lock_height(lock_height: u64) -> Box { +pub fn with_lock_height(lock_height: u64) -> Box> +where + K: Keychain, +{ Box::new( move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) { (tx, kern.with_lock_height(lock_height), sum) @@ -149,7 +169,10 @@ pub fn with_lock_height(lock_height: u64) -> Box { /// 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(excess: BlindingFactor) -> Box { +pub fn with_excess(excess: BlindingFactor) -> Box> +where + K: Keychain, +{ Box::new( move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) { (tx, kern, sum.add_blinding_factor(excess.clone())) @@ -158,7 +181,10 @@ pub fn with_excess(excess: BlindingFactor) -> Box { } /// Sets a known tx "offset". Used in final step of tx construction. -pub fn with_offset(offset: BlindingFactor) -> Box { +pub fn with_offset(offset: BlindingFactor) -> Box> +where + K: Keychain, +{ Box::new( move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) { (tx.with_offset(offset), kern, sum) @@ -169,7 +195,10 @@ pub fn with_offset(offset: BlindingFactor) -> Box { /// 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(mut tx: Transaction) -> Box { +pub fn initial_tx(mut tx: Transaction) -> Box> +where + K: Keychain, +{ assert_eq!(tx.kernels.len(), 1); let kern = tx.kernels.remove(0); Box::new( @@ -189,10 +218,13 @@ pub fn initial_tx(mut tx: Transaction) -> Box { /// let (tx2, _) = build::transaction(vec![initial_tx(tx1), with_excess(sum), /// output_rand(2)], keychain).unwrap(); /// -pub fn partial_transaction( - elems: Vec>, - keychain: &keychain::Keychain, -) -> Result<(Transaction, BlindingFactor), keychain::Error> { +pub fn partial_transaction( + elems: Vec>>, + keychain: &K, +) -> Result<(Transaction, BlindingFactor), keychain::Error> +where + K: Keychain, +{ let mut ctx = Context { keychain }; let (mut tx, kern, sum) = elems.iter().fold( (Transaction::empty(), TxKernel::empty(), BlindSum::new()), @@ -208,10 +240,13 @@ pub fn partial_transaction( } /// Builds a complete transaction. -pub fn transaction( - elems: Vec>, - keychain: &keychain::Keychain, -) -> Result { +pub fn transaction( + elems: Vec>>, + keychain: &K, +) -> Result +where + K: Keychain, +{ let (mut tx, blind_sum) = partial_transaction(elems, keychain)?; assert_eq!(tx.kernels.len(), 1); @@ -229,10 +264,13 @@ pub fn transaction( /// Builds a complete transaction, splitting the key and /// setting the excess, excess_sig and tx offset as necessary. -pub fn transaction_with_offset( - elems: Vec>, - keychain: &keychain::Keychain, -) -> Result { +pub fn transaction_with_offset( + elems: Vec>>, + keychain: &K, +) -> Result +where + K: Keychain, +{ let mut ctx = Context { keychain }; let (mut tx, mut kern, sum) = elems.iter().fold( (Transaction::empty(), TxKernel::empty(), BlindSum::new()), @@ -265,10 +303,11 @@ pub fn transaction_with_offset( #[cfg(test)] mod test { use super::*; + use keychain::ExtKeychain; #[test] fn blind_simple_tx() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); @@ -288,7 +327,7 @@ mod test { #[test] fn blind_simple_tx_with_offset() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); @@ -308,7 +347,7 @@ mod test { #[test] fn blind_simpler_tx() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); diff --git a/wallet/src/libtx/error.rs b/wallet/src/libtx/error.rs index 3772ddf2d..4951da460 100644 --- a/wallet/src/libtx/error.rs +++ b/wallet/src/libtx/error.rs @@ -18,7 +18,7 @@ use std::fmt::{self, Display}; use core::core::committed; use core::core::transaction; -use keychain::{self, extkey}; +use keychain; use util::secp; /// Lib tx error definition @@ -36,9 +36,6 @@ pub enum ErrorKind { /// Keychain error #[fail(display = "Keychain Error")] Keychain(keychain::Error), - /// Extended key error - #[fail(display = "Extended Key Error")] - ExtendedKey(extkey::Error), /// Transaction error #[fail(display = "Transaction Error")] Transaction(transaction::Error), @@ -117,14 +114,6 @@ impl From for Error { } } -impl From for Error { - fn from(error: extkey::Error) -> Error { - Error { - inner: Context::new(ErrorKind::ExtendedKey(error)), - } - } -} - impl From for Error { fn from(error: transaction::Error) -> Error { Error { diff --git a/wallet/src/libtx/proof.rs b/wallet/src/libtx/proof.rs index 5c17b419e..4f362d25f 100644 --- a/wallet/src/libtx/proof.rs +++ b/wallet/src/libtx/proof.rs @@ -15,18 +15,20 @@ //! Rangeproof library functions use blake2; -use keychain::Keychain; -use keychain::extkey::Identifier; +use keychain::{Identifier, Keychain}; use libtx::error::{Error, ErrorKind}; use util::logger::LOGGER; use util::secp::key::SecretKey; use util::secp::pedersen::{Commitment, ProofInfo, ProofMessage, RangeProof}; use util::secp::{self, Secp256k1}; -fn create_nonce(k: &Keychain, commit: &Commitment) -> Result { +fn create_nonce(k: &K, commit: &Commitment) -> Result +where + K: Keychain, +{ // hash(commit|masterkey) as nonce - let root_key = k.root_key_id().to_bytes(); - let res = blake2::blake2b::blake2b(32, &commit.0, &root_key); + let root_key = k.root_key_id(); + let res = blake2::blake2b::blake2b(32, &commit.0, &root_key.to_bytes()[..]); let res = res.as_bytes(); let mut ret_val = [0; 32]; for i in 0..res.len() { @@ -43,14 +45,17 @@ fn create_nonce(k: &Keychain, commit: &Commitment) -> Result { /// So we want this to take an opaque structure that can be called /// back to get the sensitive data -pub fn create( - k: &Keychain, +pub fn create( + k: &K, amount: u64, key_id: &Identifier, _commit: Commitment, extra_data: Option>, msg: ProofMessage, -) -> Result { +) -> Result +where + K: Keychain, +{ let commit = k.commit(amount, key_id)?; let skey = k.derived_key(key_id)?; let nonce = create_nonce(k, &commit)?; @@ -83,13 +88,16 @@ pub fn verify( } /// Rewind a rangeproof to retrieve the amount -pub fn rewind( - k: &Keychain, +pub fn rewind( + k: &K, key_id: &Identifier, commit: Commitment, extra_data: Option>, proof: RangeProof, -) -> Result { +) -> Result +where + K: Keychain, +{ let skey = k.derived_key(key_id)?; let nonce = create_nonce(k, &commit)?; let proof_message = k.secp() diff --git a/wallet/src/libtx/reward.rs b/wallet/src/libtx/reward.rs index 7c9629699..90441cf25 100644 --- a/wallet/src/libtx/reward.rs +++ b/wallet/src/libtx/reward.rs @@ -14,7 +14,7 @@ //! Builds the blinded output and related signature proof for the block //! reward. -use keychain; +use keychain::{Identifier, Keychain}; use core::consensus::reward; use core::core::KernelFeatures; @@ -24,12 +24,15 @@ use libtx::{aggsig, proof}; use util::{kernel_sig_msg, secp, static_secp_instance, LOGGER}; /// output a reward output -pub fn output( - keychain: &keychain::Keychain, - key_id: &keychain::Identifier, +pub fn output( + keychain: &K, + key_id: &Identifier, fees: u64, height: u64, -) -> Result<(Output, TxKernel), Error> { +) -> Result<(Output, TxKernel), Error> +where + K: Keychain, +{ let value = reward(fees); let commit = keychain.commit(value, key_id)?; let msg = ProofMessageElements::new(value, key_id); diff --git a/wallet/src/libtx/slate.rs b/wallet/src/libtx/slate.rs index 752e7b482..55b6ac9cd 100644 --- a/wallet/src/libtx/slate.rs +++ b/wallet/src/libtx/slate.rs @@ -104,29 +104,35 @@ impl Slate { /// Adds selected inputs and outputs to the slate's transaction /// Returns blinding factor - pub fn add_transaction_elements( + pub fn add_transaction_elements( &mut self, - keychain: &Keychain, - mut elems: Vec>, - ) -> Result { + keychain: &K, + mut elems: Vec>>, + ) -> Result + where + K: Keychain, + { // Append to the exiting transaction if self.tx.kernels.len() != 0 { elems.insert(0, build::initial_tx(self.tx.clone())); } - let (tx, blind) = build::partial_transaction(elems, &keychain)?; + let (tx, blind) = build::partial_transaction(elems, keychain)?; self.tx = tx; Ok(blind) } /// Completes callers part of round 1, adding public key info /// to the slate - pub fn fill_round_1( + pub fn fill_round_1( &mut self, - keychain: &Keychain, + keychain: &K, sec_key: &mut SecretKey, sec_nonce: &SecretKey, participant_id: usize, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + K: Keychain, + { // Whoever does this first generates the offset if self.tx.offset == BlindingFactor::zero() { self.generate_offset(keychain, sec_key)?; @@ -136,13 +142,16 @@ impl Slate { } /// Completes caller's part of round 2, completing signatures - pub fn fill_round_2( + pub fn fill_round_2( &mut self, - keychain: &Keychain, + keychain: &K, sec_key: &SecretKey, sec_nonce: &SecretKey, participant_id: usize, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + K: Keychain, + { self.check_fees()?; self.verify_part_sigs(keychain.secp())?; let sig_part = aggsig::calculate_partial_sig( @@ -160,7 +169,10 @@ impl Slate { /// Creates the final signature, callable by either the sender or recipient /// (after phase 3: sender confirmation) /// TODO: Only callable by receiver at the moment - pub fn finalize(&mut self, keychain: &Keychain) -> Result<(), Error> { + pub fn finalize(&mut self, keychain: &K) -> Result<(), Error> + where + K: Keychain, + { let final_sig = self.finalize_signature(keychain)?; self.finalize_transaction(keychain, &final_sig) } @@ -201,14 +213,17 @@ impl Slate { /// and saves participant's transaction context /// sec_key can be overriden to replace the blinding /// factor (by whoever split the offset) - fn add_participant_info( + fn add_participant_info( &mut self, - keychain: &Keychain, + keychain: &K, sec_key: &SecretKey, sec_nonce: &SecretKey, id: usize, part_sig: Option, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + K: Keychain, + { // Add our public key and nonce to the slate let pub_key = PublicKey::from_secret_key(keychain.secp(), &sec_key)?; let pub_nonce = PublicKey::from_secret_key(keychain.secp(), &sec_nonce)?; @@ -226,11 +241,10 @@ impl Slate { /// For now, we'll have the transaction initiator be responsible for it /// Return offset private key for the participant to use later in the /// transaction - fn generate_offset( - &mut self, - keychain: &Keychain, - sec_key: &mut SecretKey, - ) -> Result<(), Error> { + fn generate_offset(&mut self, keychain: &K, sec_key: &mut SecretKey) -> Result<(), Error> + where + K: Keychain, + { // Generate a random kernel offset here // and subtract it from the blind_sum so we create // the aggsig context with the "split" key @@ -308,7 +322,10 @@ impl Slate { /// /// Returns completed transaction ready for posting to the chain - fn finalize_signature(&mut self, keychain: &Keychain) -> Result { + fn finalize_signature(&mut self, keychain: &K) -> Result + where + K: Keychain, + { self.verify_part_sigs(keychain.secp())?; let part_sigs = self.part_sigs(); @@ -332,11 +349,14 @@ impl Slate { } /// builds a final transaction after the aggregated sig exchange - fn finalize_transaction( + fn finalize_transaction( &mut self, - keychain: &Keychain, + keychain: &K, final_sig: &secp::Signature, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + K: Keychain, + { let kernel_offset = self.tx.offset; self.check_fees()?; diff --git a/wallet/src/libwallet/api.rs b/wallet/src/libwallet/api.rs index e9e4a050e..39d21cdce 100644 --- a/wallet/src/libwallet/api.rs +++ b/wallet/src/libwallet/api.rs @@ -17,6 +17,8 @@ //! vs. functions to interact with someone else) //! Still experimental, not sure this is the best way to do this +use std::marker::PhantomData; + use libtx::slate::Slate; use libwallet::Error; use libwallet::internal::{tx, updater}; @@ -24,26 +26,33 @@ use libwallet::types::{BlockFees, CbData, OutputData, TxWrapper, WalletBackend, WalletInfo}; use core::ser; +use keychain::Keychain; use util::{self, LOGGER}; /// Wrapper around internal API functions, containing a reference to /// the wallet/keychain that they're acting upon -pub struct APIOwner<'a, W> +pub struct APIOwner<'a, W, K> where - W: 'a + WalletBackend + WalletClient, + W: 'a + WalletBackend + WalletClient, + K: Keychain, { /// Wallet, contains its keychain (TODO: Split these up into 2 traits /// perhaps) pub wallet: &'a mut W, + phantom: PhantomData, } -impl<'a, W> APIOwner<'a, W> +impl<'a, W, K> APIOwner<'a, W, K> where - W: 'a + WalletBackend + WalletClient, + W: 'a + WalletBackend + WalletClient, + K: Keychain, { /// Create new API instance - pub fn new(wallet_in: &'a mut W) -> APIOwner<'a, W> { - APIOwner { wallet: wallet_in } + pub fn new(wallet_in: &'a mut W) -> APIOwner<'a, W, K> { + APIOwner { + wallet: wallet_in, + phantom: PhantomData, + } } /// Attempt to update and retrieve outputs @@ -151,22 +160,28 @@ where /// Wrapper around external API functions, intended to communicate /// with other parties -pub struct APIForeign<'a, W> +pub struct APIForeign<'a, W, K> where - W: 'a + WalletBackend + WalletClient, + W: 'a + WalletBackend + WalletClient, + K: Keychain, { /// Wallet, contains its keychain (TODO: Split these up into 2 traits /// perhaps) pub wallet: &'a mut W, + phantom: PhantomData, } -impl<'a, W> APIForeign<'a, W> +impl<'a, W, K> APIForeign<'a, W, K> where - W: 'a + WalletBackend + WalletClient, + W: 'a + WalletBackend + WalletClient, + K: Keychain, { /// Create new API instance - pub fn new(wallet_in: &'a mut W) -> APIForeign<'a, W> { - APIForeign { wallet: wallet_in } + pub fn new(wallet_in: &'a mut W) -> APIForeign<'a, W, K> { + APIForeign { + wallet: wallet_in, + phantom: PhantomData, + } } /// Build a new (potential) coinbase transaction in the wallet diff --git a/wallet/src/libwallet/controller.rs b/wallet/src/libwallet/controller.rs index f1a8ad7c2..640ec1627 100644 --- a/wallet/src/libwallet/controller.rs +++ b/wallet/src/libwallet/controller.rs @@ -16,6 +16,7 @@ //! invocations) as needed. //! Still experimental use api::ApiServer; +use std::marker::PhantomData; use std::sync::{Arc, Mutex}; use bodyparser; @@ -27,6 +28,7 @@ use serde_json; use failure::Fail; +use keychain::Keychain; use libtx::slate::Slate; use libwallet::api::{APIForeign, APIOwner}; use libwallet::types::{BlockFees, CbData, OutputData, WalletBackend, WalletClient, WalletInfo}; @@ -36,10 +38,11 @@ use util::LOGGER; /// Instantiate wallet Owner API for a single-use (command line) call /// Return a function containing a loaded API context to call -pub fn owner_single_use(wallet: &mut T, f: F) -> Result<(), Error> +pub fn owner_single_use(wallet: &mut T, f: F) -> Result<(), Error> where - T: WalletBackend + WalletClient, - F: FnOnce(&mut APIOwner) -> Result<(), Error>, + T: WalletBackend + WalletClient, + F: FnOnce(&mut APIOwner) -> Result<(), Error>, + K: Keychain, { wallet.open_with_credentials()?; f(&mut APIOwner::new(wallet))?; @@ -49,10 +52,11 @@ where /// Instantiate wallet Foreign API for a single-use (command line) call /// Return a function containing a loaded API context to call -pub fn foreign_single_use(wallet: &mut T, f: F) -> Result<(), Error> +pub fn foreign_single_use(wallet: &mut T, f: F) -> Result<(), Error> where - T: WalletBackend + WalletClient, - F: FnOnce(&mut APIForeign) -> Result<(), Error>, + T: WalletBackend + WalletClient, + F: FnOnce(&mut APIForeign) -> Result<(), Error>, + K: Keychain, { wallet.open_with_credentials()?; f(&mut APIForeign::new(wallet))?; @@ -62,14 +66,13 @@ where /// Listener version, providing same API but listening for requests on a /// port and wrapping the calls -pub fn owner_listener(wallet: T, addr: &str) -> Result<(), Error> +pub fn owner_listener(wallet: T, addr: &str) -> Result<(), Error> where - T: WalletBackend, - OwnerAPIHandler: Handler, + T: WalletBackend + WalletClient, + OwnerAPIHandler: Handler, + K: Keychain, { - let api_handler = OwnerAPIHandler { - wallet: Arc::new(Mutex::new(wallet)), - }; + let api_handler = OwnerAPIHandler::new(Arc::new(Mutex::new(wallet))); let router = router!( receive_tx: get "/wallet/owner/*" => api_handler, @@ -89,14 +92,13 @@ where /// Listener version, providing same API but listening for requests on a /// port and wrapping the calls -pub fn foreign_listener(wallet: T, addr: &str) -> Result<(), Error> +pub fn foreign_listener(wallet: T, addr: &str) -> Result<(), Error> where - T: WalletBackend + WalletClient, - ForeignAPIHandler: Handler, + T: WalletBackend + WalletClient, + ForeignAPIHandler: Handler, + K: Keychain, { - let api_handler = ForeignAPIHandler { - wallet: Arc::new(Mutex::new(wallet)), - }; + let api_handler = ForeignAPIHandler::new(Arc::new(Mutex::new(wallet))); let router = router!( receive_tx: post "/wallet/foreign/*" => api_handler, @@ -115,22 +117,32 @@ where } /// API Handler/Wrapper for owner functions -pub struct OwnerAPIHandler +pub struct OwnerAPIHandler where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { /// Wallet instance pub wallet: Arc>, + phantom: PhantomData, } -impl OwnerAPIHandler +impl OwnerAPIHandler where - T: WalletBackend + WalletClient, + T: WalletBackend + WalletClient, + K: Keychain, { + pub fn new(wallet: Arc>) -> OwnerAPIHandler { + OwnerAPIHandler { + wallet, + phantom: PhantomData, + } + } + fn retrieve_outputs( &self, req: &mut Request, - api: &mut APIOwner, + api: &mut APIOwner, ) -> Result, Error> { let res = api.retrieve_outputs(false)?; Ok(res.1) @@ -139,23 +151,23 @@ where fn retrieve_summary_info( &self, req: &mut Request, - api: &mut APIOwner, + api: &mut APIOwner, ) -> Result { let res = api.retrieve_summary_info()?; Ok(res.1) } - fn issue_send_tx(&self, req: &mut Request, api: &mut APIOwner) -> Result<(), Error> { + fn issue_send_tx(&self, req: &mut Request, api: &mut APIOwner) -> Result<(), Error> { // TODO: Args api.issue_send_tx(60, 10, "", 1000, true, true) } - fn issue_burn_tx(&self, req: &mut Request, api: &mut APIOwner) -> Result<(), Error> { + fn issue_burn_tx(&self, req: &mut Request, api: &mut APIOwner) -> Result<(), Error> { // TODO: Args api.issue_burn_tx(60, 10, 1000) } - fn handle_request(&self, req: &mut Request, api: &mut APIOwner) -> IronResult { + fn handle_request(&self, req: &mut Request, api: &mut APIOwner) -> IronResult { let url = req.url.clone(); let path_elems = url.path(); match *path_elems.last().unwrap() { @@ -175,9 +187,10 @@ where } } -impl Handler for OwnerAPIHandler +impl Handler for OwnerAPIHandler where - T: WalletBackend + WalletClient + Send + Sync + 'static, + T: WalletBackend + WalletClient + Send + Sync + 'static, + K: Keychain + 'static, { fn handle(&self, req: &mut Request) -> IronResult { // every request should open with stored credentials, @@ -199,19 +212,33 @@ where /// API Handler/Wrapper for foreign functions -pub struct ForeignAPIHandler +pub struct ForeignAPIHandler where - T: WalletBackend + WalletClient, + T: WalletBackend + WalletClient, + K: Keychain, { /// Wallet instance pub wallet: Arc>, + phantom: PhantomData, } -impl ForeignAPIHandler +impl ForeignAPIHandler where - T: WalletBackend + WalletClient, + T: WalletBackend + WalletClient, + K: Keychain, { - fn build_coinbase(&self, req: &mut Request, api: &mut APIForeign) -> Result { + pub fn new(wallet: Arc>) -> ForeignAPIHandler { + ForeignAPIHandler { + wallet, + phantom: PhantomData, + } + } + + fn build_coinbase( + &self, + req: &mut Request, + api: &mut APIForeign, + ) -> Result { let struct_body = req.get::>(); match struct_body { Ok(Some(block_fees)) => api.build_coinbase(&block_fees), @@ -230,7 +257,7 @@ where } } - fn receive_tx(&self, req: &mut Request, api: &mut APIForeign) -> Result { + fn receive_tx(&self, req: &mut Request, api: &mut APIForeign) -> Result { let struct_body = req.get::>(); if let Ok(Some(mut slate)) = struct_body { api.receive_tx(&mut slate)?; @@ -240,7 +267,11 @@ where } } - fn handle_request(&self, req: &mut Request, api: &mut APIForeign) -> IronResult { + fn handle_request( + &self, + req: &mut Request, + api: &mut APIForeign, + ) -> IronResult { let url = req.url.clone(); let path_elems = url.path(); match *path_elems.last().unwrap() { @@ -256,9 +287,10 @@ where } } -impl Handler for ForeignAPIHandler +impl Handler for ForeignAPIHandler where - T: WalletBackend + WalletClient + Send + Sync + 'static, + T: WalletBackend + WalletClient + Send + Sync + 'static, + K: Keychain + 'static, { fn handle(&self, req: &mut Request) -> IronResult { // every request should open with stored credentials, diff --git a/wallet/src/libwallet/internal/keys.rs b/wallet/src/libwallet/internal/keys.rs index 20a084182..e681df7ab 100644 --- a/wallet/src/libwallet/internal/keys.rs +++ b/wallet/src/libwallet/internal/keys.rs @@ -13,22 +13,24 @@ // limitations under the License. //! Wallet key management functions -use keychain::Identifier; +use keychain::{Identifier, Keychain}; use libwallet::error::Error; use libwallet::types::WalletBackend; /// Get our next available key -pub fn new_output_key(wallet: &mut T) -> Result<(Identifier, u32), Error> +pub fn new_output_key(wallet: &mut T) -> Result<(Identifier, u32), Error> where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { wallet.with_wallet(|wallet_data| next_available_key(wallet_data)) } /// Get next available key in the wallet -pub fn next_available_key(wallet: &mut T) -> (Identifier, u32) +pub fn next_available_key(wallet: &mut T) -> (Identifier, u32) where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { let root_key_id = wallet.keychain().root_key_id(); let derivation = wallet.next_child(root_key_id.clone()); @@ -37,9 +39,10 @@ where } /// Retrieve an existing key from a wallet -pub fn retrieve_existing_key(wallet: &T, key_id: Identifier) -> (Identifier, u32) +pub fn retrieve_existing_key(wallet: &T, key_id: Identifier) -> (Identifier, u32) where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { if let Some(existing) = wallet.get_output(&key_id) { let key_id = existing.key_id.clone(); diff --git a/wallet/src/libwallet/internal/restore.rs b/wallet/src/libwallet/internal/restore.rs index 62bc16089..aa46e0f4f 100644 --- a/wallet/src/libwallet/internal/restore.rs +++ b/wallet/src/libwallet/internal/restore.rs @@ -20,7 +20,7 @@ use core::core::transaction::ProofMessageElements; use core::global; use error::{Error, ErrorKind}; use failure::{Fail, ResultExt}; -use keychain::Identifier; +use keychain::{Identifier, Keychain}; use libtx::proof; use libwallet::types::*; use util; @@ -51,9 +51,10 @@ fn coinbase_status(output: &api::OutputPrintable) -> bool { } } -fn outputs_batch(wallet: &T, start_height: u64, max: u64) -> Result +fn outputs_batch(wallet: &T, start_height: u64, max: u64) -> Result where - T: WalletBackend + WalletClient, + T: WalletBackend + WalletClient, + K: Keychain, { let query_param = format!("start_index={}&max={}", start_height, max); @@ -75,7 +76,7 @@ where } // TODO - wrap the many return values in a struct -fn find_outputs_with_key( +fn find_outputs_with_key( wallet: &mut T, outputs: Vec, found_key_index: &mut Vec, @@ -88,7 +89,11 @@ fn find_outputs_with_key( u64, bool, Option, -)> { +)> +where + T: WalletBackend + WalletClient, + K: Keychain, +{ let mut wallet_outputs: Vec<( pedersen::Commitment, Identifier, @@ -225,7 +230,11 @@ fn find_outputs_with_key( } /// Restore a wallet -pub fn restore(wallet: &mut T) -> Result<(), Error> { +pub fn restore(wallet: &mut T) -> Result<(), Error> +where + T: WalletBackend + WalletClient, + K: Keychain, +{ // Don't proceed if wallet.dat has anything in it let is_empty = wallet .read_wallet(|wallet_data| Ok(wallet_data.outputs().len() == 0)) diff --git a/wallet/src/libwallet/internal/selection.rs b/wallet/src/libwallet/internal/selection.rs index fc7413885..d8215a4cb 100644 --- a/wallet/src/libwallet/internal/selection.rs +++ b/wallet/src/libwallet/internal/selection.rs @@ -14,7 +14,7 @@ //! Selection of inputs for building transactions -use keychain::Identifier; +use keychain::{Identifier, Keychain}; use libtx::{build, tx_fee, slate::Slate}; use libwallet::error::{Error, ErrorKind}; use libwallet::internal::{keys, sigcontext}; @@ -25,7 +25,7 @@ use libwallet::types::*; /// and saves the private wallet identifiers of our selected outputs /// into our transaction context -pub fn build_send_tx_slate( +pub fn build_send_tx_slate( wallet: &mut T, num_participants: usize, amount: u64, @@ -43,7 +43,8 @@ pub fn build_send_tx_slate( Error, > where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { let (elems, inputs, change_id, amount, fee) = select_send_tx( wallet, @@ -107,7 +108,7 @@ where /// returning the key of the fresh output and a closure /// that actually performs the addition of the output to the /// wallet -pub fn build_recipient_output_with_slate( +pub fn build_recipient_output_with_slate( wallet: &mut T, slate: &mut Slate, ) -> Result< @@ -119,25 +120,27 @@ pub fn build_recipient_output_with_slate( Error, > where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { // Create a potential output for this transaction let (key_id, derivation) = keys::new_output_key(wallet)?; - let root_key_id = wallet.keychain().root_key_id(); + let keychain = wallet.keychain().clone(); + let root_key_id = keychain.root_key_id(); let key_id_inner = key_id.clone(); let amount = slate.amount; let height = slate.height; - let keychain = wallet.keychain().clone(); - let blinding = slate.add_transaction_elements(&keychain, vec![build::output(amount, key_id.clone())])?; // Add blinding sum to our context let mut context = sigcontext::Context::new( keychain.secp(), - blinding.secret_key(wallet.keychain().secp()).unwrap(), + blinding + .secret_key(wallet.keychain().clone().secp()) + .unwrap(), ); context.add_output(&key_id); @@ -166,7 +169,7 @@ where /// Builds a transaction to send to someone from the HD seed associated with the /// wallet and the amount to send. Handles reading through the wallet data file, /// selecting outputs to spend and building the change. -pub fn select_send_tx( +pub fn select_send_tx( wallet: &mut T, amount: u64, current_height: u64, @@ -176,7 +179,7 @@ pub fn select_send_tx( selection_strategy_is_use_all: bool, ) -> Result< ( - Vec>, + Vec>>, Vec, Option, u64, // amount @@ -185,9 +188,10 @@ pub fn select_send_tx( Error, > where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { - let key_id = wallet.keychain().clone().root_key_id(); + let key_id = wallet.keychain().root_key_id(); // select some spendable coins from the wallet let mut coins = wallet.read_wallet(|wallet_data| { @@ -280,15 +284,16 @@ pub fn coins_proof_count(coins: &Vec) -> usize { } /// Selects inputs and change for a transaction -pub fn inputs_and_change( +pub fn inputs_and_change( coins: &Vec, wallet: &mut T, height: u64, amount: u64, fee: u64, -) -> Result<(Vec>, Option), Error> +) -> Result<(Vec>>, Option), Error> where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { let mut parts = vec![]; diff --git a/wallet/src/libwallet/internal/sigcontext.rs b/wallet/src/libwallet/internal/sigcontext.rs index 831c19cce..a294b09d7 100644 --- a/wallet/src/libwallet/internal/sigcontext.rs +++ b/wallet/src/libwallet/internal/sigcontext.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. //! Signature context holder helper (may be removed or replaced eventually) -use keychain::extkey::Identifier; +use keychain::Identifier; use libtx::aggsig; use util::secp::key::{PublicKey, SecretKey}; use util::secp::{self, Secp256k1}; diff --git a/wallet/src/libwallet/internal/tx.rs b/wallet/src/libwallet/internal/tx.rs index e6473e1f9..4b2fd33c5 100644 --- a/wallet/src/libwallet/internal/tx.rs +++ b/wallet/src/libwallet/internal/tx.rs @@ -25,7 +25,11 @@ use util::LOGGER; /// Receive a tranaction, modifying the slate accordingly (which can then be /// sent back to sender for posting) -pub fn receive_tx(wallet: &mut T, slate: &mut Slate) -> Result<(), Error> { +pub fn receive_tx(wallet: &mut T, slate: &mut Slate) -> Result<(), Error> +where + T: WalletBackend, + K: Keychain, +{ // create an output using the amount in the slate let (_, mut context, receiver_create_fn) = selection::build_recipient_output_with_slate(wallet, slate).unwrap(); @@ -49,7 +53,7 @@ pub fn receive_tx(wallet: &mut T, slate: &mut Slate) -> Result /// Issue a new transaction to the provided sender by spending some of our /// wallet -pub fn create_send_tx( +pub fn create_send_tx( wallet: &mut T, amount: u64, minimum_confirmations: u64, @@ -62,7 +66,11 @@ pub fn create_send_tx( impl FnOnce(&mut T) -> Result<(), Error>, ), Error, -> { +> +where + T: WalletBackend + WalletClient, + K: Keychain, +{ // Get lock height let current_height = wallet.get_chain_height(wallet.node_url())?; // ensure outputs we're selecting are up to date @@ -102,11 +110,15 @@ pub fn create_send_tx( } /// Complete a transaction as the sender -pub fn complete_tx( +pub fn complete_tx( wallet: &mut T, slate: &mut Slate, context: &sigcontext::Context, -) -> Result<(), Error> { +) -> Result<(), Error> +where + T: WalletBackend, + K: Keychain, +{ let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 0)?; // Final transaction can be built by anyone at this stage let res = slate.finalize(wallet.keychain()); @@ -117,13 +129,20 @@ pub fn complete_tx( } /// Issue a burn tx -pub fn issue_burn_tx( +pub fn issue_burn_tx( wallet: &mut T, amount: u64, minimum_confirmations: u64, max_outputs: usize, -) -> Result { - let keychain = &Keychain::burn_enabled(wallet.keychain(), &Identifier::zero()); +) -> Result +where + T: WalletBackend + WalletClient, + K: Keychain, +{ + // TODO + // let keychain = &Keychain::burn_enabled(wallet.keychain(), + // &Identifier::zero()); + let keychain = wallet.keychain().clone(); let current_height = wallet.get_chain_height(wallet.node_url())?; @@ -159,14 +178,14 @@ pub fn issue_burn_tx( #[cfg(test)] mod test { - use keychain::Keychain; + use keychain::{ExtKeychain, Keychain}; use libtx::build; #[test] // demonstrate that input.commitment == referenced output.commitment // based on the public key and amount begin spent fn output_commitment_equals_input_commitment_on_spend() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); let tx1 = build::transaction(vec![build::output(105, key_id1.clone())], &keychain).unwrap(); diff --git a/wallet/src/libwallet/internal/updater.rs b/wallet/src/libwallet/internal/updater.rs index d4b9b2087..ad2ecd4e5 100644 --- a/wallet/src/libwallet/internal/updater.rs +++ b/wallet/src/libwallet/internal/updater.rs @@ -23,7 +23,7 @@ use core::consensus::reward; use core::core::{Output, TxKernel}; use core::global; use core::ser; -use keychain::Identifier; +use keychain::{Identifier, Keychain}; use libtx::reward; use libwallet::error::{Error, ErrorKind}; use libwallet::internal::keys; @@ -33,10 +33,11 @@ use util::LOGGER; use util::secp::pedersen; /// Retrieve all of the outputs (doesn't attempt to update from node) -pub fn retrieve_outputs( - wallet: &mut T, - show_spent: bool, -) -> Result, Error> { +pub fn retrieve_outputs(wallet: &mut T, show_spent: bool) -> Result, Error> +where + T: WalletBackend, + K: Keychain, +{ let root_key_id = wallet.keychain().clone().root_key_id(); let mut outputs = vec![]; @@ -66,9 +67,10 @@ pub fn retrieve_outputs( /// Refreshes the outputs in a wallet with the latest information /// from a node -pub fn refresh_outputs(wallet: &mut T) -> Result<(), Error> +pub fn refresh_outputs(wallet: &mut T) -> Result<(), Error> where - T: WalletBackend + WalletClient, + T: WalletBackend + WalletClient, + K: Keychain, { let height = wallet.get_chain_height(wallet.node_url())?; refresh_output_state(wallet, height)?; @@ -78,9 +80,10 @@ where // TODO - this might be slow if we have really old outputs that have never been // refreshed -fn refresh_missing_block_hashes(wallet: &mut T, height: u64) -> Result<(), Error> +fn refresh_missing_block_hashes(wallet: &mut T, height: u64) -> Result<(), Error> where - T: WalletBackend + WalletClient, + T: WalletBackend + WalletClient, + K: Keychain, { // build a local map of wallet outputs keyed by commit // and a list of outputs we want to query the node for @@ -125,11 +128,12 @@ where /// build a local map of wallet outputs keyed by commit /// and a list of outputs we want to query the node for -pub fn map_wallet_outputs( +pub fn map_wallet_outputs( wallet: &mut T, ) -> Result, Error> where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { let mut wallet_outputs: HashMap = HashMap::new(); let _ = wallet.read_wallet(|wallet_data| { @@ -150,19 +154,21 @@ where /// As above, but only return unspent outputs with missing block hashes /// and a list of outputs we want to query the node for -pub fn map_wallet_outputs_missing_block( +pub fn map_wallet_outputs_missing_block( wallet: &mut T, ) -> Result, Error> where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { let mut wallet_outputs: HashMap = HashMap::new(); let _ = wallet.read_wallet(|wallet_data| { let keychain = wallet_data.keychain().clone(); - for out in wallet_data.outputs().clone().values().filter(|x| { - x.root_key_id == wallet_data.keychain().root_key_id() && x.block.is_none() + let unspents = wallet_data.outputs().values().filter(|x| { + x.root_key_id == keychain.root_key_id() && x.block.is_none() && x.status == OutputStatus::Unspent - }) { + }); + for out in unspents { let commit = keychain.commit_with_key_index(out.value, out.n_child)?; wallet_outputs.insert(commit, out.key_id.clone()); } @@ -172,13 +178,14 @@ where } /// Apply refreshed API output data to the wallet -pub fn apply_api_outputs( +pub fn apply_api_outputs( wallet: &mut T, wallet_outputs: &HashMap, api_outputs: &HashMap, ) -> Result<(), Error> where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { // now for each commit, find the output in the wallet and the corresponding // api output (if it exists) and refresh it in-place in the wallet. @@ -198,9 +205,10 @@ where /// Builds a single api query to retrieve the latest output data from the node. /// So we can refresh the local wallet outputs. -fn refresh_output_state(wallet: &mut T, height: u64) -> Result<(), Error> +fn refresh_output_state(wallet: &mut T, height: u64) -> Result<(), Error> where - T: WalletBackend + WalletClient, + T: WalletBackend + WalletClient, + K: Keychain, { debug!(LOGGER, "Refreshing wallet outputs"); @@ -216,9 +224,10 @@ where Ok(()) } -fn clean_old_unconfirmed(wallet: &mut T, height: u64) -> Result<(), Error> +fn clean_old_unconfirmed(wallet: &mut T, height: u64) -> Result<(), Error> where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { if height < 500 { return Ok(()); @@ -231,10 +240,11 @@ where }) } -/// Retrieve summary info about the wallet -pub fn retrieve_info(wallet: &mut T) -> Result +/// Retrieve summar info about the wallet +pub fn retrieve_info(wallet: &mut T) -> Result where - T: WalletBackend + WalletClient, + T: WalletBackend + WalletClient, + K: Keychain, { let result = refresh_outputs(wallet); @@ -291,9 +301,10 @@ where } /// Build a coinbase output and insert into wallet -pub fn build_coinbase(wallet: &mut T, block_fees: &BlockFees) -> Result +pub fn build_coinbase(wallet: &mut T, block_fees: &BlockFees) -> Result where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { let (out, kern, block_fees) = receive_coinbase(wallet, block_fees).context(ErrorKind::Node)?; @@ -315,12 +326,13 @@ where //TODO: Split up the output creation and the wallet insertion /// Build a coinbase output and the corresponding kernel -pub fn receive_coinbase( +pub fn receive_coinbase( wallet: &mut T, block_fees: &BlockFees, ) -> Result<(Output, TxKernel, BlockFees), Error> where - T: WalletBackend, + T: WalletBackend, + K: Keychain, { let root_key_id = wallet.keychain().root_key_id(); @@ -365,7 +377,7 @@ where debug!(LOGGER, "receive_coinbase: {:?}", block_fees); let (out, kern) = reward::output( - &wallet.keychain(), + wallet.keychain(), &key_id, block_fees.fees, block_fees.height, diff --git a/wallet/src/libwallet/types.rs b/wallet/src/libwallet/types.rs index 413a12e0b..ce2e2e36e 100644 --- a/wallet/src/libwallet/types.rs +++ b/wallet/src/libwallet/types.rs @@ -36,7 +36,10 @@ use util::secp::pedersen; /// Wallets should implement this backend for their storage. All functions /// here expect that the wallet instance has instantiated itself or stored /// whatever credentials it needs -pub trait WalletBackend { +pub trait WalletBackend +where + K: Keychain, +{ /// Initialise with whatever stored credentials we have fn open_with_credentials(&mut self) -> Result<(), Error>; @@ -44,7 +47,7 @@ pub trait WalletBackend { fn close(&mut self) -> Result<(), Error>; /// Return the keychain being used - fn keychain(&mut self) -> &mut Keychain; + fn keychain(&mut self) -> &mut K; /// Return the outputs directly fn outputs(&mut self) -> &mut HashMap; diff --git a/wallet/tests/common/mod.rs b/wallet/tests/common/mod.rs index 3a0c3eb4f..07fcddbbb 100644 --- a/wallet/tests/common/mod.rs +++ b/wallet/tests/common/mod.rs @@ -27,6 +27,7 @@ use chain::Chain; use core::core::hash::Hashed; use core::core::{Output, OutputFeatures, OutputIdentifier, Transaction, TxKernel}; use core::{consensus, global, pow}; +use keychain::ExtKeychain; use wallet::file_wallet::*; use wallet::libwallet::internal::updater; use wallet::libwallet::types::*; @@ -37,10 +38,11 @@ use util::secp::pedersen; /// Mostly for testing, refreshes output state against a local chain instance /// instead of via an http API call -pub fn refresh_output_state_local( - wallet: &mut T, - chain: &chain::Chain, -) -> Result<(), Error> { +pub fn refresh_output_state_local(wallet: &mut T, chain: &chain::Chain) -> Result<(), Error> +where + T: WalletBackend, + K: keychain::Keychain, +{ let wallet_outputs = updater::map_wallet_outputs(wallet)?; let chain_outputs: Vec> = wallet_outputs .keys() @@ -66,10 +68,14 @@ pub fn refresh_output_state_local( /// (0:total, 1:amount_awaiting_confirmation, 2:confirmed but locked, /// 3:currently_spendable, 4:locked total) TODO: Should be a wallet lib /// function with nicer return values -pub fn get_wallet_balances( +pub fn get_wallet_balances( wallet: &mut T, height: u64, -) -> Result<(u64, u64, u64, u64, u64), Error> { +) -> Result<(u64, u64, u64, u64, u64), Error> +where + T: WalletBackend, + K: keychain::Keychain, +{ let ret_val = wallet.read_wallet(|wallet_data| { let mut unspent_total = 0; let mut unspent_but_locked_total = 0; @@ -151,11 +157,11 @@ pub fn add_block_with_reward(chain: &Chain, txs: Vec<&Transaction>, reward: (Out /// adds a reward output to a wallet, includes that reward in a block, mines /// the block and adds it to the chain, with option transactions included. /// Helpful for building up precise wallet balances for testing. -pub fn award_block_to_wallet( - chain: &Chain, - txs: Vec<&Transaction>, - wallet: &mut T, -) { +pub fn award_block_to_wallet(chain: &Chain, txs: Vec<&Transaction>, wallet: &mut T) +where + T: WalletBackend, + K: keychain::Keychain, +{ let prev = chain.head_header().unwrap(); let fee_amt = txs.iter().map(|tx| tx.fee()).sum(); let fees = BlockFees { @@ -188,14 +194,18 @@ pub fn award_block_to_wallet( } /// adds many block rewards to a wallet, no transactions -pub fn award_blocks_to_wallet(chain: &Chain, wallet: &mut T, num_rewards: usize) { +pub fn award_blocks_to_wallet(chain: &Chain, wallet: &mut T, num_rewards: usize) +where + T: WalletBackend, + K: keychain::Keychain, +{ for _ in 0..num_rewards { award_block_to_wallet(chain, vec![], wallet); } } /// Create a new wallet in a particular directory -pub fn create_wallet(dir: &str) -> FileWallet { +pub fn create_wallet(dir: &str) -> FileWallet { let mut wallet_config = WalletConfig::default(); wallet_config.data_file_dir = String::from(dir); wallet::WalletSeed::init_file(&wallet_config).expect("Failed to create wallet seed file."); diff --git a/wallet/tests/libwallet.rs b/wallet/tests/libwallet.rs index fcaaaa4ee..d03454d58 100644 --- a/wallet/tests/libwallet.rs +++ b/wallet/tests/libwallet.rs @@ -20,7 +20,7 @@ extern crate grin_wallet as wallet; extern crate rand; extern crate uuid; -use keychain::{BlindSum, BlindingFactor, Keychain}; +use keychain::{BlindSum, BlindingFactor, ExtKeychain, Keychain}; use util::secp::key::{PublicKey, SecretKey}; use util::secp::pedersen::ProofMessage; use util::{kernel_sig_msg, secp}; @@ -31,8 +31,8 @@ use rand::thread_rng; #[test] fn aggsig_sender_receiver_interaction() { - let sender_keychain = Keychain::from_random_seed().unwrap(); - let receiver_keychain = Keychain::from_random_seed().unwrap(); + let sender_keychain = ExtKeychain::from_random_seed().unwrap(); + let receiver_keychain = ExtKeychain::from_random_seed().unwrap(); // Calculate the kernel excess here for convenience. // Normally this would happen during transaction building. @@ -45,7 +45,7 @@ fn aggsig_sender_receiver_interaction() { .derived_key(&receiver_keychain.derive_key_id(1).unwrap()) .unwrap(); - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let blinding_factor = keychain .blind_sum(&BlindSum::new() .sub_blinding_factor(BlindingFactor::from_secret_key(skey1)) @@ -201,7 +201,7 @@ fn aggsig_sender_receiver_interaction() { // Check we can verify the sig using the kernel excess { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let msg = secp::Message::from_slice(&kernel_sig_msg(0, 0)).unwrap(); @@ -214,8 +214,8 @@ fn aggsig_sender_receiver_interaction() { #[test] fn aggsig_sender_receiver_interaction_offset() { - let sender_keychain = Keychain::from_random_seed().unwrap(); - let receiver_keychain = Keychain::from_random_seed().unwrap(); + let sender_keychain = ExtKeychain::from_random_seed().unwrap(); + let receiver_keychain = ExtKeychain::from_random_seed().unwrap(); // This is the kernel offset that we use to split the key // Summing these at the block level prevents the @@ -233,7 +233,7 @@ fn aggsig_sender_receiver_interaction_offset() { .derived_key(&receiver_keychain.derive_key_id(1).unwrap()) .unwrap(); - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let blinding_factor = keychain .blind_sum(&BlindSum::new() .sub_blinding_factor(BlindingFactor::from_secret_key(skey1)) @@ -393,7 +393,7 @@ fn aggsig_sender_receiver_interaction_offset() { // Check we can verify the sig using the kernel excess { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let msg = secp::Message::from_slice(&kernel_sig_msg(0, 0)).unwrap(); @@ -406,7 +406,7 @@ fn aggsig_sender_receiver_interaction_offset() { #[test] fn test_rewind_range_proof() { - let keychain = Keychain::from_random_seed().unwrap(); + let keychain = ExtKeychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let commit = keychain.commit(5, &key_id).unwrap(); let msg = ProofMessage::from_bytes(&[0u8; 64]); diff --git a/wallet/tests/transaction.rs b/wallet/tests/transaction.rs index 225cc55d6..222e9b109 100644 --- a/wallet/tests/transaction.rs +++ b/wallet/tests/transaction.rs @@ -132,7 +132,7 @@ fn build_transaction() { let _ = slate .fill_round_2( - &wallet1.keychain.as_ref().unwrap(), + wallet1.keychain.as_ref().unwrap(), &recp_context.sec_key, &recp_context.sec_nonce, 1, @@ -149,7 +149,7 @@ fn build_transaction() { // SENDER Part 3: Sender confirmation let _ = slate .fill_round_2( - &wallet1.keychain.as_ref().unwrap(), + wallet1.keychain.as_ref().unwrap(), &sender_context.sec_key, &sender_context.sec_nonce, 0, @@ -161,7 +161,7 @@ fn build_transaction() { debug!(LOGGER, "{:?}", slate); // Final transaction can be built by anyone at this stage - let res = slate.finalize(&wallet1.keychain.as_ref().unwrap()); + let res = slate.finalize(wallet1.keychain.as_ref().unwrap()); if let Err(e) = res { panic!("Error creating final tx: {:?}", e);