Changelog¶
Every change of a release, in full: what changed, why, and what it cost. The release notes, which say what a user has to act on, are in RELEASE_NOTES.md; this file is the record behind them.
A release’s own pull request writes the release’s section, from the squash subjects since the previous tag, and no other pull request adds an entry (sections 9 and 12 of the organization’s standard).
Only v2026.8.7 and what follows it are recorded in the changelog at all. The releases before it were documented at release-notes length in the first place, and are still in RELEASE_NOTES.md rather than duplicated here.
This file carries the most recent release, and a (work in progress…)
section where one remains; every release before it has a file of its own
under changelog/, holding that release’s section and nothing of any
other. Past a size ceiling GitHub’s contents API answers a file with an
empty content at HTTP 200, which reads as an empty file rather than as
an error, and one file per release is what keeps each of them under it.
v2026.10.16¶
btclib requires btclib-ecc 2026.10.16¶
btclib imports point_from_octets_var and point_from_bip340pub_key_var,
btclib-ecc 2026.10.16’s names for its point parsers, and SECURITY.md cites that
release’s lines (issue btclib-org/.github#1742, closes #2595, closes #2603).
hex_string, str_from_string and base58.decode quote less¶
hex_string does not quote a negative integer. str_from_string and
base58.decode name the position of a non-ASCII character, not the character,
as btclib-ecc does (issue #2625).
int_from_integer refuses an empty input¶
It read an empty string or empty bytes as 0 (closes #2671).
Transaction and header serializers check int fields and the header’s time¶
With check_validity=False, Tx, TxIn, OutPoint, TxOut and
BlockHeader refuse an int field, or a header’s time, of the wrong type or
range (closes #2644, closes #2654, closes #2655, closes #2658, closes #2666).
The p2p serializers check every int field¶
With check_validity=False, a non-int is a BTClibTypeError and an int its
width does not hold a BTClibValueError; a bool is still written as 0 or 1
(closes #2656, closes #2663, closes #2664, closes #2665).
The signature hashes refuse a bool or a float in an int field¶
legacy, segwit_v0 and the taproot from_tx refuse one in the sequence,
vout, version, lock time and amount. A float amount never returned
(closes #2627, closes #2640).
script.parse and taproot.parse return OP_0 for every empty push¶
4c00, 4d0000 and 4e00000000 parse as 00 does, so a parsed script
round-trips. A script_sig ending in 00 gives the empty redeem script
(closes #2607).
verify_amounts bounds the prevout values¶
Each prevout value and their running total must be in 0..MAX_MONEY, as Core’s
Consensus::CheckTxInputs requires (closes #2670).
The Electrum request builders check their arguments¶
They raise BTClibTypeError or BTClibValueError for a malformed argument,
and encode_request takes only a list or a tuple as params (closes #2629).
btclib.electrum reads blockchain.block.headers¶
block_headers_request and block_headers_response read the response shape
from before protocol 1.6, for btclib-wallet’s Floresta fetcher (closes #2628).
Fuzz harnesses check round trips and run in the suite¶
What a parser accepts serializes back (closes #2585). The suite runs each
harness on its seeds (closes #2624), and the stored corpora take every seed of
fuzz/corpus (closes #2614).
The parsers replay the qa-assets corpora¶
Bitcoin Core’s and rust-bitcoin’s qa-assets fuzz corpora replay through the btclib parser that reads the same bytes (closes #2586, closes #2631).
Differential checks against btcd, libbitcoinkernel and the last release¶
btclib is compared with btcd’s wire decoder over the fuzz corpora (closes #2584), its script engine with libbitcoinkernel’s (closes #2588), and its parsers with the last release’s (closes #2587).
Property tests¶
Hypothesis strategies check the round trips of scripts, output scripts, headers, blocks and the p2p payloads (issue #2582). The retarget, median time past and timewarp bound are tested as properties (closes #2583).
Vectors and cases¶
script_tests.json is Bitcoin Core’s 6b8a5524f7 (closes #2609), and
rust-bitcoin’s huge-witness transaction is a test and a fuzz seed
(closes #2591). NULLFAIL is tested on a signature or a key that cannot verify.
Flaky tests¶
The test node starts again on fresh ports when one is taken (closes #2673). The tapscript scaling tests take the best of more timed pairs (issue #2613). The script strategies draw on PyPy (closes #2636).
CI¶
Workflows are linted with the kjanat actionlint fork; release.yml calls its
reusable workflows as $/ (closes #1563). The review job names cache-mode: none in place of .github/actionlint.yaml (closes #2633).
api-break.yml labels a pull request that breaks the public API¶
It calls btclib-org/.github’s reusable-api-break.yml
(issue btclib-org/.github#1674).
Scripts and dependencies¶
check_vendored_vectors.py and check_py_arm_authority.py print a failed gh
call’s stderr and retry a server error (closes #2600, closes #2605). uv.lock
moves bitcoin-core-rpc to 2026.10.8. Actions, hooks and the fuzz image move.
Documents¶
An owner commits the uv export on a Dependabot uv pull request (closes #2596).
REPOSITORY.md records strict off on main (issue btclib-org/.github#1712). The
maintainer’s bypass is everyday practice (issue btclib-org/.github#1695).