Fixed issue with top-dir without init, better absolut e path and more cleanup
This commit is contained in:
+88
-88
@@ -1,88 +1,88 @@
|
||||
[package]
|
||||
name = "grin_wallet_controller"
|
||||
version = "5.4.0-alpha.1"
|
||||
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
|
||||
description = "Controllers for grin wallet instantiation"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/mimblewimble/grin-wallet"
|
||||
keywords = [ "crypto", "grin", "mimblewimble" ]
|
||||
exclude = ["**/*.grin", "**/*.grin2"]
|
||||
#build = "src/build/build.rs"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3"
|
||||
hyper = "0.13"
|
||||
rand = "0.7"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
serde_json = "1"
|
||||
log = "0.4"
|
||||
prettytable-rs = "0.10"
|
||||
ring = "0.16"
|
||||
term = "0.6"
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
url = "2.1"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
easy-jsonrpc-mw = "0.5.4"
|
||||
lazy_static = "1"
|
||||
thiserror = "1"
|
||||
qr_code = "1.1.0"
|
||||
|
||||
grin_wallet_util = { path = "../util", version = "5.4.0-alpha.1" }
|
||||
grin_wallet_api = { path = "../api", version = "5.4.0-alpha.1" }
|
||||
grin_wallet_impls = { path = "../impls", version = "5.4.0-alpha.1" }
|
||||
grin_wallet_libwallet = { path = "../libwallet", version = "5.4.0-alpha.1" }
|
||||
grin_wallet_config = { path = "../config", version = "5.4.0-alpha.1" }
|
||||
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_core = "5.3.3"
|
||||
grin_keychain = "5.3.3"
|
||||
grin_util = "5.3.3"
|
||||
grin_api = "5.3.3"
|
||||
|
||||
# For beta release
|
||||
|
||||
# grin_core = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3"}
|
||||
# grin_keychain = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
|
||||
# grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
|
||||
# grin_api = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
|
||||
|
||||
# For bleeding edge
|
||||
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_api = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
|
||||
# For local testing
|
||||
# grin_core = { path = "../../grin/core"}
|
||||
# grin_keychain = { path = "../../grin/keychain"}
|
||||
# grin_util = { path = "../../grin/util"}
|
||||
# grin_api = { path = "../../grin/api"}
|
||||
|
||||
#####
|
||||
|
||||
[dev-dependencies]
|
||||
ed25519-dalek = "1.0.0-pre.4"
|
||||
remove_dir_all = "0.7"
|
||||
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_chain = "5.3.3"
|
||||
|
||||
# For beta release
|
||||
|
||||
# grin_chain = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
|
||||
|
||||
# For bleeding edge
|
||||
# grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
|
||||
# For local testing
|
||||
# grin_chain = { path = "../../grin/chain"}
|
||||
|
||||
#####
|
||||
|
||||
[package]
|
||||
name = "grin_wallet_controller"
|
||||
version = "5.4.0-alpha.1"
|
||||
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
|
||||
description = "Controllers for grin wallet instantiation"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/mimblewimble/grin-wallet"
|
||||
keywords = [ "crypto", "grin", "mimblewimble" ]
|
||||
exclude = ["**/*.grin", "**/*.grin2"]
|
||||
#build = "src/build/build.rs"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3"
|
||||
hyper = "0.13"
|
||||
rand = "0.7"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
serde_json = "1"
|
||||
log = "0.4"
|
||||
prettytable-rs = "0.10"
|
||||
ring = "0.16"
|
||||
term = "0.6"
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
url = "2.1"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
easy-jsonrpc-mw = "0.5.4"
|
||||
lazy_static = "1"
|
||||
thiserror = "1"
|
||||
qr_code = "1.1.0"
|
||||
|
||||
grin_wallet_util = { path = "../util", version = "5.4.0-alpha.1" }
|
||||
grin_wallet_api = { path = "../api", version = "5.4.0-alpha.1" }
|
||||
grin_wallet_impls = { path = "../impls", version = "5.4.0-alpha.1" }
|
||||
grin_wallet_libwallet = { path = "../libwallet", version = "5.4.0-alpha.1" }
|
||||
grin_wallet_config = { path = "../config", version = "5.4.0-alpha.1" }
|
||||
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_core = "5.3.3"
|
||||
grin_keychain = "5.3.3"
|
||||
grin_util = "5.3.3"
|
||||
grin_api = "5.3.3"
|
||||
|
||||
# For beta release
|
||||
|
||||
# grin_core = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3"}
|
||||
# grin_keychain = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
|
||||
# grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
|
||||
# grin_api = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
|
||||
|
||||
# For bleeding edge
|
||||
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_api = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
|
||||
# For local testing
|
||||
# grin_core = { path = "../../grin/core"}
|
||||
# grin_keychain = { path = "../../grin/keychain"}
|
||||
# grin_util = { path = "../../grin/util"}
|
||||
# grin_api = { path = "../../grin/api"}
|
||||
|
||||
#####
|
||||
|
||||
[dev-dependencies]
|
||||
ed25519-dalek = "1.0.0-pre.4"
|
||||
remove_dir_all = "0.7"
|
||||
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_chain = "5.3.3"
|
||||
|
||||
# For beta release
|
||||
|
||||
# grin_chain = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
|
||||
|
||||
# For bleeding edge
|
||||
# grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
|
||||
# For local testing
|
||||
# grin_chain = { path = "../../grin/chain"}
|
||||
|
||||
#####
|
||||
|
||||
|
||||
+1482
-1482
File diff suppressed because it is too large
Load Diff
+875
-875
File diff suppressed because it is too large
Load Diff
+630
-630
File diff suppressed because it is too large
Load Diff
+105
-105
@@ -1,105 +1,105 @@
|
||||
// Copyright 2021 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::api;
|
||||
use crate::core::core::transaction;
|
||||
use crate::core::libtx;
|
||||
use crate::impls;
|
||||
use crate::keychain;
|
||||
use crate::libwallet;
|
||||
|
||||
/// Wallet errors, mostly wrappers around underlying crypto or I/O errors.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// LibTX Error
|
||||
#[error("LibTx Error")]
|
||||
LibTX(#[from] libtx::Error),
|
||||
|
||||
/// Impls error
|
||||
#[error("Impls Error")]
|
||||
Impls(#[from] impls::Error),
|
||||
|
||||
/// LibWallet Error
|
||||
#[error("LibWallet Error: {0}")]
|
||||
LibWallet(#[from] libwallet::Error),
|
||||
|
||||
/// Keychain error
|
||||
#[error("Keychain error")]
|
||||
Keychain(#[from] keychain::Error),
|
||||
|
||||
/// Transaction Error
|
||||
#[error("Transaction error")]
|
||||
Transaction(#[from] transaction::Error),
|
||||
|
||||
/// Secp Error
|
||||
#[error("Secp error")]
|
||||
Secp,
|
||||
|
||||
/// Filewallet error
|
||||
#[error("Wallet data error: {0}")]
|
||||
FileWallet(&'static str),
|
||||
|
||||
/// Error when formatting json
|
||||
#[error("IO error")]
|
||||
IO,
|
||||
|
||||
/// Error when formatting json
|
||||
#[error("Serde JSON error")]
|
||||
Format,
|
||||
|
||||
/// Error when contacting a node through its API
|
||||
#[error("Node API error")]
|
||||
Node(#[from] api::Error),
|
||||
|
||||
/// Error originating from hyper.
|
||||
#[error("Hyper error")]
|
||||
Hyper,
|
||||
|
||||
/// Error originating from hyper uri parsing.
|
||||
#[error("Uri parsing error")]
|
||||
Uri,
|
||||
|
||||
/// Attempt to use duplicate transaction id in separate transactions
|
||||
#[error("Duplicate transaction ID error")]
|
||||
DuplicateTransactionId,
|
||||
|
||||
/// Wallet seed already exists
|
||||
#[error("Wallet seed file exists: {0}")]
|
||||
WalletSeedExists(String),
|
||||
|
||||
/// Wallet seed doesn't exist
|
||||
#[error("Wallet seed doesn't exist error")]
|
||||
WalletSeedDoesntExist,
|
||||
|
||||
/// Enc/Decryption Error
|
||||
#[error("Enc/Decryption error (check password?)")]
|
||||
Encryption,
|
||||
|
||||
/// BIP 39 word list
|
||||
#[error("BIP39 Mnemonic (word list) Error")]
|
||||
Mnemonic,
|
||||
|
||||
/// Command line argument error
|
||||
#[error("{0}")]
|
||||
ArgumentError(String),
|
||||
|
||||
/// Other
|
||||
#[error("Listener Startup Error")]
|
||||
ListenerError,
|
||||
|
||||
/// Other
|
||||
#[error("Generic error: {0}")]
|
||||
GenericError(String),
|
||||
}
|
||||
// Copyright 2021 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::api;
|
||||
use crate::core::core::transaction;
|
||||
use crate::core::libtx;
|
||||
use crate::impls;
|
||||
use crate::keychain;
|
||||
use crate::libwallet;
|
||||
|
||||
/// Wallet errors, mostly wrappers around underlying crypto or I/O errors.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// LibTX Error
|
||||
#[error("LibTx Error")]
|
||||
LibTX(#[from] libtx::Error),
|
||||
|
||||
/// Impls error
|
||||
#[error("Impls Error")]
|
||||
Impls(#[from] impls::Error),
|
||||
|
||||
/// LibWallet Error
|
||||
#[error("LibWallet Error: {0}")]
|
||||
LibWallet(#[from] libwallet::Error),
|
||||
|
||||
/// Keychain error
|
||||
#[error("Keychain error")]
|
||||
Keychain(#[from] keychain::Error),
|
||||
|
||||
/// Transaction Error
|
||||
#[error("Transaction error")]
|
||||
Transaction(#[from] transaction::Error),
|
||||
|
||||
/// Secp Error
|
||||
#[error("Secp error")]
|
||||
Secp,
|
||||
|
||||
/// Filewallet error
|
||||
#[error("Wallet data error: {0}")]
|
||||
FileWallet(&'static str),
|
||||
|
||||
/// Error when formatting json
|
||||
#[error("IO error")]
|
||||
IO,
|
||||
|
||||
/// Error when formatting json
|
||||
#[error("Serde JSON error")]
|
||||
Format,
|
||||
|
||||
/// Error when contacting a node through its API
|
||||
#[error("Node API error")]
|
||||
Node(#[from] api::Error),
|
||||
|
||||
/// Error originating from hyper.
|
||||
#[error("Hyper error")]
|
||||
Hyper,
|
||||
|
||||
/// Error originating from hyper uri parsing.
|
||||
#[error("Uri parsing error")]
|
||||
Uri,
|
||||
|
||||
/// Attempt to use duplicate transaction id in separate transactions
|
||||
#[error("Duplicate transaction ID error")]
|
||||
DuplicateTransactionId,
|
||||
|
||||
/// Wallet seed already exists
|
||||
#[error("Wallet seed file exists: {0}")]
|
||||
WalletSeedExists(String),
|
||||
|
||||
/// Wallet seed doesn't exist
|
||||
#[error("Wallet seed doesn't exist error")]
|
||||
WalletSeedDoesntExist,
|
||||
|
||||
/// Enc/Decryption Error
|
||||
#[error("Enc/Decryption error (check password?)")]
|
||||
Encryption,
|
||||
|
||||
/// BIP 39 word list
|
||||
#[error("BIP39 Mnemonic (word list) Error")]
|
||||
Mnemonic,
|
||||
|
||||
/// Command line argument error
|
||||
#[error("{0}")]
|
||||
ArgumentError(String),
|
||||
|
||||
/// Other
|
||||
#[error("Listener Startup Error")]
|
||||
ListenerError,
|
||||
|
||||
/// Other
|
||||
#[error("Generic error: {0}")]
|
||||
GenericError(String),
|
||||
}
|
||||
|
||||
+38
-38
@@ -1,38 +1,38 @@
|
||||
// Copyright 2021 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;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
use grin_api as api;
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use grin_wallet_api as apiwallet;
|
||||
use grin_wallet_config as config;
|
||||
use grin_wallet_impls as impls;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
|
||||
pub mod command;
|
||||
pub mod controller;
|
||||
pub mod display;
|
||||
mod error;
|
||||
|
||||
pub use crate::error::Error;
|
||||
// Copyright 2021 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;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
use grin_api as api;
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use grin_wallet_api as apiwallet;
|
||||
use grin_wallet_config as config;
|
||||
use grin_wallet_impls as impls;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
|
||||
pub mod command;
|
||||
pub mod controller;
|
||||
pub mod display;
|
||||
mod error;
|
||||
|
||||
pub use crate::error::Error;
|
||||
|
||||
+276
-276
@@ -1,276 +1,276 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! tests differing accounts in the same wallet
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
|
||||
use self::core::global;
|
||||
use self::keychain::{ExtKeychain, Keychain};
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::InitTxArgs;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Various tests on accounts within the same wallet
|
||||
fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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;
|
||||
let cm = global::coinbase_maturity(); // assume all testing precedes soft fork height
|
||||
|
||||
// test default accounts exist
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let accounts = api.accounts(m)?;
|
||||
assert_eq!(accounts[0].label, "default");
|
||||
assert_eq!(accounts[0].path, ExtKeychain::derive_key_id(2, 0, 0, 0, 0));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let new_path = api.create_account_path(m, "account1").unwrap();
|
||||
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
|
||||
let new_path = api.create_account_path(m, "account2").unwrap();
|
||||
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 2, 0, 0, 0));
|
||||
let new_path = api.create_account_path(m, "account3").unwrap();
|
||||
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 3, 0, 0, 0));
|
||||
// trying to add same label again should fail
|
||||
let res = api.create_account_path(m, "account1");
|
||||
assert!(res.is_err());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add account to wallet 2
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let new_path = api.create_account_path(m, "listener_account").unwrap();
|
||||
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Default wallet 2 to listen on that account
|
||||
{
|
||||
wallet_inst!(wallet2, w);
|
||||
w.set_parent_key_id_by_name("listener_account")?;
|
||||
}
|
||||
|
||||
// Mine into two different accounts in the same wallet
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
assert_eq!(w.parent_key_id(), ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
|
||||
}
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 7, false);
|
||||
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("account2")?;
|
||||
assert_eq!(w.parent_key_id(), ExtKeychain::derive_key_id(2, 2, 0, 0, 0));
|
||||
}
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false);
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 12);
|
||||
assert_eq!(wallet1_info.total, 5 * reward);
|
||||
assert_eq!(wallet1_info.amount_currently_spendable, (5 - cm) * reward);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 5);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// now check second account
|
||||
{
|
||||
// let mut w_lock = wallet1.lock();
|
||||
// let lc = w_lock.lc_provider()?;
|
||||
// let w = lc.wallet_inst()?;
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// check last confirmed height on this account is different from above (should be 0)
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?;
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 0);
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 12);
|
||||
assert_eq!(wallet1_info.total, 7 * reward);
|
||||
assert_eq!(wallet1_info.amount_currently_spendable, 7 * reward);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 7);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// should be nothing in default account
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("default")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?;
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 0);
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 12);
|
||||
assert_eq!(wallet1_info.total, 0,);
|
||||
assert_eq!(wallet1_info.amount_currently_spendable, 0,);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 0);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Send a tx to another wallet
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: reward,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let mut slate = api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 13);
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 9);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// other account should be untouched
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("account2")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?;
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 12);
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 13);
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
println!("{:?}", txs);
|
||||
assert_eq!(txs.len(), 5);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 2 should only have this tx on the listener account
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(wallet2_info.last_confirmed_height, 13);
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
Ok(())
|
||||
})?;
|
||||
// Default account on wallet 2 should be untouched
|
||||
{
|
||||
wallet_inst!(wallet2, w);
|
||||
w.set_parent_key_id_by_name("default")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (_, wallet2_info) = api.retrieve_summary_info(m, false, 1)?;
|
||||
assert_eq!(wallet2_info.last_confirmed_height, 0);
|
||||
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(wallet2_info.last_confirmed_height, 13);
|
||||
assert_eq!(wallet2_info.total, 0,);
|
||||
assert_eq!(wallet2_info.amount_currently_spendable, 0,);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 0);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accounts() {
|
||||
let test_dir = "test_output/accounts";
|
||||
setup(test_dir);
|
||||
if let Err(e) = accounts_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! tests differing accounts in the same wallet
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
|
||||
use self::core::global;
|
||||
use self::keychain::{ExtKeychain, Keychain};
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::InitTxArgs;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Various tests on accounts within the same wallet
|
||||
fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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;
|
||||
let cm = global::coinbase_maturity(); // assume all testing precedes soft fork height
|
||||
|
||||
// test default accounts exist
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let accounts = api.accounts(m)?;
|
||||
assert_eq!(accounts[0].label, "default");
|
||||
assert_eq!(accounts[0].path, ExtKeychain::derive_key_id(2, 0, 0, 0, 0));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let new_path = api.create_account_path(m, "account1").unwrap();
|
||||
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
|
||||
let new_path = api.create_account_path(m, "account2").unwrap();
|
||||
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 2, 0, 0, 0));
|
||||
let new_path = api.create_account_path(m, "account3").unwrap();
|
||||
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 3, 0, 0, 0));
|
||||
// trying to add same label again should fail
|
||||
let res = api.create_account_path(m, "account1");
|
||||
assert!(res.is_err());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add account to wallet 2
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let new_path = api.create_account_path(m, "listener_account").unwrap();
|
||||
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Default wallet 2 to listen on that account
|
||||
{
|
||||
wallet_inst!(wallet2, w);
|
||||
w.set_parent_key_id_by_name("listener_account")?;
|
||||
}
|
||||
|
||||
// Mine into two different accounts in the same wallet
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
assert_eq!(w.parent_key_id(), ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
|
||||
}
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 7, false);
|
||||
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("account2")?;
|
||||
assert_eq!(w.parent_key_id(), ExtKeychain::derive_key_id(2, 2, 0, 0, 0));
|
||||
}
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false);
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 12);
|
||||
assert_eq!(wallet1_info.total, 5 * reward);
|
||||
assert_eq!(wallet1_info.amount_currently_spendable, (5 - cm) * reward);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 5);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// now check second account
|
||||
{
|
||||
// let mut w_lock = wallet1.lock();
|
||||
// let lc = w_lock.lc_provider()?;
|
||||
// let w = lc.wallet_inst()?;
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// check last confirmed height on this account is different from above (should be 0)
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?;
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 0);
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 12);
|
||||
assert_eq!(wallet1_info.total, 7 * reward);
|
||||
assert_eq!(wallet1_info.amount_currently_spendable, 7 * reward);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 7);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// should be nothing in default account
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("default")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?;
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 0);
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 12);
|
||||
assert_eq!(wallet1_info.total, 0,);
|
||||
assert_eq!(wallet1_info.amount_currently_spendable, 0,);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 0);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Send a tx to another wallet
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: reward,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let mut slate = api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 13);
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 9);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// other account should be untouched
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("account2")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?;
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 12);
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert_eq!(wallet1_info.last_confirmed_height, 13);
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
println!("{:?}", txs);
|
||||
assert_eq!(txs.len(), 5);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 2 should only have this tx on the listener account
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(wallet2_info.last_confirmed_height, 13);
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
Ok(())
|
||||
})?;
|
||||
// Default account on wallet 2 should be untouched
|
||||
{
|
||||
wallet_inst!(wallet2, w);
|
||||
w.set_parent_key_id_by_name("default")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (_, wallet2_info) = api.retrieve_summary_info(m, false, 1)?;
|
||||
assert_eq!(wallet2_info.last_confirmed_height, 0);
|
||||
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(wallet2_info.last_confirmed_height, 13);
|
||||
assert_eq!(wallet2_info.total, 0,);
|
||||
assert_eq!(wallet2_info.amount_currently_spendable, 0,);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 0);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accounts() {
|
||||
let test_dir = "test_output/accounts";
|
||||
setup(test_dir);
|
||||
if let Err(e) = accounts_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+171
-171
@@ -1,171 +1,171 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! tests whose only purpose is to build up a 'real' looking chain with
|
||||
//! actual transactions for testing purposes
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
use grin_core as core;
|
||||
|
||||
use self::libwallet::{InitTxArgs, Slate};
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use rand::Rng;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Builds a chain with real transactions up to the given height
|
||||
fn build_chain(test_dir: &'static str, block_height: usize) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
debug!("Mask1: {:?}", mask1);
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
debug!("Mask2: {:?}", mask2);
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Stop the scanning updater threads because it extends the time needed to build the chain
|
||||
// exponentially
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, _m| {
|
||||
api.stop_updater()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, _m| {
|
||||
api.stop_updater()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// few values to keep things shorter
|
||||
let reward = core::consensus::REWARD;
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
// Start off with a few blocks
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
|
||||
for _ in 0..block_height {
|
||||
let mut wallet_1_has_funds = false;
|
||||
|
||||
// Check wallet 1 contents
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
debug!(
|
||||
"Wallet 1 spendable - {}",
|
||||
wallet1_info.amount_currently_spendable
|
||||
);
|
||||
if wallet1_info.amount_currently_spendable > reward {
|
||||
wallet_1_has_funds = true;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let's say 1 in every 3 blocks has a transaction (i.e. random 0 here and wallet1 has funds)
|
||||
let transact = rng.gen_range(0, 2) == 0;
|
||||
if !transact || !wallet_1_has_funds {
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 1, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// send a random tx or three
|
||||
let num_txs = rng.gen_range(0, 3);
|
||||
for _ in 0..num_txs {
|
||||
let amount: u64 = rng.gen_range(1, 10_000_000_001);
|
||||
let mut slate = Slate::blank(1, false);
|
||||
debug!("Creating TX for {}", amount);
|
||||
wallet::controller::owner_single_use(
|
||||
Some(wallet1.clone()),
|
||||
mask1,
|
||||
None,
|
||||
|sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 1,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn build_chain_to_height() {
|
||||
// ******************
|
||||
// If letting this run for a while to build a chain, recommend also tweaking scan threshold around 1112 of owner.rs:
|
||||
// ***
|
||||
// let start_index = last_scanned_block.height.saturating_sub(1);
|
||||
// ***
|
||||
// TODO: Make this parameter somehow
|
||||
// ******************
|
||||
|
||||
let test_dir = "test_output/build_chain";
|
||||
clean_output_dir(test_dir);
|
||||
setup(test_dir);
|
||||
if let Err(e) = build_chain(test_dir, 2048) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
// don't clean to get the result for testing
|
||||
}
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! tests whose only purpose is to build up a 'real' looking chain with
|
||||
//! actual transactions for testing purposes
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
use grin_core as core;
|
||||
|
||||
use self::libwallet::{InitTxArgs, Slate};
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use rand::Rng;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Builds a chain with real transactions up to the given height
|
||||
fn build_chain(test_dir: &'static str, block_height: usize) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
debug!("Mask1: {:?}", mask1);
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
debug!("Mask2: {:?}", mask2);
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Stop the scanning updater threads because it extends the time needed to build the chain
|
||||
// exponentially
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, _m| {
|
||||
api.stop_updater()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, _m| {
|
||||
api.stop_updater()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// few values to keep things shorter
|
||||
let reward = core::consensus::REWARD;
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
// Start off with a few blocks
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
|
||||
for _ in 0..block_height {
|
||||
let mut wallet_1_has_funds = false;
|
||||
|
||||
// Check wallet 1 contents
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
debug!(
|
||||
"Wallet 1 spendable - {}",
|
||||
wallet1_info.amount_currently_spendable
|
||||
);
|
||||
if wallet1_info.amount_currently_spendable > reward {
|
||||
wallet_1_has_funds = true;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let's say 1 in every 3 blocks has a transaction (i.e. random 0 here and wallet1 has funds)
|
||||
let transact = rng.gen_range(0, 2) == 0;
|
||||
if !transact || !wallet_1_has_funds {
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 1, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// send a random tx or three
|
||||
let num_txs = rng.gen_range(0, 3);
|
||||
for _ in 0..num_txs {
|
||||
let amount: u64 = rng.gen_range(1, 10_000_000_001);
|
||||
let mut slate = Slate::blank(1, false);
|
||||
debug!("Creating TX for {}", amount);
|
||||
wallet::controller::owner_single_use(
|
||||
Some(wallet1.clone()),
|
||||
mask1,
|
||||
None,
|
||||
|sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 1,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn build_chain_to_height() {
|
||||
// ******************
|
||||
// If letting this run for a while to build a chain, recommend also tweaking scan threshold around 1112 of owner.rs:
|
||||
// ***
|
||||
// let start_index = last_scanned_block.height.saturating_sub(1);
|
||||
// ***
|
||||
// TODO: Make this parameter somehow
|
||||
// ******************
|
||||
|
||||
let test_dir = "test_output/build_chain";
|
||||
clean_output_dir(test_dir);
|
||||
setup(test_dir);
|
||||
if let Err(e) = build_chain(test_dir, 2048) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
// don't clean to get the result for testing
|
||||
}
|
||||
|
||||
+101
-101
@@ -1,101 +1,101 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_util;
|
||||
|
||||
use grin_core::core::OutputFeatures;
|
||||
use grin_keychain::{
|
||||
mnemonic, BlindingFactor, ExtKeychain, ExtKeychainPath, Keychain, SwitchCommitmentType,
|
||||
};
|
||||
use grin_util::{secp, ZeroingString};
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::LocalWalletClient;
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
fn build_output_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Generate seed so we can verify the blinding factor is derived correctly
|
||||
let seed: [u8; 32] = thread_rng().gen();
|
||||
let keychain = ExtKeychain::from_seed(&seed, false).unwrap();
|
||||
let mnemonic = mnemonic::from_entropy(&seed).unwrap();
|
||||
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
Some(ZeroingString::from(mnemonic)),
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
|
||||
let features = OutputFeatures::Plain;
|
||||
let amount = 60_000_000_000;
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
let built_output = sender_api.build_output(m, features, amount)?;
|
||||
|
||||
let key_id = built_output.key_id;
|
||||
assert_eq!(key_id.to_path(), ExtKeychainPath::new(3, 0, 0, 0, 0));
|
||||
|
||||
let blind = built_output.blind;
|
||||
let key = keychain.derive_key(amount, &key_id, SwitchCommitmentType::Regular)?;
|
||||
assert_eq!(blind, BlindingFactor::from_secret_key(key.clone()));
|
||||
|
||||
let output = built_output.output;
|
||||
assert_eq!(output.features(), features);
|
||||
assert_eq!(output.commitment(), secp.commit(amount, key)?);
|
||||
output.verify_proof()?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_output() {
|
||||
let test_dir = "test_output/build_output";
|
||||
setup(test_dir);
|
||||
if let Err(e) = build_output_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_util;
|
||||
|
||||
use grin_core::core::OutputFeatures;
|
||||
use grin_keychain::{
|
||||
mnemonic, BlindingFactor, ExtKeychain, ExtKeychainPath, Keychain, SwitchCommitmentType,
|
||||
};
|
||||
use grin_util::{secp, ZeroingString};
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::LocalWalletClient;
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
fn build_output_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Generate seed so we can verify the blinding factor is derived correctly
|
||||
let seed: [u8; 32] = thread_rng().gen();
|
||||
let keychain = ExtKeychain::from_seed(&seed, false).unwrap();
|
||||
let mnemonic = mnemonic::from_entropy(&seed).unwrap();
|
||||
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
Some(ZeroingString::from(mnemonic)),
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
|
||||
let features = OutputFeatures::Plain;
|
||||
let amount = 60_000_000_000;
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
let built_output = sender_api.build_output(m, features, amount)?;
|
||||
|
||||
let key_id = built_output.key_id;
|
||||
assert_eq!(key_id.to_path(), ExtKeychainPath::new(3, 0, 0, 0, 0));
|
||||
|
||||
let blind = built_output.blind;
|
||||
let key = keychain.derive_key(amount, &key_id, SwitchCommitmentType::Regular)?;
|
||||
assert_eq!(blind, BlindingFactor::from_secret_key(key.clone()));
|
||||
|
||||
let output = built_output.output;
|
||||
assert_eq!(output.features(), features);
|
||||
assert_eq!(output.commitment(), secp.commit(amount, key)?);
|
||||
output.verify_proof()?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_output() {
|
||||
let test_dir = "test_output/build_output";
|
||||
setup(test_dir);
|
||||
if let Err(e) = build_output_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+878
-878
File diff suppressed because it is too large
Load Diff
+178
-178
@@ -1,178 +1,178 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! common functions for tests (instantiating wallet and proxy, mostly)
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
|
||||
use self::core::global;
|
||||
use self::core::global::ChainTypes;
|
||||
use self::keychain::ExtKeychain;
|
||||
use self::libwallet::WalletInst;
|
||||
use impls::test_framework::{LocalWalletClient, WalletProxy};
|
||||
use impls::{DefaultLCProvider, DefaultWalletImpl};
|
||||
use std::sync::Arc;
|
||||
use util::secp::key::SecretKey;
|
||||
use util::{Mutex, ZeroingString};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! wallet_inst {
|
||||
($wallet:ident, $w: ident) => {
|
||||
let mut w_lock = $wallet.lock();
|
||||
let lc = w_lock.lc_provider()?;
|
||||
let $w = lc.wallet_inst()?;
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! create_wallet_and_add {
|
||||
($client:ident, $wallet: ident, $mask: ident, $test_dir: expr, $name: expr, $seed_phrase: expr, $proxy: expr, $create_mask: expr) => {
|
||||
let $client = LocalWalletClient::new($name, $proxy.tx.clone());
|
||||
let ($wallet, $mask) = common::create_local_wallet(
|
||||
$test_dir,
|
||||
$name,
|
||||
$seed_phrase.clone(),
|
||||
$client.clone(),
|
||||
$create_mask,
|
||||
);
|
||||
$proxy.add_wallet(
|
||||
$name,
|
||||
$client.get_send_instance(),
|
||||
$wallet.clone(),
|
||||
$mask.clone(),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! open_wallet_and_add {
|
||||
($client:ident, $wallet: ident, $mask: ident, $test_dir: expr, $name: expr, $proxy: expr, $create_mask: expr) => {
|
||||
let $client = LocalWalletClient::new($name, $proxy.tx.clone());
|
||||
let ($wallet, $mask) =
|
||||
common::open_local_wallet($test_dir, $name, $client.clone(), $create_mask);
|
||||
$proxy.add_wallet(
|
||||
$name,
|
||||
$client.get_send_instance(),
|
||||
$wallet.clone(),
|
||||
$mask.clone(),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub fn clean_output_dir(test_dir: &str) {
|
||||
let path = std::path::Path::new(test_dir);
|
||||
if path.is_dir() {
|
||||
remove_dir_all::remove_dir_all(test_dir).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup(test_dir: &str) {
|
||||
util::init_test_logger();
|
||||
clean_output_dir(test_dir);
|
||||
global::set_local_chain_type(ChainTypes::AutomatedTesting);
|
||||
}
|
||||
|
||||
/// Some tests require the global chain_type to be configured due to threads being spawned internally.
|
||||
/// It is recommended to avoid relying on this if at all possible as global chain_type
|
||||
/// leaks across multiple tests and will likely have unintended consequences.
|
||||
#[allow(dead_code)]
|
||||
pub fn setup_global_chain_type() {
|
||||
global::init_global_chain_type(global::ChainTypes::AutomatedTesting);
|
||||
}
|
||||
|
||||
pub fn create_wallet_proxy(
|
||||
test_dir: &str,
|
||||
) -> WalletProxy<DefaultLCProvider<LocalWalletClient, ExtKeychain>, LocalWalletClient, ExtKeychain>
|
||||
{
|
||||
WalletProxy::new(test_dir)
|
||||
}
|
||||
|
||||
pub fn create_local_wallet(
|
||||
test_dir: &str,
|
||||
name: &str,
|
||||
mnemonic: Option<ZeroingString>,
|
||||
client: LocalWalletClient,
|
||||
create_mask: bool,
|
||||
) -> (
|
||||
Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
Option<SecretKey>,
|
||||
) {
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>;
|
||||
let lc = wallet.lc_provider().unwrap();
|
||||
let _ = lc.set_top_level_directory(&format!("{}/{}", test_dir, name));
|
||||
lc.create_wallet(None, mnemonic, 32, ZeroingString::from(""), false)
|
||||
.unwrap();
|
||||
let mask = lc
|
||||
.open_wallet(None, ZeroingString::from(""), create_mask, false)
|
||||
.unwrap();
|
||||
(Arc::new(Mutex::new(wallet)), mask)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn open_local_wallet(
|
||||
test_dir: &str,
|
||||
name: &str,
|
||||
client: LocalWalletClient,
|
||||
create_mask: bool,
|
||||
) -> (
|
||||
Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
Option<SecretKey>,
|
||||
) {
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>;
|
||||
let lc = wallet.lc_provider().unwrap();
|
||||
let _ = lc.set_top_level_directory(&format!("{}/{}", test_dir, name));
|
||||
let mask = lc
|
||||
.open_wallet(None, ZeroingString::from(""), create_mask, false)
|
||||
.unwrap();
|
||||
(Arc::new(Mutex::new(wallet)), mask)
|
||||
}
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! common functions for tests (instantiating wallet and proxy, mostly)
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
|
||||
use self::core::global;
|
||||
use self::core::global::ChainTypes;
|
||||
use self::keychain::ExtKeychain;
|
||||
use self::libwallet::WalletInst;
|
||||
use impls::test_framework::{LocalWalletClient, WalletProxy};
|
||||
use impls::{DefaultLCProvider, DefaultWalletImpl};
|
||||
use std::sync::Arc;
|
||||
use util::secp::key::SecretKey;
|
||||
use util::{Mutex, ZeroingString};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! wallet_inst {
|
||||
($wallet:ident, $w: ident) => {
|
||||
let mut w_lock = $wallet.lock();
|
||||
let lc = w_lock.lc_provider()?;
|
||||
let $w = lc.wallet_inst()?;
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! create_wallet_and_add {
|
||||
($client:ident, $wallet: ident, $mask: ident, $test_dir: expr, $name: expr, $seed_phrase: expr, $proxy: expr, $create_mask: expr) => {
|
||||
let $client = LocalWalletClient::new($name, $proxy.tx.clone());
|
||||
let ($wallet, $mask) = common::create_local_wallet(
|
||||
$test_dir,
|
||||
$name,
|
||||
$seed_phrase.clone(),
|
||||
$client.clone(),
|
||||
$create_mask,
|
||||
);
|
||||
$proxy.add_wallet(
|
||||
$name,
|
||||
$client.get_send_instance(),
|
||||
$wallet.clone(),
|
||||
$mask.clone(),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! open_wallet_and_add {
|
||||
($client:ident, $wallet: ident, $mask: ident, $test_dir: expr, $name: expr, $proxy: expr, $create_mask: expr) => {
|
||||
let $client = LocalWalletClient::new($name, $proxy.tx.clone());
|
||||
let ($wallet, $mask) =
|
||||
common::open_local_wallet($test_dir, $name, $client.clone(), $create_mask);
|
||||
$proxy.add_wallet(
|
||||
$name,
|
||||
$client.get_send_instance(),
|
||||
$wallet.clone(),
|
||||
$mask.clone(),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub fn clean_output_dir(test_dir: &str) {
|
||||
let path = std::path::Path::new(test_dir);
|
||||
if path.is_dir() {
|
||||
remove_dir_all::remove_dir_all(test_dir).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup(test_dir: &str) {
|
||||
util::init_test_logger();
|
||||
clean_output_dir(test_dir);
|
||||
global::set_local_chain_type(ChainTypes::AutomatedTesting);
|
||||
}
|
||||
|
||||
/// Some tests require the global chain_type to be configured due to threads being spawned internally.
|
||||
/// It is recommended to avoid relying on this if at all possible as global chain_type
|
||||
/// leaks across multiple tests and will likely have unintended consequences.
|
||||
#[allow(dead_code)]
|
||||
pub fn setup_global_chain_type() {
|
||||
global::init_global_chain_type(global::ChainTypes::AutomatedTesting);
|
||||
}
|
||||
|
||||
pub fn create_wallet_proxy(
|
||||
test_dir: &str,
|
||||
) -> WalletProxy<DefaultLCProvider<LocalWalletClient, ExtKeychain>, LocalWalletClient, ExtKeychain>
|
||||
{
|
||||
WalletProxy::new(test_dir)
|
||||
}
|
||||
|
||||
pub fn create_local_wallet(
|
||||
test_dir: &str,
|
||||
name: &str,
|
||||
mnemonic: Option<ZeroingString>,
|
||||
client: LocalWalletClient,
|
||||
create_mask: bool,
|
||||
) -> (
|
||||
Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
Option<SecretKey>,
|
||||
) {
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>;
|
||||
let lc = wallet.lc_provider().unwrap();
|
||||
let _ = lc.set_top_level_directory(&format!("{}/{}", test_dir, name));
|
||||
lc.create_wallet(None, mnemonic, 32, ZeroingString::from(""), false)
|
||||
.unwrap();
|
||||
let mask = lc
|
||||
.open_wallet(None, ZeroingString::from(""), create_mask, false)
|
||||
.unwrap();
|
||||
(Arc::new(Mutex::new(wallet)), mask)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn open_local_wallet(
|
||||
test_dir: &str,
|
||||
name: &str,
|
||||
client: LocalWalletClient,
|
||||
create_mask: bool,
|
||||
) -> (
|
||||
Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
Option<SecretKey>,
|
||||
) {
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>;
|
||||
let lc = wallet.lc_provider().unwrap();
|
||||
let _ = lc.set_top_level_directory(&format!("{}/{}", test_dir, name));
|
||||
let mask = lc
|
||||
.open_wallet(None, ZeroingString::from(""), create_mask, false)
|
||||
.unwrap();
|
||||
(Arc::new(Mutex::new(wallet)), mask)
|
||||
}
|
||||
|
||||
+320
-320
@@ -1,320 +1,320 @@
|
||||
// Copyright 2021 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 file send/recieve
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use impls::{PathToSlate, SlateGetter as _, SlatePutter as _};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use grin_wallet_libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate};
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.create_account_path(m, "account1")?;
|
||||
api.create_account_path(m, "account2")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let (send_file, receive_file, final_file) = match use_bin {
|
||||
false => (
|
||||
format!("{}/standard_S1.tx", test_dir),
|
||||
format!("{}/standard_S2.tx", test_dir),
|
||||
format!("{}/standard_S3.tx", test_dir),
|
||||
),
|
||||
true => (
|
||||
format!("{}/standard_S1.txbin", test_dir),
|
||||
format!("{}/standard_S2.txbin", test_dir),
|
||||
format!("{}/standard_S3.txbin", test_dir),
|
||||
),
|
||||
};
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward);
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward * 2,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
// output tx file
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet2, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
|
||||
let mut slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
|
||||
// wallet 2 receives file, completes, sends file back
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 1 finalises and posts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let mut slate = PathToSlate(receive_file.into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
// Output final file for reference
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// Check total in mining account
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward - reward * 2);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Check total in 'wallet 2' account
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(wallet2_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet2_info.total, 2 * reward);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Now other types of exchange, for reference
|
||||
// Invoice transaction
|
||||
let (send_file, receive_file, final_file) = match use_bin {
|
||||
false => (
|
||||
format!("{}/invoice_I1.tx", test_dir),
|
||||
format!("{}/invoice_I2.tx", test_dir),
|
||||
format!("{}/invoice_I3.tx", test_dir),
|
||||
),
|
||||
true => (
|
||||
format!("{}/invoice_I1.txbin", test_dir),
|
||||
format!("{}/invoice_I2.txbin", test_dir),
|
||||
format!("{}/invoice_I3.txbin", test_dir),
|
||||
),
|
||||
};
|
||||
|
||||
let mut slate = Slate::blank(2, true);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: 1000000000,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = PathToSlate((&receive_file).into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Standard, with payment proof
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
let (send_file, receive_file, final_file) = match use_bin {
|
||||
false => (
|
||||
format!("{}/standard_pp_S1.tx", test_dir),
|
||||
format!("{}/standard_pp_S2.tx", test_dir),
|
||||
format!("{}/standard_pp_S3.tx", test_dir),
|
||||
),
|
||||
true => (
|
||||
format!("{}/standard_pp_S1.txbin", test_dir),
|
||||
format!("{}/standard_pp_S2.txbin", test_dir),
|
||||
format!("{}/standard_pp_S3.txbin", test_dir),
|
||||
),
|
||||
};
|
||||
let mut slate = Slate::blank(2, true);
|
||||
let mut address = None;
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
address = Some(api.get_slatepack_address(m, 0)?);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
payment_proof_recipient_address: address.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 1 finalises and posts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
slate = PathToSlate(receive_file.into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
// Output final file for reference
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_file_exchange_json() {
|
||||
let test_dir = "test_output/file_exchange_json";
|
||||
setup(test_dir);
|
||||
// Json output
|
||||
if let Err(e) = file_exchange_test_impl(test_dir, false) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_file_exchange_bin() {
|
||||
let test_dir = "test_output/file_exchange_bin";
|
||||
setup(test_dir);
|
||||
if let Err(e) = file_exchange_test_impl(test_dir, true) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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 file send/recieve
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use impls::{PathToSlate, SlateGetter as _, SlatePutter as _};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use grin_wallet_libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate};
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.create_account_path(m, "account1")?;
|
||||
api.create_account_path(m, "account2")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let (send_file, receive_file, final_file) = match use_bin {
|
||||
false => (
|
||||
format!("{}/standard_S1.tx", test_dir),
|
||||
format!("{}/standard_S2.tx", test_dir),
|
||||
format!("{}/standard_S3.tx", test_dir),
|
||||
),
|
||||
true => (
|
||||
format!("{}/standard_S1.txbin", test_dir),
|
||||
format!("{}/standard_S2.txbin", test_dir),
|
||||
format!("{}/standard_S3.txbin", test_dir),
|
||||
),
|
||||
};
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward);
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward * 2,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
// output tx file
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet2, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
|
||||
let mut slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
|
||||
// wallet 2 receives file, completes, sends file back
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 1 finalises and posts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let mut slate = PathToSlate(receive_file.into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
// Output final file for reference
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// Check total in mining account
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward - reward * 2);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Check total in 'wallet 2' account
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(wallet2_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet2_info.total, 2 * reward);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Now other types of exchange, for reference
|
||||
// Invoice transaction
|
||||
let (send_file, receive_file, final_file) = match use_bin {
|
||||
false => (
|
||||
format!("{}/invoice_I1.tx", test_dir),
|
||||
format!("{}/invoice_I2.tx", test_dir),
|
||||
format!("{}/invoice_I3.tx", test_dir),
|
||||
),
|
||||
true => (
|
||||
format!("{}/invoice_I1.txbin", test_dir),
|
||||
format!("{}/invoice_I2.txbin", test_dir),
|
||||
format!("{}/invoice_I3.txbin", test_dir),
|
||||
),
|
||||
};
|
||||
|
||||
let mut slate = Slate::blank(2, true);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: 1000000000,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = PathToSlate((&receive_file).into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Standard, with payment proof
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
let (send_file, receive_file, final_file) = match use_bin {
|
||||
false => (
|
||||
format!("{}/standard_pp_S1.tx", test_dir),
|
||||
format!("{}/standard_pp_S2.tx", test_dir),
|
||||
format!("{}/standard_pp_S3.tx", test_dir),
|
||||
),
|
||||
true => (
|
||||
format!("{}/standard_pp_S1.txbin", test_dir),
|
||||
format!("{}/standard_pp_S2.txbin", test_dir),
|
||||
format!("{}/standard_pp_S3.txbin", test_dir),
|
||||
),
|
||||
};
|
||||
let mut slate = Slate::blank(2, true);
|
||||
let mut address = None;
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
address = Some(api.get_slatepack_address(m, 0)?);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
payment_proof_recipient_address: address.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 1 finalises and posts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
slate = PathToSlate(receive_file.into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
// Output final file for reference
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_file_exchange_json() {
|
||||
let test_dir = "test_output/file_exchange_json";
|
||||
setup(test_dir);
|
||||
// Json output
|
||||
if let Err(e) = file_exchange_test_impl(test_dir, false) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_file_exchange_bin() {
|
||||
let test_dir = "test_output/file_exchange_bin";
|
||||
setup(test_dir);
|
||||
if let Err(e) = file_exchange_test_impl(test_dir, true) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+305
-305
@@ -1,305 +1,305 @@
|
||||
// Copyright 2021 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
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate, SlateState};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut _bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, _bh as usize, false);
|
||||
|
||||
// Sanity check wallet 1 contents
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, _bh);
|
||||
assert_eq!(wallet1_info.total, _bh * reward);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let mut slate = Slate::blank(2, true);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 inititates an invoice transaction, requesting payment
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: reward * 2,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice1);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice2);
|
||||
|
||||
// wallet 2 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice3);
|
||||
|
||||
// wallet 1 posts so wallet 2 doesn't get the mined amount
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
_bh += 1;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
_bh += 3;
|
||||
|
||||
// Check transaction log for wallet 2
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (_, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert!(refreshed);
|
||||
assert!(txs.len() == 1);
|
||||
println!(
|
||||
"last confirmed height: {}, bh: {}",
|
||||
wallet2_info.last_confirmed_height, _bh
|
||||
);
|
||||
assert!(refreshed);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Check transaction log for wallet 1, ensure only 1 entry
|
||||
// exists
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(txs.len() as u64, _bh + 1);
|
||||
println!(
|
||||
"Wallet 1: last confirmed height: {}, bh: {}",
|
||||
wallet1_info.last_confirmed_height, _bh
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Test self-sending
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 inititates an invoice transaction, requesting payment
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: reward * 2,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
println!("Self invoice slate init: {}", slate);
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
println!("Self invoice slate after process: {}", slate);
|
||||
|
||||
// wallet 1 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
//bh += 3;
|
||||
|
||||
// As above, but use owner API to finalize
|
||||
let mut slate = Slate::blank(2, true);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 inititates an invoice transaction, requesting payment
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: reward * 2,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice1);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice2);
|
||||
|
||||
// wallet 2 finalizes via owner API
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice3);
|
||||
|
||||
// test that payee can only cancel once
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
_bh += 3;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 inititates an invoice transaction, requesting payment
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: reward * 2,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice1);
|
||||
|
||||
let orig_slate = slate.clone();
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.process_invoice_tx(m, &slate, args.clone())?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
|
||||
// Wallet 1 cancels the invoice transaction
|
||||
api.cancel_tx(m, None, Some(slate.id))?;
|
||||
|
||||
// Wallet 1 attempts to repay again
|
||||
let res = api.process_invoice_tx(m, &orig_slate, args);
|
||||
assert!(res.is_err());
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice2);
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_invoice_tx() -> Result<(), libwallet::Error> {
|
||||
let test_dir = "test_output/invoice_tx";
|
||||
setup(test_dir);
|
||||
invoice_tx_impl(test_dir)?;
|
||||
clean_output_dir(test_dir);
|
||||
Ok(())
|
||||
}
|
||||
// Copyright 2021 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
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate, SlateState};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut _bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, _bh as usize, false);
|
||||
|
||||
// Sanity check wallet 1 contents
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, _bh);
|
||||
assert_eq!(wallet1_info.total, _bh * reward);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let mut slate = Slate::blank(2, true);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 inititates an invoice transaction, requesting payment
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: reward * 2,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice1);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice2);
|
||||
|
||||
// wallet 2 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice3);
|
||||
|
||||
// wallet 1 posts so wallet 2 doesn't get the mined amount
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
_bh += 1;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
_bh += 3;
|
||||
|
||||
// Check transaction log for wallet 2
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (_, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert!(refreshed);
|
||||
assert!(txs.len() == 1);
|
||||
println!(
|
||||
"last confirmed height: {}, bh: {}",
|
||||
wallet2_info.last_confirmed_height, _bh
|
||||
);
|
||||
assert!(refreshed);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Check transaction log for wallet 1, ensure only 1 entry
|
||||
// exists
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(txs.len() as u64, _bh + 1);
|
||||
println!(
|
||||
"Wallet 1: last confirmed height: {}, bh: {}",
|
||||
wallet1_info.last_confirmed_height, _bh
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Test self-sending
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 inititates an invoice transaction, requesting payment
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: reward * 2,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
println!("Self invoice slate init: {}", slate);
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
println!("Self invoice slate after process: {}", slate);
|
||||
|
||||
// wallet 1 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
//bh += 3;
|
||||
|
||||
// As above, but use owner API to finalize
|
||||
let mut slate = Slate::blank(2, true);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 inititates an invoice transaction, requesting payment
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: reward * 2,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice1);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice2);
|
||||
|
||||
// wallet 2 finalizes via owner API
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice3);
|
||||
|
||||
// test that payee can only cancel once
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
_bh += 3;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 inititates an invoice transaction, requesting payment
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: reward * 2,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice1);
|
||||
|
||||
let orig_slate = slate.clone();
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.process_invoice_tx(m, &slate, args.clone())?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
|
||||
// Wallet 1 cancels the invoice transaction
|
||||
api.cancel_tx(m, None, Some(slate.id))?;
|
||||
|
||||
// Wallet 1 attempts to repay again
|
||||
let res = api.process_invoice_tx(m, &orig_slate, args);
|
||||
assert!(res.is_err());
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice2);
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_invoice_tx() -> Result<(), libwallet::Error> {
|
||||
let test_dir = "test_output/invoice_tx";
|
||||
setup(test_dir);
|
||||
invoice_tx_impl(test_dir)?;
|
||||
clean_output_dir(test_dir);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+158
-158
@@ -1,158 +1,158 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! Tests and experimentations with late locking
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
use self::libwallet::{InitTxArgs, Slate};
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn late_lock_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.create_account_path(m, "account1")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
{
|
||||
wallet_inst!(wallet2, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 10, false)?;
|
||||
|
||||
let mut slate = Slate::blank(2, false);
|
||||
let amount = 100_000_000_000;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
late_lock: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
println!("S1 SLATE: {}", slate_i);
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
println!("S2 SLATE: {}", slate);
|
||||
|
||||
// Note we don't call `tx_lock_outputs` on the sender side here,
|
||||
// as the outputs will only be locked during finalization
|
||||
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
println!("S3 SLATE: {}", slate);
|
||||
|
||||
// Now post tx to our node for inclusion in the next block.
|
||||
sender_api.post_tx(m, &slate, true)?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false)?;
|
||||
|
||||
// update/test contents of both accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
// Reward from mining 11 blocks, minus the amount sent.
|
||||
// Note: We mined the block containing the tx, so fees are effectively refunded.
|
||||
assert_eq!(560_000_000_000, wallet_info.amount_currently_spendable);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (wallet2_refreshed, wallet_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(amount, wallet_info.amount_currently_spendable);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn late_lock() {
|
||||
let test_dir = "test_output/late_lock";
|
||||
setup(test_dir);
|
||||
if let Err(e) = late_lock_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! Tests and experimentations with late locking
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
use self::libwallet::{InitTxArgs, Slate};
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn late_lock_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.create_account_path(m, "account1")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
{
|
||||
wallet_inst!(wallet2, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 10, false)?;
|
||||
|
||||
let mut slate = Slate::blank(2, false);
|
||||
let amount = 100_000_000_000;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
late_lock: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
println!("S1 SLATE: {}", slate_i);
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
println!("S2 SLATE: {}", slate);
|
||||
|
||||
// Note we don't call `tx_lock_outputs` on the sender side here,
|
||||
// as the outputs will only be locked during finalization
|
||||
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
println!("S3 SLATE: {}", slate);
|
||||
|
||||
// Now post tx to our node for inclusion in the next block.
|
||||
sender_api.post_tx(m, &slate, true)?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false)?;
|
||||
|
||||
// update/test contents of both accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
// Reward from mining 11 blocks, minus the amount sent.
|
||||
// Note: We mined the block containing the tx, so fees are effectively refunded.
|
||||
assert_eq!(560_000_000_000, wallet_info.amount_currently_spendable);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (wallet2_refreshed, wallet_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(amount, wallet_info.amount_currently_spendable);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn late_lock() {
|
||||
let test_dir = "test_output/late_lock";
|
||||
setup(test_dir);
|
||||
if let Err(e) = late_lock_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+190
-190
@@ -1,190 +1,190 @@
|
||||
// Copyright 2024 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, then creation of comsig request
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_util as util;
|
||||
use grin_util::secp::key::SecretKey;
|
||||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{mwixnet::MixnetReqCreationParams, InitTxArgs};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn mwixnet_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
// 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(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward);
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward * 2,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let mut slate = api.init_send_tx(m, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
// Send directly to self
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, Some("listener"), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
api.post_tx(m, &slate, false)?; // mines a block
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// Check total in mining account
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
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
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("listener")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, 2 * reward);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Recipient wallet creates a mwixnet request from the last output
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let secp_locked = util::static_secp_instance();
|
||||
let secp = secp_locked.lock();
|
||||
let server_pubkey_str_1 =
|
||||
"97444ae673bb92c713c1a2f7b8882ffbfc1c67401a280a775dce1a8651584332";
|
||||
let server_pubkey_str_2 =
|
||||
"0c9414341f2140ed34a5a12a6479bf5a6404820d001ab81d9d3e8cc38f049b4e";
|
||||
let server_pubkey_str_3 =
|
||||
"b58ece97d60e71bb7e53218400b0d67bfe6a3cb7d3b4a67a44f8fb7c525cbca5";
|
||||
let server_key_1 =
|
||||
SecretKey::from_slice(&secp, &grin_util::from_hex(&server_pubkey_str_1).unwrap())
|
||||
.unwrap();
|
||||
let server_key_2 =
|
||||
SecretKey::from_slice(&secp, &grin_util::from_hex(&server_pubkey_str_2).unwrap())
|
||||
.unwrap();
|
||||
let server_key_3 =
|
||||
SecretKey::from_slice(&secp, &grin_util::from_hex(&server_pubkey_str_3).unwrap())
|
||||
.unwrap();
|
||||
let params = MixnetReqCreationParams {
|
||||
server_keys: vec![server_key_1, server_key_2, server_key_3],
|
||||
fee_per_hop: 50_000_000,
|
||||
};
|
||||
let outputs = api.retrieve_outputs(mask1, false, false, None)?;
|
||||
// get last output
|
||||
let last_output = outputs.1[outputs.1.len() - 1].clone();
|
||||
|
||||
let mwixnet_req = api.create_mwixnet_req(m, ¶ms, &last_output.commit, true)?;
|
||||
|
||||
println!("MWIXNET REQ: {:?}", mwixnet_req);
|
||||
|
||||
// check output we created comsig for is indeed locked
|
||||
let outputs = api.retrieve_outputs(mask1, false, false, None)?;
|
||||
// get last output
|
||||
let last_output = outputs.1[outputs.1.len() - 1].clone();
|
||||
assert!(last_output.output.status == libwallet::OutputStatus::Locked);
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mwixnet_comsig_test() {
|
||||
let test_dir = "test_output/mwixnet";
|
||||
setup(test_dir);
|
||||
if let Err(e) = mwixnet_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2024 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, then creation of comsig request
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_util as util;
|
||||
use grin_util::secp::key::SecretKey;
|
||||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{mwixnet::MixnetReqCreationParams, InitTxArgs};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn mwixnet_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
// 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(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward);
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward * 2,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let mut slate = api.init_send_tx(m, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
// Send directly to self
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, Some("listener"), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
api.post_tx(m, &slate, false)?; // mines a block
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// Check total in mining account
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
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
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("listener")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, 2 * reward);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Recipient wallet creates a mwixnet request from the last output
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let secp_locked = util::static_secp_instance();
|
||||
let secp = secp_locked.lock();
|
||||
let server_pubkey_str_1 =
|
||||
"97444ae673bb92c713c1a2f7b8882ffbfc1c67401a280a775dce1a8651584332";
|
||||
let server_pubkey_str_2 =
|
||||
"0c9414341f2140ed34a5a12a6479bf5a6404820d001ab81d9d3e8cc38f049b4e";
|
||||
let server_pubkey_str_3 =
|
||||
"b58ece97d60e71bb7e53218400b0d67bfe6a3cb7d3b4a67a44f8fb7c525cbca5";
|
||||
let server_key_1 =
|
||||
SecretKey::from_slice(&secp, &grin_util::from_hex(&server_pubkey_str_1).unwrap())
|
||||
.unwrap();
|
||||
let server_key_2 =
|
||||
SecretKey::from_slice(&secp, &grin_util::from_hex(&server_pubkey_str_2).unwrap())
|
||||
.unwrap();
|
||||
let server_key_3 =
|
||||
SecretKey::from_slice(&secp, &grin_util::from_hex(&server_pubkey_str_3).unwrap())
|
||||
.unwrap();
|
||||
let params = MixnetReqCreationParams {
|
||||
server_keys: vec![server_key_1, server_key_2, server_key_3],
|
||||
fee_per_hop: 50_000_000,
|
||||
};
|
||||
let outputs = api.retrieve_outputs(mask1, false, false, None)?;
|
||||
// get last output
|
||||
let last_output = outputs.1[outputs.1.len() - 1].clone();
|
||||
|
||||
let mwixnet_req = api.create_mwixnet_req(m, ¶ms, &last_output.commit, true)?;
|
||||
|
||||
println!("MWIXNET REQ: {:?}", mwixnet_req);
|
||||
|
||||
// check output we created comsig for is indeed locked
|
||||
let outputs = api.retrieve_outputs(mask1, false, false, None)?;
|
||||
// get last output
|
||||
let last_output = outputs.1[outputs.1.len() - 1].clone();
|
||||
assert!(last_output.output.status == libwallet::OutputStatus::Locked);
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mwixnet_comsig_test() {
|
||||
let test_dir = "test_output/mwixnet";
|
||||
setup(test_dir);
|
||||
if let Err(e) = mwixnet_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+212
-212
@@ -1,212 +1,212 @@
|
||||
// Copyright 2021 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 sender transaction with no change output
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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;
|
||||
|
||||
// Mine into wallet 1
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 4, false);
|
||||
let fee = core::libtx::tx_fee(1, 1, 1);
|
||||
|
||||
// send a single block's worth of transactions with minimal strategy
|
||||
let mut slate = Slate::blank(2, false);
|
||||
let mut stored_excess = None;
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: reward - fee,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
println!("Posted Slate: {:?}", slate);
|
||||
stored_excess = Some(slate.tx.as_ref().unwrap().body.kernels[0].excess);
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// ensure stored excess is correct in both wallets
|
||||
// Wallet 1 calculated the excess with the full slate // Wallet 2 only had the excess provided by
|
||||
// wallet 1
|
||||
|
||||
// Refresh and check transaction log for wallet 1
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
assert!(refreshed);
|
||||
let tx = txs[0].clone();
|
||||
println!("SIMPLE SEND - SENDING WALLET");
|
||||
println!("{:?}", tx);
|
||||
println!();
|
||||
assert!(tx.confirmed);
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Refresh and check transaction log for wallet 2
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
assert!(refreshed);
|
||||
let tx = txs[0].clone();
|
||||
println!("SIMPLE SEND - RECEIVING WALLET");
|
||||
println!("{:?}", tx);
|
||||
println!();
|
||||
assert!(tx.confirmed);
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// ensure invoice TX works as well with no change
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 inititates an invoice transaction, requesting payment
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: reward - fee,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 2 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask1, None, |api, m| {
|
||||
println!("Invoice Posted TX: {}", slate);
|
||||
stored_excess = Some(slate.tx.as_ref().unwrap().body.kernels[0].excess);
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// check wallet 2's version
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
assert!(refreshed);
|
||||
for tx in txs {
|
||||
stored_excess = tx.kernel_excess;
|
||||
println!("Wallet 2: {:?}", tx);
|
||||
println!();
|
||||
assert!(tx.confirmed);
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Refresh and check transaction log for wallet 1
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
assert!(refreshed);
|
||||
for tx in txs {
|
||||
println!("Wallet 1: {:?}", tx);
|
||||
println!();
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
assert!(tx.confirmed);
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_change() {
|
||||
let test_dir = "test_output/no_change";
|
||||
setup(test_dir);
|
||||
if let Err(e) = no_change_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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 sender transaction with no change output
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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;
|
||||
|
||||
// Mine into wallet 1
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 4, false);
|
||||
let fee = core::libtx::tx_fee(1, 1, 1);
|
||||
|
||||
// send a single block's worth of transactions with minimal strategy
|
||||
let mut slate = Slate::blank(2, false);
|
||||
let mut stored_excess = None;
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: reward - fee,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
println!("Posted Slate: {:?}", slate);
|
||||
stored_excess = Some(slate.tx.as_ref().unwrap().body.kernels[0].excess);
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// ensure stored excess is correct in both wallets
|
||||
// Wallet 1 calculated the excess with the full slate // Wallet 2 only had the excess provided by
|
||||
// wallet 1
|
||||
|
||||
// Refresh and check transaction log for wallet 1
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
assert!(refreshed);
|
||||
let tx = txs[0].clone();
|
||||
println!("SIMPLE SEND - SENDING WALLET");
|
||||
println!("{:?}", tx);
|
||||
println!();
|
||||
assert!(tx.confirmed);
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Refresh and check transaction log for wallet 2
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
assert!(refreshed);
|
||||
let tx = txs[0].clone();
|
||||
println!("SIMPLE SEND - RECEIVING WALLET");
|
||||
println!("{:?}", tx);
|
||||
println!();
|
||||
assert!(tx.confirmed);
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// ensure invoice TX works as well with no change
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 inititates an invoice transaction, requesting payment
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: reward - fee,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 2 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask1, None, |api, m| {
|
||||
println!("Invoice Posted TX: {}", slate);
|
||||
stored_excess = Some(slate.tx.as_ref().unwrap().body.kernels[0].excess);
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// check wallet 2's version
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
assert!(refreshed);
|
||||
for tx in txs {
|
||||
stored_excess = tx.kernel_excess;
|
||||
println!("Wallet 2: {:?}", tx);
|
||||
println!();
|
||||
assert!(tx.confirmed);
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Refresh and check transaction log for wallet 1
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
assert!(refreshed);
|
||||
for tx in txs {
|
||||
println!("Wallet 1: {:?}", tx);
|
||||
println!();
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
assert!(tx.confirmed);
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_change() {
|
||||
let test_dir = "test_output/no_change";
|
||||
setup(test_dir);
|
||||
if let Err(e) = no_change_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+172
-172
@@ -1,172 +1,172 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! tests differing accounts in the same wallet
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_util;
|
||||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{InitTxArgs, Slate};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Various tests on accounts within the same wallet
|
||||
fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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
|
||||
|
||||
// Do some mining
|
||||
let bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let mut address = None;
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
address = Some(api.get_slatepack_address(m, 0)?);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
println!("Public address is: {:?}", address);
|
||||
let amount = 60_000_000_000;
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
payment_proof_recipient_address: address.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
|
||||
assert_eq!(
|
||||
slate_i.payment_proof.as_ref().unwrap().receiver_address,
|
||||
address.as_ref().unwrap().pub_key,
|
||||
);
|
||||
println!(
|
||||
"Sender addr: {:?}",
|
||||
slate_i.payment_proof.as_ref().unwrap().sender_address
|
||||
);
|
||||
|
||||
// Check we are creating a tx with kernel features 0
|
||||
// We will check this produces a Plain kernel later.
|
||||
assert_eq!(0, slate.kernel_features);
|
||||
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
|
||||
// Ensure what's stored in TX log for payment proof is correct
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
assert!(txs[0].payment_proof.is_some());
|
||||
let pp = txs[0].clone().payment_proof.unwrap();
|
||||
assert_eq!(
|
||||
pp.receiver_address,
|
||||
slate_i.payment_proof.as_ref().unwrap().receiver_address
|
||||
);
|
||||
assert!(pp.receiver_signature.is_some());
|
||||
assert_eq!(pp.sender_address_path, 0);
|
||||
assert_eq!(pp.sender_signature, None);
|
||||
|
||||
// check we should get an error at this point since proof is not complete
|
||||
let pp = sender_api.retrieve_payment_proof(m, true, None, Some(slate.id));
|
||||
assert!(pp.is_err());
|
||||
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
sender_api.post_tx(m, &slate, true)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// Check payment proof here
|
||||
let mut pp = sender_api.retrieve_payment_proof(m, true, None, Some(slate.id))?;
|
||||
|
||||
println!("Payment proof: {:?}", pp);
|
||||
|
||||
// verify, should be good
|
||||
let res = sender_api.verify_payment_proof(m, &pp)?;
|
||||
assert_eq!(res, (true, false));
|
||||
|
||||
// Modify values, should not be good
|
||||
pp.amount = 20;
|
||||
let res = sender_api.verify_payment_proof(m, &pp);
|
||||
assert!(res.is_err());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payment_proofs() {
|
||||
let test_dir = "test_output/payment_proofs";
|
||||
setup(test_dir);
|
||||
if let Err(e) = payment_proofs_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! tests differing accounts in the same wallet
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_util;
|
||||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{InitTxArgs, Slate};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Various tests on accounts within the same wallet
|
||||
fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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
|
||||
|
||||
// Do some mining
|
||||
let bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let mut address = None;
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
address = Some(api.get_slatepack_address(m, 0)?);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
println!("Public address is: {:?}", address);
|
||||
let amount = 60_000_000_000;
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
payment_proof_recipient_address: address.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
|
||||
assert_eq!(
|
||||
slate_i.payment_proof.as_ref().unwrap().receiver_address,
|
||||
address.as_ref().unwrap().pub_key,
|
||||
);
|
||||
println!(
|
||||
"Sender addr: {:?}",
|
||||
slate_i.payment_proof.as_ref().unwrap().sender_address
|
||||
);
|
||||
|
||||
// Check we are creating a tx with kernel features 0
|
||||
// We will check this produces a Plain kernel later.
|
||||
assert_eq!(0, slate.kernel_features);
|
||||
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
|
||||
// Ensure what's stored in TX log for payment proof is correct
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
assert!(txs[0].payment_proof.is_some());
|
||||
let pp = txs[0].clone().payment_proof.unwrap();
|
||||
assert_eq!(
|
||||
pp.receiver_address,
|
||||
slate_i.payment_proof.as_ref().unwrap().receiver_address
|
||||
);
|
||||
assert!(pp.receiver_signature.is_some());
|
||||
assert_eq!(pp.sender_address_path, 0);
|
||||
assert_eq!(pp.sender_signature, None);
|
||||
|
||||
// check we should get an error at this point since proof is not complete
|
||||
let pp = sender_api.retrieve_payment_proof(m, true, None, Some(slate.id));
|
||||
assert!(pp.is_err());
|
||||
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
sender_api.post_tx(m, &slate, true)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// Check payment proof here
|
||||
let mut pp = sender_api.retrieve_payment_proof(m, true, None, Some(slate.id))?;
|
||||
|
||||
println!("Payment proof: {:?}", pp);
|
||||
|
||||
// verify, should be good
|
||||
let res = sender_api.verify_payment_proof(m, &pp)?;
|
||||
assert_eq!(res, (true, false));
|
||||
|
||||
// Modify values, should not be good
|
||||
pp.amount = 20;
|
||||
let res = sender_api.verify_payment_proof(m, &pp);
|
||||
assert!(res.is_err());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payment_proofs() {
|
||||
let test_dir = "test_output/payment_proofs";
|
||||
setup(test_dir);
|
||||
if let Err(e) = payment_proofs_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+268
-268
@@ -1,268 +1,268 @@
|
||||
// Copyright 2021 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 repost command
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
use grin_core as core;
|
||||
|
||||
use self::libwallet::{InitTxArgs, Slate};
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use impls::{PathToSlate, SlateGetter as _, SlatePutter as _};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.create_account_path(m, "account1")?;
|
||||
api.create_account_path(m, "account2")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let send_file = format!("{}/part_tx_1.tx", test_dir);
|
||||
let receive_file = format!("{}/part_tx_2.tx", test_dir);
|
||||
|
||||
let mut slate = Slate::blank(2, false);
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward);
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward * 2,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// wallet 1 receives file to different account, completes
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("listener")?;
|
||||
}
|
||||
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 1 receives file to different account, completes
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
|
||||
// wallet 1 finalize
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
slate = PathToSlate((&receive_file).into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Now repost from cached
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
println!("TXS[0]: {:?}", txs[0]);
|
||||
let stored_tx = api.get_stored_tx(m, None, Some(&txs[0].tx_slate_id.unwrap()))?;
|
||||
println!("Stored tx: {:?}", stored_tx);
|
||||
api.post_tx(m, &slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// update/test contents of both accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward - reward * 2);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("listener")?;
|
||||
}
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(wallet2_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet2_info.total, 2 * reward);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// as above, but syncronously
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
{
|
||||
wallet_inst!(wallet2, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
|
||||
let mut slate = Slate::blank(2, false);
|
||||
let amount = 60_000_000_000;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: reward * 2,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// Now repost from cached
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
let stored_tx_slate = api.get_stored_tx(m, Some(txs[0].id), None)?.unwrap();
|
||||
api.post_tx(m, &stored_tx_slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
//
|
||||
// update/test contents of both accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward - reward * 4);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(wallet2_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet2_info.total, 2 * amount);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_file_repost() {
|
||||
let test_dir = "test_output/file_repost";
|
||||
setup(test_dir);
|
||||
if let Err(e) = file_repost_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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 repost command
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
use grin_core as core;
|
||||
|
||||
use self::libwallet::{InitTxArgs, Slate};
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use impls::{PathToSlate, SlateGetter as _, SlatePutter as _};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.create_account_path(m, "account1")?;
|
||||
api.create_account_path(m, "account2")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let send_file = format!("{}/part_tx_1.tx", test_dir);
|
||||
let receive_file = format!("{}/part_tx_2.tx", test_dir);
|
||||
|
||||
let mut slate = Slate::blank(2, false);
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward);
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward * 2,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// wallet 1 receives file to different account, completes
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("listener")?;
|
||||
}
|
||||
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 1 receives file to different account, completes
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
|
||||
// wallet 1 finalize
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
slate = PathToSlate((&receive_file).into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Now repost from cached
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
println!("TXS[0]: {:?}", txs[0]);
|
||||
let stored_tx = api.get_stored_tx(m, None, Some(&txs[0].tx_slate_id.unwrap()))?;
|
||||
println!("Stored tx: {:?}", stored_tx);
|
||||
api.post_tx(m, &slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// update/test contents of both accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward - reward * 2);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("listener")?;
|
||||
}
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(wallet2_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet2_info.total, 2 * reward);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// as above, but syncronously
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
{
|
||||
wallet_inst!(wallet2, w);
|
||||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
|
||||
let mut slate = Slate::blank(2, false);
|
||||
let amount = 60_000_000_000;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: reward * 2,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// Now repost from cached
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
let stored_tx_slate = api.get_stored_tx(m, Some(txs[0].id), None)?.unwrap();
|
||||
api.post_tx(m, &stored_tx_slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
//
|
||||
// update/test contents of both accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward - reward * 4);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet2_refreshed);
|
||||
assert_eq!(wallet2_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet2_info.total, 2 * amount);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_file_repost() {
|
||||
let test_dir = "test_output/file_repost";
|
||||
setup(test_dir);
|
||||
if let Err(e) = file_repost_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+379
-379
@@ -1,379 +1,379 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
use grin_chain as chain;
|
||||
use grin_core as core;
|
||||
use grin_core::core::hash::Hashed;
|
||||
use grin_core::core::Transaction;
|
||||
use grin_core::global;
|
||||
use grin_keychain::ExtKeychain;
|
||||
use grin_util::secp::key::SecretKey;
|
||||
use grin_util::Mutex;
|
||||
use grin_wallet_controller::controller::owner_single_use as owner;
|
||||
use grin_wallet_impls::test_framework::*;
|
||||
use grin_wallet_impls::{DefaultLCProvider, PathToSlate, SlatePutter};
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use grin_wallet_libwallet::api_impl::types::InitTxArgs;
|
||||
use grin_wallet_libwallet::WalletInst;
|
||||
use log::error;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
type Wallet = Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>;
|
||||
|
||||
fn revert(
|
||||
test_dir: &'static str,
|
||||
) -> Result<
|
||||
(
|
||||
Arc<chain::Chain>,
|
||||
Arc<AtomicBool>,
|
||||
u64,
|
||||
u64,
|
||||
Transaction,
|
||||
Wallet,
|
||||
Option<SecretKey>,
|
||||
Wallet,
|
||||
Option<SecretKey>,
|
||||
),
|
||||
libwallet::Error,
|
||||
> {
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let test_dir2 = format!("{}/chain2", test_dir);
|
||||
let wallet_proxy2 = create_wallet_proxy(&test_dir2);
|
||||
let chain2 = wallet_proxy2.chain.clone();
|
||||
let stopper2 = wallet_proxy2.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask1 = mask1_i.as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = mask2_i.as_ref();
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
owner(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "a")?;
|
||||
api.set_active_account(m, "a")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.create_account_path(m, "b")?;
|
||||
api.set_active_account(m, "b")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let reward = core::consensus::REWARD;
|
||||
let cm = global::coinbase_maturity() as u64;
|
||||
let sent = reward * 2;
|
||||
|
||||
// Mine some blocks
|
||||
let bh = 10u64;
|
||||
award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false)?;
|
||||
|
||||
// Sanity check contents
|
||||
owner(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, bh * reward);
|
||||
assert_eq!(info.amount_currently_spendable, (bh - cm) * reward);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
let (c, _) = libwallet::TxLogEntry::sum_confirmed(&txs);
|
||||
assert_eq!(info.total, c);
|
||||
assert_eq!(txs.len(), bh as usize);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, 0);
|
||||
assert_eq!(info.amount_currently_spendable, 0);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 0);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Send some funds
|
||||
let mut tx = None;
|
||||
owner(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: sent,
|
||||
minimum_confirmations: cm,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
// output tx file
|
||||
let send_file = format!("{}/part_tx_1.tx", test_dir);
|
||||
PathToSlate(send_file.into()).put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
let slate = client1.send_tx_slate_direct("wallet2", &slate)?;
|
||||
let slate = api.finalize_tx(m, &slate)?;
|
||||
tx = slate.tx;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
let tx = tx.expect("tx from slate");
|
||||
|
||||
// Check funds have been received
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, 0);
|
||||
assert_eq!(info.amount_currently_spendable, 0);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
assert_eq!(tx.tx_type, libwallet::TxLogEntryType::TxReceived);
|
||||
assert!(!tx.confirmed);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Update parallel chain
|
||||
assert_eq!(chain2.head_header().unwrap().height, 0);
|
||||
for i in 0..bh {
|
||||
let hash = chain.get_header_by_height(i + 1).unwrap().hash();
|
||||
let block = chain.get_block(&hash).unwrap();
|
||||
process_block(&chain2, block);
|
||||
}
|
||||
assert_eq!(chain2.head_header().unwrap().height, bh);
|
||||
|
||||
// Build 2 blocks at same height: 1 with the tx, 1 without
|
||||
let head = chain.head_header().unwrap();
|
||||
let block_with =
|
||||
create_block_for_wallet(&chain, head.clone(), &[tx.clone()], wallet1.clone(), mask1)?;
|
||||
let block_without = create_block_for_wallet(&chain, head, &[], wallet1.clone(), mask1)?;
|
||||
|
||||
// Add block with tx to the chain
|
||||
process_block(&chain, block_with.clone());
|
||||
assert_eq!(chain.head_header().unwrap(), block_with.header);
|
||||
|
||||
// Add block without tx to the parallel chain
|
||||
process_block(&chain2, block_without.clone());
|
||||
assert_eq!(chain2.head_header().unwrap(), block_without.header);
|
||||
|
||||
let bh = bh + 1;
|
||||
|
||||
// Check funds have been confirmed
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, sent);
|
||||
assert_eq!(info.amount_currently_spendable, sent);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
assert_eq!(tx.tx_type, libwallet::TxLogEntryType::TxReceived);
|
||||
assert!(tx.confirmed);
|
||||
assert!(tx.kernel_excess.is_some());
|
||||
assert!(tx.reverted_after.is_none());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Attach more blocks to the parallel chain, making it the longest one
|
||||
award_block_to_wallet(&chain2, &[], wallet1.clone(), mask1)?;
|
||||
assert_eq!(chain2.head_header().unwrap().height, bh + 1);
|
||||
let new_head = chain2
|
||||
.get_block(&chain2.head_header().unwrap().hash())
|
||||
.unwrap();
|
||||
|
||||
// Input blocks from parallel chain to original chain, updating it as well
|
||||
// and effectively reverting the transaction
|
||||
process_block(&chain, block_without.clone()); // This shouldn't update the head
|
||||
assert_eq!(chain.head_header().unwrap(), block_with.header);
|
||||
process_block(&chain, new_head.clone()); // But this should!
|
||||
assert_eq!(chain.head_header().unwrap(), new_head.header);
|
||||
|
||||
let bh = bh + 1;
|
||||
|
||||
// Check funds have been reverted
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.scan(m, None, false)?;
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, 0);
|
||||
assert_eq!(info.amount_currently_spendable, 0);
|
||||
assert_eq!(info.amount_reverted, sent);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
assert_eq!(tx.tx_type, libwallet::TxLogEntryType::TxReverted);
|
||||
assert!(!tx.confirmed);
|
||||
assert!(tx.reverted_after.is_some());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
stopper2.store(false, Ordering::Relaxed);
|
||||
Ok((
|
||||
chain, stopper, sent, bh, tx, wallet1, mask1_i, wallet2, mask2_i,
|
||||
))
|
||||
}
|
||||
|
||||
fn revert_reconfirm_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
let (chain, stopper, sent, bh, tx, wallet1, mask1_i, wallet2, mask2_i) = revert(test_dir)?;
|
||||
let mask1 = mask1_i.as_ref();
|
||||
let mask2 = mask2_i.as_ref();
|
||||
|
||||
// Include the tx into the chain again, the tx should no longer be reverted
|
||||
award_block_to_wallet(&chain, &[tx], wallet1.clone(), mask1)?;
|
||||
|
||||
let bh = bh + 1;
|
||||
|
||||
// Check funds have been confirmed again
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, sent);
|
||||
assert_eq!(info.amount_currently_spendable, sent);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
assert_eq!(tx.tx_type, libwallet::TxLogEntryType::TxReceived);
|
||||
assert!(tx.confirmed);
|
||||
assert!(tx.reverted_after.is_none());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn revert_cancel_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
let (_, stopper, sent, bh, _, _, _, wallet2, mask2_i) = revert(test_dir)?;
|
||||
let mask2 = mask2_i.as_ref();
|
||||
|
||||
// Cancelling tx
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Sanity check
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, 0);
|
||||
assert_eq!(info.amount_currently_spendable, 0);
|
||||
assert_eq!(info.amount_reverted, sent);
|
||||
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
|
||||
// Cancel
|
||||
api.cancel_tx(m, Some(tx.id), None)?;
|
||||
|
||||
// Check updated summary info
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, 0);
|
||||
assert_eq!(info.amount_currently_spendable, 0);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
|
||||
// Check updated tx log
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
assert_eq!(tx.tx_type, libwallet::TxLogEntryType::TxReceivedCancelled);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tx_revert_reconfirm() {
|
||||
let test_dir = "test_output/revert_tx";
|
||||
setup(test_dir);
|
||||
if let Err(e) = revert_reconfirm_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tx_revert_cancel() {
|
||||
let test_dir = "test_output/revert_tx_cancel";
|
||||
setup(test_dir);
|
||||
if let Err(e) = revert_cancel_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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.
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
use grin_chain as chain;
|
||||
use grin_core as core;
|
||||
use grin_core::core::hash::Hashed;
|
||||
use grin_core::core::Transaction;
|
||||
use grin_core::global;
|
||||
use grin_keychain::ExtKeychain;
|
||||
use grin_util::secp::key::SecretKey;
|
||||
use grin_util::Mutex;
|
||||
use grin_wallet_controller::controller::owner_single_use as owner;
|
||||
use grin_wallet_impls::test_framework::*;
|
||||
use grin_wallet_impls::{DefaultLCProvider, PathToSlate, SlatePutter};
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use grin_wallet_libwallet::api_impl::types::InitTxArgs;
|
||||
use grin_wallet_libwallet::WalletInst;
|
||||
use log::error;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
type Wallet = Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>;
|
||||
|
||||
fn revert(
|
||||
test_dir: &'static str,
|
||||
) -> Result<
|
||||
(
|
||||
Arc<chain::Chain>,
|
||||
Arc<AtomicBool>,
|
||||
u64,
|
||||
u64,
|
||||
Transaction,
|
||||
Wallet,
|
||||
Option<SecretKey>,
|
||||
Wallet,
|
||||
Option<SecretKey>,
|
||||
),
|
||||
libwallet::Error,
|
||||
> {
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let test_dir2 = format!("{}/chain2", test_dir);
|
||||
let wallet_proxy2 = create_wallet_proxy(&test_dir2);
|
||||
let chain2 = wallet_proxy2.chain.clone();
|
||||
let stopper2 = wallet_proxy2.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask1 = mask1_i.as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = mask2_i.as_ref();
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
owner(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "a")?;
|
||||
api.set_active_account(m, "a")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.create_account_path(m, "b")?;
|
||||
api.set_active_account(m, "b")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let reward = core::consensus::REWARD;
|
||||
let cm = global::coinbase_maturity() as u64;
|
||||
let sent = reward * 2;
|
||||
|
||||
// Mine some blocks
|
||||
let bh = 10u64;
|
||||
award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false)?;
|
||||
|
||||
// Sanity check contents
|
||||
owner(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, bh * reward);
|
||||
assert_eq!(info.amount_currently_spendable, (bh - cm) * reward);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
let (c, _) = libwallet::TxLogEntry::sum_confirmed(&txs);
|
||||
assert_eq!(info.total, c);
|
||||
assert_eq!(txs.len(), bh as usize);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, 0);
|
||||
assert_eq!(info.amount_currently_spendable, 0);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 0);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Send some funds
|
||||
let mut tx = None;
|
||||
owner(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: sent,
|
||||
minimum_confirmations: cm,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
// output tx file
|
||||
let send_file = format!("{}/part_tx_1.tx", test_dir);
|
||||
PathToSlate(send_file.into()).put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
let slate = client1.send_tx_slate_direct("wallet2", &slate)?;
|
||||
let slate = api.finalize_tx(m, &slate)?;
|
||||
tx = slate.tx;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
let tx = tx.expect("tx from slate");
|
||||
|
||||
// Check funds have been received
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, 0);
|
||||
assert_eq!(info.amount_currently_spendable, 0);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
assert_eq!(tx.tx_type, libwallet::TxLogEntryType::TxReceived);
|
||||
assert!(!tx.confirmed);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Update parallel chain
|
||||
assert_eq!(chain2.head_header().unwrap().height, 0);
|
||||
for i in 0..bh {
|
||||
let hash = chain.get_header_by_height(i + 1).unwrap().hash();
|
||||
let block = chain.get_block(&hash).unwrap();
|
||||
process_block(&chain2, block);
|
||||
}
|
||||
assert_eq!(chain2.head_header().unwrap().height, bh);
|
||||
|
||||
// Build 2 blocks at same height: 1 with the tx, 1 without
|
||||
let head = chain.head_header().unwrap();
|
||||
let block_with =
|
||||
create_block_for_wallet(&chain, head.clone(), &[tx.clone()], wallet1.clone(), mask1)?;
|
||||
let block_without = create_block_for_wallet(&chain, head, &[], wallet1.clone(), mask1)?;
|
||||
|
||||
// Add block with tx to the chain
|
||||
process_block(&chain, block_with.clone());
|
||||
assert_eq!(chain.head_header().unwrap(), block_with.header);
|
||||
|
||||
// Add block without tx to the parallel chain
|
||||
process_block(&chain2, block_without.clone());
|
||||
assert_eq!(chain2.head_header().unwrap(), block_without.header);
|
||||
|
||||
let bh = bh + 1;
|
||||
|
||||
// Check funds have been confirmed
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, sent);
|
||||
assert_eq!(info.amount_currently_spendable, sent);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
assert_eq!(tx.tx_type, libwallet::TxLogEntryType::TxReceived);
|
||||
assert!(tx.confirmed);
|
||||
assert!(tx.kernel_excess.is_some());
|
||||
assert!(tx.reverted_after.is_none());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Attach more blocks to the parallel chain, making it the longest one
|
||||
award_block_to_wallet(&chain2, &[], wallet1.clone(), mask1)?;
|
||||
assert_eq!(chain2.head_header().unwrap().height, bh + 1);
|
||||
let new_head = chain2
|
||||
.get_block(&chain2.head_header().unwrap().hash())
|
||||
.unwrap();
|
||||
|
||||
// Input blocks from parallel chain to original chain, updating it as well
|
||||
// and effectively reverting the transaction
|
||||
process_block(&chain, block_without.clone()); // This shouldn't update the head
|
||||
assert_eq!(chain.head_header().unwrap(), block_with.header);
|
||||
process_block(&chain, new_head.clone()); // But this should!
|
||||
assert_eq!(chain.head_header().unwrap(), new_head.header);
|
||||
|
||||
let bh = bh + 1;
|
||||
|
||||
// Check funds have been reverted
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.scan(m, None, false)?;
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, 0);
|
||||
assert_eq!(info.amount_currently_spendable, 0);
|
||||
assert_eq!(info.amount_reverted, sent);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
assert_eq!(tx.tx_type, libwallet::TxLogEntryType::TxReverted);
|
||||
assert!(!tx.confirmed);
|
||||
assert!(tx.reverted_after.is_some());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
stopper2.store(false, Ordering::Relaxed);
|
||||
Ok((
|
||||
chain, stopper, sent, bh, tx, wallet1, mask1_i, wallet2, mask2_i,
|
||||
))
|
||||
}
|
||||
|
||||
fn revert_reconfirm_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
let (chain, stopper, sent, bh, tx, wallet1, mask1_i, wallet2, mask2_i) = revert(test_dir)?;
|
||||
let mask1 = mask1_i.as_ref();
|
||||
let mask2 = mask2_i.as_ref();
|
||||
|
||||
// Include the tx into the chain again, the tx should no longer be reverted
|
||||
award_block_to_wallet(&chain, &[tx], wallet1.clone(), mask1)?;
|
||||
|
||||
let bh = bh + 1;
|
||||
|
||||
// Check funds have been confirmed again
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, sent);
|
||||
assert_eq!(info.amount_currently_spendable, sent);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
// check tx log as well
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
assert_eq!(tx.tx_type, libwallet::TxLogEntryType::TxReceived);
|
||||
assert!(tx.confirmed);
|
||||
assert!(tx.reverted_after.is_none());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn revert_cancel_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
let (_, stopper, sent, bh, _, _, _, wallet2, mask2_i) = revert(test_dir)?;
|
||||
let mask2 = mask2_i.as_ref();
|
||||
|
||||
// Cancelling tx
|
||||
owner(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Sanity check
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, 0);
|
||||
assert_eq!(info.amount_currently_spendable, 0);
|
||||
assert_eq!(info.amount_reverted, sent);
|
||||
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
|
||||
// Cancel
|
||||
api.cancel_tx(m, Some(tx.id), None)?;
|
||||
|
||||
// Check updated summary info
|
||||
let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(info.last_confirmed_height, bh);
|
||||
assert_eq!(info.total, 0);
|
||||
assert_eq!(info.amount_currently_spendable, 0);
|
||||
assert_eq!(info.amount_reverted, 0);
|
||||
|
||||
// Check updated tx log
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, None, None)?;
|
||||
assert_eq!(txs.len(), 1);
|
||||
let tx = &txs[0];
|
||||
assert_eq!(tx.tx_type, libwallet::TxLogEntryType::TxReceivedCancelled);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tx_revert_reconfirm() {
|
||||
let test_dir = "test_output/revert_tx";
|
||||
setup(test_dir);
|
||||
if let Err(e) = revert_reconfirm_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tx_revert_cancel() {
|
||||
let test_dir = "test_output/revert_tx_cancel";
|
||||
setup(test_dir);
|
||||
if let Err(e) = revert_cancel_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+148
-148
@@ -1,148 +1,148 @@
|
||||
// Copyright 2021 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
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::InitTxArgs;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
// 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(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward);
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward * 2,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let mut slate = api.init_send_tx(m, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
// Send directly to self
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, Some("listener"), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
api.post_tx(m, &slate, false)?; // mines a block
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// Check total in mining account
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
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
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("listener")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, 2 * reward);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_self_send() {
|
||||
let test_dir = "test_output/self_send";
|
||||
setup(test_dir);
|
||||
if let Err(e) = self_send_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
|
||||
use grin_core as core;
|
||||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::InitTxArgs;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
// 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(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, bh * reward);
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward * 2,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
let mut slate = api.init_send_tx(m, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
// Send directly to self
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, Some("listener"), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
api.post_tx(m, &slate, false)?; // mines a block
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
bh += 3;
|
||||
|
||||
// Check total in mining account
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
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
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("listener")?;
|
||||
}
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
assert!(wallet1_refreshed);
|
||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||
assert_eq!(wallet1_info.total, 2 * reward);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_self_send() {
|
||||
let test_dir = "test_output/self_send";
|
||||
setup(test_dir);
|
||||
if let Err(e) = self_send_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+587
-587
File diff suppressed because it is too large
Load Diff
@@ -442,7 +442,7 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// few values to keep things shorter
|
||||
let reward = core::consensus::REWARD;
|
||||
let cm = global::coinbase_maturity(); // assume all testing precedes soft fork height
|
||||
// mine a few blocks
|
||||
// mine a few blocks
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false);
|
||||
|
||||
let amount = 30_000_000_000;
|
||||
|
||||
+185
-185
@@ -1,185 +1,185 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! tests ttl_cutoff blocks
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_util;
|
||||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{InitTxArgs, Slate, TxLogEntryType};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Test cutoff block times
|
||||
fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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
|
||||
|
||||
// Do some mining
|
||||
let bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let amount = 60_000_000_000;
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
ttl_blocks: Some(2),
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
let tx = txs[0].clone();
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(12));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Now mine past the block, and check again. Transaction should be gone.
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
let tx = txs[0].clone();
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(12));
|
||||
assert!(tx.tx_type == TxLogEntryType::TxSentCancelled);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Should also be gone in wallet 2, and output gone
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |sender_api, m| {
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
let tx = txs[0].clone();
|
||||
let outputs = sender_api.retrieve_outputs(m, false, true, None)?.1;
|
||||
assert_eq!(outputs.len(), 0);
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(12));
|
||||
assert!(tx.tx_type == TxLogEntryType::TxReceivedCancelled);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// try again, except try and send off the transaction for completion beyond the expiry
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
ttl_blocks: Some(2),
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
sender_api.tx_lock_outputs(m, &slate_i)?;
|
||||
slate = slate_i;
|
||||
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
let tx = txs[0].clone();
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(14));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Mine past the ttl block and try to send
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false);
|
||||
|
||||
// Wallet 2 will need to have updated past the TTL
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |sender_api, m| {
|
||||
let (_, _) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// And when wallet 1 sends, should be rejected
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |_sender_api, _m| {
|
||||
let res = client1.send_tx_slate_direct("wallet2", &slate);
|
||||
println!("Send after TTL result is: {:?}", res);
|
||||
assert!(res.is_err());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ttl_cutoff() {
|
||||
let test_dir = "test_output/ttl_cutoff";
|
||||
setup(test_dir);
|
||||
if let Err(e) = ttl_cutoff_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! tests ttl_cutoff blocks
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_util;
|
||||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{InitTxArgs, Slate, TxLogEntryType};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Test cutoff block times
|
||||
fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// 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
|
||||
|
||||
// Do some mining
|
||||
let bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let amount = 60_000_000_000;
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
ttl_blocks: Some(2),
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
let tx = txs[0].clone();
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(12));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Now mine past the block, and check again. Transaction should be gone.
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
let tx = txs[0].clone();
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(12));
|
||||
assert!(tx.tx_type == TxLogEntryType::TxSentCancelled);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Should also be gone in wallet 2, and output gone
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |sender_api, m| {
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
let tx = txs[0].clone();
|
||||
let outputs = sender_api.retrieve_outputs(m, false, true, None)?.1;
|
||||
assert_eq!(outputs.len(), 0);
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(12));
|
||||
assert!(tx.tx_type == TxLogEntryType::TxReceivedCancelled);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// try again, except try and send off the transaction for completion beyond the expiry
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
ttl_blocks: Some(2),
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
sender_api.tx_lock_outputs(m, &slate_i)?;
|
||||
slate = slate_i;
|
||||
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
let tx = txs[0].clone();
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(14));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Mine past the ttl block and try to send
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false);
|
||||
|
||||
// Wallet 2 will need to have updated past the TTL
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |sender_api, m| {
|
||||
let (_, _) = sender_api.retrieve_txs(m, true, None, Some(slate.id), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// And when wallet 1 sends, should be rejected
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |_sender_api, _m| {
|
||||
let res = client1.send_tx_slate_direct("wallet2", &slate);
|
||||
println!("Send after TTL result is: {:?}", res);
|
||||
assert!(res.is_err());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ttl_cutoff() {
|
||||
let test_dir = "test_output/ttl_cutoff";
|
||||
setup(test_dir);
|
||||
if let Err(e) = ttl_cutoff_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+360
-360
@@ -1,360 +1,360 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! tests of advanced TX filtering
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use libwallet::{RetrieveTxQueryArgs, RetrieveTxQuerySortField};
|
||||
|
||||
use self::libwallet::{InitTxArgs, Slate};
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use std::sync::{atomic::Ordering, Arc};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use util::secp::key::SecretKey;
|
||||
use util::Mutex;
|
||||
|
||||
use self::keychain::ExtKeychain;
|
||||
use self::libwallet::WalletInst;
|
||||
use impls::DefaultLCProvider;
|
||||
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
fn test_wallet_tx_filtering(
|
||||
wallet: Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
mask: Option<&SecretKey>,
|
||||
) -> Result<(), libwallet::Error> {
|
||||
wallet::controller::owner_single_use(Some(wallet.clone()), mask, None, |api, _m| {
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.min_id = Some(5);
|
||||
|
||||
// Min ID
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results[0].id, 5);
|
||||
assert_eq!(tx_results[tx_results.len() - 1].id, 33);
|
||||
|
||||
// Max ID
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.min_id = Some(5);
|
||||
tx_query_args.max_id = Some(20);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results[0].id, 5);
|
||||
assert_eq!(tx_results[tx_results.len() - 1].id, 20);
|
||||
|
||||
// Exclude 1 cancelled
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.exclude_cancelled = Some(true);
|
||||
tx_query_args.min_id = Some(5);
|
||||
tx_query_args.max_id = Some(50);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 28);
|
||||
|
||||
// Exclude 1 cancelled, show confirmed only
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.exclude_cancelled = Some(true);
|
||||
tx_query_args.include_confirmed_only = Some(true);
|
||||
tx_query_args.min_id = Some(5);
|
||||
tx_query_args.max_id = Some(50);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 14);
|
||||
|
||||
// show outstanding only (including cancelled)
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.exclude_cancelled = Some(false);
|
||||
tx_query_args.include_outstanding_only = Some(true);
|
||||
tx_query_args.min_id = Some(5);
|
||||
tx_query_args.max_id = Some(50);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 15);
|
||||
|
||||
// outstanding only and confirmed only should give empty set
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.exclude_cancelled = Some(false);
|
||||
tx_query_args.include_outstanding_only = Some(true);
|
||||
tx_query_args.include_confirmed_only = Some(true);
|
||||
tx_query_args.min_id = Some(5);
|
||||
tx_query_args.max_id = Some(50);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 0);
|
||||
|
||||
// include sent only
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.include_sent_only = Some(true);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 15);
|
||||
|
||||
// include received only (none in this set)
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.include_received_only = Some(true);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 0);
|
||||
|
||||
// include reverted only (none in this set)
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.include_reverted_only = Some(true);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 0);
|
||||
|
||||
// include coinbase only
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.include_coinbase_only = Some(true);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 19);
|
||||
|
||||
// Amounts
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.min_amount = Some(60_000_000_000 - 59_963_300_000);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 27);
|
||||
|
||||
// amount, should see as above with coinbases excluded
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.min_amount = Some(60_000_000_000 - 59_963_300_000);
|
||||
tx_query_args.max_amount = Some(60_000_000_000 - 1);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 8);
|
||||
|
||||
// Amount - should only see coinbase (incoming)
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.min_amount = Some(60_000_000_000);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 19);
|
||||
|
||||
// sort order
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.sort_order = Some(libwallet::RetrieveTxQuerySortOrder::Desc);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
|
||||
assert_eq!(tx_results[0].id, 33);
|
||||
assert_eq!(tx_results[tx_results.len() - 1].id, 0);
|
||||
|
||||
// change sort field to amount desc, should have coinbases first
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.sort_order = Some(libwallet::RetrieveTxQuerySortOrder::Desc);
|
||||
tx_query_args.sort_field = Some(RetrieveTxQuerySortField::TotalAmount);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results[0].amount_credited, 60_000_000_000);
|
||||
|
||||
/*for entry in tx_results.iter() {
|
||||
println!("{:?}", entry);
|
||||
}*/
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Builds a wallet + chain with a few transactions, and return wallet for further testing
|
||||
fn build_chain_for_tx_filtering(
|
||||
test_dir: &'static str,
|
||||
block_height: usize,
|
||||
) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
debug!("Mask1: {:?}", mask1);
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
debug!("Mask2: {:?}", mask2);
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Stop the scanning updater threads because it extends the time needed to build the chain
|
||||
// exponentially
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, _m| {
|
||||
api.stop_updater()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, _m| {
|
||||
api.stop_updater()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// few values to keep things shorter
|
||||
let reward = core::consensus::REWARD;
|
||||
|
||||
// Start off with a few blocks
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
|
||||
for i in 0..block_height {
|
||||
let mut wallet_1_has_funds = false;
|
||||
|
||||
// Check wallet 1 contents
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
debug!(
|
||||
"Wallet 1 spendable - {}",
|
||||
wallet1_info.amount_currently_spendable
|
||||
);
|
||||
if wallet1_info.amount_currently_spendable > reward {
|
||||
wallet_1_has_funds = true;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if !wallet_1_has_funds {
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 1, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// send a random tx
|
||||
let num_txs = 1;
|
||||
for _ in 0..num_txs {
|
||||
let amount: u64 = i as u64 * 1_000_000;
|
||||
let mut slate = Slate::blank(1, false);
|
||||
debug!("Creating TX for {}", amount);
|
||||
wallet::controller::owner_single_use(
|
||||
Some(wallet1.clone()),
|
||||
mask1,
|
||||
None,
|
||||
|sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 1,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel a tx for filtering testing
|
||||
let amount: u64 = 1_000_000;
|
||||
let mut slate = Slate::blank(1, false);
|
||||
debug!("Creating TX for {}", amount);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 1,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
sender_api.cancel_tx(m, Some(33), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Perform actual testing
|
||||
test_wallet_tx_filtering(wallet1, mask1)?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_tx_filtering() {
|
||||
let test_dir = "test_output/advanced_tx_filtering";
|
||||
clean_output_dir(test_dir);
|
||||
setup(test_dir);
|
||||
if let Err(e) = build_chain_for_tx_filtering(test_dir, 30) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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.
|
||||
|
||||
//! tests of advanced TX filtering
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
use libwallet::{RetrieveTxQueryArgs, RetrieveTxQuerySortField};
|
||||
|
||||
use self::libwallet::{InitTxArgs, Slate};
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use std::sync::{atomic::Ordering, Arc};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use util::secp::key::SecretKey;
|
||||
use util::Mutex;
|
||||
|
||||
use self::keychain::ExtKeychain;
|
||||
use self::libwallet::WalletInst;
|
||||
use impls::DefaultLCProvider;
|
||||
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
fn test_wallet_tx_filtering(
|
||||
wallet: Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
mask: Option<&SecretKey>,
|
||||
) -> Result<(), libwallet::Error> {
|
||||
wallet::controller::owner_single_use(Some(wallet.clone()), mask, None, |api, _m| {
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.min_id = Some(5);
|
||||
|
||||
// Min ID
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results[0].id, 5);
|
||||
assert_eq!(tx_results[tx_results.len() - 1].id, 33);
|
||||
|
||||
// Max ID
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.min_id = Some(5);
|
||||
tx_query_args.max_id = Some(20);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results[0].id, 5);
|
||||
assert_eq!(tx_results[tx_results.len() - 1].id, 20);
|
||||
|
||||
// Exclude 1 cancelled
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.exclude_cancelled = Some(true);
|
||||
tx_query_args.min_id = Some(5);
|
||||
tx_query_args.max_id = Some(50);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 28);
|
||||
|
||||
// Exclude 1 cancelled, show confirmed only
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.exclude_cancelled = Some(true);
|
||||
tx_query_args.include_confirmed_only = Some(true);
|
||||
tx_query_args.min_id = Some(5);
|
||||
tx_query_args.max_id = Some(50);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 14);
|
||||
|
||||
// show outstanding only (including cancelled)
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.exclude_cancelled = Some(false);
|
||||
tx_query_args.include_outstanding_only = Some(true);
|
||||
tx_query_args.min_id = Some(5);
|
||||
tx_query_args.max_id = Some(50);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 15);
|
||||
|
||||
// outstanding only and confirmed only should give empty set
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.exclude_cancelled = Some(false);
|
||||
tx_query_args.include_outstanding_only = Some(true);
|
||||
tx_query_args.include_confirmed_only = Some(true);
|
||||
tx_query_args.min_id = Some(5);
|
||||
tx_query_args.max_id = Some(50);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 0);
|
||||
|
||||
// include sent only
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.include_sent_only = Some(true);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 15);
|
||||
|
||||
// include received only (none in this set)
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.include_received_only = Some(true);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 0);
|
||||
|
||||
// include reverted only (none in this set)
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.include_reverted_only = Some(true);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 0);
|
||||
|
||||
// include coinbase only
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.include_coinbase_only = Some(true);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 19);
|
||||
|
||||
// Amounts
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.min_amount = Some(60_000_000_000 - 59_963_300_000);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 27);
|
||||
|
||||
// amount, should see as above with coinbases excluded
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.min_amount = Some(60_000_000_000 - 59_963_300_000);
|
||||
tx_query_args.max_amount = Some(60_000_000_000 - 1);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 8);
|
||||
|
||||
// Amount - should only see coinbase (incoming)
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.min_amount = Some(60_000_000_000);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results.len(), 19);
|
||||
|
||||
// sort order
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.sort_order = Some(libwallet::RetrieveTxQuerySortOrder::Desc);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
|
||||
assert_eq!(tx_results[0].id, 33);
|
||||
assert_eq!(tx_results[tx_results.len() - 1].id, 0);
|
||||
|
||||
// change sort field to amount desc, should have coinbases first
|
||||
let mut tx_query_args = RetrieveTxQueryArgs::default();
|
||||
tx_query_args.sort_order = Some(libwallet::RetrieveTxQuerySortOrder::Desc);
|
||||
tx_query_args.sort_field = Some(RetrieveTxQuerySortField::TotalAmount);
|
||||
let tx_results = api
|
||||
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
|
||||
.1;
|
||||
assert_eq!(tx_results[0].amount_credited, 60_000_000_000);
|
||||
|
||||
/*for entry in tx_results.iter() {
|
||||
println!("{:?}", entry);
|
||||
}*/
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Builds a wallet + chain with a few transactions, and return wallet for further testing
|
||||
fn build_chain_for_tx_filtering(
|
||||
test_dir: &'static str,
|
||||
block_height: usize,
|
||||
) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
true
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
debug!("Mask1: {:?}", mask1);
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
debug!("Mask2: {:?}", mask2);
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Stop the scanning updater threads because it extends the time needed to build the chain
|
||||
// exponentially
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, _m| {
|
||||
api.stop_updater()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, _m| {
|
||||
api.stop_updater()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// few values to keep things shorter
|
||||
let reward = core::consensus::REWARD;
|
||||
|
||||
// Start off with a few blocks
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
|
||||
for i in 0..block_height {
|
||||
let mut wallet_1_has_funds = false;
|
||||
|
||||
// Check wallet 1 contents
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
|
||||
debug!(
|
||||
"Wallet 1 spendable - {}",
|
||||
wallet1_info.amount_currently_spendable
|
||||
);
|
||||
if wallet1_info.amount_currently_spendable > reward {
|
||||
wallet_1_has_funds = true;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if !wallet_1_has_funds {
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 1, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// send a random tx
|
||||
let num_txs = 1;
|
||||
for _ in 0..num_txs {
|
||||
let amount: u64 = i as u64 * 1_000_000;
|
||||
let mut slate = Slate::blank(1, false);
|
||||
debug!("Creating TX for {}", amount);
|
||||
wallet::controller::owner_single_use(
|
||||
Some(wallet1.clone()),
|
||||
mask1,
|
||||
None,
|
||||
|sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 1,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel a tx for filtering testing
|
||||
let amount: u64 = 1_000_000;
|
||||
let mut slate = Slate::blank(1, false);
|
||||
debug!("Creating TX for {}", amount);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 1,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
sender_api.cancel_tx(m, Some(33), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Perform actual testing
|
||||
test_wallet_tx_filtering(wallet1, mask1)?;
|
||||
|
||||
// let logging finish
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_tx_filtering() {
|
||||
let test_dir = "test_output/advanced_tx_filtering";
|
||||
clean_output_dir(test_dir);
|
||||
setup(test_dir);
|
||||
if let Err(e) = build_chain_for_tx_filtering(test_dir, 30) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+123
-123
@@ -1,123 +1,123 @@
|
||||
// Copyright 2021 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 repost command
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_api as api;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
// use crate::libwallet::api_impl::owner_updater::{start_updater_log_thread, StatusMessage};
|
||||
// use grin_wallet_util::grin_core as core;
|
||||
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup, setup_global_chain_type};
|
||||
|
||||
/// updater thread test impl
|
||||
fn updater_thread_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.create_account_path(m, "account1")?;
|
||||
api.create_account_path(m, "account2")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let owner_api = api::Owner::new(wallet1, None);
|
||||
owner_api.start_updater(mask1, Duration::from_secs(5))?;
|
||||
|
||||
// let updater thread run a bit
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
|
||||
let messages = owner_api.get_updater_messages(1000)?;
|
||||
assert_eq!(messages.len(), 32);
|
||||
|
||||
owner_api.stop_updater()?;
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn updater_thread() {
|
||||
// The "updater" kicks off a new thread so we need to ensure the global chain_type
|
||||
// is set for this to work correctly.
|
||||
setup_global_chain_type();
|
||||
|
||||
let test_dir = "test_output/updater_thread";
|
||||
setup(test_dir);
|
||||
if let Err(e) = updater_thread_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
// Copyright 2021 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 repost command
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate grin_wallet_api as api;
|
||||
extern crate grin_wallet_controller as wallet;
|
||||
extern crate grin_wallet_impls as impls;
|
||||
extern crate grin_wallet_libwallet as libwallet;
|
||||
|
||||
// use crate::libwallet::api_impl::owner_updater::{start_updater_log_thread, StatusMessage};
|
||||
// use grin_wallet_util::grin_core as core;
|
||||
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup, setup_global_chain_type};
|
||||
|
||||
/// updater thread test impl
|
||||
fn updater_thread_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
let stopper = wallet_proxy.running.clone();
|
||||
|
||||
// Create a new wallet test client, and set its queues to communicate with the
|
||||
// proxy
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
api.create_account_path(m, "account1")?;
|
||||
api.create_account_path(m, "account2")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let owner_api = api::Owner::new(wallet1, None);
|
||||
owner_api.start_updater(mask1, Duration::from_secs(5))?;
|
||||
|
||||
// let updater thread run a bit
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
|
||||
let messages = owner_api.get_updater_messages(1000)?;
|
||||
assert_eq!(messages.len(), 32);
|
||||
|
||||
owner_api.stop_updater()?;
|
||||
stopper.store(false, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn updater_thread() {
|
||||
// The "updater" kicks off a new thread so we need to ensure the global chain_type
|
||||
// is set for this to work correctly.
|
||||
setup_global_chain_type();
|
||||
|
||||
let test_dir = "test_output/updater_thread";
|
||||
setup(test_dir);
|
||||
if let Err(e) = updater_thread_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {}", e);
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user