Commit 09bd17c4 authored by Conner Fromknecht's avatar Conner Fromknecht

feat: submit multiple sequencer txs per batch

parent 236b92e7
......@@ -10,8 +10,8 @@ import (
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/scc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
l2types "github.com/ethereum-optimism/optimism/l2geth/core/types"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum-optimism/optimism/l2geth/log"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
......@@ -19,6 +19,9 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
)
// stateRootSize is the size in bytes of a state root.
const stateRootSize = 32
var bigOne = new(big.Int).SetUint64(1) //nolint:unused
type Config struct {
......@@ -89,7 +92,6 @@ func (d *Driver) GetBatchBlockRange(
ctx context.Context) (*big.Int, *big.Int, error) {
blockOffset := new(big.Int).SetUint64(d.cfg.BlockOffset)
maxBatchSize := new(big.Int).SetUint64(1)
start, err := d.sccContract.GetTotalElements(&bind.CallOpts{
Pending: false,
......@@ -100,20 +102,14 @@ func (d *Driver) GetBatchBlockRange(
}
start.Add(start, blockOffset)
totalElements, err := d.ctcContract.GetTotalElements(&bind.CallOpts{
end, err := d.ctcContract.GetTotalElements(&bind.CallOpts{
Pending: false,
Context: ctx,
})
if err != nil {
return nil, nil, err
}
totalElements.Add(totalElements, blockOffset)
// Take min(start + blockOffset + maxBatchSize, totalElements).
end := new(big.Int).Add(start, maxBatchSize)
if totalElements.Cmp(end) < 0 {
end.Set(totalElements)
}
end.Add(end, blockOffset)
if start.Cmp(end) > 0 {
return nil, nil, fmt.Errorf("invalid range, "+
......@@ -130,29 +126,34 @@ func (d *Driver) SubmitBatchTx(
ctx context.Context,
start, end, nonce, gasPrice *big.Int) (*types.Transaction, error) {
name := d.cfg.Name
batchTxBuildStart := time.Now()
var blocks []*l2types.Block
var (
stateRoots [][32]byte
totalStateRootSize uint64
)
for i := new(big.Int).Set(start); i.Cmp(end) < 0; i.Add(i, bigOne) {
block, err := d.cfg.L2Client.BlockByNumber(ctx, i)
if err != nil {
return nil, err
}
blocks = append(blocks, block)
// TODO(conner): remove when moving to multiple blocks
break //nolint
// Consume state roots until reach our maximum tx size.
if totalStateRootSize+stateRootSize > d.cfg.MaxTxSize {
break
}
totalStateRootSize += stateRootSize
var stateRoots = make([][32]byte, 0, len(blocks))
for _, block := range blocks {
stateRoots = append(stateRoots, block.Root())
}
batchTxBuildTime := float64(time.Since(batchTxBuildStart) / time.Millisecond)
d.metrics.BatchTxBuildTime.Set(batchTxBuildTime)
d.metrics.NumTxPerBatch.Observe(float64(len(blocks)))
d.metrics.NumTxPerBatch.Observe(float64(len(stateRoots)))
log.Info(name+" batch constructed", "num_state_roots", len(stateRoots))
opts, err := bind.NewKeyedTransactorWithChainID(
d.cfg.PrivKey, d.cfg.ChainID,
......
......@@ -3,7 +3,6 @@ package sequencer
import (
"context"
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"strings"
......@@ -11,7 +10,6 @@ import (
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
l2types "github.com/ethereum-optimism/optimism/l2geth/core/types"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
......@@ -147,22 +145,33 @@ func (d *Driver) SubmitBatchTx(
batchTxBuildStart := time.Now()
var blocks []*l2types.Block
var (
batchElements []BatchElement
totalTxSize uint64
)
for i := new(big.Int).Set(start); i.Cmp(end) < 0; i.Add(i, bigOne) {
block, err := d.cfg.L2Client.BlockByNumber(ctx, i)
if err != nil {
return nil, err
}
blocks = append(blocks, block)
// TODO(conner): remove when moving to multiple blocks
break //nolint
// For each sequencer transaction, update our running total with the
// size of the transaction.
batchElement := BatchElementFromBlock(block)
if batchElement.IsSequencerTx() {
// Abort once the total size estimate is greater than the maximum
// configured size. This is a conservative estimate, as the total
// calldata size will be greater when batch contexts are included.
// Below this set will be further whittled until the raw call data
// size also adheres to this constraint.
txLen := batchElement.Tx.Size()
if totalTxSize+uint64(TxLenSize+txLen) > d.cfg.MaxTxSize {
break
}
totalTxSize += uint64(TxLenSize + txLen)
}
var batchElements = make([]BatchElement, 0, len(blocks))
for _, block := range blocks {
batchElements = append(batchElements, BatchElementFromBlock(block))
batchElements = append(batchElements, batchElement)
}
shouldStartAt := start.Uint64()
......@@ -174,8 +183,6 @@ func (d *Driver) SubmitBatchTx(
return nil, err
}
log.Info(name+" batch params", "params", fmt.Sprintf("%#v", batchParams))
batchArguments, err := batchParams.Serialize()
if err != nil {
return nil, err
......@@ -184,16 +191,20 @@ func (d *Driver) SubmitBatchTx(
appendSequencerBatchID := d.ctcABI.Methods[appendSequencerBatchMethodName].ID
batchCallData := append(appendSequencerBatchID, batchArguments...)
// Continue pruning until calldata size is less than configured max.
if uint64(len(batchCallData)) > d.cfg.MaxTxSize {
panic("call data too large")
newBatchElementsLen := (len(batchElements) * 9) / 10
batchElements = batchElements[:newBatchElementsLen]
continue
}
// Record the batch_tx_build_time.
batchTxBuildTime := float64(time.Since(batchTxBuildStart) / time.Millisecond)
d.metrics.BatchTxBuildTime.Set(batchTxBuildTime)
d.metrics.NumTxPerBatch.Observe(float64(len(blocks)))
d.metrics.NumTxPerBatch.Observe(float64(len(batchElements)))
log.Info(name+" batch call data", "data", hex.EncodeToString(batchCallData))
log.Info(name+" batch constructed", "num_txs", len(batchElements),
"length", len(batchCallData))
opts, err := bind.NewKeyedTransactorWithChainID(
d.cfg.PrivKey, d.cfg.ChainID,
......
......@@ -11,6 +11,12 @@ import (
l2rlp "github.com/ethereum-optimism/optimism/l2geth/rlp"
)
const (
// TxLenSize is the number of bytes used to represent the size of a
// serialized sequencer transaction.
TxLenSize = 3
)
var byteOrder = binary.BigEndian
// BatchContext denotes a range of transactions that belong the same batch. It
......@@ -111,7 +117,7 @@ func (p *AppendSequencerBatchParams) Write(w *bytes.Buffer) error {
// Write each length-prefixed tx.
for _, tx := range p.Txs {
writeUint64(w, uint64(tx.Size()), 3)
writeUint64(w, uint64(tx.Size()), TxLenSize)
_, _ = w.Write(tx.RawTx()) // can't fail for bytes.Buffer
}
......@@ -166,7 +172,7 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error {
// from the encoding, loop until the stream is consumed.
for {
var txLen uint64
err := readUint64(r, &txLen, 3)
err := readUint64(r, &txLen, TxLenSize)
// Getting an EOF when reading the txLen expected for a cleanly
// encoded object. Silece the error and return success.
if err == io.EOF {
......
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