From 1815cc72e4c7a1851af3d29d2a76f1f7b7a03874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 3 Nov 2025 18:27:56 +0100 Subject: [PATCH 01/35] Pass the new limit to the batch poster and use it in the batchSegments instead of the constant --- arbnode/batch_poster.go | 93 ++++++++++++++++++++++------------------- arbnode/node.go | 28 +++++++------ go-ethereum | 2 +- 3 files changed, 65 insertions(+), 58 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index b3c14e5540..fa2b4dc12d 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -122,10 +122,11 @@ type BatchPoster struct { nextRevertCheckBlock int64 // the last parent block scanned for reverting batches postedFirstBatch bool // indicates if batch poster has posted the first batch - accessList func(SequencerInboxAccs, AfterDelayedMessagesRead uint64) types.AccessList - parentChain *parent.ParentChain - checkEip7623 bool - useEip7623 bool + accessList func(SequencerInboxAccs, AfterDelayedMessagesRead uint64) types.AccessList + parentChain *parent.ParentChain + checkEip7623 bool + useEip7623 bool + maxUncompressedBatchSize uint64 } type l1BlockBound int @@ -319,18 +320,19 @@ var TestBatchPosterConfig = BatchPosterConfig{ } type BatchPosterOpts struct { - DataPosterDB ethdb.Database - L1Reader *headerreader.HeaderReader - Inbox *InboxTracker - Streamer *TransactionStreamer - VersionGetter execution.ArbOSVersionGetter - SyncMonitor *SyncMonitor - Config BatchPosterConfigFetcher - DeployInfo *chaininfo.RollupAddresses - TransactOpts *bind.TransactOpts - DAPWriter daprovider.Writer - ParentChainID *big.Int - DAPReaders *daprovider.ReaderRegistry + DataPosterDB ethdb.Database + L1Reader *headerreader.HeaderReader + Inbox *InboxTracker + Streamer *TransactionStreamer + VersionGetter execution.ArbOSVersionGetter + SyncMonitor *SyncMonitor + Config BatchPosterConfigFetcher + DeployInfo *chaininfo.RollupAddresses + TransactOpts *bind.TransactOpts + DAPWriter daprovider.Writer + ParentChainID *big.Int + DAPReaders *daprovider.ReaderRegistry + ArbitrumChainParams *params.ArbitrumChainParams } func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, error) { @@ -373,23 +375,24 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e return nil, err } b := &BatchPoster{ - l1Reader: opts.L1Reader, - inbox: opts.Inbox, - streamer: opts.Streamer, - arbOSVersionGetter: opts.VersionGetter, - syncMonitor: opts.SyncMonitor, - config: opts.Config, - seqInbox: seqInbox, - seqInboxABI: seqInboxABI, - seqInboxAddr: opts.DeployInfo.SequencerInbox, - gasRefunderAddr: opts.Config().gasRefunder, - bridgeAddr: opts.DeployInfo.Bridge, - dapWriter: opts.DAPWriter, - redisLock: redisLock, - dapReaders: opts.DAPReaders, - parentChain: &parent.ParentChain{ChainID: opts.ParentChainID, L1Reader: opts.L1Reader}, - checkEip7623: checkEip7623, - useEip7623: useEip7623, + l1Reader: opts.L1Reader, + inbox: opts.Inbox, + streamer: opts.Streamer, + arbOSVersionGetter: opts.VersionGetter, + syncMonitor: opts.SyncMonitor, + config: opts.Config, + seqInbox: seqInbox, + seqInboxABI: seqInboxABI, + seqInboxAddr: opts.DeployInfo.SequencerInbox, + gasRefunderAddr: opts.Config().gasRefunder, + bridgeAddr: opts.DeployInfo.Bridge, + dapWriter: opts.DAPWriter, + redisLock: redisLock, + dapReaders: opts.DAPReaders, + parentChain: &parent.ParentChain{ChainID: opts.ParentChainID, L1Reader: opts.L1Reader}, + checkEip7623: checkEip7623, + useEip7623: useEip7623, + maxUncompressedBatchSize: opts.ArbitrumChainParams.MaxUncompressedBatchSize, } b.messagesPerBatch, err = arbmath.NewMovingAverage[uint64](20) if err != nil { @@ -875,6 +878,7 @@ type batchSegments struct { recompressionLevel int newUncompressedSize int totalUncompressedSize int + maxUncompressedSize int lastCompressedSize int trailingHeaders int // how many trailing segments are headers isDone bool @@ -934,12 +938,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.maxUncompressedBatchSize), // #nosec G115 }, nil } @@ -954,8 +959,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) @@ -965,10 +970,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 } diff --git a/arbnode/node.go b/arbnode/node.go index db60726d95..a12652d78b 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -925,6 +925,7 @@ func getBatchPoster( ctx context.Context, config *Config, configFetcher ConfigFetcher, + l2Config *params.ChainConfig, txOptsBatchPoster *bind.TransactOpts, dapWriter daprovider.Writer, l1Reader *headerreader.HeaderReader, @@ -952,18 +953,19 @@ func getBatchPoster( } var err error batchPoster, err = NewBatchPoster(ctx, &BatchPosterOpts{ - DataPosterDB: rawdb.NewTable(arbDb, storage.BatchPosterPrefix), - L1Reader: l1Reader, - Inbox: inboxTracker, - Streamer: txStreamer, - VersionGetter: arbOSVersionGetter, - SyncMonitor: syncMonitor, - Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, - DeployInfo: deployInfo, - TransactOpts: txOptsBatchPoster, - DAPWriter: dapWriter, - ParentChainID: parentChainID, - DAPReaders: dapReaders, + DataPosterDB: rawdb.NewTable(arbDb, storage.BatchPosterPrefix), + L1Reader: l1Reader, + Inbox: inboxTracker, + Streamer: txStreamer, + VersionGetter: arbOSVersionGetter, + SyncMonitor: syncMonitor, + Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, + DeployInfo: deployInfo, + TransactOpts: txOptsBatchPoster, + DAPWriter: dapWriter, + ParentChainID: parentChainID, + DAPReaders: dapReaders, + ArbitrumChainParams: &l2Config.ArbitrumChainParams, }) if err != nil { return nil, err @@ -1160,7 +1162,7 @@ func createNodeImpl( return nil, err } - batchPoster, err := getBatchPoster(ctx, config, configFetcher, txOptsBatchPoster, dapWriter, l1Reader, inboxTracker, txStreamer, arbOSVersionGetter, arbDb, syncMonitor, deployInfo, parentChainID, dapReaders, stakerAddr) + batchPoster, err := getBatchPoster(ctx, config, configFetcher, l2Config, txOptsBatchPoster, dapWriter, l1Reader, inboxTracker, txStreamer, arbOSVersionGetter, arbDb, syncMonitor, deployInfo, parentChainID, dapReaders, stakerAddr) if err != nil { return nil, err } diff --git a/go-ethereum b/go-ethereum index 57fe4b732d..4565d679f5 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 57fe4b732d4e640e696da40773f2dacba97e722b +Subproject commit 4565d679f5bbb80d81aa94b410879ae11055c958 From a4fc03fd2bd533663982311b17d48600bff288a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 3 Nov 2025 19:15:04 +0100 Subject: [PATCH 02/35] Set the field for tests --- cmd/chaininfo/arbitrum_chain_info.json | 18 ++++++---- cmd/chaininfo/chain_defaults.go | 1 + system_tests/batch_poster_test.go | 46 ++++++++++++++------------ 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/cmd/chaininfo/arbitrum_chain_info.json b/cmd/chaininfo/arbitrum_chain_info.json index 46a9466638..a36e955d5a 100644 --- a/cmd/chaininfo/arbitrum_chain_info.json +++ b/cmd/chaininfo/arbitrum_chain_info.json @@ -37,7 +37,8 @@ "DataAvailabilityCommittee": false, "InitialArbOSVersion": 6, "InitialChainOwner": "0xd345e41ae2cb00311956aa7109fc801ae8c81a52", - "GenesisBlockNum": 0 + "GenesisBlockNum": 0, + "MaxUncompressedBatchSize": 16777216 } }, "rollup": { @@ -87,7 +88,8 @@ "DataAvailabilityCommittee": true, "InitialArbOSVersion": 1, "InitialChainOwner": "0x9c040726f2a657226ed95712245dee84b650a1b5", - "GenesisBlockNum": 0 + "GenesisBlockNum": 0, + "MaxUncompressedBatchSize": 16777216 } }, "rollup": { @@ -134,7 +136,8 @@ "DataAvailabilityCommittee": false, "InitialArbOSVersion": 2, "InitialChainOwner": "0x186b56023d42b2b4e7616589a5c62eef5fca21dd", - "GenesisBlockNum": 0 + "GenesisBlockNum": 0, + "MaxUncompressedBatchSize": 16777216 } }, "rollup": { @@ -175,7 +178,8 @@ "DataAvailabilityCommittee": false, "InitialArbOSVersion": 50, "InitialChainOwner": "0x0000000000000000000000000000000000000000", - "GenesisBlockNum": 0 + "GenesisBlockNum": 0, + "MaxUncompressedBatchSize": 16777216 } } }, @@ -207,7 +211,8 @@ "DataAvailabilityCommittee": true, "InitialArbOSVersion": 32, "InitialChainOwner": "0x0000000000000000000000000000000000000000", - "GenesisBlockNum": 0 + "GenesisBlockNum": 0, + "MaxUncompressedBatchSize": 16777216 } } }, @@ -247,7 +252,8 @@ "DataAvailabilityCommittee": false, "InitialArbOSVersion": 10, "InitialChainOwner": "0x71B61c2E250AFa05dFc36304D6c91501bE0965D8", - "GenesisBlockNum": 0 + "GenesisBlockNum": 0, + "MaxUncompressedBatchSize": 16777216 } }, "rollup": { 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/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index 4b53cd53e3..d76bf0eb20 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -149,17 +149,18 @@ func testBatchPosterParallel(t *testing.T, useRedis bool, useRedisLock bool) { batchPosterConfig := builder.nodeConfig.BatchPoster batchPoster, err := arbnode.NewBatchPoster(ctx, &arbnode.BatchPosterOpts{ - DataPosterDB: nil, - L1Reader: builder.L2.ConsensusNode.L1Reader, - Inbox: builder.L2.ConsensusNode.InboxTracker, - Streamer: builder.L2.ConsensusNode.TxStreamer, - VersionGetter: builder.L2.ExecNode, - SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, - Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, - DeployInfo: builder.L2.ConsensusNode.DeployInfo, - TransactOpts: &seqTxOpts, - DAPWriter: nil, - ParentChainID: parentChainID, + DataPosterDB: nil, + L1Reader: builder.L2.ConsensusNode.L1Reader, + Inbox: builder.L2.ConsensusNode.InboxTracker, + Streamer: builder.L2.ConsensusNode.TxStreamer, + VersionGetter: builder.L2.ExecNode, + SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, + Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, + DeployInfo: builder.L2.ConsensusNode.DeployInfo, + TransactOpts: &seqTxOpts, + DAPWriter: nil, + ParentChainID: parentChainID, + ArbitrumChainParams: &builder.chainConfig.ArbitrumChainParams, }, ) Require(t, err) @@ -289,17 +290,18 @@ func TestRedisBatchPosterHandoff(t *testing.T) { batchPosterConfig := builder.nodeConfig.BatchPoster batchPoster, err := arbnode.NewBatchPoster(ctx, &arbnode.BatchPosterOpts{ - DataPosterDB: nil, - L1Reader: builder.L2.ConsensusNode.L1Reader, - Inbox: builder.L2.ConsensusNode.InboxTracker, - Streamer: builder.L2.ConsensusNode.TxStreamer, - VersionGetter: builder.L2.ExecNode, - SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, - Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, - DeployInfo: builder.L2.ConsensusNode.DeployInfo, - TransactOpts: &seqTxOpts, - DAPWriter: nil, - ParentChainID: parentChainID, + DataPosterDB: nil, + L1Reader: builder.L2.ConsensusNode.L1Reader, + Inbox: builder.L2.ConsensusNode.InboxTracker, + Streamer: builder.L2.ConsensusNode.TxStreamer, + VersionGetter: builder.L2.ExecNode, + SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, + Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, + DeployInfo: builder.L2.ConsensusNode.DeployInfo, + TransactOpts: &seqTxOpts, + DAPWriter: nil, + ParentChainID: parentChainID, + ArbitrumChainParams: &builder.chainConfig.ArbitrumChainParams, }, ) Require(t, err) From 6d128d9a08b26a3a674ed7ee66429bb76ec7cb1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 3 Nov 2025 19:19:43 +0100 Subject: [PATCH 03/35] Get rid of unnecessary usage of the limit --- arbstate/inbox.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbstate/inbox.go b/arbstate/inbox.go index a0c308e626..9b925d1260 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -130,7 +130,7 @@ func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash decompressed, err := arbcompress.Decompress(payload[1:], MaxDecompressedLen) 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) From 82172d7ce6c4fc9346a01b9e4335fa861e1ece5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 3 Nov 2025 20:10:17 +0100 Subject: [PATCH 04/35] Cover inbox usages and tests --- arbnode/batch_poster.go | 86 ++++++++++++++++----------------- arbnode/inbox_tracker.go | 8 ++- arbstate/inbox.go | 14 +++--- arbstate/inbox_fuzz_test.go | 9 +++- cmd/replay/main.go | 8 +-- system_tests/state_fuzz_test.go | 14 +++++- 6 files changed, 82 insertions(+), 57 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index fa2b4dc12d..ef0451d140 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -94,24 +94,25 @@ type batchPosterPosition struct { type BatchPoster struct { stopwaiter.StopWaiter - l1Reader *headerreader.HeaderReader - inbox *InboxTracker - streamer *TransactionStreamer - arbOSVersionGetter execution.ArbOSVersionGetter - config BatchPosterConfigFetcher - seqInbox *bridgegen.SequencerInbox - syncMonitor *SyncMonitor - seqInboxABI *abi.ABI - seqInboxAddr common.Address - bridgeAddr common.Address - gasRefunderAddr common.Address - building *buildingBatch - dapWriter daprovider.Writer - dapReaders *daprovider.ReaderRegistry - dataPoster *dataposter.DataPoster - redisLock *redislock.Simple - messagesPerBatch *arbmath.MovingAverage[uint64] - non4844BatchCount int // Count of consecutive non-4844 batches posted + l1Reader *headerreader.HeaderReader + inbox *InboxTracker + streamer *TransactionStreamer + arbOSVersionGetter execution.ArbOSVersionGetter + arbitrumChainParams *params.ArbitrumChainParams + config BatchPosterConfigFetcher + seqInbox *bridgegen.SequencerInbox + syncMonitor *SyncMonitor + seqInboxABI *abi.ABI + seqInboxAddr common.Address + bridgeAddr common.Address + gasRefunderAddr common.Address + building *buildingBatch + dapWriter daprovider.Writer + dapReaders *daprovider.ReaderRegistry + dataPoster *dataposter.DataPoster + redisLock *redislock.Simple + messagesPerBatch *arbmath.MovingAverage[uint64] + non4844BatchCount int // Count of consecutive non-4844 batches posted // This is an atomic variable that should only be accessed atomically. // An estimate of the number of batches we want to post but haven't yet. // This doesn't include batches which we don't want to post yet due to the L1 bounds. @@ -122,11 +123,10 @@ type BatchPoster struct { nextRevertCheckBlock int64 // the last parent block scanned for reverting batches postedFirstBatch bool // indicates if batch poster has posted the first batch - accessList func(SequencerInboxAccs, AfterDelayedMessagesRead uint64) types.AccessList - parentChain *parent.ParentChain - checkEip7623 bool - useEip7623 bool - maxUncompressedBatchSize uint64 + accessList func(SequencerInboxAccs, AfterDelayedMessagesRead uint64) types.AccessList + parentChain *parent.ParentChain + checkEip7623 bool + useEip7623 bool } type l1BlockBound int @@ -375,24 +375,24 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e return nil, err } b := &BatchPoster{ - l1Reader: opts.L1Reader, - inbox: opts.Inbox, - streamer: opts.Streamer, - arbOSVersionGetter: opts.VersionGetter, - syncMonitor: opts.SyncMonitor, - config: opts.Config, - seqInbox: seqInbox, - seqInboxABI: seqInboxABI, - seqInboxAddr: opts.DeployInfo.SequencerInbox, - gasRefunderAddr: opts.Config().gasRefunder, - bridgeAddr: opts.DeployInfo.Bridge, - dapWriter: opts.DAPWriter, - redisLock: redisLock, - dapReaders: opts.DAPReaders, - parentChain: &parent.ParentChain{ChainID: opts.ParentChainID, L1Reader: opts.L1Reader}, - checkEip7623: checkEip7623, - useEip7623: useEip7623, - maxUncompressedBatchSize: opts.ArbitrumChainParams.MaxUncompressedBatchSize, + l1Reader: opts.L1Reader, + inbox: opts.Inbox, + streamer: opts.Streamer, + arbOSVersionGetter: opts.VersionGetter, + arbitrumChainParams: opts.ArbitrumChainParams, + syncMonitor: opts.SyncMonitor, + config: opts.Config, + seqInbox: seqInbox, + seqInboxABI: seqInboxABI, + seqInboxAddr: opts.DeployInfo.SequencerInbox, + gasRefunderAddr: opts.Config().gasRefunder, + bridgeAddr: opts.DeployInfo.Bridge, + dapWriter: opts.DAPWriter, + redisLock: redisLock, + dapReaders: opts.DAPReaders, + parentChain: &parent.ParentChain{ChainID: opts.ParentChainID, L1Reader: opts.L1Reader}, + checkEip7623: checkEip7623, + useEip7623: useEip7623, } b.messagesPerBatch, err = arbmath.NewMovingAverage[uint64](20) if err != nil { @@ -944,7 +944,7 @@ func (b *BatchPoster) newBatchSegments(ctx context.Context, firstDelayed uint64, recompressionLevel: recompressionLevel, rawSegments: make([][]byte, 0, 128), delayedMsg: firstDelayed, - maxUncompressedSize: int(b.maxUncompressedBatchSize), // #nosec G115 + maxUncompressedSize: int(b.arbitrumChainParams.MaxUncompressedBatchSize), // #nosec G115 }, nil } @@ -1846,7 +1846,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.arbitrumChainParams) // nolint:gosec 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/inbox_tracker.go b/arbnode/inbox_tracker.go index 5c742b1947..663022b8d5 100644 --- a/arbnode/inbox_tracker.go +++ b/arbnode/inbox_tracker.go @@ -769,7 +769,13 @@ func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client *ethclien ctx: ctx, client: client, } - multiplexer := arbstate.NewInboxMultiplexer(backend, prevbatchmeta.DelayedMessageCount, t.dapReaders, daprovider.KeysetValidate) + multiplexer := arbstate.NewInboxMultiplexer( + backend, + prevbatchmeta.DelayedMessageCount, + t.dapReaders, + daprovider.KeysetValidate, + &t.txStreamer.chainConfig.ArbitrumChainParams, + ) batchMessageCounts := make(map[uint64]arbutil.MessageIndex) currentPos := prevbatchmeta.MessageCount + 1 for { diff --git a/arbstate/inbox.go b/arbstate/inbox.go index 9b925d1260..da4dbe907a 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" @@ -47,11 +48,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 *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode) (*SequencerMessage, error) { +func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash common.Hash, data []byte, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, arbitrumChainParams *params.ArbitrumChainParams) (*SequencerMessage, error) { if len(data) < 40 { return nil, errors.New("sequencer message missing L1 header") } @@ -117,6 +116,7 @@ func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash // Stage 2: If enabled, decode the zero heavy payload (saves gas based on calldata charging). if len(payload) > 0 && daprovider.IsZeroheavyEncodedHeaderByte(payload[0]) { + maxZeroheavyDecompressedLen := 101*arbitrumChainParams.MaxUncompressedBatchSize/100 + 64 pl, err := io.ReadAll(io.LimitReader(zeroheavy.NewZeroheavyDecoder(bytes.NewReader(payload[1:])), int64(maxZeroheavyDecompressedLen))) if err != nil { log.Warn("error reading from zeroheavy decoder", err.Error()) @@ -127,7 +127,7 @@ 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(arbitrumChainParams.MaxUncompressedBatchSize)) if err == nil { reader := bytes.NewReader(decompressed) stream := rlp.NewStream(reader, 0) @@ -166,6 +166,7 @@ func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash type inboxMultiplexer struct { backend InboxBackend delayedMessagesRead uint64 + arbitrumChainParams *params.ArbitrumChainParams dapReaders *daprovider.ReaderRegistry cachedSequencerMessage *SequencerMessage cachedSequencerMessageNum uint64 @@ -180,10 +181,11 @@ type inboxMultiplexer struct { keysetValidationMode daprovider.KeysetValidationMode } -func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode) arbostypes.InboxMultiplexer { +func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, arbitrumChainParams *params.ArbitrumChainParams) arbostypes.InboxMultiplexer { return &inboxMultiplexer{ backend: backend, delayedMessagesRead: delayedMessagesRead, + arbitrumChainParams: arbitrumChainParams, dapReaders: dapReaders, cachedSequencerMessage: nil, cachedSequencerMessageNum: 0, @@ -212,7 +214,7 @@ func (r *inboxMultiplexer) Pop(ctx context.Context) (*arbostypes.MessageWithMeta } 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.arbitrumChainParams) if err != nil { return nil, err } diff --git a/arbstate/inbox_fuzz_test.go b/arbstate/inbox_fuzz_test.go index 6b49d97288..46fb957dc3 100644 --- a/arbstate/inbox_fuzz_test.go +++ b/arbstate/inbox_fuzz_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/daprovider" @@ -70,7 +71,13 @@ 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().ArbitrumChainParams, + ) _, err := multiplexer.Pop(context.TODO()) if err != nil { panic(err) diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 69d93a192d..2e1eb35cad 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -223,7 +223,7 @@ func main() { } return wavmio.ReadInboxMessage(batchNum), nil } - readMessage := func(dasEnabled bool) *arbostypes.MessageWithMetadata { + readMessage := func(dasEnabled bool, arbitrumChainParams *params.ArbitrumChainParams) *arbostypes.MessageWithMetadata { var delayedMessagesRead uint64 if lastBlockHeader != nil { delayedMessagesRead = lastBlockHeader.Nonce.Uint64() @@ -251,7 +251,7 @@ func main() { if err != nil { panic(fmt.Sprintf("Failed to register blob reader: %v", err)) } - inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode) + inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode, arbitrumChainParams) ctx := context.Background() message, err := inboxMultiplexer.Pop(ctx) if err != nil { @@ -307,7 +307,7 @@ func main() { } } - message := readMessage(chainConfig.ArbitrumChainParams.DataAvailabilityCommittee) + message := readMessage(chainConfig.ArbitrumChainParams.DataAvailabilityCommittee, &chainConfig.ArbitrumChainParams) chainContext := WavmChainContext{chainConfig: chainConfig} newBlock, _, err = arbos.ProduceBlock(message.Message, message.DelayedMessagesRead, lastBlockHeader, statedb, chainContext, false, core.NewMessageReplayContext(), false) @@ -317,7 +317,7 @@ func main() { } else { // Initialize ArbOS with this init message and create the genesis block. - message := readMessage(false) + message := readMessage(false, nil) // TODO: no idea how to correctly get chain config here (through the `initialArbosState` as above?) initMessage, err := message.Message.ParseInitMessage() if err != nil { diff --git a/system_tests/state_fuzz_test.go b/system_tests/state_fuzz_test.go index 20281b633d..c3ee263e5a 100644 --- a/system_tests/state_fuzz_test.go +++ b/system_tests/state_fuzz_test.go @@ -44,7 +44,13 @@ 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().ArbitrumChainParams, + ) ctx := context.Background() message, err := inboxMultiplexer.Pop(ctx) @@ -231,7 +237,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. @@ -239,3 +245,7 @@ func FuzzStateTransition(f *testing.F) { } }) } + +func getChainConfig() *params.ChainConfig { + return chaininfo.ArbitrumDevTestChainConfig() +} From b7aa16a32a648d2399c15ba5d7856ab343692aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 3 Nov 2025 20:42:04 +0100 Subject: [PATCH 05/35] Cover MEL --- arbnode/mel/extraction/message_extraction_function.go | 5 +++++ .../mel/extraction/message_extraction_function_test.go | 8 +++++++- arbnode/mel/extraction/types.go | 2 ++ arbnode/mel/runner/mel.go | 5 +++++ arbnode/mel/runner/mel_test.go | 1 + arbstate/inbox.go | 4 ++-- arbstate/inbox_fuzz_test.go | 2 +- 7 files changed, 23 insertions(+), 4 deletions(-) diff --git a/arbnode/mel/extraction/message_extraction_function.go b/arbnode/mel/extraction/message_extraction_function.go index 4f8fc57cd3..0bed03343a 100644 --- a/arbnode/mel/extraction/message_extraction_function.go +++ b/arbnode/mel/extraction/message_extraction_function.go @@ -9,6 +9,7 @@ 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" @@ -55,11 +56,13 @@ func ExtractMessages( delayedMsgDatabase DelayedMessageDatabase, receiptFetcher ReceiptFetcher, txsFetcher TransactionsFetcher, + arbitrumChainParams *params.ArbitrumChainParams, ) (*mel.State, []*arbostypes.MessageWithMetadata, []*mel.DelayedInboxMessage, error) { return extractMessagesImpl( ctx, inputState, parentChainHeader, + arbitrumChainParams, dataProviders, delayedMsgDatabase, txsFetcher, @@ -81,6 +84,7 @@ func extractMessagesImpl( ctx context.Context, inputState *mel.State, parentChainHeader *types.Header, + arbitrumChainParams *params.ArbitrumChainParams, dataProviders *daprovider.ReaderRegistry, delayedMsgDatabase DelayedMessageDatabase, txsFetcher TransactionsFetcher, @@ -200,6 +204,7 @@ func extractMessagesImpl( serialized, dataProviders, daprovider.KeysetValidate, + arbitrumChainParams, ) 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 8bc60ab2e1..12ae9c1e33 100644 --- a/arbnode/mel/extraction/message_extraction_function_test.go +++ b/arbnode/mel/extraction/message_extraction_function_test.go @@ -12,10 +12,12 @@ 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" ) @@ -31,7 +33,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, *daprovider.ReaderRegistry, daprovider.KeysetValidationMode) (*arbstate.SequencerMessage, error) + parseSequencerMsg func(context.Context, uint64, common.Hash, []byte, *daprovider.ReaderRegistry, daprovider.KeysetValidationMode, *params.ArbitrumChainParams) (*arbstate.SequencerMessage, error) extractBatchMessages func(context.Context, *mel.State, *arbstate.SequencerMessage, DelayedMessageDatabase) ([]*arbostypes.MessageWithMetadata, error) expectedError string expectedMsgCount uint64 @@ -150,6 +152,7 @@ func TestExtractMessages(t *testing.T) { nil, nil, txsFetcher, + &chaininfo.ArbitrumDevTestChainConfig().ArbitrumChainParams, ) } else { // Test the internal extractMessagesImpl function @@ -157,6 +160,7 @@ func TestExtractMessages(t *testing.T) { ctx, melState, header, + &chaininfo.ArbitrumDevTestChainConfig().ArbitrumChainParams, nil, nil, txsFetcher, @@ -321,6 +325,7 @@ func successfulParseSequencerMsg( data []byte, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, + arbitrumChainParams *params.ArbitrumChainParams, ) (*arbstate.SequencerMessage, error) { return nil, nil } @@ -332,6 +337,7 @@ func failingParseSequencerMsg( data []byte, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, + arbitrumChainParams *params.ArbitrumChainParams, ) (*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 a628e1b9e5..66c02a5728 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,7 @@ type sequencerMessageParserFunc func( data []byte, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, + arbitrumChainParams *params.ArbitrumChainParams, ) (*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 ddbed50c79..77d344ee2a 100644 --- a/arbnode/mel/runner/mel.go +++ b/arbnode/mel/runner/mel.go @@ -11,6 +11,7 @@ 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" @@ -37,6 +38,7 @@ type ParentChainReader interface { type MessageExtractor struct { stopwaiter.StopWaiter parentChainReader ParentChainReader + arbitrumChainParams *params.ArbitrumChainParams addrs *chaininfo.RollupAddresses melDB *Database msgConsumer mel.MessageConsumer @@ -51,6 +53,7 @@ type MessageExtractor struct { // to be used when extracting messages from the parent chain. func NewMessageExtractor( parentChainReader ParentChainReader, + arbitrumChainParams *params.ArbitrumChainParams, rollupAddrs *chaininfo.RollupAddresses, melDB *Database, msgConsumer mel.MessageConsumer, @@ -67,6 +70,7 @@ func NewMessageExtractor( } return &MessageExtractor{ parentChainReader: parentChainReader, + arbitrumChainParams: arbitrumChainParams, addrs: rollupAddrs, melDB: melDB, msgConsumer: msgConsumer, @@ -223,6 +227,7 @@ func (m *MessageExtractor) Act(ctx context.Context) (time.Duration, error) { m.melDB, receiptFetcher, txsFetcher, + m.arbitrumChainParams, ) if err != nil { return m.retryInterval, err diff --git a/arbnode/mel/runner/mel_test.go b/arbnode/mel/runner/mel_test.go index 8823576a06..3261bd78a8 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().ArbitrumChainParams, &chaininfo.RollupAddresses{}, melDb, messageConsumer, diff --git a/arbstate/inbox.go b/arbstate/inbox.go index da4dbe907a..3cceb6d555 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -117,7 +117,7 @@ func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash // Stage 2: If enabled, decode the zero heavy payload (saves gas based on calldata charging). if len(payload) > 0 && daprovider.IsZeroheavyEncodedHeaderByte(payload[0]) { maxZeroheavyDecompressedLen := 101*arbitrumChainParams.MaxUncompressedBatchSize/100 + 64 - pl, err := io.ReadAll(io.LimitReader(zeroheavy.NewZeroheavyDecoder(bytes.NewReader(payload[1:])), int64(maxZeroheavyDecompressedLen))) + 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 @@ -127,7 +127,7 @@ 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:], int(arbitrumChainParams.MaxUncompressedBatchSize)) + decompressed, err := arbcompress.Decompress(payload[1:], int(arbitrumChainParams.MaxUncompressedBatchSize)) // #nosec G115 if err == nil { reader := bytes.NewReader(decompressed) stream := rlp.NewStream(reader, 0) diff --git a/arbstate/inbox_fuzz_test.go b/arbstate/inbox_fuzz_test.go index 46fb957dc3..d3db183904 100644 --- a/arbstate/inbox_fuzz_test.go +++ b/arbstate/inbox_fuzz_test.go @@ -10,9 +10,9 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/arbos/arbostypes" + "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/daprovider" ) From 94f18b12c3f58388478a5990c14d77009c52f2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 10 Nov 2025 10:35:54 +0100 Subject: [PATCH 06/35] Add accessor for the limit in the config --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 4565d679f5..99f642a2d6 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 4565d679f5bbb80d81aa94b410879ae11055c958 +Subproject commit 99f642a2d66c4ed31819e69573636b257291e2fc From 47307591da8ffaa533ee9b0a13376fe6389bbc9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 10 Nov 2025 10:46:56 +0100 Subject: [PATCH 07/35] Pass chain config instead of arbitrum --- arbnode/batch_poster.go | 104 +++++++++--------- arbnode/inbox_tracker.go | 2 +- .../extraction/message_extraction_function.go | 8 +- .../message_extraction_function_test.go | 10 +- arbnode/mel/extraction/types.go | 2 +- arbnode/mel/runner/mel.go | 8 +- arbnode/mel/runner/mel_test.go | 2 +- arbnode/node.go | 26 ++--- arbstate/inbox.go | 14 +-- arbstate/inbox_fuzz_test.go | 2 +- cmd/replay/main.go | 6 +- system_tests/batch_poster_test.go | 48 ++++---- system_tests/state_fuzz_test.go | 2 +- 13 files changed, 117 insertions(+), 117 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index ef0451d140..f18cb4b5ba 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -94,25 +94,25 @@ type batchPosterPosition struct { type BatchPoster struct { stopwaiter.StopWaiter - l1Reader *headerreader.HeaderReader - inbox *InboxTracker - streamer *TransactionStreamer - arbOSVersionGetter execution.ArbOSVersionGetter - arbitrumChainParams *params.ArbitrumChainParams - config BatchPosterConfigFetcher - seqInbox *bridgegen.SequencerInbox - syncMonitor *SyncMonitor - seqInboxABI *abi.ABI - seqInboxAddr common.Address - bridgeAddr common.Address - gasRefunderAddr common.Address - building *buildingBatch - dapWriter daprovider.Writer - dapReaders *daprovider.ReaderRegistry - dataPoster *dataposter.DataPoster - redisLock *redislock.Simple - messagesPerBatch *arbmath.MovingAverage[uint64] - non4844BatchCount int // Count of consecutive non-4844 batches posted + l1Reader *headerreader.HeaderReader + inbox *InboxTracker + streamer *TransactionStreamer + arbOSVersionGetter execution.ArbOSVersionGetter + chainConfig *params.ChainConfig + config BatchPosterConfigFetcher + seqInbox *bridgegen.SequencerInbox + syncMonitor *SyncMonitor + seqInboxABI *abi.ABI + seqInboxAddr common.Address + bridgeAddr common.Address + gasRefunderAddr common.Address + building *buildingBatch + dapWriter daprovider.Writer + dapReaders *daprovider.ReaderRegistry + dataPoster *dataposter.DataPoster + redisLock *redislock.Simple + messagesPerBatch *arbmath.MovingAverage[uint64] + non4844BatchCount int // Count of consecutive non-4844 batches posted // This is an atomic variable that should only be accessed atomically. // An estimate of the number of batches we want to post but haven't yet. // This doesn't include batches which we don't want to post yet due to the L1 bounds. @@ -320,19 +320,19 @@ var TestBatchPosterConfig = BatchPosterConfig{ } type BatchPosterOpts struct { - DataPosterDB ethdb.Database - L1Reader *headerreader.HeaderReader - Inbox *InboxTracker - Streamer *TransactionStreamer - VersionGetter execution.ArbOSVersionGetter - SyncMonitor *SyncMonitor - Config BatchPosterConfigFetcher - DeployInfo *chaininfo.RollupAddresses - TransactOpts *bind.TransactOpts - DAPWriter daprovider.Writer - ParentChainID *big.Int - DAPReaders *daprovider.ReaderRegistry - ArbitrumChainParams *params.ArbitrumChainParams + DataPosterDB ethdb.Database + L1Reader *headerreader.HeaderReader + Inbox *InboxTracker + Streamer *TransactionStreamer + VersionGetter execution.ArbOSVersionGetter + SyncMonitor *SyncMonitor + Config BatchPosterConfigFetcher + DeployInfo *chaininfo.RollupAddresses + TransactOpts *bind.TransactOpts + DAPWriter daprovider.Writer + ParentChainID *big.Int + DAPReaders *daprovider.ReaderRegistry + ChainConfig *params.ChainConfig } func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, error) { @@ -375,24 +375,24 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e return nil, err } b := &BatchPoster{ - l1Reader: opts.L1Reader, - inbox: opts.Inbox, - streamer: opts.Streamer, - arbOSVersionGetter: opts.VersionGetter, - arbitrumChainParams: opts.ArbitrumChainParams, - syncMonitor: opts.SyncMonitor, - config: opts.Config, - seqInbox: seqInbox, - seqInboxABI: seqInboxABI, - seqInboxAddr: opts.DeployInfo.SequencerInbox, - gasRefunderAddr: opts.Config().gasRefunder, - bridgeAddr: opts.DeployInfo.Bridge, - dapWriter: opts.DAPWriter, - redisLock: redisLock, - dapReaders: opts.DAPReaders, - parentChain: &parent.ParentChain{ChainID: opts.ParentChainID, L1Reader: opts.L1Reader}, - checkEip7623: checkEip7623, - useEip7623: useEip7623, + l1Reader: opts.L1Reader, + inbox: opts.Inbox, + streamer: opts.Streamer, + arbOSVersionGetter: opts.VersionGetter, + chainConfig: opts.ChainConfig, + syncMonitor: opts.SyncMonitor, + config: opts.Config, + seqInbox: seqInbox, + seqInboxABI: seqInboxABI, + seqInboxAddr: opts.DeployInfo.SequencerInbox, + gasRefunderAddr: opts.Config().gasRefunder, + bridgeAddr: opts.DeployInfo.Bridge, + dapWriter: opts.DAPWriter, + redisLock: redisLock, + dapReaders: opts.DAPReaders, + parentChain: &parent.ParentChain{ChainID: opts.ParentChainID, L1Reader: opts.L1Reader}, + checkEip7623: checkEip7623, + useEip7623: useEip7623, } b.messagesPerBatch, err = arbmath.NewMovingAverage[uint64](20) if err != nil { @@ -944,7 +944,7 @@ func (b *BatchPoster) newBatchSegments(ctx context.Context, firstDelayed uint64, recompressionLevel: recompressionLevel, rawSegments: make([][]byte, 0, 128), delayedMsg: firstDelayed, - maxUncompressedSize: int(b.arbitrumChainParams.MaxUncompressedBatchSize), // #nosec G115 + maxUncompressedSize: int(b.chainConfig.MaxUncompressedBatchSize()), // #nosec G115 }, nil } @@ -1846,7 +1846,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, b.arbitrumChainParams) // nolint:gosec + simMux := arbstate.NewInboxMultiplexer(b.building.muxBackend, batchPosition.DelayedMessageCount, dapReaders, daprovider.KeysetValidate, b.chainConfig) // nolint:gosec 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/inbox_tracker.go b/arbnode/inbox_tracker.go index 663022b8d5..3e2199e20c 100644 --- a/arbnode/inbox_tracker.go +++ b/arbnode/inbox_tracker.go @@ -774,7 +774,7 @@ func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client *ethclien prevbatchmeta.DelayedMessageCount, t.dapReaders, daprovider.KeysetValidate, - &t.txStreamer.chainConfig.ArbitrumChainParams, + t.txStreamer.chainConfig, ) batchMessageCounts := make(map[uint64]arbutil.MessageIndex) currentPos := prevbatchmeta.MessageCount + 1 diff --git a/arbnode/mel/extraction/message_extraction_function.go b/arbnode/mel/extraction/message_extraction_function.go index 0bed03343a..262abae327 100644 --- a/arbnode/mel/extraction/message_extraction_function.go +++ b/arbnode/mel/extraction/message_extraction_function.go @@ -56,13 +56,13 @@ func ExtractMessages( delayedMsgDatabase DelayedMessageDatabase, receiptFetcher ReceiptFetcher, txsFetcher TransactionsFetcher, - arbitrumChainParams *params.ArbitrumChainParams, + chainConfig *params.ChainConfig, ) (*mel.State, []*arbostypes.MessageWithMetadata, []*mel.DelayedInboxMessage, error) { return extractMessagesImpl( ctx, inputState, parentChainHeader, - arbitrumChainParams, + chainConfig, dataProviders, delayedMsgDatabase, txsFetcher, @@ -84,7 +84,7 @@ func extractMessagesImpl( ctx context.Context, inputState *mel.State, parentChainHeader *types.Header, - arbitrumChainParams *params.ArbitrumChainParams, + chainConfig *params.ChainConfig, dataProviders *daprovider.ReaderRegistry, delayedMsgDatabase DelayedMessageDatabase, txsFetcher TransactionsFetcher, @@ -204,7 +204,7 @@ func extractMessagesImpl( serialized, dataProviders, daprovider.KeysetValidate, - arbitrumChainParams, + chainConfig, ) 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 12ae9c1e33..fb74c3f1eb 100644 --- a/arbnode/mel/extraction/message_extraction_function_test.go +++ b/arbnode/mel/extraction/message_extraction_function_test.go @@ -33,7 +33,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, *daprovider.ReaderRegistry, daprovider.KeysetValidationMode, *params.ArbitrumChainParams) (*arbstate.SequencerMessage, error) + parseSequencerMsg func(context.Context, uint64, common.Hash, []byte, *daprovider.ReaderRegistry, daprovider.KeysetValidationMode, *params.ChainConfig) (*arbstate.SequencerMessage, error) extractBatchMessages func(context.Context, *mel.State, *arbstate.SequencerMessage, DelayedMessageDatabase) ([]*arbostypes.MessageWithMetadata, error) expectedError string expectedMsgCount uint64 @@ -152,7 +152,7 @@ func TestExtractMessages(t *testing.T) { nil, nil, txsFetcher, - &chaininfo.ArbitrumDevTestChainConfig().ArbitrumChainParams, + chaininfo.ArbitrumDevTestChainConfig(), ) } else { // Test the internal extractMessagesImpl function @@ -160,7 +160,7 @@ func TestExtractMessages(t *testing.T) { ctx, melState, header, - &chaininfo.ArbitrumDevTestChainConfig().ArbitrumChainParams, + chaininfo.ArbitrumDevTestChainConfig(), nil, nil, txsFetcher, @@ -325,7 +325,7 @@ func successfulParseSequencerMsg( data []byte, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, - arbitrumChainParams *params.ArbitrumChainParams, + chainConfig *params.ChainConfig, ) (*arbstate.SequencerMessage, error) { return nil, nil } @@ -337,7 +337,7 @@ func failingParseSequencerMsg( data []byte, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, - arbitrumChainParams *params.ArbitrumChainParams, + chainConfig *params.ChainConfig, ) (*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 66c02a5728..d60462eba8 100644 --- a/arbnode/mel/extraction/types.go +++ b/arbnode/mel/extraction/types.go @@ -65,7 +65,7 @@ type sequencerMessageParserFunc func( data []byte, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, - arbitrumChainParams *params.ArbitrumChainParams, + chainConfig *params.ChainConfig, ) (*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 77d344ee2a..d1a9c2e811 100644 --- a/arbnode/mel/runner/mel.go +++ b/arbnode/mel/runner/mel.go @@ -38,7 +38,7 @@ type ParentChainReader interface { type MessageExtractor struct { stopwaiter.StopWaiter parentChainReader ParentChainReader - arbitrumChainParams *params.ArbitrumChainParams + chainConfig *params.ChainConfig addrs *chaininfo.RollupAddresses melDB *Database msgConsumer mel.MessageConsumer @@ -53,7 +53,7 @@ type MessageExtractor struct { // to be used when extracting messages from the parent chain. func NewMessageExtractor( parentChainReader ParentChainReader, - arbitrumChainParams *params.ArbitrumChainParams, + chainConfig *params.ChainConfig, rollupAddrs *chaininfo.RollupAddresses, melDB *Database, msgConsumer mel.MessageConsumer, @@ -70,7 +70,7 @@ func NewMessageExtractor( } return &MessageExtractor{ parentChainReader: parentChainReader, - arbitrumChainParams: arbitrumChainParams, + chainConfig: chainConfig, addrs: rollupAddrs, melDB: melDB, msgConsumer: msgConsumer, @@ -227,7 +227,7 @@ func (m *MessageExtractor) Act(ctx context.Context) (time.Duration, error) { m.melDB, receiptFetcher, txsFetcher, - m.arbitrumChainParams, + m.chainConfig, ) if err != nil { return m.retryInterval, err diff --git a/arbnode/mel/runner/mel_test.go b/arbnode/mel/runner/mel_test.go index 3261bd78a8..d3c8767a56 100644 --- a/arbnode/mel/runner/mel_test.go +++ b/arbnode/mel/runner/mel_test.go @@ -39,7 +39,7 @@ func TestMessageExtractor(t *testing.T) { messageConsumer := &mockMessageConsumer{} extractor, err := NewMessageExtractor( parentChainReader, - &chaininfo.ArbitrumDevTestChainConfig().ArbitrumChainParams, + chaininfo.ArbitrumDevTestChainConfig(), &chaininfo.RollupAddresses{}, melDb, messageConsumer, diff --git a/arbnode/node.go b/arbnode/node.go index a12652d78b..c478215ec9 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -953,19 +953,19 @@ func getBatchPoster( } var err error batchPoster, err = NewBatchPoster(ctx, &BatchPosterOpts{ - DataPosterDB: rawdb.NewTable(arbDb, storage.BatchPosterPrefix), - L1Reader: l1Reader, - Inbox: inboxTracker, - Streamer: txStreamer, - VersionGetter: arbOSVersionGetter, - SyncMonitor: syncMonitor, - Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, - DeployInfo: deployInfo, - TransactOpts: txOptsBatchPoster, - DAPWriter: dapWriter, - ParentChainID: parentChainID, - DAPReaders: dapReaders, - ArbitrumChainParams: &l2Config.ArbitrumChainParams, + DataPosterDB: rawdb.NewTable(arbDb, storage.BatchPosterPrefix), + L1Reader: l1Reader, + Inbox: inboxTracker, + Streamer: txStreamer, + VersionGetter: arbOSVersionGetter, + SyncMonitor: syncMonitor, + Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, + DeployInfo: deployInfo, + TransactOpts: txOptsBatchPoster, + DAPWriter: dapWriter, + ParentChainID: parentChainID, + DAPReaders: dapReaders, + ChainConfig: l2Config, }) if err != nil { return nil, err diff --git a/arbstate/inbox.go b/arbstate/inbox.go index 3cceb6d555..783ec4cee2 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -50,7 +50,7 @@ type SequencerMessage struct { const MaxSegmentsPerSequencerMessage = 100 * 1024 -func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash common.Hash, data []byte, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, arbitrumChainParams *params.ArbitrumChainParams) (*SequencerMessage, error) { +func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash common.Hash, data []byte, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, chainConfig *params.ChainConfig) (*SequencerMessage, error) { if len(data) < 40 { return nil, errors.New("sequencer message missing L1 header") } @@ -116,7 +116,7 @@ func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash // Stage 2: If enabled, decode the zero heavy payload (saves gas based on calldata charging). if len(payload) > 0 && daprovider.IsZeroheavyEncodedHeaderByte(payload[0]) { - maxZeroheavyDecompressedLen := 101*arbitrumChainParams.MaxUncompressedBatchSize/100 + 64 + maxZeroheavyDecompressedLen := 101*chainConfig.MaxUncompressedBatchSize()/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()) @@ -127,7 +127,7 @@ 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:], int(arbitrumChainParams.MaxUncompressedBatchSize)) // #nosec G115 + decompressed, err := arbcompress.Decompress(payload[1:], int(chainConfig.MaxUncompressedBatchSize())) // #nosec G115 if err == nil { reader := bytes.NewReader(decompressed) stream := rlp.NewStream(reader, 0) @@ -166,7 +166,7 @@ func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash type inboxMultiplexer struct { backend InboxBackend delayedMessagesRead uint64 - arbitrumChainParams *params.ArbitrumChainParams + chainConfig *params.ChainConfig dapReaders *daprovider.ReaderRegistry cachedSequencerMessage *SequencerMessage cachedSequencerMessageNum uint64 @@ -181,11 +181,11 @@ type inboxMultiplexer struct { keysetValidationMode daprovider.KeysetValidationMode } -func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, arbitrumChainParams *params.ArbitrumChainParams) arbostypes.InboxMultiplexer { +func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapReaders *daprovider.ReaderRegistry, keysetValidationMode daprovider.KeysetValidationMode, chainConfig *params.ChainConfig) arbostypes.InboxMultiplexer { return &inboxMultiplexer{ backend: backend, delayedMessagesRead: delayedMessagesRead, - arbitrumChainParams: arbitrumChainParams, + chainConfig: chainConfig, dapReaders: dapReaders, cachedSequencerMessage: nil, cachedSequencerMessageNum: 0, @@ -214,7 +214,7 @@ func (r *inboxMultiplexer) Pop(ctx context.Context) (*arbostypes.MessageWithMeta } r.cachedSequencerMessageNum = r.backend.GetSequencerInboxPosition() var err error - r.cachedSequencerMessage, err = ParseSequencerMessage(ctx, r.cachedSequencerMessageNum, batchBlockHash, bytes, r.dapReaders, r.keysetValidationMode, r.arbitrumChainParams) + r.cachedSequencerMessage, err = ParseSequencerMessage(ctx, r.cachedSequencerMessageNum, batchBlockHash, bytes, r.dapReaders, r.keysetValidationMode, r.chainConfig) if err != nil { return nil, err } diff --git a/arbstate/inbox_fuzz_test.go b/arbstate/inbox_fuzz_test.go index d3db183904..2c146a0e17 100644 --- a/arbstate/inbox_fuzz_test.go +++ b/arbstate/inbox_fuzz_test.go @@ -76,7 +76,7 @@ func FuzzInboxMultiplexer(f *testing.F) { 0, nil, daprovider.KeysetValidate, - &chaininfo.ArbitrumDevTestChainConfig().ArbitrumChainParams, + chaininfo.ArbitrumDevTestChainConfig(), ) _, err := multiplexer.Pop(context.TODO()) if err != nil { diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 2e1eb35cad..5361369654 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -223,7 +223,7 @@ func main() { } return wavmio.ReadInboxMessage(batchNum), nil } - readMessage := func(dasEnabled bool, arbitrumChainParams *params.ArbitrumChainParams) *arbostypes.MessageWithMetadata { + readMessage := func(dasEnabled bool, chainConfig *params.ChainConfig) *arbostypes.MessageWithMetadata { var delayedMessagesRead uint64 if lastBlockHeader != nil { delayedMessagesRead = lastBlockHeader.Nonce.Uint64() @@ -251,7 +251,7 @@ func main() { if err != nil { panic(fmt.Sprintf("Failed to register blob reader: %v", err)) } - inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode, arbitrumChainParams) + inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode, chainConfig) ctx := context.Background() message, err := inboxMultiplexer.Pop(ctx) if err != nil { @@ -307,7 +307,7 @@ func main() { } } - message := readMessage(chainConfig.ArbitrumChainParams.DataAvailabilityCommittee, &chainConfig.ArbitrumChainParams) + 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) diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index d76bf0eb20..93450d2d4e 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -149,18 +149,18 @@ func testBatchPosterParallel(t *testing.T, useRedis bool, useRedisLock bool) { batchPosterConfig := builder.nodeConfig.BatchPoster batchPoster, err := arbnode.NewBatchPoster(ctx, &arbnode.BatchPosterOpts{ - DataPosterDB: nil, - L1Reader: builder.L2.ConsensusNode.L1Reader, - Inbox: builder.L2.ConsensusNode.InboxTracker, - Streamer: builder.L2.ConsensusNode.TxStreamer, - VersionGetter: builder.L2.ExecNode, - SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, - Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, - DeployInfo: builder.L2.ConsensusNode.DeployInfo, - TransactOpts: &seqTxOpts, - DAPWriter: nil, - ParentChainID: parentChainID, - ArbitrumChainParams: &builder.chainConfig.ArbitrumChainParams, + DataPosterDB: nil, + L1Reader: builder.L2.ConsensusNode.L1Reader, + Inbox: builder.L2.ConsensusNode.InboxTracker, + Streamer: builder.L2.ConsensusNode.TxStreamer, + VersionGetter: builder.L2.ExecNode, + SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, + Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, + DeployInfo: builder.L2.ConsensusNode.DeployInfo, + TransactOpts: &seqTxOpts, + DAPWriter: nil, + ParentChainID: parentChainID, + ChainConfig: builder.chainConfig, }, ) Require(t, err) @@ -290,18 +290,18 @@ func TestRedisBatchPosterHandoff(t *testing.T) { batchPosterConfig := builder.nodeConfig.BatchPoster batchPoster, err := arbnode.NewBatchPoster(ctx, &arbnode.BatchPosterOpts{ - DataPosterDB: nil, - L1Reader: builder.L2.ConsensusNode.L1Reader, - Inbox: builder.L2.ConsensusNode.InboxTracker, - Streamer: builder.L2.ConsensusNode.TxStreamer, - VersionGetter: builder.L2.ExecNode, - SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, - Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, - DeployInfo: builder.L2.ConsensusNode.DeployInfo, - TransactOpts: &seqTxOpts, - DAPWriter: nil, - ParentChainID: parentChainID, - ArbitrumChainParams: &builder.chainConfig.ArbitrumChainParams, + DataPosterDB: nil, + L1Reader: builder.L2.ConsensusNode.L1Reader, + Inbox: builder.L2.ConsensusNode.InboxTracker, + Streamer: builder.L2.ConsensusNode.TxStreamer, + VersionGetter: builder.L2.ExecNode, + SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, + Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, + DeployInfo: builder.L2.ConsensusNode.DeployInfo, + TransactOpts: &seqTxOpts, + DAPWriter: nil, + ParentChainID: parentChainID, + ChainConfig: builder.chainConfig, }, ) Require(t, err) diff --git a/system_tests/state_fuzz_test.go b/system_tests/state_fuzz_test.go index c3ee263e5a..7f464d4aa4 100644 --- a/system_tests/state_fuzz_test.go +++ b/system_tests/state_fuzz_test.go @@ -49,7 +49,7 @@ func BuildBlock( delayedMessagesRead, nil, daprovider.KeysetValidate, - &getChainConfig().ArbitrumChainParams, + getChainConfig(), ) ctx := context.Background() From 75d679f822ceab9a7ef912f1783d2ed4e9f787df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 10 Nov 2025 10:51:23 +0100 Subject: [PATCH 08/35] Avoid nil pointer dereference --- arbstate/inbox.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arbstate/inbox.go b/arbstate/inbox.go index 783ec4cee2..021862f4fe 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -113,10 +113,16 @@ 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() + } 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]) { - maxZeroheavyDecompressedLen := 101*chainConfig.MaxUncompressedBatchSize()/100 + 64 + 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()) @@ -127,7 +133,7 @@ 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:], int(chainConfig.MaxUncompressedBatchSize())) // #nosec G115 + decompressed, err := arbcompress.Decompress(payload[1:], int(uncompressedBatchSizeLimit)) // #nosec G115 if err == nil { reader := bytes.NewReader(decompressed) stream := rlp.NewStream(reader, 0) From 5238ed686633c5ccf367544607965d37cc2e9034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 10 Nov 2025 10:53:29 +0100 Subject: [PATCH 09/35] Add rationale for nil --- cmd/replay/main.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 5361369654..8c89277c60 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -317,7 +317,9 @@ func main() { } else { // Initialize ArbOS with this init message and create the genesis block. - message := readMessage(false, nil) // TODO: no idea how to correctly get chain config here (through the `initialArbosState` as above?) + // 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 { From ab40ca20163919ba281747635ab6afb984deec4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 10 Nov 2025 11:01:40 +0100 Subject: [PATCH 10/35] update pin --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 99f642a2d6..c99cdb0b68 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 99f642a2d66c4ed31819e69573636b257291e2fc +Subproject commit c99cdb0b68b62822e7b43956df752cf5174e3923 From 69cab034de8959baec42204958a876f9fa2a3f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 10 Nov 2025 11:33:23 +0100 Subject: [PATCH 11/35] fix forkid --- system_tests/eth_config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_tests/eth_config_test.go b/system_tests/eth_config_test.go index ed0ae3848e..5da1082497 100644 --- a/system_tests/eth_config_test.go +++ b/system_tests/eth_config_test.go @@ -49,7 +49,7 @@ func TestEthConfig(t *testing.T) { ActivationTime: 0, BlobSchedule: nil, ChainId: (*hexutil.Big)(hexutil.MustDecodeBig("0x64aba")), - ForkId: (hexutil.Bytes)(hexutil.MustDecode("0x93dd2fec")), + ForkId: (hexutil.Bytes)(hexutil.MustDecode("0x17362c7e")), Precompiles: map[string]common.Address{ "ArbAddressTable": common.HexToAddress("0x0000000000000000000000000000000000000066"), "ArbAggregator": common.HexToAddress("0x000000000000000000000000000000000000006d"), From 776f1b5e502f4edb473d183d00d080053acecbcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Fri, 5 Dec 2025 12:34:48 +0100 Subject: [PATCH 12/35] Update fork id in test --- system_tests/eth_config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_tests/eth_config_test.go b/system_tests/eth_config_test.go index f4a8560519..cd6f5558c3 100644 --- a/system_tests/eth_config_test.go +++ b/system_tests/eth_config_test.go @@ -49,7 +49,7 @@ func TestEthConfig(t *testing.T) { ActivationTime: 0, BlobSchedule: nil, ChainId: (*hexutil.Big)(hexutil.MustDecodeBig("0x64aba")), - ForkId: (hexutil.Bytes)(hexutil.MustDecode("0x9aa9b1b0")), + ForkId: (hexutil.Bytes)(hexutil.MustDecode("0xdd219e5e")), Precompiles: map[string]common.Address{ "ArbAddressTable": common.HexToAddress("0x0000000000000000000000000000000000000066"), "ArbAggregator": common.HexToAddress("0x000000000000000000000000000000000000006d"), From 82e8518837293541514b1be94b43e552516bc5c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Fri, 5 Dec 2025 12:36:57 +0100 Subject: [PATCH 13/35] Leaving chain info JSON as before (consistent with on-chain info) --- cmd/chaininfo/arbitrum_chain_info.json | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/cmd/chaininfo/arbitrum_chain_info.json b/cmd/chaininfo/arbitrum_chain_info.json index a9303ebc0c..4afacd5115 100644 --- a/cmd/chaininfo/arbitrum_chain_info.json +++ b/cmd/chaininfo/arbitrum_chain_info.json @@ -37,8 +37,7 @@ "DataAvailabilityCommittee": false, "InitialArbOSVersion": 6, "InitialChainOwner": "0xd345e41ae2cb00311956aa7109fc801ae8c81a52", - "GenesisBlockNum": 22207818, - "MaxUncompressedBatchSize": 16777216 + "GenesisBlockNum": 22207818 } }, "rollup": { @@ -88,8 +87,7 @@ "DataAvailabilityCommittee": true, "InitialArbOSVersion": 1, "InitialChainOwner": "0x9c040726f2a657226ed95712245dee84b650a1b5", - "GenesisBlockNum": 0, - "MaxUncompressedBatchSize": 16777216 + "GenesisBlockNum": 0 } }, "rollup": { @@ -136,8 +134,7 @@ "DataAvailabilityCommittee": false, "InitialArbOSVersion": 2, "InitialChainOwner": "0x186b56023d42b2b4e7616589a5c62eef5fca21dd", - "GenesisBlockNum": 0, - "MaxUncompressedBatchSize": 16777216 + "GenesisBlockNum": 0 } }, "rollup": { @@ -178,8 +175,7 @@ "DataAvailabilityCommittee": false, "InitialArbOSVersion": 51, "InitialChainOwner": "0x0000000000000000000000000000000000000000", - "GenesisBlockNum": 0, - "MaxUncompressedBatchSize": 16777216 + "GenesisBlockNum": 0 } } }, @@ -211,8 +207,7 @@ "DataAvailabilityCommittee": true, "InitialArbOSVersion": 32, "InitialChainOwner": "0x0000000000000000000000000000000000000000", - "GenesisBlockNum": 0, - "MaxUncompressedBatchSize": 16777216 + "GenesisBlockNum": 0 } } }, @@ -252,8 +247,7 @@ "DataAvailabilityCommittee": false, "InitialArbOSVersion": 10, "InitialChainOwner": "0x71B61c2E250AFa05dFc36304D6c91501bE0965D8", - "GenesisBlockNum": 0, - "MaxUncompressedBatchSize": 16777216 + "GenesisBlockNum": 0 } }, "rollup": { From 6ecd436caaec2cf1d1099224ecb73fc2ebf4d0a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Fri, 5 Dec 2025 15:08:56 +0100 Subject: [PATCH 14/35] Add a test for configurable limit in batch poster --- system_tests/batch_poster_test.go | 61 +++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index 437d6a3268..8c7fcee1cb 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -971,3 +971,64 @@ func TestBatchPosterPostsReportOnlyBatchAfterMaxEmptyBatchDelay(t *testing.T) { require.NoError(t, err) require.True(t, posted, "expected second batch to be posted by MaxEmptyBatchDelay") } + +func TestBatchSizeLimitIsConfigurable(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + builder := NewNodeBuilder(ctx). + DefaultConfig(t, true). + TakeOwnership() + + // Disable automatic background posting + builder.nodeConfig.BatchPoster.PollInterval = time.Hour + // Set a small initial max batch size. Since we want to exceed it with a single tx, + // it must also be smaller than the exec config's max tx data size. + builder.chainConfig.ArbitrumChainParams.MaxUncompressedBatchSize = 50_000 // 50 kB + + cleanup := builder.Build(t) + defer cleanup() + + postBatch := func(expectToPost bool) { + posted, err := builder.L2.ConsensusNode.BatchPoster.MaybePostSequencerBatch(ctx) + require.NoError(t, err) + require.Equal(t, expectToPost, posted) + } + + // Force immediate post of first batch in case anything is pending. + postBatch(true) + + msgCountBeforeSubmission, err := builder.L2.ConsensusNode.TxStreamer.GetMessageCount() + Require(t, err) + + // Post a big tx that itself exceeds the max batch size. + bigData := make([]byte, builder.chainConfig.MaxUncompressedBatchSize()+1) + tx := builder.L2Info.PrepareTx("Faucet", "Owner", builder.L2Info.TransferGas, common.Big1, bigData) + _ = builder.L2.SendWaitTestTransactions(t, []*types.Transaction{tx}) + + msgCountAfterSubmission, err := builder.L2.ConsensusNode.TxStreamer.GetMessageCount() + Require(t, err) + + // Ensure the message was recorded and made visible to the batch poster. + require.Equal(t, msgCountBeforeSubmission+1, msgCountAfterSubmission) + + // Attempt to post batch, should not post due to size limit. + postBatch(false) + + // Increase the max batch size to allow posting and restart the L2 node to apply the change. + builder.chainConfig.ArbitrumChainParams.MaxUncompressedBatchSize = 100_000 + builder.RestartL2Node(t) + + msgCountBeforeSubmission, err = builder.L2.ConsensusNode.TxStreamer.GetMessageCount() + Require(t, err) + + // Re-submit the big tx that itself exceeds the previous max batch size. + _ = builder.L2.SendWaitTestTransactions(t, []*types.Transaction{tx}) + + msgCountAfterSubmission, err = builder.L2.ConsensusNode.TxStreamer.GetMessageCount() + Require(t, err) + require.Equal(t, msgCountBeforeSubmission+1, msgCountAfterSubmission) + + // Attempt to post batch, should post successfully now. + postBatch(true) +} From b078ad4b22e9336c63b4905401e651de6ec24033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 8 Dec 2025 15:28:26 +0100 Subject: [PATCH 15/35] Add two tests for parsing --- system_tests/batch_size_limit_test.go | 143 ++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 system_tests/batch_size_limit_test.go diff --git a/system_tests/batch_size_limit_test.go b/system_tests/batch_size_limit_test.go new file mode 100644 index 0000000000..4571813ff6 --- /dev/null +++ b/system_tests/batch_size_limit_test.go @@ -0,0 +1,143 @@ +package arbtest + +import ( + "bytes" + "context" + "math/big" + "testing" + "time" + + "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" + "github.com/stretchr/testify/require" +) + +const ( + SenderAccount = "Sender" + ReceiverAccount = "Receiver" + TransferAmount = 1000000 + NewUncompressedSizeLimit = params.DefaultMaxUncompressedBatchSize * 2 +) + +func TestTooBigBatchGetsRejected(t *testing.T) { + builder, ctx, cleanup := setUp(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 := setUp(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) +} + +// setup 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 setUp(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)) + + 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") +} From 2e898fd9f7b0a9285384334f9e831fef4391c95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 8 Dec 2025 16:07:38 +0100 Subject: [PATCH 16/35] Lint --- system_tests/batch_size_limit_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system_tests/batch_size_limit_test.go b/system_tests/batch_size_limit_test.go index 4571813ff6..9a1e3cb468 100644 --- a/system_tests/batch_size_limit_test.go +++ b/system_tests/batch_size_limit_test.go @@ -7,13 +7,15 @@ import ( "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" - "github.com/stretchr/testify/require" ) const ( From 86125b01d855704a8509ab00d4d3f360a5bf8cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 8 Dec 2025 16:09:23 +0100 Subject: [PATCH 17/35] ForkID --- system_tests/eth_config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_tests/eth_config_test.go b/system_tests/eth_config_test.go index cd6f5558c3..f4a8560519 100644 --- a/system_tests/eth_config_test.go +++ b/system_tests/eth_config_test.go @@ -49,7 +49,7 @@ func TestEthConfig(t *testing.T) { ActivationTime: 0, BlobSchedule: nil, ChainId: (*hexutil.Big)(hexutil.MustDecodeBig("0x64aba")), - ForkId: (hexutil.Bytes)(hexutil.MustDecode("0xdd219e5e")), + ForkId: (hexutil.Bytes)(hexutil.MustDecode("0x9aa9b1b0")), Precompiles: map[string]common.Address{ "ArbAddressTable": common.HexToAddress("0x0000000000000000000000000000000000000066"), "ArbAggregator": common.HexToAddress("0x000000000000000000000000000000000000006d"), From 1c4e9431f1812be59a521f02c4aa5e10c8ab95a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 14:19:52 +0100 Subject: [PATCH 18/35] Remove batch poster system test --- system_tests/batch_poster_test.go | 61 ------------------------------- 1 file changed, 61 deletions(-) diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index 8c7fcee1cb..437d6a3268 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -971,64 +971,3 @@ func TestBatchPosterPostsReportOnlyBatchAfterMaxEmptyBatchDelay(t *testing.T) { require.NoError(t, err) require.True(t, posted, "expected second batch to be posted by MaxEmptyBatchDelay") } - -func TestBatchSizeLimitIsConfigurable(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - builder := NewNodeBuilder(ctx). - DefaultConfig(t, true). - TakeOwnership() - - // Disable automatic background posting - builder.nodeConfig.BatchPoster.PollInterval = time.Hour - // Set a small initial max batch size. Since we want to exceed it with a single tx, - // it must also be smaller than the exec config's max tx data size. - builder.chainConfig.ArbitrumChainParams.MaxUncompressedBatchSize = 50_000 // 50 kB - - cleanup := builder.Build(t) - defer cleanup() - - postBatch := func(expectToPost bool) { - posted, err := builder.L2.ConsensusNode.BatchPoster.MaybePostSequencerBatch(ctx) - require.NoError(t, err) - require.Equal(t, expectToPost, posted) - } - - // Force immediate post of first batch in case anything is pending. - postBatch(true) - - msgCountBeforeSubmission, err := builder.L2.ConsensusNode.TxStreamer.GetMessageCount() - Require(t, err) - - // Post a big tx that itself exceeds the max batch size. - bigData := make([]byte, builder.chainConfig.MaxUncompressedBatchSize()+1) - tx := builder.L2Info.PrepareTx("Faucet", "Owner", builder.L2Info.TransferGas, common.Big1, bigData) - _ = builder.L2.SendWaitTestTransactions(t, []*types.Transaction{tx}) - - msgCountAfterSubmission, err := builder.L2.ConsensusNode.TxStreamer.GetMessageCount() - Require(t, err) - - // Ensure the message was recorded and made visible to the batch poster. - require.Equal(t, msgCountBeforeSubmission+1, msgCountAfterSubmission) - - // Attempt to post batch, should not post due to size limit. - postBatch(false) - - // Increase the max batch size to allow posting and restart the L2 node to apply the change. - builder.chainConfig.ArbitrumChainParams.MaxUncompressedBatchSize = 100_000 - builder.RestartL2Node(t) - - msgCountBeforeSubmission, err = builder.L2.ConsensusNode.TxStreamer.GetMessageCount() - Require(t, err) - - // Re-submit the big tx that itself exceeds the previous max batch size. - _ = builder.L2.SendWaitTestTransactions(t, []*types.Transaction{tx}) - - msgCountAfterSubmission, err = builder.L2.ConsensusNode.TxStreamer.GetMessageCount() - Require(t, err) - require.Equal(t, msgCountBeforeSubmission+1, msgCountAfterSubmission) - - // Attempt to post batch, should post successfully now. - postBatch(true) -} From dec7e982ddb15ca5aece5306c90e9c67e0d90f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 14:21:25 +0100 Subject: [PATCH 19/35] Rename setup function --- system_tests/batch_size_limit_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/system_tests/batch_size_limit_test.go b/system_tests/batch_size_limit_test.go index 9a1e3cb468..ed3514efb1 100644 --- a/system_tests/batch_size_limit_test.go +++ b/system_tests/batch_size_limit_test.go @@ -26,7 +26,7 @@ const ( ) func TestTooBigBatchGetsRejected(t *testing.T) { - builder, ctx, cleanup := setUp(t, false) + builder, ctx, cleanup := setupNodeForTestingBatchSizeLimit(t, false) defer cleanup() checkReceiverAccountBalance(t, ctx, builder, 0) @@ -40,7 +40,7 @@ func TestTooBigBatchGetsRejected(t *testing.T) { } func TestCanIncreaseBatchSizeLimit(t *testing.T) { - builder, ctx, cleanup := setUp(t, true) + builder, ctx, cleanup := setupNodeForTestingBatchSizeLimit(t, true) defer cleanup() checkReceiverAccountBalance(t, ctx, builder, 0) @@ -53,10 +53,10 @@ func TestCanIncreaseBatchSizeLimit(t *testing.T) { checkReceiverAccountBalance(t, ctx, builder, TransferAmount) } -// setup initializes a test node with the option to set a higher uncompressed batch size limit. +// 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 setUp(t *testing.T, setHighLimit bool) (*NodeBuilder, context.Context, func()) { +func setupNodeForTestingBatchSizeLimit(t *testing.T, setHighLimit bool) (*NodeBuilder, context.Context, func()) { ctx, cancel := context.WithCancel(context.Background()) builder := NewNodeBuilder(ctx).DefaultConfig(t, true) From 24a2d00ba11532a920880adb1c3070b3babc573a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 14:26:08 +0100 Subject: [PATCH 20/35] Make batch size limit arbos-version dependent --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 6b74e985c8..ae47bc8add 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 6b74e985c892f0221e444bd42e5dcfb02f1519fe +Subproject commit ae47bc8add8244eff461358bb72a5618ba284f59 From d8a53949f4e475f3570ba8bc662d4be110b5ccde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 14:54:14 +0100 Subject: [PATCH 21/35] Use arbos version in batch poster --- arbnode/batch_poster.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 898c7791d0..7e3e3b44d4 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -924,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 @@ -993,7 +993,7 @@ func (b *BatchPoster) newBatchSegments(ctx context.Context, firstDelayed uint64, recompressionLevel: recompressionLevel, rawSegments: make([][]byte, 0, 128), delayedMsg: firstDelayed, - maxUncompressedSize: int(b.chainConfig.MaxUncompressedBatchSize()), // #nosec G115 + maxUncompressedSize: int(b.chainConfig.MaxUncompressedBatchSize(arbosVersion)), // #nosec G115 }, nil } @@ -1421,6 +1421,7 @@ func (b *BatchPoster) MaybePostSequencerBatch(ctx context.Context) (bool, error) return false, err } config := b.config() + arbOSVersion, err := b.arbOSVersionGetter.ArbOSVersionForMessageIndex(arbutil.MessageIndex(arbmath.SaturatingUSub(uint64(batchPosition.MessageCount), 1))).Await(ctx) buildingForEthDA := len(b.dapWriters) == 0 || b.ethDAFallbackRemaining > 0 // Determine if we should use 4844 blobs (only relevant when posting to EthDA) var use4844 bool @@ -1428,7 +1429,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 } @@ -1488,7 +1488,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 } From b4d5ccee6e2b28c82759369a27da30b62fcf965f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 15:08:48 +0100 Subject: [PATCH 22/35] Use arbos version in inbox and MEL --- .../extraction/message_extraction_function.go | 11 +++++++++++ .../message_extraction_function_test.go | 14 +++++++++++++- arbnode/mel/extraction/types.go | 1 + arbnode/mel/runner/mel.go | 3 +++ arbstate/inbox.go | 16 ++++++++++++---- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/arbnode/mel/extraction/message_extraction_function.go b/arbnode/mel/extraction/message_extraction_function.go index 342ba93e67..53e624d044 100644 --- a/arbnode/mel/extraction/message_extraction_function.go +++ b/arbnode/mel/extraction/message_extraction_function.go @@ -14,7 +14,9 @@ import ( "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. @@ -57,6 +59,7 @@ func ExtractMessages( receiptFetcher ReceiptFetcher, txsFetcher TransactionsFetcher, chainConfig *params.ChainConfig, + arbosVersionGetter execution.ArbOSVersionGetter, ) (*mel.State, []*arbostypes.MessageWithMetadata, []*mel.DelayedInboxMessage, error) { return extractMessagesImpl( ctx, @@ -74,6 +77,7 @@ func ExtractMessages( messagesFromBatchSegments, arbstate.ParseSequencerMessage, arbostypes.ParseBatchPostingReportMessageFields, + arbosVersionGetter, ) } @@ -96,6 +100,7 @@ func extractMessagesImpl( extractBatchMessages batchMsgExtractionFunc, parseSequencerMessage sequencerMessageParserFunc, parseBatchPostingReport batchPostingReportParserFunc, + arbosVersionGetter execution.ArbOSVersionGetter, ) (*mel.State, []*arbostypes.MessageWithMetadata, []*mel.DelayedInboxMessage, error) { state := inputState.Clone() @@ -197,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(batch.SequenceNumber)).Await(ctx) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to get Arbos version for message index %d: %w", batch.SequenceNumber, err) + } + rawSequencerMsg, err := parseSequencerMessage( ctx, batch.SequenceNumber, @@ -205,6 +215,7 @@ func extractMessagesImpl( 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 b65c256ff0..847c279738 100644 --- a/arbnode/mel/extraction/message_extraction_function_test.go +++ b/arbnode/mel/extraction/message_extraction_function_test.go @@ -17,8 +17,10 @@ import ( "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/cmd/chaininfo" "github.com/offchainlabs/nitro/daprovider" + "github.com/offchainlabs/nitro/util/containers" ) func TestExtractMessages(t *testing.T) { @@ -33,7 +35,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, *params.ChainConfig) (*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 @@ -153,6 +155,7 @@ func TestExtractMessages(t *testing.T) { nil, txsFetcher, chaininfo.ArbitrumDevTestChainConfig(), + &mockedArbosVersionGetter{}, ) } else { // Test the internal extractMessagesImpl function @@ -172,6 +175,7 @@ func TestExtractMessages(t *testing.T) { tt.extractBatchMessages, tt.parseSequencerMsg, tt.parseReport, + &mockedArbosVersionGetter{}, ) } @@ -326,6 +330,7 @@ func successfulParseSequencerMsg( dapReaders arbstate.DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, chainConfig *params.ChainConfig, + arbosVersion uint64, ) (*arbstate.SequencerMessage, error) { return nil, nil } @@ -338,6 +343,7 @@ func failingParseSequencerMsg( dapReaders arbstate.DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, chainConfig *params.ChainConfig, + arbosVersion uint64, ) (*arbstate.SequencerMessage, error) { return nil, errors.New("failed to parse sequencer message") } @@ -376,3 +382,9 @@ func failingExtractBatchMessages( ) ([]*arbostypes.MessageWithMetadata, error) { return nil, errors.New("failed to extract batch messages") } + +type mockedArbosVersionGetter struct{} + +func (m *mockedArbosVersionGetter) ArbOSVersionForMessageIndex(msgIdx arbutil.MessageIndex) containers.PromiseInterface[uint64] { + return containers.NewReadyPromise(params.ArbosVersion_50, nil) +} diff --git a/arbnode/mel/extraction/types.go b/arbnode/mel/extraction/types.go index 6101349ee4..50f927841e 100644 --- a/arbnode/mel/extraction/types.go +++ b/arbnode/mel/extraction/types.go @@ -66,6 +66,7 @@ type sequencerMessageParserFunc func( 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 3b788a2cea..9cf0e4bdae 100644 --- a/arbnode/mel/runner/mel.go +++ b/arbnode/mel/runner/mel.go @@ -18,6 +18,7 @@ import ( "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" ) @@ -39,6 +40,7 @@ type MessageExtractor struct { stopwaiter.StopWaiter parentChainReader ParentChainReader chainConfig *params.ChainConfig + arbosVersionGetter execution.ArbOSVersionGetter addrs *chaininfo.RollupAddresses melDB *Database msgConsumer mel.MessageConsumer @@ -228,6 +230,7 @@ func (m *MessageExtractor) Act(ctx context.Context) (time.Duration, error) { receiptFetcher, txsFetcher, m.chainConfig, + m.arbosVersionGetter, ) if err != nil { return m.retryInterval, err diff --git a/arbstate/inbox.go b/arbstate/inbox.go index 5c2d255433..5c1d2d57cd 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -22,7 +22,9 @@ import ( "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/l1pricing" + "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/daprovider" + "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/zeroheavy" ) @@ -80,7 +82,7 @@ type SequencerMessage struct { const MaxSegmentsPerSequencerMessage = 100 * 1024 -func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash common.Hash, data []byte, dapReaders DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, chainConfig *params.ChainConfig) (*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") } @@ -148,7 +150,7 @@ func ParseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash // It's not safe to trust any part of the payload from this point onwards. var uncompressedBatchSizeLimit uint64 if chainConfig != nil { - uncompressedBatchSizeLimit = chainConfig.MaxUncompressedBatchSize() + 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 } @@ -218,6 +220,7 @@ 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 + arbOSVersionGetter execution.ArbOSVersionGetter } func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapReaders DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, chainConfig *params.ChainConfig) arbostypes.InboxMultiplexer { @@ -252,8 +255,13 @@ 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.chainConfig) + + arbosVersion, err := r.arbOSVersionGetter.ArbOSVersionForMessageIndex(arbutil.MessageIndex(r.cachedSequencerMessageNum)).Await(ctx) + if err != nil { + return nil, err + } + + r.cachedSequencerMessage, err = ParseSequencerMessage(ctx, r.cachedSequencerMessageNum, batchBlockHash, bytes, r.dapReaders, r.keysetValidationMode, r.chainConfig, arbosVersion) if err != nil { return nil, err } From 3da6e6fba115318c1a3f939a9ffc5b86b62f8c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 15:25:26 +0100 Subject: [PATCH 23/35] Checkpoint --- arbnode/batch_poster.go | 2 +- .../extraction/message_extraction_function_test.go | 13 +++---------- arbstate/inbox.go | 3 ++- cmd/replay/main.go | 5 ++++- execution/interface.go | 8 ++++++++ 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 7e3e3b44d4..7c59997bcd 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1933,7 +1933,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, b.chainConfig) // nolint:gosec + simMux := arbstate.NewInboxMultiplexer(b.building.muxBackend, batchPosition.DelayedMessageCount, dapReaders, daprovider.KeysetValidate, b.chainConfig, b.arbOSVersionGetter) // nolint:gosec 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/mel/extraction/message_extraction_function_test.go b/arbnode/mel/extraction/message_extraction_function_test.go index 847c279738..56fbb91a4d 100644 --- a/arbnode/mel/extraction/message_extraction_function_test.go +++ b/arbnode/mel/extraction/message_extraction_function_test.go @@ -17,10 +17,9 @@ import ( "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/cmd/chaininfo" "github.com/offchainlabs/nitro/daprovider" - "github.com/offchainlabs/nitro/util/containers" + "github.com/offchainlabs/nitro/execution" ) func TestExtractMessages(t *testing.T) { @@ -155,7 +154,7 @@ func TestExtractMessages(t *testing.T) { nil, txsFetcher, chaininfo.ArbitrumDevTestChainConfig(), - &mockedArbosVersionGetter{}, + &execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50}, ) } else { // Test the internal extractMessagesImpl function @@ -175,7 +174,7 @@ func TestExtractMessages(t *testing.T) { tt.extractBatchMessages, tt.parseSequencerMsg, tt.parseReport, - &mockedArbosVersionGetter{}, + &execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50}, ) } @@ -382,9 +381,3 @@ func failingExtractBatchMessages( ) ([]*arbostypes.MessageWithMetadata, error) { return nil, errors.New("failed to extract batch messages") } - -type mockedArbosVersionGetter struct{} - -func (m *mockedArbosVersionGetter) ArbOSVersionForMessageIndex(msgIdx arbutil.MessageIndex) containers.PromiseInterface[uint64] { - return containers.NewReadyPromise(params.ArbosVersion_50, nil) -} diff --git a/arbstate/inbox.go b/arbstate/inbox.go index 5c1d2d57cd..eff3c2cab5 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -223,7 +223,7 @@ type inboxMultiplexer struct { arbOSVersionGetter execution.ArbOSVersionGetter } -func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapReaders DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, chainConfig *params.ChainConfig) arbostypes.InboxMultiplexer { +func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapReaders DapReaderSource, keysetValidationMode daprovider.KeysetValidationMode, chainConfig *params.ChainConfig, arbosVersionGetter execution.ArbOSVersionGetter) arbostypes.InboxMultiplexer { return &inboxMultiplexer{ backend: backend, delayedMessagesRead: delayedMessagesRead, @@ -236,6 +236,7 @@ func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapRe cachedSegmentBlockNumber: 0, cachedSubMessageNumber: 0, keysetValidationMode: keysetValidationMode, + arbOSVersionGetter: arbosVersionGetter, } } diff --git a/cmd/replay/main.go b/cmd/replay/main.go index d1a8f39e66..1baa2f6578 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/triedb" + "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/arbos" "github.com/offchainlabs/nitro/arbos/arbosState" @@ -272,7 +273,9 @@ func main() { panic(fmt.Sprintf("Failed to register blob reader: %v", err)) } - inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode, chainConfig) + arbosVersion := types.DeserializeHeaderExtraInformation(lastBlockHeader).ArbOSFormatVersion + + inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode, chainConfig, &execution.ConstArbosVersionGetter{Version: arbosVersion}) ctx := context.Background() message, err := inboxMultiplexer.Pop(ctx) 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 { From e4ed04f5a8027568c856585e6d586af60e41e3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 17:55:12 +0100 Subject: [PATCH 24/35] checkpoint --- arbnode/inbox_tracker.go | 28 ++++++++++++++++------------ arbnode/node.go | 2 +- cmd/pruning/pruning.go | 2 +- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/arbnode/inbox_tracker.go b/arbnode/inbox_tracker.go index eb94e832e3..8e28f86c12 100644 --- a/arbnode/inbox_tracker.go +++ b/arbnode/inbox_tracker.go @@ -25,6 +25,7 @@ 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/containers" ) @@ -35,24 +36,26 @@ 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 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) (*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, } return tracker, nil } @@ -787,6 +790,7 @@ func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client *ethclien t.dapReaders, daprovider.KeysetValidate, t.txStreamer.chainConfig, + t.arbosVersionGetter, ) batchMessageCounts := make(map[uint64]arbutil.MessageIndex) currentPos := prevbatchmeta.MessageCount + 1 diff --git a/arbnode/node.go b/arbnode/node.go index df0db4c1f3..9cd1690925 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -720,7 +720,7 @@ func getInboxTrackerAndReader( sequencerInbox *SequencerInbox, exec execution.ExecutionSequencer, ) (*InboxTracker, *InboxReader, error) { - inboxTracker, err := NewInboxTracker(arbDb, txStreamer, dapReaders, config.SnapSyncTest) + inboxTracker, err := NewInboxTracker(arbDb, txStreamer, dapReaders, config.SnapSyncTest, exec) if err != nil { return nil, nil, err } diff --git a/cmd/pruning/pruning.go b/cmd/pruning/pruning.go index c7dd09a231..2472234c75 100644 --- a/cmd/pruning/pruning.go +++ b/cmd/pruning/pruning.go @@ -184,7 +184,7 @@ 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) + tracker, err := arbnode.NewInboxTracker(arbDb, nil, nil, arbnode.DefaultSnapSyncConfig, nil) if err != nil { return nil, err } From 03233dd27d9d6a521bf01fc89d9a2ab375f76ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 18:29:29 +0100 Subject: [PATCH 25/35] fix tests --- arbnode/delayed_seq_reorg_test.go | 9 +++++++-- arbstate/inbox_fuzz_test.go | 3 +++ cmd/replay/main.go | 2 +- system_tests/state_fuzz_test.go | 2 ++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/arbnode/delayed_seq_reorg_test.go b/arbnode/delayed_seq_reorg_test.go index 8505e20188..2dbdbaf6be 100644 --- a/arbnode/delayed_seq_reorg_test.go +++ b/arbnode/delayed_seq_reorg_test.go @@ -10,8 +10,10 @@ 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" ) @@ -20,7 +22,8 @@ func TestSequencerReorgFromDelayed(t *testing.T) { defer cancel() exec, streamer, db, _ := NewTransactionStreamerForTest(t, ctx, common.Address{}) - tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig) + arbosVersionGetter := execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50} + tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig, &arbosVersionGetter) Require(t, err) err = streamer.Start(ctx) @@ -220,7 +223,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: params.ArbosVersion_50} + + tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig, &arbosVersionGetter) Require(t, err) err = streamer.Start(ctx) diff --git a/arbstate/inbox_fuzz_test.go b/arbstate/inbox_fuzz_test.go index 2c146a0e17..3f846834e3 100644 --- a/arbstate/inbox_fuzz_test.go +++ b/arbstate/inbox_fuzz_test.go @@ -10,10 +10,12 @@ 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" + "github.com/offchainlabs/nitro/execution" ) // lint:require-exhaustive-initialization @@ -77,6 +79,7 @@ func FuzzInboxMultiplexer(f *testing.F) { nil, daprovider.KeysetValidate, chaininfo.ArbitrumDevTestChainConfig(), + &execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50}, ) _, err := multiplexer.Pop(context.TODO()) if err != nil { diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 1baa2f6578..436701cc59 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -24,7 +24,6 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/triedb" - "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/arbos" "github.com/offchainlabs/nitro/arbos/arbosState" @@ -36,6 +35,7 @@ import ( "github.com/offchainlabs/nitro/daprovider" "github.com/offchainlabs/nitro/daprovider/das/dastree" "github.com/offchainlabs/nitro/daprovider/das/dasutil" + "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/gethhook" "github.com/offchainlabs/nitro/wavmio" ) diff --git a/system_tests/state_fuzz_test.go b/system_tests/state_fuzz_test.go index 57f126e558..abab382058 100644 --- a/system_tests/state_fuzz_test.go +++ b/system_tests/state_fuzz_test.go @@ -28,6 +28,7 @@ import ( "github.com/offchainlabs/nitro/arbstate" "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/daprovider" + "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/testhelpers/env" ) @@ -50,6 +51,7 @@ func BuildBlock( nil, daprovider.KeysetValidate, getChainConfig(), + &execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50}, ) ctx := context.Background() From fd6384f64eaf380ac554e7c5b8f3336087fcdba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 18:35:26 +0100 Subject: [PATCH 26/35] nil protection --- arbstate/inbox.go | 3 +++ cmd/pruning/pruning.go | 2 ++ 2 files changed, 5 insertions(+) diff --git a/arbstate/inbox.go b/arbstate/inbox.go index eff3c2cab5..120bb181e3 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -257,6 +257,9 @@ func (r *inboxMultiplexer) Pop(ctx context.Context) (*arbostypes.MessageWithMeta } r.cachedSequencerMessageNum = r.backend.GetSequencerInboxPosition() + if r.arbOSVersionGetter == nil { + return nil, errors.New("arbOSVersionGetter is nil") + } arbosVersion, err := r.arbOSVersionGetter.ArbOSVersionForMessageIndex(arbutil.MessageIndex(r.cachedSequencerMessageNum)).Await(ctx) if err != nil { return nil, err diff --git a/cmd/pruning/pruning.go b/cmd/pruning/pruning.go index 2472234c75..0d1c4e0077 100644 --- a/cmd/pruning/pruning.go +++ b/cmd/pruning/pruning.go @@ -184,6 +184,8 @@ func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node return nil, fmt.Errorf("failed to get finalized block: %w", err) } l1BlockNum := l1Block.NumberU64() + // 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) if err != nil { return nil, err From c3da8510a77fd96f9b20d04b0f4658379ec85f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 18:36:58 +0100 Subject: [PATCH 27/35] update pin --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index ae47bc8add..5a76403e74 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit ae47bc8add8244eff461358bb72a5618ba284f59 +Subproject commit 5a76403e74c9b4ae9eaef8fae749471e23d7c63e From 883a3e1e98ec87fc477a6c231e80805e128db2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 10 Dec 2025 09:20:02 +0100 Subject: [PATCH 28/35] forbid changing the limit in the chain config --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 5a76403e74..3bc2944894 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 5a76403e74c9b4ae9eaef8fae749471e23d7c63e +Subproject commit 3bc2944894fa717c0be12666b87ad1d7b998374b From 93cff8b26ebbc7bda49203f118befeae4681a50f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 10 Dec 2025 09:50:44 +0100 Subject: [PATCH 29/35] tiny refactor in replay --- cmd/replay/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 436701cc59..f108c5a352 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -274,8 +274,9 @@ func main() { } arbosVersion := types.DeserializeHeaderExtraInformation(lastBlockHeader).ArbOSFormatVersion + arbosVersionGetter := execution.ConstArbosVersionGetter{Version: arbosVersion} - inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode, chainConfig, &execution.ConstArbosVersionGetter{Version: arbosVersion}) + inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode, chainConfig, &arbosVersionGetter) ctx := context.Background() message, err := inboxMultiplexer.Pop(ctx) if err != nil { From f6dd87e4bb8fd32fa42fea4cc045cea3e8f0279e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 10 Dec 2025 10:03:47 +0100 Subject: [PATCH 30/35] pass non-nil arbos version getter --- arbnode/node.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index 9cd1690925..51103e08d6 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -719,8 +719,9 @@ func getInboxTrackerAndReader( delayedBridge *DelayedBridge, sequencerInbox *SequencerInbox, exec execution.ExecutionSequencer, + arbOSVersionGetter execution.ArbOSVersionGetter, ) (*InboxTracker, *InboxReader, error) { - inboxTracker, err := NewInboxTracker(arbDb, txStreamer, dapReaders, config.SnapSyncTest, exec) + inboxTracker, err := NewInboxTracker(arbDb, txStreamer, dapReaders, config.SnapSyncTest, arbOSVersionGetter) if err != nil { return nil, nil, err } @@ -1188,7 +1189,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) if err != nil { return nil, err } @@ -1349,7 +1350,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 } From 449b987de44572ad2b6d8f5d7023c5b92b1821ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 10 Dec 2025 10:25:10 +0100 Subject: [PATCH 31/35] Use the previous message index --- arbstate/inbox.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arbstate/inbox.go b/arbstate/inbox.go index 120bb181e3..75e3e05ac6 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" + "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/arbcompress" "github.com/offchainlabs/nitro/arbos/arbosState" @@ -260,7 +261,8 @@ func (r *inboxMultiplexer) Pop(ctx context.Context) (*arbostypes.MessageWithMeta if r.arbOSVersionGetter == nil { return nil, errors.New("arbOSVersionGetter is nil") } - arbosVersion, err := r.arbOSVersionGetter.ArbOSVersionForMessageIndex(arbutil.MessageIndex(r.cachedSequencerMessageNum)).Await(ctx) + lastMessageNum := arbmath.SaturatingUSub(r.cachedSequencerMessageNum, 1) + arbosVersion, err := r.arbOSVersionGetter.ArbOSVersionForMessageIndex(arbutil.MessageIndex(lastMessageNum)).Await(ctx) if err != nil { return nil, err } From f1872b4ac4795330d91123cb97de6b97c8a151cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 11 Dec 2025 15:13:28 +0100 Subject: [PATCH 32/35] Fixed implementation --- arbnode/batch_poster.go | 13 +++++----- arbnode/inbox_tracker.go | 9 +++++-- .../extraction/message_extraction_function.go | 4 +-- arbnode/node.go | 2 +- arbstate/inbox.go | 26 +++++++------------ arbstate/inbox_fuzz_test.go | 5 ++-- cmd/replay/main.go | 9 +++---- system_tests/state_fuzz_test.go | 5 ++-- 8 files changed, 34 insertions(+), 39 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 7c59997bcd..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 @@ -1415,13 +1415,17 @@ 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 { return false, err } config := b.config() - arbOSVersion, err := b.arbOSVersionGetter.ArbOSVersionForMessageIndex(arbutil.MessageIndex(arbmath.SaturatingUSub(uint64(batchPosition.MessageCount), 1))).Await(ctx) buildingForEthDA := len(b.dapWriters) == 0 || b.ethDAFallbackRemaining > 0 // Determine if we should use 4844 blobs (only relevant when posting to EthDA) var use4844 bool @@ -1429,9 +1433,6 @@ func (b *BatchPoster) MaybePostSequencerBatch(ctx context.Context) (bool, error) config.Post4844Blobs && latestHeader.ExcessBlobGas != nil && latestHeader.BlobGasUsed != nil { - if err != nil { - return false, err - } if arbOSVersion >= params.ArbosVersion_20 { if config.IgnoreBlobPrice { use4844 = true @@ -1933,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, b.chainConfig, b.arbOSVersionGetter) // nolint:gosec + 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/inbox_tracker.go b/arbnode/inbox_tracker.go index 730a9b9433..1c3526acd2 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 @@ -18,6 +18,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" + "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbstate" @@ -784,13 +785,17 @@ func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client *ethclien ctx: ctx, client: client, } + recentArbosVersion, err := t.arbosVersionGetter.ArbOSVersionForMessageIndex(arbmath.SaturatingUSub(prevbatchmeta.MessageCount, 1)).Await(ctx) + if err != nil { + return err + } multiplexer := arbstate.NewInboxMultiplexer( backend, prevbatchmeta.DelayedMessageCount, t.dapReaders, daprovider.KeysetValidate, t.txStreamer.chainConfig, - t.arbosVersionGetter, + recentArbosVersion, ) batchMessageCounts := make(map[uint64]arbutil.MessageIndex) currentPos := prevbatchmeta.MessageCount + 1 diff --git a/arbnode/mel/extraction/message_extraction_function.go b/arbnode/mel/extraction/message_extraction_function.go index 53e624d044..a869fce871 100644 --- a/arbnode/mel/extraction/message_extraction_function.go +++ b/arbnode/mel/extraction/message_extraction_function.go @@ -202,9 +202,9 @@ 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(batch.SequenceNumber)).Await(ctx) + 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", batch.SequenceNumber, err) + return nil, nil, nil, fmt.Errorf("failed to get Arbos version for message index %d: %w", state.MsgCount, err) } rawSequencerMsg, err := parseSequencerMessage( diff --git a/arbnode/node.go b/arbnode/node.go index 51103e08d6..de6be490d7 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 diff --git a/arbstate/inbox.go b/arbstate/inbox.go index 75e3e05ac6..f1194cc2bb 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -17,15 +17,12 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/arbcompress" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/l1pricing" - "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/daprovider" - "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/zeroheavy" ) @@ -221,10 +218,15 @@ 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 - arbOSVersionGetter execution.ArbOSVersionGetter + // 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, chainConfig *params.ChainConfig, arbosVersionGetter execution.ArbOSVersionGetter) 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, @@ -237,7 +239,7 @@ func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapRe cachedSegmentBlockNumber: 0, cachedSubMessageNumber: 0, keysetValidationMode: keysetValidationMode, - arbOSVersionGetter: arbosVersionGetter, + recentArbOSVersion: recentArbOSVersion, } } @@ -258,16 +260,8 @@ func (r *inboxMultiplexer) Pop(ctx context.Context) (*arbostypes.MessageWithMeta } r.cachedSequencerMessageNum = r.backend.GetSequencerInboxPosition() - if r.arbOSVersionGetter == nil { - return nil, errors.New("arbOSVersionGetter is nil") - } - lastMessageNum := arbmath.SaturatingUSub(r.cachedSequencerMessageNum, 1) - arbosVersion, err := r.arbOSVersionGetter.ArbOSVersionForMessageIndex(arbutil.MessageIndex(lastMessageNum)).Await(ctx) - if err != nil { - return nil, err - } - - r.cachedSequencerMessage, err = ParseSequencerMessage(ctx, r.cachedSequencerMessageNum, batchBlockHash, bytes, r.dapReaders, r.keysetValidationMode, r.chainConfig, arbosVersion) + var err error + 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 3f846834e3..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 @@ -15,7 +15,6 @@ import ( "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/daprovider" - "github.com/offchainlabs/nitro/execution" ) // lint:require-exhaustive-initialization @@ -79,7 +78,7 @@ func FuzzInboxMultiplexer(f *testing.F) { nil, daprovider.KeysetValidate, chaininfo.ArbitrumDevTestChainConfig(), - &execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50}, + params.ArbosVersion_50, ) _, err := multiplexer.Pop(context.TODO()) if err != nil { diff --git a/cmd/replay/main.go b/cmd/replay/main.go index f108c5a352..21ce09a5eb 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -35,7 +35,6 @@ import ( "github.com/offchainlabs/nitro/daprovider" "github.com/offchainlabs/nitro/daprovider/das/dastree" "github.com/offchainlabs/nitro/daprovider/das/dasutil" - "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/gethhook" "github.com/offchainlabs/nitro/wavmio" ) @@ -245,9 +244,10 @@ func main() { return wavmio.ReadInboxMessage(batchNum), nil } readMessage := func(dasEnabled bool, chainConfig *params.ChainConfig) *arbostypes.MessageWithMetadata { - var delayedMessagesRead uint64 + var delayedMessagesRead, arbosVersion uint64 if lastBlockHeader != nil { delayedMessagesRead = lastBlockHeader.Nonce.Uint64() + arbosVersion = types.DeserializeHeaderExtraInformation(lastBlockHeader).ArbOSFormatVersion } var dasReader dasutil.DASReader var dasKeysetFetcher dasutil.DASKeysetFetcher @@ -273,10 +273,7 @@ func main() { panic(fmt.Sprintf("Failed to register blob reader: %v", err)) } - arbosVersion := types.DeserializeHeaderExtraInformation(lastBlockHeader).ArbOSFormatVersion - arbosVersionGetter := execution.ConstArbosVersionGetter{Version: arbosVersion} - - inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode, chainConfig, &arbosVersionGetter) + inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode, chainConfig, arbosVersion) ctx := context.Background() message, err := inboxMultiplexer.Pop(ctx) if err != nil { diff --git a/system_tests/state_fuzz_test.go b/system_tests/state_fuzz_test.go index abab382058..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 @@ -28,7 +28,6 @@ import ( "github.com/offchainlabs/nitro/arbstate" "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/daprovider" - "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/testhelpers/env" ) @@ -51,7 +50,7 @@ func BuildBlock( nil, daprovider.KeysetValidate, getChainConfig(), - &execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50}, + params.ArbosVersion_50, ) ctx := context.Background() From 072f95e23a1ea2f2a689f85bcff619ca731636ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 11 Dec 2025 15:47:51 +0100 Subject: [PATCH 33/35] Set correct version in test --- system_tests/batch_size_limit_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/system_tests/batch_size_limit_test.go b/system_tests/batch_size_limit_test.go index ed3514efb1..865ca4ccba 100644 --- a/system_tests/batch_size_limit_test.go +++ b/system_tests/batch_size_limit_test.go @@ -63,6 +63,7 @@ func setupNodeForTestingBatchSizeLimit(t *testing.T, setHighLimit bool) (*NodeBu 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 From c491e882ac0a7b554587d847b873a06c8a1f7b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 11 Dec 2025 16:00:10 +0100 Subject: [PATCH 34/35] lint --- arbnode/inbox_tracker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/inbox_tracker.go b/arbnode/inbox_tracker.go index 1c3526acd2..6535d51f91 100644 --- a/arbnode/inbox_tracker.go +++ b/arbnode/inbox_tracker.go @@ -18,7 +18,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" - "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbstate" @@ -28,6 +27,7 @@ import ( "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" ) From 4a1b615c0be5af9b676b540f3b4a52c31b496563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 11 Dec 2025 16:49:54 +0100 Subject: [PATCH 35/35] Be more resilient --- arbnode/delayed_seq_reorg_test.go | 10 +++++---- arbnode/inbox_tracker.go | 34 +++++++++++++++++-------------- arbnode/node.go | 5 +++-- cmd/pruning/pruning.go | 6 +++--- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/arbnode/delayed_seq_reorg_test.go b/arbnode/delayed_seq_reorg_test.go index 2dbdbaf6be..6d2a54d9fb 100644 --- a/arbnode/delayed_seq_reorg_test.go +++ b/arbnode/delayed_seq_reorg_test.go @@ -17,13 +17,15 @@ import ( "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{}) - arbosVersionGetter := execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50} - tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig, &arbosVersionGetter) + arbosVersionGetter := execution.ConstArbosVersionGetter{Version: arbosVersion} + tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig, &arbosVersionGetter, arbosVersion) Require(t, err) err = streamer.Start(ctx) @@ -223,9 +225,9 @@ func TestSequencerReorgFromLastDelayedMsg(t *testing.T) { defer cancel() exec, streamer, db, _ := NewTransactionStreamerForTest(t, ctx, common.Address{}) - arbosVersionGetter := execution.ConstArbosVersionGetter{Version: params.ArbosVersion_50} + arbosVersionGetter := execution.ConstArbosVersionGetter{Version: arbosVersion} - tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig, &arbosVersionGetter) + 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 6535d51f91..7ed1fa89c5 100644 --- a/arbnode/inbox_tracker.go +++ b/arbnode/inbox_tracker.go @@ -37,26 +37,28 @@ var ( ) type InboxTracker struct { - db ethdb.Database - txStreamer *TransactionStreamer - mutex sync.Mutex - validator *staker.BlockValidator - dapReaders *daprovider.DAProviderRegistry - snapSyncConfig SnapSyncConfig - arbosVersionGetter execution.ArbOSVersionGetter + 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, arbosVersionGetter execution.ArbOSVersionGetter) (*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, - arbosVersionGetter: arbosVersionGetter, + db: db, + txStreamer: txStreamer, + dapReaders: dapReaders, + batchMeta: containers.NewLruCache[uint64, BatchMetadata](1000), + snapSyncConfig: snapSyncConfig, + arbosVersionGetter: arbosVersionGetter, + lastSeenArbosVersion: initialArbosVersion, } return tracker, nil } @@ -787,7 +789,9 @@ func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client *ethclien } recentArbosVersion, err := t.arbosVersionGetter.ArbOSVersionForMessageIndex(arbmath.SaturatingUSub(prevbatchmeta.MessageCount, 1)).Await(ctx) if err != nil { - return err + recentArbosVersion = t.lastSeenArbosVersion + } else { + t.lastSeenArbosVersion = recentArbosVersion } multiplexer := arbstate.NewInboxMultiplexer( backend, diff --git a/arbnode/node.go b/arbnode/node.go index de6be490d7..072e7dfae2 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -720,8 +720,9 @@ func getInboxTrackerAndReader( sequencerInbox *SequencerInbox, exec execution.ExecutionSequencer, arbOSVersionGetter execution.ArbOSVersionGetter, + initialArbosVersion uint64, ) (*InboxTracker, *InboxReader, error) { - inboxTracker, err := NewInboxTracker(arbDb, txStreamer, dapReaders, config.SnapSyncTest, arbOSVersionGetter) + inboxTracker, err := NewInboxTracker(arbDb, txStreamer, dapReaders, config.SnapSyncTest, arbOSVersionGetter, initialArbosVersion) if err != nil { return nil, nil, err } @@ -1189,7 +1190,7 @@ func createNodeImpl( return nil, err } - inboxTracker, inboxReader, err := getInboxTrackerAndReader(ctx, arbDb, txStreamer, dapRegistry, config, configFetcher, l1client, l1Reader, deployInfo, delayedBridge, sequencerInbox, executionSequencer, arbOSVersionGetter) + 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 } diff --git a/cmd/pruning/pruning.go b/cmd/pruning/pruning.go index 0d1c4e0077..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?)") @@ -186,7 +186,7 @@ func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node l1BlockNum := l1Block.NumberU64() // 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) + tracker, err := arbnode.NewInboxTracker(arbDb, nil, nil, arbnode.DefaultSnapSyncConfig, nil, chainConfig.ArbitrumChainParams.InitialArbOSVersion) if err != nil { return nil, err } @@ -288,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) }