From 03a67dbaf2d981a8fee661ea9d0d5fcf6d080f6d Mon Sep 17 00:00:00 2001 From: George Knee Date: Tue, 25 Feb 2025 11:35:24 +0000 Subject: [PATCH] op-batcher: add `TestBatchSubmitter_sendTx_FloorDataGas` and patch `driver.sendTx` (#14517) * op-batcher: add TestBatchSubmitter_sendTx_FloorDataGas * op-batcher: use floorDataGas for transactions if greater than intrinsicGas * Update op-batcher/batcher/driver.go Co-authored-by: Sebastian Stammler * log error instead of ignoring --------- Co-authored-by: Sebastian Stammler --- op-batcher/batcher/driver.go | 34 ++++++++++++++++++++++++ op-batcher/batcher/driver_test.go | 44 +++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index 237a87861762b..94ee39e7bb0a7 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -1,10 +1,12 @@ package batcher import ( + "bytes" "context" "errors" "fmt" "io" + "math" "math/big" _ "net/http/pprof" "sync" @@ -851,6 +853,10 @@ func (l *BatchSubmitter) sendTransaction(txdata txData, queue *txmgr.Queue[txRef return nil } +type TxSender[T any] interface { + Send(id T, candidate txmgr.TxCandidate, receiptCh chan txmgr.TxReceipt[T]) +} + // sendTx uses the txmgr queue to send the given transaction candidate after setting its // gaslimit. It will block if the txmgr queue has reached its MaxPendingTransactions limit. func (l *BatchSubmitter) sendTx(txdata txData, isCancel bool, candidate *txmgr.TxCandidate, queue *txmgr.Queue[txRef], receiptsCh chan txmgr.TxReceipt[txRef]) { @@ -862,9 +868,37 @@ func (l *BatchSubmitter) sendTx(txdata txData, isCancel bool, candidate *txmgr.T candidate.GasLimit = intrinsicGas } + floorDataGas, err := floorDataGas(candidate.TxData) + if err != nil { + l.Log.Warn("Failed to compute FloorDataGas: %v, continuing with intrinsic gas.") + } else if floorDataGas > candidate.GasLimit { + l.Log.Debug("Bumping gas limit to floor data gas", "intrinsic_gas", intrinsicGas, "floor_data_gas", floorDataGas) + candidate.GasLimit = floorDataGas + } + queue.Send(txRef{id: txdata.ID(), isCancel: isCancel, isBlob: txdata.daType == DaTypeBlob}, *candidate, receiptsCh) } +// Copypaste from upstream geth +// TODO remove in #14500 +func floorDataGas(data []byte) (uint64, error) { + TxTokenPerNonZeroByte := uint64(4) + TxGas := uint64(21000) + TxCostFloorPerToken := uint64(10) + + var ( + z = uint64(bytes.Count(data, []byte{0})) + nz = uint64(len(data)) - z + tokens = nz*TxTokenPerNonZeroByte + z + ) + // Check for overflow + if (math.MaxUint64-TxGas)/TxCostFloorPerToken < tokens { + return 0, core.ErrGasUintOverflow + } + // Minimum gas required for a transaction based on its data tokens (EIP-7623). + return TxGas + tokens*TxCostFloorPerToken, nil +} + func (l *BatchSubmitter) blobTxCandidate(data txData) (*txmgr.TxCandidate, error) { blobs, err := data.Blobs() if err != nil { diff --git a/op-batcher/batcher/driver_test.go b/op-batcher/batcher/driver_test.go index f4e39e22cee2e..b0e373ff34737 100644 --- a/op-batcher/batcher/driver_test.go +++ b/op-batcher/batcher/driver_test.go @@ -3,6 +3,7 @@ package batcher import ( "context" "errors" + "sync" "testing" "github.com/ethereum-optimism/optimism/op-batcher/metrics" @@ -10,6 +11,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testutils" + "github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum/go-ethereum/log" "github.com/stretchr/testify/require" ) @@ -116,3 +118,45 @@ func TestBatchSubmitter_SafeL1Origin_FailsToResolveRollupClient(t *testing.T) { _, err := bs.safeL1Origin(context.Background()) require.Error(t, err) } + +type MockTxQueue struct { + m sync.Map +} + +func (q *MockTxQueue) Send(ref txRef, candidate txmgr.TxCandidate, receiptCh chan txmgr.TxReceipt[txRef]) { + q.m.Store(ref.id.String(), candidate) +} + +func (q *MockTxQueue) Load(id string) txmgr.TxCandidate { + c, _ := q.m.Load(id) + return c.(txmgr.TxCandidate) +} + +func TestBatchSubmitter_sendTx_FloorDataGas(t *testing.T) { + bs, _ := setup(t) + + q := new(MockTxQueue) + + txData := txData{ + frames: []frameData{ + { + data: []byte{0x01, 0x02, 0x03}, // 3 nonzero bytes = 12 tokens https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7623.md + }, + }, + } + candidate := txmgr.TxCandidate{ + To: &bs.RollupConfig.BatchInboxAddress, + TxData: txData.CallData(), + } + + bs.sendTx(txData, + false, + &candidate, + q, + make(chan txmgr.TxReceipt[txRef])) + + candidateOut := q.Load(txData.ID().String()) + + expectedFloorDataGas := uint64(21_000 + 12*10) + require.GreaterOrEqual(t, candidateOut.GasLimit, expectedFloorDataGas) +}