Skip to content

Release v0.15.0 - xchain-explorer - #20

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

Release v0.15.0 - xchain-explorer#20
jdogresorg wants to merge 57 commits into
masterfrom
release/v0.15.0

Conversation

@jdogresorg

Copy link
Copy Markdown
Contributor

Cuts xchain-explorer for the v0.15.0 release train. 55 commits from master.

Added

  • Collectibles gallery, rich list and governance overview pages, with list and detail pages composed from the shared component layer.
  • Cross-chain call completion and expiry stream on an xcall WS channel.
  • Attestation responses that have no transaction of their own are listed, timestamped from their own block.
  • The poll winning option rides the list feed and is named in the detail view.
  • The hub-mirror client is re-vendored for the attestation_responses table (schema version 5), its batch link and the mirror status accessor.
  • A freshness-alert script (bin/check-explorer-freshness.sh) for cron that mails the operator when any non-regtest coin is stale-gated or its replica carries an active sync halt.
  • The testnet4 tip-gate drop-in (deploy/tbtc-tip-gates.conf) is version-controlled: it widens TBTC's future-skew and age gates so a legally future-stamped tip cannot 503 a healthy chain.
  • A rate-limit drop-in (deploy/rate-limits.conf) pins all eight origin limits explicitly, so a production host's numbers do not depend on which build is deployed.

Changed

  • The app-wide rate limit defaults to 1080 per minute and the proof and checkpoint-verify limits to 90, sized to a measured five-address wallet session with headroom.
  • The proxy-hop setting moves to src/trustProxy.js, so the address the rate limiters key on is covered by a request-level test instead of a source check.
  • bin/vendor-vm.sh check keys its one write on an absent vendored src/ tree instead of an unreadable version string, so a drifted copy is reported as drift rather than silently repaired and passed.

Fixed

  • The getData result cache and the network-totals cache are keyed to the indexed tip, so a cached balance cannot outlive its block.
  • A self-syncing coin with no hub endpoint is refused instead of serving a frozen mirror.
  • A malformed checkpoint id or a non-numeric block id is rejected before it reaches the database.
  • An empty search renders an empty-state page instead of firing four 404ing feeds.
  • Responses carrying mutable lifecycle fields stay out of the no-TTL action cache.
  • Tx-less actions appear in the feed, reached through the action's own block index.
  • The dispensers list serves the lifecycle state and a truthful escrow.
  • The roll call detail block has a card config and reads the client as composed.
  • The config-secrets read asks the hub for unredacted values and warns once when a response comes back redacted.
  • mariadb moved off the cleartext-credential advisory range with the floor pinned in the dependency gate.

Changed

  • The home and about pages are modernized and the developers card points at the XChain SDK.
  • The db reader hotspot is split into action-list, market and staking modules.
  • The vendored coin registry is resynced from the hub.

…cover the page when a coin stops being stale
… database

A non-numeric path segment was bound as NaN and threw inside the driver, so a
bad id answered 500 rather than a refusal. It is now rejected with the same
shape and code the sibling verify route already used for a malformed height.
Byte-identical to the indexer's definition, as the conformance suite
requires. The explorer has no table-creation machinery of its own, so its
embedded mirror builds its schema from these vendored files.
The mirror gains an eighth table. The version is compared with strict
equality and a mismatch parks the whole mirror, by design, so the three
copies flip together in one synchronized window.
Byte-identical to the indexer's copy, as the conformance suite requires.
Above the response-mirror activation height a finalized attestation is
applied as a system-synthesized action: it has a real action index and a
real block, but no transaction. Two discovery queries joined transactions
inside, so such a row did not merely lose a column, it disappeared. The
list dropped it, and because the change detector discovers new actions
through the same query, the websocket event for it never fired at all.

Both queries now join blocks off the action's own block index and leave
transactions optional, which is the shape the history query already uses.
This widens the action feed beyond attestations: every system-synthesized
action was being excluded the same way, and they now ship with a null
transaction hash. That is the intended behaviour, and it is visible to
existing subscribers.

The attestations list also had one column answering two different
questions. Status carried the attester's HTTP result, so an attestation the
chain rejected read as ok. That column is now Response, and a new Action
Status column carries the chain's verdict, which is also what tints the
row. Nothing was removed. The list's tail fields are read by position here
because the shared parser takes the last two columns as status and action
index, which this feed's three extra fields displace.

The response panel gains the on-chain batch link, with an empty state that
distinguishes a body the chain has not carried yet from a response that was
its own transaction.
…ernance routes

Also reads the poll winning-option test through the shared content-source
helper, so it sees the composed page rather than a raw file.
…-source helper

The client is composed from components now, so reading the entry file alone
sees only part of it.
… registered routes

loadApiData keeps its own action-to-endpoint map beside the list-page one and
nothing held the two together, so an irregular name added to one side silently
built a URL no route answers. A miss there is quiet: the fetch fails and the
detail card never fills in.
…e overview

Three composed views over data that already had an API. The gallery classifies
tokens from their ISSUE fields as indivisible with a frozen ceiling and applies
no curation; the rich list ranks one token's holders alongside its supply stats;
the governance page shows token polls and network-parameter proposals side by
side without merging two separate systems. Also adds the point reads a
cross-chain call's live channel needs.
…hannel

A call's terminal transition on the source chain is a direct status write by the
callback interlock, so the actions cursor never sees it. A third cursor over the
resolved height emits both outcomes, and the channel is keyed by call_id, the
only stable name a call carries on both chains.
… the client as composed

The roll call block shipped without a row config, so it mounted with no order at
all, and the eviction-label test evaluated the client entry file alone, where the
helpers it calls no longer live.
Byte twin of the hub's definition, re-vendored so the conformance guard
keeps holding once the mirror carries batch_action_index.
…batch link

Byte twins of the indexer's client and table definition, so the conformance
guard keeps holding now that the attestation response apply can fill a null
batch link once while leaving every signed column as first inserted.
A response applied through the hub mirror has an action but no transaction,
so joining blocks through the transaction left its detail page showing a
block link with no time beside it. The join now reads the action's own block
index, which both delivery paths populate identically, and the conformance
suite asserts the timestamp for a tx-less response alongside the
transaction-backed control that proves the fixture discriminates.
…ead paths

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: 6422 6468 6470 6532 6557 6564 6565
…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.
…and make the trust-proxy hop a tested seam

The app-wide, action-proof and checkpoint-verify limits move to 1080, 90
and 90 per minute per client, derived from a five-address wallet's worst
minute with retries and 3x NAT headroom; the other five limiters keep
their shipped values. All eight are pinned in a version-controlled unit
drop-in for the production host. The trust-proxy hop count now lives in
src/trustProxy.js so the security test drives real requests through it,
on the HTTP and the WebSocket upgrade paths, instead of grepping source.
…y one host's layout

The logrotate stanza, its README and the rate-limit drop-in carried a
service account name, measured disk figures and the drop-in inventory of
a running host. They now state the reasoning without those specifics, and
the trust-proxy comments describe a fronting proxy and a CDN rather than
naming a particular stack.
A synthetic UNSTAKE v2 cooldown-completion never displayed its token or
contract even though the backend computed both.

Review round 7 finding #6858. Also carries review round 6's explorer work.
Review-round fixes.

Includes a cross-repo repair: the vendored hub-mirror client had drifted from
its canonical source because separate changes landed on each copy, which
reddened the byte-identity gate. The canonical copy proved the strict superset,
so the vendored copy was re-synced through its own sync script rather than
hand-merged.

Also: an attestation batch link that targeted the response chain instead of
DOGE; PRICE oracle token links that ignored the token's declared chain; and the
theme-parity probe now self-checks its capture before persisting it as evidence,
so an empty or mis-targeted capture cannot read as a pass.

Suite: 4020 passing, 0 failing.
…absent

check mode could overwrite a populated vendored tree, so a local edit under the
destination was silently replaced instead of reported. The indexer's copy already
carried the guarantee; this ports the behaviour rather than the file, keeping the
two implementations independent.

The single write is now keyed on the destination's src/ being absent, so a
populated tree is left alone and the drift is reported instead.
…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.
… accepts

The provider-byte columns of the attestation response mirror sat on a utf8mb3
tail while the on-chain fields they stand in for are utf8mb4, so a 4-byte
character failed the mirror INSERT with errno 1366 under STRICT_TRANS_TABLES.
A body the on-chain path would have carried never reached the mirror, and the
request it answered expired unresolved.

Both columns now declare utf8mb4, with a dated migration for deployments that
already have the table: a definition edit alone reaches only fresh installs.
The pinned 5.0.4 is affected by a critical ExternalCopy advisory. Moving to 6.2.0
across the vm and both of its consumers, which have to agree because the
consensus runtime pins an ABI and a validator fails closed against it.

The bump also removes a constraint rather than adding one. 5.0.4 had no prebuilt
binding, so every install compiled through node-gyp and the build failed on
newer Node for a removed V8 API, which is what bounded these repos to Node 22
and what makes a shared checkout hostile to two platforms at once. 6.2.0 ships
prebuilt bindings for both ABIs, so the dependency no longer sets the bound and
an install lands the right binding for the host that runs it.

Node 22 stays the bound for a different and still-live reason: the consensus
runtime pins process.versions.modules and the validator refuses any other ABI.
The engines rationale now says that rather than citing the build failure it no
longer has.

Adds a containment test over the isolate's reference surface, since a major
version of the isolation boundary is the wrong thing to take on trust.
…canonical source

The canonical table in the indexer gained an index whose time column leads, so the
oracle preload's consensus-time bound is a range scan rather than a filter over every
finalized row. The vendored mirror is a byte-identical twin and a conformance test
asserts it, so it is regenerated here with the indexer's sync script rather than
hand-edited.
…leeping

Adds the test tree's one poll-until helper. It REJECTS on timeout rather than
returning a flag, which is the whole contract: a wait that gives up quietly turns
a flaky test into one that passes unconditionally, and that is worse than the
flake because the flake at least reported something.

Only the site waiting on a condition is converted. Where elapsed time is itself
under test, the timer stays, and the helper's docblock says so, so the next
author does not sweep one of those by reflex.
…tive_time

Vendors the widened mirror twin (one request may hold two honest rows that
differ only in the signed stamp) and teaches the mirror migration to rebuild
the unique key on an existing mirror the same way it widened the capability
snapshot key.
The lockfile's xchain-vm entry still recorded 0.12.0 while the sibling declares
0.15.0, which freezes the nested ranges it pins. The tree cannot be re-staged on
a Mac, so the recorded version is corrected, which is the remedy the failing
check names.
@jdogresorg jdogresorg changed the title Release v0.15.0 Release v0.15.0 - xchain-explorer Sep 7, 2026
The bundled sibling snapshot check compares this repository's lockfile record
of a vendored sibling against that sibling's own manifest. The shared workflow
pins siblings at develop for any pull request, and develop never carries a
version bump, so on a train that moves every component the comparison is
unsatisfiable: the snapshot has to move for the release and develop must not.

Passing the release branch through as the sibling ref makes the train measure
itself, which is what a release PR should be proving. Everything outside a
release PR is unchanged, and the checkout falls back to a sibling's default
branch when it has no branch of that name.
… gate

The coverage ratchet re-runs the unit suite, so it needs the same siblings the
gate gets, and it was still pinning them at develop after the gate had been
made release-aware. The bundled sibling snapshot check then compared this
repository's lockfile record against a version develop is never going to carry,
and reported it against the ratchet instead of the gate.

Both call sites now pass the same expression, which the shared action asks for
in as many words.
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