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.
110 lines
4.9 KiB
JavaScript
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");
|
|
});
|
|
});
|