tn-late-join harness: the join-after target in DAA (the unpruned block count plateaus once the pruning point moves, seen on the pod 13:4x UK) and a --resume mode that attaches to a live node A and stops the named pids before B joins
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
08d97a849c
commit
343109f42a
1 changed files with 26 additions and 10 deletions
|
|
@ -44,6 +44,10 @@ const WINDOW = Number(sflag('window') || 150);
|
|||
// stalls on "proofs this peer did not deliver in 20 s".
|
||||
const PROVER = sflag('prover') || null;
|
||||
const JOIN_AFTER = Number(sflag('join-after') || 0);
|
||||
// --resume: node A is already up on BASE's ports with its chain (a harness instance whose miner ran out before the join), and
|
||||
// RESUME_PIDS names pid files of the processes to stop before B joins (the rescue miner, the prover); nothing is wiped
|
||||
const RESUME = args.includes('--resume');
|
||||
const RESUME_PIDS = (sflag('resume-pids') || '').split(',').filter(Boolean);
|
||||
const MARGIN = flag('margin', 200), WATCH = flag('watch', 600), THREADS = flag('threads', 3), MAX_MINE = flag('max-mine', 5400);
|
||||
const OUT = sflag('out') || `${TMP}/late-join-expect-${EXPECT}.json`;
|
||||
if (!['join', 'stall'].includes(EXPECT)) { console.error('usage: --expect join|stall'); process.exit(2); }
|
||||
|
|
@ -51,7 +55,7 @@ const started = [];
|
|||
const log = (...a) => console.log(new Date().toISOString().slice(11, 23), ...a);
|
||||
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
||||
for (const b of [IGNEUMD, CPU_MINER]) if (!existsSync(b)) { console.error(`missing ${b}`); process.exit(2); }
|
||||
rmSync(TMP, { recursive: true, force: true }); mkdirSync(TMP, { recursive: true });
|
||||
if (!RESUME) { rmSync(TMP, { recursive: true, force: true }); } mkdirSync(TMP, { recursive: true });
|
||||
|
||||
const baseText = readFileSync(FILE, 'utf8');
|
||||
const field = (name) => { const m = new RegExp(`"${name}":\\s*([0-9]+)`).exec(baseText); return m ? +m[1] : undefined; };
|
||||
|
|
@ -75,7 +79,7 @@ export function redepth(text) {
|
|||
}
|
||||
const DAY_MS = field('pow_day_ms');
|
||||
const override = `${TMP}/override.json`;
|
||||
writeFileSync(override, mergeOverrideText(redepth(baseText), {
|
||||
if (!RESUME) writeFileSync(override, mergeOverrideText(redepth(baseText), {
|
||||
genesis_bits: 0x1f010000, skip_proof_of_work: false,
|
||||
program_class_v3_activation_daa: NEVER, program_class_v4_activation_daa: '100000', program_class_v4_signal_window_daa: 120,
|
||||
// the testnet lane's late-join case (ledger N9's second half): consensus proof verification from genesis under the
|
||||
|
|
@ -92,6 +96,14 @@ class Node {
|
|||
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 attach() {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
try { this.rpc = await connectRpc(`ws://127.0.0.1:${this.jsonPort}`); await this.rpc.call('getBlockDagInfo'); break; } catch { this.rpc = null; await sleep(500); }
|
||||
}
|
||||
if (!this.rpc) throw new Error(`n${this.i}: no live node answers on ${this.jsonPort}`);
|
||||
log(`n${this.i} attached on json ${this.jsonPort} (resume)`);
|
||||
return this;
|
||||
}
|
||||
async start() {
|
||||
mkdirSync(this.dir, { recursive: true });
|
||||
const a = ['--devnet', `--devnet-suffix=${SUFFIX}`, '--nodnsseed', '--disable-upnp', '--nologfiles', '--enable-unsynced-mining', '--utxoindex',
|
||||
|
|
@ -131,9 +143,9 @@ process.on('SIGINT', async () => { await stopAll(); process.exit(130); });
|
|||
process.on('unhandledRejection', async (e) => { log(`FAILED: ${e?.stack || e}`); await stopAll(); process.exit(3); });
|
||||
process.on('uncaughtException', async (e) => { log(`FAILED: ${e?.stack || e}`); await stopAll(); process.exit(3); });
|
||||
|
||||
const a = await new Node(0).start();
|
||||
const a = RESUME ? await new Node(0).attach() : await new Node(0).start();
|
||||
const genesis = (await a.dag()).pruningPointHash;
|
||||
const m = miner('a-miner', a.grpc, THREADS, MAX_MINE);
|
||||
const m = RESUME ? null : 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) {
|
||||
|
|
@ -146,7 +158,8 @@ if (PROVER) {
|
|||
const t0 = Date.now();
|
||||
const since = () => ((Date.now() - t0) / 1000).toFixed(1);
|
||||
let lastReport = 0, pruningMovedAt = null, pruningPoint = null;
|
||||
while (Date.now() - t0 < MAX_MINE * 1000) {
|
||||
if (RESUME) { const d = await a.dag(); pruningMovedAt = { t: 0, daa: +d.virtualDaaScore, blocks: d.blockCount, resumed: true }; pruningPoint = d.pruningPointHash; log(`resume: A at ${d.blockCount} blocks, daa ${d.virtualDaaScore}, pruning point ${String(d.pruningPointHash).slice(0, 8)} (taken as moved)`); }
|
||||
while (!RESUME && Date.now() - t0 < MAX_MINE * 1000) {
|
||||
await sleep(10000);
|
||||
const d = await a.dag();
|
||||
if (pruningMovedAt == null && d.pruningPointHash !== genesis) { pruningMovedAt = { t: +since(), daa: +d.virtualDaaScore, blocks: d.blockCount }; pruningPoint = d.pruningPointHash; log(`A's pruning point left genesis at ${since()} s: ${String(d.pruningPointHash).slice(0, 8)} at DAA ${d.virtualDaaScore}, ${d.blockCount} blocks`); }
|
||||
|
|
@ -156,16 +169,19 @@ while (Date.now() - t0 < MAX_MINE * 1000) {
|
|||
// --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`);
|
||||
// past the pool's 600-chain-block record window, measured in DAA (one block a second here): the block count is the
|
||||
// unpruned count and plateaus once the pruning point moves (seen on the pod, 7 October 2026, 13:4x UK)
|
||||
const readyDaa = +(await a.dag()).virtualDaaScore;
|
||||
const target = readyDaa + JOIN_AFTER;
|
||||
log(`A ready at daa ${readyDaa}; mining on to daa ${target} (${JOIN_AFTER} past the ready point) before B joins`);
|
||||
for (;;) {
|
||||
const d = await a.rpc.call('getBlockDagInfo');
|
||||
if (d.blockCount >= target) break;
|
||||
if (+d.virtualDaaScore >= target) break;
|
||||
await sleep(5000);
|
||||
}
|
||||
}
|
||||
try { m.kill('SIGINT'); } catch { }
|
||||
for (const f of RESUME_PIDS) { try { const pid = +readFileSync(f, 'utf8').trim(); if (pid > 1) { process.kill(pid, 'SIGINT'); log(`resume: stopped pid ${pid} from ${f}`); } } catch (e) { log(`resume: ${f}: ${e.message}`); } }
|
||||
try { m?.kill('SIGINT'); } catch { }
|
||||
try { prover?.kill('SIGINT'); } catch { }
|
||||
await sleep(3000);
|
||||
const aAtJoin = await a.dag();
|
||||
|
|
|
|||
Loading…
Reference in a new issue