Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 12 additions & 4 deletions core/state_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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{
Expand All @@ -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{
Expand Down Expand Up @@ -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})
Expand Down
4 changes: 4 additions & 0 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Comment thread
gzliudan marked this conversation as resolved.
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
}
Comment thread
gzliudan marked this conversation as resolved.
return st.buyGas()
}

Expand Down
31 changes: 27 additions & 4 deletions core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Comment on lines +559 to +571
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PendingFilter adds GasLimitCap, and LegacyPool.Pending now truncates per-account pending lists when a tx’s gas limit exceeds the cap. There are existing unit tests for MinTip/BaseFee filtering, but none exercising the new GasLimitCap behavior. Adding a targeted test (including the nonce-order truncation case) would help prevent regressions in miner selection logic.

Copilot uses AI. Check for mistakes.
}
}
Comment thread
gzliudan marked this conversation as resolved.
}
Expand Down Expand Up @@ -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)

Expand Down
5 changes: 3 additions & 2 deletions core/txpool/subpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Comment thread
gzliudan marked this conversation as resolved.
}

// SubPool represents a specialized transaction pool that lives on its own (e.g.
Expand Down
3 changes: 3 additions & 0 deletions core/txpool/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Comment on lines +71 to +73
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new Osaka rule in ValidateTransaction rejects txs with Gas() above params.MaxTxGas, but there’s no direct unit test covering this validation path (either for ValidateTransaction itself or via pool insertion under an Osaka-enabled config). Please add a test that asserts the correct error wrapping/message for a tx with Gas = MaxTxGas+1 once Osaka is active.

Copilot uses AI. Check for mistakes.
// 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 {
Expand Down
3 changes: 3 additions & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Comment thread
gzliudan marked this conversation as resolved.
Comment thread
gzliudan marked this conversation as resolved.
pending := w.eth.TxPool().Pending(filter)
txs, specialTxs = newTransactionsByPriceAndNonce(w.current.signer, pending, feeCapacity, header.BaseFee)
}
Expand Down
2 changes: 2 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 //
Expand Down
Loading