github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/prysm/v1alpha1/validator/server.go (about) 1 // Package validator defines a gRPC validator service implementation, providing 2 // critical endpoints for validator clients to submit blocks/attestations to the 3 // beacon node, receive assignments, and more. 4 package validator 5 6 import ( 7 "context" 8 "time" 9 10 "github.com/pkg/errors" 11 "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" 12 "github.com/prysmaticlabs/prysm/beacon-chain/cache" 13 "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" 14 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" 15 blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block" 16 opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" 17 statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" 18 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 19 "github.com/prysmaticlabs/prysm/beacon-chain/db" 20 "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" 21 "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" 22 "github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits" 23 "github.com/prysmaticlabs/prysm/beacon-chain/p2p" 24 "github.com/prysmaticlabs/prysm/beacon-chain/powchain" 25 "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" 26 "github.com/prysmaticlabs/prysm/beacon-chain/sync" 27 pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 28 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 29 "github.com/prysmaticlabs/prysm/shared/bytesutil" 30 "github.com/prysmaticlabs/prysm/shared/params" 31 "google.golang.org/grpc/codes" 32 "google.golang.org/grpc/status" 33 "google.golang.org/protobuf/types/known/emptypb" 34 ) 35 36 // Server defines a server implementation of the gRPC Validator service, 37 // providing RPC endpoints for obtaining validator assignments per epoch, the slots 38 // and committees in which particular validators need to perform their responsibilities, 39 // and more. 40 type Server struct { 41 Ctx context.Context 42 BeaconDB db.NoHeadAccessDatabase 43 AttestationCache *cache.AttestationCache 44 HeadFetcher blockchain.HeadFetcher 45 ForkFetcher blockchain.ForkFetcher 46 FinalizationFetcher blockchain.FinalizationFetcher 47 TimeFetcher blockchain.TimeFetcher 48 CanonicalStateChan chan *pbp2p.BeaconState 49 BlockFetcher powchain.POWBlockFetcher 50 DepositFetcher depositcache.DepositFetcher 51 ChainStartFetcher powchain.ChainStartFetcher 52 Eth1InfoFetcher powchain.ChainInfoFetcher 53 SyncChecker sync.Checker 54 StateNotifier statefeed.Notifier 55 BlockNotifier blockfeed.Notifier 56 P2P p2p.Broadcaster 57 AttPool attestations.Pool 58 SlashingsPool slashings.PoolManager 59 ExitPool voluntaryexits.PoolManager 60 BlockReceiver blockchain.BlockReceiver 61 MockEth1Votes bool 62 Eth1BlockFetcher powchain.POWBlockFetcher 63 PendingDepositsFetcher depositcache.PendingDepositsFetcher 64 OperationNotifier opfeed.Notifier 65 StateGen stategen.StateManager 66 } 67 68 // WaitForActivation checks if a validator public key exists in the active validator registry of the current 69 // beacon state, if not, then it creates a stream which listens for canonical states which contain 70 // the validator with the public key as an active validator record. 71 func (vs *Server) WaitForActivation(req *ethpb.ValidatorActivationRequest, stream ethpb.BeaconNodeValidator_WaitForActivationServer) error { 72 activeValidatorExists, validatorStatuses, err := vs.activationStatus(stream.Context(), req.PublicKeys) 73 if err != nil { 74 return status.Errorf(codes.Internal, "Could not fetch validator status: %v", err) 75 } 76 res := ðpb.ValidatorActivationResponse{ 77 Statuses: validatorStatuses, 78 } 79 if activeValidatorExists { 80 return stream.Send(res) 81 } 82 if err := stream.Send(res); err != nil { 83 return status.Errorf(codes.Internal, "Could not send response over stream: %v", err) 84 } 85 86 for { 87 select { 88 // Pinging every slot for activation. 89 case <-time.After(time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second): 90 activeValidatorExists, validatorStatuses, err := vs.activationStatus(stream.Context(), req.PublicKeys) 91 if err != nil { 92 return status.Errorf(codes.Internal, "Could not fetch validator status: %v", err) 93 } 94 res := ðpb.ValidatorActivationResponse{ 95 Statuses: validatorStatuses, 96 } 97 if activeValidatorExists { 98 return stream.Send(res) 99 } 100 if err := stream.Send(res); err != nil { 101 return status.Errorf(codes.Internal, "Could not send response over stream: %v", err) 102 } 103 case <-stream.Context().Done(): 104 return status.Error(codes.Canceled, "Stream context canceled") 105 case <-vs.Ctx.Done(): 106 return status.Error(codes.Canceled, "RPC context canceled") 107 } 108 } 109 } 110 111 // ValidatorIndex is called by a validator to get its index location in the beacon state. 112 func (vs *Server) ValidatorIndex(ctx context.Context, req *ethpb.ValidatorIndexRequest) (*ethpb.ValidatorIndexResponse, error) { 113 st, err := vs.HeadFetcher.HeadState(ctx) 114 if err != nil { 115 return nil, status.Errorf(codes.Internal, "Could not determine head state: %v", err) 116 } 117 index, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(req.PublicKey)) 118 if !ok { 119 return nil, status.Errorf(codes.Internal, "Could not find validator index for public key %#x not found", req.PublicKey) 120 } 121 122 return ðpb.ValidatorIndexResponse{Index: index}, nil 123 } 124 125 // DomainData fetches the current domain version information from the beacon state. 126 func (vs *Server) DomainData(_ context.Context, request *ethpb.DomainRequest) (*ethpb.DomainResponse, error) { 127 fork := vs.ForkFetcher.CurrentFork() 128 headGenesisValidatorRoot := vs.HeadFetcher.HeadGenesisValidatorRoot() 129 dv, err := helpers.Domain(fork, request.Epoch, bytesutil.ToBytes4(request.Domain), headGenesisValidatorRoot[:]) 130 if err != nil { 131 return nil, err 132 } 133 return ðpb.DomainResponse{ 134 SignatureDomain: dv, 135 }, nil 136 } 137 138 // CanonicalHead of the current beacon chain. This method is requested on-demand 139 // by a validator when it is their time to propose or attest. 140 func (vs *Server) CanonicalHead(ctx context.Context, _ *emptypb.Empty) (*ethpb.SignedBeaconBlock, error) { 141 headBlk, err := vs.HeadFetcher.HeadBlock(ctx) 142 if err != nil { 143 return nil, status.Errorf(codes.Internal, "Could not get head block: %v", err) 144 } 145 b, err := headBlk.PbPhase0Block() 146 if err != nil { 147 return nil, status.Errorf(codes.Internal, "Could not get head block: %v", err) 148 } 149 return b, nil 150 } 151 152 // WaitForChainStart queries the logs of the Deposit Contract in order to verify the beacon chain 153 // has started its runtime and validators begin their responsibilities. If it has not, it then 154 // subscribes to an event stream triggered by the powchain service whenever the ChainStart log does 155 // occur in the Deposit Contract on ETH 1.0. 156 func (vs *Server) WaitForChainStart(_ *emptypb.Empty, stream ethpb.BeaconNodeValidator_WaitForChainStartServer) error { 157 head, err := vs.HeadFetcher.HeadState(stream.Context()) 158 if err != nil { 159 return status.Errorf(codes.Internal, "Could not retrieve head state: %v", err) 160 } 161 if head != nil && !head.IsNil() { 162 res := ðpb.ChainStartResponse{ 163 Started: true, 164 GenesisTime: head.GenesisTime(), 165 GenesisValidatorsRoot: head.GenesisValidatorRoot(), 166 } 167 return stream.Send(res) 168 } 169 170 stateChannel := make(chan *feed.Event, 1) 171 stateSub := vs.StateNotifier.StateFeed().Subscribe(stateChannel) 172 defer stateSub.Unsubscribe() 173 for { 174 select { 175 case event := <-stateChannel: 176 if event.Type == statefeed.Initialized { 177 data, ok := event.Data.(*statefeed.InitializedData) 178 if !ok { 179 return errors.New("event data is not type *statefeed.InitializedData") 180 } 181 log.WithField("starttime", data.StartTime).Debug("Received chain started event") 182 log.Debug("Sending genesis time notification to connected validator clients") 183 res := ðpb.ChainStartResponse{ 184 Started: true, 185 GenesisTime: uint64(data.StartTime.Unix()), 186 GenesisValidatorsRoot: data.GenesisValidatorsRoot, 187 } 188 return stream.Send(res) 189 } 190 case <-stateSub.Err(): 191 return status.Error(codes.Aborted, "Subscriber closed, exiting goroutine") 192 case <-vs.Ctx.Done(): 193 return status.Error(codes.Canceled, "Context canceled") 194 } 195 } 196 }