pqc-migration-toolkit/simulate.js
Aere Network de9a79298c Aere PQC migration toolkit: measure an address quantum exposure from its bytecode
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.
2026-08-17 10:31:31 +03:00

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();