package rpc import ( "context" "fmt" "time" ptypes "github.com/gogo/protobuf/types" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/beacon-chain/db" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" "github.com/prysmaticlabs/prysm/shared/event" "github.com/prysmaticlabs/prysm/shared/trieutil" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) type stateFeedListener interface { StateInitializedFeed() *event.Feed } // BeaconServer defines a server implementation of the gRPC Beacon service, // providing RPC endpoints for obtaining the canonical beacon chain head, // fetching latest observed attestations, and more. type BeaconServer struct { beaconDB db.Database ctx context.Context powChainService powChainService chainService stateFeedListener operationService operationService incomingAttestation chan *ethpb.Attestation canonicalStateChan chan *pbp2p.BeaconState chainStartChan chan time.Time } // WaitForChainStart queries the logs of the Deposit Contract in order to verify the beacon chain // has started its runtime and validators begin their responsibilities. If it has not, it then // subscribes to an event stream triggered by the powchain service whenever the ChainStart log does // occur in the Deposit Contract on ETH 1.0. func (bs *BeaconServer) WaitForChainStart(req *ptypes.Empty, stream pb.BeaconService_WaitForChainStartServer) error { ok := bs.powChainService.HasChainStarted() if ok { genesisTime, _ := bs.powChainService.ETH2GenesisTime() res := &pb.ChainStartResponse{ Started: true, GenesisTime: genesisTime, } return stream.Send(res) } sub := bs.chainService.StateInitializedFeed().Subscribe(bs.chainStartChan) defer sub.Unsubscribe() for { select { case chainStartTime := <-bs.chainStartChan: log.Info("Sending ChainStart log and genesis time to connected validator clients") res := &pb.ChainStartResponse{ Started: true, GenesisTime: uint64(chainStartTime.Unix()), } return stream.Send(res) case <-sub.Err(): return errors.New("subscriber closed, exiting goroutine") case <-bs.ctx.Done(): return errors.New("rpc context closed, exiting goroutine") } } } // CanonicalHead of the current beacon chain. This method is requested on-demand // by a validator when it is their time to propose or attest. func (bs *BeaconServer) CanonicalHead(ctx context.Context, req *ptypes.Empty) (*ethpb.BeaconBlock, error) { var headBlock *ethpb.BeaconBlock var err error if d, isLegacyDB := bs.beaconDB.(*db.BeaconDB); isLegacyDB { headBlock, err = d.ChainHead() if err != nil { return nil, errors.Wrap(err, "could not get canonical head block") } } else { headBlock = bs.chainService.(blockchain.HeadRetriever).HeadBlock() } return headBlock, nil } // BlockTree returns the current tree of saved blocks and their votes starting from the justified state. func (bs *BeaconServer) BlockTree(ctx context.Context, _ *ptypes.Empty) (*pb.BlockTreeResponse, error) { // TODO(3219): Add after new fork choice service. return nil, status.Error(codes.Unimplemented, "not implemented") } // BlockTreeBySlots returns the current tree of saved blocks and their // votes starting from the justified state. func (bs *BeaconServer) BlockTreeBySlots(ctx context.Context, req *pb.TreeBlockSlotRequest) (*pb.BlockTreeResponse, error) { // TODO(3219): Add after new fork choice service. return nil, status.Error(codes.Unimplemented, "not implemented") } func constructMerkleProof(trie *trieutil.MerkleTrie, index int, deposit *ethpb.Deposit) (*ethpb.Deposit, error) { proof, err := trie.MerkleProof(index) if err != nil { return nil, fmt.Errorf( "could not generate merkle proof for deposit at index %d: %v", index, err, ) } // For every deposit, we construct a Merkle proof using the powchain service's // in-memory deposits trie, which is updated only once the state's LatestETH1Data // property changes during a state transition after a voting period. deposit.Proof = proof return deposit, nil }