From 0e25cb86f0d2c7d82f1ffde1c3069f885549e731 Mon Sep 17 00:00:00 2001 From: Mohan <86064887+binarybaron@users.noreply.github.com> Date: Tue, 20 May 2025 14:21:39 +0200 Subject: [PATCH] fix(swap): Add retry logic for Bitcoin wallet sync (#333) --- CHANGELOG.md | 2 ++ swap/src/bitcoin/wallet.rs | 32 +++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aab41f86..f195ddac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- CLI + GUI + ASB: Retry the Bitcoin wallet sync up to 15 seconds to work around transient errors. + ## [1.1.0] - 2025-05-19 - GUI: Discourage swapping with makers running `< 1.1.0-rc.3` because the bdk upgrade introduced a breaking change. diff --git a/swap/src/bitcoin/wallet.rs b/swap/src/bitcoin/wallet.rs index 855eaf05..8c187f22 100644 --- a/swap/src/bitcoin/wallet.rs +++ b/swap/src/bitcoin/wallet.rs @@ -290,6 +290,9 @@ impl Wallet { /// The number of maximum chunks to use when syncing const SCAN_CHUNKS: u32 = 5; + /// Maximum time we are willing to spend retrying a wallet sync + const SYNC_MAX_ELAPSED_TIME: Duration = Duration::from_secs(15); + const WALLET_PARENT_DIR_NAME: &str = "wallet"; const WALLET_DIR_NAME: &str = "wallet-post-bdk-1.0"; const WALLET_FILE_NAME: &str = "wallet-db.sqlite"; @@ -863,9 +866,9 @@ impl Wallet { Ok(()) } - /// Sync the wallet with the blockchain - /// and emit progress events to the UI - pub async fn sync(&self) -> Result<()> { + /// Perform a single sync of the wallet with the blockchain + /// and emit progress events to the UI. + async fn sync_once(&self) -> Result<()> { let background_process_handle = self .tauri_handle .new_background_process_with_initial_progress( @@ -896,6 +899,29 @@ impl Wallet { Ok(()) } + /// Sync the wallet with the blockchain and emit progress events to the UI. + /// Retries the sync if it fails using an exponential backoff. + pub async fn sync(&self) -> Result<()> { + let backoff = backoff::ExponentialBackoffBuilder::new() + .with_max_elapsed_time(Some(Self::SYNC_MAX_ELAPSED_TIME)) + .with_max_interval(Duration::from_secs(1)) + .build(); + + backoff::future::retry_notify( + backoff, + || async { self.sync_once().await.map_err(backoff::Error::transient) }, + |err, wait_time: Duration| { + tracing::warn!( + ?err, + "Failed to sync Bitcoin wallet. We will retry in {} seconds", + wait_time.as_secs() + ); + }, + ) + .await + .context("Failed to sync Bitcoin wallet after retries") + } + /// Calculate the fee for a given transaction. /// /// Will fail if the transaction inputs are not owned by this wallet.