diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index f2d6b91e4bb..8780058bc26 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 3519d45596f..5e723f647c3 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 9f1ad75f8d7..383bee901a2 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 264bbc4f9b7..072e7b1e7b7 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());