Unify Engine API V1 & V2 methods (#6638)

Reduce code duplication.

Prerequisite: https://github.com/ledgerwatch/erigon-lib/pull/841.
This commit is contained in:
Andrew Ashikhmin 2023-01-20 18:43:08 +01:00 committed by GitHub
parent b71725ecb3
commit 9927ca3171
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 165 additions and 321 deletions

View File

@ -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,

View File

@ -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),

View File

@ -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,
})
}

View File

@ -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")),

View File

@ -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, &param)
log.Debug("BlockBuilder added", "payload", s.payloadId)
return &remote.EngineForkChoiceUpdatedReply{
return &remote.EngineForkChoiceUpdatedResponse{
PayloadStatus: &remote.EnginePayloadStatus{
Status: remote.EngineStatus_VALID,
LatestValidHash: gointerfaces.ConvertHashToH256(headHash),

2
go.mod
View File

@ -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

4
go.sum
View File

@ -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=

View File

@ -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)