Skip to content

Commit 97dcc6d

Browse files
UdjinM6codex
andcommitted
fix: address review suggestions
- remove dead spent-index declarations from validation paths - harden async index disconnect rewind handling for stale-branch notifications - make rewind failures trigger fatal index shutdown handling - refresh getaddressbalance maturity height after address index queries - apply address index rewind DB updates atomically - clean up format-string and logging lint handling around FatalError and wrapped LogPrintf calls - make timestamp index GetSerializeSize helpers static - fix a typo in test/util/data/non-backported.txt Co-authored-by: OpenAI Codex <codex@openai.com>
1 parent 331335e commit 97dcc6d

12 files changed

Lines changed: 77 additions & 35 deletions

src/index/addressindex.cpp

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <index/addressindex_util.h>
1111
#include <logging.h>
1212
#include <node/blockstorage.h>
13+
#include <tinyformat.h>
1314
#include <undo.h>
1415
#include <util/system.h>
1516

@@ -138,6 +139,26 @@ bool AddressIndex::DB::UpdateAddressUnspentIndex(const std::vector<CAddressUnspe
138139
return CDBWrapper::WriteBatch(batch);
139140
}
140141

142+
bool AddressIndex::DB::RewindBatch(const std::vector<CAddressIndexEntry>& address_entries,
143+
const std::vector<CAddressUnspentIndexEntry>& unspent_entries)
144+
{
145+
CDBBatch batch(*this);
146+
147+
for (const auto& [key, _] : address_entries) {
148+
batch.Erase(std::make_pair(DB_ADDRESSINDEX, key));
149+
}
150+
151+
for (const auto& [key, value] : unspent_entries) {
152+
if (value.IsNull()) {
153+
batch.Erase(std::make_pair(DB_ADDRESSUNSPENTINDEX, key));
154+
} else {
155+
batch.Write(std::make_pair(DB_ADDRESSUNSPENTINDEX, key), value);
156+
}
157+
}
158+
159+
return CDBWrapper::WriteBatch(batch);
160+
}
161+
141162
AddressIndex::AddressIndex(size_t n_cache_size, bool f_memory, bool f_wipe) :
142163
m_db(std::make_unique<AddressIndex::DB>(n_cache_size, f_memory, f_wipe))
143164
{
@@ -349,13 +370,9 @@ bool AddressIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new
349370
}
350371
}
351372

352-
// Erase address history entries and update unspent index
353-
if (!m_db->EraseAddressIndex(addressIndex)) {
354-
return error("%s: Failed to erase address index during rewind", __func__);
355-
}
356-
357-
if (!m_db->UpdateAddressUnspentIndex(addressUnspentIndex)) {
358-
return error("%s: Failed to update address unspent index during rewind", __func__);
373+
// Apply both rewind updates in a single batch to avoid leaving the index half-rewound.
374+
if (!m_db->RewindBatch(addressIndex, addressUnspentIndex)) {
375+
return error("%s: Failed to apply address index rewind batch", __func__);
359376
}
360377
}
361378

@@ -369,10 +386,16 @@ void AddressIndex::BlockDisconnected(const std::shared_ptr<const CBlock>& block,
369386
// to remove this block's data
370387
const CBlockIndex* best_block_index = CurrentIndex();
371388

372-
// Only rewind if we have this block indexed
389+
// Ignore stale-branch disconnect notifications that do not connect to the indexed chain.
373390
if (best_block_index && best_block_index->nHeight >= pindex->nHeight && pindex->pprev) {
391+
if (best_block_index->GetAncestor(pindex->nHeight - 1) != pindex->pprev) {
392+
LogPrintf("%s: WARNING: Block %s does not disconnect from an ancestor of " /* Continued */
393+
"known best chain (tip=%s); not updating index\n",
394+
__func__, pindex->GetBlockHash().ToString(), best_block_index->GetBlockHash().ToString());
395+
return;
396+
}
374397
if (!Rewind(best_block_index, pindex->pprev)) {
375-
error("%s: Failed to rewind %s to previous block after disconnect", __func__, GetName());
398+
FatalError("%s: Failed to rewind %s to previous block after disconnect", __func__, GetName());
376399
}
377400
}
378401
}

src/index/addressindex.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ class AddressIndex final : public BaseIndex
5252

5353
/// Update address unspent index (handles both adds and deletes)
5454
bool UpdateAddressUnspentIndex(const std::vector<CAddressUnspentIndexEntry>& entries);
55+
56+
/// Atomically erase address history entries and update unspent outputs during rewind.
57+
bool RewindBatch(const std::vector<CAddressIndexEntry>& address_entries,
58+
const std::vector<CAddressUnspentIndexEntry>& unspent_entries);
5559
};
5660

5761
/// Override to return false - we need undo data

src/index/base.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@ constexpr uint8_t DB_BEST_BLOCK{'B'};
2323
constexpr auto SYNC_LOG_INTERVAL{30s};
2424
constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s};
2525

26-
template <typename... Args>
27-
static void FatalError(const char* fmt, const Args&... args)
26+
void BaseIndex::FatalErrorImpl(const std::string& message)
2827
{
29-
std::string strMessage = tfm::format(fmt, args...);
30-
SetMiscWarning(Untranslated(strMessage));
31-
LogPrintf("*** %s\n", strMessage);
28+
SetMiscWarning(Untranslated(message));
29+
LogPrintf("*** %s\n", message);
3230
AbortError(_("A fatal internal error occurred, see debug.log for details"));
3331
StartShutdown();
3432
}

src/index/base.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define BITCOIN_INDEX_BASE_H
77

88
#include <dbwrapper.h>
9+
#include <tinyformat.h>
910
#include <util/threadinterrupt.h>
1011
#include <validationinterface.h>
1112

@@ -112,6 +113,15 @@ class BaseIndex : public CValidationInterface
112113
/// Get the name of the index for display in logs.
113114
virtual const char* GetName() const = 0;
114115

116+
/// Trigger a fatal index error and initiate shutdown.
117+
static void FatalErrorImpl(const std::string& message);
118+
119+
template <typename... Args>
120+
void FatalError(const char* fmt, const Args&... args) const
121+
{
122+
FatalErrorImpl(tfm::format(fmt, args...));
123+
}
124+
115125
/// Update the internal best block index as well as the prune lock.
116126
void SetBestBlockIndex(const CBlockIndex* block);
117127

src/index/spentindex.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <primitives/block.h>
1313
#include <primitives/transaction.h>
1414
#include <script/script.h>
15+
#include <tinyformat.h>
1516
#include <undo.h>
1617
#include <util/system.h>
1718

@@ -151,10 +152,16 @@ void SpentIndex::BlockDisconnected(const std::shared_ptr<const CBlock>& block, c
151152
// to remove this block's data
152153
const CBlockIndex* best_block_index = CurrentIndex();
153154

154-
// Only rewind if we have this block indexed
155+
// Ignore stale-branch disconnect notifications that do not connect to the indexed chain.
155156
if (best_block_index && best_block_index->nHeight >= pindex->nHeight && pindex->pprev) {
157+
if (best_block_index->GetAncestor(pindex->nHeight - 1) != pindex->pprev) {
158+
LogPrintf("%s: WARNING: Block %s does not disconnect from an ancestor of " /* Continued */
159+
"known best chain (tip=%s); not updating index\n",
160+
__func__, pindex->GetBlockHash().ToString(), best_block_index->GetBlockHash().ToString());
161+
return;
162+
}
156163
if (!Rewind(best_block_index, pindex->pprev)) {
157-
error("%s: Failed to rewind %s to previous block after disconnect", __func__, GetName());
164+
FatalError("%s: Failed to rewind %s to previous block after disconnect", __func__, GetName());
158165
}
159166
}
160167
}

src/index/timestampindex.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <chain.h>
88
#include <logging.h>
9+
#include <tinyformat.h>
910
#include <util/system.h>
1011

1112
constexpr uint8_t DB_TIMESTAMPINDEX{'s'};
@@ -93,10 +94,16 @@ void TimestampIndex::BlockDisconnected(const std::shared_ptr<const CBlock>& bloc
9394
// to remove this block's data
9495
const CBlockIndex* best_block_index = CurrentIndex();
9596

96-
// Only rewind if we have this block indexed and it's not the genesis block
97+
// Ignore stale-branch disconnect notifications that do not connect to the indexed chain.
9798
if (best_block_index && best_block_index->nHeight >= pindex->nHeight && pindex->pprev) {
99+
if (best_block_index->GetAncestor(pindex->nHeight - 1) != pindex->pprev) {
100+
LogPrintf("%s: WARNING: Block %s does not disconnect from an ancestor of " /* Continued */
101+
"known best chain (tip=%s); not updating index\n",
102+
__func__, pindex->GetBlockHash().ToString(), best_block_index->GetBlockHash().ToString());
103+
return;
104+
}
98105
if (!Rewind(best_block_index, pindex->pprev)) {
99-
error("%s: Failed to rewind %s to previous block after disconnect", __func__, GetName());
106+
FatalError("%s: Failed to rewind %s to previous block after disconnect", __func__, GetName());
100107
}
101108
}
102109
}

src/index/timestampindex_types.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct CTimestampIndexIteratorKey {
2222

2323
void SetNull() { m_time = 0; }
2424

25-
size_t GetSerializeSize(int nType, int nVersion) const { return 4; }
25+
static size_t GetSerializeSize(int nType, int nVersion) { return 4; }
2626

2727
template <typename Stream>
2828
void Serialize(Stream& s) const
@@ -54,7 +54,7 @@ struct CTimestampIndexKey {
5454
m_block_hash.SetNull();
5555
}
5656

57-
size_t GetSerializeSize(int nType, int nVersion) const { return 36; }
57+
static size_t GetSerializeSize(int nType, int nVersion) { return 36; }
5858

5959
template <typename Stream>
6060
void Serialize(Stream& s) const

src/rpc/node.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -666,13 +666,11 @@ static RPCHelpMan getaddressbalance()
666666
throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
667667
}
668668

669-
// Check sync status first to return a clear error message
670-
// Use the index's best block height instead of chain tip to avoid inconsistency
669+
// Check sync status first to return a clear error message.
671670
const IndexSummary summary = g_addressindex->GetSummary();
672671
if (!summary.synced) {
673672
throw JSONRPCError(RPC_MISC_ERROR, strprintf("Address index is syncing. Current height: %d", summary.best_block_height));
674673
}
675-
int nHeight = summary.best_block_height;
676674

677675
for (const auto& address : addresses) {
678676
if (!g_addressindex->GetAddressIndex(address.first, address.second, addressIndex,
@@ -681,6 +679,10 @@ static RPCHelpMan getaddressbalance()
681679
}
682680
}
683681

682+
// Refresh the indexed height after the query so maturity checks are based on the same
683+
// index state that produced the returned entries.
684+
const int nHeight = g_addressindex->GetSummary().best_block_height;
685+
684686

685687
CAmount balance = 0;
686688
CAmount balance_spendable = 0;

src/validation.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2008,8 +2008,6 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
20082008
return DISCONNECT_FAILED;
20092009
}
20102010

2011-
std::vector<CSpentIndexEntry> spentIndex;
2012-
20132011
std::optional<MNListUpdates> mnlist_updates_opt{std::nullopt};
20142012
if (!m_chain_helper->special_tx->UndoSpecialTxsInBlock(block, pindex, mnlist_updates_opt)) {
20152013
error("DisconnectBlock(): UndoSpecialTxsInBlock failed");
@@ -2354,8 +2352,6 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
23542352
int nInputs = 0;
23552353
unsigned int nSigOps = 0;
23562354
blockundo.vtxundo.reserve(block.vtx.size() - 1);
2357-
std::vector<CSpentIndexEntry> spentIndex;
2358-
23592355
bool fDIP0001Active_context = DeploymentActiveAt(*pindex, m_params.GetConsensus(), Consensus::DEPLOYMENT_DIP0001);
23602356

23612357
// MUST process special txes before updating UTXO to ensure consistency between mempool and block processing
@@ -4582,8 +4578,6 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i
45824578
pindex->GetBlockHash().ToString(), state.ToString());
45834579
}
45844580

4585-
std::vector<CSpentIndexEntry> spentIndex;
4586-
45874581
for (size_t i = 0; i < block.vtx.size(); i++) {
45884582
const CTransactionRef& tx = block.vtx[i];
45894583

src/validation.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,8 +1069,6 @@ class ChainstateManager
10691069

10701070
//! Load the block tree and coins database from disk, initializing state if we're running with -reindex
10711071
bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
1072-
//! Initialize additional indexes and store their flags to disk
1073-
void InitAdditionalIndexes() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
10741072

10751073
//! Check to see if caches are out of balance and if so, call
10761074
//! ResizeCoinsCaches() as needed.

0 commit comments

Comments
 (0)