From 62a8506787fd10f3e04cc3575661e3708600c019 Mon Sep 17 00:00:00 2001 From: Daniel Liu <139250065@qq.com> Date: Wed, 18 Mar 2026 15:44:48 +0800 Subject: [PATCH] feat(core,txpool,miner,params): implement eip-7825 tx gas limit cap #31824 #32230 --- core/error.go | 3 +++ core/state_processor_test.go | 16 ++++++++++---- core/state_transition.go | 4 ++++ core/txpool/legacypool/legacypool.go | 31 ++++++++++++++++++++++++---- core/txpool/subpool.go | 5 +++-- core/txpool/validation.go | 3 +++ miner/worker.go | 3 +++ params/protocol_params.go | 2 ++ 8 files changed, 57 insertions(+), 10 deletions(-) diff --git a/core/error.go b/core/error.go index 0dbd2348c375..2cda54594f58 100644 --- a/core/error.go +++ b/core/error.go @@ -114,6 +114,9 @@ var ( // Message validation errors: ErrEmptyAuthList = errors.New("EIP-7702 transaction with empty auth list") ErrSetCodeTxCreate = errors.New("EIP-7702 transaction cannot be used to create contract") + + // -- EIP-7825 errors -- + ErrGasLimitTooHigh = errors.New("transaction gas limit too high") ) // EIP-7702 state transition errors. diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 6923f23ff140..5eed37b0dec1 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -61,6 +61,7 @@ func TestStateProcessorErrors(t *testing.T) { Eip1559Block: big.NewInt(0), CancunBlock: big.NewInt(0), PragueBlock: big.NewInt(0), + OsakaBlock: big.NewInt(0), Ethash: new(params.EthashConfig), } signer = types.LatestSigner(config) @@ -134,6 +135,7 @@ func TestStateProcessorErrors(t *testing.T) { bigNumber := new(big.Int).SetBytes(common.MaxHash.Bytes()) tooBigNumber := new(big.Int).Set(bigNumber) tooBigNumber.Add(tooBigNumber, common.Big1) + gasLimit := blockchain.CurrentHeader().GasLimit for i, tt := range []struct { txs []*types.Transaction want string @@ -159,9 +161,9 @@ func TestStateProcessorErrors(t *testing.T) { }, { // ErrGasLimitReached txs: []*types.Transaction{ - makeTx(key1, 0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(12500000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), gasLimit+1, big.NewInt(12500000000), nil), }, - want: "could not apply tx 0 [0x062b0e84f2d48f09f91e434fca8cb1fb864c4fb82f8bf27d58879ebe60c9f773]: gas limit reached, have: 4712388, need: 21000000", + want: "could not apply tx 0 [0x141d5093bebc1570bf844ff66c14113a4516f601f8e4df7aa4d575a4e9bcaa33]: gas limit reached, have: 4712388, need: 4712389", }, { // ErrInsufficientFundsForTransfer txs: []*types.Transaction{ @@ -187,9 +189,9 @@ func TestStateProcessorErrors(t *testing.T) { }, { // ErrGasLimitReached txs: []*types.Transaction{ - makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(12500000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), gasLimit+1, big.NewInt(12500000000), nil), }, - want: "could not apply tx 0 [0x062b0e84f2d48f09f91e434fca8cb1fb864c4fb82f8bf27d58879ebe60c9f773]: gas limit reached, have: 4712388, need: 21000000", + want: "could not apply tx 0 [0x141d5093bebc1570bf844ff66c14113a4516f601f8e4df7aa4d575a4e9bcaa33]: gas limit reached, have: 4712388, need: 4712389", }, { // ErrFeeCapTooLow txs: []*types.Transaction{ @@ -251,6 +253,12 @@ func TestStateProcessorErrors(t *testing.T) { want: "could not apply tx 0 [0x2fadb4fa7ccf8564edc21590f8d94a5b93a981b2bb2de8256978cb7361bc69de]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)", }, // ErrSetCodeTxCreate cannot be tested: it is impossible to create a SetCode-tx with nil `to`. + { // ErrGasLimitTooHigh + txs: []*types.Transaction{ + makeTx(key1, 0, common.Address{}, big.NewInt(0), params.MaxTxGas+1, big.NewInt(params.InitialBaseFee), nil), + }, + want: "could not apply tx 0 [0xb49a1f798a865850a62b4deb6a71efb9150e5bf11a46b3f331fec62baa0547b4]: transaction gas limit too high (cap: 16777216, tx: 16777217)", + }, } { block := GenerateBadBlock(t, genesis, ethash.NewFaker(), tt.txs, gspec.Config) _, err := blockchain.InsertChain(types.Blocks{block}) diff --git a/core/state_transition.go b/core/state_transition.go index f04a512f517d..aa2d31bab770 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -359,6 +359,10 @@ func (st *StateTransition) preCheck() error { return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From) } } + // Verify tx gas limit does not exceed EIP-7825 cap. + if st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber) && msg.GasLimit > params.MaxTxGas { + return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit) + } return st.buyGas() } diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 5b4641ddb5f1..7426baab30be 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -556,11 +556,19 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address] txs := list.Flatten() // If the miner requests tip enforcement, cap the lists now - if minTipBig != nil { + if minTipBig != nil || filter.GasLimitCap != 0 { for i, tx := range txs { - if !tx.IsSpecialTransaction() && tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 { - txs = txs[:i] - break + if minTipBig != nil { + if !tx.IsSpecialTransaction() && tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 { + txs = txs[:i] + break + } + } + if filter.GasLimitCap != 0 { + if tx.Gas() > filter.GasLimitCap { + txs = txs[:i] + break + } } } } @@ -1351,6 +1359,21 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest, } pool.mu.Lock() if reset != nil { + if reset.newHead != nil && reset.oldHead != nil { + // Discard the transactions with the gas limit higher than the cap. + if pool.chainconfig.IsOsaka(reset.newHead.Number) && !pool.chainconfig.IsOsaka(reset.oldHead.Number) { + var hashes []common.Hash + pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool { + if tx.Gas() > params.MaxTxGas { + hashes = append(hashes, hash) + } + return true + }) + for _, hash := range hashes { + pool.removeTx(hash, true, true) + } + } + } // Reset from the old head to the new, rescheduling any reorged transactions pool.reset(reset.oldHead, reset.newHead) diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index 8a7e08f8afe9..2e2bf2b51c56 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -72,8 +72,9 @@ type LazyResolver interface { // a very specific call site in mind and each one can be evaluated very cheaply // by the pool implementations. Only add new ones that satisfy those constraints. type PendingFilter struct { - MinTip *uint256.Int // Minimum miner tip required to include a transaction - BaseFee *uint256.Int // Minimum 1559 basefee needed to include a transaction + MinTip *uint256.Int // Minimum miner tip required to include a transaction + BaseFee *uint256.Int // Minimum 1559 basefee needed to include a transaction + GasLimitCap uint64 // Maximum gas can be used for a single transaction execution (0 means no limit) } // SubPool represents a specialized transaction pool that lives on its own (e.g. diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 4fb7b598e734..ea470a8bf320 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -68,6 +68,9 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types if rules.IsEIP1559 && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { return fmt.Errorf("%w: code size %v, limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize) } + if rules.IsOsaka && tx.Gas() > params.MaxTxGas { + return fmt.Errorf("%w: cap %d, tx %d", core.ErrGasLimitTooHigh, params.MaxTxGas, tx.Gas()) + } // Transactions can't be negative. This may never happen using RLP decoded // transactions but may occur for transactions created using the RPC. if tx.Value().Sign() < 0 { diff --git a/miner/worker.go b/miner/worker.go index 01822771c9b4..0e89dd6e005e 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -868,6 +868,9 @@ func (w *worker) commitNewWork() { if header.BaseFee != nil { filter.BaseFee = uint256.MustFromBig(header.BaseFee) } + if w.chainConfig.IsOsaka(header.Number) { + filter.GasLimitCap = params.MaxTxGas + } pending := w.eth.TxPool().Pending(filter) txs, specialTxs = newTransactionsByPriceAndNonce(w.current.signer, pending, feeCapacity, header.BaseFee) } diff --git a/params/protocol_params.go b/params/protocol_params.go index dfca08487751..12dcdd9d9601 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -29,6 +29,8 @@ const ( GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. XDCGenesisGasLimit uint64 = 42000000 + MaxTxGas uint64 = 1 << 24 // Maximum transaction gas limit after EIP-7825 (16,777,216). + MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis. ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. SloadGas uint64 = 50 //