fast-time: the join harness's late-join hook for the testnet lane (--prover beside A's miner, --join-after past the pool's record window, per-node exec ports)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
c8624c68f0
commit
1bb91045a1
1 changed files with 30 additions and 2 deletions
|
|
@ -32,6 +32,14 @@ const flag = (name, dflt) => { const i = args.indexOf(`--${name}`); return i >=
|
|||
const sflag = (name) => { const i = args.indexOf(`--${name}`); return i >= 0 ? args[i + 1] : null; };
|
||||
const EXPECT = sflag('expect') || 'join';
|
||||
const WINDOW = Number(sflag('window') || 150);
|
||||
// The testnet lane's late-join case (ledger N9's second half, 7 October 2026): --prover "<command>" starts a prover
|
||||
// beside A's miner (its records land in A's blocks through the ordinary record flows; the command sees IGNEUM_GRPC
|
||||
// and IGNEUM_EVM_RPC for node A), and --join-after <blocks> holds B's start until A has mined that many blocks past
|
||||
// its ready point, so B joins after the pool's 600-chain-block window has passed and every proof it asks for must
|
||||
// come from the peers' archives. PASS is the same join; the failed shape is the node before the archive, which
|
||||
// stalls on "proofs this peer did not deliver in 20 s".
|
||||
const PROVER = sflag('prover') || null;
|
||||
const JOIN_AFTER = Number(sflag('join-after') || 0);
|
||||
const MARGIN = flag('margin', 200), WATCH = flag('watch', 600), THREADS = flag('threads', 3), MAX_MINE = flag('max-mine', 5400);
|
||||
const OUT = sflag('out') || `${ROOT}docs/plans/counter-asic-3-gate/headers-proof-join-expect-${EXPECT}-w${WINDOW}.json`;
|
||||
if (!['join', 'stall'].includes(EXPECT)) { console.error('usage: --expect join|stall'); process.exit(2); }
|
||||
|
|
@ -73,14 +81,14 @@ log(`expect ${EXPECT}: pruning depth 4600 (finality 120, merge 60, k 18), the wi
|
|||
|
||||
class Node {
|
||||
constructor(i, connect = null) {
|
||||
this.i = i; this.grpcPort = BASE + i * 10; this.p2pPort = BASE + i * 10 + 1; this.jsonPort = BASE + i * 10 + 2;
|
||||
this.i = i; this.grpcPort = BASE + i * 10; this.p2pPort = BASE + i * 10 + 1; this.jsonPort = BASE + i * 10 + 2; this.evmPort = BASE + i * 10 + 3;
|
||||
this.connect = connect; this.dir = `${TMP}/n${i}`; this.logFile = `${this.dir}/node.log`;
|
||||
}
|
||||
get grpc() { return `grpc://127.0.0.1:${this.grpcPort}`; }
|
||||
async start() {
|
||||
mkdirSync(this.dir, { recursive: true });
|
||||
const a = ['--devnet', `--devnet-suffix=${SUFFIX}`, '--nodnsseed', '--disable-upnp', '--nologfiles', '--enable-unsynced-mining', '--utxoindex',
|
||||
`--appdir=${this.dir}`, `--rpclisten=127.0.0.1:${this.grpcPort}`, `--rpclisten-json=127.0.0.1:${this.jsonPort}`,
|
||||
`--appdir=${this.dir}`, `--rpclisten=127.0.0.1:${this.grpcPort}`, `--rpclisten-json=127.0.0.1:${this.jsonPort}`, `--evm-rpclisten=127.0.0.1:${this.evmPort}`,
|
||||
`--listen=127.0.0.1:${this.p2pPort}`, `--override-params-file=${override}`, '--loglevel=info', '--yes'];
|
||||
if (this.connect) a.push(`--addpeer=127.0.0.1:${this.connect}`); else a.push('--outpeers=0');
|
||||
const out = openSync(this.logFile, 'a');
|
||||
|
|
@ -116,6 +124,13 @@ process.on('unhandledRejection', async (e) => { log(`FAILED: ${e?.stack || e}`);
|
|||
const a = await new Node(0).start();
|
||||
const genesis = (await a.dag()).pruningPointHash;
|
||||
const m = miner('a-miner', a.grpc, THREADS, MAX_MINE);
|
||||
// the testnet lane's prover beside A's miner (its records reach A's blocks through the record flows)
|
||||
let prover = null;
|
||||
if (PROVER) {
|
||||
const out = openSync(`${TMP}/prover.log`, 'a');
|
||||
prover = spawn('/bin/sh', ['-c', PROVER], { stdio: ['ignore', out, out], env: { ...process.env, IGNEUM_GRPC: a.grpc, IGNEUM_EVM_RPC: `http://127.0.0.1:${a.evmPort}` } });
|
||||
log(`prover up pid ${prover.pid}: ${PROVER}`);
|
||||
}
|
||||
const t0 = Date.now();
|
||||
const since = () => ((Date.now() - t0) / 1000).toFixed(1);
|
||||
let lastReport = 0, pruningMovedAt = null, pruningPoint = null;
|
||||
|
|
@ -126,7 +141,20 @@ while (Date.now() - t0 < MAX_MINE * 1000) {
|
|||
if (Date.now() - lastReport > 120000) { lastReport = Date.now(); log(`t=${since()} s A ${d.blockCount} blocks daa ${d.virtualDaaScore} pruning ${String(d.pruningPointHash).slice(0, 8)}${pruningMovedAt ? ' (moved)' : ''}`); }
|
||||
if (pruningMovedAt && d.blockCount >= pruningMovedAt.blocks + MARGIN) break;
|
||||
}
|
||||
// --join-after: A's miner runs on this many blocks past the ready point (past the pool's 600-chain-block record
|
||||
// window, so every proof B asks for must come from the peers' archives), then stops as before so B joins a still sink
|
||||
if (JOIN_AFTER > 0) {
|
||||
const ready = (await a.dag()).blockCount;
|
||||
const target = ready + JOIN_AFTER;
|
||||
log(`A ready at ${ready} blocks; mining on to ${target} (${JOIN_AFTER} past the ready point) before B joins`);
|
||||
for (;;) {
|
||||
const d = await a.rpc.call('getBlockDagInfo');
|
||||
if (d.blockCount >= target) break;
|
||||
await sleep(5000);
|
||||
}
|
||||
}
|
||||
try { m.kill('SIGINT'); } catch { }
|
||||
try { prover?.kill('SIGINT'); } catch { }
|
||||
await sleep(3000);
|
||||
const aAtJoin = await a.dag();
|
||||
const aSinkHeader = await a.headerOf(aAtJoin.sink);
|
||||
|
|
|
|||
Loading…
Reference in a new issue