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

     1  package kv
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  
     8  	"github.com/pkg/errors"
     9  	types "github.com/prysmaticlabs/eth2-types"
    10  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    11  	"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
    12  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    13  	"github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper"
    14  	"github.com/prysmaticlabs/prysm/proto/interfaces"
    15  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    16  	"github.com/prysmaticlabs/prysm/shared/params"
    17  	"github.com/prysmaticlabs/prysm/shared/sliceutil"
    18  	bolt "go.etcd.io/bbolt"
    19  	"go.opencensus.io/trace"
    20  )
    21  
    22  // used to represent errors for inconsistent slot ranges.
    23  var errInvalidSlotRange = errors.New("invalid end slot and start slot provided")
    24  
    25  // Block retrieval by root.
    26  func (s *Store) Block(ctx context.Context, blockRoot [32]byte) (interfaces.SignedBeaconBlock, error) {
    27  	ctx, span := trace.StartSpan(ctx, "BeaconDB.Block")
    28  	defer span.End()
    29  	// Return block from cache if it exists.
    30  	if v, ok := s.blockCache.Get(string(blockRoot[:])); v != nil && ok {
    31  		return v.(interfaces.SignedBeaconBlock), nil
    32  	}
    33  	var block *ethpb.SignedBeaconBlock
    34  	err := s.db.View(func(tx *bolt.Tx) error {
    35  		bkt := tx.Bucket(blocksBucket)
    36  		enc := bkt.Get(blockRoot[:])
    37  		if enc == nil {
    38  			return nil
    39  		}
    40  		block = &ethpb.SignedBeaconBlock{}
    41  		return decode(ctx, enc, block)
    42  	})
    43  	return wrapper.WrappedPhase0SignedBeaconBlock(block), err
    44  }
    45  
    46  // HeadBlock returns the latest canonical block in the Ethereum Beacon Chain.
    47  func (s *Store) HeadBlock(ctx context.Context) (interfaces.SignedBeaconBlock, error) {
    48  	ctx, span := trace.StartSpan(ctx, "BeaconDB.HeadBlock")
    49  	defer span.End()
    50  	var headBlock *ethpb.SignedBeaconBlock
    51  	err := s.db.View(func(tx *bolt.Tx) error {
    52  		bkt := tx.Bucket(blocksBucket)
    53  		headRoot := bkt.Get(headBlockRootKey)
    54  		if headRoot == nil {
    55  			return nil
    56  		}
    57  		enc := bkt.Get(headRoot)
    58  		if enc == nil {
    59  			return nil
    60  		}
    61  		headBlock = &ethpb.SignedBeaconBlock{}
    62  		return decode(ctx, enc, headBlock)
    63  	})
    64  	return wrapper.WrappedPhase0SignedBeaconBlock(headBlock), err
    65  }
    66  
    67  // Blocks retrieves a list of beacon blocks and its respective roots by filter criteria.
    68  func (s *Store) Blocks(ctx context.Context, f *filters.QueryFilter) ([]interfaces.SignedBeaconBlock, [][32]byte, error) {
    69  	ctx, span := trace.StartSpan(ctx, "BeaconDB.Blocks")
    70  	defer span.End()
    71  	blocks := make([]interfaces.SignedBeaconBlock, 0)
    72  	blockRoots := make([][32]byte, 0)
    73  
    74  	err := s.db.View(func(tx *bolt.Tx) error {
    75  		bkt := tx.Bucket(blocksBucket)
    76  
    77  		keys, err := blockRootsByFilter(ctx, tx, f)
    78  		if err != nil {
    79  			return err
    80  		}
    81  
    82  		for i := 0; i < len(keys); i++ {
    83  			encoded := bkt.Get(keys[i])
    84  			block := &ethpb.SignedBeaconBlock{}
    85  			if err := decode(ctx, encoded, block); err != nil {
    86  				return err
    87  			}
    88  			blocks = append(blocks, wrapper.WrappedPhase0SignedBeaconBlock(block))
    89  			blockRoots = append(blockRoots, bytesutil.ToBytes32(keys[i]))
    90  		}
    91  		return nil
    92  	})
    93  	return blocks, blockRoots, err
    94  }
    95  
    96  // BlockRoots retrieves a list of beacon block roots by filter criteria. If the caller
    97  // requires both the blocks and the block roots for a certain filter they should instead
    98  // use the Blocks function rather than use BlockRoots. During periods of non finality
    99  // there are potential race conditions which leads to differing roots when calling the db
   100  // multiple times for the same filter.
   101  func (s *Store) BlockRoots(ctx context.Context, f *filters.QueryFilter) ([][32]byte, error) {
   102  	ctx, span := trace.StartSpan(ctx, "BeaconDB.BlockRoots")
   103  	defer span.End()
   104  	blockRoots := make([][32]byte, 0)
   105  	err := s.db.View(func(tx *bolt.Tx) error {
   106  		keys, err := blockRootsByFilter(ctx, tx, f)
   107  		if err != nil {
   108  			return err
   109  		}
   110  
   111  		for i := 0; i < len(keys); i++ {
   112  			blockRoots = append(blockRoots, bytesutil.ToBytes32(keys[i]))
   113  		}
   114  		return nil
   115  	})
   116  	if err != nil {
   117  		return nil, errors.Wrap(err, "could not retrieve block roots")
   118  	}
   119  	return blockRoots, nil
   120  }
   121  
   122  // HasBlock checks if a block by root exists in the db.
   123  func (s *Store) HasBlock(ctx context.Context, blockRoot [32]byte) bool {
   124  	ctx, span := trace.StartSpan(ctx, "BeaconDB.HasBlock")
   125  	defer span.End()
   126  	if v, ok := s.blockCache.Get(string(blockRoot[:])); v != nil && ok {
   127  		return true
   128  	}
   129  	exists := false
   130  	if err := s.db.View(func(tx *bolt.Tx) error {
   131  		bkt := tx.Bucket(blocksBucket)
   132  		exists = bkt.Get(blockRoot[:]) != nil
   133  		return nil
   134  	}); err != nil { // This view never returns an error, but we'll handle anyway for sanity.
   135  		panic(err)
   136  	}
   137  	return exists
   138  }
   139  
   140  // BlocksBySlot retrieves a list of beacon blocks and its respective roots by slot.
   141  func (s *Store) BlocksBySlot(ctx context.Context, slot types.Slot) (bool, []interfaces.SignedBeaconBlock, error) {
   142  	ctx, span := trace.StartSpan(ctx, "BeaconDB.BlocksBySlot")
   143  	defer span.End()
   144  	blocks := make([]interfaces.SignedBeaconBlock, 0)
   145  
   146  	err := s.db.View(func(tx *bolt.Tx) error {
   147  		bkt := tx.Bucket(blocksBucket)
   148  
   149  		keys, err := blockRootsBySlot(ctx, tx, slot)
   150  		if err != nil {
   151  			return err
   152  		}
   153  
   154  		for i := 0; i < len(keys); i++ {
   155  			encoded := bkt.Get(keys[i])
   156  			block := &ethpb.SignedBeaconBlock{}
   157  			if err := decode(ctx, encoded, block); err != nil {
   158  				return err
   159  			}
   160  			blocks = append(blocks, wrapper.WrappedPhase0SignedBeaconBlock(block))
   161  		}
   162  		return nil
   163  	})
   164  	return len(blocks) > 0, blocks, err
   165  }
   166  
   167  // BlockRootsBySlot retrieves a list of beacon block roots by slot
   168  func (s *Store) BlockRootsBySlot(ctx context.Context, slot types.Slot) (bool, [][32]byte, error) {
   169  	ctx, span := trace.StartSpan(ctx, "BeaconDB.BlockRootsBySlot")
   170  	defer span.End()
   171  	blockRoots := make([][32]byte, 0)
   172  	err := s.db.View(func(tx *bolt.Tx) error {
   173  		keys, err := blockRootsBySlot(ctx, tx, slot)
   174  		if err != nil {
   175  			return err
   176  		}
   177  
   178  		for i := 0; i < len(keys); i++ {
   179  			blockRoots = append(blockRoots, bytesutil.ToBytes32(keys[i]))
   180  		}
   181  		return nil
   182  	})
   183  	if err != nil {
   184  		return false, nil, errors.Wrap(err, "could not retrieve block roots by slot")
   185  	}
   186  	return len(blockRoots) > 0, blockRoots, nil
   187  }
   188  
   189  // deleteBlock by block root.
   190  func (s *Store) deleteBlock(ctx context.Context, blockRoot [32]byte) error {
   191  	ctx, span := trace.StartSpan(ctx, "BeaconDB.deleteBlock")
   192  	defer span.End()
   193  	return s.db.Update(func(tx *bolt.Tx) error {
   194  		bkt := tx.Bucket(blocksBucket)
   195  		enc := bkt.Get(blockRoot[:])
   196  		if enc == nil {
   197  			return nil
   198  		}
   199  		block := &ethpb.SignedBeaconBlock{}
   200  		if err := decode(ctx, enc, block); err != nil {
   201  			return err
   202  		}
   203  		indicesByBucket := createBlockIndicesFromBlock(ctx, wrapper.WrappedPhase0BeaconBlock(block.Block))
   204  		if err := deleteValueForIndices(ctx, indicesByBucket, blockRoot[:], tx); err != nil {
   205  			return errors.Wrap(err, "could not delete root for DB indices")
   206  		}
   207  		s.blockCache.Del(string(blockRoot[:]))
   208  		return bkt.Delete(blockRoot[:])
   209  	})
   210  }
   211  
   212  // deleteBlocks by block roots.
   213  func (s *Store) deleteBlocks(ctx context.Context, blockRoots [][32]byte) error {
   214  	ctx, span := trace.StartSpan(ctx, "BeaconDB.deleteBlocks")
   215  	defer span.End()
   216  
   217  	return s.db.Update(func(tx *bolt.Tx) error {
   218  		bkt := tx.Bucket(blocksBucket)
   219  		for _, blockRoot := range blockRoots {
   220  			enc := bkt.Get(blockRoot[:])
   221  			if enc == nil {
   222  				return nil
   223  			}
   224  			block := &ethpb.SignedBeaconBlock{}
   225  			if err := decode(ctx, enc, block); err != nil {
   226  				return err
   227  			}
   228  			indicesByBucket := createBlockIndicesFromBlock(ctx, wrapper.WrappedPhase0BeaconBlock(block.Block))
   229  			if err := deleteValueForIndices(ctx, indicesByBucket, blockRoot[:], tx); err != nil {
   230  				return errors.Wrap(err, "could not delete root for DB indices")
   231  			}
   232  			s.blockCache.Del(string(blockRoot[:]))
   233  			if err := bkt.Delete(blockRoot[:]); err != nil {
   234  				return err
   235  			}
   236  		}
   237  		return nil
   238  	})
   239  }
   240  
   241  // SaveBlock to the db.
   242  func (s *Store) SaveBlock(ctx context.Context, signed interfaces.SignedBeaconBlock) error {
   243  	ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveBlock")
   244  	defer span.End()
   245  	blockRoot, err := signed.Block().HashTreeRoot()
   246  	if err != nil {
   247  		return err
   248  	}
   249  	if v, ok := s.blockCache.Get(string(blockRoot[:])); v != nil && ok {
   250  		return nil
   251  	}
   252  
   253  	return s.SaveBlocks(ctx, []interfaces.SignedBeaconBlock{signed})
   254  }
   255  
   256  // SaveBlocks via bulk updates to the db.
   257  func (s *Store) SaveBlocks(ctx context.Context, blocks []interfaces.SignedBeaconBlock) error {
   258  	ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveBlocks")
   259  	defer span.End()
   260  
   261  	return s.db.Update(func(tx *bolt.Tx) error {
   262  		bkt := tx.Bucket(blocksBucket)
   263  		for _, block := range blocks {
   264  			blockRoot, err := block.Block().HashTreeRoot()
   265  			if err != nil {
   266  				return err
   267  			}
   268  
   269  			if existingBlock := bkt.Get(blockRoot[:]); existingBlock != nil {
   270  				continue
   271  			}
   272  			enc, err := encode(ctx, block.Proto())
   273  			if err != nil {
   274  				return err
   275  			}
   276  			indicesByBucket := createBlockIndicesFromBlock(ctx, block.Block())
   277  			if err := updateValueForIndices(ctx, indicesByBucket, blockRoot[:], tx); err != nil {
   278  				return errors.Wrap(err, "could not update DB indices")
   279  			}
   280  			s.blockCache.Set(string(blockRoot[:]), block, int64(len(enc)))
   281  
   282  			if err := bkt.Put(blockRoot[:], enc); err != nil {
   283  				return err
   284  			}
   285  		}
   286  		return nil
   287  	})
   288  }
   289  
   290  // SaveHeadBlockRoot to the db.
   291  func (s *Store) SaveHeadBlockRoot(ctx context.Context, blockRoot [32]byte) error {
   292  	ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveHeadBlockRoot")
   293  	defer span.End()
   294  	return s.db.Update(func(tx *bolt.Tx) error {
   295  		hasStateSummaryInDB := s.HasStateSummary(ctx, blockRoot)
   296  		hasStateInDB := tx.Bucket(stateBucket).Get(blockRoot[:]) != nil
   297  		if !(hasStateInDB || hasStateSummaryInDB) {
   298  			return errors.New("no state or state summary found with head block root")
   299  		}
   300  
   301  		bucket := tx.Bucket(blocksBucket)
   302  		return bucket.Put(headBlockRootKey, blockRoot[:])
   303  	})
   304  }
   305  
   306  // GenesisBlock retrieves the genesis block of the beacon chain.
   307  func (s *Store) GenesisBlock(ctx context.Context) (interfaces.SignedBeaconBlock, error) {
   308  	ctx, span := trace.StartSpan(ctx, "BeaconDB.GenesisBlock")
   309  	defer span.End()
   310  	var block *ethpb.SignedBeaconBlock
   311  	err := s.db.View(func(tx *bolt.Tx) error {
   312  		bkt := tx.Bucket(blocksBucket)
   313  		root := bkt.Get(genesisBlockRootKey)
   314  		enc := bkt.Get(root)
   315  		if enc == nil {
   316  			return nil
   317  		}
   318  		block = &ethpb.SignedBeaconBlock{}
   319  		return decode(ctx, enc, block)
   320  	})
   321  	return wrapper.WrappedPhase0SignedBeaconBlock(block), err
   322  }
   323  
   324  // SaveGenesisBlockRoot to the db.
   325  func (s *Store) SaveGenesisBlockRoot(ctx context.Context, blockRoot [32]byte) error {
   326  	ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveGenesisBlockRoot")
   327  	defer span.End()
   328  	return s.db.Update(func(tx *bolt.Tx) error {
   329  		bucket := tx.Bucket(blocksBucket)
   330  		return bucket.Put(genesisBlockRootKey, blockRoot[:])
   331  	})
   332  }
   333  
   334  // HighestSlotBlocksBelow returns the block with the highest slot below the input slot from the db.
   335  func (s *Store) HighestSlotBlocksBelow(ctx context.Context, slot types.Slot) ([]interfaces.SignedBeaconBlock, error) {
   336  	ctx, span := trace.StartSpan(ctx, "BeaconDB.HighestSlotBlocksBelow")
   337  	defer span.End()
   338  
   339  	var best []byte
   340  	if err := s.db.View(func(tx *bolt.Tx) error {
   341  		bkt := tx.Bucket(blockSlotIndicesBucket)
   342  		// Iterate through the index, which is in byte sorted order.
   343  		c := bkt.Cursor()
   344  		for s, root := c.First(); s != nil; s, root = c.Next() {
   345  			if ctx.Err() != nil {
   346  				return ctx.Err()
   347  			}
   348  			key := bytesutil.BytesToSlotBigEndian(s)
   349  			if root == nil {
   350  				continue
   351  			}
   352  			if key >= slot {
   353  				break
   354  			}
   355  			best = root
   356  		}
   357  		return nil
   358  	}); err != nil {
   359  		return nil, err
   360  	}
   361  
   362  	var blk interfaces.SignedBeaconBlock
   363  	var err error
   364  	if best != nil {
   365  		blk, err = s.Block(ctx, bytesutil.ToBytes32(best))
   366  		if err != nil {
   367  			return nil, err
   368  		}
   369  	}
   370  	if blk == nil || blk.IsNil() {
   371  		blk, err = s.GenesisBlock(ctx)
   372  		if err != nil {
   373  			return nil, err
   374  		}
   375  	}
   376  
   377  	return []interfaces.SignedBeaconBlock{blk}, nil
   378  }
   379  
   380  // blockRootsByFilter retrieves the block roots given the filter criteria.
   381  func blockRootsByFilter(ctx context.Context, tx *bolt.Tx, f *filters.QueryFilter) ([][]byte, error) {
   382  	ctx, span := trace.StartSpan(ctx, "BeaconDB.blockRootsByFilter")
   383  	defer span.End()
   384  
   385  	// If no filter criteria are specified, return an error.
   386  	if f == nil {
   387  		return nil, errors.New("must specify a filter criteria for retrieving blocks")
   388  	}
   389  
   390  	// Creates a list of indices from the passed in filter values, such as:
   391  	// []byte("0x2093923") in the parent root indices bucket to be used for looking up
   392  	// block roots that were stored under each of those indices for O(1) lookup.
   393  	indicesByBucket, err := createBlockIndicesFromFilters(ctx, f)
   394  	if err != nil {
   395  		return nil, errors.Wrap(err, "could not determine lookup indices")
   396  	}
   397  
   398  	// We retrieve block roots that match a filter criteria of slot ranges, if specified.
   399  	filtersMap := f.Filters()
   400  	rootsBySlotRange, err := blockRootsBySlotRange(
   401  		ctx,
   402  		tx.Bucket(blockSlotIndicesBucket),
   403  		filtersMap[filters.StartSlot],
   404  		filtersMap[filters.EndSlot],
   405  		filtersMap[filters.StartEpoch],
   406  		filtersMap[filters.EndEpoch],
   407  		filtersMap[filters.SlotStep],
   408  	)
   409  	if err != nil {
   410  		return nil, err
   411  	}
   412  
   413  	// Once we have a list of block roots that correspond to each
   414  	// lookup index, we find the intersection across all of them and use
   415  	// that list of roots to lookup the block. These block will
   416  	// meet the filter criteria.
   417  	indices := lookupValuesForIndices(ctx, indicesByBucket, tx)
   418  	keys := rootsBySlotRange
   419  	if len(indices) > 0 {
   420  		// If we have found indices that meet the filter criteria, and there are also
   421  		// block roots that meet the slot range filter criteria, we find the intersection
   422  		// between these two sets of roots.
   423  		if len(rootsBySlotRange) > 0 {
   424  			joined := append([][][]byte{keys}, indices...)
   425  			keys = sliceutil.IntersectionByteSlices(joined...)
   426  		} else {
   427  			// If we have found indices that meet the filter criteria, but there are no block roots
   428  			// that meet the slot range filter criteria, we find the intersection
   429  			// of the regular filter indices.
   430  			keys = sliceutil.IntersectionByteSlices(indices...)
   431  		}
   432  	}
   433  
   434  	return keys, nil
   435  }
   436  
   437  // blockRootsBySlotRange looks into a boltDB bucket and performs a binary search
   438  // range scan using sorted left-padded byte keys using a start slot and an end slot.
   439  // However, if step is one, the implemented logic won’t skip half of the slots in the range.
   440  func blockRootsBySlotRange(
   441  	ctx context.Context,
   442  	bkt *bolt.Bucket,
   443  	startSlotEncoded, endSlotEncoded, startEpochEncoded, endEpochEncoded, slotStepEncoded interface{},
   444  ) ([][]byte, error) {
   445  	ctx, span := trace.StartSpan(ctx, "BeaconDB.blockRootsBySlotRange")
   446  	defer span.End()
   447  
   448  	// Return nothing when all slot parameters are missing
   449  	if startSlotEncoded == nil && endSlotEncoded == nil && startEpochEncoded == nil && endEpochEncoded == nil {
   450  		return [][]byte{}, nil
   451  	}
   452  
   453  	var startSlot, endSlot types.Slot
   454  	var step uint64
   455  	var ok bool
   456  	if startSlot, ok = startSlotEncoded.(types.Slot); !ok {
   457  		startSlot = 0
   458  	}
   459  	if endSlot, ok = endSlotEncoded.(types.Slot); !ok {
   460  		endSlot = 0
   461  	}
   462  	if step, ok = slotStepEncoded.(uint64); !ok || step == 0 {
   463  		step = 1
   464  	}
   465  	startEpoch, startEpochOk := startEpochEncoded.(types.Epoch)
   466  	endEpoch, endEpochOk := endEpochEncoded.(types.Epoch)
   467  	var err error
   468  	if startEpochOk && endEpochOk {
   469  		startSlot, err = helpers.StartSlot(startEpoch)
   470  		if err != nil {
   471  			return nil, err
   472  		}
   473  		endSlot, err = helpers.StartSlot(endEpoch)
   474  		if err != nil {
   475  			return nil, err
   476  		}
   477  		endSlot = endSlot + params.BeaconConfig().SlotsPerEpoch - 1
   478  	}
   479  	min := bytesutil.SlotToBytesBigEndian(startSlot)
   480  	max := bytesutil.SlotToBytesBigEndian(endSlot)
   481  
   482  	conditional := func(key, max []byte) bool {
   483  		return key != nil && bytes.Compare(key, max) <= 0
   484  	}
   485  	if endSlot < startSlot {
   486  		return nil, errInvalidSlotRange
   487  	}
   488  	rootsRange := endSlot.SubSlot(startSlot).Div(step)
   489  	roots := make([][]byte, 0, rootsRange)
   490  	c := bkt.Cursor()
   491  	for k, v := c.Seek(min); conditional(k, max); k, v = c.Next() {
   492  		if step > 1 {
   493  			slot := bytesutil.BytesToSlotBigEndian(k)
   494  			if slot.SubSlot(startSlot).Mod(step) != 0 {
   495  				continue
   496  			}
   497  		}
   498  		numOfRoots := len(v) / 32
   499  		splitRoots := make([][]byte, 0, numOfRoots)
   500  		for i := 0; i < len(v); i += 32 {
   501  			splitRoots = append(splitRoots, v[i:i+32])
   502  		}
   503  		roots = append(roots, splitRoots...)
   504  	}
   505  	return roots, nil
   506  }
   507  
   508  // blockRootsBySlot retrieves the block roots by slot
   509  func blockRootsBySlot(ctx context.Context, tx *bolt.Tx, slot types.Slot) ([][]byte, error) {
   510  	ctx, span := trace.StartSpan(ctx, "BeaconDB.blockRootsBySlot")
   511  	defer span.End()
   512  
   513  	roots := make([][]byte, 0)
   514  	bkt := tx.Bucket(blockSlotIndicesBucket)
   515  	key := bytesutil.SlotToBytesBigEndian(slot)
   516  	c := bkt.Cursor()
   517  	k, v := c.Seek(key)
   518  	if k != nil && bytes.Equal(k, key) {
   519  		for i := 0; i < len(v); i += 32 {
   520  			roots = append(roots, v[i:i+32])
   521  		}
   522  	}
   523  	return roots, nil
   524  }
   525  
   526  // createBlockIndicesFromBlock takes in a beacon block and returns
   527  // a map of bolt DB index buckets corresponding to each particular key for indices for
   528  // data, such as (shard indices bucket -> shard 5).
   529  func createBlockIndicesFromBlock(ctx context.Context, block interfaces.BeaconBlock) map[string][]byte {
   530  	ctx, span := trace.StartSpan(ctx, "BeaconDB.createBlockIndicesFromBlock")
   531  	defer span.End()
   532  	indicesByBucket := make(map[string][]byte)
   533  	// Every index has a unique bucket for fast, binary-search
   534  	// range scans for filtering across keys.
   535  	buckets := [][]byte{
   536  		blockSlotIndicesBucket,
   537  	}
   538  	indices := [][]byte{
   539  		bytesutil.SlotToBytesBigEndian(block.Slot()),
   540  	}
   541  	if block.ParentRoot() != nil && len(block.ParentRoot()) > 0 {
   542  		buckets = append(buckets, blockParentRootIndicesBucket)
   543  		indices = append(indices, block.ParentRoot())
   544  	}
   545  	for i := 0; i < len(buckets); i++ {
   546  		indicesByBucket[string(buckets[i])] = indices[i]
   547  	}
   548  	return indicesByBucket
   549  }
   550  
   551  // createBlockFiltersFromIndices takes in filter criteria and returns
   552  // a map with a single key-value pair: "block-parent-root-indices” -> parentRoot (array of bytes).
   553  //
   554  // For blocks, these are list of signing roots of block
   555  // objects. If a certain filter criterion does not apply to
   556  // blocks, an appropriate error is returned.
   557  func createBlockIndicesFromFilters(ctx context.Context, f *filters.QueryFilter) (map[string][]byte, error) {
   558  	ctx, span := trace.StartSpan(ctx, "BeaconDB.createBlockIndicesFromFilters")
   559  	defer span.End()
   560  	indicesByBucket := make(map[string][]byte)
   561  	for k, v := range f.Filters() {
   562  		switch k {
   563  		case filters.ParentRoot:
   564  			parentRoot, ok := v.([]byte)
   565  			if !ok {
   566  				return nil, errors.New("parent root is not []byte")
   567  			}
   568  			indicesByBucket[string(blockParentRootIndicesBucket)] = parentRoot
   569  		// The following cases are passthroughs for blocks, as they are not used
   570  		// for filtering indices.
   571  		case filters.StartSlot:
   572  		case filters.EndSlot:
   573  		case filters.StartEpoch:
   574  		case filters.EndEpoch:
   575  		case filters.SlotStep:
   576  		default:
   577  			return nil, fmt.Errorf("filter criterion %v not supported for blocks", k)
   578  		}
   579  	}
   580  	return indicesByBucket, nil
   581  }