From cf2c513f2cf68a2c4ba251a4a515d053a9e2d0b3 Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Thu, 8 Feb 2024 19:47:41 -0800 Subject: [PATCH 1/4] Gate 4844 Batch Posting on ArbOS 20 Plumb ExecutionEngine to BatchPoster as the ArbOSVersionGetter interface so that it can post 4844 batches for only ArbOS 20 and above. BatchPoster finds if ArbOS 20 or greater was already enabled as of MessageIndex of the first message in the batch using the ArbOSVersionGetter interface. --- arbnode/batch_poster.go | 92 +++++++++++++++------------ arbnode/node.go | 5 +- execution/gethexec/executionengine.go | 11 +++- system_tests/batch_poster_test.go | 1 + 4 files changed, 67 insertions(+), 42 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index e3af0b2afb..b617adcf39 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -71,24 +71,29 @@ type batchPosterPosition struct { NextSeqNum uint64 } +type ArbOSVersionGetter interface { + ArbOSVersionForMessageNumber(messageNum arbutil.MessageIndex) (uint64, error) +} + type BatchPoster struct { stopwaiter.StopWaiter - l1Reader *headerreader.HeaderReader - inbox *InboxTracker - streamer *TransactionStreamer - config BatchPosterConfigFetcher - seqInbox *bridgegen.SequencerInbox - bridge *bridgegen.Bridge - syncMonitor *SyncMonitor - seqInboxABI *abi.ABI - seqInboxAddr common.Address - bridgeAddr common.Address - gasRefunderAddr common.Address - building *buildingBatch - daWriter das.DataAvailabilityServiceWriter - dataPoster *dataposter.DataPoster - redisLock *redislock.Simple - messagesPerBatch *arbmath.MovingAverage[uint64] + l1Reader *headerreader.HeaderReader + inbox *InboxTracker + streamer *TransactionStreamer + arbOSVersionGetter ArbOSVersionGetter + config BatchPosterConfigFetcher + seqInbox *bridgegen.SequencerInbox + bridge *bridgegen.Bridge + syncMonitor *SyncMonitor + seqInboxABI *abi.ABI + seqInboxAddr common.Address + bridgeAddr common.Address + gasRefunderAddr common.Address + building *buildingBatch + daWriter das.DataAvailabilityServiceWriter + dataPoster *dataposter.DataPoster + redisLock *redislock.Simple + messagesPerBatch *arbmath.MovingAverage[uint64] // 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. @@ -174,7 +179,7 @@ type BatchPosterConfigFetcher func() *BatchPosterConfig func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".enable", DefaultBatchPosterConfig.Enable, "enable posting batches to l1") - f.Bool(prefix+".disable-das-fallback-store-data-on-chain", DefaultBatchPosterConfig.DisableDasFallbackStoreDataOnChain, "If unable to batch to DAS, disable fallback storing data on chain") + f.Bool(prefix+".disable-das-fallback-store-data-on-chain", DefaultBatchPosterConfig.DisableDasFallbackStoreDataOnChain, "If unable to batch to AS, disable fallback storing data on chain") f.Int(prefix+".max-size", DefaultBatchPosterConfig.MaxSize, "maximum batch size") f.Int(prefix+".max-4844-batch-size", DefaultBatchPosterConfig.Max4844BatchSize, "maximum 4844 blob enabled batch size") f.Duration(prefix+".max-delay", DefaultBatchPosterConfig.MaxDelay, "maximum batch posting delay") @@ -255,6 +260,7 @@ type BatchPosterOpts struct { L1Reader *headerreader.HeaderReader Inbox *InboxTracker Streamer *TransactionStreamer + VersionGetter ArbOSVersionGetter SyncMonitor *SyncMonitor Config BatchPosterConfigFetcher DeployInfo *chaininfo.RollupAddresses @@ -293,19 +299,20 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e return nil, err } b := &BatchPoster{ - l1Reader: opts.L1Reader, - inbox: opts.Inbox, - streamer: opts.Streamer, - syncMonitor: opts.SyncMonitor, - config: opts.Config, - bridge: bridge, - seqInbox: seqInbox, - seqInboxABI: seqInboxABI, - seqInboxAddr: opts.DeployInfo.SequencerInbox, - gasRefunderAddr: opts.Config().gasRefunder, - bridgeAddr: opts.DeployInfo.Bridge, - daWriter: opts.DAWriter, - redisLock: redisLock, + l1Reader: opts.L1Reader, + inbox: opts.Inbox, + streamer: opts.Streamer, + arbOSVersionGetter: opts.VersionGetter, + syncMonitor: opts.SyncMonitor, + config: opts.Config, + bridge: bridge, + seqInbox: seqInbox, + seqInboxABI: seqInboxABI, + seqInboxAddr: opts.DeployInfo.SequencerInbox, + gasRefunderAddr: opts.Config().gasRefunder, + bridgeAddr: opts.DeployInfo.Bridge, + daWriter: opts.DAWriter, + redisLock: redisLock, } b.messagesPerBatch, err = arbmath.NewMovingAverage[uint64](20) if err != nil { @@ -956,17 +963,24 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) var use4844 bool config := b.config() if config.Post4844Blobs && latestHeader.ExcessBlobGas != nil && latestHeader.BlobGasUsed != nil { - if config.ForcePost4844Blobs { - use4844 = true - } else { - blobFeePerByte := eip4844.CalcBlobFee(eip4844.CalcExcessBlobGas(*latestHeader.ExcessBlobGas, *latestHeader.BlobGasUsed)) - blobFeePerByte.Mul(blobFeePerByte, blobTxBlobGasPerBlob) - blobFeePerByte.Div(blobFeePerByte, usableBytesInBlob) - - calldataFeePerByte := arbmath.BigMulByUint(latestHeader.BaseFee, 16) - use4844 = arbmath.BigLessThan(blobFeePerByte, calldataFeePerByte) + arbOSVersion, err := b.arbOSVersionGetter.ArbOSVersionForMessageNumber(batchPosition.MessageCount) + if err != nil { + return false, err + } + if arbOSVersion >= 20 { + if config.ForcePost4844Blobs { + use4844 = true + } else { + blobFeePerByte := eip4844.CalcBlobFee(eip4844.CalcExcessBlobGas(*latestHeader.ExcessBlobGas, *latestHeader.BlobGasUsed)) + blobFeePerByte.Mul(blobFeePerByte, blobTxBlobGasPerBlob) + blobFeePerByte.Div(blobFeePerByte, usableBytesInBlob) + + calldataFeePerByte := arbmath.BigMulByUint(latestHeader.BaseFee, 16) + use4844 = arbmath.BigLessThan(blobFeePerByte, calldataFeePerByte) + } } } + b.building = &buildingBatch{ segments: newBatchSegments(batchPosition.DelayedMessageCount, b.config(), b.GetBacklogEstimate(), use4844), msgCount: batchPosition.MessageCount, diff --git a/arbnode/node.go b/arbnode/node.go index de9745f2a8..24d9298969 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -341,7 +341,7 @@ func StakerDataposter( func createNodeImpl( ctx context.Context, stack *node.Node, - exec execution.FullExecutionClient, + exec *gethexec.ExecutionNode, arbDb ethdb.Database, configFetcher ConfigFetcher, l2Config *params.ChainConfig, @@ -654,6 +654,7 @@ func createNodeImpl( L1Reader: l1Reader, Inbox: inboxTracker, Streamer: txStreamer, + VersionGetter: exec.ExecEngine, SyncMonitor: syncMonitor, Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, DeployInfo: deployInfo, @@ -707,7 +708,7 @@ func (n *Node) OnConfigReload(_ *Config, _ *Config) error { func CreateNode( ctx context.Context, stack *node.Node, - exec execution.FullExecutionClient, + exec *gethexec.ExecutionNode, arbDb ethdb.Database, configFetcher ConfigFetcher, l2Config *params.ChainConfig, diff --git a/execution/gethexec/executionengine.go b/execution/gethexec/executionengine.go index 003159589a..a662de3621 100644 --- a/execution/gethexec/executionengine.go +++ b/execution/gethexec/executionengine.go @@ -101,7 +101,7 @@ func (s *ExecutionEngine) Reorg(count arbutil.MessageIndex, newMessages []arbost resequencing := false defer func() { // if we are resequencing old messages - don't release the lock - // lock will be relesed by thread listening to resequenceChan + // lock will be released by thread listening to resequenceChan if !resequencing { s.createBlocksMutex.Unlock() } @@ -601,6 +601,15 @@ func (s *ExecutionEngine) digestMessageWithBlockMutex(num arbutil.MessageIndex, return nil } +func (s *ExecutionEngine) ArbOSVersionForMessageNumber(messageNum arbutil.MessageIndex) (uint64, error) { + block := s.bc.GetBlockByNumber(s.MessageIndexToBlockNumber(messageNum)) + if block == nil { + return 0, fmt.Errorf("couldn't find block for message number %d", messageNum) + } + extra := types.DeserializeHeaderExtraInformation(block.Header()) + return extra.ArbOSFormatVersion, nil +} + func (s *ExecutionEngine) Start(ctx_in context.Context) { s.StopWaiter.Start(ctx_in, s) s.LaunchThread(func(ctx context.Context) { diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index cacbe3cee4..4a62e438aa 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -166,6 +166,7 @@ func testBatchPosterParallel(t *testing.T, useRedis bool) { L1Reader: builder.L2.ConsensusNode.L1Reader, Inbox: builder.L2.ConsensusNode.InboxTracker, Streamer: builder.L2.ConsensusNode.TxStreamer, + VersionGetter: builder.L2.ExecNode.ExecEngine, SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, DeployInfo: builder.L2.ConsensusNode.DeployInfo, From 97638718755fd063934b04bc2e9757bbf5769772 Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Fri, 9 Feb 2024 17:39:43 -0800 Subject: [PATCH 2/4] Fix batch msg arbos version check off by 1 batchPosterPosition.MessageCount is the count of messages, therefore it is the index of the next message that may not exist yet. --- arbnode/batch_poster.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index b617adcf39..6c2f352b2c 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -954,7 +954,6 @@ 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) } - if b.building == nil || b.building.startMsgCount != batchPosition.MessageCount { latestHeader, err := b.l1Reader.LastHeader(ctx) if err != nil { @@ -963,7 +962,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) var use4844 bool config := b.config() if config.Post4844Blobs && latestHeader.ExcessBlobGas != nil && latestHeader.BlobGasUsed != nil { - arbOSVersion, err := b.arbOSVersionGetter.ArbOSVersionForMessageNumber(batchPosition.MessageCount) + arbOSVersion, err := b.arbOSVersionGetter.ArbOSVersionForMessageNumber(arbutil.MessageIndex(arbmath.SaturatingUSub(uint64(batchPosition.MessageCount), 1))) if err != nil { return false, err } From 9964983f30573681aeb523c177d12a676cc7b048 Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Fri, 9 Feb 2024 17:57:33 -0800 Subject: [PATCH 3/4] Fix stray edit --- arbnode/batch_poster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 6c2f352b2c..317c74be69 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -179,7 +179,7 @@ type BatchPosterConfigFetcher func() *BatchPosterConfig func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".enable", DefaultBatchPosterConfig.Enable, "enable posting batches to l1") - f.Bool(prefix+".disable-das-fallback-store-data-on-chain", DefaultBatchPosterConfig.DisableDasFallbackStoreDataOnChain, "If unable to batch to AS, disable fallback storing data on chain") + f.Bool(prefix+".disable-das-fallback-store-data-on-chain", DefaultBatchPosterConfig.DisableDasFallbackStoreDataOnChain, "If unable to batch to DAS, disable fallback storing data on chain") f.Int(prefix+".max-size", DefaultBatchPosterConfig.MaxSize, "maximum batch size") f.Int(prefix+".max-4844-batch-size", DefaultBatchPosterConfig.Max4844BatchSize, "maximum 4844 blob enabled batch size") f.Duration(prefix+".max-delay", DefaultBatchPosterConfig.MaxDelay, "maximum batch posting delay") From f81d226b871df8eb5ddbc8e756641a5764c73254 Mon Sep 17 00:00:00 2001 From: Tristan Wilson Date: Tue, 13 Feb 2024 14:43:59 -0800 Subject: [PATCH 4/4] Move ArbOSVersionForMessageNumber --- arbnode/batch_poster.go | 9 +++------ arbnode/node.go | 6 +++--- execution/gethexec/node.go | 3 +++ execution/interface.go | 2 ++ system_tests/batch_poster_test.go | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 317c74be69..3b4f36dfc4 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -41,6 +41,7 @@ import ( "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/das" + "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/util" "github.com/offchainlabs/nitro/util/arbmath" @@ -71,16 +72,12 @@ type batchPosterPosition struct { NextSeqNum uint64 } -type ArbOSVersionGetter interface { - ArbOSVersionForMessageNumber(messageNum arbutil.MessageIndex) (uint64, error) -} - type BatchPoster struct { stopwaiter.StopWaiter l1Reader *headerreader.HeaderReader inbox *InboxTracker streamer *TransactionStreamer - arbOSVersionGetter ArbOSVersionGetter + arbOSVersionGetter execution.FullExecutionClient config BatchPosterConfigFetcher seqInbox *bridgegen.SequencerInbox bridge *bridgegen.Bridge @@ -260,7 +257,7 @@ type BatchPosterOpts struct { L1Reader *headerreader.HeaderReader Inbox *InboxTracker Streamer *TransactionStreamer - VersionGetter ArbOSVersionGetter + VersionGetter execution.FullExecutionClient SyncMonitor *SyncMonitor Config BatchPosterConfigFetcher DeployInfo *chaininfo.RollupAddresses diff --git a/arbnode/node.go b/arbnode/node.go index b1e215c795..e87a6dda78 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -348,7 +348,7 @@ func StakerDataposter( func createNodeImpl( ctx context.Context, stack *node.Node, - exec *gethexec.ExecutionNode, + exec execution.FullExecutionClient, arbDb ethdb.Database, configFetcher ConfigFetcher, l2Config *params.ChainConfig, @@ -661,7 +661,7 @@ func createNodeImpl( L1Reader: l1Reader, Inbox: inboxTracker, Streamer: txStreamer, - VersionGetter: exec.ExecEngine, + VersionGetter: exec, SyncMonitor: syncMonitor, Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, DeployInfo: deployInfo, @@ -715,7 +715,7 @@ func (n *Node) OnConfigReload(_ *Config, _ *Config) error { func CreateNode( ctx context.Context, stack *node.Node, - exec *gethexec.ExecutionNode, + exec execution.FullExecutionClient, arbDb ethdb.Database, configFetcher ConfigFetcher, l2Config *params.ChainConfig, diff --git a/execution/gethexec/node.go b/execution/gethexec/node.go index 1ad73febe7..49e2d10e24 100644 --- a/execution/gethexec/node.go +++ b/execution/gethexec/node.go @@ -332,6 +332,9 @@ func (n *ExecutionNode) SequenceDelayedMessage(message *arbostypes.L1IncomingMes func (n *ExecutionNode) ResultAtPos(pos arbutil.MessageIndex) (*execution.MessageResult, error) { return n.ExecEngine.ResultAtPos(pos) } +func (n *ExecutionNode) ArbOSVersionForMessageNumber(messageNum arbutil.MessageIndex) (uint64, error) { + return n.ExecEngine.ArbOSVersionForMessageNumber(messageNum) +} func (n *ExecutionNode) RecordBlockCreation( ctx context.Context, diff --git a/execution/interface.go b/execution/interface.go index 6761011a77..2cbbf550ad 100644 --- a/execution/interface.go +++ b/execution/interface.go @@ -69,6 +69,8 @@ type FullExecutionClient interface { // TODO: only used to get safe/finalized block numbers MessageIndexToBlockNumber(messageNum arbutil.MessageIndex) uint64 + + ArbOSVersionForMessageNumber(messageNum arbutil.MessageIndex) (uint64, error) } // not implemented in execution, used as input diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index 4a62e438aa..68dea4167f 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -166,7 +166,7 @@ func testBatchPosterParallel(t *testing.T, useRedis bool) { L1Reader: builder.L2.ConsensusNode.L1Reader, Inbox: builder.L2.ConsensusNode.InboxTracker, Streamer: builder.L2.ConsensusNode.TxStreamer, - VersionGetter: builder.L2.ExecNode.ExecEngine, + VersionGetter: builder.L2.ExecNode, SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, DeployInfo: builder.L2.ConsensusNode.DeployInfo,