From bf5abe623b8ff3da39802b63b1ba672d95056e0a Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Wed, 1 Nov 2023 17:54:42 +0100 Subject: [PATCH 01/24] Added l2 single/batch timelock stage --- src-ts/proposalPipeline.ts | 2 + src-ts/proposalStage.ts | 376 ++++++++++++++++++++++++++----------- 2 files changed, 266 insertions(+), 112 deletions(-) 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..2173c798 100644 --- a/src-ts/proposalStage.ts +++ b/src-ts/proposalStage.ts @@ -12,7 +12,7 @@ import { import { Outbox__factory } from "@arbitrum/sdk/dist/lib/abi/factories/Outbox__factory"; import { L2ToL1TxEvent as NitroL2ToL1TransactionEvent } from "@arbitrum/sdk/dist/lib/abi/ArbSys"; import { OutBoxTransactionExecutedEvent } from "@arbitrum/sdk/dist/lib/abi/Outbox"; -import { TransactionReceipt } from "@ethersproject/providers"; +import { Log, TransactionReceipt } from "@ethersproject/providers"; import { BigNumber, constants, ethers, providers, Signer } from "ethers"; import { defaultAbiCoder, hexDataLength, id, keccak256 } from "ethers/lib/utils"; import { @@ -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,255 @@ 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(); + + try { + 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; + } catch (err) { + console.log(err); + throw err; + } + } + + 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 +799,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) { From 7384c94c2bc630e985a0c65dcdc5bdf7e753611b Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Wed, 1 Nov 2023 17:56:32 +0100 Subject: [PATCH 02/24] removed debug try/catch --- src-ts/proposalStage.ts | 171 +++++++++++++++++++--------------------- 1 file changed, 83 insertions(+), 88 deletions(-) diff --git a/src-ts/proposalStage.ts b/src-ts/proposalStage.ts index 2173c798..afad971a 100644 --- a/src-ts/proposalStage.ts +++ b/src-ts/proposalStage.ts @@ -646,93 +646,41 @@ export class L2TimelockExecutionSingleStage extends L2TimelockExecutionStage { const govInterface = L2ArbitrumGovernor__factory.createInterface(); const timelockInterface = ArbitrumTimelock__factory.createInterface(); - try { - 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 + 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 ); - 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 { 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 ); - 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; + + if (operationId !== callScheduledArgs.id) { + throw new Error("Invalid operation id"); } + + const timelockAddress = log.address; const executeTimelock = new L2TimelockExecutionSingleStage( - propCreatedEvent.targets[0], - (propCreatedEvent as any)[3][0], - propCreatedEvent.calldatas[0], - constants.HashZero, - id(propCreatedEvent.description), + callScheduledArgs.target, + callScheduledArgs[3], + callScheduledArgs.data, + callScheduledArgs.predecessor, + salt, timelockAddress, arbOneSignerOrProvider ); @@ -741,14 +689,61 @@ export class L2TimelockExecutionSingleStage extends L2TimelockExecutionStage { ) { 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; - return proposalStages; - } catch (err) { - console.log(err); - throw err; + 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 { From d8b056dd8b2798114a80a1e81e2c575f099b3e45 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 14:15:05 +0100 Subject: [PATCH 03/24] Updated audit --- audit-ci.jsonc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/audit-ci.jsonc b/audit-ci.jsonc index a399dcc4..e13a56c7 100644 --- a/audit-ci.jsonc +++ b/audit-ci.jsonc @@ -49,6 +49,8 @@ // 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" + "GHSA-4q6p-r6v2-jvc5", + // axios used only in sol2uml + "GHSA-wf5p-g6vw-rhxx" ] } \ No newline at end of file From f2453df5e1eebbe283f9579a985175e909e56198 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 14:18:20 +0100 Subject: [PATCH 04/24] Console logging in genNetwork --- scripts/genNetwork.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/genNetwork.ts b/scripts/genNetwork.ts index e4229f75..fdc8c358 100644 --- a/scripts/genNetwork.ts +++ b/scripts/genNetwork.ts @@ -1,6 +1,7 @@ import { Wallet, ethers } from "ethers"; import { setupNetworks, config, getSigner } from "../test-ts/testSetup"; import * as fs from "fs"; +import { formatEther, parseEther } from "ethers/lib/utils"; async function main() { const ethProvider = new ethers.providers.JsonRpcProvider(config.ethUrl); @@ -8,7 +9,13 @@ async function main() { const ethDeployer = getSigner(ethProvider, config.ethKey); const arbDeployer = getSigner(arbProvider, config.arbKey); - + console.log( + "arbdeployer", + await arbDeployer.getAddress(), + (await arbDeployer.getBalance()).toString(), + formatEther(await arbDeployer.getBalance()) + ); + const { l1Network, l2Network } = await setupNetworks( ethDeployer, arbDeployer, From 0d34471286e86168a843ff70bcee8da9bb60cbfb Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 14:57:13 +0100 Subject: [PATCH 05/24] Print addresses --- scripts/genNetwork.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/genNetwork.ts b/scripts/genNetwork.ts index fdc8c358..89fe6575 100644 --- a/scripts/genNetwork.ts +++ b/scripts/genNetwork.ts @@ -15,6 +15,11 @@ async function main() { (await arbDeployer.getBalance()).toString(), formatEther(await arbDeployer.getBalance()) ); + const l1mnemonic = "indoor dish desk flag debris potato excuse depart ticket judge file exit"; + for (let index = 0; index < 5; index++) { + const wal = ethers.Wallet.fromMnemonic(l1mnemonic, "m/44'/60'/0'/0/" + index); + console.log((await wal.connect(arbProvider).getBalance()).toString()); + } const { l1Network, l2Network } = await setupNetworks( ethDeployer, From 8a5206b60da95c83ec8db84e237e99d268d60548 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 14:58:09 +0100 Subject: [PATCH 06/24] Added address logging --- scripts/genNetwork.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/genNetwork.ts b/scripts/genNetwork.ts index 89fe6575..84b569ac 100644 --- a/scripts/genNetwork.ts +++ b/scripts/genNetwork.ts @@ -18,7 +18,7 @@ async function main() { const l1mnemonic = "indoor dish desk flag debris potato excuse depart ticket judge file exit"; for (let index = 0; index < 5; index++) { const wal = ethers.Wallet.fromMnemonic(l1mnemonic, "m/44'/60'/0'/0/" + index); - console.log((await wal.connect(arbProvider).getBalance()).toString()); + console.log(wal.address, (await wal.connect(arbProvider).getBalance()).toString()); } const { l1Network, l2Network } = await setupNetworks( From 757d1a64d25d3038bb10700da8d4d9c41758f9ee Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 15:00:14 +0100 Subject: [PATCH 07/24] Added test priv key --- scripts/genNetwork.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/genNetwork.ts b/scripts/genNetwork.ts index 84b569ac..bc4e7d50 100644 --- a/scripts/genNetwork.ts +++ b/scripts/genNetwork.ts @@ -21,6 +21,9 @@ async function main() { console.log(wal.address, (await wal.connect(arbProvider).getBalance()).toString()); } + const a = new Wallet("b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659").connect(arbProvider) + console.log(a.address, (await a.getBalance()).toString()); + const { l1Network, l2Network } = await setupNetworks( ethDeployer, arbDeployer, From 1b7021513a179809facab42b91c59d8bba37016b Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 15:21:49 +0100 Subject: [PATCH 08/24] Use 3f1 key --- files/local/.env-sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/local/.env-sample b/files/local/.env-sample index 1d60cc48..e8cf9036 100644 --- a/files/local/.env-sample +++ b/files/local/.env-sample @@ -9,7 +9,7 @@ ARB_URL="http://localhost:8547" NOVA_URL="http://localhost:8547" ETH_URL="http://localhost:8545" ## Test keys -ARB_KEY="cb5790da63720727af975f42c79f69918580209889225fa7128c92402a6d3a65" +ARB_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" ETH_KEY="cb5790da63720727af975f42c79f69918580209889225fa7128c92402a6d3a65" NOVA_KEY="cb5790da63720727af975f42c79f69918580209889225fa7128c92402a6d3a65" From c6207e2cc537ac32a6a6a29d10407ecc00a027ad Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 15:23:04 +0100 Subject: [PATCH 09/24] Removed unused logging --- scripts/genNetwork.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/scripts/genNetwork.ts b/scripts/genNetwork.ts index bc4e7d50..a80bc46b 100644 --- a/scripts/genNetwork.ts +++ b/scripts/genNetwork.ts @@ -1,7 +1,6 @@ -import { Wallet, ethers } from "ethers"; +import { ethers } from "ethers"; import { setupNetworks, config, getSigner } from "../test-ts/testSetup"; import * as fs from "fs"; -import { formatEther, parseEther } from "ethers/lib/utils"; async function main() { const ethProvider = new ethers.providers.JsonRpcProvider(config.ethUrl); @@ -9,20 +8,6 @@ async function main() { const ethDeployer = getSigner(ethProvider, config.ethKey); const arbDeployer = getSigner(arbProvider, config.arbKey); - console.log( - "arbdeployer", - await arbDeployer.getAddress(), - (await arbDeployer.getBalance()).toString(), - formatEther(await arbDeployer.getBalance()) - ); - const l1mnemonic = "indoor dish desk flag debris potato excuse depart ticket judge file exit"; - for (let index = 0; index < 5; index++) { - const wal = ethers.Wallet.fromMnemonic(l1mnemonic, "m/44'/60'/0'/0/" + index); - console.log(wal.address, (await wal.connect(arbProvider).getBalance()).toString()); - } - - const a = new Wallet("b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659").connect(arbProvider) - console.log(a.address, (await a.getBalance()).toString()); const { l1Network, l2Network } = await setupNetworks( ethDeployer, From c51181239ce0939abb2023a3a9ed6f85c889622a Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 15:38:06 +0100 Subject: [PATCH 10/24] Use same key for nova --- files/local/.env-sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/local/.env-sample b/files/local/.env-sample index e8cf9036..d40861a7 100644 --- a/files/local/.env-sample +++ b/files/local/.env-sample @@ -11,7 +11,7 @@ ETH_URL="http://localhost:8545" ## Test keys ARB_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" ETH_KEY="cb5790da63720727af975f42c79f69918580209889225fa7128c92402a6d3a65" -NOVA_KEY="cb5790da63720727af975f42c79f69918580209889225fa7128c92402a6d3a65" +NOVA_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" DEPLOY_CONFIG_FILE_LOCATION="./files/local/deployConfig.json" VESTED_RECIPIENTS_FILE_LOCATION="./files/local/vestingWalletRecipients.json" From 17dea64ebcc05067df90acacfb71ef790ab2a051 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 17:54:55 +0100 Subject: [PATCH 11/24] Updated timeouts --- test-ts/integration.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test-ts/integration.ts b/test-ts/integration.ts index 07b331bf..0bd2894e 100644 --- a/test-ts/integration.ts +++ b/test-ts/integration.ts @@ -742,6 +742,7 @@ const proposeAndExecuteL2 = async ( proposalSuccess: () => Promise, propFormed?: Awaited> ) => { + console.log("a1") const propFormedNonEmpty = propFormed!; await ( await l2Signer.sendTransaction({ @@ -749,6 +750,7 @@ const proposeAndExecuteL2 = async ( data: propFormedNonEmpty.l2Gov.propose.data, }) ).wait(); + console.log("a2") const proposalId = propFormedNonEmpty.l2Gov.proposalId; const proposalVotes = await l2GovernorContract.proposalVotes(proposalId); @@ -758,6 +760,7 @@ const proposeAndExecuteL2 = async ( l1Signer: l1Deployer, l2Signer: l2Deployer, }); + console.log("a3") // vote on the proposal expect( await (await l2GovernorContract.proposalVotes(proposalId)).forVotes.toString(), @@ -772,6 +775,7 @@ const proposeAndExecuteL2 = async ( l1Signer: l1Deployer, l2Signer: l2Deployer, }); + console.log("a4") // queue the proposal await ( @@ -780,12 +784,15 @@ const proposeAndExecuteL2 = async ( data: propFormedNonEmpty.l2Gov.queue.data, }) ).wait(); + console.log("a5") await mineBlocksAndWaitForProposalState(l2GovernorContract, proposalId, 5, { l1Signer: l1Deployer, l2Signer: l2Deployer, }); + console.log("a6") + const opIdBatch = propFormedNonEmpty.l2Gov.operationId; while (!(await l2TimelockContract.isOperationReady(opIdBatch))) { await mineBlock(l1Deployer); @@ -793,12 +800,15 @@ const proposeAndExecuteL2 = async ( await wait(1000); } + console.log("a7") + const executionTx = await ( await l2Signer.sendTransaction({ to: propFormedNonEmpty.l2Gov.execute.to, data: propFormedNonEmpty.l2Gov.execute.data, }) ).wait(); + console.log("a8") expect(await proposalSuccess(), "Proposal not executed successfully").to.be.true; return executionTx; }; @@ -855,6 +865,7 @@ export const l2L1ProposalTest = async ( const proposalSuccess = async () => { return true; }; + console.log("a") const executionTx = await proposeAndExecuteL2( l2TimelockContract, @@ -865,6 +876,7 @@ export const l2L1ProposalTest = async ( proposalSuccess, formData ); + console.log("b") const l2Transaction = new L2TransactionReceipt(executionTx); @@ -878,6 +890,7 @@ export const l2L1ProposalTest = async ( return true; }; + console.log("c") await execL1Component( l1Deployer, @@ -890,6 +903,8 @@ export const l2L1ProposalTest = async ( false, formData ); + + console.log("d") }; export const l2l1l2Proposal = async ( From 5a9f2568ca04bff65bdaa5c28365a8f3e1d12a19 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 18:46:35 +0100 Subject: [PATCH 12/24] Added more logging --- test-ts/governance.test.ts | 12 ++++++------ test-ts/integration.ts | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) 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 0bd2894e..d80daafd 100644 --- a/test-ts/integration.ts +++ b/test-ts/integration.ts @@ -684,10 +684,12 @@ const execL1Component = async ( crossChain: boolean, propForm?: Awaited> ) => { + console.log("c1") const propFormNonEmpty = propForm!; const l2ToL1Messages = await l2Tx.getL2ToL1Messages(l1Signer); const withdrawMessage = await l2ToL1Messages[0]; + console.log("c2") const state = { mining: true }; await Promise.race([ mineUntilStop(l1Deployer, state), @@ -695,9 +697,12 @@ const execL1Component = async ( withdrawMessage.waitUntilReadyToExecute(l2Signer.provider!), ]); state.mining = false; + console.log("c3") await (await withdrawMessage.execute(l2Deployer.provider!)).wait(); + console.log("c4") + await wait(5000); const opId = propFormNonEmpty.l1Gov.operationId; @@ -708,6 +713,8 @@ const execL1Component = async ( await wait(1000); } + console.log("c5") + // execute the proposal let value = BigNumber.from(0); if (crossChain) { @@ -720,16 +727,23 @@ const execL1Component = async ( value = submissionFee.mul(2); } + console.log("c6") + const tx = await l1Signer.sendTransaction({ to: propFormNonEmpty.l1Gov.execute.to, data: propFormNonEmpty.l1Gov.execute.data, value: value, }); + console.log("c7") + const rec = await tx.wait(); + console.log("c8") expect(await proposalSuccess(), "L1 proposal success").to.be.true; + console.log("c9") + return rec; }; From b98da5c44a4f2ba4617df2bcbe082d25d88dcfa0 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 13 Nov 2023 19:34:38 +0100 Subject: [PATCH 13/24] Removed console logging --- test-ts/integration.ts | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/test-ts/integration.ts b/test-ts/integration.ts index d80daafd..03f4c2e3 100644 --- a/test-ts/integration.ts +++ b/test-ts/integration.ts @@ -684,12 +684,10 @@ const execL1Component = async ( crossChain: boolean, propForm?: Awaited> ) => { - console.log("c1") const propFormNonEmpty = propForm!; const l2ToL1Messages = await l2Tx.getL2ToL1Messages(l1Signer); const withdrawMessage = await l2ToL1Messages[0]; - console.log("c2") const state = { mining: true }; await Promise.race([ mineUntilStop(l1Deployer, state), @@ -697,11 +695,9 @@ const execL1Component = async ( withdrawMessage.waitUntilReadyToExecute(l2Signer.provider!), ]); state.mining = false; - console.log("c3") await (await withdrawMessage.execute(l2Deployer.provider!)).wait(); - console.log("c4") await wait(5000); @@ -713,8 +709,6 @@ const execL1Component = async ( await wait(1000); } - console.log("c5") - // execute the proposal let value = BigNumber.from(0); if (crossChain) { @@ -727,23 +721,15 @@ const execL1Component = async ( value = submissionFee.mul(2); } - console.log("c6") - const tx = await l1Signer.sendTransaction({ to: propFormNonEmpty.l1Gov.execute.to, data: propFormNonEmpty.l1Gov.execute.data, value: value, }); - console.log("c7") - const rec = await tx.wait(); - console.log("c8") expect(await proposalSuccess(), "L1 proposal success").to.be.true; - - console.log("c9") - return rec; }; @@ -756,7 +742,6 @@ const proposeAndExecuteL2 = async ( proposalSuccess: () => Promise, propFormed?: Awaited> ) => { - console.log("a1") const propFormedNonEmpty = propFormed!; await ( await l2Signer.sendTransaction({ @@ -764,7 +749,6 @@ const proposeAndExecuteL2 = async ( data: propFormedNonEmpty.l2Gov.propose.data, }) ).wait(); - console.log("a2") const proposalId = propFormedNonEmpty.l2Gov.proposalId; const proposalVotes = await l2GovernorContract.proposalVotes(proposalId); @@ -774,7 +758,6 @@ const proposeAndExecuteL2 = async ( l1Signer: l1Deployer, l2Signer: l2Deployer, }); - console.log("a3") // vote on the proposal expect( await (await l2GovernorContract.proposalVotes(proposalId)).forVotes.toString(), @@ -789,7 +772,6 @@ const proposeAndExecuteL2 = async ( l1Signer: l1Deployer, l2Signer: l2Deployer, }); - console.log("a4") // queue the proposal await ( @@ -798,15 +780,12 @@ const proposeAndExecuteL2 = async ( data: propFormedNonEmpty.l2Gov.queue.data, }) ).wait(); - console.log("a5") await mineBlocksAndWaitForProposalState(l2GovernorContract, proposalId, 5, { l1Signer: l1Deployer, l2Signer: l2Deployer, }); - console.log("a6") - const opIdBatch = propFormedNonEmpty.l2Gov.operationId; while (!(await l2TimelockContract.isOperationReady(opIdBatch))) { await mineBlock(l1Deployer); @@ -814,15 +793,12 @@ const proposeAndExecuteL2 = async ( await wait(1000); } - console.log("a7") - const executionTx = await ( await l2Signer.sendTransaction({ to: propFormedNonEmpty.l2Gov.execute.to, data: propFormedNonEmpty.l2Gov.execute.data, }) ).wait(); - console.log("a8") expect(await proposalSuccess(), "Proposal not executed successfully").to.be.true; return executionTx; }; @@ -879,7 +855,6 @@ export const l2L1ProposalTest = async ( const proposalSuccess = async () => { return true; }; - console.log("a") const executionTx = await proposeAndExecuteL2( l2TimelockContract, @@ -890,7 +865,6 @@ export const l2L1ProposalTest = async ( proposalSuccess, formData ); - console.log("b") const l2Transaction = new L2TransactionReceipt(executionTx); @@ -904,7 +878,6 @@ export const l2L1ProposalTest = async ( return true; }; - console.log("c") await execL1Component( l1Deployer, @@ -917,8 +890,6 @@ export const l2L1ProposalTest = async ( false, formData ); - - console.log("d") }; export const l2l1l2Proposal = async ( From c6e1d32f0f9e4366ab11588a8ffcac42e02f364b Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Tue, 14 Nov 2023 12:30:11 +0100 Subject: [PATCH 14/24] Added fine grain logging and dependency --- .github/workflows/build-test.yml | 1 + scripts/proposalTests.ts | 4 +-- test-ts/integration.ts | 55 +++++++++++++++++--------------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 2443a047..d454a11a 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -194,6 +194,7 @@ jobs: test-integration: name: Test integration runs-on: ubuntu-latest + needs: test-deploy steps: - uses: actions/checkout@v3 with: 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/test-ts/integration.ts b/test-ts/integration.ts index 03f4c2e3..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,21 +694,20 @@ 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; await (await withdrawMessage.execute(l2Deployer.provider!)).wait(); - await wait(5000); 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); } @@ -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); } From 01e13042a952f478c7db1d5d2cc77b2dbae0eddd Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Tue, 14 Nov 2023 13:57:00 +0100 Subject: [PATCH 15/24] Add more stage tracker logging --- src-ts/proposalPipeline.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-ts/proposalPipeline.ts b/src-ts/proposalPipeline.ts index 6c5d2e34..3e1f1157 100644 --- a/src-ts/proposalPipeline.ts +++ b/src-ts/proposalPipeline.ts @@ -96,7 +96,7 @@ export class StageTracker extends EventEmitter { while (polling) { try { const status = await this.stage.status(); - if (currentStatus !== status) { + // if (currentStatus !== status) { // emit an event if the status changes this.emit(TrackerEventName.TRACKER_STATUS, { status, @@ -110,7 +110,7 @@ export class StageTracker extends EventEmitter { this.stage instanceof GovernorQueueStage ? this.stage.description : undefined, }); currentStatus = status; - } + // } switch (status) { case ProposalStageStatus.TERMINATED: // end of the road From 2b079c74783dff4b31d99b2b5e5dc53f32be3969 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Tue, 14 Nov 2023 14:23:31 +0100 Subject: [PATCH 16/24] Allow running in parallel again --- .github/workflows/build-test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index d454a11a..2443a047 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -194,7 +194,6 @@ jobs: test-integration: name: Test integration runs-on: ubuntu-latest - needs: test-deploy steps: - uses: actions/checkout@v3 with: From 62a27317478dcba49b79988db3913d248fffcce1 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Tue, 14 Nov 2023 18:37:06 +0100 Subject: [PATCH 17/24] Added monitoring of the node creation and confirmation --- src-ts/proposalStage.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src-ts/proposalStage.ts b/src-ts/proposalStage.ts index afad971a..263b831c 100644 --- a/src-ts/proposalStage.ts +++ b/src-ts/proposalStage.ts @@ -24,6 +24,7 @@ import { ArbSys__factory, } from "../typechain-types"; import { Inbox__factory } from "@arbitrum/sdk/dist/lib/abi/factories/Inbox__factory"; +import { RollupUserLogic__factory } from "@arbitrum/sdk/dist/lib/abi/factories/RollupUserLogic__factory"; import { EventArgs } from "@arbitrum/sdk/dist/lib/dataEntities/event"; import { InboxMessageKind } from "@arbitrum/sdk/dist/lib/dataEntities/message"; import { SubmitRetryableMessageDataParser } from "@arbitrum/sdk/dist/lib/message/messageDataParser"; @@ -795,6 +796,16 @@ export class L1OutboxStage implements ProposalStage { public async status(): Promise { const message = L2ToL1Message.fromEvent(this.l1SignerOrProvider, this.l2ToL1TxEvent); const status = await message.status(this.l2Provider); + const l2Network = await getL2Network(this.l2Provider); + const rollup = RollupUserLogic__factory.connect(l2Network.ethBridge.rollup, this.l2Provider); + + const latestConfirmedNodeNum = await rollup.callStatic.latestConfirmed(); + const latestCreatedNodeNum = await rollup.callStatic.latestNodeCreated(); + console.log( + "assertion number", + latestConfirmedNodeNum.toNumber(), + latestCreatedNodeNum.toNumber() + ); switch (status) { case L2ToL1MessageStatus.UNCONFIRMED: From 137dd859111d5f8b333d6d4afe48d4de408c3241 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Tue, 14 Nov 2023 20:15:08 +0100 Subject: [PATCH 18/24] Updated to l1 provider --- src-ts/proposalStage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ts/proposalStage.ts b/src-ts/proposalStage.ts index 263b831c..9934ea1c 100644 --- a/src-ts/proposalStage.ts +++ b/src-ts/proposalStage.ts @@ -797,7 +797,7 @@ export class L1OutboxStage implements ProposalStage { const message = L2ToL1Message.fromEvent(this.l1SignerOrProvider, this.l2ToL1TxEvent); const status = await message.status(this.l2Provider); const l2Network = await getL2Network(this.l2Provider); - const rollup = RollupUserLogic__factory.connect(l2Network.ethBridge.rollup, this.l2Provider); + const rollup = RollupUserLogic__factory.connect(l2Network.ethBridge.rollup, getProvider(this.l1SignerOrProvider)!); const latestConfirmedNodeNum = await rollup.callStatic.latestConfirmed(); const latestCreatedNodeNum = await rollup.callStatic.latestNodeCreated(); From af30cba00bfd6ed87d5d8d143bb1fca05a8e1f80 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Wed, 15 Nov 2023 13:25:49 +0100 Subject: [PATCH 19/24] Try output logging --- .github/workflows/build-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 2443a047..f52d850a 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@putput-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@putput-logging - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 From 03afb0f00fe4aaa5349674bd7f87857d9eb80bdf Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Wed, 15 Nov 2023 13:26:37 +0100 Subject: [PATCH 20/24] Typo fix --- .github/workflows/build-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index f52d850a..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@putput-logging + - 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@putput-logging + - uses: OffchainLabs/actions/run-nitro-test-node@output-logging - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 From 92d6e2b63e416103721d97cb6c6f9708e80c758f Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Wed, 15 Nov 2023 14:28:06 +0100 Subject: [PATCH 21/24] Try with the eth key --- files/local/.env-sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/local/.env-sample b/files/local/.env-sample index d40861a7..61385fa7 100644 --- a/files/local/.env-sample +++ b/files/local/.env-sample @@ -10,7 +10,7 @@ NOVA_URL="http://localhost:8547" ETH_URL="http://localhost:8545" ## Test keys ARB_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" -ETH_KEY="cb5790da63720727af975f42c79f69918580209889225fa7128c92402a6d3a65" +ETH_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" NOVA_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" DEPLOY_CONFIG_FILE_LOCATION="./files/local/deployConfig.json" From 8e7b4cc415a742e5b48705025e00da069923c026 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Wed, 15 Nov 2023 15:14:06 +0100 Subject: [PATCH 22/24] Removed assertion logging --- src-ts/proposalStage.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src-ts/proposalStage.ts b/src-ts/proposalStage.ts index 9934ea1c..13be5ab9 100644 --- a/src-ts/proposalStage.ts +++ b/src-ts/proposalStage.ts @@ -796,16 +796,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); - const l2Network = await getL2Network(this.l2Provider); - const rollup = RollupUserLogic__factory.connect(l2Network.ethBridge.rollup, getProvider(this.l1SignerOrProvider)!); - - const latestConfirmedNodeNum = await rollup.callStatic.latestConfirmed(); - const latestCreatedNodeNum = await rollup.callStatic.latestNodeCreated(); - console.log( - "assertion number", - latestConfirmedNodeNum.toNumber(), - latestCreatedNodeNum.toNumber() - ); switch (status) { case L2ToL1MessageStatus.UNCONFIRMED: From 0ebbb95bcb531c29520aab1688d435a21b6ccd48 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Fri, 17 Nov 2023 10:43:27 +0100 Subject: [PATCH 23/24] Commented back in change check --- src-ts/proposalPipeline.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-ts/proposalPipeline.ts b/src-ts/proposalPipeline.ts index 3e1f1157..6c5d2e34 100644 --- a/src-ts/proposalPipeline.ts +++ b/src-ts/proposalPipeline.ts @@ -96,7 +96,7 @@ export class StageTracker extends EventEmitter { while (polling) { try { const status = await this.stage.status(); - // if (currentStatus !== status) { + if (currentStatus !== status) { // emit an event if the status changes this.emit(TrackerEventName.TRACKER_STATUS, { status, @@ -110,7 +110,7 @@ export class StageTracker extends EventEmitter { this.stage instanceof GovernorQueueStage ? this.stage.description : undefined, }); currentStatus = status; - // } + } switch (status) { case ProposalStageStatus.TERMINATED: // end of the road From bd6dabaf8ed42f431dad37e0d140735a10d536e9 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Fri, 17 Nov 2023 10:45:44 +0100 Subject: [PATCH 24/24] Removed unused imports --- src-ts/proposalStage.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src-ts/proposalStage.ts b/src-ts/proposalStage.ts index 13be5ab9..0915c410 100644 --- a/src-ts/proposalStage.ts +++ b/src-ts/proposalStage.ts @@ -12,7 +12,7 @@ import { import { Outbox__factory } from "@arbitrum/sdk/dist/lib/abi/factories/Outbox__factory"; import { L2ToL1TxEvent as NitroL2ToL1TransactionEvent } from "@arbitrum/sdk/dist/lib/abi/ArbSys"; import { OutBoxTransactionExecutedEvent } from "@arbitrum/sdk/dist/lib/abi/Outbox"; -import { Log, TransactionReceipt } from "@ethersproject/providers"; +import { TransactionReceipt } from "@ethersproject/providers"; import { BigNumber, constants, ethers, providers, Signer } from "ethers"; import { defaultAbiCoder, hexDataLength, id, keccak256 } from "ethers/lib/utils"; import { @@ -24,7 +24,6 @@ import { ArbSys__factory, } from "../typechain-types"; import { Inbox__factory } from "@arbitrum/sdk/dist/lib/abi/factories/Inbox__factory"; -import { RollupUserLogic__factory } from "@arbitrum/sdk/dist/lib/abi/factories/RollupUserLogic__factory"; import { EventArgs } from "@arbitrum/sdk/dist/lib/dataEntities/event"; import { InboxMessageKind } from "@arbitrum/sdk/dist/lib/dataEntities/message"; import { SubmitRetryableMessageDataParser } from "@arbitrum/sdk/dist/lib/message/messageDataParser";