#29409 ipc: Expose Chain interface to IPC clients

ReviewabilityReviewsAgreementSize
#29409 ryanofsky ipc: Expose Chain interface to IPC clientsReady0 (+2)Mild2,359 + 33 tests
  • Allow external clients and separate processes to read chain data and receive block events over IPC
  • Unblock running Bitcoin Core wallet in an isolated process without freezing internal C++ APIs
  • Ready for review following rebase and the interface split
  • Broad support for enabling multiprocess wallet separation and external chain indexers
  • Unaddressed objection: node aborts if notification callback throws, author replied (xyzconstant)
  • Verified by testing with Rust BDK wallet integration and Electrs bindings (darosior, Sjors)
  • Concept approval for running external indexes and client dashboards (ariard, willcl-ark, rustaceanrob)
  • Approval from an account with no project history and no stated reason (Big621)
  • 2,359 lines added or modified outside tests
  • 33 lines added or modified in tests
  • 515 lines removed in total
  • 79 files, 8 commits
IPC / multiprocess#2 of 18
P1 · unblocks #10102
  • P1 because it unblocks out-of-process wallet separation and external process integration
  • Multiple downstream consumers and indexes depend on IPC chain access instead of P2P or RPC
Reviewability: ReadyStalePaused
Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets
Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs.
Size: lines added or modified outside tests, then in tests; greener = smaller.

Goal

Exposes the node's `interfaces::Chain` over IPC using Cap'n Proto proxy wrappers, split into a stable `Chain` interface and an `UnstableChain` interface. The stable interface covers basic block lookups for external clients under Cap'n Proto backward-compatibility rules, while `UnstableChain` exposes the remaining methods for internal Bitcoin Core consumers. It also updates the `src/ipc/libmultiprocess` subtree and makes minor adjustments to `interfaces::Chain` method signatures to support code generation.

Problem: Processes connecting to `bitcoin-node` over IPC via `-ipcbind` cannot read chain data or follow chain notifications, blocking multiprocess wallet separation (#10102). Exposing the entire raw `interfaces::Chain` C++ interface to external IPC clients would freeze internal wallet APIs against future refactoring.

Evidence of importance:

  • PR description: 'Processes connected to bitcoin-node over IPC (with -ipcbind) can use the Mining interface, but they can't currently use the Chain interface read chain data or follow chain events. Chain data could be useful to third-party clients and Bitcoin Core's own wallet could use this as well to run in a separate process (#10102).'
  • PR description references #34981 ('multiprocess: expose existing interfaces or design new ones?') as the rationale for splitting into stable and unstable interfaces.

Scope: Carries an update to the src/ipc/libmultiprocess subtree accounting for the majority of changed files and commits, alongside new Cap'n Proto schema and wrapper files. Also includes minor signature adjustments in src/interfaces/chain.h (removing const qualification from two estimation methods and passing updateRwSetting callbacks by value).

Risk of a mistake: medium (wallet, build, other). Bugs in Cap'n Proto serialization or proxy wrappers could cause node crashes or memory errors when IPC requests are processed, though IPC is only enabled when configured via -ipcbind.

Changed since the previous assessment: The bundled libmultiprocess subtree update was refreshed with upstream changes including v14 platform support and CI updates, and a compiler flag was added to suppress -Wc++23-lambda-attributes warnings.

Reviewability

Ready

Nothing known that would invalidate a review now.

Waiting on (from the thread read):

  • reviewer: re-review following rebase and Chain/UnstableChain interface split 2026-10-15

Agreement

Mild

objection that needs a change was answered, no reply from the objector (xyzconstant: node aborts due to an unhandled remote exception if an external ChainNotifications client raises an error during notific)

Thread notes: The author rebased the branch to split the Chain Cap'n Proto interface into stable Chain and UnstableChain interfaces as discussed in #34981, updated libmultiprocess dependencies, and resolved a silent conflict with #36669.

Objections, suggestions, and questions:

ReviewerTypeHarm, request, or questionStatusMust beAuthor repliedQuote
cbergqvist (contributor)suggestion, maintenanceremove unused include <any>fixed · fix pushed · author confirmed it was leftover and removed itansweredyes2024-02-27: 'Builds fine without `#include <any>` using `g++` on Linux?'
Status from: 2024-02-28: ryanofsky: 'Good catch, I think this must have been left over from a previous version of this file. Removed now.'
ariard (contributor)suggestion, approachparameterize Wrap with any CAddress version to test alternative crypto primitivesanswered · author explained current hardcoded serialization is simplest but customized formats can be added lateransweredyes2024-03-07: 'i think `Wrap` could be parameterized with any `CAddress` version.'
Status from: 2024-07-26: ryanofsky: 'For now though the easiest thing is for the wrapped stream to just hardcode parameters that serialize all the data.'
sedited (member)question, approachwhy is custom functionality introduced for findBlock and how to validate changes without testsanswered · author explained recursive structs require custom capnp translation codeansweredyes2024-07-05: 'why is so much custom functionality introduced for `findBlock`?'
Status from: 2024-07-08: ryanofsky: 'Chain::findBlock and the handful of other Chain methods taking FoundBlock input/output arguments need custom code becaus'
sedited (member)question, interfacewhether UniValue in interfaces is intended to be refactored eventuallyanswered · author explained UniValue usage in RPC forwarding, settings, and ban mapansweredyes2024-07-26: 'Is the intention to eventually refactor this?'
Status from: 2024-07-26: ryanofsky: 'In general there are lots of ways current interfaces are not ideal and can be improved over time'
josibake (member)suggestion, maintenanceupdate the PR description to mention the dependency on PR 30509answered · author updated descriptionansweredyes2024-08-07: 'can you update the description with what you mention in ..., regarding #30509 ?'
Status from: 2024-08-07: ryanofsky: 'Added following to the description:'
sedited (member)question, interfacewhether Data blobs and enums should be strongly typed in capnp for non-C++ clientsanswered · author explained tradeoffs of maintenance burden and suggested incremental adoptionansweredyes2024-11-08: 'More generally I have been wondering if it would be a good idea to eventually strongly type these `Data` blobs (and similarly the enums)?'
Status from: 2024-11-12: ryanofsky: 'tradeoff is just that defining capnproto structs for all the serialized classes adds more code that will need to be main'
pseudoramdom (member)question, approachhow to build the branch on macOS and whether to use MULTIPROCESS=1 or build libmultiprocess from sourceanswered · author explained build options and rebased branch to resolve compilation errorsansweredyes2025-02-10: 'Can I use the branch as is or would I need to rebase on `master`'
Status from: 2025-02-10: ryanofsky: 'definitely it should be easiest to just check out this branch and use it as is'
zaidmstrr (contributor)suggestion, correctnessremove flush from chain.capnp to avoid build errors after removal upstreamfixed · fix pushed · author rebased and resolved conflictchangedyes2025-05-24: 'removes this `flush()` from the codebase. I think it should also be removed from `chain.capnp` otherwise it will give build errors.'
Status from: 2025-06-03: ryanofsky: 'Rebased... due to silent conflicts with #32438 and #32641'
zaidmstrr (contributor)objection, correctnessbuild fails because rpcRunLater was removed upstreamfixed · fix pushed · author rebased to resolve conflictchangedno2025-07-21: 'The method `rpcRunLater` was removed in commit, thus there are some build errors.'
Status from: 2025-07-24: ryanofsky: 'Rebased... to fix silent merge conflict with #32862'
l0rinc (member)objection, correctnessmissing struct fields like best_height in estimateSmartFee and mismatched integer and enum types lead to serialization bugs and compilation issuesfixed · fix pushed · fixed by author in subsequent pushchangedyes2025-11-25: 'It seems to me the capnp wrapper needs some updates (missing field, 32/64 bit params, param rename, bool vs uint64, enums as unsigned, optional mapping, unused declarations, comments, etc).'
Status from: 2025-12-12: ryanofsky: 'You found a lot of annoying discrepencies and a real bug in the estimateSmartFee best_height return value, which should '
xyzconstant (contributor)question, interfacewhether ChainClient should be reduced to lifecycle and mock-time hooks and the rest moved to WalletLoaderaccepted · reviewer accepted author explanation that ChainClient is designed for node-spawned processesansweredyes2026-04-27: 'Would it be worth reducing `ChainClient` to just lifecycle + mock-time hooks and moving the rest to `WalletLoader`?'
Status from: 2026-05-07: xyzconstant: 'I get it now, thanks you!'
willcl-ark (member)objection, safetynode could hit undefined behavior when switching on broadcastmethod without safe enum mapping or a default caseanswered · author explained C++ callers enforce explicit values, and non-C++ callers must handle omitted values safelyansweredyes2026-05-20: 'broadcastmethod is switched on (e.g. here) with no default case, as is our convention. but we don't check incoming value is mapped to the enum safely.'
Status from: 2026-05-20: ryanofsky: 'In C++, the compiler enforces at each call site that explicit values need to be passed for all parameters which don't ha'
willcl-ark (member)suggestion, interfacerename descendants parameter to cluster_count post-cluster mempoolfixed · fix pushed · renamed to clusterCount in latest pushansweredyes2026-05-20: 'even though these are positional, I think we can rename descendants -> cluster_count post-cluster mempool (and as this is public facing).'
Status from: 2026-10-14: ryanofsky: 'Renamed to clusterCount in 2ee7fc45cf3658b954bff017db45024b2a37a0ba.'
willcl-ark (member)objection, safetynode segfaults if a non-C++ client passes null transaction data or unhandled enum values over IPCanswered · author replied explaining that capnp allows null/defaults and discussed trade-offs and future mitigationsansweredyes2026-05-20: 'Also what happens here is tx is a null pointer (allowed by capnp spec AFAIU)? I think we will immediately try to deference it in BroadcastTransaction and segfault.'
Status from: 2026-05-20: ryanofsky: 'This is a legitimate concern, and it's not specific to this method or to these parameters.'
willcl-ark (member)question, approachwhy not omit ChainClient::start from the exposed interface or throw an exception rather than assertinganswered · author explained libmultiprocess generation requirements and agreed an exception could be thrown insteadansweredyes2026-05-20: 'Why not omit start from the exposed base interface or make this throw a protocol error instead of asserting.'
Status from: 2026-05-20: ryanofsky: 'This could throw an exception instead of assert. It shouldn't matter very much because I don't think there's a way this '
zaidmstrr (contributor)objection, correctnessnode aborts with an assertion failure if an IPC client requests getBlockHash for a height exceeding the active chain tipanswered · author replied that node-side crash is fixed in #36520 by throwing an exception instead of assertingansweredyes2026-09-09: 'when we pass the height, which is greater than the current tip, then the node crashes and exits with the error... Assertion `chainman().ActiveChain()[height]' failed. Aborted (core dumped)'
Status from: 2026-10-14: ryanofsky: 'Thanks for reporting the getBlockHash crash. #36520 changes getBlockHash to throw an exception instead of asserting'
enirox001 (contributor)question, maintenancewhy FeeCalculation, EstimationResult, and EstimatorBucket are in common.capnp if unreferenced elsewhere, and whether they should be removedfixed · fix pushed · author removed unused fee estimation structs in latest pushansweredyes2026-10-05: 'The data structures added here; `FeeCalculation`, `EstimationResult` and `EstimatorBucket` in common.capnp what is it for? i do not think it is referenced anywhere apart from here, should they be remo'
Status from: 2026-10-14: ryanofsky: 'Removed them in 2ee7fc45cf3658b954bff017db45024b2a37a0ba, along with the note in block_policy_estimator.h that referred '
enirox001 (contributor)suggestion, maintenanceremove stale declaration or include in src/rpc/request.hanswered · author explained the struct mapping note in request.h is still accurateansweredyes2026-10-05: 'I think this is stale'
Status from: 2026-10-14: ryanofsky: 'I think this one is still accurate: JSONRPCRequest in chain.capnp has a field for each member of this struct except cont'
enirox001 (contributor)suggestion, interfaceallow size and fee fields in getTransactionAncestry to be omitted or marked optional rather than always returning empty valuesanswered · author explained returning size and fee has minimal overhead and avoids complexityansweredyes2026-10-05: 'perhaps something similar could be done for this? or an optional parameter added to the method here?'
Status from: 2026-10-14: ryanofsky: 'I think it's simpler to leave this as is. The size and fee values come from the same mempool lookup as the counts'
enirox001 (contributor)question, interfacewhy metadata is omitted here as wellanswered · author explained function pointers cannot be sent over IPC and will be addressed in #10102answeredyes2026-10-05: 'Is there a reason why the metadata is omitted here as well?'
Status from: 2026-10-14: ryanofsky: 'metadata_fn is a function pointer, so it can't be sent over IPC... I'll look at it as part of #10102.'
xyzconstant (contributor)objection, safetynode aborts due to an unhandled remote exception if an external ChainNotifications client raises an error during notification callbacksanswered · author explained notification exceptions are caught and logged in #36520changedyes2026-10-06: 'Basically, the node isn't handling errors raised by external IPC servers it makes calls to.'
Status from: 2026-10-14: ryanofsky: '#36520 fixes this by catching ipc::Exception errors from notification handlers in NotificationsProxy and logging them'
rustaceanrob (member)suggestion, interfacedefine a structured Coin type for undo data and allow clients to optionally receive spent coins in block notificationsanswered · author agreed structured undo data would be useful and deferred to a follow-up after #24230answeredyes2026-10-07: 'Could we consider creating a more detailed type here? I would suggest a "Coin" that contains the script and amount after disk deserialization, then defining a list of coins as the undo data.'
Status from: 2026-10-14: ryanofsky: 'Agree this would be useful... I'd prefer to leave both for a follow-up, since this PR is just trying to wrap the existin'

Support:

  • cbergqvist (contributor) (ACK, 2024-02-28): Surface-level ACK of 41831ab. :) [not substantive]
  • ariard (contributor) (2024-03-07) [read]: being able to work with any socket (TCP or UNIX) makes a lot of sense. you could have bitcoin-node on some secure computing host. and then one or more bitcoin-walet on over-the-network hosts.
  • josibake (member) (Concept ACK, 2024-09-26): Concept ACK [not substantive]
  • sedited (member) (ACK, 2024-11-19): lgtm ACK 34d3e2a6eaaab9de5328c3e64739f1392696c7db [not substantive]
  • darosior (member) (Tested ACK, 2024-12-09) [tested]: tested ACK 395d5eed0475558de2c2a66914d5a5814ce38f3c. I've tested this with the Rust wallet PoC... exercises getHeight, getBlockHash, hasBlocks, findAncestorByHeight, findAncestorByHash, findCommonAnce
  • sedited (member) (ACK, 2024-12-09): Re-ACK 64b833854a34d87cde4e0ca4173d75012c401a7a [not substantive]
  • Sjors (member) (2026-05-12) [both]: implemented Rust bindings and an Electrs demo using IPC instead of RPC and P2P for block fetching, tx broadcast, tip updates, and mempool sync
  • willcl-ark (member) (Concept ACK, 2026-05-20) [read]: Exposing interfaces::Chain over IPC seems very useful, especially for downstream users. I have been using a version of this branch myself on a node 'dashboard'... Long term, I think IPC should become
  • enirox001 (contributor) (2026-10-05) [read]: This is nice to have... in general having downstream clients able to call these methods is good.
  • rustaceanrob (member) (2026-10-07) [read]: allows indexes (like silent payments partial secrets or Utreexo proofs) to run on separate processes from bitcoin-node rather than supporting them directly in the node

Participants:

  • cbergqvist (contributor, support, 5 statements 2024-02-26 to 2024-02-28): gave surface-level ACK after review suggestions were addressed
  • ariard (contributor, support, 2 statements 2024-03-07 to 2024-03-07): supported socket-based IPC for node and wallet separation
  • sedited (member, support, 15 statements 2024-07-05 to 2026-04-20): ACKed the PR, tested against Rust BDK wallet PoC, and asked design questions
  • josibake (member, support, 2 statements 2024-08-07 to 2024-09-26): Concept ACK and requested PR description clarification
  • darosior (member, support, 5 statements 2024-12-05 to 2024-12-16): Tested ACK using custom Rust BDK wallet client
  • Big621 (none, neutral, 2 statements 2025-01-15 to 2025-01-15): approved without comment or prior project history
  • pseudoramdom (member, question, 1 statements 2025-02-10 to 2025-02-10): asked questions about building with multiprocess on macOS
  • zaidmstrr (contributor, objection, 3 statements 2025-05-24 to 2026-09-09): reported node aborts on out-of-bounds getBlockHash, plus earlier rebase build issues
  • maflcko (member, neutral, 2 statements 2025-07-21 to 2025-07-24): noted typo in comment and CI lint failure
  • l0rinc (member, objection, 17 statements 2025-09-30 to 2025-11-25): requested changes due to missing struct fields and parameter type discrepancies
  • Sjors (member, support, 2 statements 2026-04-01 to 2026-05-12): developed Rust bindings, demonstrated Electrs integration, and opened discussion on interface scope
  • xyzconstant (contributor, objection, 3 statements 2026-04-27 to 2026-10-06): reported node aborts when an external ChainNotifications server returns an error, and asked about ChainClient
  • willcl-ark (member, objection, 6 statements 2026-05-20 to 2026-05-20): Concept ACK, but raised concern that null pointers or unhandled enums cause node segfaults
  • enirox001 (contributor, support, 7 statements 2026-10-05 to 2026-10-05): supported exposing interfaces for downstream clients; raised questions and suggestions on capnp structures
  • rustaceanrob (member, support, 2 statements 2026-10-07 to 2026-10-07): supported enabling external index processes over IPC and suggested exposing structured undo data

Dependencies

Depends on: #36520

Enables:

Base for: #10102, #19460, #19461

Files

33 lines under test/bench/ci.

  • src/ipc/libmultiprocess/src/mp/util.cpp +216/-31
  • src/ipc/capnp/chain.cpp +217/-0
  • src/ipc/libmultiprocess/test/mp/test/test.cpp +208/-6
  • src/ipc/libmultiprocess/test/mp/test/connect_tests.cpp +212/-0
  • src/ipc/capnp/chain.capnp +196/-0
  • src/ipc/libmultiprocess/src/mp/proxy.cpp +106/-47
  • src/ipc/capnp/common-types.h +134/-0
  • src/ipc/libmultiprocess/test/mp/test/listen_tests.cpp +48/-77
  • src/ipc/libmultiprocess/include/mp/proxy-io.h +79/-42
  • src/ipc/capnp/chain-types.h +109/-0
  • src/ipc/libmultiprocess/src/mp/gen.cpp +48/-45
  • src/ipc/libmultiprocess/test/mp/test/unixlistener.h +84/-0
  • src/ipc/libmultiprocess/include/mp/util.h +63/-15
  • src/ipc/libmultiprocess/test/mp/test/foo.h +56/-1
  • src/ipc/libmultiprocess/include/mp/proxy-types.h +40/-14
  • src/ipc/libmultiprocess/include/mp/proxy.h +30/-23
  • src/ipc/libmultiprocess/cmake/compat_config.cmake +0/-42
  • src/ipc/libmultiprocess/include/mp/type-number.h +24/-15
  • src/ipc/libmultiprocess/ci/scripts/ci.sh +34/-1
  • src/ipc/libmultiprocess/.github/workflows/ci.yml +19/-14
  • src/ipc/libmultiprocess/CMakeLists.txt +24/-9
  • src/ipc/libmultiprocess/test/mp/test/spawn_tests.cpp +28/-5
  • src/ipc/capnp/common.capnp +32/-0
  • test/functional/interface_ipc.py +32/-0
  • src/ipc/libmultiprocess/test/mp/test/foo.capnp +28/-0
  • src/ipc/libmultiprocess/include/mp/type-chrono.h +25/-2
  • src/ipc/libmultiprocess/test/mp/test/common.h +27/-0
  • src/ipc/libmultiprocess/include/mp/type-struct.h +12/-12
  • src/ipc/libmultiprocess/cmake/iwyu-wrapper.sh +22/-0
  • src/ipc/libmultiprocess/include/mp/type-interface.h +10/-10
  • src/ipc/libmultiprocess/shell.nix +13/-6
  • src/ipc/libmultiprocess/include/mp/type-context.h +15/-3
  • src/ipc/capnp/handler.capnp +17/-0
  • CMakeLists.txt +14/-0
  • src/ipc/libmultiprocess/.clang-tidy +12/-2
  • src/ipc/libmultiprocess/.github/workflows/bitcoin-core-ci.yml +8/-5
  • src/ipc/libmultiprocess/example/calculator.cpp +4/-9
  • src/ipc/libmultiprocess/example/printer.cpp +4/-9
  • src/ipc/libmultiprocess/example/example.cpp +7/-5
  • src/ipc/libmultiprocess/include/mp/type-char.h +9/-3
  • src/ipc/capnp/handler-types.h +10/-0
  • src/ipc/libmultiprocess/ci/scripts/run.sh +6/-4
  • src/ipc/libmultiprocess/doc/versions.md +8/-2
  • src/ipc/libmultiprocess/include/mp/type-map.h +6/-4
  • src/ipc/libmultiprocess/ci/configs/netbsd.bash +1/-7
  • src/ipc/libmultiprocess/cmake/pthread_checks.cmake +8/-0
  • src/ipc/libmultiprocess/include/mp/type-set.h +4/-4
  • src/ipc/libmultiprocess/include/mp/type-threadmap.h +4/-4
  • src/ipc/libmultiprocess/include/mp/type-unordered-set.h +4/-4
  • src/ipc/libmultiprocess/include/mp/type-vector.h +4/-4
  • src/ipc/capnp/init.capnp +7/-0
  • src/ipc/libmultiprocess/ci/configs/olddeps.bash +5/-2
  • src/ipc/libmultiprocess/test/mp/test/foo-types.h +7/-0
  • src/interfaces/chain.h +3/-3
  • src/ipc/libmultiprocess/ci/configs/newdeps.bash +6/-0
  • src/ipc/libmultiprocess/doc/design.md +3/-3
  • src/ipc/libmultiprocess/doc/usage.md +6/-0
  • src/node/interfaces.cpp +3/-3
  • src/ipc/CMakeLists.txt +5/-0
  • src/ipc/libmultiprocess/include/mp/type-data.h +2/-3
  • src/ipc/libmultiprocess/ci/configs/sanitize.bash +2/-2
  • src/ipc/libmultiprocess/ci/scripts/config.sh +2/-2
  • src/ipc/libmultiprocess/include/mp/type-string.h +3/-1
  • src/ipc/libmultiprocess/ci/configs/default.bash +1/-1
  • src/ipc/libmultiprocess/ci/configs/freebsd.bash +1/-1
  • src/ipc/libmultiprocess/ci/configs/gnu32.bash +1/-1
  • src/ipc/libmultiprocess/ci/configs/llvm.bash +1/-1
  • src/ipc/libmultiprocess/ci/configs/macos.bash +1/-1
  • src/ipc/libmultiprocess/ci/configs/openbsd.bash +1/-1
  • src/ipc/libmultiprocess/cmake/TargetCapnpSources.cmake +1/-1
  • src/ipc/libmultiprocess/doc/install.md +1/-1
  • src/ipc/libmultiprocess/include/mp/type-function.h +1/-1
  • src/ipc/libmultiprocess/include/mp/version.h +1/-1
  • src/rpc/request.h +2/-0
  • src/ipc/capnp/init-types.h +1/-0
  • src/ipc/libmultiprocess/ci/README.md +1/-0
  • src/ipc/libmultiprocess/include/mp/config.h.in +1/-0
  • src/ipc/libmultiprocess/test/CMakeLists.txt +1/-0
  • test/functional/test_framework/ipc_util.py +1/-0

Card

The short text the ranking pass compares across PRs.

Exposes the Chain interface over IPC using Cap'n Proto, splitting it into a version-stable Chain interface with basic block lookup methods and an UnstableChain interface for Bitcoin Core internal consumers. Currently, chain data and notifications are inaccessible over IPC, blocking multiprocess wallet separation (#10102) and external clients connecting via -ipcbind. This is a core prerequisite for running the wallet in an isolated process (#19460) and depends on #36520.

Data

dossier JSON · extract JSON · model ledger, generated 2026-11-01T01:16, confidence high, input hash b956e89027e38084
thread read through 2026-10-15 (143 statements), head f770bb7f73
labels: IPC

Categories

IPC / multiprocess

P1 · unblocks #10102

Exposes Chain over IPC, unblocking process separation for the wallet (#10102) and enabling external clients like indexes and downstream tools to read chain data. Multiple contributors and maintainers tested and relied on this to build decoupled wallet prototypes, indexes, and electrs bindings.

Membership: Carries IPC label, updates libmultiprocess subtree, and implements Cap'n Proto proxy bindings for interfaces::Chain.

Factors: security/stability 0, bug 0, performance 0, user value 2, leverage 3