Skip to content

Release v0.16.0 - xchain-decoder - #16

Merged
jdogresorg merged 6 commits into
masterfrom
release/v0.16.0
Sep 8, 2026
Merged

jdogresorg merged 6 commits into
masterfrom
release/v0.16.0

Conversation

@jdogresorg

Copy link
Copy Markdown
Contributor

XChain Platform v0.16.0 release train: this repository's frozen release/v0.16.0 branch, carrying every develop commit since its last tag plus the version bump, CHANGELOG block and README refresh.

A minor train. Two consensus rails arm on Bitcoin testnet by height, both ahead of the chain at cut time: zero-confirmation ATTEST service from block 151800 and ROLLCALL v1 consensus gates from block 152208. Mainnet is unratified and byte for byte unchanged. Testnet indexers roll before hubs, the whole fleet inside one roll-call epoch.

Merge order per the release runbook (section 6, 5b): hub, then decoder/sync/encoder/utxo-tracker, then indexer, then explorer, e2e-test and sdk, node last with the pinned manifest. Merge commit, never squash: the train tag is cut on the merge commit.

Published files may not name internal tooling paths. Each comment keeps what it
was explaining and drops the path: the validator compose sentence, the log
shipper's note about the watch collector's parser, the perf job's note about the
platform job-reachability config, and the indexer's cost-report, precision-spec
and cost-weighting citations.

Text only. No behaviour changes.
chore: cite the platform tree by name, not by internal path
… as a reorg

A BTC mainnet node still in initial block download reported a tip 2666 blocks
below the stored tip (operator report, 2026-09-07). The tip-regression branch
called it orphans, verifyReorg rolled back 126 valid blocks to the safe-depth
ceiling, wrote the durable REORG_HALT and the container crash-looped 279 times
over a reorg that never happened. Harmless only because mainnet carries zero
dispensers; after launch the same first sync is a forced full resync.

Two guards. The parse loop reads initialblockdownload off the getblockchaininfo
reply it already holds and waits (one warn line, 5s poll) while it is true,
letting the forward hash compare decide once the node passes the stored tip.
And the above-tip branch of verifyReorg, which knows its depth before the first
delete, refuses up front when priorDepth + deletes + (stored tip - node tip)
would cross the ceiling: nothing deleted, no REORG_HALT, no resync owed. The
refusal is tagged and the tip-regression call site waits on it instead of
exiting into a restart loop. A prior depth already at the ceiling still takes
the durable halt.

Three harnesses pinned the old delete-to-the-ceiling order with a node tip far
below the stored tip; they now use a one-block above-tip gap with a hash fork
below it so every ceiling case still reaches the halt it pins.
… the health surface

A REORG_HALT marker could only be cleared by rebuilding the schema, so a database
nothing had been purged from (a pre-launch mainnet decoder with zero dispensers,
operator report 2026-09-07) still owed a forty-hour resync. The marker is also
easy to misread: the same database carries xchain-sync's sync_halt table, which
the operator queried instead.

The newest of REORG_HALT / REORG_HALT_CLEARED now decides (db.readReorgHaltState;
isReorgHalted and getReorgHaltMarker ride it). db.clearReorgHalt writes the
clear row carrying the reason, the check results and the halt it supersedes,
confirmed by read-back; the halt row is never deleted. src/clear-reorg-halt.js
(npm run clear-reorg-halt) is the operator tool: it refuses while rolled-back
blocks are still missing above the tip (not forceable), refuses a database that
has held dispenser state unless --force (recorded as forced), and --dry-run
reports the verdict. Health carries reorg_halt_cleared_at and
reorg_halt_cleared_reason once a halt has been cleared.
…ng_up

While the parse loop waits out a node in initial block download below the
stored tip (d3cd27b), the height stops moving and every health surface still
reads green, which is indistinguishable from a wedge. The wait is now held on
the instance as {node_height, stored_height, since} and published as
node_catching_up on /live, the health RPC and /status, null when not waiting,
so xchain-node ps can name it and the bootstrap restore can tell that this
image waits. Cleared when the node leaves IBD and when it simply reaches the
stored height, the usual end, which the wait branch never sees.
@jdogresorg
jdogresorg merged commit c3b2e8c into master Sep 8, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant