-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: vebo access control and deploy tests
- Loading branch information
Showing
4 changed files
with
407 additions
and
0 deletions.
There are no files selected for viewing
205 changes: 205 additions & 0 deletions
205
test/0.8.9/oracle/validator-exit-bus-oracle.accessControl.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
import { expect } from "chai"; | ||
import { ContractTransactionResponse, ZeroAddress } from "ethers"; | ||
import { ethers } from "hardhat"; | ||
|
||
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; | ||
|
||
import { HashConsensus__Harness, ValidatorsExitBus__Harness, ValidatorsExitBusOracle } from "typechain-types"; | ||
|
||
import { CONSENSUS_VERSION, de0x, numberToHex, SECONDS_PER_SLOT } from "lib"; | ||
|
||
import { DATA_FORMAT_LIST, deployVEBO, initVEBO } from "test/deploy"; | ||
import { Snapshot } from "test/suite"; | ||
|
||
const PUBKEYS = [ | ||
"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", | ||
"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", | ||
"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", | ||
]; | ||
|
||
describe("ValidatorsExitBusOracle.sol:accessControl", () => { | ||
let consensus: HashConsensus__Harness; | ||
let oracle: ValidatorsExitBus__Harness; | ||
let admin: HardhatEthersSigner; | ||
let originalState: string; | ||
|
||
let initTx: ContractTransactionResponse; | ||
let oracleVersion: bigint; | ||
let exitRequests: ExitRequest[]; | ||
let reportFields: ReportFields; | ||
let reportItems: ReturnType<typeof getValidatorsExitBusReportDataItems>; | ||
let reportHash: string; | ||
|
||
let member1: HardhatEthersSigner; | ||
let member2: HardhatEthersSigner; | ||
let member3: HardhatEthersSigner; | ||
let stranger: HardhatEthersSigner; | ||
let account1: HardhatEthersSigner; | ||
|
||
interface ExitRequest { | ||
moduleId: number; | ||
nodeOpId: number; | ||
valIndex: number; | ||
valPubkey: string; | ||
} | ||
|
||
interface ReportFields { | ||
consensusVersion: bigint; | ||
refSlot: bigint; | ||
requestsCount: number; | ||
dataFormat: number; | ||
data: string; | ||
} | ||
|
||
const calcValidatorsExitBusReportDataHash = (items: ReturnType<typeof getValidatorsExitBusReportDataItems>) => { | ||
const data = ethers.AbiCoder.defaultAbiCoder().encode(["(uint256,uint256,uint256,uint256,bytes)"], [items]); | ||
return ethers.keccak256(data); | ||
}; | ||
|
||
const getValidatorsExitBusReportDataItems = (r: ReportFields) => { | ||
return [r.consensusVersion, r.refSlot, r.requestsCount, r.dataFormat, r.data]; | ||
}; | ||
|
||
const encodeExitRequestHex = ({ moduleId, nodeOpId, valIndex, valPubkey }: ExitRequest) => { | ||
const pubkeyHex = de0x(valPubkey); | ||
expect(pubkeyHex.length).to.equal(48 * 2); | ||
return numberToHex(moduleId, 3) + numberToHex(nodeOpId, 5) + numberToHex(valIndex, 8) + pubkeyHex; | ||
}; | ||
|
||
const encodeExitRequestsDataList = (requests: ExitRequest[]) => { | ||
return "0x" + requests.map(encodeExitRequestHex).join(""); | ||
}; | ||
|
||
const deploy = async () => { | ||
const deployed = await deployVEBO(admin.address); | ||
oracle = deployed.oracle; | ||
consensus = deployed.consensus; | ||
|
||
initTx = await initVEBO({ admin: admin.address, oracle, consensus, resumeAfterDeploy: true }); | ||
|
||
oracleVersion = await oracle.getContractVersion(); | ||
|
||
await consensus.addMember(member1, 1); | ||
await consensus.addMember(member2, 2); | ||
await consensus.addMember(member3, 2); | ||
|
||
const { refSlot } = await consensus.getCurrentFrame(); | ||
exitRequests = [ | ||
{ moduleId: 1, nodeOpId: 0, valIndex: 0, valPubkey: PUBKEYS[0] }, | ||
{ moduleId: 1, nodeOpId: 0, valIndex: 2, valPubkey: PUBKEYS[1] }, | ||
{ moduleId: 2, nodeOpId: 0, valIndex: 1, valPubkey: PUBKEYS[2] }, | ||
]; | ||
|
||
reportFields = { | ||
consensusVersion: CONSENSUS_VERSION, | ||
dataFormat: DATA_FORMAT_LIST, | ||
refSlot: refSlot, | ||
requestsCount: exitRequests.length, | ||
data: encodeExitRequestsDataList(exitRequests), | ||
}; | ||
|
||
reportItems = getValidatorsExitBusReportDataItems(reportFields); | ||
reportHash = calcValidatorsExitBusReportDataHash(reportItems); | ||
|
||
await consensus.connect(member1).submitReport(refSlot, reportHash, CONSENSUS_VERSION); | ||
await consensus.connect(member3).submitReport(refSlot, reportHash, CONSENSUS_VERSION); | ||
}; | ||
|
||
before(async () => { | ||
[admin, member1, member2, member3, stranger, account1] = await ethers.getSigners(); | ||
|
||
await deploy(); | ||
}); | ||
|
||
beforeEach(async () => (originalState = await Snapshot.take())); | ||
|
||
afterEach(async () => await Snapshot.restore(originalState)); | ||
|
||
context("deploying", () => { | ||
it("deploying accounting oracle", async () => { | ||
expect(oracle).to.be.not.null; | ||
expect(consensus).to.be.not.null; | ||
expect(initTx).to.be.not.null; | ||
expect(oracleVersion).to.be.not.null; | ||
expect(exitRequests).to.be.not.null; | ||
expect(reportFields).to.be.not.null; | ||
expect(reportItems).to.be.not.null; | ||
expect(reportHash).to.be.not.null; | ||
}); | ||
}); | ||
|
||
context("DEFAULT_ADMIN_ROLE", () => { | ||
context("Admin is set at initialize", () => { | ||
it("should set admin at initialize", async () => { | ||
const DEFAULT_ADMIN_ROLE = await oracle.DEFAULT_ADMIN_ROLE(); | ||
await expect(initTx).to.emit(oracle, "RoleGranted").withArgs(DEFAULT_ADMIN_ROLE, admin, admin); | ||
}); | ||
it("should revert without admin address", async () => { | ||
await expect( | ||
oracle.initialize(ZeroAddress, await consensus.getAddress(), CONSENSUS_VERSION, 0), | ||
).to.be.revertedWithCustomError(oracle, "AdminCannotBeZero"); | ||
}); | ||
}); | ||
}); | ||
|
||
context("PAUSE_ROLE", () => { | ||
it("should revert without PAUSE_ROLE role", async () => { | ||
await expect(oracle.connect(stranger).pauseFor(0)).to.be.revertedWithOZAccessControlError( | ||
await stranger.getAddress(), | ||
await oracle.PAUSE_ROLE(), | ||
); | ||
}); | ||
|
||
it("should allow calling from a possessor of PAUSE_ROLE role", async () => { | ||
await oracle.grantRole(await oracle.PAUSE_ROLE(), account1); | ||
|
||
const tx = await oracle.connect(account1).pauseFor(9999); | ||
await expect(tx).to.emit(oracle, "Paused").withArgs(9999); | ||
}); | ||
}); | ||
|
||
context("RESUME_ROLE", () => { | ||
it("should revert without RESUME_ROLE role", async () => { | ||
await oracle.connect(admin).pauseFor(9999); | ||
|
||
await expect(oracle.connect(stranger).resume()).to.be.revertedWithOZAccessControlError( | ||
await stranger.getAddress(), | ||
await oracle.RESUME_ROLE(), | ||
); | ||
}); | ||
|
||
it("should allow calling from a possessor of RESUME_ROLE role", async () => { | ||
await oracle.pauseFor(9999, { from: admin }); | ||
await oracle.grantRole(await oracle.RESUME_ROLE(), account1); | ||
|
||
const tx = await oracle.connect(account1).resume(); | ||
await expect(tx).to.emit(oracle, "Resumed").withArgs(); | ||
}); | ||
}); | ||
|
||
context("SUBMIT_DATA_ROLE", () => { | ||
context("_checkMsgSenderIsAllowedToSubmitData", () => { | ||
it("should revert from not consensus member without SUBMIT_DATA_ROLE role", async () => { | ||
await expect( | ||
oracle.connect(stranger).submitReportData(reportFields, oracleVersion), | ||
).to.be.revertedWithCustomError(oracle, "SenderNotAllowed"); | ||
}); | ||
|
||
it("should allow calling from a possessor of SUBMIT_DATA_ROLE role", async () => { | ||
await oracle.grantRole(await oracle.SUBMIT_DATA_ROLE(), account1); | ||
const deadline = (await oracle.getConsensusReport()).processingDeadlineTime; | ||
await consensus.setTime(deadline); | ||
|
||
const tx = await oracle.connect(account1).submitReportData(reportFields, oracleVersion); | ||
|
||
await expect(tx).to.emit(oracle, "ProcessingStarted").withArgs(reportFields.refSlot, reportHash); | ||
}); | ||
|
||
it("should allow calling from a member", async () => { | ||
const tx = await oracle.connect(member2).submitReportData(reportFields, oracleVersion); | ||
|
||
await expect(tx).to.emit(oracle, "ProcessingStarted").withArgs(reportFields.refSlot, reportHash); | ||
}); | ||
}); | ||
}); | ||
}); |
81 changes: 81 additions & 0 deletions
81
test/0.8.9/oracle/validator-exit-bus-oracle.deploy.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { expect } from "chai"; | ||
import { ZeroAddress } from "ethers"; | ||
import { ethers } from "hardhat"; | ||
|
||
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; | ||
|
||
import { HashConsensus__Harness, ValidatorsExitBus__Harness, ValidatorsExitBusOracle } from "typechain-types"; | ||
|
||
import { CONSENSUS_VERSION, SECONDS_PER_SLOT } from "lib"; | ||
|
||
import { deployVEBO, initVEBO } from "test/deploy"; | ||
|
||
describe("ValidatorsExitBusOracle.sol:deploy", () => { | ||
context("Deployment and initial configuration", () => { | ||
let admin: HardhatEthersSigner; | ||
let defaultOracle: ValidatorsExitBusOracle; | ||
|
||
before(async () => { | ||
[admin] = await ethers.getSigners(); | ||
defaultOracle = (await deployVEBO(admin.address)).oracle; | ||
}); | ||
|
||
it("initialize reverts if admin address is zero", async () => { | ||
const deployed = await deployVEBO(admin.address); | ||
|
||
await expect( | ||
deployed.oracle.initialize(ZeroAddress, await deployed.consensus.getAddress(), CONSENSUS_VERSION, 0), | ||
).to.be.revertedWithCustomError(defaultOracle, "AdminCannotBeZero"); | ||
}); | ||
|
||
it("reverts when slotsPerSecond is zero", async () => { | ||
await expect(deployVEBO(admin.address, { secondsPerSlot: 0n })).to.be.revertedWithCustomError( | ||
defaultOracle, | ||
"SecondsPerSlotCannotBeZero", | ||
); | ||
}); | ||
|
||
context("deployment and init finishes successfully (default setup)", async () => { | ||
let consensus: HashConsensus__Harness; | ||
let oracle: ValidatorsExitBus__Harness; | ||
|
||
before(async () => { | ||
const deployed = await deployVEBO(admin.address); | ||
await initVEBO({ | ||
admin: admin.address, | ||
oracle: deployed.oracle, | ||
consensus: deployed.consensus, | ||
}); | ||
|
||
consensus = deployed.consensus; | ||
oracle = deployed.oracle; | ||
}); | ||
|
||
it("mock time-travellable setup is correct", async () => { | ||
const time1 = await consensus.getTime(); | ||
expect(await oracle.getTime()).to.equal(time1); | ||
|
||
await consensus.advanceTimeBy(SECONDS_PER_SLOT); | ||
|
||
const time2 = await consensus.getTime(); | ||
expect(time2).to.equal(time1 + SECONDS_PER_SLOT); | ||
expect(await oracle.getTime()).to.equal(time2); | ||
}); | ||
|
||
it("initial configuration is correct", async () => { | ||
expect(await oracle.getConsensusContract()).to.equal(await consensus.getAddress()); | ||
expect(await oracle.getConsensusVersion()).to.equal(CONSENSUS_VERSION); | ||
expect(await oracle.SECONDS_PER_SLOT()).to.equal(SECONDS_PER_SLOT); | ||
expect(await oracle.isPaused()).to.equal(true); | ||
}); | ||
|
||
it("pause/resume operations work", async () => { | ||
expect(await oracle.isPaused()).to.equal(true); | ||
await oracle.resume(); | ||
expect(await oracle.isPaused()).to.equal(false); | ||
await oracle.pauseFor(123); | ||
expect(await oracle.isPaused()).to.equal(true); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.