Commit 5077441f authored by Karl Floersch's avatar Karl Floersch Committed by GitHub

feat: raw transaction batch submitter (#527)

* feat: use raw transaction

Fix tests

Add chainset

* Update packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts
Co-authored-by: default avatarGeorgios Konstantopoulos <me@gakonst.com>

* Remove raw eth_getBlock, delete dead code, & lint

* Remove txTypePlaintext and queueOriginPlaintext

* style: return promise; no await

* Update changeset & fix lint error

* Remove stray newline

* Update packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts
Co-authored-by: default avatarAnnie Ke <annieke8@gmail.com>
Co-authored-by: default avatarAnnie Ke <annie@address.com>
Co-authored-by: default avatarGeorgios Konstantopoulos <me@gakonst.com>
Co-authored-by: default avatarAnnie Ke <annieke8@gmail.com>
parent 6e8fe1b9
---
"@eth-optimism/batch-submitter": minor
"@eth-optimism/core-utils": patch
---
- Use raw transaction in batch submitter -- incompatible with L2Geth v0.1.2.1
- Pass through raw transaction in l2context
......@@ -3,7 +3,12 @@ import { Promise as bPromise } from 'bluebird'
import { Contract, Signer, providers } from 'ethers'
import { TransactionReceipt } from '@ethersproject/abstract-provider'
import { getContractFactory } from 'old-contracts'
import { Logger, Bytes32, remove0x } from '@eth-optimism/core-utils'
import {
Logger,
Bytes32,
remove0x,
toRpcHexString,
} from '@eth-optimism/core-utils'
/* Internal Imports */
import { L2Block } from '..'
......
/* External Imports */
import { Promise as bPromise } from 'bluebird'
import { BigNumber, Signer, ethers, Wallet, Contract, providers } from 'ethers'
import { Signer, ethers, Contract, providers } from 'ethers'
import {
TransactionResponse,
TransactionReceipt,
} from '@ethersproject/abstract-provider'
import { getContractInterface, getContractFactory } from 'old-contracts'
import { getContractInterface as getNewContractInterface } from '@eth-optimism/contracts'
import {
Logger,
EIP155TxData,
TxType,
ctcCoder,
EthSignTxData,
txTypePlainText,
} from '@eth-optimism/core-utils'
import { Logger, ctcCoder } from '@eth-optimism/core-utils'
/* Internal Imports */
import {
......@@ -24,13 +17,7 @@ import {
AppendSequencerBatchParams,
} from '../transaction-chain-contract'
import {
L2Block,
BatchElement,
Batch,
QueueOrigin,
queueOriginPlainText,
} from '..'
import { L2Block, BatchElement, Batch, QueueOrigin } from '..'
import { RollupInfo, Range, BatchSubmitter, BLOCK_OFFSET } from '.'
export interface AutoFixBatchOptions {
......@@ -602,41 +589,11 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
queueElementBlockNumber: queueElement.blockNumber,
}
)
// This implies that we've double played a deposit.
// We can correct this by instead submitting a dummy sequencer tx
const wallet = Wallet.createRandom()
const gasLimit = 8_000_000
const gasPrice = 0
const chainId = 10
const nonce = 0
const rawTx = await wallet.signTransaction({
gasLimit,
gasPrice,
chainId,
nonce,
to: '0x1111111111111111111111111111111111111111',
data: '0x1234',
})
// tx: [0nonce, 1gasprice, 2startgas, 3to, 4value, 5data, 6v, 7r, 8s]
const tx = ethers.utils.RLP.decode(rawTx)
const dummyTx: EIP155TxData = {
sig: {
v: tx[6],
r: tx[7],
s: tx[8],
},
gasLimit,
gasPrice,
nonce,
target: tx[3],
data: tx[5],
type: TxType.EIP155,
}
const dummyTx: string = '0x1234'
return {
stateRoot: queueElement.stateRoot,
isSequencerTx: true,
sequencerTxType: TxType.EIP155,
txData: dummyTx,
rawTransaction: dummyTx,
timestamp: queueElement.timestamp,
blockNumber: queueElement.blockNumber,
}
......@@ -715,17 +672,7 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
if (!block.isSequencerTx) {
continue
}
let encoding: string
if (block.sequencerTxType === TxType.EIP155) {
encoding = ctcCoder.eip155TxData.encode(block.txData as EIP155TxData)
} else if (block.sequencerTxType === TxType.EthSign) {
encoding = ctcCoder.ethSignTxData.encode(block.txData as EthSignTxData)
} else {
throw new Error(
`Trying to build batch with unknown type ${block.sequencerTxType}`
)
}
transactions.push(encoding)
transactions.push(block.rawTransaction)
}
return {
......@@ -742,67 +689,26 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
this.log.debug('Fetched L2 block', {
block,
})
const txType = block.transactions[0].txType
if (this._isSequencerTx(block)) {
if (txType === TxType.EIP155 || txType === TxType.EthSign) {
return this._getDefaultEcdsaTxBatchElement(block)
} else {
throw new Error('Unsupported Tx Type!')
}
} else {
return {
const batchElement = {
stateRoot: block.stateRoot,
isSequencerTx: false,
sequencerTxType: undefined,
txData: undefined,
timestamp: block.timestamp,
blockNumber: block.transactions[0].l1BlockNumber,
}
}
isSequencerTx: false,
rawTransaction: undefined,
}
private async _getBlock(blockNumber: number): Promise<L2Block> {
const block = (await this.l2Provider.getBlockWithTransactions(
blockNumber
)) as L2Block
// Convert the tx type to a number
block.transactions[0].txType = txTypePlainText[block.transactions[0].txType]
block.transactions[0].queueOrigin =
queueOriginPlainText[block.transactions[0].queueOrigin]
if (!block.transactions[0].l1BlockNumber) {
this.log.error('Current block missing l1BlockNumber', {
blockNumber,
block,
})
if (this._isSequencerTx(block)) {
batchElement.isSequencerTx = true
batchElement.rawTransaction = block.transactions[0].rawTransaction
}
return block
return batchElement
}
private _getDefaultEcdsaTxBatchElement(block: L2Block): BatchElement {
const tx: TransactionResponse = block.transactions[0]
const txData: EIP155TxData = {
sig: {
v: tx.v - this.l2ChainId * 2 - 8 - 27,
r: tx.r,
s: tx.s,
},
gasLimit: BigNumber.from(tx.gasLimit).toNumber(),
gasPrice: BigNumber.from(tx.gasPrice).toNumber(),
nonce: tx.nonce,
target: tx.to ? tx.to : '00'.repeat(20),
data: tx.data,
type: block.transactions[0].txType,
}
return {
stateRoot: block.stateRoot,
isSequencerTx: true,
sequencerTxType: block.transactions[0].txType,
txData,
timestamp: block.timestamp,
blockNumber: block.transactions[0].l1BlockNumber,
}
private async _getBlock(blockNumber: number): Promise<L2Block> {
const p = this.l2Provider.getBlockWithTransactions(blockNumber)
return p as Promise<L2Block>
}
private _isSequencerTx(block: L2Block): boolean {
......
export * from './batch-submitter'
export * from './transaction-chain-contract'
export * from './types'
export * from './utils'
/* External Imports */
import {
BlockWithTransactions,
Provider,
TransactionResponse,
} from '@ethersproject/abstract-provider'
/* Internal Imports */
import { EIP155TxData, TxType } from '@eth-optimism/core-utils'
export enum QueueOrigin {
Sequencer = 0,
L1ToL2 = 1,
}
export const queueOriginPlainText = {
0: QueueOrigin.Sequencer,
1: QueueOrigin.L1ToL2,
sequencer: QueueOrigin.Sequencer,
l1ToL2: QueueOrigin.L1ToL2,
Sequencer = 'sequencer',
L1ToL2 = 'l1',
}
/**
......@@ -27,8 +16,8 @@ export const queueOriginPlainText = {
export interface L2Transaction extends TransactionResponse {
l1BlockNumber: number
l1TxOrigin: string
txType: number
queueOrigin: number
queueOrigin: string
rawTransaction: string
}
export interface L2Block extends BlockWithTransactions {
......@@ -43,8 +32,7 @@ export interface L2Block extends BlockWithTransactions {
export interface BatchElement {
stateRoot: string
isSequencerTx: boolean
sequencerTxType: undefined | TxType
txData: undefined | EIP155TxData
rawTransaction: undefined | string
timestamp: number
blockNumber: number
}
......
......@@ -247,18 +247,9 @@ describe('BatchSubmitter', () => {
const nextQueueElement = await getQueueElement(
OVM_CanonicalTransactionChain
)
const data = ctcCoder.eip155TxData.encode({
sig: DUMMY_SIG,
gasLimit: 0,
gasPrice: 0,
nonce: 0,
target: DUMMY_ADDRESS,
data: '0x',
type: TxType.EIP155,
})
l2Provider.setL2BlockData(
{
data,
rawTransaction: '0x1234',
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EIP155,
queueOrigin: QueueOrigin.Sequencer,
......@@ -305,18 +296,9 @@ describe('BatchSubmitter', () => {
OVM_CanonicalTransactionChain,
2
)
const data = ctcCoder.ethSignTxData.encode({
sig: DUMMY_SIG,
gasLimit: 0,
gasPrice: 0,
nonce: 0,
target: DUMMY_ADDRESS,
data: '0x',
type: TxType.EthSign,
})
l2Provider.setL2BlockData(
{
data,
rawTransaction: '0x1234',
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EthSign,
queueOrigin: QueueOrigin.Sequencer,
......@@ -418,18 +400,9 @@ describe('BatchSubmitter', () => {
const nextQueueElement = await getQueueElement(
OVM_CanonicalTransactionChain
)
const data = ctcCoder.eip155TxData.encode({
sig: DUMMY_SIG,
gasLimit: 0,
gasPrice: 0,
nonce: 0,
target: DUMMY_ADDRESS,
data: '0x',
type: TxType.EIP155,
})
l2Provider.setL2BlockData(
{
data,
rawTransaction: '0x1234',
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EIP155,
queueOrigin: QueueOrigin.Sequencer,
......
/* External Imports */
import { providers } from 'ethers'
import { providers, BigNumber } from 'ethers'
import {
BlockWithTransactions,
TransactionResponse,
......@@ -18,6 +18,7 @@ interface UnformattedL2Transaction extends TransactionResponse {
l1MessageSender: string
signatureHashType: string
queueOrigin: string
rawTransaction: string
}
interface UnformattedL2Block extends BlockWithTransactions {
......@@ -60,10 +61,10 @@ export class MockchainProvider extends providers.JsonRpcProvider {
}
public async send(endpoint: string, params: []): Promise<any> {
if (endpoint === 'eth_chainId') {
switch (endpoint) {
case 'eth_chainId':
return this.chainId()
}
if (endpoint === 'rollup_getInfo') {
case 'rollup_getInfo':
const info: RollupInfo = {
mode: 'sequencer',
syncing: false,
......@@ -77,9 +78,16 @@ export class MockchainProvider extends providers.JsonRpcProvider {
},
}
return info
case 'eth_getBlockByNumber':
if (params.length === 0) {
throw new Error(`Invalid params for ${endpoint}`)
}
const blockNumber = BigNumber.from((params as any)[0]).toNumber()
return this.mockBlocks[blockNumber]
default:
throw new Error('Unsupported endpoint!')
}
}
public setNumBlocksToReturn(numBlocks: number): void {
this.numBlocksToReturn = numBlocks
......@@ -119,13 +127,12 @@ export class MockchainProvider extends providers.JsonRpcProvider {
block.transactions[0].l1BlockNumber,
10
)
const queueOrigin: number = parseInt(block.transactions[0].queueOrigin, 10)
const queueOrigin: string = block.transactions[0].queueOrigin
const l1TxOrigin: string = block.transactions[0].l1MessageSender
const l2Transaction: L2Transaction = {
...block.transactions[0],
// Rename the incorrectly named fields
l1TxOrigin,
txType,
queueOrigin,
l1BlockNumber,
}
......@@ -209,8 +216,9 @@ const BLOCKS = JSON.parse(`
"creates":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -261,8 +269,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -313,8 +322,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -365,8 +375,9 @@ const BLOCKS = JSON.parse(`
"creates":"0x94BA4d5Ebb0e05A50e977FFbF6e1a1Ee3D89299c",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -417,8 +428,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -469,8 +481,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -521,8 +534,9 @@ const BLOCKS = JSON.parse(`
"creates":"0x956dA338C1518a7FB213042b70c60c021aeBd554",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -573,8 +587,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -625,8 +640,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -677,8 +693,9 @@ const BLOCKS = JSON.parse(`
"creates":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -729,8 +746,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -781,8 +799,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -833,8 +852,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -885,8 +905,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......@@ -937,8 +958,9 @@ const BLOCKS = JSON.parse(`
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
......
......@@ -33,6 +33,7 @@ export const injectL2Context = (l1Provider: providers.JsonRpcProvider) => {
b.transactions[i].l1TxOrigin = block.transactions[i].l1TxOrigin
b.transactions[i].txType = block.transactions[i].txType
b.transactions[i].queueOrigin = block.transactions[i].queueOrigin
b.transactions[i].rawTransaction = block.transactions[i].rawTransaction
}
return b
}
......
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