Libwallet refactoring - Library functions + ErrorTypes ()

* move checker and rename to updater

* rustfmt

* complete checker/updater move

* rustfmt

* move libwallet error into separate file

* rustfmt

* starting to sort our error types

* updating errors in libtx and libwallet

* rustfmt

* factor out error type

* rustfmt

* compiling, errors split into libwallet and wallet errors

* rustfmt

* changing user error reporting to new format

* rustfmt

* clean up error types

* clean up error types

* move restore into libwallet

* rustfmt
This commit is contained in:
Yeastplume 2018-06-01 15:06:59 +01:00 committed by GitHub
parent bbedeeaca3
commit 7812a02233
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 701 additions and 397 deletions

View file

@ -18,8 +18,8 @@
//! enough, consensus-relevant constants and short functions should be kept
//! here.
use std::fmt;
use std::cmp::max;
use std::fmt;
use core::target::Difficulty;
use global;
@ -155,7 +155,7 @@ pub const DAMP_FACTOR: u64 = 3;
pub const INITIAL_DIFFICULTY: u64 = 1_000_000;
/// Consensus errors
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Error {
/// Inputs/outputs/kernels must be sorted lexicographically.
SortError,

View file

@ -49,7 +49,7 @@ bitflags! {
}
/// Errors thrown by Block validation
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Eq, Debug, PartialEq)]
pub enum Error {
/// Underlying Secp256k1 error (signature validation or invalid public key
/// typically)

View file

@ -303,7 +303,7 @@ impl LocalServerContainer {
.expect("Failed to derive keychain from seed file and passphrase.");
let mut wallet = FileWallet::new(config.clone(), keychain)
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config));
wallet::retrieve_info(&mut wallet)
wallet::libwallet::updater::retrieve_info(&mut wallet).unwrap()
}
pub fn send_amount_to(
@ -342,17 +342,9 @@ impl LocalServerContainer {
dest,
selection_strategy,
),
Err(e) => match e.kind() {
wallet::ErrorKind::NotEnoughFunds(available) => {
println!(
"Tx not sent: insufficient funds (max: {})",
core::core::amount_to_hr_string(available),
);
}
_ => {
println!("Tx not sent to {}: {:?}", dest, e);
}
},
Err(e) => {
println!("Tx not sent to {}: {:?}", dest, e);
}
};
}

View file

@ -51,7 +51,7 @@ use core::core::amount_to_hr_string;
use core::global;
use tui::ui;
use util::{init_logger, LoggingConfig, LOGGER};
use wallet::FileWallet;
use wallet::{libwallet, FileWallet};
// include build information
pub mod built_info {
@ -586,30 +586,25 @@ fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) {
dest,
selection_strategy,
),
Err(e) => match e.kind() {
wallet::ErrorKind::NotEnoughFunds(available) => {
error!(
LOGGER,
"Tx not sent: insufficient funds (max: {})",
amount_to_hr_string(available),
);
}
wallet::ErrorKind::FeeExceedsAmount {
sender_amount,
recipient_fee,
} => {
error!(
LOGGER,
"Recipient rejected the transfer because transaction fee ({}) exceeded amount ({}).",
amount_to_hr_string(recipient_fee),
amount_to_hr_string(sender_amount)
);
}
_ => {
error!(LOGGER, "Tx not sent: {:?}", e);
}
},
};
Err(e) => {
error!(LOGGER, "Tx not sent: {}", e.cause());
match e.downcast::<libwallet::Error>() {
Ok(le) => {
match le.kind() {
// user errors, don't backtrace
libwallet::ErrorKind::NotEnoughFunds { .. } => {}
libwallet::ErrorKind::FeeDispute { .. } => {}
libwallet::ErrorKind::FeeExceedsAmount { .. } => {}
_ => {
// otherwise give full dump
error!(LOGGER, "Backtrace: {}", le.backtrace().unwrap());
}
};
}
_ => {}
};
}
}
}
("burn", Some(send_args)) => {
let amount = send_args
@ -626,13 +621,19 @@ fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) {
wallet::issue_burn_tx(&mut wallet, amount, minimum_confirmations, max_outputs).unwrap();
}
("info", Some(_)) => {
wallet::show_info(&mut wallet);
let res = wallet::show_info(&mut wallet);
if let Err(e) = res {
println!("Could not get wallet info: {}", e);
}
}
("outputs", Some(_)) => {
wallet::show_outputs(&mut wallet, show_spent);
}
("restore", Some(_)) => {
let _ = wallet::restore(&mut wallet);
let res = wallet.restore();
if let Err(e) = res {
println!("Could not restore wallet: {}", e);
}
}
_ => panic!("Unknown wallet command, use 'grin help wallet' for details"),
}

View file

@ -21,6 +21,7 @@ use libtx::slate::Slate;
use serde_json;
use tokio_core::reactor;
use error::{Error, ErrorKind};
use libwallet::types::*;
use std::io;
use util::LOGGER;

169
wallet/src/error.rs Normal file
View file

@ -0,0 +1,169 @@
// 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.
//! Implementation specific error types
use keychain;
use libtx;
use libwallet;
use std::fmt::{self, Display};
use core::core::transaction;
use failure::{Backtrace, Context, Fail};
/// Error definition
#[derive(Debug)]
pub struct Error {
inner: Context<ErrorKind>,
}
/// Wallet errors, mostly wrappers around underlying crypto or I/O errors.
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
/// LibTX Error
#[fail(display = "LibTx Error")]
LibTX(libtx::ErrorKind),
/// LibWallet Error
#[fail(display = "LibWallet Error")]
LibWallet(libwallet::ErrorKind),
/// Keychain error
#[fail(display = "Keychain error")]
Keychain(keychain::Error),
/// Transaction Error
#[fail(display = "Transaction error")]
Transaction(transaction::Error),
/// Secp Error
#[fail(display = "Secp error")]
Secp,
/// Filewallet error
#[fail(display = "Wallet data error: {}", _0)]
FileWallet(&'static str),
/// Error when formatting json
#[fail(display = "IO error")]
IO,
/// Error when formatting json
#[fail(display = "Serde JSON error")]
Format,
/// Error when contacting a node through its API
#[fail(display = "Node API error")]
Node,
/// Error originating from hyper.
#[fail(display = "Hyper error")]
Hyper,
/// Error originating from hyper uri parsing.
#[fail(display = "Uri parsing error")]
Uri,
/// Attempt to use duplicate transaction id in separate transactions
#[fail(display = "Duplicate transaction ID error")]
DuplicateTransactionId,
/// Wallet seed already exists
#[fail(display = "Wallet seed exists error")]
WalletSeedExists,
/// Wallet seed doesn't exist
#[fail(display = "Wallet seed doesn't exist error")]
WalletSeedDoesntExist,
/// Other
#[fail(display = "Generic error: {}", _0)]
GenericError(&'static str),
}
impl Fail for Error {
fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl Error {
/// get kind
pub fn kind(&self) -> ErrorKind {
self.inner.get_context().clone()
}
/// get cause
pub fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
/// get backtrace
pub fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner: inner }
}
}
impl From<keychain::Error> for Error {
fn from(error: keychain::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Keychain(error)),
}
}
}
impl From<transaction::Error> for Error {
fn from(error: transaction::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Transaction(error)),
}
}
}
impl From<libwallet::Error> for Error {
fn from(error: libwallet::Error) -> Error {
Error {
inner: Context::new(ErrorKind::LibWallet(error.kind())),
}
}
}
impl From<libtx::Error> for Error {
fn from(error: libtx::Error) -> Error {
Error {
inner: Context::new(ErrorKind::LibTX(error.kind())),
}
}
}

View file

@ -18,20 +18,22 @@ use std::cmp::min;
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::Path;
use std::path::MAIN_SEPARATOR;
use std::path::Path;
use serde_json;
use tokio_core::reactor;
use tokio_retry::strategy::FibonacciBackoff;
use tokio_retry::Retry;
use tokio_retry::strategy::FibonacciBackoff;
use failure::{Fail, ResultExt};
use failure::{self, ResultExt};
use keychain::{self, Keychain};
use util;
use util::LOGGER;
use error::{Error, ErrorKind};
use libwallet;
use libwallet::types::*;
const DAT_FILE: &'static str = "wallet.dat";
@ -99,7 +101,7 @@ impl WalletSeed {
pub fn derive_keychain(&self, password: &str) -> Result<keychain::Keychain, Error> {
let seed = blake2::blake2b::blake2b(64, &password.as_bytes(), &self.0);
let result = keychain::Keychain::from_seed(seed.as_bytes()).context(ErrorKind::Keychain)?;
let result = keychain::Keychain::from_seed(seed.as_bytes())?;
Ok(result)
}
@ -195,11 +197,12 @@ impl WalletBackend for FileWallet {
/// Allows for reading wallet data (without needing to acquire the write
/// lock).
fn read_wallet<T, F>(&mut self, f: F) -> Result<T, Error>
fn read_wallet<T, F>(&mut self, f: F) -> Result<T, libwallet::Error>
where
F: FnOnce(&mut Self) -> Result<T, Error>,
F: FnOnce(&mut Self) -> Result<T, libwallet::Error>,
{
self.read_or_create_paths()?;
self.read_or_create_paths()
.context(libwallet::ErrorKind::CallbackImpl("Error reading wallet"))?;
f(self)
}
@ -209,7 +212,7 @@ impl WalletBackend for FileWallet {
/// Note that due to the impossibility to do an actual file lock easily
/// across operating systems, this just creates a lock file with a "should
/// not exist" option.
fn with_wallet<T, F>(&mut self, f: F) -> Result<T, Error>
fn with_wallet<T, F>(&mut self, f: F) -> Result<T, libwallet::Error>
where
F: FnOnce(&mut Self) -> T,
{
@ -230,7 +233,10 @@ impl WalletBackend for FileWallet {
let mut core = reactor::Core::new().unwrap();
let retry_strategy = FibonacciBackoff::from_millis(1000).take(10);
let retry_future = Retry::spawn(core.handle(), retry_strategy, action);
let retry_result = core.run(retry_future);
let retry_result = core.run(retry_future)
.context(libwallet::ErrorKind::CallbackImpl(
"Failed to acquire lock file",
));
match retry_result {
Ok(_) => {}
@ -239,22 +245,22 @@ impl WalletBackend for FileWallet {
LOGGER,
"Failed to acquire wallet lock file (multiple retries)",
);
return Err(
e.context(ErrorKind::FileWallet("Failed to acquire lock file"))
.into(),
);
return Err(e.into());
}
}
// We successfully acquired the lock - so do what needs to be done.
self.read_or_create_paths()?;
self.write(&self.backup_file_path)?;
self.read_or_create_paths()
.context(libwallet::ErrorKind::CallbackImpl("Lock Error"))?;
self.write(&self.backup_file_path)
.context(libwallet::ErrorKind::CallbackImpl("Write Error"))?;
let res = f(self);
self.write(&self.data_file_path)?;
self.write(&self.data_file_path)
.context(libwallet::ErrorKind::CallbackImpl("Write Error"))?;
// delete the lock file
fs::remove_dir(&self.lock_file_path).context(ErrorKind::FileWallet(
"Could not remove wallet lock file. Maybe insufficient rights?",
fs::remove_dir(&self.lock_file_path).context(libwallet::ErrorKind::CallbackImpl(
&"Could not remove wallet lock file. Maybe insufficient rights? ",
))?;
info!(LOGGER, "... released wallet lock");
@ -384,6 +390,12 @@ impl FileWallet {
}
}
/// Restore wallet contents
pub fn restore(&mut self) -> Result<(), failure::Error> {
libwallet::restore::restore(self).context(libwallet::ErrorKind::Restore)?;
Ok(())
}
/// Read the wallet data or create brand files if the data
/// files don't yet exist
fn read_or_create_paths(&mut self) -> Result<(), Error> {
@ -402,10 +414,9 @@ impl FileWallet {
fn read_outputs(&self) -> Result<Vec<OutputData>, Error> {
let data_file = File::open(self.data_file_path.clone())
.context(ErrorKind::FileWallet(&"Could not open wallet file"))?;
serde_json::from_reader(data_file).map_err(|e| {
e.context(ErrorKind::FileWallet(&"Error reading wallet file "))
.into()
})
serde_json::from_reader(data_file)
.context(ErrorKind::Format)
.map_err(|e| e.into())
}
/// Populate wallet_data with output_data from disk.
@ -420,12 +431,12 @@ impl FileWallet {
/// Write the wallet data to disk.
fn write(&self, data_file_path: &str) -> Result<(), Error> {
let mut data_file = File::create(data_file_path)
.map_err(|e| e.context(ErrorKind::FileWallet(&"Could not create ")))?;
let mut data_file =
File::create(data_file_path).context(ErrorKind::FileWallet(&"Could not create "))?;
let mut outputs = self.outputs.values().collect::<Vec<_>>();
outputs.sort();
let res_json = serde_json::to_vec_pretty(&outputs)
.map_err(|e| e.context(ErrorKind::FileWallet("Error serializing wallet data")))?;
.context(ErrorKind::FileWallet("Error serializing wallet data"))?;
data_file
.write_all(res_json.as_slice())
.context(ErrorKind::FileWallet(&"Error writing wallet file"))

View file

@ -14,12 +14,13 @@
use std::sync::{Arc, RwLock};
use bodyparser;
use iron::Handler;
use iron::prelude::*;
use iron::status;
use iron::Handler;
use serde_json;
use core::ser;
use error::{Error, ErrorKind};
use failure::{Fail, ResultExt};
use libwallet::types::*;
use receiver::receive_coinbase;

View file

@ -12,16 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use checker;
use core::core::amount_to_hr_string;
use libwallet::types::*;
use libwallet::Error;
use libwallet::types::WalletBackend;
use libwallet::updater;
use prettytable;
pub fn show_info<T>(wallet: &mut T)
pub fn show_info<T>(wallet: &mut T) -> Result<(), Error>
where
T: WalletBackend,
{
let wallet_info = retrieve_info(wallet);
let wallet_info = updater::retrieve_info(wallet)?;
println!(
"\n____ Wallet Summary Info at {} ({}) ____\n",
wallet_info.current_height, wallet_info.data_confirmed_from
@ -44,61 +45,6 @@ where
Above info is maybe not fully updated or invalid! \
Check that your `grin server` is OK, or see `wallet help restore`"
);
}
}
pub fn retrieve_info<T>(wallet: &mut T) -> WalletInfo
where
T: WalletBackend,
{
let result = checker::refresh_outputs(wallet);
let ret_val = wallet.read_wallet(|wallet_data| {
let (current_height, from) = match checker::get_tip_from_node(&wallet_data.node_url()) {
Ok(tip) => (tip.height, "from server node"),
Err(_) => match wallet_data.outputs().values().map(|out| out.height).max() {
Some(height) => (height, "from wallet"),
None => (0, "node/wallet unavailable"),
},
};
let mut unspent_total = 0;
let mut unspent_but_locked_total = 0;
let mut unconfirmed_total = 0;
let mut locked_total = 0;
for out in wallet_data
.outputs()
.clone()
.values()
.filter(|out| out.root_key_id == wallet_data.keychain().root_key_id())
{
if out.status == OutputStatus::Unspent {
unspent_total += out.value;
if out.lock_height > current_height {
unspent_but_locked_total += out.value;
}
}
if out.status == OutputStatus::Unconfirmed && !out.is_coinbase {
unconfirmed_total += out.value;
}
if out.status == OutputStatus::Locked {
locked_total += out.value;
}
}
let mut data_confirmed = true;
if let Err(_) = result {
data_confirmed = false;
}
Ok(WalletInfo {
current_height: current_height,
total: unspent_total + unconfirmed_total,
amount_awaiting_confirmation: unconfirmed_total,
amount_confirmed_but_locked: unspent_but_locked_total,
amount_currently_spendable: unspent_total - unspent_but_locked_total,
amount_locked: locked_total,
data_confirmed: data_confirmed,
data_confirmed_from: String::from(from),
})
});
ret_val.unwrap()
};
Ok(())
}

View file

@ -46,8 +46,8 @@ extern crate grin_core as core;
extern crate grin_keychain as keychain;
extern crate grin_util as util;
pub mod checker;
pub mod client;
mod error;
pub mod file_wallet;
mod handlers;
mod info;
@ -55,14 +55,13 @@ pub mod libtx;
pub mod libwallet;
mod outputs;
pub mod receiver;
mod restore;
mod sender;
pub mod server;
pub use error::{Error, ErrorKind};
pub use file_wallet::{FileWallet, WalletConfig, WalletSeed};
pub use info::{retrieve_info, show_info};
pub use libwallet::types::{BlockFees, CbData, Error, ErrorKind, WalletInfo, WalletReceiveRequest};
pub use info::show_info;
pub use libwallet::types::{BlockFees, CbData, WalletInfo};
pub use outputs::show_outputs;
pub use receiver::WalletReceiver;
pub use restore::restore;
pub use sender::{issue_burn_tx, issue_send_tx};

View file

@ -13,10 +13,10 @@
// 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::Keychain;
use libtx::error::Error;
use libtx::error::{Error, ErrorKind};
use util::kernel_sig_msg;
use util::secp::key::{PublicKey, SecretKey};
use util::secp::pedersen::Commitment;
@ -64,7 +64,9 @@ pub fn verify_partial_sig(
) -> 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()));
Err(ErrorKind::Signature(
"Signature validation error".to_string(),
))?
}
Ok(())
}
@ -92,7 +94,9 @@ pub fn verify_single_from_commit(
// one is valid)
let pubkey = commit.to_pubkey(secp)?;
if !verify_single(secp, sig, &msg, None, &pubkey, false) {
return Err(Error::Signature("Signature validation error".to_string()));
Err(ErrorKind::Signature(
"Signature validation error".to_string(),
))?
}
Ok(())
}
@ -107,7 +111,9 @@ pub fn verify_sig_build_msg(
) -> 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()));
Err(ErrorKind::Signature(
"Signature validation error".to_string(),
))?
}
Ok(())
}

View file

@ -12,15 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Wallet lib errors
//! libtx specific errors
use failure::{Backtrace, Context, Fail};
use std::fmt::{self, Display};
use core::core::transaction;
use keychain::{self, extkey};
use util::secp;
#[derive(Fail, PartialEq, Clone, Debug)]
/// Lib tx error definition
#[derive(Debug)]
pub struct Error {
inner: Context<ErrorKind>,
}
#[derive(Clone, Debug, Eq, Fail, PartialEq)]
/// Libwallet error types
pub enum Error {
pub enum ErrorKind {
/// SECP error
#[fail(display = "Secp Error")]
Secp(secp::Error),
@ -44,26 +52,71 @@ pub enum Error {
Fee(String),
}
impl From<secp::Error> for Error {
fn from(e: secp::Error) -> Error {
Error::Secp(e)
impl Fail for Error {
fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl From<extkey::Error> for Error {
fn from(e: extkey::Error) -> Error {
Error::ExtendedKey(e)
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl Error {
/// Return errorkind
pub fn kind(&self) -> ErrorKind {
self.inner.get_context().clone()
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner: inner }
}
}
impl From<secp::Error> for Error {
fn from(error: secp::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Secp(error)),
}
}
}
impl From<keychain::Error> for Error {
fn from(e: keychain::Error) -> Error {
Error::Keychain(e)
fn from(error: keychain::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Keychain(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(e: transaction::Error) -> Error {
Error::Transaction(e)
fn from(error: transaction::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Transaction(error)),
}
}
}

View file

@ -23,7 +23,7 @@
pub mod aggsig;
pub mod build;
pub mod error;
mod error;
pub mod proof;
pub mod reward;
pub mod slate;
@ -31,6 +31,8 @@ pub mod slate;
use core::consensus;
use core::core::Transaction;
pub use libtx::error::{Error, ErrorKind};
const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN;
/// Transaction fee calculation

View file

@ -15,9 +15,9 @@
//! Rangeproof library functions
use blake2;
use keychain::extkey::Identifier;
use keychain::Keychain;
use libtx::error::Error;
use keychain::extkey::Identifier;
use libtx::error::{Error, ErrorKind};
use util::logger::LOGGER;
use util::secp::key::SecretKey;
use util::secp::pedersen::{Commitment, ProofInfo, ProofMessage, RangeProof};
@ -34,9 +34,9 @@ fn create_nonce(k: &Keychain, commit: &Commitment) -> Result<SecretKey, Error> {
}
match SecretKey::from_slice(k.secp(), &ret_val) {
Ok(sk) => Ok(sk),
Err(e) => Err(Error::RangeProof(
Err(e) => Err(ErrorKind::RangeProof(
format!("Unable to create nonce: {:?}", e).to_string(),
)),
))?,
}
}
@ -59,9 +59,9 @@ pub fn create(
} else {
if msg.len() != 64 {
error!(LOGGER, "Bullet proof message must be 64 bytes.");
return Err(Error::RangeProof(
return Err(ErrorKind::RangeProof(
"Bullet proof message must be 64 bytes".to_string(),
));
))?;
}
}
return Ok(k.secp()

View file

@ -19,11 +19,11 @@ use uuid::Uuid;
use core::core::{amount_to_hr_string, Committed, Transaction};
use keychain::{BlindSum, BlindingFactor, Keychain};
use libtx::error::Error;
use libtx::error::{Error, ErrorKind};
use libtx::{aggsig, build, tx_fee};
use util::secp::key::{PublicKey, SecretKey};
use util::secp::Signature;
use util::secp::key::{PublicKey, SecretKey};
use util::{secp, LOGGER};
/// Public data for each participant in the slate
@ -171,7 +171,7 @@ impl Slate {
.collect();
match PublicKey::from_combination(secp, pub_nonces) {
Ok(k) => Ok(k),
Err(e) => Err(Error::Secp(e)),
Err(e) => Err(ErrorKind::Secp(e))?,
}
}
@ -183,7 +183,7 @@ impl Slate {
.collect();
match PublicKey::from_combination(secp, pub_blinds) {
Ok(k) => Ok(k),
Err(e) => Err(Error::Secp(e)),
Err(e) => Err(ErrorKind::Secp(e))?,
}
}
@ -253,9 +253,9 @@ impl Slate {
None,
);
if fee > self.tx.fee() {
return Err(Error::Fee(
return Err(ErrorKind::Fee(
format!("Fee Dispute Error: {}, {}", self.tx.fee(), fee,).to_string(),
));
))?;
}
if fee > self.amount + self.fee {
@ -265,7 +265,7 @@ impl Slate {
amount_to_hr_string(self.amount + self.fee)
);
info!(LOGGER, "{}", reason);
return Err(Error::Fee(reason.to_string()));
return Err(ErrorKind::Fee(reason.to_string()))?;
}
Ok(())

View file

@ -0,0 +1,182 @@
// 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.
//! Error types for libwallet
use keychain;
use libtx;
use std::fmt::{self, Display};
use core::core::transaction;
use failure::{Backtrace, Context, Fail};
/// Error definition
#[derive(Debug, Fail)]
pub struct Error {
inner: Context<ErrorKind>,
}
/// Wallet errors, mostly wrappers around underlying crypto or I/O errors.
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
/// Not enough funds
#[fail(display = "Not enough funds. Required: {}, Available: {}", needed, available)]
NotEnoughFunds {
/// available funds
available: u64,
/// Needed funds
needed: u64,
},
/// Fee dispute
#[fail(display = "Fee dispute: sender fee {}, recipient fee {}", sender_fee, recipient_fee)]
FeeDispute {
/// sender fee
sender_fee: u64,
/// recipient fee
recipient_fee: u64,
},
/// Fee Exceeds amount
#[fail(display = "Fee exceeds amount: sender amount {}, recipient fee {}", sender_amount,
recipient_fee)]
FeeExceedsAmount {
/// sender amount
sender_amount: u64,
/// recipient fee
recipient_fee: u64,
},
/// LibTX Error
#[fail(display = "LibTx Error")]
LibTX(libtx::ErrorKind),
/// Keychain error
#[fail(display = "Keychain error")]
Keychain(keychain::Error),
/// Transaction Error
#[fail(display = "Transaction error")]
Transaction(transaction::Error),
/// Secp Error
#[fail(display = "Secp error")]
Secp,
/// Callback implementation error conversion
#[fail(display = "Callback Implementation error")]
CallbackImpl(&'static str),
/// Callback implementation error conversion
#[fail(display = "Restore Error")]
Restore,
/// An error in the format of the JSON structures exchanged by the wallet
#[fail(display = "JSON format error")]
Format,
/// IO Error
#[fail(display = "I/O error")]
IO,
/// Error when contacting a node through its API
#[fail(display = "Node API error")]
Node,
/// Error originating from hyper.
#[fail(display = "Hyper error")]
Hyper,
/// Error originating from hyper uri parsing.
#[fail(display = "Uri parsing error")]
Uri,
/// Signature error
#[fail(display = "Signature error")]
Signature(&'static str),
/// Attempt to use duplicate transaction id in separate transactions
#[fail(display = "Duplicate transaction ID error")]
DuplicateTransactionId,
/// Wallet seed already exists
#[fail(display = "Wallet seed exists error")]
WalletSeedExists,
/// Wallet seed doesn't exist
#[fail(display = "Wallet seed doesn't exist error")]
WalletSeedDoesntExist,
/// Other
#[fail(display = "Generic error: {}", _0)]
GenericError(&'static str),
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl Error {
/// get kind
pub fn kind(&self) -> ErrorKind {
self.inner.get_context().clone()
}
/// get cause
pub fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
/// get backtrace
pub fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner: inner }
}
}
impl From<keychain::Error> for Error {
fn from(error: keychain::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Keychain(error)),
}
}
}
impl From<libtx::Error> for Error {
fn from(error: libtx::Error) -> Error {
Error {
inner: Context::new(ErrorKind::LibTX(error.kind())),
}
}
}
impl From<transaction::Error> for Error {
fn from(error: transaction::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Transaction(error)),
}
}
}

View file

@ -14,7 +14,8 @@
//! Wallet key management functions
use keychain::Identifier;
use libwallet::types::{Error, WalletBackend};
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>

View file

@ -25,7 +25,12 @@
#![deny(unused_mut)]
#![warn(missing_docs)]
mod error;
pub mod keys;
pub mod restore;
pub mod selection;
pub mod sigcontext;
pub mod types;
pub mod updater;
pub use libwallet::error::{Error, ErrorKind};

View file

@ -11,19 +11,22 @@
// 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.
//! Functions to restore a wallet's outputs from just the master seed
use api;
use byteorder::{BigEndian, ByteOrder};
use core::core::transaction::ProofMessageElements;
use core::global;
use error::{Error, ErrorKind};
use failure::{Fail, ResultExt};
use keychain::Identifier;
use libtx::proof;
use libwallet::types::*;
use util;
use util::secp::pedersen;
use util::LOGGER;
use util::secp::pedersen;
pub fn get_chain_height(node_addr: &str) -> Result<u64, Error> {
fn get_chain_height(node_addr: &str) -> Result<u64, Error> {
let url = format!("{}/v1/chain", node_addr);
match api::client::get::<api::Tip>(url.as_str()) {
@ -41,10 +44,7 @@ pub fn get_chain_height(node_addr: &str) -> Result<u64, Error> {
}
}
pub fn get_merkle_proof_for_commit(
node_addr: &str,
commit: &str,
) -> Result<MerkleProofWrapper, Error> {
fn get_merkle_proof_for_commit(node_addr: &str, commit: &str) -> Result<MerkleProofWrapper, Error> {
let url = format!("{}/v1/txhashset/merkleproof?id={}", node_addr, commit);
match api::client::get::<api::OutputPrintable>(url.as_str()) {
@ -68,11 +68,7 @@ fn coinbase_status(output: &api::OutputPrintable) -> bool {
}
}
pub fn outputs_batch<T>(
wallet: &T,
start_height: u64,
max: u64,
) -> Result<api::OutputListing, Error>
fn outputs_batch<T>(wallet: &T, start_height: u64, max: u64) -> Result<api::OutputListing, Error>
where
T: WalletBackend,
{
@ -245,6 +241,7 @@ fn find_outputs_with_key<T: WalletBackend>(
wallet_outputs
}
/// Restore a wallet
pub fn restore<T: WalletBackend>(wallet: &mut T) -> Result<(), Error> {
// Don't proceed if wallet.dat has anything in it
let is_empty = wallet

View file

@ -14,9 +14,9 @@
//! Selection of inputs for building transactions
use failure::ResultExt;
use keychain::Identifier;
use libtx::{build, slate::Slate, tx_fee};
use libtx::{build, tx_fee, slate::Slate};
use libwallet::error::{Error, ErrorKind};
use libwallet::types::*;
use libwallet::{keys, sigcontext};
@ -64,9 +64,7 @@ where
let keychain = wallet.keychain().clone();
let blinding = slate
.add_transaction_elements(&keychain, elems)
.context(ErrorKind::LibWalletError)?;
let blinding = slate.add_transaction_elements(&keychain, elems)?;
// Create our own private context
let mut context = sigcontext::Context::new(
wallet.keychain().secp(),
@ -133,9 +131,8 @@ where
let keychain = wallet.keychain().clone();
let blinding = slate
.add_transaction_elements(&keychain, vec![build::output(amount, key_id.clone())])
.context(ErrorKind::LibWalletError)?;
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(
@ -228,7 +225,10 @@ where
let mut amount_with_fee = amount + fee;
if total == 0 {
return Err(ErrorKind::NotEnoughFunds(total as u64))?;
return Err(ErrorKind::NotEnoughFunds {
available: 0,
needed: amount_with_fee as u64,
})?;
}
// Check if we need to use a change address
@ -241,7 +241,10 @@ where
while total < amount_with_fee {
// End the loop if we have selected all the outputs and still not enough funds
if coins.len() == max_outputs {
return Err(ErrorKind::NotEnoughFunds(total as u64))?;
return Err(ErrorKind::NotEnoughFunds {
available: total as u64,
needed: amount_with_fee as u64,
})?;
}
// select some spendable coins from the wallet
@ -301,10 +304,7 @@ where
// build inputs using the appropriate derived key_ids
for coin in coins {
let key_id = wallet
.keychain()
.derive_key_id(coin.n_child)
.context(ErrorKind::Keychain)?;
let key_id = wallet.keychain().derive_key_id(coin.n_child)?;
if coin.is_coinbase {
let block = coin.block.clone();
let merkle_proof = coin.merkle_proof.clone();

View file

@ -15,17 +15,19 @@
//! Types and traits that should be provided by a wallet
//! implementation
use std::collections::HashMap;
use std::fmt::{self, Display};
use std::fmt;
use serde;
use failure::{Backtrace, Context, Fail, ResultExt};
use failure::ResultExt;
use core::core::hash::Hash;
use core::core::pmmr::MerkleProof;
use keychain::{Identifier, Keychain};
use libwallet::error::{Error, ErrorKind};
/// TODO:
/// Wallets should implement this backend for their storage. All functions
/// here expect that the wallet instance has instantiated itself or stored
@ -103,6 +105,7 @@ pub struct OutputData {
pub is_coinbase: bool,
/// Hash of the block this output originated from.
pub block: Option<BlockIdentifier>,
/// Merkle proof
pub merkle_proof: Option<MerkleProofWrapper>,
}
@ -165,6 +168,7 @@ impl OutputData {
}
}
/// Mark an output as spent
pub fn mark_spent(&mut self) {
match self.status {
OutputStatus::Unspent => self.status = OutputStatus::Spent,
@ -179,9 +183,13 @@ impl OutputData {
/// broadcasted or mined).
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub enum OutputStatus {
/// Unconfirmed
Unconfirmed,
/// Unspend
Unspent,
/// Locked
Locked,
/// Spent
Spent,
}
@ -196,10 +204,12 @@ impl fmt::Display for OutputStatus {
}
}
/// Wrapper for a merkle proof
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct MerkleProofWrapper(pub MerkleProof);
impl MerkleProofWrapper {
/// Create
pub fn merkle_proof(&self) -> MerkleProof {
self.0.clone()
}
@ -241,14 +251,17 @@ impl<'de> serde::de::Visitor<'de> for MerkleProofWrapperVisitor {
}
}
/// Block Identifier
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct BlockIdentifier(pub Hash);
impl BlockIdentifier {
/// return hash
pub fn hash(&self) -> Hash {
self.0
}
/// convert to hex string
pub fn from_hex(hex: &str) -> Result<BlockIdentifier, Error> {
let hash = Hash::from_hex(hex).context(ErrorKind::GenericError("Invalid hex"))?;
Ok(BlockIdentifier(hash))
@ -291,23 +304,19 @@ impl<'de> serde::de::Visitor<'de> for BlockIdentifierVisitor {
}
}
/// Amount in request to build a coinbase output.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum WalletReceiveRequest {
Coinbase(BlockFees),
PartialTransaction(String),
Finalize(String),
}
/// Fees in block to use for coinbase amount calculation
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlockFees {
/// fees
pub fees: u64,
/// height
pub height: u64,
/// key id
pub key_id: Option<Identifier>,
}
impl BlockFees {
/// return key id
pub fn key_id(&self) -> Option<Identifier> {
self.key_id.clone()
}
@ -316,8 +325,11 @@ impl BlockFees {
/// Response to build a coinbase output.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CbData {
/// Output
pub output: String,
/// Kernel
pub kernel: String,
/// Key Id
pub key_id: String,
}
@ -325,133 +337,20 @@ pub struct CbData {
/// can add more fields here over time as needed
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct WalletInfo {
// height from which info was taken
/// height from which info was taken
pub current_height: u64,
// total amount in the wallet
/// total amount in the wallet
pub total: u64,
// amount awaiting confirmation
/// amount awaiting confirmation
pub amount_awaiting_confirmation: u64,
// confirmed but locked
/// confirmed but locked
pub amount_confirmed_but_locked: u64,
// amount currently spendable
/// amount currently spendable
pub amount_currently_spendable: u64,
// amount locked by previous transactions
/// amount locked by previous transactions
pub amount_locked: u64,
// whether the data was confirmed against a live node
/// whether the data was confirmed against a live node
pub data_confirmed: bool,
// node confirming the data
/// node confirming the data
pub data_confirmed_from: String,
}
#[derive(Debug)]
pub struct Error {
inner: Context<ErrorKind>,
}
/// Wallet errors, mostly wrappers around underlying crypto or I/O errors.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
#[fail(display = "Not enough funds")]
NotEnoughFunds(u64),
#[fail(display = "Fee dispute: sender fee {}, recipient fee {}", sender_fee, recipient_fee)]
FeeDispute { sender_fee: u64, recipient_fee: u64 },
#[fail(
display = "Fee exceeds amount: sender amount {}, recipient fee {}",
sender_amount,
recipient_fee
)]
FeeExceedsAmount {
sender_amount: u64,
recipient_fee: u64,
},
#[fail(display = "Keychain error")]
Keychain,
#[fail(display = "Transaction error")]
Transaction,
#[fail(display = "Secp error")]
Secp,
#[fail(display = "LibWallet error")]
LibWalletError,
#[fail(display = "Wallet data error: {}", _0)]
FileWallet(&'static str),
/// An error in the format of the JSON structures exchanged by the wallet
#[fail(display = "JSON format error")]
Format,
#[fail(display = "I/O error")]
IO,
/// Error when contacting a node through its API
#[fail(display = "Node API error")]
Node,
/// Error originating from hyper.
#[fail(display = "Hyper error")]
Hyper,
/// Error originating from hyper uri parsing.
#[fail(display = "Uri parsing error")]
Uri,
#[fail(display = "Signature error")]
Signature(&'static str),
/// Attempt to use duplicate transaction id in separate transactions
#[fail(display = "Duplicate transaction ID error")]
DuplicateTransactionId,
/// Wallet seed already exists
#[fail(display = "Wallet seed exists error")]
WalletSeedExists,
/// Wallet seed doesn't exist
#[fail(display = "Wallet seed doesn't exist error")]
WalletSeedDoesntExist,
#[fail(display = "Generic error: {}", _0)]
GenericError(&'static str),
}
impl Fail for Error {
fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl Error {
pub fn kind(&self) -> ErrorKind {
*self.inner.get_context()
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner: inner }
}
}

View file

@ -16,16 +16,19 @@
//! the wallet storage and update them.
use failure::ResultExt;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use api;
use keychain::Identifier;
use libwallet::error::{Error, ErrorKind};
use libwallet::types::*;
use util;
use util::secp::pedersen;
use util::LOGGER;
use util::secp::pedersen;
/// Refreshes the outputs in a wallet with the latest information
/// from a node
pub fn refresh_outputs<T>(wallet: &mut T) -> Result<(), Error>
where
T: WalletBackend,
@ -130,9 +133,7 @@ where
.values()
.filter(|x| x.root_key_id == root_key_id && x.status != OutputStatus::Spent);
for out in unspents {
let commit = keychain
.commit_with_key_index(out.value, out.n_child)
.context(ErrorKind::Keychain)?;
let commit = keychain.commit_with_key_index(out.value, out.n_child)?;
wallet_outputs.insert(commit, out.key_id.clone());
}
Ok(())
@ -155,9 +156,7 @@ where
x.root_key_id == wallet_data.keychain().root_key_id() && x.block.is_none()
&& x.status == OutputStatus::Unspent
}) {
let commit = keychain
.commit_with_key_index(out.value, out.n_child)
.context(ErrorKind::Keychain)?;
let commit = keychain.commit_with_key_index(out.value, out.n_child)?;
wallet_outputs.insert(commit, out.key_id.clone());
}
Ok(())
@ -251,9 +250,67 @@ where
})
}
/// Return the chain tip from a given node
pub fn get_tip_from_node(addr: &str) -> Result<api::Tip, Error> {
let url = format!("{}/v1/chain", addr);
api::client::get::<api::Tip>(url.as_str())
.context(ErrorKind::Node)
.map_err(|e| e.into())
}
/// Retrieve summar info about the wallet
pub fn retrieve_info<T>(wallet: &mut T) -> Result<WalletInfo, Error>
where
T: WalletBackend,
{
let result = refresh_outputs(wallet);
let ret_val = wallet.read_wallet(|wallet_data| {
let (current_height, from) = match get_tip_from_node(&wallet_data.node_url()) {
Ok(tip) => (tip.height, "from server node"),
Err(_) => match wallet_data.outputs().values().map(|out| out.height).max() {
Some(height) => (height, "from wallet"),
None => (0, "node/wallet unavailable"),
},
};
let mut unspent_total = 0;
let mut unspent_but_locked_total = 0;
let mut unconfirmed_total = 0;
let mut locked_total = 0;
for out in wallet_data
.outputs()
.clone()
.values()
.filter(|out| out.root_key_id == wallet_data.keychain().root_key_id())
{
if out.status == OutputStatus::Unspent {
unspent_total += out.value;
if out.lock_height > current_height {
unspent_but_locked_total += out.value;
}
}
if out.status == OutputStatus::Unconfirmed && !out.is_coinbase {
unconfirmed_total += out.value;
}
if out.status == OutputStatus::Locked {
locked_total += out.value;
}
}
let mut data_confirmed = true;
if let Err(_) = result {
data_confirmed = false;
}
Ok(WalletInfo {
current_height: current_height,
total: unspent_total + unconfirmed_total,
amount_awaiting_confirmation: unconfirmed_total,
amount_confirmed_but_locked: unspent_but_locked_total,
amount_currently_spendable: unspent_total - unspent_but_locked_total,
amount_locked: locked_total,
data_confirmed: data_confirmed,
data_confirmed_from: String::from(from),
})
});
ret_val
}

View file

@ -12,22 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use checker;
use core::core;
use libwallet::types::*;
use libwallet::types::{OutputStatus, WalletBackend};
use libwallet::updater;
use prettytable;
use std::io::prelude::*;
use term;
pub fn show_outputs<T: WalletBackend>(wallet: &mut T, show_spent: bool) {
let root_key_id = wallet.keychain().clone().root_key_id();
let result = checker::refresh_outputs(wallet);
let result = updater::refresh_outputs(wallet);
// just read the wallet here, no need for a write lock
let _ = wallet.read_wallet(|wallet_data| {
// get the current height via the api
// if we cannot get the current height use the max height known to the wallet
let current_height = match checker::get_tip_from_node(wallet_data.node_url()) {
let current_height = match updater::get_tip_from_node(wallet_data.node_url()) {
Ok(tip) => tip.height,
Err(_) => match wallet_data.outputs().values().map(|out| out.height).max() {
Some(height) => height,

View file

@ -19,16 +19,17 @@
use std::sync::{Arc, RwLock};
use bodyparser;
use iron::Handler;
use iron::prelude::*;
use iron::status;
use iron::Handler;
use serde_json;
use api;
use core::consensus::reward;
use core::core::{Output, TxKernel};
use core::global;
use failure::{Fail, ResultExt};
use error::Error;
use failure::Fail;
use libtx::{reward, slate::Slate};
use libwallet::types::*;
use libwallet::{keys, selection};
@ -60,19 +61,15 @@ where
selection::build_recipient_output_with_slate(wallet, slate).unwrap();
// fill public keys
let _ = slate
.fill_round_1(
wallet.keychain(),
&mut context.sec_key,
&context.sec_nonce,
1,
)
.context(ErrorKind::LibWalletError)?;
let _ = slate.fill_round_1(
wallet.keychain(),
&mut context.sec_key,
&context.sec_nonce,
1,
)?;
// perform partial sig
let _ = slate
.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 1)
.context(ErrorKind::LibWalletError)?;
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 1)?;
// Save output in wallet
let _ = receiver_create_fn(wallet);

View file

@ -13,15 +13,14 @@
// limitations under the License.
use api;
use checker;
use client;
use core::core::amount_to_hr_string;
use core::ser;
use failure::ResultExt;
use error::{Error, ErrorKind};
use failure::{self, ResultExt};
use keychain::{Identifier, Keychain};
use libtx::{build, tx_fee};
use libwallet::selection;
use libwallet::types::*;
use libwallet::types::WalletBackend;
use libwallet::{selection, updater};
use receiver::TxWrapper;
use util;
use util::LOGGER;
@ -38,7 +37,7 @@ pub fn issue_send_tx<T: WalletBackend>(
max_outputs: usize,
selection_strategy_is_use_all: bool,
fluff: bool,
) -> Result<(), Error> {
) -> Result<(), failure::Error> {
// TODO: Stdout option, probably in a separate implementation
if &dest[..4] != "http" {
panic!(
@ -47,13 +46,13 @@ pub fn issue_send_tx<T: WalletBackend>(
);
}
checker::refresh_outputs(wallet)?;
updater::refresh_outputs(wallet)?;
// Get lock height
let chain_tip = checker::get_tip_from_node(wallet.node_url())?;
let chain_tip = updater::get_tip_from_node(wallet.node_url())?;
let current_height = chain_tip.height;
// ensure outputs we're selecting are up to date
checker::refresh_outputs(wallet)?;
updater::refresh_outputs(wallet)?;
let lock_height = current_height;
@ -73,52 +72,35 @@ pub fn issue_send_tx<T: WalletBackend>(
lock_height,
max_outputs,
selection_strategy_is_use_all,
).unwrap();
)?;
// Generate a kernel offset and subtract from our context's secret key. Store
// the offset in the slate's transaction kernel, and adds our public key
// information to the slate
let _ = slate
.fill_round_1(
wallet.keychain(),
&mut context.sec_key,
&context.sec_nonce,
0,
)
.unwrap();
let _ = slate.fill_round_1(
wallet.keychain(),
&mut context.sec_key,
&context.sec_nonce,
0,
)?;
let url = format!("{}/v1/receive/transaction", &dest);
debug!(LOGGER, "Posting partial transaction to {}", url);
let mut slate = match client::send_slate(&url, &slate, fluff) {
Ok(s) => s,
Err(e) => {
match e.kind() {
ErrorKind::FeeExceedsAmount {
sender_amount,
recipient_fee,
} => error!(
LOGGER,
"Recipient rejected the transfer because transaction fee ({}) exceeded amount ({}).",
amount_to_hr_string(recipient_fee),
amount_to_hr_string(sender_amount)
),
_ => error!(
LOGGER,
"Communication with receiver failed on SenderInitiation send. Aborting transaction"
),
}
return Err(e);
error!(
LOGGER,
"Communication with receiver failed on SenderInitiation send. Aborting transaction"
);
return Err(e)?;
}
};
let _ = slate
.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 0)
.context(ErrorKind::LibWalletError)?;
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 0)?;
// Final transaction can be built by anyone at this stage
slate
.finalize(wallet.keychain())
.context(ErrorKind::LibWalletError)?;
slate.finalize(wallet.keychain())?;
// So let's post it
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
@ -143,10 +125,10 @@ pub fn issue_burn_tx<T: WalletBackend>(
) -> Result<(), Error> {
let keychain = &Keychain::burn_enabled(wallet.keychain(), &Identifier::zero());
let chain_tip = checker::get_tip_from_node(wallet.node_url())?;
let chain_tip = updater::get_tip_from_node(wallet.node_url())?;
let current_height = chain_tip.height;
let _ = checker::refresh_outputs(wallet);
let _ = updater::refresh_outputs(wallet);
let key_id = keychain.root_key_id();
@ -171,8 +153,8 @@ pub fn issue_burn_tx<T: WalletBackend>(
parts.push(build::output(amount - fee, Identifier::zero()));
// finalize the burn transaction and send
let tx_burn = build::transaction(parts, &keychain).context(ErrorKind::Keychain)?;
tx_burn.validate().context(ErrorKind::Transaction)?;
let tx_burn = build::transaction(parts, &keychain)?;
tx_burn.validate()?;
let tx_hex = util::to_hex(ser::ser_vec(&tx_burn).unwrap());
let url = format!("{}/v1/pool/push", wallet.node_url());

View file

@ -13,8 +13,8 @@
// limitations under the License.
//! Common functions to facilitate wallet, walletlib and transaction testing
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
extern crate grin_api as api;
extern crate grin_chain as chain;
@ -29,7 +29,8 @@ use core::core::{Output, OutputFeatures, OutputIdentifier, Transaction, TxKernel
use core::{consensus, global, pow};
use wallet::file_wallet::*;
use wallet::libwallet::types::*;
use wallet::{checker, BlockFees};
use wallet::libwallet::updater;
use wallet::libwallet::{Error, ErrorKind};
use util::secp::pedersen;
@ -39,7 +40,7 @@ pub fn refresh_output_state_local<T: WalletBackend>(
wallet: &mut T,
chain: &chain::Chain,
) -> Result<(), Error> {
let wallet_outputs = checker::map_wallet_outputs(wallet)?;
let wallet_outputs = updater::map_wallet_outputs(wallet)?;
let chain_outputs: Vec<Option<api::Output>> = wallet_outputs
.keys()
.map(|k| match get_output_local(chain, &k) {
@ -56,7 +57,7 @@ pub fn refresh_output_state_local<T: WalletBackend>(
None => {}
}
}
checker::apply_api_outputs(wallet, &wallet_outputs, &api_outputs)?;
updater::apply_api_outputs(wallet, &wallet_outputs, &api_outputs)?;
Ok(())
}
@ -119,7 +120,9 @@ fn get_output_local(
return Ok(api::Output::new(&commit));
}
}
Err(ErrorKind::Transaction)?
Err(ErrorKind::GenericError(
"Can't get output from local instance of chain",
))?
}
/// Adds a block with a given reward to the chain and mines it