Skip to content

Conversation

@sedited
Copy link
Contributor

@sedited sedited commented Mar 12, 2024

Based on #25665.

Currently functions issuing fatal errors call the fatalError notification method to issue a shutdown procedure. This makes it hard for higher level functions to figure out if an error deeper in the call stack occurred. For users of the kernel it might also be difficult to assert which function call issued a fatal error if they are being run concurrently. If the kernel would eventually be used by external users, getting fatal error information through a callback instead of function return values would also be cumbersome and limiting. Unit, bench, and fuzz tests currently don't have a way to effectively test against fatal errors.

This patch set is an attempt to make fatal error handling in the kernel code more transparent. Fatal errors are now passed up the call stack through util::Result<T, FatalError> failure values. A previous attempt at this by theuni always immediately returned failure values if a function call returned a failure. However, this is not always desirable (see discussion here). Sometimes, further operations can still be completed even if a fatal error was issued. The solution to this is that these "ignored" errors are still moved through util::Result's error string values with its .MoveMessages method, even while a failure value in the result is not present.

Next to some smaller behavior changes, the most significant change is that the issuing of a shutdown procedure is delayed until a potential fatal error is handled as opposed to immediately when it is first encountered. Another effect is that potential fatal errors are now asserted in the bench, fuzz and unit tests. Some of the currently not immediately returned fatal errors need some further scrutiny. These are marked with a TODO (fatal error) comment and could be tackled in a later PR.

To validate this approach a new clang-tidy check is introduced. It implements the following checks:

  1. If a function calls another function with a util::Result<T, FatalCondition> return type, its return type has to be util::Result<T, FatalCondition> too, or it has to handle the value returned by the function with one of CheckFatal, HandleFatalError,
    UnwrapFatalError, or CheckFatalFailure.
  2. In functions returning a util::Result<T, FatalCondition> a call to a function returning a util::Result<T, FatalCondition> needs to propagate the value by either:
    • Returning it immediately
    • Assigning it immediately to an existing result with .MoveMessages() or .Set()
    • Eventually passing it as an argument to a .MoveMessages()

This PR is part of the libbitcoinkernel project and is a step towards stage 2, creating a more refined kernel API.

@DrahtBot
Copy link
Contributor

DrahtBot commented Mar 12, 2024

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

Code Coverage

For detailed information about the code coverage, see the test coverage report.

Reviews

See the guideline for information on the review process.

Type Reviewers
Concept ACK stickies-v, ryanofsky

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #29735 (AcceptMultipleTransactions: Fix workspace not being set as client_maxfeerate failure by instagibbs)
  • #29700 (kernel, refactor: return error status on all fatal errors by ryanofsky)
  • #29641 (scripted-diff: Use LogInfo/LogDebug over LogPrintf/LogPrint by maflcko)
  • #29553 (assumeutxo: Add dumptxoutset height param, remove shell scripts by fjahr)
  • #29415 (Broadcast own transactions only via short-lived Tor or I2P connections by vasild)
  • #29402 (mempool: Log added for dumping mempool transactions to disk by kevkevinpal)
  • #29278 (Wallet: Add maxfeerate wallet startup option by ismaelsadeeq)
  • #29231 (logging: Update to new logging API by ajtowns)
  • #29015 (kernel: Streamline util library by ryanofsky)
  • #28843 ([refactor] Remove BlockAssembler m_mempool member by TheCharlatan)
  • #28687 (C++20 std::views::reverse by stickies-v)
  • #28233 (validation: don't clear cache on periodic flush: >2x block connection speed by andrewtoth)
  • #26593 (tracing: Only prepare tracepoint arguments when actually tracing by 0xB10C)
  • #26022 (Add util::ResultPtr class by ryanofsky)
  • #25722 (refactor: Use util::Result class for wallet loading by ryanofsky)
  • #25665 (refactor: Add util::Result failure values, multiple error and warning messages 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.

@sedited sedited force-pushed the returnTypeShutdown branch from a6cbc25 to 1f4d172 Compare March 12, 2024 22:19
@stickies-v
Copy link
Contributor

Concept ACK, nice one. Will first re-review #25665

@ryanofsky
Copy link
Contributor

Concept ACK, it is better for a function to return an error status than set a flag, initiate a global shutdown, and not return a success or failure value.

But the FatalError enum and HandleFatalError / CheckFatal / clang-tidy machinery do not seem very friendly to use, and I don't see what benefits they offer over just using util::Result and [[nodiscard]].

It also seems like it would be nice to have local enums for functions like ProcessTransaction and ActivateBestChain that just have 2 or 3 error codes instead of 22 error codes like FatalError.

Also, I understand the goal of this is to improve the libbitcoinkernel interface, but I'm not sure this should mean moving AbortNode calls out of kernel code into net_processing code. I think it would be probably be better if the net_processing functions could return success/failure as well, and AbortNode calls could move to an even higher level. Or if AbortNode calls could just be left where they are and this could be a pure refactoring that just adds missing return values.

I guess my main question is would it be possible to make a stripped down version of this PR that just adds [[nodiscard]] to all functions where fatalError/flushError are called, and bubbles up util::Result values, and is just a plain refactoring that doesn't change behavior, and doesn't add the FatalError type and handlers? It seems like could make the PR smaller, and make libbitcoinkernel act like a normal library that can return errors, without changing other things.

sedited added 12 commits March 26, 2024 20:19
…lError>

To propagate the FatalError, also add the type to PreciousBlock and
ActivateBestChain.
To propagate the FatalError, also add the type to PruneBlockFilesManual,
AcceptToMemoryPool, ProcessNewPackage, ResizeCoinsCaches,
ForceFlushStateToDisk, PruneAndFlush, DisconnectTip, InvalidateBlock,
MaybeUpdateMempoolForReorg, ProcessTransaction, MaybeRebalanceCaches,
LoadMempool
To propagate the FatalError, also add the type to VerifyDB and
TestBlockValidity.

The miner module now has to handle a FatalError, so introduce shutdown
and exit_status member variables for it to be able to abort.
To propagate the FatalError, also add the type to LoadGenesisBlock.
To propagate the FatalError, also add the type to
FlushChainstateBlockFile.
To propagate the FatalError, also add the type to LoadBlockIndexDB.
Also add using declarations where now possible.
@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

@DrahtBot
Copy link
Contributor

⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?

  • Is it still relevant? ➡️ Please solve the conflicts to make it ready for review and to ensure the CI passes.
  • Is it no longer relevant? ➡️ Please close.
  • Did the author lose interest or time to work on this? ➡️ Please close it and mark it 'Up for grabs' with the label, so that it can be picked up in the future.

@DrahtBot
Copy link
Contributor

⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?

  • Is it still relevant? ➡️ Please solve the conflicts to make it ready for review and to ensure the CI passes.
  • Is it no longer relevant? ➡️ Please close.
  • Did the author lose interest or time to work on this? ➡️ Please close it and mark it 'Up for grabs' with the label, so that it can be picked up in the future.

@sedited
Copy link
Contributor Author

sedited commented Oct 10, 2024

I'm going to leave this in draft a little while longer. I think we should first focus on ryanofsky's #29700 though. If that is merged, there is always opportunity to revisit the approach taken here.

@DrahtBot
Copy link
Contributor

DrahtBot commented Jan 7, 2025

⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?

  • Is it still relevant? ➡️ Please solve the conflicts to make it ready for review and to ensure the CI passes.
  • Is it no longer relevant? ➡️ Please close.
  • Did the author lose interest or time to work on this? ➡️ Please close it and mark it 'Up for grabs' with the label, so that it can be picked up in the future.

2 similar comments
@DrahtBot
Copy link
Contributor

DrahtBot commented Apr 6, 2025

⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?

  • Is it still relevant? ➡️ Please solve the conflicts to make it ready for review and to ensure the CI passes.
  • Is it no longer relevant? ➡️ Please close.
  • Did the author lose interest or time to work on this? ➡️ Please close it and mark it 'Up for grabs' with the label, so that it can be picked up in the future.

@DrahtBot
Copy link
Contributor

DrahtBot commented Jul 4, 2025

⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?

  • Is it still relevant? ➡️ Please solve the conflicts to make it ready for review and to ensure the CI passes.
  • Is it no longer relevant? ➡️ Please close.
  • Did the author lose interest or time to work on this? ➡️ Please close it and mark it 'Up for grabs' with the label, so that it can be picked up in the future.

@DrahtBot
Copy link
Contributor

DrahtBot commented Oct 1, 2025

⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?

  • Is it still relevant? ➡️ Please solve the conflicts to make it ready for review and to ensure the CI passes.
  • Is it no longer relevant? ➡️ Please close.
  • Did the author lose interest or time to work on this? ➡️ Please close it and mark it 'Up for grabs' with the label, so that it can be picked up in the future.

fanquake added a commit that referenced this pull request Nov 4, 2025
6c7a34f kernel: Add Purpose section to header documentation (TheCharlatan)
7e9f00b kernel: Allowing reducing exports (TheCharlatan)
7990463 kernel: Add pure kernel bitcoin-chainstate (TheCharlatan)
36ec9a3 Kernel: Add functions for working with outpoints (TheCharlatan)
5eec7fa kernel: Add block hash type and block tree utility functions to C header (TheCharlatan)
f5d5d12 kernel: Add function to read block undo data from disk to C header (TheCharlatan)
09d0f62 kernel: Add functions to read block from disk to C header (TheCharlatan)
a263a4c kernel: Add function for copying block data to C header (TheCharlatan)
b30e15f kernel: Add functions for the block validation state to C header (TheCharlatan)
aa262da kernel: Add validation interface to C header (TheCharlatan)
d27e277 kernel: Add interrupt function to C header (TheCharlatan)
1976b13 kernel: Add import blocks function to C header (TheCharlatan)
a747ca1 kernel: Add chainstate load options for in-memory dbs in C header (TheCharlatan)
070e777 kernel: Add options for reindexing in C header (TheCharlatan)
ad80abc kernel: Add block validation to C header (TheCharlatan)
cb1590b kernel: Add chainstate loading when instantiating a ChainstateManager (TheCharlatan)
e2c1bd3 kernel: Add chainstate manager option for setting worker threads (TheCharlatan)
65571c3 kernel: Add chainstate manager object to C header (TheCharlatan)
c62f657 kernel: Add notifications context option to C header (TheCharlatan)
9e1bac4 kernel: Add chain params context option to C header (TheCharlatan)
337ea86 kernel: Add kernel library context object (TheCharlatan)
28d679b kernel: Add logging to kernel library C header (TheCharlatan)
2cf136d kernel: Introduce initial kernel C header API (TheCharlatan)

Pull request description:

  This is a first attempt at introducing a C header for the libbitcoinkernel library that may be used by external applications for interfacing with Bitcoin Core's validation logic. It currently is limited to operations on blocks. This is a conscious choice, since it already offers a lot of powerful functionality, but sits just on the cusp of still being reviewable scope-wise while giving some pointers on how the rest of the API could look like.

  The current design was informed by the development of some tools using the C header:

  * A re-implementation (part of this pull request) of [bitcoin-chainstate](https://github.com/bitcoin/bitcoin/blob/master/src/bitcoin-chainstate.cpp).
  * A re-implementation of the python [block linearize](https://github.com/bitcoin/bitcoin/tree/master/contrib/linearize) scripts: https://github.com/TheCharlatan/bitcoin/tree/kernelLinearize
  * A silent payment scanner: https://github.com/josibake/silent-payments-scanner
  * An electrs index builder: https://github.com/josibake/electrs/commits/electrs-kernel-integration
  * A rust bitcoin node: https://github.com/TheCharlatan/kernel-node
  * A reindexer: https://github.com/TheCharlatan/bitcoin/tree/kernelApi_Reindexer

  The library has also been used by other developers already:

  * A historical block analysis tool: https://github.com/ismaelsadeeq/mining-analysis
  * A swiftsync hints generator: https://github.com/theStack/swiftsync-hints-gen
  * Fast script validation in floresta: getfloresta/Floresta#456
  * A swiftsync node implementation: https://github.com/2140-dev/swiftsync/tree/master/node

  Next to the C++ header also made available in this pull request, bindings for other languages are available here:

  * Rust: https://github.com/TheCharlatan/rust-bitcoinkernel
  * Python: https://github.com/stickies-v/py-bitcoinkernel
  * Go: https://github.com/stringintech/go-bitcoinkernel
  * Java: https://github.com/yuvicc/java-bitcoinkernel

  The rust bindings include unit and fuzz tests for the API.

  The header currently exposes logic for enabling the following functionality:
  * Feature-parity with the now deprecated libbitcoin-consensus
  * Optimized sha256 implementations that were not available to previous users of libbitcoin-consensus thanks to a static kernel context
  * Full support for logging as well as control over categories and severity
  * Feature parity with the existing experimental bitcoin-chainstate
  * Traversing the block index as well as using block index entries for reading block and undo data.
  * Running the chainstate in memory
  * Reindexing (both full and chainstate-only)
  * Interrupting long-running functions

  The pull request introduces a new kernel-only test binary that purely relies on the kernel C header and the C++ standard library. This is intentionally done to show its capabilities without relying on other code inside the project. This may be relaxed to include some of the existing utilities, or even be merged into the existing test suite.

  The complete docs for the API as well as some usage examples are hosted on [thecharlatan.ch/kernel-docs](https://thecharlatan.ch/kernel-docs/index.html). The docs are generated from the following repository (which also holds the examples): [github.com/TheCharlatan/kernel-docs](https://github.com/TheCharlatan/kernel-docs).

  #### How can I review this PR?

  Scrutinize the commit messages, run the tests, write your own little applications using the library, let your favorite code sanitizer loose on it, hook it up to your fuzzing infrastructure, profile the difference between the existing bitcoin-chainstate and the bitcoin-chainstate introduced here, be nitty on the documentation, police the C interface, opine on your own API design philosophy.

  To get a feeling for the API, read through the tests, or one of the examples.

  To configure this PR for making the shared library and the bitcoin-chainstate and test_kernel utilities available:
  ```
  cmake -B build -DBUILD_KERNEL_LIB=ON -DBUILD_UTIL_CHAINSTATE=ON
  ```

  Once compiled the library is part of the build artifacts that can be installed with:
  ```
  cmake --install build
  ```

  #### Why a C header (and not a C++ header)

  * Shipping a shared library with a C++ header is hard, because of name mangling and an unstable ABI.
  * Mature and well-supported tooling for integrating C exists for nearly every popular language.
  * C offers a reasonably stable ABI

  Also see #30595 (comment).

  #### What about versioning?

  The header and library are still experimental and I would expect this to remain so for some time, so best not to worry about versioning yet.

  #### Potential future additions

  In future, the C header could be expanded to support (some of these have been roughly implemented):

  * Handling transactions, block headers, coins cache, utxo set, meta data, and the mempool
  * Adapters for an abstract coins store
  * Adapters for an abstract block store
  * Adapters for an abstract block tree store
  * Allocators and buffers for more efficient memory usage
  * An "[io-less](https://sans-io.readthedocs.io/how-to-sans-io.html)" interface
  * Hooks for an external mempool, or external policy rules

  #### Current drawbacks

  * For external applications to read the block index of an existing Bitcoin Core node, Bitcoin Core needs to shut down first, since leveldb does not support reading across multiple processes. Other than migrating away from leveldb, there does not seem to be a solution for this problem. Such a migration is implemented in #32427.
  * The fatal error handling through the notifications is awkward. This is partly improved through #29642.
  * Handling shared pointers in the interfaces is unfortunate. They make ownership and freeing of the resources fuzzy and poison the interfaces with additional types and complexity. However, they seem to be an artifact of the current code that interfaces with the validation engine. The validation engine itself does not seem to make extensive use of these shared pointers.
  * If multiple instances of the same type of objects are used, there is no mechanism for distinguishing the log messages produced by each of them. A potential solution is #30342.
  * The background leveldb compaction thread may not finish in time leading to a non-clean exit. There seems to be nothing we can do about this, outside of patching leveldb.

ACKs for top commit:
  alexanderwiederin:
    re-ACK 6c7a34f
  stringintech:
    re-ACK 6c7a34f
  laanwj:
    Code review ACK 6c7a34f
  ismaelsadeeq:
    reACK 6c7a34f 👾
  fanquake:
    ACK 6c7a34f - soon we'll be running bitcoin (kernel)

Tree-SHA512: ffe7d4581facb7017d06da8b685b81f4b5e4840576e878bb6845595021730eab808d8f9780ed0eb0d2b57f2647c85dcb36b6325180caaac469eaf339f7258030
@sedited sedited moved this from Ready for Review PRs to WIP PRs in The libbitcoinkernel Project Dec 4, 2025
@DrahtBot
Copy link
Contributor

⌛ There hasn't been much activity lately and the patch still needs rebase. What is the status here?

  • Is it still relevant? ➡️ Please solve the conflicts to make it ready for review and to ensure the CI passes.
  • Is it no longer relevant? ➡️ Please close.
  • Did the author lose interest or time to work on this? ➡️ Please close it and mark it with one of the labels 'Up for grabs' or 'Insufficient Review', so that it can be picked up in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Architecture & Performance

Development

Successfully merging this pull request may close these issues.

4 participants