There seem to be two separate races.
For the abort test, fAbortRescan is reset after the “Rescan started...” log, so abortrescan() could set the flag before the rescan thread clears it. We could move the reset before the log.
For the “already rescanning” test, the log only proves the rescan started, not that it is still running. We could also wait until getwalletinfo()["scanning"] shows active progress before sending the second RPC.
It can make the test more robust.
<details>
<summary>diff</summary>
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index bb79dd471e..8bc8ef0b0e 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1881,10 +1881,10 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
std::unique_ptr<FastWalletRescanFilter> fast_rescan_filter;
if (chain().hasBlockFilterIndex(BlockFilterType::BASIC)) fast_rescan_filter = std::make_unique<FastWalletRescanFilter>(*this);
+ fAbortRescan = false;
WalletLogPrintf("Rescan started from block %s... (%s)\n", start_block.ToString(),
fast_rescan_filter ? "fast variant using block filters" : "slow variant inspecting all blocks");
- fAbortRescan = false;
ShowProgress(strprintf("[%s] %s", DisplayName(), _("Rescanning…")), 0); // show rescan progress in GUI as dialog or on splashscreen, if rescan required on startup (e.g. due to corruption)
uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
uint256 end_hash = tip_hash;
diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
index 929bc62a28..a13b86a851 100755
--- a/test/functional/wallet_importdescriptors.py
+++ b/test/functional/wallet_importdescriptors.py
@@ -128,6 +128,15 @@ class ImportDescriptorsTest(BitcoinTestFramework):
w_import = self.nodes[0].create_new_rpc_connection(mode="AUTHPROXY") / f"wallet/{wallet_name}"
+ def wait_for_rescan_to_advance(wallet_rpc, future):
+ def rescan_has_progress():
+ if future.done():
+ raise AssertionError("background importdescriptors rescan finished before the concurrent RPC could be tested")
+ scanning = wallet_rpc.getwalletinfo()["scanning"]
+ return scanning is not False and scanning["progress"] > 0
+
+ self.wait_until(rescan_has_progress, timeout=10)
+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as thread:
w_rescan = self.nodes[0].create_new_rpc_connection(mode="AUTHPROXY") / f"wallet/{wallet_name}"
# Use an xprv with timestamp=0 to trigger a slow full rescan that stays in-flight
@@ -137,8 +146,9 @@ class ImportDescriptorsTest(BitcoinTestFramework):
with self.nodes[0].busy_wait_for_debug_log(expected_msgs=[log_msg.encode("utf-8")], timeout=10):
rescanning = thread.submit(w_rescan.importdescriptors, slow_desc)
+ wait_for_rescan_to_advance(w_import, rescanning)
- # Rescan is confirmed started; WalletRescanReserver is held.
+ # Rescan has advanced and WalletRescanReserver is still held.
# importdescriptors on the same wallet must fail immediately.
assert_raises_rpc_error(-4, "Wallet is currently rescanning. Abort existing rescan or wait.",
w_import.importdescriptors,
@@ -161,10 +171,10 @@ class ImportDescriptorsTest(BitcoinTestFramework):
with self.nodes[0].busy_wait_for_debug_log(expected_msgs=[log_msg.encode("utf-8")], timeout=10):
importing = thread.submit(w_import.importdescriptors, descriptor)
-
- # busy_wait_for_debug_log only returns after the expected message has
- # appeared, so the rescan has definitely started; abort it now.
abort_rpc = self.nodes[0].create_new_rpc_connection(mode="AUTHPROXY") / f"wallet/{wallet_name}"
+ wait_for_rescan_to_advance(abort_rpc, importing)
+
+ # The rescan has advanced and is still in progress; abort it now.
abort_rpc.abortrescan()
try:
</details>