From 6402adf34858b8391d0079fb2d1586f3aa2fc845 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Tue, 11 Jul 2023 12:51:14 +0300 Subject: [PATCH 01/53] fix watcher taker-side bug after restart --- mm2src/mm2_main/src/lp_swap.rs | 3 +- mm2src/mm2_main/src/lp_swap/saved_swap.rs | 2 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 277 ++++++++++++++- .../tests/docker_tests/swap_watcher_tests.rs | 315 +++++++++++++++++- 4 files changed, 578 insertions(+), 19 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index f2d6b91e4b..8780058bc2 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -124,7 +124,8 @@ pub use swap_watcher::{process_watcher_msg, watcher_topic, TakerSwapWatcherData, use taker_swap::TakerSwapEvent; pub use taker_swap::{calc_max_taker_vol, check_balance_for_taker_swap, max_taker_vol, max_taker_vol_from_available, run_taker_swap, taker_swap_trade_preimage, RunTakerSwapInput, TakerSavedSwap, TakerSwap, - TakerSwapData, TakerSwapPreparedParams, TakerTradePreimage, WATCHER_MESSAGE_SENT_LOG}; + TakerSwapData, TakerSwapPreparedParams, TakerTradePreimage, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, + TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, WATCHER_MESSAGE_SENT_LOG}; pub use trade_preimage::trade_preimage_rpc; pub const SWAP_PREFIX: TopicPrefix = "swap"; diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index 3519d45596..5e723f647c 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -104,7 +104,7 @@ impl SavedSwap { Ok(try_s!(maker_swap.recover_funds().await)) }, SavedSwap::Taker(saved) => { - let (taker_swap, _) = try_s!(TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, saved)); + let (taker_swap, _) = try_s!(TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, saved).await); Ok(try_s!(taker_swap.recover_funds().await)) }, } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 9f1ad75f8d..383bee901a 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -90,6 +90,8 @@ pub const TAKER_ERROR_EVENTS: [&str; 15] = [ ]; pub const WATCHER_MESSAGE_SENT_LOG: &str = "Watcher message sent..."; +pub const MAKER_PAYMENT_SPENT_BY_WATCHER_LOG: &str = "Maker payment is spent by the watcher..."; +pub const TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG: &str = "Taker payment is refunded by the watcher..."; #[cfg(not(target_arch = "wasm32"))] pub fn stats_taker_swap_dir(ctx: &MmArc) -> PathBuf { ctx.dbdir().join("SWAPS").join("STATS").join("TAKER") } @@ -1599,6 +1601,7 @@ impl TakerSwap { Some(maker_payment_spend.tx_hex()), Some(taker_payment_refund.tx_hex()), )); + info!("{}", WATCHER_MESSAGE_SENT_LOG); }, Err(e) => error!( "The watcher message could not be sent, error creating at least one of the preimages: {}", @@ -1901,10 +1904,10 @@ impl TakerSwap { SavedSwap::Taker(swap) => swap, SavedSwap::Maker(_) => return ERR!("Can not load TakerSwap from SavedSwap::Maker uuid: {}", swap_uuid), }; - Self::load_from_saved(ctx, maker_coin, taker_coin, saved) + Self::load_from_saved(ctx, maker_coin, taker_coin, saved).await } - pub fn load_from_saved( + pub async fn load_from_saved( ctx: MmArc, maker_coin: MmCoinEnum, taker_coin: MmCoinEnum, @@ -1944,7 +1947,7 @@ impl TakerSwap { }; let swap = TakerSwap::new( - ctx, + ctx.clone(), maker, data.maker_amount.clone().into(), data.taker_amount.clone().into(), @@ -1959,7 +1962,193 @@ impl TakerSwap { ); let command = saved.events.last().unwrap().get_command(); for saved_event in saved.events { - swap.apply_event(saved_event.event); + swap.apply_event(saved_event.event.clone()); + + if let TakerSwapEvent::WatcherMessageSent(_, _) = saved_event.event { + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let secret_hash = swap.r().secret_hash.0.clone(); + let maker_coin_start_block = swap.r().data.maker_coin_start_block; + let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); + + let taker_payment = match &swap.r().taker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about taker payment, swap is not recoverable"), + }; + + let maker_payment = match &swap.r().maker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about maker payment, swap is not recoverable"), + }; + let unique_data = swap.unique_swap_data(); + let watcher_reward = swap.r().watcher_reward; + + let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; + let taker_coin_start_block = swap.r().data.taker_coin_start_block; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); + + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, + }; + + let search_input = SearchForSwapTxSpendInput { + time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, + other_pub: other_maker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &maker_payment, + search_from_block: maker_coin_start_block, + swap_contract_address: &maker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }; + + let maker_payment_spend_tx = match swap.maker_coin.search_for_swap_tx_spend_other(search_input).await { + Ok(found_swap) => found_swap, + Err(e) => { + return ERR!("Error {} when trying to find maker payment spend", e); + }, + }; + + let search_input = SearchForSwapTxSpendInput { + time_lock: taker_payment_lock, + other_pub: other_taker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &taker_payment, + search_from_block: taker_coin_start_block, + swap_contract_address: &taker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }; + let taker_payment_spend_tx = match swap.taker_coin.search_for_swap_tx_spend_my(search_input).await { + Ok(found_swap) => found_swap, + Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), + }; + + match (maker_payment_spend_tx, taker_payment_spend_tx) { + ( + Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx)), + Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx)), + ) => { + let tx_hash = taker_payment_spend_tx.tx_hash(); + info!("Taker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), + tx_hash, + }; + let secret = match swap + .taker_coin + .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) + .await + { + Ok(bytes) => H256Json::from(bytes.as_slice()), + Err(_) => { + return ERR!("Could not extract secret from taker payment spend transaction"); + }, + }; + + let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { + transaction: tx_ident, + secret, + }); + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + + let tx_hash = maker_payment_spend_tx.tx_hash(); + info!("Maker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::MakerPaymentSpent(tx_ident); + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + swap.apply_event(TakerSwapEvent::Finished); + info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); + return Ok((swap, Some(TakerSwapCommand::Finish))); + }, + ( + Some(FoundSwapTxSpend::Refunded(_)), + Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx)), + ) + | (None, Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { + let event = TakerSwapEvent::TakerPaymentWaitForSpendFailed( + "Taker payment wait for spend failed".into(), + ); + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + + let event = TakerSwapEvent::TakerPaymentWaitRefundStarted { + wait_until: swap.wait_refund_until(), + }; + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + + let event = TakerSwapEvent::TakerPaymentRefundStarted; + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + + let tx_hash = taker_payment_refund_tx.tx_hash(); + info!("Taker refund tx hash {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(taker_payment_refund_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::TakerPaymentRefunded(Some(tx_ident)); + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + + swap.apply_event(TakerSwapEvent::TakerPaymentRefundFinished); + info!("{}", TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG); + return Ok((swap, Some(TakerSwapCommand::Finish))); + }, + (Some(_), None) => { + return ERR!( + "Could not find taker payment spend, while the maker payment is already spent or refunded" + ) + }, + _ => (), + } + } } Ok((swap, command)) } @@ -2630,7 +2819,13 @@ mod taker_swap_tests { .mock_safe(|_, _| MockResult::Return(Box::pin(futures::future::ready(Ok(None))))); let maker_coin = MmCoinEnum::Test(TestCoin::default()); let taker_coin = MmCoinEnum::Test(TestCoin::default()); - let (taker_swap, _) = TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, taker_saved_swap).unwrap(); + let (taker_swap, _) = block_on(TakerSwap::load_from_saved( + ctx, + maker_coin, + taker_coin, + taker_saved_swap, + )) + .unwrap(); let actual = block_on(taker_swap.recover_funds()).unwrap(); let expected = RecoveredSwap { action: RecoveredSwapAction::SpentOtherPayment, @@ -2672,7 +2867,13 @@ mod taker_swap_tests { }); let maker_coin = MmCoinEnum::Test(TestCoin::default()); let taker_coin = MmCoinEnum::Test(TestCoin::default()); - let (taker_swap, _) = TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, taker_saved_swap).unwrap(); + let (taker_swap, _) = block_on(TakerSwap::load_from_saved( + ctx, + maker_coin, + taker_coin, + taker_saved_swap, + )) + .unwrap(); let actual = block_on(taker_swap.recover_funds()).unwrap(); let expected = RecoveredSwap { action: RecoveredSwapAction::RefundedMyPayment, @@ -2719,7 +2920,13 @@ mod taker_swap_tests { }); let maker_coin = MmCoinEnum::Test(TestCoin::default()); let taker_coin = MmCoinEnum::Test(TestCoin::default()); - let (taker_swap, _) = TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, taker_saved_swap).unwrap(); + let (taker_swap, _) = block_on(TakerSwap::load_from_saved( + ctx, + maker_coin, + taker_coin, + taker_saved_swap, + )) + .unwrap(); let actual = block_on(taker_swap.recover_funds()).unwrap(); let expected = RecoveredSwap { action: RecoveredSwapAction::SpentOtherPayment, @@ -2757,7 +2964,13 @@ mod taker_swap_tests { }); let maker_coin = MmCoinEnum::Test(TestCoin::default()); let taker_coin = MmCoinEnum::Test(TestCoin::default()); - let (taker_swap, _) = TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, taker_saved_swap).unwrap(); + let (taker_swap, _) = block_on(TakerSwap::load_from_saved( + ctx, + maker_coin, + taker_coin, + taker_saved_swap, + )) + .unwrap(); let actual = block_on(taker_swap.recover_funds()).unwrap(); let expected = RecoveredSwap { action: RecoveredSwapAction::RefundedMyPayment, @@ -2788,7 +3001,13 @@ mod taker_swap_tests { }); let maker_coin = MmCoinEnum::Test(TestCoin::default()); let taker_coin = MmCoinEnum::Test(TestCoin::default()); - let (taker_swap, _) = TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, taker_saved_swap).unwrap(); + let (taker_swap, _) = block_on(TakerSwap::load_from_saved( + ctx, + maker_coin, + taker_coin, + taker_saved_swap, + )) + .unwrap(); let error = block_on(taker_swap.recover_funds()).unwrap_err(); assert!(error.contains("Too early to refund")); assert!(unsafe { SEARCH_TX_SPEND_CALLED }); @@ -2822,7 +3041,13 @@ mod taker_swap_tests { }); let maker_coin = MmCoinEnum::Test(TestCoin::default()); let taker_coin = MmCoinEnum::Test(TestCoin::default()); - let (taker_swap, _) = TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, taker_saved_swap).unwrap(); + let (taker_swap, _) = block_on(TakerSwap::load_from_saved( + ctx, + maker_coin, + taker_coin, + taker_saved_swap, + )) + .unwrap(); let actual = block_on(taker_swap.recover_funds()).unwrap(); let expected = RecoveredSwap { action: RecoveredSwapAction::SpentOtherPayment, @@ -2846,7 +3071,13 @@ mod taker_swap_tests { TestCoin::swap_contract_address.mock_safe(|_| MockResult::Return(None)); let maker_coin = MmCoinEnum::Test(TestCoin::default()); let taker_coin = MmCoinEnum::Test(TestCoin::default()); - let (taker_swap, _) = TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, taker_saved_swap).unwrap(); + let (taker_swap, _) = block_on(TakerSwap::load_from_saved( + ctx, + maker_coin, + taker_coin, + taker_saved_swap, + )) + .unwrap(); assert!(block_on(taker_swap.recover_funds()).is_err()); } @@ -2881,7 +3112,13 @@ mod taker_swap_tests { }); let maker_coin = MmCoinEnum::Test(TestCoin::default()); let taker_coin = MmCoinEnum::Test(TestCoin::default()); - let (taker_swap, _) = TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, taker_saved_swap).unwrap(); + let (taker_swap, _) = block_on(TakerSwap::load_from_saved( + ctx, + maker_coin, + taker_coin, + taker_saved_swap, + )) + .unwrap(); assert_eq!(unsafe { SWAP_CONTRACT_ADDRESS_CALLED }, 2); assert_eq!( @@ -2910,7 +3147,13 @@ mod taker_swap_tests { }); let maker_coin = MmCoinEnum::Test(TestCoin::default()); let taker_coin = MmCoinEnum::Test(TestCoin::default()); - let (taker_swap, _) = TakerSwap::load_from_saved(ctx, maker_coin, taker_coin, taker_saved_swap).unwrap(); + let (taker_swap, _) = block_on(TakerSwap::load_from_saved( + ctx, + maker_coin, + taker_coin, + taker_saved_swap, + )) + .unwrap(); assert_eq!(unsafe { SWAP_CONTRACT_ADDRESS_CALLED }, 1); let expected_addr = addr_from_str(ETH_DEV_SWAP_CONTRACT).unwrap(); @@ -3075,7 +3318,13 @@ mod taker_swap_tests { TestCoin::swap_contract_address.mock_safe(|_| MockResult::Return(None)); TestCoin::min_tx_amount.mock_safe(|_| MockResult::Return(BigDecimal::from(0))); - let (swap, _) = TakerSwap::load_from_saved(ctx.clone(), maker_coin, taker_coin, taker_saved_swap).unwrap(); + let (swap, _) = block_on(TakerSwap::load_from_saved( + ctx.clone(), + maker_coin, + taker_coin, + taker_saved_swap, + )) + .unwrap(); let swaps_ctx = SwapsContext::from_ctx(&ctx).unwrap(); let arc = Arc::new(swap); let weak_ref = Arc::downgrade(&arc); 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 264bbc4f9b..072e7b1e7b 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -13,12 +13,15 @@ use crypto::privkey::{key_pair_from_secret, key_pair_from_seed}; use futures01::Future; use mm2_main::mm2::lp_swap::{dex_fee_amount, dex_fee_amount_from_taker_coin, dex_fee_threshold, get_payment_locktime, MakerSwap, MAKER_PAYMENT_SENT_LOG, MAKER_PAYMENT_SPEND_FOUND_LOG, - MAKER_PAYMENT_SPEND_SENT_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, WATCHER_MESSAGE_SENT_LOG}; + MAKER_PAYMENT_SPEND_SENT_LOG, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, + TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, + WATCHER_MESSAGE_SENT_LOG}; use mm2_number::BigDecimal; use mm2_number::MmNumber; use mm2_test_helpers::for_tests::{enable_eth_coin, eth_jst_testnet_conf, eth_testnet_conf, mm_dump, my_balance, - mycoin1_conf, mycoin_conf, start_swaps, MarketMakerIt, Mm2TestConf, - DEFAULT_RPC_PASSWORD, ETH_DEV_NODES, ETH_DEV_SWAP_CONTRACT}; + mycoin1_conf, mycoin_conf, start_swaps, wait_for_swaps_finish_and_check_status, + MarketMakerIt, Mm2TestConf, DEFAULT_RPC_PASSWORD, ETH_DEV_NODES, + ETH_DEV_SWAP_CONTRACT}; use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::WatcherConf; use num_traits::{One, Zero}; @@ -254,6 +257,312 @@ fn start_swaps_and_get_balances( } } +#[test] +fn test_taker_marks_the_swap_as_complete_after_watcher_spends_maker_payment() { + let alice_privkey = hex::encode(random_secp256k1_secret()); + let bob_privkey = hex::encode(random_secp256k1_secret()); + let watcher_privkey = hex::encode(random_secp256k1_secret()); + + let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); + + let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + + let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); + let mut mm_bob = block_on(MarketMakerIt::start_with_envs( + bob_conf.conf.clone(), + bob_conf.rpc_password, + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); + log!("Bob log path: {}", mm_bob.log_path.display()); + + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); + + let watcher_conf = WatcherConf { + wait_taker_payment: 0., + wait_maker_payment_spend_factor: 0., + refund_start_factor: 1.5, + search_interval: 1.0, + }; + + let watcher_conf = Mm2TestConf::watcher_light_node( + &format!("0x{}", watcher_privkey), + &coins, + &[&mm_alice.ip.to_string()], + watcher_conf, + ) + .conf; + + let mut mm_watcher = block_on(MarketMakerIt::start_with_envs( + watcher_conf, + DEFAULT_RPC_PASSWORD.to_string(), + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + let (_watcher_dump_log, _watcher_dump_dashboard) = mm_dump(&mm_watcher.log_path); + + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + enable_coin(&mm_bob, "MYCOIN"); + enable_coin(&mm_bob, "MYCOIN1"); + enable_coin(&mm_watcher, "MYCOIN"); + enable_coin(&mm_watcher, "MYCOIN1"); + + block_on(start_swaps( + &mut mm_bob, + &mut mm_alice, + &[("MYCOIN1", "MYCOIN")], + 25., + 25., + 2., + )); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); + alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); + block_on(mm_alice.stop()).unwrap(); + block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); + + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPENT_BY_WATCHER_LOG))).unwrap(); + thread::sleep(Duration::from_secs(5)); + block_on(mm_alice.stop()).unwrap(); + + let mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf, + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + thread::sleep(Duration::from_secs(15)); +} + +#[test] +fn test_taker_marks_the_swap_as_complete_after_watcher_refunds_taker_payment() { + let alice_privkey = hex::encode(random_secp256k1_secret()); + let bob_privkey = hex::encode(random_secp256k1_secret()); + let watcher_privkey = hex::encode(random_secp256k1_secret()); + + let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); + + let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + + let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); + let mut mm_bob = block_on(MarketMakerIt::start_with_envs( + bob_conf.conf.clone(), + bob_conf.rpc_password, + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); + log!("Bob log path: {}", mm_bob.log_path.display()); + + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); + + let watcher_conf = WatcherConf { + wait_taker_payment: 0., + wait_maker_payment_spend_factor: 1., + refund_start_factor: 0., + search_interval: 1., + }; + + let watcher_conf = Mm2TestConf::watcher_light_node( + &format!("0x{}", watcher_privkey), + &coins, + &[&mm_alice.ip.to_string()], + watcher_conf, + ) + .conf; + + let mut mm_watcher = block_on(MarketMakerIt::start_with_envs( + watcher_conf, + DEFAULT_RPC_PASSWORD.to_string(), + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + let (_watcher_dump_log, _watcher_dump_dashboard) = mm_dump(&mm_watcher.log_path); + + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + enable_coin(&mm_bob, "MYCOIN"); + enable_coin(&mm_bob, "MYCOIN1"); + enable_coin(&mm_watcher, "MYCOIN"); + enable_coin(&mm_watcher, "MYCOIN1"); + + block_on(start_swaps( + &mut mm_bob, + &mut mm_alice, + &[("MYCOIN1", "MYCOIN")], + 25., + 25., + 2., + )); + + block_on(mm_bob.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SENT_LOG))).unwrap(); + block_on(mm_bob.stop()).unwrap(); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); + alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); + block_on(mm_alice.stop()).unwrap(); + block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); + + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + thread::sleep(Duration::from_secs(5)); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG))).unwrap(); + thread::sleep(Duration::from_secs(5)); + block_on(mm_alice.stop()).unwrap(); + + let mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf, + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + thread::sleep(Duration::from_secs(15)); +} + +#[test] +fn test_taker_completes_swap_after_restart() { + let alice_privkey = hex::encode(random_secp256k1_secret()); + let bob_privkey = hex::encode(random_secp256k1_secret()); + + let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); + + let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + + let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); + let mut mm_bob = block_on(MarketMakerIt::start_with_envs( + bob_conf.conf.clone(), + bob_conf.rpc_password, + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); + log!("Bob log path: {}", mm_bob.log_path.display()); + + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); + + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + enable_coin(&mm_bob, "MYCOIN"); + enable_coin(&mm_bob, "MYCOIN1"); + + let uuids = block_on(start_swaps( + &mut mm_bob, + &mut mm_alice, + &[("MYCOIN1", "MYCOIN")], + 25., + 25., + 2., + )); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); + alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); + block_on(mm_alice.stop()).unwrap(); + thread::sleep(Duration::from_secs(5)); + + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf, + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + block_on(wait_for_swaps_finish_and_check_status( + &mut mm_bob, + &mut mm_alice, + &uuids, + 2., + 25., + )); +} + #[test] fn test_watcher_spends_maker_payment_utxo_utxo() { let alice_privkey = hex::encode(random_secp256k1_secret()); From 2be4266a4034651261b34d44dab4c3d882942bc3 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Wed, 12 Jul 2023 17:35:15 +0300 Subject: [PATCH 02/53] order swap events after taker restart --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 265 ++++++++++++++-------- 1 file changed, 165 insertions(+), 100 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 383bee901a..19bbccfd30 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -1961,7 +1961,7 @@ impl TakerSwap { data.p2p_privkey.map(SerializableSecp256k1Keypair::into_inner), ); let command = saved.events.last().unwrap().get_command(); - for saved_event in saved.events { + for saved_event in &saved.events { swap.apply_event(saved_event.event.clone()); if let TakerSwapEvent::WatcherMessageSent(_, _) = saved_event.event { @@ -2029,55 +2029,68 @@ impl TakerSwap { Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx)), Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx)), ) => { - let tx_hash = taker_payment_spend_tx.tx_hash(); - info!("Taker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), - tx_hash, - }; - let secret = match swap - .taker_coin - .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) - .await + let mut execute_all = false; + if matches!( + saved.events.last().unwrap().event, + TakerSwapEvent::WatcherMessageSent(_, _) + ) { + let tx_hash = taker_payment_spend_tx.tx_hash(); + info!("Taker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), + tx_hash, + }; + let secret = match swap + .taker_coin + .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) + .await + { + Ok(bytes) => H256Json::from(bytes.as_slice()), + Err(_) => { + return ERR!("Could not extract secret from taker payment spend transaction"); + }, + }; + + let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { + transaction: tx_ident, + secret, + }); + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + + execute_all = true; + } + + if execute_all + | matches!(saved.events.last().unwrap().event, TakerSwapEvent::TakerPaymentSpent(_)) { - Ok(bytes) => H256Json::from(bytes.as_slice()), - Err(_) => { - return ERR!("Could not extract secret from taker payment spend transaction"); - }, - }; - - let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { - transaction: tx_ident, - secret, - }); - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); - - let tx_hash = maker_payment_spend_tx.tx_hash(); - info!("Maker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), - tx_hash, - }; - - let event = TakerSwapEvent::MakerPaymentSpent(tx_ident); - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); - swap.apply_event(TakerSwapEvent::Finished); + let tx_hash = maker_payment_spend_tx.tx_hash(); + info!("Maker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::MakerPaymentSpent(tx_ident); + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + swap.apply_event(TakerSwapEvent::Finished); + } + info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); return Ok((swap, Some(TakerSwapCommand::Finish))); }, @@ -2086,58 +2099,110 @@ impl TakerSwap { Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx)), ) | (None, Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { - let event = TakerSwapEvent::TakerPaymentWaitForSpendFailed( - "Taker payment wait for spend failed".into(), - ); - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); - - let event = TakerSwapEvent::TakerPaymentWaitRefundStarted { - wait_until: swap.wait_refund_until(), - }; - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); - - let event = TakerSwapEvent::TakerPaymentRefundStarted; - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); - - let tx_hash = taker_payment_refund_tx.tx_hash(); - info!("Taker refund tx hash {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(taker_payment_refund_tx.tx_hex()), - tx_hash, - }; - - let event = TakerSwapEvent::TakerPaymentRefunded(Some(tx_ident)); - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); + let mut execute_all = false; + if let TakerSwapEvent::WatcherMessageSent(_, _) = saved.events.last().unwrap().event { + let event = TakerSwapEvent::TakerPaymentWaitForSpendFailed( + "Taker payment wait for spend failed".into(), + ); + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + execute_all = true; + } + + if execute_all + | matches!( + saved.events.last().unwrap().event, + TakerSwapEvent::TakerPaymentWaitForSpendFailed(_) + ) + | matches!( + saved.events.last().unwrap().event, + TakerSwapEvent::TakerPaymentWaitConfirmFailed(_) + ) + { + let event = TakerSwapEvent::TakerPaymentWaitRefundStarted { + wait_until: swap.wait_refund_until(), + }; + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + execute_all = true; + } + + if execute_all + | matches!( + saved.events.last().unwrap().event, + TakerSwapEvent::TakerPaymentWaitRefundStarted { .. } + ) + | matches!( + saved.events.last().unwrap().event, + TakerSwapEvent::TakerPaymentRefundFailed(_) + ) + { + let event = TakerSwapEvent::TakerPaymentRefundStarted; + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + execute_all = true; + } + + if execute_all + | matches!( + saved.events.last().unwrap().event, + TakerSwapEvent::TakerPaymentRefundStarted + ) + { + let tx_hash = taker_payment_refund_tx.tx_hash(); + info!("Taker refund tx hash {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(taker_payment_refund_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::TakerPaymentRefunded(Some(tx_ident)); + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + execute_all = true; + } + + if execute_all + | matches!( + saved.events.last().unwrap().event, + TakerSwapEvent::TakerPaymentRefunded(_) + ) + { + let event = TakerSwapEvent::TakerPaymentRefundFinished; + swap.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + save_my_taker_swap_event(&ctx, &swap, to_save) + .await + .expect("!save_my_taker_swap_event"); + } - swap.apply_event(TakerSwapEvent::TakerPaymentRefundFinished); info!("{}", TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG); return Ok((swap, Some(TakerSwapCommand::Finish))); }, From 17b183708448ce67bd64880144929785f80cac0b Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 13 Jul 2023 14:12:18 +0300 Subject: [PATCH 03/53] improve tests for taker restart --- mm2src/mm2_main/src/lp_swap.rs | 3 +- .../tests/docker_tests/swap_watcher_tests.rs | 28 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 8780058bc2..db7e7f71e4 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -129,8 +129,8 @@ pub use taker_swap::{calc_max_taker_vol, check_balance_for_taker_swap, max_taker pub use trade_preimage::trade_preimage_rpc; pub const SWAP_PREFIX: TopicPrefix = "swap"; - pub const TX_HELPER_PREFIX: TopicPrefix = "txhlp"; +pub const SWAP_FINISHED_LOG: &str = "Swap finished: "; cfg_wasm32! { use mm2_db::indexed_db::{ConstructibleDb, DbLocked}; @@ -1180,6 +1180,7 @@ pub async fn swap_kick_starts(ctx: MmArc) -> Result, String> { let swaps = try_s!(SavedSwap::load_all_my_swaps_from_db(&ctx).await); for swap in swaps { if swap.is_finished() { + info!("{} {}", SWAP_FINISHED_LOG, swap.uuid()); continue; } 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 072e7b1e7b..6f92b07eff 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -13,7 +13,7 @@ use crypto::privkey::{key_pair_from_secret, key_pair_from_seed}; use futures01::Future; use mm2_main::mm2::lp_swap::{dex_fee_amount, dex_fee_amount_from_taker_coin, dex_fee_threshold, get_payment_locktime, MakerSwap, MAKER_PAYMENT_SENT_LOG, MAKER_PAYMENT_SPEND_FOUND_LOG, - MAKER_PAYMENT_SPEND_SENT_LOG, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, + MAKER_PAYMENT_SPEND_SENT_LOG, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, SWAP_FINISHED_LOG, TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, WATCHER_MESSAGE_SENT_LOG}; use mm2_number::BigDecimal; @@ -258,7 +258,7 @@ fn start_swaps_and_get_balances( } #[test] -fn test_taker_marks_the_swap_as_complete_after_watcher_spends_maker_payment() { +fn test_taker_saves_the_swap_as_finished_after_watcher_spends_maker_payment() { let alice_privkey = hex::encode(random_secp256k1_secret()); let bob_privkey = hex::encode(random_secp256k1_secret()); let watcher_privkey = hex::encode(random_secp256k1_secret()); @@ -323,7 +323,7 @@ fn test_taker_marks_the_swap_as_complete_after_watcher_spends_maker_payment() { enable_coin(&mm_watcher, "MYCOIN"); enable_coin(&mm_watcher, "MYCOIN1"); - block_on(start_swaps( + let uuids = block_on(start_swaps( &mut mm_bob, &mut mm_alice, &[("MYCOIN1", "MYCOIN")], @@ -351,10 +351,18 @@ fn test_taker_marks_the_swap_as_complete_after_watcher_spends_maker_payment() { enable_coin(&mm_alice, "MYCOIN1"); block_on(mm_alice.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPENT_BY_WATCHER_LOG))).unwrap(); - thread::sleep(Duration::from_secs(5)); + + block_on(wait_for_swaps_finish_and_check_status( + &mut mm_bob, + &mut mm_alice, + &uuids, + 2., + 25., + )); + block_on(mm_alice.stop()).unwrap(); - let mm_alice = block_on(MarketMakerIt::start_with_envs( + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( alice_conf.conf, alice_conf.rpc_password.clone(), None, @@ -367,11 +375,11 @@ fn test_taker_marks_the_swap_as_complete_after_watcher_spends_maker_payment() { enable_coin(&mm_alice, "MYCOIN"); enable_coin(&mm_alice, "MYCOIN1"); - thread::sleep(Duration::from_secs(15)); + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); } #[test] -fn test_taker_marks_the_swap_as_complete_after_watcher_refunds_taker_payment() { +fn test_taker_saves_the_swap_as_finished_after_watcher_refunds_taker_payment() { let alice_privkey = hex::encode(random_secp256k1_secret()); let bob_privkey = hex::encode(random_secp256k1_secret()); let watcher_privkey = hex::encode(random_secp256k1_secret()); @@ -436,7 +444,7 @@ fn test_taker_marks_the_swap_as_complete_after_watcher_refunds_taker_payment() { enable_coin(&mm_watcher, "MYCOIN"); enable_coin(&mm_watcher, "MYCOIN1"); - block_on(start_swaps( + let uuids = block_on(start_swaps( &mut mm_bob, &mut mm_alice, &[("MYCOIN1", "MYCOIN")], @@ -472,7 +480,7 @@ fn test_taker_marks_the_swap_as_complete_after_watcher_refunds_taker_payment() { thread::sleep(Duration::from_secs(5)); block_on(mm_alice.stop()).unwrap(); - let mm_alice = block_on(MarketMakerIt::start_with_envs( + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( alice_conf.conf, alice_conf.rpc_password.clone(), None, @@ -485,7 +493,7 @@ fn test_taker_marks_the_swap_as_complete_after_watcher_refunds_taker_payment() { enable_coin(&mm_alice, "MYCOIN"); enable_coin(&mm_alice, "MYCOIN1"); - thread::sleep(Duration::from_secs(15)); + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); } #[test] From 9f35e5cf45099392110880a9286ce5a1c6770f7f Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Fri, 14 Jul 2023 14:26:16 +0300 Subject: [PATCH 04/53] improve taker restart after watcher refund test --- .../tests/docker_tests/swap_watcher_tests.rs | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) 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 6f92b07eff..226566c018 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -19,9 +19,9 @@ use mm2_main::mm2::lp_swap::{dex_fee_amount, dex_fee_amount_from_taker_coin, dex use mm2_number::BigDecimal; use mm2_number::MmNumber; use mm2_test_helpers::for_tests::{enable_eth_coin, eth_jst_testnet_conf, eth_testnet_conf, mm_dump, my_balance, - mycoin1_conf, mycoin_conf, start_swaps, wait_for_swaps_finish_and_check_status, - MarketMakerIt, Mm2TestConf, DEFAULT_RPC_PASSWORD, ETH_DEV_NODES, - ETH_DEV_SWAP_CONTRACT}; + my_swap_status, mycoin1_conf, mycoin_conf, start_swaps, + wait_for_swaps_finish_and_check_status, MarketMakerIt, Mm2TestConf, + DEFAULT_RPC_PASSWORD, ETH_DEV_NODES, ETH_DEV_SWAP_CONTRACT}; use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::WatcherConf; use num_traits::{One, Zero}; @@ -474,10 +474,9 @@ fn test_taker_saves_the_swap_as_finished_after_watcher_refunds_taker_payment() { enable_coin(&mm_alice, "MYCOIN"); enable_coin(&mm_alice, "MYCOIN1"); - thread::sleep(Duration::from_secs(5)); - block_on(mm_alice.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG))).unwrap(); - thread::sleep(Duration::from_secs(5)); + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); + block_on(mm_alice.stop()).unwrap(); let mut mm_alice = block_on(MarketMakerIt::start_with_envs( @@ -494,6 +493,30 @@ fn test_taker_saves_the_swap_as_finished_after_watcher_refunds_taker_payment() { enable_coin(&mm_alice, "MYCOIN1"); block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); + + let expected_events = [ + "Started", + "Negotiated", + "TakerFeeSent", + "TakerPaymentInstructionsReceived", + "MakerPaymentReceived", + "MakerPaymentWaitConfirmStarted", + "MakerPaymentValidatedAndConfirmed", + "TakerPaymentSent", + "WatcherMessageSent", + "TakerPaymentWaitForSpendFailed", + "TakerPaymentWaitRefundStarted", + "TakerPaymentRefundStarted", + "TakerPaymentRefunded", + "TakerPaymentRefundFinished", + "Finished", + ]; + let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); + let events_array = status_response["result"]["events"].as_array().unwrap(); + let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); + let actual_events: Vec<&str> = actual_events.collect(); + + assert_eq!(expected_events, actual_events.as_slice()); } #[test] From fda5261886a84ff2be48e6b63591ca1d818ce5cc Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Fri, 14 Jul 2023 15:48:36 +0300 Subject: [PATCH 05/53] add append_event function for TakerSwap --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 77 +++++------------------ 1 file changed, 17 insertions(+), 60 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 19bbccfd30..2c9214dd95 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -821,6 +821,16 @@ impl TakerSwap { } } + async fn append_event(&self, ctx: &MmArc, event: TakerSwapEvent) { + self.apply_event(event.clone()); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + save_my_taker_swap_event(ctx, self, to_save) + .await + .expect("!save_my_taker_swap_event"); + } async fn handle_command( &self, command: TakerSwapCommand, @@ -2055,16 +2065,7 @@ impl TakerSwap { transaction: tx_ident, secret, }); - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); - + swap.append_event(&ctx, event).await; execute_all = true; } @@ -2079,16 +2080,7 @@ impl TakerSwap { }; let event = TakerSwapEvent::MakerPaymentSpent(tx_ident); - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); - swap.apply_event(TakerSwapEvent::Finished); + swap.append_event(&ctx, event).await; } info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); @@ -2104,14 +2096,7 @@ impl TakerSwap { let event = TakerSwapEvent::TakerPaymentWaitForSpendFailed( "Taker payment wait for spend failed".into(), ); - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); + swap.append_event(&ctx, event).await; execute_all = true; } @@ -2128,14 +2113,7 @@ impl TakerSwap { let event = TakerSwapEvent::TakerPaymentWaitRefundStarted { wait_until: swap.wait_refund_until(), }; - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); + swap.append_event(&ctx, event).await; execute_all = true; } @@ -2150,14 +2128,7 @@ impl TakerSwap { ) { let event = TakerSwapEvent::TakerPaymentRefundStarted; - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); + swap.append_event(&ctx, event).await; execute_all = true; } @@ -2175,14 +2146,7 @@ impl TakerSwap { }; let event = TakerSwapEvent::TakerPaymentRefunded(Some(tx_ident)); - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); + swap.append_event(&ctx, event).await; execute_all = true; } @@ -2193,14 +2157,7 @@ impl TakerSwap { ) { let event = TakerSwapEvent::TakerPaymentRefundFinished; - swap.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - save_my_taker_swap_event(&ctx, &swap, to_save) - .await - .expect("!save_my_taker_swap_event"); + swap.append_event(&ctx, event).await; } info!("{}", TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG); From 159c9644b8095ea8476d81d1ee52d58c46470ff3 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Tue, 18 Jul 2023 16:08:54 +0300 Subject: [PATCH 06/53] check watcher payments for unsuccessfully finished swaps --- mm2src/mm2_main/src/lp_ordermatch.rs | 9 + mm2src/mm2_main/src/lp_swap.rs | 22 +- mm2src/mm2_main/src/lp_swap/saved_swap.rs | 10 + mm2src/mm2_main/src/lp_swap/taker_swap.rs | 406 +++++++++--------- .../tests/docker_tests/swap_watcher_tests.rs | 20 +- 5 files changed, 244 insertions(+), 223 deletions(-) diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index df54276698..864cc4f35b 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -76,6 +76,9 @@ use crate::mm2::lp_swap::{calc_max_maker_vol, check_balance_for_maker_swap, chec CheckBalanceError, CheckBalanceResult, CoinVolumeInfo, MakerSwap, RunMakerSwapInput, RunTakerSwapInput, SwapConfirmationsSettings, TakerSwap}; +#[cfg(any(test, feature = "run-docker-tests"))] +use crate::mm2::lp_swap::taker_swap::FailAt; + pub use best_orders::{best_orders_rpc, best_orders_rpc_v2}; pub use orderbook_depth::orderbook_depth_rpc; pub use orderbook_rpc::{orderbook_rpc, orderbook_rpc_v2}; @@ -3065,6 +3068,10 @@ fn lp_connected_alice(ctx: MmArc, taker_order: TakerOrder, taker_match: TakerMat if let Err(e) = insert_new_swap_to_db(ctx.clone(), taker_coin.ticker(), maker_coin.ticker(), uuid, now).await { error!("Error {} on new swap insertion", e); } + + #[cfg(any(test, feature = "run-docker-tests"))] + let fail_at = std::env::var("TAKER_FAIL_AT").map(FailAt::from).ok(); + let taker_swap = TakerSwap::new( ctx.clone(), maker, @@ -3078,6 +3085,8 @@ fn lp_connected_alice(ctx: MmArc, taker_order: TakerOrder, taker_match: TakerMat taker_coin, locktime, taker_order.p2p_privkey.map(SerializableSecp256k1Keypair::into_inner), + #[cfg(any(test, feature = "run-docker-tests"))] + fail_at, ); run_taker_swap(RunTakerSwapInput::StartNew(taker_swap), ctx).await }; diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index db7e7f71e4..a0bd258f39 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -98,7 +98,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; #[path = "lp_swap/saved_swap.rs"] mod saved_swap; #[path = "lp_swap/swap_lock.rs"] mod swap_lock; #[path = "lp_swap/swap_watcher.rs"] pub(crate) mod swap_watcher; -#[path = "lp_swap/taker_swap.rs"] mod taker_swap; +#[path = "lp_swap/taker_swap.rs"] pub(crate) mod taker_swap; #[path = "lp_swap/trade_preimage.rs"] mod trade_preimage; #[cfg(target_arch = "wasm32")] @@ -122,10 +122,11 @@ pub use swap_watcher::{process_watcher_msg, watcher_topic, TakerSwapWatcherData, MAKER_PAYMENT_SPEND_SENT_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, TAKER_SWAP_ENTRY_TIMEOUT_SEC, WATCHER_PREFIX}; use taker_swap::TakerSwapEvent; -pub use taker_swap::{calc_max_taker_vol, check_balance_for_taker_swap, max_taker_vol, max_taker_vol_from_available, - run_taker_swap, taker_swap_trade_preimage, RunTakerSwapInput, TakerSavedSwap, TakerSwap, - TakerSwapData, TakerSwapPreparedParams, TakerTradePreimage, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, - TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, WATCHER_MESSAGE_SENT_LOG}; +pub use taker_swap::{calc_max_taker_vol, check_balance_for_taker_swap, check_watcher_payments, max_taker_vol, + max_taker_vol_from_available, run_taker_swap, taker_swap_trade_preimage, RunTakerSwapInput, + TakerSavedSwap, TakerSwap, TakerSwapData, TakerSwapPreparedParams, TakerTradePreimage, + MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, + WATCHER_MESSAGE_SENT_LOG}; pub use trade_preimage::trade_preimage_rpc; pub const SWAP_PREFIX: TopicPrefix = "swap"; @@ -1179,7 +1180,7 @@ pub async fn swap_kick_starts(ctx: MmArc) -> Result, String> { let mut coins = HashSet::new(); let swaps = try_s!(SavedSwap::load_all_my_swaps_from_db(&ctx).await); for swap in swaps { - if swap.is_finished() { + if swap.is_finished_and_success() || !swap.contains_watcher_message() { info!("{} {}", SWAP_FINISHED_LOG, swap.uuid()); continue; } @@ -1925,7 +1926,10 @@ mod lp_swap_tests { maker_swap.fail_at = maker_fail_at; - let mut taker_swap = TakerSwap::new( + #[cfg(any(test, feature = "run-docker-tests"))] + let fail_at = std::env::var("TAKER_FAIL_AT").map(taker_swap::FailAt::from).ok(); + + let taker_swap = TakerSwap::new( taker_ctx.clone(), maker_key_pair.public().compressed_unprefixed().unwrap().into(), maker_amount.into(), @@ -1938,10 +1942,10 @@ mod lp_swap_tests { morty_taker.into(), lock_duration, None, + #[cfg(any(test, feature = "run-docker-tests"))] + fail_at, ); - taker_swap.fail_at = taker_fail_at; - block_on(futures::future::join( run_maker_swap(RunMakerSwapInput::StartNew(maker_swap), maker_ctx.clone()), run_taker_swap(RunTakerSwapInput::StartNew(taker_swap), taker_ctx.clone()), diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index 5e723f647c..3a950b43c4 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -56,6 +56,16 @@ impl SavedSwap { } } + pub fn contains_watcher_message(&self) -> bool { + match &self { + SavedSwap::Taker(taker_swap) => taker_swap + .events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::WatcherMessageSent(_, _))), + _ => false, + } + } + pub fn uuid(&self) -> &Uuid { match self { SavedSwap::Maker(swap) => &swap.uuid, diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 2c9214dd95..712c96b8c4 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -145,6 +145,55 @@ async fn save_my_taker_swap_event(ctx: &MmArc, swap: &TakerSwap, event: TakerSav } } +async fn remove_events_until_watcher_message(ctx: &MmArc, swap: &TakerSwap) -> Result<(), String> { + let swap = match SavedSwap::load_my_swap_from_db(ctx, swap.uuid).await { + Ok(Some(swap)) => swap, + Ok(None) => SavedSwap::Taker(TakerSavedSwap { + uuid: swap.uuid, + my_order_uuid: swap.my_order_uuid, + maker_amount: Some(swap.maker_amount.to_decimal()), + maker_coin: Some(swap.maker_coin.ticker().to_owned()), + maker_coin_usd_price: None, + taker_amount: Some(swap.taker_amount.to_decimal()), + taker_coin: Some(swap.taker_coin.ticker().to_owned()), + taker_coin_usd_price: None, + gui: ctx.gui().map(|g| g.to_owned()), + mm_version: Some(ctx.mm_version.to_owned()), + events: vec![], + success_events: if ctx.use_watchers() + && swap.taker_coin.is_supported_by_watchers() + && swap.maker_coin.is_supported_by_watchers() + { + TAKER_USING_WATCHERS_SUCCESS_EVENTS + .iter() + .map(<&str>::to_string) + .collect() + } else { + TAKER_SUCCESS_EVENTS.iter().map(<&str>::to_string).collect() + }, + error_events: TAKER_ERROR_EVENTS.iter().map(<&str>::to_string).collect(), + }), + Err(e) => return ERR!("{}", e), + }; + + if let SavedSwap::Taker(mut taker_swap) = swap { + while !matches!( + taker_swap.events.last().unwrap().event, + TakerSwapEvent::WatcherMessageSent(_, _) + ) { + taker_swap.events.pop(); + } + if taker_swap.is_success().unwrap_or(false) { + taker_swap.fetch_and_set_usd_prices().await; + } + let new_swap = SavedSwap::Taker(taker_swap); + try_s!(new_swap.save_to_db(ctx).await); + Ok(()) + } else { + ERR!("Expected SavedSwap::Taker, got {:?}", swap) + } +} + #[derive(Debug, Deserialize, PartialEq, Serialize)] pub struct TakerSavedEvent { pub timestamp: u64, @@ -550,15 +599,15 @@ pub struct TakerSwapMut { payment_instructions: Option, } -#[cfg(test)] -#[derive(Eq, PartialEq)] -pub(super) enum FailAt { +#[cfg(any(test, feature = "run-docker-tests"))] +#[derive(Eq, PartialEq, Debug)] +pub enum FailAt { TakerPayment, MakerPaymentSpend, TakerPaymentRefund, } -#[cfg(test)] +#[cfg(any(test, feature = "run-docker-tests"))] impl From for FailAt { fn from(str: String) -> Self { match str.as_str() { @@ -588,7 +637,7 @@ pub struct TakerSwap { conf_settings: SwapConfirmationsSettings, payment_locktime: u64, p2p_privkey: Option, - #[cfg(test)] + #[cfg(any(test, feature = "run-docker-tests"))] pub(super) fail_at: Option, } @@ -865,6 +914,7 @@ impl TakerSwap { taker_coin: MmCoinEnum, payment_locktime: u64, p2p_privkey: Option, + #[cfg(any(test, feature = "run-docker-tests"))] fail_at: Option, ) -> Self { TakerSwap { maker_coin, @@ -901,8 +951,8 @@ impl TakerSwap { payment_instructions: None, }), ctx, - #[cfg(test)] - fail_at: None, + #[cfg(any(test, feature = "run-docker-tests"))] + fail_at, } } @@ -1741,7 +1791,7 @@ impl TakerSwap { } async fn spend_maker_payment(&self) -> Result<(Option, Vec), String> { - #[cfg(test)] + #[cfg(any(test, feature = "run-docker-tests"))] if self.fail_at == Some(FailAt::MakerPaymentSpend) { return Ok((Some(TakerSwapCommand::Finish), vec![ TakerSwapEvent::MakerPaymentSpendFailed("Explicit test failure".into()), @@ -1956,6 +2006,9 @@ impl TakerSwap { .unwrap_or_else(|| taker_coin.requires_notarization()), }; + #[cfg(any(test, feature = "run-docker-tests"))] + let fail_at = std::env::var("TAKER_FAIL_AT").map(FailAt::from).ok(); + let swap = TakerSwap::new( ctx.clone(), maker, @@ -1969,206 +2022,16 @@ impl TakerSwap { taker_coin, data.lock_duration, data.p2p_privkey.map(SerializableSecp256k1Keypair::into_inner), + #[cfg(any(test, feature = "run-docker-tests"))] + fail_at, ); let command = saved.events.last().unwrap().get_command(); for saved_event in &saved.events { swap.apply_event(saved_event.event.clone()); if let TakerSwapEvent::WatcherMessageSent(_, _) = saved_event.event { - let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; - let secret_hash = swap.r().secret_hash.0.clone(); - let maker_coin_start_block = swap.r().data.maker_coin_start_block; - let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); - - let taker_payment = match &swap.r().taker_payment { - Some(tx) => tx.tx_hex.0.clone(), - None => return ERR!("No info about taker payment, swap is not recoverable"), - }; - - let maker_payment = match &swap.r().maker_payment { - Some(tx) => tx.tx_hex.0.clone(), - None => return ERR!("No info about maker payment, swap is not recoverable"), - }; - let unique_data = swap.unique_swap_data(); - let watcher_reward = swap.r().watcher_reward; - - let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; - let taker_coin_start_block = swap.r().data.taker_coin_start_block; - let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - - let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { - Ok(_) => swap.r().data.started_at as u32, - Err(_) => swap.r().data.taker_payment_lock as u32, - }; - - let search_input = SearchForSwapTxSpendInput { - time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, - other_pub: other_maker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &maker_payment, - search_from_block: maker_coin_start_block, - swap_contract_address: &maker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }; - - let maker_payment_spend_tx = match swap.maker_coin.search_for_swap_tx_spend_other(search_input).await { - Ok(found_swap) => found_swap, - Err(e) => { - return ERR!("Error {} when trying to find maker payment spend", e); - }, - }; - - let search_input = SearchForSwapTxSpendInput { - time_lock: taker_payment_lock, - other_pub: other_taker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &taker_payment, - search_from_block: taker_coin_start_block, - swap_contract_address: &taker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }; - let taker_payment_spend_tx = match swap.taker_coin.search_for_swap_tx_spend_my(search_input).await { - Ok(found_swap) => found_swap, - Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), - }; - - match (maker_payment_spend_tx, taker_payment_spend_tx) { - ( - Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx)), - Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx)), - ) => { - let mut execute_all = false; - if matches!( - saved.events.last().unwrap().event, - TakerSwapEvent::WatcherMessageSent(_, _) - ) { - let tx_hash = taker_payment_spend_tx.tx_hash(); - info!("Taker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), - tx_hash, - }; - let secret = match swap - .taker_coin - .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) - .await - { - Ok(bytes) => H256Json::from(bytes.as_slice()), - Err(_) => { - return ERR!("Could not extract secret from taker payment spend transaction"); - }, - }; - - let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { - transaction: tx_ident, - secret, - }); - swap.append_event(&ctx, event).await; - execute_all = true; - } - - if execute_all - | matches!(saved.events.last().unwrap().event, TakerSwapEvent::TakerPaymentSpent(_)) - { - let tx_hash = maker_payment_spend_tx.tx_hash(); - info!("Maker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), - tx_hash, - }; - - let event = TakerSwapEvent::MakerPaymentSpent(tx_ident); - swap.append_event(&ctx, event).await; - } - - info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); - return Ok((swap, Some(TakerSwapCommand::Finish))); - }, - ( - Some(FoundSwapTxSpend::Refunded(_)), - Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx)), - ) - | (None, Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { - let mut execute_all = false; - if let TakerSwapEvent::WatcherMessageSent(_, _) = saved.events.last().unwrap().event { - let event = TakerSwapEvent::TakerPaymentWaitForSpendFailed( - "Taker payment wait for spend failed".into(), - ); - swap.append_event(&ctx, event).await; - execute_all = true; - } - - if execute_all - | matches!( - saved.events.last().unwrap().event, - TakerSwapEvent::TakerPaymentWaitForSpendFailed(_) - ) - | matches!( - saved.events.last().unwrap().event, - TakerSwapEvent::TakerPaymentWaitConfirmFailed(_) - ) - { - let event = TakerSwapEvent::TakerPaymentWaitRefundStarted { - wait_until: swap.wait_refund_until(), - }; - swap.append_event(&ctx, event).await; - execute_all = true; - } - - if execute_all - | matches!( - saved.events.last().unwrap().event, - TakerSwapEvent::TakerPaymentWaitRefundStarted { .. } - ) - | matches!( - saved.events.last().unwrap().event, - TakerSwapEvent::TakerPaymentRefundFailed(_) - ) - { - let event = TakerSwapEvent::TakerPaymentRefundStarted; - swap.append_event(&ctx, event).await; - execute_all = true; - } - - if execute_all - | matches!( - saved.events.last().unwrap().event, - TakerSwapEvent::TakerPaymentRefundStarted - ) - { - let tx_hash = taker_payment_refund_tx.tx_hash(); - info!("Taker refund tx hash {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(taker_payment_refund_tx.tx_hex()), - tx_hash, - }; - - let event = TakerSwapEvent::TakerPaymentRefunded(Some(tx_ident)); - swap.append_event(&ctx, event).await; - execute_all = true; - } - - if execute_all - | matches!( - saved.events.last().unwrap().event, - TakerSwapEvent::TakerPaymentRefunded(_) - ) - { - let event = TakerSwapEvent::TakerPaymentRefundFinished; - swap.append_event(&ctx, event).await; - } - - info!("{}", TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG); - return Ok((swap, Some(TakerSwapCommand::Finish))); - }, - (Some(_), None) => { - return ERR!( - "Could not find taker payment spend, while the maker payment is already spent or refunded" - ) - }, - _ => (), + if check_watcher_payments(&swap, &ctx).await?.is_some() { + return Ok((swap, Some(TakerSwapCommand::Finish))); } } } @@ -2482,6 +2345,145 @@ pub struct TakerSwapPreparedParams { maker_payment_spend_trade_fee: TradeFee, } +pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc) -> Result, String> { + remove_events_until_watcher_message(ctx, swap).await?; + + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let secret_hash = swap.r().secret_hash.0.clone(); + let maker_coin_start_block = swap.r().data.maker_coin_start_block; + let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); + + let taker_payment = match &swap.r().taker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about taker payment, swap is not recoverable"), + }; + + let maker_payment = match &swap.r().maker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about maker payment, swap is not recoverable"), + }; + let unique_data = swap.unique_swap_data(); + let watcher_reward = swap.r().watcher_reward; + + let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; + let taker_coin_start_block = swap.r().data.taker_coin_start_block; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); + + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, + }; + + let search_input = SearchForSwapTxSpendInput { + time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, + other_pub: other_maker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &maker_payment, + search_from_block: maker_coin_start_block, + swap_contract_address: &maker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }; + + let maker_payment_spend_tx = match swap.maker_coin.search_for_swap_tx_spend_other(search_input).await { + Ok(found_swap) => found_swap, + Err(e) => { + return ERR!("Error {} when trying to find maker payment spend", e); + }, + }; + + let search_input = SearchForSwapTxSpendInput { + time_lock: taker_payment_lock, + other_pub: other_taker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &taker_payment, + search_from_block: taker_coin_start_block, + swap_contract_address: &taker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }; + let taker_payment_spend_tx = match swap.taker_coin.search_for_swap_tx_spend_my(search_input).await { + Ok(found_swap) => found_swap, + Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), + }; + + match (maker_payment_spend_tx, taker_payment_spend_tx) { + ( + Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx)), + Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx)), + ) => { + let tx_hash = taker_payment_spend_tx.tx_hash(); + info!("Taker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), + tx_hash, + }; + let secret = match swap + .taker_coin + .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) + .await + { + Ok(bytes) => H256Json::from(bytes.as_slice()), + Err(_) => { + return ERR!("Could not extract secret from taker payment spend transaction"); + }, + }; + + let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { + transaction: tx_ident, + secret, + }); + swap.append_event(ctx, event).await; + + let tx_hash = maker_payment_spend_tx.tx_hash(); + info!("Maker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::MakerPaymentSpent(tx_ident); + swap.append_event(ctx, event).await; + + info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); + Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) + }, + (Some(FoundSwapTxSpend::Refunded(_)), Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) + | (None, Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { + let event = TakerSwapEvent::TakerPaymentWaitForSpendFailed("Taker payment wait for spend failed".into()); + swap.append_event(ctx, event).await; + + let event = TakerSwapEvent::TakerPaymentWaitRefundStarted { + wait_until: swap.wait_refund_until(), + }; + swap.append_event(ctx, event).await; + + let event = TakerSwapEvent::TakerPaymentRefundStarted; + swap.append_event(ctx, event).await; + + let tx_hash = taker_payment_refund_tx.tx_hash(); + info!("Taker refund tx hash {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(taker_payment_refund_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::TakerPaymentRefunded(Some(tx_ident)); + swap.append_event(ctx, event).await; + + let event = TakerSwapEvent::TakerPaymentRefundFinished; + swap.append_event(ctx, event).await; + + info!("{}", TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG); + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) + }, + (Some(_), None) => { + ERR!("Could not find taker payment spend, while the maker payment is already spent or refunded") + }, + _ => Ok(None), + } +} + pub async fn check_balance_for_taker_swap( ctx: &MmArc, my_coin: &MmCoinEnum, 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 226566c018..ba1870247a 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 crypto::privkey::{key_pair_from_secret, key_pair_from_seed}; use futures01::Future; use mm2_main::mm2::lp_swap::{dex_fee_amount, dex_fee_amount_from_taker_coin, dex_fee_threshold, get_payment_locktime, MakerSwap, MAKER_PAYMENT_SENT_LOG, MAKER_PAYMENT_SPEND_FOUND_LOG, - MAKER_PAYMENT_SPEND_SENT_LOG, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, SWAP_FINISHED_LOG, - TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, - WATCHER_MESSAGE_SENT_LOG}; + MAKER_PAYMENT_SPEND_SENT_LOG, SWAP_FINISHED_LOG, TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, + TAKER_PAYMENT_REFUND_SENT_LOG, WATCHER_MESSAGE_SENT_LOG}; use mm2_number::BigDecimal; use mm2_number::MmNumber; use mm2_test_helpers::for_tests::{enable_eth_coin, eth_jst_testnet_conf, eth_testnet_conf, mm_dump, my_balance, @@ -258,7 +257,7 @@ fn start_swaps_and_get_balances( } #[test] -fn test_taker_saves_the_swap_as_finished_after_watcher_spends_maker_payment() { +fn test_taker_saves_the_swap_as_successful_after_watcher_spends_taker_payment() { let alice_privkey = hex::encode(random_secp256k1_secret()); let bob_privkey = hex::encode(random_secp256k1_secret()); let watcher_privkey = hex::encode(random_secp256k1_secret()); @@ -270,7 +269,7 @@ fn test_taker_saves_the_swap_as_finished_after_watcher_spends_maker_payment() { alice_conf.conf.clone(), alice_conf.rpc_password.clone(), None, - &[("USE_WATCHERS", "")], + &[("USE_WATCHERS", ""), ("TAKER_FAIL_AT", "maker_payment_spend")], )) .unwrap(); let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); @@ -331,10 +330,10 @@ fn test_taker_saves_the_swap_as_finished_after_watcher_spends_maker_payment() { 25., 2., )); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); - block_on(mm_alice.stop()).unwrap(); + + block_on(mm_bob.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); let mut mm_alice = block_on(MarketMakerIt::start_with_envs( @@ -350,8 +349,6 @@ fn test_taker_saves_the_swap_as_finished_after_watcher_spends_maker_payment() { enable_coin(&mm_alice, "MYCOIN"); enable_coin(&mm_alice, "MYCOIN1"); - block_on(mm_alice.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPENT_BY_WATCHER_LOG))).unwrap(); - block_on(wait_for_swaps_finish_and_check_status( &mut mm_bob, &mut mm_alice, @@ -359,11 +356,10 @@ fn test_taker_saves_the_swap_as_finished_after_watcher_spends_maker_payment() { 2., 25., )); - block_on(mm_alice.stop()).unwrap(); let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf, + alice_conf.conf.clone(), alice_conf.rpc_password.clone(), None, &[("USE_WATCHERS", "")], From b3fc0e19fc48133597e77246e89c0b7214de95d4 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Tue, 18 Jul 2023 18:13:42 +0300 Subject: [PATCH 07/53] add test case for taker restart after watcher spending maker payment --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 7 + .../tests/docker_tests/swap_watcher_tests.rs | 120 +++++++++++++++++- 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 712c96b8c4..a3e1aab99d 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -603,6 +603,7 @@ pub struct TakerSwapMut { #[derive(Eq, PartialEq, Debug)] pub enum FailAt { TakerPayment, + WaitForTakerPaymentSpend, MakerPaymentSpend, TakerPaymentRefund, } @@ -612,6 +613,7 @@ impl From for FailAt { fn from(str: String) -> Self { match str.as_str() { "taker_payment" => FailAt::TakerPayment, + "wait_for_taker_payment_spend" => FailAt::WaitForTakerPaymentSpend, "maker_payment_spend" => FailAt::MakerPaymentSpend, "taker_payment_refund" => FailAt::TakerPaymentRefund, _ => panic!("Invalid TAKER_FAIL_AT value"), @@ -1737,6 +1739,11 @@ impl TakerSwap { ])); } + #[cfg(any(test, feature = "run-docker-tests"))] + if self.fail_at == Some(FailAt::WaitForTakerPaymentSpend) { + panic!("Taker failed unexpectedly at wait for taker payment spend"); + } + info!("Taker payment confirmed"); let f = self.taker_coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { tx_bytes: &self.r().taker_payment.clone().unwrap().tx_hex, 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 ba1870247a..3e872bdd23 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -257,7 +257,7 @@ fn start_swaps_and_get_balances( } #[test] -fn test_taker_saves_the_swap_as_successful_after_watcher_spends_taker_payment() { +fn test_taker_saves_the_swap_as_successful_after_restart_fail_at_maker_payment_spend() { let alice_privkey = hex::encode(random_secp256k1_secret()); let bob_privkey = hex::encode(random_secp256k1_secret()); let watcher_privkey = hex::encode(random_secp256k1_secret()); @@ -374,6 +374,124 @@ fn test_taker_saves_the_swap_as_successful_after_watcher_spends_taker_payment() block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); } +#[test] +fn test_taker_saves_the_swap_as_successful_after_restart_fail_at_wait_for_taker_payment_spend() { + let alice_privkey = hex::encode(random_secp256k1_secret()); + let bob_privkey = hex::encode(random_secp256k1_secret()); + let watcher_privkey = hex::encode(random_secp256k1_secret()); + + let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); + + let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", ""), ("TAKER_FAIL_AT", "wait_for_taker_payment_spend")], + )) + .unwrap(); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + + let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); + let mut mm_bob = block_on(MarketMakerIt::start_with_envs( + bob_conf.conf.clone(), + bob_conf.rpc_password, + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); + log!("Bob log path: {}", mm_bob.log_path.display()); + + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); + + let watcher_conf = WatcherConf { + wait_taker_payment: 0., + wait_maker_payment_spend_factor: 0., + refund_start_factor: 1.5, + search_interval: 1.0, + }; + + let watcher_conf = Mm2TestConf::watcher_light_node( + &format!("0x{}", watcher_privkey), + &coins, + &[&mm_alice.ip.to_string()], + watcher_conf, + ) + .conf; + + let mut mm_watcher = block_on(MarketMakerIt::start_with_envs( + watcher_conf, + DEFAULT_RPC_PASSWORD.to_string(), + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + let (_watcher_dump_log, _watcher_dump_dashboard) = mm_dump(&mm_watcher.log_path); + + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + enable_coin(&mm_bob, "MYCOIN"); + enable_coin(&mm_bob, "MYCOIN1"); + enable_coin(&mm_watcher, "MYCOIN"); + enable_coin(&mm_watcher, "MYCOIN1"); + + let uuids = block_on(start_swaps( + &mut mm_bob, + &mut mm_alice, + &[("MYCOIN1", "MYCOIN")], + 25., + 25., + 2., + )); + alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); + block_on(mm_bob.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); + block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); + + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + block_on(wait_for_swaps_finish_and_check_status( + &mut mm_bob, + &mut mm_alice, + &uuids, + 2., + 25., + )); + block_on(mm_alice.stop()).unwrap(); + + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); +} + #[test] fn test_taker_saves_the_swap_as_finished_after_watcher_refunds_taker_payment() { let alice_privkey = hex::encode(random_secp256k1_secret()); From e177b5452f61d222609579dcdb4169c1a35dfa5c Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Tue, 18 Jul 2023 22:09:55 +0300 Subject: [PATCH 08/53] add TakerPaymentRefundedByWatcher event and a test case --- mm2src/mm2_main/src/lp_swap.rs | 2 +- mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs | 1 + mm2src/mm2_main/src/lp_swap/saved_swap.rs | 10 ++++++++++ mm2src/mm2_main/src/lp_swap/taker_swap.rs | 7 +++++++ .../tests/docker_tests/swap_watcher_tests.rs | 13 +++++++++---- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index a0bd258f39..6cf0855bf2 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -1180,7 +1180,7 @@ pub async fn swap_kick_starts(ctx: MmArc) -> Result, String> { let mut coins = HashSet::new(); let swaps = try_s!(SavedSwap::load_all_my_swaps_from_db(&ctx).await); for swap in swaps { - if swap.is_finished_and_success() || !swap.contains_watcher_message() { + if swap.is_finished_and_success() || !swap.contains_watcher_message() || swap.refunded_by_watcher() { info!("{} {}", SWAP_FINISHED_LOG, swap.uuid()); continue; } 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 a816695d84..3b9591d270 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -272,6 +272,7 @@ fn convert_taker_to_maker_events( | TakerSwapEvent::TakerPaymentRefunded(_) | TakerSwapEvent::TakerPaymentRefundFailed(_) | TakerSwapEvent::TakerPaymentRefundFinished + | TakerSwapEvent::TakerPaymentRefundedByWatcher | TakerSwapEvent::Finished => {} } } diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index 3a950b43c4..9a424d4de8 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -66,6 +66,16 @@ impl SavedSwap { } } + pub fn refunded_by_watcher(&self) -> bool { + match &self { + SavedSwap::Taker(taker_swap) => taker_swap + .events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefundedByWatcher)), + _ => false, + } + } + pub fn uuid(&self) -> &Uuid { match self { SavedSwap::Maker(swap) => &swap.uuid, diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index a3e1aab99d..75427ee7ad 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -232,6 +232,7 @@ impl TakerSavedEvent { TakerSwapEvent::TakerPaymentRefunded(_) => Some(TakerSwapCommand::FinalizeTakerPaymentRefund), TakerSwapEvent::TakerPaymentRefundFailed(_) => Some(TakerSwapCommand::Finish), TakerSwapEvent::TakerPaymentRefundFinished => Some(TakerSwapCommand::Finish), + TakerSwapEvent::TakerPaymentRefundedByWatcher => Some(TakerSwapCommand::Finish), TakerSwapEvent::Finished => None, } } @@ -696,6 +697,7 @@ pub enum TakerSwapEvent { TakerPaymentRefunded(Option), TakerPaymentRefundFailed(SwapError), TakerPaymentRefundFinished, + TakerPaymentRefundedByWatcher, Finished, } @@ -734,6 +736,7 @@ impl TakerSwapEvent { TakerSwapEvent::TakerPaymentRefunded(_) => "Taker payment refunded...".to_owned(), TakerSwapEvent::TakerPaymentRefundFailed(_) => "Taker payment refund failed...".to_owned(), TakerSwapEvent::TakerPaymentRefundFinished => "Taker payment refund finished...".to_owned(), + TakerSwapEvent::TakerPaymentRefundedByWatcher => "TakerPaymentRefundedByWatcher...".to_owned(), TakerSwapEvent::Finished => "Finished".to_owned(), } } @@ -868,6 +871,7 @@ impl TakerSwap { TakerSwapEvent::TakerPaymentRefunded(tx) => self.w().taker_payment_refund = tx, TakerSwapEvent::TakerPaymentRefundFailed(err) => self.errors.lock().push(err), TakerSwapEvent::TakerPaymentRefundFinished => (), + TakerSwapEvent::TakerPaymentRefundedByWatcher => (), TakerSwapEvent::Finished => self.finished_at.store(now_sec(), Ordering::Relaxed), } } @@ -2481,6 +2485,9 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc) -> Result Date: Tue, 18 Jul 2023 22:34:05 +0300 Subject: [PATCH 09/53] fix a test case --- mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 7b090963d1..b984180b3e 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -570,13 +570,12 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa 25., 2., )); + alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); block_on(mm_bob.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SENT_LOG))).unwrap(); block_on(mm_bob.stop()).unwrap(); block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); - alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); - block_on(mm_alice.stop()).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); let mut mm_alice = block_on(MarketMakerIt::start_with_envs( From 3508dcda3f19d4835379604796dd1f8411579e25 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Tue, 18 Jul 2023 22:46:42 +0300 Subject: [PATCH 10/53] add new test case taker restart after watcher refund --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 14 +- .../tests/docker_tests/swap_watcher_tests.rs | 150 +++++++++++++++++- 2 files changed, 159 insertions(+), 5 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 75427ee7ad..4a68daace1 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -604,9 +604,10 @@ pub struct TakerSwapMut { #[derive(Eq, PartialEq, Debug)] pub enum FailAt { TakerPayment, - WaitForTakerPaymentSpend, + WaitForTakerPaymentSpendPanic, MakerPaymentSpend, TakerPaymentRefund, + TakerPaymentRefundPanic, } #[cfg(any(test, feature = "run-docker-tests"))] @@ -614,9 +615,10 @@ impl From for FailAt { fn from(str: String) -> Self { match str.as_str() { "taker_payment" => FailAt::TakerPayment, - "wait_for_taker_payment_spend" => FailAt::WaitForTakerPaymentSpend, + "wait_for_taker_payment_spend_panic" => FailAt::WaitForTakerPaymentSpendPanic, "maker_payment_spend" => FailAt::MakerPaymentSpend, "taker_payment_refund" => FailAt::TakerPaymentRefund, + "taker_payment_refund_panic" => FailAt::TakerPaymentRefundPanic, _ => panic!("Invalid TAKER_FAIL_AT value"), } } @@ -1744,8 +1746,8 @@ impl TakerSwap { } #[cfg(any(test, feature = "run-docker-tests"))] - if self.fail_at == Some(FailAt::WaitForTakerPaymentSpend) { - panic!("Taker failed unexpectedly at wait for taker payment spend"); + if self.fail_at == Some(FailAt::WaitForTakerPaymentSpendPanic) { + panic!("Taker panicked unexpectedly at wait for taker payment spend"); } info!("Taker payment confirmed"); @@ -1875,6 +1877,10 @@ impl TakerSwap { TakerSwapEvent::TakerPaymentRefundFailed("Explicit test failure".into()), ])); } + #[cfg(test)] + if self.fail_at == Some(FailAt::TakerPaymentRefundPanic) { + panic!("Taker panicked unexpectedly at taker payment refund"); + } let taker_payment = self.r().taker_payment.clone().unwrap().tx_hex; let locktime = self.r().data.taker_payment_lock; 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 b984180b3e..3684ccf354 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -387,7 +387,10 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan alice_conf.conf.clone(), alice_conf.rpc_password.clone(), None, - &[("USE_WATCHERS", ""), ("TAKER_FAIL_AT", "wait_for_taker_payment_spend")], + &[ + ("USE_WATCHERS", ""), + ("TAKER_FAIL_AT", "wait_for_taker_payment_spend_panic"), + ], )) .unwrap(); let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); @@ -637,6 +640,151 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa assert_eq!(expected_events, actual_events.as_slice()); } +#[test] +fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_panic_at_taker_payment_refund() { + let alice_privkey = hex::encode(random_secp256k1_secret()); + let bob_privkey = hex::encode(random_secp256k1_secret()); + let watcher_privkey = hex::encode(random_secp256k1_secret()); + + let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); + + let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[ + ("USE_WATCHERS", ""), + ("USE_TEST_LOCKTIME", ""), + ("TAKER_FAIL_AT", "taker_payment_refund_panic"), + ], + )) + .unwrap(); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + + let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); + let mut mm_bob = block_on(MarketMakerIt::start_with_envs( + bob_conf.conf.clone(), + bob_conf.rpc_password, + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); + log!("Bob log path: {}", mm_bob.log_path.display()); + + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); + + let watcher_conf = WatcherConf { + wait_taker_payment: 0., + wait_maker_payment_spend_factor: 1., + refund_start_factor: 0., + search_interval: 1., + }; + + let watcher_conf = Mm2TestConf::watcher_light_node( + &format!("0x{}", watcher_privkey), + &coins, + &[&mm_alice.ip.to_string()], + watcher_conf, + ) + .conf; + + let mut mm_watcher = block_on(MarketMakerIt::start_with_envs( + watcher_conf, + DEFAULT_RPC_PASSWORD.to_string(), + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + let (_watcher_dump_log, _watcher_dump_dashboard) = mm_dump(&mm_watcher.log_path); + + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + enable_coin(&mm_bob, "MYCOIN"); + enable_coin(&mm_bob, "MYCOIN1"); + enable_coin(&mm_watcher, "MYCOIN"); + enable_coin(&mm_watcher, "MYCOIN1"); + + let uuids = block_on(start_swaps( + &mut mm_bob, + &mut mm_alice, + &[("MYCOIN1", "MYCOIN")], + 25., + 25., + 2., + )); + alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); + + block_on(mm_bob.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SENT_LOG))).unwrap(); + block_on(mm_bob.stop()).unwrap(); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); + block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); + + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG))).unwrap(); + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); + + block_on(mm_alice.stop()).unwrap(); + + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf, + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); + + let expected_events = [ + "Started", + "Negotiated", + "TakerFeeSent", + "TakerPaymentInstructionsReceived", + "MakerPaymentReceived", + "MakerPaymentWaitConfirmStarted", + "MakerPaymentValidatedAndConfirmed", + "TakerPaymentSent", + "WatcherMessageSent", + "TakerPaymentWaitForSpendFailed", + "TakerPaymentWaitRefundStarted", + "TakerPaymentRefundStarted", + "TakerPaymentRefunded", + "TakerPaymentRefundFinished", + "TakerPaymentRefundedByWatcher", + "Finished", + ]; + let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); + let events_array = status_response["result"]["events"].as_array().unwrap(); + let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); + let actual_events: Vec<&str> = actual_events.collect(); + + assert_eq!(expected_events, actual_events.as_slice()); +} + #[test] fn test_taker_completes_swap_after_restart() { let alice_privkey = hex::encode(random_secp256k1_secret()); From 2904071e216e8f1e84b109ae79024cf0a1be8b4c Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Wed, 19 Jul 2023 02:53:47 +0300 Subject: [PATCH 11/53] add WatcherRefundNotFound event, a test case for it, and fix bugs tests --- mm2src/mm2_main/src/lp_swap.rs | 10 +- .../src/lp_swap/recreate_swap_data.rs | 1 + mm2src/mm2_main/src/lp_swap/saved_swap.rs | 10 ++ mm2src/mm2_main/src/lp_swap/taker_swap.rs | 93 +++++++++++-- .../tests/docker_tests/swap_watcher_tests.rs | 124 +++++++++++++++++- 5 files changed, 218 insertions(+), 20 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 6cf0855bf2..a9d017c535 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -125,8 +125,8 @@ use taker_swap::TakerSwapEvent; pub use taker_swap::{calc_max_taker_vol, check_balance_for_taker_swap, check_watcher_payments, max_taker_vol, max_taker_vol_from_available, run_taker_swap, taker_swap_trade_preimage, RunTakerSwapInput, TakerSavedSwap, TakerSwap, TakerSwapData, TakerSwapPreparedParams, TakerTradePreimage, - MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, - WATCHER_MESSAGE_SENT_LOG}; + MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, REFUND_TEST_FAILURE_LOG, + TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, WATCHER_MESSAGE_SENT_LOG}; pub use trade_preimage::trade_preimage_rpc; pub const SWAP_PREFIX: TopicPrefix = "swap"; @@ -1180,7 +1180,11 @@ pub async fn swap_kick_starts(ctx: MmArc) -> Result, String> { let mut coins = HashSet::new(); let swaps = try_s!(SavedSwap::load_all_my_swaps_from_db(&ctx).await); for swap in swaps { - if swap.is_finished_and_success() || !swap.contains_watcher_message() || swap.refunded_by_watcher() { + if swap.is_finished_and_success() + || !swap.contains_watcher_message() + || swap.refunded_by_watcher() + || swap.watcher_refund_not_found() + { info!("{} {}", SWAP_FINISHED_LOG, swap.uuid()); continue; } 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 3b9591d270..06a00854c4 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -273,6 +273,7 @@ fn convert_taker_to_maker_events( | TakerSwapEvent::TakerPaymentRefundFailed(_) | TakerSwapEvent::TakerPaymentRefundFinished | TakerSwapEvent::TakerPaymentRefundedByWatcher + | TakerSwapEvent::WatcherRefundNotFound | TakerSwapEvent::Finished => {} } } diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index 9a424d4de8..8feb4446b2 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -76,6 +76,16 @@ impl SavedSwap { } } + pub fn watcher_refund_not_found(&self) -> bool { + match &self { + SavedSwap::Taker(taker_swap) => taker_swap + .events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::WatcherRefundNotFound)), + _ => false, + } + } + pub fn uuid(&self) -> &Uuid { match self { SavedSwap::Maker(swap) => &swap.uuid, diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 4a68daace1..b81467a97f 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -89,6 +89,7 @@ pub const TAKER_ERROR_EVENTS: [&str; 15] = [ "TakerPaymentRefundFinished", ]; +pub const REFUND_TEST_FAILURE_LOG: &str = "Explicit refund test failure..."; pub const WATCHER_MESSAGE_SENT_LOG: &str = "Watcher message sent..."; pub const MAKER_PAYMENT_SPENT_BY_WATCHER_LOG: &str = "Maker payment is spent by the watcher..."; pub const TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG: &str = "Taker payment is refunded by the watcher..."; @@ -194,6 +195,50 @@ async fn remove_events_until_watcher_message(ctx: &MmArc, swap: &TakerSwap) -> R } } +async fn remove_last_event(ctx: &MmArc, swap: &TakerSwap) -> Result<(), String> { + let swap = match SavedSwap::load_my_swap_from_db(ctx, swap.uuid).await { + Ok(Some(swap)) => swap, + Ok(None) => SavedSwap::Taker(TakerSavedSwap { + uuid: swap.uuid, + my_order_uuid: swap.my_order_uuid, + maker_amount: Some(swap.maker_amount.to_decimal()), + maker_coin: Some(swap.maker_coin.ticker().to_owned()), + maker_coin_usd_price: None, + taker_amount: Some(swap.taker_amount.to_decimal()), + taker_coin: Some(swap.taker_coin.ticker().to_owned()), + taker_coin_usd_price: None, + gui: ctx.gui().map(|g| g.to_owned()), + mm_version: Some(ctx.mm_version.to_owned()), + events: vec![], + success_events: if ctx.use_watchers() + && swap.taker_coin.is_supported_by_watchers() + && swap.maker_coin.is_supported_by_watchers() + { + TAKER_USING_WATCHERS_SUCCESS_EVENTS + .iter() + .map(<&str>::to_string) + .collect() + } else { + TAKER_SUCCESS_EVENTS.iter().map(<&str>::to_string).collect() + }, + error_events: TAKER_ERROR_EVENTS.iter().map(<&str>::to_string).collect(), + }), + Err(e) => return ERR!("{}", e), + }; + + if let SavedSwap::Taker(mut taker_swap) = swap { + taker_swap.events.pop(); + if taker_swap.is_success().unwrap_or(false) { + taker_swap.fetch_and_set_usd_prices().await; + } + let new_swap = SavedSwap::Taker(taker_swap); + try_s!(new_swap.save_to_db(ctx).await); + Ok(()) + } else { + ERR!("Expected SavedSwap::Taker, got {:?}", swap) + } +} + #[derive(Debug, Deserialize, PartialEq, Serialize)] pub struct TakerSavedEvent { pub timestamp: u64, @@ -233,6 +278,7 @@ impl TakerSavedEvent { TakerSwapEvent::TakerPaymentRefundFailed(_) => Some(TakerSwapCommand::Finish), TakerSwapEvent::TakerPaymentRefundFinished => Some(TakerSwapCommand::Finish), TakerSwapEvent::TakerPaymentRefundedByWatcher => Some(TakerSwapCommand::Finish), + TakerSwapEvent::WatcherRefundNotFound => Some(TakerSwapCommand::Finish), TakerSwapEvent::Finished => None, } } @@ -354,6 +400,8 @@ impl TakerSavedSwap { Ok(true) } + pub fn contains_failure(&self) -> bool { self.events.iter().any(|event| event.event.is_error()) } + pub async fn fetch_and_set_usd_prices(&mut self) { if let Some(rates) = fetch_swap_coins_price(self.maker_coin.clone(), self.taker_coin.clone()).await { self.maker_coin_usd_price = Some(rates.base); @@ -700,6 +748,7 @@ pub enum TakerSwapEvent { TakerPaymentRefundFailed(SwapError), TakerPaymentRefundFinished, TakerPaymentRefundedByWatcher, + WatcherRefundNotFound, Finished, } @@ -738,7 +787,8 @@ impl TakerSwapEvent { TakerSwapEvent::TakerPaymentRefunded(_) => "Taker payment refunded...".to_owned(), TakerSwapEvent::TakerPaymentRefundFailed(_) => "Taker payment refund failed...".to_owned(), TakerSwapEvent::TakerPaymentRefundFinished => "Taker payment refund finished...".to_owned(), - TakerSwapEvent::TakerPaymentRefundedByWatcher => "TakerPaymentRefundedByWatcher...".to_owned(), + TakerSwapEvent::TakerPaymentRefundedByWatcher => "Taker payment refunded by watcher...".to_owned(), + TakerSwapEvent::WatcherRefundNotFound => "Watcher refund could not be found...".to_owned(), TakerSwapEvent::Finished => "Finished".to_owned(), } } @@ -874,6 +924,7 @@ impl TakerSwap { TakerSwapEvent::TakerPaymentRefundFailed(err) => self.errors.lock().push(err), TakerSwapEvent::TakerPaymentRefundFinished => (), TakerSwapEvent::TakerPaymentRefundedByWatcher => (), + TakerSwapEvent::WatcherRefundNotFound => (), TakerSwapEvent::Finished => self.finished_at.store(now_sec(), Ordering::Relaxed), } } @@ -1751,10 +1802,16 @@ impl TakerSwap { } info!("Taker payment confirmed"); + + let wait_until = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => self.r().data.started_at, + Err(_) => self.r().data.taker_payment_lock, + }; + let f = self.taker_coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { tx_bytes: &self.r().taker_payment.clone().unwrap().tx_hex, secret_hash: &self.r().secret_hash.0, - wait_until: self.r().data.taker_payment_lock, + wait_until, from_block: self.r().data.taker_coin_start_block, swap_contract_address: &self.r().data.taker_coin_swap_contract_address, check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, @@ -1871,15 +1928,15 @@ impl TakerSwap { } async fn refund_taker_payment(&self) -> Result<(Option, Vec), String> { - #[cfg(test)] + #[cfg(any(test, feature = "run-docker-tests"))] if self.fail_at == Some(FailAt::TakerPaymentRefund) { return Ok((Some(TakerSwapCommand::Finish), vec![ TakerSwapEvent::TakerPaymentRefundFailed("Explicit test failure".into()), ])); } - #[cfg(test)] + #[cfg(any(test, feature = "run-docker-tests"))] if self.fail_at == Some(FailAt::TakerPaymentRefundPanic) { - panic!("Taker panicked unexpectedly at taker payment refund"); + panic!("{}", REFUND_TEST_FAILURE_LOG); } let taker_payment = self.r().taker_payment.clone().unwrap().tx_hex; @@ -2047,7 +2104,7 @@ impl TakerSwap { swap.apply_event(saved_event.event.clone()); if let TakerSwapEvent::WatcherMessageSent(_, _) = saved_event.event { - if check_watcher_payments(&swap, &ctx).await?.is_some() { + if check_watcher_payments(&swap, &ctx, &saved).await? { return Ok((swap, Some(TakerSwapCommand::Finish))); } } @@ -2362,9 +2419,7 @@ pub struct TakerSwapPreparedParams { maker_payment_spend_trade_fee: TradeFee, } -pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc) -> Result, String> { - remove_events_until_watcher_message(ctx, swap).await?; - +pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, saved: &TakerSavedSwap) -> Result { let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; let secret_hash = swap.r().secret_hash.0.clone(); let maker_coin_start_block = swap.r().data.maker_coin_start_block; @@ -2429,6 +2484,7 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc) -> Result { + remove_events_until_watcher_message(ctx, swap).await?; let tx_hash = taker_payment_spend_tx.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { @@ -2463,10 +2519,11 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc) -> Result { + remove_events_until_watcher_message(ctx, swap).await?; let event = TakerSwapEvent::TakerPaymentWaitForSpendFailed("Taker payment wait for spend failed".into()); swap.append_event(ctx, event).await; @@ -2495,12 +2552,24 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc) -> Result { ERR!("Could not find taker payment spend, while the maker payment is already spent or refunded") }, - _ => Ok(None), + (None, None) => { + if saved.contains_failure() { + if saved.is_finished() { + remove_last_event(ctx, swap).await?; + } + let event = TakerSwapEvent::WatcherRefundNotFound; + swap.append_event(ctx, event).await; + Ok(true) + } else { + Ok(false) + } + }, + _ => Ok(false), } } 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 3684ccf354..b28cf0349d 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -13,8 +13,9 @@ use crypto::privkey::{key_pair_from_secret, key_pair_from_seed}; use futures01::Future; use mm2_main::mm2::lp_swap::{dex_fee_amount, dex_fee_amount_from_taker_coin, dex_fee_threshold, get_payment_locktime, MakerSwap, MAKER_PAYMENT_SENT_LOG, MAKER_PAYMENT_SPEND_FOUND_LOG, - MAKER_PAYMENT_SPEND_SENT_LOG, SWAP_FINISHED_LOG, TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, - TAKER_PAYMENT_REFUND_SENT_LOG, WATCHER_MESSAGE_SENT_LOG}; + MAKER_PAYMENT_SPEND_SENT_LOG, REFUND_TEST_FAILURE_LOG, SWAP_FINISHED_LOG, + TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, + WATCHER_MESSAGE_SENT_LOG}; use mm2_number::BigDecimal; use mm2_number::MmNumber; use mm2_test_helpers::for_tests::{enable_eth_coin, eth_jst_testnet_conf, eth_testnet_conf, mm_dump, my_balance, @@ -578,7 +579,7 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa block_on(mm_bob.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SENT_LOG))).unwrap(); block_on(mm_bob.stop()).unwrap(); - block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); let mut mm_alice = block_on(MarketMakerIt::start_with_envs( @@ -594,7 +595,6 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa enable_coin(&mm_alice, "MYCOIN"); enable_coin(&mm_alice, "MYCOIN1"); - block_on(mm_alice.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG))).unwrap(); block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_alice.stop()).unwrap(); @@ -723,7 +723,7 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa block_on(mm_bob.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SENT_LOG))).unwrap(); block_on(mm_bob.stop()).unwrap(); - block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); + block_on(mm_alice.wait_for_log(120., |log| log.contains(REFUND_TEST_FAILURE_LOG))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); let mut mm_alice = block_on(MarketMakerIt::start_with_envs( @@ -785,6 +785,120 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa assert_eq!(expected_events, actual_events.as_slice()); } +#[test] +fn test_taker_adds_watcher_refund_not_found_event() { + let alice_privkey = hex::encode(random_secp256k1_secret()); + let bob_privkey = hex::encode(random_secp256k1_secret()); + + let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); + + let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[ + ("USE_WATCHERS", ""), + ("USE_TEST_LOCKTIME", ""), + ("TAKER_FAIL_AT", "taker_payment_refund"), + ], + )) + .unwrap(); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + + let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); + let mut mm_bob = block_on(MarketMakerIt::start_with_envs( + bob_conf.conf.clone(), + bob_conf.rpc_password, + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); + log!("Bob log path: {}", mm_bob.log_path.display()); + + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); + + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + enable_coin(&mm_bob, "MYCOIN"); + enable_coin(&mm_bob, "MYCOIN1"); + + let uuids = block_on(start_swaps( + &mut mm_bob, + &mut mm_alice, + &[("MYCOIN1", "MYCOIN")], + 25., + 25., + 2., + )); + alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); + + block_on(mm_bob.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SENT_LOG))).unwrap(); + block_on(mm_bob.stop()).unwrap(); + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); + + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf.clone(), + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); + + block_on(mm_alice.stop()).unwrap(); + + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf, + alice_conf.rpc_password.clone(), + None, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); + + let expected_events = [ + "Started", + "Negotiated", + "TakerFeeSent", + "TakerPaymentInstructionsReceived", + "MakerPaymentReceived", + "MakerPaymentWaitConfirmStarted", + "MakerPaymentValidatedAndConfirmed", + "TakerPaymentSent", + "WatcherMessageSent", + "TakerPaymentWaitForSpendFailed", + "TakerPaymentWaitRefundStarted", + "TakerPaymentRefundStarted", + "TakerPaymentRefundFailed", + "WatcherRefundNotFound", + "Finished", + ]; + let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); + let events_array = status_response["result"]["events"].as_array().unwrap(); + let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); + let actual_events: Vec<&str> = actual_events.collect(); + + assert_eq!(expected_events, actual_events.as_slice()); +} + #[test] fn test_taker_completes_swap_after_restart() { let alice_privkey = hex::encode(random_secp256k1_secret()); From 51443c95d48791fcfaa3baa9a7e9f629484c3270 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Wed, 19 Jul 2023 21:44:19 +0300 Subject: [PATCH 12/53] move watcher payment checks out of the saved events loop --- mm2src/mm2_main/src/lp_swap/saved_swap.rs | 5 +---- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 15 ++++++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index 8feb4446b2..cfbb989c83 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -58,10 +58,7 @@ impl SavedSwap { pub fn contains_watcher_message(&self) -> bool { match &self { - SavedSwap::Taker(taker_swap) => taker_swap - .events - .iter() - .any(|e| matches!(e.event, TakerSwapEvent::WatcherMessageSent(_, _))), + SavedSwap::Taker(taker_swap) => taker_swap.contains_watcher_message(), _ => false, } } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index b81467a97f..50e6de33b8 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -402,6 +402,12 @@ impl TakerSavedSwap { pub fn contains_failure(&self) -> bool { self.events.iter().any(|event| event.event.is_error()) } + pub fn contains_watcher_message(&self) -> bool { + self.events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::WatcherMessageSent(_, _))) + } + pub async fn fetch_and_set_usd_prices(&mut self) { if let Some(rates) = fetch_swap_coins_price(self.maker_coin.clone(), self.taker_coin.clone()).await { self.maker_coin_usd_price = Some(rates.base); @@ -2102,13 +2108,12 @@ impl TakerSwap { let command = saved.events.last().unwrap().get_command(); for saved_event in &saved.events { swap.apply_event(saved_event.event.clone()); + } - if let TakerSwapEvent::WatcherMessageSent(_, _) = saved_event.event { - if check_watcher_payments(&swap, &ctx, &saved).await? { - return Ok((swap, Some(TakerSwapCommand::Finish))); - } - } + if saved.contains_watcher_message() && check_watcher_payments(&swap, &ctx, &saved).await? { + return Ok((swap, Some(TakerSwapCommand::Finish))); } + Ok((swap, command)) } From 2e5e883c7448fcf349d5be6966bfd6cc1e130e9e Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Wed, 19 Jul 2023 23:01:23 +0300 Subject: [PATCH 13/53] improve check_watcher_payments function --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 187 ++++++++-------------- 1 file changed, 70 insertions(+), 117 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 50e6de33b8..bb725edc1a 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -146,99 +146,6 @@ async fn save_my_taker_swap_event(ctx: &MmArc, swap: &TakerSwap, event: TakerSav } } -async fn remove_events_until_watcher_message(ctx: &MmArc, swap: &TakerSwap) -> Result<(), String> { - let swap = match SavedSwap::load_my_swap_from_db(ctx, swap.uuid).await { - Ok(Some(swap)) => swap, - Ok(None) => SavedSwap::Taker(TakerSavedSwap { - uuid: swap.uuid, - my_order_uuid: swap.my_order_uuid, - maker_amount: Some(swap.maker_amount.to_decimal()), - maker_coin: Some(swap.maker_coin.ticker().to_owned()), - maker_coin_usd_price: None, - taker_amount: Some(swap.taker_amount.to_decimal()), - taker_coin: Some(swap.taker_coin.ticker().to_owned()), - taker_coin_usd_price: None, - gui: ctx.gui().map(|g| g.to_owned()), - mm_version: Some(ctx.mm_version.to_owned()), - events: vec![], - success_events: if ctx.use_watchers() - && swap.taker_coin.is_supported_by_watchers() - && swap.maker_coin.is_supported_by_watchers() - { - TAKER_USING_WATCHERS_SUCCESS_EVENTS - .iter() - .map(<&str>::to_string) - .collect() - } else { - TAKER_SUCCESS_EVENTS.iter().map(<&str>::to_string).collect() - }, - error_events: TAKER_ERROR_EVENTS.iter().map(<&str>::to_string).collect(), - }), - Err(e) => return ERR!("{}", e), - }; - - if let SavedSwap::Taker(mut taker_swap) = swap { - while !matches!( - taker_swap.events.last().unwrap().event, - TakerSwapEvent::WatcherMessageSent(_, _) - ) { - taker_swap.events.pop(); - } - if taker_swap.is_success().unwrap_or(false) { - taker_swap.fetch_and_set_usd_prices().await; - } - let new_swap = SavedSwap::Taker(taker_swap); - try_s!(new_swap.save_to_db(ctx).await); - Ok(()) - } else { - ERR!("Expected SavedSwap::Taker, got {:?}", swap) - } -} - -async fn remove_last_event(ctx: &MmArc, swap: &TakerSwap) -> Result<(), String> { - let swap = match SavedSwap::load_my_swap_from_db(ctx, swap.uuid).await { - Ok(Some(swap)) => swap, - Ok(None) => SavedSwap::Taker(TakerSavedSwap { - uuid: swap.uuid, - my_order_uuid: swap.my_order_uuid, - maker_amount: Some(swap.maker_amount.to_decimal()), - maker_coin: Some(swap.maker_coin.ticker().to_owned()), - maker_coin_usd_price: None, - taker_amount: Some(swap.taker_amount.to_decimal()), - taker_coin: Some(swap.taker_coin.ticker().to_owned()), - taker_coin_usd_price: None, - gui: ctx.gui().map(|g| g.to_owned()), - mm_version: Some(ctx.mm_version.to_owned()), - events: vec![], - success_events: if ctx.use_watchers() - && swap.taker_coin.is_supported_by_watchers() - && swap.maker_coin.is_supported_by_watchers() - { - TAKER_USING_WATCHERS_SUCCESS_EVENTS - .iter() - .map(<&str>::to_string) - .collect() - } else { - TAKER_SUCCESS_EVENTS.iter().map(<&str>::to_string).collect() - }, - error_events: TAKER_ERROR_EVENTS.iter().map(<&str>::to_string).collect(), - }), - Err(e) => return ERR!("{}", e), - }; - - if let SavedSwap::Taker(mut taker_swap) = swap { - taker_swap.events.pop(); - if taker_swap.is_success().unwrap_or(false) { - taker_swap.fetch_and_set_usd_prices().await; - } - let new_swap = SavedSwap::Taker(taker_swap); - try_s!(new_swap.save_to_db(ctx).await); - Ok(()) - } else { - ERR!("Expected SavedSwap::Taker, got {:?}", swap) - } -} - #[derive(Debug, Deserialize, PartialEq, Serialize)] pub struct TakerSavedEvent { pub timestamp: u64, @@ -935,16 +842,6 @@ impl TakerSwap { } } - async fn append_event(&self, ctx: &MmArc, event: TakerSwapEvent) { - self.apply_event(event.clone()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - save_my_taker_swap_event(ctx, self, to_save) - .await - .expect("!save_my_taker_swap_event"); - } async fn handle_command( &self, command: TakerSwapCommand, @@ -2110,7 +2007,7 @@ impl TakerSwap { swap.apply_event(saved_event.event.clone()); } - if saved.contains_watcher_message() && check_watcher_payments(&swap, &ctx, &saved).await? { + if saved.contains_watcher_message() && check_watcher_payments(&swap, &ctx, saved).await? { return Ok((swap, Some(TakerSwapCommand::Finish))); } @@ -2424,7 +2321,7 @@ pub struct TakerSwapPreparedParams { maker_payment_spend_trade_fee: TradeFee, } -pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, saved: &TakerSavedSwap) -> Result { +pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: TakerSavedSwap) -> Result { let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; let secret_hash = swap.r().secret_hash.0.clone(); let maker_coin_start_block = swap.r().data.maker_coin_start_block; @@ -2489,7 +2386,13 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, saved: &Taker Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx)), Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx)), ) => { - remove_events_until_watcher_message(ctx, swap).await?; + while !matches!( + saved.events.last().unwrap().event, + TakerSwapEvent::WatcherMessageSent(_, _) + ) { + saved.events.pop(); + } + let tx_hash = taker_payment_spend_tx.tx_hash(); info!("Taker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { @@ -2511,7 +2414,11 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, saved: &Taker transaction: tx_ident, secret, }); - swap.append_event(ctx, event).await; + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); let tx_hash = maker_payment_spend_tx.tx_hash(); info!("Maker payment spend tx {:02x}", tx_hash); @@ -2521,24 +2428,49 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, saved: &Taker }; let event = TakerSwapEvent::MakerPaymentSpent(tx_ident); - swap.append_event(ctx, event).await; + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + + let new_swap = SavedSwap::Taker(saved); + try_s!(new_swap.save_to_db(ctx).await); info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); Ok(true) }, (Some(FoundSwapTxSpend::Refunded(_)), Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) | (None, Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { - remove_events_until_watcher_message(ctx, swap).await?; + while !matches!( + saved.events.last().unwrap().event, + TakerSwapEvent::WatcherMessageSent(_, _) + ) { + saved.events.pop(); + } + let event = TakerSwapEvent::TakerPaymentWaitForSpendFailed("Taker payment wait for spend failed".into()); - swap.append_event(ctx, event).await; + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); let event = TakerSwapEvent::TakerPaymentWaitRefundStarted { wait_until: swap.wait_refund_until(), }; - swap.append_event(ctx, event).await; + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); let event = TakerSwapEvent::TakerPaymentRefundStarted; - swap.append_event(ctx, event).await; + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); let tx_hash = taker_payment_refund_tx.tx_hash(); info!("Taker refund tx hash {:02x}", tx_hash); @@ -2548,13 +2480,28 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, saved: &Taker }; let event = TakerSwapEvent::TakerPaymentRefunded(Some(tx_ident)); - swap.append_event(ctx, event).await; + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); let event = TakerSwapEvent::TakerPaymentRefundFinished; - swap.append_event(ctx, event).await; + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); let event = TakerSwapEvent::TakerPaymentRefundedByWatcher; - swap.append_event(ctx, event).await; + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + + let new_swap = SavedSwap::Taker(saved); + try_s!(new_swap.save_to_db(ctx).await); info!("{}", TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG); Ok(true) @@ -2565,10 +2512,16 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, saved: &Taker (None, None) => { if saved.contains_failure() { if saved.is_finished() { - remove_last_event(ctx, swap).await?; + saved.events.pop(); } let event = TakerSwapEvent::WatcherRefundNotFound; - swap.append_event(ctx, event).await; + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + let new_swap = SavedSwap::Taker(saved); + try_s!(new_swap.save_to_db(ctx).await); Ok(true) } else { Ok(false) From 81d9e9471bbf3f1c6ba77e646cd4b9c22c74f585 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 20 Jul 2023 00:36:17 +0300 Subject: [PATCH 14/53] add only TakerPaymentRefundedByWatcher at restart after watcher refund --- .../src/lp_swap/recreate_swap_data.rs | 2 +- mm2src/mm2_main/src/lp_swap/saved_swap.rs | 2 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 52 +++---------------- .../tests/docker_tests/swap_watcher_tests.rs | 5 +- 4 files changed, 9 insertions(+), 52 deletions(-) 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 06a00854c4..4907d2c91e 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -272,7 +272,7 @@ fn convert_taker_to_maker_events( | TakerSwapEvent::TakerPaymentRefunded(_) | TakerSwapEvent::TakerPaymentRefundFailed(_) | TakerSwapEvent::TakerPaymentRefundFinished - | TakerSwapEvent::TakerPaymentRefundedByWatcher + | TakerSwapEvent::TakerPaymentRefundedByWatcher(_) | TakerSwapEvent::WatcherRefundNotFound | TakerSwapEvent::Finished => {} } diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index cfbb989c83..55ca44f26f 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -68,7 +68,7 @@ impl SavedSwap { SavedSwap::Taker(taker_swap) => taker_swap .events .iter() - .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefundedByWatcher)), + .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefundedByWatcher(_))), _ => false, } } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index bb725edc1a..2f1542ac52 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -184,7 +184,7 @@ impl TakerSavedEvent { TakerSwapEvent::TakerPaymentRefunded(_) => Some(TakerSwapCommand::FinalizeTakerPaymentRefund), TakerSwapEvent::TakerPaymentRefundFailed(_) => Some(TakerSwapCommand::Finish), TakerSwapEvent::TakerPaymentRefundFinished => Some(TakerSwapCommand::Finish), - TakerSwapEvent::TakerPaymentRefundedByWatcher => Some(TakerSwapCommand::Finish), + TakerSwapEvent::TakerPaymentRefundedByWatcher(_) => Some(TakerSwapCommand::Finish), TakerSwapEvent::WatcherRefundNotFound => Some(TakerSwapCommand::Finish), TakerSwapEvent::Finished => None, } @@ -660,7 +660,7 @@ pub enum TakerSwapEvent { TakerPaymentRefunded(Option), TakerPaymentRefundFailed(SwapError), TakerPaymentRefundFinished, - TakerPaymentRefundedByWatcher, + TakerPaymentRefundedByWatcher(Option), WatcherRefundNotFound, Finished, } @@ -700,7 +700,7 @@ impl TakerSwapEvent { TakerSwapEvent::TakerPaymentRefunded(_) => "Taker payment refunded...".to_owned(), TakerSwapEvent::TakerPaymentRefundFailed(_) => "Taker payment refund failed...".to_owned(), TakerSwapEvent::TakerPaymentRefundFinished => "Taker payment refund finished...".to_owned(), - TakerSwapEvent::TakerPaymentRefundedByWatcher => "Taker payment refunded by watcher...".to_owned(), + TakerSwapEvent::TakerPaymentRefundedByWatcher(_) => "Taker payment refunded by watcher...".to_owned(), TakerSwapEvent::WatcherRefundNotFound => "Watcher refund could not be found...".to_owned(), TakerSwapEvent::Finished => "Finished".to_owned(), } @@ -836,7 +836,7 @@ impl TakerSwap { TakerSwapEvent::TakerPaymentRefunded(tx) => self.w().taker_payment_refund = tx, TakerSwapEvent::TakerPaymentRefundFailed(err) => self.errors.lock().push(err), TakerSwapEvent::TakerPaymentRefundFinished => (), - TakerSwapEvent::TakerPaymentRefundedByWatcher => (), + TakerSwapEvent::TakerPaymentRefundedByWatcher(tx) => self.w().taker_payment_refund = tx, TakerSwapEvent::WatcherRefundNotFound => (), TakerSwapEvent::Finished => self.finished_at.store(now_sec(), Ordering::Relaxed), } @@ -2442,36 +2442,10 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta }, (Some(FoundSwapTxSpend::Refunded(_)), Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) | (None, Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { - while !matches!( - saved.events.last().unwrap().event, - TakerSwapEvent::WatcherMessageSent(_, _) - ) { + if saved.is_finished() { saved.events.pop(); } - let event = TakerSwapEvent::TakerPaymentWaitForSpendFailed("Taker payment wait for spend failed".into()); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - - let event = TakerSwapEvent::TakerPaymentWaitRefundStarted { - wait_until: swap.wait_refund_until(), - }; - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - - let event = TakerSwapEvent::TakerPaymentRefundStarted; - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - let tx_hash = taker_payment_refund_tx.tx_hash(); info!("Taker refund tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { @@ -2479,21 +2453,7 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta tx_hash, }; - let event = TakerSwapEvent::TakerPaymentRefunded(Some(tx_ident)); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - - let event = TakerSwapEvent::TakerPaymentRefundFinished; - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - - let event = TakerSwapEvent::TakerPaymentRefundedByWatcher; + let event = TakerSwapEvent::TakerPaymentRefundedByWatcher(Some(tx_ident)); let to_save = TakerSavedEvent { timestamp: now_ms(), event, 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 b28cf0349d..486f75d970 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -627,8 +627,7 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa "TakerPaymentWaitForSpendFailed", "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", - "TakerPaymentRefunded", - "TakerPaymentRefundFinished", + "TakerPaymentRefundFailed", "TakerPaymentRefundedByWatcher", "Finished", ]; @@ -772,8 +771,6 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa "TakerPaymentWaitForSpendFailed", "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", - "TakerPaymentRefunded", - "TakerPaymentRefundFinished", "TakerPaymentRefundedByWatcher", "Finished", ]; From b87cb60c3bcdcd9c4e8b277ed420d3b61fdb87d6 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 20 Jul 2023 14:05:03 +0300 Subject: [PATCH 15/53] add MakerPaymentSpendtByWatcher event and adapt relevant test cases --- .../src/lp_swap/recreate_swap_data.rs | 1 + mm2src/mm2_main/src/lp_swap/saved_swap.rs | 2 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 126 +++++++++++++----- .../tests/docker_tests/swap_watcher_tests.rs | 65 +++++++-- 4 files changed, 144 insertions(+), 50 deletions(-) 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 4907d2c91e..744e476cf4 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,7 @@ fn convert_taker_to_maker_events( | TakerSwapEvent::MakerPaymentWaitConfirmStarted | TakerSwapEvent::MakerPaymentValidatedAndConfirmed | TakerSwapEvent::MakerPaymentSpent(_) + | TakerSwapEvent::MakerPaymentSpentByWatcher(_) | TakerSwapEvent::MakerPaymentSpendFailed(_) // We don't know the reason at the moment, so we rely on the errors handling above. | TakerSwapEvent::WatcherMessageSent(_,_) diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index 55ca44f26f..f6f699966e 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -58,7 +58,7 @@ impl SavedSwap { pub fn contains_watcher_message(&self) -> bool { match &self { - SavedSwap::Taker(taker_swap) => taker_swap.contains_watcher_message(), + SavedSwap::Taker(taker_swap) => taker_swap.watcher_message_sent(), _ => false, } } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 2f1542ac52..d3aab33c3d 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -176,6 +176,7 @@ impl TakerSavedEvent { TakerSwapEvent::TakerPaymentWaitForSpendFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), TakerSwapEvent::TakerPaymentWaitConfirmFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), TakerSwapEvent::MakerPaymentSpent(_) => Some(TakerSwapCommand::Finish), + TakerSwapEvent::MakerPaymentSpentByWatcher(_) => Some(TakerSwapCommand::Finish), TakerSwapEvent::MakerPaymentSpendFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), TakerSwapEvent::TakerPaymentWaitRefundStarted { .. } => { Some(TakerSwapCommand::PrepareForTakerPaymentRefund) @@ -297,24 +298,37 @@ impl TakerSavedSwap { if !self.is_finished() { return ERR!("Can not determine is_success state for not finished swap"); } - + if self.maker_payment_spent_by_watcher() { + return Ok(true); + } for event in self.events.iter() { if event.event.is_error() { return Ok(false); } } - Ok(true) } pub fn contains_failure(&self) -> bool { self.events.iter().any(|event| event.event.is_error()) } - pub fn contains_watcher_message(&self) -> bool { + pub fn watcher_message_sent(&self) -> bool { self.events .iter() .any(|e| matches!(e.event, TakerSwapEvent::WatcherMessageSent(_, _))) } + pub fn taker_payment_spent(&self) -> bool { + self.events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentSpent(_))) + } + + pub fn maker_payment_spent_by_watcher(&self) -> bool { + self.events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::MakerPaymentSpentByWatcher(_))) + } + pub async fn fetch_and_set_usd_prices(&mut self) { if let Some(rates) = fetch_swap_coins_price(self.maker_coin.clone(), self.taker_coin.clone()).await { self.maker_coin_usd_price = Some(rates.base); @@ -654,6 +668,7 @@ pub enum TakerSwapEvent { TakerPaymentSpent(TakerPaymentSpentData), TakerPaymentWaitForSpendFailed(SwapError), MakerPaymentSpent(TransactionIdentifier), + MakerPaymentSpentByWatcher(TransactionIdentifier), MakerPaymentSpendFailed(SwapError), TakerPaymentWaitRefundStarted { wait_until: u64 }, TakerPaymentRefundStarted, @@ -692,6 +707,7 @@ 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::MakerPaymentSpentByWatcher(_) => "Maker payment spent by watcher...".to_owned(), TakerSwapEvent::MakerPaymentSpendFailed(_) => "Maker payment spend failed...".to_owned(), TakerSwapEvent::TakerPaymentWaitRefundStarted { wait_until } => { format!("Taker payment wait refund till {} started...", wait_until) @@ -706,6 +722,41 @@ impl TakerSwapEvent { } } + pub fn to_str(&self) -> String { + match self { + TakerSwapEvent::Started(_) => "Started".to_owned(), + TakerSwapEvent::StartFailed(_) => "StartFailed".to_owned(), + TakerSwapEvent::Negotiated(_) => "Negotiated".to_owned(), + TakerSwapEvent::NegotiateFailed(_) => "NegotiateFailed".to_owned(), + TakerSwapEvent::TakerFeeSent(_) => "TakerFeeSent".to_owned(), + TakerSwapEvent::TakerFeeSendFailed(_) => "TakerFeeSendFailed".to_owned(), + TakerSwapEvent::TakerPaymentInstructionsReceived(_) => "TakerPaymentInstructionsReceived".to_owned(), + TakerSwapEvent::MakerPaymentReceived(_) => "MakerPaymentReceived".to_owned(), + TakerSwapEvent::MakerPaymentWaitConfirmStarted => "MakerPaymentWaitConfirmStarted".to_owned(), + TakerSwapEvent::MakerPaymentValidatedAndConfirmed => "MakerPaymentValidatedAndConfirmed".to_owned(), + TakerSwapEvent::MakerPaymentValidateFailed(_) => "MakerPaymentValidateFailed".to_owned(), + TakerSwapEvent::MakerPaymentWaitConfirmFailed(_) => "MakerPaymentWaitConfirmFailed".to_owned(), + TakerSwapEvent::TakerPaymentSent(_) => "TakerPaymentSent".to_owned(), + TakerSwapEvent::WatcherMessageSent(_, _) => "WatcherMessageSent".to_owned(), + TakerSwapEvent::TakerPaymentTransactionFailed(_) => "TakerPaymentTransactionFailed".to_owned(), + TakerSwapEvent::TakerPaymentDataSendFailed(_) => "TakerPaymentDataSendFailed".to_owned(), + TakerSwapEvent::TakerPaymentWaitConfirmFailed(_) => "TakerPaymentWaitConfirmFailed".to_owned(), + TakerSwapEvent::TakerPaymentSpent(_) => "TakerPaymentSpent".to_owned(), + TakerSwapEvent::TakerPaymentWaitForSpendFailed(_) => "TakerPaymentWaitForSpendFailed".to_owned(), + TakerSwapEvent::MakerPaymentSpent(_) => "MakerPaymentSpent".to_owned(), + TakerSwapEvent::MakerPaymentSpentByWatcher(_) => "MakerPaymentSpentByWatcher".to_owned(), + TakerSwapEvent::MakerPaymentSpendFailed(_) => "MakerPaymentSpendFailed".to_owned(), + TakerSwapEvent::TakerPaymentWaitRefundStarted { .. } => "TakerPaymentWaitRefundStarted".to_owned(), + TakerSwapEvent::TakerPaymentRefundStarted => "TakerPaymentRefundStarted".to_owned(), + TakerSwapEvent::TakerPaymentRefunded(_) => "TakerPaymentRefunded".to_owned(), + TakerSwapEvent::TakerPaymentRefundFailed(_) => "TakerPaymentRefundFailed".to_owned(), + TakerSwapEvent::TakerPaymentRefundFinished => "TakerPaymentRefundFinished".to_owned(), + TakerSwapEvent::TakerPaymentRefundedByWatcher(_) => "TakerPaymentRefundedByWatcher".to_owned(), + TakerSwapEvent::WatcherRefundNotFound => "WatcherRefundNotFound".to_owned(), + TakerSwapEvent::Finished => "Finished".to_owned(), + } + } + fn should_ban_maker(&self) -> bool { matches!( self, @@ -727,6 +778,7 @@ impl TakerSwapEvent { | TakerSwapEvent::TakerPaymentSent(_) | TakerSwapEvent::TakerPaymentSpent(_) | TakerSwapEvent::MakerPaymentSpent(_) + | TakerSwapEvent::MakerPaymentSpentByWatcher(_) | TakerSwapEvent::Finished ) } @@ -830,6 +882,7 @@ impl TakerSwap { }, TakerSwapEvent::TakerPaymentWaitForSpendFailed(err) => self.errors.lock().push(err), TakerSwapEvent::MakerPaymentSpent(tx) => self.w().maker_payment_spend = Some(tx), + TakerSwapEvent::MakerPaymentSpentByWatcher(tx) => self.w().maker_payment_spend = Some(tx), TakerSwapEvent::MakerPaymentSpendFailed(err) => self.errors.lock().push(err), TakerSwapEvent::TakerPaymentWaitRefundStarted { .. } => (), TakerSwapEvent::TakerPaymentRefundStarted => (), @@ -2007,7 +2060,7 @@ impl TakerSwap { swap.apply_event(saved_event.event.clone()); } - if saved.contains_watcher_message() && check_watcher_payments(&swap, &ctx, saved).await? { + if saved.watcher_message_sent() && check_watcher_payments(&swap, &ctx, saved).await? { return Ok((swap, Some(TakerSwapCommand::Finish))); } @@ -2386,54 +2439,57 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx)), Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx)), ) => { - while !matches!( - saved.events.last().unwrap().event, - TakerSwapEvent::WatcherMessageSent(_, _) - ) { + if saved.is_finished() { saved.events.pop(); } - let tx_hash = taker_payment_spend_tx.tx_hash(); - info!("Taker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), - tx_hash, - }; - let secret = match swap - .taker_coin - .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) - .await - { - Ok(bytes) => H256Json::from(bytes.as_slice()), - Err(_) => { - return ERR!("Could not extract secret from taker payment spend transaction"); - }, - }; + if !saved.taker_payment_spent() { + let tx_hash = taker_payment_spend_tx.tx_hash(); + info!("Taker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), + tx_hash, + }; + let secret = match swap + .taker_coin + .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) + .await + { + Ok(bytes) => H256Json::from(bytes.as_slice()), + Err(_) => { + return ERR!("Could not extract secret from taker payment spend transaction"); + }, + }; - let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { - transaction: tx_ident, - secret, - }); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); + let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { + transaction: tx_ident, + secret, + }); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + } let tx_hash = maker_payment_spend_tx.tx_hash(); - info!("Maker payment spend tx {:02x}", tx_hash); + info!("Watcher maker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), tx_hash, }; - let event = TakerSwapEvent::MakerPaymentSpent(tx_ident); + let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); let to_save = TakerSavedEvent { timestamp: now_ms(), event, }; saved.events.push(to_save); + let mut success_events: Vec = saved.events.iter().map(|e| e.event.to_str()).collect(); + success_events.push("Finished".into()); + saved.success_events = success_events; + let new_swap = SavedSwap::Taker(saved); try_s!(new_swap.save_to_db(ctx).await); 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 486f75d970..8d0ee90d3b 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -26,6 +26,7 @@ use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::WatcherConf; use num_traits::{One, Zero}; use primitives::hash::H256; +use serde_json as json; use std::str::FromStr; use std::thread; use std::time::Duration; @@ -350,13 +351,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai enable_coin(&mm_alice, "MYCOIN"); enable_coin(&mm_alice, "MYCOIN1"); - block_on(wait_for_swaps_finish_and_check_status( - &mut mm_bob, - &mut mm_alice, - &uuids, - 2., - 25., - )); + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_alice.stop()).unwrap(); let mut mm_alice = block_on(MarketMakerIt::start_with_envs( @@ -373,6 +368,30 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai enable_coin(&mm_alice, "MYCOIN1"); block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); + + let expected_events = [ + "Started", + "Negotiated", + "TakerFeeSent", + "TakerPaymentInstructionsReceived", + "MakerPaymentReceived", + "MakerPaymentWaitConfirmStarted", + "MakerPaymentValidatedAndConfirmed", + "TakerPaymentSent", + "WatcherMessageSent", + "TakerPaymentSpent", + "MakerPaymentSpendFailed", + "MakerPaymentSpentByWatcher", + "Finished", + ]; + let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); + let events_array = status_response["result"]["events"].as_array().unwrap(); + let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); + let actual_events: Vec<&str> = actual_events.collect(); + assert_eq!(expected_events, actual_events.as_slice()); + + let success_events: Vec = json::from_value(status_response["result"]["success_events"].clone()).unwrap(); + assert_eq!(expected_events, success_events.as_slice()); } #[test] @@ -471,13 +490,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan enable_coin(&mm_alice, "MYCOIN"); enable_coin(&mm_alice, "MYCOIN1"); - block_on(wait_for_swaps_finish_and_check_status( - &mut mm_bob, - &mut mm_alice, - &uuids, - 2., - 25., - )); + block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_alice.stop()).unwrap(); let mut mm_alice = block_on(MarketMakerIt::start_with_envs( @@ -494,6 +507,30 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan enable_coin(&mm_alice, "MYCOIN1"); block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); + + let expected_events = [ + "Started", + "Negotiated", + "TakerFeeSent", + "TakerPaymentInstructionsReceived", + "MakerPaymentReceived", + "MakerPaymentWaitConfirmStarted", + "MakerPaymentValidatedAndConfirmed", + "TakerPaymentSent", + "WatcherMessageSent", + "TakerPaymentSpent", + "MakerPaymentSpentByWatcher", + "Finished", + ]; + let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); + let events_array = status_response["result"]["events"].as_array().unwrap(); + let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); + let actual_events: Vec<&str> = actual_events.collect(); + + assert_eq!(expected_events, actual_events.as_slice()); + + let success_events: Vec = json::from_value(status_response["result"]["success_events"].clone()).unwrap(); + assert_eq!(expected_events, success_events.as_slice()); } #[test] From 07559bd0f49757747f9d06d4808356654ce46ef6 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 20 Jul 2023 14:36:19 +0300 Subject: [PATCH 16/53] minor fixes --- mm2src/mm2_main/src/lp_swap.rs | 4 ++-- .../mm2_main/src/lp_swap/recreate_swap_data.rs | 2 +- mm2src/mm2_main/src/lp_swap/saved_swap.rs | 6 +++--- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 18 +++++++++++------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index a9d017c535..94011b65ac 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -1181,9 +1181,9 @@ pub async fn swap_kick_starts(ctx: MmArc) -> Result, String> { let swaps = try_s!(SavedSwap::load_all_my_swaps_from_db(&ctx).await); for swap in swaps { if swap.is_finished_and_success() - || !swap.contains_watcher_message() + || !swap.watcher_message_sent() || swap.refunded_by_watcher() - || swap.watcher_refund_not_found() + || swap.watcher_spend_or_refund_not_found() { info!("{} {}", SWAP_FINISHED_LOG, swap.uuid()); continue; 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 744e476cf4..603ff839fd 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -274,7 +274,7 @@ fn convert_taker_to_maker_events( | TakerSwapEvent::TakerPaymentRefundFailed(_) | TakerSwapEvent::TakerPaymentRefundFinished | TakerSwapEvent::TakerPaymentRefundedByWatcher(_) - | TakerSwapEvent::WatcherRefundNotFound + | TakerSwapEvent::WatcherSpendOrRefundNotFound | TakerSwapEvent::Finished => {} } } diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index f6f699966e..307babd05f 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -56,7 +56,7 @@ impl SavedSwap { } } - pub fn contains_watcher_message(&self) -> bool { + pub fn watcher_message_sent(&self) -> bool { match &self { SavedSwap::Taker(taker_swap) => taker_swap.watcher_message_sent(), _ => false, @@ -73,12 +73,12 @@ impl SavedSwap { } } - pub fn watcher_refund_not_found(&self) -> bool { + pub fn watcher_spend_or_refund_not_found(&self) -> bool { match &self { SavedSwap::Taker(taker_swap) => taker_swap .events .iter() - .any(|e| matches!(e.event, TakerSwapEvent::WatcherRefundNotFound)), + .any(|e| matches!(e.event, TakerSwapEvent::WatcherSpendOrRefundNotFound)), _ => false, } } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index d3aab33c3d..d5262da16d 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -186,7 +186,7 @@ impl TakerSavedEvent { TakerSwapEvent::TakerPaymentRefundFailed(_) => Some(TakerSwapCommand::Finish), TakerSwapEvent::TakerPaymentRefundFinished => Some(TakerSwapCommand::Finish), TakerSwapEvent::TakerPaymentRefundedByWatcher(_) => Some(TakerSwapCommand::Finish), - TakerSwapEvent::WatcherRefundNotFound => Some(TakerSwapCommand::Finish), + TakerSwapEvent::WatcherSpendOrRefundNotFound => Some(TakerSwapCommand::Finish), TakerSwapEvent::Finished => None, } } @@ -676,7 +676,7 @@ pub enum TakerSwapEvent { TakerPaymentRefundFailed(SwapError), TakerPaymentRefundFinished, TakerPaymentRefundedByWatcher(Option), - WatcherRefundNotFound, + WatcherSpendOrRefundNotFound, Finished, } @@ -717,7 +717,7 @@ impl TakerSwapEvent { TakerSwapEvent::TakerPaymentRefundFailed(_) => "Taker payment refund failed...".to_owned(), TakerSwapEvent::TakerPaymentRefundFinished => "Taker payment refund finished...".to_owned(), TakerSwapEvent::TakerPaymentRefundedByWatcher(_) => "Taker payment refunded by watcher...".to_owned(), - TakerSwapEvent::WatcherRefundNotFound => "Watcher refund could not be found...".to_owned(), + TakerSwapEvent::WatcherSpendOrRefundNotFound => "Watcher refund could not be found...".to_owned(), TakerSwapEvent::Finished => "Finished".to_owned(), } } @@ -752,7 +752,7 @@ impl TakerSwapEvent { TakerSwapEvent::TakerPaymentRefundFailed(_) => "TakerPaymentRefundFailed".to_owned(), TakerSwapEvent::TakerPaymentRefundFinished => "TakerPaymentRefundFinished".to_owned(), TakerSwapEvent::TakerPaymentRefundedByWatcher(_) => "TakerPaymentRefundedByWatcher".to_owned(), - TakerSwapEvent::WatcherRefundNotFound => "WatcherRefundNotFound".to_owned(), + TakerSwapEvent::WatcherSpendOrRefundNotFound => "WatcherRefundNotFound".to_owned(), TakerSwapEvent::Finished => "Finished".to_owned(), } } @@ -890,7 +890,7 @@ impl TakerSwap { TakerSwapEvent::TakerPaymentRefundFailed(err) => self.errors.lock().push(err), TakerSwapEvent::TakerPaymentRefundFinished => (), TakerSwapEvent::TakerPaymentRefundedByWatcher(tx) => self.w().taker_payment_refund = tx, - TakerSwapEvent::WatcherRefundNotFound => (), + TakerSwapEvent::WatcherSpendOrRefundNotFound => (), TakerSwapEvent::Finished => self.finished_at.store(now_sec(), Ordering::Relaxed), } } @@ -2060,7 +2060,7 @@ impl TakerSwap { swap.apply_event(saved_event.event.clone()); } - if saved.watcher_message_sent() && check_watcher_payments(&swap, &ctx, saved).await? { + if check_watcher_payments(&swap, &ctx, saved).await? { return Ok((swap, Some(TakerSwapCommand::Finish))); } @@ -2375,6 +2375,10 @@ pub struct TakerSwapPreparedParams { } pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: TakerSavedSwap) -> Result { + if !saved.watcher_message_sent() { + return Ok(false); + } + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; let secret_hash = swap.r().secret_hash.0.clone(); let maker_coin_start_block = swap.r().data.maker_coin_start_block; @@ -2530,7 +2534,7 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta if saved.is_finished() { saved.events.pop(); } - let event = TakerSwapEvent::WatcherRefundNotFound; + let event = TakerSwapEvent::WatcherSpendOrRefundNotFound; let to_save = TakerSavedEvent { timestamp: now_ms(), event, From fcc4c4e06d3293e8836dd36def16a7e19f9bf42e Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 20 Jul 2023 14:49:53 +0300 Subject: [PATCH 17/53] ignore test_two_watchers_spend_maker_payment_eth_erc20 --- mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs | 1 + 1 file changed, 1 insertion(+) 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 8d0ee90d3b..1c834ecd7a 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -1428,6 +1428,7 @@ fn test_watcher_waits_for_taker_eth() { } #[test] +#[ignore] fn test_two_watchers_spend_maker_payment_eth_erc20() { let coins = json!([eth_testnet_conf(), eth_jst_testnet_conf()]); From 5d5063901879fd3aaced0c5de14e4f6615f70a87 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 20 Jul 2023 15:21:36 +0300 Subject: [PATCH 18/53] fix test_watcher_waits_for_taker_utxo --- mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1c834ecd7a..a5082ef506 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -1395,7 +1395,7 @@ fn test_watcher_waits_for_taker_utxo() { 25., 25., 2., - &[], + &[("USE_WATCHERS", "")], SwapFlow::TakerSpendsMakerPayment, alice_privkey, bob_privkey, From 105e67e383fc0d8a0b5b0bf24e26d68bb8cfd72e Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 20 Jul 2023 15:26:41 +0300 Subject: [PATCH 19/53] add debug logs to swap_watcher --- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 1e6bcdf3f2..4ec71f1e2b 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -171,6 +171,7 @@ impl State for ValidateTakerFee { type Result = (); async fn on_changed(self: Box, watcher_ctx: &mut WatcherContext) -> StateResult { + debug!("Watcher validate taker fee"); let validated_f = watcher_ctx .taker_coin .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { @@ -199,6 +200,7 @@ impl State for ValidateTakerPayment { type Result = (); async fn on_changed(self: Box, watcher_ctx: &mut WatcherContext) -> StateResult { + debug!("Watcher validate taker payment"); let taker_payment_spend_deadline = taker_payment_spend_deadline(watcher_ctx.data.swap_started_at, watcher_ctx.data.lock_duration); @@ -273,6 +275,7 @@ impl State for WaitForTakerPaymentSpend { type Result = (); async fn on_changed(self: Box, watcher_ctx: &mut WatcherContext) -> StateResult { + debug!("Watcher wait for taker payment spend"); let payment_search_interval = watcher_ctx.conf.search_interval; let wait_until = watcher_ctx.refund_start_time(); let search_input = WatcherSearchForSwapTxSpendInput { @@ -378,6 +381,7 @@ impl State for SpendMakerPayment { type Result = (); async fn on_changed(self: Box, watcher_ctx: &mut WatcherContext) -> StateResult { + debug!("Watcher spend maker payment"); let spend_fut = watcher_ctx .maker_coin .send_maker_payment_spend_preimage(SendMakerPaymentSpendPreimageInput { @@ -430,6 +434,7 @@ impl State for RefundTakerPayment { type Result = (); async fn on_changed(self: Box, watcher_ctx: &mut WatcherContext) -> StateResult { + debug!("Watcher refund taker payment"); if std::env::var("USE_TEST_LOCKTIME").is_err() { loop { match watcher_ctx From 0514d5c50bb59c31a16e6f82d0af6ab84bf897e8 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 20 Jul 2023 16:24:20 +0300 Subject: [PATCH 20/53] fix a bug in swap kick starting conditions --- mm2src/mm2_main/src/lp_swap.rs | 7 ++++--- mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 94011b65ac..abc0deed1b 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -1181,9 +1181,10 @@ pub async fn swap_kick_starts(ctx: MmArc) -> Result, String> { let swaps = try_s!(SavedSwap::load_all_my_swaps_from_db(&ctx).await); for swap in swaps { if swap.is_finished_and_success() - || !swap.watcher_message_sent() - || swap.refunded_by_watcher() - || swap.watcher_spend_or_refund_not_found() + || (swap.is_finished() + && (!swap.watcher_message_sent() + || swap.refunded_by_watcher() + || swap.watcher_spend_or_refund_not_found())) { info!("{} {}", SWAP_FINISHED_LOG, swap.uuid()); continue; 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 a5082ef506..c18da1dd6e 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -922,7 +922,7 @@ fn test_taker_adds_watcher_refund_not_found_event() { "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", "TakerPaymentRefundFailed", - "WatcherRefundNotFound", + "WatcherSpendOrRefundNotFound", "Finished", ]; let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); From 9ebd7e5e4c9e88809507d6e4703b7c248ca108dd Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 20 Jul 2023 19:12:50 +0300 Subject: [PATCH 21/53] reduce duplicate code in watcher taker restart tests --- mm2src/mm2_main/src/lp_swap.rs | 3 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 3 +- .../tests/docker_tests/swap_watcher_tests.rs | 253 ++++++------------ 3 files changed, 82 insertions(+), 177 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index abc0deed1b..181232f94f 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -125,8 +125,7 @@ use taker_swap::TakerSwapEvent; pub use taker_swap::{calc_max_taker_vol, check_balance_for_taker_swap, check_watcher_payments, max_taker_vol, max_taker_vol_from_available, run_taker_swap, taker_swap_trade_preimage, RunTakerSwapInput, TakerSavedSwap, TakerSwap, TakerSwapData, TakerSwapPreparedParams, TakerTradePreimage, - MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, REFUND_TEST_FAILURE_LOG, - TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, WATCHER_MESSAGE_SENT_LOG}; + MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, REFUND_TEST_FAILURE_LOG, WATCHER_MESSAGE_SENT_LOG}; pub use trade_preimage::trade_preimage_rpc; pub const SWAP_PREFIX: TopicPrefix = "swap"; diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index d5262da16d..8bebde2fb7 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -92,7 +92,6 @@ pub const TAKER_ERROR_EVENTS: [&str; 15] = [ pub const REFUND_TEST_FAILURE_LOG: &str = "Explicit refund test failure..."; pub const WATCHER_MESSAGE_SENT_LOG: &str = "Watcher message sent..."; pub const MAKER_PAYMENT_SPENT_BY_WATCHER_LOG: &str = "Maker payment is spent by the watcher..."; -pub const TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG: &str = "Taker payment is refunded by the watcher..."; #[cfg(not(target_arch = "wasm32"))] pub fn stats_taker_swap_dir(ctx: &MmArc) -> PathBuf { ctx.dbdir().join("SWAPS").join("STATS").join("TAKER") } @@ -2523,7 +2522,7 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta let new_swap = SavedSwap::Taker(saved); try_s!(new_swap.save_to_db(ctx).await); - info!("{}", TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG); + info!("Taker payment is refunded by the watcher"); Ok(true) }, (Some(_), None) => { 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 c18da1dd6e..8d97ad1e6f 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -14,8 +14,7 @@ use futures01::Future; use mm2_main::mm2::lp_swap::{dex_fee_amount, dex_fee_amount_from_taker_coin, dex_fee_threshold, get_payment_locktime, MakerSwap, MAKER_PAYMENT_SENT_LOG, MAKER_PAYMENT_SPEND_FOUND_LOG, MAKER_PAYMENT_SPEND_SENT_LOG, REFUND_TEST_FAILURE_LOG, SWAP_FINISHED_LOG, - TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, - WATCHER_MESSAGE_SENT_LOG}; + TAKER_PAYMENT_REFUND_SENT_LOG, WATCHER_MESSAGE_SENT_LOG}; use mm2_number::BigDecimal; use mm2_number::MmNumber; use mm2_test_helpers::for_tests::{enable_eth_coin, eth_jst_testnet_conf, eth_testnet_conf, mm_dump, my_balance, @@ -26,7 +25,7 @@ use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::WatcherConf; use num_traits::{One, Zero}; use primitives::hash::H256; -use serde_json as json; +use serde_json::{self as json, Value}; use std::str::FromStr; use std::thread; use std::time::Duration; @@ -258,6 +257,39 @@ fn start_swaps_and_get_balances( } } +fn run_alice(conf: &Mm2TestConf, envs: &[(&str, &str)], wait_until: &str) -> MarketMakerIt { + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + conf.conf.clone(), + conf.rpc_password.clone(), + None, + envs, + )) + .unwrap(); + + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(wait_until))).unwrap(); + mm_alice +} + +fn check_actual_and_success_events(mm_alice: &MarketMakerIt, uuid: &str, expected_events: &[&'static str]) { + let status_response = check_actual_events(mm_alice, uuid, expected_events); + let success_events: Vec = json::from_value(status_response["result"]["success_events"].clone()).unwrap(); + assert_eq!(expected_events, success_events.as_slice()); +} + +fn check_actual_events(mm_alice: &MarketMakerIt, uuid: &str, expected_events: &[&'static str]) -> Value { + let status_response = block_on(my_swap_status(mm_alice, uuid)); + let events_array = status_response["result"]["events"].as_array().unwrap(); + let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); + let actual_events: Vec<&str> = actual_events.collect(); + assert_eq!(expected_events, actual_events.as_slice()); + status_response +} + #[test] fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fail_at_maker_payment_spend() { let alice_privkey = hex::encode(random_secp256k1_secret()); @@ -338,37 +370,18 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, + run_alice( + &alice_conf, &[("USE_WATCHERS", "")], - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); + &format!("[swap uuid={}] Finished", &uuids[0]), + ); block_on(mm_alice.stop()).unwrap(); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, + let mm_alice = run_alice( + &alice_conf, &[("USE_WATCHERS", "")], - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); - + &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), + ); let expected_events = [ "Started", "Negotiated", @@ -384,14 +397,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai "MakerPaymentSpentByWatcher", "Finished", ]; - let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); - let events_array = status_response["result"]["events"].as_array().unwrap(); - let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); - let actual_events: Vec<&str> = actual_events.collect(); - assert_eq!(expected_events, actual_events.as_slice()); - - let success_events: Vec = json::from_value(status_response["result"]["success_events"].clone()).unwrap(); - assert_eq!(expected_events, success_events.as_slice()); + check_actual_and_success_events(&mm_alice, &uuids[0], &expected_events); } #[test] @@ -477,36 +483,18 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan block_on(mm_bob.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, + run_alice( + &alice_conf, &[("USE_WATCHERS", "")], - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); + &format!("[swap uuid={}] Finished", &uuids[0]), + ); block_on(mm_alice.stop()).unwrap(); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, + let mm_alice = run_alice( + &alice_conf, &[("USE_WATCHERS", "")], - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); + &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), + ); let expected_events = [ "Started", @@ -522,15 +510,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan "MakerPaymentSpentByWatcher", "Finished", ]; - let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); - let events_array = status_response["result"]["events"].as_array().unwrap(); - let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); - let actual_events: Vec<&str> = actual_events.collect(); - - assert_eq!(expected_events, actual_events.as_slice()); - - let success_events: Vec = json::from_value(status_response["result"]["success_events"].clone()).unwrap(); - assert_eq!(expected_events, success_events.as_slice()); + check_actual_and_success_events(&mm_alice, &uuids[0], &expected_events); } #[test] @@ -619,37 +599,18 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, + run_alice( + &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); - + &format!("[swap uuid={}] Finished", &uuids[0]), + ); block_on(mm_alice.stop()).unwrap(); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf, - alice_conf.rpc_password.clone(), - None, + let mm_alice = run_alice( + &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); + &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), + ); let expected_events = [ "Started", @@ -668,12 +629,7 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa "TakerPaymentRefundedByWatcher", "Finished", ]; - let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); - let events_array = status_response["result"]["events"].as_array().unwrap(); - let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); - let actual_events: Vec<&str> = actual_events.collect(); - - assert_eq!(expected_events, actual_events.as_slice()); + check_actual_events(&mm_alice, &uuids[0], &expected_events); } #[test] @@ -762,38 +718,18 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa block_on(mm_alice.wait_for_log(120., |log| log.contains(REFUND_TEST_FAILURE_LOG))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, + run_alice( + &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUNDED_BY_WATCHER_LOG))).unwrap(); - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); - + &format!("[swap uuid={}] Finished", &uuids[0]), + ); block_on(mm_alice.stop()).unwrap(); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf, - alice_conf.rpc_password.clone(), - None, + let mm_alice = run_alice( + &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); + &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), + ); let expected_events = [ "Started", @@ -811,12 +747,7 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa "TakerPaymentRefundedByWatcher", "Finished", ]; - let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); - let events_array = status_response["result"]["events"].as_array().unwrap(); - let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); - let actual_events: Vec<&str> = actual_events.collect(); - - assert_eq!(expected_events, actual_events.as_slice()); + check_actual_events(&mm_alice, &uuids[0], &expected_events); } #[test] @@ -876,37 +807,18 @@ fn test_taker_adds_watcher_refund_not_found_event() { block_on(mm_bob.stop()).unwrap(); block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, + run_alice( + &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); - + &format!("[swap uuid={}] Finished", &uuids[0]), + ); block_on(mm_alice.stop()).unwrap(); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf, - alice_conf.rpc_password.clone(), - None, + let mm_alice = run_alice( + &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("{} {}", SWAP_FINISHED_LOG, uuids[0])))).unwrap(); + &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), + ); let expected_events = [ "Started", @@ -925,12 +837,7 @@ fn test_taker_adds_watcher_refund_not_found_event() { "WatcherSpendOrRefundNotFound", "Finished", ]; - let status_response = block_on(my_swap_status(&mm_alice, &uuids[0])); - let events_array = status_response["result"]["events"].as_array().unwrap(); - let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); - let actual_events: Vec<&str> = actual_events.collect(); - - assert_eq!(expected_events, actual_events.as_slice()); + check_actual_events(&mm_alice, &uuids[0], &expected_events); } #[test] From f8912989f578ad3e8da9ff60a031e371519d9c22 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 20 Jul 2023 23:28:32 +0300 Subject: [PATCH 22/53] reduce duplication in watcher taker restart tests --- .../tests/docker_tests/swap_watcher_tests.rs | 446 ++++++------------ 1 file changed, 134 insertions(+), 312 deletions(-) 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 8d97ad1e6f..6e6bae334b 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -257,24 +257,6 @@ fn start_swaps_and_get_balances( } } -fn run_alice(conf: &Mm2TestConf, envs: &[(&str, &str)], wait_until: &str) -> MarketMakerIt { - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - conf.conf.clone(), - conf.rpc_password.clone(), - None, - envs, - )) - .unwrap(); - - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(wait_until))).unwrap(); - mm_alice -} - fn check_actual_and_success_events(mm_alice: &MarketMakerIt, uuid: &str, expected_events: &[&'static str]) { let status_response = check_actual_events(mm_alice, uuid, expected_events); let success_events: Vec = json::from_value(status_response["result"]["success_events"].clone()).unwrap(); @@ -290,40 +272,100 @@ fn check_actual_events(mm_alice: &MarketMakerIt, uuid: &str, expected_events: &[ status_response } -#[test] -fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fail_at_maker_payment_spend() { - let alice_privkey = hex::encode(random_secp256k1_secret()); - let bob_privkey = hex::encode(random_secp256k1_secret()); - let watcher_privkey = hex::encode(random_secp256k1_secret()); +fn run_taker_node(coins: &Value, envs: &[(&str, &str)]) -> (MarketMakerIt, Mm2TestConf) { + let privkey = hex::encode(random_secp256k1_secret()); + let conf = Mm2TestConf::seednode(&format!("0x{}", privkey), coins); + let mm = block_on(MarketMakerIt::start_with_envs( + conf.conf.clone(), + conf.rpc_password.clone(), + None, + envs, + )) + .unwrap(); + let (_dump_log, _dump_dashboard) = mm.mm_dump(); + log!("Log path: {}", mm.log_path.display()); - let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&privkey).unwrap()); + enable_coin(&mm, "MYCOIN"); + enable_coin(&mm, "MYCOIN1"); + + (mm, conf) +} - let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); +fn restart_taker_and_wait_until(conf: &Mm2TestConf, envs: &[(&str, &str)], wait_until: &str) -> MarketMakerIt { let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), + conf.conf.clone(), + conf.rpc_password.clone(), None, - &[("USE_WATCHERS", ""), ("TAKER_FAIL_AT", "maker_payment_spend")], + envs, )) .unwrap(); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); log!("Alice log path: {}", mm_alice.log_path.display()); + enable_coin(&mm_alice, "MYCOIN"); + enable_coin(&mm_alice, "MYCOIN1"); - let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); - let mut mm_bob = block_on(MarketMakerIt::start_with_envs( - bob_conf.conf.clone(), - bob_conf.rpc_password, + block_on(mm_alice.wait_for_log(120., |log| log.contains(wait_until))).unwrap(); + mm_alice +} + +fn run_maker_node(coins: &Value, envs: &[(&str, &str)], seednodes: &[&str]) -> MarketMakerIt { + let privkey = hex::encode(random_secp256k1_secret()); + let conf = Mm2TestConf::light_node(&format!("0x{}", privkey), coins, seednodes); + let mm = block_on(MarketMakerIt::start_with_envs( + conf.conf.clone(), + conf.rpc_password, None, - &[("USE_WATCHERS", "")], + envs, )) .unwrap(); - let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); - log!("Bob log path: {}", mm_bob.log_path.display()); + let (_dump_log, _dump_dashboard) = mm.mm_dump(); + log!("Log path: {}", mm.log_path.display()); - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&privkey).unwrap()); + enable_coin(&mm, "MYCOIN"); + enable_coin(&mm, "MYCOIN1"); + + mm +} + +fn run_watcher_node( + coins: &Value, + envs: &[(&str, &str)], + seednodes: &[&str], + watcher_conf: WatcherConf, +) -> MarketMakerIt { + let privkey = hex::encode(random_secp256k1_secret()); + let conf = Mm2TestConf::watcher_light_node(&format!("0x{}", privkey), coins, seednodes, watcher_conf).conf; + let mm = block_on(MarketMakerIt::start_with_envs( + conf, + DEFAULT_RPC_PASSWORD.to_string(), + None, + envs, + )) + .unwrap(); + let (_dump_log, _dump_dashboard) = mm.mm_dump(); + log!("Log path: {}", mm.log_path.display()); + + generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&privkey).unwrap()); + generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&privkey).unwrap()); + enable_coin(&mm, "MYCOIN"); + enable_coin(&mm, "MYCOIN1"); + + mm +} + +#[test] +fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fail_at_maker_payment_spend() { + let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ + ("USE_WATCHERS", ""), + ("TAKER_FAIL_AT", "maker_payment_spend"), + ]); + let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", "")], &[&mm_alice.ip.to_string()]); let watcher_conf = WatcherConf { wait_taker_payment: 0., @@ -331,30 +373,12 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai refund_start_factor: 1.5, search_interval: 1.0, }; - - let watcher_conf = Mm2TestConf::watcher_light_node( - &format!("0x{}", watcher_privkey), + let mut mm_watcher = run_watcher_node( &coins, + &[("USE_WATCHERS", "")], &[&mm_alice.ip.to_string()], watcher_conf, - ) - .conf; - - let mut mm_watcher = block_on(MarketMakerIt::start_with_envs( - watcher_conf, - DEFAULT_RPC_PASSWORD.to_string(), - None, - &[("USE_WATCHERS", "")], - )) - .unwrap(); - let (_watcher_dump_log, _watcher_dump_dashboard) = mm_dump(&mm_watcher.log_path); - - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - enable_coin(&mm_bob, "MYCOIN"); - enable_coin(&mm_bob, "MYCOIN1"); - enable_coin(&mm_watcher, "MYCOIN"); - enable_coin(&mm_watcher, "MYCOIN1"); + ); let uuids = block_on(start_swaps( &mut mm_bob, @@ -370,14 +394,14 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); - run_alice( + restart_taker_and_wait_until( &alice_conf, &[("USE_WATCHERS", "")], &format!("[swap uuid={}] Finished", &uuids[0]), ); block_on(mm_alice.stop()).unwrap(); - let mm_alice = run_alice( + let mm_alice = restart_taker_and_wait_until( &alice_conf, &[("USE_WATCHERS", "")], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), @@ -402,41 +426,12 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai #[test] fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_panic_at_wait_for_taker_payment_spend() { - let alice_privkey = hex::encode(random_secp256k1_secret()); - let bob_privkey = hex::encode(random_secp256k1_secret()); - let watcher_privkey = hex::encode(random_secp256k1_secret()); - let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - - let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, - &[ - ("USE_WATCHERS", ""), - ("TAKER_FAIL_AT", "wait_for_taker_payment_spend_panic"), - ], - )) - .unwrap(); - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - - let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); - let mut mm_bob = block_on(MarketMakerIt::start_with_envs( - bob_conf.conf.clone(), - bob_conf.rpc_password, - None, - &[("USE_WATCHERS", "")], - )) - .unwrap(); - let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); - log!("Bob log path: {}", mm_bob.log_path.display()); - - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ + ("USE_WATCHERS", ""), + ("TAKER_FAIL_AT", "wait_for_taker_payment_spend_panic"), + ]); + let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", "")], &[&mm_alice.ip.to_string()]); let watcher_conf = WatcherConf { wait_taker_payment: 0., @@ -444,30 +439,12 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan refund_start_factor: 1.5, search_interval: 1.0, }; - - let watcher_conf = Mm2TestConf::watcher_light_node( - &format!("0x{}", watcher_privkey), + let mut mm_watcher = run_watcher_node( &coins, + &[("USE_WATCHERS", "")], &[&mm_alice.ip.to_string()], watcher_conf, - ) - .conf; - - let mut mm_watcher = block_on(MarketMakerIt::start_with_envs( - watcher_conf, - DEFAULT_RPC_PASSWORD.to_string(), - None, - &[("USE_WATCHERS", "")], - )) - .unwrap(); - let (_watcher_dump_log, _watcher_dump_dashboard) = mm_dump(&mm_watcher.log_path); - - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - enable_coin(&mm_bob, "MYCOIN"); - enable_coin(&mm_bob, "MYCOIN1"); - enable_coin(&mm_watcher, "MYCOIN"); - enable_coin(&mm_watcher, "MYCOIN1"); + ); let uuids = block_on(start_swaps( &mut mm_bob, @@ -483,14 +460,14 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan block_on(mm_bob.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); - run_alice( + restart_taker_and_wait_until( &alice_conf, &[("USE_WATCHERS", "")], &format!("[swap uuid={}] Finished", &uuids[0]), ); block_on(mm_alice.stop()).unwrap(); - let mm_alice = run_alice( + let mm_alice = restart_taker_and_wait_until( &alice_conf, &[("USE_WATCHERS", "")], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), @@ -515,42 +492,15 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan #[test] fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fail_at_taker_payment_refund() { - let alice_privkey = hex::encode(random_secp256k1_secret()); - let bob_privkey = hex::encode(random_secp256k1_secret()); - let watcher_privkey = hex::encode(random_secp256k1_secret()); - let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - - let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, - &[ - ("USE_WATCHERS", ""), - ("USE_TEST_LOCKTIME", ""), - ("TAKER_FAIL_AT", "taker_payment_refund"), - ], - )) - .unwrap(); - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - - let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); - let mut mm_bob = block_on(MarketMakerIt::start_with_envs( - bob_conf.conf.clone(), - bob_conf.rpc_password, - None, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); - log!("Bob log path: {}", mm_bob.log_path.display()); - - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ + ("USE_WATCHERS", ""), + ("USE_TEST_LOCKTIME", ""), + ("TAKER_FAIL_AT", "taker_payment_refund"), + ]); + let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &[&mm_alice + .ip + .to_string()]); let watcher_conf = WatcherConf { wait_taker_payment: 0., @@ -558,30 +508,12 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa refund_start_factor: 0., search_interval: 1., }; - - let watcher_conf = Mm2TestConf::watcher_light_node( - &format!("0x{}", watcher_privkey), + let mut mm_watcher = run_watcher_node( &coins, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &[&mm_alice.ip.to_string()], watcher_conf, - ) - .conf; - - let mut mm_watcher = block_on(MarketMakerIt::start_with_envs( - watcher_conf, - DEFAULT_RPC_PASSWORD.to_string(), - None, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - let (_watcher_dump_log, _watcher_dump_dashboard) = mm_dump(&mm_watcher.log_path); - - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - enable_coin(&mm_bob, "MYCOIN"); - enable_coin(&mm_bob, "MYCOIN1"); - enable_coin(&mm_watcher, "MYCOIN"); - enable_coin(&mm_watcher, "MYCOIN1"); + ); let uuids = block_on(start_swaps( &mut mm_bob, @@ -599,14 +531,14 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); - run_alice( + restart_taker_and_wait_until( &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &format!("[swap uuid={}] Finished", &uuids[0]), ); block_on(mm_alice.stop()).unwrap(); - let mm_alice = run_alice( + let mm_alice = restart_taker_and_wait_until( &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), @@ -634,42 +566,15 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa #[test] fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_panic_at_taker_payment_refund() { - let alice_privkey = hex::encode(random_secp256k1_secret()); - let bob_privkey = hex::encode(random_secp256k1_secret()); - let watcher_privkey = hex::encode(random_secp256k1_secret()); - let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - - let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, - &[ - ("USE_WATCHERS", ""), - ("USE_TEST_LOCKTIME", ""), - ("TAKER_FAIL_AT", "taker_payment_refund_panic"), - ], - )) - .unwrap(); - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - - let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); - let mut mm_bob = block_on(MarketMakerIt::start_with_envs( - bob_conf.conf.clone(), - bob_conf.rpc_password, - None, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); - log!("Bob log path: {}", mm_bob.log_path.display()); - - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ + ("USE_WATCHERS", ""), + ("USE_TEST_LOCKTIME", ""), + ("TAKER_FAIL_AT", "taker_payment_refund_panic"), + ]); + let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &[&mm_alice + .ip + .to_string()]); let watcher_conf = WatcherConf { wait_taker_payment: 0., @@ -677,30 +582,12 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa refund_start_factor: 0., search_interval: 1., }; - - let watcher_conf = Mm2TestConf::watcher_light_node( - &format!("0x{}", watcher_privkey), + let mut mm_watcher = run_watcher_node( &coins, + &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &[&mm_alice.ip.to_string()], watcher_conf, - ) - .conf; - - let mut mm_watcher = block_on(MarketMakerIt::start_with_envs( - watcher_conf, - DEFAULT_RPC_PASSWORD.to_string(), - None, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - let (_watcher_dump_log, _watcher_dump_dashboard) = mm_dump(&mm_watcher.log_path); - - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - enable_coin(&mm_bob, "MYCOIN"); - enable_coin(&mm_bob, "MYCOIN1"); - enable_coin(&mm_watcher, "MYCOIN"); - enable_coin(&mm_watcher, "MYCOIN1"); + ); let uuids = block_on(start_swaps( &mut mm_bob, @@ -718,14 +605,14 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa block_on(mm_alice.wait_for_log(120., |log| log.contains(REFUND_TEST_FAILURE_LOG))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); - run_alice( + restart_taker_and_wait_until( &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &format!("[swap uuid={}] Finished", &uuids[0]), ); block_on(mm_alice.stop()).unwrap(); - let mm_alice = run_alice( + let mm_alice = restart_taker_and_wait_until( &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), @@ -752,46 +639,15 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa #[test] fn test_taker_adds_watcher_refund_not_found_event() { - let alice_privkey = hex::encode(random_secp256k1_secret()); - let bob_privkey = hex::encode(random_secp256k1_secret()); - let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - - let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, - &[ - ("USE_WATCHERS", ""), - ("USE_TEST_LOCKTIME", ""), - ("TAKER_FAIL_AT", "taker_payment_refund"), - ], - )) - .unwrap(); - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - - let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); - let mut mm_bob = block_on(MarketMakerIt::start_with_envs( - bob_conf.conf.clone(), - bob_conf.rpc_password, - None, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], - )) - .unwrap(); - let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); - log!("Bob log path: {}", mm_bob.log_path.display()); - - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); - - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - enable_coin(&mm_bob, "MYCOIN"); - enable_coin(&mm_bob, "MYCOIN1"); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ + ("USE_WATCHERS", ""), + ("USE_TEST_LOCKTIME", ""), + ("TAKER_FAIL_AT", "taker_payment_refund"), + ]); + let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &[&mm_alice + .ip + .to_string()]); let uuids = block_on(start_swaps( &mut mm_bob, @@ -807,14 +663,14 @@ fn test_taker_adds_watcher_refund_not_found_event() { block_on(mm_bob.stop()).unwrap(); block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); - run_alice( + restart_taker_and_wait_until( &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &format!("[swap uuid={}] Finished", &uuids[0]), ); block_on(mm_alice.stop()).unwrap(); - let mm_alice = run_alice( + let mm_alice = restart_taker_and_wait_until( &alice_conf, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), @@ -842,42 +698,9 @@ fn test_taker_adds_watcher_refund_not_found_event() { #[test] fn test_taker_completes_swap_after_restart() { - let alice_privkey = hex::encode(random_secp256k1_secret()); - let bob_privkey = hex::encode(random_secp256k1_secret()); - let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - - let mut alice_conf = Mm2TestConf::seednode(&format!("0x{}", alice_privkey), &coins); - let mut mm_alice = block_on(MarketMakerIt::start_with_envs( - alice_conf.conf.clone(), - alice_conf.rpc_password.clone(), - None, - &[("USE_WATCHERS", "")], - )) - .unwrap(); - let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); - log!("Alice log path: {}", mm_alice.log_path.display()); - - let bob_conf = Mm2TestConf::light_node(&format!("0x{}", bob_privkey), &coins, &[&mm_alice.ip.to_string()]); - let mut mm_bob = block_on(MarketMakerIt::start_with_envs( - bob_conf.conf.clone(), - bob_conf.rpc_password, - None, - &[("USE_WATCHERS", "")], - )) - .unwrap(); - let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); - log!("Bob log path: {}", mm_bob.log_path.display()); - - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN", 100.into(), H256::from_str(&alice_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&bob_privkey).unwrap()); - generate_utxo_coin_with_privkey("MYCOIN1", 100.into(), H256::from_str(&alice_privkey).unwrap()); - - enable_coin(&mm_alice, "MYCOIN"); - enable_coin(&mm_alice, "MYCOIN1"); - enable_coin(&mm_bob, "MYCOIN"); - enable_coin(&mm_bob, "MYCOIN1"); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[("USE_WATCHERS", "")]); + let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", "")], &[&mm_alice.ip.to_string()]); let uuids = block_on(start_swaps( &mut mm_bob, @@ -891,7 +714,6 @@ fn test_taker_completes_swap_after_restart() { block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); block_on(mm_alice.stop()).unwrap(); - thread::sleep(Duration::from_secs(5)); let mut mm_alice = block_on(MarketMakerIt::start_with_envs( alice_conf.conf, From b0ffd0d92da386fd49155d5df69506d1b81c321a Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Fri, 28 Jul 2023 16:20:46 +0300 Subject: [PATCH 23/53] validate maker payment spend and taker payment refund on taker restart --- mm2src/coins/eth.rs | 4 ++++ mm2src/coins/lightning.rs | 4 ++++ mm2src/coins/lp_coins.rs | 2 ++ mm2src/coins/qrc20.rs | 4 ++++ mm2src/coins/solana.rs | 4 ++++ mm2src/coins/solana/spl.rs | 4 ++++ mm2src/coins/tendermint/tendermint_coin.rs | 4 ++++ mm2src/coins/tendermint/tendermint_token.rs | 4 ++++ mm2src/coins/test_coin.rs | 4 ++++ mm2src/coins/utxo/bch.rs | 5 ++++ mm2src/coins/utxo/qtum.rs | 5 ++++ mm2src/coins/utxo/slp.rs | 4 ++++ mm2src/coins/utxo/utxo_common.rs | 26 +++++++++++++++++++++ mm2src/coins/utxo/utxo_standard.rs | 5 ++++ mm2src/coins/z_coin.rs | 4 ++++ mm2src/mm2_main/src/lp_swap/taker_swap.rs | 8 +++++++ 16 files changed, 91 insertions(+) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 3509680a0a..5f4903a661 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1461,6 +1461,10 @@ impl WatcherOps for EthCoin { // 1.Validate if taker fee is old } + fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + unimplemented!(); + } + fn watcher_validate_taker_payment(&self, input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()> { let unsigned: UnverifiedTransaction = try_f!(rlp::decode(&input.payment_tx)); let tx = diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 02a0c41cfd..12ca424162 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -989,6 +989,10 @@ impl WatcherOps for LightningCoin { unimplemented!(); } + fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + unimplemented!() + } + async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 98a7f39841..ab304ba030 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -968,6 +968,8 @@ pub trait WatcherOps { fn watcher_validate_taker_payment(&self, _input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()>; + fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError>; + async fn watcher_search_for_swap_tx_spend( &self, input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index d46371ed6f..6e86cbd3ea 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1135,6 +1135,10 @@ impl WatcherOps for Qrc20Coin { unimplemented!(); } + fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + unimplemented!() + } + async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 90e914711c..391dd4b749 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -639,6 +639,10 @@ impl WatcherOps for SolanaCoin { unimplemented!(); } + fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { + unimplemented!() + } + async fn watcher_search_for_swap_tx_spend( &self, input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index d5d5b3250e..4fd02160ec 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -460,6 +460,10 @@ impl WatcherOps for SplToken { unimplemented!(); } + fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { + unimplemented!() + } + async fn watcher_search_for_swap_tx_spend( &self, input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 5279e4fbef..00d3c1ea13 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -2623,6 +2623,10 @@ impl WatcherOps for TendermintCoin { unimplemented!(); } + fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + unimplemented!(); + } + async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index f4c128114d..3cec34c148 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -469,6 +469,10 @@ impl WatcherOps for TendermintToken { unimplemented!(); } + fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + unimplemented!(); + } + async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 1fd9694fc7..d841bb83f5 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -270,6 +270,10 @@ impl WatcherOps for TestCoin { unimplemented!(); } + fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { + unimplemented!() + } + async fn watcher_search_for_swap_tx_spend( &self, input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index 6f4da1cce7..a6b8eefca3 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1086,6 +1086,11 @@ impl WatcherOps for BchCoin { utxo_common::watcher_validate_taker_payment(self, input) } + #[inline] + fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { + utxo_common::validate_watcher_spend(self, tx) + } + async fn watcher_search_for_swap_tx_spend( &self, input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 9640e66184..b03d81507e 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -777,6 +777,11 @@ impl WatcherOps for QtumCoin { utxo_common::watcher_validate_taker_payment(self, input) } + #[inline] + fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { + utxo_common::validate_watcher_spend(self, tx) + } + async fn watcher_search_for_swap_tx_spend( &self, input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index 9d8d968b53..cd1ef2ea0f 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1518,6 +1518,10 @@ impl WatcherOps for SlpToken { unimplemented!(); } + fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + unimplemented!() + } + async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 62e12df8c4..3e03e4d409 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -2081,6 +2081,32 @@ pub fn validate_taker_payment( ) } +pub fn validate_watcher_spend( + coin: &T, + tx: TransactionEnum, +) -> Result<(), MmError> { + let mut payment_spend_tx: UtxoTx = deserialize(tx.tx_hex().as_slice())?; + payment_spend_tx.tx_hash_algo = coin.as_ref().tx_hash_algo; + + let my_address = coin.as_ref().derivation_method.single_addr_or_err()?; + let expected_script_pubkey = &output_script(my_address, ScriptType::P2PKH).to_bytes(); + let actual_script_pubkey = match payment_spend_tx.outputs.get(DEFAULT_SWAP_VOUT) { + Some(output) => &output.script_pubkey, + None => { + return MmError::err(ValidatePaymentError::WrongPaymentTx( + "Payment tx has no outputs".to_string(), + )) + }, + }; + if expected_script_pubkey != actual_script_pubkey { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Provided payment tx script pubkey doesn't match expected {:?} {:?}", + actual_script_pubkey, expected_script_pubkey + ))); + } + Ok(()) +} + pub fn check_if_my_payment_sent( coin: T, time_lock: u32, diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 8c03e66a8c..39c4fd8b99 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -542,6 +542,11 @@ impl WatcherOps for UtxoStandardCoin { utxo_common::watcher_validate_taker_payment(self, input) } + #[inline] + fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { + utxo_common::validate_watcher_spend(self, tx) + } + #[inline] async fn watcher_search_for_swap_tx_spend( &self, diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 4e3f6f2ecb..36c75a8df5 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -1593,6 +1593,10 @@ impl WatcherOps for ZCoin { unimplemented!(); } + fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + unimplemented!() + } + async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 8bebde2fb7..dadf9cb88e 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -2442,6 +2442,10 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx)), Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx)), ) => { + swap.taker_coin + .validate_watcher_spend(maker_payment_spend_tx.clone()) + .map_err(|e| e.to_string())?; + if saved.is_finished() { saved.events.pop(); } @@ -2501,6 +2505,10 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta }, (Some(FoundSwapTxSpend::Refunded(_)), Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) | (None, Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { + swap.taker_coin + .validate_watcher_spend(taker_payment_refund_tx.clone()) + .map_err(|e| e.to_string())?; + if saved.is_finished() { saved.events.pop(); } From 6749e64a6155b3ee1514982c3baf7c77c3cbcf9d Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Sun, 30 Jul 2023 20:39:22 +0300 Subject: [PATCH 24/53] add test for watcher refund validation --- .../tests/docker_tests/swap_watcher_tests.rs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) 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 6e6bae334b..7ecd006bd3 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -2279,6 +2279,73 @@ fn test_watcher_validate_taker_payment_erc20() { } } +#[test] +fn test_validate_watcher_refund_utxo() { + let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run + let time_lock_duration = get_payment_locktime(); + let wait_for_confirmation_until = wait_until_sec(time_lock_duration); + let time_lock = now_sec_u32() - 10; + + let (_ctx, taker_coin, _) = generate_utxo_coin_with_random_privkey("MYCOIN", 1000u64.into()); + let (_ctx, maker_coin, _) = generate_utxo_coin_with_random_privkey("MYCOIN", 1000u64.into()); + let maker_pubkey = maker_coin.my_public_key().unwrap(); + + let secret_hash = dhash160(&MakerSwap::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 confirm_payment_input = ConfirmPaymentInput { + payment_tx: taker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + }; + taker_coin.wait_for_confirmations(confirm_payment_input).wait().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 = taker_coin + .send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &taker_payment_refund_preimage.tx_hex(), + other_pubkey: maker_pubkey, + secret_hash: secret_hash.as_slice(), + time_lock, + swap_contract_address: &None, + swap_unique_data: &[], + watcher_reward: false, + }) + .wait() + .unwrap(); + + let validate_watcher_refund = taker_coin.validate_watcher_spend(taker_payment_refund); + assert!(validate_watcher_refund.is_ok()); +} + #[test] fn test_send_taker_payment_refund_preimage_utxo() { let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run From 955f67b47c195de1624359f87b2316d92066376b Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Sun, 30 Jul 2023 21:14:02 +0300 Subject: [PATCH 25/53] add test for validate watcher spend --- .../tests/docker_tests/swap_watcher_tests.rs | 77 ++++++++++++++++++- 1 file changed, 73 insertions(+), 4 deletions(-) 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 7ecd006bd3..65f4d05ecf 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -4,10 +4,11 @@ use crate::{generate_utxo_coin_with_privkey, generate_utxo_coin_with_random_priv use coins::coin_errors::ValidatePaymentError; use coins::utxo::{dhash160, UtxoCommonOps}; use coins::{ConfirmPaymentInput, FoundSwapTxSpend, MarketCoinOps, MmCoin, MmCoinEnum, RefundPaymentArgs, RewardTarget, - SearchForSwapTxSpendInput, SendPaymentArgs, SwapOps, WatcherOps, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, EARLY_CONFIRMATION_ERR_LOG, 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}; + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SwapOps, WatcherOps, + WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, EARLY_CONFIRMATION_ERR_LOG, + 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_u32, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::privkey::{key_pair_from_secret, key_pair_from_seed}; use futures01::Future; @@ -2346,6 +2347,74 @@ fn test_validate_watcher_refund_utxo() { assert!(validate_watcher_refund.is_ok()); } +#[test] +fn test_validate_watcher_spend_utxo() { + let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run + let time_lock_duration = get_payment_locktime(); + let wait_for_confirmation_until = wait_until_sec(time_lock_duration); + let time_lock = wait_for_confirmation_until as u32; + + let (_ctx, taker_coin, _) = generate_utxo_coin_with_random_privkey("MYCOIN", 1000u64.into()); + let (_ctx, maker_coin, _) = generate_utxo_coin_with_random_privkey("MYCOIN", 1000u64.into()); + let taker_pubkey = taker_coin.my_public_key().unwrap(); + let maker_pubkey = maker_coin.my_public_key().unwrap(); + + let secret = MakerSwap::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(); + + 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(); + + 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 = 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(); + + let validate_watcher_spend = taker_coin.validate_watcher_spend(maker_payment_spend); + assert!(validate_watcher_spend.is_ok()); +} + #[test] fn test_send_taker_payment_refund_preimage_utxo() { let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run From 2192e8853bf45612a5998e2131a09a63c30de9f5 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Tue, 1 Aug 2023 14:54:05 +0300 Subject: [PATCH 26/53] add taker payment refund validation for eth and erc20 --- mm2src/coins/eth.rs | 197 ++++++++++++++- mm2src/coins/lightning.rs | 13 +- mm2src/coins/lp_coins.rs | 15 +- mm2src/coins/qrc20.rs | 12 +- mm2src/coins/solana.rs | 14 +- mm2src/coins/solana/spl.rs | 12 +- mm2src/coins/tendermint/tendermint_coin.rs | 10 +- mm2src/coins/tendermint/tendermint_token.rs | 8 +- mm2src/coins/test_coin.rs | 7 +- mm2src/coins/utxo/bch.rs | 15 +- mm2src/coins/utxo/qtum.rs | 15 +- mm2src/coins/utxo/slp.rs | 14 +- mm2src/coins/utxo/utxo_common.rs | 45 ++-- mm2src/coins/utxo/utxo_standard.rs | 15 +- mm2src/coins/z_coin.rs | 13 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 29 ++- .../tests/docker_tests/swap_watcher_tests.rs | 225 +++++++++++++++++- 17 files changed, 577 insertions(+), 82 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 5f4903a661..940156e97f 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -23,6 +23,7 @@ use super::eth::Action::{Call, Create}; use crate::lp_price::get_base_price_in_rel; use crate::nft::nft_structs::{ContractType, ConvertChain, TransactionNftDetails, WithdrawErc1155, WithdrawErc721}; +use crate::ValidateWatcherSpendInput; use async_trait::async_trait; use bitcrypto::{keccak256, ripemd160, sha256}; use common::custom_futures::repeatable::{Ready, Retry, RetryOnError}; @@ -1461,8 +1462,200 @@ impl WatcherOps for EthCoin { // 1.Validate if taker fee is old } - fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { - unimplemented!(); + fn validate_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + let watcher_reward = try_f!(input + .watcher_reward + .clone() + .ok_or_else(|| ValidatePaymentError::WatcherRewardError("WatcherRewardNotFound".to_string()))); + let expected_reward_amount = try_f!(wei_from_big_decimal(&watcher_reward.amount, self.decimals)); + + let expected_swap_contract_address = try_f!(input + .swap_contract_address + .try_to_address() + .map_to_mm(ValidatePaymentError::InvalidParameter)); + + let unsigned: UnverifiedTransaction = try_f!(rlp::decode(&input.payment_tx)); + let tx = + try_f!(SignedEthTx::new(unsigned) + .map_to_mm(|err| ValidatePaymentError::TxDeserializationError(err.to_string()))); + + let selfi = self.clone(); + let swap_id = selfi.etomic_swap_id(input.time_lock, &input.secret_hash); + let decimals = self.decimals; + let secret_hash = if input.secret_hash.len() == 32 { + ripemd160(&input.secret_hash).to_vec() + } else { + input.secret_hash.to_vec() + }; + let receiver_addr = + try_f!(addr_from_raw_pubkey(&input.maker_pub).map_to_mm(ValidatePaymentError::InvalidParameter)); + let trade_amount = try_f!(wei_from_big_decimal(&(input.amount), decimals)); + let fut = async move { + let status = selfi + .payment_status(expected_swap_contract_address, Token::FixedBytes(swap_id.clone())) + .compat() + .await + .map_to_mm(ValidatePaymentError::Transport)?; + if status != U256::from(PaymentState::Refunded as u8) { + return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( + "Payment state is not PAYMENT_STATE_REFUNDED, got {}", + status + ))); + } + + match tx.action { + Call(contract_address) => { + if contract_address != expected_swap_contract_address { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment tx {:?} was sent to wrong address, expected {:?}", + contract_address, expected_swap_contract_address, + ))); + } + }, + Create => { + return MmError::err(ValidatePaymentError::WrongPaymentTx( + "Tx action must be Call, found Create instead".to_string(), + )); + }, + }; + + let function_name = get_function_name("senderRefund", true); + let function = SWAP_CONTRACT + .function(&function_name) + .map_to_mm(|err| ValidatePaymentError::InternalError(err.to_string()))?; + + let decoded = decode_contract_call(function, &tx.data) + .map_to_mm(|err| ValidatePaymentError::TxDeserializationError(err.to_string()))?; + + let swap_id_input = get_function_input_data(&decoded, function, 0) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if swap_id_input != Token::FixedBytes(swap_id.clone()) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx invalid swap_id arg {:?}, expected {:?}", + swap_id_input, swap_id + ))); + } + + let hash_input = get_function_input_data(&decoded, function, 2) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if hash_input != Token::FixedBytes(secret_hash.to_vec()) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx secret_hash arg {:?} is invalid, expected {:?}", + hash_input, + Token::FixedBytes(secret_hash.to_vec()), + ))); + } + + let sender_input = get_function_input_data(&decoded, function, 4) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if sender_input != Token::Address(selfi.my_address) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx sender arg {:?} is invalid, expected {:?}", + sender_input, + Token::Address(selfi.my_address) + ))); + } + + let receiver_input = get_function_input_data(&decoded, function, 5) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if receiver_input != Token::Address(receiver_addr) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx receiver arg {:?} is invalid, expected {:?}", + receiver_input, + Token::Address(receiver_addr) + ))); + } + + let reward_target_input = get_function_input_data(&decoded, function, 6) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if reward_target_input != Token::Uint(U256::from(watcher_reward.reward_target as u8)) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx reward target arg {:?} is invalid, expected {:?}", + reward_target_input, watcher_reward.reward_target as u8 + ))); + } + + let contract_reward_input = get_function_input_data(&decoded, function, 7) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if contract_reward_input != Token::Bool(watcher_reward.send_contract_reward_on_spend) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx sends_contract_reward_on_spend arg {:?} is invalid, expected {:?}", + contract_reward_input, watcher_reward.send_contract_reward_on_spend + ))); + } + + let reward_amount_input = get_function_input_data(&decoded, function, 8) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if reward_amount_input != Token::Uint(expected_reward_amount) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx watcher reward amount arg {:?} is invalid, expected {:?}", + reward_amount_input, expected_reward_amount + ))); + } + + if tx.value != U256::zero() { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment tx value arg {:?} is invalid, expected 0", + tx.value + ))); + } + + match &selfi.coin_type { + EthCoinType::Eth => { + let amount_input = get_function_input_data(&decoded, function, 1) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + let total_amount = trade_amount + expected_reward_amount; + if amount_input != Token::Uint(total_amount) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx amount arg {:?} is invalid, expected {:?}", + amount_input, + Token::Uint(total_amount), + ))); + } + + let token_address_input = get_function_input_data(&decoded, function, 3) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if token_address_input != Token::Address(Address::default()) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx token address arg {:?} is invalid, expected {:?}", + token_address_input, + Token::Address(Address::default()), + ))); + } + }, + EthCoinType::Erc20 { + platform: _, + token_addr, + } => { + let amount_input = get_function_input_data(&decoded, function, 1) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if amount_input != Token::Uint(trade_amount) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx amount arg {:?} is invalid, expected {:?}", + amount_input, + Token::Uint(trade_amount), + ))); + } + + let token_address_input = get_function_input_data(&decoded, function, 3) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if token_address_input != Token::Address(*token_addr) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Refund tx token address arg {:?} is invalid, expected {:?}", + token_address_input, + Token::Address(*token_addr), + ))); + } + }, + } + + Ok(()) + }; + Box::new(fut.boxed().compat()) + } + + fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!() } fn watcher_validate_taker_payment(&self, input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()> { diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 12ca424162..3bfdcf68f3 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -24,9 +24,10 @@ use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, C TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, TransactionEnum, TransactionErr, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, - ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, - WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest}; + ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, VerificationError, + VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, + WithdrawError, WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcoin::bech32::ToBase32; use bitcoin::hashes::Hash; @@ -989,10 +990,14 @@ impl WatcherOps for LightningCoin { unimplemented!(); } - fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } + fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!(); + } + async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index ab304ba030..743736ac08 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -600,6 +600,17 @@ pub struct WatcherValidatePaymentInput { pub maker_coin: MmCoinEnum, } +#[derive(Clone)] +pub struct ValidateWatcherSpendInput { + pub payment_tx: Vec, + pub maker_pub: Vec, + pub swap_contract_address: Option, + pub time_lock: u32, + pub secret_hash: Vec, + pub amount: BigDecimal, + pub watcher_reward: Option, +} + #[derive(Clone, Debug)] pub struct ValidatePaymentInput { pub payment_tx: Vec, @@ -968,7 +979,9 @@ pub trait WatcherOps { fn watcher_validate_taker_payment(&self, _input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()>; - fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError>; + fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()>; + + fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()>; async fn watcher_search_for_swap_tx_spend( &self, diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 6e86cbd3ea..af9fb14a16 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -25,9 +25,9 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, Coi TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, - VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, - WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, - WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; + ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, + WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; use chain::TransactionOutput; @@ -1135,10 +1135,14 @@ impl WatcherOps for Qrc20Coin { unimplemented!(); } - fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } + fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!(); + } + async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 391dd4b749..8be0f7165b 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -10,10 +10,10 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, SpendPaymentArgs, TakerSwapMakerCoin, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, - ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, - WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, - WithdrawResult}; + ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, + VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, + WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use base58::ToBase58; use bincode::{deserialize, serialize}; @@ -639,7 +639,11 @@ impl WatcherOps for SolanaCoin { unimplemented!(); } - fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { + fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!() + } + + fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 4fd02160ec..c5136059d4 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -10,9 +10,9 @@ use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPayment TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, - ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, - WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, - WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; + ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, + WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use bincode::serialize; use common::executor::{abortable_queue::AbortableQueue, AbortableSystem, AbortedError}; @@ -460,7 +460,11 @@ impl WatcherOps for SplToken { unimplemented!(); } - fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { + fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!() + } + + fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 00d3c1ea13..7c63944a1e 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -25,8 +25,8 @@ use crate::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BigDecimal, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TransactionType, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, - VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, - WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + ValidateWatcherSpendInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, + WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest}; use async_std::prelude::FutureExt as AsyncStdFutureExt; use async_trait::async_trait; @@ -2623,7 +2623,11 @@ impl WatcherOps for TendermintCoin { unimplemented!(); } - fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!(); + } + + fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!(); } diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index 3cec34c148..f69c10592f 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -19,7 +19,7 @@ use crate::{big_decimal_from_sat_unsigned, utxo::sat_from_big_decimal, BalanceFu ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest}; -use crate::{MmCoinEnum, PaymentInstructionArgs, WatcherReward, WatcherRewardError}; +use crate::{MmCoinEnum, PaymentInstructionArgs, ValidateWatcherSpendInput, WatcherReward, WatcherRewardError}; use async_trait::async_trait; use bitcrypto::sha256; use common::executor::abortable_queue::AbortableQueue; @@ -469,7 +469,11 @@ impl WatcherOps for TendermintToken { unimplemented!(); } - fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!(); + } + + fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!(); } diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index d841bb83f5..0283a07f11 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -2,6 +2,7 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, RawTransactionRequest, SwapOps, TradeFee, TransactionEnum, TransactionFut}; +use crate::ValidateWatcherSpendInput; use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, @@ -270,7 +271,11 @@ impl WatcherOps for TestCoin { unimplemented!(); } - fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { + fn validate_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!() + } + + fn validate_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index a6b8eefca3..ef36cfc5d9 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -17,9 +17,9 @@ use crate::{BlockHeightAndTime, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBal SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradePreimageValue, TransactionFut, TransactionType, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, - ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, - WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawFut}; + ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, + WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, + WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut}; use common::executor::{AbortableSystem, AbortedError}; use common::log::warn; use derive_more::Display; @@ -1087,8 +1087,13 @@ impl WatcherOps for BchCoin { } #[inline] - fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { - utxo_common::validate_watcher_spend(self, tx) + fn validate_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + utxo_common::validate_payment_spend_or_refund(self, input) + } + + #[inline] + fn validate_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + utxo_common::validate_payment_spend_or_refund(self, input) } async fn watcher_search_for_swap_tx_spend( diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index b03d81507e..7ecf50892b 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -32,9 +32,9 @@ use crate::{eth, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinWithD SwapOps, TakerSwapMakerCoin, TradePreimageValue, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, - VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, - WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, - WithdrawSenderAddress}; + ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, + WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + WatcherValidateTakerFeeInput, WithdrawFut, WithdrawSenderAddress}; use common::executor::{AbortableSystem, AbortedError}; use crypto::Bip44Chain; use ethereum_types::H160; @@ -778,8 +778,13 @@ impl WatcherOps for QtumCoin { } #[inline] - fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { - utxo_common::validate_watcher_spend(self, tx) + fn validate_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + utxo_common::validate_payment_spend_or_refund(self, input) + } + + #[inline] + fn validate_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + utxo_common::validate_payment_spend_or_refund(self, input) } async fn watcher_search_for_swap_tx_spend( diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index cd1ef2ea0f..9d22eba2d1 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -22,10 +22,10 @@ use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, C TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, - ValidateOtherPubKeyErr, ValidatePaymentInput, VerificationError, VerificationResult, - WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, - WithdrawRequest}; + ValidateOtherPubKeyErr, ValidatePaymentInput, ValidateWatcherSpendInput, VerificationError, + VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, + WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest}; use async_trait::async_trait; use bitcrypto::dhash160; use chain::constants::SEQUENCE_FINAL; @@ -1518,7 +1518,11 @@ impl WatcherOps for SlpToken { unimplemented!(); } - fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!() + } + + fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 3e03e4d409..2fe04a6418 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -20,10 +20,10 @@ use crate::{CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, ConfirmPayment RewardTarget, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, TradePreimageValue, TransactionFut, TxFeeDetails, TxMarshalingErr, ValidateAddressResult, ValidateOtherPubKeyErr, ValidatePaymentFut, - ValidatePaymentInput, VerificationError, VerificationResult, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFrom, WithdrawResult, - WithdrawSenderAddress, EARLY_CONFIRMATION_ERR_LOG, INVALID_RECEIVER_ERR_LOG, INVALID_REFUND_TX_ERR_LOG, - INVALID_SCRIPT_ERR_LOG, INVALID_SENDER_ERR_LOG, OLD_TRANSACTION_ERR_LOG}; + ValidatePaymentInput, ValidateWatcherSpendInput, VerificationError, VerificationResult, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFrom, + WithdrawResult, WithdrawSenderAddress, EARLY_CONFIRMATION_ERR_LOG, INVALID_RECEIVER_ERR_LOG, + INVALID_REFUND_TX_ERR_LOG, INVALID_SCRIPT_ERR_LOG, INVALID_SENDER_ERR_LOG, OLD_TRANSACTION_ERR_LOG}; use crate::{MmCoinEnum, WatcherReward, WatcherRewardError}; pub use bitcrypto::{dhash160, sha256, ChecksumType}; use bitcrypto::{dhash256, ripemd160}; @@ -2081,30 +2081,31 @@ pub fn validate_taker_payment( ) } -pub fn validate_watcher_spend( +pub fn validate_payment_spend_or_refund( coin: &T, - tx: TransactionEnum, -) -> Result<(), MmError> { - let mut payment_spend_tx: UtxoTx = deserialize(tx.tx_hex().as_slice())?; + input: ValidateWatcherSpendInput, +) -> ValidatePaymentFut<()> { + let mut payment_spend_tx: UtxoTx = try_f!(deserialize(input.payment_tx.as_slice())); payment_spend_tx.tx_hash_algo = coin.as_ref().tx_hash_algo; - let my_address = coin.as_ref().derivation_method.single_addr_or_err()?; + let my_address = try_f!(coin.as_ref().derivation_method.single_addr_or_err()); let expected_script_pubkey = &output_script(my_address, ScriptType::P2PKH).to_bytes(); - let actual_script_pubkey = match payment_spend_tx.outputs.get(DEFAULT_SWAP_VOUT) { - Some(output) => &output.script_pubkey, - None => { - return MmError::err(ValidatePaymentError::WrongPaymentTx( - "Payment tx has no outputs".to_string(), + let output = try_f!(payment_spend_tx + .outputs + .get(DEFAULT_SWAP_VOUT) + .ok_or_else(|| ValidatePaymentError::WrongPaymentTx("Payment tx has no outputs".to_string(),))); + + if expected_script_pubkey != &output.script_pubkey { + return Box::new(futures01::future::err( + ValidatePaymentError::WrongPaymentTx(format!( + "Provided payment tx script pubkey doesn't match expected {:?} {:?}", + output.script_pubkey, expected_script_pubkey )) - }, - }; - if expected_script_pubkey != actual_script_pubkey { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Provided payment tx script pubkey doesn't match expected {:?} {:?}", - actual_script_pubkey, expected_script_pubkey - ))); + .into(), + )); } - Ok(()) + + Box::new(futures01::future::ok(())) } pub fn check_if_my_payment_sent( diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 39c4fd8b99..24ee50c35e 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -29,9 +29,9 @@ use crate::{CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinWithDeriva SendPaymentArgs, SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradePreimageValue, TransactionFut, TxMarshalingErr, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, - VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, - WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, - WithdrawSenderAddress}; + ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, + WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + WatcherValidateTakerFeeInput, WithdrawFut, WithdrawSenderAddress}; use common::executor::{AbortableSystem, AbortedError}; use crypto::Bip44Chain; use futures::{FutureExt, TryFutureExt}; @@ -543,8 +543,13 @@ impl WatcherOps for UtxoStandardCoin { } #[inline] - fn validate_watcher_spend(&self, tx: TransactionEnum) -> Result<(), MmError> { - utxo_common::validate_watcher_spend(self, tx) + fn validate_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + utxo_common::validate_payment_spend_or_refund(self, input) + } + + #[inline] + fn validate_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + utxo_common::validate_payment_spend_or_refund(self, input) } #[inline] diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 36c75a8df5..3d3e67c016 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -22,9 +22,10 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, Coi SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionEnum, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, - ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, - WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, WithdrawRequest}; + ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, VerificationError, + VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, + WithdrawRequest}; use crate::{Transaction, WithdrawError}; use async_trait::async_trait; use bitcrypto::dhash256; @@ -1593,7 +1594,11 @@ impl WatcherOps for ZCoin { unimplemented!(); } - fn validate_watcher_spend(&self, _tx: TransactionEnum) -> Result<(), MmError> { + fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!() + } + + fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index dadf9cb88e..a090fa69b1 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -18,7 +18,7 @@ use coins::lp_price::fetch_swap_coins_price; use coins::{lp_coinfind, CanRefundHtlc, CheckIfMyPaymentSentArgs, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, RefundPaymentArgs, SearchForSwapTxSpendInput, SendPaymentArgs, SpendPaymentArgs, TradeFee, - TradePreimageValue, ValidatePaymentInput, WaitForHTLCTxSpendArgs}; + TradePreimageValue, ValidatePaymentInput, ValidateWatcherSpendInput, WaitForHTLCTxSpendArgs}; use common::executor::Timer; use common::log::{debug, error, info, warn}; use common::{bits256, now_ms, now_sec, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; @@ -2442,8 +2442,19 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx)), Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx)), ) => { + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend_tx.tx_hex(), + maker_pub: other_maker_coin_htlc_pub.to_vec(), + swap_contract_address: maker_coin_swap_contract_address, + time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, + secret_hash: secret_hash.clone(), + amount: swap.maker_amount.to_decimal(), + watcher_reward: None, + }; swap.taker_coin - .validate_watcher_spend(maker_payment_spend_tx.clone()) + .validate_taker_payment_refund(validate_input) + .compat() + .await .map_err(|e| e.to_string())?; if saved.is_finished() { @@ -2505,8 +2516,20 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta }, (Some(FoundSwapTxSpend::Refunded(_)), Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) | (None, Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund_tx.tx_hex(), + maker_pub: other_maker_coin_htlc_pub.to_vec(), + swap_contract_address: taker_coin_swap_contract_address, + time_lock: taker_payment_lock, + secret_hash: secret_hash.clone(), + amount: swap.taker_amount.to_decimal(), + watcher_reward: None, + }; + swap.taker_coin - .validate_watcher_spend(taker_payment_refund_tx.clone()) + .validate_taker_payment_refund(validate_input) + .compat() + .await .map_err(|e| e.to_string())?; if saved.is_finished() { 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 65f4d05ecf..de4c2b0efa 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -4,11 +4,11 @@ use crate::{generate_utxo_coin_with_privkey, generate_utxo_coin_with_random_priv use coins::coin_errors::ValidatePaymentError; use coins::utxo::{dhash160, UtxoCommonOps}; use coins::{ConfirmPaymentInput, FoundSwapTxSpend, MarketCoinOps, MmCoin, MmCoinEnum, RefundPaymentArgs, RewardTarget, - SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SwapOps, WatcherOps, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, EARLY_CONFIRMATION_ERR_LOG, - 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}; + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SwapOps, + ValidateWatcherSpendInput, WatcherOps, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, + EARLY_CONFIRMATION_ERR_LOG, 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_u32, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::privkey::{key_pair_from_secret, key_pair_from_seed}; use futures01::Future; @@ -2343,7 +2343,208 @@ fn test_validate_watcher_refund_utxo() { .wait() .unwrap(); - let validate_watcher_refund = taker_coin.validate_watcher_spend(taker_payment_refund); + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: maker_pubkey.to_vec(), + swap_contract_address: None, + time_lock, + secret_hash: secret_hash.to_vec(), + amount: BigDecimal::from(10), + watcher_reward: None, + }; + + let validate_watcher_refund = taker_coin.validate_taker_payment_refund(validate_input).wait(); + assert!(validate_watcher_refund.is_ok()); +} + +#[test] +fn test_validate_watcher_refund_eth() { + let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run + + let taker_coin = eth_distributor(); + let taker_keypair = taker_coin.derive_htlc_key_pair(&[]); + let taker_pub = taker_keypair.public(); + + let maker_seed = get_passphrase!(".env.client", "BOB_PASSPHRASE").unwrap(); + let maker_keypair = key_pair_from_seed(&maker_seed).unwrap(); + let maker_pub = maker_keypair.public(); + + let time_lock_duration = get_payment_locktime(); + let wait_for_confirmation_until = wait_until_sec(time_lock_duration); + let time_lock = now_sec_u32() - 10; + let taker_amount = BigDecimal::from_str("0.01").unwrap(); + let maker_amount = BigDecimal::from_str("0.01").unwrap(); + let secret_hash = dhash160(&MakerSwap::generate_secret().unwrap()); + + let watcher_reward = Some( + block_on(taker_coin.get_taker_watcher_reward( + &MmCoinEnum::from(taker_coin.clone()), + Some(taker_amount.clone()), + Some(maker_amount), + None, + wait_for_confirmation_until, + )) + .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 confirm_payment_input = ConfirmPaymentInput { + payment_tx: taker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + }; + taker_coin.wait_for_confirmations(confirm_payment_input).wait().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 = taker_coin + .send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &taker_payment_refund_preimage.tx_hex(), + other_pubkey: taker_pub, + 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 validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: taker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: taker_amount, + watcher_reward, + }; + + let validate_watcher_refund = taker_coin.validate_taker_payment_refund(validate_input).wait(); + assert!(validate_watcher_refund.is_ok()); +} + +#[test] +fn test_validate_watcher_refund_erc20() { + let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run + + let seed = get_passphrase!(".env.client", "ALICE_PASSPHRASE").unwrap(); + let taker_coin = generate_jst_with_seed(&seed); + let taker_keypair = taker_coin.derive_htlc_key_pair(&[]); + let taker_pub = taker_keypair.public(); + + let maker_seed = get_passphrase!(".env.client", "BOB_PASSPHRASE").unwrap(); + let maker_keypair = key_pair_from_seed(&maker_seed).unwrap(); + let maker_pub = maker_keypair.public(); + + let time_lock_duration = get_payment_locktime(); + let wait_for_confirmation_until = wait_until_sec(time_lock_duration); + let time_lock = now_sec_u32() - 10; + + let secret_hash = dhash160(&MakerSwap::generate_secret().unwrap()); + + let taker_amount = BigDecimal::from_str("0.01").unwrap(); + let maker_amount = BigDecimal::from_str("0.01").unwrap(); + + let watcher_reward = Some( + block_on(taker_coin.get_taker_watcher_reward( + &MmCoinEnum::from(taker_coin.clone()), + Some(taker_amount.clone()), + Some(maker_amount), + None, + wait_for_confirmation_until, + )) + .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 confirm_payment_input = ConfirmPaymentInput { + payment_tx: taker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + }; + taker_coin.wait_for_confirmations(confirm_payment_input).wait().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 = taker_coin + .send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &taker_payment_refund_preimage.tx_hex(), + other_pubkey: taker_pub, + 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 validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: taker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: taker_amount, + watcher_reward, + }; + + let validate_watcher_refund = taker_coin.validate_taker_payment_refund(validate_input).wait(); assert!(validate_watcher_refund.is_ok()); } @@ -2411,7 +2612,17 @@ fn test_validate_watcher_spend_utxo() { .wait() .unwrap(); - let validate_watcher_spend = taker_coin.validate_watcher_spend(maker_payment_spend); + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: maker_pubkey.to_vec(), + swap_contract_address: None, + time_lock, + secret_hash: secret_hash.to_vec(), + amount: BigDecimal::from(10), + watcher_reward: None, + }; + + let validate_watcher_spend = taker_coin.validate_taker_payment_refund(validate_input).wait(); assert!(validate_watcher_spend.is_ok()); } From 4dd54592b12bf0db2f7130344fbcac552c4933bd Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Fri, 4 Aug 2023 20:46:57 +0300 Subject: [PATCH 27/53] validate maker payment spend validation on taker restart for eth --- mm2src/coins/eth.rs | 224 +++++++++++++++++- mm2src/coins/lightning.rs | 4 +- mm2src/coins/lp_coins.rs | 4 +- mm2src/coins/qrc20.rs | 4 +- mm2src/coins/solana.rs | 4 +- mm2src/coins/solana/spl.rs | 4 +- mm2src/coins/tendermint/tendermint_coin.rs | 4 +- mm2src/coins/tendermint/tendermint_token.rs | 4 +- mm2src/coins/test_coin.rs | 4 +- mm2src/coins/utxo/bch.rs | 4 +- mm2src/coins/utxo/qtum.rs | 4 +- mm2src/coins/utxo/slp.rs | 4 +- mm2src/coins/utxo/utxo_standard.rs | 4 +- mm2src/coins/z_coin.rs | 4 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 6 +- .../tests/docker_tests/docker_tests_common.rs | 2 +- .../tests/docker_tests/swap_watcher_tests.rs | 219 ++++++++++++++++- 17 files changed, 448 insertions(+), 55 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 940156e97f..54d22f9134 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -25,7 +25,7 @@ use crate::lp_price::get_base_price_in_rel; use crate::nft::nft_structs::{ContractType, ConvertChain, TransactionNftDetails, WithdrawErc1155, WithdrawErc721}; use crate::ValidateWatcherSpendInput; use async_trait::async_trait; -use bitcrypto::{keccak256, ripemd160, sha256}; +use bitcrypto::{dhash160, keccak256, ripemd160, sha256}; use common::custom_futures::repeatable::{Ready, Retry, RetryOnError}; use common::custom_futures::timeout::FutureTimerExt; use common::executor::{abortable_queue::AbortableQueue, AbortableSystem, AbortedError, Timer}; @@ -1462,11 +1462,11 @@ impl WatcherOps for EthCoin { // 1.Validate if taker fee is old } - fn validate_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { let watcher_reward = try_f!(input .watcher_reward .clone() - .ok_or_else(|| ValidatePaymentError::WatcherRewardError("WatcherRewardNotFound".to_string()))); + .ok_or_else(|| ValidatePaymentError::WatcherRewardError("Watcher reward not found".to_string()))); let expected_reward_amount = try_f!(wei_from_big_decimal(&watcher_reward.amount, self.decimals)); let expected_swap_contract_address = try_f!(input @@ -1507,7 +1507,7 @@ impl WatcherOps for EthCoin { Call(contract_address) => { if contract_address != expected_swap_contract_address { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment tx {:?} was sent to wrong address, expected {:?}", + "Refund tx {:?} was sent to wrong address, expected {:?}", contract_address, expected_swap_contract_address, ))); } @@ -1532,17 +1532,18 @@ impl WatcherOps for EthCoin { if swap_id_input != Token::FixedBytes(swap_id.clone()) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( "Refund tx invalid swap_id arg {:?}, expected {:?}", - swap_id_input, swap_id + swap_id_input, + Token::FixedBytes(swap_id.clone()) ))); } let hash_input = get_function_input_data(&decoded, function, 2) .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if hash_input != Token::FixedBytes(secret_hash.to_vec()) { + if hash_input != Token::FixedBytes(secret_hash.clone()) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( "Refund tx secret_hash arg {:?} is invalid, expected {:?}", hash_input, - Token::FixedBytes(secret_hash.to_vec()), + Token::FixedBytes(secret_hash), ))); } @@ -1571,7 +1572,8 @@ impl WatcherOps for EthCoin { if reward_target_input != Token::Uint(U256::from(watcher_reward.reward_target as u8)) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( "Refund tx reward target arg {:?} is invalid, expected {:?}", - reward_target_input, watcher_reward.reward_target as u8 + reward_target_input, + Token::Uint(U256::from(watcher_reward.reward_target as u8)) ))); } @@ -1580,7 +1582,8 @@ impl WatcherOps for EthCoin { if contract_reward_input != Token::Bool(watcher_reward.send_contract_reward_on_spend) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( "Refund tx sends_contract_reward_on_spend arg {:?} is invalid, expected {:?}", - contract_reward_input, watcher_reward.send_contract_reward_on_spend + contract_reward_input, + Token::Bool(watcher_reward.send_contract_reward_on_spend) ))); } @@ -1589,13 +1592,14 @@ impl WatcherOps for EthCoin { if reward_amount_input != Token::Uint(expected_reward_amount) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( "Refund tx watcher reward amount arg {:?} is invalid, expected {:?}", - reward_amount_input, expected_reward_amount + reward_amount_input, + Token::Uint(expected_reward_amount) ))); } if tx.value != U256::zero() { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment tx value arg {:?} is invalid, expected 0", + "Refund tx value arg {:?} is invalid, expected 0", tx.value ))); } @@ -1654,8 +1658,202 @@ impl WatcherOps for EthCoin { Box::new(fut.boxed().compat()) } - fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!() + fn taker_validates_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + let watcher_reward = try_f!(input + .watcher_reward + .clone() + .ok_or_else(|| ValidatePaymentError::WatcherRewardError("Watcher reward not found".to_string()))); + let expected_reward_amount = try_f!(wei_from_big_decimal(&watcher_reward.amount, self.decimals)); + + let expected_swap_contract_address = try_f!(input + .swap_contract_address + .try_to_address() + .map_to_mm(ValidatePaymentError::InvalidParameter)); + + let unsigned: UnverifiedTransaction = try_f!(rlp::decode(&input.payment_tx)); + let tx = + try_f!(SignedEthTx::new(unsigned) + .map_to_mm(|err| ValidatePaymentError::TxDeserializationError(err.to_string()))); + + let selfi = self.clone(); + let swap_id = selfi.etomic_swap_id(input.time_lock, &input.secret_hash); + let decimals = self.decimals; + let secret_hash = if input.secret_hash.len() == 32 { + ripemd160(&input.secret_hash).to_vec() + } else { + input.secret_hash.to_vec() + }; + + let maker_addr = + try_f!(addr_from_raw_pubkey(&input.maker_pub).map_to_mm(ValidatePaymentError::InvalidParameter)); + let trade_amount = try_f!(wei_from_big_decimal(&(input.amount), decimals)); + + let fut = async move { + let status = selfi + .payment_status(expected_swap_contract_address, Token::FixedBytes(swap_id.clone())) + .compat() + .await + .map_to_mm(ValidatePaymentError::Transport)?; + if status != U256::from(PaymentState::Spent as u8) { + return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( + "Payment state is not PAYMENT_STATE_SPENT, got {}", + status + ))); + } + + match tx.action { + Call(contract_address) => { + if contract_address != expected_swap_contract_address { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx {:?} was sent to wrong address, expected {:?}", + contract_address, expected_swap_contract_address, + ))); + } + }, + Create => { + return MmError::err(ValidatePaymentError::WrongPaymentTx( + "Tx action must be Call, found Create instead".to_string(), + )); + }, + }; + + let function_name = get_function_name("receiverSpend", true); + let function = SWAP_CONTRACT + .function(&function_name) + .map_to_mm(|err| ValidatePaymentError::InternalError(err.to_string()))?; + + let decoded = decode_contract_call(function, &tx.data) + .map_to_mm(|err| ValidatePaymentError::TxDeserializationError(err.to_string()))?; + + let swap_id_input = get_function_input_data(&decoded, function, 0) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if swap_id_input != Token::FixedBytes(swap_id.clone()) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx invalid swap_id arg {:?}, expected {:?}", + swap_id_input, swap_id + ))); + } + + let secret_input = get_function_input_data(&decoded, function, 2) + .map_to_mm(ValidatePaymentError::TxDeserializationError)? + .into_fixed_bytes() + .ok_or_else(|| ValidatePaymentError::WrongPaymentTx("Invalid type for secret argument".to_string()))?; + + let secret_input_hashed = dhash160(&secret_input); + if secret_input_hashed.to_vec() != secret_hash { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx secret arg {:?} is invalid", + secret_input_hashed, + ))); + } + + let sender_input = get_function_input_data(&decoded, function, 4) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if sender_input != Token::Address(maker_addr) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx sender arg {:?} is invalid, expected {:?}", + sender_input, + Token::Address(maker_addr) + ))); + } + + let receiver_input = get_function_input_data(&decoded, function, 5) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if receiver_input != Token::Address(selfi.my_address) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx receiver arg {:?} is invalid, expected {:?}", + receiver_input, + Token::Address(selfi.my_address) + ))); + } + + let reward_target_input = get_function_input_data(&decoded, function, 6) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if reward_target_input != Token::Uint(U256::from(watcher_reward.reward_target as u8)) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx reward target arg {:?} is invalid, expected {:?}", + reward_target_input, watcher_reward.reward_target as u8 + ))); + } + + let contract_reward_input = get_function_input_data(&decoded, function, 7) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if contract_reward_input != Token::Bool(watcher_reward.send_contract_reward_on_spend) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx sends_contract_reward_on_spend arg {:?} is invalid, expected {:?}", + contract_reward_input, watcher_reward.send_contract_reward_on_spend + ))); + } + + let reward_amount_input = get_function_input_data(&decoded, function, 8) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if reward_amount_input != Token::Uint(expected_reward_amount) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx watcher reward amount arg {:?} is invalid, expected {:?}", + reward_amount_input, expected_reward_amount + ))); + } + + if tx.value != U256::zero() { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx value arg {:?} is invalid, expected 0", + tx.value + ))); + } + + match &selfi.coin_type { + EthCoinType::Eth => { + let amount_input = get_function_input_data(&decoded, function, 1) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + let total_amount = trade_amount + expected_reward_amount; + if amount_input != Token::Uint(total_amount) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx amount arg {:?} is invalid, expected {:?}", + amount_input, + Token::Uint(total_amount), + ))); + } + + let token_address_input = get_function_input_data(&decoded, function, 3) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if token_address_input != Token::Address(Address::default()) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx token address arg {:?} is invalid, expected {:?}", + token_address_input, + Token::Address(Address::default()), + ))); + } + }, + EthCoinType::Erc20 { + platform: _, + token_addr, + } => { + let amount_input = get_function_input_data(&decoded, function, 1) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if amount_input != Token::Uint(trade_amount) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx amount arg {:?} is invalid, expected {:?}", + amount_input, + Token::Uint(trade_amount), + ))); + } + + let token_address_input = get_function_input_data(&decoded, function, 3) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if token_address_input != Token::Address(*token_addr) { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Payment spend tx token address arg {:?} is invalid, expected {:?}", + token_address_input, + Token::Address(*token_addr), + ))); + } + }, + } + + Ok(()) + }; + + Box::new(fut.boxed().compat()) } fn watcher_validate_taker_payment(&self, input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()> { diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 3bfdcf68f3..32d8823c01 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -990,11 +990,11 @@ impl WatcherOps for LightningCoin { unimplemented!(); } - fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } - fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!(); } diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 743736ac08..d1557f2160 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -979,9 +979,9 @@ pub trait WatcherOps { fn watcher_validate_taker_payment(&self, _input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()>; - fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()>; + fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()>; - fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()>; + fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()>; async fn watcher_search_for_swap_tx_spend( &self, diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index af9fb14a16..8272645678 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1135,11 +1135,11 @@ impl WatcherOps for Qrc20Coin { unimplemented!(); } - fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } - fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!(); } diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 8be0f7165b..10e93efb0b 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -639,11 +639,11 @@ impl WatcherOps for SolanaCoin { unimplemented!(); } - fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } - fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index c5136059d4..34e176f107 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -460,11 +460,11 @@ impl WatcherOps for SplToken { unimplemented!(); } - fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } - fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 7c63944a1e..f0dfe7b953 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -2623,11 +2623,11 @@ impl WatcherOps for TendermintCoin { unimplemented!(); } - fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!(); } - fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!(); } diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index f69c10592f..ee83d6e742 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -469,11 +469,11 @@ impl WatcherOps for TendermintToken { unimplemented!(); } - fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!(); } - fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!(); } diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 0283a07f11..0db9d339e0 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -271,11 +271,11 @@ impl WatcherOps for TestCoin { unimplemented!(); } - fn validate_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } - fn validate_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index ef36cfc5d9..b22f3e2974 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1087,12 +1087,12 @@ impl WatcherOps for BchCoin { } #[inline] - fn validate_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { utxo_common::validate_payment_spend_or_refund(self, input) } #[inline] - fn validate_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { utxo_common::validate_payment_spend_or_refund(self, input) } diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 7ecf50892b..0562d3bdf9 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -778,12 +778,12 @@ impl WatcherOps for QtumCoin { } #[inline] - fn validate_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { utxo_common::validate_payment_spend_or_refund(self, input) } #[inline] - fn validate_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { utxo_common::validate_payment_spend_or_refund(self, input) } diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index 9d22eba2d1..af0315c9f5 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1518,11 +1518,11 @@ impl WatcherOps for SlpToken { unimplemented!(); } - fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } - fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 24ee50c35e..4dff51b426 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -543,12 +543,12 @@ impl WatcherOps for UtxoStandardCoin { } #[inline] - fn validate_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { utxo_common::validate_payment_spend_or_refund(self, input) } #[inline] - fn validate_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { utxo_common::validate_payment_spend_or_refund(self, input) } diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 3d3e67c016..289cce1e63 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -1594,11 +1594,11 @@ impl WatcherOps for ZCoin { unimplemented!(); } - fn validate_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } - fn validate_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index a090fa69b1..6cca7701b3 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -2451,8 +2451,8 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta amount: swap.maker_amount.to_decimal(), watcher_reward: None, }; - swap.taker_coin - .validate_taker_payment_refund(validate_input) + swap.maker_coin + .taker_validates_maker_payment_spend(validate_input) .compat() .await .map_err(|e| e.to_string())?; @@ -2527,7 +2527,7 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta }; swap.taker_coin - .validate_taker_payment_refund(validate_input) + .taker_validates_taker_payment_refund(validate_input) .compat() .await .map_err(|e| e.to_string())?; 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 6c5db29d00..831b30c780 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs @@ -166,7 +166,7 @@ pub fn _fill_eth(to_addr: &str) { } // Generates an ethereum coin in the sepolia network with the given seed -pub fn _generate_eth_coin_with_seed(seed: &str) -> EthCoin { +pub fn generate_eth_coin_with_seed(seed: &str) -> EthCoin { let req = json!({ "method": "enable", "coin": "ETH", 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 de4c2b0efa..5903a639e9 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -32,6 +32,8 @@ use std::thread; use std::time::Duration; use uuid::Uuid; +use super::docker_tests_common::generate_eth_coin_with_seed; + #[derive(Debug, Clone)] struct BalanceResult { alice_acoin_balance_before: BigDecimal, @@ -2281,7 +2283,7 @@ fn test_watcher_validate_taker_payment_erc20() { } #[test] -fn test_validate_watcher_refund_utxo() { +fn test_taker_validates_taker_payment_refund_utxo() { let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run let time_lock_duration = get_payment_locktime(); let wait_for_confirmation_until = wait_until_sec(time_lock_duration); @@ -2353,12 +2355,12 @@ fn test_validate_watcher_refund_utxo() { watcher_reward: None, }; - let validate_watcher_refund = taker_coin.validate_taker_payment_refund(validate_input).wait(); + let validate_watcher_refund = taker_coin.taker_validates_taker_payment_refund(validate_input).wait(); assert!(validate_watcher_refund.is_ok()); } #[test] -fn test_validate_watcher_refund_eth() { +fn test_taker_validates_taker_payment_refund_eth() { let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run let taker_coin = eth_distributor(); @@ -2372,8 +2374,8 @@ fn test_validate_watcher_refund_eth() { let time_lock_duration = get_payment_locktime(); let wait_for_confirmation_until = wait_until_sec(time_lock_duration); let time_lock = now_sec_u32() - 10; - let taker_amount = BigDecimal::from_str("0.01").unwrap(); - let maker_amount = BigDecimal::from_str("0.01").unwrap(); + let taker_amount = BigDecimal::from_str("0.001").unwrap(); + let maker_amount = BigDecimal::from_str("0.001").unwrap(); let secret_hash = dhash160(&MakerSwap::generate_secret().unwrap()); let watcher_reward = Some( @@ -2447,12 +2449,12 @@ fn test_validate_watcher_refund_eth() { watcher_reward, }; - let validate_watcher_refund = taker_coin.validate_taker_payment_refund(validate_input).wait(); + let validate_watcher_refund = taker_coin.taker_validates_taker_payment_refund(validate_input).wait(); assert!(validate_watcher_refund.is_ok()); } #[test] -fn test_validate_watcher_refund_erc20() { +fn test_taker_validates_taker_payment_refund_erc20() { let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run let seed = get_passphrase!(".env.client", "ALICE_PASSPHRASE").unwrap(); @@ -2470,8 +2472,8 @@ fn test_validate_watcher_refund_erc20() { let secret_hash = dhash160(&MakerSwap::generate_secret().unwrap()); - let taker_amount = BigDecimal::from_str("0.01").unwrap(); - let maker_amount = BigDecimal::from_str("0.01").unwrap(); + let taker_amount = BigDecimal::from_str("0.001").unwrap(); + let maker_amount = BigDecimal::from_str("0.001").unwrap(); let watcher_reward = Some( block_on(taker_coin.get_taker_watcher_reward( @@ -2544,12 +2546,12 @@ fn test_validate_watcher_refund_erc20() { watcher_reward, }; - let validate_watcher_refund = taker_coin.validate_taker_payment_refund(validate_input).wait(); + let validate_watcher_refund = taker_coin.taker_validates_taker_payment_refund(validate_input).wait(); assert!(validate_watcher_refund.is_ok()); } #[test] -fn test_validate_watcher_spend_utxo() { +fn test_taker_validates_maker_payment_spend_utxo() { let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run let time_lock_duration = get_payment_locktime(); let wait_for_confirmation_until = wait_until_sec(time_lock_duration); @@ -2622,7 +2624,200 @@ fn test_validate_watcher_spend_utxo() { watcher_reward: None, }; - let validate_watcher_spend = taker_coin.validate_taker_payment_refund(validate_input).wait(); + let validate_watcher_spend = taker_coin.taker_validates_taker_payment_refund(validate_input).wait(); + assert!(validate_watcher_spend.is_ok()); +} + +#[test] +fn test_taker_validates_maker_payment_spend_eth() { + let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run + + let taker_coin = eth_distributor(); + let taker_keypair = taker_coin.derive_htlc_key_pair(&[]); + let taker_pub = taker_keypair.public(); + + let maker_seed = get_passphrase!(".env.client", "BOB_PASSPHRASE").unwrap(); + let maker_coin = generate_eth_coin_with_seed(&maker_seed); + let maker_keypair = key_pair_from_seed(&maker_seed).unwrap(); + let maker_pub = maker_keypair.public(); + + let time_lock_duration = get_payment_locktime(); + let wait_for_confirmation_until = wait_until_sec(time_lock_duration); + let time_lock = wait_for_confirmation_until as u32; + let taker_amount = BigDecimal::from_str("0.001").unwrap(); + let maker_amount = BigDecimal::from_str("0.001").unwrap(); + + let secret = MakerSwap::generate_secret().unwrap(); + let secret_hash = dhash160(&secret); + + let watcher_reward = Some( + block_on(maker_coin.get_taker_watcher_reward( + &MmCoinEnum::from(maker_coin.clone()), + Some(taker_amount), + Some(maker_amount.clone()), + None, + wait_for_confirmation_until, + )) + .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(); + + 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(); + + 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 = 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(); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: maker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: maker_amount, + watcher_reward, + }; + + let validate_watcher_spend = taker_coin.taker_validates_maker_payment_spend(validate_input).wait(); + assert!(validate_watcher_spend.is_ok()); +} + +#[test] +fn test_taker_validates_maker_payment_spend_erc20() { + let timeout = wait_until_sec(120); // timeout if test takes more than 120 seconds to run + + let taker_seed = get_passphrase!(".env.client", "ALICE_PASSPHRASE").unwrap(); + let taker_coin = generate_jst_with_seed(&taker_seed); + let taker_keypair = taker_coin.derive_htlc_key_pair(&[]); + let taker_pub = taker_keypair.public(); + + let maker_seed = get_passphrase!(".env.client", "BOB_PASSPHRASE").unwrap(); + let maker_coin = generate_jst_with_seed(&maker_seed); + let maker_keypair = key_pair_from_seed(&maker_seed).unwrap(); + let maker_pub = maker_keypair.public(); + + let time_lock_duration = get_payment_locktime(); + let wait_for_confirmation_until = wait_until_sec(time_lock_duration); + let time_lock = wait_for_confirmation_until as u32; + let taker_amount = BigDecimal::from_str("0.001").unwrap(); + let maker_amount = BigDecimal::from_str("0.001").unwrap(); + + let secret = MakerSwap::generate_secret().unwrap(); + let secret_hash = dhash160(&secret); + + let watcher_reward = Some( + block_on(maker_coin.get_taker_watcher_reward( + &MmCoinEnum::from(maker_coin.clone()), + Some(taker_amount), + Some(maker_amount.clone()), + None, + wait_for_confirmation_until, + )) + .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(); + + 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(); + + 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 = 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(); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: maker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: maker_amount, + watcher_reward, + }; + + let validate_watcher_spend = taker_coin.taker_validates_maker_payment_spend(validate_input).wait(); assert!(validate_watcher_spend.is_ok()); } From f15afa1cf6ab517588c91c57a360c4d5da7ea055 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Sat, 5 Aug 2023 18:30:25 +0300 Subject: [PATCH 28/53] activate watchtowers for UTXO --- mm2src/coins/utxo/bch.rs | 2 +- mm2src/coins/utxo/qtum.rs | 2 +- mm2src/coins/utxo/utxo_standard.rs | 2 +- mm2src/mm2_core/src/mm_ctx.rs | 5 +- .../tests/docker_tests/swap_watcher_tests.rs | 137 +++++------------- 5 files changed, 40 insertions(+), 108 deletions(-) diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index b22f3e2974..666d5917b8 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1004,7 +1004,7 @@ impl SwapOps for BchCoin { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } - fn is_supported_by_watchers(&self) -> bool { std::env::var("USE_WATCHERS").is_ok() } + fn is_supported_by_watchers(&self) -> bool { true } } #[async_trait] diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 0562d3bdf9..bd7ff8c738 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -699,7 +699,7 @@ impl SwapOps for QtumCoin { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } - fn is_supported_by_watchers(&self) -> bool { std::env::var("USE_WATCHERS").is_ok() } + fn is_supported_by_watchers(&self) -> bool { true } } #[async_trait] diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 4dff51b426..9ac6a2af20 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -464,7 +464,7 @@ impl SwapOps for UtxoStandardCoin { MmError::err(ValidateInstructionsErr::UnsupportedCoin(self.ticker().to_string())) } - fn is_supported_by_watchers(&self) -> bool { std::env::var("USE_WATCHERS").is_ok() } + fn is_supported_by_watchers(&self) -> bool { true } } #[async_trait] diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index 493ca7c784..533f5f64f7 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -269,10 +269,7 @@ impl MmCtx { pub fn is_watcher(&self) -> bool { self.conf["is_watcher"].as_bool().unwrap_or_default() } - pub fn use_watchers(&self) -> bool { - std::env::var("USE_WATCHERS").is_ok() - //self.conf["use_watchers"].as_bool().unwrap_or(true) - } + pub fn use_watchers(&self) -> bool { self.conf["use_watchers"].as_bool().unwrap_or(true) } pub fn netid(&self) -> u16 { let netid = self.conf["netid"].as_u64().unwrap_or(0); 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 5903a639e9..102bd38bc2 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -364,11 +364,8 @@ fn run_watcher_node( #[test] fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fail_at_maker_payment_spend() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ - ("USE_WATCHERS", ""), - ("TAKER_FAIL_AT", "maker_payment_spend"), - ]); - let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", "")], &[&mm_alice.ip.to_string()]); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[("TAKER_FAIL_AT", "maker_payment_spend")]); + let mut mm_bob = run_maker_node(&coins, &[], &[&mm_alice.ip.to_string()]); let watcher_conf = WatcherConf { wait_taker_payment: 0., @@ -376,12 +373,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai refund_start_factor: 1.5, search_interval: 1.0, }; - let mut mm_watcher = run_watcher_node( - &coins, - &[("USE_WATCHERS", "")], - &[&mm_alice.ip.to_string()], - watcher_conf, - ); + let mut mm_watcher = run_watcher_node(&coins, &[], &[&mm_alice.ip.to_string()], watcher_conf); let uuids = block_on(start_swaps( &mut mm_bob, @@ -397,18 +389,10 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); - restart_taker_and_wait_until( - &alice_conf, - &[("USE_WATCHERS", "")], - &format!("[swap uuid={}] Finished", &uuids[0]), - ); + restart_taker_and_wait_until(&alice_conf, &[], &format!("[swap uuid={}] Finished", &uuids[0])); block_on(mm_alice.stop()).unwrap(); - let mm_alice = restart_taker_and_wait_until( - &alice_conf, - &[("USE_WATCHERS", "")], - &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), - ); + let mm_alice = restart_taker_and_wait_until(&alice_conf, &[], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0])); let expected_events = [ "Started", "Negotiated", @@ -430,11 +414,9 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai #[test] fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_panic_at_wait_for_taker_payment_spend() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ - ("USE_WATCHERS", ""), - ("TAKER_FAIL_AT", "wait_for_taker_payment_spend_panic"), - ]); - let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", "")], &[&mm_alice.ip.to_string()]); + let (mut mm_alice, mut alice_conf) = + run_taker_node(&coins, &[("TAKER_FAIL_AT", "wait_for_taker_payment_spend_panic")]); + let mut mm_bob = run_maker_node(&coins, &[], &[&mm_alice.ip.to_string()]); let watcher_conf = WatcherConf { wait_taker_payment: 0., @@ -442,12 +424,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan refund_start_factor: 1.5, search_interval: 1.0, }; - let mut mm_watcher = run_watcher_node( - &coins, - &[("USE_WATCHERS", "")], - &[&mm_alice.ip.to_string()], - watcher_conf, - ); + let mut mm_watcher = run_watcher_node(&coins, &[], &[&mm_alice.ip.to_string()], watcher_conf); let uuids = block_on(start_swaps( &mut mm_bob, @@ -463,18 +440,10 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan block_on(mm_bob.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); - restart_taker_and_wait_until( - &alice_conf, - &[("USE_WATCHERS", "")], - &format!("[swap uuid={}] Finished", &uuids[0]), - ); + restart_taker_and_wait_until(&alice_conf, &[], &format!("[swap uuid={}] Finished", &uuids[0])); block_on(mm_alice.stop()).unwrap(); - let mm_alice = restart_taker_and_wait_until( - &alice_conf, - &[("USE_WATCHERS", "")], - &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), - ); + let mm_alice = restart_taker_and_wait_until(&alice_conf, &[], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0])); let expected_events = [ "Started", @@ -497,13 +466,10 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fail_at_taker_payment_refund() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ - ("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", ""), ("TAKER_FAIL_AT", "taker_payment_refund"), ]); - let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &[&mm_alice - .ip - .to_string()]); + let mut mm_bob = run_maker_node(&coins, &[("USE_TEST_LOCKTIME", "")], &[&mm_alice.ip.to_string()]); let watcher_conf = WatcherConf { wait_taker_payment: 0., @@ -513,7 +479,7 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa }; let mut mm_watcher = run_watcher_node( &coins, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + &[("USE_TEST_LOCKTIME", "")], &[&mm_alice.ip.to_string()], watcher_conf, ); @@ -536,14 +502,14 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa restart_taker_and_wait_until( &alice_conf, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + &[("USE_TEST_LOCKTIME", "")], &format!("[swap uuid={}] Finished", &uuids[0]), ); block_on(mm_alice.stop()).unwrap(); let mm_alice = restart_taker_and_wait_until( &alice_conf, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + &[("USE_TEST_LOCKTIME", "")], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), ); @@ -571,13 +537,10 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fa fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_panic_at_taker_payment_refund() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ - ("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", ""), ("TAKER_FAIL_AT", "taker_payment_refund_panic"), ]); - let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &[&mm_alice - .ip - .to_string()]); + let mut mm_bob = run_maker_node(&coins, &[("USE_TEST_LOCKTIME", "")], &[&mm_alice.ip.to_string()]); let watcher_conf = WatcherConf { wait_taker_payment: 0., @@ -587,7 +550,7 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa }; let mut mm_watcher = run_watcher_node( &coins, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + &[("USE_TEST_LOCKTIME", "")], &[&mm_alice.ip.to_string()], watcher_conf, ); @@ -610,14 +573,14 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa restart_taker_and_wait_until( &alice_conf, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + &[("USE_TEST_LOCKTIME", "")], &format!("[swap uuid={}] Finished", &uuids[0]), ); block_on(mm_alice.stop()).unwrap(); let mm_alice = restart_taker_and_wait_until( &alice_conf, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + &[("USE_TEST_LOCKTIME", "")], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), ); @@ -644,13 +607,10 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa fn test_taker_adds_watcher_refund_not_found_event() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ - ("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", ""), ("TAKER_FAIL_AT", "taker_payment_refund"), ]); - let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], &[&mm_alice - .ip - .to_string()]); + let mut mm_bob = run_maker_node(&coins, &[("USE_TEST_LOCKTIME", "")], &[&mm_alice.ip.to_string()]); let uuids = block_on(start_swaps( &mut mm_bob, @@ -668,14 +628,14 @@ fn test_taker_adds_watcher_refund_not_found_event() { restart_taker_and_wait_until( &alice_conf, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + &[("USE_TEST_LOCKTIME", "")], &format!("[swap uuid={}] Finished", &uuids[0]), ); block_on(mm_alice.stop()).unwrap(); let mm_alice = restart_taker_and_wait_until( &alice_conf, - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + &[("USE_TEST_LOCKTIME", "")], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), ); @@ -702,8 +662,8 @@ fn test_taker_adds_watcher_refund_not_found_event() { #[test] fn test_taker_completes_swap_after_restart() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[("USE_WATCHERS", "")]); - let mut mm_bob = run_maker_node(&coins, &[("USE_WATCHERS", "")], &[&mm_alice.ip.to_string()]); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[]); + let mut mm_bob = run_maker_node(&coins, &[], &[&mm_alice.ip.to_string()]); let uuids = block_on(start_swaps( &mut mm_bob, @@ -722,7 +682,7 @@ fn test_taker_completes_swap_after_restart() { alice_conf.conf, alice_conf.rpc_password.clone(), None, - &[("USE_WATCHERS", "")], + &[], )) .unwrap(); @@ -752,7 +712,7 @@ fn test_watcher_spends_maker_payment_utxo_utxo() { 25., 25., 2., - &[("USE_WATCHERS", "")], + &[], SwapFlow::WatcherSpendsMakerPayment, &alice_privkey, &bob_privkey, @@ -792,7 +752,7 @@ fn test_watcher_spends_maker_payment_utxo_eth() { 0.01, 0.01, 1., - &[("USE_WATCHERS", ""), ("USE_WATCHER_REWARD", "")], + &[("USE_WATCHER_REWARD", "")], SwapFlow::WatcherSpendsMakerPayment, alice_privkey, bob_privkey, @@ -825,11 +785,7 @@ fn test_watcher_spends_maker_payment_eth_utxo() { 100., 100., 0.01, - &[ - ("USE_WATCHERS", ""), - ("TEST_COIN_PRICE", "0.01"), - ("USE_WATCHER_REWARD", ""), - ], + &[("TEST_COIN_PRICE", "0.01"), ("USE_WATCHER_REWARD", "")], SwapFlow::WatcherSpendsMakerPayment, alice_privkey, bob_privkey, @@ -875,11 +831,7 @@ fn test_watcher_spends_maker_payment_eth_erc20() { 100., 100., 0.01, - &[ - ("USE_WATCHERS", ""), - ("TEST_COIN_PRICE", "0.01"), - ("USE_WATCHER_REWARD", ""), - ], + &[("TEST_COIN_PRICE", "0.01"), ("USE_WATCHER_REWARD", "")], SwapFlow::WatcherSpendsMakerPayment, alice_privkey, bob_privkey, @@ -912,7 +864,7 @@ fn test_watcher_spends_maker_payment_erc20_eth() { 0.01, 0.01, 1., - &[("USE_WATCHERS", ""), ("USE_WATCHER_REWARD", "")], + &[("USE_WATCHER_REWARD", "")], SwapFlow::WatcherSpendsMakerPayment, alice_privkey, bob_privkey, @@ -945,11 +897,7 @@ fn test_watcher_spends_maker_payment_utxo_erc20() { 1., 1., 1., - &[ - ("USE_WATCHERS", ""), - ("TEST_COIN_PRICE", "0.01"), - ("USE_WATCHER_REWARD", ""), - ], + &[("TEST_COIN_PRICE", "0.01"), ("USE_WATCHER_REWARD", "")], SwapFlow::WatcherSpendsMakerPayment, alice_privkey, bob_privkey, @@ -982,11 +930,7 @@ fn test_watcher_spends_maker_payment_erc20_utxo() { 1., 1., 1., - &[ - ("USE_WATCHERS", ""), - ("TEST_COIN_PRICE", "0.01"), - ("USE_WATCHER_REWARD", ""), - ], + &[("TEST_COIN_PRICE", "0.01"), ("USE_WATCHER_REWARD", "")], SwapFlow::WatcherSpendsMakerPayment, alice_privkey, bob_privkey, @@ -1038,7 +982,7 @@ fn test_watcher_refunds_taker_payment_utxo() { 25., 25., 2., - &[("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", "")], + &[("USE_TEST_LOCKTIME", "")], SwapFlow::WatcherRefundsTakerPayment, alice_privkey, bob_privkey, @@ -1064,11 +1008,7 @@ fn test_watcher_refunds_taker_payment_eth() { 0.01, 0.01, 1., - &[ - ("USE_WATCHERS", ""), - ("USE_TEST_LOCKTIME", ""), - ("USE_WATCHER_REWARD", ""), - ], + &[("USE_TEST_LOCKTIME", ""), ("USE_WATCHER_REWARD", "")], SwapFlow::WatcherRefundsTakerPayment, alice_privkey, bob_privkey, @@ -1095,7 +1035,6 @@ fn test_watcher_refunds_taker_payment_erc20() { 100., 0.01, &[ - ("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", ""), ("TEST_COIN_PRICE", "0.01"), ("USE_WATCHER_REWARD", ""), @@ -1127,7 +1066,7 @@ fn test_watcher_waits_for_taker_utxo() { 25., 25., 2., - &[("USE_WATCHERS", "")], + &[], SwapFlow::TakerSpendsMakerPayment, alice_privkey, bob_privkey, @@ -1147,11 +1086,7 @@ fn test_watcher_waits_for_taker_eth() { 100., 100., 0.01, - &[ - ("USE_WATCHERS", ""), - ("TEST_COIN_PRICE", "0.01"), - ("USE_WATCHER_REWARD", ""), - ], + &[("TEST_COIN_PRICE", "0.01"), ("USE_WATCHER_REWARD", "")], SwapFlow::TakerSpendsMakerPayment, alice_privkey, bob_privkey, From 07b8947abe7edc2ca7c3d62f81dbe1be5a5a2771 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Mon, 7 Aug 2023 16:11:46 +0300 Subject: [PATCH 29/53] change structure of check_watcher_payments --- mm2src/mm2_main/src/lp_swap.rs | 1 + mm2src/mm2_main/src/lp_swap/saved_swap.rs | 7 + mm2src/mm2_main/src/lp_swap/taker_swap.rs | 438 ++++++++++++++-------- 3 files changed, 286 insertions(+), 160 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 181232f94f..e245bdb401 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -1182,6 +1182,7 @@ pub async fn swap_kick_starts(ctx: MmArc) -> Result, String> { if swap.is_finished_and_success() || (swap.is_finished() && (!swap.watcher_message_sent() + || swap.refund_finished() || swap.refunded_by_watcher() || swap.watcher_spend_or_refund_not_found())) { diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index 307babd05f..1827b87f0c 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -73,6 +73,13 @@ impl SavedSwap { } } + pub fn refund_finished(&self) -> bool { + match &self { + SavedSwap::Taker(taker_swap) => taker_swap.refund_finished(), + _ => false, + } + } + pub fn watcher_spend_or_refund_not_found(&self) -> bool { match &self { SavedSwap::Taker(taker_swap) => taker_swap diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 6cca7701b3..51bcf747f3 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -18,7 +18,8 @@ use coins::lp_price::fetch_swap_coins_price; use coins::{lp_coinfind, CanRefundHtlc, CheckIfMyPaymentSentArgs, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, RefundPaymentArgs, SearchForSwapTxSpendInput, SendPaymentArgs, SpendPaymentArgs, TradeFee, - TradePreimageValue, ValidatePaymentInput, ValidateWatcherSpendInput, WaitForHTLCTxSpendArgs}; + TradePreimageValue, TransactionEnum, ValidatePaymentInput, ValidateWatcherSpendInput, + WaitForHTLCTxSpendArgs}; use common::executor::Timer; use common::log::{debug, error, info, warn}; use common::{bits256, now_ms, now_sec, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; @@ -316,12 +317,30 @@ impl TakerSavedSwap { .any(|e| matches!(e.event, TakerSwapEvent::WatcherMessageSent(_, _))) } + pub fn refund_finished(&self) -> bool { + self.events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefundFinished)) + } + + pub fn refunded(&self) -> bool { + self.events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefunded(_))) + } + pub fn taker_payment_spent(&self) -> bool { self.events .iter() .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentSpent(_))) } + pub fn maker_payment_spent(&self) -> bool { + self.events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::MakerPaymentSpent(_))) + } + pub fn maker_payment_spent_by_watcher(&self) -> bool { self.events .iter() @@ -2373,8 +2392,8 @@ pub struct TakerSwapPreparedParams { maker_payment_spend_trade_fee: TradeFee, } -pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: TakerSavedSwap) -> Result { - if !saved.watcher_message_sent() { +pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, saved: TakerSavedSwap) -> Result { + if !saved.watcher_message_sent() || saved.refunded() || saved.maker_payment_spent() { return Ok(false); } @@ -2383,11 +2402,6 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta let maker_coin_start_block = swap.r().data.maker_coin_start_block; let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); - let taker_payment = match &swap.r().taker_payment { - Some(tx) => tx.tx_hex.0.clone(), - None => return ERR!("No info about taker payment, swap is not recoverable"), - }; - let maker_payment = match &swap.r().maker_payment { Some(tx) => tx.tx_hex.0.clone(), None => return ERR!("No info about maker payment, swap is not recoverable"), @@ -2395,15 +2409,6 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta let unique_data = swap.unique_swap_data(); let watcher_reward = swap.r().watcher_reward; - let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; - let taker_coin_start_block = swap.r().data.taker_coin_start_block; - let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - - let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { - Ok(_) => swap.r().data.started_at as u32, - Err(_) => swap.r().data.taker_payment_lock as u32, - }; - let search_input = SearchForSwapTxSpendInput { time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, other_pub: other_maker_coin_htlc_pub.as_slice(), @@ -2415,169 +2420,282 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, mut saved: Ta watcher_reward, }; - let maker_payment_spend_tx = match swap.maker_coin.search_for_swap_tx_spend_other(search_input).await { - Ok(found_swap) => found_swap, - Err(e) => { - return ERR!("Error {} when trying to find maker payment spend", e); + match swap.maker_coin.search_for_swap_tx_spend_other(search_input).await { + Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { + save_swap_as_successful(swap, ctx, saved, maker_payment_spend_tx).await }, - }; + Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => { + let taker_payment = match &swap.r().taker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about taker payment, swap is not recoverable"), + }; - let search_input = SearchForSwapTxSpendInput { - time_lock: taker_payment_lock, - other_pub: other_taker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &taker_payment, - search_from_block: taker_coin_start_block, - swap_contract_address: &taker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }; - let taker_payment_spend_tx = match swap.taker_coin.search_for_swap_tx_spend_my(search_input).await { - Ok(found_swap) => found_swap, - Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), - }; + let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; + let taker_coin_start_block = swap.r().data.taker_coin_start_block; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - match (maker_payment_spend_tx, taker_payment_spend_tx) { - ( - Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx)), - Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx)), - ) => { - let validate_input = ValidateWatcherSpendInput { - payment_tx: maker_payment_spend_tx.tx_hex(), - maker_pub: other_maker_coin_htlc_pub.to_vec(), - swap_contract_address: maker_coin_swap_contract_address, - time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, - secret_hash: secret_hash.clone(), - amount: swap.maker_amount.to_decimal(), - watcher_reward: None, + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, }; - swap.maker_coin - .taker_validates_maker_payment_spend(validate_input) - .compat() - .await - .map_err(|e| e.to_string())?; - if saved.is_finished() { - saved.events.pop(); - } + let secret_hash = swap.r().secret_hash.0.clone(); + let unique_data = swap.unique_swap_data(); + let watcher_reward = swap.r().watcher_reward; - if !saved.taker_payment_spent() { - let tx_hash = taker_payment_spend_tx.tx_hash(); - info!("Taker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), - tx_hash, - }; - let secret = match swap - .taker_coin - .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) - .await - { - Ok(bytes) => H256Json::from(bytes.as_slice()), - Err(_) => { - return ERR!("Could not extract secret from taker payment spend transaction"); - }, - }; + let search_input = SearchForSwapTxSpendInput { + time_lock: taker_payment_lock, + other_pub: other_taker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &taker_payment, + search_from_block: taker_coin_start_block, + swap_contract_address: &taker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }; + match swap.taker_coin.search_for_swap_tx_spend_my(search_input).await { + Ok(Some(FoundSwapTxSpend::Spent(payment_spend_tx))) => ERR!("Maker has cheated by both spending the taker payment with transaction {:#?}, and refunding the maker payment with transaction {:#?}", payment_spend_tx.tx_hash(), maker_payment_refund_tx.tx_hash()), + Ok(Some(FoundSwapTxSpend::Refunded(payment_refund_tx))) => save_swap_as_refunded(swap, ctx, saved, payment_refund_tx).await, + Ok(None) => prepare_for_kickstart(ctx, saved).await, + Err(e) => ERR!("Error {} when trying to find taker payment spend", e), + } + }, + Ok(None) => { + let taker_payment = match &swap.r().taker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about taker payment, swap is not recoverable"), + }; - let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { - transaction: tx_ident, - secret, - }); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - } + let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; + let taker_coin_start_block = swap.r().data.taker_coin_start_block; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - let tx_hash = maker_payment_spend_tx.tx_hash(); - info!("Watcher maker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), - tx_hash, + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, }; - let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, + let secret_hash = swap.r().secret_hash.0.clone(); + let unique_data = swap.unique_swap_data(); + let watcher_reward = swap.r().watcher_reward; + + let search_input = SearchForSwapTxSpendInput { + time_lock: taker_payment_lock, + other_pub: other_taker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &taker_payment, + search_from_block: taker_coin_start_block, + swap_contract_address: &taker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, }; - saved.events.push(to_save); + match swap.taker_coin.search_for_swap_tx_spend_my(search_input).await { + Ok(Some(FoundSwapTxSpend::Spent(_))) => prepare_for_kickstart(ctx, saved).await, + Ok(Some(FoundSwapTxSpend::Refunded(payment_refund_tx))) => { + save_swap_as_refunded(swap, ctx, saved, payment_refund_tx).await + }, + Ok(None) => prepare_for_kickstart(ctx, saved).await, + Err(e) => ERR!("Error {} when trying to find taker payment spend", e), + } + }, + Err(e) => ERR!("Error {} when trying to find maker payment spend", e), + } +} - let mut success_events: Vec = saved.events.iter().map(|e| e.event.to_str()).collect(); - success_events.push("Finished".into()); - saved.success_events = success_events; +pub async fn save_swap_as_successful( + swap: &TakerSwap, + ctx: &MmArc, + mut saved: TakerSavedSwap, + maker_payment_spend_tx: TransactionEnum, +) -> Result { + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let secret_hash = swap.r().secret_hash.0.clone(); + let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); - let new_swap = SavedSwap::Taker(saved); - try_s!(new_swap.save_to_db(ctx).await); + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend_tx.tx_hex(), + maker_pub: other_maker_coin_htlc_pub.to_vec(), + swap_contract_address: maker_coin_swap_contract_address, + time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, + secret_hash: secret_hash.clone(), + amount: swap.maker_amount.to_decimal(), + watcher_reward: None, + }; + swap.maker_coin + .taker_validates_maker_payment_spend(validate_input) + .compat() + .await + .map_err(|e| e.to_string())?; - info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); - Ok(true) - }, - (Some(FoundSwapTxSpend::Refunded(_)), Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) - | (None, Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { - let validate_input = ValidateWatcherSpendInput { - payment_tx: taker_payment_refund_tx.tx_hex(), - maker_pub: other_maker_coin_htlc_pub.to_vec(), - swap_contract_address: taker_coin_swap_contract_address, - time_lock: taker_payment_lock, - secret_hash: secret_hash.clone(), - amount: swap.taker_amount.to_decimal(), - watcher_reward: None, - }; + if saved.is_finished() { + saved.events.pop(); + } - swap.taker_coin - .taker_validates_taker_payment_refund(validate_input) - .compat() - .await - .map_err(|e| e.to_string())?; + if !saved.taker_payment_spent() { + let taker_payment = match &swap.r().taker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about taker payment, swap is not recoverable"), + }; - if saved.is_finished() { - saved.events.pop(); - } + let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; + let taker_coin_start_block = swap.r().data.taker_coin_start_block; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - let tx_hash = taker_payment_refund_tx.tx_hash(); - info!("Taker refund tx hash {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(taker_payment_refund_tx.tx_hex()), - tx_hash, - }; + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, + }; - let event = TakerSwapEvent::TakerPaymentRefundedByWatcher(Some(tx_ident)); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); + let secret_hash = swap.r().secret_hash.0.clone(); + let unique_data = swap.unique_swap_data(); + let watcher_reward = swap.r().watcher_reward; - let new_swap = SavedSwap::Taker(saved); - try_s!(new_swap.save_to_db(ctx).await); + let search_input = SearchForSwapTxSpendInput { + time_lock: taker_payment_lock, + other_pub: other_taker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &taker_payment, + search_from_block: taker_coin_start_block, + swap_contract_address: &taker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }; + let taker_payment_spend_tx = match swap.taker_coin.search_for_swap_tx_spend_my(search_input).await { + Ok(Some(FoundSwapTxSpend::Spent(payment_spend_tx))) => payment_spend_tx, + Ok(Some(FoundSwapTxSpend::Refunded(payment_refund_tx))) => { + return ERR!( + "A refund transaction {:#?} is found while spend was expected", + payment_refund_tx.tx_hash() + ) + }, + Ok(None) => return ERR!("Taker payment spend could not be found while maker payment is already spent"), + Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), + }; - info!("Taker payment is refunded by the watcher"); - Ok(true) - }, - (Some(_), None) => { - ERR!("Could not find taker payment spend, while the maker payment is already spent or refunded") - }, - (None, None) => { - if saved.contains_failure() { - if saved.is_finished() { - saved.events.pop(); - } - let event = TakerSwapEvent::WatcherSpendOrRefundNotFound; - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - let new_swap = SavedSwap::Taker(saved); - try_s!(new_swap.save_to_db(ctx).await); - Ok(true) - } else { - Ok(false) - } - }, - _ => Ok(false), + let tx_hash = taker_payment_spend_tx.tx_hash(); + info!("Taker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), + tx_hash, + }; + let secret = match swap + .taker_coin + .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) + .await + { + Ok(bytes) => H256Json::from(bytes.as_slice()), + Err(_) => { + return ERR!("Could not extract secret from taker payment spend transaction"); + }, + }; + + let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { + transaction: tx_ident, + secret, + }); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + } + + let tx_hash = maker_payment_spend_tx.tx_hash(); + info!("Watcher maker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + + let mut success_events: Vec = saved.events.iter().map(|e| e.event.to_str()).collect(); + success_events.push("Finished".into()); + saved.success_events = success_events; + + let new_swap = SavedSwap::Taker(saved); + try_s!(new_swap.save_to_db(ctx).await); + + info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); + Ok(true) +} + +pub async fn save_swap_as_refunded( + swap: &TakerSwap, + ctx: &MmArc, + mut saved: TakerSavedSwap, + taker_payment_refund_tx: TransactionEnum, +) -> Result { + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, + }; + let secret_hash = swap.r().secret_hash.0.clone(); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund_tx.tx_hex(), + maker_pub: other_maker_coin_htlc_pub.to_vec(), + swap_contract_address: taker_coin_swap_contract_address, + time_lock: taker_payment_lock, + secret_hash: secret_hash.clone(), + amount: swap.taker_amount.to_decimal(), + watcher_reward: None, + }; + + swap.taker_coin + .taker_validates_taker_payment_refund(validate_input) + .compat() + .await + .map_err(|e| e.to_string())?; + + if saved.is_finished() { + saved.events.pop(); + } + + let tx_hash = taker_payment_refund_tx.tx_hash(); + info!("Taker refund tx hash {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(taker_payment_refund_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::TakerPaymentRefundedByWatcher(Some(tx_ident)); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + + let new_swap = SavedSwap::Taker(saved); + try_s!(new_swap.save_to_db(ctx).await); + + info!("Taker payment is refunded by the watcher"); + Ok(true) +} + +pub async fn prepare_for_kickstart(ctx: &MmArc, mut saved: TakerSavedSwap) -> Result { + if saved.contains_failure() { + if saved.is_finished() { + saved.events.pop(); + } + let event = TakerSwapEvent::WatcherSpendOrRefundNotFound; + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + let new_swap = SavedSwap::Taker(saved); + try_s!(new_swap.save_to_db(ctx).await); + Ok(true) + } else { + Ok(false) } } From 47c2257796cecfc1cec09d844230f6709d2568fc Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Tue, 8 Aug 2023 16:00:34 +0300 Subject: [PATCH 30/53] kickstart only unfinished swaps --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 51bcf747f3..12c5fa12f4 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -2681,10 +2681,8 @@ pub async fn save_swap_as_refunded( } pub async fn prepare_for_kickstart(ctx: &MmArc, mut saved: TakerSavedSwap) -> Result { - if saved.contains_failure() { - if saved.is_finished() { - saved.events.pop(); - } + if saved.is_finished() { + saved.events.pop(); let event = TakerSwapEvent::WatcherSpendOrRefundNotFound; let to_save = TakerSavedEvent { timestamp: now_ms(), From 7fbc2af3977a7c0b5085b10d960df1470e83b01e Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 10 Aug 2023 14:25:34 +0300 Subject: [PATCH 31/53] build a single success scenario on taker restart --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 51 +++---------------- .../tests/docker_tests/swap_watcher_tests.rs | 13 ++--- 2 files changed, 10 insertions(+), 54 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 12c5fa12f4..fe79cd60b7 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -57,7 +57,7 @@ pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ "Finished", ]; -pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 12] = [ +pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ "Started", "Negotiated", "TakerFeeSent", @@ -69,6 +69,7 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 12] = [ "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpentByWatcher", "Finished", ]; @@ -298,9 +299,6 @@ impl TakerSavedSwap { if !self.is_finished() { return ERR!("Can not determine is_success state for not finished swap"); } - if self.maker_payment_spent_by_watcher() { - return Ok(true); - } for event in self.events.iter() { if event.event.is_error() { return Ok(false); @@ -740,41 +738,6 @@ impl TakerSwapEvent { } } - pub fn to_str(&self) -> String { - match self { - TakerSwapEvent::Started(_) => "Started".to_owned(), - TakerSwapEvent::StartFailed(_) => "StartFailed".to_owned(), - TakerSwapEvent::Negotiated(_) => "Negotiated".to_owned(), - TakerSwapEvent::NegotiateFailed(_) => "NegotiateFailed".to_owned(), - TakerSwapEvent::TakerFeeSent(_) => "TakerFeeSent".to_owned(), - TakerSwapEvent::TakerFeeSendFailed(_) => "TakerFeeSendFailed".to_owned(), - TakerSwapEvent::TakerPaymentInstructionsReceived(_) => "TakerPaymentInstructionsReceived".to_owned(), - TakerSwapEvent::MakerPaymentReceived(_) => "MakerPaymentReceived".to_owned(), - TakerSwapEvent::MakerPaymentWaitConfirmStarted => "MakerPaymentWaitConfirmStarted".to_owned(), - TakerSwapEvent::MakerPaymentValidatedAndConfirmed => "MakerPaymentValidatedAndConfirmed".to_owned(), - TakerSwapEvent::MakerPaymentValidateFailed(_) => "MakerPaymentValidateFailed".to_owned(), - TakerSwapEvent::MakerPaymentWaitConfirmFailed(_) => "MakerPaymentWaitConfirmFailed".to_owned(), - TakerSwapEvent::TakerPaymentSent(_) => "TakerPaymentSent".to_owned(), - TakerSwapEvent::WatcherMessageSent(_, _) => "WatcherMessageSent".to_owned(), - TakerSwapEvent::TakerPaymentTransactionFailed(_) => "TakerPaymentTransactionFailed".to_owned(), - TakerSwapEvent::TakerPaymentDataSendFailed(_) => "TakerPaymentDataSendFailed".to_owned(), - TakerSwapEvent::TakerPaymentWaitConfirmFailed(_) => "TakerPaymentWaitConfirmFailed".to_owned(), - TakerSwapEvent::TakerPaymentSpent(_) => "TakerPaymentSpent".to_owned(), - TakerSwapEvent::TakerPaymentWaitForSpendFailed(_) => "TakerPaymentWaitForSpendFailed".to_owned(), - TakerSwapEvent::MakerPaymentSpent(_) => "MakerPaymentSpent".to_owned(), - TakerSwapEvent::MakerPaymentSpentByWatcher(_) => "MakerPaymentSpentByWatcher".to_owned(), - TakerSwapEvent::MakerPaymentSpendFailed(_) => "MakerPaymentSpendFailed".to_owned(), - TakerSwapEvent::TakerPaymentWaitRefundStarted { .. } => "TakerPaymentWaitRefundStarted".to_owned(), - TakerSwapEvent::TakerPaymentRefundStarted => "TakerPaymentRefundStarted".to_owned(), - TakerSwapEvent::TakerPaymentRefunded(_) => "TakerPaymentRefunded".to_owned(), - TakerSwapEvent::TakerPaymentRefundFailed(_) => "TakerPaymentRefundFailed".to_owned(), - TakerSwapEvent::TakerPaymentRefundFinished => "TakerPaymentRefundFinished".to_owned(), - TakerSwapEvent::TakerPaymentRefundedByWatcher(_) => "TakerPaymentRefundedByWatcher".to_owned(), - TakerSwapEvent::WatcherSpendOrRefundNotFound => "WatcherRefundNotFound".to_owned(), - TakerSwapEvent::Finished => "Finished".to_owned(), - } - } - fn should_ban_maker(&self) -> bool { matches!( self, @@ -2527,7 +2490,11 @@ pub async fn save_swap_as_successful( .await .map_err(|e| e.to_string())?; - if saved.is_finished() { + while !(matches!( + saved.events.last().unwrap().event, + TakerSwapEvent::WatcherMessageSent(_, _) + ) || matches!(saved.events.last().unwrap().event, TakerSwapEvent::TakerPaymentSpent(_))) + { saved.events.pop(); } @@ -2614,10 +2581,6 @@ pub async fn save_swap_as_successful( }; saved.events.push(to_save); - let mut success_events: Vec = saved.events.iter().map(|e| e.event.to_str()).collect(); - success_events.push("Finished".into()); - saved.success_events = success_events; - let new_swap = SavedSwap::Taker(saved); try_s!(new_swap.save_to_db(ctx).await); 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 102bd38bc2..e57460a2bf 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -26,7 +26,7 @@ use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::WatcherConf; use num_traits::{One, Zero}; use primitives::hash::H256; -use serde_json::{self as json, Value}; +use serde_json::Value; use std::str::FromStr; use std::thread; use std::time::Duration; @@ -260,12 +260,6 @@ fn start_swaps_and_get_balances( } } -fn check_actual_and_success_events(mm_alice: &MarketMakerIt, uuid: &str, expected_events: &[&'static str]) { - let status_response = check_actual_events(mm_alice, uuid, expected_events); - let success_events: Vec = json::from_value(status_response["result"]["success_events"].clone()).unwrap(); - assert_eq!(expected_events, success_events.as_slice()); -} - fn check_actual_events(mm_alice: &MarketMakerIt, uuid: &str, expected_events: &[&'static str]) -> Value { let status_response = block_on(my_swap_status(mm_alice, uuid)); let events_array = status_response["result"]["events"].as_array().unwrap(); @@ -404,11 +398,10 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fai "TakerPaymentSent", "WatcherMessageSent", "TakerPaymentSpent", - "MakerPaymentSpendFailed", "MakerPaymentSpentByWatcher", "Finished", ]; - check_actual_and_success_events(&mm_alice, &uuids[0], &expected_events); + check_actual_events(&mm_alice, &uuids[0], &expected_events); } #[test] @@ -459,7 +452,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan "MakerPaymentSpentByWatcher", "Finished", ]; - check_actual_and_success_events(&mm_alice, &uuids[0], &expected_events); + check_actual_events(&mm_alice, &uuids[0], &expected_events); } #[test] From bfe92896a1c949c7b15375a950e4a8ff53cbcdc4 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 10 Aug 2023 20:56:52 +0300 Subject: [PATCH 32/53] add error checks for taker payment refund validations --- mm2src/coins/eth.rs | 50 ++-- .../tests/docker_tests/swap_watcher_tests.rs | 269 +++++++++++++++++- 2 files changed, 280 insertions(+), 39 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 54d22f9134..11d4cbd7bb 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1491,18 +1491,6 @@ impl WatcherOps for EthCoin { try_f!(addr_from_raw_pubkey(&input.maker_pub).map_to_mm(ValidatePaymentError::InvalidParameter)); let trade_amount = try_f!(wei_from_big_decimal(&(input.amount), decimals)); let fut = async move { - let status = selfi - .payment_status(expected_swap_contract_address, Token::FixedBytes(swap_id.clone())) - .compat() - .await - .map_to_mm(ValidatePaymentError::Transport)?; - if status != U256::from(PaymentState::Refunded as u8) { - return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( - "Payment state is not PAYMENT_STATE_REFUNDED, got {}", - status - ))); - } - match tx.action { Call(contract_address) => { if contract_address != expected_swap_contract_address { @@ -1519,6 +1507,18 @@ impl WatcherOps for EthCoin { }, }; + let status = selfi + .payment_status(expected_swap_contract_address, Token::FixedBytes(swap_id.clone())) + .compat() + .await + .map_to_mm(ValidatePaymentError::Transport)?; + if status != U256::from(PaymentState::Refunded as u8) { + return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( + "Payment state is not PAYMENT_STATE_REFUNDED, got {}", + status + ))); + } + let function_name = get_function_name("senderRefund", true); let function = SWAP_CONTRACT .function(&function_name) @@ -1581,7 +1581,7 @@ impl WatcherOps for EthCoin { .map_to_mm(ValidatePaymentError::TxDeserializationError)?; if contract_reward_input != Token::Bool(watcher_reward.send_contract_reward_on_spend) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx sends_contract_reward_on_spend arg {:?} is invalid, expected {:?}", + "Refund tx sends contract reward on spend arg {:?} is invalid, expected {:?}", contract_reward_input, Token::Bool(watcher_reward.send_contract_reward_on_spend) ))); @@ -1689,18 +1689,6 @@ impl WatcherOps for EthCoin { let trade_amount = try_f!(wei_from_big_decimal(&(input.amount), decimals)); let fut = async move { - let status = selfi - .payment_status(expected_swap_contract_address, Token::FixedBytes(swap_id.clone())) - .compat() - .await - .map_to_mm(ValidatePaymentError::Transport)?; - if status != U256::from(PaymentState::Spent as u8) { - return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( - "Payment state is not PAYMENT_STATE_SPENT, got {}", - status - ))); - } - match tx.action { Call(contract_address) => { if contract_address != expected_swap_contract_address { @@ -1717,6 +1705,18 @@ impl WatcherOps for EthCoin { }, }; + let status = selfi + .payment_status(expected_swap_contract_address, Token::FixedBytes(swap_id.clone())) + .compat() + .await + .map_to_mm(ValidatePaymentError::Transport)?; + if status != U256::from(PaymentState::Spent as u8) { + return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( + "Payment state is not PAYMENT_STATE_SPENT, got {}", + status + ))); + } + let function_name = get_function_name("receiverSpend", true); let function = SWAP_CONTRACT .function(&function_name) 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 e57460a2bf..b05dff1140 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -2298,6 +2298,7 @@ fn test_taker_validates_taker_payment_refund_eth() { let maker_seed = get_passphrase!(".env.client", "BOB_PASSPHRASE").unwrap(); let maker_keypair = key_pair_from_seed(&maker_seed).unwrap(); let maker_pub = maker_keypair.public(); + let maker_coin = generate_eth_coin_with_seed(&maker_seed); let time_lock_duration = get_payment_locktime(); let wait_for_confirmation_until = wait_until_sec(time_lock_duration); @@ -2306,16 +2307,14 @@ fn test_taker_validates_taker_payment_refund_eth() { let maker_amount = BigDecimal::from_str("0.001").unwrap(); let secret_hash = dhash160(&MakerSwap::generate_secret().unwrap()); - let watcher_reward = Some( - block_on(taker_coin.get_taker_watcher_reward( - &MmCoinEnum::from(taker_coin.clone()), - Some(taker_amount.clone()), - Some(maker_amount), - None, - wait_for_confirmation_until, - )) - .unwrap(), - ); + let watcher_reward = block_on(taker_coin.get_taker_watcher_reward( + &MmCoinEnum::from(taker_coin.clone()), + Some(taker_amount.clone()), + Some(maker_amount), + None, + wait_for_confirmation_until, + )) + .unwrap(); let taker_payment = taker_coin .send_taker_payment(SendPaymentArgs { @@ -2327,7 +2326,7 @@ fn test_taker_validates_taker_payment_refund_eth() { swap_contract_address: &taker_coin.swap_contract_address(), swap_unique_data: &[], payment_instructions: &None, - watcher_reward: watcher_reward.clone(), + watcher_reward: Some(watcher_reward.clone()), wait_for_confirmation_until, }) .wait() @@ -2354,6 +2353,32 @@ fn test_taker_validates_taker_payment_refund_eth() { .wait() .unwrap(); + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund_preimage.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: taker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: taker_amount.clone(), + watcher_reward: Some(watcher_reward.clone()), + }; + + let error = taker_coin + .taker_validates_taker_payment_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::UnexpectedPaymentState(err) => { + assert!(err.contains("Payment state is not PAYMENT_STATE_REFUNDED")) + }, + _ => panic!( + "Expected `UnexpectedPaymentState` {}, found {:?}", + "Payment state is not PAYMENT_STATE_REFUNDED", error + ), + } + let taker_payment_refund = taker_coin .send_taker_payment_refund_preimage(RefundPaymentArgs { payment_tx: &taker_payment_refund_preimage.tx_hex(), @@ -2373,12 +2398,202 @@ fn test_taker_validates_taker_payment_refund_eth() { swap_contract_address: taker_coin.swap_contract_address(), time_lock, secret_hash: secret_hash.to_vec(), - amount: taker_amount, - watcher_reward, + amount: taker_amount.clone(), + watcher_reward: Some(watcher_reward.clone()), }; let validate_watcher_refund = taker_coin.taker_validates_taker_payment_refund(validate_input).wait(); assert!(validate_watcher_refund.is_ok()); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: Some("9130b257d37a52e52f21054c4da3450c72f595ce".into()), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: taker_amount.clone(), + watcher_reward: Some(watcher_reward.clone()), + }; + let error = taker_coin + .taker_validates_taker_payment_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("was sent to wrong address")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid contract address", error + ), + } + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: taker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: taker_amount.clone(), + watcher_reward: Some(watcher_reward.clone()), + }; + + let error = maker_coin + .taker_validates_taker_payment_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Refund tx sender arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid refund tx sender arg", error + ), + } + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: taker_pub.to_vec(), + swap_contract_address: taker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: taker_amount.clone(), + watcher_reward: Some(watcher_reward.clone()), + }; + + let error = taker_coin + .taker_validates_taker_payment_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Refund tx receiver arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid refund tx receiver arg", error + ), + } + + let mut wrong_watcher_reward = watcher_reward.clone(); + wrong_watcher_reward.reward_target = RewardTarget::PaymentReceiver; + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: taker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: taker_amount.clone(), + watcher_reward: Some(wrong_watcher_reward), + }; + + let error = taker_coin + .taker_validates_taker_payment_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Refund tx reward target arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid refund tx reward target arg", error + ), + } + + let mut wrong_watcher_reward = watcher_reward.clone(); + wrong_watcher_reward.send_contract_reward_on_spend = true; + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: taker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: taker_amount.clone(), + watcher_reward: Some(wrong_watcher_reward), + }; + + let error = taker_coin + .taker_validates_taker_payment_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Refund tx sends contract reward on spend arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid refund tx sends contract reward on spend arg", error + ), + } + + let mut wrong_watcher_reward = watcher_reward.clone(); + wrong_watcher_reward.amount = BigDecimal::one(); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: taker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: taker_amount, + watcher_reward: Some(wrong_watcher_reward), + }; + + let error = taker_coin + .taker_validates_taker_payment_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Refund tx watcher reward amount arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid refund tx watcher reward amount arg", error + ), + } + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: taker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: BigDecimal::one(), + watcher_reward: Some(watcher_reward), + }; + + let error = taker_coin + .taker_validates_taker_payment_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Refund tx amount arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid refund tx amount arg", error + ), + } } #[test] @@ -2471,11 +2686,37 @@ fn test_taker_validates_taker_payment_refund_erc20() { time_lock, secret_hash: secret_hash.to_vec(), amount: taker_amount, - watcher_reward, + watcher_reward: watcher_reward.clone(), }; let validate_watcher_refund = taker_coin.taker_validates_taker_payment_refund(validate_input).wait(); assert!(validate_watcher_refund.is_ok()); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: taker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: BigDecimal::one(), + watcher_reward, + }; + + let error = taker_coin + .taker_validates_taker_payment_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Refund tx amount arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid refund tx amount arg", error + ), + } } #[test] From 484c1322479ca89803b1656935263d9ab0e4be8d Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Fri, 11 Aug 2023 00:59:13 +0300 Subject: [PATCH 33/53] add error checks for maker payment spend validations --- mm2src/coins/eth.rs | 10 +- .../tests/docker_tests/swap_watcher_tests.rs | 286 ++++++++++++++++-- 2 files changed, 268 insertions(+), 28 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 11d4cbd7bb..0f66891f3b 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1769,6 +1769,7 @@ impl WatcherOps for EthCoin { let reward_target_input = get_function_input_data(&decoded, function, 6) .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + if reward_target_input != Token::Uint(U256::from(watcher_reward.reward_target as u8)) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( "Payment spend tx reward target arg {:?} is invalid, expected {:?}", @@ -1780,7 +1781,7 @@ impl WatcherOps for EthCoin { .map_to_mm(ValidatePaymentError::TxDeserializationError)?; if contract_reward_input != Token::Bool(watcher_reward.send_contract_reward_on_spend) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx sends_contract_reward_on_spend arg {:?} is invalid, expected {:?}", + "Payment spend tx sends contract reward on spend arg {:?} is invalid, expected {:?}", contract_reward_input, watcher_reward.send_contract_reward_on_spend ))); } @@ -1805,7 +1806,12 @@ impl WatcherOps for EthCoin { EthCoinType::Eth => { let amount_input = get_function_input_data(&decoded, function, 1) .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - let total_amount = trade_amount + expected_reward_amount; + let total_amount = if let RewardTarget::None = watcher_reward.reward_target { + trade_amount + } else { + trade_amount + expected_reward_amount + }; + if amount_input != Token::Uint(total_amount) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( "Payment spend tx amount arg {:?} is invalid, expected {:?}", 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 b05dff1140..9451e7feec 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -2813,22 +2813,18 @@ fn test_taker_validates_maker_payment_spend_eth() { let time_lock_duration = get_payment_locktime(); let wait_for_confirmation_until = wait_until_sec(time_lock_duration); let time_lock = wait_for_confirmation_until as u32; - let taker_amount = BigDecimal::from_str("0.001").unwrap(); let maker_amount = BigDecimal::from_str("0.001").unwrap(); let secret = MakerSwap::generate_secret().unwrap(); let secret_hash = dhash160(&secret); - let watcher_reward = Some( - block_on(maker_coin.get_taker_watcher_reward( - &MmCoinEnum::from(maker_coin.clone()), - Some(taker_amount), - Some(maker_amount.clone()), - None, - wait_for_confirmation_until, - )) - .unwrap(), - ); + let watcher_reward = block_on(maker_coin.get_maker_watcher_reward( + &MmCoinEnum::from(taker_coin.clone()), + None, + wait_for_confirmation_until, + )) + .unwrap() + .unwrap(); let maker_payment = maker_coin .send_maker_payment(SendPaymentArgs { @@ -2840,7 +2836,7 @@ fn test_taker_validates_maker_payment_spend_eth() { swap_contract_address: &maker_coin.swap_contract_address(), swap_unique_data: &[], payment_instructions: &None, - watcher_reward: watcher_reward.clone(), + watcher_reward: Some(watcher_reward.clone()), wait_for_confirmation_until, }) .wait() @@ -2868,6 +2864,32 @@ fn test_taker_validates_maker_payment_spend_eth() { .wait() .unwrap(); + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend_preimage.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: maker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: maker_amount.clone(), + watcher_reward: Some(watcher_reward.clone()), + }; + + let error = taker_coin + .taker_validates_maker_payment_spend(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::UnexpectedPaymentState(err) => { + assert!(err.contains("Payment state is not PAYMENT_STATE_SPENT")) + }, + _ => panic!( + "Expected `UnexpectedPaymentState` {}, found {:?}", + "invalid payment state", error + ), + } + let maker_payment_spend = taker_coin .send_maker_payment_spend_preimage(SendMakerPaymentSpendPreimageInput { preimage: &maker_payment_spend_preimage.tx_hex(), @@ -2885,12 +2907,203 @@ fn test_taker_validates_maker_payment_spend_eth() { swap_contract_address: maker_coin.swap_contract_address(), time_lock, secret_hash: secret_hash.to_vec(), - amount: maker_amount, - watcher_reward, + amount: maker_amount.clone(), + watcher_reward: Some(watcher_reward.clone()), }; let validate_watcher_spend = taker_coin.taker_validates_maker_payment_spend(validate_input).wait(); assert!(validate_watcher_spend.is_ok()); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: Some("9130b257d37a52e52f21054c4da3450c72f595ce".into()), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: maker_amount.clone(), + watcher_reward: Some(watcher_reward.clone()), + }; + + let error = taker_coin + .taker_validates_maker_payment_spend(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("was sent to wrong address")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid contract address", error + ), + }; + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: taker_pub.to_vec(), + swap_contract_address: maker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: maker_amount.clone(), + watcher_reward: Some(watcher_reward.clone()), + }; + + let error = taker_coin + .taker_validates_maker_payment_spend(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Payment spend tx sender arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid payment spend tx sender arg", error + ), + }; + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: maker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: maker_amount.clone(), + watcher_reward: Some(watcher_reward.clone()), + }; + + let error = maker_coin + .taker_validates_maker_payment_spend(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Payment spend tx receiver arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid payment spend tx receiver arg", error + ), + }; + + let mut wrong_watcher_reward = watcher_reward.clone(); + wrong_watcher_reward.reward_target = RewardTarget::Contract; + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: maker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: maker_amount.clone(), + watcher_reward: Some(wrong_watcher_reward), + }; + + let error = taker_coin + .taker_validates_maker_payment_spend(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Payment spend tx reward target arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid payment spend tx reward target arg", error + ), + }; + + let mut wrong_watcher_reward = watcher_reward.clone(); + wrong_watcher_reward.send_contract_reward_on_spend = false; + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: maker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: maker_amount.clone(), + watcher_reward: Some(wrong_watcher_reward), + }; + + let error = taker_coin + .taker_validates_maker_payment_spend(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Payment spend tx sends contract reward on spend arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid payment spend tx sends contract reward on spend arg", error + ), + }; + + let mut wrong_watcher_reward = watcher_reward.clone(); + wrong_watcher_reward.amount = BigDecimal::one(); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: maker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: maker_amount, + watcher_reward: Some(wrong_watcher_reward), + }; + + let error = taker_coin + .taker_validates_maker_payment_spend(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Payment spend tx watcher reward amount arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid payment spend tx watcher reward amount arg", error + ), + }; + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: maker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: BigDecimal::one(), + watcher_reward: Some(watcher_reward), + }; + + let error = taker_coin + .taker_validates_maker_payment_spend(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Payment spend tx amount arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid payment spend tx amount arg", error + ), + }; } #[test] @@ -2910,22 +3123,17 @@ fn test_taker_validates_maker_payment_spend_erc20() { let time_lock_duration = get_payment_locktime(); let wait_for_confirmation_until = wait_until_sec(time_lock_duration); let time_lock = wait_for_confirmation_until as u32; - let taker_amount = BigDecimal::from_str("0.001").unwrap(); let maker_amount = BigDecimal::from_str("0.001").unwrap(); let secret = MakerSwap::generate_secret().unwrap(); let secret_hash = dhash160(&secret); - let watcher_reward = Some( - block_on(maker_coin.get_taker_watcher_reward( - &MmCoinEnum::from(maker_coin.clone()), - Some(taker_amount), - Some(maker_amount.clone()), - None, - wait_for_confirmation_until, - )) - .unwrap(), - ); + let watcher_reward = block_on(maker_coin.get_maker_watcher_reward( + &MmCoinEnum::from(taker_coin.clone()), + None, + wait_for_confirmation_until, + )) + .unwrap(); let maker_payment = maker_coin .send_maker_payment(SendPaymentArgs { @@ -2983,11 +3191,37 @@ fn test_taker_validates_maker_payment_spend_erc20() { time_lock, secret_hash: secret_hash.to_vec(), amount: maker_amount, - watcher_reward, + watcher_reward: watcher_reward.clone(), }; let validate_watcher_spend = taker_coin.taker_validates_maker_payment_spend(validate_input).wait(); assert!(validate_watcher_spend.is_ok()); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend.tx_hex(), + maker_pub: maker_pub.to_vec(), + swap_contract_address: maker_coin.swap_contract_address(), + time_lock, + secret_hash: secret_hash.to_vec(), + amount: BigDecimal::one(), + watcher_reward, + }; + + let error = taker_coin + .taker_validates_maker_payment_spend(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Payment spend tx amount arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid payment spend tx amount arg", error + ), + }; } #[test] From 49a305f59c5db0af27b369430a7f94a6c254577a Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Fri, 11 Aug 2023 02:04:42 +0300 Subject: [PATCH 34/53] small fixes and improvements --- mm2src/mm2_main/src/lp_swap.rs | 8 +------- mm2src/mm2_main/src/lp_swap/saved_swap.rs | 24 +++++++++++++++-------- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 21 +++++++++++++++++--- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index e245bdb401..4ed1e5000d 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -1179,13 +1179,7 @@ pub async fn swap_kick_starts(ctx: MmArc) -> Result, String> { let mut coins = HashSet::new(); let swaps = try_s!(SavedSwap::load_all_my_swaps_from_db(&ctx).await); for swap in swaps { - if swap.is_finished_and_success() - || (swap.is_finished() - && (!swap.watcher_message_sent() - || swap.refund_finished() - || swap.refunded_by_watcher() - || swap.watcher_spend_or_refund_not_found())) - { + if swap.finished_and_watcher_check_not_required() { info!("{} {}", SWAP_FINISHED_LOG, swap.uuid()); continue; } diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index 1827b87f0c..2e5c1039d0 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -56,6 +56,20 @@ impl SavedSwap { } } + pub fn finished_and_watcher_check_not_required(&self) -> bool { + match self { + SavedSwap::Maker(swap) => swap.is_finished(), + SavedSwap::Taker(swap) => { + self.is_finished_and_success() + || (swap.is_finished() + && (!swap.watcher_message_sent() + || swap.refund_finished() + || swap.refunded_by_watcher() + || swap.watcher_spend_or_refund_not_found())) + }, + } + } + pub fn watcher_message_sent(&self) -> bool { match &self { SavedSwap::Taker(taker_swap) => taker_swap.watcher_message_sent(), @@ -65,10 +79,7 @@ impl SavedSwap { pub fn refunded_by_watcher(&self) -> bool { match &self { - SavedSwap::Taker(taker_swap) => taker_swap - .events - .iter() - .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefundedByWatcher(_))), + SavedSwap::Taker(taker_swap) => taker_swap.refunded_by_watcher(), _ => false, } } @@ -82,10 +93,7 @@ impl SavedSwap { pub fn watcher_spend_or_refund_not_found(&self) -> bool { match &self { - SavedSwap::Taker(taker_swap) => taker_swap - .events - .iter() - .any(|e| matches!(e.event, TakerSwapEvent::WatcherSpendOrRefundNotFound)), + SavedSwap::Taker(taker_swap) => taker_swap.watcher_spend_or_refund_not_found(), _ => false, } } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index fe79cd60b7..8dfeab9b06 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -327,6 +327,18 @@ impl TakerSavedSwap { .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefunded(_))) } + pub fn refunded_by_watcher(&self) -> bool { + self.events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefundedByWatcher(_))) + } + + pub fn watcher_spend_or_refund_not_found(&self) -> bool { + self.events + .iter() + .any(|e| matches!(e.event, TakerSwapEvent::WatcherSpendOrRefundNotFound)) + } + pub fn taker_payment_spent(&self) -> bool { self.events .iter() @@ -2029,8 +2041,8 @@ impl TakerSwap { saved.uuid, Some(saved.uuid), conf_settings, - maker_coin, - taker_coin, + maker_coin.clone(), + taker_coin.clone(), data.lock_duration, data.p2p_privkey.map(SerializableSecp256k1Keypair::into_inner), #[cfg(any(test, feature = "run-docker-tests"))] @@ -2041,7 +2053,10 @@ impl TakerSwap { swap.apply_event(saved_event.event.clone()); } - if check_watcher_payments(&swap, &ctx, saved).await? { + if taker_coin.is_supported_by_watchers() + && maker_coin.is_supported_by_watchers() + && check_watcher_payments(&swap, &ctx, saved).await? + { return Ok((swap, Some(TakerSwapCommand::Finish))); } From 14d12d17016a387745ac92254078ed85ad5a5de8 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Fri, 11 Aug 2023 15:56:30 +0300 Subject: [PATCH 35/53] fix success events check in check_my_swap_status --- mm2src/mm2_test_helpers/src/for_tests.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index b83211556a..783011e938 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -93,7 +93,7 @@ pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ "Finished", ]; -pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 12] = [ +pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ "Started", "Negotiated", "TakerFeeSent", @@ -105,6 +105,7 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 12] = [ "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpentByWatcher", "Finished", ]; @@ -2009,9 +2010,11 @@ pub async fn check_my_swap_status(mm: &MarketMakerIt, uuid: &str, maker_amount: assert_eq!(maker_amount, actual_maker_amount); let actual_taker_amount = json::from_value(events_array[0]["event"]["data"]["taker_amount"].clone()).unwrap(); assert_eq!(taker_amount, actual_taker_amount); - let actual_events = events_array.iter().map(|item| item["event"]["type"].as_str().unwrap()); - let actual_events: Vec<&str> = actual_events.collect(); - assert_eq!(success_events, actual_events.as_slice()); + let actual_events = events_array + .iter() + .map(|item| item["event"]["type"].as_str().unwrap().to_string()) + .collect::>(); + assert!(actual_events.iter().all(|item| success_events.contains(item))); } pub async fn check_my_swap_status_amounts( From a6f24f4e50da27aed552ab94cd9778ed72c4927a Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Mon, 14 Aug 2023 00:42:45 +0300 Subject: [PATCH 36/53] check only unfinished swaps for watcher payments --- mm2src/mm2_main/src/lp_swap.rs | 2 +- mm2src/mm2_main/src/lp_swap/saved_swap.rs | 35 ---- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 18 -- .../tests/docker_tests/swap_watcher_tests.rs | 176 ------------------ 4 files changed, 1 insertion(+), 230 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 4ed1e5000d..9a79722556 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -1179,7 +1179,7 @@ pub async fn swap_kick_starts(ctx: MmArc) -> Result, String> { let mut coins = HashSet::new(); let swaps = try_s!(SavedSwap::load_all_my_swaps_from_db(&ctx).await); for swap in swaps { - if swap.finished_and_watcher_check_not_required() { + if swap.is_finished() { info!("{} {}", SWAP_FINISHED_LOG, swap.uuid()); continue; } diff --git a/mm2src/mm2_main/src/lp_swap/saved_swap.rs b/mm2src/mm2_main/src/lp_swap/saved_swap.rs index 2e5c1039d0..265c1a5142 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -56,20 +56,6 @@ impl SavedSwap { } } - pub fn finished_and_watcher_check_not_required(&self) -> bool { - match self { - SavedSwap::Maker(swap) => swap.is_finished(), - SavedSwap::Taker(swap) => { - self.is_finished_and_success() - || (swap.is_finished() - && (!swap.watcher_message_sent() - || swap.refund_finished() - || swap.refunded_by_watcher() - || swap.watcher_spend_or_refund_not_found())) - }, - } - } - pub fn watcher_message_sent(&self) -> bool { match &self { SavedSwap::Taker(taker_swap) => taker_swap.watcher_message_sent(), @@ -77,27 +63,6 @@ impl SavedSwap { } } - pub fn refunded_by_watcher(&self) -> bool { - match &self { - SavedSwap::Taker(taker_swap) => taker_swap.refunded_by_watcher(), - _ => false, - } - } - - pub fn refund_finished(&self) -> bool { - match &self { - SavedSwap::Taker(taker_swap) => taker_swap.refund_finished(), - _ => false, - } - } - - pub fn watcher_spend_or_refund_not_found(&self) -> bool { - match &self { - SavedSwap::Taker(taker_swap) => taker_swap.watcher_spend_or_refund_not_found(), - _ => false, - } - } - pub fn uuid(&self) -> &Uuid { match self { SavedSwap::Maker(swap) => &swap.uuid, diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 8dfeab9b06..cc29552198 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -315,30 +315,12 @@ impl TakerSavedSwap { .any(|e| matches!(e.event, TakerSwapEvent::WatcherMessageSent(_, _))) } - pub fn refund_finished(&self) -> bool { - self.events - .iter() - .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefundFinished)) - } - pub fn refunded(&self) -> bool { self.events .iter() .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefunded(_))) } - pub fn refunded_by_watcher(&self) -> bool { - self.events - .iter() - .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefundedByWatcher(_))) - } - - pub fn watcher_spend_or_refund_not_found(&self) -> bool { - self.events - .iter() - .any(|e| matches!(e.event, TakerSwapEvent::WatcherSpendOrRefundNotFound)) - } - pub fn taker_payment_spent(&self) -> bool { self.events .iter() 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 9451e7feec..de8b0fa927 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -355,55 +355,6 @@ fn run_watcher_node( mm } -#[test] -fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_fail_at_maker_payment_spend() { - let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[("TAKER_FAIL_AT", "maker_payment_spend")]); - let mut mm_bob = run_maker_node(&coins, &[], &[&mm_alice.ip.to_string()]); - - let watcher_conf = WatcherConf { - wait_taker_payment: 0., - wait_maker_payment_spend_factor: 0., - refund_start_factor: 1.5, - search_interval: 1.0, - }; - let mut mm_watcher = run_watcher_node(&coins, &[], &[&mm_alice.ip.to_string()], watcher_conf); - - let uuids = block_on(start_swaps( - &mut mm_bob, - &mut mm_alice, - &[("MYCOIN1", "MYCOIN")], - 25., - 25., - 2., - )); - alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); - - block_on(mm_bob.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); - block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); - - restart_taker_and_wait_until(&alice_conf, &[], &format!("[swap uuid={}] Finished", &uuids[0])); - block_on(mm_alice.stop()).unwrap(); - - let mm_alice = restart_taker_and_wait_until(&alice_conf, &[], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0])); - let expected_events = [ - "Started", - "Negotiated", - "TakerFeeSent", - "TakerPaymentInstructionsReceived", - "MakerPaymentReceived", - "MakerPaymentWaitConfirmStarted", - "MakerPaymentValidatedAndConfirmed", - "TakerPaymentSent", - "WatcherMessageSent", - "TakerPaymentSpent", - "MakerPaymentSpentByWatcher", - "Finished", - ]; - check_actual_events(&mm_alice, &uuids[0], &expected_events); -} - #[test] fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_panic_at_wait_for_taker_payment_spend() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); @@ -455,77 +406,6 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan check_actual_events(&mm_alice, &uuids[0], &expected_events); } -#[test] -fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_fail_at_taker_payment_refund() { - let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ - ("USE_TEST_LOCKTIME", ""), - ("TAKER_FAIL_AT", "taker_payment_refund"), - ]); - let mut mm_bob = run_maker_node(&coins, &[("USE_TEST_LOCKTIME", "")], &[&mm_alice.ip.to_string()]); - - let watcher_conf = WatcherConf { - wait_taker_payment: 0., - wait_maker_payment_spend_factor: 1., - refund_start_factor: 0., - search_interval: 1., - }; - let mut mm_watcher = run_watcher_node( - &coins, - &[("USE_TEST_LOCKTIME", "")], - &[&mm_alice.ip.to_string()], - watcher_conf, - ); - - let uuids = block_on(start_swaps( - &mut mm_bob, - &mut mm_alice, - &[("MYCOIN1", "MYCOIN")], - 25., - 25., - 2., - )); - alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); - - block_on(mm_bob.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SENT_LOG))).unwrap(); - block_on(mm_bob.stop()).unwrap(); - - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); - block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); - - restart_taker_and_wait_until( - &alice_conf, - &[("USE_TEST_LOCKTIME", "")], - &format!("[swap uuid={}] Finished", &uuids[0]), - ); - block_on(mm_alice.stop()).unwrap(); - - let mm_alice = restart_taker_and_wait_until( - &alice_conf, - &[("USE_TEST_LOCKTIME", "")], - &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), - ); - - let expected_events = [ - "Started", - "Negotiated", - "TakerFeeSent", - "TakerPaymentInstructionsReceived", - "MakerPaymentReceived", - "MakerPaymentWaitConfirmStarted", - "MakerPaymentValidatedAndConfirmed", - "TakerPaymentSent", - "WatcherMessageSent", - "TakerPaymentWaitForSpendFailed", - "TakerPaymentWaitRefundStarted", - "TakerPaymentRefundStarted", - "TakerPaymentRefundFailed", - "TakerPaymentRefundedByWatcher", - "Finished", - ]; - check_actual_events(&mm_alice, &uuids[0], &expected_events); -} - #[test] fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_panic_at_taker_payment_refund() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); @@ -596,62 +476,6 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa check_actual_events(&mm_alice, &uuids[0], &expected_events); } -#[test] -fn test_taker_adds_watcher_refund_not_found_event() { - let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); - let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ - ("USE_TEST_LOCKTIME", ""), - ("TAKER_FAIL_AT", "taker_payment_refund"), - ]); - let mut mm_bob = run_maker_node(&coins, &[("USE_TEST_LOCKTIME", "")], &[&mm_alice.ip.to_string()]); - - let uuids = block_on(start_swaps( - &mut mm_bob, - &mut mm_alice, - &[("MYCOIN1", "MYCOIN")], - 25., - 25., - 2., - )); - alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); - - block_on(mm_bob.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SENT_LOG))).unwrap(); - block_on(mm_bob.stop()).unwrap(); - block_on(mm_alice.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); - - restart_taker_and_wait_until( - &alice_conf, - &[("USE_TEST_LOCKTIME", "")], - &format!("[swap uuid={}] Finished", &uuids[0]), - ); - block_on(mm_alice.stop()).unwrap(); - - let mm_alice = restart_taker_and_wait_until( - &alice_conf, - &[("USE_TEST_LOCKTIME", "")], - &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), - ); - - let expected_events = [ - "Started", - "Negotiated", - "TakerFeeSent", - "TakerPaymentInstructionsReceived", - "MakerPaymentReceived", - "MakerPaymentWaitConfirmStarted", - "MakerPaymentValidatedAndConfirmed", - "TakerPaymentSent", - "WatcherMessageSent", - "TakerPaymentWaitForSpendFailed", - "TakerPaymentWaitRefundStarted", - "TakerPaymentRefundStarted", - "TakerPaymentRefundFailed", - "WatcherSpendOrRefundNotFound", - "Finished", - ]; - check_actual_events(&mm_alice, &uuids[0], &expected_events); -} - #[test] fn test_taker_completes_swap_after_restart() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); From 627d796ae5fbea2583fff083a56fd610b90dee99 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Tue, 15 Aug 2023 01:30:26 +0300 Subject: [PATCH 37/53] use commands to check watcher payments at taker restart --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 466 ++++++++---------- .../tests/docker_tests/swap_watcher_tests.rs | 127 ++++- 2 files changed, 341 insertions(+), 252 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index cc29552198..1825efd5b2 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -307,38 +307,12 @@ impl TakerSavedSwap { Ok(true) } - pub fn contains_failure(&self) -> bool { self.events.iter().any(|event| event.event.is_error()) } - pub fn watcher_message_sent(&self) -> bool { self.events .iter() .any(|e| matches!(e.event, TakerSwapEvent::WatcherMessageSent(_, _))) } - pub fn refunded(&self) -> bool { - self.events - .iter() - .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentRefunded(_))) - } - - pub fn taker_payment_spent(&self) -> bool { - self.events - .iter() - .any(|e| matches!(e.event, TakerSwapEvent::TakerPaymentSpent(_))) - } - - pub fn maker_payment_spent(&self) -> bool { - self.events - .iter() - .any(|e| matches!(e.event, TakerSwapEvent::MakerPaymentSpent(_))) - } - - pub fn maker_payment_spent_by_watcher(&self) -> bool { - self.events - .iter() - .any(|e| matches!(e.event, TakerSwapEvent::MakerPaymentSpentByWatcher(_))) - } - pub async fn fetch_and_set_usd_prices(&mut self) { if let Some(rates) = fetch_swap_coins_price(self.maker_coin.clone(), self.taker_coin.clone()).await { self.maker_coin_usd_price = Some(rates.base); @@ -591,6 +565,7 @@ pub enum FailAt { TakerPayment, WaitForTakerPaymentSpendPanic, MakerPaymentSpend, + MakerPaymentSpendPanic, TakerPaymentRefund, TakerPaymentRefundPanic, } @@ -602,6 +577,7 @@ impl From for FailAt { "taker_payment" => FailAt::TakerPayment, "wait_for_taker_payment_spend_panic" => FailAt::WaitForTakerPaymentSpendPanic, "maker_payment_spend" => FailAt::MakerPaymentSpend, + "maker_payment_spend_panic" => FailAt::MakerPaymentSpendPanic, "taker_payment_refund" => FailAt::TakerPaymentRefund, "taker_payment_refund_panic" => FailAt::TakerPaymentRefundPanic, _ => panic!("Invalid TAKER_FAIL_AT value"), @@ -1797,7 +1773,10 @@ impl TakerSwap { return Ok((Some(TakerSwapCommand::Finish), vec![ TakerSwapEvent::MakerPaymentSpendFailed("Explicit test failure".into()), ])); + } else if self.fail_at == Some(FailAt::MakerPaymentSpendPanic) { + panic!("Taker panicked unexpectedly at maker payment spend"); } + let spend_fut = self.maker_coin.send_taker_spends_maker_payment(SpendPaymentArgs { other_payment_tx: &self.r().maker_payment.clone().unwrap().tx_hex, time_lock: self.maker_payment_lock.load(Ordering::Relaxed) as u32, @@ -1864,9 +1843,7 @@ impl TakerSwap { return Ok((Some(TakerSwapCommand::Finish), vec![ TakerSwapEvent::TakerPaymentRefundFailed("Explicit test failure".into()), ])); - } - #[cfg(any(test, feature = "run-docker-tests"))] - if self.fail_at == Some(FailAt::TakerPaymentRefundPanic) { + } else if self.fail_at == Some(FailAt::TakerPaymentRefundPanic) { panic!("{}", REFUND_TEST_FAILURE_LOG); } @@ -2030,16 +2007,16 @@ impl TakerSwap { #[cfg(any(test, feature = "run-docker-tests"))] fail_at, ); - let command = saved.events.last().unwrap().get_command(); + let mut command = saved.events.last().unwrap().get_command(); for saved_event in &saved.events { swap.apply_event(saved_event.event.clone()); } if taker_coin.is_supported_by_watchers() && maker_coin.is_supported_by_watchers() - && check_watcher_payments(&swap, &ctx, saved).await? + && saved.watcher_message_sent() { - return Ok((swap, Some(TakerSwapCommand::Finish))); + command = check_watcher_payments(&swap, &ctx, saved, command).await?; } Ok((swap, command)) @@ -2352,11 +2329,99 @@ pub struct TakerSwapPreparedParams { maker_payment_spend_trade_fee: TradeFee, } -pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, saved: TakerSavedSwap) -> Result { - if !saved.watcher_message_sent() || saved.refunded() || saved.maker_payment_spent() { - return Ok(false); +pub async fn check_watcher_payments( + swap: &TakerSwap, + ctx: &MmArc, + mut saved: TakerSavedSwap, + command: Option, +) -> Result, String> { + #[cfg(not(any(test, feature = "run-docker-tests")))] + if now_sec() < swap.r().data.taker_payment_lock { + return Ok(command); + } + + let command = command.expect("Finished swaps should not enter to load_from_saved function"); + match command { + TakerSwapCommand::Start => Ok(Some(command)), + TakerSwapCommand::Negotiate =>Ok(Some(command)), + TakerSwapCommand::SendTakerFee => Ok(Some(command)), + TakerSwapCommand::WaitForMakerPayment => Ok(Some(command)), + TakerSwapCommand::ValidateMakerPayment => Ok(Some(command)), + TakerSwapCommand::SendTakerPayment => Ok(Some(command)), + TakerSwapCommand::WaitForTakerPaymentSpend => { + match check_maker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { + validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; + let taker_payment_spend_tx = match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx))) => taker_payment_spend_tx, + Ok(Some(FoundSwapTxSpend::Refunded(payment_refund_tx))) => { + return ERR!( + "A refund transaction {:#?} is found while spend was expected", + payment_refund_tx.tx_hash() + ) + }, + Ok(None) => return ERR!("Taker payment spend could not be found while maker payment is already spent"), + Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), + }; + add_taker_payment_spent_event(swap, &mut saved, taker_payment_spend_tx).await?; + add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; + Ok(Some(TakerSwapCommand::Finish)) + }, + Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => { + let taker_payment_refund_tx = match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(payment_spend_tx))) => return ERR!("Maker has cheated by both spending the taker payment with transaction {:#?}, and refunding the maker payment with transaction {:#?}", payment_spend_tx.tx_hash(), maker_payment_refund_tx.tx_hash()), + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => taker_payment_refund_tx, + Ok(None) => return Ok(Some(command)), + Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), + }; + validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; + add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; + Ok(Some(TakerSwapCommand::Finish)) + }, + Ok(None) => { + let taker_payment_refund_tx = match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(_))) => return Ok(Some(command)), + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => taker_payment_refund_tx, + Ok(None) => return Ok(Some(command)), + Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), + }; + validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; + add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; + Ok(Some(TakerSwapCommand::Finish)) + }, + Err(e) => ERR!("Error {} when trying to find maker payment spend", e) + } + }, + TakerSwapCommand::SpendMakerPayment => { + match check_maker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { + validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; + add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; + Ok(Some(TakerSwapCommand::Finish)) + }, + Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => ERR!("Maker has cheated by both spending the taker payment, and refunding the maker payment with transaction {:#?}", maker_payment_refund_tx.tx_hash()), + Ok(None) => Ok(Some(command)), + Err(e) => ERR!("Error {} when trying to find maker payment spend", e) + } + }, + TakerSwapCommand::PrepareForTakerPaymentRefund | + TakerSwapCommand::RefundTakerPayment => { + let taker_payment_refund_tx = match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(_))) => return ERR!("Taker payment is not expected to be spent at this point"), + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => taker_payment_refund_tx, + Ok(None) => return Ok(Some(command)), + Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), + }; + validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; + add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; + Ok(Some(TakerSwapCommand::Finish)) + }, + TakerSwapCommand::FinalizeTakerPaymentRefund => Ok(Some(command)), + TakerSwapCommand::Finish => Ok(Some(command)), } +} +pub async fn check_maker_payment_spend(swap: &TakerSwap) -> Result, String> { let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; let secret_hash = swap.r().secret_hash.0.clone(); let maker_coin_start_block = swap.r().data.maker_coin_start_block; @@ -2369,105 +2434,56 @@ pub async fn check_watcher_payments(swap: &TakerSwap, ctx: &MmArc, saved: TakerS let unique_data = swap.unique_swap_data(); let watcher_reward = swap.r().watcher_reward; - let search_input = SearchForSwapTxSpendInput { - time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, - other_pub: other_maker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &maker_payment, - search_from_block: maker_coin_start_block, - swap_contract_address: &maker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }; - - match swap.maker_coin.search_for_swap_tx_spend_other(search_input).await { - Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { - save_swap_as_successful(swap, ctx, saved, maker_payment_spend_tx).await - }, - Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => { - let taker_payment = match &swap.r().taker_payment { - Some(tx) => tx.tx_hex.0.clone(), - None => return ERR!("No info about taker payment, swap is not recoverable"), - }; - - let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; - let taker_coin_start_block = swap.r().data.taker_coin_start_block; - let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - - let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { - Ok(_) => swap.r().data.started_at as u32, - Err(_) => swap.r().data.taker_payment_lock as u32, - }; - - let secret_hash = swap.r().secret_hash.0.clone(); - let unique_data = swap.unique_swap_data(); - let watcher_reward = swap.r().watcher_reward; - - let search_input = SearchForSwapTxSpendInput { - time_lock: taker_payment_lock, - other_pub: other_taker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &taker_payment, - search_from_block: taker_coin_start_block, - swap_contract_address: &taker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }; - match swap.taker_coin.search_for_swap_tx_spend_my(search_input).await { - Ok(Some(FoundSwapTxSpend::Spent(payment_spend_tx))) => ERR!("Maker has cheated by both spending the taker payment with transaction {:#?}, and refunding the maker payment with transaction {:#?}", payment_spend_tx.tx_hash(), maker_payment_refund_tx.tx_hash()), - Ok(Some(FoundSwapTxSpend::Refunded(payment_refund_tx))) => save_swap_as_refunded(swap, ctx, saved, payment_refund_tx).await, - Ok(None) => prepare_for_kickstart(ctx, saved).await, - Err(e) => ERR!("Error {} when trying to find taker payment spend", e), - } - }, - Ok(None) => { - let taker_payment = match &swap.r().taker_payment { - Some(tx) => tx.tx_hex.0.clone(), - None => return ERR!("No info about taker payment, swap is not recoverable"), - }; + swap.maker_coin + .search_for_swap_tx_spend_other(SearchForSwapTxSpendInput { + time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, + other_pub: other_maker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &maker_payment, + search_from_block: maker_coin_start_block, + swap_contract_address: &maker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }) + .await +} - let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; - let taker_coin_start_block = swap.r().data.taker_coin_start_block; - let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); +pub async fn check_taker_payment_spend(swap: &TakerSwap) -> Result, String> { + let taker_payment = match &swap.r().taker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about taker payment, swap is not recoverable"), + }; - let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { - Ok(_) => swap.r().data.started_at as u32, - Err(_) => swap.r().data.taker_payment_lock as u32, - }; + let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; + let taker_coin_start_block = swap.r().data.taker_coin_start_block; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - let secret_hash = swap.r().secret_hash.0.clone(); - let unique_data = swap.unique_swap_data(); - let watcher_reward = swap.r().watcher_reward; + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, + }; + let secret_hash = swap.r().secret_hash.0.clone(); + let unique_data = swap.unique_swap_data(); + let watcher_reward = swap.r().watcher_reward; - let search_input = SearchForSwapTxSpendInput { - time_lock: taker_payment_lock, - other_pub: other_taker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &taker_payment, - search_from_block: taker_coin_start_block, - swap_contract_address: &taker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }; - match swap.taker_coin.search_for_swap_tx_spend_my(search_input).await { - Ok(Some(FoundSwapTxSpend::Spent(_))) => prepare_for_kickstart(ctx, saved).await, - Ok(Some(FoundSwapTxSpend::Refunded(payment_refund_tx))) => { - save_swap_as_refunded(swap, ctx, saved, payment_refund_tx).await - }, - Ok(None) => prepare_for_kickstart(ctx, saved).await, - Err(e) => ERR!("Error {} when trying to find taker payment spend", e), - } - }, - Err(e) => ERR!("Error {} when trying to find maker payment spend", e), - } + swap.taker_coin + .search_for_swap_tx_spend_my(SearchForSwapTxSpendInput { + time_lock: taker_payment_lock, + other_pub: other_taker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &taker_payment, + search_from_block: taker_coin_start_block, + swap_contract_address: &taker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }) + .await } -pub async fn save_swap_as_successful( +pub async fn validate_maker_payment_spend( swap: &TakerSwap, - ctx: &MmArc, - mut saved: TakerSavedSwap, - maker_payment_spend_tx: TransactionEnum, -) -> Result { + maker_payment_spend_tx: &TransactionEnum, +) -> Result<(), String> { let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; let secret_hash = swap.r().secret_hash.0.clone(); let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); @@ -2486,111 +2502,13 @@ pub async fn save_swap_as_successful( .compat() .await .map_err(|e| e.to_string())?; - - while !(matches!( - saved.events.last().unwrap().event, - TakerSwapEvent::WatcherMessageSent(_, _) - ) || matches!(saved.events.last().unwrap().event, TakerSwapEvent::TakerPaymentSpent(_))) - { - saved.events.pop(); - } - - if !saved.taker_payment_spent() { - let taker_payment = match &swap.r().taker_payment { - Some(tx) => tx.tx_hex.0.clone(), - None => return ERR!("No info about taker payment, swap is not recoverable"), - }; - - let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; - let taker_coin_start_block = swap.r().data.taker_coin_start_block; - let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - - let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { - Ok(_) => swap.r().data.started_at as u32, - Err(_) => swap.r().data.taker_payment_lock as u32, - }; - - let secret_hash = swap.r().secret_hash.0.clone(); - let unique_data = swap.unique_swap_data(); - let watcher_reward = swap.r().watcher_reward; - - let search_input = SearchForSwapTxSpendInput { - time_lock: taker_payment_lock, - other_pub: other_taker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &taker_payment, - search_from_block: taker_coin_start_block, - swap_contract_address: &taker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }; - let taker_payment_spend_tx = match swap.taker_coin.search_for_swap_tx_spend_my(search_input).await { - Ok(Some(FoundSwapTxSpend::Spent(payment_spend_tx))) => payment_spend_tx, - Ok(Some(FoundSwapTxSpend::Refunded(payment_refund_tx))) => { - return ERR!( - "A refund transaction {:#?} is found while spend was expected", - payment_refund_tx.tx_hash() - ) - }, - Ok(None) => return ERR!("Taker payment spend could not be found while maker payment is already spent"), - Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), - }; - - let tx_hash = taker_payment_spend_tx.tx_hash(); - info!("Taker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), - tx_hash, - }; - let secret = match swap - .taker_coin - .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) - .await - { - Ok(bytes) => H256Json::from(bytes.as_slice()), - Err(_) => { - return ERR!("Could not extract secret from taker payment spend transaction"); - }, - }; - - let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { - transaction: tx_ident, - secret, - }); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - } - - let tx_hash = maker_payment_spend_tx.tx_hash(); - info!("Watcher maker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), - tx_hash, - }; - - let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - - let new_swap = SavedSwap::Taker(saved); - try_s!(new_swap.save_to_db(ctx).await); - - info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); - Ok(true) + Ok(()) } -pub async fn save_swap_as_refunded( +pub async fn validate_taker_payment_refund( swap: &TakerSwap, - ctx: &MmArc, - mut saved: TakerSavedSwap, - taker_payment_refund_tx: TransactionEnum, -) -> Result { + taker_payment_refund_tx: &TransactionEnum, +) -> Result<(), String> { let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { @@ -2614,11 +2532,75 @@ pub async fn save_swap_as_refunded( .compat() .await .map_err(|e| e.to_string())?; + Ok(()) +} - if saved.is_finished() { - saved.events.pop(); - } +pub async fn add_taker_payment_spent_event( + swap: &TakerSwap, + saved: &mut TakerSavedSwap, + taker_payment_spend_tx: TransactionEnum, +) -> Result<(), String> { + let secret_hash = swap.r().secret_hash.0.clone(); + let watcher_reward = swap.r().watcher_reward; + + let tx_hash = taker_payment_spend_tx.tx_hash(); + info!("Taker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), + tx_hash, + }; + let secret = match swap + .taker_coin + .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) + .await + { + Ok(bytes) => H256Json::from(bytes.as_slice()), + Err(_) => { + return ERR!("Could not extract secret from taker payment spend transaction"); + }, + }; + + let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { + transaction: tx_ident, + secret, + }); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + Ok(()) +} + +pub async fn add_maker_payment_spent_by_watcher_event( + mut saved: TakerSavedSwap, + ctx: &MmArc, + maker_payment_spend_tx: TransactionEnum, +) -> Result<(), String> { + let tx_hash = maker_payment_spend_tx.tx_hash(); + info!("Watcher maker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + let new_swap = SavedSwap::Taker(saved); + try_s!(new_swap.save_to_db(ctx).await); + info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); + Ok(()) +} +pub async fn add_taker_payment_refunded_by_watcher_event( + mut saved: TakerSavedSwap, + ctx: &MmArc, + taker_payment_refund_tx: TransactionEnum, +) -> Result<(), String> { let tx_hash = taker_payment_refund_tx.tx_hash(); info!("Taker refund tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { @@ -2635,26 +2617,8 @@ pub async fn save_swap_as_refunded( let new_swap = SavedSwap::Taker(saved); try_s!(new_swap.save_to_db(ctx).await); - info!("Taker payment is refunded by the watcher"); - Ok(true) -} - -pub async fn prepare_for_kickstart(ctx: &MmArc, mut saved: TakerSavedSwap) -> Result { - if saved.is_finished() { - saved.events.pop(); - let event = TakerSwapEvent::WatcherSpendOrRefundNotFound; - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - let new_swap = SavedSwap::Taker(saved); - try_s!(new_swap.save_to_db(ctx).await); - Ok(true) - } else { - Ok(false) - } + Ok(()) } pub async fn check_balance_for_taker_swap( 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 de8b0fa927..3ccdd4c5ef 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -356,7 +356,7 @@ fn run_watcher_node( } #[test] -fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_panic_at_wait_for_taker_payment_spend() { +fn test_taker_saves_the_swap_as_successful_after_restart_panic_at_wait_for_taker_payment_spend() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[("TAKER_FAIL_AT", "wait_for_taker_payment_spend_panic")]); @@ -378,6 +378,8 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan 25., 2., )); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); @@ -406,6 +408,127 @@ fn test_taker_saves_the_swap_as_successful_after_restart_maker_payment_spent_pan check_actual_events(&mm_alice, &uuids[0], &expected_events); } +#[test] +fn test_taker_saves_the_swap_as_successful_after_restart_panic_at_maker_payment_spend() { + let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[("TAKER_FAIL_AT", "maker_payment_spend_panic")]); + let mut mm_bob = run_maker_node(&coins, &[], &[&mm_alice.ip.to_string()]); + + let watcher_conf = WatcherConf { + wait_taker_payment: 0., + wait_maker_payment_spend_factor: 0., + refund_start_factor: 1.5, + search_interval: 1.0, + }; + let mut mm_watcher = run_watcher_node(&coins, &[], &[&mm_alice.ip.to_string()], watcher_conf); + + let uuids = block_on(start_swaps( + &mut mm_bob, + &mut mm_alice, + &[("MYCOIN1", "MYCOIN")], + 25., + 25., + 2., + )); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); + block_on(mm_bob.wait_for_log(120., |log| log.contains(&format!("[swap uuid={}] Finished", &uuids[0])))).unwrap(); + block_on(mm_watcher.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SPEND_SENT_LOG))).unwrap(); + + restart_taker_and_wait_until(&alice_conf, &[], &format!("[swap uuid={}] Finished", &uuids[0])); + block_on(mm_alice.stop()).unwrap(); + + let mm_alice = restart_taker_and_wait_until(&alice_conf, &[], &format!("{} {}", SWAP_FINISHED_LOG, uuids[0])); + + let expected_events = [ + "Started", + "Negotiated", + "TakerFeeSent", + "TakerPaymentInstructionsReceived", + "MakerPaymentReceived", + "MakerPaymentWaitConfirmStarted", + "MakerPaymentValidatedAndConfirmed", + "TakerPaymentSent", + "WatcherMessageSent", + "TakerPaymentSpent", + "MakerPaymentSpentByWatcher", + "Finished", + ]; + check_actual_events(&mm_alice, &uuids[0], &expected_events); +} + +#[test] +fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_panic_at_wait_for_taker_payment_spend() { + let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ + ("USE_TEST_LOCKTIME", ""), + ("TAKER_FAIL_AT", "wait_for_taker_payment_spend_panic"), + ]); + let mut mm_bob = run_maker_node(&coins, &[("USE_TEST_LOCKTIME", "")], &[&mm_alice.ip.to_string()]); + + let watcher_conf = WatcherConf { + wait_taker_payment: 0., + wait_maker_payment_spend_factor: 1., + refund_start_factor: 0., + search_interval: 1., + }; + let mut mm_watcher = run_watcher_node( + &coins, + &[("USE_TEST_LOCKTIME", "")], + &[&mm_alice.ip.to_string()], + watcher_conf, + ); + + let uuids = block_on(start_swaps( + &mut mm_bob, + &mut mm_alice, + &[("MYCOIN1", "MYCOIN")], + 25., + 25., + 2., + )); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); + alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); + + block_on(mm_bob.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SENT_LOG))).unwrap(); + block_on(mm_bob.stop()).unwrap(); + + block_on(mm_alice.wait_for_log(120., |log| log.contains(WATCHER_MESSAGE_SENT_LOG))).unwrap(); + block_on(mm_watcher.wait_for_log(120., |log| log.contains(TAKER_PAYMENT_REFUND_SENT_LOG))).unwrap(); + + restart_taker_and_wait_until( + &alice_conf, + &[("USE_TEST_LOCKTIME", "")], + &format!("[swap uuid={}] Finished", &uuids[0]), + ); + block_on(mm_alice.stop()).unwrap(); + + let mm_alice = restart_taker_and_wait_until( + &alice_conf, + &[("USE_TEST_LOCKTIME", "")], + &format!("{} {}", SWAP_FINISHED_LOG, uuids[0]), + ); + + let expected_events = [ + "Started", + "Negotiated", + "TakerFeeSent", + "TakerPaymentInstructionsReceived", + "MakerPaymentReceived", + "MakerPaymentWaitConfirmStarted", + "MakerPaymentValidatedAndConfirmed", + "TakerPaymentSent", + "WatcherMessageSent", + "TakerPaymentRefundedByWatcher", + "Finished", + ]; + check_actual_events(&mm_alice, &uuids[0], &expected_events); +} + #[test] fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_panic_at_taker_payment_refund() { let coins = json!([mycoin_conf(1000), mycoin1_conf(1000)]); @@ -436,6 +559,8 @@ fn test_taker_saves_the_swap_as_finished_after_restart_taker_payment_refunded_pa 25., 2., )); + let (_alice_dump_log, _alice_dump_dashboard) = mm_alice.mm_dump(); + log!("Alice log path: {}", mm_alice.log_path.display()); alice_conf.conf["dbdir"] = mm_alice.folder.join("DB").to_str().unwrap().into(); block_on(mm_bob.wait_for_log(120., |log| log.contains(MAKER_PAYMENT_SENT_LOG))).unwrap(); From a689a626b9d457882fc7377783efcec5d1a9d904 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Tue, 15 Aug 2023 15:39:58 +0300 Subject: [PATCH 38/53] minor improvements --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 51 +++++++---------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 1825efd5b2..3c588d2137 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -2349,47 +2349,26 @@ pub async fn check_watcher_payments( TakerSwapCommand::ValidateMakerPayment => Ok(Some(command)), TakerSwapCommand::SendTakerPayment => Ok(Some(command)), TakerSwapCommand::WaitForTakerPaymentSpend => { - match check_maker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { - validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; - let taker_payment_spend_tx = match check_taker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx))) => taker_payment_spend_tx, - Ok(Some(FoundSwapTxSpend::Refunded(payment_refund_tx))) => { - return ERR!( - "A refund transaction {:#?} is found while spend was expected", - payment_refund_tx.tx_hash() - ) + match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx))) => { + add_taker_payment_spent_event(swap, &mut saved, &taker_payment_spend_tx).await?; + match check_maker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { + add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; + Ok(Some(TakerSwapCommand::Finish)) }, - Ok(None) => return ERR!("Taker payment spend could not be found while maker payment is already spent"), - Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), - }; - add_taker_payment_spent_event(swap, &mut saved, taker_payment_spend_tx).await?; - add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; - Ok(Some(TakerSwapCommand::Finish)) - }, - Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => { - let taker_payment_refund_tx = match check_taker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(payment_spend_tx))) => return ERR!("Maker has cheated by both spending the taker payment with transaction {:#?}, and refunding the maker payment with transaction {:#?}", payment_spend_tx.tx_hash(), maker_payment_refund_tx.tx_hash()), - Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => taker_payment_refund_tx, - Ok(None) => return Ok(Some(command)), - Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), - }; - validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; - add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; - Ok(Some(TakerSwapCommand::Finish)) + Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => ERR!("Maker has cheated by both spending the taker payment with transaction {:#?}, and refunding the maker payment with transaction {:#?}", taker_payment_spend_tx.tx_hash(), maker_payment_refund_tx.tx_hash()), + Ok(None) => Ok(Some(TakerSwapCommand::SpendMakerPayment)), + Err(e) => ERR!("Error {} when trying to find maker payment spend", e), + } }, - Ok(None) => { - let taker_payment_refund_tx = match check_taker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(_))) => return Ok(Some(command)), - Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => taker_payment_refund_tx, - Ok(None) => return Ok(Some(command)), - Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), - }; + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; Ok(Some(TakerSwapCommand::Finish)) }, - Err(e) => ERR!("Error {} when trying to find maker payment spend", e) + Ok(None) => Ok(Some(command)), + Err(e) => ERR!("Error {} when trying to find taker payment spend", e), } }, TakerSwapCommand::SpendMakerPayment => { @@ -2538,7 +2517,7 @@ pub async fn validate_taker_payment_refund( pub async fn add_taker_payment_spent_event( swap: &TakerSwap, saved: &mut TakerSavedSwap, - taker_payment_spend_tx: TransactionEnum, + taker_payment_spend_tx: &TransactionEnum, ) -> Result<(), String> { let secret_hash = swap.r().secret_hash.0.clone(); let watcher_reward = swap.r().watcher_reward; From 563b9d9daf51101e43d86550ec49b9ef5cf9ef21 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Tue, 15 Aug 2023 23:08:11 +0300 Subject: [PATCH 39/53] check time before checking watcher refund on restart --- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 12 ++++++++---- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 19 +++++++++++++++++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 4ec71f1e2b..56769cf387 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -56,9 +56,9 @@ impl WatcherContext { pub struct WatcherConf { #[serde(default = "common::sixty_f64")] wait_taker_payment: f64, - #[serde(default = "common::one_f64")] + #[serde(default = "default_watcher_maker_payment_spend_factor")] wait_maker_payment_spend_factor: f64, - #[serde(default = "common::one_and_half_f64")] + #[serde(default = "default_watcher_refund_factor")] refund_start_factor: f64, #[serde(default = "common::three_hundred_f64")] search_interval: f64, @@ -68,13 +68,17 @@ impl Default for WatcherConf { fn default() -> Self { WatcherConf { wait_taker_payment: common::sixty_f64(), - wait_maker_payment_spend_factor: common::one_f64(), - refund_start_factor: common::one_and_half_f64(), + wait_maker_payment_spend_factor: default_watcher_maker_payment_spend_factor(), + refund_start_factor: default_watcher_refund_factor(), search_interval: common::three_hundred_f64(), } } } +pub fn default_watcher_maker_payment_spend_factor() -> f64 { common::one_f64() } + +pub fn default_watcher_refund_factor() -> f64 { common::one_and_half_f64() } + #[derive(Clone, Debug, Deserialize, Serialize)] pub enum SwapWatcherMsg { TakerSwapWatcherMsg(TakerSwapWatcherData), diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 3c588d2137..6235dab684 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -41,6 +41,9 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; use uuid::Uuid; +#[cfg(not(any(test, feature = "run-docker-tests")))] +use super::swap_watcher::{default_watcher_maker_payment_spend_factor, default_watcher_refund_factor}; + const TAKER_PAYMENT_SPEND_SEARCH_INTERVAL: f64 = 10.; pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ @@ -2336,8 +2339,12 @@ pub async fn check_watcher_payments( command: Option, ) -> Result, String> { #[cfg(not(any(test, feature = "run-docker-tests")))] - if now_sec() < swap.r().data.taker_payment_lock { - return Ok(command); + { + let watcher_refund_time = swap.r().data.started_at + + (default_watcher_maker_payment_spend_factor() * swap.r().data.lock_duration as f64) as u64; + if now_sec() < watcher_refund_time { + return Ok(command); + } } let command = command.expect("Finished swaps should not enter to load_from_saved function"); @@ -2385,6 +2392,14 @@ pub async fn check_watcher_payments( }, TakerSwapCommand::PrepareForTakerPaymentRefund | TakerSwapCommand::RefundTakerPayment => { + #[cfg(not(any(test, feature = "run-docker-tests")))] + { + let watcher_refund_time = swap.r().data.started_at + (default_watcher_refund_factor() * swap.r().data.lock_duration as f64) as u64; + if now_sec() < watcher_refund_time { + return Ok(Some(command)); + } + } + let taker_payment_refund_tx = match check_taker_payment_spend(swap).await { Ok(Some(FoundSwapTxSpend::Spent(_))) => return ERR!("Taker payment is not expected to be spent at this point"), Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => taker_payment_refund_tx, From 87c66e73ae3ddf5f7233444311e90df05c81d97c Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Wed, 16 Aug 2023 15:03:51 +0300 Subject: [PATCH 40/53] remove the WatcherSpendOrRefundNotFound event --- mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs | 1 - mm2src/mm2_main/src/lp_swap/taker_swap.rs | 4 ---- 2 files changed, 5 deletions(-) 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 603ff839fd..56e7877b70 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -274,7 +274,6 @@ fn convert_taker_to_maker_events( | TakerSwapEvent::TakerPaymentRefundFailed(_) | TakerSwapEvent::TakerPaymentRefundFinished | TakerSwapEvent::TakerPaymentRefundedByWatcher(_) - | TakerSwapEvent::WatcherSpendOrRefundNotFound | TakerSwapEvent::Finished => {} } } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 6235dab684..018ef89a06 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -190,7 +190,6 @@ impl TakerSavedEvent { TakerSwapEvent::TakerPaymentRefundFailed(_) => Some(TakerSwapCommand::Finish), TakerSwapEvent::TakerPaymentRefundFinished => Some(TakerSwapCommand::Finish), TakerSwapEvent::TakerPaymentRefundedByWatcher(_) => Some(TakerSwapCommand::Finish), - TakerSwapEvent::WatcherSpendOrRefundNotFound => Some(TakerSwapCommand::Finish), TakerSwapEvent::Finished => None, } } @@ -665,7 +664,6 @@ pub enum TakerSwapEvent { TakerPaymentRefundFailed(SwapError), TakerPaymentRefundFinished, TakerPaymentRefundedByWatcher(Option), - WatcherSpendOrRefundNotFound, Finished, } @@ -706,7 +704,6 @@ impl TakerSwapEvent { TakerSwapEvent::TakerPaymentRefundFailed(_) => "Taker payment refund failed...".to_owned(), TakerSwapEvent::TakerPaymentRefundFinished => "Taker payment refund finished...".to_owned(), TakerSwapEvent::TakerPaymentRefundedByWatcher(_) => "Taker payment refunded by watcher...".to_owned(), - TakerSwapEvent::WatcherSpendOrRefundNotFound => "Watcher refund could not be found...".to_owned(), TakerSwapEvent::Finished => "Finished".to_owned(), } } @@ -844,7 +841,6 @@ impl TakerSwap { TakerSwapEvent::TakerPaymentRefundFailed(err) => self.errors.lock().push(err), TakerSwapEvent::TakerPaymentRefundFinished => (), TakerSwapEvent::TakerPaymentRefundedByWatcher(tx) => self.w().taker_payment_refund = tx, - TakerSwapEvent::WatcherSpendOrRefundNotFound => (), TakerSwapEvent::Finished => self.finished_at.store(now_sec(), Ordering::Relaxed), } } From 052f9a472a045c07e842c6d9494400f2b2a44cb1 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 17 Aug 2023 15:34:25 +0300 Subject: [PATCH 41/53] minor improvements on get_watcher_payments function --- mm2src/mm2_main/src/lp_swap.rs | 9 ++-- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 52 ++++++++++++----------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 9a79722556..97806a86a2 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -122,10 +122,11 @@ pub use swap_watcher::{process_watcher_msg, watcher_topic, TakerSwapWatcherData, MAKER_PAYMENT_SPEND_SENT_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, TAKER_SWAP_ENTRY_TIMEOUT_SEC, WATCHER_PREFIX}; use taker_swap::TakerSwapEvent; -pub use taker_swap::{calc_max_taker_vol, check_balance_for_taker_swap, check_watcher_payments, max_taker_vol, - max_taker_vol_from_available, run_taker_swap, taker_swap_trade_preimage, RunTakerSwapInput, - TakerSavedSwap, TakerSwap, TakerSwapData, TakerSwapPreparedParams, TakerTradePreimage, - MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, REFUND_TEST_FAILURE_LOG, WATCHER_MESSAGE_SENT_LOG}; +pub use taker_swap::{calc_max_taker_vol, check_balance_for_taker_swap, get_command_based_on_watcher_activity, + max_taker_vol, max_taker_vol_from_available, run_taker_swap, taker_swap_trade_preimage, + RunTakerSwapInput, TakerSavedSwap, TakerSwap, TakerSwapData, TakerSwapPreparedParams, + TakerTradePreimage, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, REFUND_TEST_FAILURE_LOG, + WATCHER_MESSAGE_SENT_LOG}; pub use trade_preimage::trade_preimage_rpc; pub const SWAP_PREFIX: TopicPrefix = "swap"; diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 966b555992..c121de6148 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -2006,7 +2006,12 @@ impl TakerSwap { #[cfg(any(test, feature = "run-docker-tests"))] fail_at, ); - let mut command = saved.events.last().unwrap().get_command(); + let mut command = saved + .events + .last() + .unwrap() + .get_command() + .ok_or("Finished swaps should not enter to load_from_saved function")?; for saved_event in &saved.events { swap.apply_event(saved_event.event.clone()); } @@ -2015,10 +2020,10 @@ impl TakerSwap { && maker_coin.is_supported_by_watchers() && saved.watcher_message_sent() { - command = check_watcher_payments(&swap, &ctx, saved, command).await?; + command = get_command_based_on_watcher_activity(&swap, &ctx, saved, command).await?; } - Ok((swap, command)) + Ok((swap, Some(command))) } pub async fn recover_funds(&self) -> Result { @@ -2328,12 +2333,12 @@ pub struct TakerSwapPreparedParams { maker_payment_spend_trade_fee: TradeFee, } -pub async fn check_watcher_payments( +pub async fn get_command_based_on_watcher_activity( swap: &TakerSwap, ctx: &MmArc, mut saved: TakerSavedSwap, - command: Option, -) -> Result, String> { + command: TakerSwapCommand, +) -> Result { #[cfg(not(any(test, feature = "run-docker-tests")))] { let watcher_refund_time = swap.r().data.started_at @@ -2343,14 +2348,13 @@ pub async fn check_watcher_payments( } } - let command = command.expect("Finished swaps should not enter to load_from_saved function"); match command { - TakerSwapCommand::Start => Ok(Some(command)), - TakerSwapCommand::Negotiate =>Ok(Some(command)), - TakerSwapCommand::SendTakerFee => Ok(Some(command)), - TakerSwapCommand::WaitForMakerPayment => Ok(Some(command)), - TakerSwapCommand::ValidateMakerPayment => Ok(Some(command)), - TakerSwapCommand::SendTakerPayment => Ok(Some(command)), + TakerSwapCommand::Start => Ok(command), + TakerSwapCommand::Negotiate => Ok(command), + TakerSwapCommand::SendTakerFee => Ok(command), + TakerSwapCommand::WaitForMakerPayment => Ok(command), + TakerSwapCommand::ValidateMakerPayment => Ok(command), + TakerSwapCommand::SendTakerPayment => Ok(command), TakerSwapCommand::WaitForTakerPaymentSpend => { match check_taker_payment_spend(swap).await { Ok(Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx))) => { @@ -2358,19 +2362,19 @@ pub async fn check_watcher_payments( match check_maker_payment_spend(swap).await { Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; - Ok(Some(TakerSwapCommand::Finish)) + Ok(TakerSwapCommand::Finish) }, Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => ERR!("Maker has cheated by both spending the taker payment with transaction {:#?}, and refunding the maker payment with transaction {:#?}", taker_payment_spend_tx.tx_hash(), maker_payment_refund_tx.tx_hash()), - Ok(None) => Ok(Some(TakerSwapCommand::SpendMakerPayment)), + Ok(None) => Ok(TakerSwapCommand::SpendMakerPayment), Err(e) => ERR!("Error {} when trying to find maker payment spend", e), } }, Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; - Ok(Some(TakerSwapCommand::Finish)) + Ok(TakerSwapCommand::Finish) }, - Ok(None) => Ok(Some(command)), + Ok(None) => Ok(command), Err(e) => ERR!("Error {} when trying to find taker payment spend", e), } }, @@ -2379,10 +2383,10 @@ pub async fn check_watcher_payments( Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; - Ok(Some(TakerSwapCommand::Finish)) + Ok(TakerSwapCommand::Finish) }, Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => ERR!("Maker has cheated by both spending the taker payment, and refunding the maker payment with transaction {:#?}", maker_payment_refund_tx.tx_hash()), - Ok(None) => Ok(Some(command)), + Ok(None) => Ok(command), Err(e) => ERR!("Error {} when trying to find maker payment spend", e) } }, @@ -2392,22 +2396,22 @@ pub async fn check_watcher_payments( { let watcher_refund_time = swap.r().data.started_at + (default_watcher_refund_factor() * swap.r().data.lock_duration as f64) as u64; if now_sec() < watcher_refund_time { - return Ok(Some(command)); + return Ok(command); } } let taker_payment_refund_tx = match check_taker_payment_spend(swap).await { Ok(Some(FoundSwapTxSpend::Spent(_))) => return ERR!("Taker payment is not expected to be spent at this point"), Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => taker_payment_refund_tx, - Ok(None) => return Ok(Some(command)), + Ok(None) => return Ok(command), Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), }; validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; - Ok(Some(TakerSwapCommand::Finish)) + Ok(TakerSwapCommand::Finish) }, - TakerSwapCommand::FinalizeTakerPaymentRefund => Ok(Some(command)), - TakerSwapCommand::Finish => Ok(Some(command)), + TakerSwapCommand::FinalizeTakerPaymentRefund => Ok(command), + TakerSwapCommand::Finish => Ok(command), } } From 08a0e42e642f341bafd8ddf5b68f521b1ec71c75 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 17 Aug 2023 15:38:23 +0300 Subject: [PATCH 42/53] minor fixes --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index c121de6148..3661425f54 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -2006,15 +2006,17 @@ impl TakerSwap { #[cfg(any(test, feature = "run-docker-tests"))] fail_at, ); + + for saved_event in &saved.events { + swap.apply_event(saved_event.event.clone()); + } + let mut command = saved .events .last() .unwrap() .get_command() .ok_or("Finished swaps should not enter to load_from_saved function")?; - for saved_event in &saved.events { - swap.apply_event(saved_event.event.clone()); - } if taker_coin.is_supported_by_watchers() && maker_coin.is_supported_by_watchers() @@ -2022,6 +2024,7 @@ impl TakerSwap { { command = get_command_based_on_watcher_activity(&swap, &ctx, saved, command).await?; } + drop_mutability!(command); Ok((swap, Some(command))) } From ee7b9b260012b8c802671e576993eba456fa548c Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 17 Aug 2023 19:43:28 +0300 Subject: [PATCH 43/53] move taker restart related functionality to swap watcher module --- mm2src/mm2_main/src/lp_swap.rs | 9 +- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 296 +++++++++++++++++- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 315 +------------------- 3 files changed, 311 insertions(+), 309 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 97806a86a2..ac5a1d7a74 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -122,11 +122,10 @@ pub use swap_watcher::{process_watcher_msg, watcher_topic, TakerSwapWatcherData, MAKER_PAYMENT_SPEND_SENT_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, TAKER_SWAP_ENTRY_TIMEOUT_SEC, WATCHER_PREFIX}; use taker_swap::TakerSwapEvent; -pub use taker_swap::{calc_max_taker_vol, check_balance_for_taker_swap, get_command_based_on_watcher_activity, - max_taker_vol, max_taker_vol_from_available, run_taker_swap, taker_swap_trade_preimage, - RunTakerSwapInput, TakerSavedSwap, TakerSwap, TakerSwapData, TakerSwapPreparedParams, - TakerTradePreimage, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, REFUND_TEST_FAILURE_LOG, - WATCHER_MESSAGE_SENT_LOG}; +pub use taker_swap::{calc_max_taker_vol, check_balance_for_taker_swap, max_taker_vol, max_taker_vol_from_available, + run_taker_swap, taker_swap_trade_preimage, RunTakerSwapInput, TakerSavedSwap, TakerSwap, + TakerSwapData, TakerSwapPreparedParams, TakerTradePreimage, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG, + REFUND_TEST_FAILURE_LOG, WATCHER_MESSAGE_SENT_LOG}; pub use trade_preimage::trade_preimage_rpc; pub const SWAP_PREFIX: TopicPrefix = "swap"; diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 56769cf387..fd20abfc2f 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -1,22 +1,28 @@ +use super::taker_swap::TakerSwapCommand; use super::{broadcast_p2p_tx_msg, get_payment_locktime, lp_coinfind, taker_payment_spend_deadline, tx_helper_topic, - H256Json, SwapsContext, WAIT_CONFIRM_INTERVAL_SEC}; + AtomicSwap, H256Json, SwapsContext, TakerSavedSwap, TakerSwap, WAIT_CONFIRM_INTERVAL_SEC}; use crate::mm2::lp_network::{P2PRequestError, P2PRequestResult}; +use crate::mm2::lp_swap::taker_swap::{TakerPaymentSpentData, TakerSavedEvent, TakerSwapEvent}; +use crate::mm2::lp_swap::{SavedSwap, SavedSwapIo, TransactionIdentifier, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG}; use crate::mm2::MmError; use async_trait::async_trait; use coins::{CanRefundHtlc, ConfirmPaymentInput, FoundSwapTxSpend, MmCoinEnum, RefundPaymentArgs, - SendMakerPaymentSpendPreimageInput, WaitForHTLCTxSpendArgs, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput}; + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, TransactionEnum, ValidateWatcherSpendInput, + WaitForHTLCTxSpendArgs, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + WatcherValidateTakerFeeInput}; use common::executor::{AbortSettings, SpawnAbortable, Timer}; use common::log::{debug, error, info}; use common::state_machine::prelude::*; -use common::{now_sec, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{now_ms, now_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use futures::compat::Future01CompatExt; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::MapToMmResult; use mm2_libp2p::{decode_signed, pub_sub_topic, TopicPrefix}; +use rpc::v1::types::Bytes; use serde::{Deserialize, Serialize}; use serde_json as json; use std::cmp::min; +use std::sync::atomic::Ordering; use std::sync::Arc; use uuid::Uuid; @@ -657,3 +663,285 @@ fn spawn_taker_swap_watcher(ctx: MmArc, watcher_data: TakerSwapWatcherData, veri } pub fn watcher_topic(ticker: &str) -> String { pub_sub_topic(WATCHER_PREFIX, ticker) } + +pub async fn get_command_based_on_watcher_activity( + swap: &TakerSwap, + ctx: &MmArc, + mut saved: TakerSavedSwap, + command: TakerSwapCommand, +) -> Result { + #[cfg(not(any(test, feature = "run-docker-tests")))] + { + let watcher_refund_time = swap.r().data.started_at + + (default_watcher_maker_payment_spend_factor() * swap.r().data.lock_duration as f64) as u64; + if now_sec() < watcher_refund_time { + return Ok(command); + } + } + + match command { + TakerSwapCommand::Start => Ok(command), + TakerSwapCommand::Negotiate => Ok(command), + TakerSwapCommand::SendTakerFee => Ok(command), + TakerSwapCommand::WaitForMakerPayment => Ok(command), + TakerSwapCommand::ValidateMakerPayment => Ok(command), + TakerSwapCommand::SendTakerPayment => Ok(command), + TakerSwapCommand::WaitForTakerPaymentSpend => { + match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx))) => { + add_taker_payment_spent_event(swap, &mut saved, &taker_payment_spend_tx).await?; + match check_maker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { + add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; + Ok(TakerSwapCommand::Finish) + }, + Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => ERR!("Maker has cheated by both spending the taker payment with transaction {:#?}, and refunding the maker payment with transaction {:#?}", taker_payment_spend_tx.tx_hash(), maker_payment_refund_tx.tx_hash()), + Ok(None) => Ok(TakerSwapCommand::SpendMakerPayment), + Err(e) => ERR!("Error {} when trying to find maker payment spend", e), + } + }, + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { + validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; + add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; + Ok(TakerSwapCommand::Finish) + }, + Ok(None) => Ok(command), + Err(e) => ERR!("Error {} when trying to find taker payment spend", e), + } + }, + TakerSwapCommand::SpendMakerPayment => { + match check_maker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { + validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; + add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; + Ok(TakerSwapCommand::Finish) + }, + Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => ERR!("Maker has cheated by both spending the taker payment, and refunding the maker payment with transaction {:#?}", maker_payment_refund_tx.tx_hash()), + Ok(None) => Ok(command), + Err(e) => ERR!("Error {} when trying to find maker payment spend", e) + } + }, + TakerSwapCommand::PrepareForTakerPaymentRefund | + TakerSwapCommand::RefundTakerPayment => { + #[cfg(not(any(test, feature = "run-docker-tests")))] + { + let watcher_refund_time = swap.r().data.started_at + (default_watcher_refund_factor() * swap.r().data.lock_duration as f64) as u64; + if now_sec() < watcher_refund_time { + return Ok(command); + } + } + + let taker_payment_refund_tx = match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(_))) => return ERR!("Taker payment is not expected to be spent at this point"), + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => taker_payment_refund_tx, + Ok(None) => return Ok(command), + Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), + }; + validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; + add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; + Ok(TakerSwapCommand::Finish) + }, + TakerSwapCommand::FinalizeTakerPaymentRefund => Ok(command), + TakerSwapCommand::Finish => Ok(command), + } +} + +pub async fn check_maker_payment_spend(swap: &TakerSwap) -> Result, String> { + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let secret_hash = swap.r().secret_hash.0.clone(); + let maker_coin_start_block = swap.r().data.maker_coin_start_block; + let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); + + let maker_payment = match &swap.r().maker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about maker payment, swap is not recoverable"), + }; + let unique_data = swap.unique_swap_data(); + let watcher_reward = swap.r().watcher_reward; + + swap.maker_coin + .search_for_swap_tx_spend_other(SearchForSwapTxSpendInput { + time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, + other_pub: other_maker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &maker_payment, + search_from_block: maker_coin_start_block, + swap_contract_address: &maker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }) + .await +} + +pub async fn check_taker_payment_spend(swap: &TakerSwap) -> Result, String> { + let taker_payment = match &swap.r().taker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about taker payment, swap is not recoverable"), + }; + + let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; + let taker_coin_start_block = swap.r().data.taker_coin_start_block; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); + + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, + }; + let secret_hash = swap.r().secret_hash.0.clone(); + let unique_data = swap.unique_swap_data(); + let watcher_reward = swap.r().watcher_reward; + + swap.taker_coin + .search_for_swap_tx_spend_my(SearchForSwapTxSpendInput { + time_lock: taker_payment_lock, + other_pub: other_taker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &taker_payment, + search_from_block: taker_coin_start_block, + swap_contract_address: &taker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }) + .await +} + +pub async fn validate_maker_payment_spend( + swap: &TakerSwap, + maker_payment_spend_tx: &TransactionEnum, +) -> Result<(), String> { + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let secret_hash = swap.r().secret_hash.0.clone(); + let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend_tx.tx_hex(), + maker_pub: other_maker_coin_htlc_pub.to_vec(), + swap_contract_address: maker_coin_swap_contract_address, + time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, + secret_hash: secret_hash.clone(), + amount: swap.maker_amount.to_decimal(), + watcher_reward: None, + }; + swap.maker_coin + .taker_validates_maker_payment_spend(validate_input) + .compat() + .await + .map_err(|e| e.to_string())?; + Ok(()) +} + +pub async fn validate_taker_payment_refund( + swap: &TakerSwap, + taker_payment_refund_tx: &TransactionEnum, +) -> Result<(), String> { + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, + }; + let secret_hash = swap.r().secret_hash.0.clone(); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund_tx.tx_hex(), + maker_pub: other_maker_coin_htlc_pub.to_vec(), + swap_contract_address: taker_coin_swap_contract_address, + time_lock: taker_payment_lock, + secret_hash: secret_hash.clone(), + amount: swap.taker_amount.to_decimal(), + watcher_reward: None, + }; + + swap.taker_coin + .taker_validates_taker_payment_refund(validate_input) + .compat() + .await + .map_err(|e| e.to_string())?; + Ok(()) +} + +pub async fn add_taker_payment_spent_event( + swap: &TakerSwap, + saved: &mut TakerSavedSwap, + taker_payment_spend_tx: &TransactionEnum, +) -> Result<(), String> { + let secret_hash = swap.r().secret_hash.0.clone(); + let watcher_reward = swap.r().watcher_reward; + + let tx_hash = taker_payment_spend_tx.tx_hash(); + info!("Taker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: Bytes::from(taker_payment_spend_tx.tx_hex()), + tx_hash, + }; + let secret = match swap + .taker_coin + .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) + .await + { + Ok(bytes) => H256Json::from(bytes.as_slice()), + Err(_) => { + return ERR!("Could not extract secret from taker payment spend transaction"); + }, + }; + + let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { + transaction: tx_ident, + secret, + }); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + Ok(()) +} + +pub async fn add_maker_payment_spent_by_watcher_event( + mut saved: TakerSavedSwap, + ctx: &MmArc, + maker_payment_spend_tx: TransactionEnum, +) -> Result<(), String> { + let tx_hash = maker_payment_spend_tx.tx_hash(); + info!("Watcher maker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: Bytes::from(maker_payment_spend_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + let new_swap = SavedSwap::Taker(saved); + try_s!(new_swap.save_to_db(ctx).await); + info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); + Ok(()) +} + +pub async fn add_taker_payment_refunded_by_watcher_event( + mut saved: TakerSavedSwap, + ctx: &MmArc, + taker_payment_refund_tx: TransactionEnum, +) -> Result<(), String> { + let tx_hash = taker_payment_refund_tx.tx_hash(); + info!("Taker refund tx hash {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: Bytes::from(taker_payment_refund_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::TakerPaymentRefundedByWatcher(Some(tx_ident)); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + + let new_swap = SavedSwap::Taker(saved); + try_s!(new_swap.save_to_db(ctx).await); + info!("Taker payment is refunded by the watcher"); + Ok(()) +} diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 3661425f54..d79668f674 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -12,14 +12,14 @@ use super::{broadcast_my_swap_status, broadcast_swap_message, broadcast_swap_msg SwapTxDataMsg, SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL_SEC}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::TakerOrderBuilder; +use crate::mm2::lp_swap::swap_watcher::get_command_based_on_watcher_activity; use crate::mm2::lp_swap::{broadcast_p2p_tx_msg, broadcast_swap_msg_every_delayed, tx_helper_topic, wait_for_maker_payment_conf_duration, TakerSwapWatcherData}; use coins::lp_price::fetch_swap_coins_price; use coins::{lp_coinfind, CanRefundHtlc, CheckIfMyPaymentSentArgs, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, MmCoinEnum, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, RefundPaymentArgs, SearchForSwapTxSpendInput, SendPaymentArgs, SpendPaymentArgs, TradeFee, - TradePreimageValue, TransactionEnum, ValidatePaymentInput, ValidateWatcherSpendInput, - WaitForHTLCTxSpendArgs}; + TradePreimageValue, ValidatePaymentInput, WaitForHTLCTxSpendArgs}; use common::executor::Timer; use common::log::{debug, error, info, warn}; use common::{bits256, now_ms, now_sec, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; @@ -41,9 +41,6 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; use uuid::Uuid; -#[cfg(not(any(test, feature = "run-docker-tests")))] -use super::swap_watcher::{default_watcher_maker_payment_spend_factor, default_watcher_refund_factor}; - const TAKER_PAYMENT_SPEND_SEARCH_INTERVAL: f64 = 10.; pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ @@ -543,20 +540,20 @@ pub struct TakerSwapData { } pub struct TakerSwapMut { - data: TakerSwapData, - other_maker_coin_htlc_pub: H264, - other_taker_coin_htlc_pub: H264, + pub data: TakerSwapData, + pub other_maker_coin_htlc_pub: H264, + pub other_taker_coin_htlc_pub: H264, taker_fee: Option, - maker_payment: Option, - taker_payment: Option, + pub maker_payment: Option, + pub taker_payment: Option, maker_payment_spend: Option, taker_payment_spend: Option, maker_payment_spend_preimage: Option>, taker_payment_refund_preimage: Option>, taker_payment_refund: Option, - secret_hash: BytesJson, + pub secret_hash: BytesJson, secret: H256Json, - watcher_reward: bool, + pub watcher_reward: bool, reward_amount: Option, payment_instructions: Option, } @@ -589,15 +586,15 @@ impl From for FailAt { pub struct TakerSwap { ctx: MmArc, - maker_coin: MmCoinEnum, - taker_coin: MmCoinEnum, - maker_amount: MmNumber, - taker_amount: MmNumber, + pub maker_coin: MmCoinEnum, + pub taker_coin: MmCoinEnum, + pub maker_amount: MmNumber, + pub taker_amount: MmNumber, my_persistent_pub: H264, maker: bits256, uuid: Uuid, my_order_uuid: Option, - maker_payment_lock: AtomicU64, + pub maker_payment_lock: AtomicU64, maker_payment_confirmed: AtomicBool, errors: PaMutex>, finished_at: AtomicU64, @@ -758,7 +755,7 @@ impl TakerSwap { fn w(&self) -> RwLockWriteGuard { self.mutable.write().unwrap() } #[inline] - fn r(&self) -> RwLockReadGuard { self.mutable.read().unwrap() } + pub fn r(&self) -> RwLockReadGuard { self.mutable.read().unwrap() } #[inline] fn my_maker_coin_htlc_pub(&self) -> H264Json { @@ -2336,288 +2333,6 @@ pub struct TakerSwapPreparedParams { maker_payment_spend_trade_fee: TradeFee, } -pub async fn get_command_based_on_watcher_activity( - swap: &TakerSwap, - ctx: &MmArc, - mut saved: TakerSavedSwap, - command: TakerSwapCommand, -) -> Result { - #[cfg(not(any(test, feature = "run-docker-tests")))] - { - let watcher_refund_time = swap.r().data.started_at - + (default_watcher_maker_payment_spend_factor() * swap.r().data.lock_duration as f64) as u64; - if now_sec() < watcher_refund_time { - return Ok(command); - } - } - - match command { - TakerSwapCommand::Start => Ok(command), - TakerSwapCommand::Negotiate => Ok(command), - TakerSwapCommand::SendTakerFee => Ok(command), - TakerSwapCommand::WaitForMakerPayment => Ok(command), - TakerSwapCommand::ValidateMakerPayment => Ok(command), - TakerSwapCommand::SendTakerPayment => Ok(command), - TakerSwapCommand::WaitForTakerPaymentSpend => { - match check_taker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx))) => { - add_taker_payment_spent_event(swap, &mut saved, &taker_payment_spend_tx).await?; - match check_maker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { - add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; - Ok(TakerSwapCommand::Finish) - }, - Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => ERR!("Maker has cheated by both spending the taker payment with transaction {:#?}, and refunding the maker payment with transaction {:#?}", taker_payment_spend_tx.tx_hash(), maker_payment_refund_tx.tx_hash()), - Ok(None) => Ok(TakerSwapCommand::SpendMakerPayment), - Err(e) => ERR!("Error {} when trying to find maker payment spend", e), - } - }, - Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { - validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; - add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; - Ok(TakerSwapCommand::Finish) - }, - Ok(None) => Ok(command), - Err(e) => ERR!("Error {} when trying to find taker payment spend", e), - } - }, - TakerSwapCommand::SpendMakerPayment => { - match check_maker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { - validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; - add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; - Ok(TakerSwapCommand::Finish) - }, - Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => ERR!("Maker has cheated by both spending the taker payment, and refunding the maker payment with transaction {:#?}", maker_payment_refund_tx.tx_hash()), - Ok(None) => Ok(command), - Err(e) => ERR!("Error {} when trying to find maker payment spend", e) - } - }, - TakerSwapCommand::PrepareForTakerPaymentRefund | - TakerSwapCommand::RefundTakerPayment => { - #[cfg(not(any(test, feature = "run-docker-tests")))] - { - let watcher_refund_time = swap.r().data.started_at + (default_watcher_refund_factor() * swap.r().data.lock_duration as f64) as u64; - if now_sec() < watcher_refund_time { - return Ok(command); - } - } - - let taker_payment_refund_tx = match check_taker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(_))) => return ERR!("Taker payment is not expected to be spent at this point"), - Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => taker_payment_refund_tx, - Ok(None) => return Ok(command), - Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), - }; - validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; - add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; - Ok(TakerSwapCommand::Finish) - }, - TakerSwapCommand::FinalizeTakerPaymentRefund => Ok(command), - TakerSwapCommand::Finish => Ok(command), - } -} - -pub async fn check_maker_payment_spend(swap: &TakerSwap) -> Result, String> { - let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; - let secret_hash = swap.r().secret_hash.0.clone(); - let maker_coin_start_block = swap.r().data.maker_coin_start_block; - let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); - - let maker_payment = match &swap.r().maker_payment { - Some(tx) => tx.tx_hex.0.clone(), - None => return ERR!("No info about maker payment, swap is not recoverable"), - }; - let unique_data = swap.unique_swap_data(); - let watcher_reward = swap.r().watcher_reward; - - swap.maker_coin - .search_for_swap_tx_spend_other(SearchForSwapTxSpendInput { - time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, - other_pub: other_maker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &maker_payment, - search_from_block: maker_coin_start_block, - swap_contract_address: &maker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }) - .await -} - -pub async fn check_taker_payment_spend(swap: &TakerSwap) -> Result, String> { - let taker_payment = match &swap.r().taker_payment { - Some(tx) => tx.tx_hex.0.clone(), - None => return ERR!("No info about taker payment, swap is not recoverable"), - }; - - let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; - let taker_coin_start_block = swap.r().data.taker_coin_start_block; - let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - - let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { - Ok(_) => swap.r().data.started_at as u32, - Err(_) => swap.r().data.taker_payment_lock as u32, - }; - let secret_hash = swap.r().secret_hash.0.clone(); - let unique_data = swap.unique_swap_data(); - let watcher_reward = swap.r().watcher_reward; - - swap.taker_coin - .search_for_swap_tx_spend_my(SearchForSwapTxSpendInput { - time_lock: taker_payment_lock, - other_pub: other_taker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &taker_payment, - search_from_block: taker_coin_start_block, - swap_contract_address: &taker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }) - .await -} - -pub async fn validate_maker_payment_spend( - swap: &TakerSwap, - maker_payment_spend_tx: &TransactionEnum, -) -> Result<(), String> { - let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; - let secret_hash = swap.r().secret_hash.0.clone(); - let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); - - let validate_input = ValidateWatcherSpendInput { - payment_tx: maker_payment_spend_tx.tx_hex(), - maker_pub: other_maker_coin_htlc_pub.to_vec(), - swap_contract_address: maker_coin_swap_contract_address, - time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, - secret_hash: secret_hash.clone(), - amount: swap.maker_amount.to_decimal(), - watcher_reward: None, - }; - swap.maker_coin - .taker_validates_maker_payment_spend(validate_input) - .compat() - .await - .map_err(|e| e.to_string())?; - Ok(()) -} - -pub async fn validate_taker_payment_refund( - swap: &TakerSwap, - taker_payment_refund_tx: &TransactionEnum, -) -> Result<(), String> { - let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; - let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { - Ok(_) => swap.r().data.started_at as u32, - Err(_) => swap.r().data.taker_payment_lock as u32, - }; - let secret_hash = swap.r().secret_hash.0.clone(); - - let validate_input = ValidateWatcherSpendInput { - payment_tx: taker_payment_refund_tx.tx_hex(), - maker_pub: other_maker_coin_htlc_pub.to_vec(), - swap_contract_address: taker_coin_swap_contract_address, - time_lock: taker_payment_lock, - secret_hash: secret_hash.clone(), - amount: swap.taker_amount.to_decimal(), - watcher_reward: None, - }; - - swap.taker_coin - .taker_validates_taker_payment_refund(validate_input) - .compat() - .await - .map_err(|e| e.to_string())?; - Ok(()) -} - -pub async fn add_taker_payment_spent_event( - swap: &TakerSwap, - saved: &mut TakerSavedSwap, - taker_payment_spend_tx: &TransactionEnum, -) -> Result<(), String> { - let secret_hash = swap.r().secret_hash.0.clone(); - let watcher_reward = swap.r().watcher_reward; - - let tx_hash = taker_payment_spend_tx.tx_hash(); - info!("Taker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(taker_payment_spend_tx.tx_hex()), - tx_hash, - }; - let secret = match swap - .taker_coin - .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) - .await - { - Ok(bytes) => H256Json::from(bytes.as_slice()), - Err(_) => { - return ERR!("Could not extract secret from taker payment spend transaction"); - }, - }; - - let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { - transaction: tx_ident, - secret, - }); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - Ok(()) -} - -pub async fn add_maker_payment_spent_by_watcher_event( - mut saved: TakerSavedSwap, - ctx: &MmArc, - maker_payment_spend_tx: TransactionEnum, -) -> Result<(), String> { - let tx_hash = maker_payment_spend_tx.tx_hash(); - info!("Watcher maker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(maker_payment_spend_tx.tx_hex()), - tx_hash, - }; - - let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - let new_swap = SavedSwap::Taker(saved); - try_s!(new_swap.save_to_db(ctx).await); - info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); - Ok(()) -} - -pub async fn add_taker_payment_refunded_by_watcher_event( - mut saved: TakerSavedSwap, - ctx: &MmArc, - taker_payment_refund_tx: TransactionEnum, -) -> Result<(), String> { - let tx_hash = taker_payment_refund_tx.tx_hash(); - info!("Taker refund tx hash {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: BytesJson::from(taker_payment_refund_tx.tx_hex()), - tx_hash, - }; - - let event = TakerSwapEvent::TakerPaymentRefundedByWatcher(Some(tx_ident)); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - - let new_swap = SavedSwap::Taker(saved); - try_s!(new_swap.save_to_db(ctx).await); - info!("Taker payment is refunded by the watcher"); - Ok(()) -} - pub async fn check_balance_for_taker_swap( ctx: &MmArc, my_coin: &MmCoinEnum, From cc7ddf7be085f5d1667d3105402d4b7d9e93cfcd Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Thu, 17 Aug 2023 22:23:58 +0300 Subject: [PATCH 44/53] remove duplicate code --- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 136 +++++++++----------- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 2 +- 2 files changed, 60 insertions(+), 78 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index fd20abfc2f..2627da5571 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -665,8 +665,8 @@ fn spawn_taker_swap_watcher(ctx: MmArc, watcher_data: TakerSwapWatcherData, veri pub fn watcher_topic(ticker: &str) -> String { pub_sub_topic(WATCHER_PREFIX, ticker) } pub async fn get_command_based_on_watcher_activity( - swap: &TakerSwap, ctx: &MmArc, + swap: &TakerSwap, mut saved: TakerSavedSwap, command: TakerSwapCommand, ) -> Result { @@ -686,67 +686,47 @@ pub async fn get_command_based_on_watcher_activity( TakerSwapCommand::WaitForMakerPayment => Ok(command), TakerSwapCommand::ValidateMakerPayment => Ok(command), TakerSwapCommand::SendTakerPayment => Ok(command), - TakerSwapCommand::WaitForTakerPaymentSpend => { - match check_taker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx))) => { - add_taker_payment_spent_event(swap, &mut saved, &taker_payment_spend_tx).await?; - match check_maker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { - add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; - Ok(TakerSwapCommand::Finish) - }, - Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => ERR!("Maker has cheated by both spending the taker payment with transaction {:#?}, and refunding the maker payment with transaction {:#?}", taker_payment_spend_tx.tx_hash(), maker_payment_refund_tx.tx_hash()), - Ok(None) => Ok(TakerSwapCommand::SpendMakerPayment), - Err(e) => ERR!("Error {} when trying to find maker payment spend", e), - } - }, - Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { - validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; - add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; - Ok(TakerSwapCommand::Finish) - }, - Ok(None) => Ok(command), - Err(e) => ERR!("Error {} when trying to find taker payment spend", e), - } - }, - TakerSwapCommand::SpendMakerPayment => { - match check_maker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => { - validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; - add_maker_payment_spent_by_watcher_event(saved, ctx, maker_payment_spend_tx).await?; - Ok(TakerSwapCommand::Finish) - }, - Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => ERR!("Maker has cheated by both spending the taker payment, and refunding the maker payment with transaction {:#?}", maker_payment_refund_tx.tx_hash()), - Ok(None) => Ok(command), - Err(e) => ERR!("Error {} when trying to find maker payment spend", e) - } + TakerSwapCommand::WaitForTakerPaymentSpend => match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx))) => { + add_taker_payment_spent_event(swap, &mut saved, &taker_payment_spend_tx).await?; + check_maker_payment_spend_and_add_event(ctx, swap, saved).await + }, + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { + add_taker_payment_refunded_by_watcher_event(ctx, swap, saved, taker_payment_refund_tx).await + }, + Ok(None) => Ok(command), + Err(e) => ERR!("Error {} when trying to find taker payment spend", e), }, - TakerSwapCommand::PrepareForTakerPaymentRefund | - TakerSwapCommand::RefundTakerPayment => { + TakerSwapCommand::SpendMakerPayment => check_maker_payment_spend_and_add_event(ctx, swap, saved).await, + TakerSwapCommand::PrepareForTakerPaymentRefund | TakerSwapCommand::RefundTakerPayment => { #[cfg(not(any(test, feature = "run-docker-tests")))] { - let watcher_refund_time = swap.r().data.started_at + (default_watcher_refund_factor() * swap.r().data.lock_duration as f64) as u64; + let watcher_refund_time = swap.r().data.started_at + + (default_watcher_refund_factor() * swap.r().data.lock_duration as f64) as u64; if now_sec() < watcher_refund_time { return Ok(command); } } - let taker_payment_refund_tx = match check_taker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(_))) => return ERR!("Taker payment is not expected to be spent at this point"), - Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => taker_payment_refund_tx, - Ok(None) => return Ok(command), - Err(e) => return ERR!("Error {} when trying to find taker payment spend", e), - }; - validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; - add_taker_payment_refunded_by_watcher_event(saved, ctx, taker_payment_refund_tx).await?; - Ok(TakerSwapCommand::Finish) + match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(_))) => ERR!("Taker payment is not expected to be spent at this point"), + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { + add_taker_payment_refunded_by_watcher_event(ctx, swap, saved, taker_payment_refund_tx).await + }, + Ok(None) => Ok(command), + Err(e) => ERR!("Error {} when trying to find taker payment spend", e), + } }, TakerSwapCommand::FinalizeTakerPaymentRefund => Ok(command), TakerSwapCommand::Finish => Ok(command), } } -pub async fn check_maker_payment_spend(swap: &TakerSwap) -> Result, String> { +pub async fn check_maker_payment_spend_and_add_event( + ctx: &MmArc, + swap: &TakerSwap, + mut saved: TakerSavedSwap, +) -> Result { let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; let secret_hash = swap.r().secret_hash.0.clone(); let maker_coin_start_block = swap.r().data.maker_coin_start_block; @@ -759,7 +739,7 @@ pub async fn check_maker_payment_spend(swap: &TakerSwap) -> Result Result maker_payment_spend_tx, + Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => return ERR!("Maker has cheated by both spending the taker payment, and refunding the maker payment with transaction {:#?}", maker_payment_refund_tx.tx_hash()), + Ok(None) => return Ok(TakerSwapCommand::SpendMakerPayment), + Err(e) => return ERR!("Error {} when trying to find maker payment spend", e) + }; + + validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; + let tx_hash = maker_payment_spend_tx.tx_hash(); + info!("Watcher maker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: Bytes::from(maker_payment_spend_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + 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) } pub async fn check_taker_payment_spend(swap: &TakerSwap) -> Result, String> { @@ -897,35 +901,13 @@ pub async fn add_taker_payment_spent_event( Ok(()) } -pub async fn add_maker_payment_spent_by_watcher_event( - mut saved: TakerSavedSwap, - ctx: &MmArc, - maker_payment_spend_tx: TransactionEnum, -) -> Result<(), String> { - let tx_hash = maker_payment_spend_tx.tx_hash(); - info!("Watcher maker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: Bytes::from(maker_payment_spend_tx.tx_hex()), - tx_hash, - }; - - let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - let new_swap = SavedSwap::Taker(saved); - try_s!(new_swap.save_to_db(ctx).await); - info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); - Ok(()) -} - pub async fn add_taker_payment_refunded_by_watcher_event( - mut saved: TakerSavedSwap, ctx: &MmArc, + swap: &TakerSwap, + mut saved: TakerSavedSwap, taker_payment_refund_tx: TransactionEnum, -) -> Result<(), String> { +) -> Result { + validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; let tx_hash = taker_payment_refund_tx.tx_hash(); info!("Taker refund tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { @@ -943,5 +925,5 @@ pub async fn add_taker_payment_refunded_by_watcher_event( let new_swap = SavedSwap::Taker(saved); try_s!(new_swap.save_to_db(ctx).await); info!("Taker payment is refunded by the watcher"); - Ok(()) + Ok(TakerSwapCommand::Finish) } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index d79668f674..df8edfb709 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -2019,7 +2019,7 @@ impl TakerSwap { && maker_coin.is_supported_by_watchers() && saved.watcher_message_sent() { - command = get_command_based_on_watcher_activity(&swap, &ctx, saved, command).await?; + command = get_command_based_on_watcher_activity(&ctx, &swap, saved, command).await?; } drop_mutability!(command); From 6d319ebf160a60de5e288006af02ab91c9d0ab94 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Fri, 18 Aug 2023 02:29:51 +0300 Subject: [PATCH 45/53] combine validate taker payment refund and maker payment spend functions --- mm2src/coins/eth.rs | 300 ++++-------------- mm2src/coins/lightning.rs | 6 +- mm2src/coins/lp_coins.rs | 11 +- mm2src/coins/qrc20.rs | 6 +- mm2src/coins/solana.rs | 6 +- mm2src/coins/solana/spl.rs | 6 +- mm2src/coins/tendermint/tendermint_coin.rs | 6 +- mm2src/coins/tendermint/tendermint_token.rs | 6 +- mm2src/coins/test_coin.rs | 6 +- mm2src/coins/utxo/bch.rs | 7 +- mm2src/coins/utxo/qtum.rs | 7 +- mm2src/coins/utxo/slp.rs | 6 +- mm2src/coins/utxo/utxo_standard.rs | 7 +- mm2src/coins/z_coin.rs | 6 +- mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 8 +- .../tests/docker_tests/swap_watcher_tests.rs | 126 +++++--- 16 files changed, 174 insertions(+), 346 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index cb3be9da0c..4d85b2134a 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -23,7 +23,7 @@ use super::eth::Action::{Call, Create}; use crate::lp_price::get_base_price_in_rel; use crate::nft::nft_structs::{ContractType, ConvertChain, TransactionNftDetails, WithdrawErc1155, WithdrawErc721}; -use crate::ValidateWatcherSpendInput; +use crate::{ValidateWatcherSpendInput, WatcherSpendType}; use async_trait::async_trait; use bitcrypto::{dhash160, keccak256, ripemd160, sha256}; use common::custom_futures::repeatable::{Ready, Retry, RetryOnError}; @@ -1463,7 +1463,7 @@ impl WatcherOps for EthCoin { // 1.Validate if taker fee is old } - fn taker_validates_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { let watcher_reward = try_f!(input .watcher_reward .clone() @@ -1488,15 +1488,16 @@ impl WatcherOps for EthCoin { } else { input.secret_hash.to_vec() }; - let receiver_addr = + let maker_addr = try_f!(addr_from_raw_pubkey(&input.maker_pub).map_to_mm(ValidatePaymentError::InvalidParameter)); + let trade_amount = try_f!(wei_from_big_decimal(&(input.amount), decimals)); let fut = async move { match tx.action { Call(contract_address) => { if contract_address != expected_swap_contract_address { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx {:?} was sent to wrong address, expected {:?}", + "Transaction {:?} was sent to wrong address, expected {:?}", contract_address, expected_swap_contract_address, ))); } @@ -1508,19 +1509,26 @@ impl WatcherOps for EthCoin { }, }; - let status = selfi + let actual_status = selfi .payment_status(expected_swap_contract_address, Token::FixedBytes(swap_id.clone())) .compat() .await .map_to_mm(ValidatePaymentError::Transport)?; - if status != U256::from(PaymentState::Refunded as u8) { + let expected_status = match input.spend_type { + WatcherSpendType::MakerPaymentSpend => U256::from(PaymentState::Spent as u8), + WatcherSpendType::TakerPaymentRefund => U256::from(PaymentState::Refunded as u8), + }; + if actual_status != expected_status { return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( - "Payment state is not PAYMENT_STATE_REFUNDED, got {}", - status + "Payment state is not {}, got {}", + expected_status, actual_status ))); } - let function_name = get_function_name("senderRefund", true); + let function_name = match input.spend_type { + WatcherSpendType::MakerPaymentSpend => get_function_name("receiverSpend", true), + WatcherSpendType::TakerPaymentRefund => get_function_name("senderRefund", true), + }; let function = SWAP_CONTRACT .function(&function_name) .map_to_mm(|err| ValidatePaymentError::InternalError(err.to_string()))?; @@ -1532,17 +1540,32 @@ impl WatcherOps for EthCoin { .map_to_mm(ValidatePaymentError::TxDeserializationError)?; if swap_id_input != Token::FixedBytes(swap_id.clone()) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx invalid swap_id arg {:?}, expected {:?}", + "Transaction invalid swap_id arg {:?}, expected {:?}", swap_id_input, Token::FixedBytes(swap_id.clone()) ))); } - let hash_input = get_function_input_data(&decoded, function, 2) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if hash_input != Token::FixedBytes(secret_hash.clone()) { + let hash_input = match input.spend_type { + WatcherSpendType::MakerPaymentSpend => { + let secret_input = get_function_input_data(&decoded, function, 2) + .map_to_mm(ValidatePaymentError::TxDeserializationError)? + .into_fixed_bytes() + .ok_or_else(|| { + ValidatePaymentError::WrongPaymentTx("Invalid type for secret hash argument".to_string()) + })?; + dhash160(&secret_input).to_vec() + }, + WatcherSpendType::TakerPaymentRefund => get_function_input_data(&decoded, function, 2) + .map_to_mm(ValidatePaymentError::TxDeserializationError)? + .into_fixed_bytes() + .ok_or_else(|| { + ValidatePaymentError::WrongPaymentTx("Invalid type for secret argument".to_string()) + })?, + }; + if hash_input != secret_hash { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx secret_hash arg {:?} is invalid, expected {:?}", + "Transaction secret or secret_hash arg {:?} is invalid, expected {:?}", hash_input, Token::FixedBytes(secret_hash), ))); @@ -1550,21 +1573,29 @@ impl WatcherOps for EthCoin { let sender_input = get_function_input_data(&decoded, function, 4) .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if sender_input != Token::Address(selfi.my_address) { + let expected_sender = match input.spend_type { + WatcherSpendType::MakerPaymentSpend => maker_addr, + WatcherSpendType::TakerPaymentRefund => selfi.my_address, + }; + if sender_input != Token::Address(expected_sender) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx sender arg {:?} is invalid, expected {:?}", + "Transaction sender arg {:?} is invalid, expected {:?}", sender_input, - Token::Address(selfi.my_address) + Token::Address(expected_sender) ))); } let receiver_input = get_function_input_data(&decoded, function, 5) .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if receiver_input != Token::Address(receiver_addr) { + let expected_receiver = match input.spend_type { + WatcherSpendType::MakerPaymentSpend => selfi.my_address, + WatcherSpendType::TakerPaymentRefund => maker_addr, + }; + if receiver_input != Token::Address(expected_receiver) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx receiver arg {:?} is invalid, expected {:?}", + "Transaction receiver arg {:?} is invalid, expected {:?}", receiver_input, - Token::Address(receiver_addr) + Token::Address(expected_receiver) ))); } @@ -1572,7 +1603,7 @@ impl WatcherOps for EthCoin { .map_to_mm(ValidatePaymentError::TxDeserializationError)?; if reward_target_input != Token::Uint(U256::from(watcher_reward.reward_target as u8)) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx reward target arg {:?} is invalid, expected {:?}", + "Transaction reward target arg {:?} is invalid, expected {:?}", reward_target_input, Token::Uint(U256::from(watcher_reward.reward_target as u8)) ))); @@ -1582,7 +1613,7 @@ impl WatcherOps for EthCoin { .map_to_mm(ValidatePaymentError::TxDeserializationError)?; if contract_reward_input != Token::Bool(watcher_reward.send_contract_reward_on_spend) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx sends contract reward on spend arg {:?} is invalid, expected {:?}", + "Transaction sends contract reward on spend arg {:?} is invalid, expected {:?}", contract_reward_input, Token::Bool(watcher_reward.send_contract_reward_on_spend) ))); @@ -1592,7 +1623,7 @@ impl WatcherOps for EthCoin { .map_to_mm(ValidatePaymentError::TxDeserializationError)?; if reward_amount_input != Token::Uint(expected_reward_amount) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx watcher reward amount arg {:?} is invalid, expected {:?}", + "Transaction watcher reward amount arg {:?} is invalid, expected {:?}", reward_amount_input, Token::Uint(expected_reward_amount) ))); @@ -1600,7 +1631,7 @@ impl WatcherOps for EthCoin { if tx.value != U256::zero() { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx value arg {:?} is invalid, expected 0", + "Transaction value arg {:?} is invalid, expected 0", tx.value ))); } @@ -1609,213 +1640,19 @@ impl WatcherOps for EthCoin { EthCoinType::Eth => { let amount_input = get_function_input_data(&decoded, function, 1) .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - let total_amount = trade_amount + expected_reward_amount; - if amount_input != Token::Uint(total_amount) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx amount arg {:?} is invalid, expected {:?}", - amount_input, - Token::Uint(total_amount), - ))); - } - - let token_address_input = get_function_input_data(&decoded, function, 3) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if token_address_input != Token::Address(Address::default()) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx token address arg {:?} is invalid, expected {:?}", - token_address_input, - Token::Address(Address::default()), - ))); - } - }, - EthCoinType::Erc20 { - platform: _, - token_addr, - } => { - let amount_input = get_function_input_data(&decoded, function, 1) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if amount_input != Token::Uint(trade_amount) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx amount arg {:?} is invalid, expected {:?}", - amount_input, - Token::Uint(trade_amount), - ))); - } - - let token_address_input = get_function_input_data(&decoded, function, 3) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if token_address_input != Token::Address(*token_addr) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Refund tx token address arg {:?} is invalid, expected {:?}", - token_address_input, - Token::Address(*token_addr), - ))); - } - }, - } - - Ok(()) - }; - Box::new(fut.boxed().compat()) - } - - fn taker_validates_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - let watcher_reward = try_f!(input - .watcher_reward - .clone() - .ok_or_else(|| ValidatePaymentError::WatcherRewardError("Watcher reward not found".to_string()))); - let expected_reward_amount = try_f!(wei_from_big_decimal(&watcher_reward.amount, self.decimals)); - - let expected_swap_contract_address = try_f!(input - .swap_contract_address - .try_to_address() - .map_to_mm(ValidatePaymentError::InvalidParameter)); - - let unsigned: UnverifiedTransaction = try_f!(rlp::decode(&input.payment_tx)); - let tx = - try_f!(SignedEthTx::new(unsigned) - .map_to_mm(|err| ValidatePaymentError::TxDeserializationError(err.to_string()))); - - let selfi = self.clone(); - let swap_id = selfi.etomic_swap_id(input.time_lock, &input.secret_hash); - let decimals = self.decimals; - let secret_hash = if input.secret_hash.len() == 32 { - ripemd160(&input.secret_hash).to_vec() - } else { - input.secret_hash.to_vec() - }; - - let maker_addr = - try_f!(addr_from_raw_pubkey(&input.maker_pub).map_to_mm(ValidatePaymentError::InvalidParameter)); - let trade_amount = try_f!(wei_from_big_decimal(&(input.amount), decimals)); - - let fut = async move { - match tx.action { - Call(contract_address) => { - if contract_address != expected_swap_contract_address { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx {:?} was sent to wrong address, expected {:?}", - contract_address, expected_swap_contract_address, - ))); - } - }, - Create => { - return MmError::err(ValidatePaymentError::WrongPaymentTx( - "Tx action must be Call, found Create instead".to_string(), - )); - }, - }; - - let status = selfi - .payment_status(expected_swap_contract_address, Token::FixedBytes(swap_id.clone())) - .compat() - .await - .map_to_mm(ValidatePaymentError::Transport)?; - if status != U256::from(PaymentState::Spent as u8) { - return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( - "Payment state is not PAYMENT_STATE_SPENT, got {}", - status - ))); - } - - let function_name = get_function_name("receiverSpend", true); - let function = SWAP_CONTRACT - .function(&function_name) - .map_to_mm(|err| ValidatePaymentError::InternalError(err.to_string()))?; - - let decoded = decode_contract_call(function, &tx.data) - .map_to_mm(|err| ValidatePaymentError::TxDeserializationError(err.to_string()))?; - - let swap_id_input = get_function_input_data(&decoded, function, 0) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if swap_id_input != Token::FixedBytes(swap_id.clone()) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx invalid swap_id arg {:?}, expected {:?}", - swap_id_input, swap_id - ))); - } - - let secret_input = get_function_input_data(&decoded, function, 2) - .map_to_mm(ValidatePaymentError::TxDeserializationError)? - .into_fixed_bytes() - .ok_or_else(|| ValidatePaymentError::WrongPaymentTx("Invalid type for secret argument".to_string()))?; - - let secret_input_hashed = dhash160(&secret_input); - if secret_input_hashed.to_vec() != secret_hash { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx secret arg {:?} is invalid", - secret_input_hashed, - ))); - } - - let sender_input = get_function_input_data(&decoded, function, 4) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if sender_input != Token::Address(maker_addr) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx sender arg {:?} is invalid, expected {:?}", - sender_input, - Token::Address(maker_addr) - ))); - } - - let receiver_input = get_function_input_data(&decoded, function, 5) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if receiver_input != Token::Address(selfi.my_address) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx receiver arg {:?} is invalid, expected {:?}", - receiver_input, - Token::Address(selfi.my_address) - ))); - } - - let reward_target_input = get_function_input_data(&decoded, function, 6) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - - if reward_target_input != Token::Uint(U256::from(watcher_reward.reward_target as u8)) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx reward target arg {:?} is invalid, expected {:?}", - reward_target_input, watcher_reward.reward_target as u8 - ))); - } - - let contract_reward_input = get_function_input_data(&decoded, function, 7) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if contract_reward_input != Token::Bool(watcher_reward.send_contract_reward_on_spend) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx sends contract reward on spend arg {:?} is invalid, expected {:?}", - contract_reward_input, watcher_reward.send_contract_reward_on_spend - ))); - } - - let reward_amount_input = get_function_input_data(&decoded, function, 8) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - if reward_amount_input != Token::Uint(expected_reward_amount) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx watcher reward amount arg {:?} is invalid, expected {:?}", - reward_amount_input, expected_reward_amount - ))); - } - - if tx.value != U256::zero() { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx value arg {:?} is invalid, expected 0", - tx.value - ))); - } - - match &selfi.coin_type { - EthCoinType::Eth => { - let amount_input = get_function_input_data(&decoded, function, 1) - .map_to_mm(ValidatePaymentError::TxDeserializationError)?; - let total_amount = if let RewardTarget::None = watcher_reward.reward_target { - trade_amount - } else { - trade_amount + expected_reward_amount + let total_amount = match input.spend_type { + WatcherSpendType::MakerPaymentSpend => { + if let RewardTarget::None = watcher_reward.reward_target { + trade_amount + } else { + trade_amount + expected_reward_amount + } + }, + WatcherSpendType::TakerPaymentRefund => trade_amount + expected_reward_amount, }; - if amount_input != Token::Uint(total_amount) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx amount arg {:?} is invalid, expected {:?}", + "Transaction amount arg {:?} is invalid, expected {:?}", amount_input, Token::Uint(total_amount), ))); @@ -1825,7 +1662,7 @@ impl WatcherOps for EthCoin { .map_to_mm(ValidatePaymentError::TxDeserializationError)?; if token_address_input != Token::Address(Address::default()) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx token address arg {:?} is invalid, expected {:?}", + "Transaction token address arg {:?} is invalid, expected {:?}", token_address_input, Token::Address(Address::default()), ))); @@ -1839,7 +1676,7 @@ impl WatcherOps for EthCoin { .map_to_mm(ValidatePaymentError::TxDeserializationError)?; if amount_input != Token::Uint(trade_amount) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx amount arg {:?} is invalid, expected {:?}", + "Transaction amount arg {:?} is invalid, expected {:?}", amount_input, Token::Uint(trade_amount), ))); @@ -1849,7 +1686,7 @@ impl WatcherOps for EthCoin { .map_to_mm(ValidatePaymentError::TxDeserializationError)?; if token_address_input != Token::Address(*token_addr) { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "Payment spend tx token address arg {:?} is invalid, expected {:?}", + "Transaction token address arg {:?} is invalid, expected {:?}", token_address_input, Token::Address(*token_addr), ))); @@ -1859,7 +1696,6 @@ impl WatcherOps for EthCoin { Ok(()) }; - Box::new(fut.boxed().compat()) } diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 32d8823c01..8a4d78e12e 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -990,14 +990,10 @@ impl WatcherOps for LightningCoin { unimplemented!(); } - fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } - fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!(); - } - async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 3d2c0e8f09..49e9975d6c 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -599,6 +599,12 @@ pub struct WatcherValidatePaymentInput { pub maker_coin: MmCoinEnum, } +#[derive(Clone)] +pub enum WatcherSpendType { + TakerPaymentRefund, + MakerPaymentSpend, +} + #[derive(Clone)] pub struct ValidateWatcherSpendInput { pub payment_tx: Vec, @@ -608,6 +614,7 @@ pub struct ValidateWatcherSpendInput { pub secret_hash: Vec, pub amount: BigDecimal, pub watcher_reward: Option, + pub spend_type: WatcherSpendType, } #[derive(Clone, Debug)] @@ -978,9 +985,7 @@ pub trait WatcherOps { fn watcher_validate_taker_payment(&self, _input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()>; - fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()>; - - fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()>; + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()>; async fn watcher_search_for_swap_tx_spend( &self, diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 8272645678..7d5b7aa036 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1135,14 +1135,10 @@ impl WatcherOps for Qrc20Coin { unimplemented!(); } - fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } - fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!(); - } - async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 10e93efb0b..1425814436 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -639,11 +639,7 @@ impl WatcherOps for SolanaCoin { unimplemented!(); } - fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!() - } - - fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 34e176f107..e99b7d5def 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -460,11 +460,7 @@ impl WatcherOps for SplToken { unimplemented!(); } - fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!() - } - - fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index f0dfe7b953..5bea8440cf 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -2623,11 +2623,7 @@ impl WatcherOps for TendermintCoin { unimplemented!(); } - fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!(); - } - - fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!(); } diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index ee83d6e742..6f67224bbe 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -469,11 +469,7 @@ impl WatcherOps for TendermintToken { unimplemented!(); } - fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!(); - } - - fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!(); } diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 0db9d339e0..7489ee8139 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -271,11 +271,7 @@ impl WatcherOps for TestCoin { unimplemented!(); } - fn taker_validates_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!() - } - - fn taker_validates_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index f61f9400c2..4d11ffc401 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1096,12 +1096,7 @@ impl WatcherOps for BchCoin { } #[inline] - fn taker_validates_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - utxo_common::validate_payment_spend_or_refund(self, input) - } - - #[inline] - fn taker_validates_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { utxo_common::validate_payment_spend_or_refund(self, input) } diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index bd7ff8c738..2c3eb23c2c 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -778,12 +778,7 @@ impl WatcherOps for QtumCoin { } #[inline] - fn taker_validates_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - utxo_common::validate_payment_spend_or_refund(self, input) - } - - #[inline] - fn taker_validates_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { utxo_common::validate_payment_spend_or_refund(self, input) } diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index af0315c9f5..3e32e0ddfe 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1518,11 +1518,7 @@ impl WatcherOps for SlpToken { unimplemented!(); } - fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!() - } - - fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 9ac6a2af20..043555e19a 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -543,12 +543,7 @@ impl WatcherOps for UtxoStandardCoin { } #[inline] - fn taker_validates_taker_payment_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - utxo_common::validate_payment_spend_or_refund(self, input) - } - - #[inline] - fn taker_validates_maker_payment_spend(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { utxo_common::validate_payment_spend_or_refund(self, input) } diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 289cce1e63..83672c9964 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -1594,11 +1594,7 @@ impl WatcherOps for ZCoin { unimplemented!(); } - fn taker_validates_taker_payment_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!() - } - - fn taker_validates_maker_payment_spend(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { unimplemented!() } diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 2627da5571..e0c593aedf 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -8,7 +8,7 @@ use crate::mm2::MmError; use async_trait::async_trait; use coins::{CanRefundHtlc, ConfirmPaymentInput, FoundSwapTxSpend, MmCoinEnum, RefundPaymentArgs, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, TransactionEnum, ValidateWatcherSpendInput, - WaitForHTLCTxSpendArgs, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + WaitForHTLCTxSpendArgs, WatcherSearchForSwapTxSpendInput, WatcherSpendType, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput}; use common::executor::{AbortSettings, SpawnAbortable, Timer}; use common::log::{debug, error, info}; @@ -825,9 +825,10 @@ pub async fn validate_maker_payment_spend( secret_hash: secret_hash.clone(), amount: swap.maker_amount.to_decimal(), watcher_reward: None, + spend_type: WatcherSpendType::MakerPaymentSpend, }; swap.maker_coin - .taker_validates_maker_payment_spend(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .compat() .await .map_err(|e| e.to_string())?; @@ -854,10 +855,11 @@ pub async fn validate_taker_payment_refund( secret_hash: secret_hash.clone(), amount: swap.taker_amount.to_decimal(), watcher_reward: None, + spend_type: WatcherSpendType::TakerPaymentRefund, }; swap.taker_coin - .taker_validates_taker_payment_refund(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .compat() .await .map_err(|e| e.to_string())?; 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 3ccdd4c5ef..5beb20ce75 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -5,10 +5,10 @@ use coins::coin_errors::ValidatePaymentError; use coins::utxo::{dhash160, UtxoCommonOps}; use coins::{ConfirmPaymentInput, FoundSwapTxSpend, MarketCoinOps, MmCoin, MmCoinEnum, RefundPaymentArgs, RewardTarget, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SwapOps, - ValidateWatcherSpendInput, WatcherOps, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, - EARLY_CONFIRMATION_ERR_LOG, 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}; + ValidateWatcherSpendInput, WatcherOps, WatcherSpendType, WatcherValidatePaymentInput, + WatcherValidateTakerFeeInput, EARLY_CONFIRMATION_ERR_LOG, 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_u32, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::privkey::{key_pair_from_secret, key_pair_from_seed}; use futures01::Future; @@ -2230,9 +2230,12 @@ fn test_taker_validates_taker_payment_refund_utxo() { secret_hash: secret_hash.to_vec(), amount: BigDecimal::from(10), watcher_reward: None, + spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_refund = taker_coin.taker_validates_taker_payment_refund(validate_input).wait(); + let validate_watcher_refund = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait(); assert!(validate_watcher_refund.is_ok()); } @@ -2310,21 +2313,22 @@ fn test_taker_validates_taker_payment_refund_eth() { secret_hash: secret_hash.to_vec(), amount: taker_amount.clone(), watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::TakerPaymentRefund, }; let error = taker_coin - .taker_validates_taker_payment_refund(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::UnexpectedPaymentState(err) => { - assert!(err.contains("Payment state is not PAYMENT_STATE_REFUNDED")) + assert!(err.contains("Payment state is not")) }, _ => panic!( "Expected `UnexpectedPaymentState` {}, found {:?}", - "Payment state is not PAYMENT_STATE_REFUNDED", error + "Payment state is not 3", error ), } @@ -2349,9 +2353,12 @@ fn test_taker_validates_taker_payment_refund_eth() { secret_hash: secret_hash.to_vec(), amount: taker_amount.clone(), watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_refund = taker_coin.taker_validates_taker_payment_refund(validate_input).wait(); + let validate_watcher_refund = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait(); assert!(validate_watcher_refund.is_ok()); let validate_input = ValidateWatcherSpendInput { @@ -2362,9 +2369,10 @@ fn test_taker_validates_taker_payment_refund_eth() { secret_hash: secret_hash.to_vec(), amount: taker_amount.clone(), watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::TakerPaymentRefund, }; let error = taker_coin - .taker_validates_taker_payment_refund(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); @@ -2387,17 +2395,18 @@ fn test_taker_validates_taker_payment_refund_eth() { secret_hash: secret_hash.to_vec(), amount: taker_amount.clone(), watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::TakerPaymentRefund, }; let error = maker_coin - .taker_validates_taker_payment_refund(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Refund tx sender arg")) + assert!(err.contains("Transaction sender arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -2413,17 +2422,18 @@ fn test_taker_validates_taker_payment_refund_eth() { secret_hash: secret_hash.to_vec(), amount: taker_amount.clone(), watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::TakerPaymentRefund, }; let error = taker_coin - .taker_validates_taker_payment_refund(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Refund tx receiver arg")) + assert!(err.contains("Transaction receiver arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -2442,17 +2452,18 @@ fn test_taker_validates_taker_payment_refund_eth() { secret_hash: secret_hash.to_vec(), amount: taker_amount.clone(), watcher_reward: Some(wrong_watcher_reward), + spend_type: WatcherSpendType::TakerPaymentRefund, }; let error = taker_coin - .taker_validates_taker_payment_refund(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Refund tx reward target arg")) + assert!(err.contains("Transaction reward target arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -2471,17 +2482,18 @@ fn test_taker_validates_taker_payment_refund_eth() { secret_hash: secret_hash.to_vec(), amount: taker_amount.clone(), watcher_reward: Some(wrong_watcher_reward), + spend_type: WatcherSpendType::TakerPaymentRefund, }; let error = taker_coin - .taker_validates_taker_payment_refund(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Refund tx sends contract reward on spend arg")) + assert!(err.contains("Transaction sends contract reward on spend arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -2500,17 +2512,18 @@ fn test_taker_validates_taker_payment_refund_eth() { secret_hash: secret_hash.to_vec(), amount: taker_amount, watcher_reward: Some(wrong_watcher_reward), + spend_type: WatcherSpendType::TakerPaymentRefund, }; let error = taker_coin - .taker_validates_taker_payment_refund(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Refund tx watcher reward amount arg")) + assert!(err.contains("Transaction watcher reward amount arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -2526,17 +2539,18 @@ fn test_taker_validates_taker_payment_refund_eth() { secret_hash: secret_hash.to_vec(), amount: BigDecimal::one(), watcher_reward: Some(watcher_reward), + spend_type: WatcherSpendType::TakerPaymentRefund, }; let error = taker_coin - .taker_validates_taker_payment_refund(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Refund tx amount arg")) + assert!(err.contains("Transaction amount arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -2636,9 +2650,12 @@ fn test_taker_validates_taker_payment_refund_erc20() { secret_hash: secret_hash.to_vec(), amount: taker_amount, watcher_reward: watcher_reward.clone(), + spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_refund = taker_coin.taker_validates_taker_payment_refund(validate_input).wait(); + let validate_watcher_refund = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait(); assert!(validate_watcher_refund.is_ok()); let validate_input = ValidateWatcherSpendInput { @@ -2649,17 +2666,18 @@ fn test_taker_validates_taker_payment_refund_erc20() { secret_hash: secret_hash.to_vec(), amount: BigDecimal::one(), watcher_reward, + spend_type: WatcherSpendType::TakerPaymentRefund, }; let error = taker_coin - .taker_validates_taker_payment_refund(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Refund tx amount arg")) + assert!(err.contains("Transaction amount arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -2740,9 +2758,12 @@ fn test_taker_validates_maker_payment_spend_utxo() { secret_hash: secret_hash.to_vec(), amount: BigDecimal::from(10), watcher_reward: None, + spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_spend = taker_coin.taker_validates_taker_payment_refund(validate_input).wait(); + let validate_watcher_spend = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait(); assert!(validate_watcher_spend.is_ok()); } @@ -2821,17 +2842,18 @@ fn test_taker_validates_maker_payment_spend_eth() { secret_hash: secret_hash.to_vec(), amount: maker_amount.clone(), watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::MakerPaymentSpend, }; let error = taker_coin - .taker_validates_maker_payment_spend(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::UnexpectedPaymentState(err) => { - assert!(err.contains("Payment state is not PAYMENT_STATE_SPENT")) + assert!(err.contains("Payment state is not")) }, _ => panic!( "Expected `UnexpectedPaymentState` {}, found {:?}", @@ -2858,9 +2880,12 @@ fn test_taker_validates_maker_payment_spend_eth() { secret_hash: secret_hash.to_vec(), amount: maker_amount.clone(), watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::MakerPaymentSpend, }; - let validate_watcher_spend = taker_coin.taker_validates_maker_payment_spend(validate_input).wait(); + let validate_watcher_spend = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait(); assert!(validate_watcher_spend.is_ok()); let validate_input = ValidateWatcherSpendInput { @@ -2871,10 +2896,11 @@ fn test_taker_validates_maker_payment_spend_eth() { secret_hash: secret_hash.to_vec(), amount: maker_amount.clone(), watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::MakerPaymentSpend, }; let error = taker_coin - .taker_validates_maker_payment_spend(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); @@ -2897,17 +2923,18 @@ fn test_taker_validates_maker_payment_spend_eth() { secret_hash: secret_hash.to_vec(), amount: maker_amount.clone(), watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::MakerPaymentSpend, }; let error = taker_coin - .taker_validates_maker_payment_spend(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Payment spend tx sender arg")) + assert!(err.contains("Transaction sender arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -2923,17 +2950,18 @@ fn test_taker_validates_maker_payment_spend_eth() { secret_hash: secret_hash.to_vec(), amount: maker_amount.clone(), watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::MakerPaymentSpend, }; let error = maker_coin - .taker_validates_maker_payment_spend(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Payment spend tx receiver arg")) + assert!(err.contains("Transaction receiver arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -2952,17 +2980,18 @@ fn test_taker_validates_maker_payment_spend_eth() { secret_hash: secret_hash.to_vec(), amount: maker_amount.clone(), watcher_reward: Some(wrong_watcher_reward), + spend_type: WatcherSpendType::MakerPaymentSpend, }; let error = taker_coin - .taker_validates_maker_payment_spend(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Payment spend tx reward target arg")) + assert!(err.contains("Transaction reward target arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -2981,17 +3010,18 @@ fn test_taker_validates_maker_payment_spend_eth() { secret_hash: secret_hash.to_vec(), amount: maker_amount.clone(), watcher_reward: Some(wrong_watcher_reward), + spend_type: WatcherSpendType::MakerPaymentSpend, }; let error = taker_coin - .taker_validates_maker_payment_spend(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Payment spend tx sends contract reward on spend arg")) + assert!(err.contains("Transaction sends contract reward on spend arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -3010,17 +3040,18 @@ fn test_taker_validates_maker_payment_spend_eth() { secret_hash: secret_hash.to_vec(), amount: maker_amount, watcher_reward: Some(wrong_watcher_reward), + spend_type: WatcherSpendType::MakerPaymentSpend, }; let error = taker_coin - .taker_validates_maker_payment_spend(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Payment spend tx watcher reward amount arg")) + assert!(err.contains("Transaction watcher reward amount arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -3036,17 +3067,18 @@ fn test_taker_validates_maker_payment_spend_eth() { secret_hash: secret_hash.to_vec(), amount: BigDecimal::one(), watcher_reward: Some(watcher_reward), + spend_type: WatcherSpendType::MakerPaymentSpend, }; let error = taker_coin - .taker_validates_maker_payment_spend(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Payment spend tx amount arg")) + assert!(err.contains("Transaction amount arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", @@ -3141,9 +3173,12 @@ fn test_taker_validates_maker_payment_spend_erc20() { secret_hash: secret_hash.to_vec(), amount: maker_amount, watcher_reward: watcher_reward.clone(), + spend_type: WatcherSpendType::MakerPaymentSpend, }; - let validate_watcher_spend = taker_coin.taker_validates_maker_payment_spend(validate_input).wait(); + let validate_watcher_spend = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait(); assert!(validate_watcher_spend.is_ok()); let validate_input = ValidateWatcherSpendInput { @@ -3154,17 +3189,18 @@ fn test_taker_validates_maker_payment_spend_erc20() { secret_hash: secret_hash.to_vec(), amount: BigDecimal::one(), watcher_reward, + spend_type: WatcherSpendType::MakerPaymentSpend, }; let error = taker_coin - .taker_validates_maker_payment_spend(validate_input) + .taker_validates_payment_spend_or_refund(validate_input) .wait() .unwrap_err() .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { - assert!(err.contains("Payment spend tx amount arg")) + assert!(err.contains("Transaction amount arg")) }, _ => panic!( "Expected `WrongPaymentTx` {}, found {:?}", From 1c1d93e8788234ba0f4427b18b82808a69b5a558 Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Fri, 18 Aug 2023 15:47:04 +0300 Subject: [PATCH 46/53] move taker restart functionality to a separate module --- mm2src/mm2_main/src/lp_swap.rs | 2 + mm2src/mm2_main/src/lp_swap/swap_watcher.rs | 281 +----------------- mm2src/mm2_main/src/lp_swap/taker_restart.rs | 282 +++++++++++++++++++ mm2src/mm2_main/src/lp_swap/taker_swap.rs | 2 +- 4 files changed, 290 insertions(+), 277 deletions(-) create mode 100644 mm2src/mm2_main/src/lp_swap/taker_restart.rs diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index ac5a1d7a74..d123da049b 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -98,6 +98,8 @@ use std::sync::atomic::{AtomicU64, Ordering}; #[path = "lp_swap/saved_swap.rs"] mod saved_swap; #[path = "lp_swap/swap_lock.rs"] mod swap_lock; #[path = "lp_swap/swap_watcher.rs"] pub(crate) mod swap_watcher; +#[path = "lp_swap/taker_restart.rs"] +pub(crate) mod taker_restart; #[path = "lp_swap/taker_swap.rs"] pub(crate) mod taker_swap; #[path = "lp_swap/trade_preimage.rs"] mod trade_preimage; diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index e0c593aedf..8c14bdb6ba 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -1,28 +1,23 @@ -use super::taker_swap::TakerSwapCommand; use super::{broadcast_p2p_tx_msg, get_payment_locktime, lp_coinfind, taker_payment_spend_deadline, tx_helper_topic, - AtomicSwap, H256Json, SwapsContext, TakerSavedSwap, TakerSwap, WAIT_CONFIRM_INTERVAL_SEC}; + H256Json, SwapsContext, WAIT_CONFIRM_INTERVAL_SEC}; use crate::mm2::lp_network::{P2PRequestError, P2PRequestResult}; -use crate::mm2::lp_swap::taker_swap::{TakerPaymentSpentData, TakerSavedEvent, TakerSwapEvent}; -use crate::mm2::lp_swap::{SavedSwap, SavedSwapIo, TransactionIdentifier, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG}; + use crate::mm2::MmError; use async_trait::async_trait; use coins::{CanRefundHtlc, ConfirmPaymentInput, FoundSwapTxSpend, MmCoinEnum, RefundPaymentArgs, - SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, TransactionEnum, ValidateWatcherSpendInput, - WaitForHTLCTxSpendArgs, WatcherSearchForSwapTxSpendInput, WatcherSpendType, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput}; + SendMakerPaymentSpendPreimageInput, WaitForHTLCTxSpendArgs, WatcherSearchForSwapTxSpendInput, + WatcherValidatePaymentInput, WatcherValidateTakerFeeInput}; use common::executor::{AbortSettings, SpawnAbortable, Timer}; use common::log::{debug, error, info}; use common::state_machine::prelude::*; -use common::{now_ms, now_sec, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{now_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use futures::compat::Future01CompatExt; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::MapToMmResult; use mm2_libp2p::{decode_signed, pub_sub_topic, TopicPrefix}; -use rpc::v1::types::Bytes; use serde::{Deserialize, Serialize}; use serde_json as json; use std::cmp::min; -use std::sync::atomic::Ordering; use std::sync::Arc; use uuid::Uuid; @@ -663,269 +658,3 @@ fn spawn_taker_swap_watcher(ctx: MmArc, watcher_data: TakerSwapWatcherData, veri } pub fn watcher_topic(ticker: &str) -> String { pub_sub_topic(WATCHER_PREFIX, ticker) } - -pub async fn get_command_based_on_watcher_activity( - ctx: &MmArc, - swap: &TakerSwap, - mut saved: TakerSavedSwap, - command: TakerSwapCommand, -) -> Result { - #[cfg(not(any(test, feature = "run-docker-tests")))] - { - let watcher_refund_time = swap.r().data.started_at - + (default_watcher_maker_payment_spend_factor() * swap.r().data.lock_duration as f64) as u64; - if now_sec() < watcher_refund_time { - return Ok(command); - } - } - - match command { - TakerSwapCommand::Start => Ok(command), - TakerSwapCommand::Negotiate => Ok(command), - TakerSwapCommand::SendTakerFee => Ok(command), - TakerSwapCommand::WaitForMakerPayment => Ok(command), - TakerSwapCommand::ValidateMakerPayment => Ok(command), - TakerSwapCommand::SendTakerPayment => Ok(command), - TakerSwapCommand::WaitForTakerPaymentSpend => match check_taker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx))) => { - add_taker_payment_spent_event(swap, &mut saved, &taker_payment_spend_tx).await?; - check_maker_payment_spend_and_add_event(ctx, swap, saved).await - }, - Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { - add_taker_payment_refunded_by_watcher_event(ctx, swap, saved, taker_payment_refund_tx).await - }, - Ok(None) => Ok(command), - 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::PrepareForTakerPaymentRefund | TakerSwapCommand::RefundTakerPayment => { - #[cfg(not(any(test, feature = "run-docker-tests")))] - { - let watcher_refund_time = swap.r().data.started_at - + (default_watcher_refund_factor() * swap.r().data.lock_duration as f64) as u64; - if now_sec() < watcher_refund_time { - return Ok(command); - } - } - - match check_taker_payment_spend(swap).await { - Ok(Some(FoundSwapTxSpend::Spent(_))) => ERR!("Taker payment is not expected to be spent at this point"), - Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { - add_taker_payment_refunded_by_watcher_event(ctx, swap, saved, taker_payment_refund_tx).await - }, - Ok(None) => Ok(command), - Err(e) => ERR!("Error {} when trying to find taker payment spend", e), - } - }, - TakerSwapCommand::FinalizeTakerPaymentRefund => Ok(command), - TakerSwapCommand::Finish => Ok(command), - } -} - -pub async fn check_maker_payment_spend_and_add_event( - ctx: &MmArc, - swap: &TakerSwap, - mut saved: TakerSavedSwap, -) -> Result { - let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; - let secret_hash = swap.r().secret_hash.0.clone(); - let maker_coin_start_block = swap.r().data.maker_coin_start_block; - let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); - - let maker_payment = match &swap.r().maker_payment { - Some(tx) => tx.tx_hex.0.clone(), - None => return ERR!("No info about maker payment, swap is not recoverable"), - }; - let unique_data = swap.unique_swap_data(); - let watcher_reward = swap.r().watcher_reward; - - let maker_payment_spend_tx = match swap.maker_coin - .search_for_swap_tx_spend_other(SearchForSwapTxSpendInput { - time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, - other_pub: other_maker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &maker_payment, - search_from_block: maker_coin_start_block, - swap_contract_address: &maker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }) - .await { - Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => maker_payment_spend_tx, - Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => return ERR!("Maker has cheated by both spending the taker payment, and refunding the maker payment with transaction {:#?}", maker_payment_refund_tx.tx_hash()), - Ok(None) => return Ok(TakerSwapCommand::SpendMakerPayment), - Err(e) => return ERR!("Error {} when trying to find maker payment spend", e) - }; - - validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; - let tx_hash = maker_payment_spend_tx.tx_hash(); - info!("Watcher maker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: Bytes::from(maker_payment_spend_tx.tx_hex()), - tx_hash, - }; - - let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - 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) -} - -pub async fn check_taker_payment_spend(swap: &TakerSwap) -> Result, String> { - let taker_payment = match &swap.r().taker_payment { - Some(tx) => tx.tx_hex.0.clone(), - None => return ERR!("No info about taker payment, swap is not recoverable"), - }; - - let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; - let taker_coin_start_block = swap.r().data.taker_coin_start_block; - let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - - let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { - Ok(_) => swap.r().data.started_at as u32, - Err(_) => swap.r().data.taker_payment_lock as u32, - }; - let secret_hash = swap.r().secret_hash.0.clone(); - let unique_data = swap.unique_swap_data(); - let watcher_reward = swap.r().watcher_reward; - - swap.taker_coin - .search_for_swap_tx_spend_my(SearchForSwapTxSpendInput { - time_lock: taker_payment_lock, - other_pub: other_taker_coin_htlc_pub.as_slice(), - secret_hash: &secret_hash, - tx: &taker_payment, - search_from_block: taker_coin_start_block, - swap_contract_address: &taker_coin_swap_contract_address, - swap_unique_data: &unique_data, - watcher_reward, - }) - .await -} - -pub async fn validate_maker_payment_spend( - swap: &TakerSwap, - maker_payment_spend_tx: &TransactionEnum, -) -> Result<(), String> { - let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; - let secret_hash = swap.r().secret_hash.0.clone(); - let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); - - let validate_input = ValidateWatcherSpendInput { - payment_tx: maker_payment_spend_tx.tx_hex(), - maker_pub: other_maker_coin_htlc_pub.to_vec(), - swap_contract_address: maker_coin_swap_contract_address, - time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, - secret_hash: secret_hash.clone(), - amount: swap.maker_amount.to_decimal(), - watcher_reward: None, - spend_type: WatcherSpendType::MakerPaymentSpend, - }; - swap.maker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .compat() - .await - .map_err(|e| e.to_string())?; - Ok(()) -} - -pub async fn validate_taker_payment_refund( - swap: &TakerSwap, - taker_payment_refund_tx: &TransactionEnum, -) -> Result<(), String> { - let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; - let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { - Ok(_) => swap.r().data.started_at as u32, - Err(_) => swap.r().data.taker_payment_lock as u32, - }; - let secret_hash = swap.r().secret_hash.0.clone(); - - let validate_input = ValidateWatcherSpendInput { - payment_tx: taker_payment_refund_tx.tx_hex(), - maker_pub: other_maker_coin_htlc_pub.to_vec(), - swap_contract_address: taker_coin_swap_contract_address, - time_lock: taker_payment_lock, - secret_hash: secret_hash.clone(), - amount: swap.taker_amount.to_decimal(), - watcher_reward: None, - spend_type: WatcherSpendType::TakerPaymentRefund, - }; - - swap.taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .compat() - .await - .map_err(|e| e.to_string())?; - Ok(()) -} - -pub async fn add_taker_payment_spent_event( - swap: &TakerSwap, - saved: &mut TakerSavedSwap, - taker_payment_spend_tx: &TransactionEnum, -) -> Result<(), String> { - let secret_hash = swap.r().secret_hash.0.clone(); - let watcher_reward = swap.r().watcher_reward; - - let tx_hash = taker_payment_spend_tx.tx_hash(); - info!("Taker payment spend tx {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: Bytes::from(taker_payment_spend_tx.tx_hex()), - tx_hash, - }; - let secret = match swap - .taker_coin - .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) - .await - { - Ok(bytes) => H256Json::from(bytes.as_slice()), - Err(_) => { - return ERR!("Could not extract secret from taker payment spend transaction"); - }, - }; - - let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { - transaction: tx_ident, - secret, - }); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - Ok(()) -} - -pub async fn add_taker_payment_refunded_by_watcher_event( - ctx: &MmArc, - swap: &TakerSwap, - mut saved: TakerSavedSwap, - taker_payment_refund_tx: TransactionEnum, -) -> Result { - validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; - let tx_hash = taker_payment_refund_tx.tx_hash(); - info!("Taker refund tx hash {:02x}", tx_hash); - let tx_ident = TransactionIdentifier { - tx_hex: Bytes::from(taker_payment_refund_tx.tx_hex()), - tx_hash, - }; - - let event = TakerSwapEvent::TakerPaymentRefundedByWatcher(Some(tx_ident)); - let to_save = TakerSavedEvent { - timestamp: now_ms(), - event, - }; - saved.events.push(to_save); - - let new_swap = SavedSwap::Taker(saved); - try_s!(new_swap.save_to_db(ctx).await); - info!("Taker payment is refunded by the watcher"); - Ok(TakerSwapCommand::Finish) -} diff --git a/mm2src/mm2_main/src/lp_swap/taker_restart.rs b/mm2src/mm2_main/src/lp_swap/taker_restart.rs new file mode 100644 index 0000000000..d8db402a45 --- /dev/null +++ b/mm2src/mm2_main/src/lp_swap/taker_restart.rs @@ -0,0 +1,282 @@ +use super::taker_swap::TakerSwapCommand; +use super::{AtomicSwap, TakerSavedSwap, TakerSwap}; +use crate::mm2::lp_swap::taker_swap::{TakerPaymentSpentData, TakerSavedEvent, TakerSwapEvent}; +use crate::mm2::lp_swap::{SavedSwap, SavedSwapIo, TransactionIdentifier, MAKER_PAYMENT_SPENT_BY_WATCHER_LOG}; +use coins::{FoundSwapTxSpend, SearchForSwapTxSpendInput, TransactionEnum, ValidateWatcherSpendInput, WatcherSpendType}; +use common::log::info; +use common::{now_ms, Future01CompatExt}; +use mm2_core::mm_ctx::MmArc; +use rpc::v1::types::{Bytes, H256}; +use std::sync::atomic::Ordering; + +#[cfg(not(any(test, feature = "run-docker-tests")))] +use super::swap_watcher::{default_watcher_maker_payment_spend_factor, default_watcher_refund_factor}; + +#[cfg(not(any(test, feature = "run-docker-tests")))] +use common::now_sec; + +pub async fn get_command_based_on_watcher_activity( + ctx: &MmArc, + swap: &TakerSwap, + mut saved: TakerSavedSwap, + command: TakerSwapCommand, +) -> Result { + #[cfg(not(any(test, feature = "run-docker-tests")))] + { + let watcher_refund_time = swap.r().data.started_at + + (default_watcher_maker_payment_spend_factor() * swap.r().data.lock_duration as f64) as u64; + if now_sec() < watcher_refund_time { + return Ok(command); + } + } + + match command { + TakerSwapCommand::Start => Ok(command), + TakerSwapCommand::Negotiate => Ok(command), + TakerSwapCommand::SendTakerFee => Ok(command), + TakerSwapCommand::WaitForMakerPayment => Ok(command), + TakerSwapCommand::ValidateMakerPayment => Ok(command), + TakerSwapCommand::SendTakerPayment => Ok(command), + TakerSwapCommand::WaitForTakerPaymentSpend => match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(taker_payment_spend_tx))) => { + add_taker_payment_spent_event(swap, &mut saved, &taker_payment_spend_tx).await?; + check_maker_payment_spend_and_add_event(ctx, swap, saved).await + }, + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { + add_taker_payment_refunded_by_watcher_event(ctx, swap, saved, taker_payment_refund_tx).await + }, + Ok(None) => Ok(command), + 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::PrepareForTakerPaymentRefund | TakerSwapCommand::RefundTakerPayment => { + #[cfg(not(any(test, feature = "run-docker-tests")))] + { + let watcher_refund_time = swap.r().data.started_at + + (default_watcher_refund_factor() * swap.r().data.lock_duration as f64) as u64; + if now_sec() < watcher_refund_time { + return Ok(command); + } + } + + match check_taker_payment_spend(swap).await { + Ok(Some(FoundSwapTxSpend::Spent(_))) => ERR!("Taker payment is not expected to be spent at this point"), + Ok(Some(FoundSwapTxSpend::Refunded(taker_payment_refund_tx))) => { + add_taker_payment_refunded_by_watcher_event(ctx, swap, saved, taker_payment_refund_tx).await + }, + Ok(None) => Ok(command), + Err(e) => ERR!("Error {} when trying to find taker payment spend", e), + } + }, + TakerSwapCommand::FinalizeTakerPaymentRefund => Ok(command), + TakerSwapCommand::Finish => Ok(command), + } +} + +pub async fn check_maker_payment_spend_and_add_event( + ctx: &MmArc, + swap: &TakerSwap, + mut saved: TakerSavedSwap, +) -> Result { + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let secret_hash = swap.r().secret_hash.0.clone(); + let maker_coin_start_block = swap.r().data.maker_coin_start_block; + let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); + + let maker_payment = match &swap.r().maker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about maker payment, swap is not recoverable"), + }; + let unique_data = swap.unique_swap_data(); + let watcher_reward = swap.r().watcher_reward; + + let maker_payment_spend_tx = match swap.maker_coin + .search_for_swap_tx_spend_other(SearchForSwapTxSpendInput { + time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, + other_pub: other_maker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &maker_payment, + search_from_block: maker_coin_start_block, + swap_contract_address: &maker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }) + .await { + Ok(Some(FoundSwapTxSpend::Spent(maker_payment_spend_tx))) => maker_payment_spend_tx, + Ok(Some(FoundSwapTxSpend::Refunded(maker_payment_refund_tx))) => return ERR!("Maker has cheated by both spending the taker payment, and refunding the maker payment with transaction {:#?}", maker_payment_refund_tx.tx_hash()), + Ok(None) => return Ok(TakerSwapCommand::SpendMakerPayment), + Err(e) => return ERR!("Error {} when trying to find maker payment spend", e) + }; + + validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; + let tx_hash = maker_payment_spend_tx.tx_hash(); + info!("Watcher maker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: Bytes::from(maker_payment_spend_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::MakerPaymentSpentByWatcher(tx_ident); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + 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) +} + +pub async fn check_taker_payment_spend(swap: &TakerSwap) -> Result, String> { + let taker_payment = match &swap.r().taker_payment { + Some(tx) => tx.tx_hex.0.clone(), + None => return ERR!("No info about taker payment, swap is not recoverable"), + }; + + let other_taker_coin_htlc_pub = swap.r().other_taker_coin_htlc_pub; + let taker_coin_start_block = swap.r().data.taker_coin_start_block; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); + + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, + }; + let secret_hash = swap.r().secret_hash.0.clone(); + let unique_data = swap.unique_swap_data(); + let watcher_reward = swap.r().watcher_reward; + + swap.taker_coin + .search_for_swap_tx_spend_my(SearchForSwapTxSpendInput { + time_lock: taker_payment_lock, + other_pub: other_taker_coin_htlc_pub.as_slice(), + secret_hash: &secret_hash, + tx: &taker_payment, + search_from_block: taker_coin_start_block, + swap_contract_address: &taker_coin_swap_contract_address, + swap_unique_data: &unique_data, + watcher_reward, + }) + .await +} + +pub async fn validate_maker_payment_spend( + swap: &TakerSwap, + maker_payment_spend_tx: &TransactionEnum, +) -> Result<(), String> { + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let secret_hash = swap.r().secret_hash.0.clone(); + let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend_tx.tx_hex(), + maker_pub: other_maker_coin_htlc_pub.to_vec(), + swap_contract_address: maker_coin_swap_contract_address, + time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, + secret_hash: secret_hash.clone(), + amount: swap.maker_amount.to_decimal(), + watcher_reward: None, + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + swap.maker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .compat() + .await + .map_err(|e| e.to_string())?; + Ok(()) +} + +pub async fn validate_taker_payment_refund( + swap: &TakerSwap, + taker_payment_refund_tx: &TransactionEnum, +) -> Result<(), String> { + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, + }; + let secret_hash = swap.r().secret_hash.0.clone(); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund_tx.tx_hex(), + maker_pub: other_maker_coin_htlc_pub.to_vec(), + swap_contract_address: taker_coin_swap_contract_address, + time_lock: taker_payment_lock, + secret_hash: secret_hash.clone(), + amount: swap.taker_amount.to_decimal(), + watcher_reward: None, + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + swap.taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .compat() + .await + .map_err(|e| e.to_string())?; + Ok(()) +} + +pub async fn add_taker_payment_spent_event( + swap: &TakerSwap, + saved: &mut TakerSavedSwap, + taker_payment_spend_tx: &TransactionEnum, +) -> Result<(), String> { + let secret_hash = swap.r().secret_hash.0.clone(); + let watcher_reward = swap.r().watcher_reward; + + let tx_hash = taker_payment_spend_tx.tx_hash(); + info!("Taker payment spend tx {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: Bytes::from(taker_payment_spend_tx.tx_hex()), + tx_hash, + }; + let secret = match swap + .taker_coin + .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) + .await + { + Ok(bytes) => H256::from(bytes.as_slice()), + Err(_) => { + return ERR!("Could not extract secret from taker payment spend transaction"); + }, + }; + + let event = TakerSwapEvent::TakerPaymentSpent(TakerPaymentSpentData { + transaction: tx_ident, + secret, + }); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + Ok(()) +} + +pub async fn add_taker_payment_refunded_by_watcher_event( + ctx: &MmArc, + swap: &TakerSwap, + mut saved: TakerSavedSwap, + taker_payment_refund_tx: TransactionEnum, +) -> Result { + validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; + let tx_hash = taker_payment_refund_tx.tx_hash(); + info!("Taker refund tx hash {:02x}", tx_hash); + let tx_ident = TransactionIdentifier { + tx_hex: Bytes::from(taker_payment_refund_tx.tx_hex()), + tx_hash, + }; + + let event = TakerSwapEvent::TakerPaymentRefundedByWatcher(Some(tx_ident)); + let to_save = TakerSavedEvent { + timestamp: now_ms(), + event, + }; + saved.events.push(to_save); + + let new_swap = SavedSwap::Taker(saved); + try_s!(new_swap.save_to_db(ctx).await); + info!("Taker payment is refunded by the watcher"); + Ok(TakerSwapCommand::Finish) +} diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index df8edfb709..a6cb9be9b0 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -12,7 +12,7 @@ use super::{broadcast_my_swap_status, broadcast_swap_message, broadcast_swap_msg SwapTxDataMsg, SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL_SEC}; use crate::mm2::lp_network::subscribe_to_topic; use crate::mm2::lp_ordermatch::TakerOrderBuilder; -use crate::mm2::lp_swap::swap_watcher::get_command_based_on_watcher_activity; +use crate::mm2::lp_swap::taker_restart::get_command_based_on_watcher_activity; use crate::mm2::lp_swap::{broadcast_p2p_tx_msg, broadcast_swap_msg_every_delayed, tx_helper_topic, wait_for_maker_payment_conf_duration, TakerSwapWatcherData}; use coins::lp_price::fetch_swap_coins_price; From 9888dc5d90c2e32eebacf7d2f09cddcf2dc7891d Mon Sep 17 00:00:00 2001 From: Caglar Kaya Date: Fri, 18 Aug 2023 15:56:16 +0300 Subject: [PATCH 47/53] remove unnecessary validation functions in taker restart module --- mm2src/mm2_main/src/lp_swap/taker_restart.rs | 100 ++++++++----------- 1 file changed, 41 insertions(+), 59 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_restart.rs b/mm2src/mm2_main/src/lp_swap/taker_restart.rs index d8db402a45..2e05401728 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_restart.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_restart.rs @@ -108,7 +108,22 @@ pub async fn check_maker_payment_spend_and_add_event( Err(e) => return ERR!("Error {} when trying to find maker payment spend", e) }; - validate_maker_payment_spend(swap, &maker_payment_spend_tx).await?; + let validate_input = ValidateWatcherSpendInput { + payment_tx: maker_payment_spend_tx.tx_hex(), + maker_pub: other_maker_coin_htlc_pub.to_vec(), + swap_contract_address: maker_coin_swap_contract_address, + time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, + secret_hash: secret_hash.clone(), + amount: swap.maker_amount.to_decimal(), + watcher_reward: None, + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + swap.maker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .compat() + .await + .map_err(|e| e.to_string())?; + let tx_hash = maker_payment_spend_tx.tx_hash(); info!("Watcher maker payment spend tx {:02x}", tx_hash); let tx_ident = TransactionIdentifier { @@ -160,63 +175,6 @@ pub async fn check_taker_payment_spend(swap: &TakerSwap) -> Result Result<(), String> { - let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; - let secret_hash = swap.r().secret_hash.0.clone(); - let maker_coin_swap_contract_address = swap.r().data.maker_coin_swap_contract_address.clone(); - - let validate_input = ValidateWatcherSpendInput { - payment_tx: maker_payment_spend_tx.tx_hex(), - maker_pub: other_maker_coin_htlc_pub.to_vec(), - swap_contract_address: maker_coin_swap_contract_address, - time_lock: swap.maker_payment_lock.load(Ordering::Relaxed) as u32, - secret_hash: secret_hash.clone(), - amount: swap.maker_amount.to_decimal(), - watcher_reward: None, - spend_type: WatcherSpendType::MakerPaymentSpend, - }; - swap.maker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .compat() - .await - .map_err(|e| e.to_string())?; - Ok(()) -} - -pub async fn validate_taker_payment_refund( - swap: &TakerSwap, - taker_payment_refund_tx: &TransactionEnum, -) -> Result<(), String> { - let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; - let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); - let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { - Ok(_) => swap.r().data.started_at as u32, - Err(_) => swap.r().data.taker_payment_lock as u32, - }; - let secret_hash = swap.r().secret_hash.0.clone(); - - let validate_input = ValidateWatcherSpendInput { - payment_tx: taker_payment_refund_tx.tx_hex(), - maker_pub: other_maker_coin_htlc_pub.to_vec(), - swap_contract_address: taker_coin_swap_contract_address, - time_lock: taker_payment_lock, - secret_hash: secret_hash.clone(), - amount: swap.taker_amount.to_decimal(), - watcher_reward: None, - spend_type: WatcherSpendType::TakerPaymentRefund, - }; - - swap.taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .compat() - .await - .map_err(|e| e.to_string())?; - Ok(()) -} - pub async fn add_taker_payment_spent_event( swap: &TakerSwap, saved: &mut TakerSavedSwap, @@ -260,7 +218,31 @@ pub async fn add_taker_payment_refunded_by_watcher_event( mut saved: TakerSavedSwap, taker_payment_refund_tx: TransactionEnum, ) -> Result { - validate_taker_payment_refund(swap, &taker_payment_refund_tx).await?; + let other_maker_coin_htlc_pub = swap.r().other_maker_coin_htlc_pub; + let taker_coin_swap_contract_address = swap.r().data.taker_coin_swap_contract_address.clone(); + let taker_payment_lock = match std::env::var("USE_TEST_LOCKTIME") { + Ok(_) => swap.r().data.started_at as u32, + Err(_) => swap.r().data.taker_payment_lock as u32, + }; + let secret_hash = swap.r().secret_hash.0.clone(); + + let validate_input = ValidateWatcherSpendInput { + payment_tx: taker_payment_refund_tx.tx_hex(), + maker_pub: other_maker_coin_htlc_pub.to_vec(), + swap_contract_address: taker_coin_swap_contract_address, + time_lock: taker_payment_lock, + secret_hash: secret_hash.clone(), + amount: swap.taker_amount.to_decimal(), + watcher_reward: None, + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + swap.taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .compat() + .await + .map_err(|e| e.to_string())?; + let tx_hash = taker_payment_refund_tx.tx_hash(); info!("Taker refund tx hash {:02x}", tx_hash); let tx_ident = TransactionIdentifier { From c83eb59b0c5215f11583066727eedc841b3ae664 Mon Sep 17 00:00:00 2001 From: shamardy Date: Tue, 22 Aug 2023 14:49:07 +0300 Subject: [PATCH 48/53] add TakerPaymentRefundedByWatcher to TAKER_ERROR_EVENTS, fix taker_swap.rs tests --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 38 +++++++++++++---------- mm2src/mm2_test_helpers/src/for_tests.rs | 3 +- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index a6cb9be9b0..b235f4a021 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -73,7 +73,7 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ "Finished", ]; -pub const TAKER_ERROR_EVENTS: [&str; 15] = [ +pub const TAKER_ERROR_EVENTS: [&str; 16] = [ "StartFailed", "NegotiateFailed", "TakerFeeSendFailed", @@ -87,6 +87,7 @@ pub const TAKER_ERROR_EVENTS: [&str; 15] = [ "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", "TakerPaymentRefunded", + "TakerPaymentRefundedByWatcher", "TakerPaymentRefundFailed", "TakerPaymentRefundFinished", ]; @@ -264,6 +265,7 @@ impl TakerSavedSwap { | TakerSwapEvent::TakerFeeSendFailed(_) | TakerSwapEvent::MakerPaymentValidateFailed(_) | TakerSwapEvent::TakerPaymentRefunded(_) + | TakerSwapEvent::TakerPaymentRefundedByWatcher(_) | TakerSwapEvent::MakerPaymentSpent(_) | TakerSwapEvent::MakerPaymentWaitConfirmFailed(_) => { return false; @@ -2008,12 +2010,14 @@ impl TakerSwap { swap.apply_event(saved_event.event.clone()); } - let mut command = saved - .events - .last() - .unwrap() - .get_command() - .ok_or("Finished swaps should not enter to load_from_saved function")?; + let mut command = match saved.events.last().unwrap().get_command() { + Some(command) => command, + #[cfg(not(test))] + None => return ERR!("Finished swaps should not enter to load_from_saved function"), + // load_from_saved is used in tests to load finished swaps + #[cfg(test)] + None => return Ok((swap, None)), + }; if taker_coin.is_supported_by_watchers() && maker_coin.is_supported_by_watchers() @@ -2677,7 +2681,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","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","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_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2713,7 +2717,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","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","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")); @@ -2763,7 +2767,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","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","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")); @@ -2816,7 +2820,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","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","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_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2859,7 +2863,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","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","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_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2890,7 +2894,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","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","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_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2937,7 +2941,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","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","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_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2974,7 +2978,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","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","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_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -3009,7 +3013,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":"83965c539899cc0f918552e5a26915de40ee8852"}}},{"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","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":"83965c539899cc0f918552e5a26915de40ee8852"}}},{"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_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -3043,7 +3047,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","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","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(); assert!(!swap.is_recoverable()); } diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index 5673b93f63..294af93206 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -109,7 +109,7 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ "Finished", ]; -pub const TAKER_ERROR_EVENTS: [&str; 15] = [ +pub const TAKER_ERROR_EVENTS: [&str; 16] = [ "StartFailed", "NegotiateFailed", "TakerFeeSendFailed", @@ -123,6 +123,7 @@ pub const TAKER_ERROR_EVENTS: [&str; 15] = [ "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", "TakerPaymentRefunded", + "TakerPaymentRefundedByWatcher", "TakerPaymentRefundFailed", "TakerPaymentRefundFinished", ]; From 60a303a56754cd0a2726331eb43cc877ff98f660 Mon Sep 17 00:00:00 2001 From: shamardy Date: Tue, 22 Aug 2023 15:24:47 +0300 Subject: [PATCH 49/53] fix recreate swap tests --- ...aker_swap_maker_payment_wait_confirm_failed_taker_saved.json | 2 +- .../mm2_main/src/for_tests/recreate_maker_swap_taker_saved.json | 2 +- .../src/for_tests/recreate_taker_swap_taker_expected.json | 2 +- ...r_swap_taker_payment_wait_confirm_failed_taker_expected.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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 4be55eef01..07ebe0d6b9 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 @@ -50,5 +50,5 @@ "gui":"atomicDEX 0.5.1 iOS", "mm_version":"1b065636a", "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","TakerPaymentRefunded","TakerPaymentRefundFailed"] + "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 21760c177e..8fe8622f74 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 @@ -62,5 +62,5 @@ "gui":"atomicDEX 0.5.1 iOS", "mm_version":"1b065636a", "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","TakerPaymentRefunded","TakerPaymentRefundFailed"] + "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 6fb8382594..cfb9ab66f8 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 @@ -58,5 +58,5 @@ "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","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] + "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","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 30c4b256aa..cb3146b2a3 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 @@ -54,5 +54,5 @@ "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","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] + "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] } \ No newline at end of file From 70d02135ea257934613653b484bac29f4790ae77 Mon Sep 17 00:00:00 2001 From: shamardy Date: Wed, 23 Aug 2023 18:15:27 +0300 Subject: [PATCH 50/53] fix trade_test_with_maker_segwit, trade_test_with_taker_segwit --- mm2src/mm2_test_helpers/src/for_tests.rs | 35 +++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index 294af93206..cc7c015414 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -109,6 +109,38 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ "Finished", ]; +// Taker using watchers and watcher spends maker payment +pub const TAKER_ACTUAL_EVENTS_WATCHER_SPENDS_MAKER_PAYMENT: [&str; 12] = [ + "Started", + "Negotiated", + "TakerFeeSent", + "TakerPaymentInstructionsReceived", + "MakerPaymentReceived", + "MakerPaymentWaitConfirmStarted", + "MakerPaymentValidatedAndConfirmed", + "TakerPaymentSent", + "WatcherMessageSent", + "TakerPaymentSpent", + "MakerPaymentSpentByWatcher", + "Finished", +]; + +// Taker using watchers and spends maker payment instead of watcher +pub const TAKER_ACTUAL_EVENTS_TAKER_SPENDS_MAKER_PAYMENT: [&str; 12] = [ + "Started", + "Negotiated", + "TakerFeeSent", + "TakerPaymentInstructionsReceived", + "MakerPaymentReceived", + "MakerPaymentWaitConfirmStarted", + "MakerPaymentValidatedAndConfirmed", + "TakerPaymentSent", + "WatcherMessageSent", + "TakerPaymentSpent", + "MakerPaymentSpent", + "Finished", +]; + pub const TAKER_ERROR_EVENTS: [&str; 16] = [ "StartFailed", "NegotiateFailed", @@ -2046,7 +2078,8 @@ pub async fn check_stats_swap_status(mm: &MarketMakerIt, uuid: &str) { assert_eq!(maker_actual_events.as_slice(), MAKER_SUCCESS_EVENTS); assert!( taker_actual_events.as_slice() == TAKER_SUCCESS_EVENTS - || taker_actual_events.as_slice() == TAKER_USING_WATCHERS_SUCCESS_EVENTS + || taker_actual_events.as_slice() == TAKER_ACTUAL_EVENTS_WATCHER_SPENDS_MAKER_PAYMENT + || taker_actual_events.as_slice() == TAKER_ACTUAL_EVENTS_TAKER_SPENDS_MAKER_PAYMENT ); } From 7df06c138233dda1aaaf3ab322a2171636e01a35 Mon Sep 17 00:00:00 2001 From: shamardy Date: Thu, 24 Aug 2023 12:53:40 +0300 Subject: [PATCH 51/53] fix taker_swap_should_not_kick_start_if_finished_during_waiting_for_file_lock --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 5ca988be50..e75e322ec4 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -2027,10 +2027,6 @@ impl TakerSwap { let mut command = match saved.events.last().unwrap().get_command() { Some(command) => command, - #[cfg(not(test))] - None => return ERR!("Finished swaps should not enter to load_from_saved function"), - // load_from_saved is used in tests to load finished swaps - #[cfg(test)] None => return Ok((swap, None)), }; From 1eb1d489458e99ec67cb445b9cc7ed5c1bc000f4 Mon Sep 17 00:00:00 2001 From: shamardy Date: Tue, 29 Aug 2023 08:31:28 +0300 Subject: [PATCH 52/53] fix is_recoverable for taker --- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index e75e322ec4..97d30b1e72 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -267,6 +267,7 @@ impl TakerSavedSwap { | TakerSwapEvent::TakerPaymentRefunded(_) | TakerSwapEvent::TakerPaymentRefundedByWatcher(_) | TakerSwapEvent::MakerPaymentSpent(_) + | TakerSwapEvent::MakerPaymentSpentByWatcher(_) | TakerSwapEvent::MakerPaymentWaitConfirmFailed(_) => { return false; }, From 10cd6dcb1db2d450db9c468915d99e03510d1b54 Mon Sep 17 00:00:00 2001 From: shamardy Date: Wed, 25 Oct 2023 23:18:55 +0300 Subject: [PATCH 53/53] * convert body bytes to string in single_response error * use ALICE_PASSPHRASE for eth_distributor in docker_tests_common.rs --- mm2src/coins/eth/web3_transport/http_transport.rs | 6 ++++-- .../tests/docker_tests/docker_tests_common.rs | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/mm2src/coins/eth/web3_transport/http_transport.rs b/mm2src/coins/eth/web3_transport/http_transport.rs index 75c747bc82..dc2734c074 100644 --- a/mm2src/coins/eth/web3_transport/http_transport.rs +++ b/mm2src/coins/eth/web3_transport/http_transport.rs @@ -29,8 +29,10 @@ where { let response = serde_json::from_slice(&response).map_err(|e| { Error::InvalidResponse(format!( - "url: {}, Error deserializing response: {}, raw response: {:?}", - rpc_url, e, response + "url: {}, Error deserializing response: {}, raw response: {}", + rpc_url, + e, + String::from_utf8_lossy(&response) )) })?; 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 22695cf7e5..ad8e874819 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs @@ -7,10 +7,6 @@ pub use mm2_test_helpers::for_tests::{check_my_swap_status, check_recent_swaps, ETH_DEV_SWAP_CONTRACT, ETH_DEV_TOKEN_CONTRACT, MAKER_ERROR_EVENTS, MAKER_SUCCESS_EVENTS, TAKER_ERROR_EVENTS, TAKER_SUCCESS_EVENTS}; -pub use secp256k1::{PublicKey, SecretKey}; -pub use std::env; -pub use std::thread; - use bitcrypto::{dhash160, ChecksumType}; use chain::TransactionOutput; use coins::eth::{eth_coin_from_conf_and_request, EthCoin}; @@ -33,14 +29,18 @@ use http::StatusCode; use keys::{Address, AddressHashEnum, KeyPair, NetworkPrefix as CashAddrPrefix}; use mm2_core::mm_ctx::{MmArc, MmCtxBuilder}; use mm2_number::BigDecimal; +use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::TransactionDetails; use primitives::hash::{H160, H256}; use script::Builder; use secp256k1::Secp256k1; +pub use secp256k1::{PublicKey, SecretKey}; use serde_json::{self as json, Value as Json}; +pub use std::env; use std::path::PathBuf; use std::process::Command; use std::sync::Mutex; +pub use std::thread; use std::time::Duration; use testcontainers::clients::Cli; use testcontainers::images::generic::{GenericImage, WaitFor}; @@ -148,8 +148,8 @@ pub fn eth_distributor() -> EthCoin { "urls": ETH_DEV_NODES, "swap_contract_address": ETH_DEV_SWAP_CONTRACT, }); - let keypair = - key_pair_from_seed("spice describe gravity federal blast come thank unfair canal monkey style afraid").unwrap(); + let alice_passphrase = get_passphrase!(".env.client", "ALICE_PASSPHRASE").unwrap(); + let keypair = key_pair_from_seed(&alice_passphrase).unwrap(); let priv_key_policy = PrivKeyBuildPolicy::IguanaPrivKey(keypair.private().secret); block_on(eth_coin_from_conf_and_request( &MM_CTX,