diff --git a/go/batch-submitter/drivers/proposer/driver.go b/go/batch-submitter/drivers/proposer/driver.go index 1c8ab1cddd22a..cc8ff1790cd99 100644 --- a/go/batch-submitter/drivers/proposer/driver.go +++ b/go/batch-submitter/drivers/proposer/driver.go @@ -5,7 +5,7 @@ import ( "crypto/ecdsa" "fmt" "math/big" - "time" + "strings" "github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc" "github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/scc" @@ -14,6 +14,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 +41,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 +64,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,16 +151,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 - batchTxBuildStart := time.Now() + log.Info(name+" crafting batch tx", "start", start, "end", end, + "nonce", nonce) var ( stateRoots [][stateRootSize]byte @@ -166,8 +185,6 @@ func (d *Driver) SubmitBatchTx( stateRoots = append(stateRoots, block.Root()) } - batchTxBuildTime := float64(time.Since(batchTxBuildStart) / time.Millisecond) - d.metrics.BatchTxBuildTime.Set(batchTxBuildTime) d.metrics.NumElementsPerBatch.Observe(float64(len(stateRoots))) log.Info(name+" batch constructed", "num_state_roots", len(stateRoots)) @@ -178,12 +195,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()) +} diff --git a/go/batch-submitter/drivers/sequencer/driver.go b/go/batch-submitter/drivers/sequencer/driver.go index a954da9ffbde0..1a6f203621621 100644 --- a/go/batch-submitter/drivers/sequencer/driver.go +++ b/go/batch-submitter/drivers/sequencer/driver.go @@ -6,13 +6,13 @@ import ( "fmt" "math/big" "strings" - "time" "github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc" "github.com/ethereum-optimism/optimism/go/batch-submitter/drivers" "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,19 +148,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) - - batchTxBuildStart := time.Now() + log.Info(name+" crafting batch tx", "start", start, "end", end, + "nonce", nonce) var ( batchElements []BatchElement @@ -219,9 +220,6 @@ func (d *Driver) SubmitBatchTx( continue } - // Record the batch_tx_build_time. - batchTxBuildTime := float64(time.Since(batchTxBuildStart) / time.Millisecond) - d.metrics.BatchTxBuildTime.Set(batchTxBuildTime) d.metrics.NumElementsPerBatch.Observe(float64(len(batchElements))) d.metrics.BatchPruneCount.Set(float64(pruneCount)) @@ -233,10 +231,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()) +} diff --git a/go/batch-submitter/service.go b/go/batch-submitter/service.go index 0523612c172f5..812020f0eb1e5 100644 --- a/go/batch-submitter/service.go +++ b/go/batch-submitter/service.go @@ -1,6 +1,7 @@ package batchsubmitter import ( + "bytes" "context" "math/big" "sync" @@ -43,12 +44,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 +172,26 @@ func (s *Service) eventLoop() { } nonce := new(big.Int).SetUint64(nonce64) + batchTxBuildStart := time.Now() + 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 + } + batchTxBuildTime := time.Since(batchTxBuildStart) / time.Millisecond + s.metrics.BatchTxBuildTime.Set(float64(batchTxBuildTime)) + + // Record the size of the batch transaction. + var txBuf bytes.Buffer + if err := tx.EncodeRLP(&txBuf); err != nil { + log.Error(name+" unable to encode batch tx", "err", err) + continue + } + s.metrics.BatchSizeInBytes.Observe(float64(len(txBuf.Bytes()))) + // Construct the transaction submission clousure that will attempt // to send the next transaction at the given nonce and gas price. sendTx := func( @@ -170,9 +202,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 } @@ -186,8 +216,6 @@ func (s *Service) eventLoop() { "gasPrice", gasPrice, ) - s.metrics.BatchSizeInBytes.Observe(float64(tx.Size())) - return tx, nil }