aere-contracts/test/AereThresholdAccount.gas.test.js
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

110 lines
4.9 KiB
JavaScript

// Honest gas report for AereThresholdAccount.executeThreshold. The MockPQCPrecompile returns
// almost immediately, so the MEASURED gas here is the account's own Solidity + calldata overhead
// (decode + distinct-bitmap + per-leg envelope assembly + staticcall dispatch). The REAL on-chain
// cost adds the live precompile's verify gas per leg (Falcon-512 ~40k, ML-DSA-44 ~55k, Falcon-1024
// ~75k, SLH-DSA-128s ~350k — from research/aip-draft-pqc-precompiles.md). We measure the marginal
// overhead per leg by differencing two thresholds, then project the real (scheme, t) cost.
const { ethers, network } = require("hardhat");
const PRECOMPILE = {
1: ethers.getAddress("0x0000000000000000000000000000000000000ae1"),
2: ethers.getAddress("0x0000000000000000000000000000000000000ae2"),
3: ethers.getAddress("0x0000000000000000000000000000000000000ae3"),
4: ethers.getAddress("0x0000000000000000000000000000000000000ae4"),
};
const META = { 1: { pkLen: 897, pkHeader: 0x09, esigHeader: 0x29, falcon: true } };
const REAL_PRECOMPILE_GAS = { 1: 40000, 2: 75000, 3: 55000, 4: 350000 };
function makeRng(seedText) {
let seed = ethers.keccak256(ethers.toUtf8Bytes(seedText));
return {
bytes(n) {
const out = new Uint8Array(n);
let i = 0;
while (i < n) {
seed = ethers.keccak256(seed);
const c = ethers.getBytes(seed);
for (let j = 0; j < c.length && i < n; j++, i++) out[i] = c[j];
}
return out;
},
};
}
function makePubKey(rng) {
const raw = rng.bytes(META[1].pkLen);
raw[0] = META[1].pkHeader;
return ethers.hexlify(raw);
}
function genuine(pubKey, message, rng) {
const commitment = ethers.keccak256(ethers.concat([pubKey, message]));
const nonce = rng.bytes(40);
const esig = ethers.concat([new Uint8Array([META[1].esigHeader]), commitment]);
return ethers.hexlify(ethers.concat([nonce, esig]));
}
describe("AereThresholdAccount — gas report (Falcon-512, n=8)", function () {
let mockCode, ep, echo, pubKeys, rng, factory;
const N = 8;
before(async function () {
mockCode = {};
const F = await ethers.getContractFactory("MockPQCPrecompile");
for (const s of [1, 2, 3, 4]) {
const m = await F.deploy(s);
await m.waitForDeployment();
mockCode[s] = await ethers.provider.getCode(await m.getAddress());
}
});
beforeEach(async function () {
for (const s of [1, 2, 3, 4]) await network.provider.send("hardhat_setCode", [PRECOMPILE[s], mockCode[s]]);
[ep] = await ethers.getSigners();
rng = makeRng("gas-report");
pubKeys = [];
for (let i = 0; i < N; i++) pubKeys.push(makePubKey(rng));
factory = await (await ethers.getContractFactory("AereThresholdAccountFactory")).deploy();
await factory.waitForDeployment();
echo = await (await ethers.getContractFactory("EchoTarget")).deploy();
await echo.waitForDeployment();
});
async function deploy(t, salt) {
const addr = await factory.computeAddress(ep.address, 1, t, pubKeys, salt);
await (await factory.createAccount(ep.address, 1, t, pubKeys, salt)).wait();
return ethers.getContractAt("AereThresholdAccount", addr);
}
async function execGas(account, t) {
const target = await echo.getAddress();
const data = echo.interface.encodeFunctionData("bump");
const challenge = await account.execChallenge(0, target, 0, data);
const legs = [];
for (let i = 0; i < t; i++) legs.push([i, genuine(pubKeys[i], challenge, rng)]);
const tx = await account.executeThreshold(target, 0, data, legs);
const rc = await tx.wait();
return Number(rc.gasUsed);
}
it("measures marginal overhead per leg and projects the real per-scheme cost", async function () {
const g2 = await execGas(await deploy(2, ethers.id("g2")), 2);
const g6 = await execGas(await deploy(6, ethers.id("g6")), 6);
const perLegOverhead = Math.round((g6 - g2) / 4);
const baseOverhead = g2 - 2 * perLegOverhead;
console.log(`\n executeThreshold measured gas (mock precompile ~free):`);
console.log(` t=2: ${g2} gas t=6: ${g6} gas`);
console.log(` marginal Solidity+calldata overhead per leg: ~${perLegOverhead} gas`);
console.log(` fixed base overhead: ~${baseOverhead} gas`);
console.log(` projected REAL cost = base + t*(overhead + precompile_verify):`);
for (const s of [1, 2, 3, 4]) {
const name = { 1: "Falcon-512", 2: "Falcon-1024", 3: "ML-DSA-44", 4: "SLH-DSA-128s" }[s];
const perLegReal = perLegOverhead + REAL_PRECOMPILE_GAS[s];
const t3 = baseOverhead + 3 * perLegReal;
const cap = 16777216; // EIP-7825 per-tx gas cap (2^24)
const tMax = Math.floor((cap - baseOverhead) / perLegReal);
console.log(` ${name.padEnd(13)} real/leg ~${perLegReal} gas | 3-of-n ~${t3} gas | t_max under 2^24 ~ ${tMax}`);
}
// sanity: overhead is positive and gas grew with t
if (!(g6 > g2 && perLegOverhead > 0)) throw new Error("unexpected gas profile");
});
});