Skip to content

Release v0.15.0 - xchain-sync - #8

Open
jdogresorg wants to merge 17 commits into
masterfrom
release/v0.15.0
Open

Release v0.15.0 - xchain-sync#8
jdogresorg wants to merge 17 commits into
masterfrom
release/v0.15.0

Conversation

@jdogresorg

Copy link
Copy Markdown
Contributor

Cuts xchain-sync for the v0.15.0 release train. 17 commits from master.

Added

  • attestation_responses is registered as a hub-mirror table.
  • The bootstrap mirror is vendored instead of living only on the file master.

Fixed

  • The replication-source read asks the hub for unredacted config secrets.
  • Replica columns widen from the shared utf8mb4 definition.
  • mariadb moved off the cleartext-credential advisory range with the floor pinned in the dependency gate.

Changed

  • The vendored coin registry is resynced from the hub.

Levelling develop with master, the ceremony step that closes a train. Commits
that reach master without coming back leave the branch all work lands on missing
what shipped, so the next diff off develop reads as though it never happened.
The script that copies published bootstraps from the file master out to the hosts that actually serve them existed nowhere but that one machine. It was unreviewable, undeployable and unrecoverable if the box were rebuilt, while being the only thing standing between a published archive and the public.

It now takes every deployment specific from the environment and refuses to start without it, so this copy carries no site topology.

Two corrections ride along. The cron must be hourly rather than daily, because a fixed daily slot can fall entirely before a publish that runs for hours, in which case the mirror walks the previous tree, exits zero, and leaves the serving tier advertising a stale bootstrap while every component upstream reports success. And the payload leg now excludes the publisher's in-flight .part uploads, so a pass landing inside the publish window no longer hauls a partial multi-gigabyte archive to every target only to delete it on the next pass.

The README records the trap behind all of this: the file master is not in the serving path, its docroot entry for the payload tree is a symlink, and the publisher's success line means the archive reached the master rather than the internet. Verification has to run against every address the hostname resolves to.
The twin of the indexer's entry, plus the reading-aid comment that lists
which tables are hub-mirrored rather than produced by block processing.
One wave of the review round on the xchain-platform board.
Every change was re-derived from the code rather than applied from the
finding recommended option, and each carries a control that reproduces
the original failure.

Review findings: 9a986c956bb3
…header

The review round corrected this shared module's header in the indexer copy
(#6494): it claimed the indexer flips the signed shape on this anchor, when the
indexer has no call site at all. The file is vendored in five repos under a
byte-identity gate that permits only the "live in xchain-{...}" line to differ,
so the correction has to land in all five or the gate reds.

Mechanical sync from the indexer copy, each repo keeping its own live-in line.
Carries the sync-side working-tree changes from review rounds 6 and 7.
Review-round fixes.

After the reward-uniqueness migration widened the key to five columns, this
service still spelled the reward identity with the pre-migration four at eight
executable sites, so reconciliation deleted distinct archive rewards, rollback
restored them under the wrong identity, and streaming deduplicated on the old
key. Fixed once across the mirror delete join, the reconciliation qualifier, the
rollback restore and the streaming dedup.

Suite: 2020 passing, 0 failing.
…cannot pin the socket

_post cleared its abort timer when the response HEADERS arrived and never released
res.body, so a collector that answers 200 and then stalls held the socket open with
nothing bounding it. Measured against a stalling collector, the socket was still
open at three seconds and the batch was counted as shipped.

That is worse than a leak, because the timeout was configured and did nothing: at a
400ms ship timeout the timer cleared at 22ms, so the stall outlived its only bound
and the shipper reported success.

The body is now cancelled inside the same then, within the abort timer's window,
rather than after finally has cleared it. The stream is cancelled and never read,
and a cancel on an already-errored body is swallowed.

Every existing test injected a transport and bypassed the real fetch path, which is
why this survived. The new case drives _post itself.

Vendored copy, written by the hub sync script and never hand-edited. Parity is
gated in the hub.
…ebit

The restore picked the amount before the FIRST orphaned debit by ordering on
(block_index, execution_index, slash_position). That is not application order. A
re-entrant nested EXECUTE runs its SLASH first but takes a HIGHER action_index
than the frame that called it, so the parent's later debit sorts earliest and the
restore lands a value one slash short.

Reproduced against a SQL engine on the shipped statement: a row worth 1000,
slashed 300 by a nested frame and then 200 by its parent in one block, restored
700 rather than 1000.

Debits on one row form a strictly decreasing chain, and the orphaned range is a
suffix of that chain, so the HIGHEST orphaned prev_amount is the pre-orphan value.
The positional ordering stays as the tiebreak for numerically equal amounts, which
a grid-rounded zero take can produce.

No schema change and no flag day: the restore only touches post-reorg live state,
and this moves it toward what a from-genesis replay produces.

This predicate is duplicated across two services and the two spell the SQL
differently, so parity is now gated on the BUILT statement rather than on file
text, from both sides. The comment each carried claimed a gate that did not exist.
…r's head

The archive rollback reset every head stamped at the rolled-back sequence, not
just the one that published the orphaned chunk. Two publishers at one sequence
meant a surviving batch had its stamp cleared because a DIFFERENT publisher's
chunk was orphaned.

The reset is now scoped to the chunk's own author, and a chunk whose author
cannot be resolved resets nothing rather than resetting everything.

Consensus-affecting, so it is gated. Mainnet ships UNARMED on the house sentinel
and naming the activation instant is a separate operator act; testnet and regtest
run from genesis. The arming guard refuses to arm the scope while the replica
still accepts an omitted network, so the two halves cannot drift into a state
where one scopes and the other does not.

This predicate is duplicated across two services and both halves move together.

Control: the new suite is 6 passing / 2 failing with the scope join removed and
8 passing / 0 failing with it, executing the real query the rollback builds
against a SQL engine. Reset targets go from both heads to one. The unscoped case
is asserted as a live expectation, so the defect stays reproduced rather than
merely described. The drift guard was also driven: removing the splice from one
half alone reddens it.
@jdogresorg jdogresorg changed the title Release v0.15.0 Release v0.15.0 - xchain-sync Sep 7, 2026
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