mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-08 03:43:56 -05:00
feat: added shomei-frontend linea_getProof e2e test (#1077)
* feat: added shomei-frontend linea_getProof e2e test * feat: revise the e2e test with comments * feat: make linked library name human readable
This commit is contained in:
2
Makefile
2
Makefile
@@ -70,7 +70,7 @@ start-env-with-tracing-v2-extra:
|
||||
make start-env COMPOSE_PROFILES:=l1,l2 COMPOSE_FILE:=docker/compose-tracing-v2-extra-extension.yml LINEA_PROTOCOL_CONTRACTS_ONLY=true DISABLE_JSON_RPC_PRICING_PROPAGATION=false DISABLE_TYPE2_STATE_PROOF_PROVIDER=false
|
||||
|
||||
start-env-with-tracing-v2-ci:
|
||||
make start-env COMPOSE_FILE=docker/compose-tracing-v2-ci-extension.yml
|
||||
make start-env COMPOSE_FILE=docker/compose-tracing-v2-ci-extension.yml DISABLE_TYPE2_STATE_PROOF_PROVIDER=false
|
||||
|
||||
start-env-with-staterecovery: COMPOSE_PROFILES:=l1,l2,staterecovery
|
||||
start-env-with-staterecovery: L1_CONTRACT_VERSION:=6
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -368,7 +368,7 @@ services:
|
||||
image: consensys/linea-shomei:2.3.0
|
||||
hostname: shomei-frontend
|
||||
container_name: shomei-frontend
|
||||
profiles: [ "l2", "l2-bc" ]
|
||||
profiles: [ "l2", "l2-bc", "external-to-monorepo" ]
|
||||
depends_on:
|
||||
zkbesu-shomei:
|
||||
condition: service_started
|
||||
|
||||
@@ -7,6 +7,11 @@ services:
|
||||
file: compose-spec-l2-services.yml
|
||||
service: l2-node-besu
|
||||
|
||||
shomei-frontend:
|
||||
extends:
|
||||
file: compose-spec-l2-services.yml
|
||||
service: shomei-frontend
|
||||
|
||||
postman:
|
||||
extends:
|
||||
file: compose-spec-l2-services.yml
|
||||
|
||||
@@ -74,7 +74,7 @@ export function generateRandomUUIDv4(): string {
|
||||
return randomUUID();
|
||||
}
|
||||
|
||||
async function awaitUntil<T>(
|
||||
export async function awaitUntil<T>(
|
||||
callback: () => Promise<T>,
|
||||
stopRetry: (a: T) => boolean,
|
||||
pollingIntervalMs: number = 500,
|
||||
@@ -231,6 +231,68 @@ export class LineaBundleClient {
|
||||
}
|
||||
}
|
||||
|
||||
export class LineaShomeiClient {
|
||||
private endpoint: URL;
|
||||
|
||||
public constructor(endpoint: URL) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public async rollupGetZkEVMStateMerkleProofV0(
|
||||
startBlockNumber: number,
|
||||
endBlockNumber: number,
|
||||
zkStateManagerVersion: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
): Promise<any> {
|
||||
const request = {
|
||||
method: "post",
|
||||
body: JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
method: "rollup_getZkEVMStateMerkleProofV0",
|
||||
params: [
|
||||
{
|
||||
startBlockNumber,
|
||||
endBlockNumber,
|
||||
zkStateManagerVersion,
|
||||
},
|
||||
],
|
||||
id: generateRandomInt(),
|
||||
}),
|
||||
};
|
||||
const response = await fetch(this.endpoint, request);
|
||||
const responseJson = await response.json();
|
||||
assert("result" in responseJson);
|
||||
return responseJson;
|
||||
}
|
||||
}
|
||||
|
||||
export class LineaShomeiFrontendClient {
|
||||
private endpoint: URL;
|
||||
|
||||
public constructor(endpoint: URL) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public async lineaGetProof(
|
||||
address: string,
|
||||
storageKeys: string[] = [],
|
||||
blockParameter: string = "latest",
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
): Promise<any> {
|
||||
const request = {
|
||||
method: "post",
|
||||
body: JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
method: "linea_getProof",
|
||||
params: [address, storageKeys, blockParameter],
|
||||
id: generateRandomInt(),
|
||||
}),
|
||||
};
|
||||
const response = await fetch(this.endpoint, request);
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
export class TransactionExclusionClient {
|
||||
private endpoint: URL;
|
||||
|
||||
@@ -465,6 +527,11 @@ export async function execDockerCommand(command: string, containerName: string):
|
||||
});
|
||||
}
|
||||
|
||||
export async function getDockerImageTag(containerName: string, imageRepoName: string): Promise<string> {
|
||||
const inspectJsonOutput = JSON.parse(await execDockerCommand("inspect", containerName));
|
||||
return inspectJsonOutput[0]["Config"]["Image"].replace(imageRepoName + ":", "");
|
||||
}
|
||||
|
||||
export function generateRoleAssignments(
|
||||
roles: string[],
|
||||
defaultAddress: string,
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
import { ethers } from "ethers";
|
||||
import { config } from "../tests-config";
|
||||
import { deployContract } from "../../common/deployments";
|
||||
import { DummyContract__factory, TestContract__factory } from "../../typechain";
|
||||
import {
|
||||
DummyContract__factory,
|
||||
Mimc__factory,
|
||||
SparseMerkleProof__factory,
|
||||
TestContract__factory,
|
||||
} from "../../typechain";
|
||||
import { etherToWei, sendTransactionsToGenerateTrafficWithInterval } from "../../common/utils";
|
||||
import { EMPTY_CONTRACT_CODE } from "../../common/constants";
|
||||
import { createTestLogger } from "../logger";
|
||||
@@ -36,10 +41,11 @@ async function configureOnceOffPrerequisities() {
|
||||
const to = "0x8D97689C9818892B700e27F316cc3E41e17fBeb9";
|
||||
const calldata = "0x";
|
||||
|
||||
const [dummyContract, l2DummyContract, l2TestContract] = await Promise.all([
|
||||
const [dummyContract, l2DummyContract, l2TestContract, l2MimcContract] = 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 Mimc__factory(), l2Account, [{ nonce: l2AccountNonce + 2 }]),
|
||||
|
||||
// Send ETH to the LineaRollup contract
|
||||
(
|
||||
@@ -51,7 +57,16 @@ async function configureOnceOffPrerequisities() {
|
||||
).wait(),
|
||||
]);
|
||||
|
||||
const l2MimcContractAddress = await l2MimcContract.getAddress();
|
||||
const l2SparseMerkleProofContract = await deployContract(
|
||||
new SparseMerkleProof__factory({ "contracts/Mimc.sol:Mimc": l2MimcContractAddress }),
|
||||
l2Account,
|
||||
[{ nonce: l2AccountNonce + 3 }],
|
||||
);
|
||||
|
||||
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 Mimc contract deployed. address=${l2MimcContractAddress}`);
|
||||
logger.info(`L2 SparseMerkleProof contract deployed. address=${await l2SparseMerkleProofContract.getAddress()}`);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ const config: Config = {
|
||||
tokenBridgeAddress: "",
|
||||
l2TokenAddress: "",
|
||||
l2TestContractAddress: "",
|
||||
l2SparseMerkleProofAddress: "",
|
||||
accountManager: new EnvironmentBasedAccountManager(
|
||||
new ethers.JsonRpcProvider(L2_RPC_URL.toString()),
|
||||
L2_WHALE_ACCOUNTS,
|
||||
|
||||
@@ -33,12 +33,11 @@ const config: Config = {
|
||||
besuNodeRpcUrl: L2_BESU_NODE_RPC_URL,
|
||||
chainId: 1337,
|
||||
l2MessageServiceAddress: "0xe537D669CA013d86EBeF1D64e40fC74CADC91987",
|
||||
// Nonce 10
|
||||
l2TestContractAddress: "0x997FC3aF1F193Cbdc013060076c67A13e218980e",
|
||||
// Nonce 9
|
||||
dummyContractAddress: "0xE4392c8ecC46b304C83cDB5edaf742899b1bda93",
|
||||
l2TestContractAddress: "0x997FC3aF1F193Cbdc013060076c67A13e218980e", // Nonce 10
|
||||
dummyContractAddress: "0xE4392c8ecC46b304C83cDB5edaf742899b1bda93", // Nonce 9
|
||||
tokenBridgeAddress: "0x5C95Bcd50E6D1B4E3CDC478484C9030Ff0a7D493",
|
||||
l2TokenAddress: "0xCC1B08B17301e090cbb4c1F5598Cbaa096d591FB",
|
||||
l2SparseMerkleProofAddress: "0x7917AbB0cDbf3D3C4057d6a2808eE85ec16260C1", // Nonce 12
|
||||
accountManager: new GenesisBasedAccountManager(
|
||||
new ethers.JsonRpcProvider(L2_RPC_URL.toString()),
|
||||
path.resolve(
|
||||
|
||||
@@ -42,6 +42,7 @@ const config: Config = {
|
||||
tokenBridgeAddress: "0x93DcAdf238932e6e6a85852caC89cBd71798F463",
|
||||
l2TokenAddress: "",
|
||||
l2TestContractAddress: "",
|
||||
l2SparseMerkleProofAddress: "",
|
||||
accountManager: new EnvironmentBasedAccountManager(
|
||||
new ethers.JsonRpcProvider(L2_RPC_URL.toString()),
|
||||
L2_WHALE_ACCOUNTS,
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
LineaRollupV6__factory,
|
||||
ProxyAdmin,
|
||||
ProxyAdmin__factory,
|
||||
SparseMerkleProof,
|
||||
SparseMerkleProof__factory,
|
||||
TestContract,
|
||||
TestContract__factory,
|
||||
TestERC20,
|
||||
@@ -227,6 +229,10 @@ export default class TestSetup {
|
||||
}
|
||||
}
|
||||
|
||||
public getL2SparseMerkleProofContract(): SparseMerkleProof {
|
||||
return SparseMerkleProof__factory.connect(this.config.L2.l2SparseMerkleProofAddress, this.getL2Provider());
|
||||
}
|
||||
|
||||
public getL1AccountManager(): AccountManager {
|
||||
return this.config.L1.accountManager;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ export type BaseL2Config = BaseConfig & {
|
||||
besuNodeRpcUrl?: URL;
|
||||
tokenBridgeAddress: string;
|
||||
l2TokenAddress: string;
|
||||
l2SparseMerkleProofAddress: string;
|
||||
shomeiEndpoint?: URL;
|
||||
shomeiFrontendEndpoint?: URL;
|
||||
sequencerEndpoint?: URL;
|
||||
|
||||
85
e2e/src/shomei-get-proof.spec.ts
Normal file
85
e2e/src/shomei-get-proof.spec.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { describe, it } from "@jest/globals";
|
||||
import { config } from "./config/tests-config";
|
||||
import { awaitUntil, getDockerImageTag, LineaShomeiClient, LineaShomeiFrontendClient } from "./common/utils";
|
||||
|
||||
describe("Shomei Linea get proof test suite", () => {
|
||||
const lineaRollupV6 = config.getLineaRollupContract();
|
||||
const shomeiFrontendEndpoint = config.getShomeiFrontendEndpoint();
|
||||
const shomeiEndpoint = config.getShomeiEndpoint();
|
||||
const lineaShomeiFrontenedClient = new LineaShomeiFrontendClient(shomeiFrontendEndpoint!);
|
||||
const lineaShomeiClient = new LineaShomeiClient(shomeiEndpoint!);
|
||||
|
||||
it.concurrent(
|
||||
"Call linea_getProof to Shomei frontend node and get a valid proof",
|
||||
async () => {
|
||||
const shomeiImageTag = await getDockerImageTag("shomei-frontend", "consensys/linea-shomei");
|
||||
logger.debug(`shomeiImageTag=${shomeiImageTag}`);
|
||||
|
||||
const currentL2BlockNumber = await awaitUntil(
|
||||
async () => {
|
||||
try {
|
||||
return await lineaRollupV6.currentL2BlockNumber({
|
||||
blockTag: "finalized",
|
||||
});
|
||||
} catch (err) {
|
||||
if (!(err as Error).message.includes("could not decode result data")) {
|
||||
throw err;
|
||||
} // else means the currentL2BlockNumber is not ready in the L1 rollup contract yet
|
||||
return -1n;
|
||||
}
|
||||
},
|
||||
(currentL2BlockNumber: bigint) => currentL2BlockNumber > 1n,
|
||||
2000,
|
||||
100000,
|
||||
);
|
||||
|
||||
expect(currentL2BlockNumber).toBeGreaterThan(1n);
|
||||
|
||||
logger.debug(`currentL2BlockNumber=${currentL2BlockNumber}`);
|
||||
|
||||
const provingAddress = "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73"; // from genesis file
|
||||
const getProofResponse = await awaitUntil(
|
||||
async () =>
|
||||
lineaShomeiFrontenedClient.lineaGetProof(provingAddress, [], "0x" + currentL2BlockNumber!.toString(16)),
|
||||
(getProofResponse) => getProofResponse?.result,
|
||||
2000,
|
||||
100000,
|
||||
);
|
||||
|
||||
const {
|
||||
result: { zkEndStateRootHash },
|
||||
} = await lineaShomeiClient.rollupGetZkEVMStateMerkleProofV0(
|
||||
Number(currentL2BlockNumber),
|
||||
Number(currentL2BlockNumber),
|
||||
shomeiImageTag,
|
||||
);
|
||||
|
||||
expect(zkEndStateRootHash).toBeDefined();
|
||||
|
||||
const l2SparseMerkleProofContract = config.getL2SparseMerkleProofContract();
|
||||
const isValid = await l2SparseMerkleProofContract.verifyProof(
|
||||
getProofResponse.result.accountProof.proof.proofRelatedNodes,
|
||||
getProofResponse.result.accountProof.leafIndex,
|
||||
zkEndStateRootHash,
|
||||
);
|
||||
|
||||
expect(isValid).toBeTruthy();
|
||||
|
||||
// Modify the last hex character of the original state root hash should verify the same proof as invalid
|
||||
const modifiedStateRootHash =
|
||||
zkEndStateRootHash.slice(0, -1) + ((parseInt(zkEndStateRootHash.slice(-1), 16) + 1) % 16).toString(16);
|
||||
|
||||
logger.debug(`originalStateRootHash=${zkEndStateRootHash}`);
|
||||
logger.debug(`modifiedStateRootHash=${modifiedStateRootHash}`);
|
||||
|
||||
const isInvalid = !(await l2SparseMerkleProofContract.verifyProof(
|
||||
getProofResponse.result.accountProof.proof.proofRelatedNodes,
|
||||
getProofResponse.result.accountProof.leafIndex,
|
||||
modifiedStateRootHash,
|
||||
));
|
||||
|
||||
expect(isInvalid).toBeTruthy();
|
||||
},
|
||||
100_000,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user