// Property-based fuzz of AereThresholdAccount.validateUserOp: on top of the halmos proofs and the // unit tests, hammer the counting logic with hundreds of random leg combinations (genuine / forged // / junk / duplicate / out-of-range) over fresh challenges, and assert the account authorizes iff // the number of DISTINCT in-range members with a genuine signature is >= threshold. Deterministic // (seeded) so any failure reproduces. const { expect } = require("chai"); const { ethers, network } = require("hardhat"); const PRECOMPILE = { 1: ethers.getAddress("0x0000000000000000000000000000000000000ae1"), 2: ethers.getAddress("0x0000000000000000000000000000000000000ae2"), 3: ethers.getAddress("0x0000000000000000000000000000000000000ae3"), 4: ethers.getAddress("0x0000000000000000000000000000000000000ae4"), }; const M = { pkLen: 897, pkHeader: 0x09, esigHeader: 0x29 }; function makeRng(seedText) { let seed = ethers.keccak256(ethers.toUtf8Bytes(seedText)); return { next() { seed = ethers.keccak256(seed); return BigInt(seed); }, int(n) { return Number(this.next() % BigInt(n)); }, 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(M.pkLen); raw[0] = M.pkHeader; return ethers.hexlify(raw); } function genuine(pubKey, message, rng) { const commitment = ethers.keccak256(ethers.concat([pubKey, message])); return ethers.hexlify(ethers.concat([rng.bytes(40), new Uint8Array([M.esigHeader]), ethers.getBytes(commitment)])); } function forged(pubKey, message, rng) { const b = ethers.getBytes(genuine(pubKey, message, rng)); b[41] ^= 0x01; return ethers.hexlify(b); } function junk(rng) { return ethers.hexlify(rng.bytes(41 + rng.int(60))); } function encodeLegs(legs) { return ethers.AbiCoder.defaultAbiCoder().encode( ["tuple(uint8 memberIndex, bytes signature)[]"], [legs.map((l) => [l.idx, l.sig])] ); } const SCHEME = 1, N = 6, T = 3, ITERS = 300; describe("AereThresholdAccount — property-based fuzz (300 random leg combos)", function () { this.timeout(120000); let mockCode, account, entryPoint, echo, pubKeys, rng; 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()); } for (const s of [1, 2, 3, 4]) await network.provider.send("hardhat_setCode", [PRECOMPILE[s], mockCode[s]]); [entryPoint] = await ethers.getSigners(); rng = makeRng("fuzz-seed-v1"); pubKeys = []; for (let i = 0; i < N; i++) pubKeys.push(makePubKey(rng)); const Fac = await ethers.getContractFactory("AereThresholdAccountFactory"); const factory = await Fac.deploy(); await factory.waitForDeployment(); const salt = ethers.id("fuzz"); const addr = await factory.computeAddress(entryPoint.address, SCHEME, T, pubKeys, salt); await (await factory.createAccount(entryPoint.address, SCHEME, T, pubKeys, salt)).wait(); account = await ethers.getContractAt("AereThresholdAccount", addr); echo = await (await ethers.getContractFactory("EchoTarget")).deploy(); await echo.waitForDeployment(); }); it(`authorizes iff distinct in-range genuine signers >= threshold, across ${ITERS} random inputs`, async function () { const acctAddr = await account.getAddress(); let checked = 0; for (let iter = 0; iter < ITERS; iter++) { const userOpHash = ethers.id("fuzz-op-" + iter); const challenge = await account.userOpChallenge(userOpHash); const numLegs = 1 + rng.int(8); const legs = []; const distinctValid = new Set(); for (let k = 0; k < numLegs; k++) { const idx = rng.int(N + 2); // 0..N+1 → indices N,N+1 are out-of-range const kind = rng.int(3); // 0 genuine, 1 forged, 2 junk let sig; if (kind === 0) { sig = genuine(pubKeys[idx % N], challenge, rng); if (idx < N) distinctValid.add(idx); } else if (kind === 1) sig = forged(pubKeys[idx % N], challenge, rng); else sig = junk(rng); legs.push({ idx, sig }); } const expected = distinctValid.size >= T ? 0n : 1n; const op = { sender: acctAddr, nonce: 0n, initCode: "0x", callData: "0x", accountGasLimits: ethers.ZeroHash, preVerificationGas: 0n, gasFees: ethers.ZeroHash, paymasterAndData: "0x", signature: encodeLegs(legs), }; const got = await account.connect(entryPoint).validateUserOp.staticCall(op, userOpHash, 0); if (got !== expected) { throw new Error(`FUZZ MISMATCH iter ${iter}: distinct-valid=${distinctValid.size} t=${T} expected=${expected} got=${got} legs=${JSON.stringify(legs.map((l) => l.idx))}`); } checked++; } expect(checked).to.equal(ITERS); console.log(`\n fuzz: ${ITERS}/${ITERS} random leg combinations matched the threshold invariant exactly.`); }); });