From 1b5c545e82fe3cf5027f16b43e2306aeb8d4ef9b Mon Sep 17 00:00:00 2001 From: rkrux Date: Wed, 21 May 2025 20:15:56 +0530 Subject: [PATCH] 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. --- src/wallet/wallet.cpp | 14 +++++++------- test/functional/wallet_reorgsrestore.py | 10 +++++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 36da4396106..38739d86cae 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3103,11 +3103,9 @@ bool CWallet::AttachChain(const std::shared_ptr& walletInstance, interf const std::optional tip_height = chain.getHeight(); if (tip_height) { - walletInstance->m_last_block_processed = chain.getBlockHash(*tip_height); - walletInstance->m_last_block_processed_height = *tip_height; + walletInstance->SetLastBlockProcessedInMem(*tip_height, chain.getBlockHash(*tip_height)); } else { - walletInstance->m_last_block_processed.SetNull(); - walletInstance->m_last_block_processed_height = -1; + walletInstance->SetLastBlockProcessedInMem(-1, uint256()); } if (tip_height && *tip_height != rescan_height) @@ -3172,7 +3170,7 @@ bool CWallet::AttachChain(const std::shared_ptr& walletInstance, interf return false; } // 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. walletInstance->SetLastBlockProcessed(*scan_res.last_scanned_height, scan_res.last_scanned_block); } @@ -4427,8 +4425,10 @@ void CWallet::WriteBestBlock() const CBlockLocator loc; chain().findBlock(m_last_block_processed, FoundBlock().locator(loc)); - WalletBatch batch(GetDatabase()); - batch.WriteBestBlock(loc); + if (!loc.IsNull()) { + WalletBatch batch(GetDatabase()); + batch.WriteBestBlock(loc); + } } } diff --git a/test/functional/wallet_reorgsrestore.py b/test/functional/wallet_reorgsrestore.py index 1567fb30d0e..d51ee810376 100755 --- a/test/functional/wallet_reorgsrestore.py +++ b/test/functional/wallet_reorgsrestore.py @@ -90,7 +90,7 @@ class ReorgsRestoreTest(BitcoinTestFramework): assert_equal(wallet0.gettransaction(descendant_tx_id)['details'][0]['abandoned'], True) 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] # Receive coinbase reward on a new wallet node.createwallet(wallet_name="reorg_crash", load_on_startup=True) @@ -104,6 +104,7 @@ class ReorgsRestoreTest(BitcoinTestFramework): # Disconnect tip and sync wallet state tip = wallet.getbestblockhash() + tip_height = wallet.getblockstats(tip)["height"] wallet.invalidateblock(tip) wallet.syncwithvalidationinterfacequeue() @@ -116,14 +117,17 @@ class ReorgsRestoreTest(BitcoinTestFramework): node.kill_process() # 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) # 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 - # coinbase. This should be rescanned and now un-abandoned. + # coinbase. This was rescanned and now un-abandoned. wallet = node.get_wallet_rpc("reorg_crash") 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. # Ensure this is no longer the case by simulating a new reorg.