igneum/tools/harness/lib/sim.mjs
igneum-labs 6b72c3b3e6 Fast time: the devnet at 60x for test networks (override-60x.json, --fast-time in both harnesses, proof script)
infra/fast-time/override-60x.json is the devnet with every clock-like consensus parameter divided by 60 and every
block count unchanged (finality window, ban and min_daa 120 DAA; merge depth 60; Kaspa finality depth 720; pruning
depth at the anticone bound 13,838; coinbase maturity 2; the hourly program epoch 60 blocks with a 10-block lead;
the dataset day 24 minutes). The epoch length, lead and day are consensus parameters of the node since devnet-v4
a5ef8b07, carried by the override file. README lists each field, why it scales or not, the flags and the numbers.

Measured (simnet.mjs, three devnet-v4 nodes, three vmine voters at 1 block/s, one real-hash CPU miner): next
epoch seed in the template at 56.1 s, program swap at 65.1 s wall (DAA 60), first finality lock at 185.5 s wall
(checkpoint 5, DAA 149). Both harnesses take --fast-time: finality-attacks s3 PASS in 113 s wall with 16 locks
per node (the devnet rule needs 20 min of warm-up at 6 blocks/s before any lock); harness s3 partition and heal
43 s wall for three cuts against 983 s for four on the devnet profile with the same binary. Bench-log entry.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 10:34:19 +00:00

50 lines
2.8 KiB
JavaScript

// Runs the in-process simulator (vendor worktree crate igneum/harness-sim) and reads its JSON report.
import { spawnSync, spawn } from 'node:child_process';
import { readFileSync, mkdirSync, existsSync } from 'node:fs';
import { SIM, TMP, log, FAST_TIME, FAST_TIME_FILE } from './net.mjs';
// Under --fast-time the simulator runs the 60x profile too (igneum-harness-sim --override-params-file).
const fastArgs = () => (FAST_TIME ? ['--override-params-file', FAST_TIME_FILE] : []);
export function runSim(name, args, { timeoutMs = 60 * 60_000 } = {}) {
if (!existsSync(SIM)) throw new Error(`missing ${SIM}; build igneum-harness-sim in the harness worktree`);
mkdirSync(`${TMP}/sim`, { recursive: true });
const out = `${TMP}/sim/${name}.json`;
const argv = [...args, ...fastArgs(), '--out', out];
log(`sim ${name}: igneum-harness-sim ${argv.join(' ')}`);
const t0 = Date.now();
const r = spawnSync('nice', ['-n', '19', SIM, ...argv], { encoding: 'utf8', timeout: timeoutMs, maxBuffer: 64 * 1024 * 1024 });
if (!existsSync(out)) throw new Error(`sim ${name} produced no report: ${(r.stderr || '').slice(-400)}`);
const report = JSON.parse(readFileSync(out, 'utf8'));
report.wall_s = (Date.now() - t0) / 1000;
log(`sim ${name}: ${report.end_time_s} virtual s in ${report.wall_s.toFixed(0)} s wall`);
return report;
}
/// Several simulations at once (each uses a couple of threads); returns reports in order.
export async function runSims(jobs, { parallel = 3 } = {}) {
const results = new Array(jobs.length);
let next = 0;
async function worker() {
while (next < jobs.length) {
const i = next++;
const { name, args } = jobs[i];
mkdirSync(`${TMP}/sim`, { recursive: true });
const out = `${TMP}/sim/${name}.json`;
const argv = [...args, ...fastArgs(), '--out', out];
log(`sim ${name}: igneum-harness-sim ${argv.join(' ')}`);
const t0 = Date.now();
await new Promise((resolve) => {
const p = spawn('nice', ['-n', '19', SIM, ...argv], { stdio: ['ignore', 'ignore', 'pipe'] });
let err = ''; p.stderr.on('data', d => { err += d; if (err.length > 4000) err = err.slice(-4000); });
p.on('exit', () => { if (!existsSync(out)) log(`sim ${name} FAILED: ${err.slice(-300)}`); resolve(); });
});
if (existsSync(out)) { results[i] = JSON.parse(readFileSync(out, 'utf8')); results[i].wall_s = (Date.now() - t0) / 1000; log(`sim ${name}: ${results[i].end_time_s} virtual s in ${results[i].wall_s.toFixed(0)} s wall`); }
}
}
await Promise.all(Array.from({ length: Math.min(parallel, jobs.length) }, worker));
return results;
}
export function hist(h) { return Object.entries(h || {}).map(([d, n]) => `${d}:${n}`).join(' ') || 'none'; }
export function maxDepth(h) { return Math.max(0, ...Object.keys(h || {}).map(Number)); }