From 4dcba50e99925be01e14494a79ea17ad451ef2a0 Mon Sep 17 00:00:00 2001 From: hexoscott <70711990+hexoscott@users.noreply.github.com> Date: Tue, 24 Jan 2023 13:37:03 +0100 Subject: [PATCH] engine payload bodies rpc endpoints (#6644) Very basic implementation for get payload bodies rpc calls. Once we have Hive tests for these calls I can pick this back up and work through any issues. Implementation of https://github.com/ethereum/execution-apis/pull/352. --- cmd/rpcdaemon/commands/engine_api.go | 47 ++++++++++++ cmd/rpcdaemon/rpcservices/eth_backend.go | 8 +++ ethdb/privateapi/ethbackend.go | 91 ++++++++++++++++++++++++ turbo/rpchelper/interface.go | 2 + 4 files changed, 148 insertions(+) diff --git a/cmd/rpcdaemon/commands/engine_api.go b/cmd/rpcdaemon/commands/engine_api.go index 6ae11e3ac..533f2e1a3 100644 --- a/cmd/rpcdaemon/commands/engine_api.go +++ b/cmd/rpcdaemon/commands/engine_api.go @@ -69,6 +69,11 @@ type TransitionConfiguration struct { TerminalBlockNumber *hexutil.Big `json:"terminalBlockNumber" gencodec:"required"` } +type ExecutionPayloadBodyV1 struct { + Transactions [][]byte `json:"transactions" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals" gencodec:"required"` +} + // EngineAPI Beacon chain communication endpoint type EngineAPI interface { NewPayloadV1(context.Context, *ExecutionPayload) (map[string]interface{}, error) @@ -78,6 +83,8 @@ type EngineAPI interface { 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) + GetPayloadBodiesByHashV1(ctx context.Context, hashes []libcommon.Hash) ([]*ExecutionPayloadBodyV1, error) + GetPayloadBodiesByRangeV1(ctx context.Context, start uint64, count uint64) ([]*ExecutionPayloadBodyV1, error) } // EngineImpl is implementation of the EngineAPI interface @@ -358,6 +365,46 @@ func (e *EngineImpl) ExchangeTransitionConfigurationV1(ctx context.Context, beac }, nil } +func (e *EngineImpl) GetPayloadBodiesByHashV1(ctx context.Context, hashes []libcommon.Hash) ([]*ExecutionPayloadBodyV1, error) { + h := make([]*types2.H256, len(hashes)) + for i, hash := range hashes { + h[i] = gointerfaces.ConvertHashToH256(hash) + } + + apiRes, err := e.api.EngineGetPayloadBodiesByHashV1(ctx, &remote.EngineGetPayloadBodiesByHashV1Request{Hashes: h}) + if err != nil { + return nil, err + } + + return convertExecutionPayloadV1(apiRes), nil +} + +func (e *EngineImpl) GetPayloadBodiesByRangeV1(ctx context.Context, start uint64, count uint64) ([]*ExecutionPayloadBodyV1, error) { + apiRes, err := e.api.EngineGetPayloadBodiesByRangeV1(ctx, &remote.EngineGetPayloadBodiesByRangeV1Request{Start: start, Count: count}) + if err != nil { + return nil, err + } + + return convertExecutionPayloadV1(apiRes), nil +} + +func convertExecutionPayloadV1(response *remote.EngineGetPayloadBodiesV1Response) []*ExecutionPayloadBodyV1 { + result := make([]*ExecutionPayloadBodyV1, len(response.Bodies)) + for idx, body := range response.Bodies { + if body == nil { + result[idx] = nil + } else { + pl := &ExecutionPayloadBodyV1{ + Transactions: body.Transactions, + Withdrawals: privateapi.ConvertWithdrawalsFromRpc(body.Withdrawals), + } + result[idx] = pl + } + } + + return result +} + // NewEngineAPI returns EngineImpl instance func NewEngineAPI(base *BaseAPI, db kv.RoDB, api rpchelper.ApiBackend, internalCL bool) *EngineImpl { return &EngineImpl{ diff --git a/cmd/rpcdaemon/rpcservices/eth_backend.go b/cmd/rpcdaemon/rpcservices/eth_backend.go index 9fa5510f5..bbd0e4f7a 100644 --- a/cmd/rpcdaemon/rpcservices/eth_backend.go +++ b/cmd/rpcdaemon/rpcservices/eth_backend.go @@ -212,6 +212,14 @@ func (back *RemoteBackend) EngineGetPayload(ctx context.Context, payloadId uint6 }) } +func (back *RemoteBackend) EngineGetPayloadBodiesByHashV1(ctx context.Context, request *remote.EngineGetPayloadBodiesByHashV1Request) (*remote.EngineGetPayloadBodiesV1Response, error) { + return back.remoteEthBackend.EngineGetPayloadBodiesByHashV1(ctx, request) +} + +func (back *RemoteBackend) EngineGetPayloadBodiesByRangeV1(ctx context.Context, request *remote.EngineGetPayloadBodiesByRangeV1Request) (*remote.EngineGetPayloadBodiesV1Response, error) { + return back.remoteEthBackend.EngineGetPayloadBodiesByRangeV1(ctx, request) +} + func (back *RemoteBackend) NodeInfo(ctx context.Context, limit uint32) ([]p2p.NodeInfo, error) { nodes, err := back.remoteEthBackend.NodeInfo(ctx, &remote.NodesInfoRequest{Limit: limit}) if err != nil { diff --git a/ethdb/privateapi/ethbackend.go b/ethdb/privateapi/ethbackend.go index ae8711c63..b93ace6f9 100644 --- a/ethdb/privateapi/ethbackend.go +++ b/ethdb/privateapi/ethbackend.go @@ -1,6 +1,7 @@ package privateapi import ( + "bytes" "context" "errors" "fmt" @@ -679,6 +680,96 @@ func (s *EthBackendServer) EngineForkChoiceUpdated(ctx context.Context, req *rem }, nil } +func (s *EthBackendServer) EngineGetPayloadBodiesByHashV1(ctx context.Context, request *remote.EngineGetPayloadBodiesByHashV1Request) (*remote.EngineGetPayloadBodiesV1Response, error) { + tx, err := s.db.BeginRo(ctx) + if err != nil { + return nil, err + } + + bodies := make([]*types2.ExecutionPayloadBodyV1, len(request.Hashes)) + + for hashIdx, hash := range request.Hashes { + h := gointerfaces.ConvertH256ToHash(hash) + block, err := rawdb.ReadBlockByHash(tx, h) + if err != nil { + return nil, err + } + + body, err := extractPayloadBodyFromBlock(block) + if err != nil { + return nil, err + } + bodies[hashIdx] = body + } + + return &remote.EngineGetPayloadBodiesV1Response{Bodies: bodies}, nil +} + +func (s *EthBackendServer) EngineGetPayloadBodiesByRangeV1(ctx context.Context, request *remote.EngineGetPayloadBodiesByRangeV1Request) (*remote.EngineGetPayloadBodiesV1Response, error) { + tx, err := s.db.BeginRo(ctx) + if err != nil { + return nil, err + } + + bodies := make([]*types2.ExecutionPayloadBodyV1, request.Count) + + var i uint64 + for i = 0; i < request.Count; i++ { + block, err := rawdb.ReadBlockByNumber(tx, request.Start+i) + if err != nil { + return nil, err + } + body, err := extractPayloadBodyFromBlock(block) + if err != nil { + return nil, err + } + if body == nil { + // break early if the body is nil to trim the response. A missing body indicates we don't have the + // canonical block so can just stop outputting from here + break + } + bodies[i] = body + } + + return &remote.EngineGetPayloadBodiesV1Response{Bodies: bodies}, nil +} + +func extractPayloadBodyFromBlock(block *types.Block) (*types2.ExecutionPayloadBodyV1, error) { + if block == nil { + return nil, nil + } + + txs := block.Transactions() + bdTxs := make([][]byte, len(txs)) + for idx, tx := range txs { + var buf bytes.Buffer + if err := tx.MarshalBinary(&buf); err != nil { + return nil, err + } else { + bdTxs[idx] = buf.Bytes() + } + } + + wds := block.Withdrawals() + bdWds := make([]*types2.Withdrawal, len(wds)) + + if wds == nil { + // pre shanghai blocks could have nil withdrawals so nil the slice as per spec + bdWds = nil + } else { + for idx, wd := range wds { + bdWds[idx] = &types2.Withdrawal{ + Index: wd.Index, + ValidatorIndex: wd.Validator, + Address: gointerfaces.ConvertAddressToH160(wd.Address), + Amount: wd.Amount, + } + } + } + + return &types2.ExecutionPayloadBodyV1{Transactions: bdTxs, Withdrawals: bdWds}, nil +} + func (s *EthBackendServer) evictOldBuilders() { ids := common.SortedKeys(s.builders) diff --git a/turbo/rpchelper/interface.go b/turbo/rpchelper/interface.go index 22055c387..9daee67c1 100644 --- a/turbo/rpchelper/interface.go +++ b/turbo/rpchelper/interface.go @@ -31,4 +31,6 @@ type ApiBackend interface { NodeInfo(ctx context.Context, limit uint32) ([]p2p.NodeInfo, error) Peers(ctx context.Context) ([]*p2p.PeerInfo, error) PendingBlock(ctx context.Context) (*types.Block, error) + EngineGetPayloadBodiesByHashV1(ctx context.Context, request *remote.EngineGetPayloadBodiesByHashV1Request) (*remote.EngineGetPayloadBodiesV1Response, error) + EngineGetPayloadBodiesByRangeV1(ctx context.Context, request *remote.EngineGetPayloadBodiesByRangeV1Request) (*remote.EngineGetPayloadBodiesV1Response, error) }