diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 8514af4ab6..3085d0eaa7 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -23,8 +23,9 @@ 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, WatcherSpendType}; 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}; @@ -1472,6 +1473,246 @@ impl WatcherOps for EthCoin { // 1.Validate if taker fee is old } + fn taker_validates_payment_spend_or_refund(&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 time_lock = try_f!(input + .time_lock + .try_into() + .map_to_mm(ValidatePaymentError::TimelockOverflow)); + let swap_id = selfi.etomic_swap_id(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!( + "Transaction {:?} 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 actual_status = selfi + .payment_status(expected_swap_contract_address, Token::FixedBytes(swap_id.clone())) + .compat() + .await + .map_to_mm(ValidatePaymentError::Transport)?; + 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 {}, got {}", + expected_status, actual_status + ))); + } + + 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()))?; + + 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!( + "Transaction invalid swap_id arg {:?}, expected {:?}", + swap_id_input, + Token::FixedBytes(swap_id.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!( + "Transaction secret or secret_hash arg {:?} is invalid, expected {:?}", + hash_input, + Token::FixedBytes(secret_hash), + ))); + } + + let sender_input = get_function_input_data(&decoded, function, 4) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + 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!( + "Transaction sender arg {:?} is invalid, expected {:?}", + sender_input, + Token::Address(expected_sender) + ))); + } + + let receiver_input = get_function_input_data(&decoded, function, 5) + .map_to_mm(ValidatePaymentError::TxDeserializationError)?; + 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!( + "Transaction receiver arg {:?} is invalid, expected {:?}", + receiver_input, + Token::Address(expected_receiver) + ))); + } + + 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!( + "Transaction reward target arg {:?} is invalid, expected {:?}", + reward_target_input, + Token::Uint(U256::from(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!( + "Transaction sends contract reward on spend arg {:?} is invalid, expected {:?}", + contract_reward_input, + Token::Bool(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!( + "Transaction watcher reward amount arg {:?} is invalid, expected {:?}", + reward_amount_input, + Token::Uint(expected_reward_amount) + ))); + } + + if tx.value != U256::zero() { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Transaction 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 = 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!( + "Transaction 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!( + "Transaction 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!( + "Transaction 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!( + "Transaction 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<()> { let unsigned: UnverifiedTransaction = try_f!(rlp::decode(&input.payment_tx)); let tx = 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/coins/lightning.rs b/mm2src/coins/lightning.rs index 59b026121b..74d5619c5d 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, TransactionResult, TxMarshalingErr, UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, - ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, - WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest}; + ValidatePaymentError, 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; @@ -995,6 +996,10 @@ impl WatcherOps for LightningCoin { unimplemented!(); } + fn taker_validates_payment_spend_or_refund(&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 eb15ae1ca9..ac063c946c 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -640,6 +640,24 @@ pub struct WatcherValidatePaymentInput { pub maker_coin: MmCoinEnum, } +#[derive(Clone)] +pub enum WatcherSpendType { + TakerPaymentRefund, + MakerPaymentSpend, +} + +#[derive(Clone)] +pub struct ValidateWatcherSpendInput { + pub payment_tx: Vec, + pub maker_pub: Vec, + pub swap_contract_address: Option, + pub time_lock: u64, + pub secret_hash: Vec, + pub amount: BigDecimal, + pub watcher_reward: Option, + pub spend_type: WatcherSpendType, +} + /// Helper struct wrapping arguments for [SwapOps::validate_taker_payment] and [SwapOps::validate_maker_payment]. #[derive(Clone, Debug)] pub struct ValidatePaymentInput { @@ -1039,6 +1057,8 @@ pub trait WatcherOps { fn watcher_validate_taker_payment(&self, input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()>; + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()>; + async fn watcher_search_for_swap_tx_spend( &self, input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 36f8086e70..f556ba5032 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -25,8 +25,8 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, Coi TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TransactionResult, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentFut, - ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, - WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, + WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; @@ -1147,6 +1147,10 @@ impl WatcherOps for Qrc20Coin { unimplemented!(); } + fn taker_validates_payment_spend_or_refund(&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 5f7bb9e44d..56178efa7a 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -11,7 +11,7 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, TransactionDetails, TransactionFut, TransactionResult, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, - VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, + ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; @@ -647,6 +647,10 @@ impl WatcherOps for SolanaCoin { unimplemented!(); } + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + 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 134d8204ba..9270302b0b 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, TransactionResult, 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}; @@ -468,6 +468,10 @@ impl WatcherOps for SplToken { unimplemented!(); } + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + 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 1920c38709..cc569cb1d4 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -25,10 +25,10 @@ use crate::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BigDecimal, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TransactionResult, TransactionType, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, - ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, - WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFrom, - WithdrawFut, WithdrawRequest}; + ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, VerificationError, + VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, + WithdrawError, WithdrawFee, WithdrawFrom, WithdrawFut, WithdrawRequest}; use async_std::prelude::FutureExt as AsyncStdFutureExt; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; @@ -2739,6 +2739,10 @@ impl WatcherOps for TendermintCoin { unimplemented!(); } + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + 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 d753a0e61d..061478aa09 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -20,7 +20,7 @@ use crate::{big_decimal_from_sat_unsigned, utxo::sat_from_big_decimal, BalanceFu ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFrom, 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; @@ -498,6 +498,10 @@ impl WatcherOps for TendermintToken { unimplemented!(); } + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + 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 24408cf506..3991d73e17 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, GenTakerPaymentSpendArgs, GenTakerPaymentSpendResult, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, @@ -279,6 +280,10 @@ impl WatcherOps for TestCoin { unimplemented!(); } + fn taker_validates_payment_spend_or_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + 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 c94179e131..f584ccb6bf 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, TransactionResult, TransactionType, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, - ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult, - WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut}; + ValidatePaymentError, 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; @@ -1014,7 +1014,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] @@ -1096,6 +1096,11 @@ impl WatcherOps for BchCoin { utxo_common::watcher_validate_taker_payment(self, input) } + #[inline] + fn taker_validates_payment_spend_or_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + utxo_common::validate_payment_spend_or_refund(self, input) + } + 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 2f4c57ac6e..e901a53e94 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, TransactionResult, 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; @@ -700,7 +700,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] @@ -778,6 +778,11 @@ impl WatcherOps for QtumCoin { utxo_common::watcher_validate_taker_payment(self, input) } + #[inline] + fn taker_validates_payment_spend_or_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + utxo_common::validate_payment_spend_or_refund(self, input) + } + 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 07379fcbbf..e400b3e128 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, TransactionResult, 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; @@ -1515,6 +1515,10 @@ impl WatcherOps for SlpToken { unimplemented!(); } + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + 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 4f5970afc2..9f657d02d2 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -23,7 +23,7 @@ use crate::{CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, ConfirmPayment TxFeeDetails, TxGenError, TxMarshalingErr, TxPreimageWithSig, ValidateAddressResult, ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, ValidateTakerPaymentArgs, ValidateTakerPaymentError, ValidateTakerPaymentResult, ValidateTakerPaymentSpendPreimageError, - ValidateTakerPaymentSpendPreimageResult, VerificationError, VerificationResult, + ValidateTakerPaymentSpendPreimageResult, 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}; @@ -2299,6 +2299,33 @@ pub fn validate_taker_payment( ) } +pub fn validate_payment_spend_or_refund( + coin: &T, + 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 = try_f!(coin.as_ref().derivation_method.single_addr_or_err()); + let expected_script_pubkey = &output_script(my_address, ScriptType::P2PKH).to_bytes(); + 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 + )) + .into(), + )); + } + + Box::new(futures01::future::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 47582f8524..a9b5e3aa32 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -31,9 +31,10 @@ use crate::{CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinWithDeriva TradePreimageValue, TransactionFut, TransactionResult, TxMarshalingErr, TxPreimageWithSig, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, ValidateTakerPaymentArgs, - ValidateTakerPaymentResult, ValidateTakerPaymentSpendPreimageResult, VerificationResult, - WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, WithdrawSenderAddress}; + ValidateTakerPaymentResult, ValidateTakerPaymentSpendPreimageResult, ValidateWatcherSpendInput, + VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, + WithdrawSenderAddress}; use common::executor::{AbortableSystem, AbortedError}; use crypto::Bip44Chain; use futures::{FutureExt, TryFutureExt}; @@ -467,7 +468,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] @@ -545,6 +546,11 @@ impl WatcherOps for UtxoStandardCoin { utxo_common::watcher_validate_taker_payment(self, input) } + #[inline] + fn taker_validates_payment_spend_or_refund(&self, input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + utxo_common::validate_payment_spend_or_refund(self, input) + } + #[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 047a1cf1fd..af7d816435 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, TransactionResult, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, - ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, - WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, WithdrawRequest}; + ValidatePaymentError, 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; @@ -1608,6 +1609,10 @@ impl WatcherOps for ZCoin { unimplemented!(); } + fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { + unimplemented!() + } + async fn watcher_search_for_swap_tx_spend( &self, _input: WatcherSearchForSwapTxSpendInput<'_>, diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index fc7fc5f0d9..d2537425c1 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -279,10 +279,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/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 diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index 2ad35b86e4..d40ac478f2 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -81,6 +81,9 @@ use crate::mm2::lp_swap::{calc_max_maker_vol, check_balance_for_maker_swap, chec CoinVolumeInfo, MakerSwap, RunMakerSwapInput, RunTakerSwapInput, SecretHashAlgo, 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}; @@ -3119,6 +3122,9 @@ fn lp_connected_alice(ctx: MmArc, taker_order: TakerOrder, taker_match: TakerMat _ => todo!("implement fallback to the old protocol here"), } } else { + #[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, @@ -3132,6 +3138,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 3fa86a7bd2..c2620814a6 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -103,7 +103,9 @@ use std::sync::atomic::{AtomicU64, Ordering}; #[rustfmt::skip] mod swap_v2_pb; #[path = "lp_swap/swap_watcher.rs"] pub(crate) mod swap_watcher; -#[path = "lp_swap/taker_swap.rs"] mod taker_swap; +#[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/taker_swap_v2.rs"] pub mod taker_swap_v2; #[path = "lp_swap/trade_preimage.rs"] mod trade_preimage; @@ -131,13 +133,13 @@ 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, + REFUND_TEST_FAILURE_LOG, WATCHER_MESSAGE_SENT_LOG}; pub use trade_preimage::trade_preimage_rpc; pub const SWAP_PREFIX: TopicPrefix = "swap"; - pub const SWAP_V2_PREFIX: TopicPrefix = "swapv2"; - +pub const SWAP_FINISHED_LOG: &str = "Swap finished: "; pub const TX_HELPER_PREFIX: TopicPrefix = "txhlp"; const NEGOTIATE_SEND_INTERVAL: f64 = 30.; @@ -1213,6 +1215,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; } @@ -2082,7 +2085,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(), @@ -2095,10 +2101,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/recreate_swap_data.rs b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs index a816695d84..56e7877b70 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(_,_) @@ -272,6 +273,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 3519d45596..265c1a5142 100644 --- a/mm2src/mm2_main/src/lp_swap/saved_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/saved_swap.rs @@ -56,6 +56,13 @@ impl SavedSwap { } } + pub fn watcher_message_sent(&self) -> bool { + match &self { + SavedSwap::Taker(taker_swap) => taker_swap.watcher_message_sent(), + _ => false, + } + } + pub fn uuid(&self) -> &Uuid { match self { SavedSwap::Maker(swap) => &swap.uuid, @@ -104,7 +111,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/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index bd0d0f564f..80eb41f4e3 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -1,6 +1,7 @@ use super::{broadcast_p2p_tx_msg, get_payment_locktime, lp_coinfind, taker_payment_spend_deadline, tx_helper_topic, H256Json, SwapsContext, WAIT_CONFIRM_INTERVAL_SEC}; use crate::mm2::lp_network::{P2PRequestError, P2PRequestResult}; + use crate::mm2::MmError; use async_trait::async_trait; use coins::{CanRefundHtlc, ConfirmPaymentInput, FoundSwapTxSpend, MmCoinEnum, RefundPaymentArgs, @@ -65,9 +66,9 @@ impl WatcherStateMachine { 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, @@ -77,13 +78,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), @@ -179,6 +184,7 @@ impl State for ValidateTakerFee { type StateMachine = WatcherStateMachine; async fn on_changed(self: Box, watcher_ctx: &mut WatcherStateMachine) -> StateResult { + debug!("Watcher validate taker fee"); let validated_f = watcher_ctx .taker_coin .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { @@ -206,6 +212,7 @@ impl State for ValidateTakerPayment { type StateMachine = WatcherStateMachine; async fn on_changed(self: Box, watcher_ctx: &mut WatcherStateMachine) -> 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); @@ -279,6 +286,7 @@ impl State for WaitForTakerPaymentSpend { type StateMachine = WatcherStateMachine; async fn on_changed(self: Box, watcher_ctx: &mut WatcherStateMachine) -> 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 { @@ -383,6 +391,7 @@ impl State for SpendMakerPayment { type StateMachine = WatcherStateMachine; async fn on_changed(self: Box, watcher_ctx: &mut WatcherStateMachine) -> StateResult { + debug!("Watcher spend maker payment"); let spend_fut = watcher_ctx .maker_coin .send_maker_payment_spend_preimage(SendMakerPaymentSpendPreimageInput { @@ -434,6 +443,7 @@ impl State for RefundTakerPayment { type StateMachine = WatcherStateMachine; async fn on_changed(self: Box, watcher_ctx: &mut WatcherStateMachine) -> StateResult { + debug!("Watcher refund taker payment"); if std::env::var("USE_TEST_LOCKTIME").is_err() { loop { match watcher_ctx 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..431d1a7c32 --- /dev/null +++ b/mm2src/mm2_main/src/lp_swap/taker_restart.rs @@ -0,0 +1,264 @@ +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), + 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) + }; + + 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), + 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 { + 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, + Err(_) => swap.r().data.taker_payment_lock, + }; + 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 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 { + 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, + Err(_) => swap.r().data.taker_payment_lock, + }; + 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 { + 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 168d8fc1ac..1afdcbb5f8 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -12,6 +12,7 @@ use super::{broadcast_my_swap_status, broadcast_swap_message, broadcast_swap_msg 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::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; @@ -56,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", @@ -68,10 +69,11 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 12] = [ "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpentByWatcher", "Finished", ]; -pub const TAKER_ERROR_EVENTS: [&str; 15] = [ +pub const TAKER_ERROR_EVENTS: [&str; 16] = [ "StartFailed", "NegotiateFailed", "TakerFeeSendFailed", @@ -85,11 +87,14 @@ pub const TAKER_ERROR_EVENTS: [&str; 15] = [ "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", "TakerPaymentRefunded", + "TakerPaymentRefundedByWatcher", "TakerPaymentRefundFailed", "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..."; #[cfg(not(target_arch = "wasm32"))] pub fn stats_taker_swap_dir(ctx: &MmArc) -> PathBuf { ctx.dbdir().join("SWAPS").join("STATS").join("TAKER") } @@ -173,6 +178,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) @@ -181,6 +187,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, } } @@ -258,7 +265,9 @@ impl TakerSavedSwap { | TakerSwapEvent::TakerFeeSendFailed(_) | TakerSwapEvent::MakerPaymentValidateFailed(_) | TakerSwapEvent::TakerPaymentRefunded(_) + | TakerSwapEvent::TakerPaymentRefundedByWatcher(_) | TakerSwapEvent::MakerPaymentSpent(_) + | TakerSwapEvent::MakerPaymentSpentByWatcher(_) | TakerSwapEvent::MakerPaymentWaitConfirmFailed(_) => { return false; }, @@ -292,16 +301,20 @@ impl TakerSavedSwap { if !self.is_finished() { return ERR!("Can not determine is_success state for not finished swap"); } - for event in self.events.iter() { if event.event.is_error() { return Ok(false); } } - Ok(true) } + pub fn watcher_message_sent(&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); @@ -530,39 +543,45 @@ 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, } -#[cfg(test)] -#[derive(Eq, PartialEq)] -pub(super) enum FailAt { +#[cfg(any(test, feature = "run-docker-tests"))] +#[derive(Eq, PartialEq, Debug)] +pub enum FailAt { TakerPayment, + WaitForTakerPaymentSpendPanic, MakerPaymentSpend, + MakerPaymentSpendPanic, TakerPaymentRefund, + TakerPaymentRefundPanic, } -#[cfg(test)] +#[cfg(any(test, feature = "run-docker-tests"))] impl From for FailAt { fn from(str: String) -> Self { match str.as_str() { "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"), } } @@ -570,15 +589,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, @@ -586,7 +605,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, } @@ -637,12 +656,14 @@ pub enum TakerSwapEvent { TakerPaymentSpent(TakerPaymentSpentData), TakerPaymentWaitForSpendFailed(SwapError), MakerPaymentSpent(TransactionIdentifier), + MakerPaymentSpentByWatcher(TransactionIdentifier), MakerPaymentSpendFailed(SwapError), TakerPaymentWaitRefundStarted { wait_until: u64 }, TakerPaymentRefundStarted, TakerPaymentRefunded(Option), TakerPaymentRefundFailed(SwapError), TakerPaymentRefundFinished, + TakerPaymentRefundedByWatcher(Option), Finished, } @@ -673,6 +694,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) @@ -681,6 +703,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::Finished => "Finished".to_owned(), } } @@ -706,6 +729,7 @@ impl TakerSwapEvent { | TakerSwapEvent::TakerPaymentSent(_) | TakerSwapEvent::TakerPaymentSpent(_) | TakerSwapEvent::MakerPaymentSpent(_) + | TakerSwapEvent::MakerPaymentSpentByWatcher(_) | TakerSwapEvent::Finished ) } @@ -734,7 +758,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 { @@ -809,12 +833,14 @@ 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 => (), TakerSwapEvent::TakerPaymentRefunded(tx) => self.w().taker_payment_refund = tx, TakerSwapEvent::TakerPaymentRefundFailed(err) => self.errors.lock().push(err), TakerSwapEvent::TakerPaymentRefundFinished => (), + TakerSwapEvent::TakerPaymentRefundedByWatcher(tx) => self.w().taker_payment_refund = tx, TakerSwapEvent::Finished => self.finished_at.store(now_sec(), Ordering::Relaxed), } } @@ -853,6 +879,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, @@ -889,8 +916,8 @@ impl TakerSwap { payment_instructions: None, }), ctx, - #[cfg(test)] - fail_at: None, + #[cfg(any(test, feature = "run-docker-tests"))] + fail_at, } } @@ -1600,6 +1627,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: {}", @@ -1675,11 +1703,22 @@ impl TakerSwap { ])); } + #[cfg(any(test, feature = "run-docker-tests"))] + if self.fail_at == Some(FailAt::WaitForTakerPaymentSpendPanic) { + panic!("Taker panicked unexpectedly at wait for taker payment spend"); + } + 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, @@ -1729,12 +1768,15 @@ 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()), ])); + } 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), @@ -1796,11 +1838,13 @@ 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()), ])); + } else if self.fail_at == Some(FailAt::TakerPaymentRefundPanic) { + panic!("{}", REFUND_TEST_FAILURE_LOG); } let taker_payment = self.r().taker_payment.clone().unwrap().tx_hex; @@ -1909,10 +1953,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, @@ -1951,8 +1995,11 @@ 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, + ctx.clone(), maker, data.maker_amount.clone().into(), data.taker_amount.clone().into(), @@ -1960,16 +2007,32 @@ 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"))] + fail_at, ); - let command = saved.events.last().unwrap().get_command(); - for saved_event in saved.events { - swap.apply_event(saved_event.event); + + for saved_event in &saved.events { + swap.apply_event(saved_event.event.clone()); } - Ok((swap, command)) + + let mut command = match saved.events.last().unwrap().get_command() { + Some(command) => command, + None => return Ok((swap, None)), + }; + + if taker_coin.is_supported_by_watchers() + && maker_coin.is_supported_by_watchers() + && saved.watcher_message_sent() + { + command = get_command_based_on_watcher_activity(&ctx, &swap, saved, command).await?; + } + drop_mutability!(command); + + Ok((swap, Some(command))) } pub async fn recover_funds(&self) -> Result { @@ -2622,7 +2685,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")); @@ -2637,7 +2700,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, @@ -2652,7 +2721,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")); @@ -2679,7 +2748,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, @@ -2696,7 +2771,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")); @@ -2726,7 +2801,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, @@ -2743,7 +2824,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")); @@ -2764,7 +2845,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, @@ -2780,7 +2867,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")); @@ -2795,7 +2882,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 }); @@ -2805,7 +2898,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")); @@ -2829,7 +2922,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,14 +2945,20 @@ 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")); 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()); } @@ -2877,7 +2982,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")); @@ -2888,7 +2993,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!( @@ -2906,7 +3017,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")); @@ -2917,7 +3028,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(); @@ -2934,7 +3051,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()); } @@ -3080,7 +3197,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/docker_tests_common.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs index 3ddddf6801..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, @@ -171,7 +171,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 55ef6dc435..3fce082f0f 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -4,7 +4,8 @@ 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, + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SwapOps, + 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}; @@ -13,21 +14,26 @@ 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, 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}; + REFUND_TEST_FAILURE_LOG, SWAP_FINISHED_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, + 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}; use primitives::hash::H256; +use serde_json::Value; use std::str::FromStr; 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, @@ -254,6 +260,388 @@ fn start_swaps_and_get_balances( } } +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 +} + +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()); + + 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) +} + +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( + 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 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, + 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 +} + +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_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")]); + 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_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)]); + let (mut mm_alice, mut alice_conf) = run_taker_node(&coins, &[ + ("USE_TEST_LOCKTIME", ""), + ("TAKER_FAIL_AT", "taker_payment_refund_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(REFUND_TEST_FAILURE_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", + "TakerPaymentWaitForSpendFailed", + "TakerPaymentWaitRefundStarted", + "TakerPaymentRefundStarted", + "TakerPaymentRefundedByWatcher", + "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)]); + 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, + &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(); + + let mut mm_alice = block_on(MarketMakerIt::start_with_envs( + alice_conf.conf, + alice_conf.rpc_password.clone(), + None, + &[], + )) + .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()); @@ -266,7 +654,7 @@ fn test_watcher_spends_maker_payment_utxo_utxo() { 25., 25., 2., - &[("USE_WATCHERS", "")], + &[], SwapFlow::WatcherSpendsMakerPayment, &alice_privkey, &bob_privkey, @@ -306,7 +694,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, @@ -339,11 +727,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, @@ -384,11 +768,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, @@ -421,7 +801,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, @@ -454,11 +834,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, @@ -491,11 +867,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, @@ -542,7 +914,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, @@ -568,11 +940,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, @@ -599,7 +967,6 @@ fn test_watcher_refunds_taker_payment_erc20() { 100., 0.01, &[ - ("USE_WATCHERS", ""), ("USE_TEST_LOCKTIME", ""), ("TEST_COIN_PRICE", "0.01"), ("USE_WATCHER_REWARD", ""), @@ -651,11 +1018,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, @@ -664,6 +1027,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()]); @@ -1781,6 +2145,1056 @@ fn test_watcher_validate_taker_payment_erc20() { } } +#[test] +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); + let time_lock = now_sec() - 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_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, + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + let validate_watcher_refund = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait(); + assert!(validate_watcher_refund.is_ok()); +} + +#[test] +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(); + 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 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); + let time_lock = now_sec() - 10; + 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 = 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: Some(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 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()), + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + let error = taker_coin + .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")) + }, + _ => panic!( + "Expected `UnexpectedPaymentState` {}, found {:?}", + "Payment state is not 3", error + ), + } + + 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.clone(), + watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + 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 { + 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()), + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + let error = taker_coin + .taker_validates_payment_spend_or_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()), + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + let error = maker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction 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()), + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction 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), + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction 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), + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction 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), + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction 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), + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction amount arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid refund tx amount arg", error + ), + } +} + +#[test] +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(); + 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() - 10; + + let secret_hash = dhash160(&MakerSwap::generate_secret().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( + &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: watcher_reward.clone(), + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + 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 { + 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, + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction amount arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid refund tx amount arg", error + ), + } +} + +#[test] +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); + let time_lock = wait_for_confirmation_until; + + 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_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, + spend_type: WatcherSpendType::TakerPaymentRefund, + }; + + let validate_watcher_spend = taker_coin + .taker_validates_payment_spend_or_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; + let maker_amount = BigDecimal::from_str("0.001").unwrap(); + + let secret = MakerSwap::generate_secret().unwrap(); + let secret_hash = dhash160(&secret); + + 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 { + time_lock_duration, + time_lock, + other_pubkey: taker_pub, + secret_hash: secret_hash.as_slice(), + amount: maker_amount.clone(), + swap_contract_address: &maker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: Some(watcher_reward.clone()), + wait_for_confirmation_until, + }) + .wait() + .unwrap(); + + 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 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()), + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + let error = taker_coin + .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")) + }, + _ => 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(), + 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.clone(), + watcher_reward: Some(watcher_reward.clone()), + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + 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 { + 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()), + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_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: 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()), + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction 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()), + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + let error = maker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction 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), + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction 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), + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction 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), + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction 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), + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction amount arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid payment spend tx amount arg", error + ), + }; +} + +#[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; + let maker_amount = BigDecimal::from_str("0.001").unwrap(); + + let secret = MakerSwap::generate_secret().unwrap(); + let secret_hash = dhash160(&secret); + + 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 { + 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: watcher_reward.clone(), + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + 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 { + 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, + spend_type: WatcherSpendType::MakerPaymentSpend, + }; + + let error = taker_coin + .taker_validates_payment_spend_or_refund(validate_input) + .wait() + .unwrap_err() + .into_inner(); + log!("error: {:?}", error); + match error { + ValidatePaymentError::WrongPaymentTx(err) => { + assert!(err.contains("Transaction amount arg")) + }, + _ => panic!( + "Expected `WrongPaymentTx` {}, found {:?}", + "invalid payment spend tx amount arg", error + ), + }; +} + #[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 diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index d12aad490d..0f33a332e2 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,10 +105,43 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 12] = [ "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpentByWatcher", "Finished", ]; -pub const TAKER_ERROR_EVENTS: [&str; 15] = [ +// 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", "TakerFeeSendFailed", @@ -122,6 +155,7 @@ pub const TAKER_ERROR_EVENTS: [&str; 15] = [ "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", "TakerPaymentRefunded", + "TakerPaymentRefundedByWatcher", "TakerPaymentRefundFailed", "TakerPaymentRefundFinished", ]; @@ -2082,9 +2116,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( @@ -2128,7 +2164,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 ); }