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