mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-25 12:57:18 +00:00
Faster Doppelganger Check (#9964)
* faster check * potuz's review * potuz's review
This commit is contained in:
parent
7cbef104b0
commit
d94bf32dcf
@ -24,6 +24,8 @@ import (
|
||||
var errPubkeyDoesNotExist = errors.New("pubkey does not exist")
|
||||
var nonExistentIndex = types.ValidatorIndex(^uint64(0))
|
||||
|
||||
const numStatesToCheck = 2
|
||||
|
||||
// ValidatorStatus returns the validator status of the current epoch.
|
||||
// The status response can be one of the following:
|
||||
// DEPOSITED - validator's deposit has been recognized by Ethereum 1, not yet recognized by Ethereum.
|
||||
@ -106,9 +108,17 @@ func (vs *Server) CheckDoppelGanger(ctx context.Context, req *ethpb.DoppelGanger
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get head state")
|
||||
}
|
||||
|
||||
currEpoch := slots.ToEpoch(headState.Slot())
|
||||
isRecent, resp := checkValidatorsAreRecent(currEpoch, req)
|
||||
// If all provided keys are recent we skip this check
|
||||
// as we are unable to effectively determine if a doppelganger
|
||||
// is active.
|
||||
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.
|
||||
currEpoch := slots.ToEpoch(headState.Slot())
|
||||
previousEpoch, err := currEpoch.SafeSub(1)
|
||||
if err != nil {
|
||||
previousEpoch = currEpoch
|
||||
@ -125,18 +135,18 @@ func (vs *Server) CheckDoppelGanger(ctx context.Context, req *ethpb.DoppelGanger
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not get older state")
|
||||
}
|
||||
resp := ðpb.DoppelGangerResponse{
|
||||
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 2 epochs ago, this method will not
|
||||
// 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+2 >= currEpoch {
|
||||
if v.Epoch+numStatesToCheck >= currEpoch {
|
||||
resp.Responses = append(resp.Responses,
|
||||
ðpb.DoppelGangerResponse_ValidatorResponse{
|
||||
PublicKey: v.PublicKey,
|
||||
@ -322,6 +332,44 @@ func (vs *Server) validatorStatus(
|
||||
}
|
||||
}
|
||||
|
||||
func (vs *Server) retrieveAfterEpochTransition(ctx context.Context, epoch types.Epoch) (state.BeaconState, error) {
|
||||
endSlot, err := slots.EpochEnd(epoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retState, err := vs.StateGen.StateBySlot(ctx, endSlot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transition.ProcessSlots(ctx, retState, retState.Slot()+1)
|
||||
}
|
||||
|
||||
func checkValidatorsAreRecent(headEpoch types.Epoch, req *ethpb.DoppelGangerRequest) (bool, *ethpb.DoppelGangerResponse) {
|
||||
validatorsAreRecent := true
|
||||
resp := ðpb.DoppelGangerResponse{
|
||||
Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{},
|
||||
}
|
||||
for _, v := range req.ValidatorRequests {
|
||||
// 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 {
|
||||
validatorsAreRecent = false
|
||||
// Zero out response if we encounter non-recent validators to
|
||||
// guard against potential misuse.
|
||||
resp.Responses = []*ethpb.DoppelGangerResponse_ValidatorResponse{}
|
||||
break
|
||||
}
|
||||
resp.Responses = append(resp.Responses,
|
||||
ðpb.DoppelGangerResponse_ValidatorResponse{
|
||||
PublicKey: v.PublicKey,
|
||||
DuplicateExists: false,
|
||||
})
|
||||
}
|
||||
return validatorsAreRecent, resp
|
||||
}
|
||||
|
||||
func statusForPubKey(headState state.ReadOnlyBeaconState, pubKey []byte) (ethpb.ValidatorStatus, types.ValidatorIndex, error) {
|
||||
if headState == nil || headState.IsNil() {
|
||||
return ethpb.ValidatorStatus_UNKNOWN_STATUS, 0, errors.New("head state does not exist")
|
||||
@ -371,15 +419,3 @@ func depositStatus(depositOrBalance uint64) ethpb.ValidatorStatus {
|
||||
}
|
||||
return ethpb.ValidatorStatus_DEPOSITED
|
||||
}
|
||||
|
||||
func (vs *Server) retrieveAfterEpochTransition(ctx context.Context, epoch types.Epoch) (state.BeaconState, error) {
|
||||
endSlot, err := slots.EpochEnd(epoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retState, err := vs.StateGen.StateBySlot(ctx, endSlot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transition.ProcessSlots(ctx, retState, retState.Slot()+1)
|
||||
}
|
||||
|
@ -1170,25 +1170,10 @@ func TestServer_CheckDoppelGanger(t *testing.T) {
|
||||
svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) {
|
||||
mockGen := stategen.NewMockService()
|
||||
|
||||
hs, ps, os, keys := createStateSetup(t, 4, mockGen)
|
||||
// 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 active validator
|
||||
assert.NoError(t, ps.UpdateBalancesAtIndex(types.ValidatorIndex(i), bal-1000000000))
|
||||
}
|
||||
|
||||
// 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 active validator
|
||||
assert.NoError(t, os.UpdateBalancesAtIndex(types.ValidatorIndex(i), bal-2000000000))
|
||||
}
|
||||
hs, _, _, keys := createStateSetup(t, 4, mockGen)
|
||||
|
||||
vs := &Server{
|
||||
StateGen: mockGen,
|
||||
StateGen: nil,
|
||||
HeadFetcher: &mockChain.ChainService{
|
||||
State: hs,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user