prysm-pulse/beacon-chain/rpc/eth/v1/validator/validator.go

164 lines
6.7 KiB
Go
Raw Normal View History

package validator
import (
"context"
emptypb "github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
statev1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
v1 "github.com/prysmaticlabs/prysm/proto/eth/v1"
"go.opencensus.io/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// GetAttesterDuties requests the beacon node to provide a set of attestation duties,
// which should be performed by validators, for a particular epoch.
func (vs *Server) GetAttesterDuties(ctx context.Context, req *v1.AttesterDutiesRequest) (*v1.AttesterDutiesResponse, error) {
ctx, span := trace.StartSpan(ctx, "validatorv1.GetAttesterDuties")
defer span.End()
if vs.SyncChecker.Syncing() {
return nil, status.Error(codes.Unavailable, "Syncing to latest head, not ready to respond")
}
cs := vs.TimeFetcher.CurrentSlot()
currentEpoch := helpers.SlotToEpoch(cs)
if req.Epoch > currentEpoch+1 {
return nil, status.Errorf(codes.InvalidArgument, "Request epoch %d can not be greater than next epoch %d", req.Epoch, currentEpoch+1)
}
s, err := vs.HeadFetcher.HeadState(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get head state: %v", err)
}
// Advance state with empty transitions up to the requested epoch start slot.
// In case 1 epoch ahead was requested, we take the start slot of the current epoch.
// Taking the start slot of the next epoch would result in an error inside state.ProcessSlots.
var epochStartSlot types.Slot
if req.Epoch == currentEpoch+1 {
epochStartSlot, err = helpers.StartSlot(req.Epoch.Sub(1))
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not obtain epoch's start slot: %v", err)
}
} else {
epochStartSlot, err = helpers.StartSlot(req.Epoch)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not obtain epoch's start slot: %v", err)
}
}
if s.Slot() < epochStartSlot {
s, err = state.ProcessSlots(ctx, s, epochStartSlot)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not process slots up to %d: %v", epochStartSlot, err)
}
}
committeeAssignments, _, err := helpers.CommitteeAssignments(s, req.Epoch)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not compute committee assignments: %v", err)
}
activeValidatorCount, err := helpers.ActiveValidatorCount(s, req.Epoch)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get active validator count: %v", err)
}
committeesAtSlot := helpers.SlotCommitteeCount(activeValidatorCount)
duties := make([]*v1.AttesterDuty, len(req.Index))
for i, index := range req.Index {
val, err := s.ValidatorAtIndexReadOnly(index)
if _, ok := err.(*statev1.ValidatorIndexOutOfRangeError); ok {
return nil, status.Errorf(codes.InvalidArgument, "Invalid index: %v", err)
} else if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get validator: %v", err)
}
pubkey := val.PublicKey()
committee := committeeAssignments[index]
var valIndexInCommittee types.CommitteeIndex
// valIndexInCommittee will be 0 in case we don't get a match. This is a potential false positive,
// however it's an impossible condition because every validator must be assigned to a committee.
for cIndex, vIndex := range committee.Committee {
if vIndex == index {
valIndexInCommittee = types.CommitteeIndex(uint64(cIndex))
break
}
}
duties[i] = &v1.AttesterDuty{
Pubkey: pubkey[:],
ValidatorIndex: index,
CommitteeIndex: committee.CommitteeIndex,
CommitteeLength: uint64(len(committee.Committee)),
CommitteesAtSlot: committeesAtSlot,
ValidatorCommitteeIndex: valIndexInCommittee,
Slot: committee.AttesterSlot,
}
}
root, err := dependentRoot(s, req.Epoch)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get dependent root: %v", err)
}
return &v1.AttesterDutiesResponse{
DependentRoot: root,
Data: duties,
}, nil
}
// GetProposerDuties requests beacon node to provide all validators that are scheduled to propose a block in the given epoch.
func (vs *Server) GetProposerDuties(ctx context.Context, req *v1.ProposerDutiesRequest) (*v1.ProposerDutiesResponse, error) {
return nil, errors.New("Unimplemented")
}
// ProduceBlock requests the beacon node to produce a valid unsigned beacon block, which can then be signed by a proposer and submitted.
func (vs *Server) ProduceBlock(ctx context.Context, req *v1.ProduceBlockRequest) (*v1.ProduceBlockResponse, error) {
return nil, errors.New("Unimplemented")
}
// ProduceAttestationData requests that the beacon node produces attestation data for
// the requested committee index and slot based on the nodes current head.
func (vs *Server) ProduceAttestationData(ctx context.Context, req *v1.ProduceAttestationDataRequest) (*v1.ProduceAttestationDataResponse, error) {
return nil, errors.New("Unimplemented")
}
// GetAggregateAttestation aggregates all attestations matching the given attestation data root and slot, returning the aggregated result.
func (vs *Server) GetAggregateAttestation(ctx context.Context, req *v1.AggregateAttestationRequest) (*v1.AggregateAttestationResponse, error) {
return nil, errors.New("Unimplemented")
}
// SubmitAggregateAndProofs verifies given aggregate and proofs and publishes them on appropriate gossipsub topic.
func (vs *Server) SubmitAggregateAndProofs(ctx context.Context, req *v1.SubmitAggregateAndProofsRequest) (*emptypb.Empty, error) {
return nil, errors.New("Unimplemented")
}
// SubmitBeaconCommitteeSubscription searches using discv5 for peers related to the provided subnet information
// and replaces current peers with those ones if necessary.
func (vs *Server) SubmitBeaconCommitteeSubscription(ctx context.Context, req *v1.SubmitBeaconCommitteeSubscriptionsRequest) (*emptypb.Empty, error) {
return nil, errors.New("Unimplemented")
}
// dependentRoot is get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch - 1) - 1)
// or the genesis block root in the case of underflow.
func dependentRoot(s iface.BeaconState, epoch types.Epoch) ([]byte, error) {
var dependentRootSlot types.Slot
if epoch <= 1 {
dependentRootSlot = 0
} else {
prevEpochStartSlot, err := helpers.StartSlot(epoch.Sub(1))
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not obtain epoch's start slot: %v", err)
}
dependentRootSlot = prevEpochStartSlot.Sub(1)
}
root, err := helpers.BlockRootAtSlot(s, dependentRootSlot)
if err != nil {
return nil, errors.Wrap(err, "could not get block root")
}
return root, nil
}