Zero dependencies, no account, no API. Every command takes --rpc, so it runs against a node you operate and never needs ours. That is the point: an exposure measurement you cannot re-run is an opinion with a logo on it. Ships with two self-checks meant to be run by you, not just by us. The second, proba-vocabular.mjs, exists because of a real defect found on 2026-08-16: the scanner emits RED / YELLOW / GREEN, two downstream files each wrote their own copy of that list, and both wrote AMBER, so a hybrid contract, exactly a client who has already started migrating, was reported as unmeasurable. Both sides were self-consistent, so nothing we had could see it. The vocabulary now has one exported source, and the test walks the whole path with a negative control that makes it able to fail. The same scanner returns RED about our own contracts. See VERIFY-US.md.
82 lines
2.6 KiB
JavaScript
82 lines
2.6 KiB
JavaScript
#!/usr/bin/env node
|
|
// simulate.js, migration cost simulator CLI.
|
|
//
|
|
// node simulate.js --count 100 --scheme falcon512 --auths 5
|
|
// node simulate.js --accounts accounts.json
|
|
// node simulate.js --schemes list the modeled schemes
|
|
//
|
|
// Estimates the gas to migrate a set of accounts to a post-quantum scheme,
|
|
// using the MEASURED PQC verify costs from AERE-BENCHMARK-REPORT.md.
|
|
|
|
import { readFileSync } from 'node:fs';
|
|
import { simulateMigration, formatSimulation, schemeList, ACCOUNT_DEPLOY_GAS } from './lib/simulate.js';
|
|
|
|
function usage(code = 0) {
|
|
console.log(`Aere PQC migration cost simulator
|
|
|
|
Usage
|
|
node simulate.js --count <n> [--scheme <s>] [--auths <k>] [--no-deploy] [--gwei <g>]
|
|
node simulate.js --accounts <file.json> [--gwei <g>]
|
|
node simulate.js --schemes list modeled schemes
|
|
node simulate.js --json machine-readable output
|
|
|
|
Schemes: ${schemeList().join(', ')}
|
|
falcon512_solidity models the CURRENTLY-DEPLOYED AerePQCAccount (~10.5M gas).
|
|
|
|
accounts.json shape:
|
|
[ { "address": "0x..", "scheme": "hybrid", "authsPerAccount": 3, "deployAccount": true }, ... ]`);
|
|
process.exit(code);
|
|
}
|
|
|
|
function parseArgs(argv) {
|
|
const a = { _: [] };
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const t = argv[i];
|
|
if (t === '--count') a.count = parseInt(argv[++i], 10);
|
|
else if (t === '--scheme') a.scheme = argv[++i];
|
|
else if (t === '--auths') a.auths = parseInt(argv[++i], 10);
|
|
else if (t === '--gwei') a.gwei = parseFloat(argv[++i]);
|
|
else if (t === '--no-deploy') a.noDeploy = true;
|
|
else if (t === '--accounts') a.accounts = argv[++i];
|
|
else if (t === '--schemes') a.schemes = true;
|
|
else if (t === '--json') a.json = true;
|
|
else if (t === '--help' || t === '-h') a.help = true;
|
|
else a._.push(t);
|
|
}
|
|
return a;
|
|
}
|
|
|
|
function main() {
|
|
const args = parseArgs(process.argv.slice(2));
|
|
if (args.help) usage(0);
|
|
if (args.schemes) {
|
|
console.log(schemeList().join('\n'));
|
|
return;
|
|
}
|
|
|
|
let accounts;
|
|
if (args.accounts) {
|
|
accounts = JSON.parse(readFileSync(args.accounts, 'utf8'));
|
|
if (!Array.isArray(accounts)) { console.error('accounts file must be a JSON array'); process.exit(1); }
|
|
} else if (args.count) {
|
|
accounts = Array.from({ length: args.count }, () => ({
|
|
scheme: args.scheme || 'falcon512',
|
|
authsPerAccount: args.auths ?? 1,
|
|
deployAccount: !args.noDeploy,
|
|
}));
|
|
} else {
|
|
usage(1);
|
|
}
|
|
|
|
const sim = simulateMigration(accounts, { gweiPrice: args.gwei });
|
|
if (args.json) {
|
|
console.log(JSON.stringify(sim, null, 2));
|
|
} else {
|
|
console.log('');
|
|
console.log(formatSimulation(sim));
|
|
console.log('');
|
|
}
|
|
}
|
|
|
|
main();
|