Merge bitcoin/bitcoin#35621: validation: Ignore eventual error message from flushing in AcceptBlock

256482ab56 validation: In AcceptBlock, ignore flush result (optout)

Pull request description:

  Shortcut: see https://github.com/bitcoin/bitcoin/pull/35570#issuecomment-4827437071 and https://github.com/bitcoin/bitcoin/pull/29700#discussion_r3488550312 .

  **Summary.** At the end of `ChainstateManager::AcceptBlock`, the flushing of the chain state is initiated, but any potential failure from `FlushStateToDisk` is ignored. However, the error message might get propagated upwards. This change makes sure that both the error status and the error message are ignored, and adds an explanatory comment.

  **Related/background.** The potential inconsistency between the return value and state returned from `AcceptBlock`  was found during refactoring of the return of `BlockValidationState` in #35570. PR #29700 also touches this part by adding an explanatory comment, see https://github.com/bitcoin/bitcoin/pull/29700#discussion_r3488550312 . Splitting this minor behavior change out of #35570 makes that PR a clean (no-bahavior-change) refactor (this option was mentioned from the start, and also proposed by reviewers; see: https://github.com/bitcoin/bitcoin/pull/35570#issuecomment-4827437071 ).

  **Motivation**

  - Get rid of the potential inconsistency between the return value (`true`) and state returned (`Error`). This facilitates refactoring of the error returns in #35570.
  - Make the ignoring clear and explicit, so there is no doubt about it being intentional or not.

  **Details.** `ChainstateManager::AcceptBlock` is called for new block candidates. After checks and inclusion, it triggers flushing of the updated chain state to disk, by calling `Chainstate::FlushStateToDisk`. This method returns a bool and also takes & returns a `BlockValidationState` state. In case of error it returns `false`, and sets the state (to `Error`) and the reject reason string. At the call site, the return value is ignored, but for the state the state variable of `AcceptBlock` is provided, so an Error state may get propagated upwards in the call chain. A caller may react to this Error state.

  **Rationale for ignoring the flush error**
  The validation code flushes internally in several places, and mostly doesn't treat flush failures as errors returned to callers. Disk errors should never be mistreated as block validation failure. Note that the fatal error notification inside `FlushStateToDisk` still fires, so the node will shut down on unrecoverable flush errors regardless.

  **Relevant use case.** `AcceptBlock` is called from the following methods (excluding test code):

  - `ChainstateManager::ProcessNewBlock`
  - Twice from `ChainstateManager::LoadExternalBlockFile`

  Of these, in two places the `state` returned is not used at all. Only at `validation.cpp:5056` is the state used.

  ```
              BlockValidationState state;
              if (AcceptBlock(pblock, state, nullptr, true, dbp, nullptr, true)) {
                  nLoaded++;
              }
              if (state.IsError()) {
                  break;
              }
  ```

  Here a flush error *may* trigger the `break`, causing an early exit from the loop over blocks in a block file. Post-change the returned state will not be `Error` in a disk error case, so the `break` will not be hit here. However:

  - This branch is only called for blocks that are not known yet
  - In case of disk error case the fatal error handler will cause the node to shut down in any case.

  Based on the above, the change in error return behavior is acceptable.

ACKs for top commit:
  maflcko:
    lgtm ACK 256482ab56
  l0rinc:
    lightly tested ACK 256482ab56
  dergoegge:
    Code review ACK 256482ab56
  sedited:
    ACK 256482ab56
  ismaelsadeeq:
    ACK 256482ab56

Tree-SHA512: c72747b5725739117d6061c3b3d5e6910bd06325a072b20708475c8e178a7ad8a6509b30cc8b547c92011a1267e4ad2feef31a992110072e5f37a4ac1f5c79d8
This commit is contained in:
merge-script
2026-07-06 10:19:12 +01:00

View File

@@ -4398,7 +4398,14 @@ bool ChainstateManager::AcceptBlock(const std::shared_ptr<const CBlock>& pblock,
// the block files may be pruned, so we can just call this on one
// chainstate (particularly if we haven't implemented pruning with
// background validation yet).
ActiveChainstate().FlushStateToDisk(state, FlushStateMode::NONE);
//
// Flush errors (e.g. low disk space during pruning) are ignored, so that
// callers can't mistreat a flush failure as a block validation failure.
// The fatal error notification inside FlushStateToDisk still fires,
// so the node will shut down on unrecoverable flush errors regardless.
// For state a dummy value is used, and the return value is ignored.
BlockValidationState flush_state_ignore;
(void)ActiveChainstate().FlushStateToDisk(flush_state_ignore, FlushStateMode::NONE);
CheckBlockIndex();