Add REST implementation for validator's GetSyncMessageBlockRoot (#11824)

* add REST implementation for GetSyncMessageBlockRoot

* improve sync_message_block_root_test.go to handle errors

* include context

* fix unit tests
This commit is contained in:
Dhruv Bodani 2023-01-10 01:05:33 +05:30 committed by GitHub
parent 375a76d6c9
commit c89ab764e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 137 additions and 7 deletions

View File

@ -21,6 +21,7 @@ go_library(
"state_validators.go",
"status.go",
"sync_committee.go",
"sync_message_block_root.go",
],
importpath = "github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api",
visibility = ["//validator:__subpackages__"],
@ -72,6 +73,7 @@ go_test(
"state_validators_test.go",
"status_test.go",
"sync_committee_test.go",
"sync_message_block_root_test.go",
"wait_for_chain_start_test.go",
],
embed = [":go_default_library"],

View File

@ -95,13 +95,8 @@ func (c *beaconApiValidatorClient) GetSyncCommitteeContribution(ctx context.Cont
panic("beaconApiValidatorClient.GetSyncCommitteeContribution is not implemented. To use a fallback client, create this validator with NewBeaconApiValidatorClientWithFallback instead.")
}
func (c *beaconApiValidatorClient) GetSyncMessageBlockRoot(ctx context.Context, in *empty.Empty) (*ethpb.SyncMessageBlockRootResponse, error) {
if c.fallbackClient != nil {
return c.fallbackClient.GetSyncMessageBlockRoot(ctx, in)
}
// TODO: Implement me
panic("beaconApiValidatorClient.GetSyncMessageBlockRoot is not implemented. To use a fallback client, create this validator with NewBeaconApiValidatorClientWithFallback instead.")
func (c *beaconApiValidatorClient) GetSyncMessageBlockRoot(ctx context.Context, _ *empty.Empty) (*ethpb.SyncMessageBlockRootResponse, error) {
return c.getSyncMessageBlockRoot(ctx)
}
func (c *beaconApiValidatorClient) GetSyncSubcommitteeIndex(ctx context.Context, in *ethpb.SyncSubcommitteeIndexRequest) (*ethpb.SyncSubcommitteeIndexResponse, error) {

View File

@ -0,0 +1,41 @@
package beacon_api
import (
"context"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
)
func (c *beaconApiValidatorClient) getSyncMessageBlockRoot(ctx context.Context) (*ethpb.SyncMessageBlockRootResponse, error) {
// Get head beacon block root.
var resp apimiddleware.BlockRootResponseJson
if _, err := c.jsonRestHandler.GetRestJsonResponse(ctx, "/eth/v1/beacon/blocks/head/root", &resp); err != nil {
return nil, errors.Wrap(err, "failed to query GET REST endpoint")
}
// An optimistic validator MUST NOT participate in sync committees
// (i.e., sign across the DOMAIN_SYNC_COMMITTEE, DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF or DOMAIN_CONTRIBUTION_AND_PROOF domains).
if resp.ExecutionOptimistic {
return nil, errors.New("the node is currently optimistic and cannot serve validators")
}
if resp.Data == nil {
return nil, errors.New("no data returned")
}
if resp.Data.Root == "" {
return nil, errors.New("no root returned")
}
blockRoot, err := hexutil.Decode(resp.Data.Root)
if err != nil {
return nil, errors.Wrap(err, "failed to decode beacon block root")
}
return &ethpb.SyncMessageBlockRootResponse{
Root: blockRoot,
}, nil
}

View File

@ -0,0 +1,92 @@
package beacon_api
import (
"context"
"errors"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/mock/gomock"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/mock"
)
func TestGetSyncMessageBlockRoot(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
const blockRoot = "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
tests := []struct {
name string
endpointError error
expectedErrorMessage string
expectedResponse apimiddleware.BlockRootResponseJson
}{
{
name: "valid request",
expectedResponse: apimiddleware.BlockRootResponseJson{
Data: &apimiddleware.BlockRootContainerJson{
Root: blockRoot,
},
},
},
{
name: "internal server error",
expectedErrorMessage: "internal server error",
endpointError: errors.New("internal server error"),
},
{
name: "execution optimistic",
expectedResponse: apimiddleware.BlockRootResponseJson{
ExecutionOptimistic: true,
},
expectedErrorMessage: "the node is currently optimistic and cannot serve validators",
},
{
name: "no data",
expectedResponse: apimiddleware.BlockRootResponseJson{},
expectedErrorMessage: "no data returned",
},
{
name: "no root",
expectedResponse: apimiddleware.BlockRootResponseJson{
Data: new(apimiddleware.BlockRootContainerJson),
},
expectedErrorMessage: "no root returned",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := context.Background()
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
jsonRestHandler.EXPECT().GetRestJsonResponse(
ctx,
"/eth/v1/beacon/blocks/head/root",
&apimiddleware.BlockRootResponseJson{},
).SetArg(
2,
test.expectedResponse,
).Return(
nil,
test.endpointError,
).Times(1)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
actualResponse, err := validatorClient.getSyncMessageBlockRoot(ctx)
if test.expectedErrorMessage != "" {
require.ErrorContains(t, test.expectedErrorMessage, err)
return
}
require.NoError(t, err)
expectedRootBytes, err := hexutil.Decode(test.expectedResponse.Data.Root)
require.NoError(t, err)
require.NoError(t, err)
require.DeepEqual(t, expectedRootBytes, actualResponse.Root)
})
}
}