Skip to content

miner, core, core/txpool: implement EIP 7825 - Transaction Gas Limit Cap #31824

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 6 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
2 changes: 2 additions & 0 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Blob transactions may be present after the Cancun fork.
var blobs int
for i, tx := range block.Transactions() {
// TODO: check that the transaction doesn't consume more than 30_000_000 gas

// Count the number of blobs to validate against the header's blobGasUsed
blobs += len(tx.BlobHashes())

Expand Down
14 changes: 14 additions & 0 deletions core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ func (pool *LegacyPool) ValidateTxBasics(tx *types.Transaction) error {
MaxSize: txMaxSize,
MinTip: pool.gasTip.Load().ToBig(),
}
// TODO: validate that the tx doesn't have a gas limit higher than 30 million
return txpool.ValidateTransaction(tx, pool.currentHead.Load(), pool.signer, opts)
}

Expand Down Expand Up @@ -1262,6 +1263,19 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
}
pool.mu.Lock()
if reset != nil {
// TODO: ensure oldHead refers to a head block that we were previously synced to
if pool.chainconfig.IsOsaka(reset.newHead.Number, reset.newHead.Time) && !pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) {
var removeHashes []common.Hash
pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
if tx.Gas() > 30_000_000 {
removeHashes = append(removeHashes, hash)
}
return true
})
for _, hash := range removeHashes {
pool.all.Remove(hash)
}
}
// Reset from the old head to the new, rescheduling any reorged transactions
pool.reset(reset.oldHead, reset.newHead)

Expand Down
17 changes: 17 additions & 0 deletions core/txpool/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ type TxPool struct {
term chan struct{} // Termination channel to detect a closed pool

sync chan chan error // Testing / simulator channel to block until internal reset is done

headLock sync.RWMutex
head *types.Header // the head of the current pool state
}

// New creates a new transaction pool to gather, sort and filter inbound
Expand Down Expand Up @@ -186,6 +189,9 @@ func (p *TxPool) loop(head *types.Header) {
// Try to inject a busy marker and start a reset if successful
select {
case resetBusy <- struct{}{}:
// TODO: if we've just passed the fusaka fork, purge any transactions
// in the pool that have a gas limit of >= 30_000_000

// Updates the statedb with the new chain head. The head state may be
// unavailable if the initial state sync has not yet completed.
if statedb, err := p.chain.StateAt(newHead.Root); err != nil {
Expand All @@ -203,6 +209,9 @@ func (p *TxPool) loop(head *types.Header) {
}
select {
case resetDone <- newHead:
p.headLock.Lock()
p.head = newHead
p.headLock.Unlock()
case <-p.term:
}
}(oldHead, newHead)
Expand Down Expand Up @@ -257,6 +266,14 @@ func (p *TxPool) loop(head *types.Header) {
errc <- nil
}

// Head returns the header which corresponds to the most recent successful pool
// reset.
func (p *TxPool) Head() *types.Header {
p.headLock.RLock()
defer p.headLock.RUnlock()
return types.CopyHeader(p.head)
}

// SetGasTip updates the minimum gas tip required by the transaction pool for a
// new transaction, and drops all transactions below this threshold.
func (p *TxPool) SetGasTip(tip *big.Int) {
Expand Down
3 changes: 3 additions & 0 deletions core/txpool/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
if rules.IsShanghai && 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() > 30_000_000 {
return fmt.Errorf("transaction gas exceeded max allowed (30_000_000): %d", 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 {
Expand Down
43 changes: 42 additions & 1 deletion miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,14 @@
evm *vm.EVM

header *types.Header
parent *types.Header

Check failure on line 57 in miner/worker.go

View workflow job for this annotation

GitHub Actions / Lint

field parent is unused (unused)
txs []*types.Transaction
receipts []*types.Receipt
sidecars []*types.BlobTxSidecar
blobs int

witness *stateless.Witness
witness *stateless.Witness
chainConfig *params.ChainConfig
}

const (
Expand Down Expand Up @@ -425,6 +427,27 @@
return nil
}

func (e *environment) isOsakaForkBlock() bool {

Check failure on line 430 in miner/worker.go

View workflow job for this annotation

GitHub Actions / Lint

func (*environment).isOsakaForkBlock is unused (unused)
return e.chainConfig.IsOsaka(e.header.Number, e.header.Time) && !e.chainConfig.IsOsaka(e.header.Number, e.header.Time)
}

// fiterTxsAboveGas removes any transactions from a sender which exceed masGas.
// If a transaction is removed, all higher-nonce transactions from the same account
// will also be filtered out.
func filterTxsAboveGas(txs map[common.Address][]*txpool.LazyTransaction, maxGas uint64) {
for sender, senderTxs := range txs {
for i, tx := range senderTxs {
if tx.Gas > maxGas {
if i == 0 {
delete(txs, sender)
} else {
txs[sender] = txs[sender][:i]
}
}
}
}
}

// fillTransactions retrieves the pending transactions from the txpool and fills them
// into the given sealing block. The transaction selection and ordering strategy can
// be customized with the plugin in the future.
Expand All @@ -450,10 +473,22 @@
filter.OnlyPlainTxs, filter.OnlyBlobTxs = false, true
pendingBlobTxs := miner.txpool.Pending(filter)

// if this is the fusaka fork block, filter all txs w/ gas >= 30_000_000 from the pending ones

// Split the pending transactions into locals and remotes.
prioPlainTxs, normalPlainTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingPlainTxs
prioBlobTxs, normalBlobTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingBlobTxs

// if we have just entered the Osaka fork, it is possible due to the async
// nature of txpool resets that the state of the pool has not reset to a point
// past the fork block. We need to filter out any transactions that exceed the
// eip 7823 tx gas threshold.
poolHead := miner.txpool.Head()
if env.chainConfig.IsOsaka(env.header.Number, env.header.Time) && !env.chainConfig.IsOsaka(poolHead.Number, poolHead.Time) {
filterTxsAboveGas(prioPlainTxs, 30_000_000)
filterTxsAboveGas(prioBlobTxs, 30_000_000)
}

for _, account := range prio {
if txs := normalPlainTxs[account]; len(txs) > 0 {
delete(normalPlainTxs, account)
Expand All @@ -464,6 +499,12 @@
prioBlobTxs[account] = txs
}
}

// TODO: if we are at fusaka and any transactions exceed 30_000_000 gas limit, don't include them.
// there's no way to ensure they are purged from the mempool before we can get them here
// because of the asynchronous nature: the mempool may not receive notification of a head
// change past the fusaka fork before we start building the payload here.

// Fill the block with all available pending transactions.
if len(prioPlainTxs) > 0 || len(prioBlobTxs) > 0 {
plainTxs := newTransactionsByPriceAndNonce(env.signer, prioPlainTxs, env.header.BaseFee)
Expand Down
Loading