Skip to content

Commit

Permalink
Validator status updates (prysmaticlabs#4675)
Browse files Browse the repository at this point in the history
* Update ValidatorStatus to match Ethereum APIs
* Tidy up status calculation
* Merge branch 'master' into validator-status-updates
* Merge branch 'master' into validator-status-updates
* Update beacon-chain/rpc/beacon/config.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update test names
  • Loading branch information
mcdee authored and cryptomental committed Feb 28, 2020
1 parent 8a03ff4 commit cf8d0af
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 164 deletions.
2 changes: 1 addition & 1 deletion WORKSPACE
Expand Up @@ -1286,7 +1286,7 @@ go_repository(
go_repository(
name = "com_github_prysmaticlabs_ethereumapis",
commit = "8a785e129627db2be96339a35fb2317b200160b1",
commit = "e1e8777cb75e9dd568c1a8e096b0497bfe37d631",
importpath = "github.com/prysmaticlabs/ethereumapis",
patch_args = ["-p1"],
patches = [
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/rpc/beacon/BUILD.bazel
Expand Up @@ -7,6 +7,7 @@ go_library(
"attestations.go",
"blocks.go",
"committees.go",
"config.go",
"server.go",
"validators.go",
],
Expand Down
15 changes: 15 additions & 0 deletions beacon-chain/rpc/beacon/config.go
@@ -0,0 +1,15 @@
package beacon

import (
"context"

ptypes "github.com/gogo/protobuf/types"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

// GetBeaconConfig returns the configuration of the beacon chain as understood by this node.
func (bs *Server) GetBeaconConfig(ctx context.Context, _ *ptypes.Empty) (*ethpb.BeaconConfig, error) {
return nil, status.Error(codes.Internal, "not implemented")
}
4 changes: 1 addition & 3 deletions beacon-chain/rpc/validator/server.go
Expand Up @@ -130,9 +130,7 @@ func (vs *Server) ExitedValidators(
exitedKeys := make([][]byte, 0)
for _, st := range statuses {
s := st.Status.Status
if s == ethpb.ValidatorStatus_EXITED ||
s == ethpb.ValidatorStatus_EXITED_SLASHED ||
s == ethpb.ValidatorStatus_INITIATED_EXIT {
if s == ethpb.ValidatorStatus_EXITED {
exitedKeys = append(exitedKeys, st.PublicKey)
}
}
Expand Down
62 changes: 32 additions & 30 deletions beacon-chain/rpc/validator/status.go
Expand Up @@ -20,12 +20,13 @@ var errPubkeyDoesNotExist = errors.New("pubkey does not exist")

// ValidatorStatus returns the validator status of the current epoch.
// The status response can be one of the following:
// PENDING_ACTIVE - validator is waiting to get activated.
// ACTIVE - validator is active.
// INITIATED_EXIT - validator has initiated an an exit request.
// WITHDRAWABLE - validator's deposit can be withdrawn after lock up period.
// EXITED - validator has exited, means the deposit has been withdrawn.
// EXITED_SLASHED - validator was forcefully exited due to slashing.
// DEPOSITED - validator's deposit has been recognized by Ethereum 1, not yet recognized by Ethereum 2.
// PENDING - validator is in Ethereum 2's activation queue.
// ACTIVE - validator is active.
// EXITING - validator has initiated an an exit request, or has dropped below the ejection balance and is being kicked out.
// EXITED - validator is no longer validating.
// SLASHING - validator has been kicked out due to meeting a slashing condition.
// UNKNOWN_STATUS - validator does not have a known status in the network.
func (vs *Server) ValidatorStatus(
ctx context.Context,
req *ethpb.ValidatorStatusRequest) (*ethpb.ValidatorStatusResponse, error) {
Expand Down Expand Up @@ -99,7 +100,7 @@ func (vs *Server) validatorStatus(ctx context.Context, pubKey []byte, headState
}

if resp.Status == ethpb.ValidatorStatus_UNKNOWN_STATUS {
resp.Status = ethpb.ValidatorStatus_DEPOSIT_RECEIVED
resp.Status = ethpb.ValidatorStatus_DEPOSITED
}

resp.Eth1DepositBlockNumber = eth1BlockNumBigInt.Uint64()
Expand All @@ -115,7 +116,7 @@ func (vs *Server) validatorStatus(ctx context.Context, pubKey []byte, headState
// we return the request early too. We only proceed if its status is pending active
// Additionally, if idx is zero (default return value) then we know this
// validator cannot be in the queue either.
if resp.Status != ethpb.ValidatorStatus_PENDING_ACTIVE || idx == 0 {
if resp.Status != ethpb.ValidatorStatus_PENDING || idx == 0 {
return resp
}

Expand All @@ -140,41 +141,42 @@ func (vs *Server) retrieveStatusFromState(
headState *pbp2p.BeaconState,
) (ethpb.ValidatorStatus, uint64, error) {
if headState == nil {
return ethpb.ValidatorStatus(0), 0, errors.New("head state does not exist")
return ethpb.ValidatorStatus_UNKNOWN_STATUS, 0, errors.New("head state does not exist")
}
idx, ok, err := vs.BeaconDB.ValidatorIndex(ctx, pubKey)
if err != nil {
return ethpb.ValidatorStatus(0), 0, err
return ethpb.ValidatorStatus_UNKNOWN_STATUS, 0, err
}
if !ok || int(idx) >= len(headState.Validators) {
return ethpb.ValidatorStatus(0), 0, errPubkeyDoesNotExist
return ethpb.ValidatorStatus_UNKNOWN_STATUS, 0, errPubkeyDoesNotExist
}
return vs.assignmentStatus(idx, headState), idx, nil
}

func (vs *Server) assignmentStatus(validatorIdx uint64, beaconState *pbp2p.BeaconState) ethpb.ValidatorStatus {
var status ethpb.ValidatorStatus
v := beaconState.Validators[validatorIdx]
epoch := helpers.CurrentEpoch(beaconState)
validator := beaconState.Validators[validatorIdx]
currentEpoch := helpers.CurrentEpoch(beaconState)
farFutureEpoch := params.BeaconConfig().FarFutureEpoch

if epoch < v.ActivationEpoch {
status = ethpb.ValidatorStatus_PENDING_ACTIVE
} else if v.ExitEpoch == farFutureEpoch {
status = ethpb.ValidatorStatus_ACTIVE
} else if epoch >= v.WithdrawableEpoch {
status = ethpb.ValidatorStatus_WITHDRAWABLE
} else if v.Slashed && epoch >= v.ExitEpoch {
status = ethpb.ValidatorStatus_EXITED_SLASHED
} else if epoch >= v.ExitEpoch {
status = ethpb.ValidatorStatus_EXITED
} else if v.ExitEpoch != farFutureEpoch {
status = ethpb.ValidatorStatus_INITIATED_EXIT
} else {
status = ethpb.ValidatorStatus_UNKNOWN_STATUS
if validator == nil {
return ethpb.ValidatorStatus_UNKNOWN_STATUS
}

return status
if currentEpoch < validator.ActivationEligibilityEpoch {
return ethpb.ValidatorStatus_DEPOSITED
}
if currentEpoch < validator.ActivationEpoch {
return ethpb.ValidatorStatus_PENDING
}
if validator.ExitEpoch == farFutureEpoch {
return ethpb.ValidatorStatus_ACTIVE
}
if currentEpoch < validator.ExitEpoch {
if validator.Slashed {
return ethpb.ValidatorStatus_SLASHING
}
return ethpb.ValidatorStatus_EXITING
}
return ethpb.ValidatorStatus_EXITED
}

func (vs *Server) depositBlockSlot(ctx context.Context, eth1BlockNumBigInt *big.Int, beaconState *pbp2p.BeaconState) (uint64, error) {
Expand Down
153 changes: 47 additions & 106 deletions beacon-chain/rpc/validator/status_test.go
Expand Up @@ -21,7 +21,7 @@ import (
"github.com/prysmaticlabs/prysm/shared/trieutil"
)

func TestValidatorStatus_DepositReceived(t *testing.T) {
func TestValidatorStatus_Deposited(t *testing.T) {
db := dbutil.SetupDB(t)
defer dbutil.TeardownDB(t, db)
ctx := context.Background()
Expand Down Expand Up @@ -63,12 +63,12 @@ func TestValidatorStatus_DepositReceived(t *testing.T) {
if err != nil {
t.Fatalf("Could not get validator status %v", err)
}
if resp.Status != ethpb.ValidatorStatus_DEPOSIT_RECEIVED {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_DEPOSIT_RECEIVED, resp.Status)
if resp.Status != ethpb.ValidatorStatus_DEPOSITED {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_DEPOSITED, resp.Status)
}
}

func TestValidatorStatus_PendingActive(t *testing.T) {
func TestValidatorStatus_Pending(t *testing.T) {
db := dbutil.SetupDB(t)
defer dbutil.TeardownDB(t, db)
ctx := context.Background()
Expand All @@ -89,8 +89,10 @@ func TestValidatorStatus_PendingActive(t *testing.T) {
state := &pbp2p.BeaconState{
Validators: []*ethpb.Validator{
{
ActivationEpoch: params.BeaconConfig().FarFutureEpoch,
PublicKey: pubKey,
ActivationEpoch: params.BeaconConfig().FarFutureEpoch,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
PublicKey: pubKey,
},
},
Slot: 5000,
Expand Down Expand Up @@ -139,8 +141,8 @@ func TestValidatorStatus_PendingActive(t *testing.T) {
if err != nil {
t.Fatalf("Could not get validator status %v", err)
}
if resp.Status != ethpb.ValidatorStatus_PENDING_ACTIVE {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_PENDING_ACTIVE, resp.Status)
if resp.Status != ethpb.ValidatorStatus_PENDING {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_PENDING, resp.Status)
}
}

Expand Down Expand Up @@ -189,9 +191,10 @@ func TestValidatorStatus_Active(t *testing.T) {
GenesisTime: uint64(time.Unix(0, 0).Unix()),
Slot: 10000,
Validators: []*ethpb.Validator{{
ActivationEpoch: activeEpoch,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
PublicKey: pubKey},
ActivationEpoch: activeEpoch,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
PublicKey: pubKey},
}}

timestamp := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
Expand Down Expand Up @@ -226,7 +229,7 @@ func TestValidatorStatus_Active(t *testing.T) {
}
}

func TestValidatorStatus_InitiatedExit(t *testing.T) {
func TestValidatorStatus_Exiting(t *testing.T) {
db := dbutil.SetupDB(t)
defer dbutil.TeardownDB(t, db)
ctx := context.Background()
Expand Down Expand Up @@ -295,82 +298,12 @@ func TestValidatorStatus_InitiatedExit(t *testing.T) {
if err != nil {
t.Fatalf("Could not get validator status %v", err)
}
if resp.Status != ethpb.ValidatorStatus_INITIATED_EXIT {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_INITIATED_EXIT, resp.Status)
if resp.Status != ethpb.ValidatorStatus_EXITING {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_EXITING, resp.Status)
}
}

func TestValidatorStatus_Withdrawable(t *testing.T) {
db := dbutil.SetupDB(t)
defer dbutil.TeardownDB(t, db)
ctx := context.Background()

pubKey := pubKey(1)
if err := db.SaveValidatorIndex(ctx, pubKey, 0); err != nil {
t.Fatalf("Could not save validator index: %v", err)
}

// Withdrawable exit because current epoch is after validator withdrawable epoch.
slot := uint64(10000)
epoch := helpers.SlotToEpoch(slot)
block := blk.NewGenesisBlock([]byte{})
if err := db.SaveBlock(ctx, block); err != nil {
t.Fatalf("Could not save genesis block: %v", err)
}
genesisRoot, err := ssz.HashTreeRoot(block.Block)
if err != nil {
t.Fatalf("Could not get signing root %v", err)
}

state := &pbp2p.BeaconState{
Slot: 10000,
Validators: []*ethpb.Validator{{
WithdrawableEpoch: epoch - 1,
ExitEpoch: epoch - 2,
PublicKey: pubKey},
}}
depData := &ethpb.Deposit_Data{
PublicKey: pubKey,
Signature: []byte("hi"),
WithdrawalCredentials: []byte("hey"),
}

deposit := &ethpb.Deposit{
Data: depData,
}
depositTrie, err := trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth))
if err != nil {
t.Fatalf("Could not setup deposit trie: %v", err)
}
depositCache := depositcache.NewDepositCache()
depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, depositTrie.Root())
height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
p := &mockPOW.POWChain{
TimesByHeight: map[int]uint64{
0: uint64(height),
},
}
vs := &Server{
BeaconDB: db,
ChainStartFetcher: p,
BlockFetcher: p,
Eth1InfoFetcher: p,
DepositFetcher: depositCache,
HeadFetcher: &mockChain.ChainService{State: state, Root: genesisRoot[:]},
}
req := &ethpb.ValidatorStatusRequest{
PublicKey: pubKey,
}
resp, err := vs.ValidatorStatus(context.Background(), req)
if err != nil {
t.Fatalf("Could not get validator status %v", err)
}
if resp.Status != ethpb.ValidatorStatus_WITHDRAWABLE {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_WITHDRAWABLE, resp.Status)
}
}

func TestValidatorStatus_ExitedSlashed(t *testing.T) {
func TestValidatorStatus_Exited_Slashed(t *testing.T) {
db := dbutil.SetupDB(t)
defer dbutil.TeardownDB(t, db)
ctx := context.Background()
Expand Down Expand Up @@ -435,8 +368,8 @@ func TestValidatorStatus_ExitedSlashed(t *testing.T) {
if err != nil {
t.Fatalf("Could not get validator status %v", err)
}
if resp.Status != ethpb.ValidatorStatus_EXITED_SLASHED {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_EXITED_SLASHED, resp.Status)
if resp.Status != ethpb.ValidatorStatus_EXITED {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_EXITED, resp.Status)
}
}

Expand Down Expand Up @@ -632,7 +565,7 @@ func TestMultipleValidatorStatus_OK(t *testing.T) {
response[1].PublicKey)
}

if response[2].Status.Status != ethpb.ValidatorStatus_DEPOSIT_RECEIVED {
if response[2].Status.Status != ethpb.ValidatorStatus_DEPOSITED {
t.Errorf("Validator with pubkey %#x is not activated and instead has this status: %s",
response[2].PublicKey, response[2].Status.Status.String())
}
Expand Down Expand Up @@ -660,32 +593,40 @@ func TestValidatorStatus_CorrectActivationQueue(t *testing.T) {
state := &pbp2p.BeaconState{
Validators: []*ethpb.Validator{
{
ActivationEpoch: 0,
PublicKey: pubKey(0),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
ActivationEpoch: 0,
PublicKey: pubKey(0),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
},
{
ActivationEpoch: 0,
PublicKey: pubKey(1),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
ActivationEpoch: 0,
PublicKey: pubKey(1),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
},
{
ActivationEpoch: 0,
PublicKey: pubKey(2),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
ActivationEpoch: 0,
PublicKey: pubKey(2),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
},
{
ActivationEpoch: 0,
PublicKey: pubKey(3),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
ActivationEpoch: 0,
PublicKey: pubKey(3),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
},
{
ActivationEpoch: currentSlot/params.BeaconConfig().SlotsPerEpoch + 1,
PublicKey: pbKey,
ActivationEpoch: currentSlot/params.BeaconConfig().SlotsPerEpoch + 1,
PublicKey: pbKey,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
},
{
ActivationEpoch: currentSlot/params.BeaconConfig().SlotsPerEpoch + 4,
PublicKey: pubKey(5),
ActivationEpoch: currentSlot/params.BeaconConfig().SlotsPerEpoch + 4,
PublicKey: pubKey(5),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
},
},
Slot: currentSlot,
Expand Down Expand Up @@ -738,8 +679,8 @@ func TestValidatorStatus_CorrectActivationQueue(t *testing.T) {
if err != nil {
t.Fatalf("Could not get validator status %v", err)
}
if resp.Status != ethpb.ValidatorStatus_PENDING_ACTIVE {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_PENDING_ACTIVE, resp.Status)
if resp.Status != ethpb.ValidatorStatus_PENDING {
t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_PENDING, resp.Status)
}
if resp.PositionInActivationQueue != 2 {
t.Errorf("Expected Position in activation queue of %d but instead got %d", 2, resp.PositionInActivationQueue)
Expand Down

0 comments on commit cf8d0af

Please sign in to comment.