#34254 validation: Prevent duplicate logging and looping in invalid block handling
| Reviewability | Reviews | Agreement | Size | ||||
|---|---|---|---|---|---|---|---|
| #34254 validation: Prevent duplicate logging and looping in invalid block handling | Author silent 74d | 2 (+1) | Strong | 35 + 29 tests | |||
|
|
|
|
| |||
Reviewability: ReadyStalePaused Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs. Size: lines added or modified outside tests, then in tests; greener = smaller. | |||||||
Goal
Refactors invalid block handling in validation by moving block failure flag propagation, best header recalculation, and fork warning checks out of InvalidChainFound into InvalidBlockFound and InvalidateBlock. In ActivateBestChain, this avoids redundant loops over the block index and duplicated log entries previously triggered by running the full failure sequence twice. Renames the residual InvalidChainFound function to UpdateBestInvalid to reflect its narrowed role of tracking the invalid block with the most work.
Problem: When ActivateBestChain encounters an invalid block, it calls InvalidBlockFound on the failed block and then called InvalidChainFound on the chain candidate. Because InvalidChainFound ran SetBlockFailureFlags and RecalculateBestHeader and logged chain state, these block index tree traversals and log messages were executed twice in immediate succession.
Evidence of importance:
- PR description cites review discussions in PR #31405 (review comment r2135717500) and PR #31533 (review comment r2607189790) identifying repeated work in InvalidateBlock and ActivateBestChain.
Risk of a mistake: medium (validation). A mistake in propagating block failure flags or recalculating the best header could leave nodes tracking an invalid chain tip or misrepresenting chain validity.
Changed since the previous assessment: Added a functional test in test/functional/rpc_invalidateblock.py verifying that invalidating headers that were never connected to the active chain properly marks descendant headers invalid and resets the best header.
Reviewability
Stale · Author silent 74d
No author activity for 74 days; a review may wait a long time for a response.
Waiting on (from the thread read):
- nothing
Agreement
Strong
substantive support, no open objection (Crypt-iQ)
Thread notes: Brought the record up to date under the new rules. Added questions and suggestions from Crypt-iQ, frankomosh, and stratospher; adjusted clears_with on the out-of-chain bug; and updated sedited's stance to support following their review ACK. / Updated under schema 2 rules: corrected clears_with to change on Crypt-iQ's out-of-chain defect objection and set status to accepted following the fix push and crACK; added questions and suggestions from Crypt-iQ, frankomosh, and stratospher; and updated se
Objections, suggestions, and questions:
| Reviewer | Type | Harm, request, or question | Status | Must be | Author replied | Quote |
|---|---|---|---|---|---|---|
| Crypt-iQ (member) | question, correctness | whether m_best_invalid fails to be set properly when pindex is in the main chain | accepted · author confirmed it is pre-existing; reviewer agreed it can be addressed in a follow-up | answered | yes | 2026-01-20: 'I think this can fail to set `m_best_invalid` properly since `to_mark_failed` walks backwards (if `pindex` is in the main chain) and then `UpdateBestInvalid` is called after looping?' Status from: 2026-08-27: Crypt-iQ: 'I also think `m_best_invalid` could be updated properly in a follow-up PR.' |
| frankomosh (contributor) | question, approach | whether the second call to UpdateBestInvalid is redundant with InvalidBlockFound in ConnectTip | accepted · Crypt-iQ explained the difference and frankomosh accepted | answered | by others | 2026-01-23: 'I think this update is already handled by `InvalidBlockFound()`, called from within `ConnectTip()`, so this second call is maybe unnecessary?' Status from: 2026-01-24: frankomosh: 'Ah, I see and understand it now. Thanks for clarifying' |
| Crypt-iQ (member) | objection, correctness | InvalidateBlock misses SetBlockFailureFlags and RecalculateBestHeader when the target block is not in the active chain | fixed · fix pushed · author restored missing calls in ff06c05 with test; reviewer crACKed | changed | yes | 2026-02-02: 'for the case where to_mark_failed is out-of-chain, I think it's missing SetBlockFailureFlags & RecalculateBestHeader?' Status from: 2026-08-27: Crypt-iQ: 'crACK ff06c05043bf2324085dd0a1969239a9fd19eda5' |
| stratospher (member) | suggestion, approach | combine the two iterations over the block index into a single pass | answered · author explained that optimizing this rare error path is unnecessary | answered | yes | 2026-03-24: 'wondering if it’s also possible to avoid looping over the block index twice and instead combine this into a single pass?' Status from: 2026-08-19: mzumsande: 'I didn't do that here - I feel like it's not really necessary to optimize too much' |
| frankomosh (contributor) | objection, interface | Changes to InvalidateBlock log prefix and format may break external log monitoring | answered · author explained logs are not a stable interface and RPC logging is distinct | answered | yes | 2026-05-18: 'might silently break any log monitoring on the old string.' Status from: 2026-08-19: mzumsande: 'Since logs are not a guaranteed stable interface, I don't think log monitoring is an issue' |
Support:
- bensig (contributor) (ACK, 2026-01-12): ACK 73e0bab7adb0b16b2ac4d13c9d601a04b7184749 [not substantive]
- frankomosh (contributor) (Concept ACK, 2026-01-23): Concept ACK on the overall refactoring approach. [not substantive]
- stratospher (member) (Concept ACK, 2026-03-24) [read: validation.h]: Concept ACK. nice cleanup! [not substantive]
- Crypt-iQ (member) (ACK, 2026-08-27) [read: ActivateBestChain, InvalidateBlock]: This PR by itself is a good improvement to ActivateBestChain.
- sedited (member) (ACK, 2026-10-09): ACK ff06c05043bf2324085dd0a1969239a9fd19eda5 [not substantive]
Participants:
- bensig (contributor, support, 1 statements 2026-01-12 to 2026-01-12): ACKed an early commit
- Crypt-iQ (member, objection, 7 statements 2026-01-20 to 2026-08-27): pointed out missing SetBlockFailureFlags and RecalculateBestHeader for out-of-chain InvalidateBlock (fixed); subsequently crACKed
- frankomosh (contributor, objection, 4 statements 2026-01-23 to 2026-05-18): Concept ACKed overall approach; questioned logging format changes
- stratospher (member, support, 1 statements 2026-03-24 to 2026-03-24): Concept ACKed as a nice cleanup and suggested combining block index iteration passes
- sedited (member, support, 3 statements 2026-08-10 to 2026-10-09): pinged for rebase, left a naming nit, and ACKed
Files
36 lines under test/bench/ci.
- src/validation.cpp +34/-22
- test/functional/rpc_invalidateblock.py +22/-0
- src/test/util/validation.cpp +4/-4
- src/test/fuzz/block_index_tree.cpp +2/-2
- src/test/util/validation.h +1/-1
- src/validation.h +1/-1
Card
The short text the ranking pass compares across PRs.
This PR refactors invalid block handling by relocating block failure flag updates, best header recalculations, and logging from InvalidChainFound into InvalidBlockFound and InvalidateBlock. When ActivateBestChain encountered an invalid block, it previously performed duplicate block index traversals and emitted duplicate log messages by invoking the failure routines twice. The change eliminates the redundant iterations and renames the remaining helper to UpdateBestInvalid. The author cites review discussions in PR 31405 and PR 31533 highlighting this repeated work.
Data
thread read through 2026-10-09 (24 statements), head ff06c05043
labels: Validation
Categories
Validation
P3 · cleanup (ranking pass: Moved down: redundant work only in a rare invalid-block path, least consequential of the refactor group)
Cleans up invalid block processing in `ActivateBestChain` to eliminate redundant block index traversals and duplicated log messages. As noted in the description citing reviews in #31405 and #31533, moving failure flag propagation and best header recalculation out of `InvalidChainFound` avoids immediately executing the failure sequence twice upon encountering an invalid block.
Membership: Carries the Validation label and refactors invalid block handling and block index tree traversals in `src/validation.cpp`.
Factors: security/stability 0, bug 1, performance 1, user value 0, leverage 0