Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions builds/gnu/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ src_libbitcoin_server_la_SOURCES = \
${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/electrum/protocol_electrum.cpp \
${srcdir}/../../src/protocols/electrum/protocol_electrum_addresses.cpp \
${srcdir}/../../src/protocols/electrum/protocol_electrum_fees.cpp \
Expand Down Expand Up @@ -108,6 +111,7 @@ include_bitcoin_server_channelsdir = \

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 \
Expand Down Expand Up @@ -155,6 +159,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_electrum.hpp \
${srcdir}/../../include/bitcoin/server/protocols/protocol_electrum_version.hpp \
${srcdir}/../../include/bitcoin/server/protocols/protocol_html.hpp \
Expand Down Expand Up @@ -217,6 +222,8 @@ test_libbitcoin_server_test_SOURCES = \
${srcdir}/../../test/protocols/bitcoind/bitcoind_rest.cpp \
${srcdir}/../../test/protocols/bitcoind/bitcoind_rpc.cpp \
${srcdir}/../../test/protocols/bitcoind/bitcoind_setup_fixture.cpp \
${srcdir}/../../test/protocols/btcd/btcd_rpc.cpp \
${srcdir}/../../test/protocols/btcd/btcd_setup_fixture.cpp \
${srcdir}/../../test/protocols/electrum/electrum_addresses.cpp \
${srcdir}/../../test/protocols/electrum/electrum_disabled.cpp \
${srcdir}/../../test/protocols/electrum/electrum_fees.cpp \
Expand Down
365 changes: 365 additions & 0 deletions docs/btcd-endpoint.md

Large diffs are not rendered by default.

107 changes: 107 additions & 0 deletions endpoints/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ bind = 0.0.0.0:8332
bind = [::]:8332
connections = 10

[btcd]
bind = 0.0.0.0:8334
bind = [::]:8334
connections = 10

[electrum]
bind = 0.0.0.0:50001
bind = [::]:50001
Expand Down Expand Up @@ -80,6 +85,9 @@ pytest test_native.py
# Test only bitcoind RPC
pytest test_bitcoind_rpc.py

# Test only btcd JSON-RPC/websocket
pytest test_btcd_rpc.py

# Test only Electrum protocol
pytest test_electrum.py
```
Expand Down Expand Up @@ -130,6 +138,34 @@ pytest test_bitcoind_rpc.py \
- `--bitcoind-auth` — Enable authentication (cookie file)
- `--bitcoind-cookie` — Path to cookie file (default: /mnt/core/.cookie)

### btcd JSON-RPC/WebSocket Options

```bash
pytest test_btcd_rpc.py \
--btcd-host=localhost \
--btcd-port=8334
```

**With basic auth / ws `authenticate` credentials configured:**

```bash
pytest test_btcd_rpc.py \
--btcd-username=user \
--btcd-password=pass
```

**Available options:**
- `--btcd-host` — Host for btcd JSON-RPC/websocket (default: localhost)
- `--btcd-port` — Port for btcd JSON-RPC/websocket (default: 8334)
- `--btcd-username` / `--btcd-password` — Credentials to test `authenticate`
against, if the server has `btcd.credential=username:password` configured
(default: none — the credential-specific tests are skipped)

No extra Python dependency is required: the websocket client is a small
stdlib-only implementation (see `WebSocketConnection` in `test_btcd_rpc.py`),
matching this suite's existing preference for hand-rolled wire protocol over
Electrum's raw TCP handling.

### Electrum Protocol Options

```bash
Expand Down Expand Up @@ -169,6 +205,16 @@ pytest test_native.py \
--timeout=30
```

- `--run-slow` — Include tests marked `@pytest.mark.slow`: these wait on a
real external event (e.g. `test_blockconnected_notification` waits up to
`--subscription-timeout` for an actual new block) and are skipped by
default so a normal run stays fast.

```bash
# Include slow/live-event tests, e.g. against a node expecting a block soon
pytest test_btcd_rpc.py --run-slow --subscription-timeout=120
```

## Test Organization

### test_native.py — Native REST
Expand Down Expand Up @@ -221,6 +267,66 @@ pytest test_bitcoind_rpc.py \
pytest test_bitcoind_rpc.py -k "getblock"
```

### test_btcd_rpc.py — btcd JSON-RPC/WebSocket Compatibility

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

Unlike the other endpoint suites, this one is explicitly split into two
kinds of test:
- **No `xfail` marker** — asserts real, currently-implemented behavior. A
failure here is a regression.
- **`@pytest.mark.xfail(strict=False)`** — describes intended behavior that
isn't implemented yet (currently: only the mempool-dependent
`notifynewtransactions`/`stopnotifynewtransactions`, blocked on a v5
mempool). Expected to fail today; flips to XPASS once implemented, which
is the cue to tighten the assertion and remove the marker. Use
`pytest -m xfail -rx` to see the current development checklist.

**Coverage:**
- ✅ Session management — `authenticate` (no-op / credential match / credential
mismatch), `session`
- ✅ 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
connection (`getbestblockhash`, `getblockcount`, `getblockhash`,
`getblockheader`, `gettxout`, `getrawtransaction`)
- ✅ `loadtxfilter`/`rescanblocks` (address/outpoint watch-list, filtered
block notifications), `getcurrentnet`, `getbestblock`, `rescan` (empty-
addrs/outpoints bootstrap case), `getdifficulty`, `getinfo`,
`getnettotals`, `getnetworkhashps`, `createrawtransaction`,
`decoderawtransaction`, `decodescript`, `validateaddress`, `help`
- ✅ btcd-only methods (e.g. `getinfo`) reachable over plain http post too,
not only ws
- ✅ Deprecated methods stay rejected — `notifyreceived`, `stopnotifyreceived`,
`notifyspent`, `stopnotifyspent`; `stop`; `rescan` with a non-empty
address/outpoint list (permanent, no dev-target xfail — these are guarded
against ever silently "working")
- ✅ Response envelope — id echo across requests, unknown-method error
without dropping the connection
- 🚧 (xfail) `notifynewtransactions`/`stopnotifynewtransactions` — blocked on
a v5 mempool

**Example test runs:**

```bash
pytest test_btcd_rpc.py
pytest test_btcd_rpc.py --btcd-port=8334
pytest test_btcd_rpc.py -k "not xfail" # only currently-real behavior
pytest test_btcd_rpc.py -m xfail -rx # what's left to implement
BTCD_DEBUG=1 pytest test_btcd_rpc.py -s -k session

# blockconnected waits for a real block; trigger one instead of waiting out
# the timeout (regtest/testnet), or just let it wait on a synced mainnet node
BTCD_TRIGGER_BLOCK="bitcoin-cli generatetoaddress 1 <address>" \
pytest test_btcd_rpc.py -k blockconnected --subscription-timeout=120
```

### test_electrum.py — Electrum Protocol

Tests Electrum Protocol JSON-RPC over TCP. All assertions are spec-driven — they
Expand Down Expand Up @@ -508,6 +614,7 @@ Each test module supports a debug environment variable that enables pretty-print
| `test_electrum.py` | `ELECTRUM_DEBUG=1` | `>>>` JSON-RPC payload / `<<<` response with elapsed time |
| `test_native.py` | `NATIVE_DEBUG=1` | `>>> GET <url>` / `<<<` response JSON with elapsed time |
| `test_bitcoind_rpc.py` | `BITCOIND_DEBUG=1` | `>>>` JSON-RPC payload / `<<<` response with elapsed time |
| `test_btcd_rpc.py` | `BTCD_DEBUG=1` | `>>>` JSON-RPC payload / `<<<` response over websocket |

```bash
# Electrum — pretty-print all requests and responses
Expand Down
63 changes: 63 additions & 0 deletions endpoints/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,32 @@ def pytest_addoption(parser):
help="Port for bitcoind REST (default: 8332)"
)

# btcd options
parser.addoption(
"--btcd-host",
action="store",
default="localhost",
help="Host for btcd JSON-RPC/websocket (default: localhost)"
)
parser.addoption(
"--btcd-port",
action="store",
default="8334",
help="Port for btcd JSON-RPC/websocket (default: 8334)"
)
parser.addoption(
"--btcd-username",
action="store",
default=None,
help="Username for btcd basic auth / ws authenticate (default: none configured)"
)
parser.addoption(
"--btcd-password",
action="store",
default=None,
help="Password for btcd basic auth / ws authenticate (default: none configured)"
)

# Electrum options
parser.addoption(
"--electrum-host",
Expand Down Expand Up @@ -132,6 +158,30 @@ def pytest_addoption(parser):
default="30",
help="Default timeout for requests in seconds (default: 30)"
)
parser.addoption(
"--run-slow",
action="store_true",
default=False,
help="Run tests marked @pytest.mark.slow (wait on a real external "
"event, e.g. a live block, up to --subscription-timeout)"
)


def pytest_configure(config):
config.addinivalue_line(
"markers",
"slow: waits on a real external event (e.g. a live block); "
"skipped by default, use --run-slow to include"
)


def pytest_collection_modifyitems(config, items):
if config.getoption("--run-slow"):
return
skip_slow = pytest.mark.skip(reason="needs --run-slow (waits on a real external event)")
for item in items:
if "slow" in item.keywords:
item.add_marker(skip_slow)


def pytest_report_header(config):
Expand Down Expand Up @@ -197,6 +247,19 @@ def bitcoind_rest_config(request):
}


@pytest.fixture(scope="session")
def btcd_config(request):
"""Configuration for btcd JSON-RPC/websocket tests."""
return {
"host": request.config.getoption("--btcd-host"),
"port": int(request.config.getoption("--btcd-port")),
"username": request.config.getoption("--btcd-username"),
"password": request.config.getoption("--btcd-password"),
"timeout": float(request.config.getoption("--timeout")),
"subscription_timeout": float(request.config.getoption("--subscription-timeout")),
}


@pytest.fixture(scope="session")
def electrum_config(request):
"""Configuration for Electrum protocol tests."""
Expand Down
Loading