Can recover from missing state summary in DB (#11167)

* Can recover from missing state summary in DB

* Tests

* fix tests

Co-authored-by: Potuz <potuz@prysmaticlabs.com>
This commit is contained in:
terencechain
2022-08-04 06:01:07 -07:00
committed by GitHub
parent 8aa6057b60
commit a82325615b
4 changed files with 93 additions and 3 deletions

View File

@@ -2,6 +2,7 @@ package kv
import (
"context"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/params"
@@ -63,7 +64,10 @@ func (s *Store) SaveJustifiedCheckpoint(ctx context.Context, checkpoint *ethpb.C
hasStateSummary := s.hasStateSummaryBytes(tx, bytesutil.ToBytes32(checkpoint.Root))
hasStateInDB := tx.Bucket(stateBucket).Get(checkpoint.Root) != nil
if !(hasStateInDB || hasStateSummary) {
return errors.Wrapf(errMissingStateForCheckpoint, "could not save justified checkpoint, justified root: %#x", bytesutil.Trunc(checkpoint.Root))
log.Warnf("Recovering state summary for justified root: %#x", bytesutil.Trunc(checkpoint.Root))
if err := recoverStateSummary(ctx, tx, checkpoint.Root); err != nil {
return errors.Wrapf(errMissingStateForCheckpoint, "could not save justified checkpoint, finalized root: %#x", bytesutil.Trunc(checkpoint.Root))
}
}
return bucket.Put(justifiedCheckpointKey, enc)
})
@@ -83,7 +87,10 @@ func (s *Store) SaveFinalizedCheckpoint(ctx context.Context, checkpoint *ethpb.C
hasStateSummary := s.hasStateSummaryBytes(tx, bytesutil.ToBytes32(checkpoint.Root))
hasStateInDB := tx.Bucket(stateBucket).Get(checkpoint.Root) != nil
if !(hasStateInDB || hasStateSummary) {
return errors.Wrapf(errMissingStateForCheckpoint, "could not save Finalized checkpoint, finalized root: %#x", bytesutil.Trunc(checkpoint.Root))
log.Warnf("Recovering state summary for finalized root: %#x", bytesutil.Trunc(checkpoint.Root))
if err := recoverStateSummary(ctx, tx, checkpoint.Root); err != nil {
return errors.Wrapf(errMissingStateForCheckpoint, "could not save finalized checkpoint, finalized root: %#x", bytesutil.Trunc(checkpoint.Root))
}
}
if err := bucket.Put(finalizedCheckpointKey, enc); err != nil {
return err
@@ -92,3 +99,25 @@ func (s *Store) SaveFinalizedCheckpoint(ctx context.Context, checkpoint *ethpb.C
return s.updateFinalizedBlockRoots(ctx, tx, checkpoint)
})
}
// Recovers and saves state summary for a given root if the root has a block in the DB.
func recoverStateSummary(ctx context.Context, tx *bolt.Tx, root []byte) error {
blkBucket := tx.Bucket(blocksBucket)
blkEnc := blkBucket.Get(root)
if blkEnc == nil {
return fmt.Errorf("nil block, root: %#x", bytesutil.Trunc(root))
}
blk, err := unmarshalBlock(ctx, blkEnc)
if err != nil {
return errors.Wrapf(err, "Could not unmarshal block: %#x", bytesutil.Trunc(root))
}
summaryEnc, err := encode(ctx, &ethpb.StateSummary{
Slot: blk.Block().Slot(),
Root: root,
})
if err != nil {
return err
}
summaryBucket := tx.Bucket(stateBucket)
return summaryBucket.Put(root, summaryEnc)
}

View File

@@ -33,6 +33,25 @@ func TestStore_JustifiedCheckpoint_CanSaveRetrieve(t *testing.T) {
assert.Equal(t, true, proto.Equal(cp, retrieved), "Wanted %v, received %v", cp, retrieved)
}
func TestStore_JustifiedCheckpoint_Recover(t *testing.T) {
db := setupDB(t)
ctx := context.Background()
blk := util.HydrateSignedBeaconBlock(&ethpb.SignedBeaconBlock{})
r, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
cp := &ethpb.Checkpoint{
Epoch: 2,
Root: r[:],
}
wb, err := blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, db.SaveBlock(ctx, wb))
require.NoError(t, db.SaveJustifiedCheckpoint(ctx, cp))
retrieved, err := db.JustifiedCheckpoint(ctx)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(cp, retrieved), "Wanted %v, received %v", cp, retrieved)
}
func TestStore_FinalizedCheckpoint_CanSaveRetrieve(t *testing.T) {
db := setupDB(t)
ctx := context.Background()
@@ -69,6 +88,26 @@ func TestStore_FinalizedCheckpoint_CanSaveRetrieve(t *testing.T) {
assert.Equal(t, true, proto.Equal(cp, retrieved), "Wanted %v, received %v", cp, retrieved)
}
func TestStore_FinalizedCheckpoint_Recover(t *testing.T) {
db := setupDB(t)
ctx := context.Background()
blk := util.HydrateSignedBeaconBlock(&ethpb.SignedBeaconBlock{})
r, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
cp := &ethpb.Checkpoint{
Epoch: 2,
Root: r[:],
}
wb, err := blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, r))
require.NoError(t, db.SaveBlock(ctx, wb))
require.NoError(t, db.SaveFinalizedCheckpoint(ctx, cp))
retrieved, err := db.FinalizedCheckpoint(ctx)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(cp, retrieved), "Wanted %v, received %v", cp, retrieved)
}
func TestStore_JustifiedCheckpoint_DefaultIsZeroHash(t *testing.T) {
db := setupDB(t)
ctx := context.Background()

View File

@@ -43,7 +43,10 @@ func (s *Store) SaveLastValidatedCheckpoint(ctx context.Context, checkpoint *eth
hasStateSummary := s.hasStateSummaryBytes(tx, bytesutil.ToBytes32(checkpoint.Root))
hasStateInDB := tx.Bucket(stateBucket).Get(checkpoint.Root) != nil
if !(hasStateInDB || hasStateSummary) {
return errors.Wrapf(errMissingStateForCheckpoint, "could not save last validated checkpoint, checkpoint root: %#x", bytesutil.Trunc(checkpoint.Root))
log.Warnf("Recovering state summary for last validated root: %#x", bytesutil.Trunc(checkpoint.Root))
if err := recoverStateSummary(ctx, tx, checkpoint.Root); err != nil {
return errors.Wrapf(errMissingStateForCheckpoint, "could not save finalized checkpoint, last validated root: %#x", bytesutil.Trunc(checkpoint.Root))
}
}
return bucket.Put(lastValidatedCheckpointKey, enc)
})

View File

@@ -32,6 +32,25 @@ func TestStore_LastValidatedCheckpoint_CanSaveRetrieve(t *testing.T) {
assert.Equal(t, true, proto.Equal(cp, retrieved), "Wanted %v, received %v", cp, retrieved)
}
func TestStore_LastValidatedCheckpoint_Recover(t *testing.T) {
db := setupDB(t)
ctx := context.Background()
blk := util.HydrateSignedBeaconBlock(&ethpb.SignedBeaconBlock{})
r, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
cp := &ethpb.Checkpoint{
Epoch: 2,
Root: r[:],
}
wb, err := blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, db.SaveBlock(ctx, wb))
require.NoError(t, db.SaveLastValidatedCheckpoint(ctx, cp))
retrieved, err := db.LastValidatedCheckpoint(ctx)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(cp, retrieved), "Wanted %v, received %v", cp, retrieved)
}
func TestStore_LastValidatedCheckpoint_DefaultIsFinalized(t *testing.T) {
db := setupDB(t)
ctx := context.Background()