Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reduce evm wait_for_confirmations calls, fix endless loop in wait_for_htlc_tx_spend #1724

Merged
merged 11 commits into from
Mar 21, 2023
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
- Get rid of unnecessary / old dependencies: `crossterm`, `crossterm_winapi`, `mio 0.7.13`, `miow`, `ntapi`, `signal-hook`, `signal-hook-mio` in [#1710](https://github.com/KomodoPlatform/atomicDEX-API/pull/1710)
- A bug that caused EVM swap payments validation to fail because the tx was not available yet in the RPC node when calling `eth_getTransactionByHash` was fixed in [#1716](https://github.com/KomodoPlatform/atomicDEX-API/pull/1716). `eth_getTransactionByHash` in now retried in `wait_for_confirmations` until tx is found in the RPC node, this makes sure that the transaction is returned from `eth_getTransactionByHash` later when validating.
- `OperationFailure::Other` error was expanded. New error variants were matched with `HwRpcError`, so error type will be `HwError`, not `InternalError` [#1719](https://github.com/KomodoPlatform/atomicDEX-API/pull/1719)
- RPC calls for evm chains was reduced in `wait_for_confirmations` function in [#1724](https://github.com/KomodoPlatform/atomicDEX-API/pull/1724)
- A possible endless loop in evm `wait_for_htlc_tx_spend` was fixed in [#1724](https://github.com/KomodoPlatform/atomicDEX-API/pull/1724)


## v1.0.0-beta - 2023-03-08
Expand Down
4 changes: 3 additions & 1 deletion mm2src/coins/coin_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ impl From<Web3RpcError> for ValidatePaymentError {
match e {
Web3RpcError::Transport(tr) => ValidatePaymentError::Transport(tr),
Web3RpcError::InvalidResponse(resp) => ValidatePaymentError::InvalidRpcResponse(resp),
Web3RpcError::Internal(internal) => ValidatePaymentError::InternalError(internal),
Web3RpcError::Internal(internal) | Web3RpcError::Timeout(internal) => {
ValidatePaymentError::InternalError(internal)
},
}
}
}
Expand Down
307 changes: 186 additions & 121 deletions mm2src/coins/eth.rs

Large diffs are not rendered by default.

39 changes: 16 additions & 23 deletions mm2src/coins/lightning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ use crate::lightning::ln_utils::{filter_channels, pay_invoice_with_max_total_clt
use crate::utxo::rpc_clients::UtxoRpcClientEnum;
use crate::utxo::utxo_common::{big_decimal_from_sat, big_decimal_from_sat_unsigned};
use crate::utxo::{sat_from_big_decimal, utxo_common, BlockchainNetwork};
use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, FeeApproxStage, FoundSwapTxSpend,
HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, NegotiateSwapContractAddrErr,
PaymentInstructions, PaymentInstructionsErr, RawTransactionError, RawTransactionFut,
RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput,
SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureError, SignatureResult, SpendPaymentArgs,
SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue,
Transaction, TransactionEnum, TransactionErr, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod,
UtxoStandardCoin, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr,
ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult,
WatcherOps, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput,
WithdrawError, WithdrawFut, WithdrawRequest};
use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage,
FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin,
NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, RawTransactionError,
RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult,
SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureError,
SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageFut,
TradePreimageResult, TradePreimageValue, Transaction, TransactionEnum, TransactionErr, TransactionFut,
TxMarshalingErr, UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidateFeeArgs,
ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut,
ValidatePaymentInput, VerificationError, VerificationResult, WatcherOps, WatcherSearchForSwapTxSpendInput,
WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest};
use async_trait::async_trait;
use bitcoin::bech32::ToBase32;
use bitcoin::hashes::Hash;
Expand Down Expand Up @@ -1071,24 +1071,17 @@ impl MarketCoinOps for LightningCoin {

// Todo: Add waiting for confirmations logic for the case of if the channel is closed and the htlc can be claimed on-chain
// Todo: The above is postponed and might not be needed after this issue is resolved https://github.com/lightningdevkit/rust-lightning/issues/2017
fn wait_for_confirmations(
&self,
tx: &[u8],
_confirmations: u64,
_requires_nota: bool,
wait_until: u64,
check_every: u64,
) -> Box<dyn Future<Item = (), Error = String> + Send> {
let payment_hash = try_f!(payment_hash_from_slice(tx).map_err(|e| e.to_string()));
fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
let payment_hash = try_f!(payment_hash_from_slice(&input.payment_tx).map_err(|e| e.to_string()));
let payment_hex = hex::encode(payment_hash.0);

let coin = self.clone();
let fut = async move {
loop {
if now_ms() / 1000 > wait_until {
if now_ms() / 1000 > input.wait_until {
return ERR!(
"Waited too long until {} for payment {} to be received",
wait_until,
input.wait_until,
payment_hex
);
}
Expand Down Expand Up @@ -1124,7 +1117,7 @@ impl MarketCoinOps for LightningCoin {
// note: When sleeping for only 1 second the test_send_payment_and_swaps unit test took 20 seconds to complete instead of 37 seconds when WAIT_CONFIRM_INTERVAL (15 seconds) is used
// Todo: In next sprints, should add a mutex for lightning swap payments to avoid overloading the shared db connection with requests when the sleep time is reduced and multiple swaps are ran together
// Todo: The aim is to make lightning swap payments as fast as possible. Running swap payments statuses should be loaded from db on restarts in this case.
Timer::sleep(check_every as f64).await;
Timer::sleep(input.check_every as f64).await;
}
};
Box::new(fut.boxed().compat())
Expand Down
18 changes: 10 additions & 8 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,15 @@ pub enum ValidateOtherPubKeyErr {
InvalidPubKey(String),
}

#[derive(Clone, Debug)]
pub struct ConfirmPaymentInput {
pub payment_tx: Vec<u8>,
pub confirmations: u64,
pub requires_nota: bool,
pub wait_until: u64,
pub check_every: u64,
}

#[derive(Clone, Debug)]
pub struct WatcherValidateTakerFeeInput {
pub taker_fee_hash: Vec<u8>,
Expand Down Expand Up @@ -941,14 +950,7 @@ pub trait MarketCoinOps {
/// Receives raw transaction bytes as input and returns tx hash in hexadecimal format
fn send_raw_tx_bytes(&self, tx: &[u8]) -> Box<dyn Future<Item = String, Error = String> + Send>;

fn wait_for_confirmations(
&self,
tx: &[u8],
confirmations: u64,
requires_nota: bool,
wait_until: u64,
check_every: u64,
) -> Box<dyn Future<Item = (), Error = String> + Send>;
fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send>;

fn wait_for_htlc_tx_spend(
&self,
Expand Down
25 changes: 12 additions & 13 deletions mm2src/coins/qrc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use crate::utxo::{qtum, ActualTxFee, AdditionalTxData, AddrFromStrError, Broadca
GetUtxoListOps, HistoryUtxoTx, HistoryUtxoTxMap, MatureUnspentList, RecentlySpentOutPointsGuard,
UtxoActivationParams, UtxoAddressFormat, UtxoCoinFields, UtxoCommonOps, UtxoFromLegacyReqErr,
UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom, UTXO_LOCK};
use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, FeeApproxStage,
FoundSwapTxSpend, HistorySyncState, IguanaPrivKey, MakerSwapTakerCoin, MarketCoinOps, MmCoin,
NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy,
use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput,
FeeApproxStage, FoundSwapTxSpend, HistorySyncState, IguanaPrivKey, MakerSwapTakerCoin, MarketCoinOps,
MmCoin, NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy,
PrivKeyPolicyNotAllowed, RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs,
RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs,
SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageError,
Expand Down Expand Up @@ -1220,19 +1220,18 @@ impl MarketCoinOps for Qrc20Coin {
utxo_common::send_raw_tx_bytes(&self.utxo, tx)
}

fn wait_for_confirmations(
&self,
tx: &[u8],
confirmations: u64,
requires_nota: bool,
wait_until: u64,
check_every: u64,
) -> Box<dyn Future<Item = (), Error = String> + Send> {
let tx: UtxoTx = try_fus!(deserialize(tx).map_err(|e| ERRL!("{:?}", e)));
fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
let tx: UtxoTx = try_fus!(deserialize(input.payment_tx.as_slice()).map_err(|e| ERRL!("{:?}", e)));
let selfi = self.clone();
let fut = async move {
selfi
.wait_for_confirmations_and_check_result(tx, confirmations, requires_nota, wait_until, check_every)
.wait_for_confirmations_and_check_result(
tx,
input.confirmations,
input.requires_nota,
input.wait_until,
input.check_every,
)
.await
};
Box::new(fut.boxed().compat())
Expand Down
35 changes: 24 additions & 11 deletions mm2src/coins/qrc20/qrc20_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,27 +260,40 @@ fn test_wait_for_confirmations_excepted() {
let requires_nota = false;
let wait_until = (now_ms() / 1000) + 1; // the transaction is mined already
let check_every = 1;
coin.wait_for_confirmations(&payment_tx, confirmations, requires_nota, wait_until, check_every)
.wait()
.unwrap();
let confirm_payment_input = ConfirmPaymentInput {
payment_tx,
confirmations,
requires_nota,
wait_until,
check_every,
};
coin.wait_for_confirmations(confirm_payment_input).wait().unwrap();

// tx_hash: ed53b97deb2ad76974c972cb084f6ba63bd9f16c91c4a39106a20c6d14599b2a
// `erc20Payment` contract call excepted
let payment_tx = hex::decode("01000000014c1411bac38ca25a2816342b019df81f503e1db75b25c6da618b08484dc2ff49010000006b483045022100da3e90fbcc45a94573c28213b36dc616630e3adfa42a7f16bdf917e8a76b954502206ad0830bb16e5c25466903ae7f749e291586726f1497ae9fc2e709c1b6cd1857012103693bff1b39e8b5a306810023c29b95397eb395530b106b1820ea235fd81d9ce9ffffffff040000000000000000625403a08601012844095ea7b3000000000000000000000000ba8b71f3544b93e2f681f996da519a98ace0107a000000000000000000000000000000000000000000000000000000000000000014d362e096e873eb7907e205fadc6175c6fec7bc44c20000000000000000625403a08601012844095ea7b3000000000000000000000000ba8b71f3544b93e2f681f996da519a98ace0107a000000000000000000000000000000000000000000000000000000000000000a14d362e096e873eb7907e205fadc6175c6fec7bc44c20000000000000000e35403a0860101284cc49b415b2a0a1a8b4af2762154115ced87e2424b3cb940c0181cc3c850523702f1ec298fef0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc44000000000000000000000000783cf0be521101942da509846ea476e683aad8324b6b2e5444c2639cc0fb7bcea5afba3f3cdce239000000000000000000000000000000000000000000000000000000000000000000000000000000005fa0fffb14ba8b71f3544b93e2f681f996da519a98ace0107ac2493d4a03000000001976a9149e032d4b0090a11dc40fe6c47601499a35d55fbb88acae2ea15f").unwrap();
let error = coin
.wait_for_confirmations(&payment_tx, confirmations, requires_nota, wait_until, check_every)
.wait()
.unwrap_err();
let confirm_payment_input = ConfirmPaymentInput {
payment_tx,
confirmations,
requires_nota,
wait_until,
check_every,
};
let error = coin.wait_for_confirmations(confirm_payment_input).wait().unwrap_err();
log!("error: {:?}", error);
assert!(error.contains("Contract call failed with an error: Revert"));

// tx_hash: aa992c028c07e239dbd2ff32bf67251f026929c644b4d02a469e351cb44abab7
// `receiverSpend` contract call excepted
let payment_tx = hex::decode("0100000007077ccb377a68fd6079503f856df4e553e337015f8419cd0f2a949c31db175df7050000006a473044022058097f54be31ae5af197f72e4410b33b22f29fad5b1a1cefb30ee45b3b3477dc02205c1098850fa2f2c1929c27af6261f83abce7682eb769f909dd09e9be5e0bd469012102aa32922f4b05cbc7384dd85b86021c98e4102f5da3df48bc516aa76f8119559affffffffc191895a431db3dccbf4f9d4b8cd8301124343e66275194ad734a77ffe56b95e030000006a4730440220491fed7954c6f43acc7226c337bb16ac71b38df50f55a819441d9b2b9e4a04b502201f95be6941b6619c0ca246e15adb090b82cd908f7c85108a1dcc02eafb7cc725012102aa32922f4b05cbc7384dd85b86021c98e4102f5da3df48bc516aa76f8119559afffffffff678de174fb81d3820df43a2c29945b08df4fb080deb8088ef11b3711c0fe8df020000006a473044022071d9c0ec57ab23360a4f73d0edfc2f67614b56f6d2e54387b39c3de1fa894c7d022030ea65d157784ff68cae9c9acb0dd626205073f478003b1cb1d0d581dcb27b1c012102aa32922f4b05cbc7384dd85b86021c98e4102f5da3df48bc516aa76f8119559affffffffe1ef8740ce51ed3172efea91a5e559b5fe63dc6fede8a9037ad47fbc38560b51040000006a47304402203f056dff0be1f24ed96c72904c9aac3ac964913d0c3228bfab3fa4bef7f22c060220658a121bf8f29d86c18ec1aee4460f363c0704d2f05cc9d7923e978e917f48ca012102aa32922f4b05cbc7384dd85b86021c98e4102f5da3df48bc516aa76f8119559affffffffe825dea61113bbd67dd35cbc9d88890ac222f55bf0201a7f9fb96592e0614d4d080000006b483045022100bb10f195c57c1eed9de3d9d9726484f839e25d83deb54cf2142df37099df6a8d02202a025182caaa5348350b410ee783180e9ce3ccac5e361eb50b162311e9d803f1012102aa32922f4b05cbc7384dd85b86021c98e4102f5da3df48bc516aa76f8119559affffffffe1ef8740ce51ed3172efea91a5e559b5fe63dc6fede8a9037ad47fbc38560b51060000006a47304402205550e0b4e1425f2f7a8645c6fd408ba0603cca5ca408202729041f5eab0b0cd202205c98fc8e91a37960d38f0104e81d3d48f737c4000ef45e2372c84d857455da34012102aa32922f4b05cbc7384dd85b86021c98e4102f5da3df48bc516aa76f8119559affffffffe825dea61113bbd67dd35cbc9d88890ac222f55bf0201a7f9fb96592e0614d4d060000006b483045022100b0d21cbb5d94b4995d9cb81e7440849dbe645416bca6d51bb5450e10753523220220299f105d573cdb785233699b5a9be8f907d9821a74cfd91fb72911a4a6e1bdb8012102aa32922f4b05cbc7384dd85b86021c98e4102f5da3df48bc516aa76f8119559affffffff020000000000000000c35403a0860101284ca402ed292be8b1d4904e8f1924bd7a2eb4d8085214c17af3d8d7574b2740a86b6296d343c00000000000000000000000000000000000000000000000000000000005f5e10028fcc0c5f6d9619d3c1f90af51e891d62333eb748c568f7da2a7734240d37d38000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc44000000000000000000000000d020b63f5a989776516bdc04d426ba118130c00214ba8b71f3544b93e2f681f996da519a98ace0107ac270630800000000001976a914fb7dad7ce97deecf50a4573a2bd7639c79bdc08588aca64aaa5f").unwrap();
let error = coin
.wait_for_confirmations(&payment_tx, confirmations, requires_nota, wait_until, check_every)
.wait()
.unwrap_err();
let confirm_payment_input = ConfirmPaymentInput {
payment_tx,
confirmations,
requires_nota,
wait_until,
check_every,
};
let error = coin.wait_for_confirmations(confirm_payment_input).wait().unwrap_err();
log!("error: {:?}", error);
assert!(error.contains("Contract call failed with an error: Revert"));
}
Expand Down
30 changes: 12 additions & 18 deletions mm2src/coins/solana.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, SwapOps, Trade
use crate::coin_errors::MyAddressError;
use crate::solana::solana_common::{lamports_to_sol, PrepareTransferData, SufficientBalanceError};
use crate::solana::spl::SplTokenInfo;
use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, FeeApproxStage, FoundSwapTxSpend,
MakerSwapTakerCoin, NegotiateSwapContractAddrErr, PaymentInstructions, PaymentInstructionsErr,
PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, RawTransactionFut, RawTransactionRequest, RefundError,
RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput,
SendPaymentArgs, SignatureResult, SpendPaymentArgs, TakerSwapMakerCoin, TradePreimageFut,
TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionFut, TransactionType,
TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs,
ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut,
ValidatePaymentInput, VerificationResult, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput,
WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult};
use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage,
FoundSwapTxSpend, MakerSwapTakerCoin, NegotiateSwapContractAddrErr, PaymentInstructions,
PaymentInstructionsErr, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, RawTransactionFut,
RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput,
SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureResult, SpendPaymentArgs,
TakerSwapMakerCoin, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionDetails,
TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult,
ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError,
ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WatcherSearchForSwapTxSpendInput,
WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest,
WithdrawResult};
use async_trait::async_trait;
use base58::ToBase58;
use bincode::{deserialize, serialize};
Expand Down Expand Up @@ -441,14 +442,7 @@ impl MarketCoinOps for SolanaCoin {
Box::new(fut.boxed().compat())
}

fn wait_for_confirmations(
&self,
_tx: &[u8],
_confirmations: u64,
_requires_nota: bool,
_wait_until: u64,
_check_every: u64,
) -> Box<dyn Future<Item = (), Error = String> + Send> {
fn wait_for_confirmations(&self, _input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
unimplemented!()
}

Expand Down
Loading