mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-02-01 08:51:09 +03:00
APIV2 refactoring (#23)
* continue refactor * rustfmt * further refactoring * impl crate compiling * main crate compilation * rustfmt * test fix * rustfmt * test compilation * rustfmt * refwallet tests passing * rustfmt * all tests passing * move http listener startup out of adapter * rustfmt * rename refwallet->controller * rustfmt * travis tests and api doctests * rustfmt
This commit is contained in:
parent
8cca9821bd
commit
db015960a9
50 changed files with 1760 additions and 564 deletions
|
@ -54,7 +54,9 @@ matrix:
|
|||
- os: linux
|
||||
env: CI_JOB="test" CI_JOB_ARGS="config libwallet api"
|
||||
- os: linux
|
||||
env: CI_JOB="test" CI_JOB_ARGS="refwallet ."
|
||||
env: CI_JOB="test" CI_JOB_ARGS="impls"
|
||||
- os: linux
|
||||
env: CI_JOB="test" CI_JOB_ARGS="controller ."
|
||||
- os: linux
|
||||
env: CI_JOB="release" CI_JOB_ARGS=
|
||||
- os: osx
|
||||
|
|
101
Cargo.lock
generated
101
Cargo.lock
generated
|
@ -764,8 +764,9 @@ dependencies = [
|
|||
"grin_util 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_wallet_api 1.1.0",
|
||||
"grin_wallet_config 1.1.0",
|
||||
"grin_wallet_controller 1.1.0",
|
||||
"grin_wallet_impls 1.1.0",
|
||||
"grin_wallet_libwallet 1.1.0",
|
||||
"grin_wallet_refwallet 1.1.0",
|
||||
"linefeed 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"prettytable-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -786,8 +787,8 @@ dependencies = [
|
|||
"grin_store 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_util 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_wallet_config 1.1.0",
|
||||
"grin_wallet_impls 1.1.0",
|
||||
"grin_wallet_libwallet 1.1.0",
|
||||
"grin_wallet_refwallet 1.1.0",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -808,6 +809,68 @@ dependencies = [
|
|||
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grin_wallet_controller"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"grin_api 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_chain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_core 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_keychain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_util 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_wallet_api 1.1.0",
|
||||
"grin_wallet_config 1.1.0",
|
||||
"grin_wallet_impls 1.1.0",
|
||||
"grin_wallet_libwallet 1.1.0",
|
||||
"hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"prettytable-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grin_wallet_impls"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"grin_api 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_chain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_core 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_keychain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_store 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_util 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_wallet_config 1.1.0",
|
||||
"grin_wallet_libwallet 1.1.0",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grin_wallet_libwallet"
|
||||
version = "1.1.0"
|
||||
|
@ -829,40 +892,6 @@ dependencies = [
|
|||
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grin_wallet_refwallet"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"grin_api 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_chain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_core 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_keychain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_store 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_util 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)",
|
||||
"grin_wallet_api 1.1.0",
|
||||
"grin_wallet_config 1.1.0",
|
||||
"grin_wallet_libwallet 1.1.0",
|
||||
"hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"prettytable-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.1.16"
|
||||
|
|
|
@ -16,7 +16,7 @@ name = "grin-wallet"
|
|||
path = "src/bin/grin-wallet.rs"
|
||||
|
||||
[workspace]
|
||||
members = ["api", "config", "libwallet", "refwallet"]
|
||||
members = ["api", "config", "controller", "impls", "libwallet"]
|
||||
exclude = ["integration"]
|
||||
|
||||
[dependencies]
|
||||
|
@ -30,8 +30,9 @@ log = "0.4"
|
|||
linefeed = "0.5"
|
||||
|
||||
grin_wallet_api = { path = "./api", version = "1.1.0" }
|
||||
grin_wallet_impls = { path = "./impls", version = "1.1.0" }
|
||||
grin_wallet_libwallet = { path = "./libwallet", version = "1.1.0" }
|
||||
grin_wallet_refwallet = { path = "./refwallet", version = "1.1.0" }
|
||||
grin_wallet_controller = { path = "./controller", version = "1.1.0" }
|
||||
grin_wallet_config = { path = "./config", version = "1.1.0" }
|
||||
|
||||
grin_core = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
||||
|
|
|
@ -28,6 +28,6 @@ grin_api = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1
|
|||
grin_store = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
grin_wallet_refwallet = { path = "../refwallet", version = "1.1.0" }
|
||||
grin_wallet_impls = { path = "../impls", version = "1.1.0" }
|
||||
serde_json = "1"
|
||||
tempfile = "3.0.7"
|
||||
|
|
|
@ -27,17 +27,14 @@
|
|||
//! seed).
|
||||
|
||||
use crate::keychain::Keychain;
|
||||
use crate::libwallet::internal::{tx, updater};
|
||||
use crate::libwallet::api_impl::foreign;
|
||||
use crate::libwallet::slate::Slate;
|
||||
use crate::libwallet::types::{BlockFees, CbData, NodeClient, TxLogEntryType, WalletBackend};
|
||||
use crate::libwallet::{Error, ErrorKind};
|
||||
use crate::util::secp::{ContextFlag, Secp256k1};
|
||||
use crate::libwallet::types::{BlockFees, CbData, NodeClient, WalletBackend};
|
||||
use crate::libwallet::Error;
|
||||
use crate::util::Mutex;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
const USER_MESSAGE_MAX_LEN: usize = 256;
|
||||
|
||||
/// Wrapper around external API functions, intended to communicate
|
||||
/// with other parties
|
||||
pub struct Foreign<W: ?Sized, C, K>
|
||||
|
@ -72,16 +69,14 @@ where
|
|||
pub fn build_coinbase(&self, block_fees: &BlockFees) -> Result<CbData, Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let res = updater::build_coinbase(&mut *w, block_fees);
|
||||
let res = foreign::build_coinbase(&mut *w, block_fees);
|
||||
w.close()?;
|
||||
res
|
||||
}
|
||||
|
||||
/// Verifies all messages in the slate match their public keys
|
||||
pub fn verify_slate_messages(&self, slate: &Slate) -> Result<(), Error> {
|
||||
let secp = Secp256k1::with_caps(ContextFlag::VerifyOnly);
|
||||
slate.verify_messages(&secp)?;
|
||||
Ok(())
|
||||
foreign::verify_slate_messages(slate)
|
||||
}
|
||||
|
||||
/// Receive a transaction from a sender
|
||||
|
@ -93,35 +88,8 @@ where
|
|||
) -> Result<(), Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let parent_key_id = match dest_acct_name {
|
||||
Some(d) => {
|
||||
let pm = w.get_acct_path(d.to_owned())?;
|
||||
match pm {
|
||||
Some(p) => p.path,
|
||||
None => w.parent_key_id(),
|
||||
}
|
||||
}
|
||||
None => w.parent_key_id(),
|
||||
};
|
||||
// Don't do this multiple times
|
||||
let tx = updater::retrieve_txs(&mut *w, None, Some(slate.id), Some(&parent_key_id), false)?;
|
||||
for t in &tx {
|
||||
if t.tx_type == TxLogEntryType::TxReceived {
|
||||
return Err(ErrorKind::TransactionAlreadyReceived(slate.id.to_string()).into());
|
||||
}
|
||||
}
|
||||
|
||||
let message = match message {
|
||||
Some(mut m) => {
|
||||
m.truncate(USER_MESSAGE_MAX_LEN);
|
||||
Some(m)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
tx::add_output_to_slate(&mut *w, slate, &parent_key_id, 1, message)?;
|
||||
tx::update_message(&mut *w, slate)?;
|
||||
let res = foreign::receive_tx(&mut *w, slate, dest_acct_name, message);
|
||||
w.close()?;
|
||||
Ok(())
|
||||
res
|
||||
}
|
||||
}
|
||||
|
|
|
@ -209,7 +209,8 @@ macro_rules! doctest_helper_json_rpc_foreign_assert_response {
|
|||
use grin_util::Mutex;
|
||||
use grin_wallet_api::{Foreign, ForeignRpc};
|
||||
use grin_wallet_config::WalletConfig;
|
||||
use grin_wallet_refwallet::{HTTPNodeClient, LMDBBackend, WalletBackend};
|
||||
use grin_wallet_impls::{HTTPNodeClient, LMDBBackend};
|
||||
use grin_wallet_libwallet::types::WalletBackend;
|
||||
use serde_json;
|
||||
use std::sync::Arc;
|
||||
use tempfile::tempdir;
|
||||
|
|
208
api/src/owner.rs
208
api/src/owner.rs
|
@ -31,20 +31,15 @@ use std::marker::PhantomData;
|
|||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::core::core::hash::Hashed;
|
||||
use crate::core::core::Transaction;
|
||||
use crate::core::ser;
|
||||
use crate::keychain::{Identifier, Keychain};
|
||||
use crate::libwallet::internal::{keys, selection, tx, updater};
|
||||
use crate::libwallet::api_impl::owner;
|
||||
use crate::libwallet::slate::Slate;
|
||||
use crate::libwallet::types::{
|
||||
AcctPathMapping, NodeClient, OutputData, TxLogEntry, TxWrapper, WalletBackend, WalletInfo,
|
||||
AcctPathMapping, NodeClient, OutputData, TxLogEntry, WalletBackend, WalletInfo,
|
||||
};
|
||||
use crate::libwallet::{Error, ErrorKind};
|
||||
use crate::util;
|
||||
use crate::util::secp::{pedersen, ContextFlag, Secp256k1};
|
||||
|
||||
const USER_MESSAGE_MAX_LEN: usize = 256;
|
||||
use crate::libwallet::Error;
|
||||
use crate::util::secp::pedersen;
|
||||
|
||||
/// Functions intended for use by the owner (e.g. master seed holder) of the wallet.
|
||||
pub struct Owner<W: ?Sized, C, K>
|
||||
|
@ -169,7 +164,7 @@ where
|
|||
|
||||
pub fn accounts(&self) -> Result<Vec<AcctPathMapping>, Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
keys::accounts(&mut *w)
|
||||
owner::accounts(&mut *w)
|
||||
}
|
||||
|
||||
/// Creates a new 'account', which is a mapping of a user-specified
|
||||
|
@ -225,7 +220,7 @@ where
|
|||
|
||||
pub fn create_account_path(&self, label: &str) -> Result<Identifier, Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
keys::new_acct_path(&mut *w, label)
|
||||
owner::create_account_path(&mut *w, label)
|
||||
}
|
||||
|
||||
/// Sets the wallet's currently active account. This sets the
|
||||
|
@ -280,8 +275,7 @@ where
|
|||
|
||||
pub fn set_active_account(&self, label: &str) -> Result<(), Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.set_parent_key_id_by_name(label)?;
|
||||
Ok(())
|
||||
owner::set_active_account(&mut *w, label)
|
||||
}
|
||||
|
||||
/// Returns a list of outputs from the active account in the wallet.
|
||||
|
@ -346,18 +340,7 @@ where
|
|||
) -> Result<(bool, Vec<(OutputData, pedersen::Commitment)>), Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let parent_key_id = w.parent_key_id();
|
||||
|
||||
let mut validated = false;
|
||||
if refresh_from_node {
|
||||
validated = self.update_outputs(&mut w, false);
|
||||
}
|
||||
|
||||
let res = Ok((
|
||||
validated,
|
||||
updater::retrieve_outputs(&mut *w, include_spent, tx_id, Some(&parent_key_id))?,
|
||||
));
|
||||
|
||||
let res = owner::retrieve_outputs(&mut *w, include_spent, refresh_from_node, tx_id);
|
||||
w.close()?;
|
||||
res
|
||||
}
|
||||
|
@ -424,18 +407,7 @@ where
|
|||
) -> Result<(bool, Vec<TxLogEntry>), Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let parent_key_id = w.parent_key_id();
|
||||
|
||||
let mut validated = false;
|
||||
if refresh_from_node {
|
||||
validated = self.update_outputs(&mut w, false);
|
||||
}
|
||||
|
||||
let res = Ok((
|
||||
validated,
|
||||
updater::retrieve_txs(&mut *w, tx_id, tx_slate_id, Some(&parent_key_id), false)?,
|
||||
));
|
||||
|
||||
let res = owner::retrieve_txs(&mut *w, refresh_from_node, tx_id, tx_slate_id);
|
||||
w.close()?;
|
||||
res
|
||||
}
|
||||
|
@ -496,16 +468,7 @@ where
|
|||
) -> Result<(bool, WalletInfo), Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let parent_key_id = w.parent_key_id();
|
||||
|
||||
let mut validated = false;
|
||||
if refresh_from_node {
|
||||
validated = self.update_outputs(&mut w, false);
|
||||
}
|
||||
|
||||
let wallet_info = updater::retrieve_info(&mut *w, &parent_key_id, minimum_confirmations)?;
|
||||
let res = Ok((validated, wallet_info));
|
||||
|
||||
let res = owner::retrieve_summary_info(&mut *w, refresh_from_node, minimum_confirmations);
|
||||
w.close()?;
|
||||
res
|
||||
}
|
||||
|
@ -627,53 +590,19 @@ where
|
|||
) -> Result<Slate, Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let parent_key_id = match src_acct_name {
|
||||
Some(d) => {
|
||||
let pm = w.get_acct_path(d.to_owned())?;
|
||||
match pm {
|
||||
Some(p) => p.path,
|
||||
None => w.parent_key_id(),
|
||||
}
|
||||
}
|
||||
None => w.parent_key_id(),
|
||||
};
|
||||
|
||||
let message = match message {
|
||||
Some(mut m) => {
|
||||
m.truncate(USER_MESSAGE_MAX_LEN);
|
||||
Some(m)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut slate = tx::new_tx_slate(&mut *w, amount, 2)?;
|
||||
|
||||
let context = tx::add_inputs_to_slate(
|
||||
let res = owner::initiate_tx(
|
||||
&mut *w,
|
||||
&mut slate,
|
||||
src_acct_name,
|
||||
amount,
|
||||
minimum_confirmations,
|
||||
max_outputs,
|
||||
num_change_outputs,
|
||||
selection_strategy_is_use_all,
|
||||
&parent_key_id,
|
||||
0,
|
||||
message,
|
||||
)?;
|
||||
|
||||
// Save the aggsig context in our DB for when we
|
||||
// recieve the transaction back
|
||||
{
|
||||
let mut batch = w.batch()?;
|
||||
batch.save_private_context(slate.id.as_bytes(), &context)?;
|
||||
batch.commit()?;
|
||||
}
|
||||
|
||||
target_slate_version,
|
||||
);
|
||||
w.close()?;
|
||||
// set target slate version
|
||||
if let Some(v) = target_slate_version {
|
||||
slate.version_info.orig_version = v;
|
||||
}
|
||||
Ok(slate)
|
||||
res
|
||||
}
|
||||
|
||||
/// Estimates the amount to be locked and fee for the transaction without creating one
|
||||
|
@ -723,25 +652,17 @@ where
|
|||
> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let parent_key_id = match src_acct_name {
|
||||
Some(d) => {
|
||||
let pm = w.get_acct_path(d.to_owned())?;
|
||||
match pm {
|
||||
Some(p) => p.path,
|
||||
None => w.parent_key_id(),
|
||||
}
|
||||
}
|
||||
None => w.parent_key_id(),
|
||||
};
|
||||
tx::estimate_send_tx(
|
||||
let res = owner::estimate_initiate_tx(
|
||||
&mut *w,
|
||||
src_acct_name,
|
||||
amount,
|
||||
minimum_confirmations,
|
||||
max_outputs,
|
||||
num_change_outputs,
|
||||
selection_strategy_is_use_all,
|
||||
&parent_key_id,
|
||||
)
|
||||
);
|
||||
w.close()?;
|
||||
res
|
||||
}
|
||||
|
||||
/// Lock outputs associated with a given slate/transaction
|
||||
|
@ -749,11 +670,9 @@ where
|
|||
pub fn tx_lock_outputs(&self, slate: &Slate) -> Result<(), Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let context = w.get_private_context(slate.id.as_bytes())?;
|
||||
w.open_with_credentials()?;
|
||||
selection::lock_tx_context(&mut *w, slate, &context)?;
|
||||
let res = owner::tx_lock_outputs(&mut *w, slate);
|
||||
w.close()?;
|
||||
Ok(())
|
||||
res
|
||||
}
|
||||
|
||||
/// Sender finalization of the transaction. Takes the file returned by the
|
||||
|
@ -763,17 +682,9 @@ where
|
|||
pub fn finalize_tx(&self, slate: &mut Slate) -> Result<(), Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let context = w.get_private_context(slate.id.as_bytes())?;
|
||||
tx::complete_tx(&mut *w, slate, 0, &context)?;
|
||||
tx::update_stored_tx(&mut *w, slate)?;
|
||||
tx::update_message(&mut *w, slate)?;
|
||||
{
|
||||
let mut batch = w.batch()?;
|
||||
batch.delete_private_context(slate.id.as_bytes())?;
|
||||
batch.commit()?;
|
||||
}
|
||||
let res = owner::finalize_tx(&mut *w, slate);
|
||||
w.close()?;
|
||||
Ok(())
|
||||
res
|
||||
}
|
||||
|
||||
/// Roll back a transaction and all associated outputs with a given
|
||||
|
@ -784,96 +695,55 @@ where
|
|||
pub fn cancel_tx(&self, tx_id: Option<u32>, tx_slate_id: Option<Uuid>) -> Result<(), Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let parent_key_id = w.parent_key_id();
|
||||
if !self.update_outputs(&mut w, false) {
|
||||
return Err(ErrorKind::TransactionCancellationError(
|
||||
"Can't contact running Grin node. Not Cancelling.",
|
||||
))?;
|
||||
}
|
||||
tx::cancel_tx(&mut *w, &parent_key_id, tx_id, tx_slate_id)?;
|
||||
let res = owner::cancel_tx(&mut *w, tx_id, tx_slate_id);
|
||||
w.close()?;
|
||||
Ok(())
|
||||
res
|
||||
}
|
||||
|
||||
/// Retrieves a stored transaction from a TxLogEntry
|
||||
pub fn get_stored_tx(&self, entry: &TxLogEntry) -> Result<Option<Transaction>, Error> {
|
||||
let w = self.wallet.lock();
|
||||
w.get_stored_tx(entry)
|
||||
owner::get_stored_tx(&*w, entry)
|
||||
}
|
||||
|
||||
/// Posts a transaction to the chain
|
||||
pub fn post_tx(&self, tx: &Transaction, fluff: bool) -> Result<(), Error> {
|
||||
let tx_hex = util::to_hex(ser::ser_vec(tx).unwrap());
|
||||
let client = {
|
||||
let mut w = self.wallet.lock();
|
||||
w.w2n_client().clone()
|
||||
};
|
||||
let res = client.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff);
|
||||
if let Err(e) = res {
|
||||
error!("api: post_tx: failed with error: {}", e);
|
||||
Err(e)
|
||||
} else {
|
||||
debug!(
|
||||
"api: post_tx: successfully posted tx: {}, fluff? {}",
|
||||
tx.hash(),
|
||||
fluff
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
owner::post_tx(&client, tx, fluff)
|
||||
}
|
||||
|
||||
/// Verifies all messages in the slate match their public keys
|
||||
pub fn verify_slate_messages(&self, slate: &Slate) -> Result<(), Error> {
|
||||
let secp = Secp256k1::with_caps(ContextFlag::VerifyOnly);
|
||||
slate.verify_messages(&secp)?;
|
||||
Ok(())
|
||||
owner::verify_slate_messages(slate)
|
||||
}
|
||||
|
||||
/// Attempt to restore contents of wallet
|
||||
pub fn restore(&self) -> Result<(), Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
w.restore()?;
|
||||
let res = owner::restore(&mut *w);
|
||||
w.close()?;
|
||||
Ok(())
|
||||
res
|
||||
}
|
||||
|
||||
/// Attempt to check and fix the contents of the wallet
|
||||
pub fn check_repair(&self, delete_unconfirmed: bool) -> Result<(), Error> {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
self.update_outputs(&mut w, true);
|
||||
w.check_repair(delete_unconfirmed)?;
|
||||
let res = owner::check_repair(&mut *w, delete_unconfirmed);
|
||||
w.close()?;
|
||||
Ok(())
|
||||
res
|
||||
}
|
||||
|
||||
/// Retrieve current height from node
|
||||
pub fn node_height(&self) -> Result<(u64, bool), Error> {
|
||||
let res = {
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
w.w2n_client().get_chain_height()
|
||||
};
|
||||
match res {
|
||||
Ok(height) => Ok((height, true)),
|
||||
Err(_) => {
|
||||
let outputs = self.retrieve_outputs(true, false, None)?;
|
||||
let height = match outputs.1.iter().map(|(out, _)| out.height).max() {
|
||||
Some(height) => height,
|
||||
None => 0,
|
||||
};
|
||||
Ok((height, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to update outputs in wallet, return whether it was successful
|
||||
fn update_outputs(&self, w: &mut W, update_all: bool) -> bool {
|
||||
let parent_key_id = w.parent_key_id();
|
||||
match updater::refresh_outputs(&mut *w, &parent_key_id, update_all) {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
}
|
||||
let mut w = self.wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let res = owner::node_height(&mut *w);
|
||||
w.close()?;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
|
|
@ -737,7 +737,8 @@ macro_rules! doctest_helper_json_rpc_owner_assert_response {
|
|||
use grin_util::Mutex;
|
||||
use grin_wallet_api::{Owner, OwnerRpc};
|
||||
use grin_wallet_config::WalletConfig;
|
||||
use grin_wallet_refwallet::{HTTPNodeClient, LMDBBackend, WalletBackend};
|
||||
use grin_wallet_impls::{HTTPNodeClient, LMDBBackend};
|
||||
use grin_wallet_libwallet::types::WalletBackend;
|
||||
use serde_json;
|
||||
use std::sync::Arc;
|
||||
use tempfile::tempdir;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "grin_wallet_refwallet"
|
||||
name = "grin_wallet_controller"
|
||||
version = "1.1.0"
|
||||
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
|
||||
description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
|
||||
description = "Controllers for grin wallet instantiation"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/mimblewimble/grin"
|
||||
keywords = [ "crypto", "grin", "mimblewimble" ]
|
||||
|
@ -12,7 +12,6 @@ exclude = ["**/*.grin", "**/*.grin2"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
blake2-rfc = "0.2"
|
||||
failure = "0.1"
|
||||
failure_derive = "0.1"
|
||||
futures = "0.1"
|
||||
|
@ -33,6 +32,7 @@ url = "1.7.0"
|
|||
chrono = { version = "0.4.4", features = ["serde"] }
|
||||
|
||||
grin_wallet_api = { path = "../api", version = "1.1.0" }
|
||||
grin_wallet_impls = { path = "../impls", version = "1.1.0" }
|
||||
grin_wallet_libwallet = { path = "../libwallet", version = "1.1.0" }
|
||||
grin_wallet_config = { path = "../config", version = "1.1.0" }
|
||||
|
||||
|
@ -41,4 +41,3 @@ grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "milest
|
|||
grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
||||
grin_util = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
||||
grin_api = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
||||
grin_store = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
|
@ -28,12 +28,15 @@ use crate::api::TLSConfig;
|
|||
use crate::core::core;
|
||||
use crate::keychain;
|
||||
|
||||
use crate::config::WalletConfig;
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::{controller, display, HTTPNodeClient, WalletConfig, WalletInst, WalletSeed};
|
||||
use crate::{
|
||||
FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter, LMDBBackend,
|
||||
NodeClient, NullWalletCommAdapter,
|
||||
use crate::impls::{
|
||||
instantiate_wallet, FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter,
|
||||
LMDBBackend, NullWalletCommAdapter,
|
||||
};
|
||||
use crate::impls::{HTTPNodeClient, WalletSeed};
|
||||
use crate::libwallet::types::{NodeClient, WalletInst};
|
||||
use crate::{controller, display};
|
||||
|
||||
/// Arguments common to all wallet commands
|
||||
#[derive(Clone)]
|
||||
|
@ -90,7 +93,7 @@ pub fn recover(config: &WalletConfig, args: RecoverArgs) -> Result<(), Error> {
|
|||
let res = WalletSeed::from_file(config, &args.passphrase);
|
||||
if let Err(e) = res {
|
||||
error!("Error loading wallet seed (check password): {}", e);
|
||||
return Err(e);
|
||||
return Err(e.into());
|
||||
}
|
||||
let _ = res.unwrap().show_recovery_phrase();
|
||||
} else {
|
||||
|
@ -101,7 +104,7 @@ pub fn recover(config: &WalletConfig, args: RecoverArgs) -> Result<(), Error> {
|
|||
);
|
||||
if let Err(e) = res {
|
||||
error!("Error recovering seed - {}", e);
|
||||
return Err(e);
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -119,19 +122,44 @@ pub fn listen(config: &WalletConfig, args: &ListenArgs, g_args: &GlobalArgs) ->
|
|||
params.insert("certificate".to_owned(), t.certificate.clone());
|
||||
params.insert("private_key".to_owned(), t.private_key.clone());
|
||||
}
|
||||
let adapter = match args.method.as_str() {
|
||||
"http" => HTTPWalletCommAdapter::new(),
|
||||
"keybase" => KeybaseWalletCommAdapter::new(),
|
||||
_ => NullWalletCommAdapter::new(),
|
||||
let res = match args.method.as_str() {
|
||||
"http" => {
|
||||
// HTTP adapter can't use the listen trait method because of the
|
||||
// crate structure. May be able to fix when V1 API is deprecated
|
||||
let node_client = HTTPNodeClient::new(
|
||||
&config.check_node_api_http_addr,
|
||||
g_args.node_api_secret.clone(),
|
||||
);
|
||||
let wallet = instantiate_wallet(
|
||||
config.clone(),
|
||||
node_client,
|
||||
&g_args.password.clone().unwrap(),
|
||||
&g_args.account,
|
||||
)?;
|
||||
let listen_addr = params.get("api_listen_addr").unwrap();
|
||||
let tls_conf = match params.get("certificate") {
|
||||
Some(s) => Some(grin_api::TLSConfig::new(
|
||||
s.to_owned(),
|
||||
params.get("private_key").unwrap().to_owned(),
|
||||
)),
|
||||
None => None,
|
||||
};
|
||||
controller::foreign_listener(wallet.clone(), &listen_addr, tls_conf)?;
|
||||
Ok(())
|
||||
}
|
||||
"keybase" => {
|
||||
let adapter = KeybaseWalletCommAdapter::new();
|
||||
adapter.listen(
|
||||
params,
|
||||
config.clone(),
|
||||
&g_args.password.clone().unwrap(),
|
||||
&g_args.account,
|
||||
g_args.node_api_secret.clone(),
|
||||
)
|
||||
}
|
||||
_ => Ok(()),
|
||||
};
|
||||
|
||||
let res = adapter.listen(
|
||||
params,
|
||||
config.clone(),
|
||||
&g_args.password.clone().unwrap(),
|
||||
&g_args.account,
|
||||
g_args.node_api_secret.clone(),
|
||||
);
|
||||
if let Err(e) = res {
|
||||
return Err(ErrorKind::LibWallet(e.kind(), e.cause_string()).into());
|
||||
}
|
|
@ -15,11 +15,11 @@
|
|||
//! Controller for wallet.. instantiates and handles listeners (or single-run
|
||||
//! invocations) as needed.
|
||||
//! Still experimental
|
||||
use crate::adapters::{FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter};
|
||||
use crate::api::{ApiServer, BasicAuthMiddleware, Handler, ResponseFuture, Router, TLSConfig};
|
||||
use crate::apiwallet::{Foreign, Owner};
|
||||
use crate::core::core;
|
||||
use crate::core::core::Transaction;
|
||||
use crate::impls::{FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter};
|
||||
use crate::keychain::Keychain;
|
||||
use crate::libwallet::slate::Slate;
|
||||
use crate::libwallet::types::{
|
|
@ -16,6 +16,7 @@
|
|||
use crate::api;
|
||||
use crate::core::core::transaction;
|
||||
use crate::core::libtx;
|
||||
use crate::impls;
|
||||
use crate::keychain;
|
||||
use crate::libwallet;
|
||||
use failure::{Backtrace, Context, Fail};
|
||||
|
@ -35,6 +36,10 @@ pub enum ErrorKind {
|
|||
#[fail(display = "LibTx Error")]
|
||||
LibTX(libtx::ErrorKind),
|
||||
|
||||
/// Impls error
|
||||
#[fail(display = "Impls Error")]
|
||||
Impls(impls::ErrorKind),
|
||||
|
||||
/// LibWallet Error
|
||||
#[fail(display = "LibWallet Error: {}", _1)]
|
||||
LibWallet(libwallet::ErrorKind, String),
|
||||
|
@ -208,3 +213,11 @@ impl From<libtx::Error> for Error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<impls::Error> for Error {
|
||||
fn from(error: impls::Error) -> Error {
|
||||
Error {
|
||||
inner: Context::new(ErrorKind::Impls(error.kind())),
|
||||
}
|
||||
}
|
||||
}
|
37
controller/src/lib.rs
Normal file
37
controller/src/lib.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
// 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.
|
||||
|
||||
//! Library module for the main wallet functionalities provided by Grin.
|
||||
|
||||
#[macro_use]
|
||||
extern crate prettytable;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
use failure;
|
||||
use grin_api as api;
|
||||
extern crate grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use grin_wallet_api as apiwallet;
|
||||
use grin_wallet_impls as impls;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
extern crate grin_wallet_config as config;
|
||||
|
||||
pub mod command;
|
||||
pub mod controller;
|
||||
pub mod display;
|
||||
mod error;
|
||||
|
||||
pub use crate::error::{Error, ErrorKind};
|
|
@ -14,7 +14,8 @@
|
|||
//! tests differing accounts in the same wallet
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_refwallet as wallet;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use self::core::global;
|
||||
use self::core::global::ChainTypes;
|
||||
|
@ -23,10 +24,10 @@ use grin_core as core;
|
|||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use std::fs;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use wallet::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
|
||||
fn clean_output_dir(test_dir: &str) {
|
||||
let _ = fs::remove_dir_all(test_dir);
|
|
@ -14,7 +14,8 @@
|
|||
//! tests differing accounts in the same wallet
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_refwallet as wallet;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use self::core::consensus;
|
||||
use self::core::global;
|
||||
|
@ -24,11 +25,32 @@ use grin_core as core;
|
|||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use impls::FileWalletCommAdapter;
|
||||
use libwallet::types::WalletInst;
|
||||
use std::fs;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use wallet::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use wallet::FileWalletCommAdapter;
|
||||
|
||||
macro_rules! send_to_dest {
|
||||
($a:expr, $b:expr, $c:expr, $d:expr) => {
|
||||
test_framework::send_to_dest::<
|
||||
WalletInst<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>($a, $b, $c, $d)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! wallet_info {
|
||||
($a:expr) => {
|
||||
test_framework::wallet_info::<
|
||||
WalletInst<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>($a)
|
||||
};
|
||||
}
|
||||
|
||||
fn clean_output_dir(test_dir: &str) {
|
||||
let _ = fs::remove_dir_all(test_dir);
|
||||
|
@ -318,49 +340,37 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), bh as usize);
|
||||
|
||||
// send some funds to wallets 1
|
||||
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet1", base_amount * 1)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet1", base_amount * 2)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet1", base_amount * 3)?;
|
||||
bh += 3;
|
||||
Ok(())
|
||||
})?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet1", base_amount * 1)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet1", base_amount * 2)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet1", base_amount * 3)?;
|
||||
bh += 3;
|
||||
|
||||
// 0) Check repair when all is okay should leave wallet contents alone
|
||||
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
api.check_repair(true)?;
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet1.clone())?;
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 6);
|
||||
assert_eq!(info.total, base_amount * 6);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// send some funds to wallet 2
|
||||
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet2", base_amount * 4)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet2", base_amount * 5)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet2", base_amount * 6)?;
|
||||
bh += 3;
|
||||
Ok(())
|
||||
})?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet2", base_amount * 4)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet2", base_amount * 5)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet2", base_amount * 6)?;
|
||||
bh += 3;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm);
|
||||
bh += cm as u64;
|
||||
|
||||
// confirm balances
|
||||
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 6);
|
||||
assert_eq!(info.total, base_amount * 6);
|
||||
Ok(())
|
||||
})?;
|
||||
let info = wallet_info!(wallet1.clone())?;
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 6);
|
||||
assert_eq!(info.total, base_amount * 6);
|
||||
|
||||
wallet::controller::owner_single_use(wallet2.clone(), |api| {
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 15);
|
||||
assert_eq!(info.total, base_amount * 15);
|
||||
Ok(())
|
||||
})?;
|
||||
let info = wallet_info!(wallet2.clone())?;
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 15);
|
||||
assert_eq!(info.total, base_amount * 15);
|
||||
|
||||
// Now there should be outputs on the chain using the same
|
||||
// seed + BIP32 path.
|
||||
|
@ -372,7 +382,7 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(wallet3.clone(), |api| {
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet3.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 6);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 21);
|
||||
|
@ -387,7 +397,7 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet1.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 6);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 21);
|
||||
|
@ -396,18 +406,16 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
|
||||
// 3) If I recover from seed and start using the wallet without restoring,
|
||||
// check_repair should restore the older outputs
|
||||
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet4", base_amount * 7)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet4", base_amount * 8)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet4", base_amount * 9)?;
|
||||
bh += 3;
|
||||
Ok(())
|
||||
})?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet4", base_amount * 7)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet4", base_amount * 8)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet4", base_amount * 9)?;
|
||||
bh += 3;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm);
|
||||
bh += cm as u64;
|
||||
|
||||
wallet::controller::owner_single_use(wallet4.clone(), |api| {
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet4.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 3);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 24);
|
||||
|
@ -420,7 +428,7 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(wallet5.clone(), |api| {
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet5.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 9);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * (45));
|
||||
|
@ -429,18 +437,16 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
|
||||
// 4) If I recover from seed and start using the wallet without restoring,
|
||||
// check_repair should restore the older outputs
|
||||
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet6", base_amount * 10)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet6", base_amount * 11)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet6", base_amount * 12)?;
|
||||
bh += 3;
|
||||
Ok(())
|
||||
})?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet6", base_amount * 10)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet6", base_amount * 11)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet6", base_amount * 12)?;
|
||||
bh += 3;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm as usize);
|
||||
bh += cm as u64;
|
||||
|
||||
wallet::controller::owner_single_use(wallet6.clone(), |api| {
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet6.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 3);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 33);
|
||||
|
@ -453,7 +459,7 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(wallet6.clone(), |api| {
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet6.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 12);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * (78));
|
||||
|
@ -463,13 +469,10 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
// 5) Start using same seed with a different account, amounts should
|
||||
// be distinct and restore should return funds from other account
|
||||
|
||||
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 13)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 14)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 15)?;
|
||||
bh += 3;
|
||||
Ok(())
|
||||
})?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 13)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 14)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 15)?;
|
||||
bh += 3;
|
||||
|
||||
// mix it up a bit
|
||||
wallet::controller::owner_single_use(wallet7.clone(), |api| {
|
||||
|
@ -478,25 +481,22 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 1)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 2)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 3)?;
|
||||
bh += 3;
|
||||
Ok(())
|
||||
})?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 1)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 2)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 3)?;
|
||||
bh += 3;
|
||||
|
||||
// check balances
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm);
|
||||
bh += cm as u64;
|
||||
|
||||
wallet::controller::owner_single_use(wallet7.clone(), |api| {
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet7.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 3);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 6);
|
||||
api.set_active_account("default")?;
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet7.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 3);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 42);
|
||||
|
@ -505,12 +505,12 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
|
||||
wallet::controller::owner_single_use(wallet8.clone(), |api| {
|
||||
api.restore()?;
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet8.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 15);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 120);
|
||||
api.set_active_account("account_1")?;
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet8.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 3);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 6);
|
||||
|
@ -526,27 +526,26 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
api.set_active_account("account_1")?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet9", base_amount * 4)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet9", base_amount * 5)?;
|
||||
test_framework::send_to_dest(m_client.clone(), api, "wallet9", base_amount * 6)?;
|
||||
bh += 3;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet9", base_amount * 4)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet9", base_amount * 5)?;
|
||||
send_to_dest!(miner.clone(), m_client.clone(), "wallet9", base_amount * 6)?;
|
||||
bh += 3;
|
||||
let _bh = bh;
|
||||
|
||||
wallet::controller::owner_single_use(wallet9.clone(), |api| {
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet9.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 3);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 15);
|
||||
api.check_repair(true)?;
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet9.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 6);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 21);
|
||||
|
||||
api.set_active_account("default")?;
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet9.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 15);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 120);
|
||||
|
@ -559,13 +558,13 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
wallet::controller::owner_single_use(wallet10.clone(), |api| {
|
||||
api.check_repair(true)?;
|
||||
api.set_active_account("account_1")?;
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet10.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 6);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 21);
|
||||
|
||||
api.set_active_account("default")?;
|
||||
let info = test_framework::wallet_info(api)?;
|
||||
let info = wallet_info!(wallet10.clone())?;
|
||||
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||
assert_eq!(outputs.len(), 15);
|
||||
assert_eq!(info.amount_currently_spendable, base_amount * 120);
|
|
@ -14,17 +14,18 @@
|
|||
//! Test a wallet file send/recieve
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_refwallet as wallet;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use self::core::global;
|
||||
use self::core::global::ChainTypes;
|
||||
use self::keychain::ExtKeychain;
|
||||
use self::wallet::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use self::wallet::FileWalletCommAdapter;
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use impls::FileWalletCommAdapter;
|
||||
use std::fs;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
|
@ -14,18 +14,19 @@
|
|||
//! Test a wallet repost command
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
extern crate grin_wallet_refwallet as wallet;
|
||||
|
||||
use self::core::global;
|
||||
use self::core::global::ChainTypes;
|
||||
use self::keychain::ExtKeychain;
|
||||
use self::libwallet::slate::Slate;
|
||||
use self::wallet::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use self::wallet::FileWalletCommAdapter;
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use impls::FileWalletCommAdapter;
|
||||
use std::fs;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
|
@ -14,18 +14,19 @@
|
|||
//! tests for wallet restore
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
extern crate grin_wallet_refwallet as wallet;
|
||||
|
||||
use self::core::global;
|
||||
use self::core::global::ChainTypes;
|
||||
use self::keychain::{ExtKeychain, Identifier, Keychain};
|
||||
use self::libwallet::slate::Slate;
|
||||
use self::libwallet::types::AcctPathMapping;
|
||||
use self::wallet::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use std::fs;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
|
@ -14,16 +14,17 @@
|
|||
//! Test a wallet sending to self
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_refwallet as wallet;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use self::core::global;
|
||||
use self::core::global::ChainTypes;
|
||||
use self::keychain::ExtKeychain;
|
||||
use self::wallet::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use std::fs;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
|
@ -14,18 +14,19 @@
|
|||
//! tests for transactions building within core::libtx
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
extern crate grin_wallet_refwallet as wallet;
|
||||
|
||||
use self::core::global;
|
||||
use self::core::global::ChainTypes;
|
||||
use self::keychain::ExtKeychain;
|
||||
use self::libwallet::slate::Slate;
|
||||
use self::libwallet::types::OutputStatus;
|
||||
use self::wallet::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use std::fs;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
38
impls/Cargo.toml
Normal file
38
impls/Cargo.toml
Normal file
|
@ -0,0 +1,38 @@
|
|||
[package]
|
||||
name = "grin_wallet_impls"
|
||||
version = "1.1.0"
|
||||
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
|
||||
description = "Concrete types derived from libwallet traits"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/mimblewimble/grin"
|
||||
keywords = [ "crypto", "grin", "mimblewimble" ]
|
||||
readme = "README.md"
|
||||
exclude = ["**/*.grin", "**/*.grin2"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
blake2-rfc = "0.2"
|
||||
failure = "0.1"
|
||||
failure_derive = "0.1"
|
||||
futures = "0.1"
|
||||
rand = "0.5"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
serde_json = "1"
|
||||
log = "0.4"
|
||||
ring = "0.13"
|
||||
tokio = "= 0.1.11"
|
||||
tokio-core = "0.1"
|
||||
tokio-retry = "0.1"
|
||||
uuid = { version = "0.6", features = ["serde", "v4"] }
|
||||
chrono = { version = "0.4.4", features = ["serde"] }
|
||||
|
||||
grin_wallet_libwallet = { path = "../libwallet", version = "1.1.0" }
|
||||
grin_wallet_config = { path = "../config", version = "1.1.0" }
|
||||
|
||||
grin_core = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
||||
grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
||||
grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
||||
grin_util = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
||||
grin_api = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
||||
grin_store = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" }
|
|
@ -16,9 +16,10 @@
|
|||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use crate::config::WalletConfig;
|
||||
use crate::libwallet::slate::Slate;
|
||||
use crate::libwallet::Error;
|
||||
use crate::{WalletCommAdapter, WalletConfig};
|
||||
use crate::WalletCommAdapter;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone)]
|
|
@ -12,13 +12,12 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// HTTP Wallet 'plugin' implementation
|
||||
use crate::api;
|
||||
use crate::controller;
|
||||
use crate::libwallet::slate::Slate;
|
||||
use crate::libwallet::{Error, ErrorKind};
|
||||
use crate::{instantiate_wallet, HTTPNodeClient, WalletCommAdapter, WalletConfig};
|
||||
/// HTTP Wallet 'plugin' implementation
|
||||
use failure::ResultExt;
|
||||
use crate::WalletCommAdapter;
|
||||
use config::WalletConfig;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -69,24 +68,12 @@ impl WalletCommAdapter for HTTPWalletCommAdapter {
|
|||
|
||||
fn listen(
|
||||
&self,
|
||||
params: HashMap<String, String>,
|
||||
config: WalletConfig,
|
||||
passphrase: &str,
|
||||
account: &str,
|
||||
node_api_secret: Option<String>,
|
||||
_params: HashMap<String, String>,
|
||||
_config: WalletConfig,
|
||||
_passphrase: &str,
|
||||
_account: &str,
|
||||
_node_api_secret: Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
let node_client = HTTPNodeClient::new(&config.check_node_api_http_addr, node_api_secret);
|
||||
let wallet = instantiate_wallet(config.clone(), node_client, passphrase, account)
|
||||
.context(ErrorKind::WalletSeedDecryption)?;
|
||||
let listen_addr = params.get("api_listen_addr").unwrap();
|
||||
let tls_conf = match params.get("certificate") {
|
||||
Some(s) => Some(api::TLSConfig::new(
|
||||
s.to_owned(),
|
||||
params.get("private_key").unwrap().to_owned(),
|
||||
)),
|
||||
None => None,
|
||||
};
|
||||
controller::foreign_listener(wallet.clone(), &listen_addr, tls_conf)?;
|
||||
Ok(())
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
|
@ -14,10 +14,11 @@
|
|||
|
||||
// Keybase Wallet Plugin
|
||||
|
||||
use crate::controller;
|
||||
use crate::config::WalletConfig;
|
||||
use crate::libwallet::api_impl::foreign;
|
||||
use crate::libwallet::slate::Slate;
|
||||
use crate::libwallet::{Error, ErrorKind};
|
||||
use crate::{instantiate_wallet, HTTPNodeClient, WalletCommAdapter, WalletConfig};
|
||||
use crate::{instantiate_wallet, HTTPNodeClient, WalletCommAdapter};
|
||||
use failure::ResultExt;
|
||||
use serde::Serialize;
|
||||
use serde_json::{from_str, json, to_string, Value};
|
||||
|
@ -374,14 +375,18 @@ impl WalletCommAdapter for KeybaseWalletCommAdapter {
|
|||
slate.amount as f64 / 1000000000.0,
|
||||
tx_uuid,
|
||||
);
|
||||
match controller::foreign_single_use(wallet.clone(), |api| {
|
||||
if let Err(e) = api.verify_slate_messages(&slate) {
|
||||
error!("Error validating participant messages: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
api.receive_tx(&mut slate, None, None)?;
|
||||
Ok(())
|
||||
}) {
|
||||
if let Err(e) = slate.verify_messages() {
|
||||
error!("Error validating participant messages: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
let res = {
|
||||
let mut w = wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let r = foreign::receive_tx(&mut *w, &mut slate, None, None);
|
||||
w.close()?;
|
||||
r
|
||||
};
|
||||
match res {
|
||||
// Reply to the same channel with topic SLATE_SIGNED
|
||||
Ok(_) => {
|
||||
let slate = slate
|
|
@ -22,9 +22,9 @@ pub use self::http::HTTPWalletCommAdapter;
|
|||
pub use self::keybase::KeybaseWalletCommAdapter;
|
||||
pub use self::null::NullWalletCommAdapter;
|
||||
|
||||
use crate::config::WalletConfig;
|
||||
use crate::libwallet::slate::Slate;
|
||||
use crate::libwallet::Error;
|
||||
use crate::WalletConfig;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Encapsulate wallet to wallet communication functions
|
|
@ -12,10 +12,11 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::config::WalletConfig;
|
||||
/// Null Output 'plugin' implementation
|
||||
use crate::libwallet::slate::Slate;
|
||||
use crate::libwallet::Error;
|
||||
use crate::{WalletCommAdapter, WalletConfig};
|
||||
use crate::WalletCommAdapter;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
563
impls/src/backends/lmdb.rs
Normal file
563
impls/src/backends/lmdb.rs
Normal file
|
@ -0,0 +1,563 @@
|
|||
// 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.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::{fs, path};
|
||||
|
||||
// for writing storedtransaction files
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use failure::ResultExt;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::blake2::blake2b::Blake2b;
|
||||
|
||||
use crate::keychain::{ChildNumber, ExtKeychain, Identifier, Keychain};
|
||||
use crate::store::{self, option_to_not_found, to_key, to_key_u64};
|
||||
|
||||
use crate::core::core::Transaction;
|
||||
use crate::core::{global, ser};
|
||||
use crate::libwallet::types::*;
|
||||
use crate::libwallet::{internal, Error, ErrorKind};
|
||||
use crate::util;
|
||||
use crate::util::secp::constants::SECRET_KEY_SIZE;
|
||||
use crate::util::ZeroingString;
|
||||
use crate::WalletSeed;
|
||||
use config::WalletConfig;
|
||||
|
||||
pub const DB_DIR: &'static str = "db";
|
||||
pub const TX_SAVE_DIR: &'static str = "saved_txs";
|
||||
|
||||
const OUTPUT_PREFIX: u8 = 'o' as u8;
|
||||
const DERIV_PREFIX: u8 = 'd' as u8;
|
||||
const CONFIRMED_HEIGHT_PREFIX: u8 = 'c' as u8;
|
||||
const PRIVATE_TX_CONTEXT_PREFIX: u8 = 'p' as u8;
|
||||
const TX_LOG_ENTRY_PREFIX: u8 = 't' as u8;
|
||||
const TX_LOG_ID_PREFIX: u8 = 'i' as u8;
|
||||
const ACCOUNT_PATH_MAPPING_PREFIX: u8 = 'a' as u8;
|
||||
|
||||
/// test to see if database files exist in the current directory. If so,
|
||||
/// use a DB backend for all operations
|
||||
pub fn wallet_db_exists(config: WalletConfig) -> bool {
|
||||
let db_path = path::Path::new(&config.data_file_dir).join(DB_DIR);
|
||||
db_path.exists()
|
||||
}
|
||||
|
||||
/// Helper to derive XOR keys for storing private transaction keys in the DB
|
||||
/// (blind_xor_key, nonce_xor_key)
|
||||
fn private_ctx_xor_keys<K>(
|
||||
keychain: &K,
|
||||
slate_id: &[u8],
|
||||
) -> Result<([u8; SECRET_KEY_SIZE], [u8; SECRET_KEY_SIZE]), Error>
|
||||
where
|
||||
K: Keychain,
|
||||
{
|
||||
let root_key = keychain.derive_key(0, &K::root_key_id())?;
|
||||
|
||||
// derive XOR values for storing secret values in DB
|
||||
// h(root_key|slate_id|"blind")
|
||||
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
|
||||
hasher.update(&root_key.0[..]);
|
||||
hasher.update(&slate_id[..]);
|
||||
hasher.update(&"blind".as_bytes()[..]);
|
||||
let blind_xor_key = hasher.finalize();
|
||||
let mut ret_blind = [0; SECRET_KEY_SIZE];
|
||||
ret_blind.copy_from_slice(&blind_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
|
||||
|
||||
// h(root_key|slate_id|"nonce")
|
||||
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
|
||||
hasher.update(&root_key.0[..]);
|
||||
hasher.update(&slate_id[..]);
|
||||
hasher.update(&"nonce".as_bytes()[..]);
|
||||
let nonce_xor_key = hasher.finalize();
|
||||
let mut ret_nonce = [0; SECRET_KEY_SIZE];
|
||||
ret_nonce.copy_from_slice(&nonce_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
|
||||
|
||||
Ok((ret_blind, ret_nonce))
|
||||
}
|
||||
|
||||
pub struct LMDBBackend<C, K> {
|
||||
db: store::Store,
|
||||
config: WalletConfig,
|
||||
/// passphrase: TODO better ways of dealing with this other than storing
|
||||
passphrase: ZeroingString,
|
||||
/// Keychain
|
||||
pub keychain: Option<K>,
|
||||
/// Parent path to use by default for output operations
|
||||
parent_key_id: Identifier,
|
||||
/// wallet to node client
|
||||
w2n_client: C,
|
||||
}
|
||||
|
||||
impl<C, K> LMDBBackend<C, K> {
|
||||
pub fn new(config: WalletConfig, passphrase: &str, n_client: C) -> Result<Self, Error> {
|
||||
let db_path = path::Path::new(&config.data_file_dir).join(DB_DIR);
|
||||
fs::create_dir_all(&db_path).expect("Couldn't create wallet backend directory!");
|
||||
|
||||
let stored_tx_path = path::Path::new(&config.data_file_dir).join(TX_SAVE_DIR);
|
||||
fs::create_dir_all(&stored_tx_path)
|
||||
.expect("Couldn't create wallet backend tx storage directory!");
|
||||
|
||||
let store = store::Store::new(db_path.to_str().unwrap(), None, Some(DB_DIR), None)?;
|
||||
|
||||
// Make sure default wallet derivation path always exists
|
||||
// as well as path (so it can be retrieved by batches to know where to store
|
||||
// completed transactions, for reference
|
||||
let default_account = AcctPathMapping {
|
||||
label: "default".to_owned(),
|
||||
path: LMDBBackend::<C, K>::default_path(),
|
||||
};
|
||||
let acct_key = to_key(
|
||||
ACCOUNT_PATH_MAPPING_PREFIX,
|
||||
&mut default_account.label.as_bytes().to_vec(),
|
||||
);
|
||||
|
||||
{
|
||||
let batch = store.batch()?;
|
||||
batch.put_ser(&acct_key, &default_account)?;
|
||||
batch.commit()?;
|
||||
}
|
||||
|
||||
let res = LMDBBackend {
|
||||
db: store,
|
||||
config: config.clone(),
|
||||
passphrase: ZeroingString::from(passphrase),
|
||||
keychain: None,
|
||||
parent_key_id: LMDBBackend::<C, K>::default_path(),
|
||||
w2n_client: n_client,
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn default_path() -> Identifier {
|
||||
// return the default parent wallet path, corresponding to the default account
|
||||
// in the BIP32 spec. Parent is account 0 at level 2, child output identifiers
|
||||
// are all at level 3
|
||||
ExtKeychain::derive_key_id(2, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
/// Just test to see if database files exist in the current directory. If
|
||||
/// so, use a DB backend for all operations
|
||||
pub fn exists(config: WalletConfig) -> bool {
|
||||
let db_path = path::Path::new(&config.data_file_dir).join(DB_DIR);
|
||||
db_path.exists()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, K> WalletBackend<C, K> for LMDBBackend<C, K>
|
||||
where
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Initialise with whatever stored credentials we have
|
||||
fn open_with_credentials(&mut self) -> Result<(), Error> {
|
||||
let wallet_seed = WalletSeed::from_file(&self.config, &self.passphrase)
|
||||
.context(ErrorKind::CallbackImpl("Error opening wallet"))?;
|
||||
self.keychain = Some(
|
||||
wallet_seed
|
||||
.derive_keychain(global::is_floonet())
|
||||
.context(ErrorKind::CallbackImpl("Error deriving keychain"))?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Close wallet and remove any stored credentials (TBD)
|
||||
fn close(&mut self) -> Result<(), Error> {
|
||||
self.keychain = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the keychain being used
|
||||
fn keychain(&mut self) -> &mut K {
|
||||
self.keychain.as_mut().unwrap()
|
||||
}
|
||||
|
||||
/// Return the node client being used
|
||||
fn w2n_client(&mut self) -> &mut C {
|
||||
&mut self.w2n_client
|
||||
}
|
||||
|
||||
/// return the version of the commit for caching
|
||||
fn calc_commit_for_cache(
|
||||
&mut self,
|
||||
amount: u64,
|
||||
id: &Identifier,
|
||||
) -> Result<Option<String>, Error> {
|
||||
if self.config.no_commit_cache == Some(true) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(util::to_hex(
|
||||
self.keychain().commit(amount, &id)?.0.to_vec(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Set parent path by account name
|
||||
fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error> {
|
||||
let label = label.to_owned();
|
||||
let res = self.acct_path_iter().find(|l| l.label == label);
|
||||
if let Some(a) = res {
|
||||
self.set_parent_key_id(a.path);
|
||||
Ok(())
|
||||
} else {
|
||||
return Err(ErrorKind::UnknownAccountLabel(label.clone()).into());
|
||||
}
|
||||
}
|
||||
|
||||
/// set parent path
|
||||
fn set_parent_key_id(&mut self, id: Identifier) {
|
||||
self.parent_key_id = id;
|
||||
}
|
||||
|
||||
fn parent_key_id(&mut self) -> Identifier {
|
||||
self.parent_key_id.clone()
|
||||
}
|
||||
|
||||
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error> {
|
||||
let key = match mmr_index {
|
||||
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i),
|
||||
None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()),
|
||||
};
|
||||
option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id)).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = OutputData> + 'a> {
|
||||
Box::new(self.db.iter(&[OUTPUT_PREFIX]).unwrap())
|
||||
}
|
||||
|
||||
fn get_tx_log_entry(&self, u: &Uuid) -> Result<Option<TxLogEntry>, Error> {
|
||||
let key = to_key(TX_LOG_ENTRY_PREFIX, &mut u.as_bytes().to_vec());
|
||||
self.db.get_ser(&key).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn tx_log_iter<'a>(&'a self) -> Box<dyn Iterator<Item = TxLogEntry> + 'a> {
|
||||
Box::new(self.db.iter(&[TX_LOG_ENTRY_PREFIX]).unwrap())
|
||||
}
|
||||
|
||||
fn get_private_context(&mut self, slate_id: &[u8]) -> Result<Context, Error> {
|
||||
let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec());
|
||||
let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?;
|
||||
|
||||
let mut ctx: Context = option_to_not_found(
|
||||
self.db.get_ser(&ctx_key),
|
||||
&format!("Slate id: {:x?}", slate_id.to_vec()),
|
||||
)?;
|
||||
|
||||
for i in 0..SECRET_KEY_SIZE {
|
||||
ctx.sec_key.0[i] = ctx.sec_key.0[i] ^ blind_xor_key[i];
|
||||
ctx.sec_nonce.0[i] = ctx.sec_nonce.0[i] ^ nonce_xor_key[i];
|
||||
}
|
||||
|
||||
Ok(ctx)
|
||||
}
|
||||
|
||||
fn acct_path_iter<'a>(&'a self) -> Box<dyn Iterator<Item = AcctPathMapping> + 'a> {
|
||||
Box::new(self.db.iter(&[ACCOUNT_PATH_MAPPING_PREFIX]).unwrap())
|
||||
}
|
||||
|
||||
fn get_acct_path(&self, label: String) -> Result<Option<AcctPathMapping>, Error> {
|
||||
let acct_key = to_key(ACCOUNT_PATH_MAPPING_PREFIX, &mut label.as_bytes().to_vec());
|
||||
self.db.get_ser(&acct_key).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn store_tx(&self, uuid: &str, tx: &Transaction) -> Result<(), Error> {
|
||||
let filename = format!("{}.grintx", uuid);
|
||||
let path = path::Path::new(&self.config.data_file_dir)
|
||||
.join(TX_SAVE_DIR)
|
||||
.join(filename);
|
||||
let path_buf = Path::new(&path).to_path_buf();
|
||||
let mut stored_tx = File::create(path_buf)?;
|
||||
let tx_hex = util::to_hex(ser::ser_vec(tx).unwrap());;
|
||||
stored_tx.write_all(&tx_hex.as_bytes())?;
|
||||
stored_tx.sync_all()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_stored_tx(&self, entry: &TxLogEntry) -> Result<Option<Transaction>, Error> {
|
||||
let filename = match entry.stored_tx.clone() {
|
||||
Some(f) => f,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let path = path::Path::new(&self.config.data_file_dir)
|
||||
.join(TX_SAVE_DIR)
|
||||
.join(filename);
|
||||
let tx_file = Path::new(&path).to_path_buf();
|
||||
let mut tx_f = File::open(tx_file)?;
|
||||
let mut content = String::new();
|
||||
tx_f.read_to_string(&mut content)?;
|
||||
let tx_bin = util::from_hex(content).unwrap();
|
||||
Ok(Some(
|
||||
ser::deserialize::<Transaction>(&mut &tx_bin[..]).unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
fn batch<'a>(&'a mut self) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error> {
|
||||
Ok(Box::new(Batch {
|
||||
_store: self,
|
||||
db: RefCell::new(Some(self.db.batch()?)),
|
||||
keychain: self.keychain.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn next_child<'a>(&mut self) -> Result<Identifier, Error> {
|
||||
let parent_key_id = self.parent_key_id.clone();
|
||||
let mut deriv_idx = {
|
||||
let batch = self.db.batch()?;
|
||||
let deriv_key = to_key(DERIV_PREFIX, &mut self.parent_key_id.to_bytes().to_vec());
|
||||
match batch.get_ser(&deriv_key)? {
|
||||
Some(idx) => idx,
|
||||
None => 0,
|
||||
}
|
||||
};
|
||||
let mut return_path = self.parent_key_id.to_path();
|
||||
return_path.depth = return_path.depth + 1;
|
||||
return_path.path[return_path.depth as usize - 1] = ChildNumber::from(deriv_idx);
|
||||
deriv_idx = deriv_idx + 1;
|
||||
let mut batch = self.batch()?;
|
||||
batch.save_child_index(&parent_key_id, deriv_idx)?;
|
||||
batch.commit()?;
|
||||
Ok(Identifier::from_path(&return_path))
|
||||
}
|
||||
|
||||
fn last_confirmed_height<'a>(&mut self) -> Result<u64, Error> {
|
||||
let batch = self.db.batch()?;
|
||||
let height_key = to_key(
|
||||
CONFIRMED_HEIGHT_PREFIX,
|
||||
&mut self.parent_key_id.to_bytes().to_vec(),
|
||||
);
|
||||
let last_confirmed_height = match batch.get_ser(&height_key)? {
|
||||
Some(h) => h,
|
||||
None => 0,
|
||||
};
|
||||
Ok(last_confirmed_height)
|
||||
}
|
||||
|
||||
fn restore(&mut self) -> Result<(), Error> {
|
||||
internal::restore::restore(self).context(ErrorKind::Restore)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_repair(&mut self, delete_unconfirmed: bool) -> Result<(), Error> {
|
||||
internal::restore::check_repair(self, delete_unconfirmed).context(ErrorKind::Restore)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// An atomic batch in which all changes can be committed all at once or
|
||||
/// discarded on error.
|
||||
pub struct Batch<'a, C, K>
|
||||
where
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
_store: &'a LMDBBackend<C, K>,
|
||||
db: RefCell<Option<store::Batch<'a>>>,
|
||||
/// Keychain
|
||||
keychain: Option<K>,
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<'a, C, K> WalletOutputBatch<K> for Batch<'a, C, K>
|
||||
where
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
fn keychain(&mut self) -> &mut K {
|
||||
self.keychain.as_mut().unwrap()
|
||||
}
|
||||
|
||||
fn save(&mut self, out: OutputData) -> Result<(), Error> {
|
||||
// Save the output data to the db.
|
||||
{
|
||||
let key = match out.mmr_index {
|
||||
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec(), i),
|
||||
None => to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec()),
|
||||
};
|
||||
self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error> {
|
||||
let key = match mmr_index {
|
||||
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i),
|
||||
None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()),
|
||||
};
|
||||
option_to_not_found(
|
||||
self.db.borrow().as_ref().unwrap().get_ser(&key),
|
||||
&format!("Key ID: {}", id),
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn iter(&self) -> Box<dyn Iterator<Item = OutputData>> {
|
||||
Box::new(
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iter(&[OUTPUT_PREFIX])
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn delete(&mut self, id: &Identifier, mmr_index: &Option<u64>) -> Result<(), Error> {
|
||||
// Delete the output data.
|
||||
{
|
||||
let key = match mmr_index {
|
||||
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i),
|
||||
None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()),
|
||||
};
|
||||
let _ = self.db.borrow().as_ref().unwrap().delete(&key);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_tx_log_id(&mut self, parent_key_id: &Identifier) -> Result<u32, Error> {
|
||||
let tx_id_key = to_key(TX_LOG_ID_PREFIX, &mut parent_key_id.to_bytes().to_vec());
|
||||
let last_tx_log_id = match self.db.borrow().as_ref().unwrap().get_ser(&tx_id_key)? {
|
||||
Some(t) => t,
|
||||
None => 0,
|
||||
};
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.put_ser(&tx_id_key, &(last_tx_log_id + 1))?;
|
||||
Ok(last_tx_log_id)
|
||||
}
|
||||
|
||||
fn tx_log_iter(&self) -> Box<dyn Iterator<Item = TxLogEntry>> {
|
||||
Box::new(
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iter(&[TX_LOG_ENTRY_PREFIX])
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn save_last_confirmed_height(
|
||||
&mut self,
|
||||
parent_key_id: &Identifier,
|
||||
height: u64,
|
||||
) -> Result<(), Error> {
|
||||
let height_key = to_key(
|
||||
CONFIRMED_HEIGHT_PREFIX,
|
||||
&mut parent_key_id.to_bytes().to_vec(),
|
||||
);
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.put_ser(&height_key, &height)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn save_child_index(&mut self, parent_id: &Identifier, child_n: u32) -> Result<(), Error> {
|
||||
let deriv_key = to_key(DERIV_PREFIX, &mut parent_id.to_bytes().to_vec());
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.put_ser(&deriv_key, &child_n)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn save_tx_log_entry(
|
||||
&mut self,
|
||||
tx_in: TxLogEntry,
|
||||
parent_id: &Identifier,
|
||||
) -> Result<(), Error> {
|
||||
let tx_log_key = to_key_u64(
|
||||
TX_LOG_ENTRY_PREFIX,
|
||||
&mut parent_id.to_bytes().to_vec(),
|
||||
tx_in.id as u64,
|
||||
);
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.put_ser(&tx_log_key, &tx_in)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn save_acct_path(&mut self, mapping: AcctPathMapping) -> Result<(), Error> {
|
||||
let acct_key = to_key(
|
||||
ACCOUNT_PATH_MAPPING_PREFIX,
|
||||
&mut mapping.label.as_bytes().to_vec(),
|
||||
);
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.put_ser(&acct_key, &mapping)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn acct_path_iter(&self) -> Box<dyn Iterator<Item = AcctPathMapping>> {
|
||||
Box::new(
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iter(&[ACCOUNT_PATH_MAPPING_PREFIX])
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn lock_output(&mut self, out: &mut OutputData) -> Result<(), Error> {
|
||||
out.lock();
|
||||
self.save(out.clone())
|
||||
}
|
||||
|
||||
fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error> {
|
||||
let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec());
|
||||
let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?;
|
||||
|
||||
let mut s_ctx = ctx.clone();
|
||||
for i in 0..SECRET_KEY_SIZE {
|
||||
s_ctx.sec_key.0[i] = s_ctx.sec_key.0[i] ^ blind_xor_key[i];
|
||||
s_ctx.sec_nonce.0[i] = s_ctx.sec_nonce.0[i] ^ nonce_xor_key[i];
|
||||
}
|
||||
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.put_ser(&ctx_key, &s_ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_private_context(&mut self, slate_id: &[u8]) -> Result<(), Error> {
|
||||
let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec());
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.delete(&ctx_key)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn commit(&self) -> Result<(), Error> {
|
||||
let db = self.db.replace(None);
|
||||
db.unwrap().commit()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
17
impls/src/backends/mod.rs
Normal file
17
impls/src/backends/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
// 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.
|
||||
|
||||
mod lmdb;
|
||||
|
||||
pub use self::lmdb::LMDBBackend;
|
164
impls/src/error.rs
Normal file
164
impls/src/error.rs
Normal file
|
@ -0,0 +1,164 @@
|
|||
// 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 crate::core::libtx;
|
||||
use crate::keychain;
|
||||
use crate::libwallet;
|
||||
use failure::{Backtrace, Context, Fail};
|
||||
use std::env;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
/// Error definition
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
pub 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: {}", _1)]
|
||||
LibWallet(libwallet::ErrorKind, String),
|
||||
|
||||
/// Keychain error
|
||||
#[fail(display = "Keychain error")]
|
||||
Keychain(keychain::Error),
|
||||
|
||||
/// Error when formatting json
|
||||
#[fail(display = "IO error")]
|
||||
IO,
|
||||
|
||||
/// Error when formatting json
|
||||
#[fail(display = "Serde JSON error")]
|
||||
Format,
|
||||
|
||||
/// Wallet seed already exists
|
||||
#[fail(display = "Wallet seed file exists: {}", _0)]
|
||||
WalletSeedExists(String),
|
||||
|
||||
/// Wallet seed doesn't exist
|
||||
#[fail(display = "Wallet seed doesn't exist error")]
|
||||
WalletSeedDoesntExist,
|
||||
|
||||
/// Enc/Decryption Error
|
||||
#[fail(display = "Enc/Decryption error (check password?)")]
|
||||
Encryption,
|
||||
|
||||
/// BIP 39 word list
|
||||
#[fail(display = "BIP39 Mnemonic (word list) Error")]
|
||||
Mnemonic,
|
||||
|
||||
/// Command line argument error
|
||||
#[fail(display = "{}", _0)]
|
||||
ArgumentError(String),
|
||||
|
||||
/// Other
|
||||
#[fail(display = "Generic error: {}", _0)]
|
||||
GenericError(String),
|
||||
}
|
||||
|
||||
impl Fail for Error {
|
||||
fn cause(&self) -> Option<&dyn 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 {
|
||||
let show_bt = match env::var("RUST_BACKTRACE") {
|
||||
Ok(r) => {
|
||||
if r == "1" {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Err(_) => false,
|
||||
};
|
||||
let backtrace = match self.backtrace() {
|
||||
Some(b) => format!("{}", b),
|
||||
None => String::from("Unknown"),
|
||||
};
|
||||
let inner_output = format!("{}", self.inner,);
|
||||
let backtrace_output = format!("\nBacktrace: {}", backtrace);
|
||||
let mut output = inner_output.clone();
|
||||
if show_bt {
|
||||
output.push_str(&backtrace_output);
|
||||
}
|
||||
Display::fmt(&output, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// get kind
|
||||
pub fn kind(&self) -> ErrorKind {
|
||||
self.inner.get_context().clone()
|
||||
}
|
||||
/// get cause
|
||||
pub fn cause(&self) -> Option<&dyn 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<libwallet::Error> for Error {
|
||||
fn from(error: libwallet::Error) -> Error {
|
||||
Error {
|
||||
inner: Context::new(ErrorKind::LibWallet(error.kind(), format!("{}", error))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libtx::Error> for Error {
|
||||
fn from(error: libtx::Error) -> Error {
|
||||
Error {
|
||||
inner: Context::new(ErrorKind::LibTX(error.kind())),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,57 +12,48 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Library module for the main wallet functionalities provided by Grin.
|
||||
//! Concrete implementations of types found in libwallet, organised this
|
||||
//! way mostly to avoid any circular dependencies of any kind
|
||||
//! Functions in this crate should not use the wallet api crate directly
|
||||
|
||||
use blake2_rfc as blake2;
|
||||
|
||||
#[macro_use]
|
||||
extern crate prettytable;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
use failure;
|
||||
use grin_api as api;
|
||||
extern crate grin_core as core;
|
||||
use grin_api as api;
|
||||
use grin_keychain as keychain;
|
||||
use grin_store as store;
|
||||
use grin_util as util;
|
||||
use grin_wallet_api as apiwallet;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
extern crate grin_wallet_config as config;
|
||||
|
||||
mod adapters;
|
||||
pub mod command;
|
||||
pub mod controller;
|
||||
pub mod display;
|
||||
mod error;
|
||||
pub mod lmdb_wallet;
|
||||
mod lmdb_wallet;
|
||||
mod node_clients;
|
||||
mod seed;
|
||||
pub mod test_framework;
|
||||
mod types;
|
||||
|
||||
pub use crate::adapters::{
|
||||
FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter, NullWalletCommAdapter,
|
||||
WalletCommAdapter,
|
||||
};
|
||||
pub use crate::error::{Error, ErrorKind};
|
||||
pub use crate::libwallet::slate::Slate;
|
||||
pub use crate::libwallet::types::{
|
||||
BlockFees, CbData, NodeClient, WalletBackend, WalletInfo, WalletInst,
|
||||
};
|
||||
pub use crate::lmdb_wallet::{wallet_db_exists, LMDBBackend};
|
||||
pub use crate::node_clients::{create_coinbase, HTTPNodeClient};
|
||||
pub use crate::types::{EncryptedWalletSeed, WalletSeed, SEED_FILE};
|
||||
use config::WalletConfig;
|
||||
pub use crate::node_clients::HTTPNodeClient;
|
||||
pub use crate::seed::{EncryptedWalletSeed, WalletSeed, SEED_FILE};
|
||||
|
||||
use crate::util::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
use libwallet::types::{NodeClient, WalletBackend, WalletInst};
|
||||
|
||||
/// Helper to create an instance of the LMDB wallet
|
||||
pub fn instantiate_wallet(
|
||||
wallet_config: WalletConfig,
|
||||
wallet_config: config::WalletConfig,
|
||||
node_client: impl NodeClient + 'static,
|
||||
passphrase: &str,
|
||||
account: &str,
|
|
@ -31,11 +31,12 @@ use crate::store::{self, option_to_not_found, to_key, to_key_u64};
|
|||
use crate::core::core::Transaction;
|
||||
use crate::core::{global, ser};
|
||||
use crate::libwallet::types::*;
|
||||
use crate::libwallet::{internal, Error, ErrorKind};
|
||||
use crate::types::WalletSeed;
|
||||
use crate::libwallet::{check_repair, restore};
|
||||
use crate::libwallet::{Error, ErrorKind};
|
||||
use crate::util;
|
||||
use crate::util::secp::constants::SECRET_KEY_SIZE;
|
||||
use crate::util::ZeroingString;
|
||||
use crate::WalletSeed;
|
||||
use config::WalletConfig;
|
||||
|
||||
pub const DB_DIR: &'static str = "db";
|
||||
|
@ -346,12 +347,12 @@ where
|
|||
}
|
||||
|
||||
fn restore(&mut self) -> Result<(), Error> {
|
||||
internal::restore::restore(self).context(ErrorKind::Restore)?;
|
||||
restore(self).context(ErrorKind::Restore)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_repair(&mut self, delete_unconfirmed: bool) -> Result<(), Error> {
|
||||
internal::restore::check_repair(self, delete_unconfirmed).context(ErrorKind::Restore)?;
|
||||
check_repair(self, delete_unconfirmed).context(ErrorKind::Restore)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -15,7 +15,6 @@
|
|||
//! Client functions, implementations of the NodeClient trait
|
||||
//! specific to the FileWallet
|
||||
|
||||
use failure::ResultExt;
|
||||
use futures::{stream, Stream};
|
||||
|
||||
use crate::libwallet::types::*;
|
||||
|
@ -23,7 +22,6 @@ use std::collections::HashMap;
|
|||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::api;
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::libwallet;
|
||||
use crate::util;
|
||||
use crate::util::secp::pedersen;
|
||||
|
@ -192,6 +190,7 @@ impl NodeClient for HTTPNodeClient {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// Call the wallet API to create a coinbase output for the given block_fees.
|
||||
/// Will retry based on default "retry forever with backoff" behavior.
|
||||
pub fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
|
||||
|
@ -216,4 +215,4 @@ fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, E
|
|||
"Posting create coinbase".to_string(),
|
||||
))?;
|
||||
Ok(res)
|
||||
}
|
||||
}*/
|
|
@ -14,4 +14,4 @@
|
|||
|
||||
mod http;
|
||||
|
||||
pub use self::http::{create_coinbase, HTTPNodeClient};
|
||||
pub use self::http::HTTPNodeClient;
|
|
@ -24,9 +24,9 @@ use serde_json;
|
|||
use ring::aead;
|
||||
use ring::{digest, pbkdf2};
|
||||
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::keychain::{mnemonic, Keychain};
|
||||
use crate::util;
|
||||
use crate::{Error, ErrorKind};
|
||||
use config::WalletConfig;
|
||||
use failure::ResultExt;
|
||||
|
|
@ -17,11 +17,14 @@ use self::core::core::{OutputFeatures, OutputIdentifier, Transaction};
|
|||
use self::core::{consensus, global, pow, ser};
|
||||
use self::util::secp::pedersen;
|
||||
use self::util::Mutex;
|
||||
use crate::apiwallet::Owner;
|
||||
use crate::libwallet::types::{BlockFees, CbData, NodeClient, WalletInfo, WalletInst};
|
||||
use crate::config::WalletConfig;
|
||||
use crate::libwallet;
|
||||
use crate::libwallet::api_impl::{foreign, owner};
|
||||
use crate::libwallet::types::{
|
||||
BlockFees, CbData, NodeClient, WalletBackend, WalletInfo, WalletInst,
|
||||
};
|
||||
use crate::lmdb_wallet::LMDBBackend;
|
||||
use crate::{controller, libwallet, WalletSeed};
|
||||
use crate::{WalletBackend, WalletConfig};
|
||||
use crate::WalletSeed;
|
||||
use chrono::Duration;
|
||||
use grin_api as api;
|
||||
use grin_chain as chain;
|
||||
|
@ -34,15 +37,6 @@ mod testclient;
|
|||
|
||||
pub use self::{testclient::LocalWalletClient, testclient::WalletProxy};
|
||||
|
||||
/// types of backends tests should iterate through
|
||||
//#[derive(Clone)]
|
||||
//pub enum BackendType {
|
||||
// /// File
|
||||
// FileBackend,
|
||||
// /// LMDB
|
||||
// LMDBBackend,
|
||||
//}
|
||||
|
||||
/// Get an output from the chain locally and present it back as an API output
|
||||
fn get_output_local(chain: &chain::Chain, commit: &pedersen::Commitment) -> Option<api::Output> {
|
||||
let outputs = [
|
||||
|
@ -130,11 +124,14 @@ where
|
|||
height: prev.height + 1,
|
||||
};
|
||||
// build coinbase (via api) and add block
|
||||
controller::foreign_single_use(wallet.clone(), |api| {
|
||||
let coinbase_tx = api.build_coinbase(&block_fees)?;
|
||||
add_block_with_reward(chain, txs, coinbase_tx.clone());
|
||||
Ok(())
|
||||
})?;
|
||||
let coinbase_tx = {
|
||||
let mut w = wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let res = foreign::build_coinbase(&mut *w, &block_fees)?;
|
||||
w.close()?;
|
||||
res
|
||||
};
|
||||
add_block_with_reward(chain, txs, coinbase_tx.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -184,8 +181,8 @@ where
|
|||
|
||||
/// send an amount to a destination
|
||||
pub fn send_to_dest<T: ?Sized, C, K>(
|
||||
wallet: Arc<Mutex<dyn WalletInst<C, K>>>,
|
||||
client: LocalWalletClient,
|
||||
api: &mut Owner<T, C, K>,
|
||||
dest: &str,
|
||||
amount: u64,
|
||||
) -> Result<(), libwallet::Error>
|
||||
|
@ -194,32 +191,45 @@ where
|
|||
C: NodeClient,
|
||||
K: keychain::Keychain,
|
||||
{
|
||||
let slate_i = api.initiate_tx(
|
||||
None, // account
|
||||
amount, // amount
|
||||
2, // minimum confirmations
|
||||
500, // max outputs
|
||||
1, // num change outputs
|
||||
true, // select all outputs
|
||||
None, None,
|
||||
)?;
|
||||
let mut slate = client.send_tx_slate_direct(dest, &slate_i)?;
|
||||
api.tx_lock_outputs(&slate)?;
|
||||
api.finalize_tx(&mut slate)?;
|
||||
api.post_tx(&slate.tx, false)?; // mines a block
|
||||
let slate = {
|
||||
let mut w = wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let slate_i = owner::initiate_tx(
|
||||
&mut *w, None, // account
|
||||
amount, // amount
|
||||
2, // minimum confirmations
|
||||
500, // max outputs
|
||||
1, // num change outputs
|
||||
true, // select all outputs
|
||||
None, None,
|
||||
)?;
|
||||
let mut slate = client.send_tx_slate_direct(dest, &slate_i)?;
|
||||
owner::tx_lock_outputs(&mut *w, &slate)?;
|
||||
owner::finalize_tx(&mut *w, &mut slate)?;
|
||||
w.close()?;
|
||||
slate
|
||||
};
|
||||
let client = {
|
||||
let mut w = wallet.lock();
|
||||
w.w2n_client().clone()
|
||||
};
|
||||
owner::post_tx(&client, &slate.tx, false)?; // mines a block
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// get wallet info totals
|
||||
pub fn wallet_info<T: ?Sized, C, K>(
|
||||
api: &mut Owner<T, C, K>,
|
||||
wallet: Arc<Mutex<dyn WalletInst<C, K>>>,
|
||||
) -> Result<WalletInfo, libwallet::Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: keychain::Keychain,
|
||||
{
|
||||
let (wallet_refreshed, wallet_info) = api.retrieve_summary_info(true, 1)?;
|
||||
let mut w = wallet.lock();
|
||||
w.open_with_credentials()?;
|
||||
let (wallet_refreshed, wallet_info) = owner::retrieve_summary_info(&mut *w, true, 1)?;
|
||||
assert!(wallet_refreshed);
|
||||
w.close()?;
|
||||
Ok(wallet_info)
|
||||
}
|
|
@ -26,9 +26,11 @@ use self::keychain::Keychain;
|
|||
use self::util::secp::pedersen;
|
||||
use self::util::secp::pedersen::Commitment;
|
||||
use self::util::{Mutex, RwLock, StopState};
|
||||
use crate::config::WalletConfig;
|
||||
use crate::libwallet::api_impl::foreign;
|
||||
use crate::libwallet::slate::Slate;
|
||||
use crate::libwallet::types::*;
|
||||
use crate::{controller, libwallet, WalletCommAdapter, WalletConfig};
|
||||
use crate::{libwallet, WalletCommAdapter};
|
||||
use failure::ResultExt;
|
||||
use grin_api as api;
|
||||
use grin_chain as chain;
|
||||
|
@ -205,15 +207,23 @@ where
|
|||
m: WalletProxyMessage,
|
||||
) -> Result<WalletProxyMessage, libwallet::Error> {
|
||||
let dest_wallet = self.wallets.get_mut(&m.dest);
|
||||
if let None = dest_wallet {
|
||||
panic!("Unknown wallet destination for send_tx_slate: {:?}", m);
|
||||
let wallet = match dest_wallet {
|
||||
None => panic!("Unknown wallet destination for send_tx_slate: {:?}", m),
|
||||
Some(w) => w,
|
||||
};
|
||||
|
||||
let mut slate = serde_json::from_str(&m.body).context(
|
||||
libwallet::ErrorKind::ClientCallback("Error parsing TxWrapper".to_owned()),
|
||||
)?;
|
||||
;
|
||||
{
|
||||
let mut w = wallet.1.lock();
|
||||
w.open_with_credentials()?;
|
||||
// receive tx
|
||||
foreign::receive_tx(&mut *w, &mut slate, None, None)?;
|
||||
w.close()?;
|
||||
}
|
||||
let w = dest_wallet.unwrap().1.clone();
|
||||
let mut slate = serde_json::from_str(&m.body).unwrap();
|
||||
controller::foreign_single_use(w.clone(), |listener_api| {
|
||||
listener_api.receive_tx(&mut slate, None, None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(WalletProxyMessage {
|
||||
sender_id: m.dest,
|
||||
dest: m.sender_id,
|
|
@ -11,10 +11,10 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! core::libtx specific tests
|
||||
extern crate grin_wallet_refwallet as wallet;
|
||||
//! Wallet seed encryption tests
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use self::wallet::{EncryptedWalletSeed, WalletSeed};
|
||||
use impls::{EncryptedWalletSeed, WalletSeed};
|
||||
|
||||
#[test]
|
||||
fn wallet_seed_encrypt() {
|
25
libwallet/src/api_impl.rs
Normal file
25
libwallet/src/api_impl.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
// 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.
|
||||
|
||||
//! lower-level wallet functions which build upon core::libtx to perform wallet
|
||||
//! operations
|
||||
|
||||
#![deny(non_upper_case_globals)]
|
||||
#![deny(non_camel_case_types)]
|
||||
#![deny(non_snake_case)]
|
||||
#![deny(unused_mut)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod foreign;
|
||||
pub mod owner;
|
81
libwallet/src/api_impl/foreign.rs
Normal file
81
libwallet/src/api_impl/foreign.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
// 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.
|
||||
|
||||
//! Generic implementation of owner API functions
|
||||
|
||||
use crate::internal::{tx, updater};
|
||||
use crate::keychain::Keychain;
|
||||
use crate::slate::Slate;
|
||||
use crate::types::{BlockFees, CbData, NodeClient, TxLogEntryType, WalletBackend};
|
||||
use crate::{Error, ErrorKind};
|
||||
|
||||
const USER_MESSAGE_MAX_LEN: usize = 256;
|
||||
|
||||
/// Build a coinbase transaction
|
||||
pub fn build_coinbase<T: ?Sized, C, K>(w: &mut T, block_fees: &BlockFees) -> Result<CbData, Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
updater::build_coinbase(&mut *w, block_fees)
|
||||
}
|
||||
|
||||
/// verify slate messages
|
||||
pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> {
|
||||
slate.verify_messages()
|
||||
}
|
||||
|
||||
/// Receive a tx as recipient
|
||||
pub fn receive_tx<T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
slate: &mut Slate,
|
||||
dest_acct_name: Option<&str>,
|
||||
message: Option<String>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = match dest_acct_name {
|
||||
Some(d) => {
|
||||
let pm = w.get_acct_path(d.to_owned())?;
|
||||
match pm {
|
||||
Some(p) => p.path,
|
||||
None => w.parent_key_id(),
|
||||
}
|
||||
}
|
||||
None => w.parent_key_id(),
|
||||
};
|
||||
// Don't do this multiple times
|
||||
let tx = updater::retrieve_txs(&mut *w, None, Some(slate.id), Some(&parent_key_id), false)?;
|
||||
for t in &tx {
|
||||
if t.tx_type == TxLogEntryType::TxReceived {
|
||||
return Err(ErrorKind::TransactionAlreadyReceived(slate.id.to_string()).into());
|
||||
}
|
||||
}
|
||||
|
||||
let message = match message {
|
||||
Some(mut m) => {
|
||||
m.truncate(USER_MESSAGE_MAX_LEN);
|
||||
Some(m)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
tx::add_output_to_slate(&mut *w, slate, &parent_key_id, 1, message)?;
|
||||
tx::update_message(&mut *w, slate)?;
|
||||
Ok(())
|
||||
}
|
384
libwallet/src/api_impl/owner.rs
Normal file
384
libwallet/src/api_impl/owner.rs
Normal file
|
@ -0,0 +1,384 @@
|
|||
// 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.
|
||||
|
||||
//! Generic implementation of owner API functions
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::core::core::hash::Hashed;
|
||||
use crate::core::core::Transaction;
|
||||
use crate::core::ser;
|
||||
|
||||
use crate::internal::{keys, selection, tx, updater};
|
||||
use crate::keychain::{Identifier, Keychain};
|
||||
use crate::slate::Slate;
|
||||
use crate::types::{
|
||||
AcctPathMapping, NodeClient, OutputData, TxLogEntry, TxWrapper, WalletBackend, WalletInfo,
|
||||
};
|
||||
use crate::util::secp::pedersen;
|
||||
use crate::{Error, ErrorKind};
|
||||
|
||||
const USER_MESSAGE_MAX_LEN: usize = 256;
|
||||
|
||||
/// List of accounts
|
||||
pub fn accounts<T: ?Sized, C, K>(w: &mut T) -> Result<Vec<AcctPathMapping>, Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
keys::accounts(&mut *w)
|
||||
}
|
||||
|
||||
/// new account path
|
||||
pub fn create_account_path<T: ?Sized, C, K>(w: &mut T, label: &str) -> Result<Identifier, Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
keys::new_acct_path(&mut *w, label)
|
||||
}
|
||||
|
||||
/// set active account
|
||||
pub fn set_active_account<T: ?Sized, C, K>(w: &mut T, label: &str) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
w.set_parent_key_id_by_name(label)
|
||||
}
|
||||
|
||||
/// retrieve outputs
|
||||
pub fn retrieve_outputs<T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
include_spent: bool,
|
||||
refresh_from_node: bool,
|
||||
tx_id: Option<u32>,
|
||||
) -> Result<(bool, Vec<(OutputData, pedersen::Commitment)>), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = w.parent_key_id();
|
||||
|
||||
let mut validated = false;
|
||||
if refresh_from_node {
|
||||
validated = update_outputs(w, false);
|
||||
}
|
||||
|
||||
Ok((
|
||||
validated,
|
||||
updater::retrieve_outputs(&mut *w, include_spent, tx_id, Some(&parent_key_id))?,
|
||||
))
|
||||
}
|
||||
|
||||
/// Retrieve txs
|
||||
pub fn retrieve_txs<T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
refresh_from_node: bool,
|
||||
tx_id: Option<u32>,
|
||||
tx_slate_id: Option<Uuid>,
|
||||
) -> Result<(bool, Vec<TxLogEntry>), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = w.parent_key_id();
|
||||
|
||||
let mut validated = false;
|
||||
if refresh_from_node {
|
||||
validated = update_outputs(w, false);
|
||||
}
|
||||
|
||||
Ok((
|
||||
validated,
|
||||
updater::retrieve_txs(&mut *w, tx_id, tx_slate_id, Some(&parent_key_id), false)?,
|
||||
))
|
||||
}
|
||||
|
||||
/// Retrieve summary info
|
||||
pub fn retrieve_summary_info<T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
refresh_from_node: bool,
|
||||
minimum_confirmations: u64,
|
||||
) -> Result<(bool, WalletInfo), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = w.parent_key_id();
|
||||
|
||||
let mut validated = false;
|
||||
if refresh_from_node {
|
||||
validated = update_outputs(w, false);
|
||||
}
|
||||
|
||||
let wallet_info = updater::retrieve_info(&mut *w, &parent_key_id, minimum_confirmations)?;
|
||||
Ok((validated, wallet_info))
|
||||
}
|
||||
|
||||
/// Initiate tx as sender
|
||||
pub fn initiate_tx<T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
src_acct_name: Option<&str>,
|
||||
amount: u64,
|
||||
minimum_confirmations: u64,
|
||||
max_outputs: usize,
|
||||
num_change_outputs: usize,
|
||||
selection_strategy_is_use_all: bool,
|
||||
message: Option<String>,
|
||||
target_slate_version: Option<u16>,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = match src_acct_name {
|
||||
Some(d) => {
|
||||
let pm = w.get_acct_path(d.to_owned())?;
|
||||
match pm {
|
||||
Some(p) => p.path,
|
||||
None => w.parent_key_id(),
|
||||
}
|
||||
}
|
||||
None => w.parent_key_id(),
|
||||
};
|
||||
|
||||
let message = match message {
|
||||
Some(mut m) => {
|
||||
m.truncate(USER_MESSAGE_MAX_LEN);
|
||||
Some(m)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut slate = tx::new_tx_slate(&mut *w, amount, 2)?;
|
||||
|
||||
let context = tx::add_inputs_to_slate(
|
||||
&mut *w,
|
||||
&mut slate,
|
||||
minimum_confirmations,
|
||||
max_outputs,
|
||||
num_change_outputs,
|
||||
selection_strategy_is_use_all,
|
||||
&parent_key_id,
|
||||
0,
|
||||
message,
|
||||
)?;
|
||||
|
||||
// Save the aggsig context in our DB for when we
|
||||
// recieve the transaction back
|
||||
{
|
||||
let mut batch = w.batch()?;
|
||||
batch.save_private_context(slate.id.as_bytes(), &context)?;
|
||||
batch.commit()?;
|
||||
}
|
||||
if let Some(v) = target_slate_version {
|
||||
slate.version_info.orig_version = v;
|
||||
}
|
||||
Ok(slate)
|
||||
}
|
||||
|
||||
/// Estimate
|
||||
pub fn estimate_initiate_tx<T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
src_acct_name: Option<&str>,
|
||||
amount: u64,
|
||||
minimum_confirmations: u64,
|
||||
max_outputs: usize,
|
||||
num_change_outputs: usize,
|
||||
selection_strategy_is_use_all: bool,
|
||||
) -> Result<
|
||||
(
|
||||
u64, // total
|
||||
u64, // fee
|
||||
),
|
||||
Error,
|
||||
>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = match src_acct_name {
|
||||
Some(d) => {
|
||||
let pm = w.get_acct_path(d.to_owned())?;
|
||||
match pm {
|
||||
Some(p) => p.path,
|
||||
None => w.parent_key_id(),
|
||||
}
|
||||
}
|
||||
None => w.parent_key_id(),
|
||||
};
|
||||
tx::estimate_send_tx(
|
||||
&mut *w,
|
||||
amount,
|
||||
minimum_confirmations,
|
||||
max_outputs,
|
||||
num_change_outputs,
|
||||
selection_strategy_is_use_all,
|
||||
&parent_key_id,
|
||||
)
|
||||
}
|
||||
|
||||
/// Lock sender outputs
|
||||
pub fn tx_lock_outputs<T: ?Sized, C, K>(w: &mut T, slate: &Slate) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let context = w.get_private_context(slate.id.as_bytes())?;
|
||||
selection::lock_tx_context(&mut *w, slate, &context)
|
||||
}
|
||||
|
||||
/// Finalize slate
|
||||
pub fn finalize_tx<T: ?Sized, C, K>(w: &mut T, slate: &mut Slate) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let context = w.get_private_context(slate.id.as_bytes())?;
|
||||
tx::complete_tx(&mut *w, slate, 0, &context)?;
|
||||
tx::update_stored_tx(&mut *w, slate)?;
|
||||
tx::update_message(&mut *w, slate)?;
|
||||
{
|
||||
let mut batch = w.batch()?;
|
||||
batch.delete_private_context(slate.id.as_bytes())?;
|
||||
batch.commit()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// cancel tx
|
||||
pub fn cancel_tx<T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
tx_id: Option<u32>,
|
||||
tx_slate_id: Option<Uuid>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = w.parent_key_id();
|
||||
if !update_outputs(w, false) {
|
||||
return Err(ErrorKind::TransactionCancellationError(
|
||||
"Can't contact running Grin node. Not Cancelling.",
|
||||
))?;
|
||||
}
|
||||
tx::cancel_tx(&mut *w, &parent_key_id, tx_id, tx_slate_id)
|
||||
}
|
||||
|
||||
/// get stored tx
|
||||
pub fn get_stored_tx<T: ?Sized, C, K>(
|
||||
w: &T,
|
||||
entry: &TxLogEntry,
|
||||
) -> Result<Option<Transaction>, Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
w.get_stored_tx(entry)
|
||||
}
|
||||
|
||||
/// Posts a transaction to the chain
|
||||
/// take a client impl instead of wallet so as not to have to lock the wallet
|
||||
pub fn post_tx<C>(client: &C, tx: &Transaction, fluff: bool) -> Result<(), Error>
|
||||
where
|
||||
C: NodeClient,
|
||||
{
|
||||
let tx_hex = util::to_hex(ser::ser_vec(tx).unwrap());
|
||||
let res = client.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff);
|
||||
if let Err(e) = res {
|
||||
error!("api: post_tx: failed with error: {}", e);
|
||||
Err(e)
|
||||
} else {
|
||||
debug!(
|
||||
"api: post_tx: successfully posted tx: {}, fluff? {}",
|
||||
tx.hash(),
|
||||
fluff
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// verify slate messages
|
||||
pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> {
|
||||
slate.verify_messages()
|
||||
}
|
||||
|
||||
/// Attempt to restore contents of wallet
|
||||
pub fn restore<T: ?Sized, C, K>(w: &mut T) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
w.restore()
|
||||
}
|
||||
|
||||
/// check repair
|
||||
pub fn check_repair<T: ?Sized, C, K>(w: &mut T, delete_unconfirmed: bool) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
update_outputs(w, true);
|
||||
w.check_repair(delete_unconfirmed)
|
||||
}
|
||||
|
||||
/// node height
|
||||
pub fn node_height<T: ?Sized, C, K>(w: &mut T) -> Result<(u64, bool), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let res = w.w2n_client().get_chain_height();
|
||||
match res {
|
||||
Ok(height) => Ok((height, true)),
|
||||
Err(_) => {
|
||||
let outputs = retrieve_outputs(w, true, false, None)?;
|
||||
let height = match outputs.1.iter().map(|(out, _)| out.height).max() {
|
||||
Some(height) => height,
|
||||
None => 0,
|
||||
};
|
||||
Ok((height, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to update outputs in wallet, return whether it was successful
|
||||
fn update_outputs<T: ?Sized, C, K>(w: &mut T, update_all: bool) -> bool
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = w.parent_key_id();
|
||||
match updater::refresh_outputs(&mut *w, &parent_key_id, update_all) {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
|
@ -234,26 +234,6 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Retrieve the associated stored finalised hex Transaction for a given transaction Id
|
||||
/// as well as whether it's been confirmed
|
||||
pub fn retrieve_tx_hex<T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
parent_key_id: &Identifier,
|
||||
tx_id: u32,
|
||||
) -> Result<(bool, Option<String>), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let tx_vec = updater::retrieve_txs(wallet, Some(tx_id), None, Some(parent_key_id), false)?;
|
||||
if tx_vec.len() != 1 {
|
||||
return Err(ErrorKind::TransactionDoesntExist(tx_id.to_string()))?;
|
||||
}
|
||||
let tx = tx_vec[0].clone();
|
||||
Ok((tx.confirmed, tx.stored_tx))
|
||||
}
|
||||
|
||||
/// Update the stored transaction (this update needs to happen when the TX is finalised)
|
||||
pub fn update_stored_tx<T: ?Sized, C, K>(wallet: &mut T, slate: &Slate) -> Result<(), Error>
|
||||
where
|
||||
|
@ -299,6 +279,7 @@ where
|
|||
batch.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::core::libtx::build;
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate grin_core as core;
|
||||
|
||||
extern crate grin_keychain as keychain;
|
||||
extern crate grin_store as store;
|
||||
extern crate grin_util as util;
|
||||
|
@ -39,10 +38,12 @@ extern crate serde_derive;
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod api_impl;
|
||||
mod error;
|
||||
pub mod internal;
|
||||
mod internal;
|
||||
pub mod slate;
|
||||
pub mod slate_versions;
|
||||
pub mod types;
|
||||
|
||||
pub use crate::error::{Error, ErrorKind};
|
||||
pub use internal::restore::{check_repair, restore};
|
||||
|
|
|
@ -22,16 +22,16 @@ use crate::util::secp;
|
|||
use crate::util::secp::key::{PublicKey, SecretKey};
|
||||
use crate::util::secp::Signature;
|
||||
use crate::util::RwLock;
|
||||
use failure::ResultExt;
|
||||
use grin_core::core::amount_to_hr_string;
|
||||
use grin_core::core::committed::Committed;
|
||||
use grin_core::core::transaction::{kernel_features, kernel_sig_msg, Transaction, Weighting};
|
||||
use grin_core::core::verifier_cache::LruVerifierCache;
|
||||
use grin_core::libtx::{aggsig, build, secp_ser, tx_fee};
|
||||
use rand::thread_rng;
|
||||
use serde_json;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
use failure::ResultExt;
|
||||
use serde_json;
|
||||
|
||||
use crate::slate_versions::v0::SlateV0;
|
||||
use crate::slate_versions::v1::SlateV1;
|
||||
|
@ -159,15 +159,15 @@ impl Slate {
|
|||
fn parse_slate_version(slate_json: &str) -> Result<u16, Error> {
|
||||
// keep attempting to deser, working through known versions until we have
|
||||
// enough to get the version out
|
||||
let res : Result<SlateV2, serde_json::Error> = serde_json::from_str(slate_json);
|
||||
let res: Result<SlateV2, serde_json::Error> = serde_json::from_str(slate_json);
|
||||
if let Ok(s) = res {
|
||||
return Ok(s.version_info.version);
|
||||
}
|
||||
let res : Result<SlateV1, serde_json::Error> = serde_json::from_str(slate_json);
|
||||
let res: Result<SlateV1, serde_json::Error> = serde_json::from_str(slate_json);
|
||||
if let Ok(s) = res {
|
||||
return Ok(s.version as u16);
|
||||
}
|
||||
let res : Result<SlateV0, serde_json::Error> = serde_json::from_str(slate_json);
|
||||
let res: Result<SlateV0, serde_json::Error> = serde_json::from_str(slate_json);
|
||||
if let Ok(_) = res {
|
||||
return Ok(0);
|
||||
}
|
||||
|
@ -180,12 +180,14 @@ impl Slate {
|
|||
let v2 = match version {
|
||||
2 => serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?,
|
||||
1 => {
|
||||
let mut v1 : SlateV1 = serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?;
|
||||
let mut v1: SlateV1 =
|
||||
serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?;
|
||||
v1.orig_version = 1;
|
||||
SlateV2::from(v1)
|
||||
}
|
||||
0 => {
|
||||
let v0 : SlateV0 = serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?;
|
||||
let v0: SlateV0 =
|
||||
serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?;
|
||||
let v1 = SlateV1::from(v0);
|
||||
SlateV2::from(v1)
|
||||
}
|
||||
|
@ -205,12 +207,12 @@ impl Slate {
|
|||
match version {
|
||||
2 => Ok(ser_self.clone()),
|
||||
1 => {
|
||||
let v2 : SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?;
|
||||
let v2: SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?;
|
||||
let v1 = SlateV1::from(v2);
|
||||
Ok(serde_json::to_string(&v1).context(ErrorKind::SlateDeser)?)
|
||||
}
|
||||
0 => {
|
||||
let v2 : SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?;
|
||||
let v2: SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?;
|
||||
let v1 = SlateV1::from(v2);
|
||||
let v0 = SlateV0::from(v1);
|
||||
Ok(serde_json::to_string(&v0).context(ErrorKind::SlateDeser)?)
|
||||
|
@ -487,7 +489,8 @@ impl Slate {
|
|||
}
|
||||
|
||||
/// Verifies any messages in the slate's participant data match their signatures
|
||||
pub fn verify_messages(&self, secp: &secp::Secp256k1) -> Result<(), Error> {
|
||||
pub fn verify_messages(&self) -> Result<(), Error> {
|
||||
let secp = secp::Secp256k1::with_caps(secp::ContextFlag::VerifyOnly);
|
||||
for p in self.participant_data.iter() {
|
||||
if let Some(msg) = &p.message {
|
||||
let hashed = blake2b(secp::constants::MESSAGE_SIZE, &[], &msg.as_bytes()[..]);
|
||||
|
@ -503,7 +506,7 @@ impl Slate {
|
|||
Some(s) => s,
|
||||
};
|
||||
if !aggsig::verify_single(
|
||||
secp,
|
||||
&secp,
|
||||
&signature,
|
||||
&m,
|
||||
None,
|
||||
|
|
|
@ -14,9 +14,7 @@
|
|||
|
||||
//! Contains V0 of the slate (grin 1.0.0)
|
||||
//! And methods to downgrade v1 to v0
|
||||
use crate::core::core::transaction::{
|
||||
KernelFeatures, OutputFeatures,
|
||||
};
|
||||
use crate::core::core::transaction::{KernelFeatures, OutputFeatures};
|
||||
use crate::keychain::BlindingFactor;
|
||||
use crate::util::secp;
|
||||
use crate::util::secp::key::PublicKey;
|
||||
|
@ -24,7 +22,6 @@ use crate::util::secp::pedersen::{Commitment, RangeProof};
|
|||
use crate::util::secp::Signature;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SlateV0 {
|
||||
/// The number of participants intended to take part in this transaction
|
||||
|
|
|
@ -12,14 +12,11 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
//! Contains V1 of the slate (grin 1.0.1, 1.0.2)
|
||||
//! Changes from V0:
|
||||
//! * Addition of a version field to Slate struct
|
||||
|
||||
use crate::core::core::transaction::{
|
||||
KernelFeatures, OutputFeatures,
|
||||
};
|
||||
use crate::core::core::transaction::{KernelFeatures, OutputFeatures};
|
||||
use crate::keychain::BlindingFactor;
|
||||
use crate::util::secp;
|
||||
use crate::util::secp::key::PublicKey;
|
||||
|
@ -27,7 +24,9 @@ use crate::util::secp::pedersen::{Commitment, RangeProof};
|
|||
use crate::util::secp::Signature;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::slate_versions::v0::{SlateV0, TransactionV0, TransactionBodyV0, InputV0, OutputV0,TxKernelV0, ParticipantDataV0};
|
||||
use crate::slate_versions::v0::{
|
||||
InputV0, OutputV0, ParticipantDataV0, SlateV0, TransactionBodyV0, TransactionV0, TxKernelV0,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SlateV1 {
|
||||
|
@ -191,10 +190,7 @@ impl From<TransactionV1> for TransactionV0 {
|
|||
fn from(tx: TransactionV1) -> TransactionV0 {
|
||||
let TransactionV1 { offset, body } = tx;
|
||||
let body = TransactionBodyV0::from(&body);
|
||||
TransactionV0 {
|
||||
offset,
|
||||
body,
|
||||
}
|
||||
TransactionV0 { offset, body }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,10 +316,7 @@ impl From<TransactionV0> for TransactionV1 {
|
|||
fn from(tx: TransactionV0) -> TransactionV1 {
|
||||
let TransactionV0 { offset, body } = tx;
|
||||
let body = TransactionBodyV1::from(&body);
|
||||
TransactionV1 {
|
||||
offset,
|
||||
body,
|
||||
}
|
||||
TransactionV1 { offset, body }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,6 +379,3 @@ impl From<&TxKernelV0> for TxKernelV1 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
//! Contains V2 of the slate (grin-wallet 1.1.0)
|
||||
//! Changes from V1:
|
||||
//! * ParticipantData struct fields serialized as hex strings instead of arrays:
|
||||
|
@ -36,19 +35,19 @@
|
|||
//! orig_verion: u16,
|
||||
//! min_compat_version: u16
|
||||
|
||||
use crate::core::core::transaction::{
|
||||
KernelFeatures, OutputFeatures,
|
||||
};
|
||||
use crate::core::core::transaction::{KernelFeatures, OutputFeatures};
|
||||
|
||||
use crate::core::libtx::secp_ser;
|
||||
use crate::keychain::BlindingFactor;
|
||||
use crate::util::secp;
|
||||
use crate::util::secp::key::PublicKey;
|
||||
use crate::util::secp::pedersen::{Commitment, RangeProof};
|
||||
use crate::util::secp::Signature;
|
||||
use crate::core::libtx::secp_ser;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::slate_versions::v1::{SlateV1, TransactionV1, TransactionBodyV1, InputV1, OutputV1,TxKernelV1, ParticipantDataV1};
|
||||
use crate::slate_versions::v1::{
|
||||
InputV1, OutputV1, ParticipantDataV1, SlateV1, TransactionBodyV1, TransactionV1, TxKernelV1,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SlateV2 {
|
||||
|
@ -201,7 +200,7 @@ impl From<SlateV2> for SlateV1 {
|
|||
let tx = TransactionV1::from(tx);
|
||||
let version = 1;
|
||||
let orig_version = version_info.orig_version as u64;
|
||||
let participant_data = map_vec!(participant_data, |data| ParticipantDataV1::from(data));
|
||||
let participant_data = map_vec!(participant_data, |data| ParticipantDataV1::from(data));
|
||||
SlateV1 {
|
||||
num_participants,
|
||||
id,
|
||||
|
@ -250,10 +249,7 @@ impl From<TransactionV2> for TransactionV1 {
|
|||
let body = TransactionBodyV1::from(&body);
|
||||
/*let transaction = TransactionV2::new(body.inputs, body.outputs, body.kernels);
|
||||
transaction.with_offset(offset)*/
|
||||
TransactionV1 {
|
||||
offset,
|
||||
body,
|
||||
}
|
||||
TransactionV1 { offset, body }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,10 +386,7 @@ impl From<TransactionV1> for TransactionV2 {
|
|||
let body = TransactionBodyV2::from(&body);
|
||||
/*let transaction = TransactionV2::new(body.inputs, body.outputs, body.kernels);
|
||||
transaction.with_offset(offset)*/
|
||||
TransactionV2 {
|
||||
offset,
|
||||
body,
|
||||
}
|
||||
TransactionV2 { offset, body }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,5 +449,3 @@ impl From<&TxKernelV1> for TxKernelV2 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::cmd::wallet_args;
|
|||
use crate::config::GlobalWalletConfig;
|
||||
use clap::ArgMatches;
|
||||
use grin_wallet_config::WalletConfig;
|
||||
use grin_wallet_refwallet::{self, HTTPNodeClient, WalletSeed};
|
||||
use grin_wallet_impls::{HTTPNodeClient, WalletSeed, SEED_FILE};
|
||||
use std::path::PathBuf;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
@ -31,7 +31,7 @@ pub fn _init_wallet_seed(wallet_config: WalletConfig, password: &str) {
|
|||
pub fn seed_exists(wallet_config: WalletConfig) -> bool {
|
||||
let mut data_file_dir = PathBuf::new();
|
||||
data_file_dir.push(wallet_config.data_file_dir);
|
||||
data_file_dir.push(grin_wallet_refwallet::SEED_FILE);
|
||||
data_file_dir.push(SEED_FILE);
|
||||
if data_file_dir.exists() {
|
||||
true
|
||||
} else {
|
||||
|
|
|
@ -21,8 +21,10 @@ use failure::Fail;
|
|||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_wallet_config::WalletConfig;
|
||||
use grin_wallet_refwallet::{command, instantiate_wallet, NodeClient, WalletInst, WalletSeed};
|
||||
use grin_wallet_refwallet::{Error, ErrorKind};
|
||||
use grin_wallet_controller::command;
|
||||
use grin_wallet_controller::{Error, ErrorKind};
|
||||
use grin_wallet_impls::{instantiate_wallet, WalletSeed};
|
||||
use grin_wallet_libwallet::types::{NodeClient, WalletInst};
|
||||
use linefeed::terminal::Signal;
|
||||
use linefeed::{Interface, ReadResult};
|
||||
use rpassword;
|
||||
|
@ -152,7 +154,7 @@ pub fn inst_wallet(
|
|||
Err(e) => {
|
||||
let msg = {
|
||||
match e.kind() {
|
||||
ErrorKind::Encryption => {
|
||||
grin_wallet_impls::ErrorKind::Encryption => {
|
||||
format!("Error decrypting wallet seed (check provided password)")
|
||||
}
|
||||
_ => format!("Error instantiating wallet: {}", e),
|
||||
|
|
|
@ -16,9 +16,8 @@
|
|||
mod wallet_tests {
|
||||
use clap;
|
||||
use grin_util as util;
|
||||
use grin_wallet_refwallet;
|
||||
|
||||
use grin_wallet_refwallet::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
use grin_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
|
||||
use clap::{App, ArgMatches};
|
||||
use grin_util::Mutex;
|
||||
|
@ -31,7 +30,8 @@ mod wallet_tests {
|
|||
use grin_core::global::ChainTypes;
|
||||
use grin_keychain::ExtKeychain;
|
||||
use grin_wallet_config::{GlobalWalletConfig, WalletConfig};
|
||||
use grin_wallet_refwallet::{LMDBBackend, WalletBackend, WalletInst, WalletSeed};
|
||||
use grin_wallet_impls::{LMDBBackend, WalletSeed};
|
||||
use grin_wallet_libwallet::types::{WalletBackend, WalletInst};
|
||||
|
||||
use super::super::wallet_args;
|
||||
|
||||
|
@ -49,7 +49,7 @@ mod wallet_tests {
|
|||
pub fn config_command_wallet(
|
||||
dir_name: &str,
|
||||
wallet_name: &str,
|
||||
) -> Result<(), grin_wallet_refwallet::Error> {
|
||||
) -> Result<(), grin_wallet_controller::Error> {
|
||||
let mut current_dir;
|
||||
let mut default_config = GlobalWalletConfig::default();
|
||||
current_dir = env::current_dir().unwrap_or_else(|e| {
|
||||
|
@ -61,7 +61,7 @@ mod wallet_tests {
|
|||
let mut config_file_name = current_dir.clone();
|
||||
config_file_name.push("grin-wallet.toml");
|
||||
if config_file_name.exists() {
|
||||
return Err(grin_wallet_refwallet::ErrorKind::ArgumentError(
|
||||
return Err(grin_wallet_controller::ErrorKind::ArgumentError(
|
||||
"grin-wallet.toml already exists in the target directory. Please remove it first"
|
||||
.to_owned(),
|
||||
))?;
|
||||
|
@ -122,7 +122,7 @@ mod wallet_tests {
|
|||
node_client: LocalWalletClient,
|
||||
passphrase: &str,
|
||||
account: &str,
|
||||
) -> Result<Arc<Mutex<WalletInst<LocalWalletClient, ExtKeychain>>>, grin_wallet_refwallet::Error>
|
||||
) -> Result<Arc<Mutex<WalletInst<LocalWalletClient, ExtKeychain>>>, grin_wallet_controller::Error>
|
||||
{
|
||||
wallet_config.chain_type = None;
|
||||
// First test decryption, so we can abort early if we have the wrong password
|
||||
|
@ -139,7 +139,7 @@ mod wallet_tests {
|
|||
wallet_name: &str,
|
||||
client: &LocalWalletClient,
|
||||
arg_vec: Vec<&str>,
|
||||
) -> Result<String, grin_wallet_refwallet::Error> {
|
||||
) -> Result<String, grin_wallet_controller::Error> {
|
||||
let args = app.clone().get_matches_from(arg_vec);
|
||||
let _ = get_wallet_subcommand(test_dir, wallet_name, args.clone());
|
||||
let mut config = initial_setup_wallet(test_dir, wallet_name);
|
||||
|
@ -149,7 +149,7 @@ mod wallet_tests {
|
|||
}
|
||||
|
||||
/// command line tests
|
||||
fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_refwallet::Error> {
|
||||
fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::Error> {
|
||||
setup(test_dir);
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> =
|
||||
|
@ -239,7 +239,7 @@ mod wallet_tests {
|
|||
// Mine a bit into wallet 1 so we have something to send
|
||||
// (TODO: Be able to stop listeners so we can test this better)
|
||||
let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
|
||||
grin_wallet_refwallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
api.set_active_account("mining")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -312,7 +312,7 @@ mod wallet_tests {
|
|||
let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
|
||||
|
||||
// Check our transaction log, should have 10 entries
|
||||
grin_wallet_refwallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
api.set_active_account("mining")?;
|
||||
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
|
||||
assert!(refreshed);
|
||||
|
@ -332,7 +332,7 @@ mod wallet_tests {
|
|||
|
||||
// check results in wallet 2
|
||||
let wallet2 = instantiate_wallet(config2.clone(), client2.clone(), "password", "default")?;
|
||||
grin_wallet_refwallet::controller::owner_single_use(wallet2.clone(), |api| {
|
||||
grin_wallet_controller::controller::owner_single_use(wallet2.clone(), |api| {
|
||||
api.set_active_account("account_1")?;
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(true, 1)?;
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
|
@ -388,7 +388,7 @@ mod wallet_tests {
|
|||
// Check our transaction log, should have bh entries + one for the self receive
|
||||
let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
|
||||
|
||||
grin_wallet_refwallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
api.set_active_account("mining")?;
|
||||
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
|
||||
assert!(refreshed);
|
||||
|
@ -422,7 +422,7 @@ mod wallet_tests {
|
|||
// Check our transaction log, should have bh entries + 2 for the self receives
|
||||
let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
|
||||
|
||||
grin_wallet_refwallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| {
|
||||
api.set_active_account("mining")?;
|
||||
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
|
||||
assert!(refreshed);
|
||||
|
|
Loading…
Reference in a new issue