Skip to content
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
2 changes: 2 additions & 0 deletions cl/beacon/handler/block_production.go
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,8 @@ func (a *ApiHandler) produceBeaconBody(
if stateVersion.AfterOrEqual(clparams.GloasVersion) {
sn := hexutil.Uint64(targetSlot)
attrs.SlotNumber = &sn
tgl := hexutil.Uint64(a.beaconChainCfg.DefaultBuilderGasLimit)
attrs.TargetGasLimit = &tgl
}
idBytes, err := a.engine.ForkChoiceUpdate(
ctx,
Expand Down
2 changes: 2 additions & 0 deletions cl/phase1/stages/forkchoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ func emitNextPaylodAttributesEvent(cfg *Cfg, headSlot uint64, headRoot common.Ha
if cfg.beaconCfg.GetCurrentStateVersion(epoch).AfterOrEqual(clparams.GloasVersion) {
sn := hexutil.Uint64(nextSlot)
payloadAttributes.SlotNumber = &sn
tgl := hexutil.Uint64(cfg.beaconCfg.DefaultBuilderGasLimit)
payloadAttributes.TargetGasLimit = &tgl
}
e := &beaconevents.PayloadAttributesData{
Version: cfg.beaconCfg.GetCurrentStateVersion(epoch).String(),
Expand Down
8 changes: 7 additions & 1 deletion execution/builder/create_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,13 @@ func createBlock(ctx context.Context, sd *execctx.SharedDomains, tx kv.TemporalT
uncles: mapset.NewSet[common.Hash](),
}

header := MakeEmptyHeader(parent, cfg.chainConfig, timestamp, cfg.builder.BuilderConfig.GasLimit)
targetGasLimit := cfg.builder.BuilderConfig.GasLimit
if cfg.blockBuilderParameters != nil && cfg.blockBuilderParameters.TargetGasLimit != nil {
// PayloadAttributesV4: CL-supplied target gas limit takes precedence over the
// static --miner.gaslimit so the EL follows engine_forkchoiceUpdatedV4.
targetGasLimit = cfg.blockBuilderParameters.TargetGasLimit
}
header := MakeEmptyHeader(parent, cfg.chainConfig, timestamp, targetGasLimit)
if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil {
logger.Warn("Failed to verify gas limit given by the validator, defaulting to parent gas limit", "err", err)
header.GasLimit = parent.GasLimit
Expand Down
1 change: 1 addition & 0 deletions execution/builder/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Parameters struct {
Withdrawals []*types.Withdrawal // added in Shapella (EIP-4895)
ParentBeaconBlockRoot *common.Hash // added in Dencun (EIP-4788)
SlotNumber *uint64 // added in Amsterdam (EIP-7843)
TargetGasLimit *uint64 // added in Amsterdam PayloadAttributesV4
// CustomTxnProvider overrides the block's transaction source when non-nil.
// nil → use the injected TxnProvider (normal mempool path)
CustomTxnProvider txnprovider.TxnProvider
Expand Down
52 changes: 52 additions & 0 deletions execution/engineapi/engine_api_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/erigontech/erigon/execution/protocol/params"
"github.com/erigontech/erigon/execution/state/contracts"
"github.com/erigontech/erigon/execution/types"
"github.com/erigontech/erigon/node/ethconfig"
"github.com/erigontech/erigon/rpc"
)

Expand Down Expand Up @@ -264,6 +265,57 @@ func TestEngineApiBlockGasOverflowSpillsToNextBlock(t *testing.T) {
})
}

// TestEngineApiV4TargetGasLimitOverridesMinerGasLimit checks that a CL-supplied
// targetGasLimit in PayloadAttributesV4 (engine_forkchoiceUpdatedV4) overrides
// the EL's static --miner.gaslimit when building a block — and that the
// resulting block respects the CL target as a cap.
//
// Setup picks numbers so the two values produce distinguishable block contents:
// - parent gas limit = 42_000 (room for two 21K-gas transfers)
// - static --miner.gaslimit = 21_000 (would cap the block at one transfer)
// - CL targetGasLimit = 42_000 (room for two transfers)
//
// Three transfers are submitted; only two must fit. If the static target won,
// the block would gas-limit at ~41_960 and contain a single transfer.
// See https://github.com/ethereum/execution-apis/pull/796.
func TestEngineApiV4TargetGasLimitOverridesMinerGasLimit(t *testing.T) {
ctx := t.Context()
logger := testlog.Logger(t, log.LvlDebug)
const targetGasLimit uint64 = 42_000
const minerGasLimit uint64 = 21_000
genesis, coinbaseKey, err := engineapitester.DefaultEngineApiTesterGenesis()
require.NoError(t, err)
genesis.GasLimit = targetGasLimit
eat, err := engineapitester.InitialiseEngineApiTester(ctx, engineapitester.EngineApiTesterInitArgs{
Logger: logger,
DataDir: t.TempDir(),
Genesis: genesis,
CoinbaseKey: coinbaseKey,
EthConfigTweaker: func(config *ethconfig.Config) {
gl := minerGasLimit
config.Builder.GasLimit = &gl
},
})
require.NoError(t, err)
t.Cleanup(func() {
err := eat.Close()
require.NoError(t, err)
})
eat.Run(t, func(ctx context.Context, t *testing.T, eat engineapitester.EngineApiTester) {
receiver := common.HexToAddress("0x42")
// Submit 3 transfers; only 2 should fit under the CL-supplied 42K cap.
for i := 0; i < 3; i++ {
_, err := eat.Transactor.SubmitSimpleTransfer(eat.CoinbaseKey, receiver, big.NewInt(int64(i+1)))
require.NoError(t, err)
}
payload, err := eat.MockCl.BuildCanonicalBlock(ctx)
require.NoError(t, err)
// Block gas limit follows the CL target — not the EL's --miner.gaslimit.
require.Equal(t, hexutil.Uint64(targetGasLimit), payload.ExecutionPayload.GasLimit)
require.Len(t, payload.ExecutionPayload.Transactions, 2)
})
}

func TestEngineApiSequentialNonceAdvancement(t *testing.T) {
ctx := t.Context()
logger := testlog.Logger(t, log.LvlDebug)
Expand Down
5 changes: 5 additions & 0 deletions execution/engineapi/engine_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ func (s *EngineServer) validatePayloadAttributesPostFCU(version clparams.StateVe
if version >= clparams.GloasVersion && payloadAttributes.SlotNumber == nil {
return &engine_helpers.InvalidPayloadAttributesErr // SlotNumber required for Glamsterdam (EIP-7843)
}
// TODO: enable once tests catch up with glamsterdam-devnet-4 spec
// if version >= clparams.GloasVersion && payloadAttributes.TargetGasLimit == nil {
Comment on lines 231 to +235
// return &engine_helpers.InvalidPayloadAttributesErr // TargetGasLimit required for V4 attrs
// }
return nil
}

Expand Down Expand Up @@ -795,6 +799,7 @@ func (s *EngineServer) forkchoiceUpdated(ctx context.Context, forkchoiceState *e
PrevRandao: payloadAttributes.PrevRandao,
SuggestedFeeRecipient: payloadAttributes.SuggestedFeeRecipient,
SlotNumber: (*uint64)(payloadAttributes.SlotNumber),
TargetGasLimit: (*uint64)(payloadAttributes.TargetGasLimit),
}

if version >= clparams.CapellaVersion {
Expand Down
1 change: 1 addition & 0 deletions execution/engineapi/engine_types/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type PayloadAttributes struct {
Withdrawals []*types.Withdrawal `json:"withdrawals"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
TargetGasLimit *hexutil.Uint64 `json:"targetGasLimit"`
SSZVersion clparams.StateVersion `json:"-"`
}

Expand Down
11 changes: 9 additions & 2 deletions execution/engineapi/engine_types/ssz.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ func (a *PayloadAttributes) EncodeSSZ(dst []byte) ([]byte, error) {
if a.SlotNumber != nil {
slot = uint64(*a.SlotNumber)
}
var targetGasLimit uint64
if a.TargetGasLimit != nil {
targetGasLimit = uint64(*a.TargetGasLimit)
}
switch version {
case clparams.BellatrixVersion:
return ssz2.MarshalSSZ(dst, uint64(a.Timestamp), a.PrevRandao[:], a.SuggestedFeeRecipient[:])
Expand All @@ -310,7 +314,7 @@ func (a *PayloadAttributes) EncodeSSZ(dst []byte) ([]byte, error) {
case clparams.DenebVersion:
return ssz2.MarshalSSZ(dst, uint64(a.Timestamp), a.PrevRandao[:], a.SuggestedFeeRecipient[:], withdrawals, root[:])
default:
return ssz2.MarshalSSZ(dst, uint64(a.Timestamp), a.PrevRandao[:], a.SuggestedFeeRecipient[:], withdrawals, root[:], slot)
return ssz2.MarshalSSZ(dst, uint64(a.Timestamp), a.PrevRandao[:], a.SuggestedFeeRecipient[:], withdrawals, root[:], slot, targetGasLimit)
}
}

Expand All @@ -320,6 +324,7 @@ func (a *PayloadAttributes) DecodeSSZ(buf []byte, version int) error {
var timestamp uint64
var root common.Hash
var slot uint64
var targetGasLimit uint64
switch a.SSZVersion {
case clparams.BellatrixVersion:
if err := ssz2.UnmarshalSSZ(buf, version, &timestamp, a.PrevRandao[:], a.SuggestedFeeRecipient[:]); err != nil {
Expand All @@ -337,13 +342,15 @@ func (a *PayloadAttributes) DecodeSSZ(buf []byte, version int) error {
a.Withdrawals = withdrawalsFromList(withdrawals)
a.ParentBeaconBlockRoot = &root
default:
if err := ssz2.UnmarshalSSZ(buf, version, &timestamp, a.PrevRandao[:], a.SuggestedFeeRecipient[:], withdrawals, root[:], &slot); err != nil {
if err := ssz2.UnmarshalSSZ(buf, version, &timestamp, a.PrevRandao[:], a.SuggestedFeeRecipient[:], withdrawals, root[:], &slot, &targetGasLimit); err != nil {
return err
}
a.Withdrawals = withdrawalsFromList(withdrawals)
a.ParentBeaconBlockRoot = &root
slotNumber := hexutil.Uint64(slot)
a.SlotNumber = &slotNumber
tgl := hexutil.Uint64(targetGasLimit)
a.TargetGasLimit = &tgl
}
a.Timestamp = hexutil.Uint64(timestamp)
return nil
Expand Down
4 changes: 4 additions & 0 deletions execution/engineapi/engineapitester/mock_cl.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type MockCl struct {
engineApiClient *engineapi.JsonRpcClient
suggestedFeeRecipient common.Address
genesis common.Hash
genesisGasLimit uint64
state *MockClState
blockListener *shutter.BlockListener
chainConfig *chain.Config
Expand All @@ -71,6 +72,7 @@ func NewMockCl(ctx context.Context, logger log.Logger, elClient *engineapi.JsonR
blockListener: shutter.NewBlockListener(logger, stateChangesClient),
suggestedFeeRecipient: genesis.Coinbase(),
genesis: genesis.Hash(),
genesisGasLimit: genesis.GasLimit(),
chainConfig: chainConfig,
state: &MockClState{
ParentElBlock: genesis.Hash(),
Expand Down Expand Up @@ -152,6 +154,8 @@ func (cl *MockCl) BuildNewPayload(ctx context.Context, opts ...BlockBuildingOpti
}
if cl.chainConfig.AmsterdamTime != nil {
payloadAttributes.SlotNumber = (*hexutil.Uint64)(&slotNumber)
targetGasLimit := hexutil.Uint64(cl.genesisGasLimit)
payloadAttributes.TargetGasLimit = &targetGasLimit
}
cl.logger.Debug("[mock-cl] building block", "timestamp", timestamp)
// start the block building process
Expand Down
4 changes: 4 additions & 0 deletions execution/engineapi/sszrest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,13 @@ func TestSSZRESTNewPayloadV5UsesGloasPayloadSchema(t *testing.T) {

func TestSSZRESTForkchoiceV4UsesGloasPayloadAttributesSchema(t *testing.T) {
slotNumber := hexutil.Uint64(456)
targetGasLimit := hexutil.Uint64(36000000)
attrs := &engine_types.PayloadAttributes{
Timestamp: 1,
SuggestedFeeRecipient: common.HexToAddress("0x1234"),
Withdrawals: nil,
SlotNumber: &slotNumber,
Comment on lines 251 to 255
TargetGasLimit: &targetGasLimit,
SSZVersion: clparams.GloasVersion,
}
state := engine_types.ForkChoiceState{}
Expand All @@ -266,6 +268,8 @@ func TestSSZRESTForkchoiceV4UsesGloasPayloadAttributesSchema(t *testing.T) {
require.NotNil(t, engineAttrs)
require.NotNil(t, engineAttrs.SlotNumber)
require.Equal(t, hexutil.Uint64(456), *engineAttrs.SlotNumber)
require.NotNil(t, engineAttrs.TargetGasLimit)
require.Equal(t, hexutil.Uint64(36000000), *engineAttrs.TargetGasLimit)
}

func TestExchangeCapabilitiesAdvertisesJSONRPCAndSSZREST(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions execution/engineapi/testing_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ func (t *testingImpl) BuildBlockV1(
PrevRandao: payloadAttributes.PrevRandao,
SuggestedFeeRecipient: payloadAttributes.SuggestedFeeRecipient,
SlotNumber: (*uint64)(payloadAttributes.SlotNumber),
TargetGasLimit: (*uint64)(payloadAttributes.TargetGasLimit),
CustomTxnProvider: customProvider,
}
if version >= clparams.CapellaVersion {
Expand Down
1 change: 1 addition & 0 deletions execution/execmodule/chainreader/chain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ func (c ChainReaderWriterEth1) AssembleBlock(baseHash common.Hash, attributes *e
SuggestedFeeRecipient: attributes.SuggestedFeeRecipient,
Withdrawals: attributes.Withdrawals,
SlotNumber: (*uint64)(attributes.SlotNumber),
TargetGasLimit: (*uint64)(attributes.TargetGasLimit),
ParentBeaconBlockRoot: attributes.ParentBeaconBlockRoot,
}
result, err := c.executionModule.AssembleBlock(context.Background(), params)
Expand Down
Loading