Releases: btclib-org/btclib-node
Release list
v2026.9.4
The second release, and the first that is an upgrade from
something. Everything below is measured against v2026.8.27:
what changed since, and what it costs to move past it.
CHANGELOG.md's own v2026.9.4 section is the record of everything
that went into it.
Breaking changes
-
A data directory written by
v2026.8.27is refused (issue #569).
A node upgrading past this change will not start against its
existing one, and says so rather than reading it wrongly -- one
line, wrapped here to fit the page:btclib_node.exceptions.IncompatibleStoreError: <data directory> holds a version 0 store, which this version (1) cannot read: delete the directory and sync againDelete the data directory and sync again; nothing outside it has to
be touched, the blocks, the undo data, the chainstate and the log
all living under it. There is no migration to run instead, and the
reason is the change itself:COINBASE_MATURITYwas enforced
nowhere because the record could not express it, a stored output
carrying no height for anything to ask how deep the coinbase that
created it was. The record and the undo data now carry each coin's
height and its coinbase bit, and both are on-disk formats a
directory written byv2026.8.27does not hold. Recovering the
missing height means reading every block again in order, which is
the sync under another name.The store now carries a schema version so that an old directory
fails on the first read rather than misparsing a record deep into
one. The released version wrote no such stamp, which is exactly what
the refusal recognises.This is the first change to break an installation of
v2026.8.27,
that being the first release there was.CHANGELOG.md's own entry
has what changed and why; this one is only what a user has to do. -
Config(pruned=True)used to construct and do nothing; it now
raisesPruningNotImplementedError(issue #574). A caller relying
on the old, silently-ignored value was getting a full node that
wrote every block to disk regardless of what it asked for --
pruned=False, the default, is unaffected. There is nothing to
migrate: passpruned=False, or drop the argument, until pruning
itself is built (issue #601). -
The rpc listener's default port is now Core's own, not the p2p
port plus one (issue #605).v2026.8.27listened on 8334, 18334,
38334 and 18445 for mainnet, testnet, signet and regtest; it now
listens on Core's 8332, 18332, 38332 and 18443, which is where
bitcoin-cliand anything else written against Core looks. A client
or a firewall rule pointed at an old default has to move; a caller
passingrpc_port=explicitly is unaffected. -
A storage fault while connecting a block used to be recorded as
that block's own rejection, silently, and the node kept running; it
now stops the node (issue #620). Where a node exits this way, do
not restart it before reading the log for the exception and clearing
whatever the store or disk reported -- restarting against the same
fault reaches the same exit again. -
A kill, a crash, or anything else that stops the node without
going through a clean shutdown can now cost revalidating up to a few
dozen of the most recently connected blocks the next time it starts
(issue #586). A datadir this happens to is not corrupted and needs no
repair: the node simply offers those blocks to itself again, the same
way it would a block arriving for the first time, and the store never
ends up holding a UTXO set, a block status or a filter more advanced
than the other two. A clean stop --SIGINT,SIGTERM, or thestop
RPC -- is unaffected and loses nothing: the store is flushed before it
closes either way, and this cost is only ever paid by the shutdown
that skips that step. -
A corrupted, node-owned
utxo-record read back while accepting a
transaction into the mempool used to be answered as that transaction's
own refusal, and could get the peer that sent it discouraged; it now
answers as this node's own fault instead (issue #631). Corrupted
here means what the store's own checksum catches, raised as
StoreCorruptionError(issue #641):sendrawtransactionanswers an
internal-error response rather thanVERIFY_REJECTED/"Invalid signatures or script";testmempoolacceptanswers the same
internal-error response for the whole batch, rather than the one
entry's own reject-reason (issue #668); the peer-to-peer path no
longer discourages a peer for exposing this node's own corrupted
storage. Read the log forStoreCorruptionErrorrather than
trusting any of them. A record whose bytes pass that checksum and
still cannot be parsed is a different case, answered as absent (issue
#650): the spend is refused as"Missing prevouts", exactly as a
genuinely missing prevout is, and nothing is logged -- which is what
Core's ownCCoinsViewDB::GetCoinanswers for the same record. -
The store is now RocksDB, not
sqlite3; a data directory written
by an older release cannot be read (issues #637, #641). A node
upgrading past this change refuses its existing directory rather than
reading it wrongly:btclib_node.exceptions.IncompatibleStoreError: <data directory> holds a sqlite3 database, which this version cannot read: delete the directory and sync againDelete the data directory and sync again; there is no migration from
the old store's own.sqlitefile into RocksDB's own format. This is
the same shape of refusal, and the same remedy, as the schema-version
one above -- it is a second, independent reason the same directory
can now be refused, not a repeat of it. What is gained for the cost:
a bit flipped on disk is now caught at the read that meets it, the
same per-block checksum Bitcoin Core's own LevelDB store already
carries, where the old store answered a corrupted value silently and
could lose a corrupted key without ever reading it at all. -
scripts/chains/mainnet.py,testnet.pyandsignet.pyare gone
(issues #583, #581, #573).pip install btclib-nodenow puts
btclib-nodeonPATH; runbtclib-nodein place of
python scripts/chains/mainnet.py,btclib-node -testnet/-signet/
-regtestin place of the other two, andbtclib-node -hfor every
flag.btclib-node -conf=<file>reads an existingbitcoin.confthe
way Core reads one. -
Config(pruned=True)now builds a node and actually prunes,
instead of raisingPruningNotImplementedError(issue #601). A
caller catching that exception around the constructor no longer has
one to catch;pruned=False, the default, is unaffected. A pruned
node keeps only the last 288 blocks and their undo data on disk and
deletes the rest as the chain advances -- there is no-prune=<n>
MiB target and nopruneblockchainRPC, only whether pruning is on.
Nothing to migrate for an existing, unpruned data directory: pruning
only ever removes data going forward from when it is first turned on. -
-prune=<n>now matches Core's own manual/automatic split, instead
of collapsing every nonzero<n>to the fixed depth above (issue
#705).-prune=1is manual pruning: nothing is deleted on its own any
more, only the newpruneblockchainRPC deletes, and only when asked
-- a node started with-prune=1expecting the old automatic deletion
now has to call that RPC itself, or use-prune=550or higher for
automatic pruning to a MiB target instead.-prune=<n>from2to
549now refuses to start rather than pruning to the fixed depth,
Core's own wording: too small a target to actually run a node on.
-prune=<n>at550or above is new: automatic pruning to roughly
<n>MiB on disk,getblockchaininfo's ownautomatic_pruningand
prune_target_sizeanswering for it. -
A mempool candidate is now also checked against Core's own relay
policy, not only consensus (issue #810).v2026.8.27held a
transaction togetblockchaininfo's activation-gated consensus rules
alone; a script only a standardness rule refuses --CLEANSTACK
among them -- now gets the transaction dropped rather than accepted,
matching what a real Bitcoin Core peer would refuse to relay or mine
in the first place.sendrawtransactionanswersVERIFY_REJECTED/
"Invalid signatures or script"for one,testmempoolacceptreports
it not allowed, and the peer that relayed it over p2p is kept rather
than discouraged -- Core's own policy is not to penalise a peer for
forwarding a transaction only the non-mandatory rules refuse. Nothing
to migrate: a transaction built to consensus rules alone and relying
on this node's mempool to hold it despite failing standardness has to
be built to a standard script instead, the same script a real network
would have refused it over regardless.
Windows
pip install btclib-nodenow claims Windows (issue #430): its
classifiers name it, andtest.yml's gate checks the claim on every
pull request.
v2026.8.27
The first release of btclib-node. Nothing here is an upgrade: no
version of this package has ever been on an index, so there is no
installation for this one to change the behaviour of and nothing to
read these notes against. pip install btclib-node reaches a released
btclib-node for the first time with this version.
What it is, and what it is not, is README.md's: a bitcoin node whose
consensus and network code is python, over
btclib. It has downloaded and
validated the whole chain. Its Development Status classifier says
3 - Alpha and means it — the interfaces are not promised stable, and
this file is where a break in them is announced from the next release
on.
CHANGELOG.md's own v2026.8.27 section is the record of everything
that went into it, which for a first release is the whole history
rather than a cycle's worth.
Two things to know before installing
- The JSON-RPC listener binds every interface and authenticates
nothing. SECURITY.md carries that and the rest of
what is known. Do not expose it. Node.__init__does not install signal handlers (#436). If a
Nodeyou build is meant to stop on an operator'sSIGINT,
SIGTERMorSIGTSTP, callinstall_signal_handlers(node)for it,
the wayscripts/chains/does right after building the node each of
them starts. This is listed here rather than under a Breaking
changes heading on purpose: nothing published can have broken,
there having been nothing published, and the change is a break only
against the unreleased tree — anyone who was running this from git
before #467 landed is the only reader it can surprise.