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
This commit is contained in:
Ignotus Peverell 2018-06-08 06:21:54 +01:00 committed by GitHub
parent a6590ea0ae
commit af178f82f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 840 additions and 678 deletions

View file

@ -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;

View file

@ -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(),

View file

@ -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<K>(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<K>(
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<K>(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<K>(
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<K>(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(),

View file

@ -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 =

View file

@ -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();

View file

@ -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();

View file

@ -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();

View file

@ -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);

View file

@ -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<K>(
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<K>(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()
}

View file

@ -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();

View file

@ -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();

View file

@ -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<secp::Error> for Error {
fn from(e: secp::Error) -> Error {
Error::Secp(e)
}
}
impl From<num::ParseIntError> 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serializer.serialize_str(&self.to_hex())
}
}
impl<'de> de::Deserialize<'de> for Identifier {
fn deserialize<D>(deserializer: D) -> Result<Identifier, D::Error>
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<E>(self, s: &str) -> Result<Self::Value, E>
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<Identifier, Error> {
let key_id = PublicKey::from_secret_key(secp, key)?;
Ok(Identifier::from_pubkey(secp, &key_id))
}
fn from_hex(hex: &str) -> Result<Identifier, Error> {
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<ExtendedKey, Error> {
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);

View file

@ -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<secp::Error> for Error {
fn from(e: secp::Error) -> Error {
Error::Secp(e)
}
}
impl From<extkey::Error> 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<Identifier, SecretKey>,
key_derivation_cache: Arc<RwLock<HashMap<Identifier, u32>>>,
}
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<Keychain, Error> {
impl Keychain for ExtKeychain {
fn from_seed(seed: &[u8]) -> Result<ExtKeychain, Error> {
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<Keychain, Error> {
fn from_random_seed() -> Result<ExtKeychain, Error> {
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<Identifier, Error> {
fn root_key_id(&self) -> Identifier {
self.extkey.root_key_id.clone()
}
fn derive_key_id(&self, derivation: u32) -> Result<Identifier, Error> {
let child_key = self.extkey.derive(&self.secp, derivation)?;
Ok(child_key.key_id)
}
pub fn derived_key(&self, key_id: &Identifier) -> Result<SecretKey, Error> {
fn derived_key(&self, key_id: &Identifier) -> Result<SecretKey, Error> {
// 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<Commitment, Error> {
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<Commitment, Error> {
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<BlindingFactor, Error> {
let mut pos_keys: Vec<SecretKey> = blind_sum
.positive_key_ids
.iter()
.filter_map(|k| self.derived_key(&k).ok())
.collect();
let mut neg_keys: Vec<SecretKey> = 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::<Vec<SecretKey>>());
neg_keys.extend(&blind_sum
.negative_blinding_factors
.iter()
.filter_map(|b| b.secret_key(&self.secp).ok())
.collect::<Vec<SecretKey>>());
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<Signature, Error> {
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<Signature, Error> {
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<extkey::ChildKey, Error> {
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<Commitment, Error> {
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<Commitment, Error> {
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<BlindingFactor, Error> {
let mut pos_keys: Vec<SecretKey> = blind_sum
.positive_key_ids
.iter()
.filter_map(|k| self.derived_key(&k).ok())
.collect();
let mut neg_keys: Vec<SecretKey> = 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::<Vec<SecretKey>>());
neg_keys.extend(&blind_sum
.negative_blinding_factors
.iter()
.filter_map(|b| b.secret_key(&self.secp).ok())
.collect::<Vec<SecretKey>>());
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<Signature, Error> {
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<Signature, Error> {
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,

View file

@ -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};

View file

@ -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<secp::Error> 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serializer.serialize_str(&self.to_hex())
}
}
impl<'de> de::Deserialize<'de> for Identifier {
fn deserialize<D>(deserializer: D) -> Result<Identifier, D::Error>
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<E>(self, s: &str) -> Result<Self::Value, E>
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<Identifier, Error> {
let key_id = PublicKey::from_secret_key(secp, key)?;
Ok(Identifier::from_pubkey(secp, &key_id))
}
pub fn from_hex(hex: &str) -> Result<Identifier, Error> {
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<SplitBlindingFactor, Error> {
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<Self, Error>;
fn from_random_seed() -> Result<Self, Error>;
fn root_key_id(&self) -> Identifier;
fn derive_key_id(&self, derivation: u32) -> Result<Identifier, Error>;
fn derived_key(&self, key_id: &Identifier) -> Result<SecretKey, Error>;
fn commit(&self, amount: u64, key_id: &Identifier) -> Result<Commitment, Error>;
fn commit_with_key_index(&self, amount: u64, derivation: u32) -> Result<Commitment, Error>;
fn blind_sum(&self, blind_sum: &BlindSum) -> Result<BlindingFactor, Error>;
fn sign(&self, msg: &Message, key_id: &Identifier) -> Result<Signature, Error>;
fn sign_with_blinding(&self, &Message, &BlindingFactor) -> Result<Signature, Error>;
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};

View file

@ -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;

View file

@ -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());

View file

@ -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());

View file

@ -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.

View file

@ -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<ChainAdapter>) -> TransactionPool<ChainAdapter> {
)
}
pub fn test_transaction_spending_coinbase(
keychain: &Keychain,
pub fn test_transaction_spending_coinbase<K>(
keychain: &K,
header: &BlockHeader,
output_values: Vec<u64>,
) -> Transaction {
) -> Transaction
where
K: Keychain,
{
let output_sum = output_values.iter().sum::<u64>() 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<K>(
keychain: &K,
input_values: Vec<u64>,
output_values: Vec<u64>,
) -> Transaction {
) -> Transaction
where
K: Keychain,
{
let input_sum = input_values.iter().sum::<u64>() as i64;
let output_sum = output_values.iter().sum::<u64>() 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 {

View file

@ -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());

View file

@ -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)

View file

@ -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<keychain::ExtKeychain> =
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;

View file

@ -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;

View file

@ -103,9 +103,9 @@ impl WalletSeed {
util::to_hex(self.0.to_vec())
}
pub fn derive_keychain(&self, password: &str) -> Result<keychain::Keychain, Error> {
pub fn derive_keychain<K: Keychain>(&self, password: &str) -> Result<K, Error> {
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<K> {
/// Keychain
pub keychain: Option<Keychain>,
pub keychain: Option<K>,
/// 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<K> WalletBackend<K> for FileWallet<K>
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<K> WalletClient for FileWallet<K> {
/// 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<K> FileWallet<K>
where
K: Keychain,
{
/// Create a new FileWallet instance
pub fn new(config: WalletConfig, passphrase: &str) -> Result<Self, Error> {
let mut retval = FileWallet {

View file

@ -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<K>(
secp: &Secp256k1,
k: &Keychain,
k: &K,
msg: &Message,
key_id: &Identifier,
) -> Result<Signature, Error> {
) -> Result<Signature, Error>
where
K: Keychain,
{
let skey = k.derived_key(key_id)?;
let sig = aggsig::sign_single(secp, &msg, &skey, None, None, None)?;
Ok(sig)

View file

@ -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<K: Keychain> = for<'a> Fn(&'a mut Context<K>, (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<K>(
value: u64,
features: OutputFeatures,
block_hash: Option<Hash>,
merkle_proof: Option<MerkleProof>,
key_id: Identifier,
) -> Box<Append> {
) -> Box<Append<K>>
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<Append> {
pub fn input<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
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<Append> {
/// 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<K>(
value: u64,
block_hash: Hash,
merkle_proof: MerkleProof,
key_id: Identifier,
) -> Box<Append> {
) -> Box<Append<K>>
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<Append> {
pub fn output<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
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<Append> {
}
/// Sets the fee on the transaction being built.
pub fn with_fee(fee: u64) -> Box<Append> {
pub fn with_fee<K>(fee: u64) -> Box<Append<K>>
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<Append> {
}
/// Sets the lock_height on the transaction being built.
pub fn with_lock_height(lock_height: u64) -> Box<Append> {
pub fn with_lock_height<K>(lock_height: u64) -> Box<Append<K>>
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<Append> {
/// 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<Append> {
pub fn with_excess<K>(excess: BlindingFactor) -> Box<Append<K>>
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<Append> {
}
/// Sets a known tx "offset". Used in final step of tx construction.
pub fn with_offset(offset: BlindingFactor) -> Box<Append> {
pub fn with_offset<K>(offset: BlindingFactor) -> Box<Append<K>>
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<Append> {
/// 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<Append> {
pub fn initial_tx<K>(mut tx: Transaction) -> Box<Append<K>>
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<Append> {
/// let (tx2, _) = build::transaction(vec![initial_tx(tx1), with_excess(sum),
/// output_rand(2)], keychain).unwrap();
///
pub fn partial_transaction(
elems: Vec<Box<Append>>,
keychain: &keychain::Keychain,
) -> Result<(Transaction, BlindingFactor), keychain::Error> {
pub fn partial_transaction<K>(
elems: Vec<Box<Append<K>>>,
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<Box<Append>>,
keychain: &keychain::Keychain,
) -> Result<Transaction, keychain::Error> {
pub fn transaction<K>(
elems: Vec<Box<Append<K>>>,
keychain: &K,
) -> Result<Transaction, keychain::Error>
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<Box<Append>>,
keychain: &keychain::Keychain,
) -> Result<Transaction, keychain::Error> {
pub fn transaction_with_offset<K>(
elems: Vec<Box<Append<K>>>,
keychain: &K,
) -> Result<Transaction, keychain::Error>
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();

View file

@ -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<keychain::Error> for Error {
}
}
impl From<extkey::Error> for Error {
fn from(error: extkey::Error) -> Error {
Error {
inner: Context::new(ErrorKind::ExtendedKey(error)),
}
}
}
impl From<transaction::Error> for Error {
fn from(error: transaction::Error) -> Error {
Error {

View file

@ -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<SecretKey, Error> {
fn create_nonce<K>(k: &K, commit: &Commitment) -> Result<SecretKey, Error>
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<SecretKey, Error> {
/// 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: &K,
amount: u64,
key_id: &Identifier,
_commit: Commitment,
extra_data: Option<Vec<u8>>,
msg: ProofMessage,
) -> Result<RangeProof, Error> {
) -> Result<RangeProof, Error>
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: &K,
key_id: &Identifier,
commit: Commitment,
extra_data: Option<Vec<u8>>,
proof: RangeProof,
) -> Result<ProofInfo, Error> {
) -> Result<ProofInfo, Error>
where
K: Keychain,
{
let skey = k.derived_key(key_id)?;
let nonce = create_nonce(k, &commit)?;
let proof_message = k.secp()

View file

@ -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<K>(
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);

View file

@ -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<K>(
&mut self,
keychain: &Keychain,
mut elems: Vec<Box<build::Append>>,
) -> Result<BlindingFactor, Error> {
keychain: &K,
mut elems: Vec<Box<build::Append<K>>>,
) -> Result<BlindingFactor, Error>
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<K>(
&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<K>(
&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<K>(&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<K>(
&mut self,
keychain: &Keychain,
keychain: &K,
sec_key: &SecretKey,
sec_nonce: &SecretKey,
id: usize,
part_sig: Option<Signature>,
) -> 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<K>(&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<Signature, Error> {
fn finalize_signature<K>(&mut self, keychain: &K) -> Result<Signature, Error>
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<K>(
&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()?;

View file

@ -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<K> + WalletClient,
K: Keychain,
{
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
/// perhaps)
pub wallet: &'a mut W,
phantom: PhantomData<K>,
}
impl<'a, W> APIOwner<'a, W>
impl<'a, W, K> APIOwner<'a, W, K>
where
W: 'a + WalletBackend + WalletClient,
W: 'a + WalletBackend<K> + 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<K> + WalletClient,
K: Keychain,
{
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
/// perhaps)
pub wallet: &'a mut W,
phantom: PhantomData<K>,
}
impl<'a, W> APIForeign<'a, W>
impl<'a, W, K> APIForeign<'a, W, K>
where
W: 'a + WalletBackend + WalletClient,
W: 'a + WalletBackend<K> + 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

View file

@ -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<F, T>(wallet: &mut T, f: F) -> Result<(), Error>
pub fn owner_single_use<F, T, K>(wallet: &mut T, f: F) -> Result<(), Error>
where
T: WalletBackend + WalletClient,
F: FnOnce(&mut APIOwner<T>) -> Result<(), Error>,
T: WalletBackend<K> + WalletClient,
F: FnOnce(&mut APIOwner<T, K>) -> 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<F, T>(wallet: &mut T, f: F) -> Result<(), Error>
pub fn foreign_single_use<F, T, K>(wallet: &mut T, f: F) -> Result<(), Error>
where
T: WalletBackend + WalletClient,
F: FnOnce(&mut APIForeign<T>) -> Result<(), Error>,
T: WalletBackend<K> + WalletClient,
F: FnOnce(&mut APIForeign<T, K>) -> 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<T>(wallet: T, addr: &str) -> Result<(), Error>
pub fn owner_listener<T, K>(wallet: T, addr: &str) -> Result<(), Error>
where
T: WalletBackend,
OwnerAPIHandler<T>: Handler,
T: WalletBackend<K> + WalletClient,
OwnerAPIHandler<T, K>: 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<T>(wallet: T, addr: &str) -> Result<(), Error>
pub fn foreign_listener<T, K>(wallet: T, addr: &str) -> Result<(), Error>
where
T: WalletBackend + WalletClient,
ForeignAPIHandler<T>: Handler,
T: WalletBackend<K> + WalletClient,
ForeignAPIHandler<T, K>: 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<T>
pub struct OwnerAPIHandler<T, K>
where
T: WalletBackend,
T: WalletBackend<K>,
K: Keychain,
{
/// Wallet instance
pub wallet: Arc<Mutex<T>>,
phantom: PhantomData<K>,
}
impl<T> OwnerAPIHandler<T>
impl<T, K> OwnerAPIHandler<T, K>
where
T: WalletBackend + WalletClient,
T: WalletBackend<K> + WalletClient,
K: Keychain,
{
pub fn new(wallet: Arc<Mutex<T>>) -> OwnerAPIHandler<T, K> {
OwnerAPIHandler {
wallet,
phantom: PhantomData,
}
}
fn retrieve_outputs(
&self,
req: &mut Request,
api: &mut APIOwner<T>,
api: &mut APIOwner<T, K>,
) -> Result<Vec<OutputData>, 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<T>,
api: &mut APIOwner<T, K>,
) -> Result<WalletInfo, Error> {
let res = api.retrieve_summary_info()?;
Ok(res.1)
}
fn issue_send_tx(&self, req: &mut Request, api: &mut APIOwner<T>) -> Result<(), Error> {
fn issue_send_tx(&self, req: &mut Request, api: &mut APIOwner<T, K>) -> Result<(), Error> {
// TODO: Args
api.issue_send_tx(60, 10, "", 1000, true, true)
}
fn issue_burn_tx(&self, req: &mut Request, api: &mut APIOwner<T>) -> Result<(), Error> {
fn issue_burn_tx(&self, req: &mut Request, api: &mut APIOwner<T, K>) -> Result<(), Error> {
// TODO: Args
api.issue_burn_tx(60, 10, 1000)
}
fn handle_request(&self, req: &mut Request, api: &mut APIOwner<T>) -> IronResult<Response> {
fn handle_request(&self, req: &mut Request, api: &mut APIOwner<T, K>) -> IronResult<Response> {
let url = req.url.clone();
let path_elems = url.path();
match *path_elems.last().unwrap() {
@ -175,9 +187,10 @@ where
}
}
impl<T> Handler for OwnerAPIHandler<T>
impl<T, K> Handler for OwnerAPIHandler<T, K>
where
T: WalletBackend + WalletClient + Send + Sync + 'static,
T: WalletBackend<K> + WalletClient + Send + Sync + 'static,
K: Keychain + 'static,
{
fn handle(&self, req: &mut Request) -> IronResult<Response> {
// every request should open with stored credentials,
@ -199,19 +212,33 @@ where
/// API Handler/Wrapper for foreign functions
pub struct ForeignAPIHandler<T>
pub struct ForeignAPIHandler<T, K>
where
T: WalletBackend + WalletClient,
T: WalletBackend<K> + WalletClient,
K: Keychain,
{
/// Wallet instance
pub wallet: Arc<Mutex<T>>,
phantom: PhantomData<K>,
}
impl<T> ForeignAPIHandler<T>
impl<T, K> ForeignAPIHandler<T, K>
where
T: WalletBackend + WalletClient,
T: WalletBackend<K> + WalletClient,
K: Keychain,
{
fn build_coinbase(&self, req: &mut Request, api: &mut APIForeign<T>) -> Result<CbData, Error> {
pub fn new(wallet: Arc<Mutex<T>>) -> ForeignAPIHandler<T, K> {
ForeignAPIHandler {
wallet,
phantom: PhantomData,
}
}
fn build_coinbase(
&self,
req: &mut Request,
api: &mut APIForeign<T, K>,
) -> Result<CbData, Error> {
let struct_body = req.get::<bodyparser::Struct<BlockFees>>();
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<T>) -> Result<Slate, Error> {
fn receive_tx(&self, req: &mut Request, api: &mut APIForeign<T, K>) -> Result<Slate, Error> {
let struct_body = req.get::<bodyparser::Struct<Slate>>();
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<T>) -> IronResult<Response> {
fn handle_request(
&self,
req: &mut Request,
api: &mut APIForeign<T, K>,
) -> IronResult<Response> {
let url = req.url.clone();
let path_elems = url.path();
match *path_elems.last().unwrap() {
@ -256,9 +287,10 @@ where
}
}
impl<T> Handler for ForeignAPIHandler<T>
impl<T, K> Handler for ForeignAPIHandler<T, K>
where
T: WalletBackend + WalletClient + Send + Sync + 'static,
T: WalletBackend<K> + WalletClient + Send + Sync + 'static,
K: Keychain + 'static,
{
fn handle(&self, req: &mut Request) -> IronResult<Response> {
// every request should open with stored credentials,

View file

@ -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<T>(wallet: &mut T) -> Result<(Identifier, u32), Error>
pub fn new_output_key<T, K>(wallet: &mut T) -> Result<(Identifier, u32), Error>
where
T: WalletBackend,
T: WalletBackend<K>,
K: Keychain,
{
wallet.with_wallet(|wallet_data| next_available_key(wallet_data))
}
/// Get next available key in the wallet
pub fn next_available_key<T>(wallet: &mut T) -> (Identifier, u32)
pub fn next_available_key<T, K>(wallet: &mut T) -> (Identifier, u32)
where
T: WalletBackend,
T: WalletBackend<K>,
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<T>(wallet: &T, key_id: Identifier) -> (Identifier, u32)
pub fn retrieve_existing_key<T, K>(wallet: &T, key_id: Identifier) -> (Identifier, u32)
where
T: WalletBackend,
T: WalletBackend<K>,
K: Keychain,
{
if let Some(existing) = wallet.get_output(&key_id) {
let key_id = existing.key_id.clone();

View file

@ -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<T>(wallet: &T, start_height: u64, max: u64) -> Result<api::OutputListing, Error>
fn outputs_batch<T, K>(wallet: &T, start_height: u64, max: u64) -> Result<api::OutputListing, Error>
where
T: WalletBackend + WalletClient,
T: WalletBackend<K> + 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<T: WalletBackend + WalletClient>(
fn find_outputs_with_key<T, K>(
wallet: &mut T,
outputs: Vec<api::OutputPrintable>,
found_key_index: &mut Vec<u32>,
@ -88,7 +89,11 @@ fn find_outputs_with_key<T: WalletBackend + WalletClient>(
u64,
bool,
Option<MerkleProofWrapper>,
)> {
)>
where
T: WalletBackend<K> + WalletClient,
K: Keychain,
{
let mut wallet_outputs: Vec<(
pedersen::Commitment,
Identifier,
@ -225,7 +230,11 @@ fn find_outputs_with_key<T: WalletBackend + WalletClient>(
}
/// Restore a wallet
pub fn restore<T: WalletBackend + WalletClient>(wallet: &mut T) -> Result<(), Error> {
pub fn restore<T, K>(wallet: &mut T) -> Result<(), Error>
where
T: WalletBackend<K> + 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))

View file

@ -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<T>(
pub fn build_send_tx_slate<T, K>(
wallet: &mut T,
num_participants: usize,
amount: u64,
@ -43,7 +43,8 @@ pub fn build_send_tx_slate<T>(
Error,
>
where
T: WalletBackend,
T: WalletBackend<K>,
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<T>(
pub fn build_recipient_output_with_slate<T, K>(
wallet: &mut T,
slate: &mut Slate,
) -> Result<
@ -119,25 +120,27 @@ pub fn build_recipient_output_with_slate<T>(
Error,
>
where
T: WalletBackend,
T: WalletBackend<K>,
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<T>(
pub fn select_send_tx<T, K>(
wallet: &mut T,
amount: u64,
current_height: u64,
@ -176,7 +179,7 @@ pub fn select_send_tx<T>(
selection_strategy_is_use_all: bool,
) -> Result<
(
Vec<Box<build::Append>>,
Vec<Box<build::Append<K>>>,
Vec<OutputData>,
Option<Identifier>,
u64, // amount
@ -185,9 +188,10 @@ pub fn select_send_tx<T>(
Error,
>
where
T: WalletBackend,
T: WalletBackend<K>,
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<OutputData>) -> usize {
}
/// Selects inputs and change for a transaction
pub fn inputs_and_change<T>(
pub fn inputs_and_change<T, K>(
coins: &Vec<OutputData>,
wallet: &mut T,
height: u64,
amount: u64,
fee: u64,
) -> Result<(Vec<Box<build::Append>>, Option<Identifier>), Error>
) -> Result<(Vec<Box<build::Append<K>>>, Option<Identifier>), Error>
where
T: WalletBackend,
T: WalletBackend<K>,
K: Keychain,
{
let mut parts = vec![];

View file

@ -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};

View file

@ -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<T: WalletBackend>(wallet: &mut T, slate: &mut Slate) -> Result<(), Error> {
pub fn receive_tx<T, K>(wallet: &mut T, slate: &mut Slate) -> Result<(), Error>
where
T: WalletBackend<K>,
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<T: WalletBackend>(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<T: WalletBackend + WalletClient>(
pub fn create_send_tx<T, K>(
wallet: &mut T,
amount: u64,
minimum_confirmations: u64,
@ -62,7 +66,11 @@ pub fn create_send_tx<T: WalletBackend + WalletClient>(
impl FnOnce(&mut T) -> Result<(), Error>,
),
Error,
> {
>
where
T: WalletBackend<K> + 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<T: WalletBackend + WalletClient>(
}
/// Complete a transaction as the sender
pub fn complete_tx<T: WalletBackend>(
pub fn complete_tx<T, K>(
wallet: &mut T,
slate: &mut Slate,
context: &sigcontext::Context,
) -> Result<(), Error> {
) -> Result<(), Error>
where
T: WalletBackend<K>,
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<T: WalletBackend>(
}
/// Issue a burn tx
pub fn issue_burn_tx<T: WalletBackend + WalletClient>(
pub fn issue_burn_tx<T, K>(
wallet: &mut T,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
) -> Result<Transaction, Error> {
let keychain = &Keychain::burn_enabled(wallet.keychain(), &Identifier::zero());
) -> Result<Transaction, Error>
where
T: WalletBackend<K> + 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<T: WalletBackend + WalletClient>(
#[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();

View file

@ -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<T: WalletBackend>(
wallet: &mut T,
show_spent: bool,
) -> Result<Vec<OutputData>, Error> {
pub fn retrieve_outputs<T, K>(wallet: &mut T, show_spent: bool) -> Result<Vec<OutputData>, Error>
where
T: WalletBackend<K>,
K: Keychain,
{
let root_key_id = wallet.keychain().clone().root_key_id();
let mut outputs = vec![];
@ -66,9 +67,10 @@ pub fn retrieve_outputs<T: WalletBackend>(
/// Refreshes the outputs in a wallet with the latest information
/// from a node
pub fn refresh_outputs<T>(wallet: &mut T) -> Result<(), Error>
pub fn refresh_outputs<T, K>(wallet: &mut T) -> Result<(), Error>
where
T: WalletBackend + WalletClient,
T: WalletBackend<K> + 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<T>(wallet: &mut T, height: u64) -> Result<(), Error>
fn refresh_missing_block_hashes<T, K>(wallet: &mut T, height: u64) -> Result<(), Error>
where
T: WalletBackend + WalletClient,
T: WalletBackend<K> + 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<T>(
pub fn map_wallet_outputs<T, K>(
wallet: &mut T,
) -> Result<HashMap<pedersen::Commitment, Identifier>, Error>
where
T: WalletBackend,
T: WalletBackend<K>,
K: Keychain,
{
let mut wallet_outputs: HashMap<pedersen::Commitment, Identifier> = 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<T>(
pub fn map_wallet_outputs_missing_block<T, K>(
wallet: &mut T,
) -> Result<HashMap<pedersen::Commitment, Identifier>, Error>
where
T: WalletBackend,
T: WalletBackend<K>,
K: Keychain,
{
let mut wallet_outputs: HashMap<pedersen::Commitment, Identifier> = 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<T>(
pub fn apply_api_outputs<T, K>(
wallet: &mut T,
wallet_outputs: &HashMap<pedersen::Commitment, Identifier>,
api_outputs: &HashMap<pedersen::Commitment, String>,
) -> Result<(), Error>
where
T: WalletBackend,
T: WalletBackend<K>,
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<T>(wallet: &mut T, height: u64) -> Result<(), Error>
fn refresh_output_state<T, K>(wallet: &mut T, height: u64) -> Result<(), Error>
where
T: WalletBackend + WalletClient,
T: WalletBackend<K> + WalletClient,
K: Keychain,
{
debug!(LOGGER, "Refreshing wallet outputs");
@ -216,9 +224,10 @@ where
Ok(())
}
fn clean_old_unconfirmed<T>(wallet: &mut T, height: u64) -> Result<(), Error>
fn clean_old_unconfirmed<T, K>(wallet: &mut T, height: u64) -> Result<(), Error>
where
T: WalletBackend,
T: WalletBackend<K>,
K: Keychain,
{
if height < 500 {
return Ok(());
@ -231,10 +240,11 @@ where
})
}
/// Retrieve summary info about the wallet
pub fn retrieve_info<T>(wallet: &mut T) -> Result<WalletInfo, Error>
/// Retrieve summar info about the wallet
pub fn retrieve_info<T, K>(wallet: &mut T) -> Result<WalletInfo, Error>
where
T: WalletBackend + WalletClient,
T: WalletBackend<K> + 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<T>(wallet: &mut T, block_fees: &BlockFees) -> Result<CbData, Error>
pub fn build_coinbase<T, K>(wallet: &mut T, block_fees: &BlockFees) -> Result<CbData, Error>
where
T: WalletBackend,
T: WalletBackend<K>,
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<T>(
pub fn receive_coinbase<T, K>(
wallet: &mut T,
block_fees: &BlockFees,
) -> Result<(Output, TxKernel, BlockFees), Error>
where
T: WalletBackend,
T: WalletBackend<K>,
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,

View file

@ -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<K>
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<String, OutputData>;

View file

@ -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<T: WalletBackend>(
wallet: &mut T,
chain: &chain::Chain,
) -> Result<(), Error> {
pub fn refresh_output_state_local<T, K>(wallet: &mut T, chain: &chain::Chain) -> Result<(), Error>
where
T: WalletBackend<K>,
K: keychain::Keychain,
{
let wallet_outputs = updater::map_wallet_outputs(wallet)?;
let chain_outputs: Vec<Option<api::Output>> = wallet_outputs
.keys()
@ -66,10 +68,14 @@ pub fn refresh_output_state_local<T: WalletBackend>(
/// (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<T: WalletBackend>(
pub fn get_wallet_balances<T, K>(
wallet: &mut T,
height: u64,
) -> Result<(u64, u64, u64, u64, u64), Error> {
) -> Result<(u64, u64, u64, u64, u64), Error>
where
T: WalletBackend<K>,
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<T: WalletBackend>(
chain: &Chain,
txs: Vec<&Transaction>,
wallet: &mut T,
) {
pub fn award_block_to_wallet<T, K>(chain: &Chain, txs: Vec<&Transaction>, wallet: &mut T)
where
T: WalletBackend<K>,
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<T: WalletBackend>(
}
/// adds many block rewards to a wallet, no transactions
pub fn award_blocks_to_wallet<T: WalletBackend>(chain: &Chain, wallet: &mut T, num_rewards: usize) {
pub fn award_blocks_to_wallet<T, K>(chain: &Chain, wallet: &mut T, num_rewards: usize)
where
T: WalletBackend<K>,
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<ExtKeychain> {
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.");

View file

@ -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]);

View file

@ -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);