From ad03938964ad11a0686e5b290a70a4b405e83131 Mon Sep 17 00:00:00 2001 From: Nishant Das Date: Sat, 30 Apr 2022 01:19:43 +0800 Subject: [PATCH] Fix Doppelganger Check (#10582) Co-authored-by: Preston Van Loon --- .../rpc/prysm/v1alpha1/validator/status.go | 13 +++-- .../prysm/v1alpha1/validator/status_test.go | 58 ++++++++++++++++--- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/status.go b/beacon-chain/rpc/prysm/v1alpha1/validator/status.go index dbf854649..3b0c6ebe4 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/status.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/status.go @@ -141,7 +141,12 @@ func (vs *Server) CheckDoppelGanger(ctx context.Context, req *ethpb.DoppelGanger // 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) + prevStateSlot := headSlot - params.BeaconConfig().SlotsPerEpoch + prevEpochEnd, err := slots.EpochEnd(slots.ToEpoch(prevStateSlot)) + if err != nil { + return nil, status.Error(codes.Internal, "Could not get previous epoch's end") + } + prevState, err := vs.ReplayerBuilder.ReplayerForSlot(prevEpochEnd).ReplayBlocks(ctx) if err != nil { return nil, status.Error(codes.Internal, "Could not get previous state") } @@ -171,7 +176,7 @@ func (vs *Server) CheckDoppelGanger(ctx context.Context, req *ethpb.DoppelGanger // 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 { + if v.Epoch+2 >= currEpoch { resp.Responses = append(resp.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{ PublicKey: v.PublicKey, @@ -376,8 +381,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 1 epoch into the past. - if v.Epoch+1 < headEpoch { + // back more than 2 epoch into the past. + if v.Epoch+2 < 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 fd256b9fd..122ba72be 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/status_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/status_test.go @@ -961,7 +961,7 @@ func TestServer_CheckDoppelGanger(t *testing.T) { svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) { hs, ps, keys := createStateSetupAltair(t, 3) rb := mockstategen.NewMockReplayerBuilder() - rb.SetMockStateForSlot(ps, 20) + rb.SetMockStateForSlot(ps, 23) vs := &Server{ HeadFetcher: &mockChain.ChainService{ State: hs, @@ -993,7 +993,7 @@ func TestServer_CheckDoppelGanger(t *testing.T) { svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) { hs, ps, keys := createStateSetupAltair(t, 3) rb := mockstategen.NewMockReplayerBuilder() - rb.SetMockStateForSlot(ps, 20) + rb.SetMockStateForSlot(ps, 23) currentIndices := make([]byte, 64) currentIndices[2] = 1 require.NoError(t, hs.SetCurrentParticipationBits(currentIndices)) @@ -1024,7 +1024,7 @@ func TestServer_CheckDoppelGanger(t *testing.T) { // Add in for duplicate validator request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{ PublicKey: keys[2].PublicKey().Marshal(), - Epoch: 1, + Epoch: 0, SignedRoot: []byte{'A'}, }) response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{ @@ -1043,7 +1043,7 @@ func TestServer_CheckDoppelGanger(t *testing.T) { prevIndices[2] = 1 require.NoError(t, ps.SetPreviousParticipationBits(prevIndices)) rb := mockstategen.NewMockReplayerBuilder() - rb.SetMockStateForSlot(ps, 20) + rb.SetMockStateForSlot(ps, 23) vs := &Server{ HeadFetcher: &mockChain.ChainService{ @@ -1071,7 +1071,7 @@ func TestServer_CheckDoppelGanger(t *testing.T) { // Add in for duplicate validator request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{ PublicKey: keys[2].PublicKey().Marshal(), - Epoch: 1, + Epoch: 0, SignedRoot: []byte{'A'}, }) response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{ @@ -1091,7 +1091,7 @@ func TestServer_CheckDoppelGanger(t *testing.T) { currentIndices[11] = 2 require.NoError(t, hs.SetPreviousParticipationBits(currentIndices)) rb := mockstategen.NewMockReplayerBuilder() - rb.SetMockStateForSlot(ps, 20) + rb.SetMockStateForSlot(ps, 23) prevIndices := make([]byte, 64) for i := 12; i < 20; i++ { @@ -1114,7 +1114,7 @@ func TestServer_CheckDoppelGanger(t *testing.T) { // Add in for duplicate validator request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{ PublicKey: keys[i].PublicKey().Marshal(), - Epoch: 1, + Epoch: 0, SignedRoot: []byte{'A'}, }) response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{ @@ -1143,8 +1143,11 @@ func TestServer_CheckDoppelGanger(t *testing.T) { svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) { hs, ps, keys := createStateSetupAltair(t, 3) rb := mockstategen.NewMockReplayerBuilder() - rb.SetMockStateForSlot(ps, 20) - + rb.SetMockStateForSlot(ps, 23) + currentIndices := make([]byte, 64) + currentIndices[0] = 1 + currentIndices[1] = 2 + require.NoError(t, hs.SetPreviousParticipationBits(currentIndices)) vs := &Server{ HeadFetcher: &mockChain.ChainService{ State: hs, @@ -1171,6 +1174,43 @@ func TestServer_CheckDoppelGanger(t *testing.T) { return vs, request, response }, }, + { + name: "attesters are too recent(previous state)", + wantErr: false, + svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) { + hs, ps, keys := createStateSetupAltair(t, 3) + rb := mockstategen.NewMockReplayerBuilder() + rb.SetMockStateForSlot(ps, 23) + currentIndices := make([]byte, 64) + currentIndices[0] = 1 + currentIndices[1] = 2 + require.NoError(t, ps.SetPreviousParticipationBits(currentIndices)) + vs := &Server{ + HeadFetcher: &mockChain.ChainService{ + State: hs, + }, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + ReplayerBuilder: rb, + } + request := ðpb.DoppelGangerRequest{ + ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0), + } + response := ðpb.DoppelGangerResponse{Responses: make([]*ethpb.DoppelGangerResponse_ValidatorResponse, 0)} + for i := 0; i < 15; i++ { + request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{ + PublicKey: keys[i].PublicKey().Marshal(), + Epoch: 1, + SignedRoot: []byte{'A'}, + }) + response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{ + PublicKey: keys[i].PublicKey().Marshal(), + DuplicateExists: false, + }) + } + + return vs, request, response + }, + }, { name: "exit early for Phase 0", wantErr: false,