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.
133 lines
6.6 KiB
JavaScript
133 lines
6.6 KiB
JavaScript
const { expect } = require("chai");
|
|
const { ethers } = require("hardhat");
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// AerePQStarkVerifier - ISP1Verifier-compatible adapter for the native PQ STARK
|
|
// precompile at 0x0AE8 (direct SP1/Plonky3 inner FRI/STARK verify, no BN254 wrap).
|
|
//
|
|
// HONEST SCOPE: the precompile at 0x0AE8 is a REFERENCE SKELETON that fail-closes
|
|
// (returns EMPTY for every input) and is NOT deployed on the hardhat EVM at all. So
|
|
// on this test EVM a staticcall to 0x0AE8 hits an empty account and returns empty ->
|
|
// the adapter must REVERT on verifyProof and return false on isValidProof for every
|
|
// input. That is exactly the safety property we want to prove: a consumer swapped to
|
|
// this adapter DENIES all proofs (never a false accept) until the precompile core is
|
|
// ported, externally audited, and founder-activated.
|
|
//
|
|
// This test also demonstrates the ONE-LINE consumer swap: it deploys the REAL
|
|
// AereEVMValidityV2 consumer with SP1_VERIFIER pointed at the PQ adapter (instead of
|
|
// the BN254 gateway) and shows the consumer fail-closes end-to-end.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const PQ_STARK_ADDR = ethers.getAddress("0x0000000000000000000000000000000000000ae8");
|
|
const SUCCESS_WORD = "0x" + "00".repeat(31) + "01";
|
|
|
|
describe("AerePQStarkVerifier (PQ STARK precompile adapter @ 0x0AE8)", function () {
|
|
let adapter;
|
|
|
|
beforeEach(async function () {
|
|
const F = await ethers.getContractFactory("AerePQStarkVerifier");
|
|
adapter = await F.deploy(1); // configId 1 = SP1 inner/shrink
|
|
await adapter.waitForDeployment();
|
|
});
|
|
|
|
it("targets precompile 0x0AE8 and reports its config/version", async function () {
|
|
expect(await adapter.CONFIG_ID()).to.equal(1);
|
|
expect(await adapter.VERSION()).to.equal("pq-stark-v1(reference-skeleton)");
|
|
// domain-separated tag, distinct from any BN254 SP1 VERIFIER_HASH
|
|
expect(await adapter.VERIFIER_HASH()).to.equal(
|
|
ethers.keccak256(ethers.toUtf8Bytes("AerePQStarkVerifier.v1"))
|
|
);
|
|
});
|
|
|
|
it("rejects configId outside {1,2}", async function () {
|
|
const F = await ethers.getContractFactory("AerePQStarkVerifier");
|
|
await expect(F.deploy(0)).to.be.reverted;
|
|
await expect(F.deploy(3)).to.be.reverted;
|
|
const two = await F.deploy(2);
|
|
await two.waitForDeployment();
|
|
expect(await two.CONFIG_ID()).to.equal(2);
|
|
});
|
|
|
|
it("FAIL-CLOSED: verifyProof reverts when the precompile is absent/empty (no false accept)", async function () {
|
|
const vkey = ethers.hexlify(ethers.randomBytes(32));
|
|
const publicValues = ethers.hexlify(ethers.randomBytes(96));
|
|
const proof = ethers.hexlify(ethers.randomBytes(512));
|
|
await expect(
|
|
adapter.verifyProof(vkey, publicValues, proof)
|
|
).to.be.revertedWithCustomError(adapter, "PQProofInvalid");
|
|
});
|
|
|
|
it("FAIL-CLOSED: verifyProof reverts even on empty public values / empty proof", async function () {
|
|
const vkey = ethers.ZeroHash;
|
|
await expect(
|
|
adapter.verifyProof(vkey, "0x", "0x")
|
|
).to.be.revertedWithCustomError(adapter, "PQProofInvalid");
|
|
});
|
|
|
|
it("FAIL-CLOSED: isValidProof returns false (never true) for arbitrary input", async function () {
|
|
const vkey = ethers.hexlify(ethers.randomBytes(32));
|
|
const publicValues = ethers.hexlify(ethers.randomBytes(288));
|
|
const proof = ethers.hexlify(ethers.randomBytes(1024));
|
|
expect(await adapter.isValidProof(vkey, publicValues, proof)).to.equal(false);
|
|
});
|
|
|
|
it("would ACCEPT only on the exact 32-byte success word (mocked precompile via setCode)", async function () {
|
|
// Prove the accept path is real: inject a tiny mock at 0x0AE8 that returns 0x..01,
|
|
// then the SAME adapter call succeeds. This shows the adapter is a faithful gate -
|
|
// it is the *precompile skeleton*, not the adapter, that currently denies everything.
|
|
// A contract that unconditionally returns the 32-byte success word:
|
|
// PUSH1 0x01 PUSH1 0x00 MSTORE (mem[0..32] = 0x..01)
|
|
// PUSH1 0x20 PUSH1 0x00 RETURN
|
|
const runtime = "0x600160005260206000f3";
|
|
await ethers.provider.send("hardhat_setCode", [PQ_STARK_ADDR, runtime]);
|
|
try {
|
|
const vkey = ethers.hexlify(ethers.randomBytes(32));
|
|
// staticcall to a mock returns success word -> verifyProof does NOT revert
|
|
await adapter.verifyProof.staticCall(vkey, "0x", "0x");
|
|
expect(await adapter.isValidProof(vkey, "0x", "0x")).to.equal(true);
|
|
} finally {
|
|
await ethers.provider.send("hardhat_setCode", [PQ_STARK_ADDR, "0x"]);
|
|
}
|
|
});
|
|
|
|
it("rejects a mock precompile that returns the WRONG word (not 0x..01)", async function () {
|
|
// mem[0..32] = 0x..02 then return 32 bytes
|
|
const runtime = "0x600260005260206000f3";
|
|
await ethers.provider.send("hardhat_setCode", [PQ_STARK_ADDR, runtime]);
|
|
try {
|
|
const vkey = ethers.hexlify(ethers.randomBytes(32));
|
|
expect(await adapter.isValidProof(vkey, "0x", "0x")).to.equal(false);
|
|
} finally {
|
|
await ethers.provider.send("hardhat_setCode", [PQ_STARK_ADDR, "0x"]);
|
|
}
|
|
});
|
|
|
|
// -------------------------------------------------------------------------
|
|
// ONE-LINE CONSUMER SWAP: AereEVMValidityV2 with SP1_VERIFIER = PQ adapter.
|
|
// -------------------------------------------------------------------------
|
|
describe("one-line swap into a real consumer (AereEVMValidityV2)", function () {
|
|
it("deploys AereEVMValidityV2 against the PQ adapter and fail-closes end-to-end", async function () {
|
|
const vkey = ethers.hexlify(ethers.randomBytes(32));
|
|
const V = await ethers.getContractFactory("AereEVMValidityV2");
|
|
// THE SWAP: first constructor arg is SP1_VERIFIER. Passing the PQ adapter instead
|
|
// of the BN254 gateway 0x9ca479... is the entire change. No consumer code edited.
|
|
const consumer = await V.deploy(await adapter.getAddress(), vkey);
|
|
await consumer.waitForDeployment();
|
|
|
|
expect(await consumer.SP1_VERIFIER()).to.equal(await adapter.getAddress());
|
|
expect(await consumer.PROGRAM_VKEY()).to.equal(vkey);
|
|
|
|
// 288-byte public values (9 x 32) the V2 consumer expects.
|
|
const abi = ethers.AbiCoder.defaultAbiCoder();
|
|
const publicValues = abi.encode(
|
|
["uint256", "uint256", "bytes32", "bytes32", "bytes32", "bytes32", "bytes32", "uint256", "uint256"],
|
|
[2800, 12345, ethers.ZeroHash, ethers.ZeroHash, ethers.ZeroHash, ethers.ZeroHash, ethers.ZeroHash, 0, 0]
|
|
);
|
|
const proof = ethers.hexlify(ethers.randomBytes(512));
|
|
|
|
// Precompile absent -> adapter reverts -> consumer.verify() reverts. Fail-closed.
|
|
await expect(consumer.verify(publicValues, proof)).to.be.reverted;
|
|
});
|
|
});
|
|
});
|