feat: add e2e tests for LineaRollup contract upgrade (#339)

* feat: add test for LineaRollup contract upgrade

* fix: add reinit event checks for the upgrade

* feat: add new test for roles assignments

* fix: add comment for fallback operator address
This commit is contained in:
Victorien Gauch
2024-11-27 10:58:21 +01:00
committed by GitHub
parent cdb74e1496
commit 0762dbb7c7
9 changed files with 332 additions and 92 deletions

View File

@@ -1,4 +1,5 @@
import { ethers } from "ethers";
import { generateKeccak256 } from "./utils";
export const ROLLING_HASH_UPDATED_EVENT_SIGNATURE =
"0xea3b023b4c8680d4b4824f0143132c95476359a2bb70a81d6c5a36f6918f6339";
@@ -9,3 +10,59 @@ export const MESSAGE_SENT_EVENT_SIGNATURE = "0xe856c2b8bd4eb0027ce32eeaf595c21b0
export const HASH_ZERO = ethers.ZeroHash;
export const TRANSACTION_CALLDATA_LIMIT = 30_000;
export const PAUSE_ALL_ROLE = generateKeccak256(["string"], ["PAUSE_ALL_ROLE"], true);
export const UNPAUSE_ALL_ROLE = generateKeccak256(["string"], ["UNPAUSE_ALL_ROLE"], true);
export const PAUSE_L1_L2_ROLE = generateKeccak256(["string"], ["PAUSE_L1_L2_ROLE"], true);
export const UNPAUSE_L1_L2_ROLE = generateKeccak256(["string"], ["UNPAUSE_L1_L2_ROLE"], true);
export const PAUSE_L2_L1_ROLE = generateKeccak256(["string"], ["PAUSE_L2_L1_ROLE"], true);
export const UNPAUSE_L2_L1_ROLE = generateKeccak256(["string"], ["UNPAUSE_L2_L1_ROLE"], true);
export const PAUSE_BLOB_SUBMISSION_ROLE = generateKeccak256(["string"], ["PAUSE_BLOB_SUBMISSION_ROLE"], true);
export const UNPAUSE_BLOB_SUBMISSION_ROLE = generateKeccak256(["string"], ["UNPAUSE_BLOB_SUBMISSION_ROLE"], true);
export const PAUSE_FINALIZATION_ROLE = generateKeccak256(["string"], ["PAUSE_FINALIZATION_ROLE"], true);
export const UNPAUSE_FINALIZATION_ROLE = generateKeccak256(["string"], ["UNPAUSE_FINALIZATION_ROLE"], true);
export const USED_RATE_LIMIT_RESETTER_ROLE = generateKeccak256(["string"], ["USED_RATE_LIMIT_RESETTER_ROLE"], true);
export const VERIFIER_UNSETTER_ROLE = generateKeccak256(["string"], ["VERIFIER_UNSETTER_ROLE"], true);
export const LINEA_ROLLUP_V6_ROLES = [
PAUSE_ALL_ROLE,
PAUSE_L1_L2_ROLE,
PAUSE_L2_L1_ROLE,
UNPAUSE_ALL_ROLE,
UNPAUSE_L1_L2_ROLE,
UNPAUSE_L2_L1_ROLE,
PAUSE_BLOB_SUBMISSION_ROLE,
UNPAUSE_BLOB_SUBMISSION_ROLE,
PAUSE_FINALIZATION_ROLE,
UNPAUSE_FINALIZATION_ROLE,
USED_RATE_LIMIT_RESETTER_ROLE,
VERIFIER_UNSETTER_ROLE,
];
export const GENERAL_PAUSE_TYPE = 1;
export const L1_L2_PAUSE_TYPE = 2;
export const L2_L1_PAUSE_TYPE = 3;
export const BLOB_SUBMISSION_PAUSE_TYPE = 4;
export const CALLDATA_SUBMISSION_PAUSE_TYPE = 5;
export const FINALIZATION_PAUSE_TYPE = 6;
export const BASE_PAUSE_TYPES_ROLES = [{ pauseType: GENERAL_PAUSE_TYPE, role: PAUSE_ALL_ROLE }];
export const BASE_UNPAUSE_TYPES_ROLES = [{ pauseType: GENERAL_PAUSE_TYPE, role: UNPAUSE_ALL_ROLE }];
export const LINEA_ROLLUP_PAUSE_TYPES_ROLES = [
...BASE_PAUSE_TYPES_ROLES,
{ pauseType: L1_L2_PAUSE_TYPE, role: PAUSE_L1_L2_ROLE },
{ pauseType: L2_L1_PAUSE_TYPE, role: PAUSE_L2_L1_ROLE },
{ pauseType: BLOB_SUBMISSION_PAUSE_TYPE, role: PAUSE_BLOB_SUBMISSION_ROLE },
{ pauseType: CALLDATA_SUBMISSION_PAUSE_TYPE, role: PAUSE_BLOB_SUBMISSION_ROLE },
{ pauseType: FINALIZATION_PAUSE_TYPE, role: PAUSE_FINALIZATION_ROLE },
];
export const LINEA_ROLLUP_UNPAUSE_TYPES_ROLES = [
...BASE_UNPAUSE_TYPES_ROLES,
{ pauseType: L1_L2_PAUSE_TYPE, role: UNPAUSE_L1_L2_ROLE },
{ pauseType: L2_L1_PAUSE_TYPE, role: UNPAUSE_L2_L1_ROLE },
{ pauseType: BLOB_SUBMISSION_PAUSE_TYPE, role: UNPAUSE_BLOB_SUBMISSION_ROLE },
{ pauseType: CALLDATA_SUBMISSION_PAUSE_TYPE, role: UNPAUSE_BLOB_SUBMISSION_ROLE },
{ pauseType: FINALIZATION_PAUSE_TYPE, role: UNPAUSE_FINALIZATION_ROLE },
];

View File

@@ -1,4 +1,4 @@
import { ContractTransactionReceipt, Wallet, ethers } from "ethers";
import { AbstractSigner, ContractTransactionReceipt, ethers } from "ethers";
import { ProxyAdmin, ProxyAdmin__factory } from "../typechain";
export function getInitializerData(
@@ -11,7 +11,7 @@ export function getInitializerData(
}
export async function upgradeContractAndCall(
deployer: Wallet,
deployer: AbstractSigner,
proxyAdminContractAddress: string,
proxyContractAddress: string,
implementationContractAddress: string,
@@ -24,7 +24,7 @@ export async function upgradeContractAndCall(
}
export async function upgradeContract(
deployer: Wallet,
deployer: AbstractSigner,
proxyAdminContractAddress: string,
proxyContractAddress: string,
implementationContractAddress: string,

View File

@@ -3,7 +3,7 @@ import assert from "assert";
import { AbstractSigner, BaseContract, BlockTag, TransactionReceipt, TransactionRequest, Wallet, ethers } from "ethers";
import path from "path";
import { exec } from "child_process";
import { L2MessageService, TokenBridge, LineaRollupV5 } from "../typechain";
import { L2MessageService, TokenBridge, LineaRollupV5, LineaRollupV6 } from "../typechain";
import { PayableOverrides, TypedContractEvent, TypedDeferredTopicFilter, TypedEventLog } from "../typechain/common";
import { MessageEvent, SendMessageArgs } from "./types";
@@ -148,7 +148,7 @@ export async function getBlockByNumberOrBlockTag(rpcUrl: URL, blockTag: BlockTag
}
export async function getEvents<
TContract extends LineaRollupV5 | L2MessageService | TokenBridge,
TContract extends LineaRollupV5 | LineaRollupV6 | L2MessageService | TokenBridge,
TEvent extends TypedContractEvent,
>(
contract: TContract,
@@ -171,7 +171,7 @@ export async function getEvents<
}
export async function waitForEvents<
TContract extends LineaRollupV5 | L2MessageService | TokenBridge,
TContract extends LineaRollupV5 | LineaRollupV6 | L2MessageService | TokenBridge,
TEvent extends TypedContractEvent,
>(
contract: TContract,
@@ -332,3 +332,49 @@ export async function execDockerCommand(command: string, containerName: string):
});
});
}
export function generateRoleAssignments(
roles: string[],
defaultAddress: string,
overrides: { role: string; addresses: string[] }[],
): { role: string; addressWithRole: string }[] {
const roleAssignments: { role: string; addressWithRole: string }[] = [];
const overridesMap = new Map<string, string[]>();
for (const override of overrides) {
overridesMap.set(override.role, override.addresses);
}
const allRolesSet = new Set<string>(roles);
for (const override of overrides) {
allRolesSet.add(override.role);
}
for (const role of allRolesSet) {
if (overridesMap.has(role)) {
const addresses = overridesMap.get(role);
if (addresses && addresses.length > 0) {
for (const addressWithRole of addresses) {
roleAssignments.push({ role, addressWithRole });
}
}
} else {
roleAssignments.push({ role, addressWithRole: defaultAddress });
}
}
return roleAssignments;
}
export function convertStringToPaddedHexBytes(strVal: string, paddedSize: number): string {
if (strVal.length > paddedSize) {
throw "Length is longer than padded size!";
}
const strBytes = ethers.toUtf8Bytes(strVal);
const bytes = ethers.zeroPadBytes(strBytes, paddedSize);
const bytes8Hex = ethers.hexlify(bytes);
return bytes8Hex;
}

View File

@@ -26,6 +26,7 @@ const config: Config = {
rpcUrl: L1_RPC_URL,
chainId: L1_CHAIN_ID,
lineaRollupAddress: "0x2A5CDCfc38856e2590E9Bd32F54Fa348e5De5f48",
lineaRollupProxyAdminAddress: "0x10b7ef80D4bA8df6b4Daed7B7638cd88C6d52F02",
tokenBridgeAddress: "",
l1TokenAddress: "",
accountManager: new EnvironmentBasedAccountManager(

View File

@@ -16,6 +16,7 @@ const config: Config = {
rpcUrl: L1_RPC_URL,
chainId: 31648428,
lineaRollupAddress: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
lineaRollupProxyAdminAddress: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0",
dummyContractAddress: "0x610178dA211FEF7D417bC0e6FeD39F05609AD788",
tokenBridgeAddress: "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6",
l1TokenAddress: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318",

View File

@@ -25,6 +25,7 @@ const config: Config = {
rpcUrl: L1_RPC_URL,
chainId: L1_CHAIN_ID,
lineaRollupAddress: "0xB218f8A4Bc926cF1cA7b3423c154a0D627Bdb7E5",
lineaRollupProxyAdminAddress: "0xa89E358Ef34921ebA90f328901B7381F86b1db52",
tokenBridgeAddress: "0x5A0a48389BB0f12E5e017116c1105da97E129142",
l1TokenAddress: "",
accountManager: new EnvironmentBasedAccountManager(

View File

@@ -9,6 +9,8 @@ import {
L2MessageService__factory,
LineaRollupV5,
LineaRollupV5__factory,
ProxyAdmin,
ProxyAdmin__factory,
TestContract,
TestContract__factory,
TestERC20,
@@ -92,6 +94,19 @@ export default class TestSetup {
return lineaRollup;
}
public getLineaRollupProxyAdminContract(signer?: AbstractSigner): ProxyAdmin {
const proxyAdmin: ProxyAdmin = ProxyAdmin__factory.connect(
this.config.L1.lineaRollupProxyAdminAddress,
this.getL1Provider(),
);
if (signer) {
return proxyAdmin.connect(signer);
}
return proxyAdmin;
}
public getL2MessageServiceContract(signer?: Wallet): L2MessageService {
const l2MessageService: L2MessageService = L2MessageService__factory.connect(
this.config.L2.l2MessageServiceAddress,

View File

@@ -9,6 +9,7 @@ export type BaseConfig = {
export type L1Config = BaseConfig & {
lineaRollupAddress: string;
lineaRollupProxyAdminAddress: string;
tokenBridgeAddress: string;
l1TokenAddress: string;
};

View File

@@ -7,8 +7,19 @@ import {
wait,
getBlockByNumberOrBlockTag,
etherToWei,
generateRoleAssignments,
convertStringToPaddedHexBytes,
getEvents,
} from "./common/utils";
import { config } from "./config/tests-config";
import { deployContract } from "./common/deployments";
import { LineaRollupV6__factory } from "./typechain";
import { getInitializerData, upgradeContractAndCall } from "./common/upgrades";
import {
LINEA_ROLLUP_PAUSE_TYPES_ROLES,
LINEA_ROLLUP_UNPAUSE_TYPES_ROLES,
LINEA_ROLLUP_V6_ROLES,
} from "./common/constants";
const l1AccountManager = config.getL1AccountManager();
@@ -53,110 +64,217 @@ describe("Submission and finalization test suite", () => {
return { l1Messages, l1Receipts };
};
it.concurrent(
"Check L2 anchoring",
async () => {
const lineaRollup = config.getLineaRollupContract();
const l2MessageService = config.getL2MessageServiceContract();
describe("Contracts v5", () => {
it.concurrent(
"Check L2 anchoring",
async () => {
const lineaRollup = config.getLineaRollupContract();
const l2MessageService = config.getL2MessageServiceContract();
const { l1Messages } = await sendMessages();
const { l1Messages } = await sendMessages();
// Wait for the last L1->L2 message to be anchored on L2
const lastNewL1MessageNumber = l1Messages.slice(-1)[0].messageNumber;
// Wait for the last L1->L2 message to be anchored on L2
const lastNewL1MessageNumber = l1Messages.slice(-1)[0].messageNumber;
console.log("Waiting for the anchoring using rolling hash...");
const [rollingHashUpdatedEvent] = await waitForEvents(
l2MessageService,
l2MessageService.filters.RollingHashUpdated(),
1_000,
0,
"latest",
async (events) => events.filter((event) => event.args.messageNumber >= lastNewL1MessageNumber),
console.log("Waiting for the anchoring using rolling hash...");
const [rollingHashUpdatedEvent] = await waitForEvents(
l2MessageService,
l2MessageService.filters.RollingHashUpdated(),
1_000,
0,
"latest",
async (events) => events.filter((event) => event.args.messageNumber >= lastNewL1MessageNumber),
);
const [lastNewMessageRollingHash, lastAnchoredL1MessageNumber] = await Promise.all([
lineaRollup.rollingHashes(rollingHashUpdatedEvent.args.messageNumber),
l2MessageService.lastAnchoredL1MessageNumber(),
]);
expect(lastNewMessageRollingHash).toEqual(rollingHashUpdatedEvent.args.rollingHash);
expect(lastAnchoredL1MessageNumber).toEqual(rollingHashUpdatedEvent.args.messageNumber);
console.log("New anchoring using rolling hash done.");
},
150_000,
);
it.concurrent(
"Check L1 data submission and finalization",
async () => {
const lineaRollup = config.getLineaRollupContract();
const [currentL2BlockNumber, startingRootHash] = await Promise.all([
lineaRollup.currentL2BlockNumber(),
lineaRollup.stateRootHashes(await lineaRollup.currentL2BlockNumber()),
]);
console.log("Waiting for data submission used to finalize with proof...");
// Waiting for data submission starting from migration block number
await waitForEvents(
lineaRollup,
lineaRollup.filters.DataSubmittedV2(undefined, currentL2BlockNumber + 1n),
1_000,
);
console.log("Waiting for the first DataFinalized event with proof...");
// Waiting for first DataFinalized event with proof
const [dataFinalizedEvent] = await waitForEvents(
lineaRollup,
lineaRollup.filters.DataFinalized(undefined, startingRootHash),
1_000,
);
const [lastBlockFinalized, newStateRootHash] = await Promise.all([
lineaRollup.currentL2BlockNumber(),
lineaRollup.stateRootHashes(dataFinalizedEvent.args.lastBlockFinalized),
]);
expect(lastBlockFinalized).toBeGreaterThanOrEqual(dataFinalizedEvent.args.lastBlockFinalized);
expect(newStateRootHash).toEqual(dataFinalizedEvent.args.finalRootHash);
expect(dataFinalizedEvent.args.withProof).toBeTruthy();
console.log("Finalization with proof done.");
},
150_000,
);
it.concurrent(
"Check L2 safe/finalized tag update on sequencer",
async () => {
const sequencerEndpoint = config.getSequencerEndpoint();
if (!sequencerEndpoint) {
console.log('Skipped the "Check L2 safe/finalized tag update on sequencer" test');
return;
}
const lastFinalizedL2BlockNumberOnL1 = 0;
console.log(`lastFinalizedL2BlockNumberOnL1=${lastFinalizedL2BlockNumberOnL1}`);
let safeL2BlockNumber = -1,
finalizedL2BlockNumber = -1;
while (
safeL2BlockNumber < lastFinalizedL2BlockNumberOnL1 ||
finalizedL2BlockNumber < lastFinalizedL2BlockNumberOnL1
) {
safeL2BlockNumber =
(await getBlockByNumberOrBlockTag(sequencerEndpoint, "safe"))?.number || safeL2BlockNumber;
finalizedL2BlockNumber =
(await getBlockByNumberOrBlockTag(sequencerEndpoint, "finalized"))?.number || finalizedL2BlockNumber;
await wait(1_000);
}
console.log(`safeL2BlockNumber=${safeL2BlockNumber} finalizedL2BlockNumber=${finalizedL2BlockNumber}`);
expect(safeL2BlockNumber).toBeGreaterThanOrEqual(lastFinalizedL2BlockNumberOnL1);
expect(finalizedL2BlockNumber).toBeGreaterThanOrEqual(lastFinalizedL2BlockNumberOnL1);
console.log("L2 safe/finalized tag update on sequencer done.");
},
150_000,
);
});
describe("LineaRollup v6 upgrade", () => {
// note: we cannot move the chain forward by 6 months, so in theory, this could be any address for e2e test purposes
const fallbackoperatorAddress = "0xcA11bde05977b3631167028862bE2a173976CA11";
beforeAll(async () => {
const l1DeployerAccount = l1AccountManager.whaleAccount(0);
const l1SecurityCouncil = l1AccountManager.whaleAccount(3);
const l1Provider = config.getL1Provider();
const proxyAdmin = config.getLineaRollupProxyAdminContract(l1DeployerAccount);
console.log("Deploying LineaRollup v6 implementation contract...");
const { maxFeePerGas, maxPriorityFeePerGas } = await l1Provider.getFeeData();
const lineaRollupV6Implementation = await deployContract(new LineaRollupV6__factory(), l1DeployerAccount, [
{ maxPriorityFeePerGas, maxFeePerGas },
]);
console.log("Upgrading LineaRollup contract to V6...");
const newRoleAddresses = generateRoleAssignments(LINEA_ROLLUP_V6_ROLES, await l1SecurityCouncil.getAddress(), []);
const initializerData = getInitializerData(
LineaRollupV6__factory.createInterface(),
"reinitializeLineaRollupV6",
[newRoleAddresses, LINEA_ROLLUP_PAUSE_TYPES_ROLES, LINEA_ROLLUP_UNPAUSE_TYPES_ROLES, fallbackoperatorAddress],
);
const [lastNewMessageRollingHash, lastAnchoredL1MessageNumber] = await Promise.all([
lineaRollup.rollingHashes(rollingHashUpdatedEvent.args.messageNumber),
l2MessageService.lastAnchoredL1MessageNumber(),
]);
expect(lastNewMessageRollingHash).toEqual(rollingHashUpdatedEvent.args.rollingHash);
expect(lastAnchoredL1MessageNumber).toEqual(rollingHashUpdatedEvent.args.messageNumber);
await upgradeContractAndCall(
l1DeployerAccount,
await proxyAdmin.getAddress(),
await config.getLineaRollupContract().getAddress(),
await lineaRollupV6Implementation.getAddress(),
initializerData,
);
});
console.log("New anchoring using rolling hash done.");
},
150_000,
);
it("Check LineaRollupVersionChanged and FallbackOperatorAddressSet events are emitted", async () => {
const lineaRollupAddress = await config.getLineaRollupContract().getAddress();
const lineaRollupV6 = LineaRollupV6__factory.connect(lineaRollupAddress, config.getL1Provider());
it.concurrent(
"Check L1 data submission and finalization",
async () => {
const lineaRollup = config.getLineaRollupContract();
const [currentL2BlockNumber, startingRootHash] = await Promise.all([
lineaRollup.currentL2BlockNumber(),
lineaRollup.stateRootHashes(await lineaRollup.currentL2BlockNumber()),
]);
console.log("Waiting for data submission used to finalize with proof...");
// Waiting for data submission starting from migration block number
console.log("Waiting for FallbackOperatorAddressSet event...");
await waitForEvents(
lineaRollup,
lineaRollup.filters.DataSubmittedV2(undefined, currentL2BlockNumber + 1n),
lineaRollupV6,
lineaRollupV6.filters.FallbackOperatorAddressSet(undefined, fallbackoperatorAddress),
1_000,
);
console.log("Waiting for the first DataFinalized event with proof...");
// Waiting for first DataFinalized event with proof
console.log("Waiting for LineaRollupVersionChanged event...");
const expectedVersion5Bytes8 = convertStringToPaddedHexBytes("5.0", 8);
const expectedVersion6Bytes8 = convertStringToPaddedHexBytes("6.0", 8);
await waitForEvents(
lineaRollupV6,
lineaRollupV6.filters.LineaRollupVersionChanged(expectedVersion5Bytes8, expectedVersion6Bytes8),
1_000,
);
});
it("Check all new roles have been granted and pauseTypes/unpauseTypes are assigned to specific roles", async () => {
const lineaRollupAddress = await config.getLineaRollupContract().getAddress();
const l1SecurityCouncilAddress = await l1AccountManager.whaleAccount(3).getAddress();
const lineaRollupV6 = LineaRollupV6__factory.connect(lineaRollupAddress, config.getL1Provider());
for (const role of LINEA_ROLLUP_V6_ROLES) {
const hasRole = await lineaRollupV6.hasRole(role, l1SecurityCouncilAddress);
expect(hasRole).toBeTruthy();
}
const [pauseTypeRoleSetEvents, unPauseTypeRoleSetEvents] = await Promise.all([
getEvents(lineaRollupV6, lineaRollupV6.filters.PauseTypeRoleSet()),
getEvents(lineaRollupV6, lineaRollupV6.filters.UnPauseTypeRoleSet()),
]);
expect(pauseTypeRoleSetEvents.length).toEqual(LINEA_ROLLUP_PAUSE_TYPES_ROLES.length);
expect(unPauseTypeRoleSetEvents.length).toEqual(LINEA_ROLLUP_UNPAUSE_TYPES_ROLES.length);
});
it("Check L1 data submission and finalization", async () => {
const lineaRollupAddress = await config.getLineaRollupContract().getAddress();
const lineaRollupV6 = LineaRollupV6__factory.connect(lineaRollupAddress, config.getL1Provider());
const currentL2BlockNumber = await lineaRollupV6.currentL2BlockNumber();
console.log("Waiting for DataSubmittedV3 used to finalize with proof...");
await waitForEvents(lineaRollupV6, lineaRollupV6.filters.DataSubmittedV3(), 1_000);
console.log("Waiting for the first DataFinalizedV3 event with proof...");
const [dataFinalizedEvent] = await waitForEvents(
lineaRollup,
lineaRollup.filters.DataFinalized(undefined, startingRootHash),
lineaRollupV6,
lineaRollupV6.filters.DataFinalizedV3(currentL2BlockNumber + 1n),
1_000,
);
const [lastBlockFinalized, newStateRootHash] = await Promise.all([
lineaRollup.currentL2BlockNumber(),
lineaRollup.stateRootHashes(dataFinalizedEvent.args.lastBlockFinalized),
lineaRollupV6.currentL2BlockNumber(),
lineaRollupV6.stateRootHashes(dataFinalizedEvent.args.endBlockNumber),
]);
expect(lastBlockFinalized).toBeGreaterThanOrEqual(dataFinalizedEvent.args.lastBlockFinalized);
expect(newStateRootHash).toEqual(dataFinalizedEvent.args.finalRootHash);
expect(dataFinalizedEvent.args.withProof).toBeTruthy();
expect(lastBlockFinalized).toBeGreaterThanOrEqual(dataFinalizedEvent.args.endBlockNumber);
expect(newStateRootHash).toEqual(dataFinalizedEvent.args.finalStateRootHash);
console.log("Finalization with proof done.");
},
150_000,
);
it.concurrent(
"Check L2 safe/finalized tag update on sequencer",
async () => {
const sequencerEndpoint = config.getSequencerEndpoint();
if (!sequencerEndpoint) {
console.log('Skipped the "Check L2 safe/finalized tag update on sequencer" test');
return;
}
const lastFinalizedL2BlockNumberOnL1 = 0;
console.log(`lastFinalizedL2BlockNumberOnL1=${lastFinalizedL2BlockNumberOnL1}`);
let safeL2BlockNumber = -1,
finalizedL2BlockNumber = -1;
while (
safeL2BlockNumber < lastFinalizedL2BlockNumberOnL1 ||
finalizedL2BlockNumber < lastFinalizedL2BlockNumberOnL1
) {
safeL2BlockNumber = (await getBlockByNumberOrBlockTag(sequencerEndpoint, "safe"))?.number || safeL2BlockNumber;
finalizedL2BlockNumber =
(await getBlockByNumberOrBlockTag(sequencerEndpoint, "finalized"))?.number || finalizedL2BlockNumber;
await wait(1_000);
}
console.log(`safeL2BlockNumber=${safeL2BlockNumber} finalizedL2BlockNumber=${finalizedL2BlockNumber}`);
expect(safeL2BlockNumber).toBeGreaterThanOrEqual(lastFinalizedL2BlockNumberOnL1);
expect(finalizedL2BlockNumber).toBeGreaterThanOrEqual(lastFinalizedL2BlockNumberOnL1);
console.log("L2 safe/finalized tag update on sequencer done.");
},
150_000,
);
}, 150_000);
});
});