Commit b64628ec authored by Conner Fromknecht's avatar Conner Fromknecht

feat: add more granular CraftBatchTx method to Driver iface

The current interface supports only one method for both crafting and
publishing a batch transaction, SubmitBatchTx. Currently this is being
executed on each new gas price that the txmgr commands, implying we are
doing a lot of extra work to rederive batches. In addition, much our
instrumentation lives inside this method, meaning that the they are also
being recorded multiple times per transaction. When we get to
processing larger batches on Kovan and Mainnet, this could also become a
resource bottleneck.

This commit remedies all of the above issues by splitting out the
transaction crafting process from the publication. A new method,
CraftBatchTx, is added to the Driver interface. This method is
responsible for creating a fully formed transaction, but does not
publish it. The responsibility of SubmitBatchTx is now modified to take
an predefined transaction, i.e. created by CraftBatchTx, overwrite the
supplied gas price, and publish. In this way, this expensive call to
build batches can be done once before handing the transaction of the
txmgr.
parent e5ba9b1d
......@@ -5,6 +5,7 @@ import (
"crypto/ecdsa"
"fmt"
"math/big"
"strings"
"time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
......@@ -14,6 +15,8 @@ import (
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum-optimism/optimism/l2geth/log"
"github.com/ethereum-optimism/optimism/l2geth/params"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
......@@ -39,11 +42,12 @@ type Config struct {
}
type Driver struct {
cfg Config
sccContract *scc.StateCommitmentChain
ctcContract *ctc.CanonicalTransactionChain
walletAddr common.Address
metrics *metrics.Metrics
cfg Config
sccContract *scc.StateCommitmentChain
rawSccContract *bind.BoundContract
ctcContract *ctc.CanonicalTransactionChain
walletAddr common.Address
metrics *metrics.Metrics
}
func NewDriver(cfg Config) (*Driver, error) {
......@@ -61,14 +65,26 @@ func NewDriver(cfg Config) (*Driver, error) {
return nil, err
}
parsed, err := abi.JSON(strings.NewReader(
scc.StateCommitmentChainABI,
))
if err != nil {
return nil, err
}
rawSccContract := bind.NewBoundContract(
cfg.SCCAddr, parsed, cfg.L1Client, cfg.L1Client, cfg.L1Client,
)
walletAddr := crypto.PubkeyToAddress(cfg.PrivKey.PublicKey)
return &Driver{
cfg: cfg,
sccContract: sccContract,
ctcContract: ctcContract,
walletAddr: walletAddr,
metrics: metrics.NewMetrics(cfg.Name),
cfg: cfg,
sccContract: sccContract,
rawSccContract: rawSccContract,
ctcContract: ctcContract,
walletAddr: walletAddr,
metrics: metrics.NewMetrics(cfg.Name),
}, nil
}
......@@ -136,15 +152,21 @@ func (d *Driver) GetBatchBlockRange(
return start, end, nil
}
// SubmitBatchTx transforms the L2 blocks between start and end into a batch
// transaction using the given nonce and gasPrice. The final transaction is
// published and returned to the call.
func (d *Driver) SubmitBatchTx(
// CraftBatchTx transforms the L2 blocks between start and end into a batch
// transaction using the given nonce. A dummy gas price is used in the resulting
// transaction to use for size estimation.
//
// NOTE: This method SHOULD NOT publish the resulting transaction.
func (d *Driver) CraftBatchTx(
ctx context.Context,
start, end, nonce, gasPrice *big.Int) (*types.Transaction, error) {
start, end, nonce *big.Int,
) (*types.Transaction, error) {
name := d.cfg.Name
log.Info(name+" crafting batch tx", "start", start, "end", end,
"nonce", nonce)
batchTxBuildStart := time.Now()
var (
......@@ -178,12 +200,35 @@ func (d *Driver) SubmitBatchTx(
if err != nil {
return nil, err
}
opts.Nonce = nonce
opts.Context = ctx
opts.GasPrice = gasPrice
opts.Nonce = nonce
opts.GasPrice = big.NewInt(params.GWei) // dummy
opts.NoSend = true
blockOffset := new(big.Int).SetUint64(d.cfg.BlockOffset)
offsetStartsAtIndex := new(big.Int).Sub(start, blockOffset)
return d.sccContract.AppendStateBatch(opts, stateRoots, offsetStartsAtIndex)
}
// SubmitBatchTx using the passed transaction as a template, signs and publishes
// an otherwise identical transaction after setting the provided gas price. The
// final transaction is returned to the caller.
func (d *Driver) SubmitBatchTx(
ctx context.Context,
tx *types.Transaction,
gasPrice *big.Int,
) (*types.Transaction, error) {
opts, err := bind.NewKeyedTransactorWithChainID(
d.cfg.PrivKey, d.cfg.ChainID,
)
if err != nil {
return nil, err
}
opts.Context = ctx
opts.Nonce = new(big.Int).SetUint64(tx.Nonce())
opts.GasPrice = gasPrice
return d.rawSccContract.RawTransact(opts, tx.Data())
}
......@@ -13,6 +13,7 @@ import (
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum-optimism/optimism/l2geth/params"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
......@@ -148,17 +149,20 @@ func (d *Driver) GetBatchBlockRange(
return start, end, nil
}
// SubmitBatchTx transforms the L2 blocks between start and end into a batch
// transaction using the given nonce and gasPrice. The final transaction is
// published and returned to the call.
func (d *Driver) SubmitBatchTx(
// CraftBatchTx transforms the L2 blocks between start and end into a batch
// transaction using the given nonce. A dummy gas price is used in the resulting
// transaction to use for size estimation.
//
// NOTE: This method SHOULD NOT publish the resulting transaction.
func (d *Driver) CraftBatchTx(
ctx context.Context,
start, end, nonce, gasPrice *big.Int) (*types.Transaction, error) {
start, end, nonce *big.Int,
) (*types.Transaction, error) {
name := d.cfg.Name
log.Info(name+" submitting batch tx", "start", start, "end", end,
"gasPrice", gasPrice)
log.Info(name+" crafting batch tx", "start", start, "end", end,
"nonce", nonce)
batchTxBuildStart := time.Now()
......@@ -233,10 +237,33 @@ func (d *Driver) SubmitBatchTx(
if err != nil {
return nil, err
}
opts.Nonce = nonce
opts.Context = ctx
opts.GasPrice = gasPrice
opts.Nonce = nonce
opts.GasPrice = big.NewInt(params.GWei) // dummy
opts.NoSend = true
return d.rawCtcContract.RawTransact(opts, batchCallData)
}
}
// SubmitBatchTx using the passed transaction as a template, signs and publishes
// an otherwise identical transaction after setting the provided gas price. The
// final transaction is returned to the caller.
func (d *Driver) SubmitBatchTx(
ctx context.Context,
tx *types.Transaction,
gasPrice *big.Int,
) (*types.Transaction, error) {
opts, err := bind.NewKeyedTransactorWithChainID(
d.cfg.PrivKey, d.cfg.ChainID,
)
if err != nil {
return nil, err
}
opts.Context = ctx
opts.Nonce = new(big.Int).SetUint64(tx.Nonce())
opts.GasPrice = gasPrice
return d.rawCtcContract.RawTransact(opts, tx.Data())
}
......@@ -43,12 +43,23 @@ type Driver interface {
// processed.
GetBatchBlockRange(ctx context.Context) (*big.Int, *big.Int, error)
// SubmitBatchTx transforms the L2 blocks between start and end into a
// batch transaction using the given nonce and gasPrice. The final
// transaction is published and returned to the call.
// CraftBatchTx transforms the L2 blocks between start and end into a batch
// transaction using the given nonce. A dummy gas price is used in the
// resulting transaction to use for size estimation.
//
// NOTE: This method SHOULD NOT publish the resulting transaction.
CraftBatchTx(
ctx context.Context,
start, end, nonce *big.Int,
) (*types.Transaction, error)
// SubmitBatchTx using the passed transaction as a template, signs and
// publishes an otherwise identical transaction after setting the provided
// gas price. The final transaction is returned to the caller.
SubmitBatchTx(
ctx context.Context,
start, end, nonce, gasPrice *big.Int,
tx *types.Transaction,
gasPrice *big.Int,
) (*types.Transaction, error)
}
......@@ -160,6 +171,15 @@ func (s *Service) eventLoop() {
}
nonce := new(big.Int).SetUint64(nonce64)
tx, err := s.cfg.Driver.CraftBatchTx(
s.ctx, start, end, nonce,
)
if err != nil {
log.Error(name+" unable to craft batch tx",
"err", err)
continue
}
// Construct the transaction submission clousure that will attempt
// to send the next transaction at the given nonce and gas price.
sendTx := func(
......@@ -170,9 +190,7 @@ func (s *Service) eventLoop() {
"end", end, "nonce", nonce,
"gasPrice", gasPrice)
tx, err := s.cfg.Driver.SubmitBatchTx(
ctx, start, end, nonce, gasPrice,
)
tx, err := s.cfg.Driver.SubmitBatchTx(ctx, tx, gasPrice)
if err != nil {
return nil, err
}
......
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