diff --git a/Cargo.lock b/Cargo.lock index ea4d2a453..5edf60356 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -805,7 +805,6 @@ dependencies = [ "grin_util 0.2.0", "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "prettytable-rs 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "router 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/servers/tests/framework/mod.rs b/servers/tests/framework/mod.rs index 8efd8a2da..7fad52ee0 100644 --- a/servers/tests/framework/mod.rs +++ b/servers/tests/framework/mod.rs @@ -23,11 +23,11 @@ extern crate grin_wallet as wallet; extern crate blake2_rfc as blake2; -use std::thread; -use std::time; use std::default::Default; use std::fs; use std::sync::{Arc, Mutex}; +use std::thread; +use std::time; use wallet::WalletConfig; diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index ef7635015..8bd8a916a 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -14,7 +14,6 @@ failure_derive = "0.1" futures = "0.1" hyper = "0.11" iron = "0.5" -lazy_static = "0.2" prettytable-rs = "0.6" rand = "0.3" router = "0.5" diff --git a/wallet/src/checker.rs b/wallet/src/checker.rs index 1fac0332d..9120f22a8 100644 --- a/wallet/src/checker.rs +++ b/wallet/src/checker.rs @@ -226,6 +226,9 @@ fn refresh_output_state( } fn clean_old_unconfirmed(config: &WalletConfig, tip: &api::Tip) -> Result<(), Error> { + if tip.height < 500 { + return Ok(()); + } WalletData::with_wallet(&config.data_file_dir, |wallet_data| { wallet_data.outputs.retain(|_, ref mut out| { !(out.status == OutputStatus::Unconfirmed && out.height > 0 diff --git a/wallet/src/grinwallet/keys.rs b/wallet/src/grinwallet/keys.rs index d20c1cb03..b77c97ce7 100644 --- a/wallet/src/grinwallet/keys.rs +++ b/wallet/src/grinwallet/keys.rs @@ -13,17 +13,8 @@ // limitations under the License. //! Grin Wallet specific key management functions -use rand::thread_rng; -use uuid::Uuid; - -use core::core::{amount_to_hr_string, Committed, Transaction}; -use failure::ResultExt; -use keychain::{BlindSum, BlindingFactor, Identifier, Keychain}; -use libwallet::{aggsig, build}; +use keychain::{Identifier, Keychain}; use types::*; -use util::secp::Signature; -use util::secp::key::{PublicKey, SecretKey}; -use util::{secp, LOGGER}; /// Get our next available key pub fn new_output_key( diff --git a/wallet/src/grinwallet/mod.rs b/wallet/src/grinwallet/mod.rs index 06c764d13..4371e24dc 100644 --- a/wallet/src/grinwallet/mod.rs +++ b/wallet/src/grinwallet/mod.rs @@ -25,5 +25,6 @@ #![deny(unused_mut)] #![warn(missing_docs)] -pub mod selection; pub mod keys; +pub mod selection; +pub mod sigcontext; diff --git a/wallet/src/grinwallet/selection.rs b/wallet/src/grinwallet/selection.rs index 9e2f92935..ba26db944 100644 --- a/wallet/src/grinwallet/selection.rs +++ b/wallet/src/grinwallet/selection.rs @@ -15,9 +15,9 @@ //! Selection of inputs for building transactions use failure::ResultExt; -use grinwallet::keys; +use grinwallet::{keys, sigcontext}; use keychain::{Identifier, Keychain}; -use libwallet::{aggsig, build, transaction}; +use libwallet::{build, transaction}; use types::*; /// Initialise a transaction on the sender side, returns a corresponding @@ -28,7 +28,6 @@ use types::*; pub fn build_send_tx_slate( config: &WalletConfig, keychain: &Keychain, - context_manager: &mut aggsig::ContextManager, num_participants: usize, amount: u64, current_height: u64, @@ -36,7 +35,14 @@ pub fn build_send_tx_slate( lock_height: u64, max_outputs: usize, selection_strategy_is_use_all: bool, -) -> Result<(transaction::Slate, impl FnOnce() -> Result<(), Error>), Error> { +) -> Result< + ( + transaction::Slate, + sigcontext::Context, + impl FnOnce() -> Result<(), Error>, + ), + Error, +> { let (elems, inputs, change_id, amount, fee) = select_send_tx( config, keychain, @@ -55,11 +61,12 @@ pub fn build_send_tx_slate( slate.lock_height = lock_height; slate.fee = fee; - let blinding = slate.add_transaction_elements(keychain, elems)?; + let blinding = slate + .add_transaction_elements(keychain, elems) + .context(ErrorKind::LibWalletError)?; // Create our own private context - let mut context = context_manager.create_context( + let mut context = sigcontext::Context::new( keychain.secp(), - &slate.id, blinding.secret_key(keychain.secp()).unwrap(), ); @@ -93,9 +100,7 @@ pub fn build_send_tx_slate( }*/ }) }; - context_manager.save_context(context); - - Ok((slate, update_sender_wallet_fn)) + Ok((slate, context, update_sender_wallet_fn)) } /// Creates a new output in the wallet for the recipient, @@ -105,9 +110,15 @@ pub fn build_send_tx_slate( pub fn build_recipient_output_with_slate( config: &WalletConfig, keychain: &Keychain, - context_manager: &mut aggsig::ContextManager, slate: &mut transaction::Slate, -) -> Result<(Identifier, impl FnOnce() -> Result<(), Error>), Error> { +) -> Result< + ( + Identifier, + sigcontext::Context, + impl FnOnce() -> Result<(), Error>, + ), + Error, +> { // Create a potential output for this transaction let (key_id, derivation) = keys::new_output_key(config, keychain)?; @@ -115,14 +126,15 @@ pub fn build_recipient_output_with_slate( let root_key_id = keychain.root_key_id(); let key_id_inner = key_id.clone(); let amount = slate.amount; + let height = slate.height; - let blinding = - slate.add_transaction_elements(keychain, vec![build::output(amount, key_id.clone())])?; + let blinding = slate + .add_transaction_elements(keychain, vec![build::output(amount, key_id.clone())]) + .context(ErrorKind::LibWalletError)?; // Add blinding sum to our context - let mut context = context_manager.create_context( + let mut context = sigcontext::Context::new( keychain.secp(), - &slate.id, blinding.secret_key(keychain.secp()).unwrap(), ); @@ -138,7 +150,7 @@ pub fn build_recipient_output_with_slate( n_child: derivation, value: amount, status: OutputStatus::Unconfirmed, - height: 0, + height: height, lock_height: 0, is_coinbase: false, block: None, @@ -146,8 +158,7 @@ pub fn build_recipient_output_with_slate( }); }) }; - context_manager.save_context(context); - Ok((key_id, wallet_add_fn)) + Ok((key_id, context, wallet_add_fn)) } /// Builds a transaction to send to someone from the HD seed associated with the @@ -203,7 +214,7 @@ pub fn select_send_tx( // sender let mut fee; // First attempt to spend without change - fee = tx_fee(coins.len(), 1, coins_proof_count(&coins), None); + fee = transaction::tx_fee(coins.len(), 1, coins_proof_count(&coins), None); let mut total: u64 = coins.iter().map(|c| c.value).sum(); let mut amount_with_fee = amount + fee; @@ -213,7 +224,7 @@ pub fn select_send_tx( // Check if we need to use a change address if total > amount_with_fee { - fee = tx_fee(coins.len(), 2, coins_proof_count(&coins), None); + fee = transaction::tx_fee(coins.len(), 2, coins_proof_count(&coins), None); amount_with_fee = amount + fee; // Here check if we have enough outputs for the amount including fee otherwise @@ -235,14 +246,15 @@ pub fn select_send_tx( selection_strategy_is_use_all, )) })?; - fee = tx_fee(coins.len(), 2, coins_proof_count(&coins), None); + fee = transaction::tx_fee(coins.len(), 2, coins_proof_count(&coins), None); total = coins.iter().map(|c| c.value).sum(); amount_with_fee = amount + fee; } } // build transaction skeleton with inputs and change - let (mut parts, change_key) = inputs_and_change(&coins, config, keychain, amount, fee)?; + let (mut parts, change_key) = + inputs_and_change(&coins, config, keychain, current_height, amount, fee)?; // This is more proof of concept than anything but here we set lock_height // on tx being sent (based on current chain height via api). @@ -261,6 +273,7 @@ pub fn inputs_and_change( coins: &Vec<OutputData>, config: &WalletConfig, keychain: &Keychain, + height: u64, amount: u64, fee: u64, ) -> Result<(Vec<Box<build::Append>>, Option<Identifier>), Error> { @@ -310,7 +323,7 @@ pub fn inputs_and_change( n_child: change_derivation, value: change as u64, status: OutputStatus::Unconfirmed, - height: 0, + height: height, lock_height: 0, is_coinbase: false, block: None, diff --git a/wallet/src/grinwallet/sigcontext.rs b/wallet/src/grinwallet/sigcontext.rs new file mode 100644 index 000000000..d49fab7a6 --- /dev/null +++ b/wallet/src/grinwallet/sigcontext.rs @@ -0,0 +1,84 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +//! Signature context holder helper (may be removed or replaced eventually) +use keychain::extkey::Identifier; +use libwallet::aggsig; +use util::secp::key::{PublicKey, SecretKey}; +use util::secp::{self, Secp256k1}; + +#[derive(Clone, Debug)] +/// Holds the context for a single aggsig transaction +pub struct Context { + /// Secret key (of which public is shared) + pub sec_key: SecretKey, + /// Secret nonce (of which public is shared) + /// (basically a SecretKey) + pub sec_nonce: SecretKey, + /// store my outputs between invocations + pub output_ids: Vec<Identifier>, + /// store my inputs + pub input_ids: Vec<Identifier>, + /// store the calculated fee + pub fee: u64, +} + +impl Context { + /// Create a new context with defaults + pub fn new(secp: &secp::Secp256k1, sec_key: SecretKey) -> Context { + Context { + sec_key: sec_key, + sec_nonce: aggsig::create_secnonce(secp).unwrap(), + input_ids: vec![], + output_ids: vec![], + fee: 0, + } + } +} + +impl Context { + /// Tracks an output contributing to my excess value (if it needs to + /// be kept between invocations + pub fn add_output(&mut self, output_id: &Identifier) { + self.output_ids.push(output_id.clone()); + } + + /// Returns all stored outputs + pub fn get_outputs(&self) -> Vec<Identifier> { + self.output_ids.clone() + } + + /// Tracks IDs of my inputs into the transaction + /// be kept between invocations + pub fn add_input(&mut self, input_id: &Identifier) { + self.input_ids.push(input_id.clone()); + } + + /// Returns all stored input identifiers + pub fn get_inputs(&self) -> Vec<Identifier> { + self.input_ids.clone() + } + + /// Returns private key, private nonce + pub fn get_private_keys(&self) -> (SecretKey, SecretKey) { + (self.sec_key.clone(), self.sec_nonce.clone()) + } + + /// Returns public key, public nonce + pub fn get_public_keys(&self, secp: &Secp256k1) -> (PublicKey, PublicKey) { + ( + PublicKey::from_secret_key(secp, &self.sec_key).unwrap(), + PublicKey::from_secret_key(secp, &self.sec_nonce).unwrap(), + ) + } +} diff --git a/wallet/src/info.rs b/wallet/src/info.rs index 190d576b3..b3de38ee5 100644 --- a/wallet/src/info.rs +++ b/wallet/src/info.rs @@ -13,10 +13,10 @@ // limitations under the License. use checker; -use keychain::Keychain; use core::core::amount_to_hr_string; -use types::{OutputStatus, WalletConfig, WalletData, WalletInfo}; +use keychain::Keychain; use prettytable; +use types::{OutputStatus, WalletConfig, WalletData, WalletInfo}; pub fn show_info(config: &WalletConfig, keychain: &Keychain) { let wallet_info = retrieve_info(config, keychain); diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 432c3d478..35d0c1fda 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -41,31 +41,28 @@ extern crate router; extern crate tokio_core; extern crate tokio_retry; -#[macro_use] -extern crate lazy_static; - extern crate grin_api as api; extern crate grin_core as core; extern crate grin_keychain as keychain; extern crate grin_util as util; pub mod checker; -mod handlers; -mod outputs; -mod info; -pub mod receiver; -mod sender; -pub mod types; -mod restore; pub mod client; -pub mod server; -pub mod libwallet; pub mod grinwallet; +mod handlers; +mod info; +pub mod libwallet; +mod outputs; +pub mod receiver; +mod restore; +mod sender; +pub mod server; +pub mod types; -pub use outputs::show_outputs; pub use info::{retrieve_info, show_info}; +pub use outputs::show_outputs; pub use receiver::WalletReceiver; +pub use restore::restore; pub use sender::{issue_burn_tx, issue_send_tx}; pub use types::{BlockFees, CbData, Error, ErrorKind, WalletConfig, WalletInfo, WalletReceiveRequest, WalletSeed}; -pub use restore::restore; diff --git a/wallet/src/libwallet/aggsig.rs b/wallet/src/libwallet/aggsig.rs index f5353b5d6..cbea06d8e 100644 --- a/wallet/src/libwallet/aggsig.rs +++ b/wallet/src/libwallet/aggsig.rs @@ -11,9 +11,8 @@ // 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. -/// Aggsig library definitions -use std::collections::HashMap; - +//! 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; @@ -22,253 +21,38 @@ use util::kernel_sig_msg; use util::secp::key::{PublicKey, SecretKey}; use util::secp::pedersen::Commitment; use util::secp::{self, aggsig, Message, Secp256k1, Signature}; -use uuid::Uuid; -#[derive(Clone, Debug)] -/// Holds the context for a single aggsig transaction -pub struct Context { - /// Transaction ID - pub transaction_id: Uuid, - /// Secret key (of which public is shared) - pub sec_key: SecretKey, - /// Secret nonce (of which public is shared) - /// (basically a SecretKey) - pub sec_nonce: SecretKey, - /// If I'm the sender, store change key - /// TODO: remove in favor of outputs below - pub change_key: Option<Identifier>, - /// store my outputs between invocations - pub output_ids: Vec<Identifier>, - /// store my inputs - pub input_ids: Vec<Identifier>, - /// store the calculated fee - pub fee: u64, +/// exports a secure nonce guaranteed to be usable +/// in aggsig creation +pub fn create_secnonce(secp: &Secp256k1) -> Result<SecretKey, Error> { + let nonce = aggsig::export_secnonce_single(secp)?; + Ok(nonce) } -/*impl Context { - /// Create a new context with defaults - pub fn new( - secp: &secp::Secp256k1, - sec_key: SecretKey, - ) -> Context { - Context { - sec_key: sec_key, - sec_nonce: aggsig::export_secnonce_single(secp).unwrap(), - change_key: None, - input_ids: vec![], - output_ids: vec![], - fee: 0, - }, -}*/ +/// Calculate a partial sig +pub fn calculate_partial_sig( + secp: &Secp256k1, + sec_key: &SecretKey, + sec_nonce: &SecretKey, + nonce_sum: &PublicKey, + fee: u64, + lock_height: u64, +) -> Result<Signature, Error> { + // Add public nonces kR*G + kS*G + let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?; -#[derive(Clone, Debug)] -/// Holds many contexts, to support multiple transactions hitting a wallet -/// receiver at once -/// TODO: Remove context manager in favour of context.. keeping multiple -/// transactions separate is a wallet-specific concern -pub struct ContextManager { - contexts: HashMap<Uuid, Context>, + //Now calculate signature using message M=fee, nonce in e=nonce_sum + let sig = aggsig::sign_single( + secp, + &msg, + sec_key, + Some(sec_nonce), + Some(nonce_sum), + Some(nonce_sum), + )?; + Ok(sig) } -impl ContextManager { - /// Create - pub fn new() -> ContextManager { - ContextManager { - contexts: HashMap::new(), - } - } - - /// Creates a context for a transaction id if required - /// otherwise does nothing - pub fn create_context( - &mut self, - secp: &secp::Secp256k1, - transaction_id: &Uuid, - sec_key: SecretKey, - ) -> Context { - if !self.contexts.contains_key(transaction_id) { - self.contexts.insert( - transaction_id.clone(), - Context { - sec_key: sec_key, - transaction_id: transaction_id.clone(), - sec_nonce: aggsig::export_secnonce_single(secp).unwrap(), - change_key: None, - input_ids: vec![], - output_ids: vec![], - fee: 0, - }, - ); - } - self.get_context(transaction_id) - } - - /// Retrieve a context by transaction id - pub fn get_context(&self, transaction_id: &Uuid) -> Context { - self.contexts.get(&transaction_id).unwrap().clone() - } - - /// Save context - pub fn save_context(&mut self, c: Context) { - self.contexts.insert(c.transaction_id.clone(), c); - } -} - -impl Context { - /// Tracks an output contributing to my excess value (if it needs to - /// be kept between invocations - pub fn add_output(&mut self, output_id: &Identifier) { - self.output_ids.push(output_id.clone()); - } - - /// Returns all stored outputs - pub fn get_outputs(&self) -> Vec<Identifier> { - self.output_ids.clone() - } - - /// Tracks IDs of my inputs into the transaction - /// be kept between invocations - pub fn add_input(&mut self, input_id: &Identifier) { - self.input_ids.push(input_id.clone()); - } - - /// Returns all stored input identifiers - pub fn get_inputs(&self) -> Vec<Identifier> { - self.input_ids.clone() - } - - /// Returns private key, private nonce - pub fn get_private_keys(&self) -> (SecretKey, SecretKey) { - (self.sec_key.clone(), self.sec_nonce.clone()) - } - - /// Returns public key, public nonce - pub fn get_public_keys(&self, secp: &Secp256k1) -> (PublicKey, PublicKey) { - ( - PublicKey::from_secret_key(secp, &self.sec_key).unwrap(), - PublicKey::from_secret_key(secp, &self.sec_nonce).unwrap(), - ) - } - - /// Note 'secnonce' here is used to perform the signature, while 'pubnonce' - /// just allows you to provide a custom public nonce to include while - /// calculating e nonce_sum is the sum used to decide whether secnonce - /// should be inverted during sig time - pub fn sign_single( - &self, - secp: &Secp256k1, - msg: &Message, - secnonce: Option<&SecretKey>, - pubnonce: Option<&PublicKey>, - nonce_sum: Option<&PublicKey>, - ) -> Result<Signature, Error> { - let sig = aggsig::sign_single(secp, msg, &self.sec_key, secnonce, pubnonce, nonce_sum)?; - Ok(sig) - } - - //Verifies other final sig corresponds with what we're expecting - pub fn verify_final_sig_build_msg( - &self, - secp: &Secp256k1, - sig: &Signature, - pubkey: &PublicKey, - fee: u64, - lock_height: u64, - ) -> bool { - let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height)).unwrap(); - verify_single(secp, sig, &msg, None, pubkey, true) - } - - //Verifies other party's sig corresponds with what we're expecting - pub fn verify_partial_sig( - &self, - secp: &Secp256k1, - sig: &Signature, - other_pub_nonce: &PublicKey, - pubkey: &PublicKey, - fee: u64, - lock_height: u64, - ) -> bool { - let (_, sec_nonce) = self.get_private_keys(); - let mut nonce_sum = other_pub_nonce.clone(); - let _ = nonce_sum.add_exp_assign(secp, &sec_nonce); - let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height)).unwrap(); - - verify_single(secp, sig, &msg, Some(&nonce_sum), pubkey, true) - } - - ///TODO: Remove when below is integrated - pub fn calculate_partial_sig( - &self, - secp: &Secp256k1, - other_pub_nonce: &PublicKey, - fee: u64, - lock_height: u64, - ) -> Result<Signature, Error> { - // Add public nonces kR*G + kS*G - let (_, sec_nonce) = self.get_private_keys(); - let mut nonce_sum = other_pub_nonce.clone(); - let _ = nonce_sum.add_exp_assign(secp, &sec_nonce); - let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?; - - //Now calculate signature using message M=fee, nonce in e=nonce_sum - self.sign_single( - secp, - &msg, - Some(&sec_nonce), - Some(&nonce_sum), - Some(&nonce_sum), - ) - } - - pub fn calculate_partial_sig_with_nonce_sum( - &self, - secp: &Secp256k1, - nonce_sum: &PublicKey, - fee: u64, - lock_height: u64, - ) -> Result<Signature, Error> { - // Add public nonces kR*G + kS*G - let (_, sec_nonce) = self.get_private_keys(); - let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?; - - //Now calculate signature using message M=fee, nonce in e=nonce_sum - self.sign_single( - secp, - &msg, - Some(&sec_nonce), - Some(&nonce_sum), - Some(&nonce_sum), - ) - } - - /// Helper function to calculate final signature - pub fn calculate_final_sig( - &self, - secp: &Secp256k1, - part_sigs: Vec<&Signature>, - nonce_sum: &PublicKey, - ) -> Result<Signature, Error> { - // Add public nonces kR*G + kS*G - let sig = aggsig::add_signatures_single(&secp, part_sigs, &nonce_sum)?; - Ok(sig) - } - - /// Helper function to calculate final public key - pub fn calculate_final_pubkey( - &self, - secp: &Secp256k1, - their_public_key: &PublicKey, - ) -> Result<PublicKey, Error> { - let (our_sec_key, _) = self.get_private_keys(); - let mut pk_sum = their_public_key.clone(); - let _ = pk_sum.add_exp_assign(secp, &our_sec_key); - Ok(pk_sum) - } -} - -// Contextless functions - /// Verifies a partial sig given all public nonces used in the round pub fn verify_partial_sig( secp: &Secp256k1, @@ -277,9 +61,12 @@ pub fn verify_partial_sig( pubkey: &PublicKey, fee: u64, lock_height: u64, -) -> bool { - let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height)).unwrap(); - verify_single(secp, sig, &msg, Some(&pub_nonce_sum), pubkey, true) +) -> Result<(), Error> { + let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?; + if !verify_single(secp, sig, &msg, Some(&pub_nonce_sum), pubkey, true) { + return Err(Error::Signature("Signature validation error".to_string())); + } + Ok(()) } /// Just a simple sig, creates its own nonce, etc @@ -300,11 +87,14 @@ pub fn verify_single_from_commit( sig: &Signature, msg: &Message, commit: &Commitment, -) -> bool { +) -> Result<(), Error> { // Extract the pubkey, unfortunately we need this hack for now, (we just hope // one is valid) - let pubkey = commit.to_pubkey(secp).unwrap(); - aggsig::verify_single(secp, &sig, &msg, None, &pubkey, false) + let pubkey = commit.to_pubkey(secp)?; + if !verify_single(secp, sig, &msg, None, &pubkey, false) { + return Err(Error::Signature("Signature validation error".to_string())); + } + Ok(()) } /// Verify a sig, with built message @@ -314,12 +104,15 @@ pub fn verify_sig_build_msg( pubkey: &PublicKey, fee: u64, lock_height: u64, -) -> bool { - let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height)).unwrap(); - verify_single(secp, sig, &msg, None, pubkey, true) +) -> Result<(), Error> { + let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?; + if !verify_single(secp, sig, &msg, None, pubkey, true) { + return Err(Error::Signature("Signature validation error".to_string())); + } + Ok(()) } -//Verifies an aggsig signature +/// Verifies an aggsig signature pub fn verify_single( secp: &Secp256k1, sig: &Signature, diff --git a/wallet/src/libwallet/blind.rs b/wallet/src/libwallet/blind.rs deleted file mode 100644 index bbf33b375..000000000 --- a/wallet/src/libwallet/blind.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2018 The Grin Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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. - -// Stub while figuring out wallet redesign diff --git a/wallet/src/libwallet/build.rs b/wallet/src/libwallet/build.rs index 71f91dab5..f9534f442 100644 --- a/wallet/src/libwallet/build.rs +++ b/wallet/src/libwallet/build.rs @@ -27,12 +27,12 @@ use util::{kernel_sig_msg, secp}; -use core::core::{Input, Output, OutputFeatures, ProofMessageElements, Transaction, TxKernel}; use core::core::hash::Hash; use core::core::pmmr::MerkleProof; -use libwallet::{aggsig, proof}; +use core::core::{Input, Output, OutputFeatures, ProofMessageElements, Transaction, TxKernel}; use keychain; use keychain::{BlindSum, BlindingFactor, Identifier, Keychain}; +use libwallet::{aggsig, proof}; use util::LOGGER; /// Context information available to transaction combinators. @@ -167,7 +167,8 @@ 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() +/// We currently only support building a tx with a single kernel with +/// build::transaction() pub fn initial_tx(mut tx: Transaction) -> Box<Append> { assert_eq!(tx.kernels.len(), 1); let kern = tx.kernels.remove(0); diff --git a/wallet/src/libwallet/error.rs b/wallet/src/libwallet/error.rs index 0c4e6c871..1b1c9695a 100644 --- a/wallet/src/libwallet/error.rs +++ b/wallet/src/libwallet/error.rs @@ -14,16 +14,34 @@ //! Wallet lib errors -use util::secp; +use core::core::transaction; use keychain::{self, extkey}; +use util::secp; -#[derive(PartialEq, Eq, Clone, Debug)] +#[derive(Fail, PartialEq, Clone, Debug)] +/// Libwallet error types pub enum Error { + /// SECP error + #[fail(display = "Secp Error")] Secp(secp::Error), + /// Keychain error + #[fail(display = "Keychain Error")] Keychain(keychain::Error), + /// Extended key error + #[fail(display = "Extended Key Error")] ExtendedKey(extkey::Error), - Transaction(String), + /// Transaction error + #[fail(display = "Transaction Error")] + Transaction(transaction::Error), + /// Signature error + #[fail(display = "Signature Error")] + Signature(String), + /// Rangeproof error + #[fail(display = "Rangeproof Error")] RangeProof(String), + /// Fee error + #[fail(display = "Fee Error")] + Fee(String), } impl From<secp::Error> for Error { @@ -43,18 +61,9 @@ impl From<keychain::Error> for Error { Error::Keychain(e) } } -/*impl error::Error for Error { - fn description(&self) -> &str { - match *self { - _ => "some kind of wallet lib error", - } + +impl From<transaction::Error> for Error { + fn from(e: transaction::Error) -> Error { + Error::Transaction(e) } } - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - _ => write!(f, "some kind of wallet lib error"), - } - } -}*/ diff --git a/wallet/src/libwallet/mod.rs b/wallet/src/libwallet/mod.rs index f2987ebb7..2bb863bc5 100644 --- a/wallet/src/libwallet/mod.rs +++ b/wallet/src/libwallet/mod.rs @@ -21,10 +21,9 @@ #![deny(unused_mut)] #![warn(missing_docs)] -pub mod error; pub mod aggsig; -pub mod blind; +pub mod build; +pub mod error; pub mod proof; pub mod reward; -pub mod build; pub mod transaction; diff --git a/wallet/src/libwallet/proof.rs b/wallet/src/libwallet/proof.rs index 73934db66..aada38291 100644 --- a/wallet/src/libwallet/proof.rs +++ b/wallet/src/libwallet/proof.rs @@ -14,16 +14,16 @@ //! Rangeproof library functions +use blake2; use keychain::Keychain; -use util::secp::pedersen::{Commitment, ProofInfo, ProofMessage, RangeProof}; -use util::secp::key::SecretKey; -use util::secp::{self, Secp256k1}; -use util::logger::LOGGER; use keychain::extkey::Identifier; use libwallet::error::Error; -use blake2; +use util::logger::LOGGER; +use util::secp::key::SecretKey; +use util::secp::pedersen::{Commitment, ProofInfo, ProofMessage, RangeProof}; +use util::secp::{self, Secp256k1}; -pub fn create_nonce(k: &Keychain, commit: &Commitment) -> SecretKey { +fn create_nonce(k: &Keychain, commit: &Commitment) -> Result<SecretKey, Error> { // hash(commit|masterkey) as nonce let root_key = k.root_key_id().to_bytes(); let res = blake2::blake2b::blake2b(32, &commit.0, &root_key); @@ -32,7 +32,12 @@ pub fn create_nonce(k: &Keychain, commit: &Commitment) -> SecretKey { for i in 0..res.len() { ret_val[i] = res[i]; } - SecretKey::from_slice(k.secp(), &ret_val).unwrap() + match SecretKey::from_slice(k.secp(), &ret_val) { + Ok(sk) => Ok(sk), + Err(e) => Err(Error::RangeProof( + format!("Unable to create nonce: {:?}", e).to_string(), + )), + } } /// So we want this to take an opaque structure that can be called @@ -48,7 +53,7 @@ pub fn create( ) -> Result<RangeProof, Error> { let commit = k.commit(amount, key_id)?; let skey = k.derived_key(key_id)?; - let nonce = create_nonce(k, &commit); + let nonce = create_nonce(k, &commit)?; if msg.len() == 0 { return Ok(k.secp().bullet_proof(amount, skey, nonce, extra_data, None)); } else { @@ -63,6 +68,7 @@ pub fn create( .bullet_proof(amount, skey, nonce, extra_data, Some(msg))); } +/// Verify a proof pub fn verify( secp: &Secp256k1, commit: Commitment, @@ -76,6 +82,7 @@ pub fn verify( } } +/// Rewind a rangeproof to retrieve the amount pub fn rewind( k: &Keychain, key_id: &Identifier, @@ -84,7 +91,7 @@ pub fn rewind( proof: RangeProof, ) -> Result<ProofInfo, Error> { let skey = k.derived_key(key_id)?; - let nonce = create_nonce(k, &commit); + let nonce = create_nonce(k, &commit)?; let proof_message = k.secp() .unwind_bullet_proof(commit, skey, nonce, extra_data, proof); let proof_info = match proof_message { diff --git a/wallet/src/libwallet/reward.rs b/wallet/src/libwallet/reward.rs index 33c7e88be..63ac2f0f8 100644 --- a/wallet/src/libwallet/reward.rs +++ b/wallet/src/libwallet/reward.rs @@ -12,16 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Builds the blinded output and related signature proof for the block -/// reward. - +//! Builds the blinded output and related signature proof for the block +//! reward. use keychain; -use core::core::{Output, OutputFeatures, ProofMessageElements, TxKernel}; use core::consensus::reward; -use libwallet::{aggsig, proof}; -use libwallet::error::Error; use core::core::KernelFeatures; +use core::core::{Output, OutputFeatures, ProofMessageElements, TxKernel}; +use libwallet::error::Error; +use libwallet::{aggsig, proof}; use util::{kernel_sig_msg, secp, static_secp_instance, LOGGER}; /// output a reward output diff --git a/wallet/src/libwallet/transaction.rs b/wallet/src/libwallet/transaction.rs index 94175565a..cb0b344f3 100644 --- a/wallet/src/libwallet/transaction.rs +++ b/wallet/src/libwallet/transaction.rs @@ -17,17 +17,17 @@ use rand::thread_rng; use uuid::Uuid; +use core::consensus; use core::core::{amount_to_hr_string, Committed, Transaction}; use keychain::{BlindSum, BlindingFactor, Keychain}; +use libwallet::error::Error; use libwallet::{aggsig, build}; -//TODO: Remove these from here, replace with libwallet error -use types::{tx_fee, Error, ErrorKind}; use util::secp::Signature; use util::secp::key::{PublicKey, SecretKey}; use util::{secp, LOGGER}; -use failure::ResultExt; +const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN; /// Public data for each participant in the slate @@ -114,8 +114,7 @@ impl Slate { if self.tx.kernels.len() != 0 { elems.insert(0, build::initial_tx(self.tx.clone())); } - let (tx, blind) = - build::partial_transaction(elems, &keychain).context(ErrorKind::Keychain)?; + let (tx, blind) = build::partial_transaction(elems, &keychain)?; self.tx = tx; Ok(blind) } @@ -125,14 +124,15 @@ impl Slate { pub fn fill_round_1( &mut self, keychain: &Keychain, - context_manager: &mut aggsig::ContextManager, + sec_key: &mut SecretKey, + sec_nonce: &SecretKey, participant_id: usize, ) -> Result<(), Error> { // Whoever does this first generates the offset if self.tx.offset == BlindingFactor::zero() { - self.generate_offset(keychain, context_manager)?; + self.generate_offset(keychain, sec_key)?; } - self.add_participant_info(keychain, context_manager, participant_id, None)?; + self.add_participant_info(keychain, &sec_key, &sec_nonce, participant_id, None)?; Ok(()) } @@ -140,20 +140,20 @@ impl Slate { pub fn fill_round_2( &mut self, keychain: &Keychain, - context_manager: &mut aggsig::ContextManager, + sec_key: &SecretKey, + sec_nonce: &SecretKey, participant_id: usize, ) -> Result<(), Error> { self.check_fees()?; self.verify_part_sigs(keychain.secp())?; - let context = context_manager.get_context(&self.id); - let sig_part = context - .calculate_partial_sig_with_nonce_sum( - keychain.secp(), - &self.pub_nonce_sum(keychain.secp()), - self.fee, - self.lock_height, - ) - .unwrap(); + let sig_part = aggsig::calculate_partial_sig( + keychain.secp(), + sec_key, + sec_nonce, + &self.pub_nonce_sum(keychain.secp())?, + self.fee, + self.lock_height, + )?; self.participant_data[participant_id].part_sig = Some(sig_part); Ok(()) } @@ -167,21 +167,27 @@ impl Slate { } /// Return the sum of public nonces - fn pub_nonce_sum(&self, secp: &secp::Secp256k1) -> PublicKey { + fn pub_nonce_sum(&self, secp: &secp::Secp256k1) -> Result<PublicKey, Error> { let pub_nonces = self.participant_data .iter() .map(|p| &p.public_nonce) .collect(); - PublicKey::from_combination(secp, pub_nonces).unwrap() + match PublicKey::from_combination(secp, pub_nonces) { + Ok(k) => Ok(k), + Err(e) => Err(Error::Secp(e)), + } } /// Return the sum of public blinding factors - fn pub_blind_sum(&self, secp: &secp::Secp256k1) -> PublicKey { + fn pub_blind_sum(&self, secp: &secp::Secp256k1) -> Result<PublicKey, Error> { let pub_blinds = self.participant_data .iter() .map(|p| &p.public_blind_excess) .collect(); - PublicKey::from_combination(secp, pub_blinds).unwrap() + match PublicKey::from_combination(secp, pub_blinds) { + Ok(k) => Ok(k), + Err(e) => Err(Error::Secp(e)), + } } /// Return vector of all partial sigs @@ -199,14 +205,14 @@ impl Slate { fn add_participant_info( &mut self, keychain: &Keychain, - context_manager: &aggsig::ContextManager, + sec_key: &SecretKey, + sec_nonce: &SecretKey, id: usize, part_sig: Option<Signature>, ) -> Result<(), Error> { - let context = context_manager.get_context(&self.id); - // Add our public key and nonce to the slate - let (pub_key, pub_nonce) = context.get_public_keys(keychain.secp()); + let pub_key = PublicKey::from_secret_key(keychain.secp(), &sec_key)?; + let pub_nonce = PublicKey::from_secret_key(keychain.secp(), &sec_nonce)?; self.participant_data.push(ParticipantData { id: id as u64, public_blind_excess: pub_key, @@ -219,27 +225,22 @@ impl Slate { /// Somebody involved needs to generate an offset with their private key /// For now, we'll have the transaction initiator be responsible for it - /// Return offset private key + /// Return offset private key for the participant to use later in the + /// transaction fn generate_offset( &mut self, keychain: &Keychain, - context_manager: &mut aggsig::ContextManager, + sec_key: &mut SecretKey, ) -> Result<(), Error> { // Generate a random kernel offset here // and subtract it from the blind_sum so we create // the aggsig context with the "split" key - let mut context = context_manager.get_context(&self.id); self.tx.offset = BlindingFactor::from_secret_key(SecretKey::new(&keychain.secp(), &mut thread_rng())); - let blind_offset = keychain - .blind_sum(&BlindSum::new() - .add_blinding_factor(BlindingFactor::from_secret_key(context.sec_key)) - .sub_blinding_factor(self.tx.offset)) - .unwrap(); - context.sec_key = blind_offset - .secret_key(&keychain.secp()) - .context(ErrorKind::Keychain)?; - context_manager.save_context(context); + let blind_offset = keychain.blind_sum(&BlindSum::new() + .add_blinding_factor(BlindingFactor::from_secret_key(sec_key.clone())) + .sub_blinding_factor(self.tx.offset))?; + *sec_key = blind_offset.secret_key(&keychain.secp())?; Ok(()) } @@ -255,23 +256,19 @@ impl Slate { None, ); if fee > self.tx.fee() { - return Err(ErrorKind::FeeDispute { - sender_fee: self.tx.fee(), - recipient_fee: fee, - })?; + return Err(Error::Fee( + format!("Fee Dispute Error: {}, {}", self.tx.fee(), fee,).to_string(), + )); } if fee > self.amount + self.fee { - info!( - LOGGER, + let reason = format!( "Rejected the transfer because transaction fee ({}) exceeds received amount ({}).", amount_to_hr_string(fee), amount_to_hr_string(self.amount + self.fee) ); - return Err(ErrorKind::FeeExceedsAmount { - sender_amount: self.amount + self.fee, - recipient_fee: fee, - })?; + info!(LOGGER, "{}", reason); + return Err(Error::Fee(reason.to_string())); } Ok(()) @@ -282,18 +279,14 @@ impl Slate { // collect public nonces for p in self.participant_data.iter() { if p.is_complete() { - if aggsig::verify_partial_sig( + aggsig::verify_partial_sig( secp, p.part_sig.as_ref().unwrap(), - &self.pub_nonce_sum(secp), + &self.pub_nonce_sum(secp)?, &p.public_blind_excess, self.fee, self.lock_height, - ) == false - { - error!(LOGGER, "Partial Sig invalid."); - return Err(ErrorKind::Signature("Partial Sig invalid."))?; - } + )?; } } Ok(()) @@ -320,27 +313,21 @@ impl Slate { self.verify_part_sigs(keychain.secp())?; let part_sigs = self.part_sigs(); - let pub_nonce_sum = self.pub_nonce_sum(keychain.secp()); - let final_pubkey = self.pub_blind_sum(keychain.secp()); + let pub_nonce_sum = self.pub_nonce_sum(keychain.secp())?; + let final_pubkey = self.pub_blind_sum(keychain.secp())?; // get the final signature - let final_sig = - aggsig::add_signatures(&keychain.secp(), part_sigs, &pub_nonce_sum).unwrap(); + let final_sig = aggsig::add_signatures(&keychain.secp(), part_sigs, &pub_nonce_sum)?; // Calculate the final public key (for our own sanity check) // Check our final sig verifies - let res = aggsig::verify_sig_build_msg( + aggsig::verify_sig_build_msg( &keychain.secp(), &final_sig, &final_pubkey, self.fee, self.lock_height, - ); - - if !res { - error!(LOGGER, "Final aggregated signature invalid."); - return Err(ErrorKind::Signature("Final aggregated signature invalid."))?; - } + )?; Ok(final_sig) } @@ -361,24 +348,20 @@ impl Slate { let final_excess = { // TODO - do we need to verify rangeproofs here? for x in &final_tx.outputs { - x.verify_proof().context(ErrorKind::Transaction)?; + x.verify_proof()?; } // sum the input/output commitments on the final tx let overage = final_tx.fee() as i64; - let tx_excess = final_tx - .sum_commitments(overage, None) - .context(ErrorKind::Transaction)?; + let tx_excess = final_tx.sum_commitments(overage, None)?; // subtract the kernel_excess (built from kernel_offset) let offset_excess = keychain .secp() - .commit(0, kernel_offset.secret_key(&keychain.secp()).unwrap()) - .unwrap(); + .commit(0, kernel_offset.secret_key(&keychain.secp())?)?; keychain .secp() - .commit_sum(vec![tx_excess], vec![offset_excess]) - .context(ErrorKind::Transaction)? + .commit_sum(vec![tx_excess], vec![offset_excess])? }; // update the tx kernel to reflect the offset excess and sig @@ -388,14 +371,22 @@ impl Slate { // confirm the kernel verifies successfully before proceeding debug!(LOGGER, "Validating final transaction"); - final_tx.kernels[0] - .verify() - .context(ErrorKind::Transaction)?; + final_tx.kernels[0].verify()?; // confirm the overall transaction is valid (including the updated kernel) - let _ = final_tx.validate().context(ErrorKind::Transaction)?; + let _ = final_tx.validate()?; self.tx = final_tx; Ok(()) } } + +/// Transaction fee calculation +pub fn tx_fee(input_len: usize, output_len: usize, proof_len: usize, base_fee: Option<u64>) -> u64 { + let use_base_fee = match base_fee { + Some(bf) => bf, + None => DEFAULT_BASE_FEE, + }; + + (Transaction::weight(input_len, output_len, proof_len) as u64) * use_base_fee +} diff --git a/wallet/src/outputs.rs b/wallet/src/outputs.rs index dae082c20..a3c554b38 100644 --- a/wallet/src/outputs.rs +++ b/wallet/src/outputs.rs @@ -13,12 +13,12 @@ // limitations under the License. use checker; -use keychain::Keychain; use core::core; -use types::{OutputStatus, WalletConfig, WalletData}; +use keychain::Keychain; use prettytable; -use term; use std::io::prelude::*; +use term; +use types::{OutputStatus, WalletConfig, WalletData}; pub fn show_outputs(config: &WalletConfig, keychain: &Keychain, show_spent: bool) { let root_key_id = keychain.root_key_id(); diff --git a/wallet/src/receiver.rs b/wallet/src/receiver.rs index cec358e6e..37e5bdfd2 100644 --- a/wallet/src/receiver.rs +++ b/wallet/src/receiver.rs @@ -21,16 +21,16 @@ use iron::Handler; use iron::prelude::*; use iron::status; use serde_json; -use std::sync::{Arc, RwLock}; use api; use core::consensus::reward; use core::core::{Output, TxKernel}; use core::global; use failure::Fail; +use failure::ResultExt; use grinwallet::{keys, selection}; use keychain::Keychain; -use libwallet::{aggsig, reward, transaction}; +use libwallet::{reward, transaction}; use types::*; use util::LOGGER; @@ -40,28 +40,24 @@ pub struct TxWrapper { pub tx_hex: String, } -lazy_static! { - /// Static reference to aggsig context (temporary while wallet is being refactored) - pub static ref AGGSIG_CONTEXT_MANAGER:Arc<RwLock<aggsig::ContextManager>> - = Arc::new(RwLock::new(aggsig::ContextManager::new())); -} - fn handle_send( config: &WalletConfig, keychain: &Keychain, - context_manager: &mut aggsig::ContextManager, slate: &mut transaction::Slate, ) -> Result<(), Error> { // create an output using the amount in the slate - let (_, receiver_create_fn) = - selection::build_recipient_output_with_slate(config, keychain, context_manager, slate) - .unwrap(); + let (_, mut context, receiver_create_fn) = + selection::build_recipient_output_with_slate(config, keychain, slate).unwrap(); // fill public keys - let _ = slate.fill_round_1(&keychain, context_manager, 1)?; + let _ = slate + .fill_round_1(&keychain, &mut context.sec_key, &context.sec_nonce, 1) + .context(ErrorKind::LibWalletError)?; // perform partial sig - let _ = slate.fill_round_2(&keychain, context_manager, 1)?; + let _ = slate + .fill_round_2(&keychain, &context.sec_key, &context.sec_nonce, 1) + .context(ErrorKind::LibWalletError)?; // Save output in wallet let _ = receiver_create_fn(); @@ -82,8 +78,7 @@ impl Handler for WalletReceiver { let struct_body = req.get::<bodyparser::Struct<transaction::Slate>>(); if let Ok(Some(mut slate)) = struct_body { - let mut acm = AGGSIG_CONTEXT_MANAGER.write().unwrap(); - let _ = handle_send(&self.config, &self.keychain, &mut acm, &mut slate) + let _ = handle_send(&self.config, &self.keychain, &mut slate) .map_err(|e| { error!( LOGGER, diff --git a/wallet/src/restore.rs b/wallet/src/restore.rs index 2e3efe1a6..f0d0292e3 100644 --- a/wallet/src/restore.rs +++ b/wallet/src/restore.rs @@ -11,18 +11,18 @@ // 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. +use api; +use byteorder::{BigEndian, ByteOrder}; +use core::core::transaction::ProofMessageElements; +use core::global; use failure::{Fail, ResultExt}; use keychain::{Identifier, Keychain}; +use libwallet::proof; +use types::{Error, ErrorKind, MerkleProofWrapper, OutputData, OutputStatus, WalletConfig, + WalletData}; use util; use util::LOGGER; use util::secp::pedersen; -use api; -use core::global; -use core::core::transaction::ProofMessageElements; -use types::{Error, ErrorKind, MerkleProofWrapper, OutputData, OutputStatus, WalletConfig, - WalletData}; -use byteorder::{BigEndian, ByteOrder}; -use libwallet::proof; pub fn get_chain_height(config: &WalletConfig) -> Result<u64, Error> { let url = format!("{}/v1/chain", config.check_node_api_http_addr); @@ -105,8 +105,17 @@ fn find_outputs_with_key( keychain: &Keychain, outputs: Vec<api::OutputPrintable>, found_key_index: &mut Vec<u32>, -) -> Vec< - ( +) -> Vec<( + pedersen::Commitment, + Identifier, + u32, + u64, + u64, + u64, + bool, + Option<MerkleProofWrapper>, +)> { + let mut wallet_outputs: Vec<( pedersen::Commitment, Identifier, u32, @@ -115,20 +124,7 @@ fn find_outputs_with_key( u64, bool, Option<MerkleProofWrapper>, - ), -> { - let mut wallet_outputs: Vec< - ( - pedersen::Commitment, - Identifier, - u32, - u64, - u64, - u64, - bool, - Option<MerkleProofWrapper>, - ), - > = Vec::new(); + )> = Vec::new(); let max_derivations = 1_000_000; diff --git a/wallet/src/sender.rs b/wallet/src/sender.rs index e37f47234..bc81103d3 100644 --- a/wallet/src/sender.rs +++ b/wallet/src/sender.rs @@ -20,7 +20,7 @@ use core::ser; use failure::ResultExt; use grinwallet::selection; use keychain::{Identifier, Keychain}; -use libwallet::{aggsig, build}; +use libwallet::{build, transaction}; use receiver::TxWrapper; use types::*; use util; @@ -50,9 +50,6 @@ pub fn issue_send_tx( checker::refresh_outputs(config, keychain)?; - // Create a new aggsig context - let mut context_manager = aggsig::ContextManager::new(); - // Get lock height let chain_tip = checker::get_tip_from_node(config)?; let current_height = chain_tip.height; @@ -61,17 +58,16 @@ pub fn issue_send_tx( let lock_height = current_height; - // Sender selects outputs into a new slate and save our corresponding IDs in - // their transaction context. The secret key in our transaction context will be + // Sender selects outputs into a new slate and save our corresponding keyss in + // a transaction context. The secret key in our transaction context will be // randomly selected. This returns the public slate, and a closure that locks // our inputs and outputs once we're convinced the transaction exchange went // according to plan // This function is just a big helper to do all of that, in theory // this process can be split up in any way - let (mut slate, sender_lock_fn) = selection::build_send_tx_slate( + let (mut slate, mut context, sender_lock_fn) = selection::build_send_tx_slate( config, keychain, - &mut context_manager, 2, amount, current_height, @@ -85,7 +81,7 @@ pub fn issue_send_tx( // the offset in the slate's transaction kernel, and adds our public key // information to the slate let _ = slate - .fill_round_1(keychain, &mut context_manager, 0) + .fill_round_1(keychain, &mut context.sec_key, &context.sec_nonce, 0) .unwrap(); let url = format!("{}/v1/receive/transaction", &dest); @@ -112,10 +108,12 @@ pub fn issue_send_tx( } }; - let _ = slate.fill_round_2(keychain, &mut context_manager, 0)?; + let _ = slate + .fill_round_2(keychain, &context.sec_key, &context.sec_nonce, 0) + .context(ErrorKind::LibWalletError)?; // Final transaction can be built by anyone at this stage - slate.finalize(keychain)?; + slate.finalize(keychain).context(ErrorKind::LibWalletError)?; // So let's post it let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap()); @@ -130,7 +128,7 @@ pub fn issue_send_tx( } api::client::post(url.as_str(), &TxWrapper { tx_hex: tx_hex }).context(ErrorKind::Node)?; - // All good so, lock our outputs + // All good so, lock our inputs sender_lock_fn()?; Ok(()) } @@ -165,8 +163,9 @@ pub fn issue_burn_tx( debug!(LOGGER, "selected some coins - {}", coins.len()); - let fee = tx_fee(coins.len(), 2, selection::coins_proof_count(&coins), None); - let (mut parts, _) = selection::inputs_and_change(&coins, config, keychain, amount, fee)?; + let fee = transaction::tx_fee(coins.len(), 2, selection::coins_proof_count(&coins), None); + let (mut parts, _) = + selection::inputs_and_change(&coins, config, keychain, current_height, amount, fee)?; // add burn output and fees parts.push(build::output(amount - fee, Identifier::zero())); diff --git a/wallet/src/server.rs b/wallet/src/server.rs index afa552612..20a38b59d 100644 --- a/wallet/src/server.rs +++ b/wallet/src/server.rs @@ -13,8 +13,8 @@ // limitations under the License. use api::ApiServer; -use keychain::Keychain; use handlers::CoinbaseHandler; +use keychain::Keychain; use receiver::WalletReceiver; use types::WalletConfig; use util::LOGGER; diff --git a/wallet/src/types.rs b/wallet/src/types.rs index 8ee5aa52a..552b1e4e9 100644 --- a/wallet/src/types.rs +++ b/wallet/src/types.rs @@ -32,8 +32,6 @@ use tokio_retry::strategy::FibonacciBackoff; use failure::{Backtrace, Context, Fail, ResultExt}; -use core::consensus; -use core::core::Transaction; use core::core::hash::Hash; use core::core::pmmr::MerkleProof; use keychain; @@ -45,18 +43,6 @@ const BCK_FILE: &'static str = "wallet.bck"; const LOCK_FILE: &'static str = "wallet.lock"; const SEED_FILE: &'static str = "wallet.seed"; -const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN; - -/// Transaction fee calculation -pub fn tx_fee(input_len: usize, output_len: usize, proof_len: usize, base_fee: Option<u64>) -> u64 { - let use_base_fee = match base_fee { - Some(bf) => bf, - None => DEFAULT_BASE_FEE, - }; - - (Transaction::weight(input_len, output_len, proof_len) as u64) * use_base_fee -} - #[derive(Debug)] pub struct Error { inner: Context<ErrorKind>, @@ -87,6 +73,9 @@ pub enum ErrorKind { #[fail(display = "Secp error")] Secp, + #[fail(display = "LibWallet error")] + LibWalletError, + #[fail(display = "Wallet data error: {}", _0)] WalletData(&'static str), diff --git a/wallet/tests/common/mod.rs b/wallet/tests/common/mod.rs index a79d86b4c..459328d4e 100644 --- a/wallet/tests/common/mod.rs +++ b/wallet/tests/common/mod.rs @@ -42,16 +42,21 @@ pub fn refresh_output_state_local( chain: &chain::Chain, ) -> Result<(), Error> { let wallet_outputs = checker::map_wallet_outputs(config, keychain)?; - let chain_outputs: Vec<api::Output> = wallet_outputs + let chain_outputs: Vec<Option<api::Output>> = wallet_outputs .keys() .map(|k| match get_output_local(chain, &k) { - Err(e) => panic!(e), - Ok(k) => k, + Err(_) => None, + Ok(k) => Some(k), }) .collect(); let mut api_outputs: HashMap<pedersen::Commitment, api::Output> = HashMap::new(); for out in chain_outputs { - api_outputs.insert(out.commit.commit(), out); + match out { + Some(o) => { + api_outputs.insert(o.commit.commit(), o); + } + None => {} + } } checker::apply_api_outputs(config, &wallet_outputs, &api_outputs)?; Ok(()) diff --git a/wallet/tests/libwallet.rs b/wallet/tests/libwallet.rs index 48bc7b9b8..f23f4c73a 100644 --- a/wallet/tests/libwallet.rs +++ b/wallet/tests/libwallet.rs @@ -24,7 +24,7 @@ use keychain::{BlindSum, BlindingFactor, Keychain}; use util::secp::key::{PublicKey, SecretKey}; use util::secp::pedersen::ProofMessage; use util::{kernel_sig_msg, secp}; -use uuid::Uuid; +use wallet::grinwallet::sigcontext; use wallet::libwallet::{aggsig, proof}; use rand::thread_rng; @@ -33,11 +33,6 @@ use rand::thread_rng; fn aggsig_sender_receiver_interaction() { let sender_keychain = Keychain::from_random_seed().unwrap(); let receiver_keychain = Keychain::from_random_seed().unwrap(); - let mut sender_aggsig_cm = aggsig::ContextManager::new(); - let mut receiver_aggsig_cm = aggsig::ContextManager::new(); - - // tx identifier for wallet interaction - let tx_id = Uuid::new_v4(); // Calculate the kernel excess here for convenience. // Normally this would happen during transaction building. @@ -63,8 +58,10 @@ fn aggsig_sender_receiver_interaction() { .unwrap() }; + let s_cx; + let mut rx_cx; // sender starts the tx interaction - let (sender_pub_excess, sender_pub_nonce) = { + let (sender_pub_excess, _sender_pub_nonce) = { let keychain = sender_keychain.clone(); let skey = keychain @@ -80,25 +77,39 @@ fn aggsig_sender_receiver_interaction() { let blind = blinding_factor.secret_key(&keychain.secp()).unwrap(); - let cx = sender_aggsig_cm.create_context(&keychain.secp(), &tx_id, blind); - cx.get_public_keys(&keychain.secp()) + s_cx = sigcontext::Context::new(&keychain.secp(), blind); + s_cx.get_public_keys(&keychain.secp()) }; + let pub_nonce_sum; // receiver receives partial tx - let (receiver_pub_excess, receiver_pub_nonce, sig_part) = { + let (receiver_pub_excess, _receiver_pub_nonce, rx_sig_part) = { let keychain = receiver_keychain.clone(); let key_id = keychain.derive_key_id(1).unwrap(); // let blind = blind_sum.secret_key(&keychain.secp())?; let blind = keychain.derived_key(&key_id).unwrap(); - let mut cx = receiver_aggsig_cm.create_context(&keychain.secp(), &tx_id, blind); - let (pub_excess, pub_nonce) = cx.get_public_keys(&keychain.secp()); - cx.add_output(&key_id); + rx_cx = sigcontext::Context::new(&keychain.secp(), blind); + let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp()); + rx_cx.add_output(&key_id); - let sig_part = cx.calculate_partial_sig(&keychain.secp(), &sender_pub_nonce, 0, 0) - .unwrap(); - receiver_aggsig_cm.save_context(cx); + pub_nonce_sum = PublicKey::from_combination( + keychain.secp(), + vec![ + &s_cx.get_public_keys(keychain.secp()).1, + &rx_cx.get_public_keys(keychain.secp()).1, + ], + ).unwrap(); + + let sig_part = aggsig::calculate_partial_sig( + &keychain.secp(), + &rx_cx.sec_key, + &rx_cx.sec_nonce, + &pub_nonce_sum, + 0, + 0, + ).unwrap(); (pub_excess, pub_nonce, sig_part) }; @@ -106,66 +117,74 @@ fn aggsig_sender_receiver_interaction() { // received in the response back from the receiver { let keychain = sender_keychain.clone(); - let cx = sender_aggsig_cm.get_context(&tx_id); - let sig_verifies = cx.verify_partial_sig( + let sig_verifies = aggsig::verify_partial_sig( &keychain.secp(), - &sig_part, - &receiver_pub_nonce, + &rx_sig_part, + &pub_nonce_sum, &receiver_pub_excess, 0, 0, ); - assert!(sig_verifies); + assert!(!sig_verifies.is_err()); } // now sender signs with their key let sender_sig_part = { let keychain = sender_keychain.clone(); - let cx = sender_aggsig_cm.get_context(&tx_id); - cx.calculate_partial_sig(&keychain.secp(), &receiver_pub_nonce, 0, 0) - .unwrap() + let sig_part = aggsig::calculate_partial_sig( + &keychain.secp(), + &s_cx.sec_key, + &s_cx.sec_nonce, + &pub_nonce_sum, + 0, + 0, + ).unwrap(); + sig_part }; // check the receiver can verify the partial signature // received by the sender { let keychain = receiver_keychain.clone(); - let cx = receiver_aggsig_cm.get_context(&tx_id); - let sig_verifies = cx.verify_partial_sig( + let sig_verifies = aggsig::verify_partial_sig( &keychain.secp(), &sender_sig_part, - &sender_pub_nonce, + &pub_nonce_sum, &sender_pub_excess, 0, 0, ); - assert!(sig_verifies); + assert!(!sig_verifies.is_err()); } // Receiver now builds final signature from sender and receiver parts let (final_sig, final_pubkey) = { let keychain = receiver_keychain.clone(); - let cx = receiver_aggsig_cm.get_context(&tx_id); - // Receiver recreates their partial sig (we do not maintain state from earlier) - let our_sig_part = cx.calculate_partial_sig(&keychain.secp(), &sender_pub_nonce, 0, 0) - .unwrap(); - - let combined_nonces = PublicKey::from_combination( - keychain.secp(), - vec![&sender_pub_nonce, &cx.get_public_keys(keychain.secp()).1], + let our_sig_part = aggsig::calculate_partial_sig( + &keychain.secp(), + &rx_cx.sec_key, + &rx_cx.sec_nonce, + &pub_nonce_sum, + 0, + 0, ).unwrap(); // Receiver now generates final signature from the two parts - let final_sig = cx.calculate_final_sig( + let final_sig = aggsig::add_signatures( &keychain.secp(), vec![&sender_sig_part, &our_sig_part], - &combined_nonces, + &pub_nonce_sum, ).unwrap(); // Receiver calculates the final public key (to verify sig later) - let final_pubkey = cx.calculate_final_pubkey(&keychain.secp(), &sender_pub_excess) - .unwrap(); + let final_pubkey = PublicKey::from_combination( + keychain.secp(), + vec![ + &s_cx.get_public_keys(keychain.secp()).0, + &rx_cx.get_public_keys(keychain.secp()).0, + ], + ).unwrap(); (final_sig, final_pubkey) }; @@ -173,12 +192,11 @@ fn aggsig_sender_receiver_interaction() { // Receiver checks the final signature verifies { let keychain = receiver_keychain.clone(); - let cx = receiver_aggsig_cm.get_context(&tx_id); // Receiver check the final signature verifies let sig_verifies = - cx.verify_final_sig_build_msg(&keychain.secp(), &final_sig, &final_pubkey, 0, 0); - assert!(sig_verifies); + aggsig::verify_sig_build_msg(&keychain.secp(), &final_sig, &final_pubkey, 0, 0); + assert!(!sig_verifies.is_err()); } // Check we can verify the sig using the kernel excess @@ -190,7 +208,7 @@ fn aggsig_sender_receiver_interaction() { let sig_verifies = aggsig::verify_single_from_commit(&keychain.secp(), &final_sig, &msg, &kernel_excess); - assert!(sig_verifies); + assert!(!sig_verifies.is_err()); } } @@ -198,11 +216,6 @@ fn aggsig_sender_receiver_interaction() { fn aggsig_sender_receiver_interaction_offset() { let sender_keychain = Keychain::from_random_seed().unwrap(); let receiver_keychain = Keychain::from_random_seed().unwrap(); - let mut sender_aggsig_cm = aggsig::ContextManager::new(); - let mut receiver_aggsig_cm = aggsig::ContextManager::new(); - - // tx identifier for wallet interaction - let tx_id = Uuid::new_v4(); // This is the kernel offset that we use to split the key // Summing these at the block level prevents the @@ -236,8 +249,10 @@ fn aggsig_sender_receiver_interaction_offset() { .unwrap() }; + let s_cx; + let mut rx_cx; // sender starts the tx interaction - let (sender_pub_excess, sender_pub_nonce) = { + let (sender_pub_excess, _sender_pub_nonce) = { let keychain = sender_keychain.clone(); let skey = keychain @@ -256,24 +271,38 @@ fn aggsig_sender_receiver_interaction_offset() { let blind = blinding_factor.secret_key(&keychain.secp()).unwrap(); - let cx = sender_aggsig_cm.create_context(&keychain.secp(), &tx_id, blind); - cx.get_public_keys(&keychain.secp()) + s_cx = sigcontext::Context::new(&keychain.secp(), blind); + s_cx.get_public_keys(&keychain.secp()) }; // receiver receives partial tx - let (receiver_pub_excess, receiver_pub_nonce, sig_part) = { + let pub_nonce_sum; + let (receiver_pub_excess, _receiver_pub_nonce, sig_part) = { let keychain = receiver_keychain.clone(); let key_id = keychain.derive_key_id(1).unwrap(); let blind = keychain.derived_key(&key_id).unwrap(); - let mut cx = receiver_aggsig_cm.create_context(&keychain.secp(), &tx_id, blind); - let (pub_excess, pub_nonce) = cx.get_public_keys(&keychain.secp()); - cx.add_output(&key_id); + rx_cx = sigcontext::Context::new(&keychain.secp(), blind); + let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp()); + rx_cx.add_output(&key_id); - let sig_part = cx.calculate_partial_sig(&keychain.secp(), &sender_pub_nonce, 0, 0) - .unwrap(); - receiver_aggsig_cm.save_context(cx); + pub_nonce_sum = PublicKey::from_combination( + keychain.secp(), + vec![ + &s_cx.get_public_keys(keychain.secp()).1, + &rx_cx.get_public_keys(keychain.secp()).1, + ], + ).unwrap(); + + let sig_part = aggsig::calculate_partial_sig( + &keychain.secp(), + &rx_cx.sec_key, + &rx_cx.sec_nonce, + &pub_nonce_sum, + 0, + 0, + ).unwrap(); (pub_excess, pub_nonce, sig_part) }; @@ -281,66 +310,73 @@ fn aggsig_sender_receiver_interaction_offset() { // received in the response back from the receiver { let keychain = sender_keychain.clone(); - let cx = sender_aggsig_cm.get_context(&tx_id); - let sig_verifies = cx.verify_partial_sig( + let sig_verifies = aggsig::verify_partial_sig( &keychain.secp(), &sig_part, - &receiver_pub_nonce, + &pub_nonce_sum, &receiver_pub_excess, 0, 0, ); - assert!(sig_verifies); + assert!(!sig_verifies.is_err()); } // now sender signs with their key let sender_sig_part = { let keychain = sender_keychain.clone(); - let cx = sender_aggsig_cm.get_context(&tx_id); - cx.calculate_partial_sig(&keychain.secp(), &receiver_pub_nonce, 0, 0) - .unwrap() + let sig_part = aggsig::calculate_partial_sig( + &keychain.secp(), + &s_cx.sec_key, + &s_cx.sec_nonce, + &pub_nonce_sum, + 0, + 0, + ).unwrap(); + sig_part }; // check the receiver can verify the partial signature // received by the sender { let keychain = receiver_keychain.clone(); - let cx = receiver_aggsig_cm.get_context(&tx_id); - let sig_verifies = cx.verify_partial_sig( + let sig_verifies = aggsig::verify_partial_sig( &keychain.secp(), &sender_sig_part, - &sender_pub_nonce, + &pub_nonce_sum, &sender_pub_excess, 0, 0, ); - assert!(sig_verifies); + assert!(!sig_verifies.is_err()); } // Receiver now builds final signature from sender and receiver parts let (final_sig, final_pubkey) = { let keychain = receiver_keychain.clone(); - let cx = receiver_aggsig_cm.get_context(&tx_id); - - // Receiver recreates their partial sig (we do not maintain state from earlier) - let our_sig_part = cx.calculate_partial_sig(&keychain.secp(), &sender_pub_nonce, 0, 0) - .unwrap(); - - let combined_nonces = PublicKey::from_combination( - keychain.secp(), - vec![&sender_pub_nonce, &cx.get_public_keys(keychain.secp()).1], + let our_sig_part = aggsig::calculate_partial_sig( + &keychain.secp(), + &rx_cx.sec_key, + &rx_cx.sec_nonce, + &pub_nonce_sum, + 0, + 0, ).unwrap(); // Receiver now generates final signature from the two parts - let final_sig = cx.calculate_final_sig( + let final_sig = aggsig::add_signatures( &keychain.secp(), vec![&sender_sig_part, &our_sig_part], - &combined_nonces, + &pub_nonce_sum, ).unwrap(); // Receiver calculates the final public key (to verify sig later) - let final_pubkey = cx.calculate_final_pubkey(&keychain.secp(), &sender_pub_excess) - .unwrap(); + let final_pubkey = PublicKey::from_combination( + keychain.secp(), + vec![ + &s_cx.get_public_keys(keychain.secp()).0, + &rx_cx.get_public_keys(keychain.secp()).0, + ], + ).unwrap(); (final_sig, final_pubkey) }; @@ -348,12 +384,11 @@ fn aggsig_sender_receiver_interaction_offset() { // Receiver checks the final signature verifies { let keychain = receiver_keychain.clone(); - let cx = receiver_aggsig_cm.get_context(&tx_id); // Receiver check the final signature verifies let sig_verifies = - cx.verify_final_sig_build_msg(&keychain.secp(), &final_sig, &final_pubkey, 0, 0); - assert!(sig_verifies); + aggsig::verify_sig_build_msg(&keychain.secp(), &final_sig, &final_pubkey, 0, 0); + assert!(!sig_verifies.is_err()); } // Check we can verify the sig using the kernel excess @@ -365,7 +400,7 @@ fn aggsig_sender_receiver_interaction_offset() { let sig_verifies = aggsig::verify_single_from_commit(&keychain.secp(), &final_sig, &msg, &kernel_excess); - assert!(sig_verifies); + assert!(!sig_verifies.is_err()); } } diff --git a/wallet/tests/transaction.rs b/wallet/tests/transaction.rs index 202570852..fe887e0bb 100644 --- a/wallet/tests/transaction.rs +++ b/wallet/tests/transaction.rs @@ -35,7 +35,6 @@ use core::global::ChainTypes; use core::{global, pow}; use util::LOGGER; use wallet::grinwallet::selection; -use wallet::libwallet::aggsig; fn clean_output_dir(test_dir: &str) { let _ = fs::remove_dir_all(test_dir); @@ -57,7 +56,7 @@ fn setup(test_dir: &str, chain_dir: &str) -> Chain { /// Build and test new version of sending API #[test] -fn build_transaction_2() { +fn build_transaction() { let chain = setup("test_output", "build_transaction_2/.grin"); let wallet1 = common::create_wallet("test_output/build_transaction_2/wallet1"); let wallet2 = common::create_wallet("test_output/build_transaction_2/wallet2"); @@ -68,6 +67,7 @@ fn build_transaction_2() { // Get lock height let chain_tip = chain.head().unwrap(); let amount = 300_000_000_000; + let min_confirmations = 3; // ensure outputs we're selecting are up to date let res = common::refresh_output_state_local(&wallet1.0, &wallet1.1, &chain); @@ -77,23 +77,20 @@ fn build_transaction_2() { } // TRANSACTION WORKFLOW STARTS HERE - // Sender creates a new aggsig context - let mut sender_context_manager = aggsig::ContextManager::new(); // Sender selects outputs into a new slate and save our corresponding IDs in - // their transaction context. The secret key in our transaction context will be + // a transaction context. The secret key in our transaction context will be // randomly selected. This returns the public slate, and a closure that locks // our inputs and outputs once we're convinced the transaction exchange went // according to plan // This function is just a big helper to do all of that, in theory // this process can be split up in any way - let (mut slate, sender_lock_fn) = selection::build_send_tx_slate( + let (mut slate, mut sender_context, sender_lock_fn) = selection::build_send_tx_slate( &wallet1.0, &wallet1.1, - &mut sender_context_manager, 2, amount, chain_tip.height, - 3, + min_confirmations, chain_tip.height, 1000, true, @@ -103,36 +100,44 @@ fn build_transaction_2() { // the offset in the slate's transaction kernel, and adds our public key // information to the slate let _ = slate - .fill_round_1(&wallet1.1, &mut sender_context_manager, 0) + .fill_round_1( + &wallet1.1, + &mut sender_context.sec_key, + &sender_context.sec_nonce, + 0, + ) .unwrap(); debug!(LOGGER, "Transaction Slate after step 1: sender initiation"); debug!(LOGGER, "-----------------------------------------"); debug!(LOGGER, "{:?}", slate); - // RECIPIENT (Handle sender initiation) - let mut recipient_context_manager = aggsig::ContextManager::new(); - // Now, just like the sender did, recipient is going to select a target output, // add it to the transaction, and keep track of the corresponding wallet // Identifier Again, this is a helper to do that, which returns a closure that // creates the output when we're satisified the process was successful - let (_, receiver_create_fn) = selection::build_recipient_output_with_slate( - &wallet2.0, - &wallet2.1, - &mut recipient_context_manager, - &mut slate, - ).unwrap(); + let (_, mut recp_context, receiver_create_fn) = + selection::build_recipient_output_with_slate(&wallet2.0, &wallet2.1, &mut slate).unwrap(); let _ = slate - .fill_round_1(&wallet2.1, &mut recipient_context_manager, 1) + .fill_round_1( + &wallet2.1, + &mut recp_context.sec_key, + &recp_context.sec_nonce, + 1, + ) .unwrap(); // recipient can proceed to round 2 now let _ = receiver_create_fn(); let _ = slate - .fill_round_2(&wallet2.1, &mut recipient_context_manager, 1) + .fill_round_2( + &wallet2.1, + &recp_context.sec_key, + &recp_context.sec_nonce, + 1, + ) .unwrap(); debug!( @@ -144,7 +149,12 @@ fn build_transaction_2() { // SENDER Part 3: Sender confirmation let _ = slate - .fill_round_2(&wallet1.1, &mut sender_context_manager, 0) + .fill_round_2( + &wallet1.1, + &sender_context.sec_key, + &sender_context.sec_nonce, + 0, + ) .unwrap(); debug!(LOGGER, "PartialTx after step 3: sender confirmation"); @@ -167,7 +177,7 @@ fn build_transaction_2() { // Insert this transaction into a new block, then mine till confirmation common::award_block_to_wallet(&chain, vec![&slate.tx], &wallet1); - common::award_blocks_to_wallet(&chain, &wallet1, 3); + common::award_blocks_to_wallet(&chain, &wallet1, 5); // Refresh wallets let res = common::refresh_output_state_local(&wallet2.0, &wallet2.1, &chain); @@ -175,8 +185,23 @@ fn build_transaction_2() { panic!("Error refreshing output state for wallet: {:?}", e); } + // check recipient wallet let chain_tip = chain.head().unwrap(); let balances = common::get_wallet_balances(&wallet2.0, &wallet2.1, chain_tip.height).unwrap(); assert_eq!(balances.3, 300_000_000_000); + + // check sender wallet + let res = common::refresh_output_state_local(&wallet1.0, &wallet1.1, &chain); + if let Err(e) = res { + panic!("Error refreshing output state for wallet: {:?}", e); + } + let balances = common::get_wallet_balances(&wallet1.0, &wallet1.1, chain_tip.height).unwrap(); + println!("tip height: {:?}", chain_tip.height); + println!("Sender balances: {:?}", balances); + // num blocks * grins per block, and wallet1 mined the fee + assert_eq!( + balances.3, + (chain_tip.height - min_confirmations) * 60_000_000_000 - amount + ); }