Skip to content

Commit af879b0

Browse files
jwasingerMariusVanDerWijden
authored andcommitted
core/vm: implement EIP 7825 - Transaction Gas Limit Cap
1 parent c7bdf6b commit af879b0

File tree

6 files changed

+85
-19
lines changed

6 files changed

+85
-19
lines changed

core/block_validator.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
8484
// Blob transactions may be present after the Cancun fork.
8585
var blobs int
8686
for i, tx := range block.Transactions() {
87+
if v.config.IsOsaka(block.Number(), block.Time()) && tx.Gas() > params.MaxTxGas {
88+
return fmt.Errorf("transaction exceeds maximum allowed gas limit (has %d gas)", tx.Gas())
89+
}
90+
8791
// Count the number of blobs to validate against the header's blobGasUsed
8892
blobs += len(tx.BlobHashes())
8993

core/txpool/legacypool/legacypool.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,20 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
12621262
}
12631263
pool.mu.Lock()
12641264
if reset != nil {
1265+
if reset.newHead != nil && reset.oldHead != nil {
1266+
if pool.chainconfig.IsOsaka(reset.newHead.Number, reset.newHead.Time) && !pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) {
1267+
var removeHashes []common.Hash
1268+
pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
1269+
if tx.Gas() > params.MaxTxGas {
1270+
removeHashes = append(removeHashes, hash)
1271+
}
1272+
return true
1273+
})
1274+
for _, hash := range removeHashes {
1275+
pool.all.Remove(hash)
1276+
}
1277+
}
1278+
}
12651279
// Reset from the old head to the new, rescheduling any reorged transactions
12661280
pool.reset(reset.oldHead, reset.newHead)
12671281

@@ -1286,7 +1300,7 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
12861300
// because of another transaction (e.g. higher gas price).
12871301
if reset != nil {
12881302
pool.demoteUnexecutables()
1289-
if reset.newHead != nil {
1303+
if reset.newHead != reset.oldHead {
12901304
if pool.chainconfig.IsLondon(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) {
12911305
pendingBaseFee := eip1559.CalcBaseFee(pool.chainconfig, reset.newHead)
12921306
pool.priced.SetBaseFee(pendingBaseFee)

core/txpool/txpool.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ type TxPool struct {
7676
term chan struct{} // Termination channel to detect a closed pool
7777

7878
sync chan chan error // Testing / simulator channel to block until internal reset is done
79+
80+
headLock sync.RWMutex
81+
head *types.Header // this reflects the state from the latest pool reset
7982
}
8083

8184
// New creates a new transaction pool to gather, sort and filter inbound
@@ -104,6 +107,7 @@ func New(gasTip uint64, chain BlockChain, subpools []SubPool) (*TxPool, error) {
104107
quit: make(chan chan error),
105108
term: make(chan struct{}),
106109
sync: make(chan chan error),
110+
head: head,
107111
}
108112
reserver := NewReservationTracker()
109113
for i, subpool := range subpools {
@@ -203,6 +207,9 @@ func (p *TxPool) loop(head *types.Header) {
203207
}
204208
select {
205209
case resetDone <- newHead:
210+
p.headLock.Lock()
211+
p.head = newHead
212+
p.headLock.Unlock()
206213
case <-p.term:
207214
}
208215
}(oldHead, newHead)
@@ -257,6 +264,14 @@ func (p *TxPool) loop(head *types.Header) {
257264
errc <- nil
258265
}
259266

267+
// Head returns the header which corresponds to the most recent successful pool
268+
// reset.
269+
func (p *TxPool) Head() *types.Header {
270+
p.headLock.RLock()
271+
defer p.headLock.RUnlock()
272+
return types.CopyHeader(p.head)
273+
}
274+
260275
// SetGasTip updates the minimum gas tip required by the transaction pool for a
261276
// new transaction, and drops all transactions below this threshold.
262277
func (p *TxPool) SetGasTip(tip *big.Int) {

core/txpool/validation.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
8686
if rules.IsShanghai && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize {
8787
return fmt.Errorf("%w: code size %v, limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize)
8888
}
89+
if rules.IsOsaka && tx.Gas() > params.MaxTxGas {
90+
return fmt.Errorf("transaction gas exceeded max allowed (%d): %d", params.MaxTxGas, tx.Gas())
91+
}
8992
// Transactions can't be negative. This may never happen using RLP decoded
9093
// transactions but may occur for transactions created using the RPC.
9194
if tx.Value().Sign() < 0 {

miner/worker.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ type environment struct {
5959
sidecars []*types.BlobTxSidecar
6060
blobs int
6161

62-
witness *stateless.Witness
62+
witness *stateless.Witness
63+
chainConfig *params.ChainConfig
6364
}
6465

6566
const (
@@ -253,12 +254,13 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
253254
}
254255
// Note the passed coinbase may be different with header.Coinbase.
255256
return &environment{
256-
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
257-
state: state,
258-
coinbase: coinbase,
259-
header: header,
260-
witness: state.Witness(),
261-
evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vm.Config{}),
257+
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
258+
state: state,
259+
coinbase: coinbase,
260+
header: header,
261+
witness: state.Witness(),
262+
evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vm.Config{}),
263+
chainConfig: miner.chainConfig,
262264
}, nil
263265
}
264266

@@ -425,6 +427,23 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran
425427
return nil
426428
}
427429

430+
// fiterTxsAboveGas removes any transactions from the set which exceed a gas threshold.
431+
// If a transaction is removed, all higher-nonce transactions from the same account
432+
// will also be filtered out.
433+
func filterTxsAboveGas(txs map[common.Address][]*txpool.LazyTransaction, maxGas uint64) {
434+
for sender, senderTxs := range txs {
435+
for i, tx := range senderTxs {
436+
if tx.Gas > maxGas {
437+
if i == 0 {
438+
delete(txs, sender)
439+
} else {
440+
txs[sender] = txs[sender][:i]
441+
}
442+
}
443+
}
444+
}
445+
}
446+
428447
// fillTransactions retrieves the pending transactions from the txpool and fills them
429448
// into the given sealing block. The transaction selection and ordering strategy can
430449
// be customized with the plugin in the future.
@@ -454,6 +473,16 @@ func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment)
454473
prioPlainTxs, normalPlainTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingPlainTxs
455474
prioBlobTxs, normalBlobTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingBlobTxs
456475

476+
// if we have just entered the Osaka fork, it is possible due to the async
477+
// nature of txpool resets that the state of the pool has not finished resetting
478+
// to a post-fork block. If so, it may not have removed txs that exceed the
479+
// eip-7825 transaction maximum gas limit.
480+
poolHead := miner.txpool.Head()
481+
if env.chainConfig.IsOsaka(env.header.Number, env.header.Time) && !env.chainConfig.IsOsaka(poolHead.Number, poolHead.Time) {
482+
filterTxsAboveGas(prioPlainTxs, params.MaxTxGas)
483+
filterTxsAboveGas(prioBlobTxs, params.MaxTxGas)
484+
}
485+
457486
for _, account := range prio {
458487
if txs := normalPlainTxs[account]; len(txs) > 0 {
459488
delete(normalPlainTxs, account)

params/protocol_params.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,18 @@ const (
2828
MaxGasLimit uint64 = 0x7fffffffffffffff // Maximum the gas limit (2^63-1).
2929
GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block.
3030

31-
MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis.
32-
ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction.
33-
SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
34-
CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero.
35-
CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior.
36-
TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions.
37-
TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions.
38-
TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
39-
QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation.
40-
LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
41-
CallStipend uint64 = 2300 // Free gas given at beginning of call.
31+
MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis.
32+
ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction.
33+
SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
34+
CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero.
35+
CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior.
36+
TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions.
37+
MaxTxGas uint64 = 30_000_000 // eip-7825 maximum transaction gas limit
38+
TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions.
39+
TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
40+
QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation.
41+
LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
42+
CallStipend uint64 = 2300 // Free gas given at beginning of call.
4243

4344
Keccak256Gas uint64 = 30 // Once per KECCAK256 operation.
4445
Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data.

0 commit comments

Comments
 (0)