From 079ea5e4fd9929a73a6f3a7f62240933d7537a43 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:11:15 +0300 Subject: [PATCH 01/16] fix(db): stop creating the all-zeroes dir on KDF start (#2218) KDF was creating the default all zeroes db dir on start which is not used, this commit fixes this. --- mm2src/mm2_core/src/mm_ctx.rs | 7 +++++-- mm2src/mm2_main/src/lp_native_dex.rs | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index e891f0b649..ec7ceb036a 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -287,10 +287,13 @@ impl MmCtx { }) } + /// Returns the path to the MM databases root. + #[cfg(not(target_arch = "wasm32"))] + pub fn db_root(&self) -> PathBuf { path_to_db_root(self.conf["dbdir"].as_str()) } + #[cfg(not(target_arch = "wasm32"))] pub fn wallet_file_path(&self, wallet_name: &str) -> PathBuf { - let db_root = path_to_db_root(self.conf["dbdir"].as_str()); - db_root.join(wallet_name.to_string() + ".dat") + self.db_root().join(wallet_name.to_string() + ".dat") } /// MM database path. diff --git a/mm2src/mm2_main/src/lp_native_dex.rs b/mm2src/mm2_main/src/lp_native_dex.rs index 3b73947ffb..bd875511b0 100644 --- a/mm2src/mm2_main/src/lp_native_dex.rs +++ b/mm2src/mm2_main/src/lp_native_dex.rs @@ -498,9 +498,11 @@ pub async fn lp_init_continue(ctx: MmArc) -> MmInitResult<()> { pub async fn lp_init(ctx: MmArc, version: String, datetime: String) -> MmInitResult<()> { info!("Version: {} DT {}", version, datetime); + // Ensure the database root directory exists before initializing the wallet passphrase. + // This is necessary to store the encrypted wallet passphrase if needed. #[cfg(not(target_arch = "wasm32"))] { - let dbdir = ctx.dbdir(); + let dbdir = ctx.db_root(); fs::create_dir_all(&dbdir).map_to_mm(|e| MmInitError::ErrorCreatingDbDir { path: dbdir.clone(), error: e.to_string(), From d6bee9ddd9b9d08db24a827c54699c5dec0f1b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Wed, 18 Sep 2024 12:57:35 +0300 Subject: [PATCH 02/16] remove the non-sense arguments (#2216) Signed-off-by: onur-ozkan --- mm2src/common/common.rs | 13 ------------- mm2src/mm2_main/src/mm2.rs | 11 ----------- 2 files changed, 24 deletions(-) diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index c98fe610d2..0eeb726ad4 100644 --- a/mm2src/common/common.rs +++ b/mm2src/common/common.rs @@ -518,19 +518,6 @@ pub fn set_panic_hook() { })) } -/// Simulates the panic-in-panic crash. -pub fn double_panic_crash() { - struct Panicker; - impl Drop for Panicker { - fn drop(&mut self) { panic!("panic in drop") } - } - let panicker = Panicker; - if 1 < 2 { - panic!("first panic") - } - drop(panicker) // Delays the drop. -} - /// RPC response, returned by the RPC handlers. /// NB: By default the future is executed on the shared asynchronous reactor (`CORE`), /// the handler is responsible for spawning the future on another reactor if it doesn't fit the `CORE` well. diff --git a/mm2src/mm2_main/src/mm2.rs b/mm2src/mm2_main/src/mm2.rs index 7dcc5572cb..4ecfb5706d 100644 --- a/mm2src/mm2_main/src/mm2.rs +++ b/mm2src/mm2_main/src/mm2.rs @@ -42,7 +42,6 @@ #[cfg(not(target_arch = "wasm32"))] use common::block_on; use common::crash_reports::init_crash_reports; -use common::double_panic_crash; use common::log::LogLevel; use common::password_policy::password_policy; use mm2_core::mm_ctx::MmCtxBuilder; @@ -248,16 +247,6 @@ pub fn mm2_main(version: String, datetime: String) { // we're not checking them for the mode switches in order not to risk [untrusted] data being mistaken for a mode switch. let first_arg = args_os.get(1).and_then(|arg| arg.to_str()); - if first_arg == Some("panic") { - panic!("panic message") - } - if first_arg == Some("crash") { - double_panic_crash() - } - if first_arg == Some("stderr") { - eprintln!("This goes to stderr"); - return; - } if first_arg == Some("update_config") { match on_update_config(&args_os) { Ok(_) => println!("Success"), From 3b271721ac7db39f5c99e8ac660f8cea4bfb1eea Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Thu, 19 Sep 2024 03:40:16 +0300 Subject: [PATCH 03/16] docs(README): fix typos (#2212) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0d91d3cded..bec7bd1166 100755 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ ## What is the Komodo DeFi Framework? -The Komodo DeFi Framework is open-source [atomic-swap](https://komodoplatform.com/en/academy/atomic-swaps/) software for seamless, decentralised, peer to peer trading between almost every blockchain asset in existence. This software works with propagation of orderbooks and swap states through the [libp2p](https://libp2p.io/) protocol and uses [Hash Time Lock Contracts (HTLCs)](https://en.bitcoinwiki.org/wiki/Hashed_Timelock_Contracts) for ensuring that the two parties in a swap either mutually complete a trade, or funds return to thier original owner. +The Komodo DeFi Framework is open-source [atomic-swap](https://komodoplatform.com/en/academy/atomic-swaps/) software for seamless, decentralized, peer to peer trading between almost every blockchain asset in existence. This software works with propagation of orderbooks and swap states through the [libp2p](https://libp2p.io/) protocol and uses [Hash Time Lock Contracts (HTLCs)](https://en.bitcoinwiki.org/wiki/Hashed_Timelock_Contracts) for ensuring that the two parties in a swap either mutually complete a trade, or funds return to thier original owner. There is no 3rd party intermediary, no proxy tokens, and at all times users remain in sole possession of their private keys. @@ -172,7 +172,7 @@ Refer to the [Komodo Developer Docs](https://developers.komodoplatform.com/basic ## Project structure -[mm2src](mm2src) - Rust code, contains some parts ported from C `as is` (e.g. `lp_ordermatch`) to reach the most essential/error prone code. Some other modules/crates are reimplemented from scratch. +[mm2src](mm2src) - Rust code, contains some parts ported from C `as is` (e.g. `lp_ordermatch`) to reach the most essential/error-prone code. Some other modules/crates are reimplemented from scratch. ## Additional docs for developers @@ -185,8 +185,8 @@ Refer to the [Komodo Developer Docs](https://developers.komodoplatform.com/basic ## Disclaimer -This repository contains the `work in progress` code of the brand new Komodo DeFi Framework (kdf) built mainly on Rust. -The current state can be considered as a alpha version. +This repository contains the `work in progress` code of the brand-new Komodo DeFi Framework (kdf) built mainly on Rust. +The current state can be considered as an alpha version. **WARNING: Use with test coins only or with assets which value does not exceed an amount you are willing to lose. This is alpha stage software! ** From baa72a74fef4b0e22f766186a04a6252ed785b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Thu, 19 Sep 2024 04:35:10 +0300 Subject: [PATCH 04/16] feat(core): handling CTRL-C signal with graceful shutdown (#2213) --- Cargo.toml | 2 +- mm2src/mm2_bin_lib/src/lib.rs | 2 +- mm2src/mm2_core/src/mm_ctx.rs | 15 ++++++++++- mm2src/mm2_main/Cargo.toml | 2 +- mm2src/mm2_main/src/mm2.rs | 27 +++++++++++++++++-- .../src/rpc/lp_commands/lp_commands_legacy.rs | 19 +------------ 6 files changed, 43 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index deec6b843f..f6f7f67e61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ opt-level = 3 strip = true codegen-units = 1 # lto = true -panic = "abort" +panic = 'unwind' [profile.dev] opt-level = 0 diff --git a/mm2src/mm2_bin_lib/src/lib.rs b/mm2src/mm2_bin_lib/src/lib.rs index 81c532419f..c78233e64a 100644 --- a/mm2src/mm2_bin_lib/src/lib.rs +++ b/mm2src/mm2_bin_lib/src/lib.rs @@ -99,5 +99,5 @@ fn prepare_for_mm2_stop() -> PrepareForStopResult { async fn finalize_mm2_stop(ctx: MmArc) { dispatch_lp_event(ctx.clone(), StopCtxEvent.into()).await; - let _ = ctx.stop(); + let _ = ctx.stop().await; } diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index ec7ceb036a..0be8e66734 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -503,7 +503,10 @@ lazy_static! { impl MmArc { pub fn new(ctx: MmCtx) -> MmArc { MmArc(SharedRc::new(ctx)) } - pub fn stop(&self) -> Result<(), String> { + pub async fn stop(&self) -> Result<(), String> { + #[cfg(not(target_arch = "wasm32"))] + try_s!(self.close_async_connection().await); + try_s!(self.stop.pin(true)); // Notify shutdown listeners. @@ -517,6 +520,16 @@ impl MmArc { Ok(()) } + #[cfg(not(target_arch = "wasm32"))] + async fn close_async_connection(&self) -> Result<(), db_common::async_sql_conn::AsyncConnError> { + if let Some(async_conn) = self.async_sqlite_connection.as_option() { + let mut conn = async_conn.lock().await; + conn.close().await?; + } + + Ok(()) + } + #[cfg(feature = "track-ctx-pointer")] fn track_ctx_pointer(&self) { let ctx_weak = self.weak(); diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 60c3e9aa62..3013bce994 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -116,7 +116,7 @@ hyper = { version = "0.14.26", features = ["client", "http2", "server", "tcp"] } rcgen = "0.10" rustls = { version = "0.21", default-features = false } rustls-pemfile = "1.0.2" -tokio = { version = "1.20", features = ["io-util", "rt-multi-thread", "net"] } +tokio = { version = "1.20", features = ["io-util", "rt-multi-thread", "net", "signal"] } [target.'cfg(windows)'.dependencies] winapi = "0.3" diff --git a/mm2src/mm2_main/src/mm2.rs b/mm2src/mm2_main/src/mm2.rs index 4ecfb5706d..734e71becf 100644 --- a/mm2src/mm2_main/src/mm2.rs +++ b/mm2src/mm2_main/src/mm2.rs @@ -42,6 +42,7 @@ #[cfg(not(target_arch = "wasm32"))] use common::block_on; use common::crash_reports::init_crash_reports; +use common::log; use common::log::LogLevel; use common::password_policy::password_policy; use mm2_core::mm_ctx::MmCtxBuilder; @@ -53,7 +54,6 @@ use lp_swap::PAYMENT_LOCKTIME; use std::sync::atomic::Ordering; use gstuff::slurp; - use serde::ser::Serialize; use serde_json::{self as json, Value as Json}; @@ -63,7 +63,6 @@ use std::process::exit; use std::ptr::null; use std::str; -mod lp_native_dex; pub use self::lp_native_dex::init_hw; pub use self::lp_native_dex::lp_init; use coins::update_coins_config; @@ -74,6 +73,7 @@ use mm2_err_handle::prelude::*; pub mod heartbeat_event; pub mod lp_dispatcher; pub mod lp_message_service; +mod lp_native_dex; pub mod lp_network; pub mod lp_ordermatch; pub mod lp_stats; @@ -159,10 +159,33 @@ pub async fn lp_main( .with_datetime(datetime.clone()) .into_mm_arc(); ctx_cb(try_s!(ctx.ffi_handle())); + + #[cfg(not(target_arch = "wasm32"))] + spawn_ctrl_c_handler(ctx.clone()); + try_s!(lp_init(ctx, version, datetime).await); Ok(()) } +/// Handles CTRL-C signals and shutdowns the KDF runtime gracefully. +/// +/// It's important to spawn this task as soon as `Ctx` is in the correct state. +#[cfg(not(target_arch = "wasm32"))] +fn spawn_ctrl_c_handler(ctx: mm2_core::mm_ctx::MmArc) { + use crate::lp_dispatcher::{dispatch_lp_event, StopCtxEvent}; + + common::executor::spawn(async move { + tokio::signal::ctrl_c() + .await + .expect("Couldn't listen for the CTRL-C signal."); + + log::info!("Wrapping things up and shutting down..."); + + dispatch_lp_event(ctx.clone(), StopCtxEvent.into()).await; + ctx.stop().await.expect("Couldn't stop the KDF runtime."); + }); +} + fn help() { const HELP_MSG: &str = r#"Command-line options. The first command-line argument is special and designates the mode. diff --git a/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs b/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs index 7db7c5b02b..5ef386942c 100644 --- a/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs +++ b/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs @@ -21,7 +21,6 @@ use coins::{lp_coinfind, lp_coinfind_any, lp_coininit, CoinsContext, MmCoinEnum}; use common::executor::Timer; -use common::log::error; use common::{rpc_err_response, rpc_response, HyRes}; use futures::compat::Future01CompatExt; use http::Response; @@ -242,29 +241,13 @@ pub async fn my_balance(ctx: MmArc, req: Json) -> Result>, Stri Ok(try_s!(Response::builder().body(res))) } -#[cfg(not(target_arch = "wasm32"))] -async fn close_async_connection(ctx: &MmArc) { - if let Some(async_conn) = ctx.async_sqlite_connection.as_option() { - let mut conn = async_conn.lock().await; - if let Err(e) = conn.close().await { - error!("Error stopping AsyncConnection: {}", e); - } - } -} - pub async fn stop(ctx: MmArc) -> Result>, String> { dispatch_lp_event(ctx.clone(), StopCtxEvent.into()).await; // Should delay the shutdown a bit in order not to trip the "stop" RPC call in unit tests. // Stopping immediately leads to the "stop" RPC call failing with the "errno 10054" sometimes. let fut = async move { Timer::sleep(0.05).await; - - #[cfg(not(target_arch = "wasm32"))] - close_async_connection(&ctx).await; - - if let Err(e) = ctx.stop() { - error!("Error stopping MmCtx: {}", e); - } + ctx.stop().await.expect("Couldn't stop the KDF runtime."); }; // Please note we shouldn't use `MmCtx::spawner` to spawn this future, From cde7f365265a93ab2d8718a146eac498c2a46bce Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Mon, 23 Sep 2024 10:28:05 +0000 Subject: [PATCH 05/16] fix(clippy): fix coins mod clippy warnings in wasm (#2224) --- mm2src/coins/eth/eth_tests.rs | 58 +++++++--- mm2src/coins/lp_coins.rs | 4 +- .../coins/z_coin/storage/walletdb/wasm/mod.rs | 105 ++++++++++++++---- 3 files changed, 127 insertions(+), 40 deletions(-) diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index 9332594931..c20c12046e 100644 --- a/mm2src/coins/eth/eth_tests.rs +++ b/mm2src/coins/eth/eth_tests.rs @@ -1,27 +1,31 @@ use super::*; -use crate::eth::for_tests::{eth_coin_for_test, eth_coin_from_keypair}; -use crate::{DexFee, IguanaPrivKey}; -use common::{block_on, now_sec}; -#[cfg(not(target_arch = "wasm32"))] -use ethkey::{Generator, Random}; +use crate::IguanaPrivKey; +use common::block_on; use mm2_core::mm_ctx::MmCtxBuilder; -use mm2_test_helpers::for_tests::{ETH_MAINNET_CHAIN_ID, ETH_MAINNET_NODE, ETH_SEPOLIA_CHAIN_ID, ETH_SEPOLIA_NODES, + +cfg_native!( + use crate::eth::for_tests::{eth_coin_for_test, eth_coin_from_keypair}; + use crate::DexFee; + + use common::now_sec; + use ethkey::{Generator, Random}; + use mm2_test_helpers::for_tests::{ETH_MAINNET_CHAIN_ID, ETH_MAINNET_NODE, ETH_SEPOLIA_CHAIN_ID, ETH_SEPOLIA_NODES, ETH_SEPOLIA_TOKEN_CONTRACT}; -use mocktopus::mocking::*; - -/// The gas price for the tests -const GAS_PRICE: u64 = 50_000_000_000; -// `GAS_PRICE` increased by 3% -const GAS_PRICE_APPROXIMATION_ON_START_SWAP: u64 = 51_500_000_000; -// `GAS_PRICE` increased by 5% -const GAS_PRICE_APPROXIMATION_ON_ORDER_ISSUE: u64 = 52_500_000_000; -// `GAS_PRICE` increased by 7% -const GAS_PRICE_APPROXIMATION_ON_TRADE_PREIMAGE: u64 = 53_500_000_000; + use mocktopus::mocking::*; + + /// The gas price for the tests + const GAS_PRICE: u64 = 50_000_000_000; + /// `GAS_PRICE` increased by 3% + const GAS_PRICE_APPROXIMATION_ON_START_SWAP: u64 = 51_500_000_000; + /// `GAS_PRICE` increased by 5% + const GAS_PRICE_APPROXIMATION_ON_ORDER_ISSUE: u64 = 52_500_000_000; + /// `GAS_PRICE` increased by 7% + const GAS_PRICE_APPROXIMATION_ON_TRADE_PREIMAGE: u64 = 53_500_000_000; +); + // old way to add some extra gas to the returned value from gas station (non-existent now), still used in tests const GAS_PRICE_PERCENT: u64 = 10; -const TAKER_PAYMENT_SPEND_SEARCH_INTERVAL: f64 = 1.; - fn check_sum(addr: &str, expected: &str) { let actual = checksum_address(addr); assert_eq!(expected, actual); @@ -154,8 +158,11 @@ fn test_wei_from_big_decimal() { assert_eq!(expected_wei, wei); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_wait_for_payment_spend_timeout() { + const TAKER_PAYMENT_SPEND_SEARCH_INTERVAL: f64 = 1.; + EthCoin::spend_events.mock_safe(|_, _, _, _| MockResult::Return(Box::new(futures01::future::ok(vec![])))); EthCoin::current_block.mock_safe(|_| MockResult::Return(Box::new(futures01::future::ok(900)))); @@ -306,6 +313,7 @@ fn test_add_ten_pct_one_gwei() { assert_eq!(expected, actual); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn get_sender_trade_preimage() { /// Trade fee for the ETH coin is `2 * 150_000 * gas_price` always. @@ -361,6 +369,7 @@ fn get_sender_trade_preimage() { assert_eq!(actual, expected); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn get_erc20_sender_trade_preimage() { const APPROVE_GAS_LIMIT: u64 = 60_000; @@ -463,6 +472,7 @@ fn get_erc20_sender_trade_preimage() { ); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn get_receiver_trade_preimage() { EthCoin::get_gas_price.mock_safe(|_| MockResult::Return(Box::pin(futures::future::ok(GAS_PRICE.into())))); @@ -483,6 +493,7 @@ fn get_receiver_trade_preimage() { assert_eq!(actual, expected_fee); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_get_fee_to_send_taker_fee() { const DEX_FEE_AMOUNT: u64 = 100_000; @@ -533,6 +544,7 @@ fn test_get_fee_to_send_taker_fee() { /// /// Please note this test doesn't work correctly now, /// because as of now [`EthCoin::get_fee_to_send_taker_fee`] doesn't process the `Exception` web3 error correctly. +#[cfg(not(target_arch = "wasm32"))] #[test] #[ignore] fn test_get_fee_to_send_taker_fee_insufficient_balance() { @@ -562,6 +574,7 @@ fn test_get_fee_to_send_taker_fee_insufficient_balance() { ); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn validate_dex_fee_invalid_sender_eth() { let (_ctx, coin) = eth_coin_for_test(EthCoinType::Eth, &[ETH_MAINNET_NODE], None, ETH_MAINNET_CHAIN_ID); @@ -589,6 +602,7 @@ fn validate_dex_fee_invalid_sender_eth() { } } +#[cfg(not(target_arch = "wasm32"))] #[test] fn validate_dex_fee_invalid_sender_erc() { let (_ctx, coin) = eth_coin_for_test( @@ -624,6 +638,7 @@ fn validate_dex_fee_invalid_sender_erc() { } } +#[cfg(not(target_arch = "wasm32"))] fn sender_compressed_pub(tx: &SignedEthTx) -> [u8; 33] { let tx_pubkey = tx.public.unwrap(); let mut raw_pubkey = [0; 65]; @@ -633,6 +648,7 @@ fn sender_compressed_pub(tx: &SignedEthTx) -> [u8; 33] { secp_public.serialize() } +#[cfg(not(target_arch = "wasm32"))] #[test] fn validate_dex_fee_eth_confirmed_before_min_block() { let (_ctx, coin) = eth_coin_for_test(EthCoinType::Eth, &[ETH_MAINNET_NODE], None, ETH_MAINNET_CHAIN_ID); @@ -662,6 +678,7 @@ fn validate_dex_fee_eth_confirmed_before_min_block() { } } +#[cfg(not(target_arch = "wasm32"))] #[test] fn validate_dex_fee_erc_confirmed_before_min_block() { let (_ctx, coin) = eth_coin_for_test( @@ -700,6 +717,7 @@ fn validate_dex_fee_erc_confirmed_before_min_block() { } } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_negotiate_swap_contract_addr_no_fallback() { let (_, coin) = eth_coin_for_test(EthCoinType::Eth, &[ETH_MAINNET_NODE], None, ETH_MAINNET_CHAIN_ID); @@ -727,6 +745,7 @@ fn test_negotiate_swap_contract_addr_no_fallback() { assert_eq!(Some(slice.to_vec().into()), result); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_negotiate_swap_contract_addr_has_fallback() { let fallback = Address::from_str("0x8500AFc0bc5214728082163326C2FF0C73f4a871").unwrap(); @@ -824,6 +843,7 @@ fn polygon_check_if_my_payment_sent() { assert_eq!(expected_hash, my_payment.tx_hash_as_bytes()); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_message_hash() { let key_pair = Random.generate().unwrap(); @@ -842,6 +862,7 @@ fn test_message_hash() { ); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_sign_verify_message() { let key_pair = KeyPair::from_secret_slice( @@ -866,6 +887,7 @@ fn test_sign_verify_message() { assert!(is_valid); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_eth_extract_secret() { let key_pair = Random.generate().unwrap(); diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 1af6027e30..500963fba8 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -124,7 +124,7 @@ macro_rules! try_f { }; } -#[cfg(feature = "enable-solana")] +#[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] macro_rules! try_tx_fus_err { ($err: expr) => { return Box::new(futures01::future::err(crate::TransactionErr::Plain(ERRL!( @@ -133,7 +133,7 @@ macro_rules! try_tx_fus_err { }; } -#[cfg(feature = "enable-solana")] +#[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] macro_rules! try_tx_fus_opt { ($e: expr, $err: expr) => { match $e { diff --git a/mm2src/coins/z_coin/storage/walletdb/wasm/mod.rs b/mm2src/coins/z_coin/storage/walletdb/wasm/mod.rs index 907a007c55..4c68fec22c 100644 --- a/mm2src/coins/z_coin/storage/walletdb/wasm/mod.rs +++ b/mm2src/coins/z_coin/storage/walletdb/wasm/mod.rs @@ -253,7 +253,12 @@ mod wasm_test { // scan the cache let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -293,7 +298,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -347,7 +357,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -436,7 +451,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -520,7 +540,12 @@ mod wasm_test { // Scan the cache let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -545,7 +570,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -579,7 +609,12 @@ mod wasm_test { // Scan cache let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -592,7 +627,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); let scan = blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap_err(); match scan.get_inner() { @@ -611,7 +651,12 @@ mod wasm_test { blockdb.insert_block(cb2.height as u32, cb2_bytes).await.unwrap(); let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); assert!(blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None + ) .await .is_ok()); @@ -650,7 +695,12 @@ mod wasm_test { // Scan the cache let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); assert!(blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None + ) .await .is_ok()); @@ -666,7 +716,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); assert!(blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None + ) .await .is_ok()); @@ -703,7 +758,12 @@ mod wasm_test { // Scan the cache let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); assert!(blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None + ) .await .is_ok()); @@ -728,7 +788,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); let scan = blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await; assert!(scan.is_ok()); @@ -767,7 +832,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // assert!(blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .is_ok()); // @@ -787,7 +852,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // assert!(blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .is_ok()); // @@ -832,7 +897,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // assert!(blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .is_ok()); // @@ -863,7 +928,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // assert!(blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .is_ok()); // @@ -1033,7 +1098,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .unwrap(); // assert_eq!(walletdb.get_balance(AccountId(0)).await.unwrap(), value); @@ -1090,7 +1155,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .unwrap(); // @@ -1126,7 +1191,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .unwrap(); // From 4fc838838ee9aa5878bcedebc8be4dd168f4f3b5 Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Mon, 23 Sep 2024 14:32:59 +0000 Subject: [PATCH 06/16] fix(native-rpc): remove escaped response body (#2219) --- mm2src/mm2_main/Cargo.toml | 3 --- mm2src/mm2_main/src/rpc.rs | 50 ++------------------------------------ 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 3013bce994..8f84ebb90a 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -77,9 +77,6 @@ primitives = { path = "../mm2_bitcoin/primitives" } prost = "0.11" rand = { version = "0.7", features = ["std", "small_rng"] } rand6 = { version = "0.6", package = "rand" } -# TODO: Reduce the size of regex by disabling the features we don't use. -# cf. https://github.com/rust-lang/regex/issues/583 -regex = "1" rmp-serde = "0.14.3" rpc = { path = "../mm2_bitcoin/rpc" } rpc_task = { path = "../rpc_task" } diff --git a/mm2src/mm2_main/src/rpc.rs b/mm2src/mm2_main/src/rpc.rs index 2709d76f32..1bef856e15 100644 --- a/mm2src/mm2_main/src/rpc.rs +++ b/mm2src/mm2_main/src/rpc.rs @@ -28,14 +28,11 @@ use futures::future::{join_all, FutureExt}; use http::header::{HeaderValue, ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE}; use http::request::Parts; use http::{Method, Request, Response, StatusCode}; -use lazy_static::lazy_static; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_rpc::mm_protocol::{MmRpcBuilder, MmRpcResponse, MmRpcVersion}; -use regex::Regex; use serde::Serialize; use serde_json::{self as json, Value as Json}; -use std::borrow::Cow; use std::net::SocketAddr; cfg_native! { @@ -178,35 +175,6 @@ fn response_from_dispatcher_error( response.serialize_http_response() } -pub fn escape_answer<'a, S: Into>>(input: S) -> Cow<'a, str> { - lazy_static! { - static ref REGEX: Regex = Regex::new("[<>&]").unwrap(); - } - - let input = input.into(); - let mut last_match = 0; - - if REGEX.is_match(&input) { - let matches = REGEX.find_iter(&input); - let mut output = String::with_capacity(input.len()); - for mat in matches { - let (begin, end) = (mat.start(), mat.end()); - output.push_str(&input[last_match..begin]); - match &input[begin..end] { - "<" => output.push_str("<"), - ">" => output.push_str(">"), - "&" => output.push_str("&"), - _ => unreachable!(), - } - last_match = end; - } - output.push_str(&input[last_match..]); - Cow::Owned(output) - } else { - input - } -} - async fn process_single_request(ctx: MmArc, req: Json, client: SocketAddr) -> Result>, String> { let local_only = ctx.conf["rpc_local_only"].as_bool().unwrap_or(true); if req["mmrpc"].is_null() { @@ -314,23 +282,9 @@ async fn rpc_service(req: Request, ctx_h: u32, client: SocketAddr) -> Resp let res = try_sf!(process_rpc_request(ctx, req, req_json, client).await, ACCESS_CONTROL_ALLOW_ORIGIN => rpc_cors); let (mut parts, body) = res.into_parts(); - let body_escaped = { - let body_utf8 = match std::str::from_utf8(&body) { - Ok(body_utf8) => body_utf8, - Err(_) => { - return Response::builder() - .status(500) - .header(ACCESS_CONTROL_ALLOW_ORIGIN, rpc_cors) - .header(CONTENT_TYPE, APPLICATION_JSON) - .body(Body::from(err_to_rpc_json_string("Non UTF-8 output"))) - .unwrap(); - }, - }; - let escaped = escape_answer(body_utf8); - escaped.as_bytes().to_vec() - }; parts.headers.insert(ACCESS_CONTROL_ALLOW_ORIGIN, rpc_cors); - Response::from_parts(parts, Body::from(body_escaped)) + + Response::from_parts(parts, Body::from(body)) } // TODO: This should exclude TCP internals, as including them results in having to From 5c324f2cc5122bbc4a2a527941e908ccffcf6f2c Mon Sep 17 00:00:00 2001 From: Omer Yacine Date: Tue, 24 Sep 2024 11:56:02 +0300 Subject: [PATCH 07/16] chore(tests): don't use `.wait()` and use `block_on` instead (#2220) Fixes an issue regarding a tokio tcp stream that breaks because it was spawned in a non-tokio runtime. the `.wait` methods were using a different runtime - from futures - to run the future. This commit uses `block_on(fut.compat())` from KDF's tokio runtime instead. --- clippy.toml | 4 + mm2src/coins/eth/eth_tests.rs | 54 +- mm2src/coins/lightning/ln_platform.rs | 22 +- mm2src/coins/qrc20/qrc20_tests.rs | 201 +-- mm2src/coins/solana/solana_tests.rs | 6 +- mm2src/coins/tendermint/tendermint_coin.rs | 112 +- mm2src/coins/utxo/slp.rs | 5 +- mm2src/coins/utxo/utxo_tests.rs | 660 +++---- mm2src/coins/z_coin/z_coin_native_tests.rs | 25 +- mm2src/common/common.rs | 13 + mm2src/mm2_err_handle/src/map_to_mm_fut.rs | 2 +- mm2src/mm2_err_handle/src/mm_error.rs | 7 +- .../tests/docker_tests/docker_tests_common.rs | 75 +- .../tests/docker_tests/docker_tests_inner.rs | 51 +- .../tests/docker_tests/eth_docker_tests.rs | 35 +- .../tests/docker_tests/qrc20_tests.rs | 236 +-- .../tests/docker_tests/swap_proto_v2_tests.rs | 9 +- .../tests/docker_tests/swap_watcher_tests.rs | 1580 ++++++++--------- 18 files changed, 1446 insertions(+), 1651 deletions(-) create mode 100644 clippy.toml diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000000..068d7e886b --- /dev/null +++ b/clippy.toml @@ -0,0 +1,4 @@ +[[disallowed-methods]] +path = "futures::future::Future::wait" +replacement = "common::block_on_f01" +reason = "Use the default KDF async executor." \ No newline at end of file diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index c20c12046e..03705ff4ca 100644 --- a/mm2src/coins/eth/eth_tests.rs +++ b/mm2src/coins/eth/eth_tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::IguanaPrivKey; -use common::block_on; +use common::{block_on, block_on_f01}; use mm2_core::mm_ctx::MmCtxBuilder; cfg_native!( @@ -191,18 +191,16 @@ fn test_wait_for_payment_spend_timeout() { 184, 42, 106, ]; - assert!(coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &tx_bytes, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &coin.swap_contract_address(), - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false - }) - .wait() - .is_err()); + assert!(block_on_f01(coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &tx_bytes, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &coin.swap_contract_address(), + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false + })) + .is_err()); } #[cfg(not(target_arch = "wasm32"))] @@ -229,7 +227,7 @@ fn test_withdraw_impl_manual_fee() { memo: None, ibc_source_channel: None, }; - coin.get_balance().wait().unwrap(); + block_on_f01(coin.get_balance()).unwrap(); let tx_details = block_on(withdraw_impl(coin, withdraw_req)).unwrap(); let expected = Some( @@ -278,7 +276,7 @@ fn test_withdraw_impl_fee_details() { memo: None, ibc_source_channel: None, }; - coin.get_balance().wait().unwrap(); + block_on_f01(coin.get_balance()).unwrap(); let tx_details = block_on(withdraw_impl(coin, withdraw_req)).unwrap(); let expected = Some( @@ -486,10 +484,8 @@ fn get_receiver_trade_preimage() { paid_from_trading_vol: false, }; - let actual = coin - .get_receiver_trade_fee(FeeApproxStage::WithoutApprox) - .wait() - .expect("!get_sender_trade_fee"); + let actual = + block_on_f01(coin.get_receiver_trade_fee(FeeApproxStage::WithoutApprox)).expect("!get_sender_trade_fee"); assert_eq!(actual, expected_fee); } @@ -595,7 +591,9 @@ fn validate_dex_fee_invalid_sender_eth() { min_block_number: 0, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("was sent from wrong address")), _ => panic!("Expected `WrongPaymentTx` wrong sender address, found {:?}", error), @@ -631,7 +629,9 @@ fn validate_dex_fee_invalid_sender_erc() { min_block_number: 0, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("was sent from wrong address")), _ => panic!("Expected `WrongPaymentTx` wrong sender address, found {:?}", error), @@ -671,7 +671,9 @@ fn validate_dex_fee_eth_confirmed_before_min_block() { min_block_number: 11784793, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min_block")), _ => panic!("Expected `WrongPaymentTx` early confirmation, found {:?}", error), @@ -710,7 +712,9 @@ fn validate_dex_fee_erc_confirmed_before_min_block() { min_block_number: 11823975, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min_block")), _ => panic!("Expected `WrongPaymentTx` early confirmation, found {:?}", error), @@ -834,9 +838,7 @@ fn polygon_check_if_my_payment_sent() { amount: &BigDecimal::default(), payment_instructions: &None, }; - let my_payment = coin - .check_if_my_payment_sent(if_my_payment_sent_args) - .wait() + let my_payment = block_on_f01(coin.check_if_my_payment_sent(if_my_payment_sent_args)) .unwrap() .unwrap(); let expected_hash = BytesJson::from("69a20008cea0c15ee483b5bbdff942752634aa072dfd2ff715fe87eec302de11"); diff --git a/mm2src/coins/lightning/ln_platform.rs b/mm2src/coins/lightning/ln_platform.rs index 4d0573af95..0a5fae0a4f 100644 --- a/mm2src/coins/lightning/ln_platform.rs +++ b/mm2src/coins/lightning/ln_platform.rs @@ -15,7 +15,7 @@ use bitcoin::hash_types::{BlockHash, TxMerkleNode, Txid}; use bitcoin_hashes::{sha256d, Hash}; use common::executor::{abortable_queue::AbortableQueue, AbortableSystem, SpawnFuture, Timer}; use common::log::{debug, error, info}; -use common::wait_until_sec; +use common::{block_on_f01, wait_until_sec}; use futures::compat::Future01CompatExt; use futures::future::join_all; use keys::hash::H256; @@ -570,17 +570,15 @@ impl FeeEstimator for Platform { ConfirmationTarget::HighPriority => self.confirmations_targets.high_priority, }; let fee_per_kb = tokio::task::block_in_place(move || { - self.rpc_client() - .estimate_fee_sat( - platform_coin.decimals(), - // Todo: when implementing Native client detect_fee_method should be used for Native and - // EstimateFeeMethod::Standard for Electrum - &EstimateFeeMethod::Standard, - &conf.estimate_fee_mode, - n_blocks, - ) - .wait() - .unwrap_or(latest_fees) + block_on_f01(self.rpc_client().estimate_fee_sat( + platform_coin.decimals(), + // Todo: when implementing Native client detect_fee_method should be used for Native and + // EstimateFeeMethod::Standard for Electrum + &EstimateFeeMethod::Standard, + &conf.estimate_fee_mode, + n_blocks, + )) + .unwrap_or(latest_fees) }); // Set default fee to last known fee for the corresponding confirmation target diff --git a/mm2src/coins/qrc20/qrc20_tests.rs b/mm2src/coins/qrc20/qrc20_tests.rs index 2caf87c3bf..d77afa2f9b 100644 --- a/mm2src/coins/qrc20/qrc20_tests.rs +++ b/mm2src/coins/qrc20/qrc20_tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::{DexFee, TxFeeDetails, WaitForHTLCTxSpendArgs}; -use common::{block_on, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{block_on, block_on_f01, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::Secp256k1Secret; use itertools::Itertools; use keys::Address; @@ -96,7 +96,7 @@ fn test_withdraw_to_p2sh_address_should_fail() { memo: None, ibc_source_channel: None, }; - let err = coin.withdraw(req).wait().unwrap_err().into_inner(); + let err = block_on_f01(coin.withdraw(req)).unwrap_err().into_inner(); let expect = WithdrawError::InvalidAddress("QRC20 can be sent to P2PKH addresses only".to_owned()); assert_eq!(err, expect); } @@ -112,19 +112,22 @@ fn test_withdraw_impl_fee_details() { let (_ctx, coin) = qrc20_coin_for_test(priv_key, None); Qrc20Coin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let withdraw_req = WithdrawRequest { @@ -140,7 +143,7 @@ fn test_withdraw_impl_fee_details() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); let expected: Qrc20FeeDetails = json::from_value(json!({ "coin": "QTUM", @@ -287,7 +290,7 @@ fn test_wait_for_confirmations_excepted() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); // tx_hash: ed53b97deb2ad76974c972cb084f6ba63bd9f16c91c4a39106a20c6d14599b2a // `erc20Payment` contract call excepted @@ -299,7 +302,7 @@ fn test_wait_for_confirmations_excepted() { wait_until, check_every, }; - let error = coin.wait_for_confirmations(confirm_payment_input).wait().unwrap_err(); + let error = block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap_err(); log!("error: {:?}", error); assert!(error.contains("Contract call failed with an error: Revert")); @@ -313,7 +316,7 @@ fn test_wait_for_confirmations_excepted() { wait_until, check_every, }; - let error = coin.wait_for_confirmations(confirm_payment_input).wait().unwrap_err(); + let error = block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap_err(); log!("error: {:?}", error); assert!(error.contains("Contract call failed with an error: Revert")); } @@ -333,67 +336,59 @@ fn test_validate_fee() { let amount = BigDecimal::from_str("0.01").unwrap(); - let result = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &sender_pub, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(amount.clone().into()), - min_block_number: 0, - uuid: &[], - }) - .wait(); + let result = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &sender_pub, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(amount.clone().into()), + min_block_number: 0, + uuid: &[], + })); assert!(result.is_ok()); let fee_addr_dif = hex::decode("03bc2c7ba671bae4a6fc835244c9762b41647b9827d4780a89a949b984a8ddcc05").unwrap(); - let err = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &sender_pub, - fee_addr: &fee_addr_dif, - dex_fee: &DexFee::Standard(amount.clone().into()), - min_block_number: 0, - uuid: &[], - }) - .wait() - .expect_err("Expected an error") - .into_inner(); + let err = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &sender_pub, + fee_addr: &fee_addr_dif, + dex_fee: &DexFee::Standard(amount.clone().into()), + min_block_number: 0, + uuid: &[], + })) + .expect_err("Expected an error") + .into_inner(); log!("error: {:?}", err); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("QRC20 Fee tx was sent to wrong address")), _ => panic!("Expected `WrongPaymentTx` wrong receiver address, found {:?}", err), } - let err = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(amount.clone().into()), - min_block_number: 0, - uuid: &[], - }) - .wait() - .expect_err("Expected an error") - .into_inner(); + let err = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(amount.clone().into()), + min_block_number: 0, + uuid: &[], + })) + .expect_err("Expected an error") + .into_inner(); log!("error: {:?}", err); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("was sent from wrong address")), _ => panic!("Expected `WrongPaymentTx` wrong sender address, found {:?}", err), } - let err = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &sender_pub, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(amount.clone().into()), - min_block_number: 2000000, - uuid: &[], - }) - .wait() - .expect_err("Expected an error") - .into_inner(); + let err = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &sender_pub, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(amount.clone().into()), + min_block_number: 2000000, + uuid: &[], + })) + .expect_err("Expected an error") + .into_inner(); log!("error: {:?}", err); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min_block")), @@ -401,18 +396,16 @@ fn test_validate_fee() { } let amount_dif = BigDecimal::from_str("0.02").unwrap(); - let err = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &sender_pub, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(amount_dif.into()), - min_block_number: 0, - uuid: &[], - }) - .wait() - .expect_err("Expected an error") - .into_inner(); + let err = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &sender_pub, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(amount_dif.into()), + min_block_number: 0, + uuid: &[], + })) + .expect_err("Expected an error") + .into_inner(); log!("error: {:?}", err); match err { ValidatePaymentError::WrongPaymentTx(err) => { @@ -424,18 +417,16 @@ fn test_validate_fee() { // QTUM tx "8a51f0ffd45f34974de50f07c5bf2f0949da4e88433f8f75191953a442cf9310" let tx = TransactionEnum::UtxoTx("020000000113640281c9332caeddd02a8dd0d784809e1ad87bda3c972d89d5ae41f5494b85010000006a47304402207c5c904a93310b8672f4ecdbab356b65dd869a426e92f1064a567be7ccfc61ff02203e4173b9467127f7de4682513a21efb5980e66dbed4da91dff46534b8e77c7ef012102baefe72b3591de2070c0da3853226b00f082d72daa417688b61cb18c1d543d1afeffffff020001b2c4000000001976a9149e032d4b0090a11dc40fe6c47601499a35d55fbb88acbc4dd20c2f0000001976a9144208fa7be80dcf972f767194ad365950495064a488ac76e70800".into()); let sender_pub = hex::decode("02baefe72b3591de2070c0da3853226b00f082d72daa417688b61cb18c1d543d1a").unwrap(); - let err = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &sender_pub, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(amount.into()), - min_block_number: 0, - uuid: &[], - }) - .wait() - .expect_err("Expected an error") - .into_inner(); + let err = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &sender_pub, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(amount.into()), + min_block_number: 0, + uuid: &[], + })) + .expect_err("Expected an error") + .into_inner(); log!("error: {:?}", err); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Expected 'transfer' contract call")), @@ -462,18 +453,16 @@ fn test_wait_for_tx_spend_malicious() { let payment_tx = hex::decode("01000000016601daa208531d20532c460d0c86b74a275f4a126bbffcf4eafdf33835af2859010000006a47304402205825657548bc1b5acf3f4bb2f89635a02b04f3228cd08126e63c5834888e7ac402207ca05fa0a629a31908a97a508e15076e925f8e621b155312b7526a6666b06a76012103693bff1b39e8b5a306810023c29b95397eb395530b106b1820ea235fd81d9ce9ffffffff020000000000000000e35403a0860101284cc49b415b2a8620ad3b72361a5aeba5dffd333fb64750089d935a1ec974d6a91ef4f24ff6ba0000000000000000000000000000000000000000000000000000000001312d00000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc44000000000000000000000000783cf0be521101942da509846ea476e683aad8324b6b2e5444c2639cc0fb7bcea5afba3f3cdce239000000000000000000000000000000000000000000000000000000000000000000000000000000005f855c7614ba8b71f3544b93e2f681f996da519a98ace0107ac2203de400000000001976a9149e032d4b0090a11dc40fe6c47601499a35d55fbb88ac415d855f").unwrap(); let wait_until = now_sec() + 1; let from_block = 696245; - let found = coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &payment_tx, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &coin.swap_contract_address(), - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false, - }) - .wait() - .unwrap(); + let found = block_on_f01(coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &payment_tx, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &coin.swap_contract_address(), + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false, + })) + .unwrap(); let spend_tx = match found { TransactionEnum::UtxoTx(tx) => tx, @@ -731,7 +720,7 @@ fn test_get_trade_fee() { // check if the coin's tx fee is expected check_tx_fee(&coin, ActualTxFee::FixedPerKb(EXPECTED_TX_FEE as u64)); - let actual_trade_fee = coin.get_trade_fee().wait().unwrap(); + let actual_trade_fee = block_on_f01(coin.get_trade_fee()).unwrap(); let expected_trade_fee_amount = big_decimal_from_sat( 2 * CONTRACT_CALL_GAS_FEE + SWAP_PAYMENT_GAS_FEE + EXPECTED_TX_FEE, coin.utxo.decimals, @@ -905,10 +894,8 @@ fn test_receiver_trade_preimage() { // check if the coin's tx fee is expected check_tx_fee(&coin, ActualTxFee::FixedPerKb(EXPECTED_TX_FEE as u64)); - let actual = coin - .get_receiver_trade_fee(FeeApproxStage::WithoutApprox) - .wait() - .expect("!get_receiver_trade_fee"); + let actual = + block_on_f01(coin.get_receiver_trade_fee(FeeApproxStage::WithoutApprox)).expect("!get_receiver_trade_fee"); // only one contract call should be included into the expected trade fee let expected_receiver_fee = big_decimal_from_sat(CONTRACT_CALL_GAS_FEE + EXPECTED_TX_FEE, coin.utxo.decimals); let expected = TradeFee { @@ -935,7 +922,7 @@ fn test_taker_fee_tx_fee() { spendable: BigDecimal::from(5u32), unspendable: BigDecimal::from(0u32), }; - assert_eq!(coin.my_balance().wait().expect("!my_balance"), expected_balance); + assert_eq!(block_on_f01(coin.my_balance()).expect("!my_balance"), expected_balance); let dex_fee_amount = BigDecimal::from(5u32); let actual = block_on(coin.get_fee_to_send_taker_fee( diff --git a/mm2src/coins/solana/solana_tests.rs b/mm2src/coins/solana/solana_tests.rs index fe2b104293..d4126f35c0 100644 --- a/mm2src/coins/solana/solana_tests.rs +++ b/mm2src/coins/solana/solana_tests.rs @@ -1,5 +1,5 @@ use base58::ToBase58; -use common::{block_on, Future01CompatExt}; +use common::{block_on, block_on_f01, Future01CompatExt}; use rpc::v1::types::Bytes; use solana_client::rpc_request::TokenAccountsFilter; use solana_sdk::{bs58, @@ -367,7 +367,7 @@ fn solana_coin_send_and_refund_maker_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(args)).unwrap(); log!("swap tx {:?}", tx); let refund_args = RefundPaymentArgs { @@ -415,7 +415,7 @@ fn solana_coin_send_and_spend_maker_payment() { wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); log!("swap tx {:?}", tx); let maker_pub = taker_pub; diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index bc5e12f6ff..9c7a1c1d96 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -3313,7 +3313,7 @@ fn parse_expected_sequence_number(e: &str) -> MmResult { @@ -3725,18 +3723,16 @@ pub mod tendermint_coin_tests { data: TxRaw::decode(random_transfer_tx_bytes.as_slice()).unwrap(), }); - let error = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &random_transfer_tx, - expected_sender: &[], - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(invalid_amount.clone()), - min_block_number: 0, - uuid: &[1; 16], - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &random_transfer_tx, + expected_sender: &[], + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(invalid_amount.clone()), + min_block_number: 0, + uuid: &[1; 16], + })) + .unwrap_err() + .into_inner(); println!("{}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("sent to wrong address")), @@ -3758,18 +3754,16 @@ pub mod tendermint_coin_tests { data: TxRaw::decode(dex_fee_tx.encode_to_vec().as_slice()).unwrap(), }); - let error = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &dex_fee_tx, - expected_sender: &[], - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(invalid_amount), - min_block_number: 0, - uuid: &[1; 16], - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &dex_fee_tx, + expected_sender: &[], + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(invalid_amount), + min_block_number: 0, + uuid: &[1; 16], + })) + .unwrap_err() + .into_inner(); println!("{}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Invalid amount")), @@ -3778,18 +3772,16 @@ pub mod tendermint_coin_tests { let valid_amount: BigDecimal = "0.0001".parse().unwrap(); // valid amount but invalid sender - let error = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &dex_fee_tx, - expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(valid_amount.clone().into()), - min_block_number: 0, - uuid: &[1; 16], - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &dex_fee_tx, + expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(valid_amount.clone().into()), + min_block_number: 0, + uuid: &[1; 16], + })) + .unwrap_err() + .into_inner(); println!("{}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Invalid sender")), @@ -3797,18 +3789,16 @@ pub mod tendermint_coin_tests { } // invalid memo - let error = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &dex_fee_tx, - expected_sender: &pubkey, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(valid_amount.into()), - min_block_number: 0, - uuid: &[1; 16], - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &dex_fee_tx, + expected_sender: &pubkey, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(valid_amount.into()), + min_block_number: 0, + uuid: &[1; 16], + })) + .unwrap_err() + .into_inner(); println!("{}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Invalid memo")), diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index c559449c22..503611445e 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1945,6 +1945,7 @@ mod slp_tests { use crate::utxo::GetUtxoListOps; use crate::{utxo::bch::tbch_coin_for_test, TransactionErr}; use common::block_on; + use common::block_on_f01; use mocktopus::mocking::{MockResult, Mockable}; use std::mem::discriminant; @@ -2206,11 +2207,11 @@ mod slp_tests { ]; let tx_bytes_str = hex::encode(tx_bytes); - let err = fusd.send_raw_tx(&tx_bytes_str).wait().unwrap_err(); + let err = block_on_f01(fusd.send_raw_tx(&tx_bytes_str)).unwrap_err(); println!("{:?}", err); assert!(err.contains("is not valid with reason outputs greater than inputs")); - let err2 = fusd.send_raw_tx_bytes(tx_bytes).wait().unwrap_err(); + let err2 = block_on_f01(fusd.send_raw_tx_bytes(tx_bytes)).unwrap_err(); println!("{:?}", err2); assert!(err2.contains("is not valid with reason outputs greater than inputs")); assert_eq!(err, err2); diff --git a/mm2src/coins/utxo/utxo_tests.rs b/mm2src/coins/utxo/utxo_tests.rs index 868f558f1f..9456361c64 100644 --- a/mm2src/coins/utxo/utxo_tests.rs +++ b/mm2src/coins/utxo/utxo_tests.rs @@ -34,13 +34,12 @@ use crate::{BlockHeightAndTime, CoinBalance, ConfirmPaymentInput, DexFee, Iguana use crate::{WaitForHTLCTxSpendArgs, WithdrawFee}; use chain::{BlockHeader, BlockHeaderBits, OutPoint}; use common::executor::Timer; -use common::{block_on, wait_until_sec, OrdRange, PagingOptionsEnum, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{block_on, block_on_f01, wait_until_sec, OrdRange, PagingOptionsEnum, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::{privkey::key_pair_from_seed, Bip44Chain, HDPathToAccount, RpcDerivationPath, Secp256k1Secret}; #[cfg(not(target_arch = "wasm32"))] use db_common::sqlite::rusqlite::Connection; use futures::channel::mpsc::channel; -use futures::future::join_all; -use futures::TryFutureExt; +use futures::future::{join_all, Either, FutureExt, TryFutureExt}; use keys::prefixes::*; use mm2_core::mm_ctx::MmCtxBuilder; use mm2_number::bigdecimal::{BigDecimal, Signed}; @@ -438,18 +437,16 @@ fn test_wait_for_payment_spend_timeout_native() { let wait_until = now_sec() - 1; let from_block = 1000; - assert!(coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &transaction, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &None, - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false - }) - .wait() - .is_err()); + assert!(block_on_f01(coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &transaction, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &None, + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false + })) + .is_err()); assert!(unsafe { OUTPUT_SPEND_CALLED }); } @@ -486,18 +483,16 @@ fn test_wait_for_payment_spend_timeout_electrum() { let wait_until = now_sec() - 1; let from_block = 1000; - assert!(coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &transaction, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &None, - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false - }) - .wait() - .is_err()); + assert!(block_on_f01(coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &transaction, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &None, + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false + })) + .is_err()); assert!(unsafe { OUTPUT_SPEND_CALLED }); } @@ -575,19 +570,22 @@ fn test_search_for_swap_tx_spend_electrum_was_refunded() { #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_set_fixed_fee() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -613,7 +611,7 @@ fn test_withdraw_impl_set_fixed_fee() { } .into(), ); - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(expected, tx_details.fee_details); } @@ -621,19 +619,22 @@ fn test_withdraw_impl_set_fixed_fee() { #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_sat_per_kb_fee() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -662,7 +663,7 @@ fn test_withdraw_impl_sat_per_kb_fee() { } .into(), ); - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(expected, tx_details.fee_details); } @@ -670,19 +671,22 @@ fn test_withdraw_impl_sat_per_kb_fee() { #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -701,7 +705,7 @@ fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); // The resulting transaction size might be 210 or 211 bytes depending on signature size // MM2 always expects the worst case during fee calculation // 0.1 * 211 / 1000 = 0.0211 @@ -721,19 +725,22 @@ fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max() { #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max_dust_included_to_fee() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -752,7 +759,7 @@ fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max_dust_included_to_fee() memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); // The resulting transaction size might be 210 or 211 bytes depending on signature size // MM2 always expects the worst case during fee calculation // 0.1 * 211 / 1000 = 0.0211 @@ -772,19 +779,22 @@ fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max_dust_included_to_fee() #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_sat_per_kb_fee_amount_over_max() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -803,26 +813,29 @@ fn test_withdraw_impl_sat_per_kb_fee_amount_over_max() { memo: None, ibc_source_channel: None, }; - coin.withdraw(withdraw_req).wait().unwrap_err(); + block_on_f01(coin.withdraw(withdraw_req)).unwrap_err(); } #[test] #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_sat_per_kb_fee_max() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -851,7 +864,7 @@ fn test_withdraw_impl_sat_per_kb_fee_max() { } .into(), ); - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(expected, tx_details.fee_details); } @@ -866,20 +879,23 @@ fn test_withdraw_kmd_rewards_impl( let verbose: RpcTransaction = json::from_str(verbose_serialized).unwrap(); let unspent_height = verbose.height; UtxoStandardCoin::get_unspent_ordered_list.mock_safe(move |coin: &UtxoStandardCoin, _| { - let tx: UtxoTx = tx_hex.into(); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: tx.hash(), - index: 0, - }, - value: tx.outputs[0].value, - height: unspent_height, - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let tx: UtxoTx = tx_hex.into(); + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: tx.hash(), + index: 0, + }, + value: tx.outputs[0].value, + height: unspent_height, + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); UtxoStandardCoin::get_current_mtp .mock_safe(move |_fields| MockResult::Return(Box::pin(futures::future::ok(current_mtp)))); @@ -909,7 +925,7 @@ fn test_withdraw_kmd_rewards_impl( coin: Some("KMD".into()), amount: "0.00001".parse().unwrap(), }); - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(tx_details.fee_details, Some(expected_fee)); let expected_rewards = expected_rewards.map(|amount| KmdRewardsDetails { @@ -958,20 +974,23 @@ fn test_withdraw_rick_rewards_none() { const TX_HEX: &str = "0400008085202f8901df8119c507aa61d32332cd246dbfeb3818a4f96e76492454c1fbba5aa097977e000000004847304402205a7e229ea6929c97fd6dde254c19e4eb890a90353249721701ae7a1c477d99c402206a8b7c5bf42b5095585731d6b4c589ce557f63c20aed69ff242eca22ecfcdc7a01feffffff02d04d1bffbc050000232102afdbba3e3c90db5f0f4064118f79cf308f926c68afd64ea7afc930975663e4c4ac402dd913000000001976a9143e17014eca06281ee600adffa34b4afb0922a22288ac2bdab86035a00e000000000000000000000000"; UtxoStandardCoin::get_unspent_ordered_list.mock_safe(move |coin, _| { - let tx: UtxoTx = TX_HEX.into(); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: tx.hash(), - index: 0, - }, - value: tx.outputs[0].value, - height: Some(1431628), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let tx: UtxoTx = TX_HEX.into(); + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: tx.hash(), + index: 0, + }, + value: tx.outputs[0].value, + height: Some(1431628), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let withdraw_req = WithdrawRequest { @@ -988,7 +1007,7 @@ fn test_withdraw_rick_rewards_none() { coin: Some(TEST_COIN_NAME.into()), amount: "0.00001".parse().unwrap(), }); - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(tx_details.fee_details, Some(expected_fee)); assert_eq!(tx_details.kmd_rewards, None); } @@ -1066,7 +1085,7 @@ fn test_electrum_rpc_client_error() { let client = electrum_client_for_test(&["electrum1.cipig.net:10060"]); let empty_hash = H256Json::default(); - let err = client.get_verbose_transaction(&empty_hash).wait().unwrap_err(); + let err = block_on_f01(client.get_verbose_transaction(&empty_hash)).unwrap_err(); // use the static string instead because the actual error message cannot be obtain // by serde_json serialization @@ -1305,10 +1324,7 @@ fn test_get_median_time_past_from_electrum_kmd() { "electrum3.cipig.net:10001", ]); - let mtp = client - .get_median_time_past(1773390, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard) - .wait() - .unwrap(); + let mtp = block_on_f01(client.get_median_time_past(1773390, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard)).unwrap(); // the MTP is block time of 1773385 in this case assert_eq!(1583159915, mtp); } @@ -1321,10 +1337,7 @@ fn test_get_median_time_past_from_electrum_btc() { "electrum3.cipig.net:10000", ]); - let mtp = client - .get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard) - .wait() - .unwrap(); + let mtp = block_on_f01(client.get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard)).unwrap(); assert_eq!(1591173041, mtp); } @@ -1348,10 +1361,7 @@ fn test_get_median_time_past_from_native_has_median_in_get_block() { ) }); - let mtp = client - .get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard) - .wait() - .unwrap(); + let mtp = block_on_f01(client.get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard)).unwrap(); assert_eq!(1591173041, mtp); } @@ -1394,10 +1404,7 @@ fn test_get_median_time_past_from_native_does_not_have_median_in_get_block() { MockResult::Return(Box::new(futures01::future::ok(block))) }); - let mtp = client - .get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard) - .wait() - .unwrap(); + let mtp = block_on_f01(client.get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard)).unwrap(); assert_eq!(1591173041, mtp); } @@ -1597,13 +1604,11 @@ fn test_spam_rick() { fn test_one_unavailable_electrum_proto_version() { // check if the electrum-mona.bitbank.cc:50001 doesn't support the protocol version 1.4 let client = electrum_client_for_test(&["electrum-mona.bitbank.cc:50001"]); - let result = client - .server_version( - "electrum-mona.bitbank.cc:50001", - "AtomicDEX", - &OrdRange::new(1.4, 1.4).unwrap(), - ) - .wait(); + let result = block_on_f01(client.server_version( + "electrum-mona.bitbank.cc:50001", + "AtomicDEX", + &OrdRange::new(1.4, 1.4).unwrap(), + )); assert!(result .err() .unwrap() @@ -1628,7 +1633,7 @@ fn test_one_unavailable_electrum_proto_version() { block_on(async { Timer::sleep(0.5).await }); - assert!(coin.as_ref().rpc_client.get_block_count().wait().is_ok()); + assert!(block_on_f01(coin.as_ref().rpc_client.get_block_count()).is_ok()); } #[test] @@ -1685,7 +1690,7 @@ fn test_qtum_add_delegation() { address: address.to_string(), fee: Some(10), }; - let res = coin.add_delegation(request).wait().unwrap(); + let res = block_on_f01(coin.add_delegation(request)).unwrap(); // Eligible for delegation assert!(res.my_balance_change.is_negative()); assert_eq!(res.total_amount, res.spent_by_me); @@ -1695,7 +1700,7 @@ fn test_qtum_add_delegation() { address: "fake_address".to_string(), fee: Some(10), }; - let res = coin.add_delegation(request).wait(); + let res = block_on_f01(coin.add_delegation(request)); // Wrong address assert!(res.is_err()); } @@ -1728,7 +1733,7 @@ fn test_qtum_add_delegation_on_already_delegating() { address: address.to_string(), fee: Some(10), }; - let res = coin.add_delegation(request).wait(); + let res = block_on_f01(coin.add_delegation(request)); // Already Delegating assert!(res.is_err()); } @@ -1754,7 +1759,7 @@ fn test_qtum_get_delegation_infos() { keypair.private().secret, )) .unwrap(); - let staking_infos = coin.get_delegation_infos().wait().unwrap(); + let staking_infos = block_on_f01(coin.get_delegation_infos()).unwrap(); match staking_infos.staking_infos_details { StakingInfosDetails::Qtum(staking_details) => { assert!(staking_details.am_i_staking); @@ -1784,49 +1789,49 @@ fn test_qtum_remove_delegation() { keypair.private().secret, )) .unwrap(); - let res = coin.remove_delegation().wait(); + let res = block_on_f01(coin.remove_delegation()); assert!(res.is_ok()); } #[test] fn test_qtum_my_balance() { QtumCoin::get_mature_unspent_ordered_list.mock_safe(move |coin, _address| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - // spendable balance (66.0) - let mature = vec![ - UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + // spendable balance (66.0) + let mature = vec![ + UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 5000000000, + height: Default::default(), + script: Vec::new().into(), }, - value: 5000000000, - height: Default::default(), - script: Vec::new().into(), - }, - UnspentInfo { + UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1600000000, + height: Default::default(), + script: Vec::new().into(), + }, + ]; + // unspendable (2.0) + let immature = vec![UnspentInfo { outpoint: OutPoint { hash: 1.into(), index: 0, }, - value: 1600000000, + value: 200000000, height: Default::default(), script: Vec::new().into(), - }, - ]; - // unspendable (2.0) - let immature = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 200000000, - height: Default::default(), - script: Vec::new().into(), - }]; - MockResult::Return(Box::pin(futures::future::ok(( - MatureUnspentList { mature, immature }, - cache, - )))) + }]; + Ok((MatureUnspentList { mature, immature }, cache)) + }; + MockResult::Return(fut.boxed()) }); let conf = json!({"coin":"tQTUM","rpcport":13889,"pubtype":120,"p2shtype":110}); @@ -1845,7 +1850,7 @@ fn test_qtum_my_balance() { let params = UtxoActivationParams::from_legacy_req(&req).unwrap(); let coin = block_on(qtum_coin_with_priv_key(&ctx, "tQTUM", &conf, ¶ms, priv_key)).unwrap(); - let CoinBalance { spendable, unspendable } = coin.my_balance().wait().unwrap(); + let CoinBalance { spendable, unspendable } = block_on_f01(coin.my_balance()).unwrap(); let expected_spendable = BigDecimal::from(66); let expected_unspendable = BigDecimal::from(2); assert_eq!(spendable, expected_spendable); @@ -1881,7 +1886,7 @@ fn test_qtum_my_balance_with_check_utxo_maturity_false() { let params = UtxoActivationParams::from_legacy_req(&req).unwrap(); let coin = block_on(qtum_coin_with_priv_key(&ctx, "tQTUM", &conf, ¶ms, priv_key)).unwrap(); - let CoinBalance { spendable, unspendable } = coin.my_balance().wait().unwrap(); + let CoinBalance { spendable, unspendable } = block_on_f01(coin.my_balance()).unwrap(); let expected_spendable = BigDecimal::from(DISPLAY_BALANCE); let expected_unspendable = BigDecimal::from(0); assert_eq!(spendable, expected_spendable); @@ -1899,7 +1904,7 @@ fn test_get_mature_unspent_ordered_map_from_cache_impl( const TX_HASH: &str = "b43f9ed47f7b97d4766b6f1614136fa0c55b9a52c97342428333521fa13ad714"; let tx_hash: H256Json = hex::decode(TX_HASH).unwrap().as_slice().into(); let client = electrum_client_for_test(DOC_ELECTRUM_ADDRS); - let mut verbose = client.get_verbose_transaction(&tx_hash).wait().unwrap(); + let mut verbose = block_on_f01(client.get_verbose_transaction(&tx_hash)).unwrap(); verbose.confirmations = cached_confs; verbose.height = cached_height; @@ -2517,15 +2522,13 @@ fn test_find_output_spend_skips_conflicting_transactions() { let tx: UtxoTx = "0400008085202f89027f57730fcbbc2c72fb18bcc3766a713044831a117bb1cade3ed88644864f7333020000006a47304402206e3737b2fcf078b61b16fa67340cc3e79c5d5e2dc9ffda09608371552a3887450220460a332aa1b8ad8f2de92d319666f70751078b221199951f80265b4f7cef8543012102d8c948c6af848c588517288168faa397d6ba3ea924596d03d1d84f224b5123c2ffffffff42b916a80430b80a77e114445b08cf120735447a524de10742fac8f6a9d4170f000000006a473044022004aa053edafb9d161ea8146e0c21ed1593aa6b9404dd44294bcdf920a1695fd902202365eac15dbcc5e9f83e2eed56a8f2f0e5aded36206f9c3fabc668fd4665fa2d012102d8c948c6af848c588517288168faa397d6ba3ea924596d03d1d84f224b5123c2ffffffff03547b16000000000017a9143e8ad0e2bf573d32cb0b3d3a304d9ebcd0c2023b870000000000000000166a144e2b3c0323ab3c2dc6f86dc5ec0729f11e42f56103970400000000001976a91450f4f098306f988d8843004689fae28c83ef16e888ac89c5925f000000000000000000000000000000".into(); let vout = 0; let from_block = 0; - let actual = client - .find_output_spend( - tx.hash(), - &tx.outputs[vout].script_pubkey, - vout, - BlockHashOrHeight::Height(from_block), - TxHashAlgo::DSHA256, - ) - .wait(); + let actual = block_on_f01(client.find_output_spend( + tx.hash(), + &tx.outputs[vout].script_pubkey, + vout, + BlockHashOrHeight::Height(from_block), + TxHashAlgo::DSHA256, + )); assert_eq!(actual, Ok(None)); assert_eq!(unsafe { GET_RAW_TRANSACTION_BYTES_CALLED }, 1); } @@ -2609,7 +2612,7 @@ fn test_get_sender_trade_fee_dynamic_tx_fee() { ); coin_fields.tx_fee = TxFee::Dynamic(EstimateFeeMethod::Standard); let coin = utxo_coin_from_fields(coin_fields); - let my_balance = coin.my_spendable_balance().wait().expect("!my_balance"); + let my_balance = block_on_f01(coin.my_spendable_balance()).expect("!my_balance"); let expected_balance = BigDecimal::from_str("2.22222").expect("!BigDecimal::from_str"); assert_eq!(my_balance, expected_balance); @@ -2657,7 +2660,9 @@ fn test_validate_fee_wrong_sender() { min_block_number: 0, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains(INVALID_SENDER_ERR_LOG)), @@ -2682,7 +2687,9 @@ fn test_validate_fee_min_block() { min_block_number: 278455, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min_block")), _ => panic!("Expected `WrongPaymentTx` early confirmation, found {:?}", error), @@ -2711,7 +2718,7 @@ fn test_validate_fee_bch_70_bytes_signature() { min_block_number: 0, uuid: &[], }; - coin.validate_fee(validate_fee_args).wait().unwrap(); + block_on_f01(coin.validate_fee(validate_fee_args)).unwrap(); } #[test] @@ -2766,7 +2773,7 @@ fn firo_lelantus_tx() { "electrumx02.firo.org:50001", "electrumx03.firo.org:50001", ]); - let _tx = electrum.get_verbose_transaction(&tx_hash).wait().unwrap(); + let _tx = block_on_f01(electrum.get_verbose_transaction(&tx_hash)).unwrap(); } #[test] @@ -2903,9 +2910,7 @@ fn doge_mtp() { "electrum2.cipig.net:10060", "electrum3.cipig.net:10060", ]); - let mtp = electrum - .get_median_time_past(3631820, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(3631820, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1614849084); } @@ -2917,9 +2922,7 @@ fn firo_mtp() { "electrumx02.firo.org:50001", "electrumx03.firo.org:50001", ]); - let mtp = electrum - .get_median_time_past(356730, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(356730, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1616492629); } @@ -2927,9 +2930,7 @@ fn firo_mtp() { #[test] fn verus_mtp() { let electrum = electrum_client_for_test(&["el0.verus.io:17485", "el1.verus.io:17485", "el2.verus.io:17485"]); - let mtp = electrum - .get_median_time_past(1480113, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(1480113, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1618579909); } @@ -2941,9 +2942,7 @@ fn sys_mtp() { "electrum2.cipig.net:10064", "electrum3.cipig.net:10064", ]); - let mtp = electrum - .get_median_time_past(1006678, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(1006678, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1620019628); } @@ -2955,9 +2954,7 @@ fn btc_mtp() { "electrum2.cipig.net:10000", "electrum3.cipig.net:10000", ]); - let mtp = electrum - .get_median_time_past(681659, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(681659, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1620019527); } @@ -2969,9 +2966,7 @@ fn rvn_mtp() { "electrum2.cipig.net:10051", "electrum3.cipig.net:10051", ]); - let mtp = electrum - .get_median_time_past(1968120, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(1968120, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1633946264); } @@ -2983,10 +2978,8 @@ fn qtum_mtp() { "electrum2.cipig.net:10050", "electrum3.cipig.net:10050", ]); - let mtp = electrum - .get_median_time_past(681659, NonZeroU64::new(11).unwrap(), CoinVariant::Qtum) - .wait() - .unwrap(); + let mtp = + block_on_f01(electrum.get_median_time_past(681659, NonZeroU64::new(11).unwrap(), CoinVariant::Qtum)).unwrap(); assert_eq!(mtp, 1598854128); } @@ -2997,9 +2990,7 @@ fn zer_mtp() { "electrum2.cipig.net:10065", "electrum3.cipig.net:10065", ]); - let mtp = electrum - .get_median_time_past(1130915, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(1130915, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1623240214); } @@ -3196,7 +3187,7 @@ fn test_withdraw_to_p2pk_fails() { }; assert!(matches!( - coin.withdraw(withdraw_req).wait().unwrap_err().into_inner(), + block_on_f01(coin.withdraw(withdraw_req)).unwrap_err().into_inner(), WithdrawError::InvalidAddress(..) )) } @@ -3209,19 +3200,22 @@ fn test_withdraw_to_p2pkh() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Native(client), None, false); UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); // Create a p2pkh address for the test coin @@ -3249,7 +3243,7 @@ fn test_withdraw_to_p2pkh() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); let transaction: UtxoTx = deserialize(tx_details.tx.tx_hex().unwrap().as_slice()).unwrap(); let output_script: Script = transaction.outputs[0].script_pubkey.clone().into(); @@ -3266,19 +3260,22 @@ fn test_withdraw_to_p2sh() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Native(client), None, false); UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); // Create a p2sh address for the test coin @@ -3306,7 +3303,7 @@ fn test_withdraw_to_p2sh() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); let transaction: UtxoTx = deserialize(tx_details.tx.tx_hex().unwrap().as_slice()).unwrap(); let output_script: Script = transaction.outputs[0].script_pubkey.clone().into(); @@ -3323,19 +3320,22 @@ fn test_withdraw_to_p2wpkh() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Native(client), None, true); UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); // Create a p2wpkh address for the test coin @@ -3363,7 +3363,7 @@ fn test_withdraw_to_p2wpkh() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); let transaction: UtxoTx = deserialize(tx_details.tx.tx_hex().unwrap().as_slice()).unwrap(); let output_script: Script = transaction.outputs[0].script_pubkey.clone().into(); @@ -3380,22 +3380,29 @@ fn test_withdraw_p2pk_balance() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Native(client), None, false); UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - // Use a p2pk output script for this UTXO - script: output_script_p2pk( - &block_on(coin.as_ref().derivation_method.unwrap_single_addr()) - .pubkey() - .unwrap(), - ), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + // Use a p2pk output script for this UTXO + script: output_script_p2pk( + &coin + .as_ref() + .derivation_method + .unwrap_single_addr() + .await + .pubkey() + .unwrap(), + ), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); // Create a dummy p2pkh address to withdraw the coins to. @@ -3411,7 +3418,7 @@ fn test_withdraw_p2pk_balance() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); let transaction: UtxoTx = deserialize(tx_details.tx.tx_hex().unwrap().as_slice()).unwrap(); // The change should be in a p2pkh script. @@ -3432,8 +3439,11 @@ fn test_utxo_standard_with_check_utxo_maturity_true() { UtxoStandardCoin::get_mature_unspent_ordered_list.mock_safe(|coin, _| { unsafe { GET_MATURE_UNSPENT_ORDERED_LIST_CALLED = true }; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - MockResult::Return(Box::pin(futures::future::ok((MatureUnspentList::default(), cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + Ok((MatureUnspentList::default(), cache)) + }; + MockResult::Return(fut.boxed()) }); let conf = json!({"coin":"RICK","asset":"RICK","rpcport":25435,"txversion":4,"overwintered":1,"mm2":1,"protocol":{"type":"UTXO"}}); @@ -3451,7 +3461,7 @@ fn test_utxo_standard_with_check_utxo_maturity_true() { let address = Address::from_legacyaddress("R9o9xTocqr6CeEDGDH6mEYpwLoMz6jNjMW", &KMD_PREFIXES).unwrap(); // Don't use `block_on` here because it's used within a mock of [`GetUtxoListOps::get_mature_unspent_ordered_list`]. - coin.get_unspent_ordered_list(&address).compat().wait().unwrap(); + block_on_f01(coin.get_unspent_ordered_list(&address).compat()).unwrap(); assert!(unsafe { GET_MATURE_UNSPENT_ORDERED_LIST_CALLED }); } @@ -3464,9 +3474,11 @@ fn test_utxo_standard_without_check_utxo_maturity() { UtxoStandardCoin::get_all_unspent_ordered_list.mock_safe(|coin, _| { unsafe { GET_ALL_UNSPENT_ORDERED_LIST_CALLED = true }; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = Vec::new(); - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + Ok((Vec::new(), cache)) + }; + MockResult::Return(fut.boxed()) }); UtxoStandardCoin::get_mature_unspent_ordered_list.mock_safe(|_, _| { @@ -3487,7 +3499,7 @@ fn test_utxo_standard_without_check_utxo_maturity() { let address = Address::from_legacyaddress("R9o9xTocqr6CeEDGDH6mEYpwLoMz6jNjMW", &KMD_PREFIXES).unwrap(); // Don't use `block_on` here because it's used within a mock of [`UtxoStandardCoin::get_all_unspent_ordered_list`]. - coin.get_unspent_ordered_list(&address).compat().wait().unwrap(); + block_on_f01(coin.get_unspent_ordered_list(&address).compat()).unwrap(); assert!(unsafe { GET_ALL_UNSPENT_ORDERED_LIST_CALLED }); } @@ -3500,8 +3512,11 @@ fn test_qtum_without_check_utxo_maturity() { QtumCoin::get_mature_unspent_ordered_list.mock_safe(|coin, _| { unsafe { GET_MATURE_UNSPENT_ORDERED_LIST_CALLED = true }; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - MockResult::Return(Box::pin(futures::future::ok((MatureUnspentList::default(), cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + Ok((MatureUnspentList::default(), cache)) + }; + MockResult::Return(fut.boxed()) }); let conf = json!({"coin":"tQTUM","rpcport":13889,"pubtype":120,"p2shtype":110}); @@ -3526,7 +3541,7 @@ fn test_qtum_without_check_utxo_maturity() { ) .unwrap(); // Don't use `block_on` here because it's used within a mock of [`QtumCoin::get_mature_unspent_ordered_list`]. - coin.get_unspent_ordered_list(&address).compat().wait().unwrap(); + block_on_f01(coin.get_unspent_ordered_list(&address).compat()).unwrap(); assert!(unsafe { GET_MATURE_UNSPENT_ORDERED_LIST_CALLED }); } @@ -3604,9 +3619,12 @@ fn test_qtum_with_check_utxo_maturity_false() { QtumCoin::get_all_unspent_ordered_list.mock_safe(|coin, _address| { unsafe { GET_ALL_UNSPENT_ORDERED_LIST_CALLED = true }; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = Vec::new(); - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = Vec::new(); + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); QtumCoin::get_mature_unspent_ordered_list.mock_safe(|_, _| { panic!( @@ -3637,7 +3655,7 @@ fn test_qtum_with_check_utxo_maturity_false() { ) .unwrap(); // Don't use `block_on` here because it's used within a mock of [`QtumCoin::get_all_unspent_ordered_list`]. - coin.get_unspent_ordered_list(&address).compat().wait().unwrap(); + block_on_f01(coin.get_unspent_ordered_list(&address).compat()).unwrap(); assert!(unsafe { GET_ALL_UNSPENT_ORDERED_LIST_CALLED }); } @@ -4365,7 +4383,9 @@ fn test_for_non_existent_tx_hex_utxo_electrum() { wait_until: timeout, check_every: 1, }; - let actual = coin.wait_for_confirmations(confirm_payment_input).wait().err().unwrap(); + let actual = block_on_f01(coin.wait_for_confirmations(confirm_payment_input)) + .err() + .unwrap(); assert!(actual.contains( "Tx d342ff9da528a2e262bddf2b6f9a27d1beb7aeb03f0fc8d9eac2987266447e44 was not found on chain after 10 tries" )); @@ -4408,10 +4428,7 @@ fn test_native_display_balances() { Address::from_legacyaddress("RJeDDtDRtKUoL8BCKdH7TNCHqUKr7kQRsi", &KMD_PREFIXES).unwrap(), Address::from_legacyaddress("RQHn9VPHBqNjYwyKfJbZCiaxVrWPKGQjeF", &KMD_PREFIXES).unwrap(), ]; - let actual = rpc_client - .display_balances(addresses, TEST_COIN_DECIMALS) - .wait() - .unwrap(); + let actual = block_on_f01(rpc_client.display_balances(addresses, TEST_COIN_DECIMALS)).unwrap(); let expected: Vec<(Address, BigDecimal)> = vec![ ( @@ -4549,7 +4566,6 @@ fn test_utxo_validate_valid_and_invalid_pubkey() { #[test] fn test_block_header_utxo_loop() { use crate::utxo::utxo_builder::{block_header_utxo_loop, BlockHeaderUtxoLoopExtraArgs}; - use futures::future::{Either, FutureExt}; use keys::hash::H256 as H256Json; static mut CURRENT_BLOCK_COUNT: u64 = 13; diff --git a/mm2src/coins/z_coin/z_coin_native_tests.rs b/mm2src/coins/z_coin/z_coin_native_tests.rs index 9e4358727d..790f42818f 100644 --- a/mm2src/coins/z_coin/z_coin_native_tests.rs +++ b/mm2src/coins/z_coin/z_coin_native_tests.rs @@ -1,14 +1,13 @@ use bitcrypto::dhash160; -use common::{block_on, now_sec}; +use common::{block_on, block_on_f01, now_sec}; use mm2_core::mm_ctx::MmCtxBuilder; use mm2_test_helpers::for_tests::zombie_conf; use std::path::PathBuf; use std::time::Duration; use zcash_client_backend::encoding::decode_extended_spending_key; -use super::{z_coin_from_conf_and_params_with_z_key, z_mainnet_constants, Future, PrivKeyBuildPolicy, - RefundPaymentArgs, SendPaymentArgs, SpendPaymentArgs, SwapOps, ValidateFeeArgs, ValidatePaymentError, - ZTransaction}; +use super::{z_coin_from_conf_and_params_with_z_key, z_mainnet_constants, PrivKeyBuildPolicy, RefundPaymentArgs, + SendPaymentArgs, SpendPaymentArgs, SwapOps, ValidateFeeArgs, ValidatePaymentError, ZTransaction}; use crate::z_coin::{z_htlc::z_send_dex_fee, ZcoinActivationParams, ZcoinRpcMode}; use crate::DexFee; use crate::{CoinProtocol, SwapTxTypeWithSecretHash}; @@ -55,7 +54,7 @@ fn zombie_coin_send_and_refund_maker_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(args)).unwrap(); log!("swap tx {}", hex::encode(tx.tx_hash_as_bytes().0)); let refund_args = RefundPaymentArgs { @@ -116,7 +115,7 @@ fn zombie_coin_send_and_spend_maker_payment() { wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); log!("swap tx {}", hex::encode(tx.tx_hash_as_bytes().0)); let maker_pub = taker_pub; @@ -234,7 +233,9 @@ fn zombie_coin_validate_dex_fee() { uuid: &[1; 16], }; // Invalid amount should return an error - let err = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let err = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Dex fee has invalid amount")), _ => panic!("Expected `WrongPaymentTx`: {:?}", err), @@ -249,7 +250,9 @@ fn zombie_coin_validate_dex_fee() { min_block_number: 12000, uuid: &[2; 16], }; - let err = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let err = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Dex fee has invalid memo")), _ => panic!("Expected `WrongPaymentTx`: {:?}", err), @@ -264,7 +267,9 @@ fn zombie_coin_validate_dex_fee() { min_block_number: 14000, uuid: &[1; 16], }; - let err = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let err = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min block")), _ => panic!("Expected `WrongPaymentTx`: {:?}", err), @@ -279,7 +284,7 @@ fn zombie_coin_validate_dex_fee() { min_block_number: 12000, uuid: &[1; 16], }; - coin.validate_fee(validate_fee_args).wait().unwrap(); + block_on_f01(coin.validate_fee(validate_fee_args)).unwrap(); } fn default_zcoin_activation_params() -> ZcoinActivationParams { diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index 0eeb726ad4..f665a9a8e0 100644 --- a/mm2src/common/common.rs +++ b/mm2src/common/common.rs @@ -621,7 +621,20 @@ pub fn var(name: &str) -> Result { #[cfg(target_arch = "wasm32")] pub fn var(_name: &str) -> Result { ERR!("Environment variable not supported in WASM") } +/// Runs the given future on MM2's executor and waits for the result. +/// +/// This is compatible with futures 0.1. +pub fn block_on_f01(f: F) -> Result +where + F: Future, +{ + block_on(f.compat()) +} + #[cfg(not(target_arch = "wasm32"))] +/// Runs the given future on MM2's executor and waits for the result. +/// +/// This is compatible with futures 0.3. pub fn block_on(f: F) -> F::Output where F: Future03, diff --git a/mm2src/mm2_err_handle/src/map_to_mm_fut.rs b/mm2src/mm2_err_handle/src/map_to_mm_fut.rs index 01de1b7d7e..0cfeb9cf13 100644 --- a/mm2src/mm2_err_handle/src/map_to_mm_fut.rs +++ b/mm2src/mm2_err_handle/src/map_to_mm_fut.rs @@ -21,7 +21,7 @@ where /// /// ```rust /// let fut = futures01::future::err("An error".to_owned()); - /// let mapped_res: Result<(), MmError> = fut.map_to_mm_fut(|e| e.len()).wait(); + /// let mapped_res: Result<(), MmError> = block_on_f01(fut.map_to_mm_fut(|e| e.len())); /// ``` #[track_caller] fn map_to_mm_fut(self, f: F) -> MapToMmFuture<'a, T, E1, E2> diff --git a/mm2src/mm2_err_handle/src/mm_error.rs b/mm2src/mm2_err_handle/src/mm_error.rs index 56e7f29f50..1ea7bf1555 100644 --- a/mm2src/mm2_err_handle/src/mm_error.rs +++ b/mm2src/mm2_err_handle/src/mm_error.rs @@ -332,6 +332,7 @@ impl FormattedTrace for Vec { mod tests { use super::*; use crate::prelude::*; + use common::block_on_f01; use futures01::Future; use ser_error_derive::SerializeErrorType; use serde_json::{self as json, json}; @@ -425,10 +426,8 @@ mod tests { } let into_mm_line = line!() + 2; - let mm_err = generate_error("An error") - .map_to_mm_fut(|error| error.len()) - .wait() - .expect_err("Expected an error"); + let mm_err = + block_on_f01(generate_error("An error").map_to_mm_fut(|error| error.len())).expect_err("Expected an error"); assert_eq!(mm_err.etype, 8); assert_eq!(mm_err.trace, vec![TraceLocation::new("mm_error", into_mm_line)]); } diff --git a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs index fe5d7f96d4..49150fab55 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs @@ -1,4 +1,4 @@ -pub use common::{block_on, now_ms, now_sec, wait_until_ms, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; +pub use common::{block_on, block_on_f01, now_ms, now_sec, wait_until_ms, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; pub use mm2_number::MmNumber; use mm2_rpc::data::legacy::BalanceResponse; pub use mm2_test_helpers::for_tests::{check_my_swap_status, check_recent_swaps, enable_eth_coin, enable_native, @@ -27,7 +27,6 @@ use crypto::Secp256k1Secret; use ethabi::Token; use ethereum_types::{H160 as H160Eth, U256}; use futures::TryFutureExt; -use futures01::Future; use http::StatusCode; use keys::{Address, AddressBuilder, AddressHashEnum, AddressPrefix, KeyPair, NetworkAddressPrefixes, NetworkPrefix as CashAddrPrefix}; @@ -155,13 +154,13 @@ pub trait CoinDockerOps { fn wait_ready(&self, expected_tx_version: i32) { let timeout = wait_until_ms(120000); loop { - match self.rpc_client().get_block_count().wait() { + match block_on_f01(self.rpc_client().get_block_count()) { Ok(n) => { if n > 1 { if let UtxoRpcClientEnum::Native(client) = self.rpc_client() { - let hash = client.get_block_hash(n).wait().unwrap(); - let block = client.get_block(hash).wait().unwrap(); - let coinbase = client.get_verbose_transaction(&block.tx[0]).wait().unwrap(); + let hash = block_on_f01(client.get_block_hash(n)).unwrap(); + let block = block_on_f01(client.get_block(hash)).unwrap(); + let coinbase = block_on_f01(client.get_verbose_transaction(&block.tx[0])).unwrap(); log!("Coinbase tx {:?} in block {}", coinbase, n); if coinbase.version == expected_tx_version { break; @@ -251,10 +250,11 @@ impl BchDockerOps { .build() .expect("valid address props"); - self.native_client() - .import_address(&address.to_string(), &address.to_string(), false) - .wait() - .unwrap(); + block_on_f01( + self.native_client() + .import_address(&address.to_string(), &address.to_string(), false), + ) + .unwrap(); let script_pubkey = Builder::build_p2pkh(&key_pair.public().address_hash().into()); @@ -270,9 +270,7 @@ impl BchDockerOps { slp_privkeys.push(*key_pair.private_ref()); } - let slp_genesis_tx = send_outputs_from_my_address(self.coin.clone(), bch_outputs) - .wait() - .unwrap(); + let slp_genesis_tx = block_on_f01(send_outputs_from_my_address(self.coin.clone(), bch_outputs)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: slp_genesis_tx.tx_hex(), confirmations: 1, @@ -280,7 +278,7 @@ impl BchDockerOps { wait_until: wait_until_sec(30), check_every: 1, }; - self.coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(self.coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let adex_slp = SlpToken::new( 8, @@ -299,7 +297,7 @@ impl BchDockerOps { wait_until: wait_until_sec(30), check_every: 1, }; - self.coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(self.coin.wait_for_confirmations(confirm_payment_input)).unwrap(); *SLP_TOKEN_OWNERS.lock().unwrap() = slp_privkeys; *SLP_TOKEN_ID.lock().unwrap() = slp_genesis_tx.tx_hash_as_bytes().as_slice().into(); } @@ -466,7 +464,7 @@ where match coin.as_ref().rpc_client { UtxoRpcClientEnum::Native(ref native) => { let my_address = coin.my_address().unwrap(); - native.import_address(&my_address, &my_address, false).wait().unwrap() + block_on_f01(native.import_address(&my_address, &my_address, false)).unwrap() }, UtxoRpcClientEnum::Electrum(_) => panic!("Expected NativeClient"), } @@ -586,9 +584,7 @@ where UtxoRpcClientEnum::Native(ref native) => native, UtxoRpcClientEnum::Electrum(_) => panic!("NativeClient expected"), }; - let mut addresses = native - .get_addresses_by_label(label) - .wait() + let mut addresses = block_on_f01(native.get_addresses_by_label(label)) .expect("!getaddressesbylabel") .into_iter(); match addresses.next() { @@ -610,22 +606,20 @@ pub fn fill_qrc20_address(coin: &Qrc20Coin, amount: BigDecimal, timeout: u64) { }; let from_addr = get_address_by_label(coin, QTUM_ADDRESS_LABEL); - let to_addr = coin.my_addr_as_contract_addr().compat().wait().unwrap(); + let to_addr = block_on_f01(coin.my_addr_as_contract_addr().compat()).unwrap(); let satoshis = sat_from_big_decimal(&amount, coin.as_ref().decimals).expect("!sat_from_big_decimal"); - let hash = client - .transfer_tokens( - &coin.contract_address, - &from_addr, - to_addr, - satoshis.into(), - coin.as_ref().decimals, - ) - .wait() - .expect("!transfer_tokens") - .txid; + let hash = block_on_f01(client.transfer_tokens( + &coin.contract_address, + &from_addr, + to_addr, + satoshis.into(), + coin.as_ref().decimals, + )) + .expect("!transfer_tokens") + .txid; - let tx_bytes = client.get_transaction_bytes(&hash).wait().unwrap(); + let tx_bytes = block_on_f01(client.get_transaction_bytes(&hash)).unwrap(); log!("{:02x}", tx_bytes); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx_bytes.0, @@ -634,7 +628,7 @@ pub fn fill_qrc20_address(coin: &Qrc20Coin, amount: BigDecimal, timeout: u64) { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); } /// Generate random privkey, create a QRC20 coin and fill it's address with the specified balance. @@ -742,9 +736,9 @@ where let timeout = wait_until_sec(timeout); if let UtxoRpcClientEnum::Native(client) = &coin.as_ref().rpc_client { - client.import_address(address, address, false).wait().unwrap(); - let hash = client.send_to_address(address, &amount).wait().unwrap(); - let tx_bytes = client.get_transaction_bytes(&hash).wait().unwrap(); + block_on_f01(client.import_address(address, address, false)).unwrap(); + let hash = block_on_f01(client.send_to_address(address, &amount)).unwrap(); + let tx_bytes = block_on_f01(client.get_transaction_bytes(&hash)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx_bytes.clone().0, confirmations: 1, @@ -752,13 +746,10 @@ where wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); log!("{:02x}", tx_bytes); loop { - let unspents = client - .list_unspent_impl(0, std::i32::MAX, vec![address.to_string()]) - .wait() - .unwrap(); + let unspents = block_on_f01(client.list_unspent_impl(0, std::i32::MAX, vec![address.to_string()])).unwrap(); if !unspents.is_empty() { break; } @@ -794,7 +785,7 @@ pub fn wait_for_estimate_smart_fee(timeout: u64) -> Result<(), String> { UtxoRpcClientEnum::Electrum(_) => panic!("Expected NativeClient"), }; while now_sec() < timeout { - if let Ok(res) = client.estimate_smart_fee(&None, 1).wait() { + if let Ok(res) = block_on_f01(client.estimate_smart_fee(&None, 1)) { if res.errors.is_empty() { *state = EstimateSmartFeeState::Ok; return Ok(()); diff --git a/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs index 330dec30de..fd9e4eefb6 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs @@ -12,10 +12,9 @@ use coins::TxFeeDetails; use coins::{ConfirmPaymentInput, FoundSwapTxSpend, MarketCoinOps, MmCoin, RefundPaymentArgs, SearchForSwapTxSpendInput, SendPaymentArgs, SpendPaymentArgs, SwapOps, SwapTxTypeWithSecretHash, TransactionEnum, WithdrawRequest}; -use common::{block_on, executor::Timer, get_utc_timestamp, now_sec, wait_until_sec}; +use common::{block_on, block_on_f01, executor::Timer, get_utc_timestamp, now_sec, wait_until_sec}; use crypto::privkey::key_pair_from_seed; use crypto::{CryptoCtx, DerivationPath, KeyPairPolicy}; -use futures01::Future; use http::StatusCode; use mm2_number::{BigDecimal, BigRational, MmNumber}; use mm2_test_helpers::for_tests::{check_my_swap_status_amounts, disable_coin, disable_coin_err, enable_eth_coin, @@ -51,7 +50,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_taker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_taker_payment(taker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_taker_payment(taker_payment_args)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -60,7 +59,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &tx.tx_hex(), time_lock, @@ -81,7 +80,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -113,7 +112,9 @@ fn test_for_non_existent_tx_hex_utxo() { wait_until: timeout, check_every: 1, }; - let actual = coin.wait_for_confirmations(confirm_payment_input).wait().err().unwrap(); + let actual = block_on_f01(coin.wait_for_confirmations(confirm_payment_input)) + .err() + .unwrap(); assert!(actual.contains( "Tx d342ff9da528a2e262bddf2b6f9a27d1beb7aeb03f0fc8d9eac2987266447e44 was not found on chain after 10 tries" )); @@ -138,7 +139,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_maker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -147,7 +148,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &tx.tx_hex(), time_lock, @@ -168,7 +169,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -207,7 +208,7 @@ fn test_search_for_taker_swap_tx_spend_native_was_spent_by_maker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_taker_payment(taker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_taker_payment(taker_payment_args)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -216,7 +217,7 @@ fn test_search_for_taker_swap_tx_spend_native_was_spent_by_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_spends_payment_args = SpendPaymentArgs { other_payment_tx: &tx.tx_hex(), time_lock, @@ -236,7 +237,7 @@ fn test_search_for_taker_swap_tx_spend_native_was_spent_by_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -275,7 +276,7 @@ fn test_search_for_maker_swap_tx_spend_native_was_spent_by_taker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -284,7 +285,7 @@ fn test_search_for_maker_swap_tx_spend_native_was_spent_by_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let taker_spends_payment_args = SpendPaymentArgs { other_payment_tx: &tx.tx_hex(), time_lock, @@ -304,7 +305,7 @@ fn test_search_for_maker_swap_tx_spend_native_was_spent_by_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -346,7 +347,7 @@ fn test_one_hundred_maker_payments_in_a_row_native() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); if let TransactionEnum::UtxoTx(tx) = tx { unspents.push(UnspentInfo { outpoint: OutPoint { @@ -2472,16 +2473,12 @@ fn test_maker_order_should_not_kick_start_and_appear_in_orderbook_if_balance_is_ bob_conf["log"] = mm_bob.folder.join("mm2_dup.log").to_str().unwrap().into(); block_on(mm_bob.stop()).unwrap(); - let withdraw = coin - .withdraw(WithdrawRequest::new_max( - "MYCOIN".to_string(), - "RRYmiZSDo3UdHHqj1rLKf8cbJroyv9NxXw".to_string(), - )) - .wait() - .unwrap(); - coin.send_raw_tx(&hex::encode(&withdraw.tx.tx_hex().unwrap().0)) - .wait() - .unwrap(); + let withdraw = block_on_f01(coin.withdraw(WithdrawRequest::new_max( + "MYCOIN".to_string(), + "RRYmiZSDo3UdHHqj1rLKf8cbJroyv9NxXw".to_string(), + ))) + .unwrap(); + block_on_f01(coin.send_raw_tx(&hex::encode(&withdraw.tx.tx_hex().unwrap().0))).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: withdraw.tx.tx_hex().unwrap().0.to_owned(), confirmations: 1, @@ -2489,7 +2486,7 @@ fn test_maker_order_should_not_kick_start_and_appear_in_orderbook_if_balance_is_ wait_until: wait_until_sec(10), check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let mm_bob_dup = MarketMakerIt::start(bob_conf, "pass".to_string(), None).unwrap(); let (_bob_dup_dump_log, _bob_dup_dump_dashboard) = mm_dump(&mm_bob_dup.log_path); diff --git a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs index 3a898b0f2f..cc44cb5af1 100644 --- a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs @@ -19,10 +19,9 @@ use coins::{lp_coinfind, CoinProtocol, CoinWithDerivationMethod, CoinsContext, C SendNftMakerPaymentArgs, SendPaymentArgs, SendTakerFundingArgs, SpendNftMakerPaymentArgs, SpendPaymentArgs, SwapOps, SwapTxFeePolicy, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, ToBytes, Transaction, TxPreimageWithSig, ValidateNftMakerPaymentArgs, ValidateTakerFundingArgs}; -use common::{block_on, now_sec}; +use common::{block_on, block_on_f01, now_sec}; use crypto::Secp256k1Secret; use ethereum_types::U256; -use futures01::Future; use mm2_core::mm_ctx::MmArc; use mm2_number::{BigDecimal, BigUint}; use mm2_test_helpers::for_tests::{erc20_dev_conf, eth_dev_conf, eth_sepolia_conf, nft_dev_conf, sepolia_erc20_dev_conf}; @@ -540,7 +539,7 @@ fn send_and_refund_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { watcher_reward: None, wait_for_confirmation_until: 0, }; - let eth_maker_payment = eth_coin.send_maker_payment(send_payment_args).wait().unwrap(); + let eth_maker_payment = block_on_f01(eth_coin.send_maker_payment(send_payment_args)).unwrap(); let confirm_input = ConfirmPaymentInput { payment_tx: eth_maker_payment.tx_hex(), @@ -549,7 +548,7 @@ fn send_and_refund_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - eth_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(eth_coin.wait_for_confirmations(confirm_input)).unwrap(); let refund_args = RefundPaymentArgs { payment_tx: ð_maker_payment.tx_hex(), @@ -572,7 +571,7 @@ fn send_and_refund_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - eth_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(eth_coin.wait_for_confirmations(confirm_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -626,7 +625,7 @@ fn send_and_spend_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { watcher_reward: None, wait_for_confirmation_until: 0, }; - let eth_maker_payment = maker_eth_coin.send_maker_payment(send_payment_args).wait().unwrap(); + let eth_maker_payment = block_on_f01(maker_eth_coin.send_maker_payment(send_payment_args)).unwrap(); let confirm_input = ConfirmPaymentInput { payment_tx: eth_maker_payment.tx_hex(), @@ -635,7 +634,7 @@ fn send_and_spend_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - taker_eth_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(taker_eth_coin.wait_for_confirmations(confirm_input)).unwrap(); let spend_args = SpendPaymentArgs { other_payment_tx: ð_maker_payment.tx_hex(), @@ -657,7 +656,7 @@ fn send_and_spend_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - taker_eth_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(taker_eth_coin.wait_for_confirmations(confirm_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -709,7 +708,7 @@ fn send_and_refund_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) watcher_reward: None, wait_for_confirmation_until: now_sec() + 60, }; - let eth_maker_payment = erc20_coin.send_maker_payment(send_payment_args).wait().unwrap(); + let eth_maker_payment = block_on_f01(erc20_coin.send_maker_payment(send_payment_args)).unwrap(); let confirm_input = ConfirmPaymentInput { payment_tx: eth_maker_payment.tx_hex(), @@ -718,7 +717,7 @@ fn send_and_refund_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) wait_until: now_sec() + 60, check_every: 1, }; - erc20_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(erc20_coin.wait_for_confirmations(confirm_input)).unwrap(); let refund_args = RefundPaymentArgs { payment_tx: ð_maker_payment.tx_hex(), @@ -741,7 +740,7 @@ fn send_and_refund_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) wait_until: now_sec() + 60, check_every: 1, }; - erc20_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(erc20_coin.wait_for_confirmations(confirm_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -798,7 +797,7 @@ fn send_and_spend_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { watcher_reward: None, wait_for_confirmation_until: now_sec() + 60, }; - let eth_maker_payment = maker_erc20_coin.send_maker_payment(send_payment_args).wait().unwrap(); + let eth_maker_payment = block_on_f01(maker_erc20_coin.send_maker_payment(send_payment_args)).unwrap(); let confirm_input = ConfirmPaymentInput { payment_tx: eth_maker_payment.tx_hex(), @@ -807,7 +806,7 @@ fn send_and_spend_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - taker_erc20_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(taker_erc20_coin.wait_for_confirmations(confirm_input)).unwrap(); let spend_args = SpendPaymentArgs { other_payment_tx: ð_maker_payment.tx_hex(), @@ -829,7 +828,7 @@ fn send_and_spend_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - taker_erc20_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(taker_erc20_coin.wait_for_confirmations(confirm_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -971,12 +970,12 @@ fn test_nonce_several_urls() { // Use one working and one failing URL. let coin = eth_coin_with_random_privkey_using_urls(swap_contract(), &[GETH_RPC_URL, "http://127.0.0.1:0"]); let my_address = block_on(coin.derivation_method().single_addr_or_err()).unwrap(); - let (old_nonce, _) = coin.clone().get_addr_nonce(my_address).wait().unwrap(); + let (old_nonce, _) = block_on_f01(coin.clone().get_addr_nonce(my_address)).unwrap(); // Send a payment to increase the nonce. - coin.send_to_address(my_address, 200000000.into()).wait().unwrap(); + block_on_f01(coin.send_to_address(my_address, 200000000.into())).unwrap(); - let (new_nonce, _) = coin.get_addr_nonce(my_address).wait().unwrap(); + let (new_nonce, _) = block_on_f01(coin.get_addr_nonce(my_address)).unwrap(); assert_eq!(old_nonce + 1, new_nonce); } @@ -1308,7 +1307,7 @@ fn wait_for_confirmations(coin: &EthCoin, tx: &SignedEthTx, wait_seconds: u64) { wait_until: now_sec() + wait_seconds, check_every: 1, }; - coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_input)).unwrap(); } fn validate_nft_maker_payment(setup: &NftTestSetup, maker_payment: &SignedEthTx, amount: BigDecimal) { diff --git a/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs b/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs index e66a3a5852..6d8d03d96c 100644 --- a/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs @@ -11,10 +11,9 @@ use coins::{CheckIfMyPaymentSentArgs, ConfirmPaymentInput, DexFee, FeeApproxStag SwapTxTypeWithSecretHash, TradePreimageValue, TransactionEnum, ValidateFeeArgs, ValidatePaymentInput, WaitForHTLCTxSpendArgs}; use common::log::debug; -use common::{temp_dir, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{block_on_f01, temp_dir, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::Secp256k1Secret; use ethereum_types::H160; -use futures01::Future; use http::StatusCode; use mm2_core::mm_ctx::{MmArc, MmCtxBuilder}; use mm2_main::lp_swap::{dex_fee_amount, max_taker_vol_from_available}; @@ -78,9 +77,7 @@ impl QtumDockerOps { match self.coin.as_ref().rpc_client { UtxoRpcClientEnum::Native(ref native) => { - let result = native - .create_contract(&bytecode.into(), gas_limit, gas_price, sender) - .wait() + let result = block_on_f01(native.create_contract(&bytecode.into(), gas_limit, gas_price, sender)) .expect("!createcontract"); result.address.0.into() }, @@ -165,14 +162,8 @@ fn withdraw_and_send(mm: &MarketMakerIt, coin: &str, to: &str, amount: f64) { fn test_taker_spends_maker_payment() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); let (_ctx, taker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 1.into()); - let maker_old_balance = maker_coin - .my_spendable_balance() - .wait() - .expect("Error on get maker balance"); - let taker_old_balance = taker_coin - .my_spendable_balance() - .wait() - .expect("Error on get taker balance"); + let maker_old_balance = block_on_f01(maker_coin.my_spendable_balance()).expect("Error on get maker balance"); + let taker_old_balance = block_on_f01(taker_coin.my_spendable_balance()).expect("Error on get taker balance"); assert_eq!(maker_old_balance, BigDecimal::from(10)); assert_eq!(taker_old_balance, BigDecimal::from(1)); @@ -194,7 +185,7 @@ fn test_taker_spends_maker_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(maker_coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -210,7 +201,7 @@ fn test_taker_spends_maker_payment() { wait_until, check_every, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let input = ValidatePaymentInput { payment_tx: payment_tx_hex.clone(), @@ -249,16 +240,10 @@ fn test_taker_spends_maker_payment() { wait_until, check_every, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); - - let maker_balance = maker_coin - .my_spendable_balance() - .wait() - .expect("Error on get maker balance"); - let taker_balance = taker_coin - .my_spendable_balance() - .wait() - .expect("Error on get taker balance"); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); + + let maker_balance = block_on_f01(maker_coin.my_spendable_balance()).expect("Error on get maker balance"); + let taker_balance = block_on_f01(taker_coin.my_spendable_balance()).expect("Error on get taker balance"); assert_eq!(maker_old_balance - amount.clone(), maker_balance); assert_eq!(taker_old_balance + amount, taker_balance); } @@ -267,14 +252,8 @@ fn test_taker_spends_maker_payment() { fn test_maker_spends_taker_payment() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); let (_ctx, taker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); - let maker_old_balance = maker_coin - .my_spendable_balance() - .wait() - .expect("Error on get maker balance"); - let taker_old_balance = taker_coin - .my_spendable_balance() - .wait() - .expect("Error on get taker balance"); + let maker_old_balance = block_on_f01(maker_coin.my_spendable_balance()).expect("Error on get maker balance"); + let taker_old_balance = block_on_f01(taker_coin.my_spendable_balance()).expect("Error on get taker balance"); assert_eq!(maker_old_balance, BigDecimal::from(10)); assert_eq!(taker_old_balance, BigDecimal::from(10)); @@ -296,7 +275,7 @@ fn test_maker_spends_taker_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = taker_coin.send_taker_payment(taker_payment_args).wait().unwrap(); + let payment = block_on_f01(taker_coin.send_taker_payment(taker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Taker payment: {:?}", payment_tx_hash); @@ -312,7 +291,7 @@ fn test_maker_spends_taker_payment() { wait_until, check_every, }; - maker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let input = ValidatePaymentInput { payment_tx: payment_tx_hex.clone(), @@ -351,16 +330,10 @@ fn test_maker_spends_taker_payment() { wait_until, check_every, }; - maker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); - - let maker_balance = maker_coin - .my_spendable_balance() - .wait() - .expect("Error on get maker balance"); - let taker_balance = taker_coin - .my_spendable_balance() - .wait() - .expect("Error on get taker balance"); + block_on_f01(maker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); + + let maker_balance = block_on_f01(maker_coin.my_spendable_balance()).expect("Error on get maker balance"); + let taker_balance = block_on_f01(taker_coin.my_spendable_balance()).expect("Error on get taker balance"); assert_eq!(maker_old_balance + amount.clone(), maker_balance); assert_eq!(taker_old_balance - amount, taker_balance); } @@ -368,7 +341,7 @@ fn test_maker_spends_taker_payment() { #[test] fn test_maker_refunds_payment() { let (_ctx, coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); - let expected_balance = coin.my_spendable_balance().wait().unwrap(); + let expected_balance = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance, BigDecimal::from(10)); let timelock = now_sec() - 200; @@ -387,7 +360,7 @@ fn test_maker_refunds_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = coin.send_maker_payment(maker_payment).wait().unwrap(); + let payment = block_on_f01(coin.send_maker_payment(maker_payment)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -403,9 +376,9 @@ fn test_maker_refunds_payment() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let balance_after_payment = coin.my_spendable_balance().wait().unwrap(); + let balance_after_payment = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance.clone() - amount, balance_after_payment); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &payment_tx_hex, @@ -431,16 +404,16 @@ fn test_maker_refunds_payment() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let balance_after_refund = coin.my_spendable_balance().wait().unwrap(); + let balance_after_refund = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance, balance_after_refund); } #[test] fn test_taker_refunds_payment() { let (_ctx, coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); - let expected_balance = coin.my_spendable_balance().wait().unwrap(); + let expected_balance = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance, BigDecimal::from(10)); let timelock = now_sec() - 200; @@ -459,7 +432,7 @@ fn test_taker_refunds_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = coin.send_taker_payment(taker_payment_args).wait().unwrap(); + let payment = block_on_f01(coin.send_taker_payment(taker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Taker payment: {:?}", payment_tx_hash); @@ -475,9 +448,9 @@ fn test_taker_refunds_payment() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let balance_after_payment = coin.my_spendable_balance().wait().unwrap(); + let balance_after_payment = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance.clone() - amount, balance_after_payment); let taker_refunds_payment_args = RefundPaymentArgs { payment_tx: &payment_tx_hex, @@ -503,9 +476,9 @@ fn test_taker_refunds_payment() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let balance_after_refund = coin.my_spendable_balance().wait().unwrap(); + let balance_after_refund = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance, balance_after_refund); } @@ -528,7 +501,7 @@ fn test_check_if_my_payment_sent() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -544,9 +517,9 @@ fn test_check_if_my_payment_sent() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let search_from_block = coin.current_block().wait().expect("!current_block") - 10; + let search_from_block = block_on_f01(coin.current_block()).expect("!current_block") - 10; let if_my_payment_sent_args = CheckIfMyPaymentSentArgs { time_lock: timelock, other_pub: &taker_pub, @@ -557,7 +530,7 @@ fn test_check_if_my_payment_sent() { amount: &amount, payment_instructions: &None, }; - let found = coin.check_if_my_payment_sent(if_my_payment_sent_args).wait().unwrap(); + let found = block_on_f01(coin.check_if_my_payment_sent(if_my_payment_sent_args)).unwrap(); assert_eq!(found, Some(payment)); } @@ -565,7 +538,7 @@ fn test_check_if_my_payment_sent() { fn test_search_for_swap_tx_spend_taker_spent() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); let (_ctx, taker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 1.into()); - let search_from_block = maker_coin.current_block().wait().expect("!current_block"); + let search_from_block = block_on_f01(maker_coin.current_block()).expect("!current_block"); let timelock = now_sec() - 200; let maker_pub = maker_coin.my_public_key().unwrap(); @@ -585,7 +558,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(maker_coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -601,7 +574,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { wait_until, check_every, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let taker_spends_payment_args = SpendPaymentArgs { other_payment_tx: &payment_tx_hex, time_lock: timelock, @@ -625,7 +598,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { wait_until, check_every, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock: timelock, @@ -645,7 +618,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { #[test] fn test_search_for_swap_tx_spend_maker_refunded() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); - let search_from_block = maker_coin.current_block().wait().expect("!current_block"); + let search_from_block = block_on_f01(maker_coin.current_block()).expect("!current_block"); let timelock = now_sec() - 200; let taker_pub = hex::decode("022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1a").unwrap(); @@ -664,7 +637,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(maker_coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -680,7 +653,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { wait_until, check_every, }; - maker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &payment_tx_hex, time_lock: timelock, @@ -705,7 +678,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { wait_until, check_every, }; - maker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock: timelock, @@ -725,7 +698,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { #[test] fn test_search_for_swap_tx_spend_not_spent() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); - let search_from_block = maker_coin.current_block().wait().expect("!current_block"); + let search_from_block = block_on_f01(maker_coin.current_block()).expect("!current_block"); let timelock = now_sec() - 200; let taker_pub = hex::decode("022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1a").unwrap(); @@ -744,7 +717,7 @@ fn test_search_for_swap_tx_spend_not_spent() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(maker_coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -760,7 +733,7 @@ fn test_search_for_swap_tx_spend_not_spent() { wait_until, check_every, }; - maker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock: timelock, @@ -781,7 +754,7 @@ fn test_search_for_swap_tx_spend_not_spent() { fn test_wait_for_tx_spend() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); let (_ctx, taker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 1.into()); - let from_block = maker_coin.current_block().wait().expect("!current_block"); + let from_block = block_on_f01(maker_coin.current_block()).expect("!current_block"); let timelock = now_sec() - 200; let maker_pub = maker_coin.my_public_key().unwrap(); @@ -801,7 +774,7 @@ fn test_wait_for_tx_spend() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(maker_coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -817,22 +790,20 @@ fn test_wait_for_tx_spend() { wait_until, check_every, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); // first try to check if the wait_for_htlc_tx_spend() returns an error correctly let wait_until = wait_until_sec(5); - let tx_err = maker_coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &payment_tx_hex, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &maker_coin.swap_contract_address(), - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false, - }) - .wait() - .expect_err("Expected 'Waited too long' error"); + let tx_err = block_on_f01(maker_coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &payment_tx_hex, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &maker_coin.swap_contract_address(), + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false, + })) + .expect_err("Expected 'Waited too long' error"); let err = tx_err.get_plain_text_format(); log!("error: {:?}", err); @@ -860,18 +831,16 @@ fn test_wait_for_tx_spend() { }); let wait_until = wait_until_sec(120); - let found = maker_coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &payment_tx_hex, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &maker_coin.swap_contract_address(), - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false, - }) - .wait() - .unwrap(); + let found = block_on_f01(maker_coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &payment_tx_hex, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &maker_coin.swap_contract_address(), + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false, + })) + .unwrap(); unsafe { assert_eq!(Some(found), SPEND_TX) } } @@ -1030,7 +999,7 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ log!("{:?}", block_on(enable_native(&mm, "MYCOIN", &[], None))); log!("{:?}", block_on(enable_native(&mm, "QTUM", &[], None))); - let qtum_balance = coin.my_spendable_balance().wait().expect("!my_balance"); + let qtum_balance = block_on_f01(coin.my_spendable_balance()).expect("!my_balance"); let qtum_min_tx_amount = MmNumber::from("0.000728"); // - `max_possible = balance - locked_amount`, where `locked_amount = 0` @@ -1103,10 +1072,8 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ let secret_hash = &[0; 20]; let dex_fee = dex_fee_amount("QTUM", "MYCOIN", &expected_max_taker_vol, &qtum_min_tx_amount); - let _taker_fee_tx = coin - .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, dex_fee, &[], timelock) - .wait() - .expect("!send_taker_fee"); + let _taker_fee_tx = + block_on_f01(coin.send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, dex_fee, &[], timelock)).expect("!send_taker_fee"); let taker_payment_args = SendPaymentArgs { time_lock_duration: 0, time_lock: timelock, @@ -1120,12 +1087,9 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ wait_for_confirmation_until: 0, }; - let _taker_payment_tx = coin - .send_taker_payment(taker_payment_args) - .wait() - .expect("!send_taker_payment"); + let _taker_payment_tx = block_on_f01(coin.send_taker_payment(taker_payment_args)).expect("!send_taker_payment"); - let my_balance = coin.my_spendable_balance().wait().expect("!my_balance"); + let my_balance = block_on_f01(coin.my_spendable_balance()).expect("!my_balance"); assert_eq!( my_balance, BigDecimal::from(0u32), @@ -1519,7 +1483,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -1528,7 +1492,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &tx.tx_hex(), time_lock, @@ -1549,7 +1513,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -1587,7 +1551,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_taker_payment(taker_payment).wait().unwrap(); + let tx = block_on_f01(coin.send_taker_payment(taker_payment)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -1596,7 +1560,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &tx.tx_hex(), time_lock, @@ -1617,7 +1581,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -1741,26 +1705,23 @@ fn test_send_taker_fee_qtum() { generate_segwit_qtum_coin_with_random_privkey("QTUM", BigDecimal::try_from(0.5).unwrap(), Some(0)); let amount = BigDecimal::from_str("0.01").unwrap(); - let tx = coin - .send_taker_fee( - &DEX_FEE_ADDR_RAW_PUBKEY, - DexFee::Standard(amount.clone().into()), - &[], - 0, - ) - .wait() - .expect("!send_taker_fee"); + let tx = block_on_f01(coin.send_taker_fee( + &DEX_FEE_ADDR_RAW_PUBKEY, + DexFee::Standard(amount.clone().into()), + &[], + 0, + )) + .expect("!send_taker_fee"); assert!(matches!(tx, TransactionEnum::UtxoTx(_)), "Expected UtxoTx"); - coin.validate_fee(ValidateFeeArgs { + block_on_f01(coin.validate_fee(ValidateFeeArgs { fee_tx: &tx, expected_sender: coin.my_public_key().unwrap(), fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, dex_fee: &DexFee::Standard(amount.into()), min_block_number: 0, uuid: &[], - }) - .wait() + })) .expect("!validate_fee"); } @@ -1773,25 +1734,22 @@ fn test_send_taker_fee_qrc20() { ); let amount = BigDecimal::from_str("0.01").unwrap(); - let tx = coin - .send_taker_fee( - &DEX_FEE_ADDR_RAW_PUBKEY, - DexFee::Standard(amount.clone().into()), - &[], - 0, - ) - .wait() - .expect("!send_taker_fee"); + let tx = block_on_f01(coin.send_taker_fee( + &DEX_FEE_ADDR_RAW_PUBKEY, + DexFee::Standard(amount.clone().into()), + &[], + 0, + )) + .expect("!send_taker_fee"); assert!(matches!(tx, TransactionEnum::UtxoTx(_)), "Expected UtxoTx"); - coin.validate_fee(ValidateFeeArgs { + block_on_f01(coin.validate_fee(ValidateFeeArgs { fee_tx: &tx, expected_sender: coin.my_public_key().unwrap(), fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, dex_fee: &DexFee::Standard(amount.into()), min_block_number: 0, uuid: &[], - }) - .wait() + })) .expect("!validate_fee"); } diff --git a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs index 672ef88aa5..46d950242d 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs @@ -6,8 +6,7 @@ use coins::{ConfirmPaymentInput, DexFee, FundingTxSpend, GenTakerFundingSpendArg RefundMakerPaymentSecretArgs, RefundMakerPaymentTimelockArgs, RefundTakerPaymentArgs, SendMakerPaymentArgs, SendTakerFundingArgs, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, Transaction, ValidateMakerPaymentArgs, ValidateTakerFundingArgs}; -use common::{block_on, now_sec, DEX_FEE_ADDR_RAW_PUBKEY}; -use futures01::Future; +use common::{block_on, block_on_f01, now_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use mm2_number::MmNumber; use mm2_test_helpers::for_tests::{active_swaps, check_recent_swaps, coins_needed_for_kickstart, disable_coin, disable_coin_err, enable_native, get_locked_amount, mm_dump, my_swap_status, @@ -93,7 +92,7 @@ fn send_and_refund_taker_funding_timelock() { wait_until: now_sec() + 20, check_every: 1, }; - coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_input)).unwrap(); let found_refund_tx = block_on(coin.search_for_taker_funding_spend(&taker_funding_utxo_tx, 1, taker_secret_hash)).unwrap(); @@ -180,7 +179,7 @@ fn send_and_refund_taker_funding_secret() { wait_until: now_sec() + 20, check_every: 1, }; - coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_input)).unwrap(); let found_refund_tx = block_on(coin.search_for_taker_funding_spend(&taker_funding_utxo_tx, 1, taker_secret_hash)).unwrap(); @@ -268,7 +267,7 @@ fn send_and_spend_taker_funding() { wait_until: now_sec() + 20, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_input)).unwrap(); let found_spend_tx = block_on(taker_coin.search_for_taker_funding_spend(&taker_funding_utxo_tx, 1, taker_secret_hash)).unwrap(); diff --git a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs index 22c17c1444..aadda99709 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -13,9 +13,8 @@ use coins::{ConfirmPaymentInput, FoundSwapTxSpend, MarketCoinOps, MmCoin, MmCoin INVALID_CONTRACT_ADDRESS_ERR_LOG, INVALID_PAYMENT_STATE_ERR_LOG, INVALID_RECEIVER_ERR_LOG, INVALID_REFUND_TX_ERR_LOG, INVALID_SCRIPT_ERR_LOG, INVALID_SENDER_ERR_LOG, INVALID_SWAP_ID_ERR_LOG, OLD_TRANSACTION_ERR_LOG}; -use common::{block_on, now_sec, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{block_on, block_on_f01, now_sec, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::privkey::{key_pair_from_secret, key_pair_from_seed}; -use futures01::Future; use mm2_main::lp_swap::{dex_fee_amount, dex_fee_amount_from_taker_coin, generate_secret, get_payment_locktime, MAKER_PAYMENT_SENT_LOG, MAKER_PAYMENT_SPEND_FOUND_LOG, MAKER_PAYMENT_SPEND_SENT_LOG, REFUND_TEST_FAILURE_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, WATCHER_MESSAGE_SENT_LOG}; @@ -1208,15 +1207,13 @@ fn test_watcher_validate_taker_fee_utxo() { let taker_amount = MmNumber::from((10, 1)); let fee_amount = dex_fee_amount_from_taker_coin(&taker_coin, maker_coin.ticker(), &taker_amount); - let taker_fee = taker_coin - .send_taker_fee( - &DEX_FEE_ADDR_RAW_PUBKEY, - fee_amount, - Uuid::new_v4().as_bytes(), - lock_duration, - ) - .wait() - .unwrap(); + let taker_fee = block_on_f01(taker_coin.send_taker_fee( + &DEX_FEE_ADDR_RAW_PUBKEY, + fee_amount, + Uuid::new_v4().as_bytes(), + lock_duration, + )) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_fee.tx_hex(), @@ -1226,30 +1223,26 @@ fn test_watcher_validate_taker_fee_utxo() { check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let validate_taker_fee_res = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait(); + let validate_taker_fee_res = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })); assert!(validate_taker_fee_res.is_ok()); - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: maker_coin.my_public_key().unwrap().to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: maker_coin.my_public_key().unwrap().to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1259,17 +1252,15 @@ fn test_watcher_validate_taker_fee_utxo() { _ => panic!("Expected `WrongPaymentTx` invalid public key, found {:?}", error), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: std::u64::MAX, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: std::u64::MAX, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1281,17 +1272,15 @@ fn test_watcher_validate_taker_fee_utxo() { ), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration: 0, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration: 0, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1300,17 +1289,15 @@ fn test_watcher_validate_taker_fee_utxo() { _ => panic!("Expected `WrongPaymentTx` transaction too old, found {:?}", error), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: taker_pubkey.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: taker_pubkey.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1334,15 +1321,13 @@ fn test_watcher_validate_taker_fee_eth() { let taker_amount = MmNumber::from((1, 1)); let fee_amount = dex_fee_amount_from_taker_coin(&taker_coin, "ETH", &taker_amount); - let taker_fee = taker_coin - .send_taker_fee( - &DEX_FEE_ADDR_RAW_PUBKEY, - fee_amount, - Uuid::new_v4().as_bytes(), - lock_duration, - ) - .wait() - .unwrap(); + let taker_fee = block_on_f01(taker_coin.send_taker_fee( + &DEX_FEE_ADDR_RAW_PUBKEY, + fee_amount, + Uuid::new_v4().as_bytes(), + lock_duration, + )) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_fee.tx_hex(), @@ -1351,31 +1336,27 @@ fn test_watcher_validate_taker_fee_eth() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); - - let validate_taker_fee_res = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); + + let validate_taker_fee_res = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })); assert!(validate_taker_fee_res.is_ok()); let wrong_keypair = key_pair_from_secret(random_secp256k1_secret().as_slice()).unwrap(); - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: wrong_keypair.public().to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: wrong_keypair.public().to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1385,17 +1366,15 @@ fn test_watcher_validate_taker_fee_eth() { _ => panic!("Expected `WrongPaymentTx` invalid public key, found {:?}", error), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: std::u64::MAX, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: std::u64::MAX, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1407,17 +1386,15 @@ fn test_watcher_validate_taker_fee_eth() { ), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: taker_pubkey.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: taker_pubkey.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1441,15 +1418,13 @@ fn test_watcher_validate_taker_fee_erc20() { let taker_amount = MmNumber::from((1, 1)); let fee_amount = dex_fee_amount_from_taker_coin(&taker_coin, "ETH", &taker_amount); - let taker_fee = taker_coin - .send_taker_fee( - &DEX_FEE_ADDR_RAW_PUBKEY, - fee_amount, - Uuid::new_v4().as_bytes(), - lock_duration, - ) - .wait() - .unwrap(); + let taker_fee = block_on_f01(taker_coin.send_taker_fee( + &DEX_FEE_ADDR_RAW_PUBKEY, + fee_amount, + Uuid::new_v4().as_bytes(), + lock_duration, + )) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_fee.tx_hex(), @@ -1458,31 +1433,27 @@ fn test_watcher_validate_taker_fee_erc20() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); - - let validate_taker_fee_res = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); + + let validate_taker_fee_res = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })); assert!(validate_taker_fee_res.is_ok()); let wrong_keypair = key_pair_from_secret(random_secp256k1_secret().as_slice()).unwrap(); - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: wrong_keypair.public().to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: wrong_keypair.public().to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1492,17 +1463,15 @@ fn test_watcher_validate_taker_fee_erc20() { _ => panic!("Expected `WrongPaymentTx` invalid public key, found {:?}", error), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: std::u64::MAX, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: std::u64::MAX, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1514,17 +1483,15 @@ fn test_watcher_validate_taker_fee_erc20() { ), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: taker_pubkey.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: taker_pubkey.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1552,21 +1519,19 @@ fn test_watcher_validate_taker_payment_utxo() { let secret_hash = dhash160(&generate_secret().unwrap()); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pubkey, - secret_hash: secret_hash.as_slice(), - amount: BigDecimal::from(10), - swap_contract_address: &None, - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: None, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pubkey, + secret_hash: secret_hash.as_slice(), + amount: BigDecimal::from(10), + swap_contract_address: &None, + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: None, + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -1575,21 +1540,19 @@ fn test_watcher_validate_taker_payment_utxo() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let taker_payment_refund_preimage = taker_coin - .create_taker_payment_refund_preimage( - &taker_payment.tx_hex(), - time_lock, - maker_pubkey, - secret_hash.as_slice(), - &None, - &[], - ) - .wait() - .unwrap(); - let validate_taker_payment_res = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { + let taker_payment_refund_preimage = block_on_f01(taker_coin.create_taker_payment_refund_preimage( + &taker_payment.tx_hex(), + time_lock, + maker_pubkey, + secret_hash.as_slice(), + &None, + &[], + )) + .unwrap(); + let validate_taker_payment_res = + block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { payment_tx: taker_payment.tx_hex(), taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), time_lock, @@ -1599,25 +1562,22 @@ fn test_watcher_validate_taker_payment_utxo() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), - }) - .wait(); + })); assert!(validate_taker_payment_res.is_ok()); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), - time_lock, - taker_pub: maker_pubkey.to_vec(), - maker_pub: maker_pubkey.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), + time_lock, + taker_pub: maker_pubkey.to_vec(), + maker_pub: maker_pubkey.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1629,21 +1589,19 @@ fn test_watcher_validate_taker_payment_utxo() { // Used to get wrong swap id let wrong_secret_hash = dhash160(&generate_secret().unwrap()); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), - time_lock, - taker_pub: taker_pubkey.to_vec(), - maker_pub: maker_pubkey.to_vec(), - secret_hash: wrong_secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), + time_lock, + taker_pub: taker_pubkey.to_vec(), + maker_pub: maker_pubkey.to_vec(), + secret_hash: wrong_secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1656,21 +1614,19 @@ fn test_watcher_validate_taker_payment_utxo() { ), } - let taker_payment_wrong_secret = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pubkey, - secret_hash: wrong_secret_hash.as_slice(), - amount: BigDecimal::from(10), - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: None, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment_wrong_secret = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pubkey, + secret_hash: wrong_secret_hash.as_slice(), + amount: BigDecimal::from(10), + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: None, + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment_wrong_secret.tx_hex(), @@ -1679,23 +1635,21 @@ fn test_watcher_validate_taker_payment_utxo() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), - time_lock: 500, - taker_pub: taker_pubkey.to_vec(), - maker_pub: maker_pubkey.to_vec(), - secret_hash: wrong_secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), + time_lock: 500, + taker_pub: taker_pubkey.to_vec(), + maker_pub: maker_pubkey.to_vec(), + secret_hash: wrong_secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1708,33 +1662,29 @@ fn test_watcher_validate_taker_payment_utxo() { ), } - let wrong_taker_payment_refund_preimage = taker_coin - .create_taker_payment_refund_preimage( - &taker_payment.tx_hex(), - time_lock, - maker_pubkey, - wrong_secret_hash.as_slice(), - &None, - &[], - ) - .wait() - .unwrap(); + let wrong_taker_payment_refund_preimage = block_on_f01(taker_coin.create_taker_payment_refund_preimage( + &taker_payment.tx_hex(), + time_lock, + maker_pubkey, + wrong_secret_hash.as_slice(), + &None, + &[], + )) + .unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: wrong_taker_payment_refund_preimage.tx_hex(), - time_lock, - taker_pub: taker_pubkey.to_vec(), - maker_pub: maker_pubkey.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: wrong_taker_payment_refund_preimage.tx_hex(), + time_lock, + taker_pub: taker_pubkey.to_vec(), + maker_pub: maker_pubkey.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1777,21 +1727,19 @@ fn test_watcher_validate_taker_payment_eth() { .unwrap(), ); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -1800,10 +1748,10 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let validate_taker_payment_res = taker_coin - .watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { + let validate_taker_payment_res = block_on_f01(taker_coin.watcher_validate_taker_payment( + coins::WatcherValidatePaymentInput { payment_tx: taker_payment.tx_hex(), taker_payment_refund_preimage: Vec::new(), time_lock, @@ -1813,12 +1761,12 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait(); + }, + )); assert!(validate_taker_payment_res.is_ok()); - let error = taker_coin - .watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { + let error = block_on_f01( + taker_coin.watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { payment_tx: taker_payment.tx_hex(), taker_payment_refund_preimage: Vec::new(), time_lock, @@ -1828,10 +1776,10 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + }), + ) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1843,24 +1791,22 @@ fn test_watcher_validate_taker_payment_eth() { ), } - let taker_payment_wrong_contract = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &Some("9130b257d37a52e52f21054c4da3450c72f595ce".into()), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment_wrong_contract = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &Some("9130b257d37a52e52f21054c4da3450c72f595ce".into()), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { + let error = block_on_f01( + taker_coin.watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { payment_tx: taker_payment_wrong_contract.tx_hex(), taker_payment_refund_preimage: Vec::new(), time_lock, @@ -1870,10 +1816,10 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + }), + ) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1887,8 +1833,8 @@ fn test_watcher_validate_taker_payment_eth() { // Used to get wrong swap id let wrong_secret_hash = dhash160(&generate_secret().unwrap()); - let error = taker_coin - .watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { + let error = block_on_f01( + taker_coin.watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { payment_tx: taker_payment.tx_hex(), taker_payment_refund_preimage: Vec::new(), time_lock, @@ -1898,10 +1844,10 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + }), + ) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::UnexpectedPaymentState(err) => { @@ -1913,21 +1859,19 @@ fn test_watcher_validate_taker_payment_eth() { ), } - let taker_payment_wrong_secret = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: wrong_secret_hash.as_slice(), - amount: taker_amount, - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment_wrong_secret = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: wrong_secret_hash.as_slice(), + amount: taker_amount, + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward, + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment_wrong_secret.tx_hex(), @@ -1936,23 +1880,21 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: maker_pub.to_vec(), - secret_hash: wrong_secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: maker_pub.to_vec(), + secret_hash: wrong_secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1964,21 +1906,19 @@ fn test_watcher_validate_taker_payment_eth() { ), } - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: taker_pub.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: taker_pub.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -2023,21 +1963,19 @@ fn test_watcher_validate_taker_payment_erc20() { .unwrap(), ); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -2046,10 +1984,10 @@ fn test_watcher_validate_taker_payment_erc20() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let validate_taker_payment_res = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { + let validate_taker_payment_res = + block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { payment_tx: taker_payment.tx_hex(), taker_payment_refund_preimage: Vec::new(), time_lock, @@ -2059,25 +1997,22 @@ fn test_watcher_validate_taker_payment_erc20() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait(); + })); assert!(validate_taker_payment_res.is_ok()); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: maker_pub.to_vec(), - maker_pub: maker_pub.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: maker_pub.to_vec(), + maker_pub: maker_pub.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -2089,37 +2024,33 @@ fn test_watcher_validate_taker_payment_erc20() { ), } - let taker_payment_wrong_contract = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &Some("9130b257d37a52e52f21054c4da3450c72f595ce".into()), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment_wrong_contract = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &Some("9130b257d37a52e52f21054c4da3450c72f595ce".into()), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment_wrong_contract.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: maker_pub.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment_wrong_contract.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: maker_pub.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -2133,21 +2064,19 @@ fn test_watcher_validate_taker_payment_erc20() { // Used to get wrong swap id let wrong_secret_hash = dhash160(&generate_secret().unwrap()); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: maker_pub.to_vec(), - secret_hash: wrong_secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: maker_pub.to_vec(), + secret_hash: wrong_secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::UnexpectedPaymentState(err) => { @@ -2159,21 +2088,19 @@ fn test_watcher_validate_taker_payment_erc20() { ), } - let taker_payment_wrong_secret = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: wrong_secret_hash.as_slice(), - amount: taker_amount, - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment_wrong_secret = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: wrong_secret_hash.as_slice(), + amount: taker_amount, + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward, + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment_wrong_secret.tx_hex(), @@ -2182,23 +2109,21 @@ fn test_watcher_validate_taker_payment_erc20() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: maker_pub.to_vec(), - secret_hash: wrong_secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: maker_pub.to_vec(), + secret_hash: wrong_secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -2210,21 +2135,19 @@ fn test_watcher_validate_taker_payment_erc20() { ), } - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: taker_pub.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: taker_pub.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -2250,21 +2173,19 @@ fn test_taker_validates_taker_payment_refund_utxo() { let secret_hash = dhash160(&generate_secret().unwrap()); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pubkey, - secret_hash: secret_hash.as_slice(), - amount: BigDecimal::from(10), - swap_contract_address: &None, - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: None, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pubkey, + secret_hash: secret_hash.as_slice(), + amount: BigDecimal::from(10), + swap_contract_address: &None, + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: None, + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -2273,34 +2194,30 @@ fn test_taker_validates_taker_payment_refund_utxo() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let taker_payment_refund_preimage = taker_coin - .create_taker_payment_refund_preimage( - &taker_payment.tx_hex(), - time_lock, - maker_pubkey, - secret_hash.as_slice(), - &None, - &[], - ) - .wait() - .unwrap(); + let taker_payment_refund_preimage = block_on_f01(taker_coin.create_taker_payment_refund_preimage( + &taker_payment.tx_hex(), + time_lock, + maker_pubkey, + secret_hash.as_slice(), + &None, + &[], + )) + .unwrap(); - let taker_payment_refund = taker_coin - .send_taker_payment_refund_preimage(RefundPaymentArgs { - payment_tx: &taker_payment_refund_preimage.tx_hex(), - other_pubkey: maker_pubkey, - tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { - maker_secret_hash: secret_hash.as_slice(), - }, - time_lock, - swap_contract_address: &None, - swap_unique_data: &[], - watcher_reward: false, - }) - .wait() - .unwrap(); + let taker_payment_refund = block_on_f01(taker_coin.send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &taker_payment_refund_preimage.tx_hex(), + other_pubkey: maker_pubkey, + tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { + maker_secret_hash: secret_hash.as_slice(), + }, + time_lock, + swap_contract_address: &None, + swap_unique_data: &[], + watcher_reward: false, + })) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: taker_payment_refund.tx_hex(), @@ -2313,9 +2230,7 @@ fn test_taker_validates_taker_payment_refund_utxo() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_refund = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait(); + let validate_watcher_refund = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)); assert!(validate_watcher_refund.is_ok()); } @@ -2347,21 +2262,19 @@ fn test_taker_validates_taker_payment_refund_eth() { )) .unwrap(); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: Some(watcher_reward.clone()), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: Some(watcher_reward.clone()), + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -2370,19 +2283,17 @@ fn test_taker_validates_taker_payment_refund_eth() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let taker_payment_refund_preimage = taker_coin - .create_taker_payment_refund_preimage( - &taker_payment.tx_hex(), - time_lock, - taker_pub, - secret_hash.as_slice(), - &taker_coin.swap_contract_address(), - &[], - ) - .wait() - .unwrap(); + let taker_payment_refund_preimage = block_on_f01(taker_coin.create_taker_payment_refund_preimage( + &taker_payment.tx_hex(), + time_lock, + taker_pub, + secret_hash.as_slice(), + &taker_coin.swap_contract_address(), + &[], + )) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: taker_payment_refund_preimage.tx_hex(), @@ -2395,9 +2306,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2411,20 +2320,18 @@ fn test_taker_validates_taker_payment_refund_eth() { ), } - let taker_payment_refund = taker_coin - .send_taker_payment_refund_preimage(RefundPaymentArgs { - payment_tx: &taker_payment_refund_preimage.tx_hex(), - other_pubkey: taker_pub, - tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { - maker_secret_hash: secret_hash.as_slice(), - }, - time_lock, - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - watcher_reward: true, - }) - .wait() - .unwrap(); + let taker_payment_refund = block_on_f01(taker_coin.send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &taker_payment_refund_preimage.tx_hex(), + other_pubkey: taker_pub, + tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { + maker_secret_hash: secret_hash.as_slice(), + }, + time_lock, + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + watcher_reward: true, + })) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: taker_payment_refund.tx_hex(), @@ -2437,9 +2344,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_refund = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait(); + let validate_watcher_refund = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)); assert!(validate_watcher_refund.is_ok()); let validate_input = ValidateWatcherSpendInput { @@ -2452,9 +2357,7 @@ fn test_taker_validates_taker_payment_refund_eth() { watcher_reward: Some(watcher_reward.clone()), spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2479,9 +2382,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = maker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(maker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2506,9 +2407,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2536,9 +2435,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2566,9 +2463,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2596,9 +2491,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2623,9 +2516,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2672,21 +2563,19 @@ fn test_taker_validates_taker_payment_refund_erc20() { .unwrap(), ); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -2695,34 +2584,30 @@ fn test_taker_validates_taker_payment_refund_erc20() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let taker_payment_refund_preimage = taker_coin - .create_taker_payment_refund_preimage( - &taker_payment.tx_hex(), - time_lock, - taker_pub, - secret_hash.as_slice(), - &taker_coin.swap_contract_address(), - &[], - ) - .wait() - .unwrap(); + let taker_payment_refund_preimage = block_on_f01(taker_coin.create_taker_payment_refund_preimage( + &taker_payment.tx_hex(), + time_lock, + taker_pub, + secret_hash.as_slice(), + &taker_coin.swap_contract_address(), + &[], + )) + .unwrap(); - let taker_payment_refund = taker_coin - .send_taker_payment_refund_preimage(RefundPaymentArgs { - payment_tx: &taker_payment_refund_preimage.tx_hex(), - other_pubkey: taker_pub, - tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { - maker_secret_hash: secret_hash.as_slice(), - }, - time_lock, - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - watcher_reward: true, - }) - .wait() - .unwrap(); + let taker_payment_refund = block_on_f01(taker_coin.send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &taker_payment_refund_preimage.tx_hex(), + other_pubkey: taker_pub, + tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { + maker_secret_hash: secret_hash.as_slice(), + }, + time_lock, + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + watcher_reward: true, + })) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: taker_payment_refund.tx_hex(), @@ -2735,9 +2620,7 @@ fn test_taker_validates_taker_payment_refund_erc20() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_refund = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait(); + let validate_watcher_refund = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)); assert!(validate_watcher_refund.is_ok()); let validate_input = ValidateWatcherSpendInput { @@ -2751,9 +2634,7 @@ fn test_taker_validates_taker_payment_refund_erc20() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2783,54 +2664,48 @@ fn test_taker_validates_maker_payment_spend_utxo() { let secret = generate_secret().unwrap(); let secret_hash = dhash160(&secret); - let maker_payment = maker_coin - .send_maker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: taker_pubkey, - secret_hash: secret_hash.as_slice(), - amount: BigDecimal::from(10), - swap_contract_address: &None, - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: None, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let maker_payment = block_on_f01(maker_coin.send_maker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: taker_pubkey, + secret_hash: secret_hash.as_slice(), + amount: BigDecimal::from(10), + swap_contract_address: &None, + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: None, + wait_for_confirmation_until, + })) + .unwrap(); - maker_coin - .wait_for_confirmations(ConfirmPaymentInput { - payment_tx: maker_payment.tx_hex(), - confirmations: 1, - requires_nota: false, - wait_until: timeout, - check_every: 1, - }) - .wait() - .unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(ConfirmPaymentInput { + payment_tx: maker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + })) + .unwrap(); - let maker_payment_spend_preimage = taker_coin - .create_maker_payment_spend_preimage( - &maker_payment.tx_hex(), - time_lock, - maker_pubkey, - secret_hash.as_slice(), - &[], - ) - .wait() - .unwrap(); + let maker_payment_spend_preimage = block_on_f01(taker_coin.create_maker_payment_spend_preimage( + &maker_payment.tx_hex(), + time_lock, + maker_pubkey, + secret_hash.as_slice(), + &[], + )) + .unwrap(); - let maker_payment_spend = taker_coin - .send_maker_payment_spend_preimage(SendMakerPaymentSpendPreimageInput { + let maker_payment_spend = block_on_f01(taker_coin.send_maker_payment_spend_preimage( + SendMakerPaymentSpendPreimageInput { preimage: &maker_payment_spend_preimage.tx_hex(), secret_hash: secret_hash.as_slice(), secret: secret.as_slice(), taker_pub: taker_pubkey, watcher_reward: false, - }) - .wait() - .unwrap(); + }, + )) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend.tx_hex(), @@ -2843,9 +2718,7 @@ fn test_taker_validates_maker_payment_spend_utxo() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_spend = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait(); + let validate_watcher_spend = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)); assert!(validate_watcher_spend.is_ok()); } @@ -2877,43 +2750,37 @@ fn test_taker_validates_maker_payment_spend_eth() { .unwrap() .unwrap(); - let maker_payment = maker_coin - .send_maker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: taker_pub, - secret_hash: secret_hash.as_slice(), - amount: maker_amount.clone(), - swap_contract_address: &maker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: Some(watcher_reward.clone()), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let maker_payment = block_on_f01(maker_coin.send_maker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: taker_pub, + secret_hash: secret_hash.as_slice(), + amount: maker_amount.clone(), + swap_contract_address: &maker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: Some(watcher_reward.clone()), + wait_for_confirmation_until, + })) + .unwrap(); - maker_coin - .wait_for_confirmations(ConfirmPaymentInput { - payment_tx: maker_payment.tx_hex(), - confirmations: 1, - requires_nota: false, - wait_until: timeout, - check_every: 1, - }) - .wait() - .unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(ConfirmPaymentInput { + payment_tx: maker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + })) + .unwrap(); - let maker_payment_spend_preimage = taker_coin - .create_maker_payment_spend_preimage( - &maker_payment.tx_hex(), - time_lock, - maker_pub, - secret_hash.as_slice(), - &[], - ) - .wait() - .unwrap(); + let maker_payment_spend_preimage = block_on_f01(taker_coin.create_maker_payment_spend_preimage( + &maker_payment.tx_hex(), + time_lock, + maker_pub, + secret_hash.as_slice(), + &[], + )) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend_preimage.tx_hex(), @@ -2926,9 +2793,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2942,27 +2807,25 @@ fn test_taker_validates_maker_payment_spend_eth() { ), } - let maker_payment_spend = taker_coin - .send_maker_payment_spend_preimage(SendMakerPaymentSpendPreimageInput { + let maker_payment_spend = block_on_f01(taker_coin.send_maker_payment_spend_preimage( + SendMakerPaymentSpendPreimageInput { preimage: &maker_payment_spend_preimage.tx_hex(), secret_hash: secret_hash.as_slice(), secret: secret.as_slice(), taker_pub, watcher_reward: true, - }) - .wait() - .unwrap(); + }, + )) + .unwrap(); - maker_coin - .wait_for_confirmations(ConfirmPaymentInput { - payment_tx: maker_payment_spend.tx_hex(), - confirmations: 1, - requires_nota: false, - wait_until: timeout, - check_every: 1, - }) - .wait() - .unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(ConfirmPaymentInput { + payment_tx: maker_payment_spend.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + })) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend.tx_hex(), @@ -2975,10 +2838,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() - .unwrap(); + block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)).unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend.tx_hex(), @@ -2991,9 +2851,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3018,9 +2876,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3045,9 +2901,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = maker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(maker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3075,9 +2929,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3105,9 +2957,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3135,9 +2985,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3162,9 +3010,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3206,65 +3052,57 @@ fn test_taker_validates_maker_payment_spend_erc20() { )) .unwrap(); - let maker_payment = maker_coin - .send_maker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: taker_pub, - secret_hash: secret_hash.as_slice(), - amount: maker_amount.clone(), - swap_contract_address: &maker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let maker_payment = block_on_f01(maker_coin.send_maker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: taker_pub, + secret_hash: secret_hash.as_slice(), + amount: maker_amount.clone(), + swap_contract_address: &maker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); - maker_coin - .wait_for_confirmations(ConfirmPaymentInput { - payment_tx: maker_payment.tx_hex(), - confirmations: 1, - requires_nota: false, - wait_until: timeout, - check_every: 1, - }) - .wait() - .unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(ConfirmPaymentInput { + payment_tx: maker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + })) + .unwrap(); - let maker_payment_spend_preimage = taker_coin - .create_maker_payment_spend_preimage( - &maker_payment.tx_hex(), - time_lock, - maker_pub, - secret_hash.as_slice(), - &[], - ) - .wait() - .unwrap(); + let maker_payment_spend_preimage = block_on_f01(taker_coin.create_maker_payment_spend_preimage( + &maker_payment.tx_hex(), + time_lock, + maker_pub, + secret_hash.as_slice(), + &[], + )) + .unwrap(); - let maker_payment_spend = taker_coin - .send_maker_payment_spend_preimage(SendMakerPaymentSpendPreimageInput { + let maker_payment_spend = block_on_f01(taker_coin.send_maker_payment_spend_preimage( + SendMakerPaymentSpendPreimageInput { preimage: &maker_payment_spend_preimage.tx_hex(), secret_hash: secret_hash.as_slice(), secret: secret.as_slice(), taker_pub, watcher_reward: true, - }) - .wait() - .unwrap(); + }, + )) + .unwrap(); - maker_coin - .wait_for_confirmations(ConfirmPaymentInput { - payment_tx: maker_payment_spend.tx_hex(), - confirmations: 1, - requires_nota: false, - wait_until: timeout, - check_every: 1, - }) - .wait() - .unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(ConfirmPaymentInput { + payment_tx: maker_payment_spend.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + })) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend.tx_hex(), @@ -3277,10 +3115,7 @@ fn test_taker_validates_maker_payment_spend_erc20() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() - .unwrap(); + block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)).unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend.tx_hex(), @@ -3293,9 +3128,7 @@ fn test_taker_validates_maker_payment_spend_erc20() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3329,7 +3162,7 @@ fn test_send_taker_payment_refund_preimage_utxo() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_taker_payment(taker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_taker_payment(taker_payment_args)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -3338,27 +3171,30 @@ fn test_send_taker_payment_refund_preimage_utxo() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let refund_tx = coin - .create_taker_payment_refund_preimage(&tx.tx_hex(), time_lock, my_public_key, &[0; 20], &None, &[]) - .wait() - .unwrap(); + let refund_tx = block_on_f01(coin.create_taker_payment_refund_preimage( + &tx.tx_hex(), + time_lock, + my_public_key, + &[0; 20], + &None, + &[], + )) + .unwrap(); - let refund_tx = coin - .send_taker_payment_refund_preimage(RefundPaymentArgs { - payment_tx: &refund_tx.tx_hex(), - swap_contract_address: &None, - tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { - maker_secret_hash: &[0; 20], - }, - other_pubkey: my_public_key, - time_lock, - swap_unique_data: &[], - watcher_reward: false, - }) - .wait() - .unwrap(); + let refund_tx = block_on_f01(coin.send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &refund_tx.tx_hex(), + swap_contract_address: &None, + tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { + maker_secret_hash: &[0; 20], + }, + other_pubkey: my_public_key, + time_lock, + swap_unique_data: &[], + watcher_reward: false, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: refund_tx.tx_hex(), @@ -3367,7 +3203,7 @@ fn test_send_taker_payment_refund_preimage_utxo() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, From ab23c11955a15e1bcc1347a660d514e8e856dfe9 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Wed, 25 Sep 2024 20:05:13 +0300 Subject: [PATCH 08/16] feat(wallets): add `get_wallet_names` rpc (#2202) This commit introduces the `get_wallet_names` RPC method, which allows clients to retrieve information about all wallet names and the currently active one. --- mm2src/mm2_core/src/lib.rs | 9 +- mm2src/mm2_core/src/mm_ctx.rs | 9 ++ mm2src/mm2_io/src/fs.rs | 59 +++++++++++-- mm2src/mm2_main/src/lp_wallet.rs | 57 +++++++++++- .../src/lp_wallet/mnemonics_storage.rs | 9 +- .../src/lp_wallet/mnemonics_wasm_db.rs | 13 +++ .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 3 +- mm2src/mm2_main/src/wasm_tests.rs | 70 +++++++++++---- .../tests/mm2_tests/mm2_tests_inner.rs | 80 +++++++++++++---- mm2src/mm2_test_helpers/src/for_tests.rs | 86 +++++++++++++++++-- mm2src/mm2_test_helpers/src/structs.rs | 7 ++ 11 files changed, 343 insertions(+), 59 deletions(-) diff --git a/mm2src/mm2_core/src/lib.rs b/mm2src/mm2_core/src/lib.rs index 62e4bef09c..88b0fd5c81 100644 --- a/mm2src/mm2_core/src/lib.rs +++ b/mm2src/mm2_core/src/lib.rs @@ -1,10 +1,11 @@ -use derive_more::Display; -use rand::{thread_rng, Rng}; +#[cfg(target_arch = "wasm32")] use derive_more::Display; +#[cfg(target_arch = "wasm32")] use rand::{thread_rng, Rng}; pub mod data_asker; pub mod event_dispatcher; pub mod mm_ctx; +#[cfg(target_arch = "wasm32")] #[derive(Clone, Copy, Display, PartialEq, Default)] pub enum DbNamespaceId { #[display(fmt = "MAIN")] @@ -14,9 +15,13 @@ pub enum DbNamespaceId { Test(u64), } +#[cfg(target_arch = "wasm32")] impl DbNamespaceId { pub fn for_test() -> DbNamespaceId { let mut rng = thread_rng(); DbNamespaceId::Test(rng.gen()) } + + #[inline(always)] + pub fn for_test_with_id(id: u64) -> DbNamespaceId { DbNamespaceId::Test(id) } } diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index 0be8e66734..54d5f71004 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -291,6 +291,9 @@ impl MmCtx { #[cfg(not(target_arch = "wasm32"))] pub fn db_root(&self) -> PathBuf { path_to_db_root(self.conf["dbdir"].as_str()) } + #[cfg(not(target_arch = "wasm32"))] + pub fn db_root(&self) -> PathBuf { path_to_db_root(self.conf["dbdir"].as_str()) } + #[cfg(not(target_arch = "wasm32"))] pub fn wallet_file_path(&self, wallet_name: &str) -> PathBuf { self.db_root().join(wallet_name.to_string() + ".dat") @@ -753,6 +756,12 @@ impl MmCtxBuilder { self } + #[cfg(target_arch = "wasm32")] + pub fn with_test_db_namespace_with_id(mut self, id: u64) -> Self { + self.db_namespace = DbNamespaceId::for_test_with_id(id); + self + } + pub fn into_mm_arc(self) -> MmArc { // NB: We avoid recreating LogState // in order not to interfere with the integration tests checking LogState drop on shutdown. diff --git a/mm2src/mm2_io/src/fs.rs b/mm2src/mm2_io/src/fs.rs index 94a323c0b8..489b3bad8b 100644 --- a/mm2src/mm2_io/src/fs.rs +++ b/mm2src/mm2_io/src/fs.rs @@ -192,19 +192,52 @@ where json::from_slice(&content).map_to_mm(FsJsonError::Deserializing) } -/// Read the `dir_path` entries trying to deserialize each as the `T` type. +async fn filter_files_by_extension(dir_path: &Path, extension: &str) -> IoResult> { + let ext = Some(OsStr::new(extension).to_ascii_lowercase()); + let entries = read_dir_async(dir_path) + .await? + .into_iter() + .filter(|path| path.extension().map(|ext| ext.to_ascii_lowercase()) == ext) + .collect(); + Ok(entries) +} + +/// Helper function to extract file names or stems based on the provided extraction function. +fn extract_file_identifiers<'a, F>(entries: Vec, extractor: F) -> impl Iterator + 'a +where + F: Fn(&Path) -> Option<&OsStr> + 'a, +{ + entries + .into_iter() + .filter_map(move |path| extractor(&path).and_then(OsStr::to_str).map(ToOwned::to_owned)) +} + +/// Lists files by the specified extension from the given directory path. +/// If include_extension is true, returns full file names; otherwise, returns file stems. +pub async fn list_files_by_extension( + dir_path: &Path, + extension: &str, + include_extension: bool, +) -> IoResult> { + let entries = filter_files_by_extension(dir_path, extension).await?; + let extractor = if include_extension { + Path::file_name + } else { + Path::file_stem + }; + Ok(extract_file_identifiers(entries, extractor)) +} + +/// Read the `dir_path` entries trying to deserialize each as the `T` type, +/// filtering by the specified extension. /// Please note that files that couldn't be deserialized are skipped. -pub async fn read_dir_json(dir_path: &Path) -> FsJsonResult> +pub async fn read_files_with_extension(dir_path: &Path, extension: &str) -> FsJsonResult> where T: DeserializeOwned, { - let json_ext = Some(OsStr::new("json")); - let entries: Vec<_> = read_dir_async(dir_path) + let entries = filter_files_by_extension(dir_path, extension) .await - .mm_err(FsJsonError::IoReading)? - .into_iter() - .filter(|path| path.extension() == json_ext) - .collect(); + .mm_err(FsJsonError::IoReading)?; let type_name = std::any::type_name::(); let mut result = Vec::new(); @@ -233,6 +266,16 @@ where Ok(result) } +/// Read the `dir_path` entries trying to deserialize each as the `T` type from JSON files. +/// Please note that files that couldn't be deserialized are skipped. +#[inline(always)] +pub async fn read_dir_json(dir_path: &Path) -> FsJsonResult> +where + T: DeserializeOwned, +{ + read_files_with_extension(dir_path, "json").await +} + pub async fn write_json(t: &T, path: &Path, use_tmp_file: bool) -> FsJsonResult<()> where T: Serialize, diff --git a/mm2src/mm2_main/src/lp_wallet.rs b/mm2src/mm2_main/src/lp_wallet.rs index 19ac357cab..559821a26a 100644 --- a/mm2src/mm2_main/src/lp_wallet.rs +++ b/mm2src/mm2_main/src/lp_wallet.rs @@ -2,23 +2,24 @@ use common::HttpStatusCode; use crypto::{decrypt_mnemonic, encrypt_mnemonic, generate_mnemonic, CryptoCtx, CryptoInitError, EncryptedData, MnemonicError}; use http::StatusCode; +use itertools::Itertools; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use serde::de::DeserializeOwned; -use serde_json::{self as json}; +use serde_json::{self as json, Value as Json}; cfg_wasm32! { use crate::lp_wallet::mnemonics_wasm_db::{WalletsDb, WalletsDBError}; use mm2_core::mm_ctx::from_ctx; use mm2_db::indexed_db::{ConstructibleDb, DbLocked, InitDbResult}; - use mnemonics_wasm_db::{read_encrypted_passphrase_if_available, save_encrypted_passphrase}; + use mnemonics_wasm_db::{read_all_wallet_names, read_encrypted_passphrase_if_available, save_encrypted_passphrase}; use std::sync::Arc; type WalletsDbLocked<'a> = DbLocked<'a, WalletsDb>; } cfg_native! { - use mnemonics_storage::{read_encrypted_passphrase_if_available, save_encrypted_passphrase, WalletsStorageError}; + use mnemonics_storage::{read_all_wallet_names, read_encrypted_passphrase_if_available, save_encrypted_passphrase, WalletsStorageError}; } #[cfg(not(target_arch = "wasm32"))] mod mnemonics_storage; @@ -499,3 +500,53 @@ pub async fn get_mnemonic_rpc(ctx: MmArc, req: GetMnemonicRequest) -> MmResult, + activated_wallet: Option, +} + +#[derive(Debug, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum GetWalletsError { + #[display(fmt = "Wallets storage error: {}", _0)] + WalletsStorageError(String), + #[display(fmt = "Internal error: {}", _0)] + Internal(String), +} + +impl HttpStatusCode for GetWalletsError { + fn status_code(&self) -> StatusCode { + match self { + GetWalletsError::WalletsStorageError(_) | GetWalletsError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl From for GetWalletsError { + fn from(e: WalletsStorageError) -> Self { GetWalletsError::WalletsStorageError(e.to_string()) } +} + +#[cfg(target_arch = "wasm32")] +impl From for GetWalletsError { + fn from(e: WalletsDBError) -> Self { GetWalletsError::WalletsStorageError(e.to_string()) } +} + +/// Retrieves all created wallets and the currently activated wallet. +pub async fn get_wallet_names_rpc(ctx: MmArc, _req: Json) -> MmResult { + // We want to return wallet names in the same order for both native and wasm32 targets. + let wallets = read_all_wallet_names(&ctx).await?.sorted().collect(); + // Note: `ok_or` is used here on `Constructible>` to handle the case where the wallet name is not set. + // `wallet_name` can be `None` in the case of no-login mode. + let activated_wallet = ctx.wallet_name.ok_or(GetWalletsError::Internal( + "`wallet_name` not initialized yet!".to_string(), + ))?; + + Ok(GetWalletNamesResponse { + wallet_names: wallets, + activated_wallet: activated_wallet.clone(), + }) +} diff --git a/mm2src/mm2_main/src/lp_wallet/mnemonics_storage.rs b/mm2src/mm2_main/src/lp_wallet/mnemonics_storage.rs index 3cf40e61fb..c873b2d5ff 100644 --- a/mm2src/mm2_main/src/lp_wallet/mnemonics_storage.rs +++ b/mm2src/mm2_main/src/lp_wallet/mnemonics_storage.rs @@ -1,7 +1,7 @@ use crypto::EncryptedData; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -use mm2_io::fs::ensure_file_is_writable; +use mm2_io::fs::{ensure_file_is_writable, list_files_by_extension}; type WalletsStorageResult = Result>; @@ -61,3 +61,10 @@ pub(super) async fn read_encrypted_passphrase_if_available(ctx: &MmArc) -> Walle )) }) } + +pub(super) async fn read_all_wallet_names(ctx: &MmArc) -> WalletsStorageResult> { + let wallet_names = list_files_by_extension(&ctx.db_root(), "dat", false) + .await + .mm_err(|e| WalletsStorageError::FsReadError(format!("Error reading wallets directory: {}", e)))?; + Ok(wallet_names) +} diff --git a/mm2src/mm2_main/src/lp_wallet/mnemonics_wasm_db.rs b/mm2src/mm2_main/src/lp_wallet/mnemonics_wasm_db.rs index 40daa62029..a815bfcca1 100644 --- a/mm2src/mm2_main/src/lp_wallet/mnemonics_wasm_db.rs +++ b/mm2src/mm2_main/src/lp_wallet/mnemonics_wasm_db.rs @@ -144,3 +144,16 @@ pub(super) async fn read_encrypted_passphrase_if_available(ctx: &MmArc) -> Walle }) .transpose() } + +pub(super) async fn read_all_wallet_names(ctx: &MmArc) -> WalletsDBResult> { + let wallets_ctx = WalletsContext::from_ctx(ctx).map_to_mm(WalletsDBError::Internal)?; + + let db = wallets_ctx.wallets_db().await?; + let transaction = db.transaction().await?; + let table = transaction.table::().await?; + + let all_items = table.get_all_items().await?; + let wallet_names = all_items.into_iter().map(|(_, item)| item.wallet_name); + + Ok(wallet_names) +} diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index b9066bf540..fec08d106f 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -5,7 +5,7 @@ use crate::lp_native_dex::init_metamask::{cancel_connect_metamask, connect_metam use crate::lp_ordermatch::{best_orders_rpc_v2, orderbook_rpc_v2, start_simple_market_maker_bot, stop_simple_market_maker_bot}; use crate::lp_swap::swap_v2_rpcs::{active_swaps_rpc, my_recent_swaps_rpc, my_swap_status_rpc}; -use crate::lp_wallet::get_mnemonic_rpc; +use crate::lp_wallet::{get_mnemonic_rpc, get_wallet_names_rpc}; use crate::rpc::rate_limiter::{process_rate_limit, RateLimitContext}; use crate::{lp_stats::{add_node_to_version_stat, remove_node_from_version_stat, start_version_stat_collection, stop_version_stat_collection, update_version_stat_collection}, @@ -190,6 +190,7 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult handle_mmrpc(ctx, request, get_raw_transaction).await, "get_shared_db_id" => handle_mmrpc(ctx, request, get_shared_db_id).await, "get_staking_infos" => handle_mmrpc(ctx, request, get_staking_infos).await, + "get_wallet_names" => handle_mmrpc(ctx, request, get_wallet_names_rpc).await, "max_maker_vol" => handle_mmrpc(ctx, request, max_maker_vol).await, "my_recent_swaps" => handle_mmrpc(ctx, request, my_recent_swaps_rpc).await, "my_swap_status" => handle_mmrpc(ctx, request, my_swap_status_rpc).await, diff --git a/mm2src/mm2_main/src/wasm_tests.rs b/mm2src/mm2_main/src/wasm_tests.rs index fbf37ca545..bd24bf4a4c 100644 --- a/mm2src/mm2_main/src/wasm_tests.rs +++ b/mm2src/mm2_main/src/wasm_tests.rs @@ -1,14 +1,14 @@ use crate::lp_init; -use common::executor::{spawn, Timer}; +use common::executor::{spawn, spawn_abortable, spawn_local_abortable, AbortOnDropHandle, Timer}; use common::log::wasm_log::register_wasm_log; use mm2_core::mm_ctx::MmArc; use mm2_number::BigDecimal; use mm2_rpc::data::legacy::OrderbookResponse; use mm2_test_helpers::electrums::{doc_electrums, marty_electrums}; use mm2_test_helpers::for_tests::{check_recent_swaps, enable_electrum_json, enable_utxo_v2_electrum, - enable_z_coin_light, morty_conf, pirate_conf, rick_conf, start_swaps, - test_qrc20_history_impl, wait_for_swaps_finish_and_check_status, MarketMakerIt, - Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, ARRR, MORTY, + enable_z_coin_light, get_wallet_names, morty_conf, pirate_conf, rick_conf, + start_swaps, test_qrc20_history_impl, wait_for_swaps_finish_and_check_status, + MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, ARRR, MORTY, PIRATE_ELECTRUMS, PIRATE_LIGHTWALLETD_URLS, RICK}; use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::{Bip44Chain, EnableCoinBalance, HDAccountAddressId}; @@ -16,6 +16,7 @@ use serde_json::json; use wasm_bindgen_test::wasm_bindgen_test; const PIRATE_TEST_BALANCE_SEED: &str = "pirate test seed"; +const STOP_TIMEOUT_MS: u64 = 1000; /// Starts the WASM version of MM. fn wasm_start(ctx: MmArc) { @@ -26,13 +27,7 @@ fn wasm_start(ctx: MmArc) { /// This function runs Alice and Bob nodes, activates coins, starts swaps, /// and then immediately stops the nodes to check if `MmArc` is dropped in a short period. -async fn test_mm2_stops_impl( - pairs: &[(&'static str, &'static str)], - maker_price: f64, - taker_price: f64, - volume: f64, - stop_timeout_ms: u64, -) { +async fn test_mm2_stops_impl(pairs: &[(&'static str, &'static str)], maker_price: f64, taker_price: f64, volume: f64) { let coins = json!([rick_conf(), morty_conf()]); let bob_passphrase = get_passphrase!(".env.seed", "BOB_PASSPHRASE").unwrap(); @@ -69,20 +64,18 @@ async fn test_mm2_stops_impl( start_swaps(&mut mm_bob, &mut mm_alice, pairs, maker_price, taker_price, volume).await; mm_alice - .stop_and_wait_for_ctx_is_dropped(stop_timeout_ms) + .stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS) .await .unwrap(); - mm_bob.stop_and_wait_for_ctx_is_dropped(stop_timeout_ms).await.unwrap(); + mm_bob.stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS).await.unwrap(); } #[wasm_bindgen_test] async fn test_mm2_stops_immediately() { - const STOP_TIMEOUT_MS: u64 = 1000; - register_wasm_log(); let pairs: &[_] = &[("RICK", "MORTY")]; - test_mm2_stops_impl(pairs, 1., 1., 0.0001, STOP_TIMEOUT_MS).await; + test_mm2_stops_impl(pairs, 1., 1., 0.0001).await; } #[wasm_bindgen_test] @@ -147,8 +140,6 @@ async fn trade_base_rel_electrum( assert_eq!(0, bob_orderbook.asks.len(), "{} {} asks must be empty", base, rel); } - const STOP_TIMEOUT_MS: u64 = 1000; - mm_bob.stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS).await.unwrap(); mm_alice .stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS) @@ -266,3 +257,46 @@ async fn activate_z_coin_light() { }; assert_eq!(balance.balance.spendable, BigDecimal::default()); } + +#[wasm_bindgen_test] +async fn test_get_wallet_names() { + const DB_NAMESPACE_NUM: u64 = 1; + + let coins = json!([]); + + // Initialize the first wallet with a specific name + let wallet_1 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_1", "pass"); + let mm_wallet_1 = + MarketMakerIt::start_with_db(wallet_1.conf, wallet_1.rpc_password, Some(wasm_start), DB_NAMESPACE_NUM) + .await + .unwrap(); + + // Retrieve and verify the wallet names for the first wallet + let get_wallet_names_1 = get_wallet_names(&mm_wallet_1).await; + assert_eq!(get_wallet_names_1.wallet_names, vec!["wallet_1"]); + assert_eq!(get_wallet_names_1.activated_wallet.unwrap(), "wallet_1"); + + // Stop the first wallet before starting the second one + mm_wallet_1 + .stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS) + .await + .unwrap(); + + // Initialize the second wallet with a different name + let wallet_2 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_2", "pass"); + let mm_wallet_2 = + MarketMakerIt::start_with_db(wallet_2.conf, wallet_2.rpc_password, Some(wasm_start), DB_NAMESPACE_NUM) + .await + .unwrap(); + + // Retrieve and verify the wallet names for the second wallet + let get_wallet_names_2 = get_wallet_names(&mm_wallet_2).await; + assert_eq!(get_wallet_names_2.wallet_names, vec!["wallet_1", "wallet_2"]); + assert_eq!(get_wallet_names_2.activated_wallet.unwrap(), "wallet_2"); + + // Stop the second wallet + mm_wallet_2 + .stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS) + .await + .unwrap(); +} diff --git a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs index 137eb6ef09..57a4709758 100644 --- a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs +++ b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs @@ -14,15 +14,15 @@ use mm2_test_helpers::electrums::*; use mm2_test_helpers::for_tests::wait_check_stats_swap_status; use mm2_test_helpers::for_tests::{account_balance, btc_segwit_conf, btc_with_spv_conf, btc_with_sync_starting_header, check_recent_swaps, enable_qrc20, enable_utxo_v2_electrum, eth_dev_conf, - find_metrics_in_json, from_env_file, get_new_address, get_shared_db_id, mm_spat, - morty_conf, my_balance, rick_conf, sign_message, start_swaps, tbtc_conf, - tbtc_segwit_conf, tbtc_with_spv_conf, test_qrc20_history_impl, tqrc20_conf, - verify_message, wait_for_swaps_finish_and_check_status, - wait_till_history_has_records, MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, - Mm2TestConfForSwap, RaiiDump, DOC_ELECTRUM_ADDRS, ETH_MAINNET_NODE, - ETH_MAINNET_SWAP_CONTRACT, ETH_SEPOLIA_NODES, ETH_SEPOLIA_SWAP_CONTRACT, - MARTY_ELECTRUM_ADDRS, MORTY, QRC20_ELECTRUMS, RICK, RICK_ELECTRUM_ADDRS, - TBTC_ELECTRUMS, T_BCH_ELECTRUMS}; + find_metrics_in_json, from_env_file, get_new_address, get_shared_db_id, + get_wallet_names, mm_spat, morty_conf, my_balance, rick_conf, sign_message, + start_swaps, tbtc_conf, tbtc_segwit_conf, tbtc_with_spv_conf, + test_qrc20_history_impl, tqrc20_conf, verify_message, + wait_for_swaps_finish_and_check_status, wait_till_history_has_records, + MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, RaiiDump, + DOC_ELECTRUM_ADDRS, ETH_MAINNET_NODE, ETH_MAINNET_SWAP_CONTRACT, ETH_SEPOLIA_NODES, + ETH_SEPOLIA_SWAP_CONTRACT, MARTY_ELECTRUM_ADDRS, MORTY, QRC20_ELECTRUMS, RICK, + RICK_ELECTRUM_ADDRS, TBTC_ELECTRUMS, T_BCH_ELECTRUMS}; use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::*; use serde_json::{self as json, json, Value as Json}; @@ -2351,7 +2351,7 @@ fn test_electrum_tx_history() { {"coin":"RICK","asset":"RICK","rpcport":8923,"txversion":4,"overwintered":1,"protocol":{"type":"UTXO"}}, ]); - let mut mm = MarketMakerIt::start( + let mm_bob = MarketMakerIt::start( json! ({ "gui": "nogui", "netid": 9998, @@ -2367,14 +2367,26 @@ fn test_electrum_tx_history() { None, ) .unwrap(); + let (_dump_log, _dump_dashboard) = mm_bob.mm_dump(); + log!("log path: {}", mm_bob.log_path.display()); + + let bob_electrum = block_on(enable_electrum(&mm_bob, "RICK", false, DOC_ELECTRUM_ADDRS)); + let mut enable_res_bob = HashMap::new(); + enable_res_bob.insert("RICK", bob_electrum); + log!("enable_coins_bob: {:?}", enable_res_bob); + + let mmconf = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet", "pass"); + let mut mm = MarketMakerIt::start(mmconf.conf, mmconf.rpc_password, None).unwrap(); let (_dump_log, _dump_dashboard) = mm.mm_dump(); log!("log path: {}", mm.log_path.display()); // Enable RICK electrum client with tx_history loop. let electrum = block_on(enable_electrum(&mm, "RICK", true, DOC_ELECTRUM_ADDRS)); + log!("enable_coins: {:?}", electrum); + let receiving_address = electrum.address; // Wait till tx_history will not be loaded - block_on(mm.wait_for_log(500., |log| log.contains("history has been loaded successfully"))).unwrap(); + block_on(mm.wait_for_log(5., |log| log.contains("history has been loaded successfully"))).unwrap(); // tx_history is requested every 30 seconds, wait another iteration thread::sleep(Duration::from_secs(31)); @@ -2384,16 +2396,13 @@ fn test_electrum_tx_history() { assert_eq!(get_tx_history_request_count(&mm), 1); // make a transaction to change balance - let mut enable_res = HashMap::new(); - enable_res.insert("RICK", electrum); - log!("enable_coins: {:?}", enable_res); withdraw_and_send( - &mm, + &mm_bob, "RICK", None, - "RRYmiZSDo3UdHHqj1rLKf8cbJroyv9NxXw", - &enable_res, - "-0.00001", + &receiving_address, + &enable_res_bob, + "-0.00101", 0.001, ); @@ -5809,6 +5818,41 @@ fn test_get_shared_db_id() { ); } +#[test] +#[cfg(not(target_arch = "wasm32"))] +fn test_get_wallet_names() { + let coins = json!([]); + + // Initialize the first wallet with a specific name + let wallet_1 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_1", "pass"); + let mm_wallet_1 = MarketMakerIt::start(wallet_1.conf, wallet_1.rpc_password, None).unwrap(); + + // Retrieve and verify the wallet names for the first wallet + let get_wallet_names_1 = block_on(get_wallet_names(&mm_wallet_1)); + assert_eq!(get_wallet_names_1.wallet_names, vec!["wallet_1"]); + assert_eq!(get_wallet_names_1.activated_wallet.unwrap(), "wallet_1"); + + // Initialize the second wallet with a different name + let mut wallet_2 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_2", "pass"); + + // Set the database directory for the second wallet to the same as the first wallet + wallet_2.conf["dbdir"] = mm_wallet_1.folder.join("DB").to_str().unwrap().into(); + + // Stop the first wallet before starting the second one + block_on(mm_wallet_1.stop()).unwrap(); + + // Start the second wallet + let mm_wallet_2 = MarketMakerIt::start(wallet_2.conf, wallet_2.rpc_password, None).unwrap(); + + // Retrieve and verify the wallet names for the second wallet + let get_wallet_names_2 = block_on(get_wallet_names(&mm_wallet_2)); + assert_eq!(get_wallet_names_2.wallet_names, vec!["wallet_1", "wallet_2"]); + assert_eq!(get_wallet_names_2.activated_wallet.unwrap(), "wallet_2"); + + // Stop the second wallet + block_on(mm_wallet_2.stop()).unwrap(); +} + #[test] #[cfg(not(target_arch = "wasm32"))] fn test_sign_raw_transaction_rick() { diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index 57084a72e2..df5a488cf9 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -310,6 +310,21 @@ impl Mm2TestConf { } } + pub fn seednode_with_wallet_name(coins: &Json, wallet_name: &str, wallet_password: &str) -> Self { + Mm2TestConf { + conf: json!({ + "gui": "nogui", + "netid": 9998, + "coins": coins, + "rpc_password": DEFAULT_RPC_PASSWORD, + "i_am_seed": true, + "wallet_name": wallet_name, + "wallet_password": wallet_password, + }), + rpc_password: DEFAULT_RPC_PASSWORD.into(), + } + } + pub fn light_node(passphrase: &str, coins: &Json, seednodes: &[&str]) -> Self { Mm2TestConf { conf: json!({ @@ -1351,17 +1366,49 @@ impl MarketMakerIt { /// Start a new MarketMaker locally. /// /// * `conf` - The command-line configuration passed to the MarketMaker. + /// * `userpass` - RPC API key. + /// * `local` - Function to start the MarketMaker locally. + /// * `envs` - The environment variables passed to the process. + /// The argument is ignored for nodes running in a browser. + #[cfg(target_arch = "wasm32")] + pub async fn start_with_envs( + conf: Json, + userpass: String, + local: Option, + _envs: &[(&str, &str)], + ) -> Result { + MarketMakerIt::start_market_maker(conf, userpass, local, None).await + } + + /// Start a new MarketMaker locally with a specific database namespace. + /// + /// * `conf` - The command-line configuration passed to the MarketMaker. + /// * `userpass` - RPC API key. + /// * `local` - Function to start the MarketMaker locally. + /// * `db_namespace_id` - The test database namespace identifier. + #[cfg(target_arch = "wasm32")] + pub async fn start_with_db( + conf: Json, + userpass: String, + local: Option, + db_namespace_id: u64, + ) -> Result { + MarketMakerIt::start_market_maker(conf, userpass, local, Some(db_namespace_id)).await + } + + /// Common helper function to start the MarketMaker. + /// + /// * `conf` - The command-line configuration passed to the MarketMaker. /// Unique P2P in-memory port is injected as `p2p_in_memory_port` unless this field is already present. /// * `userpass` - RPC API key. We should probably extract it automatically from the MM log. /// * `local` - Function to start the MarketMaker locally. Required for nodes running in a browser. - /// * `envs` - The enviroment variables passed to the process. - /// The argument is ignore for nodes running in a browser. + /// * `db_namespace_id` - Optional test database namespace identifier. #[cfg(target_arch = "wasm32")] - pub async fn start_with_envs( + async fn start_market_maker( mut conf: Json, userpass: String, local: Option, - _envs: &[(&str, &str)], + db_namespace_id: Option, ) -> Result { if conf["p2p_in_memory"].is_null() { conf["p2p_in_memory"] = Json::Bool(true); @@ -1377,10 +1424,19 @@ impl MarketMakerIt { conf["p2p_in_memory_port"] = Json::Number(new_p2p_port.into()); } - let ctx = mm2_core::mm_ctx::MmCtxBuilder::new() - .with_conf(conf.clone()) - .with_test_db_namespace() - .into_mm_arc(); + let ctx = { + let builder = MmCtxBuilder::new() + .with_conf(conf.clone()); + + let builder = if let Some(ns) = db_namespace_id { + builder.with_test_db_namespace_with_id(ns) + } else { + builder.with_test_db_namespace() + }; + + builder.into_mm_arc() + }; + let local = try_s!(local.ok_or("!local")); local(ctx.clone()); @@ -2838,6 +2894,20 @@ pub async fn get_shared_db_id(mm: &MarketMakerIt) -> GetSharedDbIdResult { res.result } +pub async fn get_wallet_names(mm: &MarketMakerIt) -> GetWalletNamesResult { + let request = mm + .rpc(&json!({ + "userpass": mm.userpass, + "method": "get_wallet_names", + "mmrpc": "2.0", + })) + .await + .unwrap(); + assert_eq!(request.0, StatusCode::OK, "'get_wallet_names' failed: {}", request.1); + let res: RpcSuccessResponse<_> = json::from_str(&request.1).unwrap(); + res.result +} + pub async fn max_maker_vol(mm: &MarketMakerIt, coin: &str) -> RpcResponse { let rc = mm .rpc(&json!({ diff --git a/mm2src/mm2_test_helpers/src/structs.rs b/mm2src/mm2_test_helpers/src/structs.rs index aa2246186c..8bdcfec722 100644 --- a/mm2src/mm2_test_helpers/src/structs.rs +++ b/mm2src/mm2_test_helpers/src/structs.rs @@ -827,6 +827,13 @@ pub struct GetSharedDbIdResult { pub shared_db_id: String, } +#[derive(Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct GetWalletNamesResult { + pub wallet_names: Vec, + pub activated_wallet: Option, +} + #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct RpcV2Response { From 8e19bbd3d236131cc7daba0baaf968d141351971 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Wed, 25 Sep 2024 20:39:44 +0300 Subject: [PATCH 09/16] fix(merge): remove duplicated db_root function (#2229) --- mm2src/mm2_core/src/mm_ctx.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index 54d5f71004..c61b4e1aba 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -290,10 +290,6 @@ impl MmCtx { /// Returns the path to the MM databases root. #[cfg(not(target_arch = "wasm32"))] pub fn db_root(&self) -> PathBuf { path_to_db_root(self.conf["dbdir"].as_str()) } - - #[cfg(not(target_arch = "wasm32"))] - pub fn db_root(&self) -> PathBuf { path_to_db_root(self.conf["dbdir"].as_str()) } - #[cfg(not(target_arch = "wasm32"))] pub fn wallet_file_path(&self, wallet_name: &str) -> PathBuf { self.db_root().join(wallet_name.to_string() + ".dat") From 7ef08e47667c9cf20b900733619107d47ef0aa1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Thu, 26 Sep 2024 05:00:34 +0300 Subject: [PATCH 10/16] chore(doc): update issue address in README (#2227) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bec7bd1166..441c23ba8c 100755 --- a/README.md +++ b/README.md @@ -193,4 +193,5 @@ The current state can be considered as an alpha version. ## Help and troubleshooting -If you have any question/want to report a bug/suggest an improvement feel free to [open an issue](https://github.com/artemii235/SuperNET/issues/new) or join the [Komodo Platform Discord](https://discord.gg/PGxVm2y) `dev-marketmaker` channel. +If you have any question/want to report a bug/suggest an improvement feel free to [open an issue](https://github.com/KomodoPlatform/komodo-defi-framework/issues/new/choose) or join the [Komodo Platform Discord](https://discord.gg/PGxVm2y) `dev-marketmaker` channel. + From 6dc3c7da28af9e539c0cd632c7030f2c1d57d86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Fri, 27 Sep 2024 12:33:34 +0300 Subject: [PATCH 11/16] feat(CI): handle remote files in a safer way (#2217) This commit updates GHA runners to use locked scripts rather than always using the latest one from the master branch. It also adds a new GHA helper/plugin to easily download files and verify their checksums. --- .github/actions/deps-install/action.yml | 29 ++++++++++-- .../actions/download-and-verify/action.yml | 46 +++++++++++++++++++ .github/workflows/test.yml | 24 +++++++--- 3 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 .github/actions/download-and-verify/action.yml diff --git a/.github/actions/deps-install/action.yml b/.github/actions/deps-install/action.yml index 25ed15bf50..02b0ffbec4 100644 --- a/.github/actions/deps-install/action.yml +++ b/.github/actions/deps-install/action.yml @@ -12,33 +12,54 @@ inputs: runs: using: 'composite' steps: + - name: Download protoc (Linux) + if: runner.os == 'Linux' && contains(inputs.deps, 'protoc') + uses: ./.github/actions/download-and-verify + with: + url: "https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-linux-x86_64.zip" + output_file: "protoc-25.3-linux-x86_64.zip" + checksum: "f853e691868d0557425ea290bf7ba6384eef2fa9b04c323afab49a770ba9da80" + - name: Install protoc (Linux) env: TMP: ${{ inputs.temp || runner.temp }} if: runner.os == 'Linux' && contains(inputs.deps, 'protoc') shell: bash run: | - wget https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-linux-x86_64.zip - unzip protoc-25.3-linux-x86_64 -d "$TMP/protobuf" + unzip protoc-25.3-linux-x86_64.zip -d "$TMP/protobuf" echo "$TMP/protobuf/bin" >> $GITHUB_PATH + - name: Download protoc (MacOS) + if: runner.os == 'macOS' && contains(inputs.deps, 'protoc') + uses: ./.github/actions/download-and-verify + with: + url: "https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-osx-x86_64.zip" + output_file: "protoc-25.3-osx-x86_64.zip" + checksum: "247e003b8e115405172eacc50bd19825209d85940728e766f0848eee7c80e2a1" + - name: Install protoc (MacOS) env: TMP: ${{ inputs.temp || runner.temp }} if: runner.os == 'macOS' && contains(inputs.deps, 'protoc') shell: bash run: | - wget https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-osx-x86_64.zip unzip protoc-25.3-osx-x86_64.zip -d "$TMP/protobuf" echo "$TMP/protobuf/bin" >> $GITHUB_PATH + + - name: Download protoc (Windows) + uses: ./.github/actions/download-and-verify + with: + url: "https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-win64.zip" + output_file: "protoc-25.3-win64.zip" + checksum: "d6b336b852726364313330631656b7f395dde5b1141b169f5c4b8d43cdf01482" + - name: Install protoc (Windows) env: TMP: ${{ inputs.temp || runner.temp }} if: runner.os == 'Windows' && contains(inputs.deps, 'protoc') shell: powershell run: | - Invoke-WebRequest -Uri https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-win64.zip -OutFile protoc-25.3-win64.zip 7z x protoc-25.3-win64.zip -o"$TMP\protobuf" echo "$TMP\protobuf\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append diff --git a/.github/actions/download-and-verify/action.yml b/.github/actions/download-and-verify/action.yml new file mode 100644 index 0000000000..198601f486 --- /dev/null +++ b/.github/actions/download-and-verify/action.yml @@ -0,0 +1,46 @@ +name: "Download and verify remote files" + +runs: + using: "composite" + steps: + - name: Download (Unix) + if: runner.os != 'Windows' + shell: bash + run: curl -L -o ${{ inputs.output_file }} ${{ inputs.url }} + + - name: Download (Windows) + if: runner.os == 'Windows' + shell: powershell + run: Invoke-WebRequest -Uri ${{ inputs.url }} -OutFile ${{ inputs.output_file }} + + - name: Verify (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + if [[ "$RUNNER_OS" == "macOS" ]]; then + echo "${{ inputs.checksum }} *${{ inputs.output_file }}" | shasum -a 256 -c + else + echo "${{ inputs.checksum }} ${{ inputs.output_file }}" | sha256sum -c + fi + + - name: Verify (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + $expectedChecksum = "${{ inputs.checksum }}" + $actualChecksum = (Get-FileHash -Path "${{ inputs.output_file }}" -Algorithm SHA256).Hash + if ($expectedChecksum -ne $actualChecksum) { + Write-Output "Checksum did not match! Expected: $expectedChecksum, Found: $actualChecksum" + exit 1 + } + +inputs: + url: + description: "URL of the remote file." + required: true + output_file: + description: "Output path." + required: true + checksum: + description: "Expected checksum of the downloaded file." + required: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a9790f9b13..12a60bbc3c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -122,7 +122,7 @@ jobs: - name: Test run: | - wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/master/zcutil/fetch-params-alt.sh | bash + wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/0adeeabdd484ef40539d1275c6a765f5c530ea79/zcutil/fetch-params-alt.sh | bash cargo test --test 'mm2_tests_main' --no-fail-fast mac-x86-64-kdf-integration: @@ -154,7 +154,7 @@ jobs: - name: Test run: | - wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/master/zcutil/fetch-params-alt.sh | bash + wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/0adeeabdd484ef40539d1275c6a765f5c530ea79/zcutil/fetch-params-alt.sh | bash cargo test --test 'mm2_tests_main' --no-fail-fast win-x86-64-kdf-integration: @@ -181,10 +181,16 @@ jobs: - name: Cargo cache uses: ./.github/actions/cargo-cache + - name: Download wget64 + uses: ./.github/actions/download-and-verify + with: + url: "https://github.com/KomodoPlatform/komodo/raw/d456be35acd1f8584e1e4f971aea27bd0644d5c5/zcutil/wget64.exe" + output_file: "/wget64.exe" + checksum: "d80719431dc22b0e4a070f61fab982b113a4ed9a6d4cf25e64b5be390dcadb94" + - name: Test run: | - Invoke-WebRequest -Uri https://github.com/KomodoPlatform/komodo/raw/d456be35acd1f8584e1e4f971aea27bd0644d5c5/zcutil/wget64.exe -OutFile \wget64.exe - Invoke-WebRequest -Uri https://raw.githubusercontent.com/KomodoPlatform/komodo/master/zcutil/fetch-params-alt.bat -OutFile \cmd.bat && \cmd.bat + Invoke-WebRequest -Uri https://raw.githubusercontent.com/KomodoPlatform/komodo/0adeeabdd484ef40539d1275c6a765f5c530ea79/zcutil/fetch-params-alt.bat -OutFile \cmd.bat && \cmd.bat cargo test --test 'mm2_tests_main' --no-fail-fast docker-tests: @@ -213,7 +219,7 @@ jobs: - name: Test run: | - wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/v0.8.1/zcutil/fetch-params-alt.sh | bash + wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/v0.8.1//zcutil/fetch-params-alt.sh | bash cargo test --test 'docker_tests_main' --features run-docker-tests --no-fail-fast wasm: @@ -241,11 +247,17 @@ jobs: - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: Download geckodriver + uses: ./.github/actions/download-and-verify + with: + url: "https://github.com/mozilla/geckodriver/releases/download/v0.32.2/geckodriver-v0.32.2-linux64.tar.gz" + output_file: "geckodriver-v0.32.2-linux64.tar.gz" + checksum: "1eab226bf009599f5aa1d77d9ed4c374e10a03fd848b500be1b32cefd2cbec64" + - name: Install firefox and geckodriver run: | sudo apt-get update -y sudo apt-get install -y firefox - wget https://github.com/mozilla/geckodriver/releases/download/v0.32.2/geckodriver-v0.32.2-linux64.tar.gz sudo tar -xzvf geckodriver-v0.32.2-linux64.tar.gz -C /bin sudo chmod +x /bin/geckodriver From 76d43423ed379b9c2db1338f61505cf2c8bf6779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Tue, 1 Oct 2024 11:27:55 +0300 Subject: [PATCH 12/16] fix(proxy-signature): add message lifetime overflows (#2233) This commit prevents creating messages for proxy with too long lifetimes. --- mm2src/proxy_signature/src/lib.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/mm2src/proxy_signature/src/lib.rs b/mm2src/proxy_signature/src/lib.rs index f4ae2393a0..15cf596c89 100644 --- a/mm2src/proxy_signature/src/lib.rs +++ b/mm2src/proxy_signature/src/lib.rs @@ -2,6 +2,7 @@ use chrono::Utc; use http::Uri; use libp2p::identity::{Keypair, PublicKey, SigningError}; use serde::{Deserialize, Serialize}; +use std::convert::TryFrom; /// Represents a message and its corresponding signature. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] @@ -69,8 +70,11 @@ impl RawMessage { impl ProxySign { /// Validates if the message is still valid based on its expiration time and signature verification. - pub fn is_valid_message(&self) -> bool { - if Utc::now().timestamp() > self.raw_message.expires_at { + pub fn is_valid_message(&self, max_message_exp_secs: u64) -> bool { + let now = Utc::now().timestamp(); + let remaining_expiration_seconds = u64::try_from(self.raw_message.expires_at - now).unwrap_or(0); + + if remaining_expiration_seconds == 0 || remaining_expiration_seconds > max_message_exp_secs { return false; } @@ -111,14 +115,14 @@ pub mod proxy_signature_tests { fn sign_and_verify() { let keypair = random_keypair(); let signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); - assert!(signed_proxy_message.is_valid_message()); + assert!(signed_proxy_message.is_valid_message(10)); } #[test] fn expired_signature() { let keypair = random_keypair(); let signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, -1).unwrap(); - assert!(!signed_proxy_message.is_valid_message()); + assert!(!signed_proxy_message.is_valid_message(10)); } #[test] @@ -127,17 +131,24 @@ pub mod proxy_signature_tests { let mut signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); signed_proxy_message.raw_message.uri = "http://demo.com".to_string(); - assert!(!signed_proxy_message.is_valid_message()); + assert!(!signed_proxy_message.is_valid_message(10)); let mut signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); signed_proxy_message.raw_message.body_size += 1; - assert!(!signed_proxy_message.is_valid_message()); + assert!(!signed_proxy_message.is_valid_message(10)); let mut signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); signed_proxy_message.raw_message.expires_at += 1; - assert!(!signed_proxy_message.is_valid_message()); + assert!(!signed_proxy_message.is_valid_message(10)); + } + + #[test] + fn message_lifetime_overflow() { + let keypair = random_keypair(); + let signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); + assert!(!signed_proxy_message.is_valid_message(4)); } #[test] From 1f3dffa167cbf088870ad697205a982d9f735256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Thu, 3 Oct 2024 21:11:17 +0300 Subject: [PATCH 13/16] feat(new-RPC): connection healthcheck implementation for peers (#2194) --- mm2src/common/expirable_map.rs | 22 +- mm2src/mm2_core/src/mm_ctx.rs | 11 +- mm2src/mm2_main/Cargo.toml | 1 + mm2src/mm2_main/src/lp_healthcheck.rs | 513 ++++++++++++++++++ mm2src/mm2_main/src/lp_native_dex.rs | 11 +- mm2src/mm2_main/src/lp_network.rs | 6 +- mm2src/mm2_main/src/mm2.rs | 1 + .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 2 + .../tests/mm2_tests/mm2_tests_inner.rs | 42 +- mm2src/mm2_test_helpers/src/for_tests.rs | 38 +- 10 files changed, 629 insertions(+), 18 deletions(-) create mode 100644 mm2src/mm2_main/src/lp_healthcheck.rs diff --git a/mm2src/common/expirable_map.rs b/mm2src/common/expirable_map.rs index 9b85dea84c..e9fe7f0b4f 100644 --- a/mm2src/common/expirable_map.rs +++ b/mm2src/common/expirable_map.rs @@ -14,9 +14,26 @@ pub struct ExpirableEntry { } impl ExpirableEntry { + #[inline(always)] + pub fn new(v: V, exp: Duration) -> Self { + Self { + expires_at: Instant::now() + exp, + value: v, + } + } + + #[inline(always)] pub fn get_element(&self) -> &V { &self.value } + #[inline(always)] + pub fn update_value(&mut self, v: V) { self.value = v } + + #[inline(always)] pub fn update_expiration(&mut self, expires_at: Instant) { self.expires_at = expires_at } + + /// Checks whether entry has longer ttl than the given one. + #[inline(always)] + pub fn has_longer_life_than(&self, min_ttl: Duration) -> bool { self.expires_at > Instant::now() + min_ttl } } impl Default for ExpirableMap { @@ -47,10 +64,7 @@ impl ExpirableMap { /// If a value already exists for the given key, it will be updated and then /// the old one will be returned. pub fn insert(&mut self, k: K, v: V, exp: Duration) -> Option { - let entry = ExpirableEntry { - expires_at: Instant::now() + exp, - value: v, - }; + let entry = ExpirableEntry::new(v, exp); self.0.insert(k, entry).map(|v| v.value) } diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index c61b4e1aba..6e060d91c3 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -1,9 +1,12 @@ #[cfg(feature = "track-ctx-pointer")] use common::executor::Timer; -use common::executor::{abortable_queue::{AbortableQueue, WeakSpawner}, - graceful_shutdown, AbortSettings, AbortableSystem, SpawnAbortable, SpawnFuture}; use common::log::{self, LogLevel, LogOnError, LogState}; use common::{cfg_native, cfg_wasm32, small_rng}; +use common::{executor::{abortable_queue::{AbortableQueue, WeakSpawner}, + graceful_shutdown, AbortSettings, AbortableSystem, SpawnAbortable, SpawnFuture}, + expirable_map::ExpirableMap}; +use futures::channel::oneshot; +use futures::lock::Mutex as AsyncMutex; use gstuff::{try_s, Constructible, ERR, ERRL}; use lazy_static::lazy_static; use mm2_event_stream::{controller::Controller, Event, EventStreamConfiguration}; @@ -30,7 +33,6 @@ cfg_wasm32! { cfg_native! { use db_common::async_sql_conn::AsyncConnection; use db_common::sqlite::rusqlite::Connection; - use futures::lock::Mutex as AsyncMutex; use rustls::ServerName; use mm2_metrics::prometheus; use mm2_metrics::MmMetricsError; @@ -142,6 +144,8 @@ pub struct MmCtx { /// asynchronous handle for rusqlite connection. #[cfg(not(target_arch = "wasm32"))] pub async_sqlite_connection: Constructible>>, + /// Links the RPC context to the P2P context to handle health check responses. + pub healthcheck_response_handler: AsyncMutex>>, } impl MmCtx { @@ -191,6 +195,7 @@ impl MmCtx { nft_ctx: Mutex::new(None), #[cfg(not(target_arch = "wasm32"))] async_sqlite_connection: Constructible::default(), + healthcheck_response_handler: AsyncMutex::new(ExpirableMap::default()), } } diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 8f84ebb90a..53154efa05 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -32,6 +32,7 @@ bitcrypto = { path = "../mm2_bitcoin/crypto" } blake2 = "0.10.6" bytes = "0.4" chain = { path = "../mm2_bitcoin/chain" } +chrono = "0.4" cfg-if = "1.0" coins = { path = "../coins" } coins_activation = { path = "../coins_activation" } diff --git a/mm2src/mm2_main/src/lp_healthcheck.rs b/mm2src/mm2_main/src/lp_healthcheck.rs new file mode 100644 index 0000000000..722bc21402 --- /dev/null +++ b/mm2src/mm2_main/src/lp_healthcheck.rs @@ -0,0 +1,513 @@ +use async_std::prelude::FutureExt; +use chrono::Utc; +use common::executor::SpawnFuture; +use common::expirable_map::ExpirableEntry; +use common::{log, HttpStatusCode, StatusCode}; +use derive_more::Display; +use futures::channel::oneshot::{self, Receiver, Sender}; +use instant::{Duration, Instant}; +use lazy_static::lazy_static; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::MmError; +use mm2_libp2p::{decode_message, encode_message, pub_sub_topic, Libp2pPublic, TopicPrefix}; +use mm2_net::p2p::P2PContext; +use ser_error_derive::SerializeErrorType; +use serde::{Deserialize, Serialize}; +use std::convert::TryFrom; +use std::str::FromStr; +use std::sync::Mutex; + +use crate::lp_network::broadcast_p2p_msg; + +pub(crate) const PEER_HEALTHCHECK_PREFIX: TopicPrefix = "hcheck"; + +const fn healthcheck_message_exp_secs() -> u64 { + #[cfg(test)] + return 3; + + #[cfg(not(test))] + 10 +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(any(test, target_arch = "wasm32"), derive(PartialEq))] +pub(crate) struct HealthcheckMessage { + #[serde(deserialize_with = "deserialize_bytes")] + signature: Vec, + data: HealthcheckData, +} + +/// Wrapper of `libp2p::PeerId` with trait additional implementations. +/// +/// TODO: This should be used as a replacement of `libp2p::PeerId` in the entire project. +#[derive(Clone, Copy, Debug, Display, PartialEq)] +pub struct PeerAddress(mm2_libp2p::PeerId); + +impl From for PeerAddress { + fn from(value: mm2_libp2p::PeerId) -> Self { Self(value) } +} + +impl From for mm2_libp2p::PeerId { + fn from(value: PeerAddress) -> Self { value.0 } +} + +impl Serialize for PeerAddress { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.0.to_string()) + } +} + +impl<'de> Deserialize<'de> for PeerAddress { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct PeerAddressVisitor; + + impl<'de> serde::de::Visitor<'de> for PeerAddressVisitor { + type Value = PeerAddress; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string representation of peer id.") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + if value.len() > 100 { + return Err(serde::de::Error::invalid_length( + value.len(), + &"peer id cannot exceed 100 characters.", + )); + } + + Ok(mm2_libp2p::PeerId::from_str(value) + .map_err(serde::de::Error::custom)? + .into()) + } + + fn visit_string(self, value: String) -> Result + where + E: serde::de::Error, + { + self.visit_str(&value) + } + } + + deserializer.deserialize_str(PeerAddressVisitor) + } +} + +#[derive(Debug, Display)] +enum SignValidationError { + #[display( + fmt = "Healthcheck message is expired. Current time in UTC: {now_secs}, healthcheck `expires_at` in UTC: {expires_at_secs}" + )] + Expired { now_secs: u64, expires_at_secs: u64 }, + #[display( + fmt = "Healthcheck message have too high expiration time. Max allowed expiration seconds: {max_allowed_expiration_secs}, received message expiration seconds: {remaining_expiration_secs}" + )] + LifetimeOverflow { + max_allowed_expiration_secs: u64, + remaining_expiration_secs: u64, + }, + #[display(fmt = "Public key is not valid.")] + InvalidPublicKey, + #[display(fmt = "Signature integrity doesn't match with the public key.")] + FakeSignature, + #[display(fmt = "Process failed unexpectedly due to this reason: {reason}")] + Internal { reason: String }, +} + +impl HealthcheckMessage { + pub(crate) fn generate_message(ctx: &MmArc, is_a_reply: bool) -> Result { + let p2p_ctx = P2PContext::fetch_from_mm_arc(ctx); + let keypair = p2p_ctx.keypair(); + let sender_public_key = keypair.public().encode_protobuf(); + + let data = HealthcheckData { + sender_public_key, + expires_at_secs: u64::try_from(Utc::now().timestamp()).map_err(|e| e.to_string())? + + healthcheck_message_exp_secs(), + is_a_reply, + }; + + let signature = try_s!(keypair.sign(&try_s!(data.encode()))); + + Ok(Self { signature, data }) + } + + fn generate_or_use_cached_message(ctx: &MmArc) -> Result { + const MIN_DURATION_FOR_REUSABLE_MSG: Duration = Duration::from_secs(5); + + lazy_static! { + static ref RECENTLY_GENERATED_MESSAGE: Mutex> = + Mutex::new(ExpirableEntry::new( + // Using dummy values in order to initialize `HealthcheckMessage` context. + HealthcheckMessage { + signature: vec![], + data: HealthcheckData { + sender_public_key: vec![], + expires_at_secs: 0, + is_a_reply: false, + }, + }, + Duration::from_secs(0) + )); + } + + // If recently generated message has longer life than `MIN_DURATION_FOR_REUSABLE_MSG`, we can reuse it to + // reduce the message generation overhead under high pressure. + let mut mutexed_msg = RECENTLY_GENERATED_MESSAGE.lock().unwrap(); + + if mutexed_msg.has_longer_life_than(MIN_DURATION_FOR_REUSABLE_MSG) { + Ok(mutexed_msg.get_element().clone()) + } else { + let new_msg = HealthcheckMessage::generate_message(ctx, true)?; + + mutexed_msg.update_value(new_msg.clone()); + mutexed_msg.update_expiration(Instant::now() + Duration::from_secs(healthcheck_message_exp_secs())); + + Ok(new_msg) + } + } + + fn is_received_message_valid(&self) -> Result { + let now_secs = u64::try_from(Utc::now().timestamp()) + .map_err(|e| SignValidationError::Internal { reason: e.to_string() })?; + + let remaining_expiration_secs = self.data.expires_at_secs - now_secs; + + if remaining_expiration_secs == 0 { + return Err(SignValidationError::Expired { + now_secs, + expires_at_secs: self.data.expires_at_secs, + }); + } else if remaining_expiration_secs > healthcheck_message_exp_secs() { + return Err(SignValidationError::LifetimeOverflow { + max_allowed_expiration_secs: healthcheck_message_exp_secs(), + remaining_expiration_secs, + }); + } + + let Ok(public_key) = Libp2pPublic::try_decode_protobuf(&self.data.sender_public_key) else { + log::debug!("Couldn't decode public key from the healthcheck message."); + + return Err(SignValidationError::InvalidPublicKey); + }; + + let encoded_message = self + .data + .encode() + .map_err(|e| SignValidationError::Internal { reason: e.to_string() })?; + + if public_key.verify(&encoded_message, &self.signature) { + Ok(public_key.to_peer_id().into()) + } else { + Err(SignValidationError::FakeSignature) + } + } + + #[inline] + pub(crate) fn encode(&self) -> Result, rmp_serde::encode::Error> { encode_message(self) } + + #[inline] + pub(crate) fn decode(bytes: &[u8]) -> Result { decode_message(bytes) } + + #[inline] + pub(crate) fn should_reply(&self) -> bool { !self.data.is_a_reply } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(any(test, target_arch = "wasm32"), derive(PartialEq))] +struct HealthcheckData { + #[serde(deserialize_with = "deserialize_bytes")] + sender_public_key: Vec, + expires_at_secs: u64, + is_a_reply: bool, +} + +impl HealthcheckData { + #[inline] + fn encode(&self) -> Result, rmp_serde::encode::Error> { encode_message(self) } +} + +#[inline] +pub fn peer_healthcheck_topic(peer_address: &PeerAddress) -> String { + pub_sub_topic(PEER_HEALTHCHECK_PREFIX, &peer_address.to_string()) +} + +#[derive(Deserialize)] +pub struct RequestPayload { + peer_address: PeerAddress, +} + +fn deserialize_bytes<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + struct ByteVisitor; + + impl<'de> serde::de::Visitor<'de> for ByteVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a non-empty byte array up to 512 bytes") + } + + fn visit_seq(self, mut seq: A) -> Result, A::Error> + where + A: serde::de::SeqAccess<'de>, + { + let mut buffer = vec![]; + while let Some(byte) = seq.next_element()? { + if buffer.len() >= 512 { + return Err(serde::de::Error::invalid_length( + buffer.len(), + &"longest possible length allowed for this field is 512 bytes (with RSA algorithm).", + )); + } + + buffer.push(byte); + } + + if buffer.is_empty() { + return Err(serde::de::Error::custom("Can't be empty.")); + } + + Ok(buffer) + } + } + + deserializer.deserialize_seq(ByteVisitor) +} + +#[derive(Debug, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum HealthcheckRpcError { + MessageGenerationFailed { reason: String }, + MessageEncodingFailed { reason: String }, + Internal { reason: String }, +} + +impl HttpStatusCode for HealthcheckRpcError { + fn status_code(&self) -> common::StatusCode { + match self { + HealthcheckRpcError::MessageGenerationFailed { .. } + | HealthcheckRpcError::Internal { .. } + | HealthcheckRpcError::MessageEncodingFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +pub async fn peer_connection_healthcheck_rpc( + ctx: MmArc, + req: RequestPayload, +) -> Result> { + // When things go awry, we want records to clear themselves to keep the memory clean of unused data. + // This is unrelated to the timeout logic. + let address_record_exp = Duration::from_secs(healthcheck_message_exp_secs()); + + let target_peer_address = req.peer_address; + + let p2p_ctx = P2PContext::fetch_from_mm_arc(&ctx); + if target_peer_address == p2p_ctx.peer_id().into() { + // That's us, so return true. + return Ok(true); + } + + let message = HealthcheckMessage::generate_message(&ctx, false) + .map_err(|reason| HealthcheckRpcError::MessageGenerationFailed { reason })?; + + let encoded_message = message + .encode() + .map_err(|e| HealthcheckRpcError::MessageEncodingFailed { reason: e.to_string() })?; + + let (tx, rx): (Sender<()>, Receiver<()>) = oneshot::channel(); + + { + let mut book = ctx.healthcheck_response_handler.lock().await; + book.clear_expired_entries(); + book.insert(target_peer_address.to_string(), tx, address_record_exp); + } + + broadcast_p2p_msg( + &ctx, + peer_healthcheck_topic(&target_peer_address), + encoded_message, + None, + ); + + let timeout_duration = Duration::from_secs(healthcheck_message_exp_secs()); + Ok(rx.timeout(timeout_duration).await == Ok(Ok(()))) +} + +pub(crate) async fn process_p2p_healthcheck_message(ctx: &MmArc, message: mm2_libp2p::GossipsubMessage) { + macro_rules! try_or_return { + ($exp:expr, $msg: expr) => { + match $exp { + Ok(t) => t, + Err(e) => { + log::error!("{}, error: {e:?}", $msg); + return; + }, + } + }; + } + + let data = try_or_return!( + HealthcheckMessage::decode(&message.data), + "Couldn't decode healthcheck message" + ); + + let ctx = ctx.clone(); + + // Pass the remaining work to another thread to free up this one as soon as possible, + // so KDF can handle a high amount of healthcheck messages more efficiently. + ctx.spawner().spawn(async move { + let sender_peer = match data.is_received_message_valid() { + Ok(t) => t, + Err(e) => { + log::error!("Received an invalid healthcheck message. Error: {e}"); + return; + }, + }; + + if data.should_reply() { + // Reply the message so they know we are healthy. + + let msg = try_or_return!( + HealthcheckMessage::generate_or_use_cached_message(&ctx), + "Couldn't generate the healthcheck message, this is very unusual!" + ); + + let encoded_msg = try_or_return!( + msg.encode(), + "Couldn't encode healthcheck message, this is very unusual!" + ); + + let topic = peer_healthcheck_topic(&sender_peer); + broadcast_p2p_msg(&ctx, topic, encoded_msg, None); + } else { + // The requested peer is healthy; signal the response channel. + let mut response_handler = ctx.healthcheck_response_handler.lock().await; + if let Some(tx) = response_handler.remove(&sender_peer.to_string()) { + if tx.send(()).is_err() { + log::error!("Result channel isn't present for peer '{sender_peer}'."); + }; + } else { + log::info!("Peer '{sender_peer}' isn't recorded in the healthcheck response handler."); + }; + } + }); +} + +#[cfg(any(test, target_arch = "wasm32"))] +mod tests { + use std::mem::discriminant; + + use super::*; + use common::cross_test; + use crypto::CryptoCtx; + use mm2_libp2p::behaviours::atomicdex::generate_ed25519_keypair; + use mm2_test_helpers::for_tests::mm_ctx_with_iguana; + + common::cfg_wasm32! { + use wasm_bindgen_test::*; + wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + } + + fn ctx() -> MmArc { + let ctx = mm_ctx_with_iguana(Some("dummy-value")); + let p2p_key = { + let crypto_ctx = CryptoCtx::from_ctx(&ctx).unwrap(); + let key = bitcrypto::sha256(crypto_ctx.mm2_internal_privkey_slice()); + key.take() + }; + + let (cmd_tx, _) = futures::channel::mpsc::channel(0); + + let p2p_context = P2PContext::new(cmd_tx, generate_ed25519_keypair(p2p_key)); + p2p_context.store_to_mm_arc(&ctx); + + ctx + } + + cross_test!(test_peer_address, { + #[derive(Deserialize, Serialize)] + struct PeerAddressTest { + peer_address: PeerAddress, + } + + let address_str = "12D3KooWEtuv7kmgGCC7oAQ31hB7AR5KkhT3eEWB2bP2roo3M7rY"; + let json_content = format!("{{\"peer_address\": \"{address_str}\"}}"); + let address_struct: PeerAddressTest = serde_json::from_str(&json_content).unwrap(); + + let actual_peer_id = mm2_libp2p::PeerId::from_str(address_str).unwrap(); + let deserialized_peer_id: mm2_libp2p::PeerId = address_struct.peer_address.into(); + + assert_eq!(deserialized_peer_id, actual_peer_id); + }); + + cross_test!(test_valid_message, { + let ctx = ctx(); + let message = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + message.is_received_message_valid().unwrap(); + }); + + cross_test!(test_corrupted_messages, { + let ctx = ctx(); + + let mut message = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + message.data.expires_at_secs += healthcheck_message_exp_secs() * 3; + assert_eq!( + discriminant(&message.is_received_message_valid().err().unwrap()), + discriminant(&SignValidationError::LifetimeOverflow { + max_allowed_expiration_secs: 0, + remaining_expiration_secs: 0 + }) + ); + + let mut message = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + message.data.is_a_reply = !message.data.is_a_reply; + assert_eq!( + discriminant(&message.is_received_message_valid().err().unwrap()), + discriminant(&SignValidationError::FakeSignature) + ); + + let mut message = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + message.data.sender_public_key.push(0); + assert_eq!( + discriminant(&message.is_received_message_valid().err().unwrap()), + discriminant(&SignValidationError::InvalidPublicKey) + ); + }); + + cross_test!(test_expired_message, { + let ctx = ctx(); + let message = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + common::executor::Timer::sleep(3.).await; + assert_eq!( + discriminant(&message.is_received_message_valid().err().unwrap()), + discriminant(&SignValidationError::Expired { + now_secs: 0, + expires_at_secs: 0 + }) + ); + }); + + cross_test!(test_encode_decode, { + let ctx = ctx(); + let original = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + + let encoded = original.encode().unwrap(); + assert!(!encoded.is_empty()); + + let decoded = HealthcheckMessage::decode(&encoded).unwrap(); + assert_eq!(original, decoded); + }); +} diff --git a/mm2src/mm2_main/src/lp_native_dex.rs b/mm2src/mm2_main/src/lp_native_dex.rs index bd875511b0..cd055132a5 100644 --- a/mm2src/mm2_main/src/lp_native_dex.rs +++ b/mm2src/mm2_main/src/lp_native_dex.rs @@ -36,7 +36,7 @@ use mm2_metrics::mm_gauge; use mm2_net::network_event::NetworkEvent; use mm2_net::p2p::P2PContext; use rpc_task::RpcTaskError; -use serde_json::{self as json}; +use serde_json as json; use std::convert::TryInto; use std::io; use std::path::PathBuf; @@ -47,8 +47,9 @@ use std::{fs, usize}; #[cfg(not(target_arch = "wasm32"))] use crate::database::init_and_migrate_sql_db; use crate::heartbeat_event::HeartbeatEvent; +use crate::lp_healthcheck::peer_healthcheck_topic; use crate::lp_message_service::{init_message_service, InitMessageServiceError}; -use crate::lp_network::{lp_network_ports, p2p_event_process_loop, NetIdError}; +use crate::lp_network::{lp_network_ports, p2p_event_process_loop, subscribe_to_topic, NetIdError}; use crate::lp_ordermatch::{broadcast_maker_orders_keep_alive_loop, clean_memory_loop, init_ordermatch_context, lp_ordermatch_loop, orders_kick_start, BalanceUpdateOrdermatchHandler, OrdermatchInitError}; use crate::lp_swap::{running_swaps_num, swap_kick_starts}; @@ -635,7 +636,8 @@ pub async fn init_p2p(ctx: MmArc) -> P2PResult<()> { ); }) .await; - let (cmd_tx, event_rx, _peer_id) = spawn_result?; + + let (cmd_tx, event_rx, peer_id) = spawn_result?; let p2p_context = P2PContext::new(cmd_tx, generate_ed25519_keypair(p2p_key)); p2p_context.store_to_mm_arc(&ctx); @@ -643,6 +645,9 @@ pub async fn init_p2p(ctx: MmArc) -> P2PResult<()> { let fut = p2p_event_process_loop(ctx.weak(), event_rx, i_am_seed); ctx.spawner().spawn(fut); + // Listen for health check messages. + subscribe_to_topic(&ctx, peer_healthcheck_topic(&peer_id.into())); + Ok(()) } diff --git a/mm2src/mm2_main/src/lp_network.rs b/mm2src/mm2_main/src/lp_network.rs index eb84f390d1..8e5195e93a 100644 --- a/mm2src/mm2_main/src/lp_network.rs +++ b/mm2src/mm2_main/src/lp_network.rs @@ -38,8 +38,7 @@ use mm2_net::p2p::P2PContext; use serde::de; use std::net::ToSocketAddrs; -use crate::lp_ordermatch; -use crate::{lp_stats, lp_swap}; +use crate::{lp_healthcheck, lp_ordermatch, lp_stats, lp_swap}; pub type P2PRequestResult = Result>; pub type P2PProcessResult = Result>; @@ -216,6 +215,9 @@ async fn process_p2p_message( } } }, + Some(lp_healthcheck::PEER_HEALTHCHECK_PREFIX) => { + lp_healthcheck::process_p2p_healthcheck_message(&ctx, message).await + }, None | Some(_) => (), } diff --git a/mm2src/mm2_main/src/mm2.rs b/mm2src/mm2_main/src/mm2.rs index 734e71becf..5224ed1b92 100644 --- a/mm2src/mm2_main/src/mm2.rs +++ b/mm2src/mm2_main/src/mm2.rs @@ -72,6 +72,7 @@ use mm2_err_handle::prelude::*; pub mod heartbeat_event; pub mod lp_dispatcher; +pub mod lp_healthcheck; pub mod lp_message_service; mod lp_native_dex; pub mod lp_network; diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index fec08d106f..5a480a0a2e 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -1,4 +1,5 @@ use super::{DispatcherError, DispatcherResult, PUBLIC_METHODS}; +use crate::lp_healthcheck::peer_connection_healthcheck_rpc; use crate::lp_native_dex::init_hw::{cancel_init_trezor, init_trezor, init_trezor_status, init_trezor_user_action}; #[cfg(target_arch = "wasm32")] use crate::lp_native_dex::init_metamask::{cancel_connect_metamask, connect_metamask, connect_metamask_status}; @@ -214,6 +215,7 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult handle_mmrpc(ctx, request, withdraw).await, "ibc_chains" => handle_mmrpc(ctx, request, ibc_chains).await, "ibc_transfer_channels" => handle_mmrpc(ctx, request, ibc_transfer_channels).await, + "peer_connection_healthcheck" => handle_mmrpc(ctx, request, peer_connection_healthcheck_rpc).await, "withdraw_nft" => handle_mmrpc(ctx, request, withdraw_nft).await, "start_eth_fee_estimator" => handle_mmrpc(ctx, request, start_eth_fee_estimator).await, "stop_eth_fee_estimator" => handle_mmrpc(ctx, request, stop_eth_fee_estimator).await, diff --git a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs index 57a4709758..906af55887 100644 --- a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs +++ b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs @@ -35,7 +35,7 @@ use uuid::Uuid; cfg_native! { use common::block_on; - use mm2_test_helpers::for_tests::{get_passphrase, new_mm2_temp_folder_path}; + use mm2_test_helpers::for_tests::{get_passphrase, new_mm2_temp_folder_path, peer_connection_healthcheck}; use mm2_io::fs::slurp; use hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN; } @@ -5963,6 +5963,46 @@ fn test_sign_raw_transaction_p2wpkh() { assert!(response["error"].as_str().unwrap().contains("Signing error")); } +#[test] +#[cfg(not(target_arch = "wasm32"))] +fn test_connection_healthcheck_rpc() { + const BOB_ADDRESS: &str = "12D3KooWEtuv7kmgGCC7oAQ31hB7AR5KkhT3eEWB2bP2roo3M7rY"; + const BOB_SEED: &str = "dummy-value-bob"; + + const ALICE_ADDRESS: &str = "12D3KooWHnoKd2Lr7BoxHCCeBhcnfAZsdiCdojbEMLE7DDSbMo1g"; + const ALICE_SEED: &str = "dummy-value-alice"; + + let bob_conf = Mm2TestConf::seednode(BOB_SEED, &json!([])); + let bob_mm = MarketMakerIt::start(bob_conf.conf, bob_conf.rpc_password, None).unwrap(); + + thread::sleep(Duration::from_secs(2)); + + let mut alice_conf = Mm2TestConf::seednode(ALICE_SEED, &json!([])); + alice_conf.conf["seednodes"] = json!([bob_mm.my_seed_addr()]); + alice_conf.conf["skip_startup_checks"] = json!(true); + let alice_mm = MarketMakerIt::start(alice_conf.conf, alice_conf.rpc_password, None).unwrap(); + + thread::sleep(Duration::from_secs(2)); + + // Self-address check for Bob + let response = block_on(peer_connection_healthcheck(&bob_mm, BOB_ADDRESS)); + assert_eq!(response["result"], json!(true)); + + // Check address of Alice + let response = block_on(peer_connection_healthcheck(&bob_mm, ALICE_ADDRESS)); + assert_eq!(response["result"], json!(true)); + + thread::sleep(Duration::from_secs(1)); + + // Self-address check for Alice + let response = block_on(peer_connection_healthcheck(&alice_mm, ALICE_ADDRESS)); + assert_eq!(response["result"], json!(true)); + + // Check address of Bob + let response = block_on(peer_connection_healthcheck(&alice_mm, BOB_ADDRESS)); + assert_eq!(response["result"], json!(true)); +} + #[cfg(all(feature = "run-device-tests", not(target_arch = "wasm32")))] mod trezor_tests { use coins::eth::{eth_coin_from_conf_and_request, gas_limit, EthCoin}; diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index df5a488cf9..196112cc0b 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -868,9 +868,7 @@ pub fn nft_dev_conf() -> Json { }) } -fn set_chain_id(conf: &mut Json, chain_id: u64) { - conf["chain_id"] = json!(chain_id); -} +fn set_chain_id(conf: &mut Json, chain_id: u64) { conf["chain_id"] = json!(chain_id); } pub fn eth_sepolia_conf() -> Json { json!({ @@ -1896,6 +1894,30 @@ pub async fn enable_qrc20( json::from_str(&electrum.1).unwrap() } +pub async fn peer_connection_healthcheck(mm: &MarketMakerIt, peer_address: &str) -> Json { + let response = mm + .rpc(&json!({ + "userpass": mm.userpass, + "method": "peer_connection_healthcheck", + "mmrpc": "2.0", + "params": { + "peer_address": peer_address + } + })) + .await + .unwrap(); + + assert_eq!( + response.0, + StatusCode::OK, + "RPC «peer_connection_healthcheck» failed with {} {}", + response.0, + response.1 + ); + + json::from_str(&response.1).unwrap() +} + /// Reads passphrase and userpass from .env file pub fn from_env_file(env: Vec) -> (Option, Option) { use regex::bytes::Regex; @@ -2973,7 +2995,10 @@ pub async fn enable_tendermint( tx_history: bool, ) -> Json { let ibc_requests: Vec<_> = ibc_assets.iter().map(|ticker| json!({ "ticker": ticker })).collect(); - let nodes: Vec = rpc_urls.iter().map(|u| json!({"url": u, "komodo_proxy": false })).collect(); + let nodes: Vec = rpc_urls + .iter() + .map(|u| json!({"url": u, "komodo_proxy": false })) + .collect(); let request = json!({ "userpass": mm.userpass, @@ -3010,7 +3035,10 @@ pub async fn enable_tendermint_without_balance( tx_history: bool, ) -> Json { let ibc_requests: Vec<_> = ibc_assets.iter().map(|ticker| json!({ "ticker": ticker })).collect(); - let nodes: Vec = rpc_urls.iter().map(|u| json!({"url": u, "komodo_proxy": false })).collect(); + let nodes: Vec = rpc_urls + .iter() + .map(|u| json!({"url": u, "komodo_proxy": false })) + .collect(); let request = json!({ "userpass": mm.userpass, From 122112aca622838585ef9aaaa8f9fc98ad1bca87 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:46:04 +0300 Subject: [PATCH 14/16] chore(adex-cli): deprecate adex-cli (#2234) --- .github/workflows/adex-cli.yml | 37 ---------------------------------- mm2src/adex_cli/README.md | 3 +++ 2 files changed, 3 insertions(+), 37 deletions(-) delete mode 100644 .github/workflows/adex-cli.yml create mode 100644 mm2src/adex_cli/README.md diff --git a/.github/workflows/adex-cli.yml b/.github/workflows/adex-cli.yml deleted file mode 100644 index 32b62f04ed..0000000000 --- a/.github/workflows/adex-cli.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Adex CLI -on: [push] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -env: - BRANCH_NAME: ${{ github.head_ref || github.ref_name }} - -jobs: - code-check: - name: Code Checks - timeout-minutes: 60 - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - steps: - - uses: actions/checkout@v3 - - - name: Cargo cache - uses: ./.github/actions/cargo-cache - - - name: Start checking code format and lint - continue-on-error: true - run: | - cargo fmt --manifest-path ./mm2src/adex_cli/Cargo.toml --all -- --check - cargo clippy --manifest-path ./mm2src/adex_cli/Cargo.toml --all-targets --all-features -- --D warnings - - - name: Start building - run: | - cargo build --manifest-path ./mm2src/adex_cli/Cargo.toml - - - name: Start testing - run: | - cargo test --manifest-path ./mm2src/adex_cli/Cargo.toml --no-fail-fast diff --git a/mm2src/adex_cli/README.md b/mm2src/adex_cli/README.md new file mode 100644 index 0000000000..6066b2c0bc --- /dev/null +++ b/mm2src/adex_cli/README.md @@ -0,0 +1,3 @@ +## ⚠️ Deprecated + +**The `adex-cli` tool is no longer maintained and has been deprecated.** \ No newline at end of file From 359cb5c83dcd6914225025d9831864d64f0e04c4 Mon Sep 17 00:00:00 2001 From: Alina Sharon <52405288+laruh@users.noreply.github.com> Date: Fri, 4 Oct 2024 17:56:10 +0700 Subject: [PATCH 15/16] fix(legacy-swap): taker failed spend maker payment marked as failed (#2199) This fixes the issue where taker doesn't wait for maker payment spend confirmation. Two new events were added: - "MakerPaymentSpendConfirmed": Success event, fired after "MakerPaymentSpent". - "MakerPaymentSpendConfirmFailed": Error event, means that the taker spend maker payment transaction confirmation was failed. --- .../for_tests/iris_nimda_rick_taker_swap.json | 8 +- ...yment_wait_confirm_failed_taker_saved.json | 2 +- .../recreate_maker_swap_taker_saved.json | 2 +- .../recreate_taker_swap_taker_expected.json | 4 +- ...nt_wait_confirm_failed_taker_expected.json | 4 +- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 8 +- .../src/lp_swap/recreate_swap_data.rs | 2 + mm2src/mm2_main/src/lp_swap/taker_restart.rs | 3 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 101 ++++++++++++++---- .../tests/docker_tests/swap_watcher_tests.rs | 2 + .../docker_tests/swaps_file_lock_tests.rs | 4 +- mm2src/mm2_test_helpers/src/for_tests.rs | 15 ++- 12 files changed, 115 insertions(+), 40 deletions(-) diff --git a/mm2src/mm2_main/src/for_tests/iris_nimda_rick_taker_swap.json b/mm2src/mm2_main/src/for_tests/iris_nimda_rick_taker_swap.json index b040229f93..93cab41558 100644 --- a/mm2src/mm2_main/src/for_tests/iris_nimda_rick_taker_swap.json +++ b/mm2src/mm2_main/src/for_tests/iris_nimda_rick_taker_swap.json @@ -145,12 +145,14 @@ "Started", "Negotiated", "TakerFeeSent", + "TakerPaymentInstructionsReceived", "MakerPaymentReceived", "MakerPaymentWaitConfirmStarted", "MakerPaymentValidatedAndConfirmed", "TakerPaymentSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpendConfirmed", "Finished" ], "error_events": [ @@ -164,8 +166,12 @@ "TakerPaymentDataSendFailed", "TakerPaymentWaitForSpendFailed", "MakerPaymentSpendFailed", + "MakerPaymentSpendConfirmFailed", "TakerPaymentWaitRefundStarted", + "TakerPaymentRefundStarted", "TakerPaymentRefunded", - "TakerPaymentRefundFailed" + "TakerPaymentRefundedByWatcher", + "TakerPaymentRefundFailed", + "TakerPaymentRefundFinished" ] } \ No newline at end of file diff --git a/mm2src/mm2_main/src/for_tests/recreate_maker_swap_maker_payment_wait_confirm_failed_taker_saved.json b/mm2src/mm2_main/src/for_tests/recreate_maker_swap_maker_payment_wait_confirm_failed_taker_saved.json index 07ebe0d6b9..73261f641e 100644 --- a/mm2src/mm2_main/src/for_tests/recreate_maker_swap_maker_payment_wait_confirm_failed_taker_saved.json +++ b/mm2src/mm2_main/src/for_tests/recreate_maker_swap_maker_payment_wait_confirm_failed_taker_saved.json @@ -49,6 +49,6 @@ "taker_coin":"MORTY", "gui":"atomicDEX 0.5.1 iOS", "mm_version":"1b065636a", - "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"], + "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"], "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"] } \ No newline at end of file diff --git a/mm2src/mm2_main/src/for_tests/recreate_maker_swap_taker_saved.json b/mm2src/mm2_main/src/for_tests/recreate_maker_swap_taker_saved.json index 8fe8622f74..a3158581c6 100644 --- a/mm2src/mm2_main/src/for_tests/recreate_maker_swap_taker_saved.json +++ b/mm2src/mm2_main/src/for_tests/recreate_maker_swap_taker_saved.json @@ -61,6 +61,6 @@ "taker_coin":"MORTY", "gui":"atomicDEX 0.5.1 iOS", "mm_version":"1b065636a", - "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"], + "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"], "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"] } \ No newline at end of file diff --git a/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_expected.json b/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_expected.json index cfb9ab66f8..1c1cc39acf 100644 --- a/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_expected.json +++ b/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_expected.json @@ -57,6 +57,6 @@ "taker_coin":"MORTY", "gui":null, "mm_version":"", - "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"], - "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] + "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"], + "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] } \ No newline at end of file diff --git a/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_payment_wait_confirm_failed_taker_expected.json b/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_payment_wait_confirm_failed_taker_expected.json index cb3146b2a3..e743669805 100644 --- a/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_payment_wait_confirm_failed_taker_expected.json +++ b/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_payment_wait_confirm_failed_taker_expected.json @@ -53,6 +53,6 @@ "taker_coin":"MORTY", "gui":null, "mm_version":"", - "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"], - "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] + "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"], + "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] } \ No newline at end of file diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 82e3809c6c..812e7c366a 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -1141,12 +1141,12 @@ impl MakerSwap { } async fn confirm_taker_payment_spend(&self) -> Result<(Option, Vec), String> { - // we should wait for only one confirmation to make sure our spend transaction is not failed - let confirmations = std::cmp::min(1, self.r().data.taker_payment_confirmations); + // We should wait for only one confirmation to make sure our spend transaction is not failed. + // However, we allow the user to use 0 confirmations if specified. let requires_nota = false; let confirm_taker_payment_spend_input = ConfirmPaymentInput { payment_tx: self.r().taker_payment_spend.clone().unwrap().tx_hex.0, - confirmations, + confirmations: std::cmp::min(1, self.r().data.taker_payment_confirmations), requires_nota, wait_until: self.wait_refund_until(), check_every: WAIT_CONFIRM_INTERVAL_SEC, @@ -1679,7 +1679,7 @@ impl MakerSwapEvent { }, MakerSwapEvent::TakerPaymentSpent(_) => "Taker payment spent...".to_owned(), MakerSwapEvent::TakerPaymentSpendFailed(_) => "Taker payment spend failed...".to_owned(), - MakerSwapEvent::TakerPaymentSpendConfirmStarted => "Taker payment send wait confirm started...".to_owned(), + MakerSwapEvent::TakerPaymentSpendConfirmStarted => "Taker payment spend confirm started...".to_owned(), MakerSwapEvent::TakerPaymentSpendConfirmed => "Taker payment spend confirmed...".to_owned(), MakerSwapEvent::TakerPaymentSpendConfirmFailed(_) => "Taker payment spend confirm failed...".to_owned(), MakerSwapEvent::MakerPaymentWaitRefundStarted { wait_until } => { diff --git a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs index 0d139df62f..e4e430c71c 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -264,6 +264,8 @@ fn convert_taker_to_maker_events( | TakerSwapEvent::MakerPaymentWaitConfirmStarted | TakerSwapEvent::MakerPaymentValidatedAndConfirmed | TakerSwapEvent::MakerPaymentSpent(_) + | TakerSwapEvent::MakerPaymentSpendConfirmed + | TakerSwapEvent::MakerPaymentSpendConfirmFailed(_) | TakerSwapEvent::MakerPaymentSpentByWatcher(_) | TakerSwapEvent::MakerPaymentSpendFailed(_) // We don't know the reason at the moment, so we rely on the errors handling above. diff --git a/mm2src/mm2_main/src/lp_swap/taker_restart.rs b/mm2src/mm2_main/src/lp_swap/taker_restart.rs index 9ab1b48092..d934b6b11e 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_restart.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_restart.rs @@ -49,6 +49,7 @@ pub async fn get_command_based_on_maker_or_watcher_activity( Err(e) => ERR!("Error {} when trying to find taker payment spend", e), }, TakerSwapCommand::SpendMakerPayment => check_maker_payment_spend_and_add_event(ctx, swap, saved).await, + TakerSwapCommand::ConfirmMakerPaymentSpend => Ok(command), TakerSwapCommand::PrepareForTakerPaymentRefund | TakerSwapCommand::RefundTakerPayment => { #[cfg(not(any(test, feature = "run-docker-tests")))] { @@ -141,7 +142,7 @@ pub async fn check_maker_payment_spend_and_add_event( let new_swap = SavedSwap::Taker(saved); try_s!(new_swap.save_to_db(ctx).await); info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); - Ok(TakerSwapCommand::Finish) + Ok(TakerSwapCommand::ConfirmMakerPaymentSpend) } pub async fn check_taker_payment_spend(swap: &TakerSwap) -> Result, String> { diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 1b72199244..3bedf837e4 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -44,7 +44,7 @@ use uuid::Uuid; const TAKER_PAYMENT_SPEND_SEARCH_INTERVAL: f64 = 10.; -pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ +pub const TAKER_SUCCESS_EVENTS: [&str; 12] = [ "Started", "Negotiated", "TakerFeeSent", @@ -55,10 +55,11 @@ pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ "TakerPaymentSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpendConfirmed", "Finished", ]; -pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ +pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 14] = [ "Started", "Negotiated", "TakerFeeSent", @@ -71,10 +72,11 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ "TakerPaymentSpent", "MakerPaymentSpent", "MakerPaymentSpentByWatcher", + "MakerPaymentSpendConfirmed", "Finished", ]; -pub const TAKER_ERROR_EVENTS: [&str; 16] = [ +pub const TAKER_ERROR_EVENTS: [&str; 17] = [ "StartFailed", "NegotiateFailed", "TakerFeeSendFailed", @@ -85,6 +87,7 @@ pub const TAKER_ERROR_EVENTS: [&str; 16] = [ "TakerPaymentDataSendFailed", "TakerPaymentWaitForSpendFailed", "MakerPaymentSpendFailed", + "MakerPaymentSpendConfirmFailed", "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", "TakerPaymentRefunded", @@ -178,8 +181,10 @@ impl TakerSavedEvent { TakerSwapEvent::TakerPaymentSpent(_) => Some(TakerSwapCommand::SpendMakerPayment), TakerSwapEvent::TakerPaymentWaitForSpendFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), TakerSwapEvent::TakerPaymentWaitConfirmFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), - TakerSwapEvent::MakerPaymentSpent(_) => Some(TakerSwapCommand::Finish), - TakerSwapEvent::MakerPaymentSpentByWatcher(_) => Some(TakerSwapCommand::Finish), + TakerSwapEvent::MakerPaymentSpent(_) => Some(TakerSwapCommand::ConfirmMakerPaymentSpend), + TakerSwapEvent::MakerPaymentSpendConfirmed => Some(TakerSwapCommand::Finish), + TakerSwapEvent::MakerPaymentSpendConfirmFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), + TakerSwapEvent::MakerPaymentSpentByWatcher(_) => Some(TakerSwapCommand::ConfirmMakerPaymentSpend), TakerSwapEvent::MakerPaymentSpendFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), TakerSwapEvent::TakerPaymentWaitRefundStarted { .. } => { Some(TakerSwapCommand::PrepareForTakerPaymentRefund) @@ -259,6 +264,9 @@ impl TakerSavedSwap { if !self.is_finished() { return false; }; + let mut maker_payment_spent = false; + let mut maker_payment_spent_by_watcher = false; + let mut maker_payment_spend_confirmed_failed = false; for event in self.events.iter() { match event.event { TakerSwapEvent::StartFailed(_) @@ -267,15 +275,26 @@ impl TakerSavedSwap { | TakerSwapEvent::MakerPaymentValidateFailed(_) | TakerSwapEvent::TakerPaymentRefunded(_) | TakerSwapEvent::TakerPaymentRefundedByWatcher(_) - | TakerSwapEvent::MakerPaymentSpent(_) - | TakerSwapEvent::MakerPaymentSpentByWatcher(_) + | TakerSwapEvent::MakerPaymentSpendConfirmed | TakerSwapEvent::MakerPaymentWaitConfirmFailed(_) => { return false; }, + TakerSwapEvent::MakerPaymentSpent(_) => { + maker_payment_spent = true; + }, + TakerSwapEvent::MakerPaymentSpentByWatcher(_) => { + maker_payment_spent_by_watcher = true; + }, + TakerSwapEvent::MakerPaymentSpendConfirmFailed(_) => { + maker_payment_spend_confirmed_failed = true; + }, _ => (), } } - true + // MakerPaymentSpent or MakerPaymentSpentByWatcher were the last success events but a new step `MakerPaymentSpendConfirmed` was added after them. + // For backward compatibility (old saved swaps) we need to check for MakerPaymentSpent or MakerPaymentSpentByWatcher + // and if there is no MakerPaymentSpendConfirmFailed. + maker_payment_spend_confirmed_failed || (!maker_payment_spent && !maker_payment_spent_by_watcher) } pub fn swap_data(&self) -> Result<&TakerSwapData, String> { @@ -554,6 +573,7 @@ pub struct TakerSwapMut { pub maker_payment: Option, pub taker_payment: Option, maker_payment_spend: Option, + maker_payment_spend_confirmed: bool, taker_payment_spend: Option, maker_payment_spend_preimage: Option>, taker_payment_refund_preimage: Option>, @@ -660,6 +680,8 @@ pub enum TakerSwapEvent { TakerPaymentSpent(TakerPaymentSpentData), TakerPaymentWaitForSpendFailed(SwapError), MakerPaymentSpent(TransactionIdentifier), + MakerPaymentSpendConfirmed, + MakerPaymentSpendConfirmFailed(SwapError), MakerPaymentSpentByWatcher(TransactionIdentifier), MakerPaymentSpendFailed(SwapError), TakerPaymentWaitRefundStarted { wait_until: u64 }, @@ -698,6 +720,8 @@ impl TakerSwapEvent { TakerSwapEvent::TakerPaymentSpent(_) => "Taker payment spent...".to_owned(), TakerSwapEvent::TakerPaymentWaitForSpendFailed(_) => "Taker payment wait for spend failed...".to_owned(), TakerSwapEvent::MakerPaymentSpent(_) => "Maker payment spent...".to_owned(), + TakerSwapEvent::MakerPaymentSpendConfirmed => "Maker payment spent confirmed...".to_owned(), + TakerSwapEvent::MakerPaymentSpendConfirmFailed(_) => "Maker payment spend confirm failed...".to_owned(), TakerSwapEvent::MakerPaymentSpentByWatcher(_) => "Maker payment spent by watcher...".to_owned(), TakerSwapEvent::MakerPaymentSpendFailed(_) => "Maker payment spend failed...".to_owned(), TakerSwapEvent::TakerPaymentWaitRefundStarted { wait_until } => { @@ -733,6 +757,7 @@ impl TakerSwapEvent { | TakerSwapEvent::TakerPaymentSent(_) | TakerSwapEvent::TakerPaymentSpent(_) | TakerSwapEvent::MakerPaymentSpent(_) + | TakerSwapEvent::MakerPaymentSpendConfirmed | TakerSwapEvent::MakerPaymentSpentByWatcher(_) | TakerSwapEvent::Finished ) @@ -751,6 +776,7 @@ pub enum TakerSwapCommand { SendTakerPayment, WaitForTakerPaymentSpend, SpendMakerPayment, + ConfirmMakerPaymentSpend, PrepareForTakerPaymentRefund, RefundTakerPayment, FinalizeTakerPaymentRefund, @@ -837,6 +863,8 @@ impl TakerSwap { }, TakerSwapEvent::TakerPaymentWaitForSpendFailed(err) => self.errors.lock().push(err), TakerSwapEvent::MakerPaymentSpent(tx) => self.w().maker_payment_spend = Some(tx), + TakerSwapEvent::MakerPaymentSpendConfirmed => self.w().maker_payment_spend_confirmed = true, + TakerSwapEvent::MakerPaymentSpendConfirmFailed(err) => self.errors.lock().push(err), TakerSwapEvent::MakerPaymentSpentByWatcher(tx) => self.w().maker_payment_spend = Some(tx), TakerSwapEvent::MakerPaymentSpendFailed(err) => self.errors.lock().push(err), TakerSwapEvent::TakerPaymentWaitRefundStarted { .. } => (), @@ -862,6 +890,7 @@ impl TakerSwap { TakerSwapCommand::SendTakerPayment => self.send_taker_payment().await, TakerSwapCommand::WaitForTakerPaymentSpend => self.wait_for_taker_payment_spend().await, TakerSwapCommand::SpendMakerPayment => self.spend_maker_payment().await, + TakerSwapCommand::ConfirmMakerPaymentSpend => self.confirm_maker_payment_spend().await, TakerSwapCommand::PrepareForTakerPaymentRefund => self.prepare_for_taker_payment_refund().await, TakerSwapCommand::RefundTakerPayment => self.refund_taker_payment().await, TakerSwapCommand::FinalizeTakerPaymentRefund => self.finalize_taker_payment_refund().await, @@ -907,6 +936,7 @@ impl TakerSwap { other_taker_coin_htlc_pub: H264::default(), taker_fee: None, maker_payment: None, + maker_payment_spend_confirmed: false, taker_payment: None, taker_payment_spend: None, maker_payment_spend_preimage: None, @@ -1829,9 +1859,38 @@ impl TakerSwap { tx_hash, }; - Ok((Some(TakerSwapCommand::Finish), vec![TakerSwapEvent::MakerPaymentSpent( - tx_ident, - )])) + Ok((Some(TakerSwapCommand::ConfirmMakerPaymentSpend), vec![ + TakerSwapEvent::MakerPaymentSpent(tx_ident), + ])) + } + + async fn confirm_maker_payment_spend(&self) -> Result<(Option, Vec), String> { + // We should wait for only one confirmation to ensure our spend transaction does not fail. + // However, we allow the user to use 0 confirmations if specified. + let confirm_maker_payment_spend_input = ConfirmPaymentInput { + payment_tx: self.r().maker_payment_spend.clone().unwrap().tx_hex.0, + confirmations: std::cmp::min(1, self.r().data.maker_payment_confirmations), + requires_nota: false, + wait_until: self.wait_refund_until(), + check_every: WAIT_CONFIRM_INTERVAL_SEC, + }; + let wait_fut = self + .maker_coin + .wait_for_confirmations(confirm_maker_payment_spend_input); + if let Err(err) = wait_fut.compat().await { + return Ok((Some(TakerSwapCommand::PrepareForTakerPaymentRefund), vec![ + TakerSwapEvent::MakerPaymentSpendConfirmFailed( + ERRL!("!wait for maker payment spend confirmations: {}", err).into(), + ), + TakerSwapEvent::TakerPaymentWaitRefundStarted { + wait_until: self.wait_refund_until(), + }, + ])); + } + info!("Maker payment spend confirmed"); + Ok((Some(TakerSwapCommand::Finish), vec![ + TakerSwapEvent::MakerPaymentSpendConfirmed, + ])) } async fn prepare_for_taker_payment_refund( @@ -2708,7 +2767,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_maker_payment_spend_errored() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1564050693585},{"event":{"data":{"tx_hash":"539cb6dbdc25465bbccc575554f05d1bb04c70efce4316e41194e747375c3659","tx_hex":"0100000001ffc8a8a1b43b4dceed0f8b7dcc2f72fdda92d52f32d25cc21c6d2d498b82debd010000006a47304402203967b7f9f5532fa47116585c7d1bcba51861ea2059cca00409f34660db18e33a0220640991911852533a12fdfeb039fb9c8ca2c45482c6993bd84636af3670d49c1501210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff0200f2052a0100000017a914f2fa08ae416b576779ae5da975e5442663215fce87415173f9000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac0585395d"},"type":"TakerPaymentSent"},"timestamp":1564050695611},{"event":{"data":{"secret":"1b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093","transaction":{"tx_hash":"cc5af1cf68d246419fee49c3d74c0cd173599d115b86efe274368a614951bc47","tx_hex":"010000000159365c3747e79411e41643ceef704cb01b5df0545557ccbc5b4625dcdbb69c5300000000d747304402200e78e27d2f1c18676f98ca3dfa4e4a9eeaa8209b55f57b4dd5d9e1abdf034cfa0220623b5c22b62234cec230342aa306c497e43494b44ec2425b84e236b1bf01257001201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b6304a7a2395db175210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a88821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68ffffffff01008d380c010000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8c77395d"}},"type":"TakerPaymentSpent"},"timestamp":1564051092890},{"event":{"data":{"error":"lp_swap:1981] utxo:891] rpc_clients:738] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"67\", method: \"blockchain.transaction.broadcast\", params: [String(\"0400008085202f890182b342c114f806c5325f23f7e78dae5d186221ab502c86302c2c8082fa110f0a00000000d7473044022035791ea5548f87484065c9e1f0bdca9ebc699f2c7f51182c84f360102e32dc3d02200612ed53bca52d9c2568437f087598531534badf26229fe0f652ea72ddf03ca501201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b630420c1395db17521031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a888210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac68ffffffff01460ec000000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac967e395d000000000000000000000000000000\")] }, error: Transport(\"rpc_clients:668] All electrums are currently disconnected\") }"},"type":"MakerPaymentSpendFailed"},"timestamp":1564051092897},{"event":{"type":"Finished"},"timestamp":1564051092900}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1564050693585},{"event":{"data":{"tx_hash":"539cb6dbdc25465bbccc575554f05d1bb04c70efce4316e41194e747375c3659","tx_hex":"0100000001ffc8a8a1b43b4dceed0f8b7dcc2f72fdda92d52f32d25cc21c6d2d498b82debd010000006a47304402203967b7f9f5532fa47116585c7d1bcba51861ea2059cca00409f34660db18e33a0220640991911852533a12fdfeb039fb9c8ca2c45482c6993bd84636af3670d49c1501210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff0200f2052a0100000017a914f2fa08ae416b576779ae5da975e5442663215fce87415173f9000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac0585395d"},"type":"TakerPaymentSent"},"timestamp":1564050695611},{"event":{"data":{"secret":"1b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093","transaction":{"tx_hash":"cc5af1cf68d246419fee49c3d74c0cd173599d115b86efe274368a614951bc47","tx_hex":"010000000159365c3747e79411e41643ceef704cb01b5df0545557ccbc5b4625dcdbb69c5300000000d747304402200e78e27d2f1c18676f98ca3dfa4e4a9eeaa8209b55f57b4dd5d9e1abdf034cfa0220623b5c22b62234cec230342aa306c497e43494b44ec2425b84e236b1bf01257001201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b6304a7a2395db175210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a88821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68ffffffff01008d380c010000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8c77395d"}},"type":"TakerPaymentSpent"},"timestamp":1564051092890},{"event":{"data":{"error":"lp_swap:1981] utxo:891] rpc_clients:738] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"67\", method: \"blockchain.transaction.broadcast\", params: [String(\"0400008085202f890182b342c114f806c5325f23f7e78dae5d186221ab502c86302c2c8082fa110f0a00000000d7473044022035791ea5548f87484065c9e1f0bdca9ebc699f2c7f51182c84f360102e32dc3d02200612ed53bca52d9c2568437f087598531534badf26229fe0f652ea72ddf03ca501201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b630420c1395db17521031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a888210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac68ffffffff01460ec000000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac967e395d000000000000000000000000000000\")] }, error: Transport(\"rpc_clients:668] All electrums are currently disconnected\") }"},"type":"MakerPaymentSpendFailed"},"timestamp":1564051092897},{"event":{"type":"Finished"},"timestamp":1564051092900}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2744,7 +2803,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_taker_payment_errored_but_sent_not_spent() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"3.54932734","maker_coin":"KMD","maker_coin_start_block":1452970,"maker_payment_confirmations":1,"maker_payment_wait":1563746537,"my_persistent_pub":"03101ace6b08605b9424b0582b5cce044b70a3c8d8d10cb2965e039b0967ae92b9","started_at":1563743937,"taker_amount":"0.02004833998671660000000000","taker_coin":"ETH","taker_coin_start_block":8196380,"taker_payment_confirmations":1,"taker_payment_lock":1563751737,"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"},"type":"Started"},"timestamp":1563743937741},{"event":{"data":{"maker_payment_locktime":1563759539,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"432c8272ac59b47dea2d299b5cf1ee64ea1917b9"},"type":"Negotiated"},"timestamp":1563744003530},{"event":{"data":{"tx_hash":"a59203eb2328827de00bed699a29389792906e4f39fdea145eb40dc6b3821bd6","tx_hex":"f8690284ee6b280082520894d8997941dd1346e9231118d5685d866294f59e5b865af3107a4000801ca0743d2b7c9fad65805d882179062012261be328d7628ae12ee08eff8d7657d993a07eecbd051f49d35279416778faa4664962726d516ce65e18755c9b9406a9c2fd"},"type":"TakerFeeSent"},"timestamp":1563744020598},{"event":{"data":{"tx_hash":"0cf4acbcefde53645851c5c6053ea61fe0cbb5f828a906d69eb809e0b071a03b","tx_hex":"0400008085202f89025d5ae3e8c87418c9b735f8f2f7d29e26820c33c9f30d53f2d31f8b99ea9b1490010000006a47304402201185c06ca575261c539b287175751b7de642eb7466c59128639a19b4c2dd2f9b02201c8c4167d581864bedd4d1deb5596472e6e3ce29fe9e7996907a7b59c905d5490121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff06dbf9971c8dfd4a0c8c49f4f15c51de59ba13b2efa702682e26869843af9a87000000006a473044022012b47c12c7f6ad7d8b778fc4b5dcfd56a39325daf302f56e7b84753ba5216cfa022076bf571cf9e20facf70d2f134e8ed2de67aa08581a27ff3128bf93a9b594ac770121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff02fed727150000000017a914d5268b31131a652f9b6ddf57db62f02285cdfad1874e1d7835000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac37cf345d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563744071778},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563744071781},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563744118073},{"event":{"data":{"error":"lp_swap:1888] eth:654] RPC error: Error { code: ServerError(-32010), message: \"Transaction with the same hash was already imported.\", data: None }"},"type":"TakerPaymentTransactionFailed"},"timestamp":1563744118577},{"event":{"type":"Finished"},"timestamp":1563744118580}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"3.54932734","maker_coin":"KMD","maker_coin_start_block":1452970,"maker_payment_confirmations":1,"maker_payment_wait":1563746537,"my_persistent_pub":"03101ace6b08605b9424b0582b5cce044b70a3c8d8d10cb2965e039b0967ae92b9","started_at":1563743937,"taker_amount":"0.02004833998671660000000000","taker_coin":"ETH","taker_coin_start_block":8196380,"taker_payment_confirmations":1,"taker_payment_lock":1563751737,"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"},"type":"Started"},"timestamp":1563743937741},{"event":{"data":{"maker_payment_locktime":1563759539,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"432c8272ac59b47dea2d299b5cf1ee64ea1917b9"},"type":"Negotiated"},"timestamp":1563744003530},{"event":{"data":{"tx_hash":"a59203eb2328827de00bed699a29389792906e4f39fdea145eb40dc6b3821bd6","tx_hex":"f8690284ee6b280082520894d8997941dd1346e9231118d5685d866294f59e5b865af3107a4000801ca0743d2b7c9fad65805d882179062012261be328d7628ae12ee08eff8d7657d993a07eecbd051f49d35279416778faa4664962726d516ce65e18755c9b9406a9c2fd"},"type":"TakerFeeSent"},"timestamp":1563744020598},{"event":{"data":{"tx_hash":"0cf4acbcefde53645851c5c6053ea61fe0cbb5f828a906d69eb809e0b071a03b","tx_hex":"0400008085202f89025d5ae3e8c87418c9b735f8f2f7d29e26820c33c9f30d53f2d31f8b99ea9b1490010000006a47304402201185c06ca575261c539b287175751b7de642eb7466c59128639a19b4c2dd2f9b02201c8c4167d581864bedd4d1deb5596472e6e3ce29fe9e7996907a7b59c905d5490121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff06dbf9971c8dfd4a0c8c49f4f15c51de59ba13b2efa702682e26869843af9a87000000006a473044022012b47c12c7f6ad7d8b778fc4b5dcfd56a39325daf302f56e7b84753ba5216cfa022076bf571cf9e20facf70d2f134e8ed2de67aa08581a27ff3128bf93a9b594ac770121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff02fed727150000000017a914d5268b31131a652f9b6ddf57db62f02285cdfad1874e1d7835000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac37cf345d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563744071778},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563744071781},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563744118073},{"event":{"data":{"error":"lp_swap:1888] eth:654] RPC error: Error { code: ServerError(-32010), message: \"Transaction with the same hash was already imported.\", data: None }"},"type":"TakerPaymentTransactionFailed"},"timestamp":1563744118577},{"event":{"type":"Finished"},"timestamp":1563744118580}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2794,7 +2853,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_taker_payment_errored_but_sent_and_spent_by_maker() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"3.54932734","maker_coin":"KMD","maker_coin_start_block":1452970,"maker_payment_confirmations":1,"maker_payment_wait":1563746537,"my_persistent_pub":"03101ace6b08605b9424b0582b5cce044b70a3c8d8d10cb2965e039b0967ae92b9","started_at":1563743937,"taker_amount":"0.02004833998671660000000000","taker_coin":"ETH","taker_coin_start_block":8196380,"taker_payment_confirmations":1,"taker_payment_lock":1563751737,"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"},"type":"Started"},"timestamp":1563743937741},{"event":{"data":{"maker_payment_locktime":1563759539,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"432c8272ac59b47dea2d299b5cf1ee64ea1917b9"},"type":"Negotiated"},"timestamp":1563744003530},{"event":{"data":{"tx_hash":"a59203eb2328827de00bed699a29389792906e4f39fdea145eb40dc6b3821bd6","tx_hex":"f8690284ee6b280082520894d8997941dd1346e9231118d5685d866294f59e5b865af3107a4000801ca0743d2b7c9fad65805d882179062012261be328d7628ae12ee08eff8d7657d993a07eecbd051f49d35279416778faa4664962726d516ce65e18755c9b9406a9c2fd"},"type":"TakerFeeSent"},"timestamp":1563744020598},{"event":{"data":{"tx_hash":"0cf4acbcefde53645851c5c6053ea61fe0cbb5f828a906d69eb809e0b071a03b","tx_hex":"0400008085202f89025d5ae3e8c87418c9b735f8f2f7d29e26820c33c9f30d53f2d31f8b99ea9b1490010000006a47304402201185c06ca575261c539b287175751b7de642eb7466c59128639a19b4c2dd2f9b02201c8c4167d581864bedd4d1deb5596472e6e3ce29fe9e7996907a7b59c905d5490121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff06dbf9971c8dfd4a0c8c49f4f15c51de59ba13b2efa702682e26869843af9a87000000006a473044022012b47c12c7f6ad7d8b778fc4b5dcfd56a39325daf302f56e7b84753ba5216cfa022076bf571cf9e20facf70d2f134e8ed2de67aa08581a27ff3128bf93a9b594ac770121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff02fed727150000000017a914d5268b31131a652f9b6ddf57db62f02285cdfad1874e1d7835000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac37cf345d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563744071778},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563744071781},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563744118073},{"event":{"data":{"error":"lp_swap:1888] eth:654] RPC error: Error { code: ServerError(-32010), message: \"Transaction with the same hash was already imported.\", data: None }"},"type":"TakerPaymentTransactionFailed"},"timestamp":1563744118577},{"event":{"type":"Finished"},"timestamp":1563744118580}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"3.54932734","maker_coin":"KMD","maker_coin_start_block":1452970,"maker_payment_confirmations":1,"maker_payment_wait":1563746537,"my_persistent_pub":"03101ace6b08605b9424b0582b5cce044b70a3c8d8d10cb2965e039b0967ae92b9","started_at":1563743937,"taker_amount":"0.02004833998671660000000000","taker_coin":"ETH","taker_coin_start_block":8196380,"taker_payment_confirmations":1,"taker_payment_lock":1563751737,"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"},"type":"Started"},"timestamp":1563743937741},{"event":{"data":{"maker_payment_locktime":1563759539,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"432c8272ac59b47dea2d299b5cf1ee64ea1917b9"},"type":"Negotiated"},"timestamp":1563744003530},{"event":{"data":{"tx_hash":"a59203eb2328827de00bed699a29389792906e4f39fdea145eb40dc6b3821bd6","tx_hex":"f8690284ee6b280082520894d8997941dd1346e9231118d5685d866294f59e5b865af3107a4000801ca0743d2b7c9fad65805d882179062012261be328d7628ae12ee08eff8d7657d993a07eecbd051f49d35279416778faa4664962726d516ce65e18755c9b9406a9c2fd"},"type":"TakerFeeSent"},"timestamp":1563744020598},{"event":{"data":{"tx_hash":"0cf4acbcefde53645851c5c6053ea61fe0cbb5f828a906d69eb809e0b071a03b","tx_hex":"0400008085202f89025d5ae3e8c87418c9b735f8f2f7d29e26820c33c9f30d53f2d31f8b99ea9b1490010000006a47304402201185c06ca575261c539b287175751b7de642eb7466c59128639a19b4c2dd2f9b02201c8c4167d581864bedd4d1deb5596472e6e3ce29fe9e7996907a7b59c905d5490121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff06dbf9971c8dfd4a0c8c49f4f15c51de59ba13b2efa702682e26869843af9a87000000006a473044022012b47c12c7f6ad7d8b778fc4b5dcfd56a39325daf302f56e7b84753ba5216cfa022076bf571cf9e20facf70d2f134e8ed2de67aa08581a27ff3128bf93a9b594ac770121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff02fed727150000000017a914d5268b31131a652f9b6ddf57db62f02285cdfad1874e1d7835000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac37cf345d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563744071778},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563744071781},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563744118073},{"event":{"data":{"error":"lp_swap:1888] eth:654] RPC error: Error { code: ServerError(-32010), message: \"Transaction with the same hash was already imported.\", data: None }"},"type":"TakerPaymentTransactionFailed"},"timestamp":1563744118577},{"event":{"type":"Finished"},"timestamp":1563744118580}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2847,7 +2906,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_taker_payment_refund_failed_not_spent() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2890,7 +2949,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_taker_payment_refund_failed_not_spent_too_early_to_refund() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2921,7 +2980,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_taker_payment_refund_failed_spent_by_maker() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2968,7 +3027,7 @@ mod taker_swap_tests { let ctx = mm_ctx_with_iguana(PASSPHRASE); // the json doesn't have Finished event at the end - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1564050693585},{"event":{"data":{"tx_hash":"539cb6dbdc25465bbccc575554f05d1bb04c70efce4316e41194e747375c3659","tx_hex":"0100000001ffc8a8a1b43b4dceed0f8b7dcc2f72fdda92d52f32d25cc21c6d2d498b82debd010000006a47304402203967b7f9f5532fa47116585c7d1bcba51861ea2059cca00409f34660db18e33a0220640991911852533a12fdfeb039fb9c8ca2c45482c6993bd84636af3670d49c1501210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff0200f2052a0100000017a914f2fa08ae416b576779ae5da975e5442663215fce87415173f9000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac0585395d"},"type":"TakerPaymentSent"},"timestamp":1564050695611},{"event":{"data":{"secret":"1b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093","transaction":{"tx_hash":"cc5af1cf68d246419fee49c3d74c0cd173599d115b86efe274368a614951bc47","tx_hex":"010000000159365c3747e79411e41643ceef704cb01b5df0545557ccbc5b4625dcdbb69c5300000000d747304402200e78e27d2f1c18676f98ca3dfa4e4a9eeaa8209b55f57b4dd5d9e1abdf034cfa0220623b5c22b62234cec230342aa306c497e43494b44ec2425b84e236b1bf01257001201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b6304a7a2395db175210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a88821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68ffffffff01008d380c010000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8c77395d"}},"type":"TakerPaymentSpent"},"timestamp":1564051092890},{"event":{"data":{"error":"lp_swap:1981] utxo:891] rpc_clients:738] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"67\", method: \"blockchain.transaction.broadcast\", params: [String(\"0400008085202f890182b342c114f806c5325f23f7e78dae5d186221ab502c86302c2c8082fa110f0a00000000d7473044022035791ea5548f87484065c9e1f0bdca9ebc699f2c7f51182c84f360102e32dc3d02200612ed53bca52d9c2568437f087598531534badf26229fe0f652ea72ddf03ca501201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b630420c1395db17521031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a888210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac68ffffffff01460ec000000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac967e395d000000000000000000000000000000\")] }, error: Transport(\"rpc_clients:668] All electrums are currently disconnected\") }"},"type":"MakerPaymentSpendFailed"},"timestamp":1564051092897}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1564050693585},{"event":{"data":{"tx_hash":"539cb6dbdc25465bbccc575554f05d1bb04c70efce4316e41194e747375c3659","tx_hex":"0100000001ffc8a8a1b43b4dceed0f8b7dcc2f72fdda92d52f32d25cc21c6d2d498b82debd010000006a47304402203967b7f9f5532fa47116585c7d1bcba51861ea2059cca00409f34660db18e33a0220640991911852533a12fdfeb039fb9c8ca2c45482c6993bd84636af3670d49c1501210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff0200f2052a0100000017a914f2fa08ae416b576779ae5da975e5442663215fce87415173f9000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac0585395d"},"type":"TakerPaymentSent"},"timestamp":1564050695611},{"event":{"data":{"secret":"1b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093","transaction":{"tx_hash":"cc5af1cf68d246419fee49c3d74c0cd173599d115b86efe274368a614951bc47","tx_hex":"010000000159365c3747e79411e41643ceef704cb01b5df0545557ccbc5b4625dcdbb69c5300000000d747304402200e78e27d2f1c18676f98ca3dfa4e4a9eeaa8209b55f57b4dd5d9e1abdf034cfa0220623b5c22b62234cec230342aa306c497e43494b44ec2425b84e236b1bf01257001201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b6304a7a2395db175210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a88821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68ffffffff01008d380c010000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8c77395d"}},"type":"TakerPaymentSpent"},"timestamp":1564051092890},{"event":{"data":{"error":"lp_swap:1981] utxo:891] rpc_clients:738] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"67\", method: \"blockchain.transaction.broadcast\", params: [String(\"0400008085202f890182b342c114f806c5325f23f7e78dae5d186221ab502c86302c2c8082fa110f0a00000000d7473044022035791ea5548f87484065c9e1f0bdca9ebc699f2c7f51182c84f360102e32dc3d02200612ed53bca52d9c2568437f087598531534badf26229fe0f652ea72ddf03ca501201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b630420c1395db17521031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a888210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac68ffffffff01460ec000000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac967e395d000000000000000000000000000000\")] }, error: Transport(\"rpc_clients:668] All electrums are currently disconnected\") }"},"type":"MakerPaymentSpendFailed"},"timestamp":1564051092897}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -3005,7 +3064,7 @@ mod taker_swap_tests { let ctx = mm_ctx_with_iguana(PASSPHRASE); // swap file contains neither maker_coin_swap_contract_address nor taker_coin_swap_contract_address - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -3040,7 +3099,7 @@ mod taker_swap_tests { let ctx = mm_ctx_with_iguana(PASSPHRASE); // swap file contains only maker_coin_swap_contract_address - let taker_saved_json = r#"{"type":"Taker","uuid":"49c79ea4-e1eb-4fb2-a0ef-265bded0b77f","events":[{"timestamp":1608542326909,"event":{"type":"Started","data":{"taker_coin":"RICK","maker_coin":"ETH","maker":"c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3ed","my_persistent_pub":"02031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3","lock_duration":7800,"maker_amount":"0.1","taker_amount":"0.1","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":0,"taker_payment_requires_nota":false,"taker_payment_lock":1608550126,"uuid":"49c79ea4-e1eb-4fb2-a0ef-265bded0b77f","started_at":1608542326,"maker_payment_wait":1608545446,"maker_coin_start_block":14360,"taker_coin_start_block":723123,"maker_coin_swap_contract_address":"eA6D65434A15377081495a9E7C5893543E7c32cB"}}},{"timestamp":1608542327416,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1608557926,"maker_pubkey":"03c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3ed","secret_hash":"8b0221f3b977c1c65dddf17c1c28e2bbced9e7b4"}}},{"timestamp":1608542332604,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f89011ca964f77200b73d64b481f47de84098041d3470d6256e44f2741f080e2b11cf020000006b4830450221008a064f5e51ef8281d43eb7bcd016fed7e560ea1eb7b0713ec977602c96d8f79b02205bfaa6655b849b9922c03276b938273f2edb8fb9ffcaa2a9212d7220560f6060012102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ffffffff0246320000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac62752e27000000001976a91405aab5342166f8594baf17a7d9bef5d56744332788ac7768e05f000000000000000000000000000000","tx_hash":"3793df28ed2aac6188d2c48ec65eff12eea301089d60da655fc96f598326d708"}}},{"timestamp":1608542334018,"event":{"type":"MakerPaymentReceived","data":{"tx_hex":"f8ef82021c80830249f094a09ad3cd7e96586ebd05a2607ee56b56fb2db8fd88016345785d8a0000b884152cf3af50aebafeaf827c62c2eed09e265fa5aa9e013c0f27f0a88259f1aaa1279f0c32000000000000000000000000bab36286672fbdc7b250804bf6d14be0df69fa298b0221f3b977c1c65dddf17c1c28e2bbced9e7b4000000000000000000000000000000000000000000000000000000000000000000000000000000005fe0a5661ba0f18a0c5c349462b51dacd1a0761e4997d4572a01e48480c4e310d69a40308ad3a04510513f01a79c59f22c9cb79952547c8dfc4c74785b630f512d64369323e0c1","tx_hash":"6782323490584a2bc768cd5199506bfa1ed91e7515b35bb72fa269604b7dc0aa"}}},{"timestamp":1608542334019,"event":{"type":"MakerPaymentWaitConfirmStarted"}},{"timestamp":1608542334825,"event":{"type":"MakerPaymentValidatedAndConfirmed"}},{"timestamp":1608542337671,"event":{"type":"TakerPaymentSent","data":{"tx_hex":"0400008085202f890108d72683596fc95f65da609d0801a3ee12ff5ec68ec4d28861ac2aed28df9337010000006b48304502210086a03db599438b243bee2b02af56e23447f85d09854416b51305536b9ca5890e02204b288acdea4cdc7ab1ffbd9766a7bdf95f5bd02d2917dfb7089dbf29032591b0012102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ffffffff03809698000000000017a914888e9e1816214c3960eac7b55e35521ca4426b0c870000000000000000166a148b0221f3b977c1c65dddf17c1c28e2bbced9e7b4fada9526000000001976a91405aab5342166f8594baf17a7d9bef5d56744332788ac7f68e05f000000000000000000000000000000","tx_hash":"44fa493757df5fdca823bbac05a8b8feb5862d799d4947fd544abcd129feceea"}}},{"timestamp":1608542348271,"event":{"type":"TakerPaymentSpent","data":{"transaction":{"tx_hex":"0400008085202f8901eacefe29d1bc4a54fd47499d792d86b5feb8a805acbb23a8dc5fdf573749fa4400000000d74730440220508c853cc4f1fcb9e6aa00e704eef99adaee9a4ea63a1fd6393bb7ff18da02c802200396bb5d52157bd77ff26ac521ed75aca388d3ec1e5e3ebb7b3aed73c3d33ec50120df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e004c6b6304ee86e05fb1752102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ac6782012088a9148b0221f3b977c1c65dddf17c1c28e2bbced9e7b4882103c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3edac68ffffffff0198929800000000001976a9146d9d2b554d768232320587df75c4338ecc8bf37d88ac725ae05f000000000000000000000000000000","tx_hash":"9376dde62249802a0aba8259f51def9bb2e509af85a5ec7df04b479a9da28a29"},"secret":"df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e"}}},{"timestamp":1608542349372,"event":{"type":"MakerPaymentSpent","data":{"tx_hex":"f90107821fb980830249f094a09ad3cd7e96586ebd05a2607ee56b56fb2db8fd80b8a402ed292b50aebafeaf827c62c2eed09e265fa5aa9e013c0f27f0a88259f1aaa1279f0c32000000000000000000000000000000000000000000000000016345785d8a0000df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b2d0d6c2c785217457b69b922a2a9cea98f71e91ca0ed6a4942a78c7ae6eb3c9dec496459a9ef68b34cb389acd939d13d3ecaf7e4aca021bb77e80fc60acf25a7a01cc1272b1b76594a521fb1abe1322d650e58a672c2","tx_hash":"c2d206e665aee159a5ab9aff60f76444e97bdad8f9152eccb6ca07d9204974ca"}}},{"timestamp":1608542349373,"event":{"type":"Finished"}}],"maker_amount":"0.1","maker_coin":"ETH","taker_amount":"0.1","taker_coin":"RICK","gui":"nogui","mm_version":"1a6082121","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"]}"#; + let taker_saved_json = r#"{"type":"Taker","uuid":"49c79ea4-e1eb-4fb2-a0ef-265bded0b77f","events":[{"timestamp":1608542326909,"event":{"type":"Started","data":{"taker_coin":"RICK","maker_coin":"ETH","maker":"c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3ed","my_persistent_pub":"02031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3","lock_duration":7800,"maker_amount":"0.1","taker_amount":"0.1","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":0,"taker_payment_requires_nota":false,"taker_payment_lock":1608550126,"uuid":"49c79ea4-e1eb-4fb2-a0ef-265bded0b77f","started_at":1608542326,"maker_payment_wait":1608545446,"maker_coin_start_block":14360,"taker_coin_start_block":723123,"maker_coin_swap_contract_address":"eA6D65434A15377081495a9E7C5893543E7c32cB"}}},{"timestamp":1608542327416,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1608557926,"maker_pubkey":"03c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3ed","secret_hash":"8b0221f3b977c1c65dddf17c1c28e2bbced9e7b4"}}},{"timestamp":1608542332604,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f89011ca964f77200b73d64b481f47de84098041d3470d6256e44f2741f080e2b11cf020000006b4830450221008a064f5e51ef8281d43eb7bcd016fed7e560ea1eb7b0713ec977602c96d8f79b02205bfaa6655b849b9922c03276b938273f2edb8fb9ffcaa2a9212d7220560f6060012102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ffffffff0246320000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac62752e27000000001976a91405aab5342166f8594baf17a7d9bef5d56744332788ac7768e05f000000000000000000000000000000","tx_hash":"3793df28ed2aac6188d2c48ec65eff12eea301089d60da655fc96f598326d708"}}},{"timestamp":1608542334018,"event":{"type":"MakerPaymentReceived","data":{"tx_hex":"f8ef82021c80830249f094a09ad3cd7e96586ebd05a2607ee56b56fb2db8fd88016345785d8a0000b884152cf3af50aebafeaf827c62c2eed09e265fa5aa9e013c0f27f0a88259f1aaa1279f0c32000000000000000000000000bab36286672fbdc7b250804bf6d14be0df69fa298b0221f3b977c1c65dddf17c1c28e2bbced9e7b4000000000000000000000000000000000000000000000000000000000000000000000000000000005fe0a5661ba0f18a0c5c349462b51dacd1a0761e4997d4572a01e48480c4e310d69a40308ad3a04510513f01a79c59f22c9cb79952547c8dfc4c74785b630f512d64369323e0c1","tx_hash":"6782323490584a2bc768cd5199506bfa1ed91e7515b35bb72fa269604b7dc0aa"}}},{"timestamp":1608542334019,"event":{"type":"MakerPaymentWaitConfirmStarted"}},{"timestamp":1608542334825,"event":{"type":"MakerPaymentValidatedAndConfirmed"}},{"timestamp":1608542337671,"event":{"type":"TakerPaymentSent","data":{"tx_hex":"0400008085202f890108d72683596fc95f65da609d0801a3ee12ff5ec68ec4d28861ac2aed28df9337010000006b48304502210086a03db599438b243bee2b02af56e23447f85d09854416b51305536b9ca5890e02204b288acdea4cdc7ab1ffbd9766a7bdf95f5bd02d2917dfb7089dbf29032591b0012102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ffffffff03809698000000000017a914888e9e1816214c3960eac7b55e35521ca4426b0c870000000000000000166a148b0221f3b977c1c65dddf17c1c28e2bbced9e7b4fada9526000000001976a91405aab5342166f8594baf17a7d9bef5d56744332788ac7f68e05f000000000000000000000000000000","tx_hash":"44fa493757df5fdca823bbac05a8b8feb5862d799d4947fd544abcd129feceea"}}},{"timestamp":1608542348271,"event":{"type":"TakerPaymentSpent","data":{"transaction":{"tx_hex":"0400008085202f8901eacefe29d1bc4a54fd47499d792d86b5feb8a805acbb23a8dc5fdf573749fa4400000000d74730440220508c853cc4f1fcb9e6aa00e704eef99adaee9a4ea63a1fd6393bb7ff18da02c802200396bb5d52157bd77ff26ac521ed75aca388d3ec1e5e3ebb7b3aed73c3d33ec50120df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e004c6b6304ee86e05fb1752102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ac6782012088a9148b0221f3b977c1c65dddf17c1c28e2bbced9e7b4882103c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3edac68ffffffff0198929800000000001976a9146d9d2b554d768232320587df75c4338ecc8bf37d88ac725ae05f000000000000000000000000000000","tx_hash":"9376dde62249802a0aba8259f51def9bb2e509af85a5ec7df04b479a9da28a29"},"secret":"df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e"}}},{"timestamp":1608542349372,"event":{"type":"MakerPaymentSpent","data":{"tx_hex":"f90107821fb980830249f094a09ad3cd7e96586ebd05a2607ee56b56fb2db8fd80b8a402ed292b50aebafeaf827c62c2eed09e265fa5aa9e013c0f27f0a88259f1aaa1279f0c32000000000000000000000000000000000000000000000000016345785d8a0000df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b2d0d6c2c785217457b69b922a2a9cea98f71e91ca0ed6a4942a78c7ae6eb3c9dec496459a9ef68b34cb389acd939d13d3ecaf7e4aca021bb77e80fc60acf25a7a01cc1272b1b76594a521fb1abe1322d650e58a672c2","tx_hash":"c2d206e665aee159a5ab9aff60f76444e97bdad8f9152eccb6ca07d9204974ca"}}},{"timestamp":1608542349373,"event":{"type":"Finished"}}],"maker_amount":"0.1","maker_coin":"ETH","taker_amount":"0.1","taker_coin":"RICK","gui":"nogui","mm_version":"1a6082121","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"]}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -3074,7 +3133,7 @@ mod taker_swap_tests { fn test_recoverable() { // Swap ended with MakerPaymentWaitConfirmFailed event. // MM2 did not attempt to send the payment in this case so swap is not recoverable. - let swap: TakerSavedSwap = json::from_str(r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"data":{"error":"error"},"type":"MakerPaymentWaitConfirmFailed"},"timestamp":1564051092897},{"event":{"type":"Finished"},"timestamp":1564051092900}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#).unwrap(); + let swap: TakerSavedSwap = json::from_str(r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"data":{"error":"error"},"type":"MakerPaymentWaitConfirmFailed"},"timestamp":1564051092897},{"event":{"type":"Finished"},"timestamp":1564051092900}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#).unwrap(); assert!(!swap.is_recoverable()); } diff --git a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs index aadda99709..dc6f8cb5ba 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -409,6 +409,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_panic_at_wait_for_taker "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpentByWatcher", + "MakerPaymentSpendConfirmed", "Finished", ]; check_actual_events(&mm_alice, &uuids[0], &expected_events); @@ -467,6 +468,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_panic_at_maker_payment_ "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpentByWatcher", + "MakerPaymentSpendConfirmed", "Finished", ]; check_actual_events(&mm_alice, &uuids[0], &expected_events); diff --git a/mm2src/mm2_main/tests/docker_tests/swaps_file_lock_tests.rs b/mm2src/mm2_main/tests/docker_tests/swaps_file_lock_tests.rs index 8ca965e784..785eb0a849 100644 --- a/mm2src/mm2_main/tests/docker_tests/swaps_file_lock_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swaps_file_lock_tests.rs @@ -13,8 +13,8 @@ use std::time::Duration; const UNFINISHED_MAKER_SWAP: &str = r#"{"type":"Maker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036250,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","taker":"859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","secret":"dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107","my_persistent_pub":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"maker_payment_lock":1588259636,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038035,"event":{"type":"Negotiated","data":{"taker_payment_locktime":1588251836,"taker_pubkey":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521"}}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeValidated","MakerPaymentSent","TakerPaymentReceived","TakerPaymentWaitConfirmStarted","TakerPaymentValidatedAndConfirmed","TakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeValidateFailed","MakerPaymentTransactionFailed","MakerPaymentDataSendFailed","MakerPaymentWaitConfirmFailed","TakerPaymentValidateFailed","TakerPaymentWaitConfirmFailed","TakerPaymentSpendFailed","MakerPaymentWaitRefundStarted","MakerPaymentRefunded","MakerPaymentRefundFailed"]}"#; const FINISHED_MAKER_SWAP: &str = r#"{"type":"Maker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036250,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","taker":"859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","secret":"dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107","my_persistent_pub":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"maker_payment_lock":1588259636,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038035,"event":{"type":"Negotiated","data":{"taker_payment_locktime":1588251836,"taker_pubkey":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521"}}},{"timestamp":1588244038463,"event":{"type":"TakerFeeValidated","data":{"tx_hex":"0400008085202f8901bdde9bca02870787441f6068e4c2a869a3aac3d1d0925f6a6e27874343544d0a010000006a47304402206694a794693b55fbe8205cb1cbb992d92fa2a9a851763ad1bf1628c16deaf73e02203cfff465504bdd6c51e8fbd45dd2e1187142fdc29e82f36f577616d9d6097d7a012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff02dfceab07000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac39fd41892e0000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac46aeaa5e000000000000000000000000000000","tx_hash":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RThtXup6Zo7LZAi8kRWgjAyi1s4u6U9Cpf","RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"2000","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":13,"timestamp":1588244038,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6"}}},{"timestamp":1588244038505,"event":{"type":"MakerPaymentSent","data":{"tx_hex":"0400008085202f890163cc0aceb3b432f84c1407991a0389b74b7842f030aa261203aa0b4b9a9a15fd010000006b483045022100f3239794b7b0e1c75aae084a65535270f154231ce86a4739976ff69eeff1ebd402206c0aeb28b7b4b2e77e98b3c3efd9a20d85c2cd0627acc3f45d577c0e88c34fb4012102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dffffffff0118e476481700000017a914dc4ed4686174503b85374bfb0aefe07a9fb37bcf8746aeaa5e000000000000000000000000000000","tx_hash":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf","from":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"to":["bYp9ncp3V7FYsymipriVbdd3QL72hK9hio"],"total_amount":"1000","spent_by_me":"1000","received_by_me":"0","my_balance_change":"-1000","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN","internal_id":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf"}}},{"timestamp":1588244053938,"event":{"type":"TakerPaymentReceived","data":{"tx_hex":"0400008085202f8901c6272ed6f0900c449a6a6f948d74f85688228a843a672661ddbf1c4d48d71871010000006b483045022100c37627385c66b7bdf466b4dd4e7095b7551e6f5ea35f9bcc6344eb629f2edcb202203280eaba64b4d72010500166fab62cf34a687a516b2fe83d4eceaf8572cb37a7012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff0218e476481700000017a91431ff75ed72cd135a7ce50d121e71efc37066b9f9873915cb40170000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac55aeaa5e000000000000000000000000000000","tx_hash":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC","bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"total_amount":"1998.71298873","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f"}}},{"timestamp":1588244053939,"event":{"type":"TakerPaymentWaitConfirmStarted"}},{"timestamp":1588244068951,"event":{"type":"TakerPaymentValidatedAndConfirmed"}},{"timestamp":1588244068959,"event":{"type":"TakerPaymentSpent","data":{"tx_hex":"0400008085202f89018ff02d45bbd1f70ff21071fb3873584beab48ac1700bab73903fa44ae98c71b400000000d747304402204f0d641a3916e54d6788744c3229110a431eff18634c66fbd1741f9ca7dba99d02202315ee1d9317cc4d5d75d01f066f2c8a59876f790106d310144cdc03c25f985e0120dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498004c6b6304bcccaa5eb1752102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ac6782012088a9149304bce3196f344b2a22dc99db406e95ab6f3107882102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dac68ffffffff0130e07648170000001976a91412c553e8469363f2d30268c475af1e9186cc90af88ac54a0aa5e000000000000000000000000000000","tx_hash":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9","from":["bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"to":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"total_amount":"999.99999","spent_by_me":"0","received_by_me":"999.99998","my_balance_change":"999.99998","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9"}}},{"timestamp":1588244068960,"event":{"type":"Finished"}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeValidated","MakerPaymentSent","TakerPaymentReceived","TakerPaymentWaitConfirmStarted","TakerPaymentValidatedAndConfirmed","TakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeValidateFailed","MakerPaymentTransactionFailed","MakerPaymentDataSendFailed","MakerPaymentWaitConfirmFailed","TakerPaymentValidateFailed","TakerPaymentWaitConfirmFailed","TakerPaymentSpendFailed","MakerPaymentWaitRefundStarted","MakerPaymentRefunded","MakerPaymentRefundFailed"]}"#; -const UNFINISHED_TAKER_SWAP: &str = r#"{"type":"Taker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036253,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","maker":"987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","my_persistent_pub":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"taker_payment_lock":1588251836,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_payment_wait":1588247156,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038239,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1588259636,"maker_pubkey":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107"}}},{"timestamp":1588244038271,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f8901bdde9bca02870787441f6068e4c2a869a3aac3d1d0925f6a6e27874343544d0a010000006a47304402206694a794693b55fbe8205cb1cbb992d92fa2a9a851763ad1bf1628c16deaf73e02203cfff465504bdd6c51e8fbd45dd2e1187142fdc29e82f36f577616d9d6097d7a012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff02dfceab07000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac39fd41892e0000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac46aeaa5e000000000000000000000000000000","tx_hash":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RThtXup6Zo7LZAi8kRWgjAyi1s4u6U9Cpf","RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"2000","spent_by_me":"2000","received_by_me":"1998.71298873","my_balance_change":"-1.28701127","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6"}}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundFailed"]}"#; -const FINISHED_TAKER_SWAP: &str = r#"{"type":"Taker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036253,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","maker":"987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","my_persistent_pub":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"taker_payment_lock":1588251836,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_payment_wait":1588247156,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038239,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1588259636,"maker_pubkey":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107"}}},{"timestamp":1588244038271,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f8901bdde9bca02870787441f6068e4c2a869a3aac3d1d0925f6a6e27874343544d0a010000006a47304402206694a794693b55fbe8205cb1cbb992d92fa2a9a851763ad1bf1628c16deaf73e02203cfff465504bdd6c51e8fbd45dd2e1187142fdc29e82f36f577616d9d6097d7a012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff02dfceab07000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac39fd41892e0000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac46aeaa5e000000000000000000000000000000","tx_hash":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RThtXup6Zo7LZAi8kRWgjAyi1s4u6U9Cpf","RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"2000","spent_by_me":"2000","received_by_me":"1998.71298873","my_balance_change":"-1.28701127","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6"}}},{"timestamp":1588244038698,"event":{"type":"MakerPaymentReceived","data":{"tx_hex":"0400008085202f890163cc0aceb3b432f84c1407991a0389b74b7842f030aa261203aa0b4b9a9a15fd010000006b483045022100f3239794b7b0e1c75aae084a65535270f154231ce86a4739976ff69eeff1ebd402206c0aeb28b7b4b2e77e98b3c3efd9a20d85c2cd0627acc3f45d577c0e88c34fb4012102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dffffffff0118e476481700000017a914dc4ed4686174503b85374bfb0aefe07a9fb37bcf8746aeaa5e000000000000000000000000000000","tx_hash":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf","from":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"to":["bYp9ncp3V7FYsymipriVbdd3QL72hK9hio"],"total_amount":"1000","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN","internal_id":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf"}}},{"timestamp":1588244038699,"event":{"type":"MakerPaymentWaitConfirmStarted"}},{"timestamp":1588244053712,"event":{"type":"MakerPaymentValidatedAndConfirmed"}},{"timestamp":1588244053745,"event":{"type":"TakerPaymentSent","data":{"tx_hex":"0400008085202f8901c6272ed6f0900c449a6a6f948d74f85688228a843a672661ddbf1c4d48d71871010000006b483045022100c37627385c66b7bdf466b4dd4e7095b7551e6f5ea35f9bcc6344eb629f2edcb202203280eaba64b4d72010500166fab62cf34a687a516b2fe83d4eceaf8572cb37a7012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff0218e476481700000017a91431ff75ed72cd135a7ce50d121e71efc37066b9f9873915cb40170000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac55aeaa5e000000000000000000000000000000","tx_hash":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC","bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"total_amount":"1998.71298873","spent_by_me":"1998.71298873","received_by_me":"998.71298873","my_balance_change":"-1000","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f"}}},{"timestamp":1588244078860,"event":{"type":"TakerPaymentSpent","data":{"transaction":{"tx_hex":"0400008085202f89018ff02d45bbd1f70ff21071fb3873584beab48ac1700bab73903fa44ae98c71b400000000d747304402204f0d641a3916e54d6788744c3229110a431eff18634c66fbd1741f9ca7dba99d02202315ee1d9317cc4d5d75d01f066f2c8a59876f790106d310144cdc03c25f985e0120dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498004c6b6304bcccaa5eb1752102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ac6782012088a9149304bce3196f344b2a22dc99db406e95ab6f3107882102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dac68ffffffff0130e07648170000001976a91412c553e8469363f2d30268c475af1e9186cc90af88ac54a0aa5e000000000000000000000000000000","tx_hash":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9","from":["bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"to":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"total_amount":"999.99999","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":29,"timestamp":1588244070,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9"},"secret":"dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498"}}},{"timestamp":1588244078870,"event":{"type":"MakerPaymentSpent","data":{"tx_hex":"0400008085202f8901dfbf5ad1339c6f9478c4917cd3d90ac56bfdc2e75c57c94db20bed853f0b4c9a00000000d848304502210092535c081325ba5261699d7cfd4c503fb6125dde86389b83f40f3e2c006039bb022063cfd72aa15558dee874cac08b22dbcf11d3f06c8e48b0ddaf75b86887d604410120dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498004c6b630434ebaa5eb1752102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dac6782012088a9149304bce3196f344b2a22dc99db406e95ab6f3107882102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ac68ffffffff0130e07648170000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac5ea0aa5e000000000000000000000000000000","tx_hash":"caea128b1c85a88abd5924e512780ee18952dadc217b0c06f4b2820eb71d03bc","from":["bYp9ncp3V7FYsymipriVbdd3QL72hK9hio"],"to":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"999.99999","spent_by_me":"0","received_by_me":"999.99998","my_balance_change":"999.99998","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN","internal_id":"caea128b1c85a88abd5924e512780ee18952dadc217b0c06f4b2820eb71d03bc"}}},{"timestamp":1588244078871,"event":{"type":"Finished"}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundFailed"]}"#; +const UNFINISHED_TAKER_SWAP: &str = r#"{"type":"Taker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036253,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","maker":"987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","my_persistent_pub":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"taker_payment_lock":1588251836,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_payment_wait":1588247156,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038239,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1588259636,"maker_pubkey":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107"}}},{"timestamp":1588244038271,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f8901bdde9bca02870787441f6068e4c2a869a3aac3d1d0925f6a6e27874343544d0a010000006a47304402206694a794693b55fbe8205cb1cbb992d92fa2a9a851763ad1bf1628c16deaf73e02203cfff465504bdd6c51e8fbd45dd2e1187142fdc29e82f36f577616d9d6097d7a012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff02dfceab07000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac39fd41892e0000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac46aeaa5e000000000000000000000000000000","tx_hash":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RThtXup6Zo7LZAi8kRWgjAyi1s4u6U9Cpf","RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"2000","spent_by_me":"2000","received_by_me":"1998.71298873","my_balance_change":"-1.28701127","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6"}}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundFailed"]}"#; +const FINISHED_TAKER_SWAP: &str = r#"{"type":"Taker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036253,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","maker":"987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","my_persistent_pub":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"taker_payment_lock":1588251836,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_payment_wait":1588247156,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038239,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1588259636,"maker_pubkey":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107"}}},{"timestamp":1588244038271,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f8901bdde9bca02870787441f6068e4c2a869a3aac3d1d0925f6a6e27874343544d0a010000006a47304402206694a794693b55fbe8205cb1cbb992d92fa2a9a851763ad1bf1628c16deaf73e02203cfff465504bdd6c51e8fbd45dd2e1187142fdc29e82f36f577616d9d6097d7a012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff02dfceab07000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac39fd41892e0000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac46aeaa5e000000000000000000000000000000","tx_hash":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RThtXup6Zo7LZAi8kRWgjAyi1s4u6U9Cpf","RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"2000","spent_by_me":"2000","received_by_me":"1998.71298873","my_balance_change":"-1.28701127","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6"}}},{"timestamp":1588244038698,"event":{"type":"MakerPaymentReceived","data":{"tx_hex":"0400008085202f890163cc0aceb3b432f84c1407991a0389b74b7842f030aa261203aa0b4b9a9a15fd010000006b483045022100f3239794b7b0e1c75aae084a65535270f154231ce86a4739976ff69eeff1ebd402206c0aeb28b7b4b2e77e98b3c3efd9a20d85c2cd0627acc3f45d577c0e88c34fb4012102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dffffffff0118e476481700000017a914dc4ed4686174503b85374bfb0aefe07a9fb37bcf8746aeaa5e000000000000000000000000000000","tx_hash":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf","from":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"to":["bYp9ncp3V7FYsymipriVbdd3QL72hK9hio"],"total_amount":"1000","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN","internal_id":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf"}}},{"timestamp":1588244038699,"event":{"type":"MakerPaymentWaitConfirmStarted"}},{"timestamp":1588244053712,"event":{"type":"MakerPaymentValidatedAndConfirmed"}},{"timestamp":1588244053745,"event":{"type":"TakerPaymentSent","data":{"tx_hex":"0400008085202f8901c6272ed6f0900c449a6a6f948d74f85688228a843a672661ddbf1c4d48d71871010000006b483045022100c37627385c66b7bdf466b4dd4e7095b7551e6f5ea35f9bcc6344eb629f2edcb202203280eaba64b4d72010500166fab62cf34a687a516b2fe83d4eceaf8572cb37a7012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff0218e476481700000017a91431ff75ed72cd135a7ce50d121e71efc37066b9f9873915cb40170000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac55aeaa5e000000000000000000000000000000","tx_hash":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC","bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"total_amount":"1998.71298873","spent_by_me":"1998.71298873","received_by_me":"998.71298873","my_balance_change":"-1000","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f"}}},{"timestamp":1588244078860,"event":{"type":"TakerPaymentSpent","data":{"transaction":{"tx_hex":"0400008085202f89018ff02d45bbd1f70ff21071fb3873584beab48ac1700bab73903fa44ae98c71b400000000d747304402204f0d641a3916e54d6788744c3229110a431eff18634c66fbd1741f9ca7dba99d02202315ee1d9317cc4d5d75d01f066f2c8a59876f790106d310144cdc03c25f985e0120dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498004c6b6304bcccaa5eb1752102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ac6782012088a9149304bce3196f344b2a22dc99db406e95ab6f3107882102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dac68ffffffff0130e07648170000001976a91412c553e8469363f2d30268c475af1e9186cc90af88ac54a0aa5e000000000000000000000000000000","tx_hash":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9","from":["bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"to":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"total_amount":"999.99999","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":29,"timestamp":1588244070,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9"},"secret":"dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498"}}},{"timestamp":1588244078870,"event":{"type":"MakerPaymentSpent","data":{"tx_hex":"0400008085202f8901dfbf5ad1339c6f9478c4917cd3d90ac56bfdc2e75c57c94db20bed853f0b4c9a00000000d848304502210092535c081325ba5261699d7cfd4c503fb6125dde86389b83f40f3e2c006039bb022063cfd72aa15558dee874cac08b22dbcf11d3f06c8e48b0ddaf75b86887d604410120dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498004c6b630434ebaa5eb1752102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dac6782012088a9149304bce3196f344b2a22dc99db406e95ab6f3107882102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ac68ffffffff0130e07648170000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac5ea0aa5e000000000000000000000000000000","tx_hash":"caea128b1c85a88abd5924e512780ee18952dadc217b0c06f4b2820eb71d03bc","from":["bYp9ncp3V7FYsymipriVbdd3QL72hK9hio"],"to":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"999.99999","spent_by_me":"0","received_by_me":"999.99998","my_balance_change":"999.99998","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN","internal_id":"caea128b1c85a88abd5924e512780ee18952dadc217b0c06f4b2820eb71d03bc"}}},{"timestamp":1588244078871,"event":{"type":"MakerPaymentSpendConfirmed"}},{"timestamp":1588244078872,"event":{"type":"Finished"}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundFailed"]}"#; fn swap_file_lock_prevents_double_swap_start_on_kick_start(swap_json: &str) { let (_ctx, _, bob_priv_key) = generate_utxo_coin_with_random_privkey("MYCOIN", 1000.into()); diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index 196112cc0b..ffe7659a43 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -81,7 +81,7 @@ pub const MAKER_ERROR_EVENTS: [&str; 15] = [ "MakerPaymentRefundFinished", ]; -pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ +pub const TAKER_SUCCESS_EVENTS: [&str; 12] = [ "Started", "Negotiated", "TakerFeeSent", @@ -92,10 +92,11 @@ pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ "TakerPaymentSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpendConfirmed", "Finished", ]; -pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ +pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 14] = [ "Started", "Negotiated", "TakerFeeSent", @@ -108,11 +109,12 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ "TakerPaymentSpent", "MakerPaymentSpent", "MakerPaymentSpentByWatcher", + "MakerPaymentSpendConfirmed", "Finished", ]; // Taker using watchers and watcher spends maker payment -pub const TAKER_ACTUAL_EVENTS_WATCHER_SPENDS_MAKER_PAYMENT: [&str; 12] = [ +pub const TAKER_ACTUAL_EVENTS_WATCHER_SPENDS_MAKER_PAYMENT: [&str; 13] = [ "Started", "Negotiated", "TakerFeeSent", @@ -124,11 +126,12 @@ pub const TAKER_ACTUAL_EVENTS_WATCHER_SPENDS_MAKER_PAYMENT: [&str; 12] = [ "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpentByWatcher", + "MakerPaymentSpendConfirmed", "Finished", ]; // Taker using watchers and spends maker payment instead of watcher -pub const TAKER_ACTUAL_EVENTS_TAKER_SPENDS_MAKER_PAYMENT: [&str; 12] = [ +pub const TAKER_ACTUAL_EVENTS_TAKER_SPENDS_MAKER_PAYMENT: [&str; 13] = [ "Started", "Negotiated", "TakerFeeSent", @@ -140,10 +143,11 @@ pub const TAKER_ACTUAL_EVENTS_TAKER_SPENDS_MAKER_PAYMENT: [&str; 12] = [ "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpendConfirmed", "Finished", ]; -pub const TAKER_ERROR_EVENTS: [&str; 16] = [ +pub const TAKER_ERROR_EVENTS: [&str; 17] = [ "StartFailed", "NegotiateFailed", "TakerFeeSendFailed", @@ -154,6 +158,7 @@ pub const TAKER_ERROR_EVENTS: [&str; 16] = [ "TakerPaymentDataSendFailed", "TakerPaymentWaitForSpendFailed", "MakerPaymentSpendFailed", + "MakerPaymentSpendConfirmFailed", "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", "TakerPaymentRefunded", From ce183766480abafb7275a10f384f308c60711118 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:20:52 +0300 Subject: [PATCH 16/16] fix(orders): fix cancel order race condition using time-based cache (#2232) This commit addresses the issue where cancelled orders can sometimes remain in the orderbook due to P2P message caching and out-of-order message delivery. --- Cargo.lock | 1 + .../eth/web3_transport/websocket_transport.rs | 17 +--- mm2src/common/expirable_map.rs | 90 +++++++++++++------ mm2src/mm2_core/Cargo.toml | 1 + mm2src/mm2_core/src/data_asker.rs | 2 - mm2src/mm2_core/src/mm_ctx.rs | 3 +- mm2src/mm2_main/Cargo.toml | 2 +- mm2src/mm2_main/src/lp_healthcheck.rs | 74 +-------------- mm2src/mm2_main/src/lp_ordermatch.rs | 86 +++++++++++++----- .../tests/mm2_tests/orderbook_sync_tests.rs | 75 +++++++++++++++- mm2src/mm2_p2p/src/lib.rs | 65 ++++++++++++++ 11 files changed, 278 insertions(+), 138 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 431959e5c5..08a985285b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4404,6 +4404,7 @@ dependencies = [ "mm2_err_handle", "mm2_event_stream", "mm2_metrics", + "mm2_p2p", "mm2_rpc", "primitives", "rand 0.7.3", diff --git a/mm2src/coins/eth/web3_transport/websocket_transport.rs b/mm2src/coins/eth/web3_transport/websocket_transport.rs index d17ef99d7e..fd1220e92e 100644 --- a/mm2src/coins/eth/web3_transport/websocket_transport.rs +++ b/mm2src/coins/eth/web3_transport/websocket_transport.rs @@ -97,12 +97,7 @@ impl WebsocketTransport { } } - async fn handle_keepalive( - &self, - wsocket: &mut WebSocketStream, - response_notifiers: &mut ExpirableMap>>, - expires_at: Option, - ) -> OuterAction { + async fn handle_keepalive(&self, wsocket: &mut WebSocketStream, expires_at: Option) -> OuterAction { const SIMPLE_REQUEST: &str = r#"{"jsonrpc":"2.0","method":"net_version","params":[],"id": 0 }"#; if let Some(expires_at) = expires_at { @@ -112,10 +107,7 @@ impl WebsocketTransport { } } - // Drop expired response notifier channels - response_notifiers.clear_expired_entries(); - - let mut should_continue = Default::default(); + let mut should_continue = false; for _ in 0..MAX_ATTEMPTS { match wsocket .send(tokio_tungstenite_wasm::Message::Text(SIMPLE_REQUEST.to_string())) @@ -206,9 +198,6 @@ impl WebsocketTransport { } if let Some(id) = inc_event.get("id") { - // just to ensure we don't have outdated entries - response_notifiers.clear_expired_entries(); - let request_id = id.as_u64().unwrap_or_default() as usize; if let Some(notifier) = response_notifiers.remove(&request_id) { @@ -279,7 +268,7 @@ impl WebsocketTransport { loop { futures_util::select! { _ = keepalive_interval.next().fuse() => { - match self.handle_keepalive(&mut wsocket, &mut response_notifiers, expires_at).await { + match self.handle_keepalive(&mut wsocket, expires_at).await { OuterAction::None => {}, OuterAction::Continue => continue, OuterAction::Break => break, diff --git a/mm2src/common/expirable_map.rs b/mm2src/common/expirable_map.rs index e9fe7f0b4f..996e2edfae 100644 --- a/mm2src/common/expirable_map.rs +++ b/mm2src/common/expirable_map.rs @@ -5,7 +5,7 @@ use instant::{Duration, Instant}; use rustc_hash::FxHashMap; -use std::hash::Hash; +use std::{collections::BTreeMap, hash::Hash}; #[derive(Clone, Debug)] pub struct ExpirableEntry { @@ -36,45 +36,83 @@ impl ExpirableEntry { pub fn has_longer_life_than(&self, min_ttl: Duration) -> bool { self.expires_at > Instant::now() + min_ttl } } -impl Default for ExpirableMap { +impl Default for ExpirableMap { fn default() -> Self { Self::new() } } /// A map that allows associating values with keys and expiring entries. -/// It is important to note that this implementation does not automatically -/// remove any entries; it is the caller's responsibility to invoke `clear_expired_entries` -/// at specified intervals. +/// It is important to note that this implementation does not have a background worker to +/// automatically clear expired entries. Outdated entries are only removed when the control flow +/// is handed back to the map mutably (i.e. some mutable method of the map is invoked). /// /// WARNING: This is designed for performance-oriented use-cases utilizing `FxHashMap` /// under the hood and is not suitable for cryptographic purposes. #[derive(Clone, Debug)] -pub struct ExpirableMap(FxHashMap>); +pub struct ExpirableMap { + map: FxHashMap>, + /// A sorted inverse map from expiration times to keys to speed up expired entries clearing. + expiries: BTreeMap, +} -impl ExpirableMap { +impl ExpirableMap { /// Creates a new empty `ExpirableMap` #[inline] - pub fn new() -> Self { Self(FxHashMap::default()) } + pub fn new() -> Self { + Self { + map: FxHashMap::default(), + expiries: BTreeMap::new(), + } + } + + /// Returns the associated value if present and not expired. + #[inline] + pub fn get(&self, k: &K) -> Option<&V> { + self.map + .get(k) + .filter(|v| v.expires_at > Instant::now()) + .map(|v| &v.value) + } - /// Returns the associated value if present. + /// Removes a key-value pair from the map and returns the associated value if present and not expired. #[inline] - pub fn get(&mut self, k: &K) -> Option<&V> { self.0.get(k).map(|v| &v.value) } + pub fn remove(&mut self, k: &K) -> Option { + self.map.remove(k).filter(|v| v.expires_at > Instant::now()).map(|v| { + self.expiries.remove(&v.expires_at); + v.value + }) + } /// Inserts a key-value pair with an expiration duration. /// /// If a value already exists for the given key, it will be updated and then /// the old one will be returned. pub fn insert(&mut self, k: K, v: V, exp: Duration) -> Option { + self.clear_expired_entries(); let entry = ExpirableEntry::new(v, exp); - - self.0.insert(k, entry).map(|v| v.value) + self.expiries.insert(entry.expires_at, k); + self.map.insert(k, entry).map(|v| v.value) } /// Removes expired entries from the map. - pub fn clear_expired_entries(&mut self) { self.0.retain(|_k, v| Instant::now() < v.expires_at); } - - /// Removes a key-value pair from the map and returns the associated value if present. - #[inline] - pub fn remove(&mut self, k: &K) -> Option { self.0.remove(k).map(|v| v.value) } + /// + /// Iterates through the `expiries` in order, removing entries that have expired. + /// Stops at the first non-expired entry, leveraging the sorted nature of `BTreeMap`. + fn clear_expired_entries(&mut self) { + let now = Instant::now(); + + // `pop_first()` is used here as it efficiently removes expired entries. + // `first_key_value()` was considered as it wouldn't need re-insertion for + // non-expired entries, but it would require an extra remove operation for + // each expired entry. `pop_first()` needs only one re-insertion per call, + // which is an acceptable trade-off compared to multiple remove operations. + while let Some((exp, key)) = self.expiries.pop_first() { + if exp > now { + self.expiries.insert(exp, key); + break; + } + self.map.remove(&key); + } + } } #[cfg(any(test, target_arch = "wasm32"))] @@ -94,8 +132,8 @@ mod tests { let exp = Duration::from_secs(1); // Insert 2 entries with 1 sec expiration time - expirable_map.insert("key1".to_string(), value.to_string(), exp); - expirable_map.insert("key2".to_string(), value.to_string(), exp); + expirable_map.insert("key1", value, exp); + expirable_map.insert("key2", value, exp); // Wait for entries to expire Timer::sleep(2.).await; @@ -104,14 +142,14 @@ mod tests { expirable_map.clear_expired_entries(); // We waited for 2 seconds, so we shouldn't have any entry accessible - assert_eq!(expirable_map.0.len(), 0); + assert_eq!(expirable_map.map.len(), 0); // Insert 5 entries - expirable_map.insert("key1".to_string(), value.to_string(), Duration::from_secs(5)); - expirable_map.insert("key2".to_string(), value.to_string(), Duration::from_secs(4)); - expirable_map.insert("key3".to_string(), value.to_string(), Duration::from_secs(7)); - expirable_map.insert("key4".to_string(), value.to_string(), Duration::from_secs(2)); - expirable_map.insert("key5".to_string(), value.to_string(), Duration::from_millis(3750)); + expirable_map.insert("key1", value, Duration::from_secs(5)); + expirable_map.insert("key2", value, Duration::from_secs(4)); + expirable_map.insert("key3", value, Duration::from_secs(7)); + expirable_map.insert("key4", value, Duration::from_secs(2)); + expirable_map.insert("key5", value, Duration::from_millis(3750)); // Wait 2 seconds to expire some entries Timer::sleep(2.).await; @@ -120,6 +158,6 @@ mod tests { expirable_map.clear_expired_entries(); // We waited for 2 seconds, only one entry should expire - assert_eq!(expirable_map.0.len(), 4); + assert_eq!(expirable_map.map.len(), 4); }); } diff --git a/mm2src/mm2_core/Cargo.toml b/mm2src/mm2_core/Cargo.toml index 943dcac280..a9d308be9a 100644 --- a/mm2src/mm2_core/Cargo.toml +++ b/mm2src/mm2_core/Cargo.toml @@ -20,6 +20,7 @@ lazy_static = "1.4" mm2_err_handle = { path = "../mm2_err_handle" } mm2_event_stream = { path = "../mm2_event_stream" } mm2_metrics = { path = "../mm2_metrics" } +mm2_libp2p = { path = "../mm2_p2p", package = "mm2_p2p" } primitives = { path = "../mm2_bitcoin/primitives" } rand = { version = "0.7", features = ["std", "small_rng", "wasm-bindgen"] } serde = "1" diff --git a/mm2src/mm2_core/src/data_asker.rs b/mm2src/mm2_core/src/data_asker.rs index 5d01310630..7f32f93365 100644 --- a/mm2src/mm2_core/src/data_asker.rs +++ b/mm2src/mm2_core/src/data_asker.rs @@ -113,8 +113,6 @@ pub async fn send_asked_data_rpc( asked_data: SendAskedDataRequest, ) -> Result> { let mut awaiting_asks = ctx.data_asker.awaiting_asks.lock().await; - awaiting_asks.clear_expired_entries(); - match awaiting_asks.remove(&asked_data.data_id) { Some(sender) => { sender.send(asked_data.data).map_to_mm(|_| { diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index 6e060d91c3..3f56970f3c 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -10,6 +10,7 @@ use futures::lock::Mutex as AsyncMutex; use gstuff::{try_s, Constructible, ERR, ERRL}; use lazy_static::lazy_static; use mm2_event_stream::{controller::Controller, Event, EventStreamConfiguration}; +use mm2_libp2p::PeerAddress; use mm2_metrics::{MetricsArc, MetricsOps}; use primitives::hash::H160; use rand::Rng; @@ -145,7 +146,7 @@ pub struct MmCtx { #[cfg(not(target_arch = "wasm32"))] pub async_sqlite_connection: Constructible>>, /// Links the RPC context to the P2P context to handle health check responses. - pub healthcheck_response_handler: AsyncMutex>>, + pub healthcheck_response_handler: AsyncMutex>>, } impl MmCtx { diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 53154efa05..07091e2631 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -65,7 +65,7 @@ mm2_err_handle = { path = "../mm2_err_handle" } mm2_event_stream = { path = "../mm2_event_stream" } mm2_gui_storage = { path = "../mm2_gui_storage" } mm2_io = { path = "../mm2_io" } -mm2-libp2p = { path = "../mm2_p2p", package = "mm2_p2p" } +mm2_libp2p = { path = "../mm2_p2p", package = "mm2_p2p" } mm2_metrics = { path = "../mm2_metrics" } mm2_net = { path = "../mm2_net", features = ["event-stream", "p2p"] } mm2_number = { path = "../mm2_number" } diff --git a/mm2src/mm2_main/src/lp_healthcheck.rs b/mm2src/mm2_main/src/lp_healthcheck.rs index 722bc21402..849f478d5f 100644 --- a/mm2src/mm2_main/src/lp_healthcheck.rs +++ b/mm2src/mm2_main/src/lp_healthcheck.rs @@ -9,12 +9,11 @@ use instant::{Duration, Instant}; use lazy_static::lazy_static; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::MmError; -use mm2_libp2p::{decode_message, encode_message, pub_sub_topic, Libp2pPublic, TopicPrefix}; +use mm2_libp2p::{decode_message, encode_message, pub_sub_topic, Libp2pPublic, PeerAddress, TopicPrefix}; use mm2_net::p2p::P2PContext; use ser_error_derive::SerializeErrorType; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; -use std::str::FromStr; use std::sync::Mutex; use crate::lp_network::broadcast_p2p_msg; @@ -37,71 +36,6 @@ pub(crate) struct HealthcheckMessage { data: HealthcheckData, } -/// Wrapper of `libp2p::PeerId` with trait additional implementations. -/// -/// TODO: This should be used as a replacement of `libp2p::PeerId` in the entire project. -#[derive(Clone, Copy, Debug, Display, PartialEq)] -pub struct PeerAddress(mm2_libp2p::PeerId); - -impl From for PeerAddress { - fn from(value: mm2_libp2p::PeerId) -> Self { Self(value) } -} - -impl From for mm2_libp2p::PeerId { - fn from(value: PeerAddress) -> Self { value.0 } -} - -impl Serialize for PeerAddress { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.0.to_string()) - } -} - -impl<'de> Deserialize<'de> for PeerAddress { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct PeerAddressVisitor; - - impl<'de> serde::de::Visitor<'de> for PeerAddressVisitor { - type Value = PeerAddress; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a string representation of peer id.") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - if value.len() > 100 { - return Err(serde::de::Error::invalid_length( - value.len(), - &"peer id cannot exceed 100 characters.", - )); - } - - Ok(mm2_libp2p::PeerId::from_str(value) - .map_err(serde::de::Error::custom)? - .into()) - } - - fn visit_string(self, value: String) -> Result - where - E: serde::de::Error, - { - self.visit_str(&value) - } - } - - deserializer.deserialize_str(PeerAddressVisitor) - } -} - #[derive(Debug, Display)] enum SignValidationError { #[display( @@ -331,8 +265,7 @@ pub async fn peer_connection_healthcheck_rpc( { let mut book = ctx.healthcheck_response_handler.lock().await; - book.clear_expired_entries(); - book.insert(target_peer_address.to_string(), tx, address_record_exp); + book.insert(target_peer_address, tx, address_record_exp); } broadcast_p2p_msg( @@ -395,7 +328,7 @@ pub(crate) async fn process_p2p_healthcheck_message(ctx: &MmArc, message: mm2_li } else { // The requested peer is healthy; signal the response channel. let mut response_handler = ctx.healthcheck_response_handler.lock().await; - if let Some(tx) = response_handler.remove(&sender_peer.to_string()) { + if let Some(tx) = response_handler.remove(&sender_peer) { if tx.send(()).is_err() { log::error!("Result channel isn't present for peer '{sender_peer}'."); }; @@ -409,6 +342,7 @@ pub(crate) async fn process_p2p_healthcheck_message(ctx: &MmArc, message: mm2_li #[cfg(any(test, target_arch = "wasm32"))] mod tests { use std::mem::discriminant; + use std::str::FromStr; use super::*; use common::cross_test; diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index 9829fed75a..dc9b85082b 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -122,6 +122,7 @@ const TAKER_ORDER_TIMEOUT: u64 = 30; const ORDER_MATCH_TIMEOUT: u64 = 30; const ORDERBOOK_REQUESTING_TIMEOUT: u64 = MIN_ORDER_KEEP_ALIVE_INTERVAL * 2; const MAX_ORDERS_NUMBER_IN_ORDERBOOK_RESPONSE: usize = 1000; +const RECENTLY_CANCELLED_TIMEOUT: Duration = Duration::from_secs(120); #[cfg(not(test))] const TRIE_STATE_HISTORY_TIMEOUT: u64 = 14400; #[cfg(test)] @@ -331,6 +332,12 @@ async fn process_orders_keep_alive( Ok(()) } +#[inline] +fn process_maker_order_created(ctx: &MmArc, from_pubkey: String, created_msg: new_protocol::MakerOrderCreated) { + let order: OrderbookItem = (created_msg, from_pubkey).into(); + insert_or_update_order(ctx, order); +} + fn process_maker_order_updated( ctx: MmArc, from_pubkey: String, @@ -350,6 +357,22 @@ fn process_maker_order_updated( Ok(()) } +fn process_maker_order_cancelled(ctx: &MmArc, from_pubkey: String, cancelled_msg: new_protocol::MakerOrderCancelled) { + let uuid = Uuid::from(cancelled_msg.uuid); + let ordermatch_ctx = OrdermatchContext::from_ctx(ctx).expect("from_ctx failed"); + let mut orderbook = ordermatch_ctx.orderbook.lock(); + // Add the order to the recently cancelled list to ignore it if a new order with the same uuid + // is received within the `RECENTLY_CANCELLED_TIMEOUT` timeframe. + // We do this even if the order is in the order_set, because it could have been added through + // means other than the order creation message. + orderbook.recently_cancelled.insert(uuid, from_pubkey.clone()); + if let Some(order) = orderbook.order_set.get(&uuid) { + if order.pubkey == from_pubkey { + orderbook.remove_order_trie_update(uuid); + } + } +} + // fn verify_pubkey_orderbook(orderbook: &GetOrderbookPubkeyItem) -> Result<(), String> { // let keys: Vec<(_, _)> = orderbook // .orders @@ -456,16 +479,6 @@ fn insert_or_update_my_order(ctx: &MmArc, item: OrderbookItem, my_order: &MakerO } } -fn delete_order(ctx: &MmArc, pubkey: &str, uuid: Uuid) { - let ordermatch_ctx = OrdermatchContext::from_ctx(ctx).expect("from_ctx failed"); - let mut orderbook = ordermatch_ctx.orderbook.lock(); - if let Some(order) = orderbook.order_set.get(&uuid) { - if order.pubkey == pubkey { - orderbook.remove_order_trie_update(uuid); - } - } -} - fn delete_my_order(ctx: &MmArc, uuid: Uuid, p2p_privkey: Option) { let ordermatch_ctx: Arc = OrdermatchContext::from_ctx(ctx).expect("from_ctx failed"); let mut orderbook = ordermatch_ctx.orderbook.lock(); @@ -548,8 +561,7 @@ pub async fn process_msg(ctx: MmArc, from_peer: String, msg: &[u8], i_am_relay: log::debug!("received ordermatch message {:?}", message); match message { new_protocol::OrdermatchMessage::MakerOrderCreated(created_msg) => { - let order: OrderbookItem = (created_msg, hex::encode(pubkey.to_bytes().as_slice())).into(); - insert_or_update_order(&ctx, order); + process_maker_order_created(&ctx, pubkey.to_hex(), created_msg); Ok(()) }, new_protocol::OrdermatchMessage::PubkeyKeepAlive(keep_alive) => { @@ -576,7 +588,7 @@ pub async fn process_msg(ctx: MmArc, from_peer: String, msg: &[u8], i_am_relay: Ok(()) }, new_protocol::OrdermatchMessage::MakerOrderCancelled(cancelled_msg) => { - delete_order(&ctx, &pubkey.to_hex(), cancelled_msg.uuid.into()); + process_maker_order_cancelled(&ctx, pubkey.to_hex(), cancelled_msg); Ok(()) }, new_protocol::OrdermatchMessage::MakerOrderUpdated(updated_msg) => { @@ -1083,15 +1095,15 @@ fn maker_order_updated_p2p_notify( broadcast_p2p_msg(&ctx, topic, encoded_msg, peer_id); } -fn maker_order_cancelled_p2p_notify(ctx: MmArc, order: &MakerOrder) { +fn maker_order_cancelled_p2p_notify(ctx: &MmArc, order: &MakerOrder) { let message = new_protocol::OrdermatchMessage::MakerOrderCancelled(new_protocol::MakerOrderCancelled { uuid: order.uuid.into(), timestamp: now_sec(), pair_trie_root: H64::default(), }); - delete_my_order(&ctx, order.uuid, order.p2p_privkey); + delete_my_order(ctx, order.uuid, order.p2p_privkey); log::debug!("maker_order_cancelled_p2p_notify called, message {:?}", message); - broadcast_ordermatch_message(&ctx, order.orderbook_topic(), message, order.p2p_keypair()); + broadcast_ordermatch_message(ctx, order.orderbook_topic(), message, order.p2p_keypair()); } pub struct BalanceUpdateOrdermatchHandler { @@ -1141,7 +1153,7 @@ impl BalanceTradeFeeUpdatedHandler for BalanceUpdateOrdermatchHandler { // This checks that the order hasn't been removed by another process if removed_order_mutex.is_some() { // cancel the order - maker_order_cancelled_p2p_notify(ctx.clone(), &order); + maker_order_cancelled_p2p_notify(&ctx, &order); delete_my_maker_order( ctx.clone(), order.clone(), @@ -2477,7 +2489,6 @@ fn collect_orderbook_metrics(ctx: &MmArc, orderbook: &Orderbook) { mm_gauge!(ctx.metrics, "orderbook.memory_db", memory_db_size as f64); } -#[derive(Default)] struct Orderbook { /// A map from (base, rel). ordered: HashMap<(String, String), BTreeSet>, @@ -2490,12 +2501,34 @@ struct Orderbook { order_set: HashMap, /// a map of orderbook states of known maker pubkeys pubkeys_state: HashMap, + /// The `TimeCache` of recently canceled orders, mapping `Uuid` to the maker pubkey as `String`, + /// used to avoid order recreation in case of out-of-order p2p messages, + /// e.g., when receiving the order cancellation message before the order is created. + /// Entries are kept for `RECENTLY_CANCELLED_TIMEOUT` seconds. + recently_cancelled: TimeCache, topics_subscribed_to: HashMap, /// MemoryDB instance to store Patricia Tries data memory_db: MemoryDB, my_p2p_pubkeys: HashSet, } +impl Default for Orderbook { + fn default() -> Self { + Orderbook { + ordered: HashMap::default(), + pairs_existing_for_base: HashMap::default(), + pairs_existing_for_rel: HashMap::default(), + unordered: HashMap::default(), + order_set: HashMap::default(), + pubkeys_state: HashMap::default(), + recently_cancelled: TimeCache::new(RECENTLY_CANCELLED_TIMEOUT), + topics_subscribed_to: HashMap::default(), + memory_db: MemoryDB::default(), + my_p2p_pubkeys: HashSet::default(), + } + } +} + fn hashed_null_node() -> TrieHash { ::hashed_null_node() } impl Orderbook { @@ -2512,6 +2545,12 @@ impl Orderbook { fn find_order_by_uuid(&self, uuid: &Uuid) -> Option { self.order_set.get(uuid).cloned() } fn insert_or_update_order_update_trie(&mut self, order: OrderbookItem) { + // Ignore the order if it was recently cancelled + if self.recently_cancelled.get(&order.uuid) == Some(&order.pubkey) { + warn!("Maker order {} was recently cancelled, ignoring", order.uuid); + return; + } + let zero = BigRational::from_integer(0.into()); if order.max_volume <= zero || order.price <= zero || order.min_volume < zero { self.remove_order_trie_update(order.uuid); @@ -3314,6 +3353,7 @@ pub async fn lp_ordermatch_loop(ctx: MmArc) { // This checks that the order hasn't been removed by another process if let Some(order_mutex) = removed_order_mutex { let order = order_mutex.lock().await; + maker_order_cancelled_p2p_notify(&ctx, &order); delete_my_maker_order( ctx.clone(), order.clone(), @@ -3430,7 +3470,7 @@ async fn check_balance_for_maker_orders(ctx: MmArc, ordermatch_ctx: &OrdermatchC let removed_order_mutex = ordermatch_ctx.maker_orders_ctx.lock().remove_order(&uuid); // This checks that the order hasn't been removed by another process if removed_order_mutex.is_some() { - maker_order_cancelled_p2p_notify(ctx.clone(), &order); + maker_order_cancelled_p2p_notify(&ctx, &order); delete_my_maker_order(ctx.clone(), order.clone(), reason) .compat() .await @@ -4739,7 +4779,7 @@ async fn cancel_previous_maker_orders( let removed_order_mutex = ordermatch_ctx.maker_orders_ctx.lock().remove_order(&uuid); // This checks that the uuid, &order.base hasn't been removed by another process if removed_order_mutex.is_some() { - maker_order_cancelled_p2p_notify(ctx.clone(), &order); + maker_order_cancelled_p2p_notify(ctx, &order); delete_my_maker_order(ctx.clone(), order.clone(), MakerOrderCancellationReason::Cancelled) .compat() .await @@ -5141,7 +5181,7 @@ pub async fn cancel_order(ctx: MmArc, req: CancelOrderReq) -> Result Result> let removed_order_mutex = ordermatch_ctx.maker_orders_ctx.lock().remove_order(&order.uuid); // This checks that the order hasn't been removed by another process if removed_order_mutex.is_some() { - maker_order_cancelled_p2p_notify(ctx.clone(), &order); + maker_order_cancelled_p2p_notify(&ctx, &order); delete_my_maker_order(ctx, order.clone(), MakerOrderCancellationReason::Cancelled) .compat() .await @@ -5539,7 +5579,7 @@ pub async fn cancel_orders_by(ctx: &MmArc, cancel_by: CancelBy) -> Result<(Vec = Secp256k1::signing_only(); } +/// Wrapper of `libp2p::PeerId` with trait additional implementations. +/// +/// TODO: This should be used as a replacement of `libp2p::PeerId` in the entire project. +#[derive(Clone, Copy, Debug, Display, Eq, Hash, PartialEq)] +pub struct PeerAddress(PeerId); + +impl From for PeerAddress { + fn from(value: PeerId) -> Self { Self(value) } +} + +impl From for PeerId { + fn from(value: PeerAddress) -> Self { value.0 } +} + +impl Serialize for PeerAddress { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.0.to_string()) + } +} + +impl<'de> Deserialize<'de> for PeerAddress { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct PeerAddressVisitor; + + impl<'de> serde::de::Visitor<'de> for PeerAddressVisitor { + type Value = PeerAddress; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string representation of peer id.") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + if value.len() > 100 { + return Err(serde::de::Error::invalid_length( + value.len(), + &"peer id cannot exceed 100 characters.", + )); + } + + Ok(PeerId::from_str(value).map_err(de::Error::custom)?.into()) + } + + fn visit_string(self, value: String) -> Result + where + E: de::Error, + { + self.visit_str(&value) + } + } + + deserializer.deserialize_str(PeerAddressVisitor) + } +} + #[derive(Clone, Copy, Debug)] pub enum NetworkInfo { /// The in-memory network.