github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/prysm/v1alpha1/beacon/blocks.go (about)

     1  package beacon
     2  
     3  import (
     4  	"context"
     5  	"strconv"
     6  
     7  	"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
     8  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
     9  	blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block"
    10  	statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
    11  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    12  	"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
    13  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    14  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    15  	"github.com/prysmaticlabs/prysm/shared/cmd"
    16  	"github.com/prysmaticlabs/prysm/shared/event"
    17  	"github.com/prysmaticlabs/prysm/shared/pagination"
    18  	"github.com/prysmaticlabs/prysm/shared/params"
    19  	"google.golang.org/grpc/codes"
    20  	"google.golang.org/grpc/status"
    21  	"google.golang.org/protobuf/types/known/emptypb"
    22  )
    23  
    24  // ListBlocks retrieves blocks by root, slot, or epoch.
    25  //
    26  // The server may return multiple blocks in the case that a slot or epoch is
    27  // provided as the filter criteria. The server may return an empty list when
    28  // no blocks in their database match the filter criteria. This RPC should
    29  // not return NOT_FOUND. Only one filter criteria should be used.
    30  func (bs *Server) ListBlocks(
    31  	ctx context.Context, req *ethpb.ListBlocksRequest,
    32  ) (*ethpb.ListBlocksResponse, error) {
    33  	if int(req.PageSize) > cmd.Get().MaxRPCPageSize {
    34  		return nil, status.Errorf(codes.InvalidArgument, "Requested page size %d can not be greater than max size %d",
    35  			req.PageSize, cmd.Get().MaxRPCPageSize)
    36  	}
    37  
    38  	switch q := req.QueryFilter.(type) {
    39  	case *ethpb.ListBlocksRequest_Epoch:
    40  		blks, _, err := bs.BeaconDB.Blocks(ctx, filters.NewFilter().SetStartEpoch(q.Epoch).SetEndEpoch(q.Epoch))
    41  		if err != nil {
    42  			return nil, status.Errorf(codes.Internal, "Could not get blocks: %v", err)
    43  		}
    44  
    45  		numBlks := len(blks)
    46  		if numBlks == 0 {
    47  			return &ethpb.ListBlocksResponse{
    48  				BlockContainers: make([]*ethpb.BeaconBlockContainer, 0),
    49  				TotalSize:       0,
    50  				NextPageToken:   strconv.Itoa(0),
    51  			}, nil
    52  		}
    53  
    54  		start, end, nextPageToken, err := pagination.StartAndEndPage(req.PageToken, int(req.PageSize), numBlks)
    55  		if err != nil {
    56  			return nil, status.Errorf(codes.Internal, "Could not paginate blocks: %v", err)
    57  		}
    58  
    59  		returnedBlks := blks[start:end]
    60  		containers := make([]*ethpb.BeaconBlockContainer, len(returnedBlks))
    61  		for i, b := range returnedBlks {
    62  			root, err := b.Block().HashTreeRoot()
    63  			if err != nil {
    64  				return nil, err
    65  			}
    66  			canonical, err := bs.CanonicalFetcher.IsCanonical(ctx, root)
    67  			if err != nil {
    68  				return nil, status.Errorf(codes.Internal, "Could not determine if block is canonical: %v", err)
    69  			}
    70  			phBlk, err := b.PbPhase0Block()
    71  			if err != nil {
    72  				return nil, status.Errorf(codes.Internal, "Could not get phase 0 block: %v", err)
    73  			}
    74  			containers[i] = &ethpb.BeaconBlockContainer{
    75  				Block:     phBlk,
    76  				BlockRoot: root[:],
    77  				Canonical: canonical,
    78  			}
    79  		}
    80  
    81  		return &ethpb.ListBlocksResponse{
    82  			BlockContainers: containers,
    83  			TotalSize:       int32(numBlks),
    84  			NextPageToken:   nextPageToken,
    85  		}, nil
    86  	case *ethpb.ListBlocksRequest_Root:
    87  		blk, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(q.Root))
    88  		if err != nil {
    89  			return nil, status.Errorf(codes.Internal, "Could not retrieve block: %v", err)
    90  		}
    91  		if blk == nil || blk.IsNil() {
    92  			return &ethpb.ListBlocksResponse{
    93  				BlockContainers: make([]*ethpb.BeaconBlockContainer, 0),
    94  				TotalSize:       0,
    95  				NextPageToken:   strconv.Itoa(0),
    96  			}, nil
    97  		}
    98  		root, err := blk.Block().HashTreeRoot()
    99  		if err != nil {
   100  			return nil, err
   101  		}
   102  		canonical, err := bs.CanonicalFetcher.IsCanonical(ctx, root)
   103  		if err != nil {
   104  			return nil, status.Errorf(codes.Internal, "Could not determine if block is canonical: %v", err)
   105  		}
   106  		phBlk, err := blk.PbPhase0Block()
   107  		if err != nil {
   108  			return nil, status.Errorf(codes.Internal, "Could not determine if block is phase 0 block: %v", err)
   109  		}
   110  		return &ethpb.ListBlocksResponse{
   111  			BlockContainers: []*ethpb.BeaconBlockContainer{{
   112  				Block:     phBlk,
   113  				BlockRoot: root[:],
   114  				Canonical: canonical},
   115  			},
   116  			TotalSize: 1,
   117  		}, nil
   118  
   119  	case *ethpb.ListBlocksRequest_Slot:
   120  		hasBlocks, blks, err := bs.BeaconDB.BlocksBySlot(ctx, q.Slot)
   121  		if err != nil {
   122  			return nil, status.Errorf(codes.Internal, "Could not retrieve blocks for slot %d: %v", q.Slot, err)
   123  		}
   124  		if !hasBlocks {
   125  			return &ethpb.ListBlocksResponse{
   126  				BlockContainers: make([]*ethpb.BeaconBlockContainer, 0),
   127  				TotalSize:       0,
   128  				NextPageToken:   strconv.Itoa(0),
   129  			}, nil
   130  		}
   131  
   132  		numBlks := len(blks)
   133  
   134  		start, end, nextPageToken, err := pagination.StartAndEndPage(req.PageToken, int(req.PageSize), numBlks)
   135  		if err != nil {
   136  			return nil, status.Errorf(codes.Internal, "Could not paginate blocks: %v", err)
   137  		}
   138  
   139  		returnedBlks := blks[start:end]
   140  		containers := make([]*ethpb.BeaconBlockContainer, len(returnedBlks))
   141  		for i, b := range returnedBlks {
   142  			root, err := b.Block().HashTreeRoot()
   143  			if err != nil {
   144  				return nil, err
   145  			}
   146  			canonical, err := bs.CanonicalFetcher.IsCanonical(ctx, root)
   147  			if err != nil {
   148  				return nil, status.Errorf(codes.Internal, "Could not determine if block is canonical: %v", err)
   149  			}
   150  			phBlk, err := b.PbPhase0Block()
   151  			if err != nil {
   152  				return nil, status.Errorf(codes.Internal, "Could not determine if block is phase 0 block: %v", err)
   153  			}
   154  			containers[i] = &ethpb.BeaconBlockContainer{
   155  				Block:     phBlk,
   156  				BlockRoot: root[:],
   157  				Canonical: canonical,
   158  			}
   159  		}
   160  
   161  		return &ethpb.ListBlocksResponse{
   162  			BlockContainers: containers,
   163  			TotalSize:       int32(numBlks),
   164  			NextPageToken:   nextPageToken,
   165  		}, nil
   166  	case *ethpb.ListBlocksRequest_Genesis:
   167  		genBlk, err := bs.BeaconDB.GenesisBlock(ctx)
   168  		if err != nil {
   169  			return nil, status.Errorf(codes.Internal, "Could not retrieve blocks for genesis slot: %v", err)
   170  		}
   171  		if genBlk.IsNil() {
   172  			return nil, status.Error(codes.Internal, "Could not find genesis block")
   173  		}
   174  		root, err := genBlk.Block().HashTreeRoot()
   175  		if err != nil {
   176  			return nil, err
   177  		}
   178  		phBlk, err := genBlk.PbPhase0Block()
   179  		if err != nil {
   180  			return nil, status.Errorf(codes.Internal, "Could not determine if block is phase 0 block: %v", err)
   181  		}
   182  		containers := []*ethpb.BeaconBlockContainer{
   183  			{
   184  				Block:     phBlk,
   185  				BlockRoot: root[:],
   186  				Canonical: true,
   187  			},
   188  		}
   189  
   190  		return &ethpb.ListBlocksResponse{
   191  			BlockContainers: containers,
   192  			TotalSize:       int32(1),
   193  			NextPageToken:   strconv.Itoa(0),
   194  		}, nil
   195  	}
   196  
   197  	return nil, status.Error(codes.InvalidArgument, "Must specify a filter criteria for fetching blocks")
   198  }
   199  
   200  // GetChainHead retrieves information about the head of the beacon chain from
   201  // the view of the beacon chain node.
   202  //
   203  // This includes the head block slot and root as well as information about
   204  // the most recent finalized and justified slots.
   205  func (bs *Server) GetChainHead(ctx context.Context, _ *emptypb.Empty) (*ethpb.ChainHead, error) {
   206  	return bs.chainHeadRetrieval(ctx)
   207  }
   208  
   209  // StreamBlocks to clients every single time a block is received by the beacon node.
   210  func (bs *Server) StreamBlocks(req *ethpb.StreamBlocksRequest, stream ethpb.BeaconChain_StreamBlocksServer) error {
   211  	blocksChannel := make(chan *feed.Event, 1)
   212  	var blockSub event.Subscription
   213  	if req.VerifiedOnly {
   214  		blockSub = bs.StateNotifier.StateFeed().Subscribe(blocksChannel)
   215  	} else {
   216  		blockSub = bs.BlockNotifier.BlockFeed().Subscribe(blocksChannel)
   217  	}
   218  	defer blockSub.Unsubscribe()
   219  
   220  	for {
   221  		select {
   222  		case blockEvent := <-blocksChannel:
   223  			if req.VerifiedOnly {
   224  				if blockEvent.Type == statefeed.BlockProcessed {
   225  					data, ok := blockEvent.Data.(*statefeed.BlockProcessedData)
   226  					if !ok || data == nil {
   227  						continue
   228  					}
   229  					phBlk, err := data.SignedBlock.PbPhase0Block()
   230  					if err != nil {
   231  						log.Error(err)
   232  						continue
   233  					}
   234  					if err := stream.Send(phBlk); err != nil {
   235  						return status.Errorf(codes.Unavailable, "Could not send over stream: %v", err)
   236  					}
   237  				}
   238  			} else {
   239  				if blockEvent.Type == blockfeed.ReceivedBlock {
   240  					data, ok := blockEvent.Data.(*blockfeed.ReceivedBlockData)
   241  					if !ok {
   242  						// Got bad data over the stream.
   243  						continue
   244  					}
   245  					if data.SignedBlock == nil {
   246  						// One nil block shouldn't stop the stream.
   247  						continue
   248  					}
   249  					headState, err := bs.HeadFetcher.HeadState(bs.Ctx)
   250  					if err != nil {
   251  						log.WithError(err).WithField("blockSlot", data.SignedBlock.Block().Slot()).Error("Could not get head state")
   252  						continue
   253  					}
   254  					signed := data.SignedBlock
   255  					if err := blocks.VerifyBlockSignature(headState, signed.Block().ProposerIndex(), signed.Signature(), signed.Block().HashTreeRoot); err != nil {
   256  						log.WithError(err).WithField("blockSlot", data.SignedBlock.Block().Slot()).Error("Could not verify block signature")
   257  						continue
   258  					}
   259  					phBlk, err := signed.PbPhase0Block()
   260  					if err != nil {
   261  						log.Error(err)
   262  						continue
   263  					}
   264  					if err := stream.Send(phBlk); err != nil {
   265  						return status.Errorf(codes.Unavailable, "Could not send over stream: %v", err)
   266  					}
   267  				}
   268  			}
   269  		case <-blockSub.Err():
   270  			return status.Error(codes.Aborted, "Subscriber closed, exiting goroutine")
   271  		case <-bs.Ctx.Done():
   272  			return status.Error(codes.Canceled, "Context canceled")
   273  		case <-stream.Context().Done():
   274  			return status.Error(codes.Canceled, "Context canceled")
   275  		}
   276  	}
   277  }
   278  
   279  // StreamChainHead to clients every single time the head block and state of the chain change.
   280  func (bs *Server) StreamChainHead(_ *emptypb.Empty, stream ethpb.BeaconChain_StreamChainHeadServer) error {
   281  	stateChannel := make(chan *feed.Event, 1)
   282  	stateSub := bs.StateNotifier.StateFeed().Subscribe(stateChannel)
   283  	defer stateSub.Unsubscribe()
   284  	for {
   285  		select {
   286  		case stateEvent := <-stateChannel:
   287  			if stateEvent.Type == statefeed.BlockProcessed {
   288  				res, err := bs.chainHeadRetrieval(stream.Context())
   289  				if err != nil {
   290  					return status.Errorf(codes.Internal, "Could not retrieve chain head: %v", err)
   291  				}
   292  				if err := stream.Send(res); err != nil {
   293  					return status.Errorf(codes.Unavailable, "Could not send over stream: %v", err)
   294  				}
   295  			}
   296  		case <-stateSub.Err():
   297  			return status.Error(codes.Aborted, "Subscriber closed, exiting goroutine")
   298  		case <-bs.Ctx.Done():
   299  			return status.Error(codes.Canceled, "Context canceled")
   300  		case <-stream.Context().Done():
   301  			return status.Error(codes.Canceled, "Context canceled")
   302  		}
   303  	}
   304  }
   305  
   306  // Retrieve chain head information from the DB and the current beacon state.
   307  func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, error) {
   308  	headBlock, err := bs.HeadFetcher.HeadBlock(ctx)
   309  	if err != nil {
   310  		return nil, status.Error(codes.Internal, "Could not get head block")
   311  	}
   312  	if headBlock == nil || headBlock.IsNil() || headBlock.Block().IsNil() {
   313  		return nil, status.Error(codes.Internal, "Head block of chain was nil")
   314  	}
   315  	headBlockRoot, err := headBlock.Block().HashTreeRoot()
   316  	if err != nil {
   317  		return nil, status.Errorf(codes.Internal, "Could not get head block root: %v", err)
   318  	}
   319  
   320  	isGenesis := func(cp *ethpb.Checkpoint) bool {
   321  		return bytesutil.ToBytes32(cp.Root) == params.BeaconConfig().ZeroHash && cp.Epoch == 0
   322  	}
   323  	// Retrieve genesis block in the event we have genesis checkpoints.
   324  	genBlock, err := bs.BeaconDB.GenesisBlock(ctx)
   325  	if err != nil || genBlock == nil || genBlock.IsNil() || genBlock.Block().IsNil() {
   326  		return nil, status.Error(codes.Internal, "Could not get genesis block")
   327  	}
   328  
   329  	finalizedCheckpoint := bs.FinalizationFetcher.FinalizedCheckpt()
   330  	if !isGenesis(finalizedCheckpoint) {
   331  		b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(finalizedCheckpoint.Root))
   332  		if err != nil {
   333  			return nil, status.Error(codes.Internal, "Could not get finalized block")
   334  		}
   335  		if err := helpers.VerifyNilBeaconBlock(b); err != nil {
   336  			return nil, status.Errorf(codes.Internal, "Could not get finalized block: %v", err)
   337  		}
   338  	}
   339  
   340  	justifiedCheckpoint := bs.FinalizationFetcher.CurrentJustifiedCheckpt()
   341  	if !isGenesis(justifiedCheckpoint) {
   342  		b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(justifiedCheckpoint.Root))
   343  		if err != nil {
   344  			return nil, status.Error(codes.Internal, "Could not get justified block")
   345  		}
   346  		if err := helpers.VerifyNilBeaconBlock(b); err != nil {
   347  			return nil, status.Errorf(codes.Internal, "Could not get justified block: %v", err)
   348  		}
   349  	}
   350  
   351  	prevJustifiedCheckpoint := bs.FinalizationFetcher.PreviousJustifiedCheckpt()
   352  	if !isGenesis(prevJustifiedCheckpoint) {
   353  		b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(prevJustifiedCheckpoint.Root))
   354  		if err != nil {
   355  			return nil, status.Error(codes.Internal, "Could not get prev justified block")
   356  		}
   357  		if err := helpers.VerifyNilBeaconBlock(b); err != nil {
   358  			return nil, status.Errorf(codes.Internal, "Could not get prev justified block: %v", err)
   359  		}
   360  	}
   361  
   362  	fSlot, err := helpers.StartSlot(finalizedCheckpoint.Epoch)
   363  	if err != nil {
   364  		return nil, err
   365  	}
   366  	jSlot, err := helpers.StartSlot(justifiedCheckpoint.Epoch)
   367  	if err != nil {
   368  		return nil, err
   369  	}
   370  	pjSlot, err := helpers.StartSlot(prevJustifiedCheckpoint.Epoch)
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  	return &ethpb.ChainHead{
   375  		HeadSlot:                   headBlock.Block().Slot(),
   376  		HeadEpoch:                  helpers.SlotToEpoch(headBlock.Block().Slot()),
   377  		HeadBlockRoot:              headBlockRoot[:],
   378  		FinalizedSlot:              fSlot,
   379  		FinalizedEpoch:             finalizedCheckpoint.Epoch,
   380  		FinalizedBlockRoot:         finalizedCheckpoint.Root,
   381  		JustifiedSlot:              jSlot,
   382  		JustifiedEpoch:             justifiedCheckpoint.Epoch,
   383  		JustifiedBlockRoot:         justifiedCheckpoint.Root,
   384  		PreviousJustifiedSlot:      pjSlot,
   385  		PreviousJustifiedEpoch:     prevJustifiedCheckpoint.Epoch,
   386  		PreviousJustifiedBlockRoot: prevJustifiedCheckpoint.Root,
   387  	}, nil
   388  }
   389  
   390  // GetWeakSubjectivityCheckpoint retrieves weak subjectivity state root, block root, and epoch.
   391  func (bs *Server) GetWeakSubjectivityCheckpoint(ctx context.Context, _ *emptypb.Empty) (*ethpb.WeakSubjectivityCheckpoint, error) {
   392  	hs, err := bs.HeadFetcher.HeadState(ctx)
   393  	if err != nil {
   394  		return nil, status.Error(codes.Internal, "Could not get head state")
   395  	}
   396  	wsEpoch, err := helpers.LatestWeakSubjectivityEpoch(hs)
   397  	if err != nil {
   398  		return nil, status.Error(codes.Internal, "Could not get weak subjectivity epoch")
   399  	}
   400  	wsSlot, err := helpers.StartSlot(wsEpoch)
   401  	if err != nil {
   402  		return nil, status.Error(codes.Internal, "Could not get weak subjectivity slot")
   403  	}
   404  
   405  	wsState, err := bs.StateGen.StateBySlot(ctx, wsSlot)
   406  	if err != nil {
   407  		return nil, status.Error(codes.Internal, "Could not get weak subjectivity state")
   408  	}
   409  	stateRoot, err := wsState.HashTreeRoot(ctx)
   410  	if err != nil {
   411  		return nil, status.Error(codes.Internal, "Could not get weak subjectivity state root")
   412  	}
   413  	blkRoot, err := wsState.LatestBlockHeader().HashTreeRoot()
   414  	if err != nil {
   415  		return nil, status.Error(codes.Internal, "Could not get weak subjectivity block root")
   416  	}
   417  
   418  	return &ethpb.WeakSubjectivityCheckpoint{
   419  		BlockRoot: blkRoot[:],
   420  		StateRoot: stateRoot[:],
   421  		Epoch:     wsEpoch,
   422  	}, nil
   423  }