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

     1  package kv
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  
     8  	"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
     9  	dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db"
    10  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    11  	"github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper"
    12  	"github.com/prysmaticlabs/prysm/proto/interfaces"
    13  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    14  	"github.com/prysmaticlabs/prysm/shared/traceutil"
    15  	bolt "go.etcd.io/bbolt"
    16  	"go.opencensus.io/trace"
    17  )
    18  
    19  var previousFinalizedCheckpointKey = []byte("previous-finalized-checkpoint")
    20  
    21  // Blocks from the recent finalized epoch are not part of the finalized and canonical chain in this
    22  // index. These containers will be removed on the next update of finalized checkpoint. Note that
    23  // these block roots may be considered canonical in the "head view" of the beacon chain, but not so
    24  // in this index.
    25  var containerFinalizedButNotCanonical = []byte("recent block needs reindexing to determine canonical")
    26  
    27  // The finalized block roots index tracks beacon blocks which are finalized in the canonical chain.
    28  // The finalized checkpoint contains the the epoch which was finalized and the highest beacon block
    29  // root where block.slot <= start_slot(epoch). As a result, we cannot index the finalized canonical
    30  // beacon block chain using the finalized root alone as this would exclude all other blocks in the
    31  // finalized epoch from being indexed as "final and canonical".
    32  //
    33  // The algorithm for building the index works as follows:
    34  //   - De-index all finalized beacon block roots from previous_finalized_epoch to
    35  //     new_finalized_epoch. (I.e. delete these roots from the index, to be re-indexed.)
    36  //   - Build the canonical finalized chain by walking up the ancestry chain from the finalized block
    37  //     root until a parent is found in the index or the parent is genesis.
    38  //   - Add all block roots in the database where epoch(block.slot) == checkpoint.epoch.
    39  //
    40  // This method ensures that all blocks from the current finalized epoch are considered "final" while
    41  // maintaining only canonical and finalized blocks older than the current finalized epoch.
    42  func (s *Store) updateFinalizedBlockRoots(ctx context.Context, tx *bolt.Tx, checkpoint *ethpb.Checkpoint) error {
    43  	ctx, span := trace.StartSpan(ctx, "BeaconDB.updateFinalizedBlockRoots")
    44  	defer span.End()
    45  
    46  	bkt := tx.Bucket(finalizedBlockRootsIndexBucket)
    47  
    48  	root := checkpoint.Root
    49  	var previousRoot []byte
    50  	genesisRoot := tx.Bucket(blocksBucket).Get(genesisBlockRootKey)
    51  
    52  	// De-index recent finalized block roots, to be re-indexed.
    53  	previousFinalizedCheckpoint := &ethpb.Checkpoint{}
    54  	if b := bkt.Get(previousFinalizedCheckpointKey); b != nil {
    55  		if err := decode(ctx, b, previousFinalizedCheckpoint); err != nil {
    56  			traceutil.AnnotateError(span, err)
    57  			return err
    58  		}
    59  	}
    60  
    61  	blockRoots, err := s.BlockRoots(ctx, filters.NewFilter().
    62  		SetStartEpoch(previousFinalizedCheckpoint.Epoch).
    63  		SetEndEpoch(checkpoint.Epoch+1),
    64  	)
    65  	if err != nil {
    66  		traceutil.AnnotateError(span, err)
    67  		return err
    68  	}
    69  	for _, root := range blockRoots {
    70  		if err := bkt.Delete(root[:]); err != nil {
    71  			traceutil.AnnotateError(span, err)
    72  			return err
    73  		}
    74  	}
    75  
    76  	// Walk up the ancestry chain until we reach a block root present in the finalized block roots
    77  	// index bucket or genesis block root.
    78  	for {
    79  		if bytes.Equal(root, genesisRoot) {
    80  			break
    81  		}
    82  
    83  		signedBlock, err := s.Block(ctx, bytesutil.ToBytes32(root))
    84  		if err != nil {
    85  			traceutil.AnnotateError(span, err)
    86  			return err
    87  		}
    88  		if signedBlock == nil || signedBlock.IsNil() || signedBlock.Block().IsNil() {
    89  			err := fmt.Errorf("missing block in database: block root=%#x", root)
    90  			traceutil.AnnotateError(span, err)
    91  			return err
    92  		}
    93  		block := signedBlock.Block()
    94  
    95  		container := &dbpb.FinalizedBlockRootContainer{
    96  			ParentRoot: block.ParentRoot(),
    97  			ChildRoot:  previousRoot,
    98  		}
    99  
   100  		enc, err := encode(ctx, container)
   101  		if err != nil {
   102  			traceutil.AnnotateError(span, err)
   103  			return err
   104  		}
   105  		if err := bkt.Put(root, enc); err != nil {
   106  			traceutil.AnnotateError(span, err)
   107  			return err
   108  		}
   109  
   110  		// Found parent, loop exit condition.
   111  		if parentBytes := bkt.Get(block.ParentRoot()); parentBytes != nil {
   112  			parent := &dbpb.FinalizedBlockRootContainer{}
   113  			if err := decode(ctx, parentBytes, parent); err != nil {
   114  				traceutil.AnnotateError(span, err)
   115  				return err
   116  			}
   117  			parent.ChildRoot = root
   118  			enc, err := encode(ctx, parent)
   119  			if err != nil {
   120  				traceutil.AnnotateError(span, err)
   121  				return err
   122  			}
   123  			if err := bkt.Put(block.ParentRoot(), enc); err != nil {
   124  				traceutil.AnnotateError(span, err)
   125  				return err
   126  			}
   127  			break
   128  		}
   129  		previousRoot = root
   130  		root = block.ParentRoot()
   131  	}
   132  
   133  	// Upsert blocks from the current finalized epoch.
   134  	roots, err := s.BlockRoots(ctx, filters.NewFilter().SetStartEpoch(checkpoint.Epoch).SetEndEpoch(checkpoint.Epoch+1))
   135  	if err != nil {
   136  		traceutil.AnnotateError(span, err)
   137  		return err
   138  	}
   139  	for _, root := range roots {
   140  		root := root[:]
   141  		if bytes.Equal(root, checkpoint.Root) || bkt.Get(root) != nil {
   142  			continue
   143  		}
   144  		if err := bkt.Put(root, containerFinalizedButNotCanonical); err != nil {
   145  			traceutil.AnnotateError(span, err)
   146  			return err
   147  		}
   148  	}
   149  
   150  	// Update previous checkpoint
   151  	enc, err := encode(ctx, checkpoint)
   152  	if err != nil {
   153  		traceutil.AnnotateError(span, err)
   154  		return err
   155  	}
   156  
   157  	return bkt.Put(previousFinalizedCheckpointKey, enc)
   158  }
   159  
   160  // IsFinalizedBlock returns true if the block root is present in the finalized block root index.
   161  // A beacon block root contained exists in this index if it is considered finalized and canonical.
   162  // Note: beacon blocks from the latest finalized epoch return true, whether or not they are
   163  // considered canonical in the "head view" of the beacon node.
   164  func (s *Store) IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool {
   165  	ctx, span := trace.StartSpan(ctx, "BeaconDB.IsFinalizedBlock")
   166  	defer span.End()
   167  
   168  	var exists bool
   169  	err := s.db.View(func(tx *bolt.Tx) error {
   170  		exists = tx.Bucket(finalizedBlockRootsIndexBucket).Get(blockRoot[:]) != nil
   171  		// Check genesis block root.
   172  		if !exists {
   173  			genRoot := tx.Bucket(blocksBucket).Get(genesisBlockRootKey)
   174  			exists = bytesutil.ToBytes32(genRoot) == blockRoot
   175  		}
   176  		return nil
   177  	})
   178  	if err != nil {
   179  		traceutil.AnnotateError(span, err)
   180  	}
   181  	return exists
   182  }
   183  
   184  // FinalizedChildBlock returns the child block of a provided finalized block. If
   185  // no finalized block or its respective child block exists we return with a nil
   186  // block.
   187  func (s *Store) FinalizedChildBlock(ctx context.Context, blockRoot [32]byte) (interfaces.SignedBeaconBlock, error) {
   188  	ctx, span := trace.StartSpan(ctx, "BeaconDB.FinalizedChildBlock")
   189  	defer span.End()
   190  
   191  	var blk *ethpb.SignedBeaconBlock
   192  	err := s.db.View(func(tx *bolt.Tx) error {
   193  		blkBytes := tx.Bucket(finalizedBlockRootsIndexBucket).Get(blockRoot[:])
   194  		if blkBytes == nil {
   195  			return nil
   196  		}
   197  		if bytes.Equal(blkBytes, containerFinalizedButNotCanonical) {
   198  			return nil
   199  		}
   200  		ctr := &dbpb.FinalizedBlockRootContainer{}
   201  		if err := decode(ctx, blkBytes, ctr); err != nil {
   202  			traceutil.AnnotateError(span, err)
   203  			return err
   204  		}
   205  		enc := tx.Bucket(blocksBucket).Get(ctr.ChildRoot)
   206  		if enc == nil {
   207  			return nil
   208  		}
   209  		blk = &ethpb.SignedBeaconBlock{}
   210  		return decode(ctx, enc, blk)
   211  	})
   212  	traceutil.AnnotateError(span, err)
   213  	return wrapper.WrappedPhase0SignedBeaconBlock(blk), err
   214  }