Commit bb0285bc authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Merge branch 'develop' into feat/caching-itests

parents 71d64834 f4903adb
---
'@eth-optimism/core-utils': patch
---
Cleans up the internal file and folder structure for the typings exported by core-utils
---
'@eth-optimism/batch-submitter-service': minor
---
Add multi-tx support, clear pending txs on startup
...@@ -29,6 +29,7 @@ jobs: ...@@ -29,6 +29,7 @@ jobs:
rpc-proxy : ${{ steps.packages.outputs.rpc-proxy }} rpc-proxy : ${{ steps.packages.outputs.rpc-proxy }}
op-exporter : ${{ steps.packages.outputs.op-exporter }} op-exporter : ${{ steps.packages.outputs.op-exporter }}
l2geth-exporter : ${{ steps.packages.outputs.l2geth-exporter }} l2geth-exporter : ${{ steps.packages.outputs.l2geth-exporter }}
batch-submitter-service : ${{ steps.packages.outputs.batch-submitter-service }}
steps: steps:
- name: Check out source code - name: Check out source code
...@@ -506,3 +507,29 @@ jobs: ...@@ -506,3 +507,29 @@ jobs:
file: ./ops/docker/Dockerfile.rpc-proxy file: ./ops/docker/Dockerfile.rpc-proxy
push: true push: true
tags: ethereumoptimism/rpc-proxy:${{ needs.canary-publish.outputs.rpc-proxy }} tags: ethereumoptimism/rpc-proxy:${{ needs.canary-publish.outputs.rpc-proxy }}
batch-submitter-service:
name: Publish batch-submitter-service Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.batch-submitter-service != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.batch-submitter-service
push: true
tags: ethereumoptimism/batch-submitter-service:${{ needs.canary-publish.outputs.batch-submitter-service }}
...@@ -25,6 +25,7 @@ jobs: ...@@ -25,6 +25,7 @@ jobs:
hardhat-node: ${{ steps.packages.outputs.hardhat-node }} hardhat-node: ${{ steps.packages.outputs.hardhat-node }}
op-exporter : ${{ steps.packages.outputs.op-exporter }} op-exporter : ${{ steps.packages.outputs.op-exporter }}
l2geth-exporter : ${{ steps.packages.outputs.l2geth-exporter }} l2geth-exporter : ${{ steps.packages.outputs.l2geth-exporter }}
batch-submitter-service : ${{ steps.packages.outputs.batch-submitter-service }}
steps: steps:
- name: Checkout Repo - name: Checkout Repo
...@@ -502,3 +503,29 @@ jobs: ...@@ -502,3 +503,29 @@ jobs:
push: true push: true
tags: ethereumoptimism/replica-healthcheck:${{ needs.builder.outputs.replica-healthcheck }},ethereumoptimism/replica-healthcheck:latest tags: ethereumoptimism/replica-healthcheck:${{ needs.builder.outputs.replica-healthcheck }},ethereumoptimism/replica-healthcheck:latest
build-args: BUILDER_TAG=${{ needs.builder.outputs.builder }} build-args: BUILDER_TAG=${{ needs.builder.outputs.builder }}
batch-submitter-service:
name: Publish batch-submitter-service Version ${{ needs.release.outputs.batch-submitter-service }}
needs: release
if: needs.release.outputs.batch-submitter-service != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.batch-submitter-service
push: true
tags: ethereumoptimism/batch-submitter-service:${{ needs.release.outputs.batch-submitter-service }},ethereumoptimism/batch-submitter-service:latest
export * from './types'
export * from './sequencer-batch' export * from './sequencer-batch'
...@@ -2,8 +2,7 @@ export * from './coders' ...@@ -2,8 +2,7 @@ export * from './coders'
export * from './common' export * from './common'
export * from './watcher' export * from './watcher'
export * from './l2context' export * from './l2context'
export * from './batches' export * from './types'
export * from './bcfg'
export * from './fees' export * from './fees'
export * from './provider' export * from './provider'
export * from './alias' export * from './alias'
......
// Use this file for simple types that aren't necessarily associated with a specific project or
// package. Often used for alias types like Address = string.
export interface Signature { export interface Signature {
r: string r: string
s: string s: string
......
// Optimism PBC 2021 // Types explicitly related to dealing with Geth.
// Represents the ethereum state /**
* Represents the Ethereum state, in the format that Geth expects it.
*/
export interface State { export interface State {
[address: string]: { [address: string]: {
nonce: number nonce: number
...@@ -14,7 +16,9 @@ export interface State { ...@@ -14,7 +16,9 @@ export interface State {
} }
} }
// Represents a genesis file that geth can consume /**
* Represents Geth's genesis file format.
*/
export interface Genesis { export interface Genesis {
config: { config: {
chainId: number chainId: number
......
export * from './geth'
export * from './bcfg'
export * from './rollup'
export * from './basic'
...@@ -4,6 +4,9 @@ import { ...@@ -4,6 +4,9 @@ import {
TransactionResponse, TransactionResponse,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
/**
* Structure of the response returned by L2Geth nodes when querying the `rollup_getInfo` endpoint.
*/
export interface RollupInfo { export interface RollupInfo {
mode: 'sequencer' | 'verifier' mode: 'sequencer' | 'verifier'
syncing: boolean syncing: boolean
...@@ -17,14 +20,18 @@ export interface RollupInfo { ...@@ -17,14 +20,18 @@ export interface RollupInfo {
} }
} }
/**
* Enum used for the two transaction types (queue and direct to Sequencer).
*/
export enum QueueOrigin { export enum QueueOrigin {
Sequencer = 'sequencer', Sequencer = 'sequencer',
L1ToL2 = 'l1', L1ToL2 = 'l1',
} }
/** /**
* Transaction & Blocks. These are the true data-types we expect * JSON transaction representation when returned by L2Geth nodes. This is simply an extension to
* from running a batch submitter. * the standard transaction response type. You do NOT need to use this type unless you care about
* having typed access to L2-specific fields.
*/ */
export interface L2Transaction extends TransactionResponse { export interface L2Transaction extends TransactionResponse {
l1BlockNumber: number l1BlockNumber: number
...@@ -33,21 +40,32 @@ export interface L2Transaction extends TransactionResponse { ...@@ -33,21 +40,32 @@ export interface L2Transaction extends TransactionResponse {
rawTransaction: string rawTransaction: string
} }
/**
* JSON block representation when returned by L2Geth nodes. Just a normal block but with
* L2Transaction objects instead of the standard transaction response object.
*/
export interface L2Block extends BlockWithTransactions { export interface L2Block extends BlockWithTransactions {
stateRoot: string stateRoot: string
transactions: [L2Transaction] transactions: [L2Transaction]
} }
/** /**
* BatchElement & Batch. These are the data-types of the compressed / batched * Generic batch element, either a state root batch element or a transaction batch element.
* block data we submit to L1.
*/ */
export interface BatchElement { export interface BatchElement {
// Only exists on state root batch elements.
stateRoot: string stateRoot: string
// Only exists on transaction batch elements.
isSequencerTx: boolean isSequencerTx: boolean
rawTransaction: undefined | string rawTransaction: undefined | string
// Batch element context, exists on all batch elements.
timestamp: number timestamp: number
blockNumber: number blockNumber: number
} }
/**
* List of batch elements.
*/
export type Batch = BatchElement[] export type Batch = BatchElement[]
...@@ -421,9 +421,27 @@ export class CrossChainProvider implements ICrossChainProvider { ...@@ -421,9 +421,27 @@ export class CrossChainProvider implements ICrossChainProvider {
} }
public async estimateL2MessageGasLimit( public async estimateL2MessageGasLimit(
message: MessageLike message: MessageLike,
opts?: {
bufferPercent?: number
}
): Promise<BigNumber> { ): Promise<BigNumber> {
throw new Error('Not implemented') const resolved = await this.toCrossChainMessage(message)
// L2 message gas estimation is only used for L1 => L2 messages.
if (resolved.direction === MessageDirection.L2_TO_L1) {
throw new Error(`cannot estimate gas limit for L2 => L1 message`)
}
const estimate = await this.l2Provider.estimateGas({
from: resolved.sender,
to: resolved.target,
data: resolved.message,
})
// Return the estimate plus a buffer of 20% just in case.
const bufferPercent = opts?.bufferPercent || 20
return estimate.mul(100 + bufferPercent).div(100)
} }
public async estimateMessageWaitTimeSeconds( public async estimateMessageWaitTimeSeconds(
......
...@@ -201,9 +201,16 @@ export interface ICrossChainProvider { ...@@ -201,9 +201,16 @@ export interface ICrossChainProvider {
* L1 => L2 messages. You would supply this gas limit when sending the message to L2. * L1 => L2 messages. You would supply this gas limit when sending the message to L2.
* *
* @param message Message get a gas estimate for. * @param message Message get a gas estimate for.
* @param opts Options object.
* @param opts.bufferPercent Percentage of gas to add to the estimate. Defaults to 20.
* @returns Estimates L2 gas limit. * @returns Estimates L2 gas limit.
*/ */
estimateL2MessageGasLimit(message: MessageLike): Promise<BigNumber> estimateL2MessageGasLimit(
message: MessageLike,
opts?: {
bufferPercent?: number
}
): Promise<BigNumber>
/** /**
* Returns the estimated amount of time before the message can be executed. When this is a * Returns the estimated amount of time before the message can be executed. When this is a
......
import { Provider } from '@ethersproject/abstract-provider' import { Provider } from '@ethersproject/abstract-provider'
import { expectApprox } from '@eth-optimism/core-utils'
import { Contract } from 'ethers' import { Contract } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
...@@ -1091,7 +1092,93 @@ describe('CrossChainProvider', () => { ...@@ -1091,7 +1092,93 @@ describe('CrossChainProvider', () => {
}) })
describe('estimateL2MessageGasLimit', () => { describe('estimateL2MessageGasLimit', () => {
it('should perform a gas estimation of the L2 action') let provider: CrossChainProvider
beforeEach(async () => {
provider = new CrossChainProvider({
l1Provider: ethers.provider,
l2Provider: ethers.provider,
l1ChainId: 31337,
})
})
describe('when the message is an L1 to L2 message', () => {
it('should return an accurate gas estimate plus a ~20% buffer', async () => {
const message = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
logIndex: 0,
blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32),
}
const estimate = await ethers.provider.estimateGas({
to: message.target,
from: message.sender,
data: message.message,
})
// Approximately 20% greater than the estimate, +/- 1%.
expectApprox(
await provider.estimateL2MessageGasLimit(message),
estimate.mul(120).div(100),
{
percentUpperDeviation: 1,
percentLowerDeviation: 1,
}
)
})
it('should return an accurate gas estimate when a custom buffer is provided', async () => {
const message = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
logIndex: 0,
blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32),
}
const estimate = await ethers.provider.estimateGas({
to: message.target,
from: message.sender,
data: message.message,
})
// Approximately 30% greater than the estimate, +/- 1%.
expectApprox(
await provider.estimateL2MessageGasLimit(message, {
bufferPercent: 30,
}),
estimate.mul(130).div(100),
{
percentUpperDeviation: 1,
percentLowerDeviation: 1,
}
)
})
})
describe('when the message is an L2 to L1 message', () => {
it('should throw an error', async () => {
const message = {
direction: MessageDirection.L2_TO_L1,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
logIndex: 0,
blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32),
}
await expect(provider.estimateL2MessageGasLimit(message)).to.be.rejected
})
})
}) })
describe('estimateMessageWaitTimeBlocks', () => { describe('estimateMessageWaitTimeBlocks', () => {
......
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