Add REST API endpoint for node client's GetGenesis (#12168)

* Add REST API endpoint for node client's GetGenesis

* Remove unused parameter

* Fix test name

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
Patrice Vignola
2023-03-28 04:36:41 -07:00
committed by GitHub
parent f5bfb8e9eb
commit 83ccb15e66
3 changed files with 198 additions and 5 deletions

View File

@@ -56,6 +56,7 @@ go_library(
"@com_github_sirupsen_logrus//:go_default_library",
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
],
)
@@ -66,6 +67,7 @@ go_test(
"activation_test.go",
"attestation_data_test.go",
"beacon_api_helpers_test.go",
"beacon_api_node_client_test.go",
"beacon_api_validator_client_test.go",
"beacon_block_converter_test.go",
"beacon_block_json_helpers_test.go",
@@ -118,5 +120,6 @@ go_test(
"@com_github_pkg_errors//:go_default_library",
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
],
)

View File

@@ -3,16 +3,22 @@ package beacon_api
import (
"context"
"net/http"
"strconv"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/validator/client/iface"
"google.golang.org/protobuf/types/known/timestamppb"
)
type beaconApiNodeClient struct {
fallbackClient iface.NodeClient
jsonRestHandler jsonRestHandler
genesisProvider genesisProvider
}
func (c *beaconApiNodeClient) GetSyncStatus(ctx context.Context, in *empty.Empty) (*ethpb.SyncStatus, error) {
@@ -24,13 +30,43 @@ func (c *beaconApiNodeClient) GetSyncStatus(ctx context.Context, in *empty.Empty
panic("beaconApiNodeClient.GetSyncStatus is not implemented. To use a fallback client, pass a fallback client as the last argument of NewBeaconApiNodeClientWithFallback.")
}
func (c *beaconApiNodeClient) GetGenesis(ctx context.Context, in *empty.Empty) (*ethpb.Genesis, error) {
if c.fallbackClient != nil {
return c.fallbackClient.GetGenesis(ctx, in)
func (c *beaconApiNodeClient) GetGenesis(ctx context.Context, _ *empty.Empty) (*ethpb.Genesis, error) {
genesisJson, _, err := c.genesisProvider.GetGenesis(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to get genesis")
}
// TODO: Implement me
panic("beaconApiNodeClient.GetGenesis is not implemented. To use a fallback client, pass a fallback client as the last argument of NewBeaconApiNodeClientWithFallback.")
genesisValidatorRoot, err := hexutil.Decode(genesisJson.GenesisValidatorsRoot)
if err != nil {
return nil, errors.Wrapf(err, "failed to decode genesis validator root `%s`", genesisJson.GenesisValidatorsRoot)
}
genesisTime, err := strconv.ParseInt(genesisJson.GenesisTime, 10, 64)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse genesis time `%s`", genesisJson.GenesisTime)
}
depositContractJson := apimiddleware.DepositContractResponseJson{}
if _, err = c.jsonRestHandler.GetRestJsonResponse(ctx, "/eth/v1/config/deposit_contract", &depositContractJson); err != nil {
return nil, errors.Wrapf(err, "failed to query deposit contract information")
}
if depositContractJson.Data == nil {
return nil, errors.New("deposit contract data is nil")
}
depositContactAddress, err := hexutil.Decode(depositContractJson.Data.Address)
if err != nil {
return nil, errors.Wrapf(err, "failed to decode deposit contract address `%s`", depositContractJson.Data.Address)
}
return &ethpb.Genesis{
GenesisTime: &timestamppb.Timestamp{
Seconds: genesisTime,
},
DepositContractAddress: depositContactAddress,
GenesisValidatorsRoot: genesisValidatorRoot,
}, nil
}
func (c *beaconApiNodeClient) GetVersion(ctx context.Context, in *empty.Empty) (*ethpb.Version, error) {
@@ -60,5 +96,6 @@ func NewNodeClientWithFallback(host string, timeout time.Duration, fallbackClien
return &beaconApiNodeClient{
jsonRestHandler: jsonRestHandler,
fallbackClient: fallbackClient,
genesisProvider: beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler},
}
}

View File

@@ -0,0 +1,153 @@
package beacon_api
import (
"context"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/mock/gomock"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/mock"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
)
func TestGetGenesis(t *testing.T) {
testCases := []struct {
name string
genesisResponse *apimiddleware.GenesisResponse_GenesisJson
genesisError error
depositContractResponse apimiddleware.DepositContractResponseJson
depositContractError error
queriesDepositContract bool
expectedResponse *ethpb.Genesis
expectedError string
}{
{
name: "fails to get genesis",
genesisError: errors.New("foo error"),
expectedError: "failed to get genesis: foo error",
},
{
name: "fails to decode genesis validator root",
genesisResponse: &apimiddleware.GenesisResponse_GenesisJson{
GenesisTime: "1",
GenesisValidatorsRoot: "foo",
},
expectedError: "failed to decode genesis validator root `foo`",
},
{
name: "fails to parse genesis time",
genesisResponse: &apimiddleware.GenesisResponse_GenesisJson{
GenesisTime: "foo",
GenesisValidatorsRoot: hexutil.Encode([]byte{1}),
},
expectedError: "failed to parse genesis time `foo`",
},
{
name: "fails to query contract information",
genesisResponse: &apimiddleware.GenesisResponse_GenesisJson{
GenesisTime: "1",
GenesisValidatorsRoot: hexutil.Encode([]byte{2}),
},
depositContractError: errors.New("foo error"),
queriesDepositContract: true,
expectedError: "failed to query deposit contract information: foo error",
},
{
name: "fails to read nil deposit contract data",
genesisResponse: &apimiddleware.GenesisResponse_GenesisJson{
GenesisTime: "1",
GenesisValidatorsRoot: hexutil.Encode([]byte{2}),
},
queriesDepositContract: true,
depositContractResponse: apimiddleware.DepositContractResponseJson{
Data: nil,
},
expectedError: "deposit contract data is nil",
},
{
name: "fails to decode deposit contract address",
genesisResponse: &apimiddleware.GenesisResponse_GenesisJson{
GenesisTime: "1",
GenesisValidatorsRoot: hexutil.Encode([]byte{2}),
},
queriesDepositContract: true,
depositContractResponse: apimiddleware.DepositContractResponseJson{
Data: &apimiddleware.DepositContractJson{
Address: "foo",
},
},
expectedError: "failed to decode deposit contract address `foo`",
},
{
name: "successfully retrieves genesis info",
genesisResponse: &apimiddleware.GenesisResponse_GenesisJson{
GenesisTime: "654812",
GenesisValidatorsRoot: hexutil.Encode([]byte{2}),
},
queriesDepositContract: true,
depositContractResponse: apimiddleware.DepositContractResponseJson{
Data: &apimiddleware.DepositContractJson{
Address: hexutil.Encode([]byte{3}),
},
},
expectedResponse: &ethpb.Genesis{
GenesisTime: &timestamppb.Timestamp{
Seconds: 654812,
},
DepositContractAddress: []byte{3},
GenesisValidatorsRoot: []byte{2},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.Background()
genesisProvider := mock.NewMockgenesisProvider(ctrl)
genesisProvider.EXPECT().GetGenesis(
ctx,
).Return(
testCase.genesisResponse,
nil,
testCase.genesisError,
)
depositContractJson := apimiddleware.DepositContractResponseJson{}
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
if testCase.queriesDepositContract {
jsonRestHandler.EXPECT().GetRestJsonResponse(
ctx,
"/eth/v1/config/deposit_contract",
&depositContractJson,
).Return(
nil,
testCase.depositContractError,
).SetArg(
2,
testCase.depositContractResponse,
)
}
nodeClient := &beaconApiNodeClient{
genesisProvider: genesisProvider,
jsonRestHandler: jsonRestHandler,
}
response, err := nodeClient.GetGenesis(ctx, &emptypb.Empty{})
if testCase.expectedResponse == nil {
assert.ErrorContains(t, testCase.expectedError, err)
} else {
assert.DeepEqual(t, testCase.expectedResponse, response)
}
})
}
}