ValidatorStatus Estimating Activation RPC Server (#2469)

* fix spacing

* working on position in queue

* fmt

* spacing

* feedback

* tests

* rename
This commit is contained in:
Raul Jordan
2019-05-02 14:14:25 -05:00
committed by GitHub
parent b1334c6120
commit f876df42d7
12 changed files with 224 additions and 34 deletions

View File

@@ -20,6 +20,7 @@ go_library(
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/state:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bytesutil:go_default_library",

View File

@@ -1,12 +1,15 @@
package db
import (
"bytes"
"context"
"fmt"
"math/big"
"sort"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
@@ -80,3 +83,27 @@ func (db *BeaconDB) AllDeposits(ctx context.Context, beforeBlk *big.Int) []*pb.D
return deposits
}
// DepositByPubkey looks through historical deposits and finds one which contains
// a certain public key within its deposit data.
func (db *BeaconDB) DepositByPubkey(ctx context.Context, pubKey []byte) (*pb.Deposit, *big.Int, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.DepositByPubkey")
defer span.End()
db.depositsLock.RLock()
defer db.depositsLock.RUnlock()
var deposit *pb.Deposit
var blockNum *big.Int
for _, ctnr := range db.deposits {
depositInput, err := helpers.DecodeDepositInput(ctnr.deposit.DepositData)
if err != nil {
return nil, nil, fmt.Errorf("could not decode deposit input: %v", err)
}
if bytes.Equal(depositInput.Pubkey, pubKey) {
deposit = ctnr.deposit
blockNum = ctnr.block
break
}
}
return deposit, blockNum, nil
}

View File

@@ -6,10 +6,11 @@ package internal
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
v1 "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
metadata "google.golang.org/grpc/metadata"
reflect "reflect"
)
// MockValidatorServiceServer is a mock of ValidatorServiceServer interface

View File

@@ -1,6 +1,7 @@
package rpc
import (
"bytes"
"context"
"encoding/hex"
"errors"
@@ -214,14 +215,69 @@ func (vs *ValidatorServer) ValidatorStatus(
return nil, fmt.Errorf("could not fetch beacon state: %v", err)
}
_, eth1BlockNumBigInt, err := vs.beaconDB.DepositByPubkey(ctx, req.PublicKey)
if err != nil {
return nil, err
}
if eth1BlockNumBigInt == nil {
return &pb.ValidatorStatusResponse{
Status: pb.ValidatorStatus_UNKNOWN_STATUS,
ActivationEpoch: params.BeaconConfig().FarFutureEpoch,
Eth1DepositBlockNumber: eth1BlockNumBigInt.Uint64(),
}, nil
}
currEpoch := helpers.CurrentEpoch(beaconState)
eth1BlockNum := eth1BlockNumBigInt.Uint64()
timeToInclusion := (eth1BlockNum + params.BeaconConfig().Eth1FollowDistance) * params.BeaconConfig().GoerliBlockTime
votingPeriodSlots := helpers.StartSlot(params.BeaconConfig().EpochsPerEth1VotingPeriod)
depositBlockSlot := (timeToInclusion / params.BeaconConfig().SecondsPerSlot) + votingPeriodSlots
var validatorInState *pbp2p.Validator
var validatorIndex uint64
for idx, val := range beaconState.ValidatorRegistry {
if bytes.Equal(val.Pubkey, req.PublicKey) {
if helpers.IsActiveValidator(val, currEpoch) {
return &pb.ValidatorStatusResponse{
Status: pb.ValidatorStatus_ACTIVE,
ActivationEpoch: val.ActivationEpoch,
Eth1DepositBlockNumber: eth1BlockNum,
DepositInclusionSlot: depositBlockSlot,
}, nil
}
validatorInState = val
validatorIndex = uint64(idx)
break
}
}
var positionInQueue uint64
// If the validator has deposited and has been added to the state:
if validatorInState != nil {
var lastActivatedValidatorIdx uint64
for j := len(beaconState.ValidatorRegistry) - 1; j >= 0; j-- {
if helpers.IsActiveValidator(beaconState.ValidatorRegistry[j], currEpoch) {
lastActivatedValidatorIdx = uint64(j)
break
}
}
// Our position in the activation queue is the above index - our validator index.
positionInQueue = lastActivatedValidatorIdx - validatorIndex
}
status, err := vs.validatorStatus(req.PublicKey, beaconState)
if err != nil {
return nil, err
}
res := &pb.ValidatorStatusResponse{
Status: status,
Eth1DepositBlockNumber: eth1BlockNum,
PositionInActivationQueue: positionInQueue,
DepositInclusionSlot: depositBlockSlot,
ActivationEpoch: params.BeaconConfig().FarFutureEpoch,
}
return &pb.ValidatorStatusResponse{
Status: status,
}, nil
return res, nil
}
func (vs *ValidatorServer) validatorStatus(pubkey []byte, beaconState *pbp2p.BeaconState) (pb.ValidatorStatus, error) {
@@ -274,7 +330,11 @@ func (vs *ValidatorServer) filterActivePublicKeys(beaconState *pbp2p.BeaconState
return activeKeys
}
func (vs *ValidatorServer) addNonActivePublicKeysAssignmentStatus(beaconState *pbp2p.BeaconState, pubkeys [][]byte, assignments []*pb.CommitteeAssignmentResponse_CommitteeAssignment) []*pb.CommitteeAssignmentResponse_CommitteeAssignment {
func (vs *ValidatorServer) addNonActivePublicKeysAssignmentStatus(
beaconState *pbp2p.BeaconState,
pubkeys [][]byte,
assignments []*pb.CommitteeAssignmentResponse_CommitteeAssignment,
) []*pb.CommitteeAssignmentResponse_CommitteeAssignment {
// Generate a map for O(1) lookup of existence of pub keys in request.
validatorMap := make(map[string]*pbp2p.Validator)
for _, v := range beaconState.ValidatorRegistry {

View File

@@ -5,12 +5,15 @@ import (
"context"
"fmt"
"math"
"math/big"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/gogo/protobuf/proto"
"github.com/golang/mock/gomock"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
@@ -306,26 +309,6 @@ func TestCommitteeAssignment_multipleKeys_OK(t *testing.T) {
}
}
func TestValidatorStatus_CantFindValidatorIdx(t *testing.T) {
db := internal.SetupDB(t)
defer internal.TeardownDB(t, db)
ctx := context.Background()
if err := db.SaveState(ctx, &pbp2p.BeaconState{ValidatorRegistry: []*pbp2p.Validator{}}); err != nil {
t.Fatalf("could not save state: %v", err)
}
vs := &ValidatorServer{
beaconDB: db,
}
req := &pb.ValidatorIndexRequest{
PublicKey: []byte{'B'},
}
want := fmt.Sprintf("validator %#x does not exist", req.PublicKey)
if _, err := vs.ValidatorStatus(context.Background(), req); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %v, received %v", want, err)
}
}
func TestValidatorStatus_PendingActive(t *testing.T) {
db := internal.SetupDB(t)
defer internal.TeardownDB(t, db)
@@ -342,6 +325,20 @@ func TestValidatorStatus_PendingActive(t *testing.T) {
}}); err != nil {
t.Fatalf("could not save state: %v", err)
}
depositInput := &pbp2p.DepositInput{
Pubkey: pubKey,
ProofOfPossession: []byte("hi"),
WithdrawalCredentialsHash32: []byte("hey"),
}
depData, err := helpers.EncodeDepositData(depositInput, params.BeaconConfig().MaxDepositAmount, 0)
if err != nil {
t.Fatal(err)
}
deposit := &pbp2p.Deposit{
DepositData: depData,
}
db.InsertDeposit(ctx, deposit, big.NewInt(0))
vs := &ValidatorServer{
beaconDB: db,
@@ -368,6 +365,21 @@ func TestValidatorStatus_Active(t *testing.T) {
t.Fatalf("Could not save validator index: %v", err)
}
depositInput := &pbp2p.DepositInput{
Pubkey: pubKey,
ProofOfPossession: []byte("hi"),
WithdrawalCredentialsHash32: []byte("hey"),
}
depData, err := helpers.EncodeDepositData(depositInput, params.BeaconConfig().MaxDepositAmount, 0)
if err != nil {
t.Fatal(err)
}
deposit := &pbp2p.Deposit{
DepositData: depData,
}
db.InsertDeposit(ctx, deposit, big.NewInt(0))
// Active because activation epoch <= current epoch < exit epoch.
if err := db.SaveState(ctx, &pbp2p.BeaconState{
Slot: params.BeaconConfig().GenesisSlot,
@@ -389,9 +401,24 @@ func TestValidatorStatus_Active(t *testing.T) {
if err != nil {
t.Fatalf("Could not get validator status %v", err)
}
if resp.Status != pb.ValidatorStatus_ACTIVE {
t.Errorf("Wanted %v, got %v", pb.ValidatorStatus_ACTIVE, resp.Status)
timeToInclusion := params.BeaconConfig().Eth1FollowDistance * params.BeaconConfig().GoerliBlockTime
votingPeriodSlots := helpers.StartSlot(params.BeaconConfig().EpochsPerEth1VotingPeriod)
depositBlockSlot := (timeToInclusion / params.BeaconConfig().SecondsPerSlot) + votingPeriodSlots
expected := &pb.ValidatorStatusResponse{
Status: pb.ValidatorStatus_ACTIVE,
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
DepositInclusionSlot: depositBlockSlot,
Eth1DepositBlockNumber: 0,
}
if !proto.Equal(resp, expected) {
t.Errorf("Wanted %v, got %v", expected, resp)
}
}
func TestValidatorStatus_PendingStateActivation(t *testing.T) {
}
func TestValidatorStatus_InitiatedExit(t *testing.T) {
@@ -413,7 +440,20 @@ func TestValidatorStatus_InitiatedExit(t *testing.T) {
}}); err != nil {
t.Fatalf("could not save state: %v", err)
}
depositInput := &pbp2p.DepositInput{
Pubkey: pubKey,
ProofOfPossession: []byte("hi"),
WithdrawalCredentialsHash32: []byte("hey"),
}
depData, err := helpers.EncodeDepositData(depositInput, params.BeaconConfig().MaxDepositAmount, 0)
if err != nil {
t.Fatal(err)
}
deposit := &pbp2p.Deposit{
DepositData: depData,
}
db.InsertDeposit(ctx, deposit, big.NewInt(0))
vs := &ValidatorServer{
beaconDB: db,
}
@@ -448,7 +488,20 @@ func TestValidatorStatus_Withdrawable(t *testing.T) {
}}); err != nil {
t.Fatalf("could not save state: %v", err)
}
depositInput := &pbp2p.DepositInput{
Pubkey: pubKey,
ProofOfPossession: []byte("hi"),
WithdrawalCredentialsHash32: []byte("hey"),
}
depData, err := helpers.EncodeDepositData(depositInput, params.BeaconConfig().MaxDepositAmount, 0)
if err != nil {
t.Fatal(err)
}
deposit := &pbp2p.Deposit{
DepositData: depData,
}
db.InsertDeposit(ctx, deposit, big.NewInt(0))
vs := &ValidatorServer{
beaconDB: db,
}
@@ -482,7 +535,20 @@ func TestValidatorStatus_ExitedSlashed(t *testing.T) {
}}); err != nil {
t.Fatalf("could not save state: %v", err)
}
depositInput := &pbp2p.DepositInput{
Pubkey: pubKey,
ProofOfPossession: []byte("hi"),
WithdrawalCredentialsHash32: []byte("hey"),
}
depData, err := helpers.EncodeDepositData(depositInput, params.BeaconConfig().MaxDepositAmount, 0)
if err != nil {
t.Fatal(err)
}
deposit := &pbp2p.Deposit{
DepositData: depData,
}
db.InsertDeposit(ctx, deposit, big.NewInt(0))
vs := &ValidatorServer{
beaconDB: db,
}
@@ -517,7 +583,20 @@ func TestValidatorStatus_Exited(t *testing.T) {
}}); err != nil {
t.Fatalf("could not save state: %v", err)
}
depositInput := &pbp2p.DepositInput{
Pubkey: pubKey,
ProofOfPossession: []byte("hi"),
WithdrawalCredentialsHash32: []byte("hey"),
}
depData, err := helpers.EncodeDepositData(depositInput, params.BeaconConfig().MaxDepositAmount, 0)
if err != nil {
t.Fatal(err)
}
deposit := &pbp2p.Deposit{
DepositData: depData,
}
db.InsertDeposit(ctx, deposit, big.NewInt(0))
vs := &ValidatorServer{
beaconDB: db,
}
@@ -553,6 +632,20 @@ func TestValidatorStatus_UnknownStatus(t *testing.T) {
t.Fatalf("could not save state: %v", err)
}
depositInput := &pbp2p.DepositInput{
Pubkey: pubKey,
ProofOfPossession: []byte("hi"),
WithdrawalCredentialsHash32: []byte("hey"),
}
depData, err := helpers.EncodeDepositData(depositInput, params.BeaconConfig().MaxDepositAmount, 0)
if err != nil {
t.Fatal(err)
}
deposit := &pbp2p.Deposit{
DepositData: depData,
}
db.InsertDeposit(ctx, deposit, big.NewInt(0))
vs := &ValidatorServer{
beaconDB: db,
}

View File

@@ -5,9 +5,10 @@ package ethereum_beacon_p2p_v1
import (
fmt "fmt"
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
proto "github.com/gogo/protobuf/proto"
)
// Reference imports to suppress errors if they are not otherwise used.

View File

@@ -5,10 +5,11 @@ package ethereum_beacon_p2p_v1
import (
fmt "fmt"
proto "github.com/gogo/protobuf/proto"
_ "github.com/prysmaticlabs/prysm/proto/common"
io "io"
math "math"
proto "github.com/gogo/protobuf/proto"
_ "github.com/prysmaticlabs/prysm/proto/common"
)
// Reference imports to suppress errors if they are not otherwise used.

View File

@@ -88,6 +88,7 @@ type BeaconChainConfig struct {
LogBlockDelay int64 // Number of blocks to wait from the current head before processing logs from the deposit contract.
RPCSyncCheck time.Duration // Number of seconds to query the sync service, to find out if the node is synced or not.
TestnetContractEndpoint string // TestnetContractEndpoint to fetch the contract address of the Prysmatic Labs testnet.
GoerliBlockTime uint64 // GoerliBlockTime is the number of seconds on avg a Goerli block is created.
}
// DepositContractConfig contains the deposits for
@@ -178,6 +179,7 @@ var defaultBeaconConfig = &BeaconChainConfig{
MaxNumLog2Validators: 24,
LogBlockDelay: 2, //
RPCSyncCheck: 1,
GoerliBlockTime: 14, // 14 seconds on average for a goerli block to be created.
// Testnet misc values.
TestnetContractEndpoint: "https://beta.prylabs.net/contract", // defines an http endpoint to fetch the testnet contract addr.

View File

@@ -6,11 +6,12 @@ package internal
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
v1 "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
v10 "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
grpc "google.golang.org/grpc"
reflect "reflect"
)
// MockAttesterServiceClient is a mock of AttesterServiceClient interface

View File

@@ -6,13 +6,14 @@ package internal
import (
context "context"
reflect "reflect"
types "github.com/gogo/protobuf/types"
gomock "github.com/golang/mock/gomock"
v1 "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
v10 "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
grpc "google.golang.org/grpc"
metadata "google.golang.org/grpc/metadata"
reflect "reflect"
)
// MockBeaconServiceClient is a mock of BeaconServiceClient interface

View File

@@ -6,11 +6,12 @@ package internal
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
v1 "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
v10 "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
grpc "google.golang.org/grpc"
reflect "reflect"
)
// MockProposerServiceClient is a mock of ProposerServiceClient interface

View File

@@ -6,11 +6,12 @@ package internal
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
v1 "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
grpc "google.golang.org/grpc"
metadata "google.golang.org/grpc/metadata"
reflect "reflect"
)
// MockValidatorServiceClient is a mock of ValidatorServiceClient interface