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 := ðpb.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 = ðpb.SignedBeaconBlock{} 210 return decode(ctx, enc, blk) 211 }) 212 traceutil.AnnotateError(span, err) 213 return wrapper.WrappedPhase0SignedBeaconBlock(blk), err 214 }