Archive of the libbitcoin Slack #general channel.
bitcoind endpoint surface. It's sprawling, but our frameworks have been accommodating it nicely.
Last week I added a high performance scan to produce unspent and unspendable totals at a given block height. That was the only thing that the utxo accumulator model provided that we don't already index for. But since this is really only an administrative function (satisfying curiosity), there's no reason to cache it.
We won't be supporting any wallet methods that require the presence of the bitcoind wallet, or that require submission of a private key to the server. There is actually a set of RPC functions for performing pure computation for PSBT, and I've added those. But have rejected the two of those that performing signing and therefore require private key transmission. All of these can be one locally by a client/wallet - in no way requiring a node.version3 support (builds, patches and repos not used by v4: blockchain, client, protocol, consensus. At the same time we could close out the very dated contrib project repos: pylibbitcoin, python-libbitcoin, and libbitcoin-c, and drop the original electrum fork (created from our original spesmilo repo when libbitcoin split off from that) and the secp256k1 fork (used to maintain version control in v3 before that library was versioned).
If any v3 code is in use it's likely system and possibly explorer. Given that explorer depends on client and protocol (as well as system), this would end access to that as well. Before we release v4 we should bring bx up to at least its v3 feature set, updated for v4 server. I don't expect that to be more than three days work.
I've also reviewed all open issues on the 5 bs repos and closed out those that are not relevant to v4. There were a few that are basically just indicating that there is no tx pool and no mining interface, which really don't need tracking issues. I've also added the major issues open on my v4 punch list, which is getting short. operation UltrafastSecp libsecp v0.8.0 ratio
--------------------------------------------------------------
FIELD
mul 20.18 23.60 1.17x
sqr 18.81 19.27 1.02x
add 7.62 12.14 1.59x
negate 10.51 11.75 1.12x
normalize 5.74 13.73 2.39x
from_bytes 5.12 13.00 2.54x
inverse (var) 1,502.74 1,565.12 1.04x ← იყო 0.86x
SCALAR
mul 48.26 54.40 1.13x
add 9.27 9.73 1.05x
negate 10.50 12.95 1.23x
from_bytes 8.75 9.29 1.06x ← იყო 0.71x
inverse (var) 1,573.29 1,577.07 1.00x
inverse (CT) 2,518.37 2,632.50 1.05x
GROUP LAW
double 111.77 127.89 1.14x
add (mixed J+A) 225.75 216.72 0.96x ← ერთადერთი
pubkey combine 1,754.53 3,244.23 1.85x
SCALAR MULT
ecmult a*P+b*G 35,069.80 34,724.28 0.99x
ecmult_gen k*G 8,800.88 16,888.72 1.92x
scalar_mul k*P 31,475.22 32,441.95 1.03x
pubkey_create 8,800.88 19,767.52 2.25x
SERIALIZATION
compressed 33B 15.44 25.69 1.66x
uncompressed 65B 13.23 28.77 2.17x
SIGNING (CT vs CT)
ECDSA sign 19,447.31 28,368.92 1.46x
Schnorr sign 15,371.24 21,390.94 1.39x
Schnorr keypair 14,684.50 19,762.92 1.35x
VERIFICATION
ECDSA verify 37,523.71 37,516.00 1.00x
Schnorr verify 37,738.10 37,841.44 1.00x
--------------------------------------------------------------2026-09-03T22:00:45Z.5 Invalid headers payload from [74.138.71.14:8333] with bytes (000dfdd0070000032018fb3ddd3ac22973b6b9bb02e04323f15fc610f155da000000000000000000003a1d4f36eb09fe7700d59b90c0851e479fe7986c5f59d1cb6a63dd21adc72e0b514c746ad43a0217823085fa000020b426bec7d77c250698748e1e3e6f7d790941a2407ab58ef101000000000000000000a16b2dbc21347f5e6424c746d0b73c97a46f0d547ac2944315c9da209d22afddc34c746ad43a02178ccd50c10000c0a623fdb0cc955f1b0ab6d839a78f065c8299a19765fefe28000000000000000000004ae1b5ca5a763bd538d781ad0e51cf59f96b9ce97875a636d6fa42a259955313854d746ad43a02174298d2e50000c01429c9b776...)
00010000000000000000000010110000
[peer] invalid_services = 268435632 # 4 shitcoins(edited)
btcd extends bitcoind jrpc interface, so it reimplements it and then adds methods to the same endpoint. We are doing the same, so there's a btcd combined interface, which is just derived from bitcoind, and an independent bitcoind interface.
However, the btcd implementation of bitcoind is not only ancient, it's drifted. I made the decision to not attempt to follow btcd down that road, and instead we serve up actual/current bitcoind under btcd. It's a compat break, but I think a pretty reasonable one.btcd implements json-rpc-v2, which includes batching and a version property (and some other semantics). However it does not implement named parameters, which is an incompatibility with jrpcv2. We do support named params for btcd, which is a compatible extension. I derived the jrpc parameter names from the Go source code.outs bucket count disables the address index, and a higher bucket count reduces its load factor (e.g. for faster search on bigger machines).shelly at ~5Gbps (closer to 4.6) is 23.8 mins, pruned and with the full address index (all LF2.5). That's fully network limited.8GB for milestone and 16GB for full validation, +2GB each on macOS. Below those levels heads cannot remain mostly RAM-resident, and the node will thrash during sync, and Linux/macOS may OOM kill the process.22 secs no hash, 36 secs muhash, and 83 secs serialized hash. These are 3 different options for the utxo accumulation state in the bitcoind rpc call. Peak private memory during the scan has been reduced to 1.5 GB (2.3 GB serialized). These are insanely fast numbers given that this is scan of all outputs, and their spenders, in the confirmed chain of 950k blocks, correlated to detect spentness, and the hashing includes the hash accumulation of the entire set of unspent output scripts and values. Nothing is cached or indexed, just a very parallelized total scan.scantxoutset method Core returns the unspent outputs of an address. Of course it has no address index, so it just searches the entire utxo index. So just for fun it returns in the response, not just the relevant unspents, but the count of unspents that it searched (i.e. the size of the utxo index). This number isn't useful for anything, but they just have it so why not send it.
So we have an implementation of the method that does the same - a full scan of the chain, about 25 sec, and returns the nonsense number. But with the address index enabled, we can return the answer immediately - but without the utxo set size. I've implemented it such that when there is an address index, the set size just returns the found size, and without it we do the full scan (just for this one method).
But today there are two fun warts. http is half duplex, so you can't make a second call with a first call pending. But these calls are slow. So what Core does to get progress or cancellation for these calls is to accept those from an independent call. A second call asks if there are other calls outstanding on another channel, and allows a second call to cancel them (instead of the first caller just dropping the connection, which is what we do). And of course if the call hasn't returned yet, you know it's in progress still.
So why do this? Because they don't monitor the socket for drop when there work in progress. As a result, when the first caller drops, there's no association to the work in progress. So the long-running query just keeps running! So a second caller can say hey - stop that other call that I dropped. And you might be wondering, can the orphaned calls accumulate? No, because they allow one channel scan to block scans on all other channels. Seriously, it's this stupid.ZeroMQ socket is in and tested, now working on CurveZMQ (CurveCP for ZeroMQ). We had this in v3, but with the full ZeroMQ dependency (what would also now be a Libsodium dependency given that ZMQ dropped Tweet NaCl). This is so much simpler, since all we implement is ZMQ framing as an ASIO stream, and Curve as a privacy stream (same as the Boost Beast TLS stream, and our custom bip324 P2P privacy stream).CurveZMQ is integrated, including config and utilities for keygen. We have zmq keygen commands in bx, and the preexisting conventional base85 key encoding, so these will drop right in for bx support as well. We can add self-signed tls certgen there also, once WolfSSL is factored into a base lib, since bx doesn't take the network dependency.ZeroMQ interface is merged, including CurveZMQ with both server auth and mutual auth (neither available in bitcoind natively). Config settings:
[bitcoind_zmq] # IP address to bind, multiple allowed, defaults to empty (disabled). bind = <value> # The Z85 encoded CurveZMQ public key of an authorized client, multiple allowed, defaults to empty (any client). cert = <value> # The required maximum number of connections, defaults to '0'. connections = <value> # The maximum connection duration, defaults to '60'. expiration_minutes = <value> # The idle timeout, defaults to '10'. inactivity_minutes = <value> # The Z85 encoded CurveZMQ server secret key, defaults to none (unencrypted). key = <value> # The maximum allowed request size, defaults to '4000000'. maximum_request = <value> # The maximum topic subscriptions per connection, defaults to '100'. maximum_subscriptions = <value> # The minimum retained read buffer size, defaults to '4000000'. minimum_buffer = <value> # The send rate limit in bytes per second, defaults to '0' (unlimited). rate_limit = <value> # IP address to secure (CurveZMQ) bind, multiple allowed, defaults to empty (disabled). safe = <value>
sha3 based on our sha::algorithm<> template. And also base_32 based on our radix library patterns. The previous base_32 ("bech32") has been renamed to base_32b as it's not the RFC compliant one. These are complete and tested in system, and will provide the necessary support for the bip155 addressing enhancements listed above.zeromq socket and notification service.system::chain.
Also working through a generalization of network::channel_rpc to allow the jrpc services (electrum/sparrow, bitcoind/btcd) to operate over tcp/s, http/s, ws/s over the same socket, based on the connection handshake (and ws upgrade) alone - controlled by caller. This will also simplify some code.mmap implementations. Presently working on getting it to flush and settle RAM as the node becomes fully synced. This is automatic on Windows, but we have to aggressively hold head RAM in posix, which means we need to let it go as well. Working well.[sparrow] service to be integrated as a derivation from [electrum] in the same manner as [btcd] derives from [bitcoind]. Sparrow contains two extension methods from frigate and another unique to sparrow, and since electrum doesn't look to be taking this in the immediate future, I just dropped it in as an additional endpoint to maintain proper version control. The btcd->bitcoind and sparrow->electrum are not pass-thru, and are not doubly-implemented, they are simply class derivations.constexpr code (that evaluates at compile time) is not counted in coverage stats, either in the numerator or denominator, it's like code that's missing. We test this code with static_assert, and there's a LOT of that. So that's tacked on in the yellow bars to give some sense of scope.+/* LIBBITCOIN: fix warning when WIN32_LEAN_AND_MEAN is already defined. */
+#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
+#endifbx is now back in the fold, only dependency is system, 28 methods stubbed, to be replaced over bs native interface. lots to do still, but was a big chunk of work just to get building and tests passing.[x] warn on RAM low (hard/soft) [x] warn on CPU build if not optimal [x] warn on GPU build/config if not optimal [x] commandline option to skip warnings [x] chain/machine/wallet unit test coverage [x] add esplora interface [x] wolfssl upgrade
CPU (simd/sha/neon/crypto), min RAM, store on internal SSD, file system space, and GPU. The GPU detection uses the same criteria as ultrafast, but doesn't require it in the build. So it works like the CPU features, where we can tell the user that the build is not optimal, without the dependency in place. With the dependency in place and usable, we can also automatically enable it - making it configurable but on by default if available.--accept (or -a) command line option disables this prompt and the milestone prompt, and both prompts are disabled at daemon startup.With the dependency in place and usable, we can also automatically enable it - making it configurable but on by default if available.
not enabled incoming p2p tx or package acceptance or relay, and am not accepting conflicting txs, and am not indexing unconfirmed txs. But this will accept/reject from client/server submissions, based on own node minimum fee rate, and relay those to peers based on their minimum fee rate. I still have a bit to do in terms fee rate, and package forwarding.
The significant aspects are really in the store, whereas the store subsumes unconfirmed txs into blocks (not compact yet, just full blocks). I've enabled this once the node becomes current, and it remains enabled until shutdown, even if the node falls out of currency. This is necessary to ensure that txs get absorbed once they are accepted. It's a race condition, and duplicates are of course allowed, and we already had unconfirmed txs (as a consequence of reorgs). The absorption is costly and unnecessary during IBD, which is why we keep it disabled until current. This coincides with the node rejecting submitted txs/packages when it is not current.address pool/relay generalization. That's done now and I'll be moving on to integrating that with Tor/I2P connections. Settings changes: these are added (peer.gossip_ipv6 replaces old outbound.use_ipv6).
[peer] gossip_ipv4 = true gossip_ipv6 = true gossip_tor = true gossip_i2p = true
envelope.head.
This will allow the store to carry the one-way latch into pooling, since once pooling starts it really should remain on even through a restart, but presently there's no way to save or detect that state. It's a general issue that will keep coming up, so better to resolve it.peer.enable_privacy is being replaced by node.provide_privacy for consistency, as the latter is how we set all service bits (advertisements).node.limited_blocks is now part of the settings stored in the envelope (one way latch at store create). This ensures pruning stays consistent (checkpoint and milestone blocks are pruned). However since the milestone drives pruning, it gets stored and therefore becomes immutable as well.address v2 complexities of including tor/i2p. All complete now in seed/outbound/manual/inbound, with the ability to enable disable/gossip of IPv4/IPv6/Tor/I2P. The only limitation is that we don't have support for incoming I2P connections. This enables completion of some of the WIP bitcoind/btcd methods around connection admin as well.--settings option so that it now emits all config setting values, and annotates each non-defaulted value with # defined.I2P Simple Anonymous Messaging (SAM) proxy for I2P inbound. Interestingly this is an inbound proxy analog to our outbound only socks5 proxy. This will derive from our acceptor class in the same manner that the socks proxy derives from our connector class. Config almost the same as socks, the rest already implemented. This is the last piece of full support for both I2P and Tor.peer.protocol_maximum = 70016 so that address v2 gossip is available by default.--settings dump. But when configured will still show as configured using the non-default comment: outbound.password = # configuredhosts.cache with address v2.
[peer] gossip_ipv4 = true # defaults true gossip_ipv6 = true # defaults false gossip_tor = true # defaults false gossip_i2p = true # defaults false enable_address_v2 = true # defaults true protocol_maximum = 70016 # defaults 70016 (send_address_v2)
66ohefkl3ny7mzw4rhzvql4eat3iooesbp7u4mg7nuanzel5dcvfwuqd.onion:8333/1789617292/3081 41.90.136.50:8333/1789646788/3081 emusbuwtrttrwf56twzwqiy65kfepwrdvjqlbhxrxxn3spseiha4ytad.onion:8333/1788270203/201329677 60.249.8.215:8333/1789648459/1033 209.222.97.190:8333/1789639930/3145 31.21.184.247:8333/1789529806/3081 89.0.148.160:8333/1787192611/3145 2enmmjciteoq4223fhu3zjd5abtkmg6svkac7e3q2fjpk3vdmp4hwsid.onion:8333/1789597157/3081 100.40.127.65:8333/1789395506/3081 95.168.169.227:8333/1789617869/134220877 149.102.228.62:8333/1787543186/67112009 86.49.255.26:8333/1789637844/1101 dktamk3jkks2wgjg6lrfbaya7nmqioadhppa6oauckilncwitqi2qxyd.onion:8333/1789593913/3145 tzzqhl4op7i2qncxxtwl7mlvluxelxgsrdeuqnfyvxvxmzcyho6votyd.onion:8333/1789636464/3081 viize3apqmtbzoixt3kozr7ktwc5nm5peuwtuvsvjtvpyhodxz72kgid.onion:8333/1789646121/67109901 89.35.197.146:8333/1789642995/3145 tvrb4r2cyf3rzrrpymfsx7ooikixam3k2obygm3akd6kg2ypmiba.b32.i2p/1789632435/3145 e2xlezwhgifo3sek4s7r2guepb4cuqr6rdyarv45nvrz63rerawypbad.onion:8333/1789617464/3081 98.10.145.173:8333/1789535259/201329673 85.185.32.154:8333/1789477190/3081 m54twflw5jblnrax2kivvtcyx27epxhjeyidvbkukgmydv5slxx5rjid.onion:8333/1787117698/1037 93.88.126.109:8333/1789561467/3145(edited)
tx_archived event (set for validated and archived unconfirmed tx - i.e. arrived individually or via package, over p2p or client-server interface). It was logging the surrogate key, now it's logging the fee rate (directly comparable to the config setting of min fee rate).gsl (github.com/imatix/gsl) maintained? I'm unable to build it because it requires pcre3, which apparently was deprecated and replaced with pcre2 😅getblockfrompeer, as I can find no scenario where it makes sense. It implies we have a header, and want a block, and aren't getting it so it's holding us up, and there's a given peer that we are already connected to that we know has it so we'll make that request and stuff it into the store if it succeeds (otherwise nothing). We already make requests to all the peers we are connected to (and can get connected to) to find a block that isn't being found. This seems to be some kind of test hack and maybe an assumeutxo hack and maybe a hack to unstick a stalled sync (even though it will eventually move to other nodes). One of the dumbest I've found, but in this case there's also no way to make sense of it given our architecture.We pace all responses to their requests
outbound address binding. This allows you to control what NICs your outbound traffic is carried on. Inbound traffic is already bound, independently for each service. Outbound by default choses bindings sort of randomly. This prevents useful load balancing in most scenarios. On Linux you may have to modify your local routing tables to get efficient balancing, but otherwise it should balance fairly across networks of comparable available bandwidth. I haven't incorporated anything to manage networks of different quality, except that this allows you to avoid certain addresses (e.g. your Wi-Fi NIC).24.7 mins milestone after all above changes (record is 23 mins), so still going strong.libbitcoin-system coverage is about 98.17% after factoring in the thousands of static_assert(...) tests and the behavior of INLINE on the coverage metrics.machine:: namespace should be zero at this point, which is a higher bar.nvidia-smi shows proper support with processes using the GPU as expected):
2026-09-23T23:50:16Z.0 Hardware configuration... 2026-09-23T23:50:16Z.0 intel... platform:1. 2026-09-23T23:50:16Z.0 avx512.. platform:0 compiled:0. 2026-09-23T23:50:16Z.0 avx2.... platform:1 compiled:1. 2026-09-23T23:50:16Z.0 sse41... platform:1 compiled:1. 2026-09-23T23:50:16Z.0 shani... platform:1 compiled:1. 2026-09-23T23:50:16Z.0 gpu..... platform:1 compiled:0.
Version Information... libbitcoin-system..... 4.0.9791 24165d6b-dirty libbitcoin-database... 4.0.4407 3d19705c-dirty libbitcoin-network.... 4.0.7077 5cab58c1-dirty libbitcoin-node....... 4.0.4002 726f5981-dirty libbitcoin-server..... 4.0.2979 355cd0c0-dirty compiled schema....... 4.0 database schema....... 4.0
1.45 hrs. Validation from 950k to top took 2.6 hrs, since there are just 4 slow threads running at 90-100%.gold-plating shall continue.bypass block.tx validations for pooled txs. Pooled txs are validated and archived, so there is no reason to do either again (except in the very rare case where the validation context has changed, which we manage by revalidating in the block's context).
First we added pooling to make wallets happy. Then we added block consumption of pooled txs to save space and prevent the unnecessary archival step. That's been running all week. But when we validate we still pull the whole block and prevouts, and validate the whole thing. That's very wasteful, since most of the txs have already been validated.
So now we are writing the validation context, along with fees, sigops, and all prevout parent tx links, to the validated_tx table for each tx. This now happens at every tx/package acceptance, in the new tx chaser. These values are all that is required to validate and confirm the tx for the block, without ever reading the tx. This part is done - table schema updated, queries updated, and unconfirmed txs validated and archived with this state as they arrive. That part's done.
Nest step is to identify the state record for each tx in a block for validation. This is optimized by the fact that we can determine from the header->txs->tx table association values alone, whether the tx was pooled or arrived with the block (unpooled). Pooled tx links will always be lower in value than the coinbase link in the association set. So we query up the metadata for pooled txs, validate the other txs, use the combined metadata to validate the block, and set the state of the block.
The prevout parent tx links are used, along with those of the queried/unpooled txs, to populate the prevout table cache, for optimization of block confirmation. The prevout table has been used for a couple years now, and is purged once the node is fully synced (current-coalesced state). Signature batching tables are also dropped in the purge. And now the tx metadata (validated_tx) table will be as well. This last one we'll probably retain much longer for miners in v5.can we do compact blocks without a mempool
validated_tx table as a SoA (multicolumn) that will allow us to scan for compact block identifiers using SIMD or GPU, though SIMD is plenty fast unless it's like a 100M txs in the pool.
The genius of compact blocks is that you have to perform a linear scan over every single unconfirmed tx for every single compact_block.tx. It cannot be indexed, because the key changes with every new compact block. It's really the same model as silent payments (stealth) scanning.
And in another stroke of batshit crazy, cb v2 operates over the wtxid, which wasn't necessary, isn't optimizing or DoS protecting, and there is no other compelling reason to store it. But it's 32 bytes per tx, which is huge. So finding a way to avoid this storage and the cost of sweeping it for every mined tx, and still validate the block in picoseconds is the challenge.validated_bk table to state (this carries the state transitions of a block as it moves through check->accept->validate->confirm->organize (or unconfirmable). And the validated_tx table to pool, as this now holds something like a tx pool, not validation states.
Transaction metadata remains in this table after a tx is confirmed (append only), though the table is pruned once the node becomes current (along with the prevouts cache and the signature batching tables).qrencode C lib has been replaced with our own native c++ implementation of just the parts we use. github.com/libbitcoin/libbitcoin-system/pull/2014Hardware configuration... intel...... platform:1. shani...... platform:1 compiled:1. sse41...... platform:1 compiled:1. avx2....... platform:1 compiled:1. avx512..... platform:1 compiled:1. avx512ifma. platform:1 compiled:0. avxifma.... platform:0 compiled:0. aesni...... platform:1 compiled:0. vaes....... platform:1 compiled:0. gpu........ platform:0 compiled:0.
10% faster at Taproot Tweak (part of script verification) because we compute the tweaked key as tweak·G + P with a single point addition. libsecp256k1 doesn't implement batch vectorization via SIMD, so we are are 34x faster at CPU batching on AVX512. Other configurations show 0-2% slower on scalar signature verify, but there's still a little room left to squeeze that out.
1.41 hrs on shelly, with address index. Previous range of 1.34 hrs to 1.70 hrs tho those were without the address index.
sha512 on newer platforms, which I'm adding in now. Don't have a machine for it though.libsecp256k1 or UltrafastSecp256k1, through installer and visual studio integrated options, and currently ultrafast is the only GPU option. I haven't retested CPU batching in a while, I'll be doing that soon across all three (libsecp256k1 is scalar only, so it's the floor).UltrafastSecp256k1 now fully dominates on libsecp256k1 in benchmarks some parity most dominatntLibrary Notes ------------------------- --------------------------------------------- aes256 chacha20 chacha20_poly1305 fschacha20 fschacha20_poly1305 salsa20 xsalsa20_poly1305 poly1305 x25519 secp256k1 ecdsa, schnorr, ellswift, recover, keys, math, batch sha sha160, sha256, sha512 (all variants) sha3 keccak (k), sha3 (h224, h256, h384, h512) rmd rmd128, rmd160 hmac hkdf pbkd PBKDF scrypt siphash muhash
UltrafastSecp256k1 uf can be without gpu side also if you dont need gpu part cpu not dependent on gpuVersion Information... boost................. 1.91.0 secp256k1............. internal openssl (tls)......... wolfssl 5.9.2 libbitcoin-system..... 4.0.9899 323645f2 libbitcoin-database... 4.0.4474 380acf4a libbitcoin-network.... 4.0.7103 752690d0 libbitcoin-node....... 4.0.4039 6cba6135 libbitcoin-server..... 4.0.3047 38fed506 compiled schema....... 4.0.1 database schema....... 4.0.1
Hardware configuration (arm64)... crypto..... platform:1 compiled:1. sha3....... platform:1 compiled:1. neon....... platform:1 compiled:1. gpu........ platform:1 compiled:1.
Hardware configuration (x64)... aesni....... platform:1 compiled:1. vaes........ platform:1 compiled:1. shani....... platform:1 compiled:1. sha512...... platform:0 compiled:0. sse41....... platform:1 compiled:1. avx2........ platform:1 compiled:1. avxifma..... platform:0 compiled:0. avx512...... platform:1 compiled:1. avx512ifma.. platform:1 compiled:1. gpu......... platform:1 compiled:0. Available hardware acceleration is not compiled into this build.
AVX512 platforms that cause downclocking (including bigmac) and reporting those as platform: 0 because the loss exceeds the gain (unfortunately, because the gain is otherwise good). But we retain the options on the newer platforms that don't downclock after its use. Core doesn't implement avx512 vectorization.avx512ifma, as was predicted.#ifdef noise in the algorithms, and it's a single for for scalar and vectorized at all three levels. We come out on top in taproot, sha1 and sha512 simply because we have the intrinsics implemented and brandx doesn't. This is all done in C++, not a single line of C or Assembly.RPi5) is passing 900k blocks on its meager 4 threads, totally CPU bound, otherwise calm and steady. Despite growth, the straight-line completion projections are slightly declining on a per tx basis. This would have been impossible a year ago.include/bitcoin/system/impl/chain/script_patterns.ipp file - The results are great overall - over 95% of mutation score. You can be seen the result at: mutanthub.space/mutants?project=libbitcoin%2Flibbitc…&file=script_patterns&…