index, refactor: Append blocks to coinstatsindex without db read
What changed, and why it matters
This is a small internal cleanup in Bitcoin Core's coin-statistics index. It replaces a database read of the previous block's hash with a member variable that is already kept in memory, making block appending slightly faster and simpler. There is no obvious security bug introduced, and the commit message frames it as a refactor.
No security action required. Treat as normal code-review item; verify that m_current_block_hash is correctly maintained across all state transitions (init, append, revert) and that the fallback lookup still triggers on reorg.
Security signals we found
Change removes a database read and substitutes an in-memory field for a chain-consistency check
Fallback database lookup for the expected previous block hash is retained
No new external inputs, no memory allocations from untrusted data, no cryptographic operations changed
Commit title and message describe the change as a refactor
Evidence from the diff
CoinStatsIndex::CustomAppend previously read the previous height’s DBVal from LevelDB just to obtain its block hash and compare it against block.prev_hash. The patch introduces m_current_block_hash, updates it on init, append, and revert, and uses it for that consistency check instead. The fallback DBHashKey lookup for the expected previous block remains. This is a performance/refactoring change; the previous-block validation logic is preserved, only the source of the ‘current’ hash changes from disk to memory.
Changed components
src/index/coinstatsindex.cppsrc/index/coinstatsindex.hInspect captured patch +9 / −7
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index bb4be90b..e4daf9ef 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -137,16 +137,12 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
// Ignore genesis block
if (block.height > 0) {
- std::pair<uint256, DBVal> read_out;
- if (!m_db->Read(DBHeightKey(block.height - 1), read_out)) {
- return false;
- }
-
uint256 expected_block_hash{*Assert(block.prev_hash)};
- if (read_out.first != expected_block_hash) {
+ if (m_current_block_hash != expected_block_hash) {
LogWarning("previous block header belongs to unexpected block %s; expected %s",
- read_out.first.ToString(), expected_block_hash.ToString());
+ m_current_block_hash.ToString(), expected_block_hash.ToString());
+ std::pair<uint256, DBVal> read_out;
if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
LogError("previous block header not found; expected %s",
expected_block_hash.ToString());
@@ -240,6 +236,8 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
m_muhash.Finalize(out);
value.second.muhash = out;
+ m_current_block_hash = block.hash;
+
// Intentionally do not update DB_MUHASH here so it stays in sync with
// DB_BEST_BLOCK, and the index is not corrupted if there is an unclean shutdown.
return m_db->Write(DBHeightKey(block.height), value);
@@ -373,6 +371,7 @@ bool CoinStatsIndex::CustomInit(const std::optional<interfaces::BlockRef>& block
m_total_unspendables_bip30 = entry.total_unspendables_bip30;
m_total_unspendables_scripts = entry.total_unspendables_scripts;
m_total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
+ m_current_block_hash = block->hash;
}
return true;
@@ -466,6 +465,7 @@ bool CoinStatsIndex::RevertBlock(const interfaces::BlockInfo& block)
m_total_unspendables_bip30 = read_out.second.total_unspendables_bip30;
m_total_unspendables_scripts = read_out.second.total_unspendables_scripts;
m_total_unspendables_unclaimed_rewards = read_out.second.total_unspendables_unclaimed_rewards;
+ m_current_block_hash = *block.prev_hash;
return true;
}
diff --git a/src/index/coinstatsindex.h b/src/index/coinstatsindex.h
index 5dcbc186..7e48f4c4 100644
--- a/src/index/coinstatsindex.h
+++ b/src/index/coinstatsindex.h
@@ -38,6 +38,8 @@ private:
CAmount m_total_unspendables_scripts{0};
CAmount m_total_unspendables_unclaimed_rewards{0};
+ uint256 m_current_block_hash{};
+
[[nodiscard]] bool RevertBlock(const interfaces::BlockInfo& block);
bool AllowPrune() const override { return true; }
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.