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:
Yeastplume 2019-03-17 19:14:58 +00:00 committed by GitHub
parent 8cca9821bd
commit db015960a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 1760 additions and 564 deletions

View file

@ -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
View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 res = owner::node_height(&mut *w);
w.close()?;
res
}
}

View file

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

View file

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

View file

@ -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,
};
let res = adapter.listen(
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(()),
};
if let Err(e) = res {
return Err(ErrorKind::LibWallet(e.kind(), e.cause_string()).into());
}

View file

@ -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::{

View file

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

View file

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

View file

@ -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)?;
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;
Ok(())
})?;
// 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)?;
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;
Ok(())
})?;
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)?;
let info = wallet_info!(wallet1.clone())?;
assert_eq!(info.amount_currently_spendable, base_amount * 6);
assert_eq!(info.total, base_amount * 6);
Ok(())
})?;
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let info = test_framework::wallet_info(api)?;
let info = wallet_info!(wallet2.clone())?;
assert_eq!(info.amount_currently_spendable, base_amount * 15);
assert_eq!(info.total, base_amount * 15);
Ok(())
})?;
// 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)?;
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;
Ok(())
})?;
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)?;
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;
Ok(())
})?;
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)?;
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;
Ok(())
})?;
// 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)?;
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;
Ok(())
})?;
// 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)?;
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;
Ok(())
})?;
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);

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View 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" }

View file

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

View file

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

View file

@ -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) {
if let Err(e) = slate.verify_messages() {
error!("Error validating participant messages: {}", e);
return Err(e);
}
api.receive_tx(&mut slate, None, None)?;
Ok(())
}) {
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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
}
}*/

View file

@ -14,4 +14,4 @@
mod http;
pub use self::http::{create_coinbase, HTTPNodeClient};
pub use self::http::HTTPNodeClient;

View file

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

View file

@ -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,12 +124,15 @@ 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)?;
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(())
})?;
Ok(())
}
/// Award a blocks to a wallet directly
@ -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,8 +191,11 @@ where
C: NodeClient,
K: keychain::Keychain,
{
let slate_i = api.initiate_tx(
None, // account
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
@ -204,22 +204,32 @@ where
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
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)
}

View file

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

View file

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

View 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(())
}

View 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,
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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