-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Doppelganger Check #9120
Changes from 12 commits
fa6d7ea
3c3c39e
461faa7
240127d
baba3ee
a74b646
e0c84be
5102d5a
c53ca33
382c221
43101ed
2f23842
db90b7a
69d4c6a
dfaaf4a
20d95bc
3611f5d
d624bbd
09f260e
0a3941a
959bcbc
3845258
684038c
2e5ad7f
dac200f
c6b6b21
5f3677e
4b74efa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,6 +88,101 @@ func (vs *Server) MultipleValidatorStatus( | |
}, nil | ||
} | ||
|
||
// CheckDoppelGanger checks if the provided keys are currently active in the network. | ||
func (vs *Server) CheckDoppelGanger(ctx context.Context, req *ethpb.DoppelGangerRequest) (*ethpb.DoppelGangerResponse, error) { | ||
if vs.SyncChecker.Syncing() { | ||
return nil, status.Errorf(codes.Unavailable, "Syncing to latest head, not ready to respond") | ||
} | ||
if req == nil || req.ValidatorRequests == nil || len(req.ValidatorRequests) == 0 { | ||
return ðpb.DoppelGangerResponse{ | ||
Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}, | ||
}, nil | ||
} | ||
headState, err := vs.HeadFetcher.HeadState(ctx) | ||
if err != nil { | ||
return nil, status.Error(codes.Internal, "Could not get head state") | ||
} | ||
// 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 := helpers.SlotToEpoch(headState.Slot()) | ||
previousEpoch, err := currEpoch.SafeSub(1) | ||
if err != nil { | ||
previousEpoch = currEpoch | ||
} | ||
prestonvanloon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
olderEpoch, err := previousEpoch.SafeSub(1) | ||
if err != nil { | ||
olderEpoch = previousEpoch | ||
} | ||
prestonvanloon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
prevState, err := vs.StateGen.StateBySlot(ctx, params.BeaconConfig().SlotsPerEpoch.Mul(uint64(previousEpoch))) | ||
if err != nil { | ||
return nil, status.Error(codes.Internal, "Could not get previous state") | ||
} | ||
olderState, err := vs.StateGen.StateBySlot(ctx, params.BeaconConfig().SlotsPerEpoch.Mul(uint64(olderEpoch))) | ||
if err != nil { | ||
return nil, status.Error(codes.Internal, "Could not get previous state") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Could not get older state" would be appropriate. |
||
} | ||
resp := ðpb.DoppelGangerResponse{ | ||
Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}, | ||
} | ||
for _, v := range req.ValidatorRequests { | ||
// If the validator's last recorded epoch was | ||
// less than 2 epoch ago, this method will not | ||
// be able to catch duplicates. | ||
if v.Epoch+2 >= currEpoch { | ||
resp.Responses = append(resp.Responses, | ||
ðpb.DoppelGangerResponse_ValidatorResponse{ | ||
PublicKey: v.PublicKey, | ||
DuplicateExists: false, | ||
}) | ||
continue | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this the case? It isn't clear to the reader why a request within the last 2 epochs will be marked as false or safe to proceed without actually checking. |
||
valIndex, ok := olderState.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 { | ||
prestonvanloon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 { | ||
resp.Responses = append(resp.Responses, | ||
ðpb.DoppelGangerResponse_ValidatorResponse{ | ||
PublicKey: v.PublicKey, | ||
DuplicateExists: true, | ||
}) | ||
continue | ||
} | ||
// Mark the public key as valid. | ||
resp.Responses = append(resp.Responses, | ||
ðpb.DoppelGangerResponse_ValidatorResponse{ | ||
PublicKey: v.PublicKey, | ||
DuplicateExists: false, | ||
}) | ||
} | ||
return resp, nil | ||
} | ||
|
||
// activationStatus returns the validator status response for the set of validators | ||
// requested by their pub keys. | ||
func (vs *Server) activationStatus( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For my benefit. why this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simply changes it from a struct to an interface, this allows us to mock it correctly for tests.
*stategen.State
fulfills the state manager interface.