Skip to content

core,miner,params: implement EIP-7934 - RLP Execution Block Size Limit #31990

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

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain) *Bloc
// header's transaction and uncle roots. The headers are assumed to be already
// validated at this point.
func (v *BlockValidator) ValidateBody(block *types.Block) error {
// check EIP 7934 RLP-encoded block size cap
if v.config.IsOsaka(block.Number(), block.Time()) && block.Size() > params.BlockRLPSizeCap {
return ErrBlockOversized
}
// Check whether the block is already imported.
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
return ErrKnownBlock
Expand Down
4 changes: 4 additions & 0 deletions core/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ var (

// ErrNoGenesis is returned when there is no Genesis Block.
ErrNoGenesis = errors.New("genesis not found in chain")

// ErrBlockOversized is returned if the size of the RLP-encoded block
// exceeds the cap established by EIP 7934
ErrBlockOversized = errors.New("block RLP-encoded size exceeds maximum")
)

// List of evm-call-message pre-checking errors. All state transition messages will
Expand Down
58 changes: 51 additions & 7 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type environment struct {
signer types.Signer
state *state.StateDB // apply state changes here
tcount int // tx count in cycle
size uint64 // size of the block we are building
gasPool *core.GasPool // available gas used to pack transactions
coinbase common.Address
evm *vm.EVM
Expand All @@ -68,6 +69,11 @@ const (
commitInterruptNewHead
commitInterruptResubmit
commitInterruptTimeout

// cap the size of blocks we will produce below the max allowed by
// EIP-7934. This gives us buffer room if the estimated size of the
// block we are building is off from the actual encoded size.
blockRLPSizeCapBuffer = 1_000_000
)

// newPayloadResult is the result of payload generation.
Expand Down Expand Up @@ -95,12 +101,37 @@ type generateParams struct {
}

// generateWork generates a sealing block based on the given parameters.
func (miner *Miner) generateWork(params *generateParams, witness bool) *newPayloadResult {
work, err := miner.prepareWork(params, witness)
func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPayloadResult {
work, err := miner.prepareWork(genParam, witness)
if err != nil {
return &newPayloadResult{err: err}
}
if !params.noTxs {
var includedWithdrawals types.Withdrawals

// If we are post-osaka, incorporate the requested withdrawals into the
// block size calculation up-front to ensure that all requested withdrawals
// can be included even if we hit the size cap when filling the block with
// txs.
//
// Also, ensure that including all requested withdrawals wouldn't bring us
// over the block size cap limit. The withdrawal cap ensures that this can't
// actually happen right now, but it doesn't hurt to make this code
// future-proof for a situation where the withdrawal cap is lifted.
if miner.chainConfig.IsOsaka(work.header.Number, work.header.Time) {
maxBlockSize := params.BlockRLPSizeCap - blockRLPSizeCapBuffer

for _, withdrawal := range genParam.withdrawals {
if int(work.size)+params.WithdrawalSize > maxBlockSize {
break
Copy link
Member

Choose a reason for hiding this comment

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

From the CL team:

potuz — 2025/6/10 21:11
This makes the payload invalid
Technically it will make the consensus block that contains that payload invalid.

Probably we need to put a note here, at least adding a warning log that a part of withdrawals specified by the CL are discarded due to the size restriction. In practice, it's impossible to occur.

}
work.size += params.WithdrawalSize
includedWithdrawals = append(includedWithdrawals, withdrawal)
}
} else {
includedWithdrawals = genParam.withdrawals
}

if !genParam.noTxs {
interrupt := new(atomic.Int32)
timer := time.AfterFunc(miner.config.Recommit, func() {
interrupt.Store(commitInterruptTimeout)
Expand All @@ -112,8 +143,8 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo
log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.config.Recommit))
}
}
body := types.Body{Transactions: work.txs, Withdrawals: includedWithdrawals}

body := types.Body{Transactions: work.txs, Withdrawals: params.withdrawals}
allLogs := make([]*types.Log, 0)
for _, r := range work.receipts {
allLogs = append(allLogs, r.Logs...)
Expand Down Expand Up @@ -256,6 +287,7 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
return &environment{
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
state: state,
size: uint64(header.Size()),
coinbase: coinbase,
header: header,
witness: state.Witness(),
Expand All @@ -273,6 +305,7 @@ func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction) e
}
env.txs = append(env.txs, tx)
env.receipts = append(env.receipts, receipt)
env.size += tx.Size()
Copy link
Member

Choose a reason for hiding this comment

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

It only applies the non-blob-tx, but for the blob-tx, the tx withouth sidecar should also be considered for the inclusion size.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think blobs don't need to be included here, since they do not become a part of the block.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gary's point was that his path wasn't called for blob transactions. We need to factor the blob transaction without the sidecar into the size calc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i've since updated this pr.

env.tcount++
return nil
}
Expand All @@ -298,6 +331,7 @@ func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transactio
env.receipts = append(env.receipts, receipt)
env.sidecars = append(env.sidecars, sc)
env.blobs += len(sc.Blobs)
env.size += tx.WithoutBlobTxSidecar().Size()
*env.header.BlobGasUsed += receipt.BlobGasUsed
env.tcount++
return nil
Expand All @@ -318,7 +352,11 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*
}

func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
gasLimit := env.header.GasLimit
var (
isOsaka = miner.chainConfig.IsOsaka(env.header.Number, env.header.Time)
isCancun = miner.chainConfig.IsCancun(env.header.Number, env.header.Time)
gasLimit = env.header.GasLimit
)
if env.gasPool == nil {
env.gasPool = new(core.GasPool).AddGas(gasLimit)
}
Expand Down Expand Up @@ -374,7 +412,7 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran
// Most of the blob gas logic here is agnostic as to if the chain supports
// blobs or not, however the max check panics when called on a chain without
// a defined schedule, so we need to verify it's safe to call.
if miner.chainConfig.IsCancun(env.header.Number, env.header.Time) {
if isCancun {
left := eip4844.MaxBlobsPerBlock(miner.chainConfig, env.header.Time) - env.blobs
if left < int(ltx.BlobGas/params.BlobTxBlobGasPerBlob) {
log.Trace("Not enough blob space left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas/params.BlobTxBlobGasPerBlob)
Expand All @@ -391,8 +429,14 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran
continue
}

// if inclusion of the transaction would put the block size over the
// maximum we allow, don't add any more txs to the payload.
if isOsaka && env.size+tx.Size() > params.BlockRLPSizeCap-blockRLPSizeCapBuffer {
break
}

// Make sure all transactions after osaka have cell proofs
if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) {
if isOsaka {
if sidecar := tx.BlobTxSidecar(); sidecar != nil {
if sidecar.Version == 0 {
log.Info("Including blob tx with v0 sidecar, recomputing proofs", "hash", ltx.Hash)
Expand Down
3 changes: 3 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ const (
BlobTxPointEvaluationPrecompileGas = 50000 // Gas price for the point evaluation precompile.

HistoryServeWindow = 8192 // Number of blocks to serve historical block hashes for, EIP-2935.

WithdrawalSize = 23 // size of a withdrawal
BlockRLPSizeCap = 9_961_472 // maximum size of an RLP-encoded block
)

// Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation
Expand Down