mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-08 19:58:01 -05:00
feat: add send-bundle e2e test case with workaround to support traces… (#771)
* feat: add send-bundle e2e test case with workaround to support traces-v1 sequencer * feat: linting and removed io.consensys in maven gradle * feat: RPC besu node to forward sendBundle to sequencer in e2e * feat: update besu nodes to devnet-9d6e914 and coordinator version update * feat: update coordinator version and l2-node-besu plugins config * feat: remove gas-limit e2e tests as already moved to 2b block gas limit for all envs * feat: remove opcode test contract related variables * feat: update coordinator version * feat: update linea-besu-package version * feat: revise test cases based on review comment * feat: skip send bundle tests explicitly if traces-v1 * feat: always pass send bundle tests if traces-v1 * feat: remove unused helper function * feat: skip send bundle tests explicitly if traces-v1 * feat: update to use l2-node-besu log4j.xml in l2-node-besu * feat: use describe.skip instead of it.skip for skipping bundle tests
This commit is contained in:
@@ -23,7 +23,6 @@ repositories {
|
||||
maven {
|
||||
url "https://artifacts.consensys.net/public/linea-besu/maven/"
|
||||
content {
|
||||
includeGroupAndSubgroups('io.consensys')
|
||||
includeGroupAndSubgroups('org.hyperledger')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ services:
|
||||
l1-el-node:
|
||||
container_name: l1-el-node
|
||||
hostname: l1-el-node
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-02eb1e1}
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-aa00a36}
|
||||
profiles: [ "l1", "debug", "external-to-monorepo" ]
|
||||
depends_on:
|
||||
l1-node-genesis-generator:
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
sequencer:
|
||||
hostname: sequencer
|
||||
container_name: sequencer
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-02eb1e1}
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-aa00a36}
|
||||
profiles: [ "l2", "l2-bc", "debug", "external-to-monorepo" ]
|
||||
ports:
|
||||
- "8545:8545"
|
||||
@@ -82,7 +82,7 @@ services:
|
||||
l2-node-besu:
|
||||
hostname: l2-node-besu
|
||||
container_name: l2-node-besu
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-02eb1e1}
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-aa00a36}
|
||||
profiles: [ "l2", "l2-bc", "debug", "external-to-monorepo" ]
|
||||
depends_on:
|
||||
sequencer:
|
||||
@@ -162,7 +162,7 @@ services:
|
||||
traces-node-v2:
|
||||
hostname: traces-node-v2
|
||||
container_name: traces-node-v2
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-02eb1e1}
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-aa00a36}
|
||||
profiles: [ "l2", "l2-bc", "debug", "external-to-monorepo" ]
|
||||
depends_on:
|
||||
sequencer:
|
||||
@@ -226,7 +226,7 @@ services:
|
||||
prover-v3: # prover compatible with the traces from zkbesu
|
||||
container_name: prover-v3
|
||||
hostname: prover-v3
|
||||
image: consensys/linea-prover:${PROVER_TAG:-cd7228e}
|
||||
image: consensys/linea-prover:${PROVER_TAG:-811743b}
|
||||
platform: linux/amd64
|
||||
# to avoid spinning up on CI for now
|
||||
profiles: [ "l2" ]
|
||||
@@ -247,7 +247,7 @@ services:
|
||||
postman:
|
||||
container_name: postman
|
||||
hostname: postman
|
||||
image: consensys/linea-postman:${POSTMAN_TAG:-cd7228e}
|
||||
image: consensys/linea-postman:${POSTMAN_TAG:-811743b}
|
||||
profiles: [ "l2", "debug" ]
|
||||
platform: linux/amd64
|
||||
restart: on-failure
|
||||
@@ -266,7 +266,7 @@ services:
|
||||
traces-api:
|
||||
hostname: traces-api
|
||||
container_name: traces-api
|
||||
image: consensys/linea-traces-api-facade:${TRACES_API_TAG:-cd7228e}
|
||||
image: consensys/linea-traces-api-facade:${TRACES_API_TAG:-811743b}
|
||||
profiles: [ "l2", "debug" ]
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
@@ -287,7 +287,7 @@ services:
|
||||
coordinator:
|
||||
hostname: coordinator
|
||||
container_name: coordinator
|
||||
image: consensys/linea-coordinator:${COORDINATOR_TAG:-29db47d}
|
||||
image: consensys/linea-coordinator:${COORDINATOR_TAG:-811743b}
|
||||
platform: linux/amd64
|
||||
profiles: [ "l2", "debug" ]
|
||||
depends_on:
|
||||
@@ -367,7 +367,7 @@ services:
|
||||
- l1network
|
||||
|
||||
zkbesu-shomei:
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-02eb1e1}
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-aa00a36}
|
||||
hostname: zkbesu-shomei
|
||||
container_name: zkbesu-shomei
|
||||
profiles: [ "l2", "l2-bc", "external-to-monorepo" ]
|
||||
@@ -490,7 +490,7 @@ services:
|
||||
transaction-exclusion-api:
|
||||
hostname: transaction-exclusion-api
|
||||
container_name: transaction-exclusion-api
|
||||
image: consensys/linea-transaction-exclusion-api:${TRANSACTION_EXCLUSION_API_TAG:-cd7228e}
|
||||
image: consensys/linea-transaction-exclusion-api:${TRANSACTION_EXCLUSION_API_TAG:-811743b}
|
||||
profiles: [ "l2", "debug" ]
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
@@ -583,7 +583,7 @@ services:
|
||||
ipv4_address: 10.10.10.205
|
||||
|
||||
zkbesu-shomei-sr:
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-02eb1e1}
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-devnet-aa00a36}
|
||||
hostname: zkbesu-shomei-sr
|
||||
container_name: zkbesu-shomei-sr
|
||||
profiles: [ "external-to-monorepo", "staterecovery" ]
|
||||
|
||||
@@ -12,6 +12,8 @@ services:
|
||||
file: compose-spec-l2-services.yml
|
||||
service: l2-node-besu
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-mainnet-402ebda}
|
||||
environment:
|
||||
BESU_PLUGINS: "LineaEstimateGasEndpointPlugin,LineaL1FinalizationTagUpdaterPlugin,LineaExtraDataPlugin,LineaTransactionPoolValidatorPlugin"
|
||||
volumes:
|
||||
- ../config/common/traces-limits-besu-v1.toml:/var/lib/besu/traces-limits.toml:ro
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ services:
|
||||
file: compose-spec-l2-services.yml
|
||||
service: sequencer
|
||||
image: consensys/linea-besu-package:${BESU_PACKAGE_TAG:-mainnet-402ebda}
|
||||
environment:
|
||||
BESU_PLUGINS: "LineaEstimateGasEndpointPlugin,LineaL1FinalizationTagUpdaterPlugin,LineaExtraDataPlugin,LineaTransactionPoolValidatorPlugin"
|
||||
volumes:
|
||||
- ../config/common/traces-limits-besu-v1.toml:/var/lib/besu/traces-limits.toml:ro
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ services:
|
||||
extends:
|
||||
file: compose-spec-l2-services.yml
|
||||
service: l2-node-besu
|
||||
environment:
|
||||
BESU_PLUGIN_LINEA_BUNDLES_FORWARD_URLS: "http://sequencer:8545"
|
||||
BESU_PLUGIN_LINEA_BUNDLES_FORWARD_RETRY_DELAY: 1000
|
||||
BESU_PLUGIN_LINEA_BUNDLES_FORWARD_TIMEOUT: 5000
|
||||
volumes:
|
||||
- ../config/common/traces-limits-besu-v2.toml:/var/lib/besu/traces-limits.toml:ro
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ metrics-port=9545
|
||||
data-storage-format="BONSAI"
|
||||
|
||||
# plugins
|
||||
plugins=["LineaEstimateGasEndpointPlugin","LineaL1FinalizationTagUpdaterPlugin","LineaExtraDataPlugin", "LineaTransactionPoolValidatorPlugin"]
|
||||
plugins=["LineaEstimateGasEndpointPlugin","LineaL1FinalizationTagUpdaterPlugin","LineaExtraDataPlugin","LineaTransactionPoolValidatorPlugin","LineaBundleEndpointsPlugin","ForwardBundlesPlugin"]
|
||||
plugin-linea-module-limit-file-path="/var/lib/besu/traces-limits.toml"
|
||||
plugin-linea-deny-list-path="/var/lib/besu/deny-list.txt"
|
||||
plugin-linea-l1l2-bridge-contract="0xe537D669CA013d86EBeF1D64e40fC74CADC91987"
|
||||
|
||||
@@ -28,7 +28,7 @@ rpc-http-max-active-connections=20000
|
||||
rpc-ws-enabled=true
|
||||
rpc-ws-host="0.0.0.0"
|
||||
rpc-ws-port=8546
|
||||
rpc-ws-api=["ADMIN","DEBUG","NET","ETH","CLIQUE","MINER","WEB3","TRACE"]
|
||||
rpc-ws-api=["ADMIN","DEBUG","NET","ETH","CLIQUE","MINER","WEB3","TRACE","LINEA"]
|
||||
rpc-ws-max-active-connections=200
|
||||
|
||||
# graphql
|
||||
@@ -47,7 +47,7 @@ api-gas-and-priority-fee-upper-bound-coefficient=300
|
||||
poa-block-txs-selection-max-time=1000
|
||||
|
||||
# plugins
|
||||
plugins=["LineaEstimateGasEndpointPlugin","LineaL1FinalizationTagUpdaterPlugin","LineaExtraDataPlugin","LineaTransactionPoolValidatorPlugin"]
|
||||
plugins=["LineaEstimateGasEndpointPlugin","LineaL1FinalizationTagUpdaterPlugin","LineaExtraDataPlugin","LineaTransactionPoolValidatorPlugin","LineaBundleEndpointsPlugin","LineaSetExtraDataEndpointPlugin","LineaTransactionSelectorPlugin"]
|
||||
plugin-linea-module-limit-file-path="/var/lib/besu/traces-limits.toml"
|
||||
plugin-linea-deny-list-path="/var/lib/besu/deny-list.txt"
|
||||
plugin-linea-estimate-gas-compatibility-mode-enabled=false
|
||||
|
||||
@@ -4,15 +4,10 @@ import { AbstractSigner, BaseContract, BlockTag, TransactionReceipt, Transaction
|
||||
import path from "path";
|
||||
import { exec } from "child_process";
|
||||
import { L2MessageServiceV1 as L2MessageService, TokenBridgeV1_1 as TokenBridge, LineaRollupV6 } from "../typechain";
|
||||
import {
|
||||
PayableOverrides,
|
||||
TypedContractEvent,
|
||||
TypedDeferredTopicFilter,
|
||||
TypedEventLog,
|
||||
TypedContractMethod,
|
||||
} from "../typechain/common";
|
||||
import { PayableOverrides, TypedContractEvent, TypedDeferredTopicFilter, TypedEventLog } from "../typechain/common";
|
||||
import { MessageEvent, SendMessageArgs } from "./types";
|
||||
import { createTestLogger } from "../config/logger";
|
||||
import { randomUUID, randomInt } from "crypto";
|
||||
|
||||
const logger = createTestLogger();
|
||||
|
||||
@@ -57,6 +52,64 @@ export const encodeData = (types: string[], values: unknown[], packed?: boolean)
|
||||
return ethers.AbiCoder.defaultAbiCoder().encode(types, values);
|
||||
};
|
||||
|
||||
export async function isSendBundleMethodNotFound(rpcEndpoint: URL, targetBlockNumber = "0xffff") {
|
||||
const lineaSendBundleClient = new LineaBundleClient(rpcEndpoint);
|
||||
try {
|
||||
await lineaSendBundleClient.lineaSendBundle([], generateRandomUUIDv4(), targetBlockNumber);
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
if (err.message === "Method not found") {
|
||||
// Bundle request doesn't support in traces-v1 besu nodes
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function generateRandomInt(max = 1000): number {
|
||||
return randomInt(max);
|
||||
}
|
||||
|
||||
export function generateRandomUUIDv4(): string {
|
||||
return randomUUID();
|
||||
}
|
||||
|
||||
async function awaitUntil<T>(
|
||||
callback: () => Promise<T>,
|
||||
stopRetry: (a: T) => boolean,
|
||||
pollingIntervalMs: number = 500,
|
||||
timeoutMs: number = 2 * 60 * 1000,
|
||||
): Promise<T | null> {
|
||||
let isExceedTimeOut = false;
|
||||
setTimeout(() => {
|
||||
isExceedTimeOut = true;
|
||||
}, timeoutMs);
|
||||
|
||||
while (!isExceedTimeOut) {
|
||||
const result = await callback();
|
||||
if (stopRetry(result)) return result;
|
||||
await wait(pollingIntervalMs);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function pollForBlockNumber(
|
||||
provider: ethers.JsonRpcProvider,
|
||||
expectedBlockNumber: number,
|
||||
pollingIntervalMs: number = 500,
|
||||
timeoutMs: number = 2 * 60 * 1000,
|
||||
): Promise<boolean> {
|
||||
return (
|
||||
(await awaitUntil(
|
||||
async () => await provider.getBlockNumber(),
|
||||
(a: number) => a >= expectedBlockNumber,
|
||||
pollingIntervalMs,
|
||||
timeoutMs,
|
||||
)) != null
|
||||
);
|
||||
}
|
||||
|
||||
export class RollupGetZkEVMBlockNumberClient {
|
||||
private endpoint: URL;
|
||||
private request = {
|
||||
@@ -65,7 +118,7 @@ export class RollupGetZkEVMBlockNumberClient {
|
||||
jsonrpc: "2.0",
|
||||
method: "rollup_getZkEVMBlockNumber",
|
||||
params: [],
|
||||
id: 1,
|
||||
id: generateRandomInt(),
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -107,7 +160,7 @@ export class LineaEstimateGasClient {
|
||||
value,
|
||||
},
|
||||
],
|
||||
id: 1,
|
||||
id: generateRandomInt(),
|
||||
}),
|
||||
};
|
||||
const response = await fetch(this.endpoint, request);
|
||||
@@ -121,6 +174,64 @@ export class LineaEstimateGasClient {
|
||||
}
|
||||
}
|
||||
|
||||
export class LineaBundleClient {
|
||||
private endpoint: URL;
|
||||
|
||||
public constructor(endpoint: URL) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public async lineaSendBundle(
|
||||
txs: string[],
|
||||
replacementUUID: string,
|
||||
blockNumber: string,
|
||||
): Promise<{ bundleHash: string }> {
|
||||
const request = {
|
||||
method: "post",
|
||||
body: JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
method: "linea_sendBundle",
|
||||
params: [
|
||||
{
|
||||
txs,
|
||||
replacementUUID,
|
||||
blockNumber,
|
||||
},
|
||||
],
|
||||
id: generateRandomInt(),
|
||||
}),
|
||||
};
|
||||
const response = await fetch(this.endpoint, request);
|
||||
const responseJson = await response.json();
|
||||
if (responseJson.error?.code === -32601 && responseJson.error?.message === "Method not found") {
|
||||
throw Error("Method not found");
|
||||
}
|
||||
assert("result" in responseJson);
|
||||
return {
|
||||
bundleHash: responseJson.result.bundleHash,
|
||||
};
|
||||
}
|
||||
|
||||
public async lineaCancelBundle(replacementUUID: string): Promise<boolean> {
|
||||
const request = {
|
||||
method: "post",
|
||||
body: JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
method: "linea_cancelBundle",
|
||||
params: [replacementUUID],
|
||||
id: generateRandomInt(),
|
||||
}),
|
||||
};
|
||||
const response = await fetch(this.endpoint, request);
|
||||
const responseJson = await response.json();
|
||||
if (responseJson.error?.code === -32601 && responseJson.error?.message === "Method not found") {
|
||||
throw Error("Method not found");
|
||||
}
|
||||
assert("result" in responseJson);
|
||||
return responseJson.result;
|
||||
}
|
||||
}
|
||||
|
||||
export class TransactionExclusionClient {
|
||||
private endpoint: URL;
|
||||
|
||||
@@ -136,7 +247,7 @@ export class TransactionExclusionClient {
|
||||
jsonrpc: "2.0",
|
||||
method: "linea_getTransactionExclusionStatusV1",
|
||||
params: [txHash],
|
||||
id: 1,
|
||||
id: generateRandomInt(),
|
||||
}),
|
||||
};
|
||||
const response = await fetch(this.endpoint, request);
|
||||
@@ -172,7 +283,7 @@ export class TransactionExclusionClient {
|
||||
jsonrpc: "2.0",
|
||||
method: "linea_saveRejectedTransactionV1",
|
||||
params: params,
|
||||
id: 1,
|
||||
id: generateRandomInt(),
|
||||
}),
|
||||
};
|
||||
const response = await fetch(this.endpoint, request);
|
||||
@@ -180,9 +291,13 @@ export class TransactionExclusionClient {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTransactionHash(txRequest: TransactionRequest, signer: Wallet): Promise<string> {
|
||||
export async function getRawTransactionHex(txRequest: TransactionRequest, signer: Wallet): Promise<string> {
|
||||
const rawTransaction = await signer.populateTransaction(txRequest);
|
||||
const signature = await signer.signTransaction(rawTransaction);
|
||||
return await signer.signTransaction(rawTransaction);
|
||||
}
|
||||
|
||||
export async function getTransactionHash(txRequest: TransactionRequest, signer: Wallet): Promise<string> {
|
||||
const signature = await getRawTransactionHex(txRequest, signer);
|
||||
return ethers.keccak256(signature);
|
||||
}
|
||||
|
||||
@@ -225,57 +340,18 @@ export async function waitForEvents<
|
||||
>(
|
||||
contract: TContract,
|
||||
eventFilter: TypedDeferredTopicFilter<TEvent>,
|
||||
pollingInterval: number = 500,
|
||||
pollingIntervalMs: number = 500,
|
||||
fromBlock?: BlockTag,
|
||||
toBlock?: BlockTag,
|
||||
criteria?: (events: TypedEventLog<TEvent>[]) => Promise<TypedEventLog<TEvent>[]>,
|
||||
): Promise<TypedEventLog<TEvent>[]> {
|
||||
let events = await getEvents(contract, eventFilter, fromBlock, toBlock, criteria);
|
||||
|
||||
while (events.length === 0) {
|
||||
events = await getEvents(contract, eventFilter, fromBlock, toBlock, criteria);
|
||||
await wait(pollingInterval);
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
// Currently only handle simple single return types - uint256 | bytesX | string | bool
|
||||
export async function pollForContractMethodReturnValue<
|
||||
ExpectedReturnType extends bigint | string | boolean,
|
||||
R extends [ExpectedReturnType],
|
||||
>(
|
||||
method: TypedContractMethod<[], R, "view">,
|
||||
expectedReturnValue: ExpectedReturnType,
|
||||
compareFunction: (a: ExpectedReturnType, b: ExpectedReturnType) => boolean = (a, b) => a === b,
|
||||
pollingInterval: number = 500,
|
||||
timeout: number = 2 * 60 * 1000,
|
||||
): Promise<boolean> {
|
||||
let isExceedTimeOut = false;
|
||||
setTimeout(() => {
|
||||
isExceedTimeOut = true;
|
||||
}, timeout);
|
||||
|
||||
while (!isExceedTimeOut) {
|
||||
const returnValue = await method();
|
||||
if (compareFunction(returnValue, expectedReturnValue)) return true;
|
||||
await wait(pollingInterval);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Currently only handle single uint256 return type
|
||||
export async function pollForContractMethodReturnValueExceedTarget<
|
||||
ExpectedReturnType extends bigint,
|
||||
R extends [ExpectedReturnType],
|
||||
>(
|
||||
method: TypedContractMethod<[], R, "view">,
|
||||
targetReturnValue: ExpectedReturnType,
|
||||
pollingInterval: number = 500,
|
||||
timeout: number = 2 * 60 * 1000,
|
||||
): Promise<boolean> {
|
||||
return pollForContractMethodReturnValue(method, targetReturnValue, (a, b) => a >= b, pollingInterval, timeout);
|
||||
return (
|
||||
(await awaitUntil(
|
||||
async () => await getEvents(contract, eventFilter, fromBlock, toBlock, criteria),
|
||||
(a: TypedEventLog<TEvent>[]) => a.length > 0,
|
||||
pollingIntervalMs,
|
||||
)) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
export function getFiles(directory: string, fileRegex: RegExp[]): string[] {
|
||||
@@ -284,36 +360,6 @@ export function getFiles(directory: string, fileRegex: RegExp[]): string[] {
|
||||
return filteredFiles.map((file) => fs.readFileSync(path.join(directory, file.name), "utf-8"));
|
||||
}
|
||||
|
||||
export async function waitForFile(
|
||||
directory: string,
|
||||
regex: RegExp,
|
||||
pollingInterval: number,
|
||||
timeout: number,
|
||||
criteria?: (fileName: string) => boolean,
|
||||
): Promise<string> {
|
||||
const endTime = Date.now() + timeout;
|
||||
|
||||
while (Date.now() < endTime) {
|
||||
try {
|
||||
const files = fs.readdirSync(directory);
|
||||
|
||||
for (const file of files) {
|
||||
if (regex.test(file) && (!criteria || criteria(file))) {
|
||||
const filePath = path.join(directory, file);
|
||||
const content = fs.readFileSync(filePath, "utf-8");
|
||||
return content;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Error reading directory: ${(err as Error).message}`);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, pollingInterval));
|
||||
}
|
||||
|
||||
throw new Error("File check timed out");
|
||||
}
|
||||
|
||||
export async function sendTransactionsToGenerateTrafficWithInterval(
|
||||
signer: AbstractSigner,
|
||||
pollingInterval: number = 1_000,
|
||||
|
||||
1
e2e/src/config/global.d.ts
vendored
1
e2e/src/config/global.d.ts
vendored
@@ -4,6 +4,7 @@ import { Logger } from "winston";
|
||||
|
||||
declare global {
|
||||
var stopL2TrafficGeneration: () => void;
|
||||
var shouldSkipBundleTests: boolean;
|
||||
var logger: Logger;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
import { ethers } from "ethers";
|
||||
import { config } from "../tests-config";
|
||||
import { deployContract } from "../../common/deployments";
|
||||
import { DummyContract__factory, TestContract__factory, OpcodeTestContract__factory } from "../../typechain";
|
||||
import { etherToWei, sendTransactionsToGenerateTrafficWithInterval } from "../../common/utils";
|
||||
import { DummyContract__factory, TestContract__factory } from "../../typechain";
|
||||
import {
|
||||
etherToWei,
|
||||
isSendBundleMethodNotFound,
|
||||
sendTransactionsToGenerateTrafficWithInterval,
|
||||
} from "../../common/utils";
|
||||
import { EMPTY_CONTRACT_CODE } from "../../common/constants";
|
||||
import { createTestLogger } from "../logger";
|
||||
|
||||
@@ -18,6 +22,8 @@ export default async (): Promise<void> => {
|
||||
await configureOnceOffPrerequisities();
|
||||
}
|
||||
|
||||
process.env.SHOULD_SKIP_BUNDLE_TESTS = (await isSendBundleMethodNotFound(config.getL2BesuNodeEndpoint()!)).toString();
|
||||
|
||||
logger.info("Generating L2 traffic...");
|
||||
const pollingAccount = await config.getL2AccountManager().generateAccount(etherToWei("200"));
|
||||
const stopPolling = await sendTransactionsToGenerateTrafficWithInterval(pollingAccount, 2_000);
|
||||
@@ -36,11 +42,10 @@ async function configureOnceOffPrerequisities() {
|
||||
const to = "0x8D97689C9818892B700e27F316cc3E41e17fBeb9";
|
||||
const calldata = "0x";
|
||||
|
||||
const [dummyContract, l2DummyContract, l2TestContract, opcodeTestContract] = await Promise.all([
|
||||
const [dummyContract, l2DummyContract, l2TestContract] = await Promise.all([
|
||||
deployContract(new DummyContract__factory(), account, [{ nonce: l1AccountNonce }]),
|
||||
deployContract(new DummyContract__factory(), l2Account, [{ nonce: l2AccountNonce }]),
|
||||
deployContract(new TestContract__factory(), l2Account, [{ nonce: l2AccountNonce + 1 }]),
|
||||
deployContract(new OpcodeTestContract__factory(), l2Account, [{ nonce: l2AccountNonce + 2 }]),
|
||||
|
||||
// Send ETH to the LineaRollup contract
|
||||
(
|
||||
@@ -55,5 +60,4 @@ async function configureOnceOffPrerequisities() {
|
||||
logger.info(`L1 Dummy contract deployed. address=${await dummyContract.getAddress()}`);
|
||||
logger.info(`L2 Dummy contract deployed. address=${await l2DummyContract.getAddress()}`);
|
||||
logger.info(`L2 Test contract deployed. address=${await l2TestContract.getAddress()}`);
|
||||
logger.info(`L2 OpcodeTest contract deployed. address=${await opcodeTestContract.getAddress()}`);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createTestLogger } from "../logger";
|
||||
|
||||
global.logger = createTestLogger();
|
||||
global.shouldSkipBundleTests = process.env.SHOULD_SKIP_BUNDLE_TESTS === "true";
|
||||
|
||||
@@ -49,7 +49,6 @@ const config: Config = {
|
||||
L2_CHAIN_ID,
|
||||
),
|
||||
dummyContractAddress: "",
|
||||
opcodeTestContractAddress: "",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -50,8 +50,6 @@ const config: Config = {
|
||||
shomeiFrontendEndpoint: SHOMEI_FRONTEND_ENDPOINT,
|
||||
sequencerEndpoint: SEQUENCER_ENDPOINT,
|
||||
transactionExclusionEndpoint: TRANSACTION_EXCLUSION_ENDPOINT,
|
||||
// Nonce 11
|
||||
opcodeTestContractAddress: "0xFCc2155b495B6Bf6701eb322D3a97b7817898306",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ const config: Config = {
|
||||
L2_CHAIN_ID,
|
||||
),
|
||||
dummyContractAddress: "",
|
||||
opcodeTestContractAddress: "",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@ import {
|
||||
L2MessageServiceV1__factory as L2MessageService__factory,
|
||||
LineaRollupV6,
|
||||
LineaRollupV6__factory,
|
||||
OpcodeTestContract,
|
||||
OpcodeTestContract__factory,
|
||||
ProxyAdmin,
|
||||
ProxyAdmin__factory,
|
||||
TestContract,
|
||||
@@ -229,19 +227,6 @@ export default class TestSetup {
|
||||
}
|
||||
}
|
||||
|
||||
public getOpcodeTestContract(signer?: Wallet): OpcodeTestContract {
|
||||
const opcodeTestContract = OpcodeTestContract__factory.connect(
|
||||
this.config.L2.opcodeTestContractAddress,
|
||||
this.getL2Provider(),
|
||||
);
|
||||
|
||||
if (signer) {
|
||||
return opcodeTestContract.connect(signer);
|
||||
}
|
||||
|
||||
return opcodeTestContract;
|
||||
}
|
||||
|
||||
public getL1AccountManager(): AccountManager {
|
||||
return this.config.L1.accountManager;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ export type BaseL2Config = BaseConfig & {
|
||||
shomeiFrontendEndpoint?: URL;
|
||||
sequencerEndpoint?: URL;
|
||||
transactionExclusionEndpoint?: URL;
|
||||
opcodeTestContractAddress: string;
|
||||
};
|
||||
|
||||
export type LocalL2Config = BaseL2Config & {
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
import { describe, expect, it } from "@jest/globals";
|
||||
import { pollForContractMethodReturnValueExceedTarget, wait } from "./common/utils";
|
||||
import { config } from "./config/tests-config";
|
||||
import { ContractTransactionReceipt, Wallet } from "ethers";
|
||||
|
||||
const l2AccountManager = config.getL2AccountManager();
|
||||
const l2Provider = config.getL2Provider();
|
||||
|
||||
describe("Gas limit test suite", () => {
|
||||
const setGasLimit = async (account: Wallet): Promise<ContractTransactionReceipt | null> => {
|
||||
logger.debug(`setGasLimit called with account=${account.address}`);
|
||||
|
||||
const opcodeTestContract = config.getOpcodeTestContract(account);
|
||||
const nonce = await l2Provider.getTransactionCount(account.address, "pending");
|
||||
logger.debug(`Fetched nonce. nonce=${nonce} account=${account.address}`);
|
||||
|
||||
const { maxPriorityFeePerGas, maxFeePerGas } = await l2Provider.getFeeData();
|
||||
logger.debug(`Fetched fee data. maxPriorityFeePerGas=${maxPriorityFeePerGas} maxFeePerGas=${maxFeePerGas}`);
|
||||
|
||||
const tx = await opcodeTestContract.connect(account).setGasLimit({
|
||||
nonce: nonce,
|
||||
maxPriorityFeePerGas: maxPriorityFeePerGas,
|
||||
maxFeePerGas: maxFeePerGas,
|
||||
});
|
||||
logger.debug(`setGasLimit transaction sent. transactionHash=${tx.hash}`);
|
||||
|
||||
const receipt = await tx.wait();
|
||||
logger.debug(`Transaction receipt received. transactionHash=${tx.hash} status=${receipt?.status}`);
|
||||
|
||||
return receipt;
|
||||
};
|
||||
|
||||
const getGasLimit = async (): Promise<bigint> => {
|
||||
const opcodeTestContract = config.getOpcodeTestContract();
|
||||
const gasLimit = await opcodeTestContract.getGasLimit();
|
||||
|
||||
logger.debug(`Current gas limit retrieved. gasLimit=${gasLimit}`);
|
||||
|
||||
return gasLimit;
|
||||
};
|
||||
|
||||
it.concurrent("Should successfully invoke OpcodeTestContract.setGasLimit()", async () => {
|
||||
const account = await l2AccountManager.generateAccount();
|
||||
const receipt = await setGasLimit(account);
|
||||
expect(receipt?.status).toEqual(1);
|
||||
});
|
||||
|
||||
it.concurrent("Should successfully finalize OpcodeTestContract.setGasLimit()", async () => {
|
||||
const account = await l2AccountManager.generateAccount();
|
||||
const lineaRollupV6 = config.getLineaRollupContract();
|
||||
|
||||
const txReceipt = await setGasLimit(account);
|
||||
expect(txReceipt?.status).toEqual(1);
|
||||
// Ok to type assertion here, because txReceipt won't be null if it passed above assertion.
|
||||
const txBlockNumber = <number>txReceipt?.blockNumber;
|
||||
|
||||
logger.debug(`Waiting for block to be finalized... blockNumber=${txBlockNumber}`);
|
||||
|
||||
const isBlockFinalized = await pollForContractMethodReturnValueExceedTarget(
|
||||
lineaRollupV6.currentL2BlockNumber,
|
||||
BigInt(txBlockNumber),
|
||||
);
|
||||
|
||||
logger.debug(`Block finalized. blockNumber=${txBlockNumber}`);
|
||||
|
||||
expect(isBlockFinalized).toEqual(true);
|
||||
});
|
||||
|
||||
// One-time test to test block gas limit increase from 61M -> 2B
|
||||
it.skip("Should successfully reach the target gas limit, and finalize the corresponding transaction", async () => {
|
||||
const targetBlockGasLimit = 2_000_000_000n;
|
||||
let isTargetBlockGasLimitReached = false;
|
||||
let blockNumberToCheckFinalization = 0;
|
||||
const account = await l2AccountManager.generateAccount();
|
||||
const lineaRollupV6 = config.getLineaRollupContract();
|
||||
|
||||
logger.debug(`Target block gasLimit=${targetBlockGasLimit}`);
|
||||
|
||||
while (!isTargetBlockGasLimitReached) {
|
||||
const txReceipt = await setGasLimit(account);
|
||||
expect(txReceipt?.status).toEqual(1);
|
||||
const blockGasLimit = await getGasLimit();
|
||||
|
||||
if (blockGasLimit === targetBlockGasLimit) {
|
||||
isTargetBlockGasLimitReached = true;
|
||||
// Ok to type assertion here, because txReceipt won't be null if it passed above assertion.
|
||||
blockNumberToCheckFinalization = <number>txReceipt?.blockNumber;
|
||||
}
|
||||
await wait(1000);
|
||||
}
|
||||
|
||||
logger.debug(`Waiting for block to be finalized... blockNumber=${blockNumberToCheckFinalization}`);
|
||||
|
||||
const isBlockFinalized = await pollForContractMethodReturnValueExceedTarget(
|
||||
lineaRollupV6.currentL2BlockNumber,
|
||||
BigInt(blockNumberToCheckFinalization),
|
||||
);
|
||||
|
||||
logger.debug(`Block finalized. blockNumber=${blockNumberToCheckFinalization}`);
|
||||
|
||||
expect(isBlockFinalized).toEqual(true);
|
||||
// Timeout of 6 hrs
|
||||
}, 21_600_000);
|
||||
});
|
||||
139
e2e/src/send-bundle.spec.ts
Normal file
139
e2e/src/send-bundle.spec.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { describe, expect, it } from "@jest/globals";
|
||||
import { config } from "./config/tests-config";
|
||||
import {
|
||||
generateRandomUUIDv4,
|
||||
getRawTransactionHex,
|
||||
getTransactionHash,
|
||||
getWallet,
|
||||
LineaBundleClient,
|
||||
pollForBlockNumber,
|
||||
} from "./common/utils";
|
||||
import { ethers, TransactionRequest } from "ethers";
|
||||
|
||||
const describeIf = shouldSkipBundleTests ? describe.skip : describe;
|
||||
if (shouldSkipBundleTests) {
|
||||
logger.info("Skip bundle tests due to tracing-v1 besu nodes");
|
||||
}
|
||||
|
||||
describeIf("Send bundle test suite", () => {
|
||||
const l2AccountManager = config.getL2AccountManager();
|
||||
const lineaCancelBundleClient = new LineaBundleClient(config.getSequencerEndpoint()!);
|
||||
const lineaSendBundleClient = new LineaBundleClient(config.getL2BesuNodeEndpoint()!);
|
||||
|
||||
it.concurrent(
|
||||
"Call sendBundle to RPC node and the bundled txs should get included",
|
||||
async () => {
|
||||
const senderAccount = await l2AccountManager.generateAccount();
|
||||
const senderWallet = getWallet(senderAccount.privateKey, config.getL2BesuNodeProvider()!);
|
||||
const recipientAccount = await l2AccountManager.generateAccount(0n);
|
||||
|
||||
let senderNonce = await senderAccount.getNonce();
|
||||
const txHashes: string[] = [];
|
||||
const txs: string[] = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const txRequest: TransactionRequest = {
|
||||
to: recipientAccount.address,
|
||||
value: ethers.parseUnits("1000", "wei"),
|
||||
maxPriorityFeePerGas: ethers.parseEther("0.000000001"), // 1 Gwei
|
||||
maxFeePerGas: ethers.parseEther("0.00000001"), // 10 Gwei
|
||||
nonce: senderNonce++,
|
||||
};
|
||||
txs.push(await getRawTransactionHex(txRequest, senderWallet));
|
||||
txHashes.push(await getTransactionHash(txRequest, senderWallet));
|
||||
}
|
||||
const targetBlockNumber = (await config.getL2Provider().getBlockNumber()) + 5;
|
||||
const replacementUUID = generateRandomUUIDv4();
|
||||
|
||||
await lineaSendBundleClient.lineaSendBundle(txs, replacementUUID, "0x" + targetBlockNumber.toString(16));
|
||||
|
||||
const hasReachedTargeBlockNumber = await pollForBlockNumber(config.getL2Provider(), targetBlockNumber);
|
||||
|
||||
expect(hasReachedTargeBlockNumber).toBeTruthy();
|
||||
for (const tx of txHashes) {
|
||||
const receipt = await config.getL2Provider().getTransactionReceipt(tx);
|
||||
expect(receipt?.status).toStrictEqual(1);
|
||||
}
|
||||
},
|
||||
120_000,
|
||||
);
|
||||
|
||||
it.concurrent(
|
||||
"Call sendBundle to RPC node but the bundled txs should not get included as not all of them is valid",
|
||||
async () => {
|
||||
// 1500 wei should just be enough for the first ETH transfer tx, and the second and third would fail
|
||||
const senderAccount = await l2AccountManager.generateAccount(ethers.parseUnits("1500", "wei"));
|
||||
const senderWallet = getWallet(senderAccount.privateKey, config.getL2BesuNodeProvider()!);
|
||||
const recipientAccount = await l2AccountManager.generateAccount(0n);
|
||||
|
||||
let senderNonce = await senderAccount.getNonce();
|
||||
const txHashes: string[] = [];
|
||||
const txs: string[] = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const txRequest: TransactionRequest = {
|
||||
to: recipientAccount.address,
|
||||
value: ethers.parseUnits("1000", "wei"),
|
||||
maxPriorityFeePerGas: ethers.parseEther("0.000000001"), // 1 Gwei
|
||||
maxFeePerGas: ethers.parseEther("0.00000001"), // 10 Gwei
|
||||
nonce: senderNonce++,
|
||||
};
|
||||
txs.push(await getRawTransactionHex(txRequest, senderWallet));
|
||||
txHashes.push(await getTransactionHash(txRequest, senderWallet));
|
||||
}
|
||||
const targetBlockNumber = (await config.getL2Provider().getBlockNumber()) + 5;
|
||||
const replacementUUID = generateRandomUUIDv4();
|
||||
|
||||
await lineaSendBundleClient.lineaSendBundle(txs, replacementUUID, "0x" + targetBlockNumber.toString(16));
|
||||
|
||||
const hasReachedTargeBlockNumber = await pollForBlockNumber(config.getL2Provider(), targetBlockNumber);
|
||||
|
||||
expect(hasReachedTargeBlockNumber).toBeTruthy();
|
||||
// None of the bundled txs should be included as not all of them is valid
|
||||
for (const tx of txHashes) {
|
||||
const receipt = await config.getL2Provider().getTransactionReceipt(tx);
|
||||
expect(receipt?.status).toBeUndefined();
|
||||
}
|
||||
},
|
||||
120_000,
|
||||
);
|
||||
|
||||
it.concurrent(
|
||||
"Call sendBundle to RPC node and then cancelBundle to sequencer and no bundled txs should get included",
|
||||
async () => {
|
||||
const senderAccount = await l2AccountManager.generateAccount();
|
||||
const senderWallet = getWallet(senderAccount.privateKey, config.getL2BesuNodeProvider()!);
|
||||
const recipientAccount = await l2AccountManager.generateAccount(0n);
|
||||
|
||||
let senderNonce = await senderAccount.getNonce();
|
||||
const txHashes: string[] = [];
|
||||
const txs: string[] = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const txRequest: TransactionRequest = {
|
||||
to: recipientAccount.address,
|
||||
value: ethers.parseUnits("1000", "wei"),
|
||||
maxPriorityFeePerGas: ethers.parseEther("0.000000001"), // 1 Gwei
|
||||
maxFeePerGas: ethers.parseEther("0.00000001"), // 10 Gwei
|
||||
nonce: senderNonce++,
|
||||
};
|
||||
txs.push(await getRawTransactionHex(txRequest, senderWallet));
|
||||
txHashes.push(await getTransactionHash(txRequest, senderWallet));
|
||||
}
|
||||
const targetBlockNumber = (await config.getL2Provider().getBlockNumber()) + 10;
|
||||
const replacementUUID = generateRandomUUIDv4();
|
||||
|
||||
await lineaSendBundleClient.lineaSendBundle(txs, replacementUUID, "0x" + targetBlockNumber.toString(16));
|
||||
|
||||
await pollForBlockNumber(config.getL2Provider(), targetBlockNumber - 5);
|
||||
const cancelled = await lineaCancelBundleClient.lineaCancelBundle(replacementUUID);
|
||||
expect(cancelled).toBeTruthy();
|
||||
|
||||
const hasReachedTargeBlockNumber = await pollForBlockNumber(config.getL2Provider(), targetBlockNumber);
|
||||
|
||||
expect(hasReachedTargeBlockNumber).toBeTruthy();
|
||||
for (const tx of txHashes) {
|
||||
const receipt = await config.getL2Provider().getTransactionReceipt(tx);
|
||||
expect(receipt?.status).toBeUndefined();
|
||||
}
|
||||
},
|
||||
120_000,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user