From 0c8cefdf344e263b94cd80bcc8a002c63f3b4ed1 Mon Sep 17 00:00:00 2001 From: MariusVanDerWijden Date: Wed, 17 Jun 2026 08:17:47 +0200 Subject: [PATCH] eth/catalyst: pass TargetGasLimit via engine api --- beacon/engine/pa_codec.go | 6 +++++ beacon/engine/types.go | 6 +++-- eth/catalyst/api.go | 3 +++ miner/payload_building.go | 56 +++++++++++++++++++++------------------ miner/worker.go | 29 ++++++++++++-------- 5 files changed, 61 insertions(+), 39 deletions(-) diff --git a/beacon/engine/pa_codec.go b/beacon/engine/pa_codec.go index 3a5d7ae593..3d28897b0e 100644 --- a/beacon/engine/pa_codec.go +++ b/beacon/engine/pa_codec.go @@ -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 } diff --git a/beacon/engine/types.go b/beacon/engine/types.go index c108718d07..d29d458fc7 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -71,12 +71,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 diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index ba153b196d..ac40fd9486 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -227,6 +227,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") } @@ -390,6 +392,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() diff --git a/miner/payload_building.go b/miner/payload_building.go index c4322e3317..52a112be01 100644 --- a/miner/payload_building.go +++ b/miner/payload_building.go @@ -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, diff --git a/miner/worker.go b/miner/worker.go index 01a14b8a02..99633a5868 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -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, }