2019-10-29 15:14:17 +00:00
|
|
|
package kv
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
2019-11-02 18:57:35 +00:00
|
|
|
"fmt"
|
2019-10-29 15:14:17 +00:00
|
|
|
|
|
|
|
"github.com/boltdb/bolt"
|
|
|
|
"github.com/gogo/protobuf/proto"
|
2019-11-08 03:00:47 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
2019-10-29 15:14:17 +00:00
|
|
|
dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db"
|
|
|
|
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
2019-11-08 03:00:47 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
2019-10-29 15:14:17 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/traceutil"
|
|
|
|
"go.opencensus.io/trace"
|
|
|
|
)
|
|
|
|
|
2019-11-08 03:00:47 +00:00
|
|
|
var previousFinalizedCheckpointKey = []byte("previous-finalized-checkpoint")
|
2019-10-29 15:14:17 +00:00
|
|
|
|
2019-11-08 03:00:47 +00:00
|
|
|
// Blocks from the recent finalized epoch are not part of the finalized and canonical chain in this
|
|
|
|
// index. These containers will be removed on the next update of finalized checkpoint. Note that
|
|
|
|
// these block roots may be considered canonical in the "head view" of the beacon chain, but not so
|
|
|
|
// in this index.
|
|
|
|
var containerFinalizedButNotCanonical = []byte("recent block needs reindexing to determine canonical")
|
|
|
|
|
|
|
|
// The finalized block roots index tracks beacon blocks which are finalized in the canonical chain.
|
|
|
|
// The finalized checkpoint contains the the epoch which was finalized and the highest beacon block
|
|
|
|
// root where block.slot <= start_slot(epoch). As a result, we cannot index the finalized canonical
|
|
|
|
// beacon block chain using the finalized root alone as this would exclude all other blocks in the
|
|
|
|
// finalized epoch from being indexed as "final and canonical".
|
|
|
|
//
|
|
|
|
// The algorithm for building the index works as follows:
|
|
|
|
// - De-index all finalized beacon block roots from previous_finalized_epoch to
|
|
|
|
// new_finalized_epoch. (I.e. delete these roots from the index, to be re-indexed.)
|
|
|
|
// - Build the canonical finalized chain by walking up the ancestry chain from the finalized block
|
|
|
|
// root until a parent is found in the index or the parent is genesis.
|
|
|
|
// - Add all block roots in the database where epoch(block.slot) == checkpoint.epoch.
|
|
|
|
//
|
|
|
|
// This method ensures that all blocks from the current finalized epoch are considered "final" while
|
|
|
|
// maintaining only canonical and finalized blocks older than the current finalized epoch.
|
|
|
|
func (kv *Store) updateFinalizedBlockRoots(ctx context.Context, tx *bolt.Tx, checkpoint *ethpb.Checkpoint) error {
|
2019-10-29 15:14:17 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "BeaconDB.updateFinalizedBlockRoots")
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
bkt := tx.Bucket(finalizedBlockRootsIndexBucket)
|
|
|
|
|
|
|
|
root := checkpoint.Root
|
|
|
|
var previousRoot []byte
|
2019-11-08 03:00:47 +00:00
|
|
|
genesisRoot := tx.Bucket(blocksBucket).Get(genesisBlockRootKey)
|
|
|
|
|
|
|
|
// De-index recent finalized block roots, to be re-indexed.
|
|
|
|
prevousFinalizedCheckpoint := ðpb.Checkpoint{}
|
|
|
|
if b := bkt.Get(previousFinalizedCheckpointKey); b != nil {
|
|
|
|
if err := proto.Unmarshal(b, prevousFinalizedCheckpoint); err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
previousFinalizedCheckpointStartSlot := helpers.StartSlot(prevousFinalizedCheckpoint.Epoch)
|
|
|
|
recentFinalizedCheckpointEndSlot := helpers.StartSlot(checkpoint.Epoch + 1)
|
|
|
|
blockRoots, err := kv.BlockRoots(ctx, filters.NewFilter().SetStartSlot(previousFinalizedCheckpointStartSlot).SetEndSlot(recentFinalizedCheckpointEndSlot))
|
|
|
|
if err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, root := range blockRoots {
|
|
|
|
if err := bkt.Delete(root[:]); err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2019-10-29 15:14:17 +00:00
|
|
|
|
|
|
|
// Walk up the ancestry chain until we reach a block root present in the finalized block roots
|
|
|
|
// index bucket or genesis block root.
|
2019-11-02 18:57:35 +00:00
|
|
|
for {
|
|
|
|
if bytes.Equal(root, genesisRoot) {
|
2019-11-08 03:00:47 +00:00
|
|
|
break
|
2019-11-02 18:57:35 +00:00
|
|
|
}
|
2019-10-29 15:14:17 +00:00
|
|
|
|
2019-11-08 03:00:47 +00:00
|
|
|
block, err := kv.Block(ctx, bytesutil.ToBytes32(root))
|
|
|
|
if err != nil {
|
2019-11-02 18:57:35 +00:00
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
2019-11-08 03:00:47 +00:00
|
|
|
if block == nil {
|
|
|
|
err := fmt.Errorf("missing block in database: block root=%#x", root)
|
2019-11-02 18:57:35 +00:00
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
2019-10-29 15:14:17 +00:00
|
|
|
|
2019-11-02 18:57:35 +00:00
|
|
|
container := &dbpb.FinalizedBlockRootContainer{
|
|
|
|
ParentRoot: block.ParentRoot,
|
|
|
|
ChildRoot: previousRoot,
|
|
|
|
}
|
2019-10-29 15:14:17 +00:00
|
|
|
|
2019-11-02 18:57:35 +00:00
|
|
|
enc, err := proto.Marshal(container)
|
|
|
|
if err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := bkt.Put(root, enc); err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
2019-11-08 03:00:47 +00:00
|
|
|
|
|
|
|
// Found parent, loop exit condition.
|
2019-11-02 18:57:35 +00:00
|
|
|
if parentBytes := bkt.Get(block.ParentRoot); parentBytes != nil {
|
|
|
|
parent := &dbpb.FinalizedBlockRootContainer{}
|
|
|
|
if err := proto.Unmarshal(parentBytes, parent); err != nil {
|
2019-10-29 15:14:17 +00:00
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
2019-11-02 18:57:35 +00:00
|
|
|
parent.ChildRoot = root
|
|
|
|
enc, err := proto.Marshal(parent)
|
|
|
|
if err != nil {
|
2019-10-29 15:14:17 +00:00
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
2019-11-08 03:00:47 +00:00
|
|
|
if err := bkt.Put(block.ParentRoot, enc); err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
break
|
2019-10-29 15:14:17 +00:00
|
|
|
}
|
2019-11-02 18:57:35 +00:00
|
|
|
previousRoot = root
|
|
|
|
root = block.ParentRoot
|
2019-10-29 15:14:17 +00:00
|
|
|
}
|
2019-11-08 03:00:47 +00:00
|
|
|
|
|
|
|
// Upsert blocks from the current finalized epoch.
|
|
|
|
roots, err := kv.BlockRoots(ctx, filters.NewFilter().SetStartSlot(helpers.StartSlot(checkpoint.Epoch)).SetEndSlot(helpers.StartSlot(checkpoint.Epoch+1)))
|
|
|
|
if err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, root := range roots {
|
|
|
|
root := root[:]
|
|
|
|
if bytes.Equal(root, checkpoint.Root) || bkt.Get(root) != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err := bkt.Put(root, containerFinalizedButNotCanonical); err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update previous checkpoint
|
|
|
|
enc, err := proto.Marshal(checkpoint)
|
|
|
|
if err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return bkt.Put(previousFinalizedCheckpointKey, enc)
|
2019-10-29 15:14:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsFinalizedBlock returns true if the block root is present in the finalized block root index.
|
2019-11-08 03:00:47 +00:00
|
|
|
// A beacon block root contained exists in this index if it is considered finalized and canonical.
|
|
|
|
// Note: beacon blocks from the latest finalized epoch return true, whether or not they are
|
|
|
|
// considered canonical in the "head view" of the beacon node.
|
2019-10-29 15:14:17 +00:00
|
|
|
func (kv *Store) IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool {
|
|
|
|
ctx, span := trace.StartSpan(ctx, "BeaconDB.IsFinalizedBlock")
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
var exists bool
|
2019-11-08 03:00:47 +00:00
|
|
|
err := kv.db.View(func(tx *bolt.Tx) error {
|
2019-10-29 15:14:17 +00:00
|
|
|
exists = tx.Bucket(finalizedBlockRootsIndexBucket).Get(blockRoot[:]) != nil
|
|
|
|
return nil
|
|
|
|
})
|
2019-11-08 03:00:47 +00:00
|
|
|
if err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
}
|
2019-10-29 15:14:17 +00:00
|
|
|
return exists
|
|
|
|
}
|