From 9e33e8b1d193e59b07c549c90f622af8b6a2a502 Mon Sep 17 00:00:00 2001 From: binarybaron <86064887+binarybaron@users.noreply.github.com> Date: Thu, 14 Dec 2023 17:22:55 +0100 Subject: [PATCH] Give feedback to user about state of monero refresh and retry if fails This commit changes the following behaviour in the refresh functionality of the monero wallet - Allows for multiple retries because in some cases users have experienced an issue where the wallet rpc returns `no connection to daemon` even though the daemon is available. I'm not 100% sure why this happens but retrying often fixes the issue - Attempt to print the current sync height while the wallet is syncing. This only works to some degree because the `monero-wallet-rpc` stops responding (or takes a long time to respond) while it's refreshing - The `monero-wallet-rpc` is started with the `--no-initial-sync` flag which ensures that as soon as it's started, it's ready to respond to requests --- swap/src/monero/wallet.rs | 68 +++++++++++++++++++++++++++++++++-- swap/src/monero/wallet_rpc.rs | 1 + swap/src/protocol/bob/swap.rs | 2 +- swap/tests/harness/mod.rs | 2 +- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/swap/src/monero/wallet.rs b/swap/src/monero/wallet.rs index 56fd8e60..3ba99aac 100644 --- a/swap/src/monero/wallet.rs +++ b/swap/src/monero/wallet.rs @@ -45,6 +45,7 @@ impl Wallet { pub async fn connect(client: wallet::Client, name: String, env_config: Config) -> Result { let main_address = monero::Address::from_str(client.get_address(0).await?.address.as_str())?; + Ok(Self { inner: Mutex::new(client), network: env_config.monero_network, @@ -144,7 +145,7 @@ impl Wallet { .await?; // Try to send all the funds from the generated wallet to the default wallet - match wallet.refresh().await { + match self.refresh(3).await { Ok(_) => match wallet.sweep_all(self.main_address.to_string()).await { Ok(sweep_all) => { for tx in sweep_all.tx_hash_list { @@ -261,8 +262,69 @@ impl Wallet { self.main_address } - pub async fn refresh(&self) -> Result { - Ok(self.inner.lock().await.refresh().await?) + pub async fn refresh(&self, max_attempts: usize) -> Result { + const GET_HEIGHT_INTERVAL: Duration = Duration::from_secs(5); + const RETRY_INTERVAL: Duration = Duration::from_secs(2); + + let inner = self.inner.lock().await; + + // Cloning this is relatively cheap because reqwest::Client is a wrapper around an Arc + let inner_clone = inner.clone(); + let wallet_name_clone = self.name.clone(); + + let refresh_task = tokio::task::spawn(async move { + loop { + let height = inner_clone.get_height().await; + + match height { + Err(error) => { + tracing::warn!(name = %wallet_name_clone, %error, "Failed to get current Monero wallet sync height"); + } + Ok(height) => { + tracing::debug!(name = %wallet_name_clone, current_sync_height = height.height, "Syncing Monero wallet"); + } + } + + tokio::time::sleep(GET_HEIGHT_INTERVAL).await; + } + }); + + let refresh_result = tokio::select! { + biased; + _ = refresh_task => { + unreachable!("Current sync height refresh task should never finish") + } + refresh_result = async { + for i in 1..=max_attempts { + tracing::info!(name = %self.name, attempt=i, "Syncing Monero wallet"); + + let result = inner.refresh().await; + + match result { + Ok(refreshed) => { + tracing::info!(name = %self.name, "Monero wallet synced"); + return Ok(refreshed); + } + Err(error) => { + let attempts_left = max_attempts - i; + tracing::warn!(attempt=i, %attempts_left, name = %self.name, %error, "Failed to sync Monero wallet"); + + if attempts_left == 0 { + return Err(error); + } + } + } + + tokio::time::sleep(RETRY_INTERVAL).await; + } + + unreachable!("Loop should always return before it breaks") + } => { + refresh_result + } + }; + + Ok(refresh_result?) } } diff --git a/swap/src/monero/wallet_rpc.rs b/swap/src/monero/wallet_rpc.rs index cecdd620..e2f019a3 100644 --- a/swap/src/monero/wallet_rpc.rs +++ b/swap/src/monero/wallet_rpc.rs @@ -309,6 +309,7 @@ impl WalletRpc { .arg("--disable-rpc-login") .arg("--wallet-dir") .arg(self.working_dir.join("monero-data")) + .arg("--no-initial-sync") .spawn()?; let stdout = child diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 66933a87..fa4cd8d3 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -247,7 +247,7 @@ async fn next_state( } // Ensure that the generated wallet is synced so we have a proper balance - monero_wallet.refresh().await?; + monero_wallet.refresh(3).await?; // Sweep (transfer all funds) to the given address let tx_hashes = monero_wallet.sweep_all(monero_receive_address).await?; diff --git a/swap/tests/harness/mod.rs b/swap/tests/harness/mod.rs index 4f3f5fee..e81c78d3 100644 --- a/swap/tests/harness/mod.rs +++ b/swap/tests/harness/mod.rs @@ -865,7 +865,7 @@ impl Wallet for monero::Wallet { type Amount = monero::Amount; async fn refresh(&self) -> Result<()> { - self.refresh().await?; + self.refresh(1).await?; Ok(()) }