mirror of
https://github.com/jwinterm/grin-wallet.git
synced 2026-10-17 15:21:39 +02:00
Slatepack Workflow (#423)
* debugging tor sends * use fixed yeast version of hyper-socks2 * send command working according to slatepack * changes to handling proof address * modifications of recieve command to work with slatepack and to attempt to return to sender via TOR * finalize command tested * attempting to add invoice processing * fixes to tests, propagation of test mode where needed * modify return values of api functions that can send as sync * cleanup, testing and post_tx function * revert changes to API, many test fixes * deprecate http on the command-line warning
This commit is contained in:
+493
-205
@@ -15,21 +15,23 @@
|
||||
//! Grin wallet command-line function implementations
|
||||
|
||||
use crate::api::TLSConfig;
|
||||
use crate::apiwallet::Owner;
|
||||
use crate::apiwallet::{try_slatepack_sync_workflow, Owner};
|
||||
use crate::config::{TorConfig, WalletConfig, WALLET_CONFIG_FILE_NAME};
|
||||
use crate::core::{core, global};
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::impls::{create_sender, KeybaseAllChannels, SlateGetter as _, SlateReceiver as _};
|
||||
use crate::impls::{HttpSlateSender, PathToSlate, SlatePutter};
|
||||
use crate::impls::SlateGetter as _;
|
||||
use crate::impls::{HttpSlateSender, PathToSlate, PathToSlatepack, SlatePutter};
|
||||
use crate::keychain;
|
||||
use crate::libwallet::{
|
||||
self, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, Slate, SlateVersion,
|
||||
SlatepackAddress, WalletLCProvider,
|
||||
SlatepackAddress, Slatepacker, SlatepackerArgs, WalletLCProvider,
|
||||
};
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::{Mutex, ZeroingString};
|
||||
use crate::{controller, display};
|
||||
use grin_wallet_util::OnionV3Address;
|
||||
use serde_json as json;
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::atomic::Ordering;
|
||||
@@ -71,6 +73,7 @@ pub fn init<L, C, K>(
|
||||
owner_api: &mut Owner<L, C, K>,
|
||||
_g_args: &GlobalArgs,
|
||||
args: InitArgs,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
@@ -88,7 +91,7 @@ where
|
||||
args.recovery_phrase,
|
||||
args.list_length,
|
||||
args.password.clone(),
|
||||
false,
|
||||
test_mode,
|
||||
)?;
|
||||
|
||||
let m = p.get_mnemonic(None, args.password)?;
|
||||
@@ -115,72 +118,51 @@ where
|
||||
}
|
||||
|
||||
/// Arguments for listen command
|
||||
pub struct ListenArgs {
|
||||
pub method: String,
|
||||
}
|
||||
pub struct ListenArgs {}
|
||||
|
||||
pub fn listen<L, C, K>(
|
||||
owner_api: &mut Owner<L, C, K>,
|
||||
keychain_mask: Arc<Mutex<Option<SecretKey>>>,
|
||||
config: &WalletConfig,
|
||||
tor_config: &TorConfig,
|
||||
args: &ListenArgs,
|
||||
_args: &ListenArgs,
|
||||
g_args: &GlobalArgs,
|
||||
cli_mode: bool,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let res = match args.method.as_str() {
|
||||
"http" => {
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
let config = config.clone();
|
||||
let tor_config = tor_config.clone();
|
||||
let g_args = g_args.clone();
|
||||
let api_thread = thread::Builder::new()
|
||||
.name("wallet-http-listener".to_string())
|
||||
.spawn(move || {
|
||||
let res = controller::foreign_listener(
|
||||
wallet_inst,
|
||||
keychain_mask,
|
||||
&config.api_listen_addr(),
|
||||
g_args.tls_conf.clone(),
|
||||
tor_config.use_tor_listener,
|
||||
);
|
||||
if let Err(e) = res {
|
||||
error!("Error starting listener: {}", e);
|
||||
}
|
||||
});
|
||||
if let Ok(t) = api_thread {
|
||||
if !cli_mode {
|
||||
let r = t.join();
|
||||
if let Err(_) = r {
|
||||
error!("Error starting listener");
|
||||
return Err(ErrorKind::ListenerError.into());
|
||||
}
|
||||
}
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
let config = config.clone();
|
||||
let tor_config = tor_config.clone();
|
||||
let g_args = g_args.clone();
|
||||
let api_thread = thread::Builder::new()
|
||||
.name("wallet-http-listener".to_string())
|
||||
.spawn(move || {
|
||||
let res = controller::foreign_listener(
|
||||
wallet_inst,
|
||||
keychain_mask,
|
||||
&config.api_listen_addr(),
|
||||
g_args.tls_conf.clone(),
|
||||
tor_config.use_tor_listener,
|
||||
test_mode,
|
||||
Some(tor_config.clone()),
|
||||
);
|
||||
if let Err(e) = res {
|
||||
error!("Error starting listener: {}", e);
|
||||
}
|
||||
});
|
||||
if let Ok(t) = api_thread {
|
||||
if !cli_mode {
|
||||
let r = t.join();
|
||||
if let Err(_) = r {
|
||||
error!("Error starting listener");
|
||||
return Err(ErrorKind::ListenerError.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
"keybase" => KeybaseAllChannels::new()?.listen(
|
||||
config.clone(),
|
||||
g_args.password.clone().unwrap(),
|
||||
&g_args.account,
|
||||
g_args.node_api_secret.clone(),
|
||||
),
|
||||
method => {
|
||||
return Err(ErrorKind::ArgumentError(format!(
|
||||
"No listener for method \"{}\".",
|
||||
method
|
||||
))
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(ErrorKind::LibWallet(e.kind(), e.cause_string()).into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -191,6 +173,7 @@ pub fn owner_api<L, C, K>(
|
||||
config: &WalletConfig,
|
||||
tor_config: &TorConfig,
|
||||
g_args: &GlobalArgs,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + Send + Sync + 'static,
|
||||
@@ -208,6 +191,7 @@ where
|
||||
g_args.tls_conf.clone(),
|
||||
config.owner_api_include_foreign.clone(),
|
||||
Some(tor_config.clone()),
|
||||
test_mode,
|
||||
);
|
||||
if let Err(e) = res {
|
||||
return Err(ErrorKind::LibWallet(e.kind(), e.cause_string()).into());
|
||||
@@ -260,12 +244,12 @@ where
|
||||
}
|
||||
|
||||
/// Arguments for the send command
|
||||
#[derive(Clone)]
|
||||
pub struct SendArgs {
|
||||
pub amount: u64,
|
||||
pub minimum_confirmations: u64,
|
||||
pub selection_strategy: String,
|
||||
pub estimate_selection_strategies: bool,
|
||||
pub method: String,
|
||||
pub dest: String,
|
||||
pub change_outputs: usize,
|
||||
pub fluff: bool,
|
||||
@@ -283,6 +267,7 @@ pub fn send<L, C, K>(
|
||||
tor_config: Option<TorConfig>,
|
||||
args: SendArgs,
|
||||
dark_scheme: bool,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
@@ -295,55 +280,121 @@ where
|
||||
// TODO: This block is temporary, for the period between the release of v4.0.0 and HF3,
|
||||
// after which this should be removable
|
||||
let mut args = args;
|
||||
|
||||
//TODO: Remove block post HF3
|
||||
// All this block does is determine whether the slate should be
|
||||
// output as a V3 Slate for the receiver
|
||||
let mut tor_sender = None;
|
||||
let is_pre_fork;
|
||||
{
|
||||
let invalid = || {
|
||||
ErrorKind::GenericError(format!(
|
||||
"Invalid wallet comm type and destination. method: {}, dest: {}",
|
||||
args.method, args.dest
|
||||
))
|
||||
is_pre_fork = {
|
||||
let cur_height = {
|
||||
libwallet::wallet_lock!(wallet_inst, w);
|
||||
w.w2n_client().get_chain_tip()?.0
|
||||
};
|
||||
match global::get_chain_type() {
|
||||
global::ChainTypes::Mainnet => {
|
||||
if cur_height < 786240 && !args.output_v4_slate {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
global::ChainTypes::Floonet => {
|
||||
if cur_height < 552960 && !args.output_v4_slate {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
let trailing = match args.dest.ends_with('/') {
|
||||
true => "",
|
||||
false => "/",
|
||||
};
|
||||
let url_str = format!("{}{}v2/foreign", args.dest, trailing);
|
||||
match args.method.as_ref() {
|
||||
"http" => {
|
||||
let v_sender = HttpSlateSender::new(&args.dest).map_err(|_| invalid())?;
|
||||
let other_version = v_sender.check_other_version(&url_str)?;
|
||||
if other_version == SlateVersion::V3 {
|
||||
args.target_slate_version = Some(3);
|
||||
|
||||
if is_pre_fork {
|
||||
let trailing = match args.dest.ends_with('/') {
|
||||
true => "",
|
||||
false => "/",
|
||||
};
|
||||
|
||||
let mut address_found = false;
|
||||
// For sync methods, derive intended endpoint from dest
|
||||
match SlatepackAddress::try_from(args.dest.as_str()) {
|
||||
Ok(address) => {
|
||||
let tor_addr = OnionV3Address::try_from(&address).unwrap();
|
||||
// Try pinging the destination via TOR
|
||||
debug!("Version ping: TOR address is: {}", tor_addr);
|
||||
match HttpSlateSender::with_socks_proxy(
|
||||
&tor_addr.to_http_str(),
|
||||
&tor_config.as_ref().unwrap().socks_proxy_addr,
|
||||
&tor_config.as_ref().unwrap().send_config_dir,
|
||||
) {
|
||||
Ok(mut sender) => {
|
||||
let url_str =
|
||||
format!("{}{}v2/foreign", tor_addr.to_http_str(), trailing);
|
||||
if let Ok(v) = sender.check_other_version(&url_str) {
|
||||
if v == SlateVersion::V3 {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
address_found = true;
|
||||
}
|
||||
tor_sender = Some(sender);
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(
|
||||
"Version ping: Couldn't create slate sender for TOR: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Version ping: Address is not SlatepackAddress: {:?}", e);
|
||||
}
|
||||
}
|
||||
"tor" => {
|
||||
let v_sender = HttpSlateSender::with_socks_proxy(
|
||||
&args.dest,
|
||||
&tor_config.as_ref().unwrap().socks_proxy_addr,
|
||||
&tor_config.as_ref().unwrap().send_config_dir,
|
||||
)
|
||||
.map_err(|_| invalid())?;
|
||||
let other_version = v_sender.check_other_version(&url_str)?;
|
||||
if other_version == SlateVersion::V3 {
|
||||
args.target_slate_version = Some(3);
|
||||
|
||||
// now try http
|
||||
if !address_found {
|
||||
// Try pinging the destination via TOR
|
||||
match HttpSlateSender::new(&args.dest) {
|
||||
Ok(mut sender) => {
|
||||
let url_str = format!("{}{}v2/foreign", args.dest, trailing);
|
||||
match sender.check_other_version(&url_str) {
|
||||
Ok(v) => {
|
||||
if v == SlateVersion::V3 {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
address_found = true;
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(
|
||||
"Version ping: Couldn't get other version for HTTP: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(
|
||||
"Version ping: Couldn't create slate sender for HTTP: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
"file" => {
|
||||
|
||||
if !address_found {
|
||||
// otherwise, determine slate format based on block height
|
||||
// For files spit out a V3 Slate if we're before HF3,
|
||||
// Or V4 slate otherwise
|
||||
let cur_height = {
|
||||
libwallet::wallet_lock!(wallet_inst, w);
|
||||
w.w2n_client().get_chain_tip()?.0
|
||||
};
|
||||
// TODO: Floonet HF4
|
||||
if cur_height < 786240 && !args.output_v4_slate {
|
||||
if is_pre_fork {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// end block to delete post HF3
|
||||
} // end pre HF3 Block
|
||||
|
||||
let mut slate = Slate::blank(2, false);
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
if args.estimate_selection_strategies {
|
||||
let strategies = vec!["smallest", "all"]
|
||||
@@ -364,6 +415,7 @@ where
|
||||
})
|
||||
.collect();
|
||||
display::estimate(args.amount, strategies, dark_scheme);
|
||||
return Ok(());
|
||||
} else {
|
||||
let init_args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
@@ -379,7 +431,7 @@ where
|
||||
..Default::default()
|
||||
};
|
||||
let result = api.init_send_tx(m, init_args);
|
||||
let mut slate = match result {
|
||||
slate = match result {
|
||||
Ok(s) => {
|
||||
info!(
|
||||
"Tx created: {} grin to {} (strategy '{}')",
|
||||
@@ -394,57 +446,208 @@ where
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
match args.method.as_str() {
|
||||
"file" => {
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
return Ok(());
|
||||
}
|
||||
"binfile" => {
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate, true)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
return Ok(());
|
||||
}
|
||||
"self" => {
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
let km = match keychain_mask.as_ref() {
|
||||
None => None,
|
||||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
controller::foreign_single_use(wallet_inst, km, |api| {
|
||||
slate = api.receive_tx(&slate, Some(&args.dest))?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
method => {
|
||||
let sender = create_sender(method, &args.dest, tor_config)?;
|
||||
slate = sender.send_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
}
|
||||
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
let result = api.post_tx(m, &slate, args.fluff);
|
||||
match result {
|
||||
Ok(_) => {
|
||||
info!("Tx sent ok",);
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Tx sent fail: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let res =
|
||||
try_slatepack_sync_workflow(&slate, &args.dest, tor_config, tor_sender, false, test_mode);
|
||||
|
||||
match res {
|
||||
Ok(Some(s)) => {
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
api.tx_lock_outputs(m, &s)?;
|
||||
let ret_slate = api.finalize_tx(m, &s)?;
|
||||
let result = api.post_tx(m, &ret_slate, args.fluff);
|
||||
match result {
|
||||
Ok(_) => {
|
||||
println!("Tx sent successfully",);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Tx sent fail: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
})?;
|
||||
}
|
||||
Ok(None) => {
|
||||
output_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
&slate,
|
||||
args.dest.as_str(),
|
||||
true,
|
||||
false,
|
||||
is_pre_fork,
|
||||
)?;
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn output_slatepack<L, C, K>(
|
||||
owner_api: &mut Owner<L, C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
dest: &str,
|
||||
lock: bool,
|
||||
file_only: bool,
|
||||
is_pre_fork: bool,
|
||||
) -> Result<(), libwallet::Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
// Output the slatepack file to stdout and to a file
|
||||
let mut message = String::from("");
|
||||
let mut address = None;
|
||||
let mut tld = String::from("");
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
address = match SlatepackAddress::try_from(dest) {
|
||||
Ok(a) => Some(a),
|
||||
Err(_) => None,
|
||||
};
|
||||
// encrypt for recipient by default
|
||||
let recipients = match address.clone() {
|
||||
Some(a) => vec![a],
|
||||
None => vec![],
|
||||
};
|
||||
message = api.create_slatepack_message(m, &slate, Some(0), recipients)?;
|
||||
tld = api.get_top_level_directory()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// create a directory to which files will be output
|
||||
let slate_dir = format!("{}/{}", tld, "slatepack");
|
||||
let _ = std::fs::create_dir_all(slate_dir.clone());
|
||||
let out_file_name = format!("{}/{}.{}.slatepack", slate_dir, slate.id, slate.state);
|
||||
|
||||
if lock {
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
// TODO: Remove HF3
|
||||
if is_pre_fork || file_only {
|
||||
PathToSlate((&out_file_name).into()).put_tx(&slate, false)?;
|
||||
println!();
|
||||
println!("Transaction file was output to:");
|
||||
println!();
|
||||
println!("{}", out_file_name);
|
||||
println!();
|
||||
if !file_only {
|
||||
println!("Please send this file to the other party manually");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("{}", out_file_name);
|
||||
let mut output = File::create(out_file_name.clone())?;
|
||||
output.write_all(&message.as_bytes())?;
|
||||
output.sync_all()?;
|
||||
|
||||
println!();
|
||||
println!("Slatepack data follows. Please provide this output to the other party");
|
||||
println!();
|
||||
println!("--- CUT BELOW THIS LINE ---");
|
||||
println!();
|
||||
println!("{}", message);
|
||||
println!("--- CUT ABOVE THIS LINE ---");
|
||||
println!();
|
||||
println!("Slatepack data was also output to");
|
||||
println!();
|
||||
println!("{}", out_file_name);
|
||||
println!();
|
||||
if address.is_some() {
|
||||
println!("The slatepack data is encrypted for the recipient only");
|
||||
} else {
|
||||
println!("The slatepack data is NOT encrypted");
|
||||
}
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Parse a slate and slatepack from a message
|
||||
pub fn parse_slatepack<L, C, K>(
|
||||
owner_api: &mut Owner<L, C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
filename: Option<String>,
|
||||
message: Option<String>,
|
||||
) -> Result<(Slate, Option<SlatepackAddress>), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K>,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let mut ret_address = None;
|
||||
let slate = match filename {
|
||||
Some(f) => {
|
||||
// first try regular slate - remove HF3
|
||||
let mut sl = match PathToSlate((&f).into()).get_tx() {
|
||||
Ok(s) => Some(s.0), //pre HF3, regular slate
|
||||
Err(_) => None,
|
||||
};
|
||||
// otherwise, get slate from slatepack
|
||||
if sl.is_none() {
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
let dec_key = api.get_slatepack_secret_key(m, 0)?;
|
||||
let packer = Slatepacker::new(SlatepackerArgs {
|
||||
sender: None,
|
||||
recipients: vec![],
|
||||
dec_key: Some(&dec_key),
|
||||
});
|
||||
let pts = PathToSlatepack::new(f.into(), &packer, true);
|
||||
sl = Some(pts.get_tx()?.0);
|
||||
ret_address = pts.get_slatepack()?.sender;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
sl
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let slate = match slate {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
// try and parse directly from input_slatepack_message
|
||||
let mut slate = Slate::blank(2, false);
|
||||
match message {
|
||||
Some(message) => {
|
||||
controller::owner_single_use(
|
||||
None,
|
||||
keychain_mask,
|
||||
Some(owner_api),
|
||||
|api, m| {
|
||||
slate =
|
||||
api.slate_from_slatepack_message(m, message.clone(), vec![0])?;
|
||||
let slatepack = api.decode_slatepack_message(message)?;
|
||||
ret_address = slatepack.sender;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
}
|
||||
None => {
|
||||
let msg = "No slate provided via file or direct input";
|
||||
return Err(ErrorKind::GenericError(msg.into()).into());
|
||||
}
|
||||
}
|
||||
slate
|
||||
}
|
||||
};
|
||||
Ok((slate, ret_address))
|
||||
}
|
||||
|
||||
/// Receive command argument
|
||||
#[derive(Clone)]
|
||||
pub struct ReceiveArgs {
|
||||
pub input: String,
|
||||
pub input_file: Option<String>,
|
||||
pub input_slatepack_message: Option<String>,
|
||||
}
|
||||
|
||||
pub fn receive<L, C, K>(
|
||||
@@ -452,35 +655,71 @@ pub fn receive<L, C, K>(
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
g_args: &GlobalArgs,
|
||||
args: ReceiveArgs,
|
||||
tor_config: Option<TorConfig>,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K>,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let (mut slate, was_bin) = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let (mut slate, ret_address) = parse_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
args.input_file,
|
||||
args.input_slatepack_message,
|
||||
)?;
|
||||
|
||||
let km = match keychain_mask.as_ref() {
|
||||
None => None,
|
||||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
|
||||
controller::foreign_single_use(owner_api.wallet_inst.clone(), km, |api| {
|
||||
slate = api.receive_tx(&slate, Some(&g_args.account))?;
|
||||
slate = api.receive_tx(&slate, Some(&g_args.account), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
PathToSlate(format!("{}.response", args.input).into()).put_tx(&slate, was_bin)?;
|
||||
info!(
|
||||
"Response file {}.response generated, and can be sent back to the transaction originator.",
|
||||
args.input
|
||||
);
|
||||
Ok(())
|
||||
|
||||
let dest = match ret_address {
|
||||
Some(a) => String::try_from(&a).unwrap(),
|
||||
None => String::from(""),
|
||||
};
|
||||
|
||||
let res = try_slatepack_sync_workflow(&slate, &dest, tor_config, None, true, test_mode);
|
||||
|
||||
match res {
|
||||
Ok(Some(_)) => {
|
||||
println!();
|
||||
println!(
|
||||
"Transaction recieved and sent back to sender at {} for finalization.",
|
||||
dest
|
||||
);
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
Ok(None) => {
|
||||
output_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
&slate,
|
||||
&dest,
|
||||
false,
|
||||
false,
|
||||
slate.version_info.version < 4,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Finalize command args
|
||||
#[derive(Clone)]
|
||||
pub struct FinalizeArgs {
|
||||
pub input: String,
|
||||
pub input_file: Option<String>,
|
||||
pub input_slatepack_message: Option<String>,
|
||||
pub fluff: bool,
|
||||
pub nopost: bool,
|
||||
pub dest: Option<String>,
|
||||
}
|
||||
|
||||
pub fn finalize<L, C, K>(
|
||||
@@ -493,7 +732,12 @@ where
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let (mut slate, was_bin) = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let (mut slate, _ret_address) = parse_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
args.input_file.clone(),
|
||||
args.input_slatepack_message.clone(),
|
||||
)?;
|
||||
|
||||
// Rather than duplicating the entire command, we'll just
|
||||
// try to determine what kind of finalization this is
|
||||
@@ -512,7 +756,7 @@ where
|
||||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
controller::foreign_single_use(owner_api.wallet_inst.clone(), km, |api| {
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
} else {
|
||||
@@ -522,7 +766,7 @@ where
|
||||
})?;
|
||||
}
|
||||
|
||||
if !args.nopost {
|
||||
if !&args.nopost {
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
let result = api.post_tx(m, &slate, args.fluff);
|
||||
match result {
|
||||
@@ -530,6 +774,7 @@ where
|
||||
info!(
|
||||
"Transaction sent successfully, check the wallet again for confirmation."
|
||||
);
|
||||
println!("Transaction posted");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -540,9 +785,17 @@ where
|
||||
})?;
|
||||
}
|
||||
|
||||
if args.dest.is_some() {
|
||||
PathToSlate((&args.dest.unwrap()).into()).put_tx(&slate, was_bin)?;
|
||||
}
|
||||
println!("Transaction finalized successfully");
|
||||
|
||||
output_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
&slate,
|
||||
"",
|
||||
false,
|
||||
true,
|
||||
slate.version_info.version < 4,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -553,9 +806,6 @@ pub struct IssueInvoiceArgs {
|
||||
pub dest: String,
|
||||
/// issue invoice tx args
|
||||
pub issue_args: IssueInvoiceTxArgs,
|
||||
/// whether to output as bin
|
||||
pub bin: bool,
|
||||
// TODO: Remove HF3
|
||||
/// whether to output a V4 slate
|
||||
pub output_v4_slate: bool,
|
||||
}
|
||||
@@ -571,24 +821,52 @@ where
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
//TODO: Remove block HF3
|
||||
let args = {
|
||||
let mut a = args;
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
let is_pre_fork = {
|
||||
let cur_height = {
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
libwallet::wallet_lock!(wallet_inst, w);
|
||||
w.w2n_client().get_chain_tip()?.0
|
||||
};
|
||||
// TODO: Floonet HF4
|
||||
if cur_height < 786240 && !a.output_v4_slate && !a.bin {
|
||||
a.issue_args.target_slate_version = Some(3);
|
||||
match global::get_chain_type() {
|
||||
global::ChainTypes::Mainnet => {
|
||||
if cur_height < 786240 && !&args.output_v4_slate {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
global::ChainTypes::Floonet => {
|
||||
if cur_height < 552960 && !&args.output_v4_slate {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
a
|
||||
};
|
||||
|
||||
let mut issue_args = args.issue_args.clone();
|
||||
|
||||
if is_pre_fork {
|
||||
issue_args.target_slate_version = Some(3);
|
||||
}
|
||||
|
||||
let mut slate = Slate::blank(2, false);
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
let slate = api.issue_invoice_tx(m, args.issue_args)?;
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate, args.bin)?;
|
||||
slate = api.issue_invoice_tx(m, issue_args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
output_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
&slate,
|
||||
args.dest.as_str(),
|
||||
false,
|
||||
false,
|
||||
is_pre_fork,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -596,10 +874,9 @@ where
|
||||
pub struct ProcessInvoiceArgs {
|
||||
pub minimum_confirmations: u64,
|
||||
pub selection_strategy: String,
|
||||
pub method: String,
|
||||
pub dest: String,
|
||||
pub ret_address: Option<SlatepackAddress>,
|
||||
pub max_outputs: usize,
|
||||
pub input: String,
|
||||
pub slate: Slate,
|
||||
pub estimate_selection_strategies: bool,
|
||||
pub ttl_blocks: Option<u64>,
|
||||
}
|
||||
@@ -611,14 +888,19 @@ pub fn process_invoice<L, C, K>(
|
||||
tor_config: Option<TorConfig>,
|
||||
args: ProcessInvoiceArgs,
|
||||
dark_scheme: bool,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let (slate, _) = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
let mut slate = args.slate.clone();
|
||||
let dest = match args.ret_address.clone() {
|
||||
Some(a) => String::try_from(&a).unwrap(),
|
||||
None => String::from(""),
|
||||
};
|
||||
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
if args.estimate_selection_strategies {
|
||||
let strategies = vec!["smallest", "all"]
|
||||
@@ -639,6 +921,7 @@ where
|
||||
})
|
||||
.collect();
|
||||
display::estimate(slate.amount, strategies, dark_scheme);
|
||||
return Ok(());
|
||||
} else {
|
||||
let init_args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
@@ -652,12 +935,11 @@ where
|
||||
..Default::default()
|
||||
};
|
||||
let result = api.process_invoice_tx(m, &slate, init_args);
|
||||
let mut slate = match result {
|
||||
slate = match result {
|
||||
Ok(s) => {
|
||||
info!(
|
||||
"Invoice processed: {} grin to {} (strategy '{}')",
|
||||
"Invoice processed: {} grin (strategy '{}')",
|
||||
core::amount_to_hr_string(slate.amount, false),
|
||||
args.dest,
|
||||
args.selection_strategy,
|
||||
);
|
||||
s
|
||||
@@ -667,40 +949,38 @@ where
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
match args.method.as_str() {
|
||||
"file" => {
|
||||
let slate_putter = PathToSlate((&args.dest).into());
|
||||
slate_putter.put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
"filebin" => {
|
||||
let slate_putter = PathToSlate((&args.dest).into());
|
||||
slate_putter.put_tx(&slate, true)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
"self" => {
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
let km = match keychain_mask.as_ref() {
|
||||
None => None,
|
||||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
controller::foreign_single_use(wallet_inst, km, |api| {
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
method => {
|
||||
let sender = create_sender(method, &args.dest, tor_config)?;
|
||||
slate = sender.send_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
|
||||
let res = try_slatepack_sync_workflow(&slate, &dest, tor_config, None, true, test_mode);
|
||||
|
||||
match res {
|
||||
Ok(Some(_)) => {
|
||||
println!();
|
||||
println!(
|
||||
"Transaction paid and sent back to initiator at {} for finalization.",
|
||||
dest
|
||||
);
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
Ok(None) => {
|
||||
output_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
&slate,
|
||||
&dest,
|
||||
true,
|
||||
false,
|
||||
slate.version_info.version < 4,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Info command args
|
||||
pub struct InfoArgs {
|
||||
pub minimum_confirmations: u64,
|
||||
@@ -828,8 +1108,10 @@ where
|
||||
}
|
||||
|
||||
/// Post
|
||||
#[derive(Clone)]
|
||||
pub struct PostArgs {
|
||||
pub input: String,
|
||||
pub input_file: Option<String>,
|
||||
pub input_slatepack_message: Option<String>,
|
||||
pub fluff: bool,
|
||||
}
|
||||
|
||||
@@ -843,10 +1125,16 @@ where
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let slate = PathToSlate((&args.input).into()).get_tx()?.0;
|
||||
let (slate, _ret_address) = parse_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
args.input_file,
|
||||
args.input_slatepack_message,
|
||||
)?;
|
||||
|
||||
let fluff = args.fluff;
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
api.post_tx(m, &slate, args.fluff)?;
|
||||
api.post_tx(m, &slate, fluff)?;
|
||||
info!("Posted transaction");
|
||||
return Ok(());
|
||||
})?;
|
||||
|
||||
@@ -18,8 +18,8 @@ use crate::api::{self, ApiServer, BasicAuthMiddleware, ResponseFuture, Router, T
|
||||
use crate::config::TorConfig;
|
||||
use crate::keychain::Keychain;
|
||||
use crate::libwallet::{
|
||||
address, Error, ErrorKind, NodeClient, NodeVersionInfo, Slate, WalletInst, WalletLCProvider,
|
||||
GRIN_BLOCK_HEADER_VERSION,
|
||||
address, Error, ErrorKind, NodeClient, NodeVersionInfo, Slate, SlatepackAddress, WalletInst,
|
||||
WalletLCProvider, GRIN_BLOCK_HEADER_VERSION,
|
||||
};
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::{from_hex, static_secp_instance, to_base64, Mutex};
|
||||
@@ -32,6 +32,7 @@ use hyper::{Body, Request, Response, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -59,12 +60,12 @@ fn check_middleware(
|
||||
// allow coinbases to be built regardless
|
||||
ForeignCheckMiddlewareFn::BuildCoinbase => Ok(()),
|
||||
_ => {
|
||||
let mut bhv = 2;
|
||||
let mut bhv = 3;
|
||||
if let Some(n) = node_version_info {
|
||||
bhv = n.block_header_version;
|
||||
}
|
||||
if let Some(s) = slate {
|
||||
if bhv > 3 && s.version_info.block_header_version < GRIN_BLOCK_HEADER_VERSION {
|
||||
if bhv > 4 && s.version_info.block_header_version < GRIN_BLOCK_HEADER_VERSION {
|
||||
Err(ErrorKind::Compatibility(
|
||||
"Incoming Slate is not compatible with this wallet. \
|
||||
Please upgrade the node or use a different one."
|
||||
@@ -82,7 +83,7 @@ fn init_tor_listener<L, C, K>(
|
||||
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
keychain_mask: Arc<Mutex<Option<SecretKey>>>,
|
||||
addr: &str,
|
||||
) -> Result<tor_process::TorProcess, Error>
|
||||
) -> Result<(tor_process::TorProcess, SlatepackAddress), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
@@ -101,6 +102,7 @@ where
|
||||
.map_err(|e| ErrorKind::TorConfig(format!("{:?}", e).into()))?;
|
||||
let onion_address = OnionV3Address::from_private(&sec_key.0)
|
||||
.map_err(|e| ErrorKind::TorConfig(format!("{:?}", e).into()))?;
|
||||
let sp_address = SlatepackAddress::try_from(onion_address.clone())?;
|
||||
warn!(
|
||||
"Starting TOR Hidden Service for API listener at address {}, binding to {}",
|
||||
onion_address, addr
|
||||
@@ -115,7 +117,7 @@ where
|
||||
.completion_percent(100)
|
||||
.launch()
|
||||
.map_err(|e| ErrorKind::TorProcess(format!("{:?}", e).into()))?;
|
||||
Ok(process)
|
||||
Ok((process, sp_address))
|
||||
}
|
||||
|
||||
/// Instantiate wallet Owner API for a single-use (command line) call
|
||||
@@ -167,6 +169,7 @@ where
|
||||
wallet,
|
||||
keychain_mask,
|
||||
Some(check_middleware),
|
||||
false,
|
||||
))?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -183,6 +186,7 @@ pub fn owner_listener<L, C, K>(
|
||||
tls_config: Option<TLSConfig>,
|
||||
owner_api_include_foreign: Option<bool>,
|
||||
tor_config: Option<TorConfig>,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
@@ -208,7 +212,7 @@ where
|
||||
let api_handler_v3 = OwnerAPIHandlerV3::new(
|
||||
wallet.clone(),
|
||||
keychain_mask.clone(),
|
||||
tor_config,
|
||||
tor_config.clone(),
|
||||
running_foreign,
|
||||
);
|
||||
|
||||
@@ -219,7 +223,8 @@ where
|
||||
// If so configured, add the foreign API to the same port
|
||||
if running_foreign {
|
||||
warn!("Starting HTTP Foreign API on Owner server at {}.", addr);
|
||||
let foreign_api_handler_v2 = ForeignAPIHandlerV2::new(wallet, keychain_mask);
|
||||
let foreign_api_handler_v2 =
|
||||
ForeignAPIHandlerV2::new(wallet, keychain_mask, test_mode, Mutex::new(tor_config));
|
||||
router
|
||||
.add_route("/v2/foreign", Arc::new(foreign_api_handler_v2))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
@@ -247,6 +252,8 @@ pub fn foreign_listener<L, C, K>(
|
||||
addr: &str,
|
||||
tls_config: Option<TLSConfig>,
|
||||
use_tor: bool,
|
||||
test_mode: bool,
|
||||
tor_config: Option<TorConfig>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
@@ -260,20 +267,21 @@ where
|
||||
let _ = lc.wallet_inst()?;
|
||||
}
|
||||
// need to keep in scope while the main listener is running
|
||||
let _tor_process = match use_tor {
|
||||
let (_tor_process, address) = match use_tor {
|
||||
true => match init_tor_listener(wallet.clone(), keychain_mask.clone(), addr) {
|
||||
Ok(tp) => Some(tp),
|
||||
Ok((tp, addr)) => (Some(tp), Some(addr)),
|
||||
Err(e) => {
|
||||
warn!("Unable to start TOR listener; Check that TOR executable is installed and on your path");
|
||||
warn!("Tor Error: {}", e);
|
||||
warn!("Listener will be available via HTTP only");
|
||||
None
|
||||
(None, None)
|
||||
}
|
||||
},
|
||||
false => None,
|
||||
false => (None, None),
|
||||
};
|
||||
|
||||
let api_handler_v2 = ForeignAPIHandlerV2::new(wallet, keychain_mask);
|
||||
let api_handler_v2 =
|
||||
ForeignAPIHandlerV2::new(wallet, keychain_mask, test_mode, Mutex::new(tor_config));
|
||||
let mut router = Router::new();
|
||||
|
||||
router
|
||||
@@ -290,6 +298,9 @@ where
|
||||
))?;
|
||||
|
||||
warn!("HTTP Foreign listener started.");
|
||||
if let Some(a) = address {
|
||||
warn!("Slatepack Address is: {}", a);
|
||||
}
|
||||
|
||||
api_thread
|
||||
.join()
|
||||
@@ -669,6 +680,10 @@ where
|
||||
pub wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
/// Keychain mask
|
||||
pub keychain_mask: Arc<Mutex<Option<SecretKey>>>,
|
||||
/// run in doctest mode
|
||||
pub test_mode: bool,
|
||||
/// tor config
|
||||
pub tor_config: Mutex<Option<TorConfig>>,
|
||||
}
|
||||
|
||||
impl<L, C, K> ForeignAPIHandlerV2<L, C, K>
|
||||
@@ -681,10 +696,14 @@ where
|
||||
pub fn new(
|
||||
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
keychain_mask: Arc<Mutex<Option<SecretKey>>>,
|
||||
test_mode: bool,
|
||||
tor_config: Mutex<Option<TorConfig>>,
|
||||
) -> ForeignAPIHandlerV2<L, C, K> {
|
||||
ForeignAPIHandlerV2 {
|
||||
wallet,
|
||||
keychain_mask,
|
||||
test_mode,
|
||||
tor_config,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -707,8 +726,11 @@ where
|
||||
req: Request<Body>,
|
||||
mask: Option<SecretKey>,
|
||||
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
test_mode: bool,
|
||||
tor_config: Option<TorConfig>,
|
||||
) -> Result<Response<Body>, Error> {
|
||||
let api = Foreign::new(wallet, mask, Some(check_middleware));
|
||||
let api = Foreign::new(wallet, mask, Some(check_middleware), test_mode);
|
||||
api.set_tor_config(tor_config);
|
||||
let res = Self::call_api(req, api).await?;
|
||||
Ok(json_response_pretty(&res))
|
||||
}
|
||||
@@ -723,9 +745,11 @@ where
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
let mask = self.keychain_mask.lock().clone();
|
||||
let wallet = self.wallet.clone();
|
||||
let test_mode = self.test_mode;
|
||||
let tor_config = self.tor_config.lock().clone();
|
||||
|
||||
Box::pin(async move {
|
||||
match Self::handle_post_request(req, mask, wallet).await {
|
||||
match Self::handle_post_request(req, mask, wallet, test_mode, tor_config).await {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => {
|
||||
error!("Request Error: {:?}", e);
|
||||
|
||||
@@ -143,7 +143,7 @@ fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(),
|
||||
|
||||
// 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)?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
@@ -226,7 +226,7 @@ fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(),
|
||||
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_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
@@ -276,7 +276,7 @@ fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(),
|
||||
|
||||
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)?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
@@ -128,7 +128,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// 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_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice3);
|
||||
@@ -200,7 +200,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// 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_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// 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_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask1, None, |api, m| {
|
||||
|
||||
@@ -134,7 +134,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
|
||||
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)?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
@@ -97,7 +97,7 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
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"))?;
|
||||
slate = api.receive_tx(&slate, Some("listener"), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
|
||||
@@ -237,7 +237,7 @@ fn slatepack_exchange_test_impl(
|
||||
|
||||
// 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)?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
output_slatepack(
|
||||
&slate,
|
||||
&receive_file,
|
||||
@@ -351,7 +351,7 @@ fn slatepack_exchange_test_impl(
|
||||
// Wallet 2 receives the invoice transaction
|
||||
let res = slate_from_packed(&receive_file, use_armored, (&dec_key_2).as_ref())?;
|
||||
slate = res.1;
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
output_slatepack(&slate, &final_file, use_armored, use_bin, None, vec![])?;
|
||||
Ok(())
|
||||
})?;
|
||||
@@ -411,7 +411,7 @@ fn slatepack_exchange_test_impl(
|
||||
let res = slate_from_packed(&send_file, use_armored, (&dec_key_2).as_ref())?;
|
||||
slatepack = res.0;
|
||||
slate = res.1;
|
||||
slate = api.receive_tx(&slate, None)?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
output_slatepack(
|
||||
&slate,
|
||||
&receive_file,
|
||||
|
||||
Reference in New Issue
Block a user