Bitcoin full node and query server

February 2026

Archive of the libbitcoin Slack #general channel.

Tuesday, February 3, 2026

neillm20:36
What's the status of server/node builds with regards to wolfssl (on GNU/Linux)? I'm currently unable to build node or server
pmienk20:37
Broken due to wolfssl integration I'm working on now. Can be forced through with appropriate cppflags for includes and define
3 replies · neillm, eynhaender · last Feb 03, 2026
neillm20:38
Gotcha, thanks!
eynhaender21:17
I worked around it by just adding the missing include folders within the build directories with this
export CPLUS_INCLUDE_PATH="/home/eynhaender/development/libbitcoin/build/release_static/include/bitcoin/network/ssl:$CPLUS_INCLUDE_PATH"
export CPATH="/home/eynhaender/development/libbitcoin/build/release_static/include/bitcoin/network/ssl:$CPATH"
neillm21:17
Thanks. I got things working though using @pmienk’s modification to the CXXFLAGS
pmienk20:37
See my node fork for an example via autotools ci
🙏 1

Thursday, February 5, 2026

evoskuil16:43
I won't make the meeting today. No significant updates for the past week. Will be back next meeting.
👍 2👍🏻 1
Huddle with pmienk, eynhaender · 30 min16:55

Saturday, February 7, 2026

evoskuil00:15
Yesterday I improved error logging for TLS. Today I added http basic auth (for bitcoind compat) and verified using Sparrow and verified over TLS using curl (bitcoind doesn't implement TLS).
🎉 1
neillm03:33
Did I miss that [web] and [explore] configs were replaced?
👍🏻 1
Ah, is it [native] I'm looking for?
Hrm, no. Looking to reconnect via web. What's the setting I'm missing?
evoskuil03:50
[native] is the old explore. [admin] is the web admin site.
(edited)
neillm03:53
Hrm, looking for the web interface via browser and not seeing it
evoskuil03:54
It's the same as before, just the section renamed to admin. The block explorer is native.
neillm03:59
Sorry, I'm confused -- I'm seeing this:
$ master/install/bin/bs -c bs.cfg.v4
Error: unrecognised option 'web.bind'
$ master/install/bin/bs -c bs.cfg.v4
Error: unrecognised option 'explore.bind'
Removing explore and web allows it to start ok, but there's no http access
Adding native works fine, but no http access
Derp -- nevermind, got it
👍🏻 1
What's the ELI5 version for explore->native and web->admin?
evoskuil04:05
Same settings, just different section names. Just dump bs -s
5 replies · neillm, evoskuil · last Feb 08, 2026
neillm04:06
I thought admin would be a place to adjust settings, not read bitcoin values via http 🤔
evoskuil04:07
Native is the native api, admin is the admin site.
neillm04:08
$ master/install/bin/bs --settings > bs.cfg.v4.raw
terminate called without an active exception
Aborted
evoskuil06:12
This fix is merged.
👍 1
neillm04:05
And do I have to re-sync?
Screenshot(1200).png
3 replies · evoskuil, neillm · last Feb 07, 2026
evoskuil04:08
No
neillm04:10
Hrm, the interface was much nicer before 😅
evoskuil05:00
image.png
jungly14:58
https://x.com/i/status/2019951674501079446 Didn't realise the grenade had made it so far!
X (formerly Twitter)
The Bitcoin Grenade is a staple of CryptoCloaks. Gen 3 nades are here. Customize yours. The same nade you've loved for 6 years, now heavier, cleaner and ready to end fiat.
👍🏻 1

Sunday, February 8, 2026

evoskuil06:43
Spent some time today on daemon/ntservice registration, and linux/macOS logoff/shutdown management. Then started implementing ElectrumX methods. Got far enough for Sparrow to recognize the connection as operational.
image.png
We'll have protocol support for all versions of Electrum, based on the negotiated version. Similar to our handling of p2p network protocol versions.
enum class electrum_version
{
    /// Invalid version.
    v0_0,

    /// 2011, initial protocol negotiation.
    v0_6,

    /// 2012, enhanced protocol negotiation.
    v0_8,

    /// 2012, added pruning limits and transport indicators.
    v0_9,

    /// 2013, baseline for core methods in the official specification.
    v0_10,

    /// 2014, 1.x series, deprecations of utxo and block number methods.
    v1_0,

    /// 2015, updated version response and introduced scripthash methods.
    v1_1,

    /// 2017, added optional parameters for transactions and headers.
    v1_2,

    /// 2018, defaulted raw headers and introduced new block methods.
    v1_3,

    /// 2019, removed deserialized headers and added merkle proof features.
    v1_4,

    /// 2019, modifications for auxiliary proof-of-work handling.
    v1_4_1,

    /// 2020, added scripthash unsubscribe functionality.
    v1_4_2,

    /// 2022, updated response formats and added fee estimation modes.
    v1_6
};

Monday, February 9, 2026

evoskuil23:03
network::protocol
├── [server:protocol_stratum_v2]
├── protocol_rpc<Channel>
│   └── [server::protocol_rpc<Channel>]
├── protocol_http
│   ├── [protocol_bitcoind_rpc]
│   └── protocol_ws
│       └── [server::protocol_html]
└── protocol_peer
    ├── [node::protocol_peer]
    ├── protocol_alert_311
    ├── protocol_reject_70002
    ├── protocol_seed_209
    ├── protocol_address_in_209
    ├── protocol_address_out_209
    ├── protocol_ping_106
    │   └── protocol_ping_60001
    └── protocol_version_106
        └── protocol_version_70001
            └── protocol_version_70002
                └── protocol_version_70016

node::protocol
├── [server::protocol]
└── protocol_peer -> network::protocol_peer
    ├── protocol_observer
    ├── protocol_filter_out_70015
    ├── protocol_block_in_106
    ├── protocol_performer
    │   └── protocol_block_in_31800
    ├── protocol_block_out_106
    │   └── protocol_block_out_70012
    ├── protocol_header_in_31800
    │   └── protocol_header_in_70012
    ├── protocol_header_out_31800
    │   └── protocol_header_out_70012
    ├── protocol_transaction_in_106
    └── protocol_transaction_out_106

server::protocol -> node::protocol
├── protocol_stratum_v2 -> network::protocol
├── protocol_rpc<channel_stratum_v1> -> network::protocol_rpc<channel_stratum_v1>
│   └── protocol_stratum_v1
├── protocol_rpc<channel_electrum> -> network::protocol_rpc<channel_electrum>
│   ├── protocol_electrum
│   └── protocol_electrum_version
└── protocol_http
    ├── protocol_html -> network::protocol_ws
    │   ├── protocol_admin
    │   └── protocol_native
    └── protocol_bitcoind_rpc -> network::protocol_http
        └── protocol_bitcoind_rest
(edited)

Tuesday, February 10, 2026

evoskuil01:17
network::channel
├── [server::channel_stratum_v2]
├── channel_rpc<Interface>
│   └── [server::channel_stratum_v1]
│   └── [server::channel_electrum]
├── channel_http
│   ├── [server::channel_http]
│   └── channel_ws
│       └── [server::channel_ws]
└── channel_peer
    └── [node::channel_peer]

node::channel
├── [server::channel]
└── channel_peer       → network::channel_peer

server::channel → node::channel
├── channel_stratum_v2 → network::channel
├── channel_stratum_v1 → network::channel_rpc<interface::stratum_v1>
├── channel_electrum   → network::channel_rpc<interface::electrum>
├── channel_http       → network::channel_http
└── channel_ws         → network::channel_ws
evoskuil01:57
network::session
├── session_peer
│   ├── session_seed
│   ├── session_inbound
│   │   └── [node::session_peer<network::session_inbound>]
│   ├── session_outbound
│   │   └── [node::session_peer<network::session_outbound>]
│   └── session_manual
│       └── [node::session_peer<network::session_manual>]
└── session_server
    └── [server::session_server<...Protocols>]

node::session
├── [server::session]
└── session_peer<NetworkSession> → NetworkSession
    ╞══ session_peer<network::session_inbound>
    │   └── session_inbound
    ╞══ session_peer<network::session_outbound>
    │   └── session_outbound
    ╘══ session_peer<network::session_manual>
        └── session_manual

server::session → node::session
└── server::session_server<...Protocols> → network::session_server
    ╞══ session_admin      = server::session_server<protocol_admin>         
    ╞══ session_native     = server::session_server<protocol_native>
    ╞══ session_bitcoind   = server::session_server<protocol_bitcoind_rest>
    ╞══ session_stratum_v1 = server::session_server<protocol_stratum_v1>
    ╞══ session_stratum_v2 = server::session_server<protocol_stratum_v2>
    └── server::session_handshake<...Protocols>
        ╘══ session_electrum = server::session_handshake<
                protocol_electrum_version, protocol_electrum>
evoskuil02:02
Above represents the major class hierarchies in the network stack (network/node/server). Single lines are the primary derivation path, and → represents secondary derivation. Double lines are not derivation but template instantiation, and = represents type alias.
evoskuil02:08
The pattern for all three is similar. server derives from node, but neither derive from network. node and server establish base functionality for the applicable context, and then multiply-derive network to mix in the core network functionality. Overall there is very little duplication, although network::session_server and network::session_inbound are very similar. These could probably be collapsed, but I've specifically avoided crossing the peer-to-peer and client-server streams above the lowest level base classes.
(edited)
Diamond inheritance is strictly avoided (which is evident from the true hierarchy). I've added these diagrams to the server sources for future reference. peer protocols will expand as we add tx pool functionality, and but otherwise I don't expect much more growth.

Thursday, February 12, 2026

Huddle with pmienk, evoskuil, eynhaender, Rowan Voskuil · 126 min16:55
evoskuil19:55
Found the issue that led to the admin embedded site rendering from the native embedded site: https://github.com/libbitcoin/libbitcoin-server/pull/623
🙏 1
2 replies · neillm, evoskuil · last Feb 12, 2026
neillm21:43
Thanks, this solved my web issues
👍🏻 1
evoskuil21:46
If you set admin.path (optional admin.default) it allows you to serve a site from the file system. This was working which is why we weren't seeing it, since the embedded admin site is just a stub.
[admin]
path = m:/server
default = bikers.html
👍 1

Saturday, February 14, 2026

evoskuil18:58
Checked in on sync time last night, after some orthogonal store modifications. 53.8 min to block 900,000 on maxi, within typical variation from the record time of 51.9 min.

Monday, February 16, 2026

evoskuil08:29
I completed the electrum handle_blockchain_block_header() and handle_blockchain_block_headers() methods. This was a little more than expected because version 1.4 added a checkpointing scheme designed to minimize header download at clients. Headers are small, and this feature introduces additional trust beyond SPV (trust of header checkpoints embedded in the wallet code). Implementing this requires computing a unique merkle tree of all block hashes up to the given block - for any block on demand. This requires caching partial merkle root computations at fixed intervals across the full chain. The caching is fast and very small, and the query computations are reasonable. But it was a lot of moving parts to organize and test. It is controllable via config, and we'd want it off unless supporting electrum. A large depth effectively precludes the cache, since it's cached on block height intervals of 2^depth. The cache is associated to blocks vs. heights (reorg safe) and append-only in the txs table. This should set to 11 (caching one merkle subtree root hash for every 2048 blocks) when serving electrum. Otherwise checkpointed header queries produce a merkle tree spanning the entire header chain (O(n) where n is the number of blocks) and will take some time.
[database]
interval_depth = 255
(edited)
evoskuil08:35
There's also a new setting for electrum only:
[electrum]
maximum_headers = 20160
This is the max number of headers the server will allow to return on a given request. This value is passed to clients and is ideally a multiple of the retarget period (e.g. 2016).
evoskuil16:38
Updated maxi run to 900k took 52.3 mins, with database.interval_depth = 11, and cache space < 14kb, so we can safely consider the electrum caching cost to be inconsequential.
👍🏻 1
evoskuil18:38
Also verified the size difference in the txs table with and without the cache, up to block 900k, is 14,048 bytes:
  4,802,912,942 (cache)
- 4,802,898,894 (no cache)
         14,048 bytes
This is as expected given:
floor(900,001 blocks / 2^11 blocks/interval) = 439 intervals
439 intervals * 32 bytes/interval = 14,048 bytes
The cache simply slips into the txs association table record for every interval block. This is simple because txs is a slab table (variably-sized records). This can also be done as blocks are concurrently downloaded and stored out of order, since the computed interval merkle root is a function of header hashes within an interval, set at the last block of each interval. So it's not necessary that preceding blocks be downloaded (header chain is already archived).
Given that the interval is controlled by a one byte config value, which if changed after some amount of sync will cause a catastrophic store failure, I'm going to set that one byte as the first value in the genesis block txs entry. This will be read at startup and cause the config value to be ignored after store creation (which stores the genesis block).

Tuesday, February 17, 2026

evoskuil03:01
That's done, so the node remains safe against breaking config changes.
This would have been easier to just embed as a fixed value, but this allows us to make space/perf tradeoffs in terms of server query response. It's fast already, so maybe overkill, but it's nice to be able to tune these things without rebuilding.

Wednesday, February 18, 2026

evoskuil05:08
The fee estimation accumulator, updates, and query are now implemented. I'll implement the unit tests tomorrow. This will satisfy both the Electrum and bitcoind API compat requirements. It relies on full block query and prevout population to initialize and update the accumulator (e.g. 10k sample blocks). This aspect can be dramatically optimized without materially affecting this code (by just querying for output values vs. building entire txs/blocks). But this will probably be fast enough to call complete for now. Also still need to push some constants into config for better flexibility. Also because so little of this is actually store dependent I may eventually restructure it into an independent class, maybe in system. https://github.com/evoskuil/libbitcoin-database/blob/7ff01398172132c0fc459400cab0c008b2006ee7/include/bitcoin/database/impl/query/estimate.ipp

Thursday, February 19, 2026

Huddle with pmienk, Marnix, evoskuil, eynhaender · 158 min16:55

Friday, February 20, 2026

evoskuil05:00
I split the application logic from the fee querying, so that the database code for the fee estimation is now independent (just fee collection) and about 100 lines. That's parallelized over blocks, and can be radically optimized within each block (and for individual block queries, parallelized by tx). The application logic becomes a server class, initialized and updated using a vector of fee vectors (blocks -> txs -> fees). This allows for parallel fee collection by block, which is optimal for the initialization. The cache is then just member state of the class. The factoring makes testing significantly easier and simplifies the logic. Small impact on performance by prob not even noticeable. This is because the population query is parallel but the computation is independent. The computation can be made parallel as well, but setting it up twice is additional overhead, and may not even be worth it. I've left the atomics in so I can compare it. But with sequential computation the atomics can be dropped. That is a bit simplifying, and a also enables dynamic sizing for the buckets if we wanted to do that. I also verified that the Core implementation is fixed buckets. Also updated the logic to allow for < 1 sat per byte fee rate, which was easy. Unlike Core, we don't rely on a populated vector to tell us the bucket values, since that's just nanosecond math. Not worth the additional complexity and giant lookup table. Also we don't operate in a multiple of 1000 (sats per 1000 bytes) because it's all floating point to start with, and there's ridiculous precision in a double. So we just compute in sat per vbyte, and set the min/max at compile (presently). To emit in per kvb units (e.g. for compat APIs, we obviously just divide it out).
👍 2
evoskuil05:45
I also just implemented the markov alternative that @pmienk suggested today, so that will be an API option.
👏🏻 1

Sunday, February 22, 2026

evoskuil02:56
Tests complete for fee-related database queries, fee estimator class moved to server, and tests complete there as well. I'm going to implement the direct fee query in place of the present query that pulls up each whole block from the store. Turns out it's pretty straightforward and should only take an hour. Tests are already written. Performance difference will be significant. I'll initially leave both options just to measure the difference out of curiosity.
(edited)
👍🏻 1
evoskuil18:57
Saved a couple MB (variably-sized records) by removing fees from the validate_bk table and converting it to an array. Given that it doesn't get populated unless fully validating or populating block filters, it wasn't consistently available anyway. Since block fees are not required by either validation or candidate block creation, and they can be queried quickly enough for service query and service fee estimator initialization, there's no reason for the cache.
This however does change the store structure, which means that future builds will require a resync of the store (old store will be undefined behavior and surely fail).
evoskuil22:16
I've used some of the above space by adding a non-witness size (for blocks) to the txs table. This is 32 bits per block and sits next to the witness size. This matches the pair of values in the tx table. That allows us to produce virtual byte size for blocks and txs at almost no cost, which optimizes for fee computation. The two sizes in the txs table could be compressed using variable sized integer, which would cut the storage way down. Also we can compress the is_coinbase bit in the tx table into one of the sizes, which would eliminate 1byte per tx, or around 1GB total.

Monday, February 23, 2026

eynhaender13:25
This is the first profiling I did in a while. Current build on my 96GB Notebook with Starlink in Panama.
profile.png
🔥 2
evoskuil20:02
First starlink sync?
eynhaender20:26
First Starlink only at least. I have a Starlink connection in my load balancing setup in Germany but this is only partially representative due to the "unpredictability".
Let me polish this up a bit more, then it'll be good enough for a tweet.
👍🏻 1

Tuesday, February 24, 2026

evoskuil03:49
Merged a couple more breaking store changes just now. Not planning on making any more, though it could come up. These were on the backlog for some time, so I figured I'd knock them out together to minimize disruption.
evoskuil17:39
I see that in the release notes for BC 31 they have added spender indexing. This is integral to BS, and I assume they are trying to close these gaps where possible. This will give us a more comparable store size metric, since this is a large index - especially if you also store whole blocks independently as BC does. With this and tx indexing enabled and with our address index disabled, the indexation is fairly comparable. Yet we are about the same size without it, which means this should highlight the space inefficiency of storing whole blocks. IIRC client filters are optional for BC (as for BS).
(edited)
evoskuil17:46
After making server minor store optimizations and backlogged updates yesterday, store performance is unaffected. Last night's maxi milestone run to 900k completed in 53.2 mins, which is just 1.3 mins off the record (within typical deviation).
evoskuil18:30
I also see that BC 30 slapped in another custom protocol (over IPC), so that mining tools get block templates and then relay them (possibly via SV2). The hacks just keep coming. Here's a discussion where Matt (who initially developed sv2) strongly makes the obvious argument - this is just more BC attempted lock-in. https://gnusha.org/bitcoin-core-dev/2024-10-24.log
"So I’m really quite confused here, somehow we’ve ended up with Bitcoin Core NIHing a new work providing protocol, except without any of the features of the Sv2-defined one, which is borderline worse than getblocktemplate and where no one who works on mining stuff was consulted on the design of the new protocol."
evoskuil18:51
https://x.com/spiralbtc/status/2026337685309206613 Spiral funded work on “swift sync”. This is where trusted providers publish utxo store state and you sync with it and hope that it was valid, find out when the sync is done if it was. Allows the “assume valid” sync to proceed in parallel. Getting to parallel validation is a much larger step.
😞 1

Wednesday, February 25, 2026

evoskuil07:44
I've completed the direct block/tx fee query optimization. Not only does this significantly optimize the fee accumulator initialization and updates, but also gets us very close to full optimization for all of our REST queries. There were a few that required population of the full block or tx from the store, as well as previous outputs, in order to obtain computed properties (such as fees). This is now reduced to just the sigops property. Sigops can be computed directly via query, just like I've done with fees. I'll probably do that eventually, at least when we get to candidate block generations. sigops and fees were originally intended for caching, but the requirement for prevouts precludes caching during archival, and to preserve the performance of milestone sync, we don't want to compel validation queries just for this. Since these cached values are not used in validation, the query performance is more than sufficient. In the mean time I may drop the sigops field from the native REST queries in order to get the performance benefit of avoiding the full block/tx queries just for this one property.
(edited)
evoskuil21:22
I've dropped sigops and wtxid from native interface for now. These block/tx details methods will no longer populate the full tx or block objects and so are very fast, but these two values would still require that full object population. The perf boost is a side benefit of yesterday's work to optimize fee and fee rate queries for the electrum and bitcoind fee estimation methods. I'll be hooking up the fee estimator in server today, and integrating it into the several dependent service methods.
(edited)
evoskuil21:39
A few quick endpoint queries show an order of magnitude improvement for the details methods.
(edited)
evoskuil21:51
Details queries for a full block are now under 15ms, and for a tx are < 1ms. These compute all fees from raw block/tx data (fees are not cached). Sizes are cached for blocks/txs, because these do not require prevouts. http://localhost/v1/tx/d71ec91f99a5e6865a904a04374a0c64a446bdb55e2df687030c2774495c7d99/details?format=json
{
  "segregated": true,
  "coinbase": false,
  "nominal": 772,
  "maximal": 1533,
  "weight": 3849,
  "virtual": 963,
  "value": 7285048621,
  "spend": 7284659165,
  "fee": 389456,
  "confirmed":
  {
    "height": 500000,
    "position": 42
  }
}
{
  "hash": "00000000000000000024fb37364cbf81fd49cc2d51c09c75c35433c3a1945d04",
  "height": 500000,
  "count": 2701,
  "segregated": true,
  "nominal": 981404,
  "maximal": 1048581,
  "weight": 3992793,
  "virtual": 998199,
  "value": 1402076969679,
  "fees": 339351625,
  "subsidy": 1250000000,
  "reward": 1589351625,
  "claim": 1589351625
}
evoskuil22:02
Not only are the fee computations now direct relational queries, but for blocks they are executed concurrently by tx. Because spend and value are both output in the API, this results in two independent calls to summarize inputs and outputs, both summing concurrently over txs in the block. For a single query to just obtain the fees for each tx, or for the block as a whole, this is further optimized to operating in a single concurrent run over all txs. That's not used above but is used to initialize the fee estimation accumulator, which by default collects fee data for all txs in the top 10,000 blocks. I expect this to now be sufficiently fast to allow it to initialize from the store at each server startup (when enabled), so there will be no need to cache fee accumulator state (e.g. as BC does). If each block takes 1ms then a 1000 block fee accumulator will be initialized just 1sec after startup. For a cold stare this will take a little longer, but should still be sufficiently fast. And there's no need to block startup for this, it can just give service callers a busy response until ready.
evoskuil22:31
Above should read 1,000 blocks in both instances (not 10,000). Technically it's 1,008, based on the maximum horizon value. This is chosen arbitrarily to target 1 week of blocks.
    /// Bucket depth sizing parameters (number of blocks).
    enum horizon : size_t
    {
        /// 2 hrs × 60 mins/hr / 10 mins/block = 12 blocks.
        small  = 12,

        /// 8 hrs × 60 mins/hr / 10 mins/block = 48 blocks.
        medium = 48,

        /// 7 days * 24 hrs/day × 60 mins/hr / 10 mins/block = 1008 blocks.
        large  = 1008
    };
evoskuil22:58
Caching to disk not only adds complexity, in this case it expires quickly anyway. We try to derive everything possible from store state to avoid these various sidecars. I should have a cache initialization time cost soon.

Thursday, February 26, 2026

evoskuil01:25
It's just under 10ms per block on average to derive fees, sizes, and initialize accumulator, so about 10 seconds to initialize 1008 blocks worth of tx fee accumulation. This will kick in when the node gets to either current or coalesced (which also kicks off a snapshot), since it's not beneficial unless at the top.
(edited)
Huddle with pmienk, evoskuil, Rowan Voskuil, eynhaender · 139 min16:54
1 reply · eynhaender · last Feb 26, 2026
eynhaender18:58
test_electrum.py::test_blockchain_block_headers >>> {"jsonrpc": "2.0", "id": 0, "method": "blockchain.block.headers", "params": [9000, 5, 0]}
<<< {"jsonrpc":"2.0","id":0,"result":{"max":2.016E4,"headers":["01000000632a1b699754e40b9140d32191c89286c0184098a9c17d12d356d39800000000882dc97ea4bfbadb281c1d1181df2033aa20d3f4b691b4a61fa0cd4c0b1af7a5a63cce49ffff001d2407559e","01000000b5a5110084cc6f6997b64868ce2f95e5145416a329ed543e0f7a5c4100000000820147ccdf83e6c8a32e9cc21fb189604edf27bbcd2e619c3fc12e2601a9299a353fce49ffff001d3873aec6","0100000016b2f2560640da0c58031c7a56fa6857783a9e76ef552aa821527503000000004707a4b853c8785fb385f5e548ce4f66b846c533894a13b316093ce3cc022922e43fce49ffff001d137f0304","01000000458df161b46a9b44ef54d3e327fd1d013f81a64e6bf6c8e4f2012c9a00000000c539c674e5ee6ca324b0ce0b3abdc84b10e66d72ed132acb584061bbd68b0e0fa542ce49ffff001dd6c1b205","010000009e2d02f749d322731e9d3dc53c1774bb65629cc0460d1700ab0d4f5400000000d72d89d4810566abde0ccf84437c48f962843c4bf75151728db23613d461f5197d44ce49ffff001d1f0ea6c9"],"count":5E0}}
<<< parsed: {
  "jsonrpc": "2.0",
  "id": 0,
  "result": {
    "max": 20160.0,
    "headers": [
      "01000000632a1b699754e40b9140d32191c89286c0184098a9c17d12d356d39800000000882dc97ea4bfbadb281c1d1181df2033aa20d3f4b691b4a61fa0cd4c0b1af7a5a63cce49ffff001d2407559e",
      "01000000b5a5110084cc6f6997b64868ce2f95e5145416a329ed543e0f7a5c4100000000820147ccdf83e6c8a32e9cc21fb189604edf27bbcd2e619c3fc12e2601a9299a353fce49ffff001d3873aec6",
      "0100000016b2f2560640da0c58031c7a56fa6857783a9e76ef552aa821527503000000004707a4b853c8785fb385f5e548ce4f66b846c533894a13b316093ce3cc022922e43fce49ffff001d137f0304",
      "01000000458df161b46a9b44ef54d3e327fd1d013f81a64e6bf6c8e4f2012c9a00000000c539c674e5ee6ca324b0ce0b3abdc84b10e66d72ed132acb584061bbd68b0e0fa542ce49ffff001dd6c1b205",
      "010000009e2d02f749d322731e9d3dc53c1774bb65629cc0460d1700ab0d4f5400000000d72d89d4810566abde0ccf84437c48f962843c4bf75151728db23613d461f5197d44ce49ffff001d1f0ea6c9"
    ],
    "count": 5.0
  }
}
PASSED
test_electrum.py::test_blockchain_block_headers_with_cp_single >>> {"jsonrpc": "2.0", "id": 0, "method": "blockchain.block.headers", "params": [9000, 1, 9010]}
<<< {"jsonrpc":"2.0","id":0,"result":{"branch":["16b2f2560640da0c58031c7a56fa6857783a9e76ef552aa82152750300000000","af67b874c777601fb1997dbc7e3560814b7a07938846494f28ffbfd0453793c8","19ba225cde8397b132baa72169fcf3879d02ed6b9762267f11251c9551415940","ade0e2d88b8b211e0c8199cba24e79df45438bb6a125f45cdd59f01f0d48dac1","ef1d1c2daa471e6addb3c5cc30e59873e4ade283094fe1c39cf7b9ddeba21f8c","b9d0f914675e08f905186fc7d7c3aa63494ff4392e516279e209a86cea2e8949","61ff014347f8675fe3f18e058b6450a0edda5934f50066009954ce9da9c6a105","2445f9beccdcd53e6936155b81ec3f3f0398a7b9f8e685902a59aa35def5ce5d","e4659700b0385b875e2140ccaadf8dd3acb8f968203889eaca3d58b7db0d4b68"],"root":"af5f46d9b7665d18ebc6ae5a79533e8894ca4dcafa322b43ffb898e9ba703ac6","max":2.016E4,"headers":["01000000632a1b699754e40b9140d32191c89286c0184098a9c17d12d356d39800000000882dc97ea4bfbadb281c1d1181df2033aa20d3f4b691b4a61fa0cd4c0b1af7a5a63cce49ffff001d2407559e"],"count":1E0}}
<<< parsed: {
  "jsonrpc": "2.0",
  "id": 0,
  "result": {
    "branch": [
      "16b2f2560640da0c58031c7a56fa6857783a9e76ef552aa82152750300000000",
      "af67b874c777601fb1997dbc7e3560814b7a07938846494f28ffbfd0453793c8",
      "19ba225cde8397b132baa72169fcf3879d02ed6b9762267f11251c9551415940",
      "ade0e2d88b8b211e0c8199cba24e79df45438bb6a125f45cdd59f01f0d48dac1",
      "ef1d1c2daa471e6addb3c5cc30e59873e4ade283094fe1c39cf7b9ddeba21f8c",
      "b9d0f914675e08f905186fc7d7c3aa63494ff4392e516279e209a86cea2e8949",
      "61ff014347f8675fe3f18e058b6450a0edda5934f50066009954ce9da9c6a105",
      "2445f9beccdcd53e6936155b81ec3f3f0398a7b9f8e685902a59aa35def5ce5d",
      "e4659700b0385b875e2140ccaadf8dd3acb8f968203889eaca3d58b7db0d4b68"
    ],
    "root": "af5f46d9b7665d18ebc6ae5a79533e8894ca4dcafa322b43ffb898e9ba703ac6",
    "max": 20160.0,
    "headers": [
      "01000000632a1b699754e40b9140d32191c89286c0184098a9c17d12d356d39800000000882dc97ea4bfbadb281c1d1181df2033aa20d3f4b691b4a61fa0cd4c0b1af7a5a63cce49ffff001d2407559e"
    ],
    "count": 1.0
  }
}
PASSED
test_electrum.py::test_blockchain_block_headers_with_cp_multiple >>> {"jsonrpc": "2.0", "id": 0, "method": "blockchain.block.headers", "params": [9000, 5, 9020]}
<<< {"jsonrpc":"2.0","id":0,"result":{"branch":["7f3510be609d64b91f03dd5b5cd9c5b600cf70d5517b4d83eb54c88400000000","ce4fca3c41bbb9d99a9b51d0b8af01530bb53df66dc307864cea4ead6d7c672a","33ffd7862fd75b61faea4a93b1e0b24174cf3037e417ed8d97a8b3a11d36ae3b","ade0e2d88b8b211e0c8199cba24e79df45438bb6a125f45cdd59f01f0d48dac1","605b77e30ec358cc6413ea8ab6506380048bad8adc0974a3130aa06bac805047","b9d0f914675e08f905186fc7d7c3aa63494ff4392e516279e209a86cea2e8949","61ff014347f8675fe3f18e058b6450a0edda5934f50066009954ce9da9c6a105","2445f9beccdcd53e6936155b81ec3f3f0398a7b9f8e685902a59aa35def5ce5d","e4659700b0385b875e2140ccaadf8dd3acb8f968203889eaca3d58b7db0d4b68"],"root":"57e9f7ae00b1865b84ca9d0ffed55384ac65f6227924aa4a0229a78bf86a4d18","max":2.016E4,"headers":["01000000632a1b699754e40b9140d32191c89286c0184098a9c17d12d356d39800000000882dc97ea4bfbadb281c1d1181df2033aa20d3f4b691b4a61fa0cd4c0b1af7a5a63cce49ffff001d2407559e","01000000b5a5110084cc6f6997b64868ce2f95e5145416a329ed543e0f7a5c4100000000820147ccdf83e6c8a32e9cc21fb189604edf27bbcd2e619c3fc12e2601a9299a353fce49ffff001d3873aec6","0100000016b2f2560640da0c58031c7a56fa6857783a9e76ef552aa821527503000000004707a4b853c8785fb385f5e548ce4f66b846c533894a13b316093ce3cc022922e43fce49ffff001d137f0304","01000000458df161b46a9b44ef54d3e327fd1d013f81a64e6bf6c8e4f2012c9a00000000c539c674e5ee6ca324b0ce0b3abdc84b10e66d72ed132acb584061bbd68b0e0fa542ce49ffff001dd6c1b205","010000009e2d02f749d322731e9d3dc53c1774bb65629cc0460d1700ab0d4f5400000000d72d89d4810566abde0ccf84437c48f962843c4bf75151728db23613d461f5197d44ce49ffff001d1f0ea6c9"],"count":5E0}}
<<< parsed: {
  "jsonrpc": "2.0",
  "id": 0,
  "result": {
    "branch": [
      "7f3510be609d64b91f03dd5b5cd9c5b600cf70d5517b4d83eb54c88400000000",
      "ce4fca3c41bbb9d99a9b51d0b8af01530bb53df66dc307864cea4ead6d7c672a",
      "33ffd7862fd75b61faea4a93b1e0b24174cf3037e417ed8d97a8b3a11d36ae3b",
      "ade0e2d88b8b211e0c8199cba24e79df45438bb6a125f45cdd59f01f0d48dac1",
      "605b77e30ec358cc6413ea8ab6506380048bad8adc0974a3130aa06bac805047",
      "b9d0f914675e08f905186fc7d7c3aa63494ff4392e516279e209a86cea2e8949",
      "61ff014347f8675fe3f18e058b6450a0edda5934f50066009954ce9da9c6a105",
      "2445f9beccdcd53e6936155b81ec3f3f0398a7b9f8e685902a59aa35def5ce5d",
      "e4659700b0385b875e2140ccaadf8dd3acb8f968203889eaca3d58b7db0d4b68"
    ],
    "root": "57e9f7ae00b1865b84ca9d0ffed55384ac65f6227924aa4a0229a78bf86a4d18",
    "max": 20160.0,
    "headers": [
      "01000000632a1b699754e40b9140d32191c89286c0184098a9c17d12d356d39800000000882dc97ea4bfbadb281c1d1181df2033aa20d3f4b691b4a61fa0cd4c0b1af7a5a63cce49ffff001d2407559e",
      "01000000b5a5110084cc6f6997b64868ce2f95e5145416a329ed543e0f7a5c4100000000820147ccdf83e6c8a32e9cc21fb189604edf27bbcd2e619c3fc12e2601a9299a353fce49ffff001d3873aec6",
      "0100000016b2f2560640da0c58031c7a56fa6857783a9e76ef552aa821527503000000004707a4b853c8785fb385f5e548ce4f66b846c533894a13b316093ce3cc022922e43fce49ffff001d137f0304",
      "01000000458df161b46a9b44ef54d3e327fd1d013f81a64e6bf6c8e4f2012c9a00000000c539c674e5ee6ca324b0ce0b3abdc84b10e66d72ed132acb584061bbd68b0e0fa542ce49ffff001dd6c1b205",
      "010000009e2d02f749d322731e9d3dc53c1774bb65629cc0460d1700ab0d4f5400000000d72d89d4810566abde0ccf84437c48f962843c4bf75151728db23613d461f5197d44ce49ffff001d1f0ea6c9"
    ],
    "count": 5.0
  }
}
PASSED
🎉 1