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
This commit is contained in:
binarybaron 2023-12-14 17:22:55 +01:00
parent dc1ad6df7d
commit 9e33e8b1d1
4 changed files with 68 additions and 5 deletions

View File

@ -45,6 +45,7 @@ impl Wallet {
pub async fn connect(client: wallet::Client, name: String, env_config: Config) -> Result<Self> {
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<Refreshed> {
Ok(self.inner.lock().await.refresh().await?)
pub async fn refresh(&self, max_attempts: usize) -> Result<Refreshed> {
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?)
}
}

View File

@ -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

View File

@ -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?;

View File

@ -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(())
}