Skip to content

Commit 1b995c3

Browse files
authored
Merge pull request #322 from bnb-chain/develop
chore: release for v0.5.10
2 parents 54c9f24 + 469ceb0 commit 1b995c3

16 files changed

Lines changed: 160 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# Changelog
22

3+
4+
## v0.5.10
5+
6+
This release enforces a per-transaction gas limit (EIP-7825, 16,777,216) at both tx pool admission and block packing to prevent oversized transactions from impacting network security. It also adds essential logging to the bundle pool for production debugging, and adjusts MaxBundleAliveBlock to 240 to align with the current 250ms block time.
7+
8+
### What's Changed
9+
10+
### BUGFIX
11+
12+
* [\#315](https://github.com/bnb-chain/op-geth/pull/315) fix: add bundle logs
13+
* [\#318](https://github.com/bnb-chain/op-geth/pull/318) feat: support config tx gas limit
14+
* [\#319](https://github.com/bnb-chain/op-geth/pull/318) fix: adjust MaxBundleAliveBlock to 240
15+
16+
### Docker Images
17+
18+
ghcr.io/bnb-chain/op-geth:v0.5.10
19+
20+
**Full Changelog**: https://github.com/bnb-chain/op-geth/compare/v0.5.9...v0.5.10
21+
22+
323
## v0.5.9
424

525
This release confirm the time of Mainnet Fourier Hardfork, effectively reducing the block time from 500 milliseconds to an impressive 250 milliseconds.

cmd/geth/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,13 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isCon
476476
}()
477477
}
478478

479+
// Set per-transaction gas limit cap on the txpool if configured
480+
if ethBackend, ok := backend.(*eth.EthAPIBackend); ok {
481+
if txGasLimit := ethBackend.Miner().TxGasLimit(); txGasLimit > 0 {
482+
ethBackend.TxPool().SetMaxTxGas(txGasLimit)
483+
}
484+
}
485+
479486
// Start auxiliary services if enabled
480487
if ctx.Bool(utils.MiningEnabledFlag.Name) {
481488
// Mining only makes sense if a full Ethereum node is running

cmd/utils/flags.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,6 +1798,8 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
17981798
if ctx.IsSet(MevBundleGasPriceFloorFlag.Name) {
17991799
cfg.Mev.MevBundleGasPriceFloor = ctx.Int64(MevBundleGasPriceFloorFlag.Name)
18001800
}
1801+
// Set the maximum gas allowed per individual transaction to the default 16,777,216
1802+
cfg.TxGasLimit = params.MaxTxGas
18011803
}
18021804

18031805
func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {

core/error.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,8 @@ var (
113113

114114
// ErrSystemTxNotSupported is returned for any deposit tx with IsSystemTx=true after the Regolith fork
115115
ErrSystemTxNotSupported = errors.New("system tx not supported")
116+
117+
// -- EIP-7825 errors --
118+
// ErrTxGasLimitTooHigh is returned if a transaction's gas limit exceeds the per-tx cap.
119+
ErrTxGasLimitTooHigh = errors.New("transaction gas limit too high")
116120
)

core/txpool/blobpool/blobpool.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"path/filepath"
2828
"sort"
2929
"sync"
30+
"sync/atomic"
3031
"time"
3132

3233
"github.com/ethereum/go-ethereum/common"
@@ -306,6 +307,8 @@ type BlobPool struct {
306307
state *state.StateDB // Current state at the head of the chain
307308
gasTip *uint256.Int // Currently accepted minimum gas tip
308309

310+
maxTxGas atomic.Uint64 // Maximum gas allowed per individual transaction
311+
309312
lookup map[common.Hash]uint64 // Lookup table mapping hashes to tx billy entries
310313
index map[common.Address][]*blobTxMeta // Blob transactions grouped by accounts, sorted by nonce
311314
spent map[common.Address]*uint256.Int // Expenditure tracking for individual accounts
@@ -1076,16 +1079,27 @@ func (p *BlobPool) SetGasTip(tip *big.Int) {
10761079
p.updateStorageMetrics()
10771080
}
10781081

1082+
// SetMaxTxGas implements txpool.SubPool, allowing the blob pool's per-tx gas
1083+
// limit to be kept in sync with the main transaction pool.
1084+
func (p *BlobPool) SetMaxTxGas(maxTxGas uint64) {
1085+
p.maxTxGas.Store(maxTxGas)
1086+
}
1087+
1088+
func (p *BlobPool) GetMaxTxGas() uint64 {
1089+
return p.maxTxGas.Load()
1090+
}
1091+
10791092
// validateTx checks whether a transaction is valid according to the consensus
10801093
// rules and adheres to some heuristic limits of the local node (price and size).
10811094
func (p *BlobPool) validateTx(tx *types.Transaction) error {
10821095
// Ensure the transaction adheres to basic pool filters (type, size, tip) and
10831096
// consensus rules
10841097
baseOpts := &txpool.ValidationOptions{
1085-
Config: p.chain.Config(),
1086-
Accept: 1 << types.BlobTxType,
1087-
MaxSize: txMaxSize,
1088-
MinTip: p.gasTip.ToBig(),
1098+
Config: p.chain.Config(),
1099+
Accept: 1 << types.BlobTxType,
1100+
MaxSize: txMaxSize,
1101+
MinTip: p.gasTip.ToBig(),
1102+
MaxTxGas: p.GetMaxTxGas(),
10891103
}
10901104
if err := txpool.ValidateTransaction(tx, p.head, p.signer, baseOpts); err != nil {
10911105
return err
@@ -1488,6 +1502,9 @@ func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*tx
14881502
break // blobfee too low, cannot be included, discard rest of txs from the account
14891503
}
14901504
}
1505+
if filter.GasLimitCap != 0 && tx.execGas > filter.GasLimitCap {
1506+
break // execution gas limit too high, discard rest of txs from the account
1507+
}
14911508
// Transaction was accepted according to the filter, append to the pending list
14921509
lazies = append(lazies, &txpool.LazyTransaction{
14931510
Pool: p,

core/txpool/bundlepool/bundlepool.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ import (
44
"container/heap"
55
"context"
66
"errors"
7-
mapset "github.com/deckarep/golang-set/v2"
8-
"github.com/ethereum/go-ethereum/miner"
9-
"github.com/ethereum/go-ethereum/rpc"
107
"math/big"
118
"sync"
129
"time"
1310

11+
mapset "github.com/deckarep/golang-set/v2"
1412
"github.com/ethereum/go-ethereum/common"
1513
"github.com/ethereum/go-ethereum/core"
1614
"github.com/ethereum/go-ethereum/core/state"
@@ -19,7 +17,9 @@ import (
1917
"github.com/ethereum/go-ethereum/event"
2018
"github.com/ethereum/go-ethereum/log"
2119
"github.com/ethereum/go-ethereum/metrics"
20+
"github.com/ethereum/go-ethereum/miner"
2221
"github.com/ethereum/go-ethereum/params"
22+
"github.com/ethereum/go-ethereum/rpc"
2323
)
2424

2525
const (
@@ -167,22 +167,25 @@ func (p *BundlePool) AddBundle(bundle *types.Bundle, originBundle *types.SendBun
167167
return ErrBundleTimestampTooHigh
168168
}
169169

170+
hash := bundle.Hash()
170171
price, err := p.simulator.SimulateBundle(bundle)
171172
if err != nil {
173+
log.Warn("Bundle simulation failed", "hash", hash, "err", err)
172174
return err
173175
}
174176
bundle.Price = price
175177

176178
p.mu.Lock()
177179
defer p.mu.Unlock()
178180

179-
hash := bundle.Hash()
180181
if _, ok := p.bundles[hash]; ok {
182+
log.Debug("Bundle already exists in pool", "hash", hash)
181183
return ErrBundleAlreadyExist
182184
}
183185

184186
if p.slots+numSlots(bundle) > p.config.GlobalSlots {
185187
if !p.drop(bundle) {
188+
log.Warn("Bundle rejected, gas price too low to replace existing", "hash", hash, "price", price)
186189
return ErrBundleGasPriceLow
187190
}
188191
}
@@ -205,13 +208,15 @@ func (p *BundlePool) AddBundle(bundle *types.Bundle, originBundle *types.SendBun
205208
heap.Push(&p.bundleHeap, bundle)
206209
p.slots += numSlots(bundle)
207210

211+
log.Info("Bundle added to pool", "hash", hash, "price", price, "txCount", len(bundle.Txs), "poolSize", len(p.bundles), "slots", p.slots)
212+
208213
bundleGauge.Update(int64(len(p.bundles)))
209214
slotsGauge.Update(int64(p.slots))
210215
return nil
211216
}
212217

213218
func (p *BundlePool) GetBundle(hash common.Hash) *types.Bundle {
214-
p.mu.RUnlock()
219+
p.mu.RLock()
215220
defer p.mu.RUnlock()
216221

217222
return p.bundles[hash]
@@ -220,6 +225,7 @@ func (p *BundlePool) GetBundle(hash common.Hash) *types.Bundle {
220225
func (p *BundlePool) PruneBundle(hash common.Hash) {
221226
p.mu.Lock()
222227
defer p.mu.Unlock()
228+
log.Debug("Bundle pruned externally", "hash", hash)
223229
p.deleteBundle(hash)
224230
}
225231

@@ -232,6 +238,7 @@ func (p *BundlePool) PendingBundles(blockNumber uint64, blockTimestamp uint64) [
232238
// Prune outdated bundles
233239
if (bundle.MaxTimestamp != 0 && blockTimestamp > bundle.MaxTimestamp) ||
234240
(bundle.MaxBlockNumber != 0 && blockNumber > bundle.MaxBlockNumber) {
241+
log.Debug("Bundle expired in PendingBundles", "hash", hash, "maxTimestamp", bundle.MaxTimestamp, "blockTimestamp", blockTimestamp, "maxBlockNumber", bundle.MaxBlockNumber, "blockNumber", blockNumber)
235242
p.deleteBundle(hash)
236243
continue
237244
}
@@ -373,9 +380,11 @@ func (p *BundlePool) reset(newHead *types.Header) {
373380
for hash, bundle := range p.bundles {
374381
if (bundle.MaxTimestamp != 0 && newHead.Time > bundle.MaxTimestamp) ||
375382
(bundle.MaxBlockNumber != 0 && newHead.Number.Cmp(new(big.Int).SetUint64(bundle.MaxBlockNumber)) > 0) {
383+
log.Debug("Bundle pruned on reset (expired)", "hash", hash, "headNumber", newHead.Number, "headTime", newHead.Time, "maxBlockNumber", bundle.MaxBlockNumber, "maxTimestamp", bundle.MaxTimestamp)
376384
p.slots -= numSlots(p.bundles[hash])
377385
delete(p.bundles, hash)
378386
} else if txSet.Contains(bundle.Txs[0].Hash()) {
387+
log.Debug("Bundle pruned on reset (tx already included)", "hash", hash, "headNumber", newHead.Number, "firstTx", bundle.Txs[0].Hash())
379388
p.slots -= numSlots(p.bundles[hash])
380389
delete(p.bundles, hash)
381390
}
@@ -402,6 +411,7 @@ func (p *BundlePool) drop(bundle *types.Bundle) bool {
402411
for len(p.bundleHeap) > 0 {
403412
if dropSlots >= numSlots(bundle) {
404413
for _, dropBundle := range dropBundles {
414+
log.Info("Bundle dropped due to slot limit", "hash", dropBundle.Hash(), "price", dropBundle.Price)
405415
p.deleteBundle(dropBundle.Hash())
406416
}
407417
return true
@@ -438,7 +448,7 @@ func (p *BundlePool) minimalBundleGasPrice() *big.Int {
438448
return new(big.Int)
439449
}
440450

441-
func (p *BundlePool) SetMaxGas(maxGas uint64) {}
451+
func (p *BundlePool) SetMaxTxGas(maxTxGas uint64) {}
442452

443453
// =====================================================================================================================
444454

core/txpool/legacypool/legacypool.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ type LegacyPool struct {
276276
chainconfig *params.ChainConfig
277277
chain BlockChain
278278
gasTip atomic.Pointer[uint256.Int]
279+
maxTxGas atomic.Uint64
279280
txFeed event.Feed
280281
reannoTxFeed event.Feed
281282
signer types.Signer
@@ -585,6 +586,15 @@ func (pool *LegacyPool) SetGasTip(tip *big.Int) {
585586
log.Info("Legacy pool tip threshold updated", "tip", newTip)
586587
}
587588

589+
func (pool *LegacyPool) GetMaxTxGas() uint64 {
590+
return pool.maxTxGas.Load()
591+
}
592+
593+
// SetMaxGas updates the maximum gas allowed per individual transaction.
594+
func (pool *LegacyPool) SetMaxTxGas(maxTxGas uint64) {
595+
pool.maxTxGas.Store(maxTxGas)
596+
}
597+
588598
// Nonce returns the next nonce of an account, with all transactions executable
589599
// by the pool already applied on top.
590600
func (pool *LegacyPool) Nonce(addr common.Address) uint64 {
@@ -705,10 +715,16 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address]
705715
txs = txs[noncetoolow+1:]
706716
}
707717

708-
// If the miner requests tip enforcement, cap the lists now
709-
if minTipBig != nil && !localAddrs[addr] {
718+
// If the miner requests tip enforcement or tx gas cap, filter the lists now
719+
if (minTipBig != nil && !localAddrs[addr]) || filter.GasLimitCap != 0 {
710720
for i, tx := range txs {
711-
if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 {
721+
if minTipBig != nil && !localAddrs[addr] {
722+
if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 {
723+
txs = txs[:i]
724+
break
725+
}
726+
}
727+
if filter.GasLimitCap != 0 && tx.Gas() > filter.GasLimitCap {
712728
txs = txs[:i]
713729
break
714730
}
@@ -790,6 +806,7 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
790806
MaxSize: txMaxSize,
791807
MinTip: pool.gasTip.Load().ToBig(),
792808
EffectiveGasCeil: pool.config.EffectiveGasCeil,
809+
MaxTxGas: pool.GetMaxTxGas(),
793810
}
794811
if local {
795812
opts.MinTip = new(big.Int)

core/txpool/subpool.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ type PendingFilter struct {
8181
BaseFee *uint256.Int // Minimum 1559 basefee needed to include a transaction
8282
BlobFee *uint256.Int // Minimum 4844 blobfee needed to include a blob transaction
8383

84+
GasLimitCap uint64 // Maximum gas allowed per transaction (0 = no cap)
85+
8486
OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling)
8587
OnlyBlobTxs bool // Return only blob transactions (block blob-space filling)
8688
}
@@ -166,6 +168,9 @@ type SubPool interface {
166168
// Status returns the known status (unknown/pending/queued) of a transaction
167169
// identified by their hashes.
168170
Status(hash common.Hash) TxStatus
171+
172+
// SetMaxTxGas updates the maximum gas allowed per individual transaction.
173+
SetMaxTxGas(maxTxGas uint64)
169174
}
170175

171176
type BundleSubpool interface {

core/txpool/txpool.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,14 @@ func (p *TxPool) SetGasTip(tip *big.Int) {
286286
}
287287
}
288288

289+
// SetMaxTxGas updates the maximum gas allowed per individual transaction across
290+
// all subpools.
291+
func (p *TxPool) SetMaxTxGas(maxTxGas uint64) {
292+
for _, subpool := range p.subpools {
293+
subpool.SetMaxTxGas(maxTxGas)
294+
}
295+
}
296+
289297
// Has returns an indicator whether the pool has a transaction cached with the
290298
// given hash.
291299
func (p *TxPool) Has(hash common.Hash) bool {

core/txpool/validation.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ type ValidationOptions struct {
6565
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
6666

6767
EffectiveGasCeil uint64 // if non-zero, a gas ceiling to enforce independent of the header's gaslimit value
68+
MaxTxGas uint64 // if non-zero, maximum gas allowed per individual transaction
6869
}
6970

7071
// ValidateTransaction is a helper method to check whether a transaction is valid
@@ -120,6 +121,10 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
120121
if EffectiveGasLimit(opts.Config, head.GasLimit, opts.EffectiveGasCeil) < tx.Gas() {
121122
return ErrGasLimit
122123
}
124+
// Ensure the transaction doesn't exceed the per-tx gas limit cap
125+
if opts.MaxTxGas > 0 && tx.Gas() > opts.MaxTxGas {
126+
return fmt.Errorf("%w (cap: %d, tx: %d)", core.ErrTxGasLimitTooHigh, opts.MaxTxGas, tx.Gas())
127+
}
123128
// Sanity check for extremely large numbers (supported by RLP or RPC)
124129
if tx.GasFeeCap().BitLen() > 256 {
125130
Meter(FeeCapVeryHigh).Mark(1)

0 commit comments

Comments
 (0)