aere-contracts/test/attestation-gateway.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

164 lines
8.4 KiB
JavaScript

const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("AereAttestationGateway", () => {
let foundation, bafin, finma, subject1, outsider;
let gw;
const SCHEMA_MICA = ethers.keccak256(ethers.toUtf8Bytes("mica-casp-v1"));
const SCHEMA_AIACT = ethers.keccak256(ethers.toUtf8Bytes("ai-act-annex3-v1"));
const SPEC_HASH_MICA = ethers.keccak256(ethers.toUtf8Bytes("mica-spec-bytes"));
const SPEC_HASH_AI = ethers.keccak256(ethers.toUtf8Bytes("ai-act-spec-bytes"));
const SUBJECT_A = ethers.keccak256(ethers.toUtf8Bytes("subject:exchange-A"));
const PAYLOAD_HASH = ethers.keccak256(ethers.toUtf8Bytes("payload-bytes"));
beforeEach(async () => {
[foundation, bafin, finma, subject1, outsider] = await ethers.getSigners();
const F = await ethers.getContractFactory("AereAttestationGateway");
gw = await F.deploy(foundation.address);
await gw.waitForDeployment();
});
async function nowTs() {
return (await ethers.provider.getBlock("latest")).timestamp;
}
it("only Foundation publishes schemas; cannot republish", async () => {
await expect(
gw.connect(outsider).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "mica-casp-v1", "1.0.0", "ipfs://")
).to.be.revertedWithCustomError(gw, "NotFoundation");
await gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "mica-casp-v1", "1.0.0", "ipfs://");
expect((await gw.schemas(SCHEMA_MICA)).exists).to.equal(true);
await expect(
gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "x", "y", "z")
).to.be.revertedWithCustomError(gw, "SchemaExists");
await expect(
gw.connect(foundation).publishSchema(SCHEMA_AIACT, ethers.ZeroHash, "n", "v", "u")
).to.be.revertedWithCustomError(gw, "EmptySpec");
});
it("rejects attest from non-authorised attestor + unknown schema", async () => {
await expect(
gw.connect(bafin).attest(SCHEMA_MICA, SUBJECT_A, ethers.ZeroHash, PAYLOAD_HASH, 0, 0, "")
).to.be.revertedWithCustomError(gw, "SchemaUnknown");
await gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "n", "v", "u");
await expect(
gw.connect(bafin).attest(SCHEMA_MICA, SUBJECT_A, ethers.ZeroHash, PAYLOAD_HASH, 0, 0, "")
).to.be.revertedWithCustomError(gw, "NotAuthorisedAttestor");
});
it("authorise + attest emits + records + latestFor + subjectCurrentlyAttested", async () => {
await gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "n", "v", "u");
await gw.connect(foundation).setAttestor(SCHEMA_MICA, bafin.address, true);
const t = await nowTs();
await expect(
gw.connect(bafin).attest(
SCHEMA_MICA, SUBJECT_A, ethers.ZeroHash, PAYLOAD_HASH, t, t + 3600, "ipfs://x"
)
).to.emit(gw, "Attested");
const id = await gw.latestFor(SUBJECT_A, SCHEMA_MICA);
const a = await gw.attestationOf(id);
expect(a.attestor).to.equal(bafin.address);
expect(a.payloadHash).to.equal(PAYLOAD_HASH);
expect(await gw.isValidNow(id)).to.equal(true);
expect(await gw.subjectCurrentlyAttested(SUBJECT_A, SCHEMA_MICA)).to.equal(true);
});
it("validity window — past validUntil is invalid", async () => {
await gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "n", "v", "u");
await gw.connect(foundation).setAttestor(SCHEMA_MICA, bafin.address, true);
const t = await nowTs();
await gw.connect(bafin).attest(SCHEMA_MICA, SUBJECT_A, ethers.ZeroHash, PAYLOAD_HASH, t, t + 10, "");
await ethers.provider.send("evm_increaseTime", [20]);
await ethers.provider.send("evm_mine", []);
const id = await gw.latestFor(SUBJECT_A, SCHEMA_MICA);
expect(await gw.isValidNow(id)).to.equal(false);
});
it("bad validity window (validUntil < validFrom) rejected", async () => {
await gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "n", "v", "u");
await gw.connect(foundation).setAttestor(SCHEMA_MICA, bafin.address, true);
const t = await nowTs();
await expect(
gw.connect(bafin).attest(SCHEMA_MICA, SUBJECT_A, ethers.ZeroHash, PAYLOAD_HASH, t + 100, t, "")
).to.be.revertedWithCustomError(gw, "BadValidityWindow");
});
it("revocation: only issuer; immediate invalidity; cannot revoke twice", async () => {
await gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "n", "v", "u");
await gw.connect(foundation).setAttestor(SCHEMA_MICA, bafin.address, true);
const t = await nowTs();
await gw.connect(bafin).attest(SCHEMA_MICA, SUBJECT_A, ethers.ZeroHash, PAYLOAD_HASH, t, 0, "");
const id = await gw.latestFor(SUBJECT_A, SCHEMA_MICA);
await expect(gw.connect(outsider).revoke(id)).to.be.revertedWithCustomError(gw, "NotIssuer");
await gw.connect(bafin).revoke(id);
expect(await gw.isValidNow(id)).to.equal(false);
await expect(gw.connect(bafin).revoke(id)).to.be.revertedWithCustomError(gw, "AlreadyRevoked");
});
it("inheritance: child invalid if parent revoked OR expired", async () => {
// KYB schema by Foundation; child = best-ex schema by BAFIN.
const SCHEMA_KYB = ethers.keccak256(ethers.toUtf8Bytes("foundation-kyb-v1"));
await gw.connect(foundation).publishSchema(SCHEMA_KYB, SPEC_HASH_MICA, "kyb", "1", "u");
await gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "n", "v", "u");
await gw.connect(foundation).setAttestor(SCHEMA_KYB, foundation.address, true);
await gw.connect(foundation).setAttestor(SCHEMA_MICA, bafin.address, true);
const t = await nowTs();
await gw.connect(foundation).attest(SCHEMA_KYB, SUBJECT_A, ethers.ZeroHash, PAYLOAD_HASH, t, 0, "");
const parent = await gw.latestFor(SUBJECT_A, SCHEMA_KYB);
await gw.connect(bafin).attest(SCHEMA_MICA, SUBJECT_A, parent, PAYLOAD_HASH, t, 0, "");
const child = await gw.latestFor(SUBJECT_A, SCHEMA_MICA);
expect(await gw.isValidNow(child)).to.equal(true);
// Revoke parent → child fails.
await gw.connect(foundation).revoke(parent);
expect(await gw.isValidNow(child)).to.equal(false);
});
it("inheritance: bad parent rejected at attest time", async () => {
await gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "n", "v", "u");
await gw.connect(foundation).setAttestor(SCHEMA_MICA, bafin.address, true);
const fakeParent = ethers.keccak256(ethers.toUtf8Bytes("nonexistent"));
await expect(
gw.connect(bafin).attest(SCHEMA_MICA, SUBJECT_A, fakeParent, PAYLOAD_HASH, 0, 0, "")
).to.be.revertedWithCustomError(gw, "ParentInvalid");
});
it("inheritance: parent must match subject (audit fix HIGH #23)", async () => {
const SCHEMA_KYB = ethers.keccak256(ethers.toUtf8Bytes("kyb"));
await gw.connect(foundation).publishSchema(SCHEMA_KYB, SPEC_HASH_MICA, "kyb", "1", "u");
await gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "n", "v", "u");
await gw.connect(foundation).setAttestor(SCHEMA_KYB, foundation.address, true);
await gw.connect(foundation).setAttestor(SCHEMA_MICA, bafin.address, true);
const SUBJECT_B = ethers.keccak256(ethers.toUtf8Bytes("subject:exchange-B"));
// KYB issued for SUBJECT_B (not SUBJECT_A).
await gw.connect(foundation).attest(SCHEMA_KYB, SUBJECT_B, ethers.ZeroHash, PAYLOAD_HASH, 0, 0, "");
const parent = await gw.latestFor(SUBJECT_B, SCHEMA_KYB);
// Trying to chain that parent for SUBJECT_A → rejected.
await expect(
gw.connect(bafin).attest(SCHEMA_MICA, SUBJECT_A, parent, PAYLOAD_HASH, 0, 0, "")
).to.be.revertedWithCustomError(gw, "ParentSubjectMismatch");
});
it("toggling attestor off does not retro-invalidate past attestations", async () => {
await gw.connect(foundation).publishSchema(SCHEMA_MICA, SPEC_HASH_MICA, "n", "v", "u");
await gw.connect(foundation).setAttestor(SCHEMA_MICA, bafin.address, true);
const t = await nowTs();
await gw.connect(bafin).attest(SCHEMA_MICA, SUBJECT_A, ethers.ZeroHash, PAYLOAD_HASH, t, 0, "");
const id = await gw.latestFor(SUBJECT_A, SCHEMA_MICA);
// De-authorise BAFIN going forward.
await gw.connect(foundation).setAttestor(SCHEMA_MICA, bafin.address, false);
// Past attestation is still valid (only revocation by issuer kills it).
expect(await gw.isValidNow(id)).to.equal(true);
// But BAFIN can no longer issue new attestations under this schema.
await expect(
gw.connect(bafin).attest(SCHEMA_MICA, SUBJECT_A, ethers.ZeroHash, PAYLOAD_HASH, t, 0, "")
).to.be.revertedWithCustomError(gw, "NotAuthorisedAttestor");
});
});