validation: avoid partial override of non-valid validation state #36792

pull optout21 wants to merge 3 commits into bitcoin:master from optout21:2611-validationstate-reason-overwrite changing 2 files +61 −0
  1. optout21 commented at 6:12 PM on November 6, 2026: contributor

    ValidationState::Error() only sets the reject reason if the state is still valid. When it is called on a state that is already Invalid or Error, the mode changes but the earlier reason, result and debug message are kept. The result is a mix of the old and new error. Code review in #35570 found two places where this happens. This makes the in-and-out use of the state parameter harder to follow and to remove later.

    This PR makes both cases explicit:

    1. ConnectBlock() → CheckBlock() with a mutated block (BLOCK_MUTATED, e.g. a merkle root mismatch). This is treated as a sign of corruption and reported through FatalError(), but the resulting Error state kept BLOCK_MUTATED / bad-txnmrklroot. Now the Invalid state is logged and then replaced, so the Error state carries the corruption message.

    2. ActivateBestChainStep() → DisconnectTip() → FlushStateToDisk() failure. FlushStateToDisk() has already reported a fatal error, and ActivateBestChainStep() then called FatalError() a second time with a less specific message. Now the existing error is logged and returned unchanged, and no second fatal error is reported.

    Behavior changes:

    • Case 1: the returned state has the reject reason "Corrupt block found indicating potential hardware failure.", no result and no debug message. The original reason is logged.
    • Case 2: the duplicate fatal-error notification is no longer sent. The returned reject reason does not change.

    The first commit adds a unit test for case 1 that captures the current behavior, and the second commit updates its expectations. Case 2 has no test, because a flush failure cannot be triggered without fault injection. The path where DisconnectTip() fails while the state is still valid is covered by feature_abortnode.py and is unchanged.

  2. test: validation unit test with mutated block
    Add a unit test for the case when a mutated block is encountered
    in ConnectBlock, to document the exact return state and reject reason.
    89bd6a3a61
  3. validation: set error upon mutated block in ConnectBlock
    In order to avoid confusing partial override of error state,
    log the old state and override it in ConnectBlock().
    When CheckBlock() returns an Invalid state due to a mutated block,
    the error is upgraded to Error state. A new log entry prints the
    Invalid state, then it is overridden to an Error state.
    The behavior change is that the reject reason was previously
    kept, now it is overwritten, and result and debug messages are reset.
    208f341a13
  4. validation: don't re-report fatal error from DisconnectTip
    In order to avoid confusing partial override of error state,
    don't overwrite the error in ActivateBestChainStep().
    When DisconnectTip() returns an error state, the fatal error has
    already been reported (by FlushStateToDisk()). Log it and return,
    without reporting a second, less specific fatal error.
    Behavior change: the duplicate fatalError notification is no longer
    emitted in this case.
    b4b95702bc
  5. DrahtBot added the label Validation on Nov 6, 2026
  6. DrahtBot commented at 6:12 PM on November 6, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    External sites

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process. A summary of reviews will appear here.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #36326 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/36326.svg"></sub> (kernel: Use typed errors for fatal and flush error notifications by arejula27)
    • #36109 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/36109.svg"></sub> (test: fix coinbases built on arbitrary parents by l0rinc)
    • #35570 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/35570.svg"></sub> (refactor: Change some validation.cpp methods to return BlockValidationState by optout21)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-11-09 21:51 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me