diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 7934bdf485..be08e3471f 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -2610,6 +2610,7 @@ impl MarketCoinOps for EthCoin { }, }; + let mut tx_hash: Option = None; loop { if now_sec() > args.wait_until { return TX_PLAIN_ERR!( @@ -2628,42 +2629,61 @@ impl MarketCoinOps for EthCoin { }, }; - let events = match self - .spend_events(swap_contract_address, args.from_block, current_block) - .compat() - .await - { - Ok(ev) => ev, - Err(e) => { - error!("Error getting spend events: {}", e); - Timer::sleep(5.).await; - continue; - }, - }; + if tx_hash.is_none() { + let mut next_from_block = args.from_block; - let found = events.iter().find(|event| &event.data.0[..32] == id.as_slice()); + // Split the range into windows of size logs_block_range + while next_from_block <= current_block { + let to_block = std::cmp::min(next_from_block + self.logs_block_range - 1, current_block); - if let Some(event) = found { - if let Some(tx_hash) = event.transaction_hash { - let transaction = match self.transaction(TransactionId::Hash(tx_hash)).await { - Ok(Some(t)) => t, - Ok(None) => { - info!("Tx {} not found yet", tx_hash); - Timer::sleep(args.check_every).await; - continue; - }, + let events = match self + .events_from_block( + swap_contract_address, + "ReceiverSpent", + next_from_block, + Some(to_block), + &SWAP_CONTRACT, + ) + .await + { + Ok(ev) => ev, Err(e) => { - error!("Get tx {} error: {}", tx_hash, e); - Timer::sleep(args.check_every).await; + error!( + "Error getting spend events from {} to {} block: {}", + next_from_block, to_block, e + ); + Timer::sleep(5.).await; + next_from_block += self.logs_block_range; continue; }, }; - return Ok(TransactionEnum::from(try_tx_s!(signed_tx_from_web3_tx(transaction)))); + // Check if any event matches the SWAP ID + if let Some(found_event) = events.iter().find(|event| &event.data.0[..32] == id.as_slice()) { + if let Some(hash) = found_event.transaction_hash { + // Store tx_hash to skip fetching events in the next iteration if "eth_getTransactionByHash" is unsuccessful + tx_hash = Some(hash); + break; + } + } + + // Move to the next block range window + next_from_block += self.logs_block_range; } } - Timer::sleep(5.).await; + // Proceed getting spend transaction if we have a tx_hash + if let Some(tx_hash) = tx_hash { + match self.transaction(TransactionId::Hash(tx_hash)).await { + Ok(Some(t)) => { + return Ok(TransactionEnum::from(try_tx_s!(signed_tx_from_web3_tx(t)))); + }, + Ok(None) => info!("Tx {} not found yet", tx_hash), + Err(e) => error!("Get tx {} error: {}", tx_hash, e), + }; + Timer::sleep(args.check_every).await; + continue; + } } } @@ -4548,7 +4568,9 @@ impl EthCoin { let function = ERC20_CONTRACT.function("balanceOf")?; let data = function.encode_input(&[Token::Address(address)])?; - let res = coin.call_request(address, *token_addr, None, Some(data.into())).await?; + let res = coin + .call_request(address, *token_addr, None, Some(data.into()), BlockNumber::Latest) + .await?; let decoded = function.decode_output(&res.0)?; match decoded[0] { Token::Uint(number) => Ok(number), @@ -4612,7 +4634,7 @@ impl EthCoin { let function = ERC20_CONTRACT.function("balanceOf")?; let data = function.encode_input(&[Token::Address(address)])?; let res = self - .call_request(address, token_address, None, Some(data.into())) + .call_request(address, token_address, None, Some(data.into()), BlockNumber::Latest) .await?; let decoded = function.decode_output(&res.0)?; @@ -4639,7 +4661,7 @@ impl EthCoin { let my_address = self.derivation_method.single_addr_or_err().await?; let data = function.encode_input(&[Token::Address(my_address), Token::Uint(token_id_u256)])?; let result = self - .call_request(my_address, token_addr, None, Some(data.into())) + .call_request(my_address, token_addr, None, Some(data.into()), BlockNumber::Latest) .await?; let decoded = function.decode_output(&result.0)?; match decoded[0] { @@ -4670,7 +4692,7 @@ impl EthCoin { let data = function.encode_input(&[Token::Uint(token_id_u256)])?; let my_address = self.derivation_method.single_addr_or_err().await?; let result = self - .call_request(my_address, token_addr, None, Some(data.into())) + .call_request(my_address, token_addr, None, Some(data.into()), BlockNumber::Latest) .await?; let decoded = function.decode_output(&result.0)?; match decoded[0] { @@ -4748,6 +4770,7 @@ impl EthCoin { to: Address, value: Option, data: Option, + block_number: BlockNumber, ) -> Result { let request = CallRequest { from: Some(from), @@ -4759,7 +4782,7 @@ impl EthCoin { ..CallRequest::default() }; - self.call(request, Some(BlockId::Number(BlockNumber::Latest))).await + self.call(request, Some(BlockId::Number(block_number))).await } fn allowance(&self, spender: Address) -> Web3RpcFut { @@ -4775,7 +4798,7 @@ impl EthCoin { let data = function.encode_input(&[Token::Address(my_address), Token::Address(spender)])?; let res = coin - .call_request(my_address, *token_addr, None, Some(data.into())) + .call_request(my_address, *token_addr, None, Some(data.into()), BlockNumber::Latest) .await?; let decoded = function.decode_output(&res.0)?; @@ -4875,25 +4898,30 @@ impl EthCoin { Box::new(fut.boxed().compat()) } - /// Gets `ReceiverSpent` events from etomic swap smart contract since `from_block` - fn spend_events( + /// Returns events from `from_block` to `to_block` or current `latest` block. + /// According to ["eth_getLogs" doc](https://docs.infura.io/api/networks/ethereum/json-rpc-methods/eth_getlogs) `toBlock` is optional, default is "latest". + async fn events_from_block( &self, swap_contract_address: Address, + event_name: &str, from_block: u64, - to_block: u64, - ) -> Box, Error = String> + Send> { - let contract_event = try_fus!(SWAP_CONTRACT.event("ReceiverSpent")); - let filter = FilterBuilder::default() + to_block: Option, + swap_contract: &Contract, + ) -> MmResult, FindPaymentSpendError> { + let contract_event = swap_contract.event(event_name)?; + let mut filter_builder = FilterBuilder::default() .topics(Some(vec![contract_event.signature()]), None, None, None) .from_block(BlockNumber::Number(from_block.into())) - .to_block(BlockNumber::Number(to_block.into())) - .address(vec![swap_contract_address]) - .build(); - - let coin = self.clone(); - - let fut = async move { coin.logs(filter).await.map_err(|e| ERRL!("{}", e)) }; - Box::new(fut.boxed().compat()) + .address(vec![swap_contract_address]); + if let Some(block) = to_block { + filter_builder = filter_builder.to_block(BlockNumber::Number(block.into())); + } + let filter = filter_builder.build(); + let events_logs = self + .logs(filter) + .await + .map_err(|e| FindPaymentSpendError::Transport(e.to_string()))?; + Ok(events_logs) } fn validate_payment(&self, input: ValidatePaymentInput) -> ValidatePaymentFut<()> { @@ -5203,9 +5231,16 @@ impl EthCoin { .single_addr_or_err() .await .map_err(|e| ERRL!("{}", e))?; - coin.call_request(my_address, swap_contract_address, None, Some(data.into())) - .await - .map_err(|e| ERRL!("{}", e)) + coin.call_request( + my_address, + swap_contract_address, + None, + Some(data.into()), + // TODO worth changing this to BlockNumber::Pending + BlockNumber::Latest, + ) + .await + .map_err(|e| ERRL!("{}", e)) }; Box::new(fut.boxed().compat().and_then(move |bytes| { @@ -5255,10 +5290,16 @@ impl EthCoin { let to_block = current_block.min(from_block + self.logs_block_range); let spend_events = try_s!( - self.spend_events(swap_contract_address, from_block, to_block) - .compat() - .await + self.events_from_block( + swap_contract_address, + "ReceiverSpent", + from_block, + Some(to_block), + &SWAP_CONTRACT + ) + .await ); + let found = spend_events.iter().find(|event| &event.data.0[..32] == id.as_slice()); if let Some(event) = found { @@ -7010,7 +7051,10 @@ impl ParseCoinAssocTypes for EthCoin { } } + fn addr_to_string(&self, address: &Self::Address) -> String { eth_addr_to_hex(address) } + fn parse_address(&self, address: &str) -> Result { + // crate `Address::from_str` supports both address variants with and without `0x` prefix Address::from_str(address).map_to_mm(|e| EthAssocTypesError::InvalidHexString(e.to_string())) } @@ -7352,14 +7396,19 @@ impl TakerCoinSwapOpsV2 for EthCoin { self.sign_and_broadcast_taker_payment_spend_impl(gen_args, secret).await } - /// Wrapper for [EthCoin::wait_for_taker_payment_spend_impl] - async fn wait_for_taker_payment_spend( + /// Wrapper for [EthCoin::find_taker_payment_spend_tx_impl] + async fn find_taker_payment_spend_tx( &self, taker_payment: &Self::Tx, - _from_block: u64, + from_block: u64, wait_until: u64, - ) -> MmResult { - self.wait_for_taker_payment_spend_impl(taker_payment, wait_until).await + ) -> MmResult { + self.find_taker_payment_spend_tx_impl(taker_payment, from_block, wait_until, 10.) + .await + } + + async fn extract_secret_v2(&self, _secret_hash: &[u8], spend_tx: &Self::Tx) -> Result, String> { + self.extract_secret_v2_impl(spend_tx).await } } diff --git a/mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs b/mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs index fea91b0408..07ed14af43 100644 --- a/mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs +++ b/mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs @@ -1,12 +1,13 @@ use super::{check_decoded_length, validate_amount, validate_from_to_and_status, validate_payment_state, EthPaymentType, PaymentMethod, PrepareTxDataError, ZERO_VALUE}; -use crate::eth::{decode_contract_call, get_function_input_data, wei_from_big_decimal, EthCoin, EthCoinType, - ParseCoinAssocTypes, RefundFundingSecretArgs, RefundTakerPaymentArgs, SendTakerFundingArgs, - SignedEthTx, SwapTxTypeWithSecretHash, TakerPaymentStateV2, TransactionErr, ValidateSwapV2TxError, - ValidateSwapV2TxResult, ValidateTakerFundingArgs, TAKER_SWAP_V2}; -use crate::{FundingTxSpend, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, SearchForFundingSpendErr, - WaitForPaymentSpendError}; +use crate::eth::{decode_contract_call, get_function_input_data, signed_tx_from_web3_tx, wei_from_big_decimal, EthCoin, + EthCoinType, ParseCoinAssocTypes, RefundFundingSecretArgs, RefundTakerPaymentArgs, + SendTakerFundingArgs, SignedEthTx, SwapTxTypeWithSecretHash, TakerPaymentStateV2, TransactionErr, + ValidateSwapV2TxError, ValidateSwapV2TxResult, ValidateTakerFundingArgs, TAKER_SWAP_V2}; +use crate::{FindPaymentSpendError, FundingTxSpend, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, MarketCoinOps, + SearchForFundingSpendErr}; use common::executor::Timer; +use common::log::{error, info}; use common::now_sec; use ethabi::{Function, Token}; use ethcore_transaction::Action; @@ -15,7 +16,7 @@ use ethkey::public_to_address; use futures::compat::Future01CompatExt; use mm2_err_handle::prelude::{MapToMmResult, MmError, MmResult}; use std::convert::TryInto; -use web3::types::TransactionId; +use web3::types::{TransactionId, H256}; const ETH_TAKER_PAYMENT: &str = "ethTakerPayment"; const ERC20_TAKER_PAYMENT: &str = "erc20TakerPayment"; @@ -383,14 +384,14 @@ impl EthCoin { .await } - /// Checks that taker payment state is `TakerApproved`. - /// Accepts a taker-approved payment transaction and returns it if the state is correct. + /// Checks that taker payment state is `TakerApproved`. Called by maker. + /// Accepts a taker payment transaction and returns it if the state is correct. pub(crate) async fn search_for_taker_funding_spend_impl( &self, tx: &SignedEthTx, ) -> Result>, SearchForFundingSpendErr> { let (decoded, taker_swap_v2_contract) = self - .get_decoded_and_swap_contract(tx, TAKER_PAYMENT_APPROVE) + .get_funding_decoded_and_swap_contract(tx) .await .map_err(|e| SearchForFundingSpendErr::Internal(ERRL!("{}", e)))?; let taker_status = self @@ -457,34 +458,105 @@ impl EthCoin { Ok(spend_payment_tx) } - /// Checks that taker payment state is `MakerSpent`. - /// Accepts maker spent payment transaction and returns it if payment status is correct. - pub(crate) async fn wait_for_taker_payment_spend_impl( + pub(crate) async fn find_taker_payment_spend_tx_impl( &self, - taker_payment: &SignedEthTx, + taker_payment: &SignedEthTx, // it's approve_tx in Eth case, as in sign_and_send_taker_funding_spend we return approve_tx tx for it + from_block: u64, wait_until: u64, - ) -> MmResult { - let (decoded, taker_swap_v2_contract) = self - .get_decoded_and_swap_contract(taker_payment, "spendTakerPayment") - .await?; + check_every: f64, + ) -> MmResult { + let taker_swap_v2_contract = self + .swap_v2_contracts + .ok_or_else(|| { + FindPaymentSpendError::Internal("Expected swap_v2_contracts to be Some, but found None".to_string()) + })? + .taker_swap_v2_contract; + let approve_func = TAKER_SWAP_V2.function(TAKER_PAYMENT_APPROVE)?; + let decoded = decode_contract_call(approve_func, taker_payment.unsigned().data())?; + let id = match decoded.first() { + Some(Token::FixedBytes(bytes)) => bytes, + invalid_token => { + return MmError::err(FindPaymentSpendError::InvalidData(format!( + "Expected Token::FixedBytes, got {:?}", + invalid_token + ))) + }, + }; + let mut tx_hash: Option = None; + // loop to find maker's spendTakerPayment transaction loop { - let taker_status = self - .payment_status_v2( - taker_swap_v2_contract, - decoded[0].clone(), // id from spendTakerPayment - &TAKER_SWAP_V2, - EthPaymentType::TakerPayments, - TAKER_PAYMENT_STATE_INDEX, - ) - .await?; - if taker_status == U256::from(TakerPaymentStateV2::MakerSpent as u8) { - return Ok(taker_payment.clone()); - } let now = now_sec(); if now > wait_until { - return MmError::err(WaitForPaymentSpendError::Timeout { wait_until, now }); + return MmError::err(FindPaymentSpendError::Timeout { wait_until, now }); + } + + let current_block = match self.current_block().compat().await { + Ok(b) => b, + Err(e) => { + error!("Error getting block number: {}", e); + Timer::sleep(check_every).await; + continue; + }, + }; + + // Skip retrieving events if tx_hash is already found + if tx_hash.is_none() { + let mut next_from_block = from_block; + + while next_from_block <= current_block { + let to_block = std::cmp::min(next_from_block + self.logs_block_range - 1, current_block); + + // Fetch TakerPaymentSpent events for the current block range + let events = match self + .events_from_block( + taker_swap_v2_contract, + "TakerPaymentSpent", + next_from_block, + Some(to_block), + &TAKER_SWAP_V2, + ) + .await + { + Ok(events) => events, + Err(e) => { + error!( + "Error getting TakerPaymentSpent events from {} to {} block: {}", + next_from_block, to_block, e + ); + Timer::sleep(check_every).await; + // Move to next window if there was an error + next_from_block += self.logs_block_range; + continue; + }, + }; + + // This is how spent event looks like in EtomicSwapTakerV2: event TakerPaymentSpent(bytes32 id, bytes32 secret). + // Check if any event matches the ID. + if let Some(found_event) = events.into_iter().find(|event| &event.data.0[..32] == id.as_slice()) { + if let Some(hash) = found_event.transaction_hash { + // Store tx_hash to skip fetching events in the next iteration if "eth_getTransactionByHash" is unsuccessful + tx_hash = Some(hash); + break; + } + } + + next_from_block += self.logs_block_range; + } + } + + // Proceed to check transaction if we have a tx_hash + if let Some(tx_hash) = tx_hash { + match self.transaction(TransactionId::Hash(tx_hash)).await { + Ok(Some(t)) => { + let transaction = signed_tx_from_web3_tx(t).map_err(FindPaymentSpendError::Internal)?; + return Ok(transaction); + }, + Ok(None) => info!("spendTakerPayment transaction {} not found yet", tx_hash), + Err(e) => error!("Get spendTakerPayment transaction {} error: {}", tx_hash, e), + }; + Timer::sleep(check_every).await; + continue; } - Timer::sleep(10.).await; } } @@ -666,14 +738,14 @@ impl EthCoin { Ok((taker_swap_v2_contract, func, token_address)) } - async fn get_decoded_and_swap_contract( + async fn get_funding_decoded_and_swap_contract( &self, tx: &SignedEthTx, - function_name: &str, ) -> Result<(Vec, Address), PrepareTxDataError> { let decoded = { let func = match self.coin_type { - EthCoinType::Eth | EthCoinType::Erc20 { .. } => TAKER_SWAP_V2.function(function_name)?, + EthCoinType::Eth => TAKER_SWAP_V2.function(ETH_TAKER_PAYMENT)?, + EthCoinType::Erc20 { .. } => TAKER_SWAP_V2.function(ERC20_TAKER_PAYMENT)?, EthCoinType::Nft { .. } => { return Err(PrepareTxDataError::Internal( "NFT protocol is not supported for ETH and ERC20 Swaps".to_string(), @@ -692,6 +764,42 @@ impl EthCoin { Ok((decoded, taker_swap_v2_contract)) } + + /// Extracts the maker's secret from the input of transaction that calls the `spendTakerPayment` smart contract method. + /// + /// function spendTakerPayment( + /// bytes32 id, + /// uint256 amount, + /// uint256 dexFee, + /// address taker, + /// bytes32 takerSecretHash, + /// bytes32 makerSecret, + /// address tokenAddress + /// ) + pub(crate) async fn extract_secret_v2_impl(&self, spend_tx: &SignedEthTx) -> Result, String> { + let function = try_s!(TAKER_SWAP_V2.function("spendTakerPayment")); + // should be 0xcc90c199 + let expected_signature = function.short_signature(); + let signature = &spend_tx.unsigned().data()[0..4]; + if signature != expected_signature { + return ERR!( + "Expected 'spendTakerPayment' contract call signature: {:?}, found {:?}", + expected_signature, + signature + ); + }; + let decoded = try_s!(decode_contract_call(function, spend_tx.unsigned().data())); + if decoded.len() < 7 { + return ERR!("Invalid arguments in 'spendTakerPayment' call: {:?}", decoded); + } + match &decoded[5] { + Token::FixedBytes(secret) => Ok(secret.to_vec()), + _ => ERR!( + "Expected secret to be fixed bytes, but decoded function data is {:?}", + decoded + ), + } + } } /// Validation function for ETH taker payment data diff --git a/mm2src/coins/eth/eth_swap_v2/mod.rs b/mm2src/coins/eth/eth_swap_v2/mod.rs index 798a232d56..0b903a9b78 100644 --- a/mm2src/coins/eth/eth_swap_v2/mod.rs +++ b/mm2src/coins/eth/eth_swap_v2/mod.rs @@ -7,7 +7,7 @@ use futures::compat::Future01CompatExt; use mm2_err_handle::mm_error::MmError; use mm2_number::BigDecimal; use num_traits::Signed; -use web3::types::Transaction as Web3Tx; +use web3::types::{BlockNumber, Transaction as Web3Tx}; pub(crate) mod eth_maker_swap_v2; pub(crate) mod eth_taker_swap_v2; @@ -82,7 +82,13 @@ impl EthCoin { let function = contract_abi.function(function_name)?; let data = function.encode_input(&[swap_id])?; let bytes = self - .call_request(self.my_addr().await, swap_address, None, Some(data.into())) + .call_request( + self.my_addr().await, + swap_address, + None, + Some(data.into()), + BlockNumber::Pending, + ) .await?; let decoded_tokens = function.decode_output(&bytes.0)?; @@ -133,8 +139,8 @@ pub(crate) fn validate_from_to_and_status( ) -> Result<(), MmError> { if status != U256::from(expected_status) { return MmError::err(ValidatePaymentV2Err::UnexpectedPaymentState(format!( - "Payment state is not `PaymentSent`, got {}", - status + "tx {:?} Payment state is not `PaymentSent`, got {}", + tx_from_rpc.hash, status ))); } if tx_from_rpc.from != Some(expected_from) { diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index 19f45d10ae..2e874e1e35 100644 --- a/mm2src/coins/eth/eth_tests.rs +++ b/mm2src/coins/eth/eth_tests.rs @@ -1,6 +1,7 @@ use super::*; use crate::IguanaPrivKey; use common::{block_on, block_on_f01}; +use futures_util::future; use mm2_core::mm_ctx::MmCtxBuilder; cfg_native!( @@ -163,7 +164,7 @@ fn test_wei_from_big_decimal() { fn test_wait_for_payment_spend_timeout() { const TAKER_PAYMENT_SPEND_SEARCH_INTERVAL: f64 = 1.; - EthCoin::spend_events.mock_safe(|_, _, _, _| MockResult::Return(Box::new(futures01::future::ok(vec![])))); + EthCoin::events_from_block.mock_safe(|_, _, _, _, _, _| MockResult::Return(Box::pin(future::ok(vec![])))); EthCoin::current_block.mock_safe(|_| MockResult::Return(Box::new(futures01::future::ok(900)))); let key_pair = Random.generate().unwrap(); diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 5e40eb9221..1eb66ce655 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -212,6 +212,7 @@ pub mod watcher_common; pub mod coin_errors; use coin_errors::{MyAddressError, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentResult}; +use crypto::secret_hash_algo::SecretHashAlgo; #[doc(hidden)] #[cfg(test)] @@ -1546,6 +1547,14 @@ pub trait ParseCoinAssocTypes { async fn my_addr(&self) -> Self::Address; + /// Converts coin `Self::Address` type into a properly formatted string representation. + /// + /// Don't use `to_string` directly on `Self::Address` types in generic TPU code! + /// It may produce abbreviated or non-standard formats (e.g. `ethereum_types::Address` will be like this `0x7cc9…3874`), + /// which are not guaranteed to be parsable back into the original `Address` type. + /// This function should ensure the resulting string is consistently formatted and fully reversible. + fn addr_to_string(&self, address: &Self::Address) -> String; + fn parse_address(&self, address: &str) -> Result; fn parse_pubkey(&self, pubkey: &[u8]) -> Result; @@ -1783,7 +1792,7 @@ pub trait MakerNftSwapOpsV2: ParseCoinAssocTypes + ParseNftAssocTypes + Send + S /// Enum representing errors that can occur while waiting for taker payment spend. #[derive(Display, Debug, EnumFromStringify)] -pub enum WaitForPaymentSpendError { +pub enum FindPaymentSpendError { /// Timeout error variant, indicating that the wait for taker payment spend has timed out. #[display( fmt = "Timed out waiting for taker payment spend, wait_until {}, now {}", @@ -1806,18 +1815,18 @@ pub enum WaitForPaymentSpendError { Transport(String), } -impl From for WaitForPaymentSpendError { +impl From for FindPaymentSpendError { fn from(err: WaitForOutputSpendErr) -> Self { match err { - WaitForOutputSpendErr::Timeout { wait_until, now } => WaitForPaymentSpendError::Timeout { wait_until, now }, + WaitForOutputSpendErr::Timeout { wait_until, now } => FindPaymentSpendError::Timeout { wait_until, now }, WaitForOutputSpendErr::NoOutputWithIndex(index) => { - WaitForPaymentSpendError::InvalidInputTx(format!("Tx doesn't have output with index {}", index)) + FindPaymentSpendError::InvalidInputTx(format!("Tx doesn't have output with index {}", index)) }, } } } -impl From for WaitForPaymentSpendError { +impl From for FindPaymentSpendError { fn from(e: PaymentStatusErr) -> Self { match e { PaymentStatusErr::ABIError(e) => Self::ABIError(e), @@ -1828,7 +1837,7 @@ impl From for WaitForPaymentSpendError { } } -impl From for WaitForPaymentSpendError { +impl From for FindPaymentSpendError { fn from(e: PrepareTxDataError) -> Self { match e { PrepareTxDataError::ABIError(e) => Self::ABIError(e), @@ -1963,13 +1972,15 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn swap_unique_data: &[u8], ) -> Result; - /// Wait until taker payment spend is found on-chain - async fn wait_for_taker_payment_spend( + /// Wait until taker payment spend transaction is found on-chain + async fn find_taker_payment_spend_tx( &self, taker_payment: &Self::Tx, from_block: u64, wait_until: u64, - ) -> MmResult; + ) -> MmResult; + + async fn extract_secret_v2(&self, secret_hash: &[u8], spend_tx: &Self::Tx) -> Result, String>; } #[async_trait] @@ -3561,6 +3572,20 @@ impl MmCoinEnum { pub fn is_eth(&self) -> bool { matches!(self, MmCoinEnum::EthCoin(_)) } fn is_platform_coin(&self) -> bool { self.ticker() == self.platform_ticker() } + + pub fn maker_secret_hash_algo_v2(&self) -> SecretHashAlgo { + match self { + MmCoinEnum::EthCoin(_) => SecretHashAlgo::SHA256, + _ => SecretHashAlgo::DHASH160, + } + } + + pub fn taker_secret_hash_algo_v2(&self) -> SecretHashAlgo { + match self { + MmCoinEnum::EthCoin(_) => SecretHashAlgo::SHA256, + _ => SecretHashAlgo::DHASH160, + } + } } #[async_trait] diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 43765ab0ba..fcb4233765 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -1,8 +1,8 @@ #![allow(clippy::all)] -use super::{CoinBalance, CommonSwapOpsV2, FundingTxSpend, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, - RawTransactionRequest, RefundTakerPaymentArgs, SearchForFundingSpendErr, SwapOps, TradeFee, - TransactionEnum, TransactionFut, WaitForPaymentSpendError}; +use super::{CoinBalance, CommonSwapOpsV2, FindPaymentSpendError, FundingTxSpend, HistorySyncState, MarketCoinOps, + MmCoin, RawTransactionFut, RawTransactionRequest, RefundTakerPaymentArgs, SearchForFundingSpendErr, + SwapOps, TradeFee, TransactionEnum, TransactionFut}; use crate::coin_errors::ValidatePaymentResult; use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, GenPreimageResult, GenTakerFundingSpendArgs, @@ -456,6 +456,8 @@ impl ParseCoinAssocTypes for TestCoin { async fn my_addr(&self) -> Self::Address { todo!() } + fn addr_to_string(&self, address: &Self::Address) -> String { unimplemented!() } + fn parse_address(&self, address: &str) -> Result { todo!() } fn parse_pubkey(&self, pubkey: &[u8]) -> Result { unimplemented!() } @@ -557,12 +559,16 @@ impl TakerCoinSwapOpsV2 for TestCoin { unimplemented!() } - async fn wait_for_taker_payment_spend( + async fn find_taker_payment_spend_tx( &self, taker_payment: &Self::Tx, from_block: u64, wait_until: u64, - ) -> MmResult { + ) -> MmResult { + unimplemented!() + } + + async fn extract_secret_v2(&self, secret_hash: &[u8], spend_tx: &Self::Tx) -> Result, String> { unimplemented!() } } diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index 6d98451c7f..f1c055f3fc 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -1053,6 +1053,8 @@ impl ParseCoinAssocTypes for T { } } + fn addr_to_string(&self, address: &Self::Address) -> String { address.to_string() } + fn parse_address(&self, address: &str) -> Result { self.address_from_str(address) } diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 70c8522b58..1dddbc9fff 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -2584,12 +2584,18 @@ pub async fn get_taker_watcher_reward Result, String> { let spend_tx: UtxoTx = try_s!(deserialize(spend_tx).map_err(|e| ERRL!("{:?}", e))); + extract_secret_v2(secret_hash, &spend_tx) +} + +/// Extract a secret from the `spend_tx`. +/// Note spender could generate the spend with several inputs where the only one input is the p2sh script. +pub fn extract_secret_v2(secret_hash: &[u8], spend_tx: &UtxoTx) -> Result, String> { let expected_secret_hash = if secret_hash.len() == 32 { ripemd160(secret_hash) } else { H160::from(secret_hash) }; - for input in spend_tx.inputs.into_iter() { + for input in spend_tx.inputs.iter() { let script: Script = input.script_sig.clone().into(); for instruction in script.iter().flatten() { if instruction.opcode == Opcode::OP_PUSHBYTES_32 { diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index f5a02f5095..85e2d1b383 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -23,22 +23,22 @@ use crate::utxo::utxo_hd_wallet::{UtxoHDAccount, UtxoHDAddress}; use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, UtxoTxHistoryOps}; use crate::{CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinBalanceMap, CoinWithDerivationMethod, - CoinWithPrivKeyPolicy, CommonSwapOpsV2, ConfirmPaymentInput, DexFee, FundingTxSpend, GenPreimageResult, - GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, GetWithdrawSenderAddress, IguanaBalanceOps, - IguanaPrivKey, MakerCoinSwapOpsV2, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, - PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, - RawTransactionRequest, RawTransactionResult, RefundError, RefundFundingSecretArgs, - RefundMakerPaymentSecretArgs, RefundMakerPaymentTimelockArgs, RefundPaymentArgs, RefundResult, - RefundTakerPaymentArgs, SearchForFundingSpendErr, SearchForSwapTxSpendInput, SendMakerPaymentArgs, - SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SendTakerFundingArgs, SignRawTransactionRequest, - SignatureResult, SpendMakerPaymentArgs, SpendPaymentArgs, SwapOps, SwapTxTypeWithSecretHash, - TakerCoinSwapOpsV2, TakerSwapMakerCoin, ToBytes, TradePreimageValue, TransactionFut, TransactionResult, - TxMarshalingErr, TxPreimageWithSig, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, - ValidateMakerPaymentArgs, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, - ValidatePaymentInput, ValidateSwapV2TxResult, ValidateTakerFundingArgs, - ValidateTakerFundingSpendPreimageResult, ValidateTakerPaymentSpendPreimageResult, - ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WaitForPaymentSpendError, - WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, + CoinWithPrivKeyPolicy, CommonSwapOpsV2, ConfirmPaymentInput, DexFee, FindPaymentSpendError, + FundingTxSpend, GenPreimageResult, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, + GetWithdrawSenderAddress, IguanaBalanceOps, IguanaPrivKey, MakerCoinSwapOpsV2, MakerSwapTakerCoin, + MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, + PaymentInstructionsErr, PrivKeyBuildPolicy, RawTransactionRequest, RawTransactionResult, RefundError, + RefundFundingSecretArgs, RefundMakerPaymentSecretArgs, RefundMakerPaymentTimelockArgs, RefundPaymentArgs, + RefundResult, RefundTakerPaymentArgs, SearchForFundingSpendErr, SearchForSwapTxSpendInput, + SendMakerPaymentArgs, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SendTakerFundingArgs, + SignRawTransactionRequest, SignatureResult, SpendMakerPaymentArgs, SpendPaymentArgs, SwapOps, + SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, TakerSwapMakerCoin, ToBytes, TradePreimageValue, + TransactionFut, TransactionResult, TxMarshalingErr, TxPreimageWithSig, ValidateAddressResult, + ValidateFeeArgs, ValidateInstructionsErr, ValidateMakerPaymentArgs, ValidateOtherPubKeyErr, + ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, ValidateSwapV2TxResult, + ValidateTakerFundingArgs, ValidateTakerFundingSpendPreimageResult, + ValidateTakerPaymentSpendPreimageResult, ValidateWatcherSpendInput, VerificationResult, + WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut}; use common::executor::{AbortableSystem, AbortedError}; use futures::{FutureExt, TryFutureExt}; @@ -866,12 +866,12 @@ impl TakerCoinSwapOpsV2 for UtxoStandardCoin { utxo_common::sign_and_broadcast_taker_payment_spend(self, preimage, gen_args, secret, &htlc_keypair).await } - async fn wait_for_taker_payment_spend( + async fn find_taker_payment_spend_tx( &self, taker_payment: &Self::Tx, from_block: u64, wait_until: u64, - ) -> MmResult { + ) -> MmResult { let res = utxo_common::wait_for_output_spend_impl( self.as_ref(), taker_payment, @@ -883,6 +883,10 @@ impl TakerCoinSwapOpsV2 for UtxoStandardCoin { .await?; Ok(res) } + + async fn extract_secret_v2(&self, secret_hash: &[u8], spend_tx: &Self::Tx) -> Result, String> { + utxo_common::extract_secret_v2(secret_hash, spend_tx) + } } impl CommonSwapOpsV2 for UtxoStandardCoin { diff --git a/mm2src/crypto/src/lib.rs b/mm2src/crypto/src/lib.rs index e2651a54ea..f735203232 100644 --- a/mm2src/crypto/src/lib.rs +++ b/mm2src/crypto/src/lib.rs @@ -12,6 +12,7 @@ pub mod hw_rpc_task; mod key_derivation; pub mod mnemonic; pub mod privkey; +pub mod secret_hash_algo; mod shared_db_id; mod slip21; mod standard_hd_path; diff --git a/mm2src/crypto/src/secret_hash_algo.rs b/mm2src/crypto/src/secret_hash_algo.rs new file mode 100644 index 0000000000..15d7a08f75 --- /dev/null +++ b/mm2src/crypto/src/secret_hash_algo.rs @@ -0,0 +1,39 @@ +use bitcrypto::{dhash160, sha256}; +use derive_more::Display; +use std::convert::TryFrom; + +/// Algorithm used to hash swap secret. +#[derive(Clone, Copy, Debug, Deserialize, Serialize, Default)] +pub enum SecretHashAlgo { + /// ripemd160(sha256(secret)) + #[default] + DHASH160 = 1, + /// sha256(secret) + SHA256 = 2, +} + +#[derive(Debug, Display)] +pub struct UnsupportedSecretHashAlgo(u8); + +impl std::error::Error for UnsupportedSecretHashAlgo {} + +impl TryFrom for SecretHashAlgo { + type Error = UnsupportedSecretHashAlgo; + + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(SecretHashAlgo::DHASH160), + 2 => Ok(SecretHashAlgo::SHA256), + unsupported => Err(UnsupportedSecretHashAlgo(unsupported)), + } + } +} + +impl SecretHashAlgo { + pub fn hash_secret(&self, secret: &[u8]) -> Vec { + match self { + SecretHashAlgo::DHASH160 => dhash160(secret).take().into(), + SecretHashAlgo::SHA256 => sha256(secret).take().into(), + } + } +} diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index 620cb79bfb..0dc7fd16da 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -25,7 +25,7 @@ use blake2::digest::{Update, VariableOutput}; use blake2::Blake2bVar; use coins::utxo::{compressed_pub_key_from_priv_raw, ChecksumType, UtxoAddressFormat}; use coins::{coin_conf, find_pair, lp_coinfind, BalanceTradeFeeUpdatedHandler, CoinProtocol, CoinsContext, - FeeApproxStage, MarketCoinOps, MmCoinEnum}; + FeeApproxStage, MakerCoinSwapOpsV2, MmCoin, MmCoinEnum, TakerCoinSwapOpsV2}; use common::executor::{simple_map::AbortableSimpleMap, AbortSettings, AbortableSystem, AbortedError, SpawnAbortable, SpawnFuture, Timer}; use common::log::{error, warn, LogOnError}; @@ -57,6 +57,7 @@ use my_orders_storage::{delete_my_maker_order, delete_my_taker_order, save_maker use num_traits::identities::Zero; use parking_lot::Mutex as PaMutex; use rpc::v1::types::H256 as H256Json; +use secp256k1::PublicKey as Secp256k1Pubkey; use serde_json::{self as json, Value as Json}; use sp_trie::{delta_trie_root, MemoryDB, Trie, TrieConfiguration, TrieDB, TrieDBMut, TrieHash, TrieMut}; use std::collections::hash_map::{Entry, HashMap, RawEntryMut}; @@ -74,7 +75,7 @@ use crate::lp_network::{broadcast_p2p_msg, request_any_relay, request_one_peer, use crate::lp_swap::maker_swap_v2::{self, MakerSwapStateMachine, MakerSwapStorage}; use crate::lp_swap::taker_swap_v2::{self, TakerSwapStateMachine, TakerSwapStorage}; use crate::lp_swap::{calc_max_maker_vol, check_balance_for_maker_swap, check_balance_for_taker_swap, - check_other_coin_balance_for_swap, detect_secret_hash_algo, dex_fee_amount_from_taker_coin, + check_other_coin_balance_for_swap, detect_secret_hash_algo_v2, dex_fee_amount_from_taker_coin, generate_secret, get_max_maker_vol, insert_new_swap_to_db, is_pubkey_banned, lp_atomic_locktime, p2p_keypair_and_peer_id_to_broadcast, p2p_private_and_peer_id_to_broadcast, run_maker_swap, run_taker_swap, swap_v2_topic, AtomicLocktimeVersion, CheckBalanceError, CheckBalanceResult, @@ -85,6 +86,7 @@ use crate::lp_swap::{calc_max_maker_vol, check_balance_for_maker_swap, check_bal use crate::lp_swap::taker_swap::FailAt; pub use best_orders::{best_orders_rpc, best_orders_rpc_v2}; +use crypto::secret_hash_algo::SecretHashAlgo; pub use orderbook_depth::orderbook_depth_rpc; pub use orderbook_rpc::{orderbook_rpc, orderbook_rpc_v2}; @@ -99,6 +101,7 @@ mod best_orders; mod lp_bot; pub use lp_bot::{start_simple_market_maker_bot, stop_simple_market_maker_bot, StartSimpleMakerBotRequest, TradingBotEvent}; +use primitives::hash::{H256, H264}; mod my_orders_storage; mod new_protocol; @@ -131,6 +134,8 @@ const TRIE_STATE_HISTORY_TIMEOUT: u64 = 3; const TRIE_ORDER_HISTORY_TIMEOUT: u64 = 300; #[cfg(test)] const TRIE_ORDER_HISTORY_TIMEOUT: u64 = 3; +/// Swap protocol version +const SWAP_VERSION: u32 = 2; pub type OrderbookP2PHandlerResult = Result<(), MmError>; @@ -1167,6 +1172,9 @@ pub struct TakerRequest { #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] pub rel_protocol_info: Option>, + #[serde(default = "legacy_swap_version")] + #[serde(skip_serializing_if = "is_legacy_swap_version")] + pub swap_version: u32, } impl TakerRequest { @@ -1187,6 +1195,7 @@ impl TakerRequest { conf_settings: Some(message.conf_settings), base_protocol_info: message.base_protocol_info, rel_protocol_info: message.rel_protocol_info, + swap_version: message.swap_version, } } @@ -1232,6 +1241,7 @@ impl From for new_protocol::OrdermatchMessage { conf_settings: taker_order.request.conf_settings.unwrap(), base_protocol_info: taker_order.request.base_protocol_info, rel_protocol_info: taker_order.request.rel_protocol_info, + swap_version: taker_order.request.swap_version, }) } } @@ -1242,6 +1252,10 @@ impl TakerRequest { fn get_rel_amount(&self) -> &MmNumber { &self.rel_amount } } +fn legacy_swap_version() -> u32 { 1 } + +fn is_legacy_swap_version(swap_version: &u32) -> bool { *swap_version == 1 } + pub struct TakerOrderBuilder<'a> { base_coin: &'a MmCoinEnum, rel_coin: &'a MmCoinEnum, @@ -1257,6 +1271,7 @@ pub struct TakerOrderBuilder<'a> { min_volume: Option, timeout: u64, save_in_history: bool, + swap_version: u32, } pub enum TakerOrderBuildError { @@ -1336,6 +1351,7 @@ impl<'a> TakerOrderBuilder<'a> { order_type: OrderType::GoodTillCancelled, timeout: TAKER_ORDER_TIMEOUT, save_in_history: true, + swap_version: SWAP_VERSION, } } @@ -1399,6 +1415,17 @@ impl<'a> TakerOrderBuilder<'a> { self } + /// When a new [TakerOrderBuilder::new] is created, it sets [SWAP_VERSION] by default. + /// However, if user has not specified in the config to use TPU V2, + /// the TakerOrderBuilder's swap_version is changed to legacy. + /// In the future alls users will be using TPU V2 by default without "use_trading_proto_v2" configuration. + pub fn use_trading_proto_v2(mut self, use_trading_proto_v2: bool) -> Self { + if !use_trading_proto_v2 { + self.swap_version = 1; + } + self + } + /// Validate fields and build #[allow(clippy::result_large_err)] pub fn build(self) -> Result { @@ -1487,6 +1514,7 @@ impl<'a> TakerOrderBuilder<'a> { conf_settings: self.conf_settings, base_protocol_info: Some(base_protocol_info), rel_protocol_info: Some(rel_protocol_info), + swap_version: self.swap_version, }, matches: Default::default(), min_volume, @@ -1527,6 +1555,7 @@ impl<'a> TakerOrderBuilder<'a> { conf_settings: self.conf_settings, base_protocol_info: Some(base_protocol_info), rel_protocol_info: Some(rel_protocol_info), + swap_version: self.swap_version, }, matches: HashMap::new(), min_volume: Default::default(), @@ -1688,6 +1717,9 @@ pub struct MakerOrder { /// A custom priv key for more privacy to prevent linking orders of the same node between each other /// Commonly used with privacy coins (ARRR, ZCash, etc.) p2p_privkey: Option, + #[serde(default = "legacy_swap_version")] + #[serde(skip_serializing_if = "is_legacy_swap_version")] + pub swap_version: u32, } pub struct MakerOrderBuilder<'a> { @@ -1700,6 +1732,7 @@ pub struct MakerOrderBuilder<'a> { rel_orderbook_ticker: Option, conf_settings: Option, save_in_history: bool, + swap_version: u32, } pub enum MakerOrderBuildError { @@ -1849,6 +1882,7 @@ impl<'a> MakerOrderBuilder<'a> { price: 0.into(), conf_settings: None, save_in_history: true, + swap_version: SWAP_VERSION, } } @@ -1887,6 +1921,17 @@ impl<'a> MakerOrderBuilder<'a> { self } + /// When a new [MakerOrderBuilder::new] is created, it sets [SWAP_VERSION] by default. + /// However, if user has not specified in the config to use TPU V2, + /// the MakerOrderBuilder's swap_version is changed to legacy. + /// In the future alls users will be using TPU V2 by default without "use_trading_proto_v2" configuration. + pub fn use_trading_proto_v2(mut self, use_trading_proto_v2: bool) -> Self { + if !use_trading_proto_v2 { + self.swap_version = 1; + } + self + } + /// Build MakerOrder #[allow(clippy::result_large_err)] pub fn build(self) -> Result { @@ -1943,6 +1988,7 @@ impl<'a> MakerOrderBuilder<'a> { base_orderbook_ticker: self.base_orderbook_ticker, rel_orderbook_ticker: self.rel_orderbook_ticker, p2p_privkey, + swap_version: self.swap_version, }) } @@ -1967,6 +2013,7 @@ impl<'a> MakerOrderBuilder<'a> { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: self.swap_version, } } } @@ -2097,6 +2144,7 @@ impl From for MakerOrder { base_orderbook_ticker: taker_order.base_orderbook_ticker, rel_orderbook_ticker: taker_order.rel_orderbook_ticker, p2p_privkey: taker_order.p2p_privkey, + swap_version: taker_order.request.swap_version, }, // The "buy" taker order is recreated with reversed pair as Maker order is always considered as "sell" TakerAction::Buy => { @@ -2119,6 +2167,7 @@ impl From for MakerOrder { base_orderbook_ticker: taker_order.rel_orderbook_ticker, rel_orderbook_ticker: taker_order.base_orderbook_ticker, p2p_privkey: taker_order.p2p_privkey, + swap_version: taker_order.request.swap_version, } }, } @@ -2171,6 +2220,9 @@ pub struct MakerReserved { #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] pub rel_protocol_info: Option>, + #[serde(default = "legacy_swap_version")] + #[serde(skip_serializing_if = "is_legacy_swap_version")] + pub swap_version: u32, } impl MakerReserved { @@ -2198,6 +2250,7 @@ impl MakerReserved { conf_settings: Some(message.conf_settings), base_protocol_info: message.base_protocol_info, rel_protocol_info: message.rel_protocol_info, + swap_version: message.swap_version, } } } @@ -2214,6 +2267,7 @@ impl From for new_protocol::OrdermatchMessage { conf_settings: maker_reserved.conf_settings.unwrap(), base_protocol_info: maker_reserved.base_protocol_info, rel_protocol_info: maker_reserved.rel_protocol_info, + swap_version: maker_reserved.swap_version, }) } } @@ -2967,8 +3021,6 @@ fn lp_connect_start_bob(ctx: MmArc, maker_match: MakerMatch, maker_order: MakerO uuid ); - let now = now_sec(); - let secret = match generate_secret() { Ok(s) => s.into(), Err(e) => { @@ -2977,72 +3029,62 @@ fn lp_connect_start_bob(ctx: MmArc, maker_match: MakerMatch, maker_order: MakerO }, }; - if ctx.use_trading_proto_v2() { - let secret_hash_algo = detect_secret_hash_algo(&maker_coin, &taker_coin); - match (maker_coin, taker_coin) { - (MmCoinEnum::UtxoCoin(m), MmCoinEnum::UtxoCoin(t)) => { - let mut maker_swap_state_machine = MakerSwapStateMachine { - storage: MakerSwapStorage::new(ctx.clone()), - abortable_system: ctx - .abortable_system - .create_subsystem() - .expect("create_subsystem should not fail"), - ctx, - started_at: now_sec(), - maker_coin: m.clone(), - maker_volume: maker_amount, - secret, - taker_coin: t.clone(), - dex_fee: dex_fee_amount_from_taker_coin(&t, m.ticker(), &taker_amount), - taker_volume: taker_amount, - taker_premium: Default::default(), - conf_settings: my_conf_settings, - p2p_topic: swap_v2_topic(&uuid), - uuid, - p2p_keypair: maker_order.p2p_privkey.map(SerializableSecp256k1Keypair::into_inner), - secret_hash_algo, - lock_duration: lock_time, - taker_p2p_pubkey: match taker_p2p_pubkey { - PublicKey::Secp256k1(pubkey) => pubkey.into(), - }, - }; - #[allow(clippy::box_default)] - maker_swap_state_machine - .run(Box::new(maker_swap_v2::Initialize::default())) - .await - .error_log(); - }, - _ => todo!("implement fallback to the old protocol here"), - } - } else { - if let Err(e) = insert_new_swap_to_db( - ctx.clone(), - maker_coin.ticker(), - taker_coin.ticker(), - uuid, - now, - LEGACY_SWAP_TYPE, - ) - .await - { - error!("Error {} on new swap insertion", e); - } - let maker_swap = MakerSwap::new( - ctx.clone(), - alice, - maker_amount.to_decimal(), - taker_amount.to_decimal(), - my_persistent_pub, - uuid, - Some(maker_order.uuid), - my_conf_settings, - maker_coin, - taker_coin, - lock_time, - maker_order.p2p_privkey.map(SerializableSecp256k1Keypair::into_inner), - secret, - ); - run_maker_swap(RunMakerSwapInput::StartNew(maker_swap), ctx).await; + let alice_swap_v = maker_match.request.swap_version; + // Start legacy swap if taker uses legacy protocol (version 1) or if conditions for trading_proto_v2 aren't met. + if alice_swap_v == 1u32 || !(ctx.use_trading_proto_v2() && alice_swap_v == 2u32) { + let params = LegacySwapParams { + maker_coin: &maker_coin, + taker_coin: &taker_coin, + uuid: &uuid, + my_conf_settings: &my_conf_settings, + my_persistent_pub: &my_persistent_pub, + maker_amount: &maker_amount, + taker_amount: &taker_amount, + locktime: &lock_time, + }; + start_maker_legacy_swap(&ctx, maker_order, alice, secret, params).await; + return; + } + + let params = StateMachineParams { + secret_hash_algo: &detect_secret_hash_algo_v2(&maker_coin, &taker_coin), + uuid: &uuid, + my_conf_settings: &my_conf_settings, + locktime: &lock_time, + maker_amount: &maker_amount, + taker_amount: &taker_amount, + }; + let taker_p2p_pubkey = match taker_p2p_pubkey { + PublicKey::Secp256k1(pubkey) => pubkey.into(), + }; + + // TODO try to handle it more gracefully during project redesign + match (&maker_coin, &taker_coin) { + (MmCoinEnum::UtxoCoin(m), MmCoinEnum::UtxoCoin(t)) => { + start_maker_swap_state_machine(&ctx, &maker_order, &taker_p2p_pubkey, &secret, m, t, ¶ms).await; + }, + (MmCoinEnum::EthCoin(m), MmCoinEnum::EthCoin(t)) => { + start_maker_swap_state_machine(&ctx, &maker_order, &taker_p2p_pubkey, &secret, m, t, ¶ms).await; + }, + (MmCoinEnum::UtxoCoin(m), MmCoinEnum::EthCoin(t)) => { + start_maker_swap_state_machine(&ctx, &maker_order, &taker_p2p_pubkey, &secret, m, t, ¶ms).await; + }, + (MmCoinEnum::EthCoin(m), MmCoinEnum::UtxoCoin(t)) => { + start_maker_swap_state_machine(&ctx, &maker_order, &taker_p2p_pubkey, &secret, m, t, ¶ms).await; + }, + _ => { + let params = LegacySwapParams { + maker_coin: &maker_coin, + taker_coin: &taker_coin, + uuid: &uuid, + my_conf_settings: &my_conf_settings, + my_persistent_pub: &my_persistent_pub, + maker_amount: &maker_amount, + taker_amount: &taker_amount, + locktime: &lock_time, + }; + start_maker_legacy_swap(&ctx, maker_order, alice, secret, params).await + }, } }; @@ -3050,6 +3092,98 @@ fn lp_connect_start_bob(ctx: MmArc, maker_match: MakerMatch, maker_order: MakerO spawner.spawn_with_settings(fut, settings); } +struct LegacySwapParams<'a> { + maker_coin: &'a MmCoinEnum, + taker_coin: &'a MmCoinEnum, + uuid: &'a Uuid, + my_conf_settings: &'a SwapConfirmationsSettings, + my_persistent_pub: &'a H264, + maker_amount: &'a MmNumber, + taker_amount: &'a MmNumber, + locktime: &'a u64, +} + +async fn start_maker_legacy_swap( + ctx: &MmArc, + maker_order: MakerOrder, + alice: bits256, + secret: H256, + params: LegacySwapParams<'_>, +) { + if let Err(e) = insert_new_swap_to_db( + ctx.clone(), + params.maker_coin.ticker(), + params.taker_coin.ticker(), + *params.uuid, + now_sec(), + LEGACY_SWAP_TYPE, + ) + .await + { + error!("Error {} on new swap insertion", e); + } + + let maker_swap = MakerSwap::new( + ctx.clone(), + alice, + params.maker_amount.to_decimal(), + params.taker_amount.to_decimal(), + *params.my_persistent_pub, + *params.uuid, + Some(maker_order.uuid), + *params.my_conf_settings, + params.maker_coin.clone(), + params.taker_coin.clone(), + *params.locktime, + maker_order.p2p_privkey.map(SerializableSecp256k1Keypair::into_inner), + secret, + ); + run_maker_swap(RunMakerSwapInput::StartNew(maker_swap), ctx.clone()).await; +} + +async fn start_maker_swap_state_machine< + MakerCoin: MmCoin + MakerCoinSwapOpsV2 + Clone, + TakerCoin: MmCoin + TakerCoinSwapOpsV2 + Clone, +>( + ctx: &MmArc, + maker_order: &MakerOrder, + taker_p2p_pubkey: &Secp256k1Pubkey, + secret: &H256, + maker_coin: &MakerCoin, + taker_coin: &TakerCoin, + params: &StateMachineParams<'_>, +) { + let mut maker_swap_state_machine = MakerSwapStateMachine { + storage: MakerSwapStorage::new(ctx.clone()), + abortable_system: ctx + .abortable_system + .create_subsystem() + .expect("create_subsystem should not fail"), + ctx: ctx.clone(), + started_at: now_sec(), + maker_coin: maker_coin.clone(), + maker_volume: params.maker_amount.clone(), + secret: *secret, + taker_coin: taker_coin.clone(), + dex_fee: dex_fee_amount_from_taker_coin(taker_coin, maker_coin.ticker(), params.taker_amount), + taker_volume: params.taker_amount.clone(), + taker_premium: Default::default(), + conf_settings: *params.my_conf_settings, + p2p_topic: swap_v2_topic(params.uuid), + uuid: *params.uuid, + p2p_keypair: maker_order.p2p_privkey.map(SerializableSecp256k1Keypair::into_inner), + secret_hash_algo: *params.secret_hash_algo, + lock_duration: *params.locktime, + taker_p2p_pubkey: *taker_p2p_pubkey, + require_taker_payment_spend_confirm: true, + }; + #[allow(clippy::box_default)] + maker_swap_state_machine + .run(Box::new(maker_swap_v2::Initialize::default())) + .await + .error_log(); +} + fn lp_connected_alice(ctx: MmArc, taker_order: TakerOrder, taker_match: TakerMatch, maker_p2p_pubkey: PublicKey) { let spawner = ctx.spawner(); let uuid = taker_match.reserved.taker_order_uuid; @@ -3127,86 +3261,73 @@ fn lp_connected_alice(ctx: MmArc, taker_order: TakerOrder, taker_match: TakerMat uuid ); - let now = now_sec(); - if ctx.use_trading_proto_v2() { - let taker_secret = match generate_secret() { - Ok(s) => s.into(), - Err(e) => { - error!("Error {} on secret generation", e); - return; - }, + let bob_swap_v = taker_match.reserved.swap_version; + // Start legacy swap if maker uses legacy protocol (version 1) or if conditions for trading_proto_v2 aren't met. + if bob_swap_v == 1u32 || !(ctx.use_trading_proto_v2() && bob_swap_v == 2u32) { + let params = LegacySwapParams { + maker_coin: &maker_coin, + taker_coin: &taker_coin, + uuid: &uuid, + my_conf_settings: &my_conf_settings, + my_persistent_pub: &my_persistent_pub, + maker_amount: &maker_amount, + taker_amount: &taker_amount, + locktime: &locktime, }; - let secret_hash_algo = detect_secret_hash_algo(&maker_coin, &taker_coin); - match (maker_coin, taker_coin) { - (MmCoinEnum::UtxoCoin(m), MmCoinEnum::UtxoCoin(t)) => { - let mut taker_swap_state_machine = TakerSwapStateMachine { - storage: TakerSwapStorage::new(ctx.clone()), - abortable_system: ctx - .abortable_system - .create_subsystem() - .expect("create_subsystem should not fail"), - ctx, - started_at: now, - lock_duration: locktime, - maker_coin: m.clone(), - maker_volume: maker_amount, - taker_coin: t.clone(), - dex_fee: dex_fee_amount_from_taker_coin(&t, maker_coin_ticker, &taker_amount), - taker_volume: taker_amount, - taker_premium: Default::default(), - secret_hash_algo, - conf_settings: my_conf_settings, - p2p_topic: swap_v2_topic(&uuid), - uuid, - p2p_keypair: taker_order.p2p_privkey.map(SerializableSecp256k1Keypair::into_inner), - taker_secret, - maker_p2p_pubkey: match maker_p2p_pubkey { - PublicKey::Secp256k1(pubkey) => pubkey.into(), - }, - require_maker_payment_confirm_before_funding_spend: true, - }; - #[allow(clippy::box_default)] - taker_swap_state_machine - .run(Box::new(taker_swap_v2::Initialize::default())) - .await - .error_log(); - }, - _ => 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(); + start_taker_legacy_swap(&ctx, taker_order, maker, params).await; + return; + } - if let Err(e) = insert_new_swap_to_db( - ctx.clone(), - taker_coin.ticker(), - maker_coin.ticker(), - uuid, - now, - LEGACY_SWAP_TYPE, - ) - .await - { - error!("Error {} on new swap insertion", e); - } + let taker_secret = match generate_secret() { + Ok(s) => s.into(), + Err(e) => { + error!("Error {} on secret generation", e); + return; + }, + }; + let params = StateMachineParams { + secret_hash_algo: &detect_secret_hash_algo_v2(&maker_coin, &taker_coin), + uuid: &uuid, + my_conf_settings: &my_conf_settings, + locktime: &locktime, + maker_amount: &maker_amount, + taker_amount: &taker_amount, + }; + let maker_p2p_pubkey = match maker_p2p_pubkey { + PublicKey::Secp256k1(pubkey) => pubkey.into(), + }; - let taker_swap = TakerSwap::new( - ctx.clone(), - maker, - maker_amount, - taker_amount, - my_persistent_pub, - uuid, - Some(uuid), - my_conf_settings, - maker_coin, - 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 + // TODO try to handle it more gracefully during project redesign + match (&maker_coin, &taker_coin) { + (MmCoinEnum::UtxoCoin(m), MmCoinEnum::UtxoCoin(t)) => { + start_taker_swap_state_machine(&ctx, &taker_order, &maker_p2p_pubkey, &taker_secret, m, t, ¶ms) + .await; + }, + (MmCoinEnum::EthCoin(m), MmCoinEnum::EthCoin(t)) => { + start_taker_swap_state_machine(&ctx, &taker_order, &maker_p2p_pubkey, &taker_secret, m, t, ¶ms) + .await; + }, + (MmCoinEnum::UtxoCoin(m), MmCoinEnum::EthCoin(t)) => { + start_taker_swap_state_machine(&ctx, &taker_order, &maker_p2p_pubkey, &taker_secret, m, t, ¶ms) + .await; + }, + (MmCoinEnum::EthCoin(m), MmCoinEnum::UtxoCoin(t)) => { + start_taker_swap_state_machine(&ctx, &taker_order, &maker_p2p_pubkey, &taker_secret, m, t, ¶ms) + .await; + }, + _ => { + let params = LegacySwapParams { + maker_coin: &maker_coin, + taker_coin: &taker_coin, + uuid: &uuid, + my_conf_settings: &my_conf_settings, + my_persistent_pub: &my_persistent_pub, + maker_amount: &maker_amount, + taker_amount: &taker_amount, + locktime: &locktime, + }; + start_taker_legacy_swap(&ctx, taker_order, maker, params).await; + }, } }; @@ -3214,6 +3335,95 @@ fn lp_connected_alice(ctx: MmArc, taker_order: TakerOrder, taker_match: TakerMat spawner.spawn_with_settings(fut, settings) } +async fn start_taker_legacy_swap(ctx: &MmArc, taker_order: TakerOrder, maker: bits256, params: LegacySwapParams<'_>) { + #[cfg(any(test, feature = "run-docker-tests"))] + let fail_at = std::env::var("TAKER_FAIL_AT").map(FailAt::from).ok(); + + if let Err(e) = insert_new_swap_to_db( + ctx.clone(), + params.taker_coin.ticker(), + params.maker_coin.ticker(), + *params.uuid, + now_sec(), + LEGACY_SWAP_TYPE, + ) + .await + { + error!("Error {} on new swap insertion", e); + } + + let taker_swap = TakerSwap::new( + ctx.clone(), + maker, + params.maker_amount.clone(), + params.taker_amount.clone(), + *params.my_persistent_pub, + *params.uuid, + Some(*params.uuid), + *params.my_conf_settings, + params.maker_coin.clone(), + params.taker_coin.clone(), + *params.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.clone()).await +} + +struct StateMachineParams<'a> { + secret_hash_algo: &'a SecretHashAlgo, + uuid: &'a Uuid, + my_conf_settings: &'a SwapConfirmationsSettings, + locktime: &'a u64, + maker_amount: &'a MmNumber, + taker_amount: &'a MmNumber, +} + +async fn start_taker_swap_state_machine< + MakerCoin: MmCoin + MakerCoinSwapOpsV2 + Clone, + TakerCoin: MmCoin + TakerCoinSwapOpsV2 + Clone, +>( + ctx: &MmArc, + taker_order: &TakerOrder, + maker_p2p_pubkey: &Secp256k1Pubkey, + taker_secret: &H256, + maker_coin: &MakerCoin, + taker_coin: &TakerCoin, + params: &StateMachineParams<'_>, +) { + let mut taker_swap_state_machine = TakerSwapStateMachine { + storage: TakerSwapStorage::new(ctx.clone()), + abortable_system: ctx + .abortable_system + .create_subsystem() + .expect("create_subsystem should not fail"), + ctx: ctx.clone(), + started_at: now_sec(), + lock_duration: *params.locktime, + maker_coin: maker_coin.clone(), + maker_volume: params.maker_amount.clone(), + taker_coin: taker_coin.clone(), + dex_fee: dex_fee_amount_from_taker_coin(taker_coin, taker_order.maker_coin_ticker(), params.taker_amount), + taker_volume: params.taker_amount.clone(), + taker_premium: Default::default(), + secret_hash_algo: *params.secret_hash_algo, + conf_settings: *params.my_conf_settings, + p2p_topic: swap_v2_topic(params.uuid), + uuid: *params.uuid, + p2p_keypair: taker_order.p2p_privkey.map(SerializableSecp256k1Keypair::into_inner), + taker_secret: *taker_secret, + maker_p2p_pubkey: *maker_p2p_pubkey, + require_maker_payment_confirm_before_funding_spend: true, + require_maker_payment_spend_confirm: true, + }; + #[allow(clippy::box_default)] + taker_swap_state_machine + .run(Box::new(taker_swap_v2::Initialize::default())) + .await + .error_log(); +} + pub async fn lp_ordermatch_loop(ctx: MmArc) { // lp_ordermatch_loop is spawned only if CryptoCtx is initialized let my_pubsecp = CryptoCtx::from_ctx(&ctx) @@ -3712,6 +3922,7 @@ async fn process_taker_request(ctx: MmArc, from_pubkey: H256Json, taker_request: }), base_protocol_info: Some(base_coin.coin_protocol_info(None)), rel_protocol_info: Some(rel_coin.coin_protocol_info(Some(rel_amount.clone()))), + swap_version: order.swap_version, }; let topic = order.orderbook_topic(); log::debug!("Request matched sending reserved {:?}", reserved); @@ -3944,7 +4155,8 @@ pub async fn lp_auto_buy( .with_sender_pubkey(H256Json::from(our_public_id.bytes)) .with_save_in_history(input.save_in_history) .with_base_orderbook_ticker(ordermatch_ctx.orderbook_ticker(base_coin.ticker())) - .with_rel_orderbook_ticker(ordermatch_ctx.orderbook_ticker(rel_coin.ticker())); + .with_rel_orderbook_ticker(ordermatch_ctx.orderbook_ticker(rel_coin.ticker())) + .use_trading_proto_v2(ctx.use_trading_proto_v2()); if let Some(timeout) = input.timeout { order_builder = order_builder.with_timeout(timeout); } @@ -4689,7 +4901,8 @@ pub async fn create_maker_order(ctx: &MmArc, req: SetPriceReq) -> Result MakerOrder { MakerOrder { base: "BASE".to_owned(), @@ -724,6 +726,7 @@ mod tests { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, } } @@ -742,6 +745,7 @@ mod tests { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }, matches: HashMap::new(), created_at: now_ms(), diff --git a/mm2src/mm2_main/src/lp_ordermatch/new_protocol.rs b/mm2src/mm2_main/src/lp_ordermatch/new_protocol.rs index 72aa53597d..d7435f3258 100644 --- a/mm2src/mm2_main/src/lp_ordermatch/new_protocol.rs +++ b/mm2src/mm2_main/src/lp_ordermatch/new_protocol.rs @@ -5,7 +5,7 @@ use mm2_rpc::data::legacy::{MatchBy as SuperMatchBy, OrderConfirmationsSettings, use std::collections::{HashMap, HashSet}; use uuid::Uuid; -use crate::lp_ordermatch::{AlbOrderedOrderbookPair, H64}; +use crate::lp_ordermatch::{is_legacy_swap_version, legacy_swap_version, AlbOrderedOrderbookPair, H64}; #[derive(Debug, Deserialize, Serialize)] #[allow(clippy::large_enum_variant)] @@ -265,6 +265,9 @@ pub struct TakerRequest { #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] pub rel_protocol_info: Option>, + #[serde(default = "legacy_swap_version")] + #[serde(skip_serializing_if = "is_legacy_swap_version")] + pub swap_version: u32, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -282,6 +285,9 @@ pub struct MakerReserved { #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] pub rel_protocol_info: Option>, + #[serde(default = "legacy_swap_version")] + #[serde(skip_serializing_if = "is_legacy_swap_version")] + pub swap_version: u32, } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 0acb7fc443..3ae9aa57a5 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -61,7 +61,7 @@ use super::lp_network::P2PRequestResult; use crate::lp_network::{broadcast_p2p_msg, Libp2pPeerId, P2PProcessError, P2PProcessResult, P2PRequestError}; use crate::lp_swap::maker_swap_v2::{MakerSwapStateMachine, MakerSwapStorage}; use crate::lp_swap::taker_swap_v2::{TakerSwapStateMachine, TakerSwapStorage}; -use bitcrypto::{dhash160, sha256}; +use bitcrypto::sha256; use coins::{lp_coinfind, lp_coinfind_or_err, CoinFindError, DexFee, MmCoin, MmCoinEnum, TradeFee, TransactionEnum}; use common::log::{debug, warn}; use common::now_sec; @@ -83,7 +83,6 @@ use secp256k1::{PublicKey, SecretKey, Signature}; use serde::Serialize; use serde_json::{self as json, Value as Json}; use std::collections::{HashMap, HashSet}; -use std::convert::TryFrom; use std::num::NonZeroUsize; use std::path::PathBuf; use std::str::FromStr; @@ -118,6 +117,7 @@ mod trade_preimage; pub use check_balance::{check_other_coin_balance_for_swap, CheckBalanceError, CheckBalanceResult}; use coins::utxo::utxo_standard::UtxoStandardCoin; +use crypto::secret_hash_algo::SecretHashAlgo; use crypto::CryptoCtx; use keys::{KeyPair, SECP_SIGN, SECP_VERIFY}; use maker_swap::MakerSwapEvent; @@ -1624,42 +1624,6 @@ pub async fn active_swaps_rpc(ctx: MmArc, req: Json) -> Result> Ok(try_s!(Response::builder().body(res))) } -/// Algorithm used to hash swap secret. -#[derive(Clone, Copy, Debug, Deserialize, Serialize, Default)] -pub enum SecretHashAlgo { - /// ripemd160(sha256(secret)) - #[default] - DHASH160 = 1, - /// sha256(secret) - SHA256 = 2, -} - -#[derive(Debug, Display)] -pub struct UnsupportedSecretHashAlgo(u8); - -impl std::error::Error for UnsupportedSecretHashAlgo {} - -impl TryFrom for SecretHashAlgo { - type Error = UnsupportedSecretHashAlgo; - - fn try_from(value: u8) -> Result { - match value { - 1 => Ok(SecretHashAlgo::DHASH160), - 2 => Ok(SecretHashAlgo::SHA256), - unsupported => Err(UnsupportedSecretHashAlgo(unsupported)), - } - } -} - -impl SecretHashAlgo { - fn hash_secret(&self, secret: &[u8]) -> Vec { - match self { - SecretHashAlgo::DHASH160 => dhash160(secret).take().into(), - SecretHashAlgo::SHA256 => sha256(secret).take().into(), - } - } -} - // Todo: Maybe add a secret_hash_algo method to the SwapOps trait instead /// Selects secret hash algorithm depending on types of coins being swapped #[cfg(not(target_arch = "wasm32"))] @@ -1684,6 +1648,19 @@ pub fn detect_secret_hash_algo(maker_coin: &MmCoinEnum, taker_coin: &MmCoinEnum) } } +pub fn detect_secret_hash_algo_v2(maker_coin: &MmCoinEnum, taker_coin: &MmCoinEnum) -> SecretHashAlgo { + let maker_algo = maker_coin.maker_secret_hash_algo_v2(); + let taker_algo = taker_coin.taker_secret_hash_algo_v2(); + if matches!( + (maker_algo, taker_algo), + (SecretHashAlgo::SHA256, _) | (_, SecretHashAlgo::SHA256) + ) { + SecretHashAlgo::SHA256 + } else { + SecretHashAlgo::DHASH160 + } +} + pub struct SwapPubkeys { pub maker: String, pub taker: String, diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 0eb72b8a71..8031efe9e5 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -8,8 +8,8 @@ use super::{broadcast_my_swap_status, broadcast_p2p_tx_msg, broadcast_swap_msg_e get_locked_amount, recv_swap_msg, swap_topic, taker_payment_spend_deadline, tx_helper_topic, wait_for_maker_payment_conf_until, AtomicSwap, LockedAmount, MySwapInfo, NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, RecoveredSwap, RecoveredSwapAction, SavedSwap, SavedSwapIo, - SavedTradeFee, SecretHashAlgo, SwapConfirmationsSettings, SwapError, SwapMsg, SwapPubkeys, SwapTxDataMsg, - SwapsContext, TransactionIdentifier, INCLUDE_REFUND_FEE, NO_REFUND_FEE, TAKER_FEE_VALIDATION_ATTEMPTS, + SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapPubkeys, SwapTxDataMsg, SwapsContext, + TransactionIdentifier, INCLUDE_REFUND_FEE, NO_REFUND_FEE, TAKER_FEE_VALIDATION_ATTEMPTS, TAKER_FEE_VALIDATION_RETRY_DELAY_SECS, WAIT_CONFIRM_INTERVAL_SEC}; use crate::lp_dispatcher::{DispatcherContext, LpEvents}; use crate::lp_network::subscribe_to_topic; @@ -25,6 +25,7 @@ use common::log::{debug, error, info, warn}; use common::{bits256, executor::Timer, now_ms, DEX_FEE_ADDR_RAW_PUBKEY}; use common::{now_sec, wait_until_sec}; use crypto::privkey::SerializableSecp256k1Keypair; +use crypto::secret_hash_algo::SecretHashAlgo; use crypto::CryptoCtx; use futures::{compat::Future01CompatExt, select, FutureExt}; use keys::KeyPair; diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs index d0e667a752..a306ab97e9 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs @@ -3,7 +3,7 @@ use super::{swap_v2_topic, LockedAmount, LockedAmountInfo, SavedTradeFee, SwapsC NEGOTIATION_TIMEOUT_SEC}; use crate::lp_swap::maker_swap::MakerSwapPreparedParams; use crate::lp_swap::swap_lock::SwapLock; -use crate::lp_swap::{broadcast_swap_v2_msg_every, check_balance_for_maker_swap, recv_swap_v2_msg, SecretHashAlgo, +use crate::lp_swap::{broadcast_swap_v2_msg_every, check_balance_for_maker_swap, recv_swap_v2_msg, SwapConfirmationsSettings, TransactionIdentifier, MAKER_SWAP_V2_TYPE, MAX_STARTED_AT_DIFF}; use crate::lp_swap::{swap_v2_pb::*, NO_REFUND_FEE}; use async_trait::async_trait; @@ -17,6 +17,7 @@ use common::executor::{AbortableSystem, Timer}; use common::log::{debug, error, info, warn}; use common::{now_sec, Future01CompatExt, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::privkey::SerializableSecp256k1Keypair; +use crypto::secret_hash_algo::SecretHashAlgo; use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; @@ -121,6 +122,7 @@ pub enum MakerSwapEvent { maker_payment: TransactionIdentifier, taker_payment: TransactionIdentifier, taker_payment_spend: TransactionIdentifier, + negotiation_data: StoredNegotiationData, }, /// Swap has been aborted before maker payment was sent. Aborted { reason: AbortReason }, @@ -387,6 +389,7 @@ pub struct MakerSwapStateMachine @@ -400,6 +403,9 @@ impl u64 { self.started_at + 2 * self.lock_duration } + #[inline] + fn spend_taker_payment_conf_timeout(&self) -> u64 { self.started_at + 3 * self.lock_duration } + /// Returns secret hash generated using selected [SecretHashAlgo]. fn secret_hash(&self) -> Vec { match self.secret_hash_algo { @@ -593,6 +599,7 @@ impl Box::new(TakerPaymentSpent { maker_coin_start_block, taker_coin_start_block, @@ -608,6 +615,11 @@ impl return MmError::err(SwapRecreateError::SwapAborted), MakerSwapEvent::Completed => return MmError::err(SwapRecreateError::SwapCompleted), @@ -645,6 +657,7 @@ impl, state_machine: &mut Self::StateMachine) -> StateResult { let unique_data = state_machine.unique_data(); + let taker_coin_address = state_machine.taker_coin.my_addr().await; let maker_negotiation_msg = MakerNegotiation { started_at: state_machine.started_at, @@ -909,7 +923,7 @@ impl TransitionFrom> for MakerPaymentRefundRequired { } +impl + TransitionFrom> for MakerPaymentRefundRequired +{ +} #[async_trait] impl State @@ -1707,6 +1726,7 @@ impl, } impl @@ -1754,6 +1775,27 @@ impl; async fn on_changed(self: Box, state_machine: &mut Self::StateMachine) -> StateResult { + if state_machine.require_taker_payment_spend_confirm { + let input = ConfirmPaymentInput { + payment_tx: self.taker_payment_spend.tx_hex(), + confirmations: state_machine.conf_settings.taker_coin_confs, + requires_nota: state_machine.conf_settings.taker_coin_nota, + wait_until: state_machine.spend_taker_payment_conf_timeout(), + check_every: 10, + }; + + if let Err(e) = state_machine.taker_coin.wait_for_confirmations(input).compat().await { + let next_state = MakerPaymentRefundRequired { + maker_coin_start_block: self.maker_coin_start_block, + taker_coin_start_block: self.taker_coin_start_block, + negotiation_data: self.negotiation_data, + maker_payment: self.maker_payment, + reason: MakerPaymentRefundReason::TakerPaymentSpendNotConfirmedInTime(e.to_string()), + }; + return Self::change_state(next_state, state_machine).await; + } + } + Self::change_state(Completed::new(), state_machine).await } } @@ -1779,6 +1821,7 @@ impl (m, t), _ => { diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs index 29f3d07277..aaa40a72b5 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs @@ -3,8 +3,7 @@ use super::{LockedAmount, LockedAmountInfo, SavedTradeFee, SwapsContext, TakerSw NEGOTIATE_SEND_INTERVAL, NEGOTIATION_TIMEOUT_SEC}; use crate::lp_swap::swap_lock::SwapLock; use crate::lp_swap::{broadcast_swap_v2_msg_every, check_balance_for_taker_swap, recv_swap_v2_msg, swap_v2_topic, - SecretHashAlgo, SwapConfirmationsSettings, TransactionIdentifier, MAX_STARTED_AT_DIFF, - TAKER_SWAP_V2_TYPE}; + SwapConfirmationsSettings, TransactionIdentifier, MAX_STARTED_AT_DIFF, TAKER_SWAP_V2_TYPE}; use crate::lp_swap::{swap_v2_pb::*, NO_REFUND_FEE}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; @@ -18,6 +17,7 @@ use common::executor::{AbortableSystem, Timer}; use common::log::{debug, error, info, warn}; use common::{Future01CompatExt, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::privkey::SerializableSecp256k1Keypair; +use crypto::secret_hash_algo::SecretHashAlgo; use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; @@ -141,6 +141,7 @@ pub enum TakerSwapEvent { taker_payment: TransactionIdentifier, taker_payment_spend: TransactionIdentifier, maker_payment_spend: TransactionIdentifier, + negotiation_data: StoredNegotiationData, }, /// Swap has been finished with taker funding tx refund TakerFundingRefunded { @@ -421,6 +422,7 @@ pub struct TakerSwapStateMachine @@ -432,6 +434,9 @@ impl u64 { self.started_at + self.lock_duration } + #[inline] + fn spend_maker_payment_conf_timeout(&self) -> u64 { self.started_at + 4 * self.lock_duration } + fn unique_data(&self) -> Vec { self.uuid.as_bytes().to_vec() } /// Returns secret hash generated using selected [SecretHashAlgo]. @@ -702,6 +707,7 @@ impl Box::new(MakerPaymentSpent { maker_coin_start_block, taker_coin_start_block, @@ -721,6 +727,11 @@ impl return MmError::err(SwapRecreateError::SwapAborted), TakerSwapEvent::Completed => return MmError::err(SwapRecreateError::SwapCompleted), @@ -762,6 +773,7 @@ impl TransitionFrom> for TakerPaymentRefundRequired { } +impl + TransitionFrom> for TakerPaymentRefundRequired +{ +} #[async_trait] impl State @@ -2004,7 +2021,7 @@ impl s, @@ -2116,6 +2129,7 @@ impl, } impl @@ -2186,6 +2201,7 @@ impl; async fn on_changed(self: Box, state_machine: &mut Self::StateMachine) -> StateResult { + if state_machine.require_maker_payment_spend_confirm { + let input = ConfirmPaymentInput { + payment_tx: self.maker_payment_spend.tx_hex(), + confirmations: state_machine.conf_settings.maker_coin_confs, + requires_nota: state_machine.conf_settings.maker_coin_nota, + wait_until: state_machine.spend_maker_payment_conf_timeout(), + check_every: 10, + }; + + if let Err(e) = state_machine.maker_coin.wait_for_confirmations(input).compat().await { + let next_state = TakerPaymentRefundRequired { + taker_payment: self.taker_payment, + negotiation_data: self.negotiation_data, + reason: TakerPaymentRefundReason::MakerPaymentSpendNotConfirmedInTime(format!("{}", e)), + }; + return Self::change_state(next_state, state_machine).await; + } + } + Self::change_state(Completed::new(), state_machine).await } } diff --git a/mm2src/mm2_main/src/ordermatch_tests.rs b/mm2src/mm2_main/src/ordermatch_tests.rs index 1ac83697af..86fac98f32 100644 --- a/mm2src/mm2_main/src/ordermatch_tests.rs +++ b/mm2src/mm2_main/src/ordermatch_tests.rs @@ -20,6 +20,8 @@ use std::collections::HashSet; use std::iter::{self, FromIterator}; use std::sync::Mutex; +const LEGACY_SWAP_V: u32 = 1; + #[test] fn test_match_maker_order_and_taker_request() { let maker = MakerOrder { @@ -39,6 +41,7 @@ fn test_match_maker_order_and_taker_request() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; let request = TakerRequest { @@ -54,6 +57,7 @@ fn test_match_maker_order_and_taker_request() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let actual = maker.match_with_request(&request); @@ -77,6 +81,7 @@ fn test_match_maker_order_and_taker_request() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; let request = TakerRequest { @@ -92,6 +97,7 @@ fn test_match_maker_order_and_taker_request() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let actual = maker.match_with_request(&request); @@ -115,6 +121,7 @@ fn test_match_maker_order_and_taker_request() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; let request = TakerRequest { @@ -130,6 +137,7 @@ fn test_match_maker_order_and_taker_request() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let actual = maker.match_with_request(&request); @@ -153,6 +161,7 @@ fn test_match_maker_order_and_taker_request() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; let request = TakerRequest { @@ -168,6 +177,7 @@ fn test_match_maker_order_and_taker_request() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let actual = maker.match_with_request(&request); @@ -191,6 +201,7 @@ fn test_match_maker_order_and_taker_request() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; let request = TakerRequest { @@ -206,6 +217,7 @@ fn test_match_maker_order_and_taker_request() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let actual = maker.match_with_request(&request); @@ -229,6 +241,7 @@ fn test_match_maker_order_and_taker_request() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; let request = TakerRequest { @@ -244,6 +257,7 @@ fn test_match_maker_order_and_taker_request() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let actual = maker.match_with_request(&request); @@ -269,6 +283,7 @@ fn test_match_maker_order_and_taker_request() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; let request = TakerRequest { base: "KMD".to_owned(), @@ -283,6 +298,7 @@ fn test_match_maker_order_and_taker_request() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let actual = maker.match_with_request(&request); assert_eq!(actual, OrderMatchResult::NotMatched); @@ -309,6 +325,7 @@ fn test_match_maker_order_and_taker_request() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; let request = TakerRequest { base: "REL".to_owned(), @@ -323,6 +340,7 @@ fn test_match_maker_order_and_taker_request() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let actual = maker.match_with_request(&request); let expected_base_amount = MmNumber::from(3); @@ -380,6 +398,7 @@ fn test_maker_order_available_amount() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; maker.matches.insert(new_uuid(), MakerMatch { request: TakerRequest { @@ -395,6 +414,7 @@ fn test_maker_order_available_amount() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }, reserved: MakerReserved { base: "BASE".into(), @@ -408,6 +428,7 @@ fn test_maker_order_available_amount() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }, connect: None, connected: None, @@ -427,6 +448,7 @@ fn test_maker_order_available_amount() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }, reserved: MakerReserved { base: "BASE".into(), @@ -440,6 +462,7 @@ fn test_maker_order_available_amount() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }, connect: None, connected: None, @@ -468,6 +491,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let order = TakerOrder { @@ -495,6 +519,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; assert_eq!(MatchReservedResult::Matched, order.match_reserved(&reserved)); @@ -512,6 +537,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let order = TakerOrder { @@ -539,6 +565,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; assert_eq!(MatchReservedResult::Matched, order.match_reserved(&reserved)); @@ -556,6 +583,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let order = TakerOrder { @@ -583,6 +611,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; assert_eq!(MatchReservedResult::Matched, order.match_reserved(&reserved)); @@ -600,6 +629,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let order = TakerOrder { @@ -627,6 +657,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; assert_eq!(MatchReservedResult::NotMatched, order.match_reserved(&reserved)); @@ -644,6 +675,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let order = TakerOrder { @@ -671,6 +703,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; assert_eq!(MatchReservedResult::Matched, order.match_reserved(&reserved)); @@ -688,6 +721,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let order = TakerOrder { @@ -715,6 +749,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; assert_eq!(MatchReservedResult::Matched, order.match_reserved(&reserved)); @@ -732,6 +767,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let order = TakerOrder { @@ -759,6 +795,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; assert_eq!(MatchReservedResult::Matched, order.match_reserved(&reserved)); @@ -776,6 +813,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let order = TakerOrder { @@ -803,6 +841,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; assert_eq!(MatchReservedResult::NotMatched, order.match_reserved(&reserved)); @@ -824,6 +863,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }, matches: HashMap::new(), order_type: OrderType::GoodTillCancelled, @@ -847,6 +887,7 @@ fn test_taker_match_reserved() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; assert_eq!(MatchReservedResult::Matched, order.match_reserved(&reserved)); @@ -867,6 +908,7 @@ fn test_taker_order_cancellable() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let order = TakerOrder { @@ -897,6 +939,7 @@ fn test_taker_order_cancellable() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let mut order = TakerOrder { @@ -926,6 +969,7 @@ fn test_taker_order_cancellable() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }, connect: TakerConnect { sender_pubkey: H256Json::default(), @@ -974,6 +1018,7 @@ fn prepare_for_cancel_by(ctx: &MmArc) -> mpsc::Receiver { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }, None, ); @@ -996,6 +1041,7 @@ fn prepare_for_cancel_by(ctx: &MmArc) -> mpsc::Receiver { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }, None, ); @@ -1018,6 +1064,7 @@ fn prepare_for_cancel_by(ctx: &MmArc) -> mpsc::Receiver { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }, None, ); @@ -1037,6 +1084,7 @@ fn prepare_for_cancel_by(ctx: &MmArc) -> mpsc::Receiver { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }, order_type: OrderType::GoodTillCancelled, min_volume: 0.into(), @@ -1130,6 +1178,7 @@ fn test_taker_order_match_by() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; let mut order = TakerOrder { @@ -1157,6 +1206,7 @@ fn test_taker_order_match_by() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: LEGACY_SWAP_V, }; assert_eq!(MatchReservedResult::NotMatched, order.match_reserved(&reserved)); @@ -1197,6 +1247,7 @@ fn test_maker_order_was_updated() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; let mut update_msg = MakerOrderUpdated::new(maker_order.uuid); update_msg.with_new_price(BigRational::from_integer(2.into())); @@ -3203,6 +3254,7 @@ fn test_maker_order_balance_loops() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; let morty_order = MakerOrder { @@ -3222,6 +3274,7 @@ fn test_maker_order_balance_loops() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; assert!(!maker_orders_ctx.balance_loop_exists(rick_ticker)); @@ -3254,6 +3307,7 @@ fn test_maker_order_balance_loops() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: LEGACY_SWAP_V, }; maker_orders_ctx.add_order(ctx.weak(), rick_order_2.clone(), None); diff --git a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs index 7ea038a8f7..fdcdb1c9f3 100644 --- a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs @@ -1698,6 +1698,7 @@ fn taker_send_approve_and_spend_eth() { swap_unique_data: &[], }; wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let taker_coin_start_block = block_on(taker_coin.current_block().compat()).unwrap(); let funding_tx = block_on(taker_coin.send_taker_funding(payment_args)).unwrap(); log!("Taker sent ETH funding, tx hash: {:02x}", funding_tx.tx_hash()); wait_for_confirmations(&taker_coin, &funding_tx, 100); @@ -1740,12 +1741,12 @@ fn taker_send_approve_and_spend_eth() { wait_for_confirmations(&taker_coin, &taker_approve_tx, 100); wait_pending_transactions(Address::from_slice(maker_address.as_bytes())); - let check_taker_approved_tx = block_on(maker_coin.search_for_taker_funding_spend(&taker_approve_tx, 0u64, &[])) + let check_taker_approved_tx = block_on(maker_coin.search_for_taker_funding_spend(&funding_tx, 0u64, &[])) .unwrap() .unwrap(); match check_taker_approved_tx { FundingTxSpend::TransferredToTakerPayment(tx) => { - assert_eq!(tx, taker_approve_tx); + assert_eq!(tx, funding_tx); }, FundingTxSpend::RefundedTimelock(_) | FundingTxSpend::RefundedSecret { .. } => { panic!("Wrong FundingTxSpend variant, expected TransferredToTakerPayment") @@ -1771,7 +1772,11 @@ fn taker_send_approve_and_spend_eth() { .unwrap(); log!("Maker spent ETH payment, tx hash: {:02x}", spend_tx.tx_hash()); wait_for_confirmations(&maker_coin, &spend_tx, 100); - block_on(taker_coin.wait_for_taker_payment_spend(&spend_tx, 0u64, payment_time_lock)).unwrap(); + let found_spend_tx = + block_on(taker_coin.find_taker_payment_spend_tx(&taker_approve_tx, taker_coin_start_block, payment_time_lock)) + .unwrap(); + let extracted_maker_secret = block_on(taker_coin.extract_secret_v2(&[], &found_spend_tx)).unwrap(); + assert_eq!(maker_secret, extracted_maker_secret); } #[cfg(feature = "sepolia-taker-swap-v2-tests")] @@ -1809,6 +1814,7 @@ fn taker_send_approve_and_spend_erc20() { swap_unique_data: &[], }; wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let taker_coin_start_block = block_on(taker_coin.current_block().compat()).unwrap(); let funding_tx = block_on(taker_coin.send_taker_funding(payment_args)).unwrap(); log!("Taker sent ERC20 funding, tx hash: {:02x}", funding_tx.tx_hash()); wait_for_confirmations(&taker_coin, &funding_tx, 100); @@ -1851,12 +1857,12 @@ fn taker_send_approve_and_spend_erc20() { wait_for_confirmations(&taker_coin, &taker_approve_tx, 100); wait_pending_transactions(Address::from_slice(maker_address.as_bytes())); - let check_taker_approved_tx = block_on(maker_coin.search_for_taker_funding_spend(&taker_approve_tx, 0u64, &[])) + let check_taker_approved_tx = block_on(maker_coin.search_for_taker_funding_spend(&funding_tx, 0u64, &[])) .unwrap() .unwrap(); match check_taker_approved_tx { FundingTxSpend::TransferredToTakerPayment(tx) => { - assert_eq!(tx, taker_approve_tx); + assert_eq!(tx, funding_tx); }, FundingTxSpend::RefundedTimelock(_) | FundingTxSpend::RefundedSecret { .. } => { panic!("Wrong FundingTxSpend variant, expected TransferredToTakerPayment") @@ -1881,7 +1887,11 @@ fn taker_send_approve_and_spend_erc20() { block_on(maker_coin.sign_and_broadcast_taker_payment_spend(&preimage, &spend_args, &maker_secret, &[])) .unwrap(); log!("Maker spent ERC20 payment, tx hash: {:02x}", spend_tx.tx_hash()); - block_on(taker_coin.wait_for_taker_payment_spend(&spend_tx, 0u64, payment_time_lock)).unwrap(); + let found_spend_tx = + block_on(taker_coin.find_taker_payment_spend_tx(&taker_approve_tx, taker_coin_start_block, payment_time_lock)) + .unwrap(); + let extracted_maker_secret = block_on(taker_coin.extract_secret_v2(&[], &found_spend_tx)).unwrap(); + assert_eq!(maker_secret, extracted_maker_secret); } #[cfg(feature = "sepolia-taker-swap-v2-tests")] diff --git a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs index 304f6f4819..1cba25152b 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs @@ -699,8 +699,8 @@ fn test_v2_swap_utxo_utxo() { assert_eq!(locked_bob.locked_amount, expected); for uuid in uuids { - block_on(wait_for_swap_finished(&mm_bob, &uuid, 60)); - block_on(wait_for_swap_finished(&mm_alice, &uuid, 30)); + block_on(wait_for_swap_finished(&mm_bob, &uuid, 180)); + block_on(wait_for_swap_finished(&mm_alice, &uuid, 100)); let maker_swap_status = block_on(my_swap_status(&mm_bob, &uuid)); log!("{:?}", maker_swap_status); @@ -829,8 +829,8 @@ fn test_v2_swap_utxo_utxo_kickstart() { assert_eq!(locked_bob.locked_amount, expected); for uuid in uuids { - block_on(wait_for_swap_finished(&mm_bob, &uuid, 60)); - block_on(wait_for_swap_finished(&mm_alice, &uuid, 30)); + block_on(wait_for_swap_finished(&mm_bob, &uuid, 180)); + block_on(wait_for_swap_finished(&mm_alice, &uuid, 100)); } }