Commit dcdcc757 authored by Kelvin Fichter's avatar Kelvin Fichter

feat: update message-relayer to use the SDK

Removes all message relaying utilities found inside the message-relayer
package. All of these utilities are now present in the SDK. Also
re-implements the message-relayer service with the utilities found in
the SDK.
parent 4da19032
---
'@eth-optimism/message-relayer': minor
'@eth-optimism/integration-tests': patch
---
Removes message relaying utilities from the Message Relayer, to be replaced by the SDK
......@@ -30,7 +30,6 @@
"devDependencies": {
"@eth-optimism/contracts": "0.5.14",
"@eth-optimism/core-utils": "0.7.7",
"@eth-optimism/message-relayer": "0.2.18",
"@eth-optimism/sdk": "0.2.2",
"@ethersproject/abstract-provider": "^5.5.1",
"@ethersproject/providers": "^5.5.3",
......
......@@ -3,8 +3,7 @@ import { Contract, utils, Wallet, providers } from 'ethers'
import { TransactionResponse } from '@ethersproject/providers'
import { getContractFactory, predeploys } from '@eth-optimism/contracts'
import { sleep } from '@eth-optimism/core-utils'
import { getMessagesAndProofsForL2Transaction } from '@eth-optimism/message-relayer'
import { CrossChainMessenger } from '@eth-optimism/sdk'
import { CrossChainMessenger, MessageStatus } from '@eth-optimism/sdk'
/* Imports: Internal */
import {
......@@ -21,7 +20,6 @@ import {
getL1Bridge,
getL2Bridge,
envConfig,
DEFAULT_TEST_GAS_L1,
} from './utils'
import {
CrossDomainMessagePair,
......@@ -184,67 +182,48 @@ export class OptimismEnv {
tx = await tx
await tx.wait()
let messagePairs = []
while (true) {
try {
messagePairs = await getMessagesAndProofsForL2Transaction(
l1Provider,
l2Provider,
this.scc.address,
predeploys.L2CrossDomainMessenger,
tx.hash
)
break
} catch (err) {
if (err.message.includes('unable to find state root batch for tx')) {
await sleep(5000)
} else {
throw err
}
}
const messages = await this.messenger.getMessagesByTransaction(tx)
if (messages.length === 0) {
return
}
for (const { message, proof } of messagePairs) {
while (true) {
for (const message of messages) {
let status: MessageStatus
while (
status !== MessageStatus.READY_FOR_RELAY &&
status !== MessageStatus.RELAYED
) {
status = await this.messenger.getMessageStatus(message)
await sleep(1000)
}
let relayed = false
while (!relayed) {
try {
const result = await this.l1Messenger
.connect(this.l1Wallet)
.relayMessage(
message.target,
message.sender,
message.message,
message.messageNonce,
proof,
{
gasLimit: DEFAULT_TEST_GAS_L1 * 10,
}
)
await result.wait()
break
await this.messenger.finalizeMessage(message)
relayed = true
} catch (err) {
if (err.message.includes('execution failed due to an exception')) {
await sleep(5000)
} else if (err.message.includes('Nonce too low')) {
await sleep(5000)
} else if (err.message.includes('transaction was replaced')) {
// this happens when we run tests in parallel
await sleep(5000)
} else if (
if (
err.message.includes('Nonce too low') ||
err.message.includes('transaction was replaced') ||
err.message.includes(
'another transaction with same nonce in the queue'
)
) {
// this happens when we run tests in parallel
// Sometimes happens when we run tests in parallel.
await sleep(5000)
} else if (
err.message.includes('message has already been received')
) {
break
// Message already relayed, this is fine.
relayed = true
} else {
throw err
}
}
}
await this.messenger.waitForMessageReceipt(message)
}
}
}
......@@ -67,6 +67,7 @@
"@codechecks/client": "^0.1.11",
"@defi-wonderland/smock": "^2.0.2",
"@eth-optimism/smock": "1.1.10",
"@nomiclabs/ethereumjs-vm": "^4.2.2",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-etherscan": "^2.1.6",
"@nomiclabs/hardhat-waffle": "^2.0.1",
......
......@@ -5,7 +5,7 @@ import * as Sentry from '@sentry/node'
import * as dotenv from 'dotenv'
import Config from 'bcfg'
import { MessageRelayerService } from '../service'
import { MessageRelayerService } from '../src'
dotenv.config()
......@@ -59,14 +59,6 @@ const main = async () => {
'get-logs-interval',
parseInt(env.GET_LOGS_INTERVAL, 10) || 2000
)
const L2_BLOCK_OFFSET = config.uint(
'l2-start-offset',
parseInt(env.L2_BLOCK_OFFSET, 10) || 1
)
const L1_START_OFFSET = config.uint(
'l1-start-offset',
parseInt(env.L1_BLOCK_OFFSET, 10) || 1
)
const FROM_L2_TRANSACTION_INDEX = config.uint(
'from-l2-transaction-index',
parseInt(env.FROM_L2_TRANSACTION_INDEX, 10) || 0
......@@ -102,15 +94,11 @@ const main = async () => {
}
const service = new MessageRelayerService({
l1RpcProvider: l1Provider,
l2RpcProvider: l2Provider,
addressManagerAddress: ADDRESS_MANAGER_ADDRESS,
l1Wallet: wallet,
relayGasLimit: RELAY_GAS_LIMIT,
fromL2TransactionIndex: FROM_L2_TRANSACTION_INDEX,
pollingInterval: POLLING_INTERVAL,
l2BlockOffset: L2_BLOCK_OFFSET,
l1StartOffset: L1_START_OFFSET,
getLogsInterval: GET_LOGS_INTERVAL,
logger,
})
......
......@@ -7,19 +7,14 @@
"files": [
"dist/*"
],
"bin": {
"withdraw": "./src/exec/withdraw.ts"
},
"scripts": {
"start": "ts-node ./src/exec/run.ts",
"start": "ts-node ./bin/run.ts",
"build": "tsc -p ./tsconfig.build.json",
"clean": "rimraf dist/ ./tsconfig.build.tsbuildinfo",
"lint": "yarn lint:fix && yarn lint:check",
"pre-commit": "lint-staged",
"lint:fix": "yarn lint:check --fix",
"lint:check": "eslint . --max-warnings=0",
"test": "hardhat test --show-stack-traces",
"test:coverage": "nyc hardhat test && nyc merge .nyc_output coverage.json"
"lint:check": "eslint . --max-warnings=0"
},
"keywords": [
"optimism",
......@@ -35,28 +30,19 @@
},
"dependencies": {
"@eth-optimism/common-ts": "0.2.1",
"@eth-optimism/contracts": "0.5.14",
"@eth-optimism/core-utils": "0.7.7",
"@eth-optimism/sdk": "^0.2.1",
"@sentry/node": "^6.3.1",
"bcfg": "^0.1.6",
"dotenv": "^10.0.0",
"ethers": "^5.5.4",
"merkletreejs": "^0.2.18",
"rlp": "^2.2.6"
"ethers": "^5.5.4"
},
"devDependencies": {
"@eth-optimism/smock": "1.1.10",
"@nomiclabs/ethereumjs-vm": "^4",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/chai": "^4.2.18",
"@types/chai-as-promised": "^7.1.4",
"@types/mocha": "^8.2.2",
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"babel-eslint": "^10.1.0",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
......@@ -68,8 +54,6 @@
"ethereum-waffle": "^3.3.0",
"hardhat": "^2.3.0",
"lint-staged": "11.0.0",
"lodash": "^4.17.21",
"mocha": "^8.4.0",
"prettier": "^2.3.1",
"ts-node": "^10.0.0",
"typescript": "^4.3.5"
......
#!/usr/bin/env ts-node
/**
* Utility that will relay all L2 => L1 messages created within a given L2 transaction.
*/
/* Imports: External */
import { ethers } from 'ethers'
import { predeploys, getContractInterface } from '@eth-optimism/contracts'
import { sleep } from '@eth-optimism/core-utils'
import dotenv from 'dotenv'
/* Imports: Internal */
import { getMessagesAndProofsForL2Transaction } from '../relay-tx'
dotenv.config()
const l1RpcProviderUrl = process.env.WITHDRAW__L1_RPC_URL
const l2RpcProviderUrl = process.env.WITHDRAW__L2_RPC_URL
const l1PrivateKey = process.env.WITHDRAW__L1_PRIVATE_KEY
const l1StateCommitmentChainAddress =
process.env.WITHDRAW__STATE_COMMITMENT_CHAIN_ADDRESS
const l1CrossDomainMessengerAddress =
process.env.WITHDRAW__L1_CROSS_DOMAIN_MESSENGER_ADDRESS
const main = async () => {
const l2TransactionHash = process.argv[2]
if (l2TransactionHash === undefined) {
throw new Error(`must provide l2 transaction hash`)
}
const l1RpcProvider = new ethers.providers.JsonRpcProvider(l1RpcProviderUrl)
const l1Wallet = new ethers.Wallet(l1PrivateKey, l1RpcProvider)
const l1WalletBalance = await l1Wallet.getBalance()
console.log(`relayer address: ${l1Wallet.address}`)
console.log(`relayer balance: ${ethers.utils.formatEther(l1WalletBalance)}`)
const l1CrossDomainMessenger = new ethers.Contract(
l1CrossDomainMessengerAddress,
getContractInterface('L1CrossDomainMessenger'),
l1Wallet
)
console.log(`searching for messages in transaction: ${l2TransactionHash}`)
let messagePairs = []
while (true) {
try {
messagePairs = await getMessagesAndProofsForL2Transaction(
l1RpcProviderUrl,
l2RpcProviderUrl,
l1StateCommitmentChainAddress,
predeploys.L2CrossDomainMessenger,
l2TransactionHash
)
break
} catch (err) {
if (err.message.includes('unable to find state root batch for tx')) {
console.log(`no state root batch for tx yet, trying again in 5s...`)
await sleep(5000)
} else {
throw err
}
}
}
console.log(`found ${messagePairs.length} messages`)
for (let i = 0; i < messagePairs.length; i++) {
console.log(`relaying message ${i + 1}/${messagePairs.length}`)
const { message, proof } = messagePairs[i]
while (true) {
try {
const result = await l1CrossDomainMessenger.relayMessage(
message.target,
message.sender,
message.message,
message.messageNonce,
proof
)
await result.wait()
console.log(
`relayed message ${i + 1}/${messagePairs.length}! L1 tx hash: ${
result.hash
}`
)
break
} catch (err) {
if (err.message.includes('execution failed due to an exception')) {
console.log(`fraud proof may not be elapsed, trying again in 5s...`)
await sleep(5000)
} else if (err.message.includes('message has already been received')) {
console.log(
`message ${i + 1}/${
messagePairs.length
} was relayed by someone else`
)
break
} else {
throw err
}
}
}
}
}
main()
export * from './relay-tx'
export * from './service'
This diff is collapsed.
This diff is collapsed.
import { BigNumber } from 'ethers'
export interface StateRootBatchHeader {
batchIndex: BigNumber
batchRoot: string
batchSize: BigNumber
prevTotalElements: BigNumber
extraData: string
}
export interface SentMessage {
target: string
sender: string
message: string
messageNonce: number
encodedMessage: string
encodedMessageHash: string
parentTransactionIndex: number
parentTransactionHash: string
}
export interface SentMessageProof {
stateRoot: string
stateRootBatchHeader: StateRootBatchHeader
stateRootProof: StateRootProof
stateTrieWitness: string | Buffer
storageTrieWitness: string | Buffer
}
export interface StateRootProof {
index: number
siblings: string[]
}
/* External Imports */
import chai = require('chai')
import Mocha from 'mocha'
import { solidity } from 'ethereum-waffle'
import chaiAsPromised from 'chai-as-promised'
chai.use(solidity)
chai.use(chaiAsPromised)
const should = chai.should()
const expect = chai.expect
export { should, expect, Mocha }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract MockL2CrossDomainMessenger {
struct MessageData {
address target;
address sender;
bytes message;
uint256 messageNonce;
}
event SentMessage(
address indexed target,
address sender,
bytes message,
uint256 messageNonce,
uint256 gasLimit);
function emitSentMessageEvent(
MessageData memory _message
)
public
{
emit SentMessage(
_message.target,
_message.sender,
_message.message,
_message.messageNonce,
0
);
}
function emitMultipleSentMessageEvents(
MessageData[] memory _messages
)
public
{
for (uint256 i = 0; i < _messages.length; i++) {
emitSentMessageEvent(
_messages[i]
);
}
}
function doNothing() public {}
}
......@@ -2311,7 +2311,7 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@nomiclabs/ethereumjs-vm@^4":
"@nomiclabs/ethereumjs-vm@^4.2.2":
version "4.2.2"
resolved "https://registry.yarnpkg.com/@nomiclabs/ethereumjs-vm/-/ethereumjs-vm-4.2.2.tgz#2f8817113ca0fb6c44c1b870d0a809f0e026a6cc"
integrity sha512-8WmX94mMcJaZ7/m7yBbyuS6B+wuOul+eF+RY9fBpGhNaUpyMR/vFIcDojqcWQ4Yafe1tMKY5LDu2yfT4NZgV4Q==
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment