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.
153 lines
7.0 KiB
JavaScript
153 lines
7.0 KiB
JavaScript
const { expect } = require("chai");
|
|
const { ethers } = require("hardhat");
|
|
|
|
describe("AereDeathSwitch", () => {
|
|
let foundation, alice, bob;
|
|
let bridge, dex, ds;
|
|
|
|
const BUCKET_BRIDGE = ethers.keccak256(ethers.toUtf8Bytes("bridge"));
|
|
const BUCKET_DEX = ethers.keccak256(ethers.toUtf8Bytes("dex"));
|
|
|
|
beforeEach(async () => {
|
|
[foundation, alice, bob] = await ethers.getSigners();
|
|
|
|
const M = await ethers.getContractFactory("MockDeathSwitchCondition");
|
|
bridge = await M.deploy(BUCKET_BRIDGE, "bridge collateral ratio breached");
|
|
await bridge.waitForDeployment();
|
|
dex = await M.deploy(BUCKET_DEX, "dex pool drained > 50%");
|
|
await dex.waitForDeployment();
|
|
|
|
const D = await ethers.getContractFactory("AereDeathSwitch");
|
|
ds = await D.deploy(foundation.address, [await bridge.getAddress(), await dex.getAddress()]);
|
|
await ds.waitForDeployment();
|
|
});
|
|
|
|
it("constructor registers conditions + buckets", async () => {
|
|
expect(await ds.conditionsLength()).to.equal(2n);
|
|
const [addr0, b0] = await ds.conditionAt(0);
|
|
expect(addr0).to.equal(await bridge.getAddress());
|
|
expect(b0).to.equal(BUCKET_BRIDGE);
|
|
expect(await ds.isTripped()).to.equal(false);
|
|
});
|
|
|
|
it("only Foundation can declare emergency; declares + lock", async () => {
|
|
await expect(
|
|
ds.connect(alice).armForDeclaredEmergency("rogue Foundation key?")
|
|
).to.be.revertedWithCustomError(ds, "NotFoundation");
|
|
await ds.connect(foundation).armForDeclaredEmergency("test incident");
|
|
expect(await ds.isTripped()).to.equal(true);
|
|
expect(await ds.lastTripLabel()).to.equal("test incident");
|
|
expect(await ds.lastTripReason()).to.equal(ethers.keccak256(ethers.toUtf8Bytes("declared")));
|
|
});
|
|
|
|
it("permissionless trip succeeds when condition breached", async () => {
|
|
await bridge.setBreached(true);
|
|
await expect(ds.connect(alice).permissionlessTrip(0))
|
|
.to.emit(ds, "Tripped")
|
|
.withArgs(BUCKET_BRIDGE, alice.address, 0n, "bridge collateral ratio breached");
|
|
expect(await ds.isTripped()).to.equal(true);
|
|
expect(await ds.isBucketTripped(BUCKET_BRIDGE)).to.equal(true);
|
|
});
|
|
|
|
it("permissionless trip rejects when condition not breached / unknown id", async () => {
|
|
await expect(ds.connect(alice).permissionlessTrip(0))
|
|
.to.be.revertedWithCustomError(ds, "ConditionNotBreached");
|
|
await expect(ds.connect(alice).permissionlessTrip(99))
|
|
.to.be.revertedWithCustomError(ds, "UnknownCondition");
|
|
});
|
|
|
|
it("cannot trip twice; rearm flow gated by 7-day timelock", async () => {
|
|
await ds.connect(foundation).armForDeclaredEmergency("incident");
|
|
await expect(ds.connect(foundation).armForDeclaredEmergency("twice"))
|
|
.to.be.revertedWithCustomError(ds, "AlreadyTripped");
|
|
|
|
// Only Foundation can request rearm.
|
|
await expect(ds.connect(alice).requestRearm(ethers.ZeroHash))
|
|
.to.be.revertedWithCustomError(ds, "NotFoundation");
|
|
|
|
const reason = ethers.keccak256(ethers.toUtf8Bytes("rotated keys"));
|
|
await ds.connect(foundation).requestRearm(reason);
|
|
// Double request prevented.
|
|
await expect(ds.connect(foundation).requestRearm(reason))
|
|
.to.be.revertedWithCustomError(ds, "RearmAlreadyRequested");
|
|
|
|
// Cannot finalize before timelock.
|
|
await expect(ds.connect(foundation).finalizeRearm())
|
|
.to.be.revertedWithCustomError(ds, "RearmTimelockOpen");
|
|
|
|
// Fast-forward 7d.
|
|
await ethers.provider.send("evm_increaseTime", [7 * 24 * 3600 + 1]);
|
|
await ethers.provider.send("evm_mine", []);
|
|
|
|
await expect(ds.connect(foundation).finalizeRearm()).to.emit(ds, "Rearmed");
|
|
expect(await ds.isTripped()).to.equal(false);
|
|
expect(await ds.rearmReadyAt()).to.equal(0n);
|
|
});
|
|
|
|
it("finalizeRearm reverts when no request pending", async () => {
|
|
await ds.connect(foundation).armForDeclaredEmergency("incident");
|
|
await expect(ds.connect(foundation).finalizeRearm())
|
|
.to.be.revertedWithCustomError(ds, "NoRearmRequest");
|
|
});
|
|
|
|
it("cancelRearmRequest drops a pending request + emits (audit fix MED #37)", async () => {
|
|
await ds.connect(foundation).armForDeclaredEmergency("incident");
|
|
const reason = ethers.keccak256(ethers.toUtf8Bytes("reason"));
|
|
await ds.connect(foundation).requestRearm(reason);
|
|
await expect(ds.connect(foundation).cancelRearmRequest()).to.emit(ds, "RearmCancelled").withArgs(reason);
|
|
expect(await ds.rearmReadyAt()).to.equal(0n);
|
|
await ds.connect(foundation).requestRearm(ethers.ZeroHash);
|
|
expect(await ds.rearmReadyAt()).to.be.gt(0n);
|
|
});
|
|
|
|
it("permissionlessUnarmDeclared after 24h if no condition breached (audit fix HIGH #19)", async () => {
|
|
await ds.connect(foundation).armForDeclaredEmergency("Foundation pager test");
|
|
// Too early.
|
|
await expect(ds.connect(alice).permissionlessUnarmDeclared()).to.be.revertedWithCustomError(ds, "RearmTimelockOpen");
|
|
await ethers.provider.send("evm_increaseTime", [24 * 3600 + 1]);
|
|
await ethers.provider.send("evm_mine", []);
|
|
// If a condition is breached now, unarm still rejected.
|
|
await bridge.setBreached(true);
|
|
await expect(ds.connect(alice).permissionlessUnarmDeclared()).to.be.revertedWithCustomError(ds, "NoConditionsBreached");
|
|
await bridge.setBreached(false);
|
|
// Now succeeds — any address can call.
|
|
await ds.connect(alice).permissionlessUnarmDeclared();
|
|
expect(await ds.isTripped()).to.equal(false);
|
|
});
|
|
|
|
it("permissionlessUnarmDeclared not applicable for condition-tripped emergencies", async () => {
|
|
await bridge.setBreached(true);
|
|
await ds.connect(alice).permissionlessTrip(0);
|
|
await ethers.provider.send("evm_increaseTime", [24 * 3600 + 1]);
|
|
await ethers.provider.send("evm_mine", []);
|
|
await expect(ds.connect(alice).permissionlessUnarmDeclared()).to.be.revertedWithCustomError(ds, "ConditionNotBreached");
|
|
});
|
|
|
|
it("bucketTripped flags clear after rearm finalize", async () => {
|
|
await bridge.setBreached(true);
|
|
await ds.connect(alice).permissionlessTrip(0);
|
|
expect(await ds.bucketTripped(BUCKET_BRIDGE)).to.equal(true);
|
|
|
|
await bridge.setBreached(false);
|
|
await ds.connect(foundation).requestRearm(ethers.ZeroHash);
|
|
await ethers.provider.send("evm_increaseTime", [7 * 24 * 3600 + 1]);
|
|
await ethers.provider.send("evm_mine", []);
|
|
await ds.connect(foundation).finalizeRearm();
|
|
expect(await ds.bucketTripped(BUCKET_BRIDGE)).to.equal(false);
|
|
});
|
|
|
|
it("isBucketTripped is true while master tripped, even for unregistered buckets", async () => {
|
|
const FOO = ethers.keccak256(ethers.toUtf8Bytes("foo"));
|
|
await ds.connect(foundation).armForDeclaredEmergency("incident");
|
|
expect(await ds.isBucketTripped(FOO)).to.equal(true);
|
|
});
|
|
|
|
it("constructor with empty conditions list works (manual-only mode)", async () => {
|
|
const D = await ethers.getContractFactory("AereDeathSwitch");
|
|
const manual = await D.deploy(foundation.address, []);
|
|
expect(await manual.conditionsLength()).to.equal(0n);
|
|
await manual.connect(foundation).armForDeclaredEmergency("manual-only");
|
|
expect(await manual.isTripped()).to.equal(true);
|
|
});
|
|
});
|