From 9927ca31713447003db9d225e7d232cbb766cd26 Mon Sep 17 00:00:00 2001 From: Andrew Ashikhmin <34320705+yperbasis@users.noreply.github.com> Date: Fri, 20 Jan 2023 18:43:08 +0100 Subject: [PATCH] Unify Engine API V1 & V2 methods (#6638) Reduce code duplication. Prerequisite: https://github.com/ledgerwatch/erigon-lib/pull/841. --- cmd/lightclient/lightclient/execution.go | 15 +- cmd/rpcdaemon/commands/engine_api.go | 294 ++++++----------------- cmd/rpcdaemon/rpcservices/eth_backend.go | 26 +- ethdb/privateapi/engine_test.go | 12 +- ethdb/privateapi/ethbackend.go | 124 +++++----- go.mod | 2 +- go.sum | 4 +- turbo/rpchelper/interface.go | 9 +- 8 files changed, 165 insertions(+), 321 deletions(-) diff --git a/cmd/lightclient/lightclient/execution.go b/cmd/lightclient/lightclient/execution.go index cffa35e33..7524d170f 100644 --- a/cmd/lightclient/lightclient/execution.go +++ b/cmd/lightclient/lightclient/execution.go @@ -7,7 +7,9 @@ import ( "github.com/ledgerwatch/erigon-lib/gointerfaces" "github.com/ledgerwatch/erigon-lib/gointerfaces/remote" "github.com/ledgerwatch/erigon-lib/gointerfaces/types" + "github.com/ledgerwatch/erigon/cl/cltypes" + "github.com/ledgerwatch/erigon/ethdb/privateapi" ) func convertLightrpcExecutionPayloadToEthbacked(e *cltypes.Eth1Block) *types.ExecutionPayload { @@ -25,7 +27,8 @@ func convertLightrpcExecutionPayloadToEthbacked(e *cltypes.Eth1Block) *types.Exe } } - return &types.ExecutionPayload{ + res := &types.ExecutionPayload{ + Version: 1, ParentHash: gointerfaces.ConvertHashToH256(header.ParentHash), Coinbase: gointerfaces.ConvertAddressToH160(header.Coinbase), StateRoot: gointerfaces.ConvertHashToH256(header.Root), @@ -41,6 +44,12 @@ func convertLightrpcExecutionPayloadToEthbacked(e *cltypes.Eth1Block) *types.Exe BlockHash: gointerfaces.ConvertHashToH256(header.BlockHashCL), Transactions: body.Transactions, } + if body.Withdrawals != nil { + res.Version = 2 + res.Withdrawals = privateapi.ConvertWithdrawalsToRpc(body.Withdrawals) + } + + return res } func (l *LightClient) processBeaconBlock(beaconBlock *cltypes.BeaconBlock) error { @@ -62,14 +71,14 @@ func (l *LightClient) processBeaconBlock(beaconBlock *cltypes.BeaconBlock) error payload := convertLightrpcExecutionPayloadToEthbacked(beaconBlock.Body.ExecutionPayload) - _, err = l.execution.EngineNewPayloadV1(l.ctx, payload) + _, err = l.execution.EngineNewPayload(l.ctx, payload) if err != nil { return err } // Wait a bit time.Sleep(500 * time.Millisecond) - _, err = l.execution.EngineForkChoiceUpdatedV1(l.ctx, &remote.EngineForkChoiceUpdatedRequest{ + _, err = l.execution.EngineForkChoiceUpdated(l.ctx, &remote.EngineForkChoiceUpdatedRequest{ ForkchoiceState: &remote.EngineForkChoiceState{ HeadBlockHash: payloadHash, SafeBlockHash: payloadHash, diff --git a/cmd/rpcdaemon/commands/engine_api.go b/cmd/rpcdaemon/commands/engine_api.go index ec75dcf24..6ae11e3ac 100644 --- a/cmd/rpcdaemon/commands/engine_api.go +++ b/cmd/rpcdaemon/commands/engine_api.go @@ -1,7 +1,6 @@ package commands import ( - "bytes" "context" "encoding/binary" "fmt" @@ -9,7 +8,6 @@ import ( "github.com/holiman/uint256" libcommon "github.com/ledgerwatch/erigon-lib/common" - "github.com/ledgerwatch/erigon-lib/gointerfaces" "github.com/ledgerwatch/erigon-lib/gointerfaces/remote" types2 "github.com/ledgerwatch/erigon-lib/gointerfaces/types" @@ -21,30 +19,11 @@ import ( "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/ethdb/privateapi" - "github.com/ledgerwatch/erigon/rlp" "github.com/ledgerwatch/erigon/turbo/rpchelper" ) -// ExecutionPayloadV1 represents an execution payload (aka block) without withdrawals -type ExecutionPayloadV1 struct { - ParentHash libcommon.Hash `json:"parentHash" gencodec:"required"` - FeeRecipient libcommon.Address `json:"feeRecipient" gencodec:"required"` - StateRoot libcommon.Hash `json:"stateRoot" gencodec:"required"` - ReceiptsRoot libcommon.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` - PrevRandao libcommon.Hash `json:"prevRandao" gencodec:"required"` - BlockNumber 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 libcommon.Hash `json:"blockHash" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` -} - -// ExecutionPayloadV2 represents an execution payload (aka block) with withdrawals -type ExecutionPayloadV2 struct { +// ExecutionPayload represents an execution payload (aka block) +type ExecutionPayload struct { ParentHash libcommon.Hash `json:"parentHash" gencodec:"required"` FeeRecipient libcommon.Address `json:"feeRecipient" gencodec:"required"` StateRoot libcommon.Hash `json:"stateRoot" gencodec:"required"` @@ -59,13 +38,13 @@ type ExecutionPayloadV2 struct { BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` BlockHash libcommon.Hash `json:"blockHash" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - Withdrawals []*types.Withdrawal `json:"withdrawals" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` } // GetPayloadV2Response represents the response of the getPayloadV2 method type GetPayloadV2Response struct { - ExecutionPayload ExecutionPayloadV2 `json:"executionPayload" gencodec:"required"` - BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` + ExecutionPayload *ExecutionPayload `json:"executionPayload" gencodec:"required"` + BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` } // PayloadAttributes represent the attributes required to start assembling a payload @@ -75,19 +54,12 @@ type ForkChoiceState struct { FinalizedBlockHash libcommon.Hash `json:"finalizedBlockHash" gencodec:"required"` } -// PayloadAttributesV1 represent the attributes required to start assembling a payload without withdrawals -type PayloadAttributesV1 struct { - Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` - PrevRandao libcommon.Hash `json:"prevRandao" gencodec:"required"` - SuggestedFeeRecipient libcommon.Address `json:"suggestedFeeRecipient" gencodec:"required"` -} - -// PayloadAttributesV2 represent the attributes required to start assembling a payload with withdrawals -type PayloadAttributesV2 struct { +// PayloadAttributes represent the attributes required to start assembling a payload +type PayloadAttributes struct { Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` PrevRandao libcommon.Hash `json:"prevRandao" gencodec:"required"` SuggestedFeeRecipient libcommon.Address `json:"suggestedFeeRecipient" gencodec:"required"` - Withdrawals []*types.Withdrawal `json:"withdrawals" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` } // TransitionConfiguration represents the correct configurations of the CL and the EL @@ -99,11 +71,11 @@ type TransitionConfiguration struct { // EngineAPI Beacon chain communication endpoint type EngineAPI interface { - NewPayloadV1(context.Context, *ExecutionPayloadV1) (map[string]interface{}, error) - NewPayloadV2(context.Context, *ExecutionPayloadV2) (map[string]interface{}, error) - ForkchoiceUpdatedV1(ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributesV1) (map[string]interface{}, error) - ForkchoiceUpdatedV2(ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributesV2) (map[string]interface{}, error) - GetPayloadV1(ctx context.Context, payloadID hexutil.Bytes) (*ExecutionPayloadV1, error) + NewPayloadV1(context.Context, *ExecutionPayload) (map[string]interface{}, error) + NewPayloadV2(context.Context, *ExecutionPayload) (map[string]interface{}, error) + ForkchoiceUpdatedV1(ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributes) (map[string]interface{}, error) + ForkchoiceUpdatedV2(ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributes) (map[string]interface{}, error) + GetPayloadV1(ctx context.Context, payloadID hexutil.Bytes) (*ExecutionPayload, error) GetPayloadV2(ctx context.Context, payloadID hexutil.Bytes) (*GetPayloadV2Response, error) ExchangeTransitionConfigurationV1(ctx context.Context, transitionConfiguration *TransitionConfiguration) (*TransitionConfiguration, error) } @@ -163,23 +135,36 @@ func addPayloadId(json map[string]interface{}, payloadId uint64) { } } -func (e *EngineImpl) ForkchoiceUpdatedV1(ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributesV1) (map[string]interface{}, error) { +func (e *EngineImpl) ForkchoiceUpdatedV1(ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributes) (map[string]interface{}, error) { + return e.forkchoiceUpdated(1, ctx, forkChoiceState, payloadAttributes) +} + +func (e *EngineImpl) ForkchoiceUpdatedV2(ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributes) (map[string]interface{}, error) { + return e.forkchoiceUpdated(2, ctx, forkChoiceState, payloadAttributes) +} + +func (e *EngineImpl) forkchoiceUpdated(version uint32, ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributes) (map[string]interface{}, error) { if e.internalCL { log.Error("EXTERNAL CONSENSUS LAYER IS NOT ENABLED, PLEASE RESTART WITH FLAG --externalcl") return nil, fmt.Errorf("engine api should not be used, restart with --externalcl") } - log.Debug("Received ForkchoiceUpdatedV1", "head", forkChoiceState.HeadHash, "safe", forkChoiceState.HeadHash, "finalized", forkChoiceState.FinalizedBlockHash, + log.Debug("Received ForkchoiceUpdated", "version", version, "head", forkChoiceState.HeadHash, "safe", forkChoiceState.HeadHash, "finalized", forkChoiceState.FinalizedBlockHash, "build", payloadAttributes != nil) var attributes *remote.EnginePayloadAttributes if payloadAttributes != nil { attributes = &remote.EnginePayloadAttributes{ + Version: 1, Timestamp: uint64(payloadAttributes.Timestamp), PrevRandao: gointerfaces.ConvertHashToH256(payloadAttributes.PrevRandao), SuggestedFeeRecipient: gointerfaces.ConvertAddressToH160(payloadAttributes.SuggestedFeeRecipient), } + if version >= 2 && payloadAttributes.Withdrawals != nil { + attributes.Version = 2 + attributes.Withdrawals = privateapi.ConvertWithdrawalsToRpc(payloadAttributes.Withdrawals) + } } - reply, err := e.api.EngineForkchoiceUpdatedV1(ctx, &remote.EngineForkChoiceUpdatedRequest{ + reply, err := e.api.EngineForkchoiceUpdated(ctx, &remote.EngineForkChoiceUpdatedRequest{ ForkchoiceState: &remote.EngineForkChoiceState{ HeadBlockHash: gointerfaces.ConvertHashToH256(forkChoiceState.HeadHash), SafeBlockHash: gointerfaces.ConvertHashToH256(forkChoiceState.SafeBlockHash), @@ -204,113 +189,29 @@ func (e *EngineImpl) ForkchoiceUpdatedV1(ctx context.Context, forkChoiceState *F return json, nil } -func (e *EngineImpl) ForkchoiceUpdatedV2(ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributesV2) (map[string]interface{}, error) { - if e.internalCL { - log.Error("EXTERNAL CONSENSUS LAYER IS NOT ENABLED, PLEASE RESTART WITH FLAG --externalcl") - return nil, fmt.Errorf("engine api should not be used, restart with --externalcl") - } - log.Debug("Received ForkchoiceUpdatedV2", "head", forkChoiceState.HeadHash, "safe", forkChoiceState.HeadHash, "finalized", forkChoiceState.FinalizedBlockHash, - "build", payloadAttributes != nil) - - var attributesV2 *remote.EnginePayloadAttributesV2 - if payloadAttributes != nil { - attributes := &remote.EnginePayloadAttributes{ - Timestamp: uint64(payloadAttributes.Timestamp), - PrevRandao: gointerfaces.ConvertHashToH256(payloadAttributes.PrevRandao), - SuggestedFeeRecipient: gointerfaces.ConvertAddressToH160(payloadAttributes.SuggestedFeeRecipient), - } - withdrawals := privateapi.ConvertWithdrawalsToRpc(payloadAttributes.Withdrawals) - attributesV2 = &remote.EnginePayloadAttributesV2{Attributes: attributes, Withdrawals: withdrawals} - } - reply, err := e.api.EngineForkchoiceUpdatedV2(ctx, &remote.EngineForkChoiceUpdatedRequestV2{ - ForkchoiceState: &remote.EngineForkChoiceState{ - HeadBlockHash: gointerfaces.ConvertHashToH256(forkChoiceState.HeadHash), - SafeBlockHash: gointerfaces.ConvertHashToH256(forkChoiceState.SafeBlockHash), - FinalizedBlockHash: gointerfaces.ConvertHashToH256(forkChoiceState.FinalizedBlockHash), - }, - PayloadAttributes: attributesV2, - }) - if err != nil { - return nil, err - } - - payloadStatus, err := convertPayloadStatus(ctx, e.db, reply.PayloadStatus) - if err != nil { - return nil, err - } - - json := map[string]interface{}{ - "payloadStatus": payloadStatus, - } - addPayloadId(json, reply.PayloadId) - - return json, nil -} - // NewPayloadV1 processes new payloads (blocks) from the beacon chain without withdrawals. -// See https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#engine_newpayloadv1 -func (e *EngineImpl) NewPayloadV1(ctx context.Context, payload *ExecutionPayloadV1) (map[string]interface{}, error) { - if e.internalCL { - log.Error("EXTERNAL CONSENSUS LAYER IS NOT ENABLED, PLEASE RESTART WITH FLAG --externalcl") - return nil, fmt.Errorf("engine api should not be used, restart with --externalcl") - } - log.Debug("Received NewPayloadV1", "height", uint64(payload.BlockNumber), "hash", payload.BlockHash) - - var baseFee *uint256.Int - if payload.BaseFeePerGas != nil { - var overflow bool - baseFee, overflow = uint256.FromBig((*big.Int)(payload.BaseFeePerGas)) - if overflow { - log.Warn("NewPayload BaseFeePerGas overflow") - return nil, fmt.Errorf("invalid request") - } - } - - // Convert slice of hexutil.Bytes to a slice of slice of bytes - transactions := make([][]byte, len(payload.Transactions)) - for i, transaction := range payload.Transactions { - transactions[i] = transaction - } - res, err := e.api.EngineNewPayloadV1(ctx, &types2.ExecutionPayload{ - ParentHash: gointerfaces.ConvertHashToH256(payload.ParentHash), - Coinbase: gointerfaces.ConvertAddressToH160(payload.FeeRecipient), - StateRoot: gointerfaces.ConvertHashToH256(payload.StateRoot), - ReceiptRoot: gointerfaces.ConvertHashToH256(payload.ReceiptsRoot), - LogsBloom: gointerfaces.ConvertBytesToH2048(payload.LogsBloom), - PrevRandao: gointerfaces.ConvertHashToH256(payload.PrevRandao), - BlockNumber: uint64(payload.BlockNumber), - GasLimit: uint64(payload.GasLimit), - GasUsed: uint64(payload.GasUsed), - Timestamp: uint64(payload.Timestamp), - ExtraData: payload.ExtraData, - BaseFeePerGas: gointerfaces.ConvertUint256IntToH256(baseFee), - BlockHash: gointerfaces.ConvertHashToH256(payload.BlockHash), - Transactions: transactions, - }) - if err != nil { - log.Warn("NewPayloadV1", "err", err) - return nil, err - } - return convertPayloadStatus(ctx, e.db, res) +// See https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#engine_newpayloadv1 +func (e *EngineImpl) NewPayloadV1(ctx context.Context, payload *ExecutionPayload) (map[string]interface{}, error) { + return e.newPayload(1, ctx, payload) } // NewPayloadV2 processes new payloads (blocks) from the beacon chain with withdrawals. -// See https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#engine_newpayloadv2 -func (e *EngineImpl) NewPayloadV2(ctx context.Context, payload *ExecutionPayloadV2) (map[string]interface{}, error) { +// See https://github.com/ethereum/execution-apis/blob/main/src/engine/shanghai.md#engine_newpayloadv2 +func (e *EngineImpl) NewPayloadV2(ctx context.Context, payload *ExecutionPayload) (map[string]interface{}, error) { + return e.newPayload(2, ctx, payload) +} + +func (e *EngineImpl) newPayload(version uint32, ctx context.Context, payload *ExecutionPayload) (map[string]interface{}, error) { if e.internalCL { log.Error("EXTERNAL CONSENSUS LAYER IS NOT ENABLED, PLEASE RESTART WITH FLAG --externalcl") return nil, fmt.Errorf("engine api should not be used, restart with --externalcl") } - log.Debug("Received NewPayloadV2", "height", uint64(payload.BlockNumber), "hash", payload.BlockHash) + log.Debug("Received NewPayload", "version", version, "height", uint64(payload.BlockNumber), "hash", payload.BlockHash) - var baseFee *uint256.Int - if payload.BaseFeePerGas != nil { - var overflow bool - baseFee, overflow = uint256.FromBig((*big.Int)(payload.BaseFeePerGas)) - if overflow { - log.Warn("NewPayload BaseFeePerGas overflow") - return nil, fmt.Errorf("invalid request") - } + baseFee, overflow := uint256.FromBig((*big.Int)(payload.BaseFeePerGas)) + if overflow { + log.Warn("NewPayload BaseFeePerGas overflow") + return nil, fmt.Errorf("invalid request") } // Convert slice of hexutil.Bytes to a slice of slice of bytes @@ -319,6 +220,7 @@ func (e *EngineImpl) NewPayloadV2(ctx context.Context, payload *ExecutionPayload transactions[i] = transaction } ep := &types2.ExecutionPayload{ + Version: 1, ParentHash: gointerfaces.ConvertHashToH256(payload.ParentHash), Coinbase: gointerfaces.ConvertAddressToH160(payload.FeeRecipient), StateRoot: gointerfaces.ConvertHashToH256(payload.StateRoot), @@ -334,41 +236,30 @@ func (e *EngineImpl) NewPayloadV2(ctx context.Context, payload *ExecutionPayload BlockHash: gointerfaces.ConvertHashToH256(payload.BlockHash), Transactions: transactions, } - withdrawals := privateapi.ConvertWithdrawalsToRpc(payload.Withdrawals) - res, err := e.api.EngineNewPayloadV2(ctx, &types2.ExecutionPayloadV2{Payload: ep, Withdrawals: withdrawals}) + if version >= 2 && payload.Withdrawals != nil { + ep.Version = 2 + ep.Withdrawals = privateapi.ConvertWithdrawalsToRpc(payload.Withdrawals) + } + + res, err := e.api.EngineNewPayload(ctx, ep) if err != nil { - log.Warn("NewPayloadV2", "err", err) + log.Warn("NewPayload", "err", err) return nil, err } return convertPayloadStatus(ctx, e.db, res) } -func (e *EngineImpl) GetPayloadV1(ctx context.Context, payloadID hexutil.Bytes) (*ExecutionPayloadV1, error) { - if e.internalCL { - log.Error("EXTERNAL CONSENSUS LAYER IS NOT ENABLED, PLEASE RESTART WITH FLAG --externalcl") - return nil, fmt.Errorf("engine api should not be used, restart with --externalcl") - } - - decodedPayloadId := binary.BigEndian.Uint64(payloadID) - log.Info("Received GetPayloadV1", "payloadId", decodedPayloadId) - - payload, err := e.api.EngineGetPayloadV1(ctx, decodedPayloadId) - if err != nil { - return nil, err - } +func convertPayloadFromRpc(payload *types2.ExecutionPayload) *ExecutionPayload { var bloom types.Bloom = gointerfaces.ConvertH2048ToBloom(payload.LogsBloom) - - var baseFee *big.Int - if payload.BaseFeePerGas != nil { - baseFee = gointerfaces.ConvertH256ToUint256Int(payload.BaseFeePerGas).ToBig() - } + baseFee := gointerfaces.ConvertH256ToUint256Int(payload.BaseFeePerGas).ToBig() // Convert slice of hexutil.Bytes to a slice of slice of bytes transactions := make([]hexutil.Bytes, len(payload.Transactions)) for i, transaction := range payload.Transactions { transactions[i] = transaction } - return &ExecutionPayloadV1{ + + res := &ExecutionPayload{ ParentHash: gointerfaces.ConvertH256ToHash(payload.ParentHash), FeeRecipient: gointerfaces.ConvertH160toAddress(payload.Coinbase), StateRoot: gointerfaces.ConvertH256ToHash(payload.StateRoot), @@ -383,30 +274,29 @@ func (e *EngineImpl) GetPayloadV1(ctx context.Context, payloadID hexutil.Bytes) BaseFeePerGas: (*hexutil.Big)(baseFee), BlockHash: gointerfaces.ConvertH256ToHash(payload.BlockHash), Transactions: transactions, - }, nil + } + if payload.Version >= 2 { + res.Withdrawals = privateapi.ConvertWithdrawalsFromRpc(payload.Withdrawals) + } + + return res } -func getTxValueForBlockValue(transaction []byte, baseFee *big.Int) (*big.Int, error) { - // calculate blockValue by summing tips - see: https://github.com/ethereum/execution-apis/pull/314 - s := rlp.NewStream(bytes.NewReader(transaction), uint64(len(transaction))) - t, err := types.DecodeTransaction(s) +func (e *EngineImpl) GetPayloadV1(ctx context.Context, payloadID hexutil.Bytes) (*ExecutionPayload, error) { + if e.internalCL { + log.Error("EXTERNAL CONSENSUS LAYER IS NOT ENABLED, PLEASE RESTART WITH FLAG --externalcl") + return nil, fmt.Errorf("engine api should not be used, restart with --externalcl") + } + + decodedPayloadId := binary.BigEndian.Uint64(payloadID) + log.Info("Received GetPayloadV1", "payloadId", decodedPayloadId) + + response, err := e.api.EngineGetPayload(ctx, decodedPayloadId) if err != nil { - log.Error("Failed to decode transaction", "err", err) return nil, err } - // convert baseFee to uint256 - baseFeeUint256, overflow := uint256.FromBig(baseFee) - if overflow { - log.Warn("baseFee overflow") - return nil, fmt.Errorf("baseFee overflow") - } - - effectiveTip := t.GetEffectiveGasTip(baseFeeUint256) - amount := new(uint256.Int).SetUint64(t.GetGas()) - amount.Mul(amount, effectiveTip) // gasUsed * effectiveTip = how much goes to the block producer (miner, validator) - - return amount.ToBig(), nil + return convertPayloadFromRpc(response.ExecutionPayload), nil } func (e *EngineImpl) GetPayloadV2(ctx context.Context, payloadID hexutil.Bytes) (*GetPayloadV2Response, error) { @@ -418,47 +308,13 @@ func (e *EngineImpl) GetPayloadV2(ctx context.Context, payloadID hexutil.Bytes) decodedPayloadId := binary.BigEndian.Uint64(payloadID) log.Info("Received GetPayloadV2", "payloadId", decodedPayloadId) - ep, err := e.api.EngineGetPayloadV2(ctx, decodedPayloadId) + response, err := e.api.EngineGetPayload(ctx, decodedPayloadId) if err != nil { return nil, err } - payload := ep.Payload - var bloom types.Bloom = gointerfaces.ConvertH2048ToBloom(payload.LogsBloom) - - var baseFee *big.Int - if payload.BaseFeePerGas != nil { - baseFee = gointerfaces.ConvertH256ToUint256Int(payload.BaseFeePerGas).ToBig() - } - - blockValue := big.NewInt(0) - transactions := make([]hexutil.Bytes, len(payload.Transactions)) - for i, transaction := range payload.Transactions { - transactions[i] = transaction - txVal, err := getTxValueForBlockValue(transaction, baseFee) - if err != nil { - return nil, err - } - blockValue.Add(blockValue, txVal) - } - - epl := ExecutionPayloadV2{ - ParentHash: gointerfaces.ConvertH256ToHash(payload.ParentHash), - FeeRecipient: gointerfaces.ConvertH160toAddress(payload.Coinbase), - StateRoot: gointerfaces.ConvertH256ToHash(payload.StateRoot), - ReceiptsRoot: gointerfaces.ConvertH256ToHash(payload.ReceiptRoot), - LogsBloom: bloom[:], - PrevRandao: gointerfaces.ConvertH256ToHash(payload.PrevRandao), - BlockNumber: hexutil.Uint64(payload.BlockNumber), - GasLimit: hexutil.Uint64(payload.GasLimit), - GasUsed: hexutil.Uint64(payload.GasUsed), - Timestamp: hexutil.Uint64(payload.Timestamp), - ExtraData: payload.ExtraData, - BaseFeePerGas: (*hexutil.Big)(baseFee), - BlockHash: gointerfaces.ConvertH256ToHash(payload.BlockHash), - Transactions: transactions, - Withdrawals: privateapi.ConvertWithdrawalsFromRpc(ep.Withdrawals), - } + epl := convertPayloadFromRpc(response.ExecutionPayload) + blockValue := gointerfaces.ConvertH256ToUint256Int(response.BlockValue).ToBig() return &GetPayloadV2Response{ epl, (*hexutil.Big)(blockValue), diff --git a/cmd/rpcdaemon/rpcservices/eth_backend.go b/cmd/rpcdaemon/rpcservices/eth_backend.go index db67e6a4f..9fa5510f5 100644 --- a/cmd/rpcdaemon/rpcservices/eth_backend.go +++ b/cmd/rpcdaemon/rpcservices/eth_backend.go @@ -198,30 +198,16 @@ func (back *RemoteBackend) TxnByIdxInBlock(ctx context.Context, tx kv.Getter, bl return back.blockReader.TxnByIdxInBlock(ctx, tx, blockNum, i) } -func (back *RemoteBackend) EngineNewPayloadV1(ctx context.Context, payload *types2.ExecutionPayload) (res *remote.EnginePayloadStatus, err error) { - return back.remoteEthBackend.EngineNewPayloadV1(ctx, payload) +func (back *RemoteBackend) EngineNewPayload(ctx context.Context, payload *types2.ExecutionPayload) (res *remote.EnginePayloadStatus, err error) { + return back.remoteEthBackend.EngineNewPayload(ctx, payload) } -func (back *RemoteBackend) EngineNewPayloadV2(ctx context.Context, payload *types2.ExecutionPayloadV2) (res *remote.EnginePayloadStatus, err error) { - return back.remoteEthBackend.EngineNewPayloadV2(ctx, payload) +func (back *RemoteBackend) EngineForkchoiceUpdated(ctx context.Context, request *remote.EngineForkChoiceUpdatedRequest) (*remote.EngineForkChoiceUpdatedResponse, error) { + return back.remoteEthBackend.EngineForkChoiceUpdated(ctx, request) } -func (back *RemoteBackend) EngineForkchoiceUpdatedV1(ctx context.Context, request *remote.EngineForkChoiceUpdatedRequest) (*remote.EngineForkChoiceUpdatedReply, error) { - return back.remoteEthBackend.EngineForkChoiceUpdatedV1(ctx, request) -} - -func (back *RemoteBackend) EngineForkchoiceUpdatedV2(ctx context.Context, request *remote.EngineForkChoiceUpdatedRequestV2) (*remote.EngineForkChoiceUpdatedReply, error) { - return back.remoteEthBackend.EngineForkChoiceUpdatedV2(ctx, request) -} - -func (back *RemoteBackend) EngineGetPayloadV1(ctx context.Context, payloadId uint64) (res *types2.ExecutionPayload, err error) { - return back.remoteEthBackend.EngineGetPayloadV1(ctx, &remote.EngineGetPayloadRequest{ - PayloadId: payloadId, - }) -} - -func (back *RemoteBackend) EngineGetPayloadV2(ctx context.Context, payloadId uint64) (res *types2.ExecutionPayloadV2, err error) { - return back.remoteEthBackend.EngineGetPayloadV2(ctx, &remote.EngineGetPayloadRequest{ +func (back *RemoteBackend) EngineGetPayload(ctx context.Context, payloadId uint64) (res *remote.EngineGetPayloadResponse, err error) { + return back.remoteEthBackend.EngineGetPayload(ctx, &remote.EngineGetPayloadRequest{ PayloadId: payloadId, }) } diff --git a/ethdb/privateapi/engine_test.go b/ethdb/privateapi/engine_test.go index b424054a3..c5f25a0bf 100644 --- a/ethdb/privateapi/engine_test.go +++ b/ethdb/privateapi/engine_test.go @@ -103,7 +103,7 @@ func TestMockDownloadRequest(t *testing.T) { done := make(chan bool) go func() { - reply, err = backend.EngineNewPayloadV1(ctx, mockPayload1) + reply, err = backend.EngineNewPayload(ctx, mockPayload1) done <- true }() @@ -116,7 +116,7 @@ func TestMockDownloadRequest(t *testing.T) { // If we get another request we don't need to process it with processDownloadCh and ignore it and return Syncing status go func() { - reply, err = backend.EngineNewPayloadV1(ctx, mockPayload2) + reply, err = backend.EngineNewPayload(ctx, mockPayload2) done <- true }() @@ -133,7 +133,7 @@ func TestMockDownloadRequest(t *testing.T) { _ = tx.Commit() // Now we try to sync the next payload again go func() { - reply, err = backend.EngineNewPayloadV1(ctx, mockPayload2) + reply, err = backend.EngineNewPayload(ctx, mockPayload2) done <- true }() @@ -162,7 +162,7 @@ func TestMockValidExecution(t *testing.T) { done := make(chan bool) go func() { - reply, err = backend.EngineNewPayloadV1(ctx, mockPayload3) + reply, err = backend.EngineNewPayload(ctx, mockPayload3) done <- true }() @@ -198,7 +198,7 @@ func TestMockInvalidExecution(t *testing.T) { done := make(chan bool) go func() { - reply, err = backend.EngineNewPayloadV1(ctx, mockPayload3) + reply, err = backend.EngineNewPayload(ctx, mockPayload3) done <- true }() @@ -233,7 +233,7 @@ func TestNoTTD(t *testing.T) { done := make(chan bool) go func() { - _, err = backend.EngineNewPayloadV1(ctx, &types2.ExecutionPayload{ + _, err = backend.EngineNewPayload(ctx, &types2.ExecutionPayload{ ParentHash: gointerfaces.ConvertHashToH256(libcommon.HexToHash("0x2")), BlockHash: gointerfaces.ConvertHashToH256(libcommon.HexToHash("0xe6a580606b065e08034dcd6eea026cfdcbd3b41918d98b41cb9bf797d0c27033")), ReceiptRoot: gointerfaces.ConvertHashToH256(libcommon.HexToHash("0x4")), diff --git a/ethdb/privateapi/ethbackend.go b/ethdb/privateapi/ethbackend.go index 6a981baf2..43e576e61 100644 --- a/ethdb/privateapi/ethbackend.go +++ b/ethdb/privateapi/ethbackend.go @@ -282,16 +282,8 @@ func (s *EthBackendServer) stageLoopIsBusy() bool { return !s.hd.BeaconRequestList.IsWaiting() } -func (s *EthBackendServer) EngineNewPayloadV1(ctx context.Context, req *types2.ExecutionPayload) (*remote.EnginePayloadStatus, error) { - return s.engineNewPayload(req, nil) -} - -func (s *EthBackendServer) EngineNewPayloadV2(ctx context.Context, req *types2.ExecutionPayloadV2) (*remote.EnginePayloadStatus, error) { - return s.engineNewPayload(req.Payload, ConvertWithdrawalsFromRpc(req.Withdrawals)) -} - -// engineNewPayload validates and possibly executes payload -func (s *EthBackendServer) engineNewPayload(req *types2.ExecutionPayload, withdrawals []*types.Withdrawal) (*remote.EnginePayloadStatus, error) { +// EngineNewPayload validates and possibly executes payload +func (s *EthBackendServer) EngineNewPayload(ctx context.Context, req *types2.ExecutionPayload) (*remote.EnginePayloadStatus, error) { header := types.Header{ ParentHash: gointerfaces.ConvertH256ToHash(req.ParentHash), Coinbase: gointerfaces.ConvertH160toAddress(req.Coinbase), @@ -310,6 +302,11 @@ func (s *EthBackendServer) engineNewPayload(req *types2.ExecutionPayload, withdr ReceiptHash: gointerfaces.ConvertH256ToHash(req.ReceiptRoot), TxHash: types.DeriveSha(types.BinaryTransactions(req.Transactions)), } + var withdrawals []*types.Withdrawal + if req.Version >= 2 { + withdrawals = ConvertWithdrawalsFromRpc(req.Withdrawals) + } + if withdrawals != nil { wh := types.DeriveSha(types.Withdrawals(withdrawals)) header.WithdrawalsHash = &wh @@ -503,28 +500,26 @@ func (s *EthBackendServer) getQuickPayloadStatusIfPossible(blockHash libcommon.H return nil, nil } -func (s *EthBackendServer) EngineGetPayloadV1(ctx context.Context, req *remote.EngineGetPayloadRequest) (*types2.ExecutionPayload, error) { - _, payload, err := s.engineGetPayload(req) - return payload, err -} - -func (s *EthBackendServer) EngineGetPayloadV2(ctx context.Context, req *remote.EngineGetPayloadRequest) (*types2.ExecutionPayloadV2, error) { - block, payload, err := s.engineGetPayload(req) - if err != nil { - return nil, err +// The expected value to be received by the feeRecipient in wei +func blockValue(block *types.Block, baseFee *uint256.Int) *uint256.Int { + blockValue := uint256.NewInt(0) + for _, tx := range block.Transactions() { + gas := new(uint256.Int).SetUint64(tx.GetGas()) + effectiveTip := tx.GetEffectiveGasTip(baseFee) + txValue := new(uint256.Int).Mul(gas, effectiveTip) + blockValue.Add(blockValue, txValue) } - withdrawals := ConvertWithdrawalsToRpc(block.Withdrawals()) - return &types2.ExecutionPayloadV2{Payload: payload, Withdrawals: withdrawals}, nil + return blockValue } -// engineGetPayload retrieves previously assembled payload (Validators only) -func (s *EthBackendServer) engineGetPayload(req *remote.EngineGetPayloadRequest) (*types.Block, *types2.ExecutionPayload, error) { +// EngineGetPayload retrieves previously assembled payload (Validators only) +func (s *EthBackendServer) EngineGetPayload(ctx context.Context, req *remote.EngineGetPayloadRequest) (*remote.EngineGetPayloadResponse, error) { if !s.proposing { - return nil, nil, fmt.Errorf("execution layer not running as a proposer. enable proposer by taking out the --proposer.disable flag on startup") + return nil, fmt.Errorf("execution layer not running as a proposer. enable proposer by taking out the --proposer.disable flag on startup") } if s.config.TerminalTotalDifficulty == nil { - return nil, nil, fmt.Errorf("not a proof-of-stake chain") + return nil, fmt.Errorf("not a proof-of-stake chain") } log.Debug("[GetPayload] acquiring lock") @@ -535,28 +530,25 @@ func (s *EthBackendServer) engineGetPayload(req *remote.EngineGetPayloadRequest) builder, ok := s.builders[req.PayloadId] if !ok { log.Warn("Payload not stored", "payloadId", req.PayloadId) - return nil, nil, &UnknownPayloadErr + return nil, &UnknownPayloadErr } block, err := builder.Stop() if err != nil { log.Error("Failed to build PoS block", "err", err) - return nil, nil, err + return nil, err } - var baseFeeReply *types2.H256 - if block.Header().BaseFee != nil { - var baseFee uint256.Int - baseFee.SetFromBig(block.Header().BaseFee) - baseFeeReply = gointerfaces.ConvertUint256IntToH256(&baseFee) - } + baseFee := new(uint256.Int) + baseFee.SetFromBig(block.Header().BaseFee) encodedTransactions, err := types.MarshalTransactionsBinary(block.Transactions()) if err != nil { - return nil, nil, err + return nil, err } - return block, &types2.ExecutionPayload{ + payload := &types2.ExecutionPayload{ + Version: 1, ParentHash: gointerfaces.ConvertHashToH256(block.Header().ParentHash), Coinbase: gointerfaces.ConvertAddressToH160(block.Header().Coinbase), Timestamp: block.Header().Time, @@ -568,28 +560,29 @@ func (s *EthBackendServer) engineGetPayload(req *remote.EngineGetPayloadRequest) GasUsed: block.GasUsed(), BlockNumber: block.NumberU64(), ExtraData: block.Extra(), - BaseFeePerGas: baseFeeReply, + BaseFeePerGas: gointerfaces.ConvertUint256IntToH256(baseFee), BlockHash: gointerfaces.ConvertHashToH256(block.Header().Hash()), Transactions: encodedTransactions, + } + if block.Withdrawals() != nil { + payload.Version = 2 + payload.Withdrawals = ConvertWithdrawalsToRpc(block.Withdrawals()) + } + + blockValue := blockValue(block, baseFee) + return &remote.EngineGetPayloadResponse{ + ExecutionPayload: payload, + BlockValue: gointerfaces.ConvertUint256IntToH256(blockValue), }, nil } -func (s *EthBackendServer) EngineForkChoiceUpdatedV1(ctx context.Context, req *remote.EngineForkChoiceUpdatedRequest) (*remote.EngineForkChoiceUpdatedReply, error) { - return s.engineForkChoiceUpdated(ctx, req.ForkchoiceState, req.PayloadAttributes, nil) -} - -func (s *EthBackendServer) EngineForkChoiceUpdatedV2(ctx context.Context, req *remote.EngineForkChoiceUpdatedRequestV2) (*remote.EngineForkChoiceUpdatedReply, error) { - return s.engineForkChoiceUpdated(ctx, req.ForkchoiceState, req.PayloadAttributes.GetAttributes(), req.PayloadAttributes.GetWithdrawals()) -} - // engineForkChoiceUpdated either states new block head or request the assembling of a new block -func (s *EthBackendServer) engineForkChoiceUpdated(ctx context.Context, reqForkchoice *remote.EngineForkChoiceState, - payloadAttributes *remote.EnginePayloadAttributes, reqWithdrawals []*types2.Withdrawal, -) (*remote.EngineForkChoiceUpdatedReply, error) { +func (s *EthBackendServer) EngineForkChoiceUpdated(ctx context.Context, req *remote.EngineForkChoiceUpdatedRequest, +) (*remote.EngineForkChoiceUpdatedResponse, error) { forkChoice := engineapi.ForkChoiceMessage{ - HeadBlockHash: gointerfaces.ConvertH256ToHash(reqForkchoice.HeadBlockHash), - SafeBlockHash: gointerfaces.ConvertH256ToHash(reqForkchoice.SafeBlockHash), - FinalizedBlockHash: gointerfaces.ConvertH256ToHash(reqForkchoice.FinalizedBlockHash), + HeadBlockHash: gointerfaces.ConvertH256ToHash(req.ForkchoiceState.HeadBlockHash), + SafeBlockHash: gointerfaces.ConvertH256ToHash(req.ForkchoiceState.SafeBlockHash), + FinalizedBlockHash: gointerfaces.ConvertH256ToHash(req.ForkchoiceState.FinalizedBlockHash), } status, err := s.getQuickPayloadStatusIfPossible(forkChoice.HeadBlockHash, 0, libcommon.Hash{}, &forkChoice, false) @@ -614,8 +607,9 @@ func (s *EthBackendServer) engineForkChoiceUpdated(ctx context.Context, reqForkc } // No need for payload building + payloadAttributes := req.PayloadAttributes if payloadAttributes == nil || status.Status != remote.EngineStatus_VALID { - return &remote.EngineForkChoiceUpdatedReply{PayloadStatus: convertPayloadStatus(status)}, nil + return &remote.EngineForkChoiceUpdatedResponse{PayloadStatus: convertPayloadStatus(status)}, nil } if !s.proposing { @@ -642,15 +636,26 @@ func (s *EthBackendServer) engineForkChoiceUpdated(ctx context.Context, reqForkc log.Warn("Skipping payload building because forkchoiceState.headBlockHash is not the head of the canonical chain", "forkChoice.HeadBlockHash", forkChoice.HeadBlockHash, "headHeader.Hash", headHeader.Hash()) - return &remote.EngineForkChoiceUpdatedReply{PayloadStatus: convertPayloadStatus(status)}, nil + return &remote.EngineForkChoiceUpdatedResponse{PayloadStatus: convertPayloadStatus(status)}, nil } if headHeader.Time >= payloadAttributes.Timestamp { return nil, &InvalidPayloadAttributesErr } - // If pre-Shanghai and there are withdrawals, we should error - if !s.config.IsShanghai(headHeader.Time) && reqWithdrawals != nil { + param := core.BlockBuilderParameters{ + ParentHash: forkChoice.HeadBlockHash, + Timestamp: payloadAttributes.Timestamp, + PrevRandao: gointerfaces.ConvertH256ToHash(payloadAttributes.PrevRandao), + SuggestedFeeRecipient: gointerfaces.ConvertH160toAddress(payloadAttributes.SuggestedFeeRecipient), + PayloadId: s.payloadId, + } + if payloadAttributes.Version >= 2 { + param.Withdrawals = ConvertWithdrawalsFromRpc(payloadAttributes.Withdrawals) + } + + if (!s.config.IsShanghai(payloadAttributes.Timestamp) && param.Withdrawals != nil) || + (s.config.IsShanghai(payloadAttributes.Timestamp) && param.Withdrawals == nil) { return nil, &InvalidParamsErr } @@ -661,19 +666,10 @@ func (s *EthBackendServer) engineForkChoiceUpdated(ctx context.Context, reqForkc // payload IDs start from 1 (0 signifies null) s.payloadId++ - param := core.BlockBuilderParameters{ - ParentHash: forkChoice.HeadBlockHash, - Timestamp: payloadAttributes.Timestamp, - PrevRandao: gointerfaces.ConvertH256ToHash(payloadAttributes.PrevRandao), - SuggestedFeeRecipient: gointerfaces.ConvertH160toAddress(payloadAttributes.SuggestedFeeRecipient), - Withdrawals: ConvertWithdrawalsFromRpc(reqWithdrawals), - PayloadId: s.payloadId, - } - s.builders[s.payloadId] = builder.NewBlockBuilder(s.builderFunc, ¶m) log.Debug("BlockBuilder added", "payload", s.payloadId) - return &remote.EngineForkChoiceUpdatedReply{ + return &remote.EngineForkChoiceUpdatedResponse{ PayloadStatus: &remote.EnginePayloadStatus{ Status: remote.EngineStatus_VALID, LatestValidHash: gointerfaces.ConvertHashToH256(headHash), diff --git a/go.mod b/go.mod index cfd6e8bda..cd099a1fa 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/ledgerwatch/erigon go 1.18 require ( - github.com/ledgerwatch/erigon-lib v0.0.0-20230120091748-2ea0cb82e919 + github.com/ledgerwatch/erigon-lib v0.0.0-20230120150133-840f3ae632dc github.com/ledgerwatch/erigon-snapshot v1.1.1-0.20230120022649-cd9409a200da github.com/ledgerwatch/log/v3 v3.7.0 github.com/ledgerwatch/secp256k1 v1.0.0 diff --git a/go.sum b/go.sum index f1aa3e495..15018a434 100644 --- a/go.sum +++ b/go.sum @@ -565,8 +565,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758 h1:0D5M2HQSGD3PYPwICLl+/9oulQauOuETfgFvhBDffs0= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/ledgerwatch/erigon-lib v0.0.0-20230120091748-2ea0cb82e919 h1:w9D+tV8V29cOk6v9iKNREb06cUtkqHB893wkoDw/GJo= -github.com/ledgerwatch/erigon-lib v0.0.0-20230120091748-2ea0cb82e919/go.mod h1:GAsj4lIZdXUGf0oSy6jSoG6nFZ+B0FK1KlcfR6JUuDY= +github.com/ledgerwatch/erigon-lib v0.0.0-20230120150133-840f3ae632dc h1:/omu+iTyFTEt36w8PIOOkqz7EN/820EVUHjAZHtii74= +github.com/ledgerwatch/erigon-lib v0.0.0-20230120150133-840f3ae632dc/go.mod h1:hItoXGcvka8hN2gi8JO7YPAQ+jIyFzrXqCPuzo6k140= github.com/ledgerwatch/erigon-snapshot v1.1.1-0.20230120022649-cd9409a200da h1:lQQBOHzAUThkymfXJj/m07vAjyMx9XoMMy3OomaeOrA= github.com/ledgerwatch/erigon-snapshot v1.1.1-0.20230120022649-cd9409a200da/go.mod h1:3AuPxZc85jkehh/HA9h8gabv5MSi3kb/ddtzBsTVJFo= github.com/ledgerwatch/log/v3 v3.7.0 h1:aFPEZdwZx4jzA3+/Pf8wNDN5tCI0cIolq/kfvgcM+og= diff --git a/turbo/rpchelper/interface.go b/turbo/rpchelper/interface.go index e1408a974..22055c387 100644 --- a/turbo/rpchelper/interface.go +++ b/turbo/rpchelper/interface.go @@ -25,12 +25,9 @@ type ApiBackend interface { Subscribe(ctx context.Context, cb func(*remote.SubscribeReply)) error SubscribeLogs(ctx context.Context, cb func(*remote.SubscribeLogsReply), requestor *atomic.Value) error BlockWithSenders(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (block *types.Block, senders []libcommon.Address, err error) - EngineNewPayloadV1(ctx context.Context, payload *types2.ExecutionPayload) (*remote.EnginePayloadStatus, error) - EngineNewPayloadV2(ctx context.Context, payload *types2.ExecutionPayloadV2) (*remote.EnginePayloadStatus, error) - EngineForkchoiceUpdatedV1(ctx context.Context, request *remote.EngineForkChoiceUpdatedRequest) (*remote.EngineForkChoiceUpdatedReply, error) - EngineForkchoiceUpdatedV2(ctx context.Context, request *remote.EngineForkChoiceUpdatedRequestV2) (*remote.EngineForkChoiceUpdatedReply, error) - EngineGetPayloadV1(ctx context.Context, payloadId uint64) (*types2.ExecutionPayload, error) - EngineGetPayloadV2(ctx context.Context, payloadId uint64) (*types2.ExecutionPayloadV2, error) + EngineNewPayload(ctx context.Context, payload *types2.ExecutionPayload) (*remote.EnginePayloadStatus, error) + EngineForkchoiceUpdated(ctx context.Context, request *remote.EngineForkChoiceUpdatedRequest) (*remote.EngineForkChoiceUpdatedResponse, error) + EngineGetPayload(ctx context.Context, payloadId uint64) (*remote.EngineGetPayloadResponse, error) NodeInfo(ctx context.Context, limit uint32) ([]p2p.NodeInfo, error) Peers(ctx context.Context) ([]*p2p.PeerInfo, error) PendingBlock(ctx context.Context) (*types.Block, error)