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.
110 lines
5.0 KiB
JavaScript
110 lines
5.0 KiB
JavaScript
const { expect } = require("chai");
|
|
const { ethers, network } = require("hardhat");
|
|
|
|
// Local validation of the AereGovernor + TimelockController binding machinery
|
|
// (block-number clock). Mirrors scripts/deploy-governance-stack.js self-test but
|
|
// drives block progression with evm_mine.
|
|
async function mine(n = 1) {
|
|
for (let i = 0; i < n; i++) await network.provider.send("evm_mine");
|
|
}
|
|
|
|
describe("AereGovernor binding self-test (local)", function () {
|
|
it("propose -> vote -> queue -> execute flips an owned target", async function () {
|
|
const [deployer] = await ethers.getSigners();
|
|
|
|
const Votes = await ethers.getContractFactory("GovSelfTestVotes");
|
|
const votes = await Votes.deploy();
|
|
await votes.waitForDeployment();
|
|
await (await votes.mint(deployer.address, ethers.parseEther("1000000"))).wait();
|
|
await (await votes.delegate(deployer.address)).wait();
|
|
|
|
const TL = await ethers.getContractFactory("TimelockController");
|
|
const timelock = await TL.deploy(0, [], [], deployer.address);
|
|
await timelock.waitForDeployment();
|
|
|
|
const Gov = await ethers.getContractFactory("AereGovernor");
|
|
const gov = await Gov.deploy(await votes.getAddress(), await timelock.getAddress(), 1, 5, 0, 4);
|
|
await gov.waitForDeployment();
|
|
|
|
const PROPOSER = await timelock.PROPOSER_ROLE();
|
|
const EXECUTOR = await timelock.EXECUTOR_ROLE();
|
|
const CANCELLER = await timelock.CANCELLER_ROLE();
|
|
await (await timelock.grantRole(PROPOSER, await gov.getAddress())).wait();
|
|
await (await timelock.grantRole(EXECUTOR, await gov.getAddress())).wait();
|
|
await (await timelock.grantRole(CANCELLER, await gov.getAddress())).wait();
|
|
|
|
const Target = await ethers.getContractFactory("GovSelfTestTarget");
|
|
const target = await Target.deploy();
|
|
await target.waitForDeployment();
|
|
await (await target.transferOwnership(await timelock.getAddress())).wait();
|
|
expect(await target.owner()).to.equal(await timelock.getAddress());
|
|
expect(await target.value()).to.equal(0n);
|
|
|
|
await mine(2);
|
|
|
|
const targets = [await target.getAddress()];
|
|
const values = [0n];
|
|
const calldatas = [target.interface.encodeFunctionData("setValue", [42])];
|
|
const description = "local selftest setValue(42)";
|
|
const descHash = ethers.id(description);
|
|
const proposalId = await gov.hashProposal(targets, values, calldatas, descHash);
|
|
|
|
await (await gov.propose(targets, values, calldatas, description)).wait();
|
|
const snapshot = await gov.proposalSnapshot(proposalId);
|
|
const deadline = await gov.proposalDeadline(proposalId);
|
|
|
|
// advance past snapshot -> Active
|
|
const cur = await ethers.provider.getBlockNumber();
|
|
await mine(Number(snapshot) - cur + 1);
|
|
expect(Number(await gov.state(proposalId))).to.equal(1); // Active
|
|
|
|
await (await gov.castVote(proposalId, 1)).wait();
|
|
|
|
const cur2 = await ethers.provider.getBlockNumber();
|
|
await mine(Number(deadline) - cur2 + 1);
|
|
expect(Number(await gov.state(proposalId))).to.equal(4); // Succeeded
|
|
|
|
await (await gov.queue(targets, values, calldatas, descHash)).wait();
|
|
expect(Number(await gov.state(proposalId))).to.equal(5); // Queued
|
|
|
|
await mine(1);
|
|
await (await gov.execute(targets, values, calldatas, descHash)).wait();
|
|
expect(Number(await gov.state(proposalId))).to.equal(7); // Executed
|
|
expect(await target.value()).to.equal(42n);
|
|
});
|
|
|
|
it("gAERE adapter is non-transferable and zero-supply against empty staking", async function () {
|
|
const [deployer, other] = await ethers.getSigners();
|
|
const Mock = await ethers.getContractFactory("MockStakingForVotes");
|
|
const stakingMock = await Mock.deploy();
|
|
await stakingMock.waitForDeployment();
|
|
const lockedMock = await Mock.deploy();
|
|
await lockedMock.waitForDeployment();
|
|
|
|
const G = await ethers.getContractFactory("AereStakedGovVotes");
|
|
const g = await G.deploy(await stakingMock.getAddress(), await lockedMock.getAddress());
|
|
await g.waitForDeployment();
|
|
|
|
// empty staking -> zero power
|
|
expect(await g.stakePowerOf(deployer.address)).to.equal(0n);
|
|
await (await g.sync(deployer.address)).wait();
|
|
expect(await g.totalSupply()).to.equal(0n);
|
|
|
|
// seed mock delegation, sync mints mirrored balance
|
|
await (await stakingMock.setValidator(deployer.address, ethers.parseEther("500"))).wait();
|
|
await (await stakingMock.setDelegation(deployer.address, other.address, ethers.parseEther("1234"))).wait();
|
|
expect(await g.stakePowerOf(other.address)).to.equal(ethers.parseEther("1234"));
|
|
expect(await g.stakePowerOf(deployer.address)).to.equal(ethers.parseEther("500")); // self-stake
|
|
await (await g.sync(other.address)).wait();
|
|
expect(await g.balanceOf(other.address)).to.equal(ethers.parseEther("1234"));
|
|
|
|
// non-transferable
|
|
await expect(g.connect(other).transfer(deployer.address, 1n)).to.be.revertedWith("gAERE: non-transferable mirror");
|
|
|
|
// unstake -> sync burns
|
|
await (await stakingMock.setDelegation(deployer.address, other.address, 0n)).wait();
|
|
await (await g.sync(other.address)).wait();
|
|
expect(await g.balanceOf(other.address)).to.equal(0n);
|
|
});
|
|
});
|