mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
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:
@@ -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, ðpb.StateSummary{
|
||||
Slot: blk.Block().Slot(),
|
||||
Root: root,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
summaryBucket := tx.Bucket(stateBucket)
|
||||
return summaryBucket.Put(root, summaryEnc)
|
||||
}
|
||||
|
||||
@@ -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(ðpb.SignedBeaconBlock{})
|
||||
r, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
cp := ðpb.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(ðpb.SignedBeaconBlock{})
|
||||
r, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
cp := ðpb.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()
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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(ðpb.SignedBeaconBlock{})
|
||||
r, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
cp := ðpb.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()
|
||||
|
||||
Reference in New Issue
Block a user