From dee8244007a9fc57b9fafb9661291dd92daf1799 Mon Sep 17 00:00:00 2001 From: Kolby Moroz Liebl <31669092+KolbyML@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:20:14 -0700 Subject: [PATCH 1/2] Add MaxTxSize check to ValidateExpressLaneTx() --- cmd/el-proxy/config.go | 7 + cmd/el-proxy/main.go | 6 +- .../gethexec/express_lane_service_test.go | 225 +++++++++--------- execution/gethexec/express_lane_tracker.go | 14 +- execution/gethexec/node.go | 6 +- execution/gethexec/sequencer.go | 30 ++- system_tests/timeboost_test.go | 4 +- timeboost/errors.go | 1 + 8 files changed, 158 insertions(+), 135 deletions(-) diff --git a/cmd/el-proxy/config.go b/cmd/el-proxy/config.go index 3785d5e77d..1a3d833c93 100644 --- a/cmd/el-proxy/config.go +++ b/cmd/el-proxy/config.go @@ -13,6 +13,7 @@ import ( "github.com/offchainlabs/nitro/cmd/conf" "github.com/offchainlabs/nitro/cmd/genericconf" + "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/util/colors" ) @@ -31,6 +32,7 @@ type ExpressLaneProxyConfig struct { HTTP genericconf.HTTPConfig `koanf:"http"` WS genericconf.WSConfig `koanf:"ws"` IPC genericconf.IPCConfig `koanf:"ipc"` + MaxTxDataSize uint64 `koanf:"max-tx-data-size" reload:"hot"` Metrics bool `koanf:"metrics"` MetricsServer genericconf.MetricsServerConfig `koanf:"metrics-server"` PProf bool `koanf:"pprof"` @@ -72,6 +74,7 @@ var ExpressLaneProxyConfigDefault = ExpressLaneProxyConfig{ HTTP: HTTPConfigDefault, WS: WSConfigDefault, IPC: IPCConfigDefault, + MaxTxDataSize: uint64(gethexec.DefaultSequencerConfig.MaxTxDataSize), Metrics: false, MetricsServer: genericconf.MetricsServerConfigDefault, PProf: false, @@ -94,6 +97,7 @@ func ExpressLaneProxyConfigAddOptions(f *pflag.FlagSet) { genericconf.HTTPConfigAddOptions("http", f) genericconf.WSConfigAddOptions("ws", f) genericconf.IPCConfigAddOptions("ipc", f) + f.Uint64("max-tx-data-size", ExpressLaneProxyConfigDefault.MaxTxDataSize, "maximum transaction size the sequencer will accept") f.Bool("metrics", ExpressLaneProxyConfigDefault.Metrics, "enable metrics") genericconf.MetricsServerAddOptions("metrics-server", f) f.Bool("pprof", ExpressLaneProxyConfigDefault.PProf, "enable pprof") @@ -143,6 +147,9 @@ func (c *ExpressLaneProxyConfig) GetReloadInterval() time.Duration { } func (c *ExpressLaneProxyConfig) Validate() error { + if err := gethexec.ValidateMaxTxDataSize(c.MaxTxDataSize); err != nil { + return err + } return nil } diff --git a/cmd/el-proxy/main.go b/cmd/el-proxy/main.go index 116890748a..79edc76b62 100644 --- a/cmd/el-proxy/main.go +++ b/cmd/el-proxy/main.go @@ -121,15 +121,19 @@ func NewExpressLaneProxy( return nil, err } - expressLaneTracker := gethexec.NewExpressLaneTracker( + expressLaneTracker, err := gethexec.NewExpressLaneTracker( *roundTimingInfo, time.Millisecond*250, &HeaderProviderAdapter{arbClient}, auctionContract, auctionContractAddr, ¶ms.ChainConfig{ChainID: big.NewInt(config.ChainId)}, + config.MaxTxDataSize, 0, ) + if err != nil { + return nil, fmt.Errorf("error creating express lane tracker: %w", err) + } _, dataSignerFunc, err := util.OpenWallet("el-proxy", &config.Wallet, big.NewInt(config.ChainId)) if err != nil { diff --git a/execution/gethexec/express_lane_service_test.go b/execution/gethexec/express_lane_service_test.go index 6aa8941bd3..77052fcb95 100644 --- a/execution/gethexec/express_lane_service_test.go +++ b/execution/gethexec/express_lane_service_test.go @@ -51,6 +51,7 @@ func defaultTestRoundTimingInfo(offset time.Time) timeboost.RoundTimingInfo { } func Test_expressLaneService_validateExpressLaneTx(t *testing.T) { + validSubmission := buildValidSubmission(t, common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), testPriv, 0) tests := []struct { name string t *ExpressLaneTracker @@ -62,13 +63,13 @@ func Test_expressLaneService_validateExpressLaneTx(t *testing.T) { { name: "nil msg", sub: nil, - t: &ExpressLaneTracker{}, + t: defaultTestTracker(), expectedErr: timeboost.ErrMalformedData, }, { name: "nil tx", sub: &timeboost.ExpressLaneSubmission{}, - t: &ExpressLaneTracker{}, + t: defaultTestTracker(), expectedErr: timeboost.ErrMalformedData, }, { @@ -76,67 +77,54 @@ func Test_expressLaneService_validateExpressLaneTx(t *testing.T) { sub: &timeboost.ExpressLaneSubmission{ Transaction: &types.Transaction{}, }, - t: &ExpressLaneTracker{}, + t: defaultTestTracker(), expectedErr: timeboost.ErrMalformedData, }, + { + name: "oversized data", + sub: func() *timeboost.ExpressLaneSubmission { + submission := cloneSubmission(validSubmission) + submission.Transaction = types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), make([]byte, DefaultSequencerConfig.MaxTxDataSize)) + return submission + }(), + t: defaultTestTracker(), + expectedErr: timeboost.ErrOversizedData, + }, { name: "wrong chain id", - t: &ExpressLaneTracker{ - chainConfig: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - }, - }, - sub: &timeboost.ExpressLaneSubmission{ - ChainId: big.NewInt(2), - Transaction: &types.Transaction{}, - Signature: []byte{'a'}, - }, + t: defaultTestTrackerWithChainID(1), + sub: func() *timeboost.ExpressLaneSubmission { + submission := cloneSubmission(validSubmission) + submission.ChainId = big.NewInt(2) + return submission + }(), expectedErr: timeboost.ErrWrongChainId, }, { name: "wrong auction contract", - t: &ExpressLaneTracker{ - auctionContractAddr: common.Address{'a'}, - chainConfig: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - }, - }, - sub: &timeboost.ExpressLaneSubmission{ - ChainId: big.NewInt(1), - AuctionContractAddress: common.Address{'b'}, - Transaction: &types.Transaction{}, - Signature: []byte{'b'}, - }, + t: defaultTestTrackerWithConfig(common.Address{'a'}, defaultTestRoundTimingInfo(time.Now())), + sub: func() *timeboost.ExpressLaneSubmission { + submission := cloneSubmission(validSubmission) + submission.AuctionContractAddress = common.Address{'b'} + return submission + }(), expectedErr: timeboost.ErrWrongAuctionContract, }, { - name: "bad round number", - t: &ExpressLaneTracker{ - auctionContractAddr: common.Address{'a'}, - roundTimingInfo: defaultTestRoundTimingInfo(time.Now()), - chainConfig: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - }, - }, + name: "bad round number", + t: defaultTestTrackerWithConfig(common.Address{'a'}, defaultTestRoundTimingInfo(time.Now())), controller: common.Address{'b'}, - sub: &timeboost.ExpressLaneSubmission{ - ChainId: big.NewInt(1), - AuctionContractAddress: common.Address{'a'}, - Transaction: &types.Transaction{}, - Signature: []byte{'b'}, - Round: 100, - }, + sub: func() *timeboost.ExpressLaneSubmission { + submission := cloneSubmission(validSubmission) + submission.AuctionContractAddress = common.Address{'a'} + submission.Round = 100 + return submission + }(), expectedErr: timeboost.ErrBadRoundNumber, }, { - name: "malformed signature", - t: &ExpressLaneTracker{ - auctionContractAddr: common.Address{'a'}, - roundTimingInfo: defaultTestRoundTimingInfo(time.Now()), - chainConfig: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - }, - }, + name: "malformed signature", + t: defaultTestTrackerWithConfig(common.Address{'a'}, defaultTestRoundTimingInfo(time.Now())), controller: common.Address{'b'}, sub: &timeboost.ExpressLaneSubmission{ @@ -149,14 +137,8 @@ func Test_expressLaneService_validateExpressLaneTx(t *testing.T) { expectedErr: timeboost.ErrMalformedData, }, { - name: "wrong signature", - t: &ExpressLaneTracker{ - auctionContractAddr: common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), - roundTimingInfo: defaultTestRoundTimingInfo(time.Now()), - chainConfig: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - }, - }, + name: "wrong signature", + t: defaultTestTrackerWithConfig(common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), defaultTestRoundTimingInfo(time.Now())), controller: common.Address{'b'}, sub: buildInvalidSignatureSubmission(t, common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6")), expectedErr: timeboost.ErrNotExpressLaneController, @@ -164,45 +146,26 @@ func Test_expressLaneService_validateExpressLaneTx(t *testing.T) { { name: "no onchain controller", - t: &ExpressLaneTracker{ - auctionContractAddr: common.Address{'a'}, - roundTimingInfo: defaultTestRoundTimingInfo(time.Now()), - chainConfig: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - }, - }, - sub: &timeboost.ExpressLaneSubmission{ - ChainId: big.NewInt(1), - AuctionContractAddress: common.Address{'a'}, - Transaction: &types.Transaction{}, - Signature: []byte{'b'}, - }, + t: defaultTestTrackerWithConfig(common.Address{'a'}, defaultTestRoundTimingInfo(time.Now())), + sub: func() *timeboost.ExpressLaneSubmission { + submission := cloneSubmission(validSubmission) + submission.AuctionContractAddress = common.Address{'a'} + return submission + }(), expectedErr: timeboost.ErrNoOnchainController, }, { - name: "not express lane controller", - t: &ExpressLaneTracker{ - auctionContractAddr: common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), - roundTimingInfo: defaultTestRoundTimingInfo(time.Now()), - chainConfig: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - }, - }, + name: "not express lane controller", + t: defaultTestTrackerWithConfig(common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), defaultTestRoundTimingInfo(time.Now())), controller: common.Address{'b'}, - sub: buildValidSubmission(t, common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), testPriv, 0), + sub: validSubmission, expectedErr: timeboost.ErrNotExpressLaneController, }, { - name: "OK", - t: &ExpressLaneTracker{ - auctionContractAddr: common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), - roundTimingInfo: defaultTestRoundTimingInfo(time.Now()), - chainConfig: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - }, - }, + name: "OK", + t: defaultTestTrackerWithConfig(common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), defaultTestRoundTimingInfo(time.Now())), controller: crypto.PubkeyToAddress(testPriv.PublicKey), - sub: buildValidSubmission(t, common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), testPriv, 0), + sub: validSubmission, valid: true, }, } @@ -225,18 +188,12 @@ func Test_expressLaneService_validateExpressLaneTx(t *testing.T) { func Test_expressLaneService_validateExpressLaneTx_gracePeriod(t *testing.T) { auctionContractAddr := common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6") - tr := &ExpressLaneTracker{ - auctionContractAddr: auctionContractAddr, - roundTimingInfo: timeboost.RoundTimingInfo{ - Offset: time.Now(), - Round: time.Second * 10, - AuctionClosing: time.Second * 5, - }, - earlySubmissionGrace: time.Second * 2, - chainConfig: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - }, - } + tr := defaultTestTrackerWithConfig(common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), timeboost.RoundTimingInfo{ + Offset: time.Now(), + Round: time.Second * 10, + AuctionClosing: time.Second * 5, + }) + tr.earlySubmissionGrace = time.Second * 2 tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) tr.roundControl.Store(1, crypto.PubkeyToAddress(testPriv2.PublicKey)) @@ -293,7 +250,7 @@ func (s *stubPublisher) PublishTimeboostedTransaction(parentCtx context.Context, func Test_expressLaneService_sequenceExpressLaneSubmission_nonceTooLow(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - tr := &ExpressLaneTracker{} + tr := defaultTestTracker() tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) els := &expressLaneService{ roundInfo: containers.NewLruCache[uint64, *expressLaneRoundInfo](8), @@ -314,7 +271,7 @@ func Test_expressLaneService_sequenceExpressLaneSubmission_duplicateNonce(t *tes defer cancel() redisUrl := redisutil.CreateTestRedis(ctx, t) timingInfo := defaultTestRoundTimingInfo(time.Now()) - tr := &ExpressLaneTracker{} + tr := defaultTestTracker() tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) els := &expressLaneService{ roundInfo: containers.NewLruCache[uint64, *expressLaneRoundInfo](8), @@ -361,7 +318,7 @@ func Test_expressLaneService_sequenceExpressLaneSubmission_outOfOrder(t *testing defer cancel() redisUrl := redisutil.CreateTestRedis(ctx, t) timingInfo := defaultTestRoundTimingInfo(time.Now()) - tr := &ExpressLaneTracker{} + tr := defaultTestTracker() tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) els := &expressLaneService{ roundInfo: containers.NewLruCache[uint64, *expressLaneRoundInfo](8), @@ -422,7 +379,7 @@ func Test_expressLaneService_sequenceExpressLaneSubmission_erroredTx(t *testing. defer cancel() redisUrl := redisutil.CreateTestRedis(ctx, t) timingInfo := defaultTestRoundTimingInfo(time.Now()) - tr := &ExpressLaneTracker{} + tr := defaultTestTracker() tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) els := &expressLaneService{ roundInfo: containers.NewLruCache[uint64, *expressLaneRoundInfo](8), @@ -465,7 +422,7 @@ func Test_expressLaneService_syncFromRedis(t *testing.T) { defer cancel() redisUrl := redisutil.CreateTestRedis(ctx, t) timingInfo := defaultTestRoundTimingInfo(time.Now()) - tr := &ExpressLaneTracker{} + tr := defaultTestTracker() tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) els1 := &expressLaneService{ roundInfo: containers.NewLruCache[uint64, *expressLaneRoundInfo](8), @@ -506,7 +463,7 @@ func Test_expressLaneService_syncFromRedis(t *testing.T) { time.Sleep(time.Second) // wait for parallel redis update threads to complete - tr2 := &ExpressLaneTracker{} + tr2 := defaultTestTracker() tr2.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) els2 := &expressLaneService{ roundInfo: containers.NewLruCache[uint64, *expressLaneRoundInfo](8), @@ -609,7 +566,7 @@ func Test_expressLaneService_dontCareSequence(t *testing.T) { defer cancel() timingInfo := defaultTestRoundTimingInfo(time.Now()) - tr := &ExpressLaneTracker{} + tr := defaultTestTracker() tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) mp := &struct { @@ -653,7 +610,7 @@ func Test_expressLaneService_mixedSequenceNumbersDontCareFirst(t *testing.T) { defer cancel() timingInfo := defaultTestRoundTimingInfo(time.Now()) - tr := &ExpressLaneTracker{} + tr := defaultTestTracker() tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) publishedTxs := []*types.Transaction{} @@ -724,7 +681,7 @@ func Test_expressLaneService_mixedSequenceNumbersNormalFirst(t *testing.T) { defer cancel() timingInfo := defaultTestRoundTimingInfo(time.Now()) - tr := &ExpressLaneTracker{} + tr := defaultTestTracker() tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) publishedTxs := []*types.Transaction{} @@ -795,7 +752,7 @@ func Test_expressLaneService_mixedSequenceNumbersIntermixed(t *testing.T) { defer cancel() timingInfo := defaultTestRoundTimingInfo(time.Now()) - tr := &ExpressLaneTracker{} + tr := defaultTestTracker() tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) publishedTxs := []*types.Transaction{} @@ -870,7 +827,7 @@ func Test_expressLaneService_dontCareWithQueuedTransactions(t *testing.T) { defer cancel() timingInfo := defaultTestRoundTimingInfo(time.Now()) - tr := &ExpressLaneTracker{} + tr := defaultTestTracker() tr.roundControl.Store(0, crypto.PubkeyToAddress(testPriv.PublicKey)) publishedTxs := []*types.Transaction{} @@ -949,13 +906,7 @@ func Test_expressLaneService_dontCareWithQueuedTransactions(t *testing.T) { func Benchmark_expressLaneService_validateExpressLaneTx(b *testing.B) { b.StopTimer() addr := crypto.PubkeyToAddress(testPriv.PublicKey) - tr := &ExpressLaneTracker{ - auctionContractAddr: common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), - roundTimingInfo: defaultTestRoundTimingInfo(time.Now()), - chainConfig: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - }, - } + tr := defaultTestTrackerWithConfig(common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), defaultTestRoundTimingInfo(time.Now())) tr.roundControl.Store(0, addr) sub := buildValidSubmission(b, common.HexToAddress("0x2Aef36410182881a4b13664a1E079762D7F716e6"), testPriv, 0) @@ -1045,3 +996,43 @@ func buildValidSubmissionWithSeqAndTx( b.Signature = signature return b } + +func cloneSubmission(original *timeboost.ExpressLaneSubmission) *timeboost.ExpressLaneSubmission { + return &timeboost.ExpressLaneSubmission{ + ChainId: new(big.Int).Set(original.ChainId), + AuctionContractAddress: original.AuctionContractAddress, + Transaction: original.Transaction, + Signature: append([]byte{}, original.Signature...), + Round: original.Round, + SequenceNumber: original.SequenceNumber, + } +} + +func defaultTestTracker() *ExpressLaneTracker { + return &ExpressLaneTracker{ + maxTxSize: uint64(DefaultSequencerConfig.MaxTxDataSize), // #nosec G115 + } +} + +func defaultTestTrackerWithConfig( + auctionAddr common.Address, + roundTimingInfo timeboost.RoundTimingInfo, +) *ExpressLaneTracker { + return &ExpressLaneTracker{ + auctionContractAddr: auctionAddr, + roundTimingInfo: roundTimingInfo, + chainConfig: ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + }, + maxTxSize: uint64(DefaultSequencerConfig.MaxTxDataSize), // #nosec G115 + } +} + +func defaultTestTrackerWithChainID(chainID int64) *ExpressLaneTracker { + return &ExpressLaneTracker{ + chainConfig: ¶ms.ChainConfig{ + ChainID: big.NewInt(chainID), + }, + maxTxSize: uint64(DefaultSequencerConfig.MaxTxDataSize), // #nosec G115 + } +} diff --git a/execution/gethexec/express_lane_tracker.go b/execution/gethexec/express_lane_tracker.go index bdbcbc88e2..f428f3bb85 100644 --- a/execution/gethexec/express_lane_tracker.go +++ b/execution/gethexec/express_lane_tracker.go @@ -37,6 +37,7 @@ type ExpressLaneTracker struct { roundTimingInfo timeboost.RoundTimingInfo pollInterval time.Duration chainConfig *params.ChainConfig + maxTxSize uint64 // maximum transaction size the sequencer will accept headerProvider HeaderProvider auctionContract *express_lane_auctiongen.ExpressLaneAuction @@ -54,7 +55,11 @@ func NewExpressLaneTracker( auctionContract *express_lane_auctiongen.ExpressLaneAuction, auctionContractAddr common.Address, chainConfig *params.ChainConfig, - earlySubmissionGrace time.Duration) *ExpressLaneTracker { + maxTxSize uint64, + earlySubmissionGrace time.Duration) (*ExpressLaneTracker, error) { + if err := ValidateMaxTxDataSize(maxTxSize); err != nil { + return nil, err + } return &ExpressLaneTracker{ roundTimingInfo: roundTimingInfo, pollInterval: pollInterval, @@ -63,8 +68,9 @@ func NewExpressLaneTracker( auctionContractAddr: auctionContractAddr, earlySubmissionGrace: earlySubmissionGrace, chainConfig: chainConfig, + maxTxSize: maxTxSize, useLogs: false, // default to use contract polling - } + }, nil } func (t *ExpressLaneTracker) Start(ctxIn context.Context) { @@ -93,6 +99,10 @@ func (t *ExpressLaneTracker) ValidateExpressLaneTx(msg *timeboost.ExpressLaneSub if msg == nil || msg.Transaction == nil || msg.Signature == nil { return timeboost.ErrMalformedData } + txSize := msg.Transaction.Size() + if txSize > t.maxTxSize { + return errors.Wrapf(timeboost.ErrOversizedData, "express lane tx size %d exceeds maximum allowed size %d", txSize, t.maxTxSize) + } if msg.ChainId.Cmp(t.chainConfig.ChainID) != 0 { return errors.Wrapf(timeboost.ErrWrongChainId, "express lane tx chain ID %d does not match current chain ID %d", msg.ChainId, t.chainConfig.ChainID) } diff --git a/execution/gethexec/node.go b/execution/gethexec/node.go index 41dc140bb0..a3ba92ec75 100644 --- a/execution/gethexec/node.go +++ b/execution/gethexec/node.go @@ -595,15 +595,19 @@ func (n *ExecutionNode) InitializeTimeboost(ctx context.Context, chainConfig *pa return err } - expressLaneTracker := NewExpressLaneTracker( + expressLaneTracker, err := NewExpressLaneTracker( *roundTimingInfo, execNodeConfig.Sequencer.MaxBlockSpeed, n.Backend.APIBackend(), auctionContract, auctionContractAddr, chainConfig, + uint64(execNodeConfig.Sequencer.MaxTxDataSize), // #nosec G115 execNodeConfig.Sequencer.Timeboost.EarlySubmissionGrace, ) + if err != nil { + return fmt.Errorf("error creating express lane tracker: %w", err) + } n.TxPreChecker.SetExpressLaneTracker(expressLaneTracker) diff --git a/execution/gethexec/sequencer.go b/execution/gethexec/sequencer.go index f8658bda21..459ec83d2d 100644 --- a/execution/gethexec/sequencer.go +++ b/execution/gethexec/sequencer.go @@ -146,8 +146,9 @@ func (c *SequencerConfig) Validate() error { if c.expectedSurplusSoftThreshold < c.expectedSurplusHardThreshold { return errors.New("expected-surplus-soft-threshold cannot be lower than expected-surplus-hard-threshold") } - if c.MaxTxDataSize > arbostypes.MaxL2MessageSize-50000 { - return errors.New("max-tx-data-size too large for MaxL2MessageSize") + maxTxDataSize := uint64(c.MaxTxDataSize) // #nosec G115 + if err := ValidateMaxTxDataSize(maxTxDataSize); err != nil { + return err } if c.Timeboost.Enable { if len(c.Timeboost.AuctionContractAddress) > 0 && !common.IsHexAddress(c.Timeboost.AuctionContractAddress) { @@ -171,6 +172,18 @@ func (c *SequencerConfig) Validate() error { return nil } +func ValidateMaxTxDataSize(maxTxDataSize uint64) error { + // tighter limit https://github.com/OffchainLabs/nitro/commit/ed015e752d7d24e59ec9e6f894fe1a26ffa19036 + // The default block gas limit can fit 1523 txs + // Each Tx adds an 8-byte of length prefix. + // 50K is enoguh to add 1523 times 8 bytes and still stay in an L2 message + const maxConfigurableTxDataSize = arbostypes.MaxL2MessageSize - 50000 + if maxTxDataSize > maxConfigurableTxDataSize { + return fmt.Errorf("max-tx-data-size %d exceeds maximum allowed value of %d", maxTxDataSize, maxConfigurableTxDataSize) + } + return nil +} + type SequencerConfigFetcher func() *SequencerConfig var DefaultSequencerConfig = SequencerConfig{ @@ -598,14 +611,10 @@ func (s *Sequencer) PublishAuctionResolutionTransaction(ctx context.Context, tx if !s.expressLaneService.roundTimingInfo.IsWithinAuctionCloseWindow(arrivalTime) { return fmt.Errorf("transaction arrival time not within auction closure window: %v", arrivalTime) } - txBytes, err := tx.MarshalBinary() - if err != nil { - return err - } log.Info("Prioritizing auction resolution transaction from auctioneer", "txHash", tx.Hash().Hex()) s.timeboostAuctionResolutionTxQueue <- txQueueItem{ tx: tx, - txSize: len(txBytes), + txSize: int(tx.Size()), options: nil, resultChan: make(chan error, 1), returnedResult: &atomic.Bool{}, @@ -684,11 +693,6 @@ func (s *Sequencer) publishTransactionToQueue(queueCtx context.Context, tx *type return types.ErrTxTypeNotSupported } - txBytes, err := tx.MarshalBinary() - if err != nil { - return err - } - if s.config().Timeboost.Enable && s.expressLaneService != nil { if !isExpressLaneController && s.expressLaneService.currentRoundHasController() { time.Sleep(s.config().Timeboost.ExpressLaneAdvantage) @@ -702,7 +706,7 @@ func (s *Sequencer) publishTransactionToQueue(queueCtx context.Context, tx *type queueItem := txQueueItem{ tx: tx, - txSize: len(txBytes), + txSize: int(tx.Size()), options: options, resultChan: resultChan, returnedResult: &atomic.Bool{}, diff --git a/system_tests/timeboost_test.go b/system_tests/timeboost_test.go index b2feecb77b..d6ed80ec9a 100644 --- a/system_tests/timeboost_test.go +++ b/system_tests/timeboost_test.go @@ -1706,15 +1706,17 @@ func setupExpressLaneAuction( roundTimingInfo, err := gethexec.GetRoundTimingInfo(auctionContract) Require(t, err) - expressLaneTracker := gethexec.NewExpressLaneTracker( + expressLaneTracker, err := gethexec.NewExpressLaneTracker( *roundTimingInfo, builderSeq.execConfig.Sequencer.MaxBlockSpeed, builderSeq.L2.ExecNode.Backend.APIBackend(), auctionContract, proxyAddr, builderSeq.chainConfig, + uint64(builderSeq.execConfig.Sequencer.MaxTxDataSize), // #nosec G115 builderSeq.execConfig.Sequencer.Timeboost.EarlySubmissionGrace, ) + Require(t, err) err = builderSeq.L2.ExecNode.Sequencer.InitializeExpressLaneService( auctioneerAddr, diff --git a/timeboost/errors.go b/timeboost/errors.go index 012859b46a..120b7e3c39 100644 --- a/timeboost/errors.go +++ b/timeboost/errors.go @@ -16,4 +16,5 @@ var ( ErrDuplicateSequenceNumber = errors.New("SEQUENCE_NUMBER_ALREADY_SEEN") ErrSequenceNumberTooLow = errors.New("SEQUENCE_NUMBER_TOO_LOW") ErrTooManyBids = errors.New("PER_ROUND_BID_LIMIT_REACHED") + ErrOversizedData = errors.New("OVERSIZED_DATA") ) From c23ed203323c78d20d05dbf2affdf99b9ec7df30 Mon Sep 17 00:00:00 2001 From: Kolby Moroz Liebl <31669092+KolbyML@users.noreply.github.com> Date: Wed, 10 Dec 2025 07:38:22 -0700 Subject: [PATCH 2/2] Fix lint --- execution/gethexec/sequencer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/execution/gethexec/sequencer.go b/execution/gethexec/sequencer.go index 459ec83d2d..e1deadf60f 100644 --- a/execution/gethexec/sequencer.go +++ b/execution/gethexec/sequencer.go @@ -614,7 +614,7 @@ func (s *Sequencer) PublishAuctionResolutionTransaction(ctx context.Context, tx log.Info("Prioritizing auction resolution transaction from auctioneer", "txHash", tx.Hash().Hex()) s.timeboostAuctionResolutionTxQueue <- txQueueItem{ tx: tx, - txSize: int(tx.Size()), + txSize: int(tx.Size()), // #nosec G115 options: nil, resultChan: make(chan error, 1), returnedResult: &atomic.Bool{}, @@ -706,7 +706,7 @@ func (s *Sequencer) publishTransactionToQueue(queueCtx context.Context, tx *type queueItem := txQueueItem{ tx: tx, - txSize: int(tx.Size()), + txSize: int(tx.Size()), // #nosec G115 options: options, resultChan: resultChan, returnedResult: &atomic.Bool{},