From 2fb4ddcbe747de84b7912239bcf7399840c22643 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 24 Feb 2022 11:35:01 -0800 Subject: [PATCH] Engine API: add payload status handling and tests (#10282) * Add status handling and tests * Update client.go * Fmt * Update mock_engine_test.go * Update client_test.go --- beacon-chain/blockchain/mock_engine_test.go | 7 +- .../powchain/engine-api-client/v1/client.go | 49 ++- .../engine-api-client/v1/client_test.go | 407 +++++++++++++----- .../powchain/engine-api-client/v1/errors.go | 8 + 4 files changed, 362 insertions(+), 109 deletions(-) diff --git a/beacon-chain/blockchain/mock_engine_test.go b/beacon-chain/blockchain/mock_engine_test.go index 1a44bdd7b4..b35e04b220 100644 --- a/beacon-chain/blockchain/mock_engine_test.go +++ b/beacon-chain/blockchain/mock_engine_test.go @@ -5,7 +5,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - v1 "github.com/prysmaticlabs/prysm/beacon-chain/powchain/engine-api-client/v1" enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1" ) @@ -13,12 +12,12 @@ type mockEngineService struct { blks map[[32]byte]*enginev1.ExecutionBlock } -func (*mockEngineService) NewPayload(context.Context, *enginev1.ExecutionPayload) (*enginev1.PayloadStatus, error) { +func (*mockEngineService) NewPayload(context.Context, *enginev1.ExecutionPayload) ([]byte, error) { return nil, nil } -func (*mockEngineService) ForkchoiceUpdated(context.Context, *enginev1.ForkchoiceState, *enginev1.PayloadAttributes) (*v1.ForkchoiceUpdatedResponse, error) { - return nil, nil +func (*mockEngineService) ForkchoiceUpdated(context.Context, *enginev1.ForkchoiceState, *enginev1.PayloadAttributes) (*enginev1.PayloadIDBytes, []byte, error) { + return nil, nil, nil } func (*mockEngineService) GetPayloadV1( diff --git a/beacon-chain/powchain/engine-api-client/v1/client.go b/beacon-chain/powchain/engine-api-client/v1/client.go index bb6b150ac4..c619995837 100644 --- a/beacon-chain/powchain/engine-api-client/v1/client.go +++ b/beacon-chain/powchain/engine-api-client/v1/client.go @@ -6,6 +6,7 @@ package v1 import ( "bytes" "context" + "fmt" "math/big" "net/url" "time" @@ -44,10 +45,10 @@ type ForkchoiceUpdatedResponse struct { // EngineCaller defines a client that can interact with an Ethereum // execution node's engine service via JSON-RPC. type EngineCaller interface { - NewPayload(ctx context.Context, payload *pb.ExecutionPayload) (*pb.PayloadStatus, error) + NewPayload(ctx context.Context, payload *pb.ExecutionPayload) ([]byte, error) ForkchoiceUpdated( ctx context.Context, state *pb.ForkchoiceState, attrs *pb.PayloadAttributes, - ) (*ForkchoiceUpdatedResponse, error) + ) (*pb.PayloadIDBytes, []byte, error) GetPayload(ctx context.Context, payloadId [8]byte) (*pb.ExecutionPayload, error) ExchangeTransitionConfiguration( ctx context.Context, cfg *pb.TransitionConfiguration, @@ -93,19 +94,55 @@ func New(ctx context.Context, endpoint string, opts ...Option) (*Client, error) } // NewPayload calls the engine_newPayloadV1 method via JSON-RPC. -func (c *Client) NewPayload(ctx context.Context, payload *pb.ExecutionPayload) (*pb.PayloadStatus, error) { +func (c *Client) NewPayload(ctx context.Context, payload *pb.ExecutionPayload) ([]byte, error) { result := &pb.PayloadStatus{} err := c.rpc.CallContext(ctx, result, NewPayloadMethod, payload) - return result, handleRPCError(err) + if err != nil { + return nil, handleRPCError(err) + } + + switch result.Status { + case pb.PayloadStatus_INVALID_BLOCK_HASH: + return nil, fmt.Errorf("could not validate block hash: %v", result.ValidationError) + case pb.PayloadStatus_INVALID_TERMINAL_BLOCK: + return nil, fmt.Errorf("could not satisfy terminal block condition: %v", result.ValidationError) + case pb.PayloadStatus_ACCEPTED, pb.PayloadStatus_SYNCING: + return nil, ErrAcceptedSyncingPayloadStatus + case pb.PayloadStatus_INVALID: + return result.LatestValidHash, ErrInvalidPayloadStatus + case pb.PayloadStatus_VALID: + return result.LatestValidHash, nil + default: + return nil, ErrUnknownPayloadStatus + } } // ForkchoiceUpdated calls the engine_forkchoiceUpdatedV1 method via JSON-RPC. func (c *Client) ForkchoiceUpdated( ctx context.Context, state *pb.ForkchoiceState, attrs *pb.PayloadAttributes, -) (*ForkchoiceUpdatedResponse, error) { +) (*pb.PayloadIDBytes, []byte, error) { result := &ForkchoiceUpdatedResponse{} err := c.rpc.CallContext(ctx, result, ForkchoiceUpdatedMethod, state, attrs) - return result, handleRPCError(err) + if err != nil { + return nil, nil, handleRPCError(err) + } + + if result.Status == nil { + return nil, nil, ErrNilResponse + } + resp := result.Status + switch resp.Status { + case pb.PayloadStatus_INVALID_TERMINAL_BLOCK: + return nil, nil, fmt.Errorf("could not satisfy terminal block condition: %v", resp.ValidationError) + case pb.PayloadStatus_SYNCING: + return nil, nil, ErrAcceptedSyncingPayloadStatus + case pb.PayloadStatus_INVALID: + return nil, resp.LatestValidHash, ErrInvalidPayloadStatus + case pb.PayloadStatus_VALID: + return result.PayloadId, resp.LatestValidHash, nil + default: + return nil, nil, ErrUnknownPayloadStatus + } } // GetPayload calls the engine_getPayloadV1 method via JSON-RPC. diff --git a/beacon-chain/powchain/engine-api-client/v1/client_test.go b/beacon-chain/powchain/engine-api-client/v1/client_test.go index a980d4532e..b40cc5eb90 100644 --- a/beacon-chain/powchain/engine-api-client/v1/client_test.go +++ b/beacon-chain/powchain/engine-api-client/v1/client_test.go @@ -45,28 +45,19 @@ func TestClient_IPC(t *testing.T) { t.Run(ForkchoiceUpdatedMethod, func(t *testing.T) { want, ok := fix["ForkchoiceUpdatedResponse"].(*ForkchoiceUpdatedResponse) require.Equal(t, true, ok) - resp, err := client.ForkchoiceUpdated(ctx, &pb.ForkchoiceState{}, &pb.PayloadAttributes{}) + payloadID, validHash, err := client.ForkchoiceUpdated(ctx, &pb.ForkchoiceState{}, &pb.PayloadAttributes{}) require.NoError(t, err) - require.DeepEqual(t, want.Status, resp.Status) - require.DeepEqual(t, want.PayloadId, resp.PayloadId) + require.DeepEqual(t, want.Status.LatestValidHash, validHash) + require.DeepEqual(t, want.PayloadId, payloadID) }) t.Run(NewPayloadMethod, func(t *testing.T) { - want, ok := fix["PayloadStatus"].(*pb.PayloadStatus) + want, ok := fix["ValidPayloadStatus"].(*pb.PayloadStatus) require.Equal(t, true, ok) req, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload) require.Equal(t, true, ok) - resp, err := client.NewPayload(ctx, req) + latestValidHash, err := client.NewPayload(ctx, req) require.NoError(t, err) - require.DeepEqual(t, want, resp) - }) - t.Run(NewPayloadMethod, func(t *testing.T) { - want, ok := fix["PayloadStatus"].(*pb.PayloadStatus) - require.Equal(t, true, ok) - req, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload) - require.Equal(t, true, ok) - resp, err := client.NewPayload(ctx, req) - require.NoError(t, err) - require.DeepEqual(t, want, resp) + require.DeepEqual(t, bytesutil.ToBytes32(want.LatestValidHash), bytesutil.ToBytes32(latestValidHash)) }) t.Run(ExchangeTransitionConfigurationMethod, func(t *testing.T) { want, ok := fix["TransitionConfiguration"].(*pb.TransitionConfiguration) @@ -138,7 +129,7 @@ func TestClient_HTTP(t *testing.T) { require.NoError(t, err) require.DeepEqual(t, want, resp) }) - t.Run(ForkchoiceUpdatedMethod, func(t *testing.T) { + t.Run(ForkchoiceUpdatedMethod+" VALID status", func(t *testing.T) { forkChoiceState := &pb.ForkchoiceState{ HeadBlockHash: []byte("head"), SafeBlockHash: []byte("safe"), @@ -151,93 +142,169 @@ func TestClient_HTTP(t *testing.T) { } want, ok := fix["ForkchoiceUpdatedResponse"].(*ForkchoiceUpdatedResponse) require.Equal(t, true, ok) - - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - defer func() { - require.NoError(t, r.Body.Close()) - }() - enc, err := ioutil.ReadAll(r.Body) - require.NoError(t, err) - jsonRequestString := string(enc) - - forkChoiceStateReq, err := json.Marshal(forkChoiceState) - require.NoError(t, err) - payloadAttrsReq, err := json.Marshal(payloadAttributes) - require.NoError(t, err) - - // We expect the JSON string RPC request contains the right arguments. - require.Equal(t, true, strings.Contains( - jsonRequestString, string(forkChoiceStateReq), - )) - require.Equal(t, true, strings.Contains( - jsonRequestString, string(payloadAttrsReq), - )) - resp := map[string]interface{}{ - "jsonrpc": "2.0", - "id": 1, - "result": want, - } - err = json.NewEncoder(w).Encode(resp) - require.NoError(t, err) - })) - defer srv.Close() - - rpcClient, err := rpc.DialHTTP(srv.URL) - require.NoError(t, err) - defer rpcClient.Close() - - client := &Client{} - client.rpc = rpcClient + client := forkchoiceUpdateSetup(t, forkChoiceState, payloadAttributes, want) // We call the RPC method via HTTP and expect a proper result. - resp, err := client.ForkchoiceUpdated(ctx, forkChoiceState, payloadAttributes) + payloadID, validHash, err := client.ForkchoiceUpdated(ctx, forkChoiceState, payloadAttributes) require.NoError(t, err) - require.DeepEqual(t, want.Status, resp.Status) - require.DeepEqual(t, want.PayloadId, resp.PayloadId) + require.DeepEqual(t, want.Status.LatestValidHash, validHash) + require.DeepEqual(t, want.PayloadId, payloadID) }) - t.Run(NewPayloadMethod, func(t *testing.T) { + t.Run(ForkchoiceUpdatedMethod+" SYNCING status", func(t *testing.T) { + forkChoiceState := &pb.ForkchoiceState{ + HeadBlockHash: []byte("head"), + SafeBlockHash: []byte("safe"), + FinalizedBlockHash: []byte("finalized"), + } + payloadAttributes := &pb.PayloadAttributes{ + Timestamp: 1, + Random: []byte("random"), + SuggestedFeeRecipient: []byte("suggestedFeeRecipient"), + } + want, ok := fix["ForkchoiceUpdatedSyncingResponse"].(*ForkchoiceUpdatedResponse) + require.Equal(t, true, ok) + client := forkchoiceUpdateSetup(t, forkChoiceState, payloadAttributes, want) + + // We call the RPC method via HTTP and expect a proper result. + payloadID, validHash, err := client.ForkchoiceUpdated(ctx, forkChoiceState, payloadAttributes) + require.ErrorIs(t, err, ErrAcceptedSyncingPayloadStatus) + require.DeepEqual(t, (*pb.PayloadIDBytes)(nil), payloadID) + require.DeepEqual(t, []byte(nil), validHash) + }) + t.Run(ForkchoiceUpdatedMethod+" INVALID status", func(t *testing.T) { + forkChoiceState := &pb.ForkchoiceState{ + HeadBlockHash: []byte("head"), + SafeBlockHash: []byte("safe"), + FinalizedBlockHash: []byte("finalized"), + } + payloadAttributes := &pb.PayloadAttributes{ + Timestamp: 1, + Random: []byte("random"), + SuggestedFeeRecipient: []byte("suggestedFeeRecipient"), + } + want, ok := fix["ForkchoiceUpdatedInvalidResponse"].(*ForkchoiceUpdatedResponse) + require.Equal(t, true, ok) + client := forkchoiceUpdateSetup(t, forkChoiceState, payloadAttributes, want) + + // We call the RPC method via HTTP and expect a proper result. + payloadID, validHash, err := client.ForkchoiceUpdated(ctx, forkChoiceState, payloadAttributes) + require.ErrorIs(t, err, ErrInvalidPayloadStatus) + require.DeepEqual(t, (*pb.PayloadIDBytes)(nil), payloadID) + require.DeepEqual(t, want.Status.LatestValidHash, validHash) + }) + t.Run(ForkchoiceUpdatedMethod+" UNKNOWN status", func(t *testing.T) { + forkChoiceState := &pb.ForkchoiceState{ + HeadBlockHash: []byte("head"), + SafeBlockHash: []byte("safe"), + FinalizedBlockHash: []byte("finalized"), + } + payloadAttributes := &pb.PayloadAttributes{ + Timestamp: 1, + Random: []byte("random"), + SuggestedFeeRecipient: []byte("suggestedFeeRecipient"), + } + want, ok := fix["ForkchoiceUpdatedAcceptedResponse"].(*ForkchoiceUpdatedResponse) + require.Equal(t, true, ok) + client := forkchoiceUpdateSetup(t, forkChoiceState, payloadAttributes, want) + + // We call the RPC method via HTTP and expect a proper result. + payloadID, validHash, err := client.ForkchoiceUpdated(ctx, forkChoiceState, payloadAttributes) + require.ErrorIs(t, err, ErrUnknownPayloadStatus) + require.DeepEqual(t, (*pb.PayloadIDBytes)(nil), payloadID) + require.DeepEqual(t, []byte(nil), validHash) + }) + t.Run(ForkchoiceUpdatedMethod+" INVALID_TERMINAL_BLOCK status", func(t *testing.T) { + forkChoiceState := &pb.ForkchoiceState{ + HeadBlockHash: []byte("head"), + SafeBlockHash: []byte("safe"), + FinalizedBlockHash: []byte("finalized"), + } + payloadAttributes := &pb.PayloadAttributes{ + Timestamp: 1, + Random: []byte("random"), + SuggestedFeeRecipient: []byte("suggestedFeeRecipient"), + } + want, ok := fix["ForkchoiceUpdatedInvalidTerminalBlockResponse"].(*ForkchoiceUpdatedResponse) + require.Equal(t, true, ok) + client := forkchoiceUpdateSetup(t, forkChoiceState, payloadAttributes, want) + + // We call the RPC method via HTTP and expect a proper result. + payloadID, validHash, err := client.ForkchoiceUpdated(ctx, forkChoiceState, payloadAttributes) + require.ErrorContains(t, "could not satisfy terminal block condition", err) + require.DeepEqual(t, (*pb.PayloadIDBytes)(nil), payloadID) + require.DeepEqual(t, []byte(nil), validHash) + }) + t.Run(NewPayloadMethod+" VALID status", func(t *testing.T) { execPayload, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload) require.Equal(t, true, ok) - want, ok := fix["PayloadStatus"].(*pb.PayloadStatus) + want, ok := fix["ValidPayloadStatus"].(*pb.PayloadStatus) require.Equal(t, true, ok) - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - defer func() { - require.NoError(t, r.Body.Close()) - }() - enc, err := ioutil.ReadAll(r.Body) - require.NoError(t, err) - jsonRequestString := string(enc) - - reqArg, err := json.Marshal(execPayload) - require.NoError(t, err) - - // We expect the JSON string RPC request contains the right arguments. - require.Equal(t, true, strings.Contains( - jsonRequestString, string(reqArg), - )) - resp := map[string]interface{}{ - "jsonrpc": "2.0", - "id": 1, - "result": want, - } - err = json.NewEncoder(w).Encode(resp) - require.NoError(t, err) - })) - defer srv.Close() - - rpcClient, err := rpc.DialHTTP(srv.URL) - require.NoError(t, err) - defer rpcClient.Close() - - client := &Client{} - client.rpc = rpcClient + client := newPayloadSetup(t, want, execPayload) // We call the RPC method via HTTP and expect a proper result. resp, err := client.NewPayload(ctx, execPayload) require.NoError(t, err) - require.DeepEqual(t, want, resp) + require.DeepEqual(t, want.LatestValidHash, resp) + }) + t.Run(NewPayloadMethod+" SYNCING status", func(t *testing.T) { + execPayload, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload) + require.Equal(t, true, ok) + want, ok := fix["SyncingStatus"].(*pb.PayloadStatus) + require.Equal(t, true, ok) + client := newPayloadSetup(t, want, execPayload) + + // We call the RPC method via HTTP and expect a proper result. + resp, err := client.NewPayload(ctx, execPayload) + require.ErrorIs(t, ErrAcceptedSyncingPayloadStatus, err) + require.DeepEqual(t, []uint8(nil), resp) + }) + t.Run(NewPayloadMethod+" INVALID_BLOCK_HASH status", func(t *testing.T) { + execPayload, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload) + require.Equal(t, true, ok) + want, ok := fix["InvalidBlockHashStatus"].(*pb.PayloadStatus) + require.Equal(t, true, ok) + client := newPayloadSetup(t, want, execPayload) + + // We call the RPC method via HTTP and expect a proper result. + resp, err := client.NewPayload(ctx, execPayload) + require.ErrorContains(t, "could not validate block hash", err) + require.DeepEqual(t, []uint8(nil), resp) + }) + t.Run(NewPayloadMethod+" INVALID_TERMINAL_BLOCK status", func(t *testing.T) { + execPayload, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload) + require.Equal(t, true, ok) + want, ok := fix["InvalidTerminalBlockStatus"].(*pb.PayloadStatus) + require.Equal(t, true, ok) + client := newPayloadSetup(t, want, execPayload) + + // We call the RPC method via HTTP and expect a proper result. + resp, err := client.NewPayload(ctx, execPayload) + require.ErrorContains(t, "could not satisfy terminal block condition", err) + require.DeepEqual(t, []uint8(nil), resp) + }) + t.Run(NewPayloadMethod+" INVALID status", func(t *testing.T) { + execPayload, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload) + require.Equal(t, true, ok) + want, ok := fix["InvalidStatus"].(*pb.PayloadStatus) + require.Equal(t, true, ok) + client := newPayloadSetup(t, want, execPayload) + + // We call the RPC method via HTTP and expect a proper result. + resp, err := client.NewPayload(ctx, execPayload) + require.ErrorIs(t, ErrInvalidPayloadStatus, err) + require.DeepEqual(t, want.LatestValidHash, resp) + }) + t.Run(NewPayloadMethod+" UNKNOWN status", func(t *testing.T) { + execPayload, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload) + require.Equal(t, true, ok) + want, ok := fix["UnknownStatus"].(*pb.PayloadStatus) + require.Equal(t, true, ok) + client := newPayloadSetup(t, want, execPayload) + + // We call the RPC method via HTTP and expect a proper result. + resp, err := client.NewPayload(ctx, execPayload) + require.ErrorIs(t, ErrUnknownPayloadStatus, err) + require.DeepEqual(t, []uint8(nil), resp) }) t.Run(ExecutionBlockByNumberMethod, func(t *testing.T) { want, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock) @@ -574,7 +641,7 @@ func fixtures() map[string]interface{} { Uncles: [][]byte{foo[:]}, } status := &pb.PayloadStatus{ - Status: pb.PayloadStatus_ACCEPTED, + Status: pb.PayloadStatus_VALID, LatestValidHash: foo[:], ValidationError: "", } @@ -583,17 +650,84 @@ func fixtures() map[string]interface{} { Status: status, PayloadId: &id, } + forkChoiceSyncingResp := &ForkchoiceUpdatedResponse{ + Status: &pb.PayloadStatus{ + Status: pb.PayloadStatus_SYNCING, + LatestValidHash: nil, + }, + PayloadId: &id, + } + forkChoiceInvalidTerminalBlockResp := &ForkchoiceUpdatedResponse{ + Status: &pb.PayloadStatus{ + Status: pb.PayloadStatus_INVALID_TERMINAL_BLOCK, + LatestValidHash: nil, + }, + PayloadId: &id, + } + forkChoiceAcceptedResp := &ForkchoiceUpdatedResponse{ + Status: &pb.PayloadStatus{ + Status: pb.PayloadStatus_ACCEPTED, + LatestValidHash: nil, + }, + PayloadId: &id, + } + forkChoiceInvalidResp := &ForkchoiceUpdatedResponse{ + Status: &pb.PayloadStatus{ + Status: pb.PayloadStatus_INVALID, + LatestValidHash: []byte("latestValidHash"), + }, + PayloadId: &id, + } transitionCfg := &pb.TransitionConfiguration{ TerminalBlockHash: params.BeaconConfig().TerminalBlockHash[:], TerminalTotalDifficulty: params.BeaconConfig().TerminalTotalDifficulty, TerminalBlockNumber: big.NewInt(0).Bytes(), } + validStatus := &pb.PayloadStatus{ + Status: pb.PayloadStatus_VALID, + LatestValidHash: foo[:], + ValidationError: "", + } + inValidBlockHashStatus := &pb.PayloadStatus{ + Status: pb.PayloadStatus_INVALID_BLOCK_HASH, + LatestValidHash: nil, + } + inValidTerminalBlockStatus := &pb.PayloadStatus{ + Status: pb.PayloadStatus_INVALID_TERMINAL_BLOCK, + LatestValidHash: nil, + } + acceptedStatus := &pb.PayloadStatus{ + Status: pb.PayloadStatus_ACCEPTED, + LatestValidHash: nil, + } + syncingStatus := &pb.PayloadStatus{ + Status: pb.PayloadStatus_SYNCING, + LatestValidHash: nil, + } + invalidStatus := &pb.PayloadStatus{ + Status: pb.PayloadStatus_INVALID, + LatestValidHash: foo[:], + } + unknownStatus := &pb.PayloadStatus{ + Status: pb.PayloadStatus_UNKNOWN, + LatestValidHash: foo[:], + } return map[string]interface{}{ - "ExecutionBlock": executionBlock, - "ExecutionPayload": executionPayloadFixture, - "PayloadStatus": status, - "ForkchoiceUpdatedResponse": forkChoiceResp, - "TransitionConfiguration": transitionCfg, + "ExecutionBlock": executionBlock, + "ExecutionPayload": executionPayloadFixture, + "ValidPayloadStatus": validStatus, + "InvalidBlockHashStatus": inValidBlockHashStatus, + "InvalidTerminalBlockStatus": inValidTerminalBlockStatus, + "AcceptedStatus": acceptedStatus, + "SyncingStatus": syncingStatus, + "InvalidStatus": invalidStatus, + "UnknownStatus": unknownStatus, + "ForkchoiceUpdatedResponse": forkChoiceResp, + "ForkchoiceUpdatedSyncingResponse": forkChoiceSyncingResp, + "ForkchoiceUpdatedInvalidTerminalBlockResponse": forkChoiceInvalidTerminalBlockResp, + "ForkchoiceUpdatedAcceptedResponse": forkChoiceAcceptedResp, + "ForkchoiceUpdatedInvalidResponse": forkChoiceInvalidResp, + "TransitionConfiguration": transitionCfg, } } @@ -653,6 +787,7 @@ func (*testEngineService) ForkchoiceUpdatedV1( if !ok { panic("not found") } + item.Status.Status = pb.PayloadStatus_VALID return item } @@ -660,9 +795,83 @@ func (*testEngineService) NewPayloadV1( _ context.Context, _ *pb.ExecutionPayload, ) *pb.PayloadStatus { fix := fixtures() - item, ok := fix["PayloadStatus"].(*pb.PayloadStatus) + item, ok := fix["ValidPayloadStatus"].(*pb.PayloadStatus) if !ok { panic("not found") } return item } + +func forkchoiceUpdateSetup(t *testing.T, fcs *pb.ForkchoiceState, att *pb.PayloadAttributes, res *ForkchoiceUpdatedResponse) *Client { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + defer func() { + require.NoError(t, r.Body.Close()) + }() + enc, err := ioutil.ReadAll(r.Body) + require.NoError(t, err) + jsonRequestString := string(enc) + + forkChoiceStateReq, err := json.Marshal(fcs) + require.NoError(t, err) + payloadAttrsReq, err := json.Marshal(att) + require.NoError(t, err) + + // We expect the JSON string RPC request contains the right arguments. + require.Equal(t, true, strings.Contains( + jsonRequestString, string(forkChoiceStateReq), + )) + require.Equal(t, true, strings.Contains( + jsonRequestString, string(payloadAttrsReq), + )) + resp := map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": res, + } + err = json.NewEncoder(w).Encode(resp) + require.NoError(t, err) + })) + + rpcClient, err := rpc.DialHTTP(srv.URL) + require.NoError(t, err) + + client := &Client{} + client.rpc = rpcClient + + return client +} + +func newPayloadSetup(t *testing.T, status *pb.PayloadStatus, payload *pb.ExecutionPayload) *Client { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + defer func() { + require.NoError(t, r.Body.Close()) + }() + enc, err := ioutil.ReadAll(r.Body) + require.NoError(t, err) + jsonRequestString := string(enc) + + reqArg, err := json.Marshal(payload) + require.NoError(t, err) + + // We expect the JSON string RPC request contains the right arguments. + require.Equal(t, true, strings.Contains( + jsonRequestString, string(reqArg), + )) + resp := map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": status, + } + err = json.NewEncoder(w).Encode(resp) + require.NoError(t, err) + })) + + rpcClient, err := rpc.DialHTTP(srv.URL) + require.NoError(t, err) + + client := &Client{} + client.rpc = rpcClient + return client +} diff --git a/beacon-chain/powchain/engine-api-client/v1/errors.go b/beacon-chain/powchain/engine-api-client/v1/errors.go index cf50faa63d..57995fad1b 100644 --- a/beacon-chain/powchain/engine-api-client/v1/errors.go +++ b/beacon-chain/powchain/engine-api-client/v1/errors.go @@ -17,6 +17,8 @@ var ( ErrServer = errors.New("client error while processing request") // ErrUnknownPayload corresponds to JSON-RPC code -32001. ErrUnknownPayload = errors.New("payload does not exist or is not available") + // ErrUnknownPayloadStatus when the payload status is unknown. + ErrUnknownPayloadStatus = errors.New("unknown payload status") // ErrUnsupportedScheme for unsupported URL schemes. ErrUnsupportedScheme = errors.New("unsupported url scheme, only http(s) and ipc are supported") // ErrMismatchTerminalBlockHash when the terminal block hash value received via @@ -25,4 +27,10 @@ var ( // ErrMismatchTerminalTotalDiff when the terminal total difficulty value received via // the API mismatches Prysm's configuration value. ErrMismatchTerminalTotalDiff = errors.New("terminal total difficulty mismatch") + // ErrAcceptedSyncingPayloadStatus when the status of the payload is syncing or accepted. + ErrAcceptedSyncingPayloadStatus = errors.New("payload status is SYNCING or ACCEPTED") + // ErrInvalidPayloadStatus when the status of the payload is invalid. + ErrInvalidPayloadStatus = errors.New("payload status is INVALID") + // ErrNilResponse when the response is nil. + ErrNilResponse = errors.New("nil response") )