diff --git a/beacon-chain/blockchain/forkchoice/process_block.go b/beacon-chain/blockchain/forkchoice/process_block.go index da0b8a904c..7e68e0a1fb 100644 --- a/beacon-chain/blockchain/forkchoice/process_block.go +++ b/beacon-chain/blockchain/forkchoice/process_block.go @@ -105,14 +105,14 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error { return errors.Wrap(err, "could not save finalized checkpoint") } - startSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch + 1) - finalizedSlot := helpers.StartSlot(postState.FinalizedCheckpoint.Epoch) - endSlot := helpers.StartSlot(postState.FinalizedCheckpoint.Epoch+1) - 1 // Inclusive - if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, finalizedSlot, endSlot); err != nil { + startSlot := helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) + 1 + endSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch) + if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, endSlot); err != nil { return errors.Wrapf(err, "could not delete states prior to finalized check point, range: %d, %d", startSlot, endSlot+params.BeaconConfig().SlotsPerEpoch) } + s.prevFinalizedCheckpt = s.finalizedCheckpt s.finalizedCheckpt = postState.FinalizedCheckpoint } @@ -186,18 +186,20 @@ func (s *Store) OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.Bea if postState.FinalizedCheckpoint.Epoch > s.finalizedCheckpt.Epoch { s.clearSeenAtts() helpers.ClearAllCaches() - startSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch + 1) - finalizedSlot := helpers.StartSlot(postState.FinalizedCheckpoint.Epoch) - endSlot := helpers.StartSlot(postState.FinalizedCheckpoint.Epoch+1) - 1 // Inclusive - if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, finalizedSlot, endSlot); err != nil { + + startSlot := helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) + 1 + endSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch) + if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, endSlot); err != nil { return errors.Wrapf(err, "could not delete states prior to finalized check point, range: %d, %d", startSlot, endSlot+params.BeaconConfig().SlotsPerEpoch) } - s.finalizedCheckpt = postState.FinalizedCheckpoint if err := s.db.SaveFinalizedCheckpoint(ctx, postState.FinalizedCheckpoint); err != nil { return errors.Wrap(err, "could not save finalized checkpoint") } + + s.prevFinalizedCheckpt = s.finalizedCheckpt + s.finalizedCheckpt = postState.FinalizedCheckpoint } // Update validator indices in database as needed. @@ -380,33 +382,38 @@ func (s *Store) clearSeenAtts() { } // rmStatesOlderThanLastFinalized deletes the states in db since last finalized check point. -func (s *Store) rmStatesOlderThanLastFinalized(ctx context.Context, startSlot uint64, finalizedSlot uint64, endSlot uint64) error { +func (s *Store) rmStatesOlderThanLastFinalized(ctx context.Context, startSlot uint64, endSlot uint64) error { + ctx, span := trace.StartSpan(ctx, "forkchoice.rmStatesBySlots") + defer span.End() + if !featureconfig.Get().PruneFinalizedStates { return nil } - ctx, span := trace.StartSpan(ctx, "forkchoice.rmStatesBySlots") - defer span.End() + // Make sure finalized slot is not a skipped slot. + for i := endSlot; i > 0; i-- { + filter := filters.NewFilter().SetStartSlot(i).SetEndSlot(i) + b, err := s.db.Blocks(ctx, filter) + if err != nil { + return err + } + if len(b) > 0 { + endSlot = i - 1 + break + } + } - roots := make([][32]byte, 0, endSlot-startSlot) - var err error // Do not remove genesis state if startSlot == 0 { startSlot++ } // Do not remove finalized state that's in the middle of slot ranges. - filter := filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(finalizedSlot - 1) - r1, err := s.db.BlockRoots(ctx, filter) + filter := filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot) + roots, err := s.db.BlockRoots(ctx, filter) if err != nil { return err } - filter = filters.NewFilter().SetStartSlot(finalizedSlot + 1).SetEndSlot(endSlot) - r2, err := s.db.BlockRoots(ctx, filter) - if err != nil { - return err - } - roots = append(r1, r2...) if err := s.db.DeleteStates(ctx, roots); err != nil { return err diff --git a/beacon-chain/blockchain/forkchoice/process_block_test.go b/beacon-chain/blockchain/forkchoice/process_block_test.go index 85202b34a0..a67d339b6c 100644 --- a/beacon-chain/blockchain/forkchoice/process_block_test.go +++ b/beacon-chain/blockchain/forkchoice/process_block_test.go @@ -317,7 +317,7 @@ func TestRemoveStateSinceLastFinalized(t *testing.T) { finalizedEpoch := uint64(1) finalizedSlot := finalizedEpoch * params.BeaconConfig().SlotsPerEpoch endSlot := helpers.StartSlot(finalizedEpoch+1) - 1 // Inclusive - if err := store.rmStatesOlderThanLastFinalized(ctx, 0, finalizedSlot, endSlot); err != nil { + if err := store.rmStatesOlderThanLastFinalized(ctx, 0, endSlot); err != nil { t.Fatal(err) } for _, r := range blockRoots { @@ -335,7 +335,7 @@ func TestRemoveStateSinceLastFinalized(t *testing.T) { newFinalizedEpoch := uint64(5) newFinalizedSlot := newFinalizedEpoch * params.BeaconConfig().SlotsPerEpoch endSlot = helpers.StartSlot(newFinalizedEpoch+1) - 1 // Inclusive - if err := store.rmStatesOlderThanLastFinalized(ctx, helpers.StartSlot(finalizedEpoch+1), newFinalizedSlot, endSlot); err != nil { + if err := store.rmStatesOlderThanLastFinalized(ctx, helpers.StartSlot(finalizedEpoch+1)-1, endSlot); err != nil { t.Fatal(err) } for _, r := range blockRoots { @@ -348,13 +348,4 @@ func TestRemoveStateSinceLastFinalized(t *testing.T) { t.Errorf("State with slot %d should not be in DB", s.Slot) } } - - // Verify finalized state did not get deleted - s, err := store.db.State(ctx, blockRoots[newFinalizedSlot]) - if err != nil { - t.Fatal(err) - } - if s == nil { - t.Error("Finalized state got deleted") - } } diff --git a/beacon-chain/blockchain/forkchoice/service.go b/beacon-chain/blockchain/forkchoice/service.go index 6f3d28d82b..cc798cfc3e 100644 --- a/beacon-chain/blockchain/forkchoice/service.go +++ b/beacon-chain/blockchain/forkchoice/service.go @@ -31,17 +31,18 @@ type ForkChoicer interface { // Store represents a service struct that handles the forkchoice // logic of managing the full PoS beacon chain. type Store struct { - ctx context.Context - cancel context.CancelFunc - db db.Database - justifiedCheckpt *ethpb.Checkpoint - finalizedCheckpt *ethpb.Checkpoint - checkpointState *cache.CheckpointStateCache - checkpointStateLock sync.Mutex - attsQueue map[[32]byte]*ethpb.Attestation - attsQueueLock sync.Mutex - seenAtts map[[32]byte]bool - seenAttsLock sync.Mutex + ctx context.Context + cancel context.CancelFunc + db db.Database + justifiedCheckpt *ethpb.Checkpoint + finalizedCheckpt *ethpb.Checkpoint + prevFinalizedCheckpt *ethpb.Checkpoint + checkpointState *cache.CheckpointStateCache + checkpointStateLock sync.Mutex + attsQueue map[[32]byte]*ethpb.Attestation + attsQueueLock sync.Mutex + seenAtts map[[32]byte]bool + seenAttsLock sync.Mutex } // NewForkChoiceService instantiates a new service instance that will @@ -82,6 +83,7 @@ func (s *Store) GenesisStore( s.justifiedCheckpt = proto.Clone(justifiedCheckpoint).(*ethpb.Checkpoint) s.finalizedCheckpt = proto.Clone(finalizedCheckpoint).(*ethpb.Checkpoint) + s.prevFinalizedCheckpt = proto.Clone(finalizedCheckpoint).(*ethpb.Checkpoint) justifiedState, err := s.db.State(ctx, bytesutil.ToBytes32(s.justifiedCheckpt.Root)) if err != nil {