Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

engine payload bodies rpc endpoints #6644

Merged
merged 1 commit into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions cmd/rpcdaemon/commands/engine_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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{
Expand Down
8 changes: 8 additions & 0 deletions cmd/rpcdaemon/rpcservices/eth_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
91 changes: 91 additions & 0 deletions ethdb/privateapi/ethbackend.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package privateapi

import (
"bytes"
"context"
"errors"
"fmt"
Expand Down Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing null blocks (blocks after the latest available) should be truncated (see Item 3 of the spec). In our database we don't allow any gaps in canonical blocks, so it's OK to truncate starting from the first missing block.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK good to know. There were some comments on this when I initially looked at the PR, looks like that has been finalised now so will update our code.

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)

Expand Down
2 changes: 2 additions & 0 deletions turbo/rpchelper/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}