Fix loading historical blocks to replay cold state (#6956)

* Replay: cold state only uses finalized blocks
* Replay: regression test
* Merge refs/heads/master into fix-finalized-replay
This commit is contained in:
terence tsao 2020-08-10 16:35:28 -07:00 committed by GitHub
parent 8b9138a76a
commit 537fe3f721
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 3 deletions

View File

@ -116,6 +116,7 @@ func (s *State) LoadBlocks(ctx context.Context, startSlot uint64, endSlot uint64
if err != nil { if err != nil {
return nil, err return nil, err
} }
blockRoots, err := s.beaconDB.BlockRoots(ctx, filter) blockRoots, err := s.beaconDB.BlockRoots(ctx, filter)
if err != nil { if err != nil {
return nil, err return nil, err
@ -327,20 +328,21 @@ func (s *State) processStateUpTo(ctx context.Context, state *state.BeaconState,
if state.Slot() >= slot { if state.Slot() >= slot {
return state, nil return state, nil
} }
_, lastBlockSlot, err := s.lastSavedBlock(ctx, slot)
lastBlockRoot, lastBlockSlot, err := s.lastSavedBlock(ctx, slot)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get last saved block") return nil, errors.Wrap(err, "could not get last saved block")
} }
// Short circuit if no block was saved, replay using slots only. // Short circuit if no block was saved, replay using slots only.
if lastBlockSlot == 0 { if lastBlockSlot == 0 {
return s.ReplayBlocks(ctx, state, []*ethpb.SignedBeaconBlock{}, slot) return s.ReplayBlocks(ctx, state, []*ethpb.SignedBeaconBlock{}, slot)
} }
blks, err := s.LoadBlocks(ctx, state.Slot()+1, lastBlockSlot, lastBlockRoot) blks, err := s.loadFinalizedBlocks(ctx, state.Slot()+1, lastBlockSlot)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not load blocks") return nil, errors.Wrap(err, "could not load blocks")
} }
state, err = s.ReplayBlocks(ctx, state, blks, slot) state, err = s.ReplayBlocks(ctx, state, blks, slot)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not replay blocks") return nil, errors.Wrap(err, "could not replay blocks")
@ -348,3 +350,27 @@ func (s *State) processStateUpTo(ctx context.Context, state *state.BeaconState,
return state, nil return state, nil
} }
// Given the start slot and the end slot, this returns the finalized beacon blocks in between.
// Since hot states don't have finalized blocks, this should ONLY be used for replaying cold state.
func (s *State) loadFinalizedBlocks(ctx context.Context, startSlot uint64, endSlot uint64) ([]*ethpb.SignedBeaconBlock, error) {
f := filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot)
bs, err := s.beaconDB.Blocks(ctx, f)
if err != nil {
return nil, err
}
bRoots, err := s.beaconDB.BlockRoots(ctx, f)
if err != nil {
return nil, err
}
if len(bs) != len(bRoots) {
return nil, errors.New("length of blocks and roots don't match")
}
fbs := make([]*ethpb.SignedBeaconBlock, 0, len(bs))
for i := len(bs) - 1; i >= 0; i-- {
if s.beaconDB.IsFinalizedBlock(ctx, bRoots[i]) {
fbs = append(fbs, bs[i])
}
}
return fbs, nil
}

View File

@ -697,3 +697,26 @@ func tree4(db db.Database, genesisRoot []byte) ([][32]byte, []*ethpb.SignedBeaco
return [][32]byte{r0, r21, r22, r23, r24}, returnedBlocks, nil return [][32]byte{r0, r21, r22, r23, r24}, returnedBlocks, nil
} }
func TestLoadFinalizedBlocks(t *testing.T) {
db, _ := testDB.SetupDB(t)
ctx := context.Background()
s := &State{
beaconDB: db,
}
gBlock := &ethpb.SignedBeaconBlock{}
gRoot, err := stateutil.BlockRoot(gBlock.Block)
require.NoError(t, err)
require.NoError(t, db.SaveBlock(ctx, gBlock))
roots, _, err := tree1(db, gRoot[:])
require.NoError(t, err)
filteredBlocks, err := s.loadFinalizedBlocks(ctx, 0, 8)
require.NoError(t, err)
require.Equal(t, 0, len(filteredBlocks))
require.NoError(t, db.SaveStateSummary(ctx, &pb.StateSummary{Root: roots[8][:]}))
require.NoError(t, s.beaconDB.SaveFinalizedCheckpoint(ctx, &ethpb.Checkpoint{Root: roots[8][:]}))
filteredBlocks, err = s.loadFinalizedBlocks(ctx, 0, 8)
require.Equal(t, 10, len(filteredBlocks))
}