Shortcut: see #35570 (comment) and #29700 (review) .
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 #29700 (review) . 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: #35570 (comment) ).
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 (so not in case of reindex)
- 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.