diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 5b43f97415..2cbaf73825 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -546,8 +546,9 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address] // Convert the new uint256.Int types to the old big.Int ones used by the legacy pool var ( - minTipBig *big.Int - baseFeeBig *big.Int + minTipBig *big.Int + baseFeeBig *big.Int + maxL1SizeScaled *big.Int ) if filter.MinTip != nil { minTipBig = filter.MinTip.ToBig() @@ -555,6 +556,9 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address] if filter.BaseFee != nil { baseFeeBig = filter.BaseFee.ToBig() } + if filter.MaxL1TxSize != nil { + maxL1SizeScaled = new(big.Int).Mul(filter.MaxL1TxSize, big.NewInt(1e6)) + } pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending)) for addr, list := range pool.pending { txs := list.Flatten() @@ -568,9 +572,20 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address] } } } + + if filter.MaxL1TxSize != nil && !pool.locals.contains(addr) { + for i, tx := range txs { + if types.EstimatedL1Size(tx.RollupCostData()).Cmp(maxL1SizeScaled) > 0 { + txs = txs[:i] + break + } + } + } if len(txs) > 0 { lazies := make([]*txpool.LazyTransaction, len(txs)) for i := 0; i < len(txs); i++ { + daBytes := types.EstimatedL1Size(txs[i].RollupCostData()) + daBytes.Div(daBytes, big.NewInt(1e6)) lazies[i] = &txpool.LazyTransaction{ Pool: pool, Hash: txs[i].Hash(), @@ -580,6 +595,7 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address] GasTipCap: uint256.MustFromBig(txs[i].GasTipCap()), Gas: txs[i].Gas(), BlobGas: txs[i].BlobGas(), + DABytes: daBytes, } } pending[addr] = lazies diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index 9881ed1b8f..cdc8ce8025 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -41,6 +41,8 @@ type LazyTransaction struct { Gas uint64 // Amount of gas required by the transaction BlobGas uint64 // Amount of blob gas required by the transaction + + DABytes *big.Int // Amount of data availability bytes this transaction may require if this is a rollup } // Resolve retrieves the full transaction belonging to a lazy handle if it is still @@ -83,6 +85,10 @@ type PendingFilter struct { OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling) OnlyBlobTxs bool // Return only blob transactions (block blob-space filling) + + // OP stack addition: Maximum l1 data size allowed for an included transaction (for throttling + // when batcher is backlogged). Ignored if nil. + MaxL1TxSize *big.Int } // SubPool represents a specialized transaction pool that lives on its own (e.g. diff --git a/eth/api_miner.go b/eth/api_miner.go index 8c96f4c54a..e3523ce8e1 100644 --- a/eth/api_miner.go +++ b/eth/api_miner.go @@ -56,3 +56,9 @@ func (api *MinerAPI) SetGasLimit(gasLimit hexutil.Uint64) bool { api.e.Miner().SetGasCeil(uint64(gasLimit)) return true } + +// SetMaxL1Size sets the maximum l1 data size of any tx allowed in a block. 0 means no maximum. +func (api *MinerAPI) SetMaxL1Size(maxSize hexutil.Big) bool { + api.e.Miner().SetMaxL1Size((*big.Int)(&maxSize)) + return true +} diff --git a/miner/miner.go b/miner/miner.go index 0e5d877bec..00b56bc619 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -58,7 +58,9 @@ type Config struct { RollupComputePendingBlock bool // Compute the pending block from tx-pool, instead of copying the latest-block RollupTransactionConditionalRateLimit int // Total number of conditional cost units allowed in a second - EffectiveGasCeil uint64 // if non-zero, a gas ceiling to apply independent of the header's gaslimit value + EffectiveGasCeil uint64 // if non-zero, a gas ceiling to apply independent of the header's gaslimit value + MaxL1TxSize *big.Int // if non-nil, don't include any txs with l1 data size larger than this in any built block + MaxL1BlockSize *big.Int // if non-nil, then don't build a block requiring more than this amount of total data availability } // DefaultConfig contains default settings for miner. @@ -152,6 +154,22 @@ func (miner *Miner) SetGasTip(tip *big.Int) error { return nil } +// SetMaxL1Size sets the maximum l1 data size currently allowed for inclusion. 0 means no maximum. +func (miner *Miner) SetMaxL1Size(maxTxSize, maxBlockSize *big.Int) { + miner.confMu.Lock() + if maxTxSize.BitLen() == 0 { + miner.config.MaxL1TxSize = nil + } else { + miner.config.MaxL1TxSize = new(big.Int).Set(maxTxSize) + } + if maxBlockSize.BitLen() == 0 { + miner.config.MaxL1BlockSize = nil + } else { + miner.config.MaxL1BlockSize = new(big.Int).Set(maxBlockSize) + } + miner.confMu.Unlock() +} + // BuildPayload builds the payload according to the provided parameters. func (miner *Miner) BuildPayload(args *BuildPayloadArgs, witness bool) (*Payload, error) { return miner.buildPayload(args, witness) diff --git a/miner/worker.go b/miner/worker.go index e3300db11b..0c95b03d0c 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -415,6 +415,7 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran if env.gasPool == nil { env.gasPool = new(core.GasPool).AddGas(gasLimit) } + blockDABytes := new(big.Int) for { // Check interruption signal and abort building if it's fired. if interrupt != nil { @@ -468,6 +469,14 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran txs.Pop() continue } + if ltx.DABytes != nil && miner.config.MaxL1BlockSize != nil { + after := new(big.Int).Add(blockDABytes, ltx.DABytes) + if blockDABytes.Cmp(miner.config.MaxL1BlockSize) > 0 { + txs.Pop() + continue + } + blockDABytes = after + } // Transaction seems to fit, pull it up from the pool tx := ltx.Resolve() if tx == nil { @@ -529,7 +538,8 @@ func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) // Retrieve the pending transactions pre-filtered by the 1559/4844 dynamic fees filter := txpool.PendingFilter{ - MinTip: uint256.MustFromBig(tip), + MinTip: uint256.MustFromBig(tip), + MaxL1TxSize: miner.config.MaxL1TxSize, } if env.header.BaseFee != nil { filter.BaseFee = uint256.MustFromBig(env.header.BaseFee)