diff --git a/builds/gnu/Makefile.am b/builds/gnu/Makefile.am
index fe918ebb..37eff919 100644
--- a/builds/gnu/Makefile.am
+++ b/builds/gnu/Makefile.am
@@ -55,25 +55,19 @@ src_libbitcoin_server_la_SOURCES = \
${srcdir}/../../src/parsers/admin_query.cpp \
${srcdir}/../../src/parsers/admin_target.cpp \
${srcdir}/../../src/parsers/bitcoind_target.cpp \
+ ${srcdir}/../../src/parsers/btcd_filter.cpp \
${srcdir}/../../src/parsers/electrum_version.cpp \
${srcdir}/../../src/parsers/native_query.cpp \
${srcdir}/../../src/parsers/native_target.cpp \
${srcdir}/../../src/protocols/protocol_html.cpp \
${srcdir}/../../src/protocols/protocol_http.cpp \
${srcdir}/../../src/protocols/admin/protocol_admin.cpp \
- ${srcdir}/../../src/protocols/native/protocol_native.cpp \
- ${srcdir}/../../src/protocols/native/protocol_native_address.cpp \
- ${srcdir}/../../src/protocols/native/protocol_native_block.cpp \
- ${srcdir}/../../src/protocols/native/protocol_native_configuration.cpp \
- ${srcdir}/../../src/protocols/native/protocol_native_input.cpp \
- ${srcdir}/../../src/protocols/native/protocol_native_output.cpp \
- ${srcdir}/../../src/protocols/native/protocol_native_tx.cpp \
${srcdir}/../../src/protocols/bitcoind/protocol_bitcoind_rest.cpp \
${srcdir}/../../src/protocols/bitcoind/protocol_bitcoind_rpc.cpp \
${srcdir}/../../src/protocols/bitcoind/protocol_bitcoind_rpc_json.cpp \
- ${srcdir}/../../src/protocols/btcd/protocol_btcd_rpc.cpp \
- ${srcdir}/../../src/protocols/btcd/protocol_btcd_rpc_filter.cpp \
- ${srcdir}/../../src/protocols/btcd/protocol_btcd_rpc_utility.cpp \
+ ${srcdir}/../../src/protocols/btcd/protocol_btcd.cpp \
+ ${srcdir}/../../src/protocols/btcd/protocol_btcd_filter.cpp \
+ ${srcdir}/../../src/protocols/btcd/protocol_btcd_utility.cpp \
${srcdir}/../../src/protocols/electrum/protocol_electrum.cpp \
${srcdir}/../../src/protocols/electrum/protocol_electrum_addresses.cpp \
${srcdir}/../../src/protocols/electrum/protocol_electrum_fees.cpp \
@@ -86,6 +80,13 @@ src_libbitcoin_server_la_SOURCES = \
${srcdir}/../../src/protocols/electrum/protocol_electrum_subscribe.cpp \
${srcdir}/../../src/protocols/electrum/protocol_electrum_transactions.cpp \
${srcdir}/../../src/protocols/electrum/protocol_electrum_version.cpp \
+ ${srcdir}/../../src/protocols/native/protocol_native.cpp \
+ ${srcdir}/../../src/protocols/native/protocol_native_address.cpp \
+ ${srcdir}/../../src/protocols/native/protocol_native_block.cpp \
+ ${srcdir}/../../src/protocols/native/protocol_native_configuration.cpp \
+ ${srcdir}/../../src/protocols/native/protocol_native_input.cpp \
+ ${srcdir}/../../src/protocols/native/protocol_native_output.cpp \
+ ${srcdir}/../../src/protocols/native/protocol_native_tx.cpp \
${srcdir}/../../src/protocols/stratum_v1/protocol_stratum_v1.cpp
include_bitcoindir = \
@@ -106,33 +107,38 @@ include_bitcoin_server_HEADERS = \
${srcdir}/../../include/bitcoin/server/settings.hpp \
${srcdir}/../../include/bitcoin/server/version.hpp
-include_bitcoin_server_impl_protocolsdir = \
- ${includedir}/bitcoin/server/impl/protocols
-
-include_bitcoin_server_impl_protocols_HEADERS = \
- ${srcdir}/../../include/bitcoin/server/impl/protocols/protocol_native.ipp
-
-include_bitcoin_server_sessionsdir = \
- ${includedir}/bitcoin/server/sessions
-
-include_bitcoin_server_sessions_HEADERS = \
- ${srcdir}/../../include/bitcoin/server/sessions/session.hpp \
- ${srcdir}/../../include/bitcoin/server/sessions/session_handshake.hpp \
- ${srcdir}/../../include/bitcoin/server/sessions/session_server.hpp \
- ${srcdir}/../../include/bitcoin/server/sessions/sessions.hpp
-
include_bitcoin_server_channelsdir = \
${includedir}/bitcoin/server/channels
include_bitcoin_server_channels_HEADERS = \
${srcdir}/../../include/bitcoin/server/channels/channel.hpp \
- ${srcdir}/../../include/bitcoin/server/channels/channel_btcd.hpp \
${srcdir}/../../include/bitcoin/server/channels/channel_electrum.hpp \
${srcdir}/../../include/bitcoin/server/channels/channel_http.hpp \
${srcdir}/../../include/bitcoin/server/channels/channel_stratum_v1.hpp \
${srcdir}/../../include/bitcoin/server/channels/channel_stratum_v2.hpp \
${srcdir}/../../include/bitcoin/server/channels/channels.hpp
+include_bitcoin_server_impl_protocolsdir = \
+ ${includedir}/bitcoin/server/impl/protocols
+
+include_bitcoin_server_impl_protocols_HEADERS = \
+ ${srcdir}/../../include/bitcoin/server/impl/protocols/protocol_native.ipp
+
+include_bitcoin_server_interfacesdir = \
+ ${includedir}/bitcoin/server/interfaces
+
+include_bitcoin_server_interfaces_HEADERS = \
+ ${srcdir}/../../include/bitcoin/server/interfaces/admin.hpp \
+ ${srcdir}/../../include/bitcoin/server/interfaces/bitcoind_rest.hpp \
+ ${srcdir}/../../include/bitcoin/server/interfaces/bitcoind_rpc.hpp \
+ ${srcdir}/../../include/bitcoin/server/interfaces/btcd.hpp \
+ ${srcdir}/../../include/bitcoin/server/interfaces/electrum.hpp \
+ ${srcdir}/../../include/bitcoin/server/interfaces/interfaces.hpp \
+ ${srcdir}/../../include/bitcoin/server/interfaces/native.hpp \
+ ${srcdir}/../../include/bitcoin/server/interfaces/stratum_v1.hpp \
+ ${srcdir}/../../include/bitcoin/server/interfaces/stratum_v2.hpp \
+ ${srcdir}/../../include/bitcoin/server/interfaces/types.hpp
+
include_bitcoin_server_parsersdir = \
${includedir}/bitcoin/server/parsers
@@ -140,6 +146,7 @@ include_bitcoin_server_parsers_HEADERS = \
${srcdir}/../../include/bitcoin/server/parsers/admin_query.hpp \
${srcdir}/../../include/bitcoin/server/parsers/admin_target.hpp \
${srcdir}/../../include/bitcoin/server/parsers/bitcoind_target.hpp \
+ ${srcdir}/../../include/bitcoin/server/parsers/btcd_filter.hpp \
${srcdir}/../../include/bitcoin/server/parsers/electrum_version.hpp \
${srcdir}/../../include/bitcoin/server/parsers/native_query.hpp \
${srcdir}/../../include/bitcoin/server/parsers/native_target.hpp \
@@ -153,7 +160,7 @@ include_bitcoin_server_protocols_HEADERS = \
${srcdir}/../../include/bitcoin/server/protocols/protocol_admin.hpp \
${srcdir}/../../include/bitcoin/server/protocols/protocol_bitcoind_rest.hpp \
${srcdir}/../../include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp \
- ${srcdir}/../../include/bitcoin/server/protocols/protocol_btcd_rpc.hpp \
+ ${srcdir}/../../include/bitcoin/server/protocols/protocol_btcd.hpp \
${srcdir}/../../include/bitcoin/server/protocols/protocol_electrum.hpp \
${srcdir}/../../include/bitcoin/server/protocols/protocol_electrum_version.hpp \
${srcdir}/../../include/bitcoin/server/protocols/protocol_html.hpp \
@@ -164,20 +171,14 @@ include_bitcoin_server_protocols_HEADERS = \
${srcdir}/../../include/bitcoin/server/protocols/protocol_stratum_v2.hpp \
${srcdir}/../../include/bitcoin/server/protocols/protocols.hpp
-include_bitcoin_server_interfacesdir = \
- ${includedir}/bitcoin/server/interfaces
+include_bitcoin_server_sessionsdir = \
+ ${includedir}/bitcoin/server/sessions
-include_bitcoin_server_interfaces_HEADERS = \
- ${srcdir}/../../include/bitcoin/server/interfaces/admin.hpp \
- ${srcdir}/../../include/bitcoin/server/interfaces/bitcoind_rest.hpp \
- ${srcdir}/../../include/bitcoin/server/interfaces/bitcoind_rpc.hpp \
- ${srcdir}/../../include/bitcoin/server/interfaces/btcd.hpp \
- ${srcdir}/../../include/bitcoin/server/interfaces/electrum.hpp \
- ${srcdir}/../../include/bitcoin/server/interfaces/interfaces.hpp \
- ${srcdir}/../../include/bitcoin/server/interfaces/native.hpp \
- ${srcdir}/../../include/bitcoin/server/interfaces/stratum_v1.hpp \
- ${srcdir}/../../include/bitcoin/server/interfaces/stratum_v2.hpp \
- ${srcdir}/../../include/bitcoin/server/interfaces/types.hpp
+include_bitcoin_server_sessions_HEADERS = \
+ ${srcdir}/../../include/bitcoin/server/sessions/session.hpp \
+ ${srcdir}/../../include/bitcoin/server/sessions/session_handshake.hpp \
+ ${srcdir}/../../include/bitcoin/server/sessions/session_server.hpp \
+ ${srcdir}/../../include/bitcoin/server/sessions/sessions.hpp
# Tests.
#==============================================================================
@@ -209,6 +210,8 @@ test_libbitcoin_server_test_SOURCES = \
${srcdir}/../../test/main.cpp \
${srcdir}/../../test/settings.cpp \
${srcdir}/../../test/test.cpp \
+ ${srcdir}/../../test/interfaces/btcd.cpp \
+ ${srcdir}/../../test/mocks/blocks.cpp \
${srcdir}/../../test/parsers/admin_query.cpp \
${srcdir}/../../test/parsers/admin_target.cpp \
${srcdir}/../../test/parsers/bitcoind_target.cpp \
@@ -217,13 +220,6 @@ test_libbitcoin_server_test_SOURCES = \
${srcdir}/../../test/parsers/native_target.cpp \
${srcdir}/../../test/protocols/admin/admin_diagnostics.cpp \
${srcdir}/../../test/protocols/admin/admin_setup_fixture.cpp \
- ${srcdir}/../../test/protocols/native/native_address.cpp \
- ${srcdir}/../../test/protocols/native/native_block.cpp \
- ${srcdir}/../../test/protocols/native/native_configuration.cpp \
- ${srcdir}/../../test/protocols/native/native_input.cpp \
- ${srcdir}/../../test/protocols/native/native_output.cpp \
- ${srcdir}/../../test/protocols/native/native_setup_fixture.cpp \
- ${srcdir}/../../test/protocols/native/native_tx.cpp \
${srcdir}/../../test/protocols/bitcoind/bitcoind_json.cpp \
${srcdir}/../../test/protocols/bitcoind/bitcoind_rest.cpp \
${srcdir}/../../test/protocols/bitcoind/bitcoind_rpc.cpp \
@@ -243,7 +239,13 @@ test_libbitcoin_server_test_SOURCES = \
${srcdir}/../../test/protocols/electrum/electrum_subscribe.cpp \
${srcdir}/../../test/protocols/electrum/electrum_transactions.cpp \
${srcdir}/../../test/protocols/electrum/electrum_version.cpp \
- ${srcdir}/../../test/mocks/blocks.cpp
+ ${srcdir}/../../test/protocols/native/native_address.cpp \
+ ${srcdir}/../../test/protocols/native/native_block.cpp \
+ ${srcdir}/../../test/protocols/native/native_configuration.cpp \
+ ${srcdir}/../../test/protocols/native/native_input.cpp \
+ ${srcdir}/../../test/protocols/native/native_output.cpp \
+ ${srcdir}/../../test/protocols/native/native_setup_fixture.cpp \
+ ${srcdir}/../../test/protocols/native/native_tx.cpp
TESTS = test_runner.sh
diff --git a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj
index af97a969..6c88926e 100644
--- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj
@@ -120,6 +120,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
index 5264f999..d60a9e5c 100644
--- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
@@ -10,30 +10,33 @@
{66A0E586-2E3A-448F-0000-000000000000}
-
+
{66A0E586-2E3A-448F-0000-000000000001}
-
+
{66A0E586-2E3A-448F-0000-000000000002}
-
+
{66A0E586-2E3A-448F-0000-000000000003}
-
+
{66A0E586-2E3A-448F-0000-000000000004}
-
+
{66A0E586-2E3A-448F-0000-000000000005}
-
+
{66A0E586-2E3A-448F-0000-000000000006}
-
+
{66A0E586-2E3A-448F-0000-000000000007}
-
+
{66A0E586-2E3A-448F-0000-000000000008}
+
+ {66A0E586-2E3A-448F-0000-000000000009}
+
@@ -42,6 +45,9 @@
src
+
+ src\interfaces
+
src
diff --git a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj
index c865858b..44738f13 100644
--- a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj
@@ -127,6 +127,7 @@
+
@@ -134,9 +135,9 @@
-
-
-
+
+
+
@@ -166,7 +167,6 @@
-
@@ -189,6 +189,7 @@
+
@@ -197,7 +198,7 @@
-
+
diff --git a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters
index 4304d63a..d98c5445 100644
--- a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters
@@ -87,6 +87,9 @@
src\parsers
+
+ src\parsers
+
src\parsers
@@ -108,13 +111,13 @@
src\protocols\bitcoind
-
+
src\protocols\btcd
-
+
src\protocols\btcd
-
+
src\protocols\btcd
@@ -200,9 +203,6 @@
include\bitcoin\server\channels
-
- include\bitcoin\server\channels
-
include\bitcoin\server\channels
@@ -269,6 +269,9 @@
include\bitcoin\server\parsers
+
+ include\bitcoin\server\parsers
+
include\bitcoin\server\parsers
@@ -293,7 +296,7 @@
include\bitcoin\server\protocols
-
+
include\bitcoin\server\protocols
diff --git a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj
index b2ae5c35..68c1ede7 100644
--- a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj
+++ b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj
@@ -120,6 +120,7 @@
+
diff --git a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
index 5264f999..d60a9e5c 100644
--- a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
+++ b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
@@ -10,30 +10,33 @@
{66A0E586-2E3A-448F-0000-000000000000}
-
+
{66A0E586-2E3A-448F-0000-000000000001}
-
+
{66A0E586-2E3A-448F-0000-000000000002}
-
+
{66A0E586-2E3A-448F-0000-000000000003}
-
+
{66A0E586-2E3A-448F-0000-000000000004}
-
+
{66A0E586-2E3A-448F-0000-000000000005}
-
+
{66A0E586-2E3A-448F-0000-000000000006}
-
+
{66A0E586-2E3A-448F-0000-000000000007}
-
+
{66A0E586-2E3A-448F-0000-000000000008}
+
+ {66A0E586-2E3A-448F-0000-000000000009}
+
@@ -42,6 +45,9 @@
src
+
+ src\interfaces
+
src
diff --git a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj
index 1aeb9b5a..318a680f 100644
--- a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj
+++ b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj
@@ -127,6 +127,7 @@
+
@@ -134,9 +135,9 @@
-
-
-
+
+
+
@@ -166,7 +167,6 @@
-
@@ -189,6 +189,7 @@
+
@@ -197,7 +198,7 @@
-
+
diff --git a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters
index 4304d63a..d98c5445 100644
--- a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters
+++ b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters
@@ -87,6 +87,9 @@
src\parsers
+
+ src\parsers
+
src\parsers
@@ -108,13 +111,13 @@
src\protocols\bitcoind
-
+
src\protocols\btcd
-
+
src\protocols\btcd
-
+
src\protocols\btcd
@@ -200,9 +203,6 @@
include\bitcoin\server\channels
-
- include\bitcoin\server\channels
-
include\bitcoin\server\channels
@@ -269,6 +269,9 @@
include\bitcoin\server\parsers
+
+ include\bitcoin\server\parsers
+
include\bitcoin\server\parsers
@@ -293,7 +296,7 @@
include\bitcoin\server\protocols
-
+
include\bitcoin\server\protocols
diff --git a/docs/btcd-endpoint.md b/docs/btcd-endpoint.md
deleted file mode 100644
index 94daa8f5..00000000
--- a/docs/btcd-endpoint.md
+++ /dev/null
@@ -1,377 +0,0 @@
-# btcd-Compatible Endpoint
-
-This document specifies the `btcd` JSON-RPC/WebSocket compatibility endpoint for
-libbitcoin-server, modeled on the existing `bitcoind` endpoint (see
-`src/protocols/bitcoind/`, added in #795) and on
-[btcd's JSON-RPC API](https://btcd.readthedocs.io/en/latest/json_rpc_api.html).
-
-## Background
-
-btcd is a Bitcoin full node written in Go that intentionally separates wallet and
-chain services into independent processes: unlike Core/bitcoind, a direct
-connection to btcd exposes only chain RPCs, never wallet methods. That matches
-libbitcoin-server's own scope (no wallet), which is what makes btcd a good second
-compatibility target after bitcoind.
-
-btcd exposes two transports on the same port (default `8334`):
-
-- **HTTP POST**, one JSON-RPC request/response per connection (same shape as the
- existing `bitcoind` endpoint).
-- **WebSocket** (`/ws`), btcd's preferred transport: a single connection carries
- many requests plus asynchronous push notifications (new blocks, filtered
- transactions, rescan progress).
-
-Real btcd authenticates via TLS + HTTP Basic Auth, with two credential tiers:
-`rpcuser`/`rpcpass` (full access) and `rpclimituser`/`rpclimitpass` (restricted to
-a fixed allowlist of chain-read methods). **Decision: this endpoint does not build
-a bespoke two-tier scheme.** It reuses `network::settings::http_server`'s
-credential model as-is (`btcd.credential = username:password[:method,...]`, same
-config shape as `bitcoind.*`), which happens to already support scoping a
-credential to a method allowlist generically; `channel_btcd::permitted()` enforces
-that scope over both http and ws. Sensitive methods (`stop`, mining/peer
-management) are stubbed to `not_implemented` unconditionally regardless of scope,
-so this is revisited only if a real `stop` path is implemented later.
-
-## Architecture
-
-```
-network::protocol_http
-└── server::protocol_bitcoind_rpc
- └── server::protocol_btcd_rpc (new)
-
-network::channel_http
-└── server::channel_http
- └── server::channel_btcd (new)
-
-server::session_btcd = session_server (new)
-```
-
-`protocol_btcd_rpc` inherits `protocol_bitcoind_rpc`, which already implements the
-chain-read/rawtx handlers both APIs share (`getbestblockhash`, `getblock`,
-`getblockhash`, `getblockheader`, `getblockcount`, `gettxout`, `getrawtransaction`,
-`sendrawtransaction`, ...). It adds a second dispatcher for `interface::btcd`
-(btcd-only methods) and overrides `network::protocol_http::dispatch_websocket()` —
-the extension point `protocol_http` already exposes for handling an HTTP→WS
-upgrade — to route btcd's WS-only notification/subscription methods.
-
-`channel_btcd` extends `channel_http` with an `authenticated_` flag. Nothing is
-composed or re-sent per ws message -- ws data frames structurally cannot carry
-a per-message Authorization header at all. Instead, authorization for the
-whole connection is established exactly **once**, by whichever happens first:
-real Basic Auth on the one-time ws upgrade request (a normal HTTP request,
-with real headers, checked the same way a plain http request is), or the
-in-band `authenticate` command, called once as a substitute for that header.
-Either way the result is a cached credential digest (`authenticated_`/
-`authenticated_digest_`) consulted on every later call via `permitted()`, not
-re-verified against anything sent with that call. Plain HTTP POST traffic
-(the inherited bitcoind-style chain methods) is unaffected by any of this --
-it keeps real per-request Basic Auth headers, checked on every request, same
-as bitcoind's own endpoint always did.
-
-**A `session_handshake` variant of this was
-tried and reverted.** The idea was to model btcd's auth on
-`protocol_electrum_version`'s handshake pattern: a dedicated protocol object that
-gates attachment of the real one. It doesn't fit. `session_handshake` calls
-`channel->resume()` immediately after the handshake protocol attaches, on the
-assumption that the handshake can only complete by reading a real message
-(true for electrum's `server.version`, true for p2p's version message) — but
-btcd's handshake, when no credential is configured, completes without reading
-anything at all. That created a real async gap between "handshake decided
-it's done" and "`protocol_btcd_rpc` has actually subscribed its handlers" —
-harmless for a websocket client (the upgrade round-trip masks it), but a
-plain HTTP client can land a request in that gap before anything is
-listening, and did, reliably, in testing. Real btcd has no such gap because
-it has no handshake protocol at all: `session_server` (used here) never calls
-`resume()` until *after* `attach_protocols()` has synchronously subscribed
-the real handlers — the same shape bitcoind already uses, and the reason
-bitcoind never had this problem. `authenticate` is just an ordinary
-extension method on `protocol_btcd_rpc`, exactly matching real btcd, where it
-is described as "disallowed when basic auth has already been established" —
-a one-time alternative to a Basic Auth header (see the note above on how
-authorization is cached, not composed, for ws traffic), not a mandatory step
-every connection passes through.
-
-Notification/subscription state (watched blocks, watched scripts/outpoints)
-follows the pattern already established in `protocol_electrum`
-(`src/protocols/electrum/protocol_electrum.cpp`): a small map guarded by a
-dedicated notification strand, populated by `node::chase` event callbacks, fed to
-clients via `do_`/`complete_*` handlers. Model the btcd notifier on that
-shape rather than inventing a new one.
-
-New files:
-
-- `include/bitcoin/server/channels/channel_btcd.hpp`
-- `include/bitcoin/server/protocols/protocol_btcd_rpc.hpp`
-- `include/bitcoin/server/interfaces/btcd.hpp` (method table, see
- `interfaces/bitcoind_rpc.hpp` for the pattern)
-- `src/protocols/btcd/protocol_btcd_rpc.cpp`
-- `src/protocols/btcd/protocol_btcd_rpc_subscribe.cpp`
-- `test/protocols/btcd/btcd_rpc.cpp`
-
-Modified: `channels.hpp`, `protocols.hpp`, `sessions.hpp`, `settings.hpp`,
-`server_node.hpp`/`.cpp`, `parser.cpp` (all following the `bitcoind` wiring as a
-template).
-
-## Method Scope
-
-### Inherited from `protocol_bitcoind_rpc`
-
-`getbestblockhash`, `getblock`, `getblockhash`, `getblockheader`, `getblockcount`,
-`gettxout`, `getrawtransaction`, `sendrawtransaction`. No btcd-specific work
-needed; response shapes already match Core/btcd conventions closely enough as-is.
-**Bridged into the ws dispatcher**: `dispatch_websocket` tries
-the btcd-only extension dispatcher first, then falls back to
-`protocol_bitcoind_rpc::dispatch_rpc` when that dispatcher reports
-`unexpected_method` — a pure method-name lookup miss, never conflated with an
-argument-mismatch error, so the fallback cannot mask a real handler fault.
-`send_rpc` (the shared sender all inherited chain handlers funnel through)
-branches on `websocket()`: the post path is unchanged (still derives headers
-from the cached `http::request`), while the ws path sends a minimal response
-with no header enrichment — ws frames are synthesized with no real headers to
-echo in the first place (`channel_http::create_request()`), matching
-`protocol_btcd_rpc`'s own ws senders. A ws-connected client (required for
-`authenticate`/`session`/`notifyblocks`) can now also call `getblockcount`
-etc. on that same connection, as a real lnd/btcwallet client needs.
-
-**`getblockchaininfo`** (shared with `bitcoind`, not btcd-only) also reports a
-`bip9_softforks.taproot` entry unconditionally — real btcd includes this too
-(taproot is a permanent, long-since-locked-in consensus rule, no live
-signaling left to track). Added specifically because lnd's
-`chainreg.backendSupportsTaproot` requires this key's presence before it will
-treat *any* btcd/bitcoind backend as usable.
-
-### btcd-only methods — implemented
-
-- **`authenticate`**, **`session`** — ordinary extension methods, not a
- handshake stage (see [Architecture](#architecture)); `session` returns a
- real per-channel id, `btcwallet` uses it to detect reconnect-to-a-fresh-
- server and re-arm filters.
-- **`notifyblocks`/`stopnotifyblocks`** — subscribes to
- `node::chase::organized`/`reorganized`, pushes `blockconnected`/
- `blockdisconnected` (positional `[hash, height, time]`, verified against
- `btcsuite/btcd/btcjson/chainsvrwsntfns.go`).
-- **`getcurrentnet`** — the network-match check lnd/btcwallet performs once at
- connect; returns `network_settings().identifier`, the same p2p handshake
- magic real btcd returns.
-- **`loadtxfilter`** + filtered block notifications
- (`filteredblockconnected`/`filteredblockdisconnected`) + **`rescanblocks`** —
- a per-connection in-memory watch-list (base58 p2kh/p2sh via
- `wallet::payment_address`, bech32/bech32m p2wpkh/p2wsh/p2tr via
- `wallet::witness_address` + `chain::script::output_pattern()`, plus
- outpoints), matched by direct script inspection of each newly-connected (or,
- for `rescanblocks`, explicitly named historical) block's own transactions —
- not the persisted address index (`address_enabled`/`get_history`), which is
- the wrong tool for "does this one block match this small watch-list" and
- would needlessly require the address index to be built at all. An output
- that matches a watched address has its own outpoint auto-added to the
- watched set (matching real btcd's `wsClientFilter`), so a later spend of it
- also matches without an explicit re-subscribe. This is the mechanism both
- `btcwallet`'s `chain.RPCClient` and lnd's own `chainntnfs/btcdnotify`
- depend on (see [lnd Compatibility](#lnd-compatibility)).
-- **`getbestblock`** — a distinct btcd extension from `getbestblockhash`,
- returning `{hash, height}` of the chain tip together. Found to be required
- by `btcwallet`'s own separate rpcclient connection (not lnd's own
- `chain.RPCClient`, which uses `getbestblockhash`) during wallet chain-sync
- bootstrap, via a real lnd integration test.
-- **`rescan`** (deprecated upstream, superseded by `rescanblocks`) — implemented
- only for its empty-addresses/empty-outpoints case: real btcd's own
- `handleRescan` skips scanning entirely and reports immediate completion
- (`rescanfinished` notification carrying the current chain tip) when given
- nothing to watch. This is the exact call `btcwallet`'s own rpcclient makes
- purely to bootstrap its initial sync starting point — found via the same
- real lnd integration test. A non-empty address/outpoint list remains
- `not_implemented` (see [Permanently stubbed](#permanently-stubbed-not_implemented)).
-- Every btcd-only method above is also reachable over plain HTTP POST, not
- only the websocket connection — real lnd/btcwallet clients issue
- capability-check calls (e.g. `getinfo`) this way immediately on connect,
- before establishing the persistent ws connection that subscriptions need.
- Found missing (a real, silent connection-killing bug) via a live lnd
- integration test; fixed by adding `protocol_bitcoind_rpc::
- dispatch_extension`, the post-side mirror of the ws-side fallback described
- above.
-- **`getdifficulty`, `getinfo`, `getnettotals`, `getnetworkhashps`,
- `createrawtransaction`, `decoderawtransaction`, `decodescript`,
- `validateaddress`, `help`** — no specific named consumer; these round out
- compatibility for generic btcd-speaking tooling (block explorers,
- mining-pool stats, monitoring dashboards, tx-building/debug scripts), not
- lnd.
-
-Notes on scope/fidelity (all verified against real btcd's `btcjson`
-result/command structs before implementing):
-
-- `createrawtransaction`/`decoderawtransaction`/`decodescript` reuse existing
- serializers rather than re-deriving them: `bitcoind(tx)`
- (`system::chain::json::transaction`) already produces the bare
- decoderawtransaction shape (`inject_tx_context` — block-context fields —
- is a separate, additive call, skipped here since a standalone hex tx has no
- block context); `chain::script::to_string()` already produces the "asm"
- disassembly. There is no existing script-type classifier
- (pubkeyhash/scripthash/multisig/etc.) or reverse address encoder anywhere
- in the codebase, so `decodescript`'s `type`/`address`/`p2sh` fields and
- `validateaddress` are new, built from `chain::script::output_pattern()` and
- `wallet::payment_address`/`witness_address` (the same dual-parse approach
- as `loadtxfilter`'s, kept as a separate small helper rather than forcing
- a shared abstraction onto that already-tested code).
-- `getnetworkhashps` approximates from the requested height's difficulty and
- a fixed 600s target spacing, not a true windowed average over the `blocks`
- parameter — there is no existing big-integer cumulative-work-over-a-range
- helper to build a real windowed estimate from, and this method has no named
- consumer, so the simpler approximation was chosen over adding one.
-- `getnettotals`'s byte counters are not tracked by this node and are
- reported as zero, matching the same honesty convention the inherited
- bitcoind `getnetworkinfo` handler already uses for untracked
- peer-dependent fields (rather than `not_implemented`).
-- `help` lists method names only; no per-command argument usage text.
-
-### Permanently stubbed (`not_implemented`)
-
-- **`stop`** — no secure remote-shutdown path exists in libbitcoin-server; always
- returns `not_implemented` regardless of auth tier or config.
-- **`notifynewtransactions`**, and the deprecated WS methods `notifyreceived`,
- `stopnotifyreceived`, `notifyspent`, `stopnotifyspent` (all superseded
- upstream by `loadtxfilter`/`rescanblocks`) — no mempool exists in v4, revisit in
- v5.
-- **`rescan`'s non-empty-address/outpoint case** — a real historical block scan
- (chunked `rescanprogress` notifications, per-tx `recvtx`/`redeemingtx`
- notifications — a different wire shape than `filteredblockconnected`'s
- per-block shape —, reorg recovery) that no observed real caller needs;
- `loadtxfilter`/`rescanblocks` already cover actual address/outpoint watching
- for lnd. The empty-case bootstrap call is implemented (see above).
-- **`submitblock`** — not in current scope.
-- Mining (`getgenerate`, `gethashespersec`, `getmininginfo`, `setgenerate`) and
- peer-management (`addnode`, `getaddednodeinfo`, `getconnectioncount`,
- `getpeerinfo`) — out of scope; libbitcoin-server is not a miner and peer
- management is a node-config concern, not an RPC surface here.
-
-## lnd Compatibility
-
-btcd's own websocket API is one of lnd's three supported chain backends (the
-others being `bitcoind` via ZMQ, and `neutrino`). Hooking an `lnd` node up to
-libbitcoin-server through this endpoint means satisfying the calls
-`btcwallet`'s `chain.RPCClient` (`btcsuite/btcwallet/chain/btcd.go`) and lnd's own
-`chainntnfs/btcdnotify` package make against a btcd-style server.
-
-**Verified end-to-end against a real lnd 0.21.1-beta binary**: pointed
-directly at a libbitcoin-server instance running this endpoint
-(`--bitcoin.node=btcd`), lnd reaches `Chain backend is fully synced!` —
-wallet opened/unlocked, chain-sync bootstrap completed, headers walked to
-the live chain tip. Every gap below was found this way, not by reading
-source alone; each is confirmed fixed by a subsequent clean run one step
-further than the last.
-
-**Plain RPC** (all either inherited from bitcoind or btcd extensions):
-`getbestblockhash`/`getblockcount` (chain tip), `getblockhash`, `getblockheader`
-(chain-view / `IsCurrent` checks), `getblock` (verbose, block scanning),
-`getrawtransaction`, `sendrawtransaction` (broadcasting funding/commitment/
-sweep/justice transactions), `gettxout` (UTXO-existence checks), `getcurrentnet`
-(network-match validation performed once at connect), `getbestblock`
-(`btcwallet`'s own separate rpcclient connection, used during its own
-chain-sync bootstrap — distinct from `getbestblockhash`).
-
-**WebSocket notification surface.** Both `btcwallet`'s RPCClient
-and lnd's `btcdnotify` depend on:
-
-- `NotifyBlocks` → `OnBlockConnected`/`OnBlockDisconnected` — implemented.
-- A working `session` on connect — `btcwallet` uses the session id to detect a
- reconnect to a fresh server instance, which must trigger re-registration of all
- filters/rescans. Implemented for real (not stubbed), for exactly this reason.
-- **`loadtxfilter`** (address/outpoint watch-list registration) plus
- block-filtered notification delivery (the modern wire methods
- `filteredblockconnected`/`filteredblockdisconnected`, which drive lnd's
- `OnFilteredBlockConnected` client callback — verified against
- `btcsuite/btcd/btcjson/chainsvrwsntfns.go` and `rpcclient/notify.go`; the
- deprecated per-tx `OnRecvTx`/`OnRedeemingTx` callbacks are fed by a
- different, older wire pair (`recvtx`/`redeemingtx`) that modern
- `btcwallet`/`lnd` don't use) — implemented. This is the mechanism
- both the wallet and lnd's own notifier use to detect channel-funding
- confirmations and on-chain spends of channel outputs (breach/force-close
- detection); not optional legacy behavior.
-- **`rescanblocks`** — used at wallet startup (birthday scan) and after any
- downtime to replay historical blocks against the loaded filter —
- implemented, same match logic as live filtered notifications,
- applied to explicitly named blocks instead of the newly-connected one.
-- **`rescan`** (older, deprecated upstream) — `btcwallet`'s own rpcclient
- still calls this once, with an empty address/outpoint list, purely to
- bootstrap its initial sync starting point; implemented for that case (see
- [Method Scope](#method-scope)). A general historical address/outpoint scan
- remains stubbed; no observed real caller needs it.
-
-**Hard blocker: mempool.** Both sources confirm unconfirmed-transaction awareness
-is an exercised, not merely optional, code path: `relevanttxaccepted` (the modern
-wire method, driving lnd's `OnRelevantTxAccepted` callback; armed by
-`notifynewtransactions`, already permanently stubbed — see above) fires for a
-mempool-resident transaction matching the loaded filter, and
-lnd's notifier has a dedicated mempool-spend lookup path
-(`LookupInputMempoolSpend`) used for fast breach/force-close reaction ahead of
-confirmation. **libbitcoin-server v4 has no mempool**, so `relevanttxaccepted`
-cannot be implemented now — the confirmed-only fallback (`filteredblockconnected`)
-covers the same address/outpoint watch-list, just one block
-later than a mempool-aware node would, so it means lnd would lose
-unconfirmed-spend awareness and any zero-conf-style UX. Final safety in Lightning
-still rests on confirmation + CSV/CLTV timeout margins, so this is a
-responsiveness/UX degradation rather than a correctness break, but it's a real,
-user-visible limitation worth stating plainly rather than glossing over. Revisit
-once a v5 mempool exists.
-
-**Not needed for lnd**: fee estimation — lnd's default fee estimator for the
-`btcd` backend is a web API (`chainfee.WebAPIEstimator`), not an RPC call to the
-node, so `estimatesmartfee`-equivalent support is not on the critical path.
-
-## Configuration
-
-New `[btcd]` section, mirroring `[bitcoind]`'s shape in `parser.cpp`/
-`settings.hpp` (`bind`, `safe`, `cert_auth`, `cert_path`, `key_path`, `key_pass`,
-`credential`, `connections`, `inactivity_minutes`,
-`expiration_minutes`, `minimum_buffer`, `maximum_request`, `host`, `origin`,
-`allow_opaque_origin`). No dedicated `stop`-gating config key: `stop` has no
-conditional path at all right now (always `not_implemented`, unconditionally),
-so a flag controlling it would be dead configuration. If a real guarded `stop`
-path is ever implemented, add the gating key then.
-
-Default port: `8334` (matches upstream btcd).
-
-## Code Conventions (apply from the outset, per #795 review feedback)
-
-The `bitcoind` PR (#795) went through several review rounds with @evoskuil before
-merging; apply these proactively on the btcd PR to avoid repeating the same
-cycle:
-
-- No inline (header-implemented) non-template method bodies — put them in
- `.cpp` files; keeps build/rebuild times down for a developer-facing lib.
-- Protocol-specific json/serialization helpers are protected static methods on
- the base protocol class (inherited by subclasses), not free functions in the
- `server` namespace. Split into a `_json.cpp` for organization if it grows.
-- Pass `hash_cptr` and other small shared_ptr objects as `const&`, never by
- value — a copy needlessly bumps the refcount and hits a shared lock.
-- Prefer `to_shared(std::move(x))` over manual move-then-construct.
-- Use `std::from_chars` (C++17) directly; add an explicit leading-zero guard if
- canonical parsing matters (`from_chars` alone accepts `"01"`).
-- Arithmetic: `add1()`, `floored_subtract()` — never raw `-`, the store is
- concurrent and `top >= height` is not assured — `possible_sign_cast<>`,
- `possible_wide_cast<>`, `is_lesser()` over raw casts,
- `ceilinged_add()`/`is_overflow()` for additions that could overflow,
- `std::next()` instead of pointer arithmetic.
-- Cache constants that are invariant for the process lifetime (e.g. genesis
- hash lookups) as function-local `static`; use `constexpr base16_hash("...")`
- instead of runtime `encode_hash`/string comparisons in hot paths.
-- Use the query layer's existing fluent methods instead of re-deriving values:
- `get_confirmed_headers` + `get_wire_header` instead of hand-looping
- `chain::header` objects, `get_tx_height(height, link)` alone instead of extra
- containment checks, the stored context field (single `get_context` query) for
- median-time-past instead of recomputing over 11 headers.
-- Stream into one pre-sized buffer (`stream::out::fast` + a typed writer)
- instead of looping and copying per item. Don't mask a per-item store failure
- inside such a loop as "not found" — that's `database::error::integrity`
- (`send_internal_server_error`), a different failure class.
-- Naming: `send_text()`/`send_json()`/`to_data()`/`to_text()` — "data" means
- byte array, "text" means base16 string; avoid "hex"/"bin" abbreviations.
-- Pure type→json serialization belongs in **libbitcoin-system** as a
- `chain/json/` serializer (see `system/chain/json/block.hpp`+`.cpp`+
- test), not hand-rolled long-term in the protocol layer. File a system-repo PR
- if a new btcd-only json shape is needed, rather than keeping it local.
-- Tests: Boost.Test only for Unit/Component/Functional tiers, no external
- dependencies (no Python) — that's `test/protocols/btcd/`. Data-driven loops
- only over genuine external vectors; no line-wrapping; precompute `constexpr`
- hashes/expectations at file scope. The Python suite under `endpoints/` is a
- separate, optional **acceptance** tier (run against a live compiled node),
- not a merge requirement.
diff --git a/endpoints/README.md b/endpoints/README.md
index 84355dee..8294f9a1 100644
--- a/endpoints/README.md
+++ b/endpoints/README.md
@@ -124,7 +124,7 @@ pytest test_bitcoind_rpc.py \
--bitcoind-rpc-port=8332
```
-**With authentication (for Bitcoin Core):**
+**With authentication (for bitcoind):**
```bash
pytest test_bitcoind_rpc.py \
@@ -241,7 +241,7 @@ pytest test_native.py -k "not address"
### test_bitcoind_rpc.py — bitcoind RPC Compatibility
-Tests JSON-RPC 2.0 interface compatible with Bitcoin Core RPC.
+Tests JSON-RPC 2.0 interface compatible with the bitcoind RPC interface.
**Coverage:**
- ✅ Blockchain methods (17 methods)
@@ -271,10 +271,8 @@ pytest test_bitcoind_rpc.py -k "getblock"
Tests the btcd-compatible endpoint: JSON-RPC 1.0 over a persistent websocket
connection (session/notification/filter/admin extension methods) plus plain
-http post (chain methods inherited from `protocol_bitcoind_rpc`, same shape
-as `bitcoind`). See `docs/btcd-endpoint.md` for the full design, method
-scope, and the lnd/btcwallet compatibility analysis (verified end-to-end
-against a real lnd binary).
+http post (bitcoind interface methods inherited from `protocol_bitcoind_rpc`, same shape
+as `bitcoind`).
Unlike the other endpoint suites, this one is explicitly split into two
kinds of test:
@@ -293,7 +291,7 @@ kinds of test:
- ✅ Block subscription — `notifyblocks`/`stopnotifyblocks` (ack), a real
`blockconnected` push-notification test (positional `[hash, height, time]`,
verified against upstream btcd's actual wire format)
-- ✅ Chain methods, both over http post and over the persistent ws
+- ✅ bitcoind interface methods, both over http post and over the persistent ws
connection (`getbestblockhash`, `getblockcount`, `getblockhash`,
`getblockheader`, `gettxout`, `getrawtransaction`)
- ✅ `loadtxfilter`/`rescanblocks` (address/outpoint watch-list, filtered
@@ -593,7 +591,7 @@ SKIP: No response (possibly unsupported method)
```
Some public servers (electrs in particular) do not implement all Electrum methods and simply do not respond. The test skips cleanly. This is not a test failure.
-**6. Authentication errors (Bitcoin Core)**
+**6. Authentication errors (bitcoind)**
```
RuntimeError: RPC connection error: 401 Unauthorized
```
@@ -726,7 +724,7 @@ When adding new tests:
- [pytest Documentation](https://docs.pytest.org/)
- [libbitcoin-server Documentation](../../docs/)
-- [Bitcoin Core RPC Documentation](https://developer.bitcoin.org/reference/rpc/)
+- [bitcoind RPC documentation](https://developer.bitcoin.org/reference/rpc/)
- [Electrum Protocol Documentation](https://electrumx.readthedocs.io/en/latest/protocol.html)
- [Electrum Protocol Changelog](https://electrumx.readthedocs.io/en/latest/protocol-changes.html)
- [libbitcoin-server GitHub](https://github.com/libbitcoin/libbitcoin-server)
diff --git a/endpoints/test_bitcoind_rest.py b/endpoints/test_bitcoind_rest.py
index c82475bb..fcfbff5f 100644
--- a/endpoints/test_bitcoind_rest.py
+++ b/endpoints/test_bitcoind_rest.py
@@ -1,7 +1,7 @@
"""
Tests for libbitcoin-server bitcoind REST compatibility interface.
-Covers the Bitcoin Core REST endpoints served under /rest/: chaininfo, block,
+Covers the bitcoind REST endpoints served under /rest/: chaininfo, block,
block/notxdetails, block/spent (libbitcoin extension), blockhashbyheight,
headers, blockpart (libbitcoin extension), and the basic (neutrino) filters.
diff --git a/endpoints/test_bitcoind_rpc.py b/endpoints/test_bitcoind_rpc.py
index 70f5e7c9..6d070047 100644
--- a/endpoints/test_bitcoind_rpc.py
+++ b/endpoints/test_bitcoind_rpc.py
@@ -1,7 +1,7 @@
"""
Tests for libbitcoin-server bitcoind RPC compatibility interface.
-Tests the JSON-RPC 2.0 interface that provides Bitcoin Core compatible
+Tests the JSON-RPC 2.0 interface that provides bitcoind compatible
RPC methods for blockchain queries.
Run with:
@@ -172,7 +172,7 @@ def raw_rpc(
# ═══════════════════════════════════════════════════════════════════════════════
def test_getbestblockhash(bitcoind_rpc_config):
- """Test getbestblockhash - returns hash of the best (tip) block"""
+ """Test getbestblockhash - returns hash of the top block"""
response = send_rpc(bitcoind_rpc_config, "getbestblockhash")
assert "error" not in response or response["error"] is None
@@ -222,7 +222,7 @@ def test_getblockchaininfo(bitcoind_rpc_config):
assert "chain" in result
# chainwork and size_on_disk are intentionally omitted by the implementation:
# chainwork needs a cumulative-work index (cf. getchainwork, not implemented)
- # and size_on_disk needs store-size accounting. The remaining Core fields are
+ # and size_on_disk needs store-size accounting. The remaining bitcoind fields are
# returned.
assert "difficulty" in result
assert "headers" in result
@@ -500,7 +500,7 @@ def test_getrawtransaction_segwit(bitcoind_rpc_config):
assert result["hash"] != result["txid"]
# Segwit weight accounting: vsize == ceil(weight / 4).
# (Note: libbitcoin reports "size" as the stripped, non-witness size, whereas
- # Core reports the total witnessed size, so size/vsize ordering is not
+ # bitcoind reports the total witnessed size, so size/vsize ordering is not
# asserted here.)
if "weight" in result and "vsize" in result:
assert result["vsize"] == (result["weight"] + 3) // 4
diff --git a/endpoints/test_btcd_rpc.py b/endpoints/test_btcd_rpc.py
index bc73e7e1..49bd9c96 100644
--- a/endpoints/test_btcd_rpc.py
+++ b/endpoints/test_btcd_rpc.py
@@ -2,10 +2,9 @@
Tests for libbitcoin-server btcd JSON-RPC/websocket compatibility interface.
btcd speaks JSON-RPC 1.0 over a persistent websocket connection (preferred,
-required for the session/notification extension methods) or plain HTTP POST
-(same request/response shape as the bitcoind endpoint, for the chain methods
-inherited from protocol_bitcoind_rpc). See docs/btcd-endpoint.md for the full
-design and phased scope.
+required for the session/notification methods) or plain HTTP POST
+(same request/response shape as the bitcoind endpoint, for the bitcoind
+interface methods inherited from protocol_bitcoind_rpc).
This suite is split into what's real today and what's a development target:
@@ -259,7 +258,7 @@ def read_notification(self, timeout_s: float) -> Optional[dict]:
def http_rpc(config: dict, method: str, params: Optional[list] = None) -> dict:
"""Plain http post json-rpc call to the same btcd endpoint (no ws upgrade).
- The chain methods inherited from protocol_bitcoind_rpc are reachable this
+ The bitcoind interface methods inherited from protocol_bitcoind_rpc are reachable this
way today even though they aren't yet bridged into the ws dispatcher.
"""
payload = {
@@ -290,7 +289,7 @@ def conn(btcd_config: dict) -> BtcdConnection:
the server has credentials configured.
Every method other than 'authenticate' itself is rejected over ws until
- the connection has authenticated (see protocol_btcd_rpc::dispatch_
+ the connection has authenticated (see protocol_btcd::dispatch_
websocket) -- a real client always does this handshake first, so this
fixture does it here rather than in every single test.
@@ -501,7 +500,7 @@ def test_rescan_no_addrs_no_outpoints_finishes_immediately(conn):
bootstrap its initial sync starting point (found via a real lnd
integration test), distinct from rescanblocks/loadtxfilter's actual
address-watching path. The finished notification always carries the
- current chain tip, not the requested beginblock.
+ current chain top, not the requested beginblock.
"""
response = conn.send_rpc("rescan", [ReferenceData.GENESIS_HASH, [], [], ""])
assert response.get("error") is None
@@ -569,18 +568,18 @@ def test_filteredblockconnected_notification(conn, btcd_config):
# ═══════════════════════════════════════════════════════════════════════════════
-# STANDARD CHAIN METHODS (bridged into the ws dispatcher)
+# BITCOIND INTERFACE METHODS (bridged into the ws dispatcher)
# ═══════════════════════════════════════════════════════════════════════════════
# Inherited from protocol_bitcoind_rpc. Reachable both over plain http post
# (unchanged from bitcoind) and over the same ws connection used
-# for session/notifyblocks/etc: protocol_btcd_rpc::dispatch_websocket falls
+# for session/notifyblocks/etc: protocol_btcd::dispatch_websocket falls
# back to protocol_bitcoind_rpc::dispatch_rpc when the btcd-only dispatcher
# reports "unexpected method". This is what a real lnd/btcwallet client
# needs -- it can't open a second plain-http connection once it has upgraded
# to ws, so authenticate/session/notifyblocks and getblockcount etc. must all
# work on the one connection.
-CHAIN_METHODS = [
+BITCOIND_METHODS = [
("getbestblockhash", []),
("getblockcount", []),
("getblockhash", [ReferenceData.GENESIS_HEIGHT]),
@@ -590,7 +589,7 @@ def test_filteredblockconnected_notification(conn, btcd_config):
]
-@pytest.mark.parametrize("method,params", CHAIN_METHODS)
+@pytest.mark.parametrize("method,params", BITCOIND_METHODS)
def test_chain_method_over_http_post(btcd_config, method, params):
"""Positive control: the chain logic itself works, reachable over plain
http post to the same endpoint (unchanged from bitcoind)."""
@@ -600,19 +599,19 @@ def test_chain_method_over_http_post(btcd_config, method, params):
assert "result" in data
-@pytest.mark.parametrize("method,params", CHAIN_METHODS)
+@pytest.mark.parametrize("method,params", BITCOIND_METHODS)
def test_chain_method_over_websocket(conn, method, params):
- """Standard chain methods now also work over the same ws connection used
- for session/notifyblocks/etc (see protocol_btcd_rpc::dispatch_websocket /
+ """bitcoind interface methods also work over the same ws connection used
+ for session/notifyblocks/etc (see protocol_btcd::dispatch_websocket /
protocol_bitcoind_rpc::dispatch_rpc)."""
response = conn.send_rpc(method, params)
assert "result" in response
-def test_btcd_and_chain_method_share_one_websocket_connection(conn):
+def test_btcd_and_bitcoind_method_share_one_websocket_connection(conn):
"""The actual point of the ws bridge: a single persistent ws connection -- the kind
a real lnd/btcwallet client opens once and keeps -- can reach both a
- btcd-only extension method (session) and a standard chain method
+ btcd interface method (session) and a bitcoind interface method
(getblockcount) without reconnecting or falling back to plain http post.
"""
session = conn.send_rpc("session")
@@ -627,7 +626,7 @@ def test_btcd_and_chain_method_share_one_websocket_connection(conn):
# ═══════════════════════════════════════════════════════════════════════════════
-# WIRED BUT NOT YET IMPLEMENTED (blocked on a v5 mempool, see docs/btcd-endpoint.md)
+# WIRED BUT NOT YET IMPLEMENTED (blocked on a v5 mempool)
# ═══════════════════════════════════════════════════════════════════════════════
# These methods are present in interfaces/btcd.hpp's method table today, but
# every handler unconditionally returns not_implemented. Each xfail here is a
@@ -675,11 +674,10 @@ def test_stop_always_not_implemented(conn):
# ═══════════════════════════════════════════════════════════════════════════════
-# PHASE C: generic btcd-tooling compatibility (implemented, no lnd consumer)
+# Generic btcd-tooling compatibility (implemented)
# ═══════════════════════════════════════════════════════════════════════════════
-# See docs/btcd-endpoint.md for scope/fidelity notes (getnetworkhashps is an
-# approximation, getnettotals's byte counters are untracked zeros, help lists
-# method names only).
+# getnetworkhashps is an approximation, getnettotals's byte counters are
+# untracked zeros, help lists method names only.
def test_getdifficulty_returns_number(conn):
response = conn.send_rpc("getdifficulty")
@@ -694,19 +692,10 @@ def test_getinfo_returns_object(conn):
def test_btcd_only_method_reachable_over_http_post(btcd_config):
- """A real lnd integration test found that btcwallet's chain.RPCClient
- issues capability-check calls (getblockchaininfo, then getinfo) over
- plain http post immediately on connect, before any subscription traffic
- that requires ws. getblockchaininfo (inherited from protocol_bitcoind_rpc)
- already worked over post; getinfo (a btcd-only method, previously only
- reachable via the ws dispatcher) did not -- protocol_bitcoind_rpc::
- handle_receive_post had no fallback to a derived class's own dispatcher,
- and unconditionally dropped the connection on any unrecognized method,
- so lnd's client retried forever without ever succeeding. Fixed via
- protocol_bitcoind_rpc::dispatch_extension (an override point,
- protocol_btcd_rpc's implementation tries btcd_dispatcher_) -- this is
- the post-side mirror of dispatch_websocket's existing ws-side fallback
- to the inherited chain dispatcher."""
+ """Real clients (btcwallet's chain.RPCClient) issue capability-check
+ calls (getblockchaininfo, then getinfo) over plain http post immediately
+ on connect, before any subscription traffic that requires ws. getinfo is
+ btcd-only, so this exercises the btcd post transport."""
data = http_rpc(btcd_config, "getinfo")
assert data.get("error") is None
assert isinstance(data.get("result"), dict)
diff --git a/endpoints/test_electrum.py b/endpoints/test_electrum.py
index 1f2d2cb3..17aa3ac8 100644
--- a/endpoints/test_electrum.py
+++ b/endpoints/test_electrum.py
@@ -560,7 +560,7 @@ def test_blockchain_block_headers_height_900000_with_cp():
assert isinstance(h, str) and len(h) == 64
-def test_blockchain_block_headers_20000_at_tip_with_cp():
+def test_blockchain_block_headers_20000_at_top_with_cp():
"""
Request 20,000 headers at a well-confirmed range with a checkpoint proof.
@@ -570,7 +570,7 @@ def test_blockchain_block_headers_20000_at_tip_with_cp():
the response is valid and internally consistent, not a specific count.
"""
start, count = 900000, 20000
- cp = start + count - 1 # = 919999, well below current tip
+ cp = start + count - 1 # = 919999, well below current top
result = send_rpc("blockchain.block.headers", [start, count, cp])
assert isinstance(result, dict)
@@ -589,9 +589,9 @@ def test_blockchain_block_headers_20000_at_tip_with_cp():
assert isinstance(h, str) and len(h) == 64
-def test_blockchain_block_headers_max_at_tip_with_cp():
+def test_blockchain_block_headers_max_at_top_with_cp():
"""
- Request as many headers as possible ending at the current chain tip.
+ Request as many headers as possible ending at the current chain top.
Uses count=20160 (max in the Electrum spec). The server will cap the
actual returned count at its own configured maximum. The test verifies
@@ -600,17 +600,17 @@ def test_blockchain_block_headers_max_at_tip_with_cp():
"""
count = 20160
- tip = send_rpc("blockchain.headers.subscribe")
+ top = send_rpc("blockchain.headers.subscribe")
# height key differs by version: 'height' (v1.3+) or 'block_height' (v1.0-v1.2)
- tip_height = (
- tip.get("height") or tip.get("block_height")
- if isinstance(tip, dict) else None
+ top_height = (
+ top.get("height") or top.get("block_height")
+ if isinstance(top, dict) else None
)
- if tip_height is None:
+ if top_height is None:
pytest.skip("blockchain.headers.subscribe did not return height")
- start = tip_height - count + 1
- cp = tip_height
+ start = top_height - count + 1
+ cp = top_height
result = send_rpc("blockchain.block.headers", [start, count, cp])
diff --git a/endpoints/test_native.py b/endpoints/test_native.py
index c7a9629b..6223578b 100644
--- a/endpoints/test_native.py
+++ b/endpoints/test_native.py
@@ -82,13 +82,13 @@ def test_configuration(native_config):
def test_top(native_config):
- """Test /v1/top endpoint - chain tip information."""
+ """Test /v1/top endpoint - chain top information."""
data = get_json(native_config["base_url"], "top")
if isinstance(data, int):
assert data >= 100_000, "Chain height should be at least 100k"
else:
assert isinstance(data, dict)
- height = data.get("height") or data.get("chain_height") or data.get("tip_height")
+ height = data.get("height")
assert isinstance(height, int)
assert height >= 100_000, "Chain height should be at least 100k"
diff --git a/include/bitcoin/server.hpp b/include/bitcoin/server.hpp
index 47a08daf..44e38fc4 100644
--- a/include/bitcoin/server.hpp
+++ b/include/bitcoin/server.hpp
@@ -22,20 +22,26 @@
#include
#include
#include
-#include
-#include
-#include
-#include
#include
-#include
#include
#include
#include
#include
#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#include
#include
#include
+#include
#include
#include
#include
@@ -44,7 +50,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -54,15 +60,9 @@
#include
#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
#endif
diff --git a/include/bitcoin/server/channels/channel_btcd.hpp b/include/bitcoin/server/channels/channel_btcd.hpp
deleted file mode 100644
index f5801670..00000000
--- a/include/bitcoin/server/channels/channel_btcd.hpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * Copyright (c) 2011-2026 libbitcoin developers
- *
- * This file is part of libbitcoin.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-#ifndef LIBBITCOIN_SERVER_CHANNELS_CHANNEL_BTCD_HPP
-#define LIBBITCOIN_SERVER_CHANNELS_CHANNEL_BTCD_HPP
-
-#include
-#include
-#include
-#include
-
-namespace libbitcoin {
-namespace server {
-
-class BCS_API channel_btcd
- : public server::channel,
- public network::channel_http,
- protected network::tracker
-{
-public:
- typedef std::shared_ptr ptr;
-
- inline channel_btcd(const network::logger& log,
- const network::socket::ptr& socket, uint64_t identifier,
- const node::configuration& config, const options_t& options) NOEXCEPT
- : server::channel(log, socket, identifier, config),
- network::channel_http(log, socket, identifier, config.network, options),
- network::tracker(log),
- options_(options)
- {
- }
-
- /// True once the client has authenticated over the ws 'authenticate'
- /// handshake (or basic auth was not configured, requiring none).
- inline bool authenticated() const NOEXCEPT
- {
- return authenticated_;
- }
-
- /// Latch the credential digest that satisfied the in-band 'authenticate'
- /// call (see protocol_btcd_rpc::handle_authenticate), so that permitted()
- /// can later apply that credential's method scoping to ws traffic.
- inline void set_authenticated(const system::hash_digest& digest) NOEXCEPT
- {
- authenticated_ = true;
- authenticated_digest_ = digest;
- }
-
- /// True if the credential that authorized this channel permits the rpc
- /// method. Plain http traffic defers entirely to channel_http's own
- /// header-latched digest (unchanged from bitcoind). For ws traffic, real
- /// btcd recognizes two alternative ways a connection can be authorized --
- /// basic auth already established on the ws upgrade request (also
- /// channel_http's header-latched digest, since the upgrade request has
- /// real headers even though subsequent ws data frames don't), or the
- /// in-band 'authenticate' call (digest latched above); its own docs
- /// describe 'authenticate' as "disallowed when basic auth has already
- /// been established" -- i.e. an alternative to it, not an additional
- /// requirement on top of it.
- inline bool permitted(const std::string& method) const NOEXCEPT override
- {
- if (!websocket())
- return network::channel_http::permitted(method);
-
- if (!options_.authorize())
- return true;
-
- if (network::channel_http::permitted(method))
- return true;
-
- return authenticated_ && options_.permitted(authenticated_digest_, method);
- }
-
-protected:
- using value_type = network::http::body::value_type;
-
- /// Preselect the json-rpc request parser for websocket frames, so that
- /// each btcd ws message is parsed the same way as an http post body.
- inline value_type websocket_body() const NOEXCEPT override
- {
- // There is no forwarding constructor so assign and move.
- network::http::body::value_type value{};
- value = network::rpc::request{};
- return value;
- }
-
- /// channel_http's authorized() only latches basic-auth from the ws
- /// upgrade request (the upgrade request has real headers); subsequent ws
- /// data frames are synthesized and structurally cannot carry a
- /// per-frame Authorization header. Deferring to the base check here
- /// would therefore reject every ws frame once a credential is
- /// configured, unless basic auth happened to be presented on the
- /// upgrade itself -- permitted() (above) is the real per-method gate for
- /// ws traffic, so this base check must never reject a ws frame on its
- /// own. The plain http post path (e.g. inherited bitcoind-compatible
- /// chain methods) keeps the normal per-request enforcement.
- inline bool authorized() const NOEXCEPT override
- {
- return websocket() ? true : network::channel_http::authorized();
- }
-
-private:
- // This is thread safe.
- const options_t& options_;
-
- // These are protected by strand.
- bool authenticated_{};
- system::hash_digest authenticated_digest_{};
-};
-
-} // namespace server
-} // namespace libbitcoin
-
-#endif
diff --git a/include/bitcoin/server/channels/channel_http.hpp b/include/bitcoin/server/channels/channel_http.hpp
index aab39f39..2fe2cac6 100644
--- a/include/bitcoin/server/channels/channel_http.hpp
+++ b/include/bitcoin/server/channels/channel_http.hpp
@@ -27,10 +27,13 @@
namespace libbitcoin {
namespace server {
+/// Channel for http services, websocket frames read as Body (a json-rpc
+/// service instantiates with network::rpc::request).
+template
class BCS_API channel_http
: public server::channel,
public network::channel_http,
- protected network::tracker
+ protected network::tracker>
{
public:
typedef std::shared_ptr ptr;
@@ -40,19 +43,19 @@ class BCS_API channel_http
const node::configuration& config, const options_t& options) NOEXCEPT
: server::channel(log, socket, identifier, config),
network::channel_http(log, socket, identifier, config.network, options),
- network::tracker(log)
+ network::tracker>(log)
{
}
protected:
using value_type = network::http::body::value_type;
- /// Overridden to change default websocket reader to string.
+ /// Overridden to set the websocket reader body type.
inline value_type websocket_body() const NOEXCEPT override
{
// There is no forwarding constructor so assign and move.
- network::http::body::value_type value{};
- value = network::http::string_value{};
+ value_type value{};
+ value = Body{};
return value;
}
};
diff --git a/include/bitcoin/server/channels/channels.hpp b/include/bitcoin/server/channels/channels.hpp
index 8698a76f..a151318f 100644
--- a/include/bitcoin/server/channels/channels.hpp
+++ b/include/bitcoin/server/channels/channels.hpp
@@ -20,7 +20,6 @@
#define LIBBITCOIN_SERVER_CHANNELS_CHANNELS_HPP
#include
-#include
#include
#include
#include
@@ -39,8 +38,7 @@ network::channel
│ └── [server::channel_stratum_v1]
│ └── [server::channel_electrum]
├── channel_http
-│ ├── [server::channel_http]
-│ └── [server::channel_btcd]
+│ └── [server::channel_http]
└── channel_peer
└── [node::channel_peer]
@@ -52,7 +50,6 @@ server::channel → node::channel
├── channel_stratum_v2 → network::channel
├── channel_stratum_v1 → network::channel_rpc
├── channel_electrum → network::channel_rpc
-├── channel_http → network::channel_http
-├── channel_btcd → network::channel_http
+└── channel_http → network::channel_http
*/
diff --git a/include/bitcoin/server/interfaces/btcd.hpp b/include/bitcoin/server/interfaces/btcd.hpp
index 3995849a..6bac48f2 100644
--- a/include/bitcoin/server/interfaces/btcd.hpp
+++ b/include/bitcoin/server/interfaces/btcd.hpp
@@ -1,7 +1,7 @@
-/*
+/**
* Copyright (c) 2011-2026 libbitcoin developers
*
- * This file is part of libbitcoin-server.
+ * This file is part of libbitcoin.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -28,57 +28,52 @@ namespace interface {
struct btcd_methods
{
- /// BTCD protocol (unversioned, json-rpc v1.0 over ws/s).
+ /// BTCD protocol (unversioned, json-rpc v1.0 over http/ws).
static constexpr std::tuple methods
{
- /// Handshake (disallowed when basic auth has been established).
+ /// Administrative.
method<"authenticate", string_t, string_t>{ "username", "password" },
-
- /// Admin.
+ method<"help", optional<""_t>>{ "command" },
method<"session">{},
+ method<"stop">{ unimplemented },
- /// Block subscription.
- method<"notifyblocks">{},
- method<"stopnotifyblocks">{},
-
- /// Tx subscription.
- method<"notifynewtransactions", optional>{ "verbose" },
- method<"stopnotifynewtransactions">{},
-
- /// Address/outpoint filtering.
- method<"loadtxfilter", boolean_t, value_t, value_t>{ "reload", "addresses", "outpoints" },
- method<"rescanblocks", value_t>{ "blockhashes" },
-
- /// Admin (not_implemented; no secure remote-shutdown path exists).
- method<"stop">{},
-
- /// Deprecated (address/outpoint filtering).
- method<"notifyreceived", value_t>{ "addresses" },
- method<"stopnotifyreceived", value_t>{ "addresses" },
- method<"notifyspent", value_t>{ "outpoints" },
- method<"stopnotifyspent", value_t>{ "outpoints" },
- method<"rescan", string_t, value_t, value_t, optional<""_t>>{ "beginblock", "addresses", "outpoints", "endblock" },
-
- /// Admin (network magic, checked once by btcwallet/lnd at connect).
+ /// Getters.
+ method<"getbestblock">{},
method<"getcurrentnet">{},
-
- /// Generic btcd-tooling compatibility (no lnd consumer).
method<"getdifficulty">{},
method<"getinfo">{},
method<"getnettotals">{},
method<"getnetworkhashps", optional<120_u32>, optional<-1_i32>>{ "blocks", "height" },
+
+ /// Tools.
method<"createrawtransaction", array_t, object_t, optional<0_u32>>{ "inputs", "outputs", "locktime" },
method<"decoderawtransaction", string_t>{ "hexstring" },
method<"decodescript", string_t>{ "hex" },
method<"validateaddress", string_t>{ "address" },
- method<"help", optional<""_t>>{ "command" },
- /// btcd extension: {hash, height} of the best chain tip, used by
- /// btcwallet's own rpcclient connection (distinct from lnd's own
- /// chain.RPCClient) during wallet chain-sync bootstrap.
- method<"getbestblock">{}
+ /// Subscription.
+ method<"notifyblocks">{},
+ method<"stopnotifyblocks">{},
+ method<"notifynewtransactions", optional>{ unimplemented, "verbose" },
+ method<"stopnotifynewtransactions">{ unimplemented },
+
+ /// Filters.
+ method<"loadtxfilter", boolean_t, value_t, value_t>{ "reload", "addresses", "outpoints" },
+ method<"rescanblocks", value_t>{ "blockhashes" },
+
+ /// Deprecated.
+ method<"notifyreceived", value_t>{ unimplemented, "addresses" },
+ method<"stopnotifyreceived", value_t>{ unimplemented, "addresses" },
+ method<"notifyspent", value_t>{ unimplemented, "outpoints" },
+ method<"stopnotifyspent", value_t>{ unimplemented, "outpoints" },
+ method<"rescan", string_t, value_t, value_t, optional<""_t>>{ "beginblock", "addresses", "outpoints", "endblock" }
};
+ /// Method names as reported by help.
+ static constexpr auto name_data = method_names();
+ static constexpr std::string_view names{ name_data.data(),
+ name_data.size() };
+
template
using subscriber = network::subscriber;
@@ -87,30 +82,35 @@ struct btcd_methods
// Derive this from above in c++26 using reflection.
using authenticate = at<0>;
- using session = at<1>;
- using notify_blocks = at<2>;
- using stop_notify_blocks = at<3>;
- using notify_new_transactions = at<4>;
- using stop_notify_new_transactions = at<5>;
- using load_tx_filter = at<6>;
- using rescan_blocks = at<7>;
- using stop = at<8>;
- using notify_received = at<9>;
- using stop_notify_received = at<10>;
- using notify_spent = at<11>;
- using stop_notify_spent = at<12>;
- using rescan = at<13>;
- using get_current_net = at<14>;
- using get_difficulty = at<15>;
- using get_info = at<16>;
- using get_net_totals = at<17>;
- using get_network_hash_ps = at<18>;
- using create_raw_transaction = at<19>;
- using decode_raw_transaction = at<20>;
- using decode_script = at<21>;
- using validate_address = at<22>;
- using help = at<23>;
- using get_best_block = at<24>;
+ using help = at<1>;
+ using session = at<2>;
+ using stop = at<3>;
+
+ using get_best_block = at<4>;
+ using get_current_net = at<5>;
+ using get_difficulty = at<6>;
+ using get_info = at<7>;
+ using get_net_totals = at<8>;
+ using get_network_hash_ps = at<9>;
+
+ using create_raw_transaction = at<10>;
+ using decode_raw_transaction = at<11>;
+ using decode_script = at<12>;
+ using validate_address = at<13>;
+
+ using notify_blocks = at<14>;
+ using stop_notify_blocks = at<15>;
+ using notify_new_transactions = at<16>;
+ using stop_notify_new_transactions = at<17>;
+
+ using load_tx_filter = at<18>;
+ using rescan_blocks = at<19>;
+
+ using notify_received = at<20>;
+ using stop_notify_received = at<21>;
+ using notify_spent = at<22>;
+ using stop_notify_spent = at<23>;
+ using rescan = at<24>;
};
} // namespace interface
diff --git a/include/bitcoin/server/interfaces/types.hpp b/include/bitcoin/server/interfaces/types.hpp
index 25cb1327..5628473c 100644
--- a/include/bitcoin/server/interfaces/types.hpp
+++ b/include/bitcoin/server/interfaces/types.hpp
@@ -31,6 +31,11 @@ template
using method = network::rpc::method;
template
using method_at = network::rpc::method_at;
+template
+constexpr auto method_names() NOEXCEPT
+{
+ return network::rpc::method_names();
+}
template
using publish = network::rpc::publish;
@@ -52,6 +57,8 @@ namespace empty { constexpr auto array = network::rpc::empty::array; };
namespace empty { constexpr auto object = network::rpc::empty::object; };
namespace empty { constexpr auto value = network::rpc::empty::value; };
+constexpr auto unimplemented = network::rpc::unimplemented;
+
} // namespace interface
} // namespace server
} // namespace libbitcoin
diff --git a/include/bitcoin/server/parsers/btcd_filter.hpp b/include/bitcoin/server/parsers/btcd_filter.hpp
new file mode 100644
index 00000000..35cb115f
--- /dev/null
+++ b/include/bitcoin/server/parsers/btcd_filter.hpp
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#ifndef LIBBITCOIN_SERVER_PARSERS_BTCD_FILTER_HPP
+#define LIBBITCOIN_SERVER_PARSERS_BTCD_FILTER_HPP
+
+#include
+
+namespace libbitcoin {
+namespace server {
+namespace btcd {
+
+/// Parse an address to its output script, prefixes are validated.
+BCS_API code output_script(system::chain::script& out,
+ const std::string& text, uint8_t p2kh, uint8_t p2sh,
+ const std::string& witness) NOEXCEPT;
+
+/// Parse loadtxfilter addresses to their output script hashes.
+BCS_API code filter_keys(system::hashes& out,
+ const network::rpc::value_t& addresses, uint8_t p2kh, uint8_t p2sh,
+ const std::string& witness) NOEXCEPT;
+
+/// Parse loadtxfilter outpoints to their points.
+BCS_API code filter_points(system::chain::points& out,
+ const network::rpc::value_t& outpoints) NOEXCEPT;
+
+} // namespace btcd
+} // namespace server
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/server/parsers/parsers.hpp b/include/bitcoin/server/parsers/parsers.hpp
index a76d840d..40ee10ae 100644
--- a/include/bitcoin/server/parsers/parsers.hpp
+++ b/include/bitcoin/server/parsers/parsers.hpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp b/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp
index 5330ae51..da3eb2a2 100644
--- a/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp
+++ b/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp
@@ -33,8 +33,8 @@ class BCS_API protocol_bitcoind_rpc
protected network::tracker
{
public:
- // Replace base class channel_t (network::channel_http).
- using channel_t = channel_http;
+ // Replace base class channel_t (json-rpc websocket reader body).
+ using channel_t = channel_http;
typedef std::shared_ptr ptr;
using rpc_interface = interface::bitcoind_rpc;
@@ -55,28 +55,13 @@ class BCS_API protocol_bitcoind_rpc
using post = network::http::method::post;
using options = network::http::method::options;
- /// Dispatch.
+ /// Dispatch (the post and websocket transports of the interface).
void handle_receive_options(const code& ec,
const network::http::method::options::cptr& options) NOEXCEPT override;
void handle_receive_post(const code& ec,
const post::cptr& post) NOEXCEPT override;
-
- /// Dispatch a chain-rpc message with no post-http context (websocket).
- /// Caches id/version (as handle_receive_post does via set_rpc_request)
- /// and notifies rpc_dispatcher_ directly; returns error::unexpected_method
- /// if the message names a method this dispatcher does not recognize.
- /// Callers (e.g. protocol_btcd_rpc::dispatch_websocket) use this to reach
- /// the standard chain handlers (getblockcount etc.) over websocket.
- code dispatch_rpc(const network::rpc::request_t& message) NOEXCEPT;
-
- /// Extension point for a derived class's own additional dispatcher (e.g.
- /// protocol_btcd_rpc's btcd_dispatcher_), tried by handle_receive_post
- /// when rpc_dispatcher_ reports unexpected_method -- the post-side
- /// mirror of dispatch_rpc above (ws falling back to the chain
- /// dispatcher). Default: no extension dispatcher, always
- /// unexpected_method.
- virtual code dispatch_extension(
- const network::rpc::request_t& message) NOEXCEPT;
+ void dispatch_websocket(
+ const network::http::request& request) NOEXCEPT override;
/// Handlers.
bool handle_get_best_block_hash(const code& ec,
@@ -128,9 +113,7 @@ class BCS_API protocol_bitcoind_rpc
double maxfeerate) NOEXCEPT;
/// Serialize an object (chain::header, chain::transaction, ...) to a
- /// base16 string. Template, so defined here (not *_json.cpp) -- shared
- /// with derived classes (e.g. protocol_btcd_rpc's filtered-notification
- /// tx serialization), not just this class's own handlers.
+ /// base16 string.
template
static std::string to_text(const Object& object, size_t size,
Args&&... args) NOEXCEPT
@@ -155,15 +138,25 @@ class BCS_API protocol_bitcoind_rpc
const system::chain::header& header) NOEXCEPT;
static std::string chain_name(const node::query& query) NOEXCEPT;
- /// Senders.
+ /// Senders. close_reason (if truthy) stops the channel only once the
+ /// write has completed, so the error reaches the client first.
void send_error(const code& ec) NOEXCEPT;
void send_error(const code& ec, size_t size_hint) NOEXCEPT;
+ void send_error(const code& ec, size_t size_hint,
+ const code& close_reason) NOEXCEPT;
void send_error(const code& ec, network::rpc::value_option&& error,
size_t size_hint) NOEXCEPT;
void send_text(std::string&& hexidecimal) NOEXCEPT;
void send_result(network::rpc::value_option&& result,
size_t size_hint) NOEXCEPT;
+ /// Cache rpc response context for serialization (requires strand). The
+ /// websocket overload has no http request to echo headers from.
+ void set_rpc_request(network::rpc::version version,
+ const network::rpc::id_option& id,
+ const network::http::request_cptr& request) NOEXCEPT;
+ void set_rpc_request(const network::rpc::request_t& message) NOEXCEPT;
+
private:
template
inline void subscribe(Method&& method, Args&&... args) NOEXCEPT
@@ -174,11 +167,8 @@ class BCS_API protocol_bitcoind_rpc
// Senders.
void send_rpc(network::rpc::response_t&& model,
size_t size_hint) NOEXCEPT;
-
- // Cache request for serialization (requires strand).
- void set_rpc_request(network::rpc::version version,
- const network::rpc::id_option& id,
- const network::http::request_cptr& request) NOEXCEPT;
+ void send_rpc(network::rpc::response_t&& model, size_t size_hint,
+ const code& close_reason) NOEXCEPT;
// Validate a transaction given next block context.
bool get_pool_context(system::chain::context& pool) const NOEXCEPT;
diff --git a/include/bitcoin/server/protocols/protocol_btcd.hpp b/include/bitcoin/server/protocols/protocol_btcd.hpp
new file mode 100644
index 00000000..5c2adde5
--- /dev/null
+++ b/include/bitcoin/server/protocols/protocol_btcd.hpp
@@ -0,0 +1,243 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#ifndef LIBBITCOIN_SERVER_PROTOCOLS_PROTOCOL_BTCD_HPP
+#define LIBBITCOIN_SERVER_PROTOCOLS_PROTOCOL_BTCD_HPP
+
+#include
+#include