aere-contracts/test/halo2-cubic-verifier.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

114 lines
4.0 KiB
JavaScript

// halo2-cubic-verifier.test.js
//
// Real Halo2 (bn254/KZG, Bdfg21/SHPLONK) verifier generated by
// privacy-scaling-explorations/halo2-solidity-verifier on the AERE build
// server, for a genuine 4-row standard-PLONK circuit proving knowledge of a
// private x with x^3 + x + 5 == out (witness x = 3, public instance out = 35).
//
// The proof in the fixture is a REAL halo2 proof: natively verified by
// halo2_proofs (VerifierSHPLONK) and re-verified in revm before it was
// exported. These tests re-verify it a third time inside the hardhat EVM.
const { expect } = require("chai");
const { ethers } = require("hardhat");
const fixture = require("./fixtures/aere-halo2-cubic-proof.json");
const EIP7825_TX_GAS_CAP = 16777216;
describe("AereHalo2CubicVerifier + AereHalo2ProofAnchor", function () {
let verifier, anchor, user;
const instances = fixture.instances.map(BigInt); // [35n]
before(async function () {
[user] = await ethers.getSigners();
const V = await ethers.getContractFactory("AereHalo2CubicVerifier");
verifier = await V.deploy();
await verifier.waitForDeployment();
const A = await ethers.getContractFactory("AereHalo2ProofAnchor");
anchor = await A.deploy(await verifier.getAddress());
await anchor.waitForDeployment();
});
it("verifies the real proof via the exact exported calldata", async function () {
const raw = await ethers.provider.call({
to: await verifier.getAddress(),
data: fixture.calldata,
});
expect(raw).to.equal(
"0x0000000000000000000000000000000000000000000000000000000000000001"
);
});
it("verifies the real proof via the typed ABI", async function () {
expect(
await verifier.verifyProof.staticCall(fixture.proof, instances)
).to.equal(true);
});
it("verification gas fits comfortably under the EIP-7825 per-tx cap", async function () {
const gas = await verifier.verifyProof.estimateGas(fixture.proof, instances);
expect(gas).to.be.lt(BigInt(EIP7825_TX_GAS_CAP));
});
it("rejects a corrupted proof (byte flipped)", async function () {
const bad = ethers.getBytes(fixture.proof);
bad[100] ^= 0x01;
await expect(
verifier.verifyProof.staticCall(ethers.hexlify(bad), instances)
).to.be.reverted;
});
it("rejects a truncated proof", async function () {
const bad = ethers.getBytes(fixture.proof).slice(0, 64);
await expect(
verifier.verifyProof.staticCall(ethers.hexlify(bad), instances)
).to.be.reverted;
});
it("rejects the right proof against the wrong public instance", async function () {
await expect(
verifier.verifyProof.staticCall(fixture.proof, [34n])
).to.be.reverted;
await expect(
verifier.verifyProof.staticCall(fixture.proof, [36n])
).to.be.reverted;
});
it("anchor records a successful verification and emits the event", async function () {
const proofHash = ethers.keccak256(fixture.proof);
const instancesHash = ethers.solidityPackedKeccak256(
["uint256[]"],
[instances]
);
await expect(anchor.verifyAndRecord(fixture.proof, instances))
.to.emit(anchor, "Halo2ProofVerified")
.withArgs(user.address, proofHash, instancesHash, 0n);
expect(await anchor.recordCount()).to.equal(1n);
const rec = await anchor.getRecord(0);
expect(rec.sender).to.equal(user.address);
expect(rec.proofHash).to.equal(proofHash);
expect(rec.instancesHash).to.equal(instancesHash);
expect(await anchor.verificationCountByProofHash(proofHash)).to.equal(1n);
});
it("anchor rejects an invalid proof (verifier revert bubbles up)", async function () {
const bad = ethers.getBytes(fixture.proof);
bad[200] ^= 0x01;
await expect(anchor.verifyAndRecord(ethers.hexlify(bad), instances)).to.be
.reverted;
expect(await anchor.recordCount()).to.equal(1n);
});
it("anchor refuses a zero verifier address", async function () {
const A = await ethers.getContractFactory("AereHalo2ProofAnchor");
await expect(A.deploy(ethers.ZeroAddress)).to.be.revertedWithCustomError(
A,
"ZeroVerifier"
);
});
});