diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 598a877572..ab1294cddb 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2025, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md package arbnode @@ -104,6 +104,7 @@ type BatchPoster struct { inbox *InboxTracker streamer *TransactionStreamer arbOSVersionGetter execution.ArbOSVersionGetter + chainConfig *params.ChainConfig config BatchPosterConfigFetcher seqInbox *bridgegen.SequencerInbox syncMonitor *SyncMonitor @@ -359,6 +360,7 @@ type BatchPosterOpts struct { DAPWriters []daprovider.Writer ParentChainID *big.Int DAPReaders *daprovider.DAProviderRegistry + ChainConfig *params.ChainConfig } func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, error) { @@ -405,6 +407,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e inbox: opts.Inbox, streamer: opts.Streamer, arbOSVersionGetter: opts.VersionGetter, + chainConfig: opts.ChainConfig, syncMonitor: opts.SyncMonitor, config: opts.Config, seqInbox: seqInbox, @@ -903,6 +906,7 @@ type batchSegments struct { recompressionLevel int newUncompressedSize int totalUncompressedSize int + maxUncompressedSize int lastCompressedSize int trailingHeaders int // how many trailing segments are headers isDone bool @@ -920,7 +924,7 @@ type buildingBatch struct { firstUsefulMsg *arbostypes.MessageWithMetadata } -func (b *BatchPoster) newBatchSegments(ctx context.Context, firstDelayed uint64, use4844 bool, usingAltDA bool) (*batchSegments, error) { +func (b *BatchPoster) newBatchSegments(ctx context.Context, firstDelayed uint64, use4844 bool, usingAltDA bool, arbosVersion uint64) (*batchSegments, error) { config := b.config() var maxSize int @@ -983,12 +987,13 @@ func (b *BatchPoster) newBatchSegments(ctx context.Context, firstDelayed uint64, recompressionLevel = compressionLevel } return &batchSegments{ - compressedBuffer: compressedBuffer, - compressedWriter: brotli.NewWriterLevel(compressedBuffer, compressionLevel), - sizeLimit: maxSize, - recompressionLevel: recompressionLevel, - rawSegments: make([][]byte, 0, 128), - delayedMsg: firstDelayed, + compressedBuffer: compressedBuffer, + compressedWriter: brotli.NewWriterLevel(compressedBuffer, compressionLevel), + sizeLimit: maxSize, + recompressionLevel: recompressionLevel, + rawSegments: make([][]byte, 0, 128), + delayedMsg: firstDelayed, + maxUncompressedSize: int(b.chainConfig.MaxUncompressedBatchSize(arbosVersion)), // #nosec G115 }, nil } @@ -1003,8 +1008,8 @@ func (s *batchSegments) recompressAll() error { return err } } - if s.totalUncompressedSize > arbstate.MaxDecompressedLen { - return fmt.Errorf("batch size %v exceeds maximum decompressed length %v", s.totalUncompressedSize, arbstate.MaxDecompressedLen) + if s.totalUncompressedSize > s.maxUncompressedSize { + return fmt.Errorf("batch size %v exceeds maximum uncompressed length %v", s.totalUncompressedSize, s.maxUncompressedSize) } if len(s.rawSegments) >= arbstate.MaxSegmentsPerSequencerMessage { return fmt.Errorf("number of raw segments %v excees maximum number %v", len(s.rawSegments), arbstate.MaxSegmentsPerSequencerMessage) @@ -1014,10 +1019,10 @@ func (s *batchSegments) recompressAll() error { func (s *batchSegments) testForOverflow(isHeader bool) (bool, error) { // we've reached the max decompressed size - if s.totalUncompressedSize > arbstate.MaxDecompressedLen { - log.Info("Batch full: max decompressed length exceeded", + if s.totalUncompressedSize > s.maxUncompressedSize { + log.Info("Batch full: max uncompressed length exceeded", "current", s.totalUncompressedSize, - "max", arbstate.MaxDecompressedLen, + "max", s.maxUncompressedSize, "isHeader", isHeader) return true, nil } @@ -1410,6 +1415,11 @@ func (b *BatchPoster) MaybePostSequencerBatch(ctx context.Context) (bool, error) if dbBatchCount > batchPosition.NextSeqNum { return false, fmt.Errorf("attempting to post batch %v, but the local inbox tracker database already has %v batches", batchPosition.NextSeqNum, dbBatchCount) } + + arbOSVersion, err := b.arbOSVersionGetter.ArbOSVersionForMessageIndex(arbutil.MessageIndex(arbmath.SaturatingUSub(uint64(batchPosition.MessageCount), 1))).Await(ctx) + if err != nil { + return false, err + } if b.building == nil || b.building.startMsgCount != batchPosition.MessageCount { latestHeader, err := b.l1Reader.LastHeader(ctx) if err != nil { @@ -1423,10 +1433,6 @@ func (b *BatchPoster) MaybePostSequencerBatch(ctx context.Context) (bool, error) config.Post4844Blobs && latestHeader.ExcessBlobGas != nil && latestHeader.BlobGasUsed != nil { - arbOSVersion, err := b.arbOSVersionGetter.ArbOSVersionForMessageIndex(arbutil.MessageIndex(arbmath.SaturatingUSub(uint64(batchPosition.MessageCount), 1))).Await(ctx) - if err != nil { - return false, err - } if arbOSVersion >= params.ArbosVersion_20 { if config.IgnoreBlobPrice { use4844 = true @@ -1483,7 +1489,7 @@ func (b *BatchPoster) MaybePostSequencerBatch(ctx context.Context) (bool, error) // Only use 4844 batching when posting to EthDA use4844 = use4844 && buildingForEthDA usingAltDA := !buildingForEthDA - segments, err := b.newBatchSegments(ctx, batchPosition.DelayedMessageCount, use4844, usingAltDA) + segments, err := b.newBatchSegments(ctx, batchPosition.DelayedMessageCount, use4844, usingAltDA, arbOSVersion) if err != nil { return false, err } @@ -1928,7 +1934,7 @@ func (b *BatchPoster) MaybePostSequencerBatch(ctx context.Context) (bool, error) b.building.muxBackend.seqMsg = seqMsg b.building.muxBackend.delayedInboxStart = batchPosition.DelayedMessageCount b.building.muxBackend.SetPositionWithinMessage(0) - simMux := arbstate.NewInboxMultiplexer(b.building.muxBackend, batchPosition.DelayedMessageCount, dapReaders, daprovider.KeysetValidate) + simMux := arbstate.NewInboxMultiplexer(b.building.muxBackend, batchPosition.DelayedMessageCount, dapReaders, daprovider.KeysetValidate, b.chainConfig, arbOSVersion) log.Debug("Begin checking the correctness of batch against inbox multiplexer", "startMsgSeqNum", batchPosition.MessageCount, "endMsgSeqNum", b.building.msgCount-1) for i := batchPosition.MessageCount; i < b.building.msgCount; i++ { msg, err := simMux.Pop(ctx) diff --git a/arbnode/delayed_seq_reorg_test.go b/arbnode/delayed_seq_reorg_test.go index 8505e20188..6d2a54d9fb 100644 --- a/arbnode/delayed_seq_reorg_test.go +++ b/arbnode/delayed_seq_reorg_test.go @@ -10,17 +10,22 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbos/arbostypes" + "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/solgen/go/bridgegen" ) +const arbosVersion = params.ArbosVersion_50 + func TestSequencerReorgFromDelayed(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() exec, streamer, db, _ := NewTransactionStreamerForTest(t, ctx, common.Address{}) - tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig) + arbosVersionGetter := execution.ConstArbosVersionGetter{Version: arbosVersion} + tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig, &arbosVersionGetter, arbosVersion) Require(t, err) err = streamer.Start(ctx) @@ -220,7 +225,9 @@ func TestSequencerReorgFromLastDelayedMsg(t *testing.T) { defer cancel() exec, streamer, db, _ := NewTransactionStreamerForTest(t, ctx, common.Address{}) - tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig) + arbosVersionGetter := execution.ConstArbosVersionGetter{Version: arbosVersion} + + tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig, &arbosVersionGetter, arbosVersion) Require(t, err) err = streamer.Start(ctx) diff --git a/arbnode/inbox_tracker.go b/arbnode/inbox_tracker.go index 32c73e60c2..7ed1fa89c5 100644 --- a/arbnode/inbox_tracker.go +++ b/arbnode/inbox_tracker.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2025, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md package arbnode @@ -25,7 +25,9 @@ import ( "github.com/offchainlabs/nitro/broadcaster" "github.com/offchainlabs/nitro/broadcaster/message" "github.com/offchainlabs/nitro/daprovider" + "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/staker" + "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/containers" ) @@ -35,24 +37,28 @@ var ( ) type InboxTracker struct { - db ethdb.Database - txStreamer *TransactionStreamer - mutex sync.Mutex - validator *staker.BlockValidator - dapReaders *daprovider.DAProviderRegistry - snapSyncConfig SnapSyncConfig + db ethdb.Database + txStreamer *TransactionStreamer + mutex sync.Mutex + validator *staker.BlockValidator + dapReaders *daprovider.DAProviderRegistry + snapSyncConfig SnapSyncConfig + arbosVersionGetter execution.ArbOSVersionGetter + lastSeenArbosVersion uint64 batchMetaMutex sync.Mutex batchMeta *containers.LruCache[uint64, BatchMetadata] } -func NewInboxTracker(db ethdb.Database, txStreamer *TransactionStreamer, dapReaders *daprovider.DAProviderRegistry, snapSyncConfig SnapSyncConfig) (*InboxTracker, error) { +func NewInboxTracker(db ethdb.Database, txStreamer *TransactionStreamer, dapReaders *daprovider.DAProviderRegistry, snapSyncConfig SnapSyncConfig, arbosVersionGetter execution.ArbOSVersionGetter, initialArbosVersion uint64) (*InboxTracker, error) { tracker := &InboxTracker{ - db: db, - txStreamer: txStreamer, - dapReaders: dapReaders, - batchMeta: containers.NewLruCache[uint64, BatchMetadata](1000), - snapSyncConfig: snapSyncConfig, + db: db, + txStreamer: txStreamer, + dapReaders: dapReaders, + batchMeta: containers.NewLruCache[uint64, BatchMetadata](1000), + snapSyncConfig: snapSyncConfig, + arbosVersionGetter: arbosVersionGetter, + lastSeenArbosVersion: initialArbosVersion, } return tracker, nil } @@ -781,7 +787,20 @@ func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client *ethclien ctx: ctx, client: client, } - multiplexer := arbstate.NewInboxMultiplexer(backend, prevbatchmeta.DelayedMessageCount, t.dapReaders, daprovider.KeysetValidate) + recentArbosVersion, err := t.arbosVersionGetter.ArbOSVersionForMessageIndex(arbmath.SaturatingUSub(prevbatchmeta.MessageCount, 1)).Await(ctx) + if err != nil { + recentArbosVersion = t.lastSeenArbosVersion + } else { + t.lastSeenArbosVersion = recentArbosVersion + } + multiplexer := arbstate.NewInboxMultiplexer( + backend, + prevbatchmeta.DelayedMessageCount, + t.dapReaders, + daprovider.KeysetValidate, + t.txStreamer.chainConfig, + recentArbosVersion, + ) batchMessageCounts := make(map[uint64]arbutil.MessageIndex) currentPos := prevbatchmeta.MessageCount + 1 for { diff --git a/arbnode/mel/extraction/message_extraction_function.go b/arbnode/mel/extraction/message_extraction_function.go index 767a91f5e3..a869fce871 100644 --- a/arbnode/mel/extraction/message_extraction_function.go +++ b/arbnode/mel/extraction/message_extraction_function.go @@ -9,11 +9,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode/mel" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/daprovider" + "github.com/offchainlabs/nitro/execution" ) // Defines a method that can read a delayed message from an external database. @@ -55,11 +58,14 @@ func ExtractMessages( delayedMsgDatabase DelayedMessageDatabase, receiptFetcher ReceiptFetcher, txsFetcher TransactionsFetcher, + chainConfig *params.ChainConfig, + arbosVersionGetter execution.ArbOSVersionGetter, ) (*mel.State, []*arbostypes.MessageWithMetadata, []*mel.DelayedInboxMessage, error) { return extractMessagesImpl( ctx, inputState, parentChainHeader, + chainConfig, dataProviders, delayedMsgDatabase, txsFetcher, @@ -71,6 +77,7 @@ func ExtractMessages( messagesFromBatchSegments, arbstate.ParseSequencerMessage, arbostypes.ParseBatchPostingReportMessageFields, + arbosVersionGetter, ) } @@ -81,6 +88,7 @@ func extractMessagesImpl( ctx context.Context, inputState *mel.State, parentChainHeader *types.Header, + chainConfig *params.ChainConfig, dataProviders *daprovider.DAProviderRegistry, delayedMsgDatabase DelayedMessageDatabase, txsFetcher TransactionsFetcher, @@ -92,6 +100,7 @@ func extractMessagesImpl( extractBatchMessages batchMsgExtractionFunc, parseSequencerMessage sequencerMessageParserFunc, parseBatchPostingReport batchPostingReportParserFunc, + arbosVersionGetter execution.ArbOSVersionGetter, ) (*mel.State, []*arbostypes.MessageWithMetadata, []*mel.DelayedInboxMessage, error) { state := inputState.Clone() @@ -193,6 +202,11 @@ func extractMessagesImpl( return nil, nil, nil, errors.New("encountered initialize message that is not the first delayed message and the first batch ") } + arbosVersion, err := arbosVersionGetter.ArbOSVersionForMessageIndex(arbutil.MessageIndex(state.MsgCount)).Await(ctx) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to get Arbos version for message index %d: %w", state.MsgCount, err) + } + rawSequencerMsg, err := parseSequencerMessage( ctx, batch.SequenceNumber, @@ -200,6 +214,8 @@ func extractMessagesImpl( serialized, dataProviders, daprovider.KeysetValidate, + chainConfig, + arbosVersion, ) if err != nil { return nil, nil, nil, err diff --git a/arbnode/mel/extraction/message_extraction_function_test.go b/arbnode/mel/extraction/message_extraction_function_test.go index 735c22b0ea..56fbb91a4d 100644 --- a/arbnode/mel/extraction/message_extraction_function_test.go +++ b/arbnode/mel/extraction/message_extraction_function_test.go @@ -12,11 +12,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode/mel" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/daprovider" + "github.com/offchainlabs/nitro/execution" ) func TestExtractMessages(t *testing.T) { @@ -31,7 +34,7 @@ func TestExtractMessages(t *testing.T) { lookupDelayedMsgs func(context.Context, *mel.State, *types.Header, ReceiptFetcher, TransactionsFetcher) ([]*mel.DelayedInboxMessage, error) serializer func(context.Context, *mel.SequencerInboxBatch, *types.Transaction, uint, ReceiptFetcher) ([]byte, error) parseReport func(io.Reader) (*big.Int, common.Address, common.Hash, uint64, *big.Int, uint64, error) - parseSequencerMsg func(context.Context, uint64, common.Hash, []byte, arbstate.DapReaderSource, daprovider.KeysetValidationMode) (*arbstate.SequencerMessage, error) + parseSequencerMsg func(context.Context, uint64, common.Hash, []byte, arbstate.DapReaderSource, daprovider.KeysetValidationMode, *params.ChainConfig, uint64) (*arbstate.SequencerMessage, error) extractBatchMessages func(context.Context, *mel.State, *arbstate.SequencerMessage, DelayedMessageDatabase) ([]*arbostypes.MessageWithMetadata, error) expectedError string expectedMsgCount uint64 @@ -150,6 +153,8 @@ func TestExtractMessages(t *testing.T) { nil, nil, txsFetcher, + chaininfo.ArbitrumDevTestChainConfig(), + &execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50}, ) } else { // Test the internal extractMessagesImpl function @@ -157,6 +162,7 @@ func TestExtractMessages(t *testing.T) { ctx, melState, header, + chaininfo.ArbitrumDevTestChainConfig(), nil, nil, txsFetcher, @@ -168,6 +174,7 @@ func TestExtractMessages(t *testing.T) { tt.extractBatchMessages, tt.parseSequencerMsg, tt.parseReport, + &execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50}, ) } @@ -321,6 +328,8 @@ func successfulParseSequencerMsg( data []byte, dapReaders arbstate.DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, + chainConfig *params.ChainConfig, + arbosVersion uint64, ) (*arbstate.SequencerMessage, error) { return nil, nil } @@ -332,6 +341,8 @@ func failingParseSequencerMsg( data []byte, dapReaders arbstate.DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, + chainConfig *params.ChainConfig, + arbosVersion uint64, ) (*arbstate.SequencerMessage, error) { return nil, errors.New("failed to parse sequencer message") } diff --git a/arbnode/mel/extraction/types.go b/arbnode/mel/extraction/types.go index 98829da73f..50f927841e 100644 --- a/arbnode/mel/extraction/types.go +++ b/arbnode/mel/extraction/types.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode/mel" "github.com/offchainlabs/nitro/arbos/arbostypes" @@ -64,6 +65,8 @@ type sequencerMessageParserFunc func( data []byte, dapReaders arbstate.DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, + chainConfig *params.ChainConfig, + arbosVersion uint64, ) (*arbstate.SequencerMessage, error) // Defines a function that can extract messages from a batch. diff --git a/arbnode/mel/runner/mel.go b/arbnode/mel/runner/mel.go index 369af33ccc..9cf0e4bdae 100644 --- a/arbnode/mel/runner/mel.go +++ b/arbnode/mel/runner/mel.go @@ -11,12 +11,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode/mel" "github.com/offchainlabs/nitro/arbnode/mel/extraction" "github.com/offchainlabs/nitro/bold/containers/fsm" "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/daprovider" + "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/util/stopwaiter" ) @@ -37,6 +39,8 @@ type ParentChainReader interface { type MessageExtractor struct { stopwaiter.StopWaiter parentChainReader ParentChainReader + chainConfig *params.ChainConfig + arbosVersionGetter execution.ArbOSVersionGetter addrs *chaininfo.RollupAddresses melDB *Database msgConsumer mel.MessageConsumer @@ -51,6 +55,7 @@ type MessageExtractor struct { // to be used when extracting messages from the parent chain. func NewMessageExtractor( parentChainReader ParentChainReader, + chainConfig *params.ChainConfig, rollupAddrs *chaininfo.RollupAddresses, melDB *Database, msgConsumer mel.MessageConsumer, @@ -67,6 +72,7 @@ func NewMessageExtractor( } return &MessageExtractor{ parentChainReader: parentChainReader, + chainConfig: chainConfig, addrs: rollupAddrs, melDB: melDB, msgConsumer: msgConsumer, @@ -223,6 +229,8 @@ func (m *MessageExtractor) Act(ctx context.Context) (time.Duration, error) { m.melDB, receiptFetcher, txsFetcher, + m.chainConfig, + m.arbosVersionGetter, ) if err != nil { return m.retryInterval, err diff --git a/arbnode/mel/runner/mel_test.go b/arbnode/mel/runner/mel_test.go index c905fe2586..b5c61f1cc5 100644 --- a/arbnode/mel/runner/mel_test.go +++ b/arbnode/mel/runner/mel_test.go @@ -39,6 +39,7 @@ func TestMessageExtractor(t *testing.T) { messageConsumer := &mockMessageConsumer{} extractor, err := NewMessageExtractor( parentChainReader, + chaininfo.ArbitrumDevTestChainConfig(), &chaininfo.RollupAddresses{}, melDb, messageConsumer, diff --git a/arbnode/node.go b/arbnode/node.go index 3d3fb4dbe6..072e7dfae2 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2025, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md package arbnode @@ -719,8 +719,10 @@ func getInboxTrackerAndReader( delayedBridge *DelayedBridge, sequencerInbox *SequencerInbox, exec execution.ExecutionSequencer, + arbOSVersionGetter execution.ArbOSVersionGetter, + initialArbosVersion uint64, ) (*InboxTracker, *InboxReader, error) { - inboxTracker, err := NewInboxTracker(arbDb, txStreamer, dapReaders, config.SnapSyncTest) + inboxTracker, err := NewInboxTracker(arbDb, txStreamer, dapReaders, config.SnapSyncTest, arbOSVersionGetter, initialArbosVersion) if err != nil { return nil, nil, err } @@ -971,6 +973,7 @@ func getBatchPoster( ctx context.Context, config *Config, configFetcher ConfigFetcher, + l2Config *params.ChainConfig, txOptsBatchPoster *bind.TransactOpts, dapWriters []daprovider.Writer, l1Reader *headerreader.HeaderReader, @@ -1010,6 +1013,7 @@ func getBatchPoster( DAPWriters: dapWriters, ParentChainID: parentChainID, DAPReaders: dapReaders, + ChainConfig: l2Config, }) if err != nil { return nil, err @@ -1186,7 +1190,7 @@ func createNodeImpl( return nil, err } - inboxTracker, inboxReader, err := getInboxTrackerAndReader(ctx, arbDb, txStreamer, dapRegistry, config, configFetcher, l1client, l1Reader, deployInfo, delayedBridge, sequencerInbox, executionSequencer) + inboxTracker, inboxReader, err := getInboxTrackerAndReader(ctx, arbDb, txStreamer, dapRegistry, config, configFetcher, l1client, l1Reader, deployInfo, delayedBridge, sequencerInbox, executionSequencer, arbOSVersionGetter, l2Config.ArbitrumChainParams.InitialArbOSVersion) if err != nil { return nil, err } @@ -1206,7 +1210,7 @@ func createNodeImpl( return nil, err } - batchPoster, err := getBatchPoster(ctx, config, configFetcher, txOptsBatchPoster, dapWriters, l1Reader, inboxTracker, txStreamer, arbOSVersionGetter, arbDb, syncMonitor, deployInfo, parentChainID, dapRegistry, stakerAddr) + batchPoster, err := getBatchPoster(ctx, config, configFetcher, l2Config, txOptsBatchPoster, dapWriters, l1Reader, inboxTracker, txStreamer, arbOSVersionGetter, arbDb, syncMonitor, deployInfo, parentChainID, dapRegistry, stakerAddr) if err != nil { return nil, err } @@ -1347,7 +1351,7 @@ func CreateNodeExecutionClient( if executionClient == nil { return nil, errors.New("execution client must be non-nil") } - currentNode, err := createNodeImpl(ctx, stack, executionClient, nil, nil, nil, arbDb, configFetcher, l2Config, l1client, deployInfo, txOptsValidator, txOptsBatchPoster, dataSigner, fatalErrChan, parentChainID, blobReader, latestWasmModuleRoot) + currentNode, err := createNodeImpl(ctx, stack, executionClient, nil, nil, executionClient, arbDb, configFetcher, l2Config, l1client, deployInfo, txOptsValidator, txOptsBatchPoster, dataSigner, fatalErrChan, parentChainID, blobReader, latestWasmModuleRoot) if err != nil { return nil, err } diff --git a/arbstate/inbox.go b/arbstate/inbox.go index 3fc25a2a8e..f1194cc2bb 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/offchainlabs/nitro/arbcompress" @@ -77,11 +78,9 @@ type SequencerMessage struct { Segments [][]byte } -const MaxDecompressedLen int = 1024 * 1024 * 16 // 16 MiB -const maxZeroheavyDecompressedLen = 101*MaxDecompressedLen/100 + 64 const MaxSegmentsPerSequencerMessage = 100 * 1024 -func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash common.Hash, data []byte, dapReaders DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode) (*SequencerMessage, error) { +func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash common.Hash, data []byte, dapReaders DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, chainConfig *params.ChainConfig, arbosVersion uint64) (*SequencerMessage, error) { if len(data) < 40 { return nil, errors.New("sequencer message missing L1 header") } @@ -147,10 +146,17 @@ func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash // At this point, `payload` has not been validated by the sequencer inbox at all. // It's not safe to trust any part of the payload from this point onwards. + var uncompressedBatchSizeLimit uint64 + if chainConfig != nil { + uncompressedBatchSizeLimit = chainConfig.MaxUncompressedBatchSize(arbosVersion) + } else { // In case chainConfig is nil, fall back to params default (e.g. in tests or for the genesis block) + uncompressedBatchSizeLimit = params.DefaultMaxUncompressedBatchSize + } // Stage 2: If enabled, decode the zero heavy payload (saves gas based on calldata charging). if len(payload) > 0 && daprovider.IsZeroheavyEncodedHeaderByte(payload[0]) { - pl, err := io.ReadAll(io.LimitReader(zeroheavy.NewZeroheavyDecoder(bytes.NewReader(payload[1:])), int64(maxZeroheavyDecompressedLen))) + maxZeroheavyDecompressedLen := 101*uncompressedBatchSizeLimit/100 + 64 + pl, err := io.ReadAll(io.LimitReader(zeroheavy.NewZeroheavyDecoder(bytes.NewReader(payload[1:])), int64(maxZeroheavyDecompressedLen))) // #nosec G115 if err != nil { log.Warn("error reading from zeroheavy decoder", err.Error()) return parsedMsg, nil @@ -160,10 +166,10 @@ func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash // Stage 3: Decompress the brotli payload and fill the parsedMsg.segments list. if len(payload) > 0 && daprovider.IsBrotliMessageHeaderByte(payload[0]) { - decompressed, err := arbcompress.Decompress(payload[1:], MaxDecompressedLen) + decompressed, err := arbcompress.Decompress(payload[1:], int(uncompressedBatchSizeLimit)) // #nosec G115 if err == nil { reader := bytes.NewReader(decompressed) - stream := rlp.NewStream(reader, uint64(MaxDecompressedLen)) + stream := rlp.NewStream(reader, 0) for { var segment []byte err := stream.Decode(&segment) @@ -199,6 +205,7 @@ func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash type inboxMultiplexer struct { backend InboxBackend delayedMessagesRead uint64 + chainConfig *params.ChainConfig dapReaders DapReaderSource cachedSequencerMessage *SequencerMessage cachedSequencerMessageNum uint64 @@ -211,12 +218,19 @@ type inboxMultiplexer struct { // but ParseSequencerMessage still needs this to decide whether to panic or log on validation errors. // In replay mode, this allows proper error handling based on the position within the message. keysetValidationMode daprovider.KeysetValidationMode + // ArbOS version is used to determine a batch size limit from the chain config during sequencer message parsing. + // While ideally this would be read directly from the batch series being processed (since the version might be + // just upgraded), it is impossible (we would have to execute them on-fly). However, it should be safe to pass + // "recent enough" ArbOS version here, since batch size limit is only expected to either stay the same or increase + // with time. + recentArbOSVersion uint64 } -func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapReaders DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode) arbostypes.InboxMultiplexer { +func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapReaders DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, chainConfig *params.ChainConfig, recentArbOSVersion uint64) arbostypes.InboxMultiplexer { return &inboxMultiplexer{ backend: backend, delayedMessagesRead: delayedMessagesRead, + chainConfig: chainConfig, dapReaders: dapReaders, cachedSequencerMessage: nil, cachedSequencerMessageNum: 0, @@ -225,6 +239,7 @@ func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapRe cachedSegmentBlockNumber: 0, cachedSubMessageNumber: 0, keysetValidationMode: keysetValidationMode, + recentArbOSVersion: recentArbOSVersion, } } @@ -244,8 +259,9 @@ func (r *inboxMultiplexer) Pop(ctx context.Context) (*arbostypes.MessageWithMeta return nil, realErr } r.cachedSequencerMessageNum = r.backend.GetSequencerInboxPosition() + var err error - r.cachedSequencerMessage, err = ParseSequencerMessage(ctx, r.cachedSequencerMessageNum, batchBlockHash, bytes, r.dapReaders, r.keysetValidationMode) + r.cachedSequencerMessage, err = ParseSequencerMessage(ctx, r.cachedSequencerMessageNum, batchBlockHash, bytes, r.dapReaders, r.keysetValidationMode, r.chainConfig, r.recentArbOSVersion) if err != nil { return nil, err } diff --git a/arbstate/inbox_fuzz_test.go b/arbstate/inbox_fuzz_test.go index 6b49d97288..41941789ae 100644 --- a/arbstate/inbox_fuzz_test.go +++ b/arbstate/inbox_fuzz_test.go @@ -1,4 +1,4 @@ -// Copyright 2022, Offchain Labs, Inc. +// Copyright 2022-2025, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md package arbstate @@ -10,8 +10,10 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbos/arbostypes" + "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/daprovider" ) @@ -70,7 +72,14 @@ func FuzzInboxMultiplexer(f *testing.F) { delayedMessage: delayedMsg, positionWithinMessage: 0, } - multiplexer := NewInboxMultiplexer(backend, 0, nil, daprovider.KeysetValidate) + multiplexer := NewInboxMultiplexer( + backend, + 0, + nil, + daprovider.KeysetValidate, + chaininfo.ArbitrumDevTestChainConfig(), + params.ArbosVersion_50, + ) _, err := multiplexer.Pop(context.TODO()) if err != nil { panic(err) diff --git a/cmd/chaininfo/chain_defaults.go b/cmd/chaininfo/chain_defaults.go index c281927864..bcf81d5116 100644 --- a/cmd/chaininfo/chain_defaults.go +++ b/cmd/chaininfo/chain_defaults.go @@ -38,6 +38,7 @@ func CopyArbitrumChainParams(arbChainParams params.ArbitrumChainParams) params.A GenesisBlockNum: arbChainParams.GenesisBlockNum, MaxCodeSize: arbChainParams.MaxCodeSize, MaxInitCodeSize: arbChainParams.MaxInitCodeSize, + MaxUncompressedBatchSize: arbChainParams.MaxUncompressedBatchSize, } } diff --git a/cmd/pruning/pruning.go b/cmd/pruning/pruning.go index c7dd09a231..3ba90e95f4 100644 --- a/cmd/pruning/pruning.go +++ b/cmd/pruning/pruning.go @@ -88,7 +88,7 @@ func (r *importantRoots) addHeader(header *types.Header, overwrite bool) error { var hashListRegex = regexp.MustCompile("^(0x)?[0-9a-fA-F]{64}(,(0x)?[0-9a-fA-F]{64})*$") // Finds important roots to retain while proving -func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node.Node, initConfig *conf.InitConfig, cacheConfig *core.BlockChainConfig, persistentConfig *conf.PersistentConfig, l1Client *ethclient.Client, rollupAddrs chaininfo.RollupAddresses, validatorRequired bool) ([]common.Hash, error) { +func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node.Node, initConfig *conf.InitConfig, persistentConfig *conf.PersistentConfig, l1Client *ethclient.Client, rollupAddrs chaininfo.RollupAddresses, validatorRequired bool) ([]common.Hash, error) { chainConfig := gethexec.TryReadStoredChainConfig(chainDb) if chainConfig == nil { return nil, errors.New("database doesn't have a chain config (was this node initialized?)") @@ -184,7 +184,9 @@ func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node return nil, fmt.Errorf("failed to get finalized block: %w", err) } l1BlockNum := l1Block.NumberU64() - tracker, err := arbnode.NewInboxTracker(arbDb, nil, nil, arbnode.DefaultSnapSyncConfig) + // It is safe to pass `nil` as `arbosVersionGetter` as we only need inbox tracking for batch count and metadata. + // No parsing of messages is done here. + tracker, err := arbnode.NewInboxTracker(arbDb, nil, nil, arbnode.DefaultSnapSyncConfig, nil, chainConfig.ArbitrumChainParams.InitialArbOSVersion) if err != nil { return nil, err } @@ -286,7 +288,7 @@ func PruneChainDb(ctx context.Context, chainDb ethdb.Database, stack *node.Node, if initConfig.Prune == "" { return pruner.RecoverPruning(stack.InstanceDir(), chainDb, initConfig.PruneThreads) } - root, err := findImportantRoots(ctx, chainDb, stack, initConfig, cacheConfig, persistentConfig, l1Client, rollupAddrs, validatorRequired) + root, err := findImportantRoots(ctx, chainDb, stack, initConfig, persistentConfig, l1Client, rollupAddrs, validatorRequired) if err != nil { return fmt.Errorf("failed to find root to retain for pruning: %w", err) } diff --git a/cmd/replay/main.go b/cmd/replay/main.go index c1fed2897c..21ce09a5eb 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -243,10 +243,11 @@ func main() { } return wavmio.ReadInboxMessage(batchNum), nil } - readMessage := func(dasEnabled bool) *arbostypes.MessageWithMetadata { - var delayedMessagesRead uint64 + readMessage := func(dasEnabled bool, chainConfig *params.ChainConfig) *arbostypes.MessageWithMetadata { + var delayedMessagesRead, arbosVersion uint64 if lastBlockHeader != nil { delayedMessagesRead = lastBlockHeader.Nonce.Uint64() + arbosVersion = types.DeserializeHeaderExtraInformation(lastBlockHeader).ArbOSFormatVersion } var dasReader dasutil.DASReader var dasKeysetFetcher dasutil.DASKeysetFetcher @@ -272,7 +273,7 @@ func main() { panic(fmt.Sprintf("Failed to register blob reader: %v", err)) } - inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode) + inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode, chainConfig, arbosVersion) ctx := context.Background() message, err := inboxMultiplexer.Pop(ctx) if err != nil { @@ -328,7 +329,7 @@ func main() { } } - message := readMessage(chainConfig.ArbitrumChainParams.DataAvailabilityCommittee) + message := readMessage(chainConfig.ArbitrumChainParams.DataAvailabilityCommittee, chainConfig) chainContext := WavmChainContext{chainConfig: chainConfig} newBlock, _, err = arbos.ProduceBlock(message.Message, message.DelayedMessagesRead, lastBlockHeader, statedb, chainContext, false, core.NewMessageReplayContext(), false) @@ -338,7 +339,9 @@ func main() { } else { // Initialize ArbOS with this init message and create the genesis block. - message := readMessage(false) + // Currently, the only use of `chainConfig` argument is to get a limit on the uncompressed batch size. + // However, the init message is never compressed, so we can safely pass nil here. + message := readMessage(false, nil) initMessage, err := message.Message.ParseInitMessage() if err != nil { diff --git a/execution/interface.go b/execution/interface.go index 04077cc84a..d78d2cf060 100644 --- a/execution/interface.go +++ b/execution/interface.go @@ -97,6 +97,14 @@ type ArbOSVersionGetter interface { ArbOSVersionForMessageIndex(msgIdx arbutil.MessageIndex) containers.PromiseInterface[uint64] } +type ConstArbosVersionGetter struct { + Version uint64 +} + +func (c *ConstArbosVersionGetter) ArbOSVersionForMessageIndex(msgIdx arbutil.MessageIndex) containers.PromiseInterface[uint64] { + return containers.NewReadyPromise(c.Version, nil) +} + // not implemented in execution, used as input // BatchFetcher is required for any execution node type BatchFetcher interface { diff --git a/go-ethereum b/go-ethereum index 6b19938827..3bc2944894 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 6b19938827e8d7684caf6a883765d51ee967f250 +Subproject commit 3bc2944894fa717c0be12666b87ad1d7b998374b diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index 1c38ae9884..437d6a3268 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -160,6 +160,7 @@ func testBatchPosterParallel(t *testing.T, useRedis bool, useRedisLock bool) { TransactOpts: &seqTxOpts, DAPWriters: nil, ParentChainID: parentChainID, + ChainConfig: builder.chainConfig, }, ) Require(t, err) @@ -300,6 +301,7 @@ func TestRedisBatchPosterHandoff(t *testing.T) { TransactOpts: &seqTxOpts, DAPWriters: nil, ParentChainID: parentChainID, + ChainConfig: builder.chainConfig, }, ) Require(t, err) diff --git a/system_tests/batch_size_limit_test.go b/system_tests/batch_size_limit_test.go new file mode 100644 index 0000000000..865ca4ccba --- /dev/null +++ b/system_tests/batch_size_limit_test.go @@ -0,0 +1,146 @@ +package arbtest + +import ( + "bytes" + "context" + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" + + "github.com/offchainlabs/nitro/arbcompress" + "github.com/offchainlabs/nitro/arbnode" + "github.com/offchainlabs/nitro/daprovider" + "github.com/offchainlabs/nitro/solgen/go/bridgegen" +) + +const ( + SenderAccount = "Sender" + ReceiverAccount = "Receiver" + TransferAmount = 1000000 + NewUncompressedSizeLimit = params.DefaultMaxUncompressedBatchSize * 2 +) + +func TestTooBigBatchGetsRejected(t *testing.T) { + builder, ctx, cleanup := setupNodeForTestingBatchSizeLimit(t, false) + defer cleanup() + + checkReceiverAccountBalance(t, ctx, builder, 0) + batchesProcessed := numberOfProcessedBatches(t, builder) + + bigBatch := buildBigBatch(t, builder.L2Info) + postBatch(t, ctx, builder, bigBatch) + + ensureMoreBatchesWereProcessed(t, builder, batchesProcessed) + checkReceiverAccountBalance(t, ctx, builder, 0) +} + +func TestCanIncreaseBatchSizeLimit(t *testing.T) { + builder, ctx, cleanup := setupNodeForTestingBatchSizeLimit(t, true) + defer cleanup() + + checkReceiverAccountBalance(t, ctx, builder, 0) + batchesProcessed := numberOfProcessedBatches(t, builder) + + bigBatch := buildBigBatch(t, builder.L2Info) + postBatch(t, ctx, builder, bigBatch) + + ensureMoreBatchesWereProcessed(t, builder, batchesProcessed) + checkReceiverAccountBalance(t, ctx, builder, TransferAmount) +} + +// setupNodeForTestingBatchSizeLimit initializes a test node with the option to set a higher uncompressed batch size limit. +// Also, it creates genesis accounts for sender and receiver with appropriate balances. +// It returns the NodeBuilder and a cleanup function to be called after the test. +func setupNodeForTestingBatchSizeLimit(t *testing.T, setHighLimit bool) (*NodeBuilder, context.Context, func()) { + ctx, cancel := context.WithCancel(context.Background()) + + builder := NewNodeBuilder(ctx).DefaultConfig(t, true) + builder.nodeConfig.BatchPoster.Enable = false + builder.L2Info.GenerateGenesisAccount(SenderAccount, big.NewInt(1e18)) + builder.L2Info.GenerateGenesisAccount(ReceiverAccount, big.NewInt(0)) + builder.chainConfig.ArbitrumChainParams.InitialArbOSVersion = params.ArbosVersion_BatchSize + + if setHighLimit { + builder.chainConfig.ArbitrumChainParams.MaxUncompressedBatchSize = NewUncompressedSizeLimit + } + + cleanup := builder.Build(t) + + return builder, ctx, func() { + cancel() + cleanup() + } +} + +// buildBigBatch builds a batch that: +// - consists of a valid transfer tx followed by highly compressible trash data +// - has an uncompressed size larger than DefaultMaxUncompressedBatchSize but less than NewUncompressedSizeLimit +// - has a compressed size smaller than the allowed calldata batch size for the test batch poster +// - is already compressed and has the appropriate header byte +func buildBigBatch(t *testing.T, l2Info *BlockchainTestInfo) []byte { + batchBuffer := bytes.NewBuffer([]byte{}) + + // 1. The first tx in the batch is a standard transfer tx used as an indicator whether the batch was processed successfully. + standardTx := l2Info.PrepareTx(SenderAccount, ReceiverAccount, 1000000, big.NewInt(TransferAmount), []byte{}) + err := writeTxToBatch(batchBuffer, standardTx) + Require(t, err) + + // 2. The rest of the batch is filled with highly compressible trash data. + batchBuffer.Write(bytes.Repeat([]byte{0xff}, params.DefaultMaxUncompressedBatchSize)) + + // 3. Compress the batch (as the batch poster would do). + compressed, err := arbcompress.CompressWell(batchBuffer.Bytes()) + Require(t, err) + + // 4. Ensure compressed and uncompressed sizes are as expected. + uncompressedSize, compressedSize := len(batchBuffer.Bytes()), len(compressed) + require.Greater(t, uncompressedSize, params.DefaultMaxUncompressedBatchSize) + require.Less(t, uncompressedSize, NewUncompressedSizeLimit) + require.Less(t, compressedSize, arbnode.TestBatchPosterConfig.MaxCalldataBatchSize) + + // 5. Return the compressed batch with the appropriate header byte. + return append([]byte{daprovider.BrotliMessageHeaderByte}, compressed...) +} + +// postBatch posts the given batch directly to the L1 SequencerInbox contract. +func postBatch(t *testing.T, ctx context.Context, builder *NodeBuilder, batch []byte) { + seqNum := new(big.Int).Lsh(common.Big1, 256) + seqNum.Sub(seqNum, common.Big1) + + seqInboxAddr := builder.L1Info.GetAddress("SequencerInbox") + seqInbox, err := bridgegen.NewSequencerInbox(seqInboxAddr, builder.L1.Client) + Require(t, err) + + sequencer := builder.L1Info.GetDefaultTransactOpts("Sequencer", ctx) + + tx, err := seqInbox.AddSequencerL2BatchFromOrigin8f111f3c(&sequencer, seqNum, batch, big.NewInt(1), common.Address{}, big.NewInt(0), big.NewInt(0)) + Require(t, err) + _, err = EnsureTxSucceeded(ctx, builder.L1.Client, tx) + Require(t, err) +} + +// checkReceiverAccountBalance ensures that the receiver account has the expected balance. +func checkReceiverAccountBalance(t *testing.T, ctx context.Context, builder *NodeBuilder, expectedBalance int64) { + balanceBefore, err := builder.L2.Client.BalanceAt(ctx, builder.L2Info.GetAddress(ReceiverAccount), nil) + Require(t, err) + require.True(t, balanceBefore.Cmp(big.NewInt(expectedBalance)) == 0) +} + +// numberOfProcessedBatches retrieves the number of batches processed by the L2 node's inbox tracker. +func numberOfProcessedBatches(t *testing.T, builder *NodeBuilder) uint64 { + batchesProcessed, err := builder.L2.ConsensusNode.InboxTracker.GetBatchCount() + Require(t, err) + return batchesProcessed +} + +// ensureMoreBatchesWereProcessed waits until the number of processed batches exceeds the given earlier count. +func ensureMoreBatchesWereProcessed(t *testing.T, builder *NodeBuilder, processedEarlier uint64) { + require.Eventuallyf(t, func() bool { + return numberOfProcessedBatches(t, builder) > processedEarlier + }, 5*time.Second, time.Second, "new batch processed") +} diff --git a/system_tests/state_fuzz_test.go b/system_tests/state_fuzz_test.go index 5d3ad68229..72501f714b 100644 --- a/system_tests/state_fuzz_test.go +++ b/system_tests/state_fuzz_test.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2025, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md package arbtest @@ -44,7 +44,14 @@ func BuildBlock( if lastBlockHeader != nil { delayedMessagesRead = lastBlockHeader.Nonce.Uint64() } - inboxMultiplexer := arbstate.NewInboxMultiplexer(inbox, delayedMessagesRead, nil, daprovider.KeysetValidate) + inboxMultiplexer := arbstate.NewInboxMultiplexer( + inbox, + delayedMessagesRead, + nil, + daprovider.KeysetValidate, + getChainConfig(), + params.ArbosVersion_50, + ) ctx := context.Background() message, err := inboxMultiplexer.Pop(ctx) @@ -243,7 +250,7 @@ func FuzzStateTransition(f *testing.F) { runCtx = core.NewMessageGasEstimationContext() } - _, err = BuildBlock(statedb, genesis.Header(), noopChainContext{chainConfig: chaininfo.ArbitrumDevTestChainConfig()}, inbox, seqBatch, runCtx) + _, err = BuildBlock(statedb, genesis.Header(), noopChainContext{chainConfig: getChainConfig()}, inbox, seqBatch, runCtx) if err != nil { // With the fixed header it shouldn't be possible to read a delayed message, // and no other type of error should be possible. @@ -251,3 +258,7 @@ func FuzzStateTransition(f *testing.F) { } }) } + +func getChainConfig() *params.ChainConfig { + return chaininfo.ArbitrumDevTestChainConfig() +}