Skip to content

Release v0.20.1 - #41

Merged
jdogresorg merged 109 commits into
masterfrom
release/v0.20.1
Sep 24, 2026
Merged

jdogresorg merged 109 commits into
masterfrom
release/v0.20.1

Conversation

@jdogresorg

@jdogresorg jdogresorg commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Release v0.20.1

Coordinated platform release across the 13-repo v0.20.1 train.

  • Base: master
  • Head: release/v0.20.1

What changes

  • Re-slides the v0.20.1 testnet activation train from 153221 to 154074, with BTC producer/consumer activation at 154234/154291, DOGE producer/consumer activation at 67942777/67944741, and ANCHOR_ATTEST_BARRIER_ACTIVATION at 154291; LTC:testnet mirror admission remains null under dq4 (a).
  • Carries L004-28 in the indexer mirror_admission_gate.js twin: admissionCanonicalValue() is non-throwing, and an INERT consumer with a legacy-signature mismatch follows the ordinary invalid-row path.
  • Routes the L004-28 contract through the six indexer caller groups: cross_settle, xcall, xexec, attest_response_canonical, bridge_settle/canonicals, and ed25519.
  • Adds rootless bridge-proof verification, token-bridge shared gating, cached cross-chain VM snapshots, rollcall activation RPC fields, and attest-batch admission height.
  • Tightens rollback, ticker allocation, migration, API memo, hub-drain, PRICE wire, and policy-snapshot correctness.

Sibling release PRs

Filled in by open-release-prs.sh --execute once every PR above has a number:

…ation that began it and reschedules after a reconnect
…ngs the explorer requires, and a unit test holds it to the loader's required list
… the BTC instant

The v0.20.0 family was sized 2026-09-16 20:41Z from last-99-block cadences. LTC
testnet then ran at about 82 s per block against the 146.6 s that sizing assumed,
pulling its producer boundary to 3.2 h out while BTC's stayed 53.0 h out, and
DOGE's drifted 7.5 h early. Two legs of one cross-chain match would have crossed
the flag day about two days apart, which is what the same-wall-clock-instant rule
exists to prevent.

Re-measured 2026-09-17 22:45Z over a trailing window as long as the lead being
sized: TBTC 152,891 at 576.7 s per block, TLTC 4,889,190 at 82.5 s, TDOGE
67,904,912 at 27.7 s. BTC is unchanged, still keyed to epoch close 153,216 plus
6 buried; LTC and DOGE are converted onto that same instant, and each consumer
is its own producer plus six hours at its own measured cadence rather than a
block count carried over from the first sizing.

Arms the TRAIN_ACTIVATION 0.20.0 row at testnet 153,116, 106 blocks and about
17 h below the BTC producer. resolveRuleSet reads only the local map, so with no
row every block above the boundary keeps resolving under 0.19.0, and a manifest
naming 0.20.0 halts a fleet in which no build implements it.

Records the cadence-window rule the re-cut used and makes the re-size rule per
chain instead of BTC-keyed, so an LTC or DOGE drift past the six-hour ordering
margin forces a re-cut the way an overrun BTC height already does.
Apply the anchor stamp with an update-only statement keyed by match_id. Leave absent matches for the normal REST bootstrap path so stream delivery cannot expand admission.
Clean up only witness-owned replay schemas and temporary workdirs across verdict, refusal, failure, and signal exits. Preserve and report both artifact classes when --keep is selected.
Classify backdated migrations by the schema objects they touch and record the
verdict in the run result. Keep overlapping, unreadable, and opaque reorder
cases on the existing fail-closed path.
…istry note

The tokens row's derived columns other than supply (owner_id, description, the
locks, the callback and list fields, the bridge opt-in) are re-derived from the
issues history by every valid ISSUE, an in-place mutation with the same pinned
action_index as the supply bump. Name the updated_rows class that carries them
forward and the fact that it has no state_hash twin.
Resolves bin/pins/at1-consensus-identity.json by regeneration, not by
preference. bin/consensus-identity.js recomputes the merged tree's identity
to the values master already records, so master's side of the pin is taken
whole: it is develop's plus the v0.20.0 cut's re-pinned fingerprints and its
v0200_cut_note provenance, and nothing develop-only is dropped.

consensus_rules_digest is unmoved at ee6476f2, so no gate's decided height
changes. Of 301 armed-map rows, none added or removed, exactly one differs:
protocol_changes.CONSENSUS_VERSION, 0.19.0 to 0.20.0, which is what levelling
develop onto the shipped train means. The fingerprint hashes resolved values,
so it moves with that row alone: 3b41198d bare, 506aa723 armed.
The armed LTC:testnet producer/consumer heights (4891504/4891766) are
already behind the live TLTC tip. Ruling dq4 (a), 2026-09-18: LTC:testnet
mirror admission ships null on this train and arms on a later train.
Sets both heights to null in this repo's shared_rows_2.js twin, with the
same replacement comment used across all six carriers, and re-pins the
armed-map fingerprint, the two mirror_admission_activation armed_map_rows
hashes and the consensus rules digest in the AT1 consensus identity file
to match, since those move with the arming they hash. The paired
hardcoded digest in xchain-hub's own suite moves in the same edit there.

Also excludes mirror_admission_activation's two keys from the coin-keyed
bridge gate's coin-invariance check: nulling only the LTC leg makes it a
second coin-divergent gate at any height past the BTC/DOGE testnet arming,
the same shape that check already carves out for the bridge gate itself.
Propagate the canonical v0.20.1 patch-train reslide into the indexer's
own copy of the shared consensus registry: BTC train height 153221,
BTC mirror producer/consumer 153300/153328 (producer also carries
ANCHOR_ATTEST_BARRIER_ACTIVATION), DOGE mirror producer/consumer
67916857/67917706. LTC:testnet stays null under dq4 (a) and is
untouched.

Re-pin bin/pins/at1-consensus-identity.json (armed_map_fingerprint,
the three moved armed_map_rows entries, consensus_rules_digest and
consensus_rules_gates for both the bare and armed readings) so the
pin matches the new resolved values, and update the two test fixtures
that hardcoded the superseded BTC heights (153222/153266) so the
regression suite tracks the reslide instead of failing on stale
numbers.
…ually has

The refusal hint built its cd target from $(pwd) on the Mac, so it named a
/Users path the NFS-mounting Linux runtime does not have, and named the
caller's cwd rather than the repo root. Print it relative to $HOME instead
and let the remote shell expand the tilde.
…h.sh

The stale comment claimed the dispatcher ships at origin/master with a fallback
to local HEAD. The actual script prefers the pushed branch before falling back.
Matched against xchain-documentation/.ci-siblings which has the correct wording.

(cherry picked from commit 3176f8e4bf2a47e05ef8afa3cafe298dde992118)
buildPriceBatchPayload reads an absent fifth argument as the inert network and
rebuilds the legacy canonical, so an admission-era batch signed without it
carries signatures no verifier reproduces. Four harnesses omitted it. Each now
passes the network its own mock config carries, read from the config fixture so
the signing and verifying keys cannot be typed apart.

price_range_harness also gains a load-time guard that its six-round window stays
below MIRROR_ADMISSION_ACTIVATION on every graded network, so the next registry
re-cut fails as a fixture error rather than reading as a price/v0.js rule bug.

(cherry picked from commit 2c1648e8b05af4cde8968d6868b6aa1a59ccf6a6)
…it pick

restoreCapabilitySlashAmounts had no test executing the shipped SQL, unlike
its contract and delegation-rotation twins in the same file. Add a capability
describe block that runs the real UPDATE ... JOIN capability_slash_debits
statement rollback() issues against node:sqlite, over the project's own
src/sql DDL, asserting it restores the earliest orphaned debit's prev_amount
by (block_index, slash_action_index) regardless of debit-row arrival order,
leaves surviving debits applied, and is a no-op when nothing orphaned exists.
Widen shippedRestores()'s capture regex so it also grabs the stakes/unstakes
(non-contract-prefixed) restore calls alongside the existing contract_stakes
twin.
ensureTables gates on table existence, so a KEY a twin file gained after a
consumer's mirror was first built never reached it. price_snapshots gained
idx_status_timestamp_round on 2026-09-06; on the regtest rail both indexer
mirrors (built in August) still lacked it and the price barrier's
MAX(block_timestamp) read full-scanned 1.17M rows on every poll, 2 s per
read per indexer.

On the existing-table path, parse the twin's inline KEY / UNIQUE KEY lines
and standalone CREATE INDEX statements, read the live index set from
information_schema, and ADD whatever is missing by name and by ordered
column set. Never DROPs; an ADD failure is logged and skipped so an index
can never crash-loop the consumer applying rows. Twins with no declared
index issue no extra query.
…margin

Live testnet tips (TBTC 153698, TDOGE 67924122 at 2026-09-23T15:55Z)
have passed every height the 2026-09-19 reslide armed, so the freeze
preflight refuses them. The operator ruled a second reslide to 40 h
of margin to the nearest armed height at the fastest defensible
cadence (BTC least-squares bound 383.04 s/block, DOGE median 11
s/block), keeping the prior spacing.

Propagate it into the indexer's own copy of the shared consensus registry: BTC
train height 154074 (tip + 376 blocks, 40 h), BTC mirror
producer/consumer 154234/154291 (train + 160, 17 h; producer + 57, 6 h;
the consumer also carries ANCHOR_ATTEST_BARRIER_ACTIVATION), DOGE mirror
producer/consumer 67942777/67944741 (tip + 18655, 57 h, the same
instant as the BTC producer; producer + 1964, 6 h). LTC:testnet stays
null under dq4 (a) and is untouched.

Re-pin bin/pins/at1-consensus-identity.json (armed_map_fingerprint,
the four moved armed_map_rows entries, consensus_rules_digest and the
three moved consensus_rules_gates entries, for both the bare and armed
readings of bin/consensus-identity.js --json) so the pin matches the
new resolved values, and update the three test fixtures that hardcoded
the superseded BTC heights and digest so the regression suite tracks
the reslide.
… block seam

Drive the four-cell compatibility table on all three byte-identical copies of
the mirror admission gate, and add a block-loop regression through the real
catch-up loop, block transaction and CROSS_SETTLE handler: an INERT consumer
given a valid {BTC:280} row commits block 278 from cursor 277, an INERT
consumer inside the producer-armed window settles a column-less row as legacy,
and an ARMED consumer excludes the row through 279 and admits it at 280.

The verify-side canonical callers still described the retired two-way refusal;
their comments now state that both mixed cells rebuild the legacy bytes and a
mismatched signature takes the ordinary quorum skip.
canonical() no longer throws on admission columns that merely disagree
with their era; only a malformed admission column throws now.
Replace the opaque dq4 (a) decision label, an issue-tracker id, an
internal planning reference and a specific dev-machine mount detail
with self-contained public rationale. Comment and note text only; no
activation values, logic or byte-twin membership changed.
…batch anchor

3d5c9e5 added admit_block_btc to ATTEST_BATCH_ROW_FIELDS unconditionally, and
that one list drives both the signed batch canonical and the reassembly
presence check. The batch rail has published on testnet since 151324 without
the field, so an upgraded indexer refused every old hub's batch (row-field)
while an old indexer accepted it, an old indexer refused an upgraded hub's
signature, and a replay from genesis refused every historical batch.

The row field set is now chosen by the batch's own signed btc_block_height:
the admission set (admit_block_btc after effective_time) at or above the BTC
mirror admission producer activation for its network, the frozen legacy set
below it. attestBatchRowFields is the single choice point, and the canonical,
the body and the reassembly check all read it. The wire stays self-contained,
so every entry point takes the caller's isAdmissionEra and throws without it.

A v0.19.0-encoded and signed testnet batch (anchor 151400) is kept as a
replay fixture, and the new suite drives both sides of the shipped testnet
height through the real gate. The hub twin is re-vendored byte for byte.
…on era

067a9e4 refused a PRICE batch wire with fields left after its signatures
with no activation gate. Every node through v0.20.0 accepted such a wire, so
a copied batch with junk appended was valid there and invalid on a v0.20.1
replay, diverging the prices row and its hub push.

The refusal now binds only a batch whose signed BTC anchor is in the mirror
admission era for its network, the same boundary that arms admit_block_btc
and the ADMIT_BLOCKS slot; the honest field-count formula is unchanged. The
suite drives both wire forms on the shipped testnet height: trailing data is
accepted below it and refused at and above it.
…iling mean

Operator ruling D11 (a) 2026-09-23: producer moves from 67942777 to
67936053 and consumer from 67944741 to 67936888, the same-instant rule
computed off the 84h trailing block-time mean instead of the earlier
median estimate. BTC:testnet heights are unchanged; their comments now
also carry the 84h trailing mean cadence bound alongside the existing
one. Re-pin bin/pins/at1-consensus-identity.json with the repo tool
and update the test that hardcodes the bare consensus rules digest.
The floating node:22-bookworm tag can advance to a Node patch whose
V8/ICU no longer match what the consensus runtime pin expects, so the
Dockerfile now pins to the exact image digest that does.
@jdogresorg
jdogresorg merged commit 10ae153 into master Sep 24, 2026
13 of 14 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