wallet, test: best block locator matches scan state follow-ups

Few follows-ups from #30221: Use `SetLastBlockProcessedInMem` more in
`AttachChain`, add not null locator check in `WriteBestBlock`. Add log
and few assertions in `wallet_reorgstore` test.
This commit is contained in:
rkrux committed 2025-07-02 14:30:52 +05:30
1 parent fa33592898
commit 1b5c545e82
2 files changed
+14 -10

No files matched your search

+7 -7
View File
@@ -3103,11 +3103,9 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
const std::optional<int> tip_height = chain.getHeight(); const std::optional<int> tip_height = chain.getHeight();
if (tip_height) { if (tip_height) {
walletInstance->m_last_block_processed = chain.getBlockHash(*tip_height); walletInstance->SetLastBlockProcessedInMem(*tip_height, chain.getBlockHash(*tip_height));
walletInstance->m_last_block_processed_height = *tip_height;
} else { } else {
walletInstance->m_last_block_processed.SetNull(); walletInstance->SetLastBlockProcessedInMem(-1, uint256());
walletInstance->m_last_block_processed_height = -1;
} }
if (tip_height && *tip_height != rescan_height) if (tip_height && *tip_height != rescan_height)
@@ -3172,7 +3170,7 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
return false; return false;
} }
// Set and update the best block record // Set and update the best block record
// Set last block scanned as the last block processed as it may be different in case the case of a reorg. // Set last block scanned as the last block processed as it may be different in case of a reorg.
// Also save the best block locator because rescanning only updates it intermittently. // Also save the best block locator because rescanning only updates it intermittently.
walletInstance->SetLastBlockProcessed(*scan_res.last_scanned_height, scan_res.last_scanned_block); walletInstance->SetLastBlockProcessed(*scan_res.last_scanned_height, scan_res.last_scanned_block);
} }
@@ -4427,8 +4425,10 @@ void CWallet::WriteBestBlock() const
CBlockLocator loc; CBlockLocator loc;
chain().findBlock(m_last_block_processed, FoundBlock().locator(loc)); chain().findBlock(m_last_block_processed, FoundBlock().locator(loc));
WalletBatch batch(GetDatabase()); if (!loc.IsNull()) {
batch.WriteBestBlock(loc); WalletBatch batch(GetDatabase());
batch.WriteBestBlock(loc);
}
} }
} }
+7 -3
View File
@@ -90,7 +90,7 @@ class ReorgsRestoreTest(BitcoinTestFramework):
assert_equal(wallet0.gettransaction(descendant_tx_id)['details'][0]['abandoned'], True) assert_equal(wallet0.gettransaction(descendant_tx_id)['details'][0]['abandoned'], True)
def test_reorg_handling_during_unclean_shutdown(self): def test_reorg_handling_during_unclean_shutdown(self):
self.log.info("Test that wallet doesn't crash due to a duplicate block disconnection event after an unclean shutdown") self.log.info("Test that wallet transactions are un-abandoned in case of temporarily invalidated blocks and wallet doesn't crash due to a duplicate block disconnection event after an unclean shutdown")
node = self.nodes[0] node = self.nodes[0]
# Receive coinbase reward on a new wallet # Receive coinbase reward on a new wallet
node.createwallet(wallet_name="reorg_crash", load_on_startup=True) node.createwallet(wallet_name="reorg_crash", load_on_startup=True)
@@ -104,6 +104,7 @@ class ReorgsRestoreTest(BitcoinTestFramework):
# Disconnect tip and sync wallet state # Disconnect tip and sync wallet state
tip = wallet.getbestblockhash() tip = wallet.getbestblockhash()
tip_height = wallet.getblockstats(tip)["height"]
wallet.invalidateblock(tip) wallet.invalidateblock(tip)
wallet.syncwithvalidationinterfacequeue() wallet.syncwithvalidationinterfacequeue()
@@ -116,14 +117,17 @@ class ReorgsRestoreTest(BitcoinTestFramework):
node.kill_process() node.kill_process()
# Restart the node and confirm that it has not persisted the last chain state changes to disk # Restart the node and confirm that it has not persisted the last chain state changes to disk
self.start_node(0) # that leads to a rescan by the wallet
with self.nodes[0].assert_debug_log(expected_msgs=[f"Rescanning last 1 blocks (from block {tip_height - 1})...\n"]):
self.start_node(0)
assert_equal(node.getbestblockhash(), tip) assert_equal(node.getbestblockhash(), tip)
# After disconnecting the block, the wallet should record the new best block. # After disconnecting the block, the wallet should record the new best block.
# Upon reload after the crash, since the chainstate was not flushed, the tip contains the previously abandoned # Upon reload after the crash, since the chainstate was not flushed, the tip contains the previously abandoned
# coinbase. This should be rescanned and now un-abandoned. # coinbase. This was rescanned and now un-abandoned.
wallet = node.get_wallet_rpc("reorg_crash") wallet = node.get_wallet_rpc("reorg_crash")
assert_equal(wallet.gettransaction(coinbase_tx_id)['details'][0]['abandoned'], False) assert_equal(wallet.gettransaction(coinbase_tx_id)['details'][0]['abandoned'], False)
assert_greater_than(wallet.getbalances()["mine"]["immature"], 0)
# Previously, a bug caused the node to crash if two block disconnection events occurred consecutively. # Previously, a bug caused the node to crash if two block disconnection events occurred consecutively.
# Ensure this is no longer the case by simulating a new reorg. # Ensure this is no longer the case by simulating a new reorg.