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