feat(asb): Retry locking Monero (#143)

This commit is contained in:
binarybaron 2024-11-10 17:51:05 +01:00 committed by GitHub
parent e76ff3ac9c
commit 8c3adbf1ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 61 additions and 9 deletions

View file

@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
- ASB: We now retry indefinitely to lock Monero funds until the swap is cancelled. This fixes an issue where we would fail to lock Monero on the first try (e.g., due to the daemon not being fully synced) and would never try again, forcing the swap to be refunded.
- ASB + CLI: You can now use the `logs` command to retrieve logs stored in the past, redacting addresses and id's using `logs --redact`. - ASB + CLI: You can now use the `logs` command to retrieve logs stored in the past, redacting addresses and id's using `logs --redact`.
- ASB: The `--disable-timestamp` flag has been removed - ASB: The `--disable-timestamp` flag has been removed
- ASB: The `history` command can now be used while the asb is running. - ASB: The `history` command can now be used while the asb is running.

View file

@ -1,5 +1,7 @@
//! Run an XMR/BTC swap in the role of Alice. //! Run an XMR/BTC swap in the role of Alice.
//! Alice holds XMR and wishes receive BTC. //! Alice holds XMR and wishes receive BTC.
use std::time::Duration;
use crate::asb::{EventLoopHandle, LatestRate}; use crate::asb::{EventLoopHandle, LatestRate};
use crate::bitcoin::ExpiredTimelocks; use crate::bitcoin::ExpiredTimelocks;
use crate::env::Config; use crate::env::Config;
@ -111,23 +113,72 @@ where
} }
} }
AliceState::BtcLocked { state3 } => { AliceState::BtcLocked { state3 } => {
match state3.expired_timelocks(bitcoin_wallet).await? { // We will retry indefinitely to lock the Monero funds, until the swap is cancelled
ExpiredTimelocks::None { .. } => { // Sometimes locking the Monero can fail e.g due to the daemon not being fully synced
// Record the current monero wallet block height so we don't have to scan from let backoff = backoff::ExponentialBackoffBuilder::new()
// block 0 for scenarios where we create a refund wallet. .with_max_elapsed_time(None)
let monero_wallet_restore_blockheight = monero_wallet.block_height().await?; .with_max_interval(Duration::from_secs(60))
.build();
let transfer_proof = monero_wallet let transfer_proof = backoff::future::retry(backoff, || async {
.transfer(state3.lock_xmr_transfer_request()) // We check the status of the Bitcoin lock transaction
.await?; // If the swap is cancelled, there is no need to lock the Monero funds anymore
// because there is no way for the swap to succeed.
if !matches!(
state3.expired_timelocks(bitcoin_wallet).await?,
ExpiredTimelocks::None { .. }
) {
return Ok(None);
}
// Record the current monero wallet block height so we don't have to scan from
// block 0 for scenarios where we create a refund wallet.
let monero_wallet_restore_blockheight = monero_wallet
.block_height()
.await
.inspect_err(|e| {
tracing::warn!(
swap_id = %swap_id,
error = ?e,
"Failed to get Monero wallet block height while trying to lock XMR. We will retry."
)
})
.map_err(backoff::Error::transient)?;
// Lock the Monero
monero_wallet
.transfer(state3.lock_xmr_transfer_request())
.await
.map(|proof| Some((monero_wallet_restore_blockheight, proof)))
.inspect_err(|e| {
tracing::warn!(
swap_id = %swap_id,
error = ?e,
"Failed to lock Monero. Make sure your monero-wallet-rpc is connected to a synced daemon and enough funds are available. We will retry."
)
})
.map_err(backoff::Error::transient)
})
.await?;
match transfer_proof {
// If the transfer was successful, we transition to the next state
Some((monero_wallet_restore_blockheight, transfer_proof)) => {
AliceState::XmrLockTransactionSent { AliceState::XmrLockTransactionSent {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof, transfer_proof,
state3, state3,
} }
} }
_ => AliceState::SafelyAborted, // If we were not able to lock the Monero funds before the timelock expired,
// we can safely abort the swap because we did not lock any funds
None => {
tracing::info!(
swap_id = %swap_id,
"We did not manage to lock the Monero funds before the timelock expired. Aborting swap."
);
AliceState::SafelyAborted
}
} }
} }
AliceState::XmrLockTransactionSent { AliceState::XmrLockTransactionSent {