Skip to content

Monero: let the user close the rescan dialog and never leak the refresh lock - #1490

Closed
sneurlax wants to merge 4 commits into
stagingfrom
fix/monero-rescan-dialog
Closed

sneurlax wants to merge 4 commits into
stagingfrom
fix/monero-rescan-dialog

Conversation

@sneurlax

@sneurlax sneurlax commented Nov 1, 2026

Copy link
Copy Markdown
Member

Tester report: on desktop, after starting a Monero rescan the exit button is not clickable.

Cause

_attemptRescan showed a non-dismissable RescanningDialog over the Network dialog until recover(isRescan: true) returned, and recover first waits on refreshMutex. refresh() took that lock without try/finally, so a throw (including the Tor/clearnet mismatch guard) or a hang in startSyncing held it indefinitely and the dialog never went away. The tester's log shows refresh() stuck right after Windows Defender quarantined monero_libwallet2_api_c.dll (Taiga #103).

Changes

  • rescanning_dialog.dart: a rescansInBackground flag. When set, the dialog says the rescan continues in the background, has a Close button, and back pops it.
  • wallet_network_settings_view.dart: sets the flag for Cryptonote and Epic wallets, tracks whether the dialog is still open, and only pops it if it is.
  • lib_monero_wallet.dart, lib_wownero_wallet.dart, lib_salvium_wallet.dart: refresh() releases the lock in a finally.
  • Monero only: highestPercentCached is reset after rescanBlockchain returns. Monero's call returns as soon as the async rescan starts, and resetting before it let a stale ~99.8% reading land after the reset and freeze the progress bar. Wownero and Salvium run the whole rescan in a blocking isolate call, so they keep the old order (awaiting would hold the lock for the whole rescan); the same race can still happen there.

Tested

  • Full flutter test: 687 passed, 8 skipped, 0 failed.
  • Linux desktop, Monero rescan with the run-stack-wallet skill: Close works mid-rescan, the Network dialog's X works afterwards, and sync reaches Synchronized. Mobile layout: Close works during a heavy rescan. A BTC wallet still gets the old dialog with no Close, as intended.
  • Earlier, with a simulated stuck refresh(): the old build traps the user and the new one lets both dialogs close.

Their rescanBlockchain runs the whole blocking rescan in an isolate, so
awaiting it held the refresh lock and delayed startSyncing until the
rescan finished. The Monero call returns once the async rescan starts,
so only Monero keeps the reordered reset.
@sneurlax sneurlax closed this Nov 2, 2026
sneurlax added a commit to sneurlax/stack_wallet that referenced this pull request Nov 10, 2026
refresh() in the Monero, Wownero and Salvium wallets acquires refreshMutex
and released it only on the success path. Anything thrown after the acquire
(the Tor and clearnet mismatch check, or wallet being null because open()
failed) left the lock held for the rest of the session. Later refreshes
returned early on the isLocked check and recover()'s refreshMutex.protect
waited forever, which is the stuck rescan dialog in stack-wallet cypherstack#103.

The body now runs under refreshMutex.protect, which releases in a finally
and rethrows, the same way the other wallets and recover() in these files
already use it. That makes the release-at-100% workaround in onSyncingUpdate
redundant, so it is gone from all three wallets. In the Monero wallet
highestPercentCached is reset on both sides of rescanBlockchain: before it,
so a throw leaves the guard at zero, and after it, so a progress reading
from before the rescan cannot be ordered after the reset. Wownero and
Salvium call rescanBlockchain unawaited, so the second reset would change
nothing there and they keep their order.

This is the wallet-layer part of the closed PR cypherstack#1490, taken on its own.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant