eth/catalyst: pass TargetGasLimit via engine api

This commit is contained in:
MariusVanDerWijden 2026-06-17 08:17:47 +02:00
parent 9e04883a85
commit c6a4fd8ca1
No known key found for this signature in database
6 changed files with 99 additions and 77 deletions

View file

@ -17,25 +17,25 @@ var _ = (*executableDataMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (e ExecutableData) MarshalJSON() ([]byte, error) {
type ExecutableData struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"`
Random common.Hash `json:"prevRandao" gencodec:"required"`
Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"`
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
BlockAccessList hexutil.Bytes `json:"blockAccessList,omitempty"`
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"`
Random common.Hash `json:"prevRandao" gencodec:"required"`
Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"`
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
BlockAccessList hexutil.Bytes `json:"blockAccessList,omitempty"`
}
var enc ExecutableData
enc.ParentHash = e.ParentHash
@ -68,25 +68,25 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
// UnmarshalJSON unmarshals from JSON.
func (e *ExecutableData) UnmarshalJSON(input []byte) error {
type ExecutableData struct {
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot *common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"`
Random *common.Hash `json:"prevRandao" gencodec:"required"`
Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"`
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
BlockAccessList *hexutil.Bytes `json:"blockAccessList,omitempty"`
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"`
StateRoot *common.Hash `json:"stateRoot" gencodec:"required"`
ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"`
LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"`
Random *common.Hash `json:"prevRandao" gencodec:"required"`
Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"`
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
BlockAccessList *hexutil.Bytes `json:"blockAccessList,omitempty"`
}
var dec ExecutableData
if err := json.Unmarshal(input, &dec); err != nil {

View file

@ -22,6 +22,7 @@ func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
TargetGasLimit *hexutil.Uint64 `json:"targetGasLimit"`
}
var enc PayloadAttributes
enc.Timestamp = hexutil.Uint64(p.Timestamp)
@ -30,6 +31,7 @@ func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
enc.Withdrawals = p.Withdrawals
enc.BeaconRoot = p.BeaconRoot
enc.SlotNumber = (*hexutil.Uint64)(p.SlotNumber)
enc.TargetGasLimit = (*hexutil.Uint64)(p.TargetGasLimit)
return json.Marshal(&enc)
}
@ -42,6 +44,7 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
TargetGasLimit *hexutil.Uint64 `json:"targetGasLimit"`
}
var dec PayloadAttributes
if err := json.Unmarshal(input, &dec); err != nil {
@ -68,5 +71,8 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
if dec.SlotNumber != nil {
p.SlotNumber = (*uint64)(dec.SlotNumber)
}
if dec.TargetGasLimit != nil {
p.TargetGasLimit = (*uint64)(dec.TargetGasLimit)
}
return nil
}

View file

@ -74,12 +74,14 @@ type PayloadAttributes struct {
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
SlotNumber *uint64 `json:"slotNumber"`
TargetGasLimit *uint64 `json:"targetGasLimit"`
}
// JSON type overrides for PayloadAttributes.
type payloadAttributesMarshaling struct {
Timestamp hexutil.Uint64
SlotNumber *hexutil.Uint64
Timestamp hexutil.Uint64
SlotNumber *hexutil.Uint64
TargetGasLimit *hexutil.Uint64
}
//go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out ed_codec.go

View file

@ -226,6 +226,8 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV4(ctx context.Context, update engine.
return engine.STATUS_INVALID, attributesErr("missing beacon root")
case params.SlotNumber == nil:
return engine.STATUS_INVALID, attributesErr("missing slot number")
case params.TargetGasLimit == nil:
return engine.STATUS_INVALID, attributesErr("missing target gas limit")
case !api.checkFork(params.Timestamp, forks.Amsterdam):
return engine.STATUS_INVALID, unsupportedForkErr("fcuV4 must only be called for amsterdam payloads")
}
@ -386,6 +388,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(ctx context.Context, update engine.Fo
Withdrawals: payloadAttributes.Withdrawals,
BeaconRoot: payloadAttributes.BeaconRoot,
SlotNum: payloadAttributes.SlotNumber,
TargetGasLimit: payloadAttributes.TargetGasLimit,
Version: payloadVersion,
}
id := args.Id()

View file

@ -40,14 +40,15 @@ import (
// Check engine-api specification for more details.
// https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#payloadattributesv3
type BuildPayloadArgs struct {
Parent common.Hash // The parent block to build payload on top
Timestamp uint64 // The provided timestamp of generated payload
FeeRecipient common.Address // The provided recipient address for collecting transaction fee
Random common.Hash // The provided randomness value
Withdrawals types.Withdrawals // The provided withdrawals
BeaconRoot *common.Hash // The provided beaconRoot (Cancun)
SlotNum *uint64 // The provided slotNumber
Version engine.PayloadVersion // Versioning byte for payload id calculation.
Parent common.Hash // The parent block to build payload on top
Timestamp uint64 // The provided timestamp of generated payload
FeeRecipient common.Address // The provided recipient address for collecting transaction fee
Random common.Hash // The provided randomness value
Withdrawals types.Withdrawals // The provided withdrawals
BeaconRoot *common.Hash // The provided beaconRoot (Cancun)
SlotNum *uint64 // The provided slotNumber
TargetGasLimit *uint64 // The provided target gas limit (Amsterdam)
Version engine.PayloadVersion // Versioning byte for payload id calculation.
}
// Id computes an 8-byte identifier by hashing the components of the payload arguments.
@ -246,15 +247,16 @@ func (miner *Miner) buildPayload(ctx context.Context, args *BuildPayloadArgs, wi
// enough to run. The empty payload can at least make sure there is something
// to deliver for not missing slot.
emptyParams := &generateParams{
timestamp: args.Timestamp,
forceTime: true,
parentHash: args.Parent,
coinbase: args.FeeRecipient,
random: args.Random,
withdrawals: args.Withdrawals,
beaconRoot: args.BeaconRoot,
slotNum: args.SlotNum,
noTxs: true,
timestamp: args.Timestamp,
forceTime: true,
parentHash: args.Parent,
coinbase: args.FeeRecipient,
random: args.Random,
withdrawals: args.Withdrawals,
beaconRoot: args.BeaconRoot,
slotNum: args.SlotNum,
targetGasLimit: args.TargetGasLimit,
noTxs: true,
}
empty := miner.generateWork(ctx, emptyParams, witness)
if empty.err != nil {
@ -286,15 +288,16 @@ func (miner *Miner) buildPayload(ctx context.Context, args *BuildPayloadArgs, wi
endTimer := time.NewTimer(time.Second * 12)
fullParams := &generateParams{
timestamp: args.Timestamp,
forceTime: true,
parentHash: args.Parent,
coinbase: args.FeeRecipient,
random: args.Random,
withdrawals: args.Withdrawals,
beaconRoot: args.BeaconRoot,
slotNum: args.SlotNum,
noTxs: false,
timestamp: args.Timestamp,
forceTime: true,
parentHash: args.Parent,
coinbase: args.FeeRecipient,
random: args.Random,
withdrawals: args.Withdrawals,
beaconRoot: args.BeaconRoot,
slotNum: args.SlotNum,
targetGasLimit: args.TargetGasLimit,
noTxs: false,
}
for {
select {
@ -351,6 +354,7 @@ func (miner *Miner) BuildTestingPayload(args *BuildPayloadArgs, transactions []*
withdrawals: args.Withdrawals,
beaconRoot: args.BeaconRoot,
slotNum: args.SlotNum,
targetGasLimit: args.TargetGasLimit,
noTxs: empty,
forceOverrides: true,
overrideExtraData: extraData,

View file

@ -117,15 +117,16 @@ type newPayloadResult struct {
// generateParams wraps various settings for generating sealing task.
type generateParams struct {
timestamp uint64 // The timestamp for sealing task
forceTime bool // Flag whether the given timestamp is immutable or not
parentHash common.Hash // Parent block hash, empty means the latest chain head
coinbase common.Address // The fee recipient address for including transaction
random common.Hash // The randomness generated by beacon chain, empty before the merge
withdrawals types.Withdrawals // List of withdrawals to include in block (shanghai field)
beaconRoot *common.Hash // The beacon root (cancun field).
slotNum *uint64 // The slot number (amsterdam field).
noTxs bool // Flag whether an empty block without any transaction is expected
timestamp uint64 // The timestamp for sealing task
forceTime bool // Flag whether the given timestamp is immutable or not
parentHash common.Hash // Parent block hash, empty means the latest chain head
coinbase common.Address // The fee recipient address for including transaction
random common.Hash // The randomness generated by beacon chain, empty before the merge
withdrawals types.Withdrawals // List of withdrawals to include in block (shanghai field)
beaconRoot *common.Hash // The beacon root (cancun field).
slotNum *uint64 // The slot number (amsterdam field).
targetGasLimit *uint64 // The target gas limit requested by the CL (amsterdam field).
noTxs bool // Flag whether an empty block without any transaction is expected
forceOverrides bool // Flag whether we should overwrite extraData and transactions
overrideExtraData []byte
@ -267,11 +268,17 @@ func (miner *Miner) prepareWork(ctx context.Context, genParams *generateParams,
}
timestamp = parent.Time + 1
}
// Post-Amsterdam use TargetGasLimit provided by CL
number := new(big.Int).Add(parent.Number, common.Big1)
gasCeil := miner.config.GasCeil
if miner.chainConfig.IsAmsterdam(number, timestamp) && genParams.targetGasLimit != nil {
gasCeil = *genParams.targetGasLimit
}
// Construct the sealing block header.
header := &types.Header{
ParentHash: parent.Hash(),
Number: new(big.Int).Add(parent.Number, common.Big1),
GasLimit: core.CalcGasLimit(parent.GasLimit, miner.config.GasCeil),
Number: number,
GasLimit: core.CalcGasLimit(parent.GasLimit, gasCeil),
Time: timestamp,
Coinbase: genParams.coinbase,
}