refactor: Compute work from headers without CBlockIndex
What changed, and why it matters
This is a code cleanup change. It makes the way Bitcoin Core calculates 'proof of work' from block headers simpler and slightly more efficient, without changing the actual math or security rules. There is no indication this fixes a vulnerability.
No security action required. Treat as ordinary code-quality/maintenance refactor.
Security signals we found
No security-relevant behavioral change visible in the diff
Refactor only: same nBits -> target -> work calculation
Eliminates temporary CBlockIndex construction, which may reduce object lifetime/initialization risk but is not presented as a security fix
No bounds checks, validation rules, or anti-DoS thresholds were altered
Evidence from the diff
The commit refactors proof-of-work computation so that callers can compute work directly from a 32-bit nBits value (or from a CBlockHeader) instead of first constructing a temporary CBlockIndex object. The existing GetBlockProof(const CBlockIndex&) is preserved as a thin inline wrapper around the new GetBitsProof(uint32_t). All call sites that previously built dummy CBlockIndex objects now use the header-based overload. The arithmetic and consensus behavior are unchanged.
Changed components
src/chain.cppsrc/chain.hsrc/headerssync.cppsrc/net_processing.cppsrc/validation.cppInspect captured patch +16 / −9
diff --git a/src/chain.cpp b/src/chain.cpp
index 3dd22634..622e018d 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -124,12 +124,12 @@ void CBlockIndex::BuildSkip()
pskip = pprev->GetAncestor(GetSkipHeight(nHeight));
}
-arith_uint256 GetBlockProof(const CBlockIndex& block)
+arith_uint256 GetBitsProof(uint32_t bits)
{
arith_uint256 bnTarget;
bool fNegative;
bool fOverflow;
- bnTarget.SetCompact(block.nBits, &fNegative, &fOverflow);
+ bnTarget.SetCompact(bits, &fNegative, &fOverflow);
if (fNegative || fOverflow || bnTarget == 0)
return 0;
// We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256
diff --git a/src/chain.h b/src/chain.h
index 2c865265..3b8a672d 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -348,7 +348,15 @@ protected:
CBlockIndex& operator=(CBlockIndex&&) = delete;
};
-arith_uint256 GetBlockProof(const CBlockIndex& block);
+/** Compute how much work an nBits value corresponds to. */
+arith_uint256 GetBitsProof(uint32_t bits);
+
+/** Compute how much work a block index entry corresponds to. */
+inline arith_uint256 GetBlockProof(const CBlockIndex& block) { return GetBitsProof(block.nBits); }
+
+/** Compute how much work a block header corresponds to. */
+inline arith_uint256 GetBlockProof(const CBlockHeader& header) { return GetBitsProof(header.nBits); }
+
/** Return the time it would take to redo the work difference between from and to, assuming the current hashrate corresponds to the difficulty at tip, in seconds. */
int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params&);
/** Find the forking point between two chain tips. */
diff --git a/src/headerssync.cpp b/src/headerssync.cpp
index e52a8cdc..fc73d403 100644
--- a/src/headerssync.cpp
+++ b/src/headerssync.cpp
@@ -202,7 +202,7 @@ bool HeadersSyncState::ValidateAndProcessSingleHeader(const CBlockHeader& curren
}
}
- m_current_chain_work += GetBlockProof(CBlockIndex(current));
+ m_current_chain_work += GetBlockProof(current);
m_last_header_received = current;
m_current_height = next_height;
@@ -238,7 +238,7 @@ bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& he
}
// Track work on the redownloaded chain
- m_redownload_chain_work += GetBlockProof(CBlockIndex(header));
+ m_redownload_chain_work += GetBlockProof(header);
if (m_redownload_chain_work >= m_minimum_required_work) {
m_process_all_remaining_headers = true;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 5c8d92f0..5fd11d3c 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -4348,7 +4348,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
MaybeSendGetHeaders(pfrom, GetLocator(m_chainman.m_best_header), *peer);
}
return;
- } else if (prev_block->nChainWork + CalculateClaimedHeadersWork({{cmpctblock.header}}) < GetAntiDoSWorkThreshold()) {
+ } else if (prev_block->nChainWork + GetBlockProof(cmpctblock.header) < GetAntiDoSWorkThreshold()) {
// If we get a low-work header in a compact block, we can ignore it.
LogDebug(BCLog::NET, "Ignoring low-work compact block from peer %d\n", pfrom.GetId());
return;
@@ -4666,7 +4666,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
// Check claimed work on this block against our anti-dos thresholds.
- if (prev_block && prev_block->nChainWork + CalculateClaimedHeadersWork({{pblock->GetBlockHeader()}}) >= GetAntiDoSWorkThreshold()) {
+ if (prev_block && prev_block->nChainWork + GetBlockProof(pblock->GetBlockHeader()) >= GetAntiDoSWorkThreshold()) {
min_pow_checked = true;
}
}
diff --git a/src/validation.cpp b/src/validation.cpp
index af523b06..ecc51745 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -4171,8 +4171,7 @@ arith_uint256 CalculateClaimedHeadersWork(std::span<const CBlockHeader> headers)
{
arith_uint256 total_work{0};
for (const CBlockHeader& header : headers) {
- CBlockIndex dummy(header);
- total_work += GetBlockProof(dummy);
+ total_work += GetBlockProof(header);
}
return total_work;
}
Why this scored 20/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.