From defa602e50338d8a5bb27880b438f37b43ab82ed Mon Sep 17 00:00:00 2001 From: Potuz Date: Mon, 4 Apr 2022 10:55:55 -0300 Subject: [PATCH] Adapt Doppelganger to Altair (#9969) Co-authored-by: rkapka --- .../rpc/prysm/v1alpha1/validator/status.go | 108 +++---- .../prysm/v1alpha1/validator/status_test.go | 277 +++++++----------- beacon-chain/state/stategen/mock/replayer.go | 7 + validator/client/runner.go | 17 +- 4 files changed, 172 insertions(+), 237 deletions(-) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/status.go b/beacon-chain/rpc/prysm/v1alpha1/validator/status.go index b34b0c423..22a16a0b0 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/status.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/status.go @@ -15,6 +15,7 @@ import ( "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/monitoring/tracing" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/runtime/version" "github.com/prysmaticlabs/prysm/time/slots" "go.opencensus.io/trace" "google.golang.org/grpc/codes" @@ -25,7 +26,7 @@ var errPubkeyDoesNotExist = errors.New("pubkey does not exist") var errOptimisticMode = errors.New("the node is currently optimistic and cannot serve validators") var nonExistentIndex = types.ValidatorIndex(^uint64(0)) -const numStatesToCheck = 2 +var errParticipation = status.Errorf(codes.Internal, "Failed to obtain epoch participation") // ValidatorStatus returns the validator status of the current epoch. // The status response can be one of the following: @@ -110,44 +111,67 @@ func (vs *Server) CheckDoppelGanger(ctx context.Context, req *ethpb.DoppelGanger return nil, status.Error(codes.Internal, "Could not get head state") } - currEpoch := slots.ToEpoch(headState.Slot()) - isRecent, resp := checkValidatorsAreRecent(currEpoch, req) + // Return early if we are in phase0. + if headState.Version() == version.Phase0 { + log.Info("Skipping goppelganger check for Phase 0") + + resp := ðpb.DoppelGangerResponse{ + Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}, + } + for _, v := range req.ValidatorRequests { + resp.Responses = append(resp.Responses, + ðpb.DoppelGangerResponse_ValidatorResponse{ + PublicKey: v.PublicKey, + DuplicateExists: false, + }) + } + return resp, nil + } + + headSlot := headState.Slot() + currEpoch := slots.ToEpoch(headSlot) + // If all provided keys are recent we skip this check // as we are unable to effectively determine if a doppelganger // is active. + isRecent, resp := checkValidatorsAreRecent(currEpoch, req) if isRecent { return resp, nil } - // We walk back from the current head state to the state at the beginning of the previous 2 epochs. - // Where S_i , i := 0,1,2. i = 0 would signify the current head state in this epoch. - previousEpoch, err := currEpoch.SafeSub(1) - if err != nil { - previousEpoch = currEpoch - } - olderEpoch, err := previousEpoch.SafeSub(1) - if err != nil { - olderEpoch = previousEpoch - } - prevState, err := vs.retrieveAfterEpochTransition(ctx, previousEpoch) + + // We request a state 32 slots ago. We are guaranteed to have + // currentSlot > 32 since we assume that we are in Altair's fork. + prevState, err := vs.ReplayerBuilder.ReplayerForSlot(headSlot - params.BeaconConfig().SlotsPerEpoch).ReplayBlocks(ctx) if err != nil { return nil, status.Error(codes.Internal, "Could not get previous state") } - olderState, err := vs.retrieveAfterEpochTransition(ctx, olderEpoch) + + headCurrentParticipation, err := headState.CurrentEpochParticipation() if err != nil { - return nil, status.Error(codes.Internal, "Could not get older state") + return nil, errParticipation } + headPreviousParticipation, err := headState.PreviousEpochParticipation() + if err != nil { + return nil, errParticipation + } + prevCurrentParticipation, err := prevState.CurrentEpochParticipation() + if err != nil { + return nil, errParticipation + } + prevPreviousParticipation, err := prevState.PreviousEpochParticipation() + if err != nil { + return nil, errParticipation + } + resp = ðpb.DoppelGangerResponse{ Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}, } for _, v := range req.ValidatorRequests { - // If the validator's last recorded epoch was - // less than or equal to `numStatesToCheck` epochs ago, this method will not - // be able to catch duplicates. This is due to how attestation - // inclusion works, where an attestation for the current epoch - // is able to included in the current or next epoch. Depending - // on which epoch it is included the balance change will be - // reflected in the following epoch. - if v.Epoch+numStatesToCheck >= currEpoch { + // If the validator's last recorded epoch was less than 1 epoch + // ago, the current doppelganger check will not be able to + // identify dopplelgangers since an attestation can take up to + // 31 slots to be included. + if v.Epoch+1 >= currEpoch { resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{ PublicKey: v.PublicKey, @@ -155,37 +179,15 @@ func (vs *Server) CheckDoppelGanger(ctx context.Context, req *ethpb.DoppelGanger }) continue } - valIndex, ok := olderState.ValidatorIndexByPubkey(bytesutil.ToBytes48(v.PublicKey)) + valIndex, ok := prevState.ValidatorIndexByPubkey(bytesutil.ToBytes48(v.PublicKey)) if !ok { // Ignore if validator pubkey doesn't exist. continue } - baseBal, err := olderState.BalanceAtIndex(valIndex) - if err != nil { - return nil, status.Error(codes.Internal, "Could not get validator's balance") - } - nextBal, err := prevState.BalanceAtIndex(valIndex) - if err != nil { - return nil, status.Error(codes.Internal, "Could not get validator's balance") - } - // If the next epoch's balance is higher, we mark it as an existing - // duplicate. - if nextBal > baseBal { - log.Infof("current %d with last epoch %d and difference in bal %d gwei", currEpoch, v.Epoch, nextBal-baseBal) - resp.Responses = append(resp.Responses, - ðpb.DoppelGangerResponse_ValidatorResponse{ - PublicKey: v.PublicKey, - DuplicateExists: true, - }) - continue - } - currBal, err := headState.BalanceAtIndex(valIndex) - if err != nil { - return nil, status.Error(codes.Internal, "Could not get validator's balance") - } - // If the current epoch's balance is higher, we mark it as an existing - // duplicate. - if currBal > nextBal { + + if (headCurrentParticipation[valIndex] != 0) || (headPreviousParticipation[valIndex] != 0) || + (prevCurrentParticipation[valIndex] != 0) || (prevPreviousParticipation[valIndex] != 0) { + log.WithField("ValidatorIndex", valIndex).Infof("Participation flag found") resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{ PublicKey: v.PublicKey, @@ -374,8 +376,8 @@ func checkValidatorsAreRecent(headEpoch types.Epoch, req *ethpb.DoppelGangerRequ // Due to how balances are reflected for individual // validators, we can only effectively determine if a // validator voted or not if we are able to look - // back more than `numStatesToCheck` epochs into the past. - if v.Epoch+numStatesToCheck < headEpoch { + // back more than 1 epoch into the past. + if v.Epoch+1 < headEpoch { validatorsAreRecent = false // Zero out response if we encounter non-recent validators to // guard against potential misuse. diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/status_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/status_test.go index 368040e76..653755824 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/status_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/status_test.go @@ -8,7 +8,6 @@ import ( "github.com/d4l3k/messagediff" types "github.com/prysmaticlabs/eth2-types" - "github.com/prysmaticlabs/go-bitfield" mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" @@ -17,7 +16,6 @@ import ( mockstategen "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen/mock" v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" - fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/container/trie" "github.com/prysmaticlabs/prysm/crypto/bls" @@ -961,27 +959,15 @@ func TestServer_CheckDoppelGanger(t *testing.T) { name: "normal doppelganger request", wantErr: false, svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) { - hs, ps, os, keys, builder := createStateSetup(t, 4) - // Previous Epoch State - for i := 0; i < 3; i++ { - bal, err := ps.BalanceAtIndex(types.ValidatorIndex(i)) - assert.NoError(t, err) - // Add 100 gwei, to mock an inactivity leak - assert.NoError(t, ps.UpdateBalancesAtIndex(types.ValidatorIndex(i), bal+1000000000)) - } - // Older Epoch State - for i := 0; i < 3; i++ { - bal, err := os.BalanceAtIndex(types.ValidatorIndex(i)) - assert.NoError(t, err) - // Add 200 gwei, to mock an inactivity leak - assert.NoError(t, os.UpdateBalancesAtIndex(types.ValidatorIndex(i), bal+2000000000)) - } + hs, ps, keys := createStateSetupAltair(t, 3) + rb := mockstategen.NewMockReplayerBuilder() + rb.SetMockStateForSlot(ps, 20) vs := &Server{ HeadFetcher: &mockChain.ChainService{ State: hs, }, SyncChecker: &mockSync.Sync{IsSyncing: false}, - ReplayerBuilder: builder, + ReplayerBuilder: rb, } request := ðpb.DoppelGangerRequest{ ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0), @@ -1005,37 +991,19 @@ func TestServer_CheckDoppelGanger(t *testing.T) { name: "doppelganger exists current epoch", wantErr: false, svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) { - hs, ps, os, keys, builder := createStateSetup(t, 4) - // Previous Epoch State - for i := 0; i < 2; i++ { - bal, err := ps.BalanceAtIndex(types.ValidatorIndex(i)) - assert.NoError(t, err) - // Add 100 gwei, to mock an inactivity leak - assert.NoError(t, ps.UpdateBalancesAtIndex(types.ValidatorIndex(i), bal+1000000000)) - } - bal, err := ps.BalanceAtIndex(types.ValidatorIndex(2)) - assert.NoError(t, err) - // Sub 100 gwei, to mock an active validator. - assert.NoError(t, ps.UpdateBalancesAtIndex(types.ValidatorIndex(2), bal-1000000000)) - - // Older Epoch State - for i := 0; i < 2; i++ { - bal, err := os.BalanceAtIndex(types.ValidatorIndex(i)) - assert.NoError(t, err) - // Add 200 gwei, to mock an inactivity leak - assert.NoError(t, os.UpdateBalancesAtIndex(types.ValidatorIndex(i), bal+2000000000)) - } - bal, err = os.BalanceAtIndex(types.ValidatorIndex(2)) - assert.NoError(t, err) - // Sub 100 gwei, to mock an active validator. - assert.NoError(t, os.UpdateBalancesAtIndex(types.ValidatorIndex(2), bal-1000000000)) + hs, ps, keys := createStateSetupAltair(t, 3) + rb := mockstategen.NewMockReplayerBuilder() + rb.SetMockStateForSlot(ps, 20) + currentIndices := make([]byte, 64) + currentIndices[2] = 1 + require.NoError(t, hs.SetCurrentParticipationBits(currentIndices)) vs := &Server{ HeadFetcher: &mockChain.ChainService{ State: hs, }, SyncChecker: &mockSync.Sync{IsSyncing: false}, - ReplayerBuilder: builder, + ReplayerBuilder: rb, } request := ðpb.DoppelGangerRequest{ ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0), @@ -1070,37 +1038,19 @@ func TestServer_CheckDoppelGanger(t *testing.T) { name: "doppelganger exists previous epoch", wantErr: false, svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) { - hs, ps, os, keys, builder := createStateSetup(t, 4) - // Previous Epoch State - for i := 0; i < 2; i++ { - bal, err := ps.BalanceAtIndex(types.ValidatorIndex(i)) - assert.NoError(t, err) - // Add 100 gwei, to mock an inactivity leak - assert.NoError(t, ps.UpdateBalancesAtIndex(types.ValidatorIndex(i), bal+1000000000)) - } - bal, err := ps.BalanceAtIndex(types.ValidatorIndex(2)) - assert.NoError(t, err) - // Sub 100 gwei, to mock an active validator. - assert.NoError(t, ps.UpdateBalancesAtIndex(types.ValidatorIndex(2), bal-1000000000)) - - // Older Epoch State - for i := 0; i < 2; i++ { - bal, err := os.BalanceAtIndex(types.ValidatorIndex(i)) - assert.NoError(t, err) - // Add 200 gwei, to mock an inactivity leak - assert.NoError(t, os.UpdateBalancesAtIndex(types.ValidatorIndex(i), bal+2000000000)) - } - bal, err = os.BalanceAtIndex(types.ValidatorIndex(2)) - assert.NoError(t, err) - // Sub 200 gwei, to mock an active validator. - assert.NoError(t, os.UpdateBalancesAtIndex(types.ValidatorIndex(2), bal-2000000000)) + hs, ps, keys := createStateSetupAltair(t, 3) + prevIndices := make([]byte, 64) + prevIndices[2] = 1 + require.NoError(t, ps.SetPreviousParticipationBits(prevIndices)) + rb := mockstategen.NewMockReplayerBuilder() + rb.SetMockStateForSlot(ps, 20) vs := &Server{ HeadFetcher: &mockChain.ChainService{ State: hs, }, SyncChecker: &mockSync.Sync{IsSyncing: false}, - ReplayerBuilder: builder, + ReplayerBuilder: rb, } request := ðpb.DoppelGangerRequest{ ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0), @@ -1135,29 +1085,26 @@ func TestServer_CheckDoppelGanger(t *testing.T) { name: "multiple doppelganger exists", wantErr: false, svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) { - hs, ps, os, keys, builder := createStateSetup(t, 4) - // Previous Epoch State - for i := 10; i < 15; i++ { - bal, err := ps.BalanceAtIndex(types.ValidatorIndex(i)) - assert.NoError(t, err) - // Add 100 gwei, to mock an inactivity leak - assert.NoError(t, ps.UpdateBalancesAtIndex(types.ValidatorIndex(i), bal-1000000000)) - } + hs, ps, keys := createStateSetupAltair(t, 3) + currentIndices := make([]byte, 64) + currentIndices[10] = 1 + currentIndices[11] = 2 + require.NoError(t, hs.SetPreviousParticipationBits(currentIndices)) + rb := mockstategen.NewMockReplayerBuilder() + rb.SetMockStateForSlot(ps, 20) - // Older Epoch State - for i := 10; i < 15; i++ { - bal, err := os.BalanceAtIndex(types.ValidatorIndex(i)) - assert.NoError(t, err) - // Add 200 gwei, to mock an inactivity leak - assert.NoError(t, os.UpdateBalancesAtIndex(types.ValidatorIndex(i), bal-2000000000)) + prevIndices := make([]byte, 64) + for i := 12; i < 20; i++ { + prevIndices[i] = 1 } + require.NoError(t, ps.SetCurrentParticipationBits(prevIndices)) vs := &Server{ HeadFetcher: &mockChain.ChainService{ State: hs, }, SyncChecker: &mockSync.Sync{IsSyncing: false}, - ReplayerBuilder: builder, + ReplayerBuilder: rb, } request := ðpb.DoppelGangerRequest{ ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0), @@ -1175,6 +1122,17 @@ func TestServer_CheckDoppelGanger(t *testing.T) { DuplicateExists: true, }) } + for i := 15; i < 20; i++ { + request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{ + PublicKey: keys[i].PublicKey().Marshal(), + Epoch: 3, + SignedRoot: []byte{'A'}, + }) + response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{ + PublicKey: keys[i].PublicKey().Marshal(), + DuplicateExists: false, + }) + } return vs, request, response }, @@ -1183,14 +1141,16 @@ func TestServer_CheckDoppelGanger(t *testing.T) { name: "attesters are too recent", wantErr: false, svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) { - hs, _, _, keys, _ := createStateSetup(t, 4) + hs, ps, keys := createStateSetupAltair(t, 3) + rb := mockstategen.NewMockReplayerBuilder() + rb.SetMockStateForSlot(ps, 20) vs := &Server{ HeadFetcher: &mockChain.ChainService{ State: hs, }, SyncChecker: &mockSync.Sync{IsSyncing: false}, - ReplayerBuilder: nil, + ReplayerBuilder: rb, } request := ðpb.DoppelGangerRequest{ ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0), @@ -1208,6 +1168,39 @@ func TestServer_CheckDoppelGanger(t *testing.T) { }) } + return vs, request, response + }, + }, + { + name: "exit early for Phase 0", + wantErr: false, + svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) { + hs, _, keys := createStateSetupPhase0(t, 3) + + vs := &Server{ + HeadFetcher: &mockChain.ChainService{ + State: hs, + }, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + } + request := ðpb.DoppelGangerRequest{ + ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{ + { + PublicKey: keys[0].PublicKey().Marshal(), + Epoch: 1, + SignedRoot: []byte{'A'}, + }, + }, + } + response := ðpb.DoppelGangerResponse{ + Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{ + { + PublicKey: keys[0].PublicKey().Marshal(), + DuplicateExists: false, + }, + }, + } + return vs, request, response }, }, @@ -1228,104 +1221,36 @@ func TestServer_CheckDoppelGanger(t *testing.T) { } } -func createStateSetup(t *testing.T, head types.Epoch) (state.BeaconState, - state.BeaconState, state.BeaconState, []bls.SecretKey, *mockstategen.MockReplayerBuilder) { - rb := &mockstategen.MockReplayerBuilder{} +func createStateSetupPhase0(t *testing.T, head types.Epoch) (state.BeaconState, + state.BeaconState, []bls.SecretKey) { gs, keys := util.DeterministicGenesisState(t, 64) hs := gs.Copy() + // Head State - headEpoch := head - headSlot := types.Slot(headEpoch) * params.BeaconConfig().SlotsPerEpoch + headSlot := types.Slot(head)*params.BeaconConfig().SlotsPerEpoch + params.BeaconConfig().SlotsPerEpoch/2 assert.NoError(t, hs.SetSlot(headSlot)) - assingments, _, err := helpers.CommitteeAssignments(context.Background(), hs, headEpoch) - assert.NoError(t, err) - for _, ctr := range assingments { - pendingAtt := ðpb.PendingAttestation{ - AggregationBits: bitfield.NewBitlist64(uint64(len(ctr.Committee))).ToBitlist().Not(), - Data: ðpb.AttestationData{ - Slot: ctr.AttesterSlot, - CommitteeIndex: ctr.CommitteeIndex, - BeaconBlockRoot: make([]byte, fieldparams.RootLength), - Source: ðpb.Checkpoint{ - Epoch: 0, - Root: make([]byte, fieldparams.RootLength), - }, - Target: ðpb.Checkpoint{ - Epoch: 1, - Root: make([]byte, fieldparams.RootLength), - }, - }, - InclusionDelay: 1, - ProposerIndex: 10, - } - assert.NoError(t, hs.AppendCurrentEpochAttestations(pendingAtt)) - } - rb.SetMockState(hs) // Previous Epoch State - prevEpoch := headEpoch - 1 + prevSlot := headSlot - params.BeaconConfig().SlotsPerEpoch ps := gs.Copy() - prevSlot, err := slots.EpochEnd(prevEpoch) - assert.NoError(t, err) assert.NoError(t, ps.SetSlot(prevSlot)) - assingments, _, err = helpers.CommitteeAssignments(context.Background(), ps, prevEpoch) - assert.NoError(t, err) - for _, ctr := range assingments { - pendingAtt := ðpb.PendingAttestation{ - AggregationBits: bitfield.NewBitlist64(uint64(len(ctr.Committee))).ToBitlist().Not(), - Data: ðpb.AttestationData{ - Slot: ctr.AttesterSlot, - CommitteeIndex: ctr.CommitteeIndex, - BeaconBlockRoot: make([]byte, fieldparams.RootLength), - Source: ðpb.Checkpoint{ - Epoch: 0, - Root: make([]byte, fieldparams.RootLength), - }, - Target: ðpb.Checkpoint{ - Epoch: 1, - Root: make([]byte, fieldparams.RootLength), - }, - }, - InclusionDelay: 1, - ProposerIndex: 10, - } - assert.NoError(t, ps.AppendCurrentEpochAttestations(pendingAtt)) - } - rb.SetMockState(ps) - // Older Epoch State - olderEpoch := prevEpoch - 1 - os := gs.Copy() - olderSlot, err := slots.EpochEnd(olderEpoch) - assert.NoError(t, err) - assert.NoError(t, os.SetSlot(olderSlot)) - assingments, _, err = helpers.CommitteeAssignments(context.Background(), os, olderEpoch) - assert.NoError(t, err) - for _, ctr := range assingments { - attSlot := ctr.AttesterSlot - if attSlot == olderSlot { - continue - } - pendingAtt := ðpb.PendingAttestation{ - AggregationBits: bitfield.NewBitlist64(uint64(len(ctr.Committee))).ToBitlist().Not(), - Data: ðpb.AttestationData{ - Slot: attSlot, - CommitteeIndex: ctr.CommitteeIndex, - BeaconBlockRoot: make([]byte, fieldparams.RootLength), - Source: ðpb.Checkpoint{ - Epoch: 0, - Root: make([]byte, fieldparams.RootLength), - }, - Target: ðpb.Checkpoint{ - Epoch: 1, - Root: make([]byte, fieldparams.RootLength), - }, - }, - InclusionDelay: 1, - ProposerIndex: 10, - } - assert.NoError(t, os.AppendCurrentEpochAttestations(pendingAtt)) - } - rb.SetMockState(os) - return hs, ps, os, keys, rb + return hs, ps, keys +} + +func createStateSetupAltair(t *testing.T, head types.Epoch) (state.BeaconState, + state.BeaconState, []bls.SecretKey) { + gs, keys := util.DeterministicGenesisStateAltair(t, 64) + hs := gs.Copy() + + // Head State + headSlot := types.Slot(head)*params.BeaconConfig().SlotsPerEpoch + params.BeaconConfig().SlotsPerEpoch/2 + assert.NoError(t, hs.SetSlot(headSlot)) + + // Previous Epoch State + prevSlot := headSlot - params.BeaconConfig().SlotsPerEpoch + ps := gs.Copy() + assert.NoError(t, ps.SetSlot(prevSlot)) + + return hs, ps, keys } diff --git a/beacon-chain/state/stategen/mock/replayer.go b/beacon-chain/state/stategen/mock/replayer.go index df04b9203..59e17dfb9 100644 --- a/beacon-chain/state/stategen/mock/replayer.go +++ b/beacon-chain/state/stategen/mock/replayer.go @@ -41,6 +41,13 @@ func (b *MockReplayerBuilder) SetMockState(s state.BeaconState) { b.forSlot[s.Slot()] = &MockReplayer{State: s} } +func (b *MockReplayerBuilder) SetMockStateForSlot(s state.BeaconState, slot types.Slot) { + if b.forSlot == nil { + b.forSlot = make(map[types.Slot]*MockReplayer) + } + b.forSlot[slot] = &MockReplayer{State: s} +} + func (b *MockReplayerBuilder) SetMockSlotError(s types.Slot, e error) { if b.forSlot == nil { b.forSlot = make(map[types.Slot]*MockReplayer) diff --git a/validator/client/runner.go b/validator/client/runner.go index 907527e04..ab48f2ee4 100644 --- a/validator/client/runner.go +++ b/validator/client/runner.go @@ -83,14 +83,7 @@ func run(ctx context.Context, v iface.Validator) { if err != nil { log.Fatalf("Could not wait for validator activation: %v", err) } - err = v.CheckDoppelGanger(ctx) - if isConnectionError(err) { - log.Warnf("Could not wait for checking doppelganger: %v", err) - continue - } - if err != nil { - log.Fatalf("Could not succeed with doppelganger check: %v", err) - } + headSlot, err = v.CanonicalHeadSlot(ctx) if isConnectionError(err) { log.Warnf("Could not get current canonical head slot: %v", err) @@ -99,6 +92,14 @@ func run(ctx context.Context, v iface.Validator) { if err != nil { log.Fatalf("Could not get current canonical head slot: %v", err) } + err = v.CheckDoppelGanger(ctx) + if isConnectionError(err) { + log.Warnf("Could not wait for checking doppelganger: %v", err) + continue + } + if err != nil { + log.Fatalf("Could not succeed with doppelganger check: %v", err) + } break }