mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-08 12:21:09 +03:00
Wallet self-send (#1939)
* add self send function to wallet * rustfmt * add destination to self send method * update usage doc * doc clarification * remove localhost send restriction
This commit is contained in:
parent
e9c50ccb56
commit
ee19cfcf86
8 changed files with 315 additions and 10 deletions
|
@ -177,9 +177,23 @@ It's important to understand exactly what happens during a send command, so at a
|
||||||
|
|
||||||
Outputs in your wallet will appear as unconfirmed or locked until the transaction hits the chain and is mined and validated.
|
Outputs in your wallet will appear as unconfirmed or locked until the transaction hits the chain and is mined and validated.
|
||||||
|
|
||||||
|
You can also create a transaction entirely within your own wallet by specifying the method 'self'. Using the 'self' method, you can send yourself money in a single command (for testing purposes,) or distribute funds between accounts within your wallet without having to run a listener or manipulate files. For instance, to send funds from your wallet's 'default' account to an account called 'account1', use:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[host]$ grin wallet send -m self -d "account1" 60
|
||||||
|
```
|
||||||
|
|
||||||
|
or, to send between accounts, use the -a flag to specify the source account:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[host]$ grin wallet -a "my_source_account" send -m self -d "my_dest_account" 60
|
||||||
|
```
|
||||||
|
|
||||||
|
When sending to self, the transaction will be created and posted to the chain in the same operation.
|
||||||
|
|
||||||
Other flags here are:
|
Other flags here are:
|
||||||
|
|
||||||
* `-m` 'Method', which can be 'http' or 'file'. In the first case, the transaction will be sent to the IP address which follows the `-d` flag. In the second case, Grin wallet will generate a partial transaction file under the file name specified in the `-d` flag. This file needs to be signed by the recipient using the `grin wallet receive -i filename` command and finalize by the sender using the `grin wallet finalize -i filename.response` command. To create a partial transaction file, use:
|
* `-m` 'Method', which can be 'http', 'file' or 'self' (described above). If 'http' is specified (default), the transaction will be sent to the IP address which follows the `-d` flag. If 'file' is specified, Grin wallet will generate a partial transaction file under the file name specified in the `-d` flag. This file needs to be signed by the recipient using the `grin wallet receive -i filename` command and finalized by the sender using the `grin wallet finalize -i filename.response` command. To create a partial transaction file, use:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
[host]$ grin wallet send -d "transaction" -m file 60.00
|
[host]$ grin wallet send -d "transaction" -m file 60.00
|
||||||
|
|
|
@ -267,9 +267,20 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
let method = send_args.value_of("method").ok_or_else(|| {
|
let method = send_args.value_of("method").ok_or_else(|| {
|
||||||
ErrorKind::GenericError("Payment method required".to_string())
|
ErrorKind::GenericError("Payment method required".to_string())
|
||||||
})?;
|
})?;
|
||||||
let dest = send_args.value_of("dest").ok_or_else(|| {
|
let dest = {
|
||||||
ErrorKind::GenericError("Destination wallet address required".to_string())
|
if method == "self" {
|
||||||
})?;
|
match send_args.value_of("dest") {
|
||||||
|
Some(d) => d,
|
||||||
|
None => "default",
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
send_args.value_of("dest").ok_or_else(|| {
|
||||||
|
ErrorKind::GenericError(
|
||||||
|
"Destination wallet address required".to_string(),
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
};
|
||||||
let change_outputs = send_args
|
let change_outputs = send_args
|
||||||
.value_of("change_outputs")
|
.value_of("change_outputs")
|
||||||
.ok_or_else(|| ErrorKind::GenericError("Change outputs required".to_string()))
|
.ok_or_else(|| ErrorKind::GenericError("Change outputs required".to_string()))
|
||||||
|
@ -335,6 +346,53 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
dest
|
dest
|
||||||
)).into());
|
)).into());
|
||||||
}
|
}
|
||||||
|
} else if method == "self" {
|
||||||
|
let result = api.issue_self_tx(
|
||||||
|
amount,
|
||||||
|
minimum_confirmations,
|
||||||
|
max_outputs,
|
||||||
|
change_outputs,
|
||||||
|
selection_strategy == "all",
|
||||||
|
account,
|
||||||
|
dest,
|
||||||
|
);
|
||||||
|
let slate = match result {
|
||||||
|
Ok(s) => {
|
||||||
|
info!(
|
||||||
|
"Tx created: {} grin to self, source acct: {} dest_acct: {} (strategy '{}')",
|
||||||
|
core::amount_to_hr_string(amount, false),
|
||||||
|
account,
|
||||||
|
dest,
|
||||||
|
selection_strategy,
|
||||||
|
);
|
||||||
|
s
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Tx not created: {}", e);
|
||||||
|
match e.kind() {
|
||||||
|
// user errors, don't backtrace
|
||||||
|
libwallet::ErrorKind::NotEnoughFunds { .. } => {}
|
||||||
|
libwallet::ErrorKind::FeeDispute { .. } => {}
|
||||||
|
libwallet::ErrorKind::FeeExceedsAmount { .. } => {}
|
||||||
|
_ => {
|
||||||
|
// otherwise give full dump
|
||||||
|
error!("Backtrace: {}", e.backtrace().unwrap());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = api.post_tx(&slate, fluff);
|
||||||
|
match result {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Tx sent",);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Tx not sent: {}", e);
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if method == "file" {
|
} else if method == "file" {
|
||||||
api.send_tx(
|
api.send_tx(
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -235,7 +235,7 @@ fn real_main() -> i32 {
|
||||||
.help("Method for sending this transaction.")
|
.help("Method for sending this transaction.")
|
||||||
.short("m")
|
.short("m")
|
||||||
.long("method")
|
.long("method")
|
||||||
.possible_values(&["http", "file"])
|
.possible_values(&["http", "file", "self"])
|
||||||
.default_value("http")
|
.default_value("http")
|
||||||
.takes_value(true))
|
.takes_value(true))
|
||||||
.arg(Arg::with_name("dest")
|
.arg(Arg::with_name("dest")
|
||||||
|
|
|
@ -181,6 +181,7 @@ where
|
||||||
num_change_outputs,
|
num_change_outputs,
|
||||||
selection_strategy_is_use_all,
|
selection_strategy_is_use_all,
|
||||||
&parent_key_id,
|
&parent_key_id,
|
||||||
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
lock_fn_out = lock_fn;
|
lock_fn_out = lock_fn;
|
||||||
|
@ -204,6 +205,50 @@ where
|
||||||
Ok(slate_out)
|
Ok(slate_out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Issues a send transaction to the same wallet, without needing communication
|
||||||
|
/// good for consolidating outputs, or can be extended to split outputs to multiple
|
||||||
|
/// accounts
|
||||||
|
pub fn issue_self_tx(
|
||||||
|
&mut self,
|
||||||
|
amount: u64,
|
||||||
|
minimum_confirmations: u64,
|
||||||
|
max_outputs: usize,
|
||||||
|
num_change_outputs: usize,
|
||||||
|
selection_strategy_is_use_all: bool,
|
||||||
|
src_acct_name: &str,
|
||||||
|
dest_acct_name: &str,
|
||||||
|
) -> Result<Slate, Error> {
|
||||||
|
let mut w = self.wallet.lock();
|
||||||
|
w.open_with_credentials()?;
|
||||||
|
let orig_parent_key_id = w.parent_key_id();
|
||||||
|
w.set_parent_key_id_by_name(src_acct_name)?;
|
||||||
|
let parent_key_id = w.parent_key_id();
|
||||||
|
|
||||||
|
let (mut slate, context, lock_fn) = tx::create_send_tx(
|
||||||
|
&mut **w,
|
||||||
|
amount,
|
||||||
|
minimum_confirmations,
|
||||||
|
max_outputs,
|
||||||
|
num_change_outputs,
|
||||||
|
selection_strategy_is_use_all,
|
||||||
|
&parent_key_id,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
w.set_parent_key_id_by_name(dest_acct_name)?;
|
||||||
|
let parent_key_id = w.parent_key_id();
|
||||||
|
tx::receive_tx(&mut **w, &mut slate, &parent_key_id, true)?;
|
||||||
|
|
||||||
|
tx::complete_tx(&mut **w, &mut slate, &context)?;
|
||||||
|
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
|
||||||
|
|
||||||
|
// lock our inputs
|
||||||
|
lock_fn(&mut **w, &tx_hex)?;
|
||||||
|
w.set_parent_key_id(orig_parent_key_id);
|
||||||
|
w.close()?;
|
||||||
|
Ok(slate)
|
||||||
|
}
|
||||||
|
|
||||||
/// Write a transaction to send to file so a user can transmit it to the
|
/// Write a transaction to send to file so a user can transmit it to the
|
||||||
/// receiver in whichever way they see fit (aka carrier pigeon mode).
|
/// receiver in whichever way they see fit (aka carrier pigeon mode).
|
||||||
pub fn send_tx(
|
pub fn send_tx(
|
||||||
|
@ -228,6 +273,7 @@ where
|
||||||
num_change_outputs,
|
num_change_outputs,
|
||||||
selection_strategy_is_use_all,
|
selection_strategy_is_use_all,
|
||||||
&parent_key_id,
|
&parent_key_id,
|
||||||
|
false,
|
||||||
)?;
|
)?;
|
||||||
if write_to_disk {
|
if write_to_disk {
|
||||||
let mut pub_tx = File::create(dest)?;
|
let mut pub_tx = File::create(dest)?;
|
||||||
|
@ -507,8 +553,12 @@ where
|
||||||
let parent_key_id = wallet.parent_key_id();
|
let parent_key_id = wallet.parent_key_id();
|
||||||
|
|
||||||
// create an output using the amount in the slate
|
// create an output using the amount in the slate
|
||||||
let (_, mut context, receiver_create_fn) =
|
let (_, mut context, receiver_create_fn) = selection::build_recipient_output_with_slate(
|
||||||
selection::build_recipient_output_with_slate(&mut **wallet, &mut slate, parent_key_id)?;
|
&mut **wallet,
|
||||||
|
&mut slate,
|
||||||
|
parent_key_id,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
// fill public keys
|
// fill public keys
|
||||||
let _ = slate.fill_round_1(
|
let _ = slate.fill_round_1(
|
||||||
|
@ -535,7 +585,7 @@ 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 = w.parent_key_id();
|
let parent_key_id = w.parent_key_id();
|
||||||
let res = tx::receive_tx(&mut **w, slate, &parent_key_id);
|
let res = tx::receive_tx(&mut **w, slate, &parent_key_id, false);
|
||||||
w.close()?;
|
w.close()?;
|
||||||
|
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
|
|
|
@ -36,6 +36,7 @@ pub fn build_send_tx_slate<T: ?Sized, C, K>(
|
||||||
change_outputs: usize,
|
change_outputs: usize,
|
||||||
selection_strategy_is_use_all: bool,
|
selection_strategy_is_use_all: bool,
|
||||||
parent_key_id: Identifier,
|
parent_key_id: Identifier,
|
||||||
|
is_self: bool,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
Slate,
|
Slate,
|
||||||
|
@ -98,6 +99,9 @@ where
|
||||||
let mut batch = wallet.batch()?;
|
let mut batch = wallet.batch()?;
|
||||||
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
||||||
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxSent, log_id);
|
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxSent, log_id);
|
||||||
|
if is_self {
|
||||||
|
t.tx_type = TxLogEntryType::TxSentSelf;
|
||||||
|
}
|
||||||
t.tx_slate_id = Some(slate_id);
|
t.tx_slate_id = Some(slate_id);
|
||||||
t.fee = Some(fee);
|
t.fee = Some(fee);
|
||||||
t.tx_hex = Some(tx_hex.to_owned());
|
t.tx_hex = Some(tx_hex.to_owned());
|
||||||
|
@ -144,6 +148,7 @@ pub fn build_recipient_output_with_slate<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
slate: &mut Slate,
|
slate: &mut Slate,
|
||||||
parent_key_id: Identifier,
|
parent_key_id: Identifier,
|
||||||
|
is_self: bool,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
Identifier,
|
Identifier,
|
||||||
|
@ -185,6 +190,9 @@ where
|
||||||
let mut batch = wallet.batch()?;
|
let mut batch = wallet.batch()?;
|
||||||
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
||||||
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxReceived, log_id);
|
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxReceived, log_id);
|
||||||
|
if is_self {
|
||||||
|
t.tx_type = TxLogEntryType::TxReceivedSelf;
|
||||||
|
}
|
||||||
t.tx_slate_id = Some(slate_id);
|
t.tx_slate_id = Some(slate_id);
|
||||||
t.amount_credited = amount;
|
t.amount_credited = amount;
|
||||||
t.num_outputs = 1;
|
t.num_outputs = 1;
|
||||||
|
|
|
@ -32,6 +32,7 @@ pub fn receive_tx<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
slate: &mut Slate,
|
slate: &mut Slate,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
|
is_self: bool,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K>,
|
T: WalletBackend<C, K>,
|
||||||
|
@ -39,8 +40,12 @@ where
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// create an output using the amount in the slate
|
// create an output using the amount in the slate
|
||||||
let (_, mut context, receiver_create_fn) =
|
let (_, mut context, receiver_create_fn) = selection::build_recipient_output_with_slate(
|
||||||
selection::build_recipient_output_with_slate(wallet, slate, parent_key_id.clone())?;
|
wallet,
|
||||||
|
slate,
|
||||||
|
parent_key_id.clone(),
|
||||||
|
is_self,
|
||||||
|
)?;
|
||||||
|
|
||||||
// fill public keys
|
// fill public keys
|
||||||
let _ = slate.fill_round_1(
|
let _ = slate.fill_round_1(
|
||||||
|
@ -69,6 +74,7 @@ pub fn create_send_tx<T: ?Sized, C, K>(
|
||||||
num_change_outputs: usize,
|
num_change_outputs: usize,
|
||||||
selection_strategy_is_use_all: bool,
|
selection_strategy_is_use_all: bool,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
|
is_self: bool,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
Slate,
|
Slate,
|
||||||
|
@ -107,6 +113,7 @@ where
|
||||||
num_change_outputs,
|
num_change_outputs,
|
||||||
selection_strategy_is_use_all,
|
selection_strategy_is_use_all,
|
||||||
parent_key_id.clone(),
|
parent_key_id.clone(),
|
||||||
|
is_self,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Generate a kernel offset and subtract from our context's secret key. Store
|
// Generate a kernel offset and subtract from our context's secret key. Store
|
||||||
|
|
|
@ -546,6 +546,10 @@ pub enum TxLogEntryType {
|
||||||
TxReceived,
|
TxReceived,
|
||||||
/// Inputs locked + change outputs when a transaction is created
|
/// Inputs locked + change outputs when a transaction is created
|
||||||
TxSent,
|
TxSent,
|
||||||
|
/// As above, but self-transaction
|
||||||
|
TxReceivedSelf,
|
||||||
|
/// As Above
|
||||||
|
TxSentSelf,
|
||||||
/// Received transaction that was rolled back by user
|
/// Received transaction that was rolled back by user
|
||||||
TxReceivedCancelled,
|
TxReceivedCancelled,
|
||||||
/// Sent transaction that was rolled back by user
|
/// Sent transaction that was rolled back by user
|
||||||
|
@ -558,6 +562,8 @@ impl fmt::Display for TxLogEntryType {
|
||||||
TxLogEntryType::ConfirmedCoinbase => write!(f, "Confirmed \nCoinbase"),
|
TxLogEntryType::ConfirmedCoinbase => write!(f, "Confirmed \nCoinbase"),
|
||||||
TxLogEntryType::TxReceived => write!(f, "Received Tx"),
|
TxLogEntryType::TxReceived => write!(f, "Received Tx"),
|
||||||
TxLogEntryType::TxSent => write!(f, "Sent Tx"),
|
TxLogEntryType::TxSent => write!(f, "Sent Tx"),
|
||||||
|
TxLogEntryType::TxReceivedSelf => write!(f, "Received Tx (Self)"),
|
||||||
|
TxLogEntryType::TxSentSelf => write!(f, "Sent Tx (Self)"),
|
||||||
TxLogEntryType::TxReceivedCancelled => write!(f, "Received Tx\n- Cancelled"),
|
TxLogEntryType::TxReceivedCancelled => write!(f, "Received Tx\n- Cancelled"),
|
||||||
TxLogEntryType::TxSentCancelled => write!(f, "Send Tx\n- Cancelled"),
|
TxLogEntryType::TxSentCancelled => write!(f, "Send Tx\n- Cancelled"),
|
||||||
}
|
}
|
||||||
|
|
162
wallet/tests/self_send.rs
Normal file
162
wallet/tests/self_send.rs
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Test a wallet sending to self
|
||||||
|
extern crate grin_chain as chain;
|
||||||
|
extern crate grin_core as core;
|
||||||
|
extern crate grin_keychain as keychain;
|
||||||
|
extern crate grin_store as store;
|
||||||
|
extern crate grin_util as util;
|
||||||
|
extern crate grin_wallet as wallet;
|
||||||
|
extern crate rand;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
extern crate chrono;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate uuid;
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
use common::testclient::{LocalWalletClient, WalletProxy};
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use core::global;
|
||||||
|
use core::global::ChainTypes;
|
||||||
|
use keychain::ExtKeychain;
|
||||||
|
use wallet::libwallet;
|
||||||
|
|
||||||
|
fn clean_output_dir(test_dir: &str) {
|
||||||
|
let _ = fs::remove_dir_all(test_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(test_dir: &str) {
|
||||||
|
util::init_test_logger();
|
||||||
|
clean_output_dir(test_dir);
|
||||||
|
global::set_mining_mode(ChainTypes::AutomatedTesting);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// self send impl
|
||||||
|
fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
|
setup(test_dir);
|
||||||
|
// Create a new proxy to simulate server and wallet responses
|
||||||
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
|
||||||
|
let chain = wallet_proxy.chain.clone();
|
||||||
|
|
||||||
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
|
// proxy
|
||||||
|
let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
|
let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client.clone());
|
||||||
|
wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
|
// define recipient wallet, add to proxy
|
||||||
|
let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client.clone());
|
||||||
|
let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
|
wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone());
|
||||||
|
|
||||||
|
// Set the wallet proxy listener running
|
||||||
|
thread::spawn(move || {
|
||||||
|
if let Err(e) = wallet_proxy.run() {
|
||||||
|
error!("Wallet Proxy error: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// few values to keep things shorter
|
||||||
|
let reward = core::consensus::REWARD;
|
||||||
|
|
||||||
|
// add some accounts
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
api.new_account_path("mining")?;
|
||||||
|
api.new_account_path("listener")?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// add account to wallet 2
|
||||||
|
wallet::controller::owner_single_use(wallet2.clone(), |api| {
|
||||||
|
api.new_account_path("listener")?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Default wallet 2 to listen on that account
|
||||||
|
{
|
||||||
|
let mut w = wallet2.lock();
|
||||||
|
w.set_parent_key_id_by_name("listener")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get some mining done
|
||||||
|
{
|
||||||
|
let mut w = wallet1.lock();
|
||||||
|
w.set_parent_key_id_by_name("mining")?;
|
||||||
|
}
|
||||||
|
let mut bh = 10u64;
|
||||||
|
let _ = common::award_blocks_to_wallet(&chain, wallet1.clone(), bh as usize);
|
||||||
|
|
||||||
|
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true)?;
|
||||||
|
assert!(wallet1_refreshed);
|
||||||
|
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||||
|
assert_eq!(wallet1_info.total, bh * reward);
|
||||||
|
// send to send
|
||||||
|
let slate = api.issue_self_tx(
|
||||||
|
reward * 2, // amount
|
||||||
|
2, // minimum confirmations
|
||||||
|
500, // max outputs
|
||||||
|
1, // num change outputs
|
||||||
|
true, // select all outputs
|
||||||
|
"mining",
|
||||||
|
"listener",
|
||||||
|
)?;
|
||||||
|
api.post_tx(&slate, false)?; //mines a block
|
||||||
|
bh += 1;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let _ = common::award_blocks_to_wallet(&chain, wallet1.clone(), 3);
|
||||||
|
bh += 3;
|
||||||
|
|
||||||
|
// Check total in mining account
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true)?;
|
||||||
|
assert!(wallet1_refreshed);
|
||||||
|
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||||
|
assert_eq!(wallet1_info.total, bh * reward - reward * 2);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Check total in 'listener' account
|
||||||
|
{
|
||||||
|
let mut w = wallet1.lock();
|
||||||
|
w.set_parent_key_id_by_name("listener")?;
|
||||||
|
}
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true)?;
|
||||||
|
assert!(wallet1_refreshed);
|
||||||
|
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||||
|
assert_eq!(wallet1_info.total, 2 * reward);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// let logging finish
|
||||||
|
thread::sleep(Duration::from_millis(200));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wallet_stress() {
|
||||||
|
let test_dir = "test_output/self_send";
|
||||||
|
if let Err(e) = self_send_test_impl(test_dir) {
|
||||||
|
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue