mirror of
https://github.com/mimblewimble/grin.git
synced 2025-05-07 01:31:15 +03:00
Libwallet refactoring - Library functions + ErrorTypes (#1113)
* 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:
parent
bbedeeaca3
commit
7812a02233
26 changed files with 701 additions and 397 deletions
core/src
servers/tests/framework
src/bin
wallet
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
|
|
|
@ -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
169
wallet/src/error.rs
Normal 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())),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(())
|
||||
|
|
182
wallet/src/libwallet/error.rs
Normal file
182
wallet/src/libwallet/error.rs
Normal 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)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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
|
|
@ -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();
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue