diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 2443a047..d1d210ea 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -128,7 +128,7 @@ jobs: - uses: actions/checkout@v3 with: submodules: recursive - - uses: OffchainLabs/actions/run-nitro-test-node@main + - uses: OffchainLabs/actions/run-nitro-test-node@output-logging - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 @@ -198,7 +198,7 @@ jobs: - uses: actions/checkout@v3 with: submodules: recursive - - uses: OffchainLabs/actions/run-nitro-test-node@main + - uses: OffchainLabs/actions/run-nitro-test-node@output-logging - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 diff --git a/audit-ci.jsonc b/audit-ci.jsonc index c7f640be..e13a56c7 100644 --- a/audit-ci.jsonc +++ b/audit-ci.jsonc @@ -43,6 +43,14 @@ // Server-Side Request Forgery in Request "GHSA-p8p7-x288-28g6", // OpenZeppelin: Using ERC2771Context with a custom forwarder can yield address(0) - "GHSA-g4vp-m682-qqmp" + "GHSA-g4vp-m682-qqmp", + // regular expression DoS in debug - low severity + "GHSA-gxpj-cx7g-858c", + // undici - only used in hardhat, not used in prod + "GHSA-wqq4-5wpv-mx2g", + // get-func-name - only used in chai, not used in prod + "GHSA-4q6p-r6v2-jvc5", + // axios used only in sol2uml + "GHSA-wf5p-g6vw-rhxx" ] } \ No newline at end of file diff --git a/files/local/.env-sample b/files/local/.env-sample index 6e6a814b..61385fa7 100644 --- a/files/local/.env-sample +++ b/files/local/.env-sample @@ -9,9 +9,9 @@ ARB_URL="http://localhost:8547" NOVA_URL="http://localhost:8547" ETH_URL="http://localhost:8545" ## Test keys -ARB_KEY="e887f7d17d07cc7b8004053fb8826f6657084e88904bb61590e498ca04704cf2" -ETH_KEY="" -NOVA_KEY="e887f7d17d07cc7b8004053fb8826f6657084e88904bb61590e498ca04704cf2" +ARB_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" +ETH_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" +NOVA_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" DEPLOY_CONFIG_FILE_LOCATION="./files/local/deployConfig.json" VESTED_RECIPIENTS_FILE_LOCATION="./files/local/vestingWalletRecipients.json" diff --git a/package.json b/package.json index 1cae0a9c..aa8a840a 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "hardhat": "^2.12.6", "hardhat-gas-reporter": "^1.0.9", "http-server": "^14.1.1", - "solidity-coverage": "^0.8.2", + "solidity-coverage": "^0.8.5", "ts-node": "^10.9.1", "typechain": "^8.1.0", "typescript": "^4.8.4" diff --git a/scripts/proposalTests.ts b/scripts/proposalTests.ts index d5e70653..998c2ea9 100644 --- a/scripts/proposalTests.ts +++ b/scripts/proposalTests.ts @@ -58,8 +58,8 @@ async function main() { const currentBlock = await arbProvider.getBlockNumber(); while ((await arbProvider.getBlockNumber()) - currentBlock < 2) { if (isLocal) { - await mineBlock(ethDeployer); - await mineBlock(arbDeployer); + await mineBlock(ethDeployer, "2blocketh"); + await mineBlock(arbDeployer, "2blockarb"); } await wait(1000); } diff --git a/src-ts/proposalPipeline.ts b/src-ts/proposalPipeline.ts index 9700eab2..6c5d2e34 100644 --- a/src-ts/proposalPipeline.ts +++ b/src-ts/proposalPipeline.ts @@ -11,6 +11,7 @@ import { UnreachableCaseError, getProvider, BaseGovernorExecuteStage, + L2TimelockExecutionSingleStage, } from "./proposalStage"; import { Signer } from "ethers"; import { Provider, TransactionReceipt } from "@ethersproject/abstract-provider"; @@ -28,6 +29,7 @@ export class StageFactory { return [ ...(await BaseGovernorExecuteStage.extractStages(receipt, this.arbOneSignerOrProvider)), ...(await L2TimelockExecutionBatchStage.extractStages(receipt, this.arbOneSignerOrProvider)), + ...(await L2TimelockExecutionSingleStage.extractStages(receipt, this.arbOneSignerOrProvider)), ...(await L1TimelockExecutionSingleStage.extractStages(receipt, this.l1SignerOrProvider)), ...(await L1TimelockExecutionBatchStage.extractStages(receipt, this.l1SignerOrProvider)), ...(await RetryableExecutionStage.extractStages(receipt, this.arbOneSignerOrProvider)), diff --git a/src-ts/proposalStage.ts b/src-ts/proposalStage.ts index c5c980a5..0915c410 100644 --- a/src-ts/proposalStage.ts +++ b/src-ts/proposalStage.ts @@ -21,6 +21,7 @@ import { L2ArbitrumGovernor__factory, GovernorUpgradeable__factory, SecurityCouncilNomineeElectionGovernor__factory, + ArbSys__factory, } from "../typechain-types"; import { Inbox__factory } from "@arbitrum/sdk/dist/lib/abi/factories/Inbox__factory"; import { EventArgs } from "@arbitrum/sdk/dist/lib/dataEntities/event"; @@ -34,6 +35,7 @@ import { ProposalQueuedEventObject, } from "../typechain-types/src/L2ArbitrumGovernor"; import { hasTimelock, hasVettingPeriod, getL1BlockNumberFromL2 } from "./utils"; +import { CallScheduledEvent } from "../typechain-types/src/ArbitrumTimelock"; type Provider = providers.Provider; @@ -356,30 +358,16 @@ export class GovernorQueueStage extends BaseGovernorExecuteStage { } } -/** - * When a timelock period has passed, execute the proposal action - */ -export class L2TimelockExecutionBatchStage implements ProposalStage { - public readonly name: string = "L2TimelockExecutionBatchStage"; +abstract class L2TimelockExecutionStage implements ProposalStage { public readonly identifier: string; - public constructor( - public readonly targets: string[], - public readonly values: BigNumber[], - public readonly callDatas: string[], - public readonly predecessor: string, - public readonly salt: string, - + constructor( + public readonly name: string, + public readonly operationId: string, public readonly timelockAddress: string, public readonly signerOrProvider: Signer | Provider ) { - this.identifier = L2TimelockExecutionBatchStage.hashOperationBatch( - targets, - values, - callDatas, - predecessor, - salt - ); + this.identifier = operationId; } public static async getProposalCreatedData( @@ -428,62 +416,6 @@ export class L2TimelockExecutionBatchStage implements ProposalStage { } } - public static async extractStages( - receipt: TransactionReceipt, - arbOneSignerOrProvider: Provider | Signer - ): Promise { - const govInterface = L2ArbitrumGovernor__factory.createInterface(); - const proposalStages: L2TimelockExecutionBatchStage[] = []; - for (const log of receipt.logs) { - if (log.topics.find((t) => t === govInterface.getEventTopic("ProposalQueued"))) { - const propQueuedObj = govInterface.parseLog(log) - .args as unknown as ProposalQueuedEventObject; - - // 10m ~ 1 month on arbitrum - const propCreatedStart = log.blockNumber - 10000000; - const propCreatedEnd = log.blockNumber; - - const propCreatedEvent = await this.getProposalCreatedData( - log.address, - propQueuedObj.proposalId.toHexString(), - await getProvider(arbOneSignerOrProvider)!, - propCreatedStart, - propCreatedEnd - ); - if (!propCreatedEvent) { - throw new Error( - `Could not find proposal created event: ${propQueuedObj.proposalId.toHexString()}` - ); - } - // calculate the operation id, and look for it in this receipt - const operationId = L2TimelockExecutionBatchStage.hashOperationBatch( - propCreatedEvent.targets, - (propCreatedEvent as any)[3], - propCreatedEvent.calldatas, - constants.HashZero, - id(propCreatedEvent.description) - ); - const timelockAddress = this.findTimelockAddress(operationId, receipt.logs); - if (!timelockAddress) { - throw new Error(`Could not find timelock address for operation id ${operationId}`); - } - // we know the operation id - const executeBatch = new L2TimelockExecutionBatchStage( - propCreatedEvent.targets, - (propCreatedEvent as any)[3], - propCreatedEvent.calldatas, - constants.HashZero, - id(propCreatedEvent.description), - timelockAddress, - arbOneSignerOrProvider - ); - proposalStages.push(executeBatch); - } - } - - return proposalStages; - } - public static hashOperation( target: string, value: BigNumber, @@ -541,58 +473,30 @@ export class L2TimelockExecutionBatchStage implements ProposalStage { public async status(): Promise { const timelock = ArbitrumTimelock__factory.connect(this.timelockAddress, this.signerOrProvider); - const operationId = await L2TimelockExecutionBatchStage.hashOperationBatch( - this.targets, - this.values, - this.callDatas, - this.predecessor, - this.salt - ); - // operation was cancelled if it doesn't exist - const exists = await timelock.isOperation(operationId); + const exists = await timelock.isOperation(this.operationId); if (!exists) return ProposalStageStatus.TERMINATED; // if it does exist it should be in one of the following states // check isOperationReady before pending because pending is a super set of ready - const ready = await timelock.isOperationReady(operationId); + const ready = await timelock.isOperationReady(this.operationId); if (ready) return ProposalStageStatus.READY; - const pending = await timelock.isOperationPending(operationId); + const pending = await timelock.isOperationPending(this.operationId); if (pending) return ProposalStageStatus.PENDING; - const done = await timelock.isOperationDone(operationId); + const done = await timelock.isOperationDone(this.operationId); if (done) return ProposalStageStatus.EXECUTED; throw new ProposalStageError( - `Proposal exists in unexpected state: ${operationId}`, - this.identifier, + `Proposal exists in unexpected state: ${this.operationId}`, + this.operationId, this.name ); } - public async execute(): Promise { - const timelock = ArbitrumTimelock__factory.connect(this.timelockAddress, this.signerOrProvider); - const tx = await timelock.functions.executeBatch( - this.targets, - this.values, - this.callDatas, - this.predecessor, - this.salt - ); - - await tx.wait(); - } - public async getExecuteReceipt(): Promise { const timelock = ArbitrumTimelock__factory.connect(this.timelockAddress, this.signerOrProvider); const provider = getProvider(this.signerOrProvider); - const operationId = await L2TimelockExecutionBatchStage.hashOperationBatch( - this.targets, - this.values, - this.callDatas, - this.predecessor, - this.salt - ); - const callExecutedFilter = timelock.filters.CallExecuted(operationId); + const callExecutedFilter = timelock.filters.CallExecuted(this.operationId); const logs = await provider!.getLogs({ fromBlock: 0, toBlock: "latest", @@ -610,6 +514,250 @@ export class L2TimelockExecutionBatchStage implements ProposalStage { const execReceipt = await this.getExecuteReceipt(); return `https://arbiscan.io/tx/${execReceipt.transactionHash}`; } + + public abstract execute(): Promise; +} + +/** + * When a timelock period has passed, execute the proposal action + */ +export class L2TimelockExecutionBatchStage extends L2TimelockExecutionStage { + public constructor( + public readonly targets: string[], + public readonly values: BigNumber[], + public readonly callDatas: string[], + public readonly predecessor: string, + public readonly salt: string, + + timelockAddress: string, + signerOrProvider: Signer | Provider + ) { + const operationId = L2TimelockExecutionBatchStage.hashOperationBatch( + targets, + values, + callDatas, + predecessor, + salt + ); + super("L2TimelockExecutionBatchStage", operationId, timelockAddress, signerOrProvider); + } + + public static async extractStages( + receipt: TransactionReceipt, + arbOneSignerOrProvider: Provider | Signer + ): Promise { + const govInterface = L2ArbitrumGovernor__factory.createInterface(); + const proposalStages: L2TimelockExecutionBatchStage[] = []; + for (const log of receipt.logs) { + if (log.topics.find((t) => t === govInterface.getEventTopic("ProposalQueued"))) { + const propQueuedObj = govInterface.parseLog(log) + .args as unknown as ProposalQueuedEventObject; + + // 10m ~ 1 month on arbitrum + const propCreatedStart = log.blockNumber - 10000000; + const propCreatedEnd = log.blockNumber; + + const propCreatedEvent = await this.getProposalCreatedData( + log.address, + propQueuedObj.proposalId.toHexString(), + await getProvider(arbOneSignerOrProvider)!, + propCreatedStart, + propCreatedEnd + ); + if (!propCreatedEvent) { + throw new Error( + `Could not find proposal created event: ${propQueuedObj.proposalId.toHexString()}` + ); + } + // calculate the operation id, and look for it in this receipt + const operationId = L2TimelockExecutionBatchStage.hashOperationBatch( + propCreatedEvent.targets, + (propCreatedEvent as any)[3], + propCreatedEvent.calldatas, + constants.HashZero, + id(propCreatedEvent.description) + ); + const timelockAddress = this.findTimelockAddress(operationId, receipt.logs); + if (!timelockAddress) { + // if we couldnt find the timelock address it's because the operation id was not found on a callscheduled event + // this could be because it was formed via batch instead of single, or vice versa, and is an expected result + continue; + } + // we know the operation id + const executeBatch = new L2TimelockExecutionBatchStage( + propCreatedEvent.targets, + (propCreatedEvent as any)[3], + propCreatedEvent.calldatas, + constants.HashZero, + id(propCreatedEvent.description), + timelockAddress, + arbOneSignerOrProvider + ); + proposalStages.push(executeBatch); + } + } + + return proposalStages; + } + + public async execute(): Promise { + const timelock = ArbitrumTimelock__factory.connect(this.timelockAddress, this.signerOrProvider); + const tx = await timelock.functions.executeBatch( + this.targets, + this.values, + this.callDatas, + this.predecessor, + this.salt + ); + + await tx.wait(); + } +} + +/** + * When a timelock period has passed, execute the proposal action + */ +export class L2TimelockExecutionSingleStage extends L2TimelockExecutionStage { + public constructor( + public readonly target: string, + public readonly value: BigNumber, + public readonly callData: string, + public readonly predecessor: string, + public readonly salt: string, + + timelockAddress: string, + signerOrProvider: Signer | Provider + ) { + const operationId = L2TimelockExecutionSingleStage.hashOperation( + target, + value, + callData, + predecessor, + salt + ); + super("L2TimelockExecutionSingleStage", operationId, timelockAddress, signerOrProvider); + } + + public static async extractStages( + receipt: TransactionReceipt, + arbOneSignerOrProvider: Provider | Signer + ): Promise { + const proposalStages: L2TimelockExecutionSingleStage[] = []; + const govInterface = L2ArbitrumGovernor__factory.createInterface(); + const timelockInterface = ArbitrumTimelock__factory.createInterface(); + + for (const log of receipt.logs) { + if (log.topics[0] === timelockInterface.getEventTopic("CallScheduled")) { + // try to parse the call scheduled event to get get the operation id and form as stage + // the data should be a call to arbsys sendTxToL1 + try { + const callScheduledArgs = timelockInterface.parseLog(log) + .args as CallScheduledEvent["args"]; + + const { data } = ArbSys__factory.createInterface().decodeFunctionData( + "sendTxToL1", + callScheduledArgs.data + ); + + const { salt } = this.decodeScheduleBatch(data); + + // calculate the id and check if that operation exists + const operationId = this.hashOperation( + callScheduledArgs.target, + callScheduledArgs[3], // cant use .value as ethers fails with this + callScheduledArgs.data, + callScheduledArgs.predecessor, + salt + ); + + if (operationId !== callScheduledArgs.id) { + throw new Error("Invalid operation id"); + } + + const timelockAddress = log.address; + const executeTimelock = new L2TimelockExecutionSingleStage( + callScheduledArgs.target, + callScheduledArgs[3], + callScheduledArgs.data, + callScheduledArgs.predecessor, + salt, + timelockAddress, + arbOneSignerOrProvider + ); + if ( + proposalStages.filter((s) => s.identifier === executeTimelock.identifier).length === 0 + ) { + proposalStages.push(executeTimelock); + } + } catch (err) { + // there are expected errors since the calldata may not be of the expected form for decoding + continue; + } + } else if (log.topics[0] === govInterface.getEventTopic("ProposalQueued")) { + const proposalId = ( + govInterface.parseLog(log).args as unknown as ProposalQueuedEventObject + ).proposalId.toHexString(); + + // 10m ~ 1 month on arbitrum + const propCreatedStart = log.blockNumber - 10000000; + const propCreatedEnd = log.blockNumber; + + const propCreatedEvent = await this.getProposalCreatedData( + log.address, + proposalId, + await getProvider(arbOneSignerOrProvider)!, + propCreatedStart, + propCreatedEnd + ); + if (!propCreatedEvent) { + throw new Error(`Could not find proposal created event: ${proposalId}`); + } + // calculate the operation id, and look for it in this receipt + const operationId = L2TimelockExecutionSingleStage.hashOperation( + propCreatedEvent.targets[0], + (propCreatedEvent as any)[3][0], + propCreatedEvent.calldatas[0], + constants.HashZero, + id(propCreatedEvent.description) + ); + const timelockAddress = this.findTimelockAddress(operationId, receipt.logs); + if (!timelockAddress) { + // if we couldnt find the timelock address it's because the operation id was not found on a callscheduled event + // this could be because it was formed via batch instead of single, or vice versa, and is an expected result + continue; + } + const executeTimelock = new L2TimelockExecutionSingleStage( + propCreatedEvent.targets[0], + (propCreatedEvent as any)[3][0], + propCreatedEvent.calldatas[0], + constants.HashZero, + id(propCreatedEvent.description), + timelockAddress, + arbOneSignerOrProvider + ); + if ( + proposalStages.filter((s) => s.identifier === executeTimelock.identifier).length === 0 + ) { + proposalStages.push(executeTimelock); + } + } + } + + return proposalStages; + } + + public async execute(): Promise { + const timelock = ArbitrumTimelock__factory.connect(this.timelockAddress, this.signerOrProvider); + const tx = await timelock.functions.execute( + this.target, + this.value, + this.callData, + this.predecessor, + this.salt + ); + + await tx.wait(); + } } /** @@ -646,7 +794,6 @@ export class L1OutboxStage implements ProposalStage { public async status(): Promise { const message = L2ToL1Message.fromEvent(this.l1SignerOrProvider, this.l2ToL1TxEvent); - const status = await message.status(this.l2Provider); switch (status) { diff --git a/test-ts/governance.test.ts b/test-ts/governance.test.ts index e36e131a..1b909dd1 100644 --- a/test-ts/governance.test.ts +++ b/test-ts/governance.test.ts @@ -217,7 +217,7 @@ describe("Governor", function () { l2GovernorContract, localMiners ); - }).timeout(360000); + }).timeout(600000); it("L2-L1-L2 monitoring value", async () => { const { l1Signer, l2Signer, l1Deployer, l2Deployer } = await testSetup(); @@ -240,7 +240,7 @@ describe("Governor", function () { l2GovernorContract, localMiners ); - }).timeout(360000); + }).timeout(600000); it("L2-L1 monitoring", async () => { const { l1Signer, l2Signer, l1Deployer, l2Deployer } = await testSetup(); @@ -263,7 +263,7 @@ describe("Governor", function () { l2GovernorContract, localMiners ); - }).timeout(360000); + }).timeout(600000); it("L2-L1-L2 monitoring", async () => { const { l1Signer, l2Signer, l1Deployer, l2Deployer } = await testSetup(); @@ -286,7 +286,7 @@ describe("Governor", function () { l2GovernorContract, localMiners ); - }).timeout(360000); + }).timeout(600000); it("L2-L1 proposal", async () => { const { l1Signer, l2Signer, l1Deployer, l2Deployer } = await testSetup(); @@ -307,7 +307,7 @@ describe("Governor", function () { l1TimelockContract, l2TimelockContract, ); - }).timeout(360000); + }).timeout(600000); it("L2-L1-L2 proposal", async () => { const { l1Signer, l2Signer, l1Deployer, l2Deployer } = await testSetup(); @@ -327,5 +327,5 @@ describe("Governor", function () { l2TimelockContract, l2UpgradeExecutor ); - }).timeout(360000); + }).timeout(600000); }); diff --git a/test-ts/integration.ts b/test-ts/integration.ts index 07b331bf..f26d62e8 100644 --- a/test-ts/integration.ts +++ b/test-ts/integration.ts @@ -32,16 +32,18 @@ import { const wait = async (ms: number) => new Promise((res) => setTimeout(res, ms)); -export const mineBlock = async (signer: Signer) => { +export const mineBlock = async (signer: Signer, tag: string) => { console.log( - `Mining block for ${await signer.getAddress()}:${(await signer.provider!.getNetwork()).chainId}` + `Mining block for ${tag}:${await signer.getAddress()}:${ + (await signer.provider!.getNetwork()).chainId + }` ); await (await signer.sendTransaction({ to: await signer.getAddress(), value: 0 })).wait(); }; -const mineUntilStop = async (miner: Signer, state: { mining: boolean }) => { +const mineUntilStop = async (miner: Signer, state: { mining: boolean }, tag: string) => { while (state.mining) { - await mineBlock(miner); + await mineBlock(miner, tag); await wait(15000); } }; @@ -234,6 +236,7 @@ const mineBlocksAndWaitForProposalState = async ( l2GovernorContract: L2ArbitrumGovernor, proposalId: string, state: number, + tag: string, localMiners?: { l1Signer: Signer; l2Signer: Signer; @@ -241,8 +244,8 @@ const mineBlocksAndWaitForProposalState = async ( ) => { while (true) { if (localMiners) { - await mineBlock(localMiners.l1Signer); - await mineBlock(localMiners.l2Signer); + await mineBlock(localMiners.l1Signer, tag + "-l1signer"); + await mineBlock(localMiners.l2Signer, tag + "-l2signer"); } else { await wait(1000); } @@ -350,13 +353,13 @@ export const l2L1MonitoringValueTest = async ( ).wait(); // wait a while then cast a vote - await mineBlocksAndWaitForProposalState(l2GovernorContract, proposal.id(), 1, localMiners); + await mineBlocksAndWaitForProposalState(l2GovernorContract, proposal.id(), 1, "waitforpropl2L1val", localMiners); await (await l2GovernorContract.connect(proposer).castVote(proposal.id(), 1)).wait(); const noteBefore = await noteStore.exists(noteId); expect(noteBefore, "Note exists before").to.be.false; - await mineBlocksUntilComplete(noteExists(noteStore, noteId), localMiners); + await mineBlocksUntilComplete(noteExists(noteStore, noteId), "waitfornotel2L1val", localMiners); const noteAfter = await noteStore.exists(noteId); expect(noteAfter, "Note exists after").to.be.true; @@ -364,6 +367,7 @@ export const l2L1MonitoringValueTest = async ( const mineBlocksUntilComplete = async ( completion: Promise, + tag: string, localMiners?: { l1Signer: Signer; l2Signer: Signer; @@ -383,8 +387,8 @@ const mineBlocksUntilComplete = async ( while (mining) { if (localMiners) { - await mineBlock(localMiners.l1Signer); - await mineBlock(localMiners.l2Signer); + await mineBlock(localMiners.l1Signer, tag + "-l1signer"); + await mineBlock(localMiners.l2Signer, tag + "-l2signer"); } await wait(500); } @@ -477,13 +481,13 @@ export const l2L1L2MonitoringValueTest = async ( ).wait(); // wait a while then cast a vote - await mineBlocksAndWaitForProposalState(l2GovernorContract, proposal.id(), 1, localMiners); + await mineBlocksAndWaitForProposalState(l2GovernorContract, proposal.id(), 1, "waitforpropl2L1L2val", localMiners); await (await l2GovernorContract.connect(proposer).castVote(proposal.id(), 1)).wait(); const noteBefore = await noteStore.exists(noteId); expect(noteBefore, "Note exists before").to.be.false; - await mineBlocksUntilComplete(noteExists(noteStore, noteId), localMiners); + await mineBlocksUntilComplete(noteExists(noteStore, noteId), "waitfornotel2L1L2val", localMiners); const noteAfter = await noteStore.exists(noteId); expect(noteAfter, "Note exists after").to.be.true; @@ -568,13 +572,13 @@ export const l2L1MonitoringTest = async ( await proposalMonitor.monitorSingleProposal(receipt); // wait a while then cast a vote - await mineBlocksAndWaitForProposalState(l2GovernorContract, proposal.id(), 1, localMiners); + await mineBlocksAndWaitForProposalState(l2GovernorContract, proposal.id(), 1, "waitforpropl2L1", localMiners); await (await l2GovernorContract.connect(proposer).castVote(proposal.id(), 1)).wait(); const noteBefore = await noteStore.exists(noteId); expect(noteBefore, "Note exists before").to.be.false; - await mineBlocksUntilComplete(noteExists(noteStore, noteId), localMiners); + await mineBlocksUntilComplete(noteExists(noteStore, noteId), "waitfornotel2L1",localMiners); const noteAfter = await noteStore.exists(noteId); expect(noteAfter, "Note exists after").to.be.true; @@ -661,13 +665,13 @@ export const l2L1L2MonitoringTest = async ( await proposalMonitor.monitorSingleProposal(receipt); // wait a while then cast a vote - await mineBlocksAndWaitForProposalState(l2GovernorContract, proposal.id(), 1, localMiners); + await mineBlocksAndWaitForProposalState(l2GovernorContract, proposal.id(), 1, "waitforpropL2L1L2", localMiners); await (await l2GovernorContract.connect(proposer).castVote(proposal.id(), 1)).wait(); const noteBefore = await noteStore.exists(noteId); expect(noteBefore, "Note exists before").to.be.false; - await mineBlocksUntilComplete(noteExists(noteStore, noteId), localMiners); + await mineBlocksUntilComplete(noteExists(noteStore, noteId), "waitfornoteL2L1L2", localMiners); const noteAfter = await noteStore.exists(noteId); expect(noteAfter, "Note exists after").to.be.true; @@ -690,8 +694,8 @@ const execL1Component = async ( const state = { mining: true }; await Promise.race([ - mineUntilStop(l1Deployer, state), - mineUntilStop(l2Deployer, state), + mineUntilStop(l1Deployer, state, "withdrawl1dep"), + mineUntilStop(l2Deployer, state, "withdrawl2dep"), withdrawMessage.waitUntilReadyToExecute(l2Signer.provider!), ]); state.mining = false; @@ -702,8 +706,8 @@ const execL1Component = async ( const opId = propFormNonEmpty.l1Gov.operationId; while (true) { - await mineBlock(l1Signer); - await mineBlock(l2Signer); + await mineBlock(l1Signer, "l1opreadyl1signer"); + await mineBlock(l2Signer, "l1opreadyl2signer"); if (await l1TimelockContract.isOperationReady(opId)) break; await wait(1000); } @@ -729,7 +733,6 @@ const execL1Component = async ( const rec = await tx.wait(); expect(await proposalSuccess(), "L1 proposal success").to.be.true; - return rec; }; @@ -754,7 +757,7 @@ const proposeAndExecuteL2 = async ( const proposalVotes = await l2GovernorContract.proposalVotes(proposalId); expect(proposalVotes, "Proposal exists").to.not.be.undefined; - await mineBlocksAndWaitForProposalState(l2GovernorContract, proposalId, 1, { + await mineBlocksAndWaitForProposalState(l2GovernorContract, proposalId, 1, "l2propstate1", { l1Signer: l1Deployer, l2Signer: l2Deployer, }); @@ -768,7 +771,7 @@ const proposeAndExecuteL2 = async ( .to.be.true; // wait for proposal to be in success state - await mineBlocksAndWaitForProposalState(l2GovernorContract, proposalId, 4, { + await mineBlocksAndWaitForProposalState(l2GovernorContract, proposalId, 4, "l2propstate4", { l1Signer: l1Deployer, l2Signer: l2Deployer, }); @@ -781,15 +784,15 @@ const proposeAndExecuteL2 = async ( }) ).wait(); - await mineBlocksAndWaitForProposalState(l2GovernorContract, proposalId, 5, { + await mineBlocksAndWaitForProposalState(l2GovernorContract, proposalId, 5, "l2propstate5", { l1Signer: l1Deployer, l2Signer: l2Deployer, }); const opIdBatch = propFormedNonEmpty.l2Gov.operationId; while (!(await l2TimelockContract.isOperationReady(opIdBatch))) { - await mineBlock(l1Deployer); - await mineBlock(l2Deployer); + await mineBlock(l1Deployer, "l2opreadyl1dep"); + await mineBlock(l2Deployer, "l2opreadyl2dep"); await wait(1000); } diff --git a/yarn.lock b/yarn.lock index 874201ff..e775be4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1126,7 +1126,7 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== -"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1": +"@solidity-parser/parser@^0.14.0": version "0.14.3" resolved "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.3.tgz" integrity sha512-29g2SZ29HtsqA58pLCtopI1P/cPy5/UAzlcAXO6T/CNJimG6yA8kx4NaseMyJULiC+TEs02Y9/yeHzClqoA0hw== @@ -1140,6 +1140,13 @@ dependencies: antlr4ts "^0.5.0-alpha.4" +"@solidity-parser/parser@^0.16.0": + version "0.16.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.1.tgz#f7c8a686974e1536da0105466c4db6727311253c" + integrity sha512-PdhRFNhbTtu3x8Axm0uYpqOy/lODYQK+MlYSgqIsq2L8SFYEHJPHNUiOTAJbDGzNjjr1/n9AcIayxafR/fWmYw== + dependencies: + antlr4ts "^0.5.0-alpha.4" + "@szmarczak/http-timer@^4.0.5": version "4.0.6" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" @@ -4878,35 +4885,32 @@ mnemonist@^0.38.0: dependencies: obliterator "^2.0.0" -mocha@7.1.2: - version "7.1.2" - resolved "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz" - integrity sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA== +mocha@10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== dependencies: - ansi-colors "3.2.3" + ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.3.0" - debug "3.2.6" - diff "3.5.0" - escape-string-regexp "1.0.5" - find-up "3.0.0" - glob "7.1.3" - growl "1.10.5" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" he "1.2.0" - js-yaml "3.13.1" - log-symbols "3.0.0" - minimatch "3.0.4" - mkdirp "0.5.5" - ms "2.1.1" - node-environment-flags "1.0.6" - object.assign "4.1.0" - strip-json-comments "2.0.1" - supports-color "6.0.0" - which "1.3.1" - wide-align "1.1.3" - yargs "13.3.2" - yargs-parser "13.1.2" - yargs-unparser "1.6.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" mocha@^10.0.0: version "10.1.0" @@ -6181,13 +6185,13 @@ solidity-ast@^0.4.15: resolved "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.35.tgz" integrity sha512-F5bTDLh3rmDxRmLSrs3qt3nvxJprWSEkS7h2KmuXDx7XTfJ6ZKVTV1rtPIYCqJAuPsU/qa8YUeFn7jdOAZcTPA== -solidity-coverage@^0.8.2: - version "0.8.2" - resolved "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.2.tgz" - integrity sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ== +solidity-coverage@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.5.tgz#64071c3a0c06a0cecf9a7776c35f49edc961e875" + integrity sha512-6C6N6OV2O8FQA0FWA95FdzVH+L16HU94iFgg5wAFZ29UpLFkgNI/DRR2HotG1bC0F4gAc/OMs2BJI44Q/DYlKQ== dependencies: "@ethersproject/abi" "^5.0.9" - "@solidity-parser/parser" "^0.14.1" + "@solidity-parser/parser" "^0.16.0" chalk "^2.4.2" death "^1.1.0" detect-port "^1.3.0" @@ -6198,7 +6202,7 @@ solidity-coverage@^0.8.2: globby "^10.0.1" jsonschema "^1.2.4" lodash "^4.17.15" - mocha "7.1.2" + mocha "10.2.0" node-emoji "^1.10.0" pify "^4.0.1" recursive-readdir "^2.2.2"