prysm-pulse/beacon-chain/rpc/validator_server.go
2019-02-25 20:19:24 -08:00

166 lines
5.8 KiB
Go

package rpc
import (
"context"
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
// ValidatorServer defines a server implementation of the gRPC Validator service,
// providing RPC endpoints for obtaining validator assignments per epoch, the slots
// and shards in which particular validators need to perform their responsibilities,
// and more.
type ValidatorServer struct {
beaconDB *db.BeaconDB
}
// ValidatorIndex is called by a validator to get its index location that corresponds
// to the attestation bit fields.
func (vs *ValidatorServer) ValidatorIndex(ctx context.Context, req *pb.ValidatorIndexRequest) (*pb.ValidatorIndexResponse, error) {
index, err := vs.beaconDB.ValidatorIndex(req.PublicKey)
if err != nil {
return nil, fmt.Errorf("could not get validator index: %v", err)
}
return &pb.ValidatorIndexResponse{Index: uint64(index)}, nil
}
// ValidatorEpochAssignments fetches an assignment object for a validator by public key
// such as the slot the validator needs to attest in during the epoch as well as a slot
// in which the validator may need to propose during the epoch in addition to the assigned shard.
func (vs *ValidatorServer) ValidatorEpochAssignments(
ctx context.Context,
req *pb.ValidatorEpochAssignmentsRequest,
) (*pb.ValidatorEpochAssignmentsResponse, error) {
if len(req.PublicKey) != params.BeaconConfig().BLSPubkeyLength {
return nil, fmt.Errorf(
"expected public key to have length %d, received %d",
params.BeaconConfig().BLSPubkeyLength,
len(req.PublicKey),
)
}
beaconState, err := vs.beaconDB.State()
if err != nil {
return nil, fmt.Errorf("could not get beacon state: %v", err)
}
validatorIndex, err := vs.beaconDB.ValidatorIndex(req.PublicKey)
if err != nil {
return nil, fmt.Errorf("could not get validator index: %v", err)
}
var shard uint64
var attesterSlot uint64
var proposerSlot uint64
for slot := req.EpochStart; slot < req.EpochStart+params.BeaconConfig().SlotsPerEpoch; slot++ {
var registryChanged bool
if beaconState.ValidatorRegistryUpdateEpoch == helpers.SlotToEpoch(slot)-1 &&
beaconState.ValidatorRegistryUpdateEpoch != params.BeaconConfig().GenesisEpoch {
registryChanged = true
}
crossLinkCommittees, err := helpers.CrosslinkCommitteesAtSlot(beaconState, slot, registryChanged /* registry change */)
if err != nil {
return nil, fmt.Errorf("could not get crosslink committees at slot %d: %v", slot-params.BeaconConfig().GenesisSlot, err)
}
proposerIndex, err := helpers.BeaconProposerIndex(beaconState, slot)
if err != nil {
return nil, err
}
log.Infof("Proposer index: %d, slot: %d", proposerIndex, slot-params.BeaconConfig().GenesisSlot)
if proposerIndex == uint64(validatorIndex) {
proposerSlot = slot
}
for _, committee := range crossLinkCommittees {
for _, idx := range committee.Committee {
if idx == uint64(validatorIndex) {
attesterSlot = slot
shard = committee.Shard
}
}
}
}
return &pb.ValidatorEpochAssignmentsResponse{
Assignment: &pb.Assignment{
PublicKey: req.PublicKey,
Shard: shard,
AttesterSlot: attesterSlot,
ProposerSlot: proposerSlot,
},
}, nil
}
// ValidatorCommitteeAtSlot gets the committee at a certain slot where a validator's index is contained.
func (vs *ValidatorServer) ValidatorCommitteeAtSlot(ctx context.Context, req *pb.CommitteeRequest) (*pb.CommitteeResponse, error) {
beaconState, err := vs.beaconDB.State()
if err != nil {
return nil, fmt.Errorf("could not fetch beacon state: %v", err)
}
var registryChanged bool
if beaconState.ValidatorRegistryUpdateEpoch == helpers.SlotToEpoch(req.Slot)-1 &&
beaconState.ValidatorRegistryUpdateEpoch != params.BeaconConfig().GenesisEpoch {
registryChanged = true
}
crossLinkCommittees, err := helpers.CrosslinkCommitteesAtSlot(beaconState, req.Slot, registryChanged /* registry change */)
if err != nil {
return nil, fmt.Errorf("could not get crosslink committees at slot %d: %v", req.Slot-params.BeaconConfig().GenesisSlot, err)
}
var committee []uint64
var shard uint64
var indexFound bool
for _, com := range crossLinkCommittees {
for _, i := range com.Committee {
if i == req.ValidatorIndex {
committee = com.Committee
shard = com.Shard
indexFound = true
break
}
}
// Do not keep iterating over committees once the validator's
// index has been found in the inner for loop.
if indexFound {
break
}
}
return &pb.CommitteeResponse{
Committee: committee,
Shard: shard,
}, nil
}
// NextEpochCommitteeAssignment returns the committee assignment response from a given validator public key.
// The committee assignment response contains the following fields for the next epoch:
// 1.) The list of validators in the committee.
// 2.) The shard to which the committee is assigned.
// 3.) The slot at which the committee is assigned.
// 4.) The bool signalling if the validator is expected to propose a block at the assigned slot.
func (vs *ValidatorServer) NextEpochCommitteeAssignment(
ctx context.Context,
req *pb.ValidatorIndexRequest) (*pb.CommitteeAssignmentResponse, error) {
state, err := vs.beaconDB.State()
if err != nil {
return nil, fmt.Errorf("could not fetch beacon state: %v", err)
}
idx, err := vs.beaconDB.ValidatorIndex(req.PublicKey)
if err != nil {
return nil, fmt.Errorf("could not get active validator index: %v", err)
}
committee, shard, slot, isProposer, err :=
helpers.NextEpochCommitteeAssignment(state, uint64(idx), false)
if err != nil {
return nil, fmt.Errorf("could not get next epoch committee assignment: %v", err)
}
return &pb.CommitteeAssignmentResponse{
Committee: committee,
Shard: shard,
Slot: slot,
IsProposer: isProposer,
}, nil
}