RFC: Separate out runtime errors from BlockValidationState using `util::Expected` #35646

pull yuvicc wants to merge 5 commits into bitcoin:master from yuvicc:2026-06-remove_enum_ie changing 37 files +465 −387
  1. yuvicc commented at 4:51 AM on July 3, 2026: contributor

    BlockValidationState currently carries two unrelated kinds of failure: consensus/policy invalidity which is the actual purpose and runtime errors via a M_ERROR mode in ModeState enum inside ValidationState class. This PR removes M_ERROR and routes runtime errors through util::Expected<T, std::string> instead, making the two failure modes distinct.

    Motivation

    BlockValidationState exists to describe why a block or transaction is invalid. It holds a consensus/policy reject reason. A disk write or system runtime error is not that, yet it was folded into the same object as a third state i.e. M_ERROR.

    This conflation has two costs:

    • Wrong abstraction for non-validating functions. FlushStateToDisk, DisconnectTip, ActivateBestChain, PreciousBlock, and InvalidateBlock do no block validation, but each takes a BlockValidationState out-param to report any runtime error.

    • Three outcomes in one object. Functions that can both validate and hit a runtime error (ConnectBlock, AcceptBlock, ProcessNewBlock) holds valid, invalid, and fatal into a single BlockValidationState, forcing every caller to unwrap.

    As noted in the original discussion here:

    Functions like FlushStateToDisk, ActivateBestChain aren't exactly about validating a certain block, yet they take a BlockValidationState& out-param just to absorb runtime errors, which feels like the wrong abstraction.

    And also discussion here to remove M_ERROR value.

    • This would also pave a long term fix for #35570, which returns BlockValidationState from validation methods instead of boolean value.

    util::Expected is a good option for this reason, it keeps the error handling in the same return value style (system/runtime errors and ValidationState for consensus correctness) and separates-out runtime errors from ValidationState.

    Runtime errors could be signaled either by a return value or by throwing. This PR keeps them as return values as it's a minimal change. M_ERROR was already a return-value mechanism included in ValidationState with consensus verdict. This PR doesn't open the exceptions vs returns question, it keeps the existing return-value style and just moves the fatal error into a proper Expected channel.

  2. DrahtBot commented at 4:51 AM on July 3, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/35646.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    ACK optout21

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #36188 (crypto: plug hardware optimized SHA256 into libsecp256k1's context by furszy)
    • #36171 (blockstorage: Defer error notifications to callers by w0xlt)
    • #36066 (validation: Separate check-only version of ConnectBlock by optout21)
    • #36000 (validation: prefetch blocks while connecting by l0rinc)
    • #35751 (validation: use parallel input prevout fetching in TestBlockValidity by andrewtoth)
    • #35675 (mining: add block template manager by ismaelsadeeq)
    • #35570 (refactor: Change some validation.cpp methods to return BlockValidationState by optout21)
    • #35307 (blockstorage: keep snapshot base in normal blockfile range by shuv-amp)
    • #34729 (Reduce log noise by ajtowns)
    • #34254 (validation: Prevent duplicate logging and looping in invalid block handling by mzumsande)
    • #33854 (fix assumevalid is ignored during reindex by Eunovo)
    • #32554 (bench: replace embedded raw block with configurable block generator by l0rinc)
    • #30342 (kernel, logging: Pass Logger instances to kernel objects by ryanofsky)

    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-->

    LLM Linter (✨ experimental)

    Possible typos and grammar issues:

    • // fatal error occured -> // fatal error occurred [misspelling of “occurred”]

    Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

    • ProcessNewBlock(std::make_shared<CBlock>(Params().GenesisBlock()), true, true, &ignored) in src/test/validation_block_tests.cpp
    • ProcessNewBlock(block, true, true, &ignored) in src/test/validation_block_tests.cpp
    • ProcessNewBlock(shared_pblock, true, true, nullptr) in src/test/util/setup_common.cpp
    • ProcessNewBlock(block, true, true, nullptr) in src/test/blockfilter_index_tests.cpp (3 call sites)
    • ProcessNewBlock(block, true, true, &new_block) in src/test/util/mining.cpp
    • ProcessNewBlock(std::make_shared<CBlock>(block), true, true, nullptr) in src/test/validation_chainstate_tests.cpp
    • AcceptBlock(new_block, &new_block_index, true, nullptr, nullptr, true) in src/test/baseindex_tests.cpp
    • AcceptBlock(pblockone, &pindex, true, nullptr, &newblock, true) in src/test/validation_chainstate_tests.cpp
    • AcceptBlock(pblock, nullptr, true, dbp, nullptr, true) in src/validation.cpp
    • AcceptBlock(pblockrecursive, nullptr, true, &it->second, nullptr, true) in src/validation.cpp

    <sup>2026-08-27 07:16:12</sup>

  3. DrahtBot added the label Needs rebase on Jul 6, 2026
  4. yuvicc force-pushed on Jul 6, 2026
  5. yuvicc commented at 4:28 PM on July 6, 2026: contributor

    Rebased on master to resolve conflicts with #35621. Adopted its "ignore the flush error" behavior in the new util::Expected API by dropping the flush-error propagation from AcceptBlock.

  6. DrahtBot removed the label Needs rebase on Jul 6, 2026
  7. maflcko commented at 5:13 PM on July 7, 2026: member

    Hmm, it could make sense to be more type-safe here, but looking at the code, there are places that flatten this back down to a boolean, so I wonder what the overall benefit is?

    I think it could make sense to think whether any places that flatten this down again to a boolean need a different handling? If yes, fixing that handling should probably be done early in a pull request changing the behavior.

    Moreover, I presume all of the runtime-errors are fatal, so I presume they must all call the fatal error function. Maybe this can be enforced at compile-time, so that all those fatal errors ensure that the fatal error function is called exactly once?

  8. in src/validation.cpp:2308 in 7d0ed51ec4
    2304 | @@ -2307,6 +2305,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
    2305 |      uint256 block_hash{block.GetHash()};
    2306 |      assert(*pindex->phashBlock == block_hash);
    2307 |  
    2308 | +    BlockValidationState state;
    


    optout21 commented at 3:36 AM on July 8, 2026:

    7d0ed51 validation: route fatal errors through util::Expected:

    Could get rid of this local variable if CheckBlock signature is also changed (to return state).


    yuvicc commented at 6:55 AM on July 8, 2026:

    I think this could be done in a follow-up to reduce the review burden here? #35570

  9. in src/validation.cpp:4193 in 7d0ed51ec4
    4186 | @@ -4193,9 +4187,10 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
    4187 |      return true;
    4188 |  }
    4189 |  
    4190 | -bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationState& state, CBlockIndex** ppindex, bool min_pow_checked)
    4191 | +BlockValidationState ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, CBlockIndex** ppindex, bool min_pow_checked)
    4192 |  {
    4193 |      AssertLockHeld(cs_main);
    4194 | +    BlockValidationState state;
    


    optout21 commented at 3:40 AM on July 8, 2026:

    7d0ed51 validation: route fatal errors through util::Expected:

    The scope of this local var. could be reduced, or maybe omitted completely, if CheckBlockHeader signature is also changed.


    yuvicc commented at 6:56 AM on July 8, 2026:

    same as above #35646 (review)

  10. in src/validation.cpp:4253 in 7d0ed51ec4
    4257 |  // Exposed wrapper for AcceptBlockHeader
    4258 | -bool ChainstateManager::ProcessNewBlockHeaders(std::span<const CBlockHeader> headers, bool min_pow_checked, BlockValidationState& state, const CBlockIndex** ppindex)
    4259 | +BlockValidationState ChainstateManager::ProcessNewBlockHeaders(std::span<const CBlockHeader> headers, bool min_pow_checked, const CBlockIndex** ppindex)
    4260 |  {
    4261 |      AssertLockNotHeld(cs_main);
    4262 | +    BlockValidationState state;
    


    optout21 commented at 3:41 AM on July 8, 2026:

    7d0ed51 validation: route fatal errors through util::Expected:

    The scope of this local variable could be reduced.


    yuvicc commented at 6:56 AM on July 8, 2026:

    same as above #35646 (review)

  11. optout21 commented at 3:46 AM on July 8, 2026: contributor

    Concept ACK

    The advantages of this change are:

    • enforcement of returned errors -- some methods can return only runtime errors;
    • reduced risk of accidental mix-up of validation and runtime errors.

    At first it looked a bit strange that some errors (runtime errors) are treated as exceptional cases, while validation errors are treated as return values, as previously both were treated the same. However, this makes sense; validation status can be regarded as the non-exceptional output of the checker algotihms, while runtime errors are proper exceptional errors.

    Some minor observations:

    • Maybe the big change could be broken up (e.g. in two, first the void return value changes, then the BlockValidationState changes)
    • CheckBlock, CheckBlockHeader could be also be changed to the return-state pattern
    • [[nodiscard]] could be added touched signatures, to reduce risk of ignored errors.
    • In some places where a local state variable is used, its scope could be reduced, or omitted entirely. Preferably error from a call should be handled right away, there is no need for method-wide state/error variable (left comments in a few places).
  12. yuvicc commented at 6:00 AM on July 8, 2026: contributor

    Hmm, it could make sense to be more type-safe here, but looking at the code, there are places that flatten this back down to a boolean, so I wonder what the overall benefit is?

    You're right that the top-level callers collapses the result back to yes/no, wouldn't that be the right place for it to collapse? e.g. ConnectBlock -> ConnectTip -> ActivateBestChainStep -> ActivateBestChain, where M_ERROR used to be inside ValidationState next to consensus verdict and every caller had to unwrap to distinguish b/w consensus failure v/s fatal error.

    I think it could make sense to think whether any places that flatten this down again to a boolean need a different handling?

    I've checked, every fatal error already fires at the origin(FatalError()), so by the time it reaches the top-level caller, the node might be shutting down regardless of what the caller does with the string, none of the callers branch on fatal-vs-non-fatal; they just surface the message. If other reviewers also feels the same that the caller needs to programmatically tell the two apart, we could have a distinct type for fatal and non-fatal type.

    Moreover, I presume all of the runtime-errors are fatal, so I presume they must all call the fatal error function. Maybe this can be enforced at compile-time, so that all those fatal errors ensure that the fatal error function is called exactly once?

    This is mostly true, except for invalidateblock/reconsiderblock rpc whose error returns are non-fatal and fire nothing. I think we can get a compile-time guarantee for the functions where every error is fatal, by constructing FatalError like below?

    <details>

    class FatalError
    {
    public:
        //! Fire the fatalError notification.
        [[nodiscard]] static util::Unexpected<FatalError> Raise(
            kernel::Notifications& notifications, const bilingual_str& message);
    
        //! Untranslated message, for logging or surfacing through an RPC error.
        const std::string& message() const LIFETIMEBOUND { return m_message; }
    
    private:
        explicit FatalError(std::string message) : m_message{std::move(message)} {}
        std::string m_message;
    };
    
    util::Unexpected<FatalError> FatalError::Raise(Notifications& notifications, const bilingual_str& message)
    {
        notifications.fatalError(message);                 // fire (→ AbortNode → shutdown)
        return util::Unexpected{FatalError{message.original}};  // then mint the value
    }
    

    </details>

  13. maflcko commented at 7:36 AM on July 8, 2026: member

    Hmm, it could make sense to be more type-safe here, but looking at the code, there are places that flatten this back down to a boolean, so I wonder what the overall benefit is?

    You're right that the top-level callers collapses the result back to yes/no, wouldn't that be the right place for it to collapse?

    Yeah, I am mostly wondering aloud. Because currently, your patch will map both to RPC_INTERNAL_ERROR in GenerateBlock. However, in generateblock, they are mapped to RPC_INTERNAL_ERROR or RPC_VERIFY_ERROR. And in submitblock, they are mapped to a new imaginary/undocumented? "validation-error" string?

    That doesn't seem consistent or worthwhile. I'd say:

    • Either we can distinguish the two cases clearly, in which case a separate type and handling makes sense (and they shouldn't be flattened down).
    • Or, we can not distinguish them, in which case splitting them up and then flattening them down again seems pointless?
  14. willcl-ark added the label Brainstorming on Jul 8, 2026
  15. willcl-ark added the label Validation on Jul 8, 2026
  16. DrahtBot added the label Needs rebase on Jul 9, 2026
  17. yuvicc force-pushed on Jul 15, 2026
  18. DrahtBot removed the label Needs rebase on Jul 15, 2026
  19. yuvicc commented at 5:13 AM on July 16, 2026: contributor

    That doesn't seem consistent or worthwhile. I'd say:

    Either we can distinguish the two cases clearly, in which case a separate type and handling makes sense (and they shouldn't be flattened down).

    Or, we can not distinguish them, in which case splitting them up and then flattening them down again seems pointless?

    Thanks, I agree. I went with the first option. Fatal/system failures are now represented separately as kernel::FatalError and propagated via util::Expected, while BlockValidationState now only represents valid vs. invalid consensus validation.

    Also, the existing RPC behavior is preserved.

  20. DrahtBot added the label Needs rebase on Jul 23, 2026
  21. yuvicc force-pushed on Jul 27, 2026
  22. yuvicc commented at 6:38 AM on July 27, 2026: contributor

    Rebased to master.

  23. DrahtBot removed the label Needs rebase on Jul 27, 2026
  24. DrahtBot added the label Needs rebase on Aug 14, 2026
  25. kernel: add FatalError type
    Add a move-only type for propagating fatal/runtime errors after the fatal error notification has been raised.
    Restrict construction to `Raise()` so propagation can be fired only once.
    e80ce8f4ba
  26. validation: return fatal errors with `util::Expected`
    Separate fatal errors from block validation result.
    Use `Expected<void, FatalError>` where the only failure mode is fatal.
    These methods have no additional result to return on successful completion.
    
    Use `Expected<bool, FatalError>` for methods that perform chain operations
    rather than returning the validation result of one specific block. Their existing
    false values remain meaningful and are not necessarily fatal.
    
    Use `Expected<BlockValidationState, FatalError>` for methods that
    directly produces a validity result for a particular block.
    `BlockValidationState` already represents both valid and invalid results,
    including the invalidity reason, so returning an additional boolean is
    redundant. Return the state directly and reserve the unexpected result
    for fatal runtime errors.
    9f3ae31ec4
  27. consensus: remove ValidationState error mode
    Runtime failures are now returned separately through `util::Expected`, so `ValidationState`
    only needs to represent valid and invalid validation outcomes.
    Remove `M_ERROR`, `Error()`, and `IsError()`.
    154042b4d1
  28. kernel: remove INTERNAL_ERROR validation mode
    Validation state no longer represents runtime failures, so remove INTERNAL_ERROR from the kernel C API and C++ wrapper.
    a75d25d9bc
  29. test: FatalError type
    Verify that FatalError is move-only, retains its message while propagating through util::Expected,
    and fires the fatal error notification exactly once.
    4b183f4e07
  30. yuvicc force-pushed on Aug 27, 2026
  31. yuvicc commented at 7:15 AM on August 27, 2026: contributor

    Rebased on master

  32. DrahtBot removed the label Needs rebase on Aug 27, 2026
  33. yuvicc marked this as ready for review on Sep 8, 2026
  34. yuvicc commented at 8:13 AM on September 8, 2026: contributor

    Ready for review.

  35. in src/validation.cpp:3532 in 9f3ae31ec4
    3528 | @@ -3534,10 +3529,10 @@ bool Chainstate::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex)
    3529 |          }
    3530 |      }
    3531 |  
    3532 | -    return ActivateBestChain(state, std::shared_ptr<const CBlock>());
    3533 | +    return ActivateBestChain();
    


    optout21 commented at 12:16 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    It's unclear why the 'empty' CBlock parameter was removed (it's also unclear why it was there in the first place).

  36. in src/validation.cpp:4269 in 9f3ae31ec4
    4265 | @@ -4267,10 +4266,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(std::span<const CBlockHeader> hea
    4266 |              CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
    4267 |              bool accepted{AcceptBlockHeader(header, state, &pindex, min_pow_checked)};
    4268 |              CheckBlockIndex();
    4269 | -
    4270 | -            if (!accepted) {
    4271 | -                return false;
    4272 | -            }
    4273 | +            if (!accepted) return false;
    


    optout21 commented at 12:19 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    Nit: no need to touch this, but can stay.

  37. in src/validation.cpp:4390 in 9f3ae31ec4
    4385 | @@ -4389,15 +4386,15 @@ bool ChainstateManager::AcceptBlock(const std::shared_ptr<const CBlock>& pblock,
    4386 |              blockPos = *dbp;
    4387 |              m_blockman.UpdateBlockInfo(block, pindex->nHeight, blockPos);
    4388 |          } else {
    4389 | -            blockPos = m_blockman.WriteBlock(block, pindex->nHeight);
    4390 | -            if (blockPos.IsNull()) {
    4391 | -                state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
    4392 | -                return false;
    4393 | +            auto res{m_blockman.WriteBlock(block, pindex->nHeight)};
    4394 | +            if (!res) {
    


    optout21 commented at 12:21 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    It seems to me, that the (blockPos.IsNull()) branch with the specific error return should be kept.

  38. in src/validation.cpp:4559 in 9f3ae31ec4
    4563 | -
    4564 | -    // Ensure no check returned successfully while also setting an invalid state.
    4565 | -    if (!state.IsValid()) NONFATAL_UNREACHABLE();
    4566 | -
    4567 | -    return state;
    4568 | +    return chainstate.ConnectBlock(block, &index_dummy, view_dummy, /*fJustCheck=*/true);
    


    optout21 commented at 12:24 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    Why not keeping the NONFATAL_UNREACHABLE checks?

  39. in src/node/miner.cpp:419 in 9f3ae31ec4
     416 |      CHECK_NONFATAL(chainman.m_options.signals)->UnregisterSharedValidationInterface(sc);
     417 |  
     418 | -    if (!new_block && accepted) {
     419 | +    if (!res) {
     420 | +        reason = res.error().message();
     421 | +    } else if (!new_block && accepted) {
    


    optout21 commented at 2:53 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    It's not clear to me that this does not introduce new error reasons. I have the impression that the Error case previously resulted in "inconclusive" (but I'm not sure). If that's the case, this needs to be changed.

  40. in src/rpc/blockchain.cpp:1734 in 9f3ae31ec4
    1736 | -
    1737 | -    if (!state.IsValid()) {
    1738 | -        throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
    1739 | +    if (auto res{chainman.ActiveChainstate().ActivateBestChain()}; !res) {
    1740 | +        throw JSONRPCError(RPC_DATABASE_ERROR, res.error().message());
    1741 |      }
    


    optout21 commented at 2:55 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    Note: pre-change error from ActivateBestChain was ignored.

  41. in src/rpc/mining.cpp:624 in 9f3ae31ec4
     627 | @@ -620,8 +628,6 @@ static UniValue BIP22ValidationResult(const BlockValidationState& state)
     628 |      if (state.IsValid())
     629 |          return UniValue::VNULL;
     630 |  
     631 | -    if (state.IsError())
     632 | -        throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
    


    optout21 commented at 2:58 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    Technically, in this commit BlockValidationState can still be Error, so this error check should be kept, removed in the later commit.

  42. in src/rpc/mining.cpp:1185 in 9f3ae31ec4
    1181 | @@ -1167,11 +1182,7 @@ static RPCMethod submitheader()
    1182 |      }
    1183 |  
    1184 |      BlockValidationState state;
    1185 | -    chainman.ProcessNewBlockHeaders({{h}}, /*min_pow_checked=*/true, state);
    1186 | -    if (state.IsValid()) return UniValue::VNULL;
    1187 | -    if (state.IsError()) {
    1188 | -        throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
    1189 | -    }
    1190 | +    if (chainman.ProcessNewBlockHeaders({{h}}, /*min_pow_checked=*/true, state)) return UniValue::VNULL;
    


    optout21 commented at 3:06 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    It's not clear to me that the checks for the returned state can be omitted.

  43. in src/validation.cpp:3047 in 9f3ae31ec4
    3041 | @@ -3052,15 +3042,16 @@ bool Chainstate::ConnectTip(
    3042 |      {
    3043 |          CoinsViewOverlay& view{*m_coins_views->m_connect_block_view};
    3044 |          const auto reset_guard{view.StartFetching(*block_to_connect)};
    3045 | -        bool rv = ConnectBlock(*block_to_connect, state, pindexNew, view);
    3046 | +        auto res{ConnectBlock(*block_to_connect, pindexNew, view)};
    3047 | +        if (!res) return util::Unexpected(std::move(res).error());
    3048 | +        state = std::move(*res);
    


    optout21 commented at 3:14 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    I think there is a behavior change here in case of Error: previously BlockChecked was called, and now it isn't.

  44. in src/validation.cpp:4444 in 9f3ae31ec4
    4442 |              // Store to disk
    4443 | -            ret = AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block, min_pow_checked);
    4444 | +            auto res{AcceptBlock(block, &pindex, force_processing, nullptr, new_block, min_pow_checked)};
    4445 | +            if (!res) return util::Unexpected(std::move(res).error());
    4446 | +            state = std::move(*res);
    4447 | +            accepted = state.IsValid();
    


    optout21 commented at 3:16 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    I think there is a behavior change here: in case of error from AcceptBlock previously BlockChecked was called before return, now it's not.

  45. in src/node/interfaces.cpp:1024 in 9f3ae31ec4
    1023 | -        reason = state.GetRejectReason();
    1024 | -        debug = state.GetDebugMessage();
    1025 | -        return state.IsValid();
    1026 | +        auto res{TestBlockValidity(chainman().ActiveChainstate(), block, /*check_pow=*/options.check_pow, /*check_merkle_root=*/options.check_merkle_root)};
    1027 | +        if (!res) {
    1028 | +            // fatal error occured
    


    optout21 commented at 3:20 PM on September 8, 2026:

    9f3ae31 validation: return fatal errors with util::Expected:

    Nit: typo: occured -> occurred

  46. in src/kernel/bitcoinkernel.h:398 in a75d25d9bc
     394 | + * Whether a validated data structure is valid or invalid.
     395 |   */
     396 |  typedef uint8_t btck_ValidationMode;
     397 |  #define btck_ValidationMode_VALID ((btck_ValidationMode)(0))
     398 |  #define btck_ValidationMode_INVALID ((btck_ValidationMode)(1))
     399 | -#define btck_ValidationMode_INTERNAL_ERROR ((btck_ValidationMode)(2))
    


    optout21 commented at 3:22 PM on September 8, 2026:

    a75d25d kernel: remove INTERNAL_ERROR validation mode:

    Isn't this an API change technically? If so, consider a release note for it.

  47. optout21 commented at 3:29 PM on September 8, 2026: contributor

    ConceptACK 4b183f4e077eb1e7d48cf5d53036c8591abf9478

    Reviewed again, generally looks good. I've left some comments; most are rather minor, but there are some that should be checked. Therefore not upgrading my Concept A to a proper acknowledgement as of now. This PR is related to #35570 (of mine), and my assessment is that both PR's stand on their own, but, despite an overlap, they also complement each other.

    I uphold my comment from last review:

    • I would be happy to see the second large commit broken up (possible ways: first the void return value changes, then the BlockValidationState changes; first internal validation method changes, then external ones; etc.)

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-09-09 07:56 UTC