Skip to content

Commit

Permalink
Get genesis only once (#13796)
Browse files Browse the repository at this point in the history
  • Loading branch information
rkapka authored Mar 26, 2024
1 parent 14d7416 commit 6de7df6
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 15 deletions.
2 changes: 1 addition & 1 deletion validator/client/beacon-api/beacon_api_node_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func NewNodeClientWithFallback(jsonRestHandler JsonRestHandler, fallbackClient i
b := &beaconApiNodeClient{
jsonRestHandler: jsonRestHandler,
fallbackClient: fallbackClient,
genesisProvider: beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler},
genesisProvider: &beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler},
}
b.healthTracker = beacon.NewNodeHealthTracker(b)
return b
Expand Down
2 changes: 1 addition & 1 deletion validator/client/beacon-api/beacon_api_validator_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type beaconApiValidatorClient struct {

func NewBeaconApiValidatorClient(jsonRestHandler JsonRestHandler, opts ...ValidatorClientOpt) iface.ValidatorClient {
c := &beaconApiValidatorClient{
genesisProvider: beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler},
genesisProvider: &beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler},
dutiesProvider: beaconApiDutiesProvider{jsonRestHandler: jsonRestHandler},
stateValidatorsProvider: beaconApiStateValidatorsProvider{jsonRestHandler: jsonRestHandler},
jsonRestHandler: jsonRestHandler,
Expand Down
29 changes: 20 additions & 9 deletions validator/client/beacon-api/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"net/http"
"strconv"
"sync"
"time"

"github.com/ethereum/go-ethereum/common/hexutil"
Expand All @@ -19,6 +20,8 @@ type GenesisProvider interface {

type beaconApiGenesisProvider struct {
jsonRestHandler JsonRestHandler
genesis *structs.Genesis
once sync.Once
}

func (c beaconApiValidatorClient) waitForChainStart(ctx context.Context) (*ethpb.ChainStartResponse, error) {
Expand Down Expand Up @@ -64,15 +67,23 @@ func (c beaconApiValidatorClient) waitForChainStart(ctx context.Context) (*ethpb
}

// GetGenesis gets the genesis information from the beacon node via the /eth/v1/beacon/genesis endpoint
func (c beaconApiGenesisProvider) GetGenesis(ctx context.Context) (*structs.Genesis, error) {
func (c *beaconApiGenesisProvider) GetGenesis(ctx context.Context) (*structs.Genesis, error) {
genesisJson := &structs.GetGenesisResponse{}
if err := c.jsonRestHandler.Get(ctx, "/eth/v1/beacon/genesis", genesisJson); err != nil {
return nil, err
}

if genesisJson.Data == nil {
return nil, errors.New("genesis data is nil")
var doErr error
c.once.Do(func() {
if err := c.jsonRestHandler.Get(ctx, "/eth/v1/beacon/genesis", genesisJson); err != nil {
doErr = err
return
}
if genesisJson.Data == nil {
doErr = errors.New("genesis data is nil")
return
}
c.genesis = genesisJson.Data
})
if doErr != nil {
// Allow another call because the current one returned an error
c.once = sync.Once{}
}

return genesisJson.Data, nil
return c.genesis, doErr
}
76 changes: 76 additions & 0 deletions validator/client/beacon-api/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"testing"

"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
Expand Down Expand Up @@ -66,3 +67,78 @@ func TestGetGenesis_NilData(t *testing.T) {
_, err := genesisProvider.GetGenesis(ctx)
assert.ErrorContains(t, "genesis data is nil", err)
}

func TestGetGenesis_EndpointCalledOnlyOnce(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

ctx := context.Background()

genesisResponseJson := structs.GetGenesisResponse{}
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
jsonRestHandler.EXPECT().Get(
ctx,
"/eth/v1/beacon/genesis",
&genesisResponseJson,
).Return(
nil,
).SetArg(
2,
structs.GetGenesisResponse{
Data: &structs.Genesis{
GenesisTime: "1234",
GenesisValidatorsRoot: "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
},
},
).Times(1)

genesisProvider := &beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler}
_, err := genesisProvider.GetGenesis(ctx)
assert.NoError(t, err)
resp, err := genesisProvider.GetGenesis(ctx)
assert.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "1234", resp.GenesisTime)
assert.Equal(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", resp.GenesisValidatorsRoot)
}

func TestGetGenesis_EndpointCanBeCalledAgainAfterError(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

ctx := context.Background()

genesisResponseJson := structs.GetGenesisResponse{}
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
jsonRestHandler.EXPECT().Get(
ctx,
"/eth/v1/beacon/genesis",
&genesisResponseJson,
).Return(
errors.New("foo"),
).Times(1)
jsonRestHandler.EXPECT().Get(
ctx,
"/eth/v1/beacon/genesis",
&genesisResponseJson,
).Return(
nil,
).SetArg(
2,
structs.GetGenesisResponse{
Data: &structs.Genesis{
GenesisTime: "1234",
GenesisValidatorsRoot: "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
},
},
).Times(1)

genesisProvider := &beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler}
_, err := genesisProvider.GetGenesis(ctx)
require.ErrorContains(t, "foo", err)
resp, err := genesisProvider.GetGenesis(ctx)
assert.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "1234", resp.GenesisTime)
assert.Equal(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", resp.GenesisValidatorsRoot)
}
8 changes: 4 additions & 4 deletions validator/client/beacon-api/wait_for_chain_start_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func TestWaitForChainStart_ValidGenesis(t *testing.T) {
).Times(1)

genesisProvider := beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler}
validatorClient := beaconApiValidatorClient{genesisProvider: genesisProvider}
validatorClient := beaconApiValidatorClient{genesisProvider: &genesisProvider}
resp, err := validatorClient.WaitForChainStart(ctx, &emptypb.Empty{})
assert.NoError(t, err)

Expand Down Expand Up @@ -104,7 +104,7 @@ func TestWaitForChainStart_BadGenesis(t *testing.T) {
).Times(1)

genesisProvider := beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler}
validatorClient := beaconApiValidatorClient{genesisProvider: genesisProvider}
validatorClient := beaconApiValidatorClient{genesisProvider: &genesisProvider}
_, err := validatorClient.WaitForChainStart(ctx, &emptypb.Empty{})
assert.ErrorContains(t, testCase.errorMessage, err)
})
Expand All @@ -127,7 +127,7 @@ func TestWaitForChainStart_JsonResponseError(t *testing.T) {
).Times(1)

genesisProvider := beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler}
validatorClient := beaconApiValidatorClient{genesisProvider: genesisProvider}
validatorClient := beaconApiValidatorClient{genesisProvider: &genesisProvider}
_, err := validatorClient.WaitForChainStart(ctx, &emptypb.Empty{})
assert.ErrorContains(t, "failed to get genesis data", err)
assert.ErrorContains(t, "some specific json error", err)
Expand Down Expand Up @@ -172,7 +172,7 @@ func TestWaitForChainStart_JsonResponseError404(t *testing.T) {
).Times(1)

genesisProvider := beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler}
validatorClient := beaconApiValidatorClient{genesisProvider: genesisProvider}
validatorClient := beaconApiValidatorClient{genesisProvider: &genesisProvider}
resp, err := validatorClient.WaitForChainStart(ctx, &emptypb.Empty{})
assert.NoError(t, err)

Expand Down

0 comments on commit 6de7df6

Please sign in to comment.