Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
71f153f
docs(ctlseed): restore the anchor, timestamp and round-priority comme…
jdogresorg Sep 17, 2026
821955b
test(e2e): split sleep scanner registrations
jdogresorg Sep 17, 2026
84955de
test(e2e): split hub connector registrations
jdogresorg Sep 17, 2026
c3dab61
test: isolate attest mirror aggregate legs
jdogresorg Sep 17, 2026
5d65e94
test: seed fresh attest mirror stacks
jdogresorg Sep 17, 2026
64e6a5b
test: wait for joined indexer schema
jdogresorg Sep 17, 2026
f73f349
test: provision BF6 mixed hub checkout
jdogresorg Sep 17, 2026
52f7bfe
test(parity): grade the TRAIN_ACTIVATION row and the whole shared_row…
jdogresorg Sep 18, 2026
27489e3
docs(parity): name the twin manifest without its internal path
jdogresorg Sep 18, 2026
09eef15
test(bf6): resolve the mixed-hub node_modules beside the repo, not by…
jdogresorg Sep 18, 2026
10a45f1
fix(attest-mirror): clear the stack slot before boot so a stale db-da…
jdogresorg Sep 18, 2026
1929c80
test(attest-mirror): wait for the indexer HTTP listener, not only its…
jdogresorg Sep 18, 2026
1a6eb23
test(bf2,bf4): arm the barrier-family crossing above genesis so a leg…
jdogresorg Sep 18, 2026
012769c
test(attest-mirror): give every venue leg the runner's per-slot port …
jdogresorg Sep 18, 2026
5f835f8
test(attest-mirror): retry a dropped /status socket, never an HTTP st…
jdogresorg Sep 18, 2026
2714b44
test(at0b): read the mirror schema version instead of asserting a sta…
jdogresorg Sep 18, 2026
7c2a18b
test(zc1): wait for the venue indexer to commit the request block bef…
jdogresorg Sep 18, 2026
1f473ac
test(attest-mirror): fail loudly when a runner slot has no venue port…
jdogresorg Sep 18, 2026
1cd048f
test(bf5): attach the indexer's log tail to both uncommitted-block as…
jdogresorg Sep 18, 2026
61daf53
Merge master into develop after the v0.20.0 train
jdogresorg Sep 19, 2026
742c6d8
test(at3): wait for the venue indexer to commit the request block bef…
jdogresorg Sep 19, 2026
96dafc9
test(at5): give the bridged-credit wait a verdict instead of a bare g…
jdogresorg Sep 19, 2026
15e0f39
test(at5): name why an adaptive wait could not measure lag or action …
jdogresorg Sep 19, 2026
20571ac
test(zc3): construct the victim window instead of hoping the poll mis…
jdogresorg Sep 19, 2026
eebbc89
test(barrier): add a snapshot-capability coverage seam to the family …
jdogresorg Sep 19, 2026
f69cde1
test(at1): diagnose a missing readiness dependency instead of a raw m…
jdogresorg Sep 19, 2026
b919788
feat: add offline preflight check for rail sibling repository links
jdogresorg Sep 19, 2026
a30e696
fix(e2e): bound XChainExplorerConnector requests with a timeout
jdogresorg Sep 19, 2026
b2ef3d8
feat(attestMirror): add mirror pendings census and request-row wait g…
jdogresorg Sep 19, 2026
3c77cba
fix(rail): honour the DOGE miner pause file in withMiningPaused
jdogresorg Sep 19, 2026
fa728d9
chore: drop the rail sibling-link preflight, which belongs to the pla…
jdogresorg Sep 19, 2026
7049e3c
test(at2b): prove the delivery fault is live before judging the forwa…
jdogresorg Sep 19, 2026
3581ba9
test(bf5): level the family baseline on hub-mirror readiness, not cha…
jdogresorg Sep 19, 2026
53d2d16
fix(rail): DOGE indexer regtest port is 3004
jdogresorg Sep 20, 2026
c7b0fb2
merge(lane/33): DOGE indexer regtest port is 3004
jdogresorg Sep 20, 2026
a32d98c
test(bridge-rail): run the token AT9 gates instead of quoting their r…
jdogresorg Sep 21, 2026
251e76e
test: pin offline suites and coin identity
jdogresorg Sep 21, 2026
5786d1e
Land row L013-10 from cl-l013-10
jdogresorg Sep 21, 2026
4d342ce
Merge: test: pin offline suites and coin identity
jdogresorg Sep 21, 2026
568a92a
test: an absent admit_blocks map builds the legacy canonical, never t…
jdogresorg Sep 21, 2026
50f9d4f
Merge: test: an absent admit_blocks map builds the legacy canonical, …
jdogresorg Sep 21, 2026
94b82e2
test: bind price derivation coin code explicitly
jdogresorg Sep 22, 2026
2f23feb
fix(topology): retry the published DOGE port only past a real method-…
jdogresorg Sep 22, 2026
efb2af7
test(ci): enumerate the bridge-rail DOGE miner-pause suite in the ci …
jdogresorg Sep 22, 2026
62e3a00
Merge: test: bind price derivation coin code explicitly
jdogresorg Sep 22, 2026
39095fd
Merge: fix(topology): retry the published DOGE port only past a real …
jdogresorg Sep 22, 2026
808abaf
test: BF5 inert-node assertion carries a logTail, venue logs survive …
jdogresorg Sep 22, 2026
62af8fb
Merge: test: BF5 inert-node assertion carries a logTail, venue logs s…
jdogresorg Sep 22, 2026
9387f30
fix(bf6): fetch the pinned mixed-hub commit when the clone source is …
jdogresorg Sep 22, 2026
308e744
test(e2e): replace fixed sleeps in the multi-hub activation and byzan…
jdogresorg Sep 23, 2026
fca9d3a
test: wait for the BF5 inert future-block stall
jdogresorg Sep 23, 2026
c110019
test(e2e): pin the root-bearing pre-flag-day checkpoint vector and XP…
jdogresorg Sep 23, 2026
4064c10
refactor(e2e): route bare console calls through the vendored logger
jdogresorg Sep 23, 2026
ccd1f74
Merge: test: wait for the BF5 inert future-block stall
jdogresorg Sep 23, 2026
386a57a
fix(e2e): expose live-tier verdicts to the gate
jdogresorg Sep 23, 2026
f110fb6
Merge: fix(e2e): expose live-tier verdicts to the gate
jdogresorg Sep 23, 2026
cf36dcc
fix(e2e): stop racing the gated-SEND negative check against its own wait
jdogresorg Sep 23, 2026
d9a4de1
fix(gas): pay off-BTC gas from one bridged reservoir instead of a bri…
jdogresorg Sep 23, 2026
7f4a64d
chore(release): v0.20.1
jdogresorg Sep 23, 2026
fffe2a1
Remove internal CI references from public comments
jdogresorg Sep 23, 2026
2da7171
Merge branch 'cut/v0201-dq4' into cut/v0201-land
jdogresorg Sep 23, 2026
83c9946
nativeFeeHelper: size the LTC/DOGE fee output from the indexer's curr…
jdogresorg Sep 23, 2026
f8d41ab
Fund reorg drills through gasHelper and mint a self-issued open-mint …
jdogresorg Sep 23, 2026
aae96f4
Seed price fixtures above the hub's id space so a mirrored oracle rou…
jdogresorg Sep 23, 2026
0f209a4
Pin the Node base image by digest to the consensus runtime
jdogresorg Sep 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.20.1] - 2026-09-23

### Changed
- Expanded activation, mixed-hub, bridge-rail, and attest-mirror coverage with condition-based waits and clearer failure diagnostics.


## [0.20.0] - 2026-09-17

### Added
Expand Down
10 changes: 6 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Pin Node 22 (bookworm, not alpine): the platform requires Node 22 exactly.
# node:latest resolves to Node 24+, which can't build isolated-vm, and alpine's
# musl breaks native addon builds. Matches xchain-indexer's base image.
FROM node:22-bookworm
# Pin Node 22 (bookworm, not alpine) by digest: the platform requires Node 22
# exactly, and this digest's V8/ICU build matches xchain-vm's consensus runtime
# pin, which a floating tag can drift past and fail. node:latest resolves to
# Node 24+, which can't build isolated-vm, and alpine's musl breaks native
# addon builds. Matches xchain-indexer's base image.
FROM node:22.23.2-bookworm@sha256:dd5847a04b0deee391fa145f1f4c6d214196668b6bcc7988ebed67249f226844

RUN mkdir /XChainE2ETest/
# xchain-hub is staged into the build context by xchain-node's install
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# XChain Platform End-to-End Test Suite

<p align="center">
<img src="https://img.shields.io/badge/version-0.20.0-blue" alt="Version">
<img src="https://img.shields.io/badge/version-0.20.1-blue" alt="Version">
<img src="https://img.shields.io/badge/tests-3%2C399%2B%20passing-brightgreen" alt="Tests">
<img src="https://img.shields.io/badge/node-%3E%3D22-green" alt="Node">
<img src="https://img.shields.io/badge/license-AGPL--3.0--or--later-blue" alt="License">
Expand Down
141 changes: 141 additions & 0 deletions bin/pin-identity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/usr/bin/env node
/*********************************************************************
*
* Copyright © 2025-2026 Dankest, LLC
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This file is part of XChain Platform. Licensed under the GNU Affero
* General Public License v3.0 or later; see LICENSE.md.
*
**********************************************************************
*
* Pins the byte identity of the coin registry and every vendored twin.
*
* USAGE
* node bin/pin-identity.js
* node bin/pin-identity.js --out <file>
* node bin/pin-identity.js --compare <pin>
*
********************************************************************/

'use strict';

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');

const REPO_ROOT = path.resolve(__dirname, '..');
const SUITE_TITLE_PIN = 'bin/pins/at1-suite-titles.json';
const COIN_FILES = [
'src/coins/BTC.js',
'src/coins/DOGE.js',
'src/coins/LTC.js',
'src/coins/consensus_pin.js',
'src/coins/index.js',
];
const VENDORED_TWIN_FILES = COIN_FILES.slice();

function sha256File(relativePath) {
const bytes = fs.readFileSync(path.join(REPO_ROOT, relativePath));
return crypto.createHash('sha256').update(bytes).digest('hex');
}

function hashFiles(files) {
const hashes = {};
for (const relativePath of files.slice().sort()) hashes[relativePath] = sha256File(relativePath);
return hashes;
}

function buildIdentity() {
const coinsDirectory = path.join(REPO_ROOT, 'src', 'coins');
const coinsPresent = fs.existsSync(coinsDirectory) && fs.statSync(coinsDirectory).isDirectory();
const suiteTitlePin = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, SUITE_TITLE_PIN), 'utf8'));
const pkg = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf8'));
const expectedScripts = Object.keys(pkg.scripts || {}).filter((name) => name.startsWith('test'));
const { validatePin } = require('./suite-title-map');
const suiteProblems = validatePin(suiteTitlePin, expectedScripts);
if (suiteProblems.length) throw new Error(`invalid suite-title pin: ${suiteProblems.join('; ')}`);
return {
algorithm: 'sha256',
coins: coinsPresent ? hashFiles(COIN_FILES) : { none: 'repository has no src/coins directory' },
vendoredTwins: VENDORED_TWIN_FILES.length
? hashFiles(VENDORED_TWIN_FILES)
: { none: 'repository carries no vendored twin files' },
suiteTitleMap: {
path: SUITE_TITLE_PIN,
sha256: sha256File(SUITE_TITLE_PIN),
scripts: Object.keys(suiteTitlePin.scripts).length,
titleSets: Object.keys(suiteTitlePin.titleSets).length,
},
};
}

function compare(pin, fresh) {
const differences = [];
for (const section of ['algorithm', 'coins', 'vendoredTwins', 'suiteTitleMap']) {
if (JSON.stringify(pin[section]) !== JSON.stringify(fresh[section])) differences.push(section);
}
for (const section of Object.keys(pin)) {
if (!Object.prototype.hasOwnProperty.call(fresh, section)) differences.push(section);
}
for (const section of Object.keys(fresh)) {
if (!Object.prototype.hasOwnProperty.call(pin, section)) differences.push(section);
}
return Array.from(new Set(differences)).sort();
}

function parseArgs(argv) {
const options = {};
for (let i = 0; i < argv.length; i += 1) {
if (argv[i] === '--out') {
options.out = path.resolve(argv[i + 1]);
i += 1;
} else if (argv[i] === '--compare') {
options.compare = path.resolve(argv[i + 1]);
i += 1;
} else if (argv[i] === '--help' || argv[i] === '-h') {
options.help = true;
} else {
throw new Error(`unknown argument: ${argv[i]}`);
}
}
return options;
}

function serialize(identity) {
return `${JSON.stringify(identity, null, 2)}\n`;
}

function main() {
const options = parseArgs(process.argv.slice(2));
if (options.help) {
console.log(fs.readFileSync(__filename, 'utf8').split('*/')[0]);
return;
}

const identity = buildIdentity();
if (options.compare) {
const pin = JSON.parse(fs.readFileSync(options.compare, 'utf8'));
const differences = compare(pin, identity);
if (!differences.length) {
console.log(`identity holds against ${path.relative(REPO_ROOT, options.compare)}`);
return;
}
console.error(`identity mismatch in: ${differences.join(', ')}`);
process.exitCode = 1;
return;
}

const text = serialize(identity);
if (options.out) {
fs.mkdirSync(path.dirname(options.out), { recursive: true });
fs.writeFileSync(options.out, text);
console.log(`written to ${path.relative(REPO_ROOT, options.out)}`);
return;
}
process.stdout.write(text);
}

if (require.main === module) main();

module.exports = { buildIdentity, compare, hashFiles, serialize };
Loading
Loading