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.
This commit is contained in:
hexoscott 2023-01-24 13:37:03 +01:00 committed by GitHub
parent 49f8d4c4ad
commit 4dcba50e99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 148 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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