mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-08 14:21:14 +00:00
Use evidence period when pruning (#9505)
* Added logic so when pruning, the evidence period is taken into consideration and only deletes unecessary data
This commit is contained in:
@@ -7,9 +7,11 @@ import (
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/tendermint/tendermint/evidence"
|
||||
tmsync "github.com/tendermint/tendermint/libs/sync"
|
||||
tmstore "github.com/tendermint/tendermint/proto/tendermint/store"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -264,20 +266,20 @@ func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit {
|
||||
return commit
|
||||
}
|
||||
|
||||
// PruneBlocks removes block up to (but not including) a height. It returns number of blocks pruned.
|
||||
func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) {
|
||||
// PruneBlocks removes block up to (but not including) a height. It returns number of blocks pruned and the evidence retain height - the height at which data needed to prove evidence must not be removed.
|
||||
func (bs *BlockStore) PruneBlocks(height int64, state sm.State) (uint64, int64, error) {
|
||||
if height <= 0 {
|
||||
return 0, fmt.Errorf("height must be greater than 0")
|
||||
return 0, -1, fmt.Errorf("height must be greater than 0")
|
||||
}
|
||||
bs.mtx.RLock()
|
||||
if height > bs.height {
|
||||
bs.mtx.RUnlock()
|
||||
return 0, fmt.Errorf("cannot prune beyond the latest height %v", bs.height)
|
||||
return 0, -1, fmt.Errorf("cannot prune beyond the latest height %v", bs.height)
|
||||
}
|
||||
base := bs.base
|
||||
bs.mtx.RUnlock()
|
||||
if height < base {
|
||||
return 0, fmt.Errorf("cannot prune to height %v, it is lower than base height %v",
|
||||
return 0, -1, fmt.Errorf("cannot prune to height %v, it is lower than base height %v",
|
||||
height, base)
|
||||
}
|
||||
|
||||
@@ -300,26 +302,42 @@ func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
evidencePoint := height
|
||||
for h := base; h < height; h++ {
|
||||
|
||||
meta := bs.LoadBlockMeta(h)
|
||||
if meta == nil { // assume already deleted
|
||||
continue
|
||||
}
|
||||
if err := batch.Delete(calcBlockMetaKey(h)); err != nil {
|
||||
return 0, err
|
||||
|
||||
// This logic is in place to protect data that proves malicious behavior.
|
||||
// If the height is within the evidence age, we continue to persist the header and commit data.
|
||||
|
||||
if evidencePoint == height && !evidence.IsEvidenceExpired(state.LastBlockHeight, state.LastBlockTime, h, meta.Header.Time, state.ConsensusParams.Evidence) {
|
||||
evidencePoint = h
|
||||
}
|
||||
|
||||
// if height is beyond the evidence point we dont delete the header
|
||||
if h < evidencePoint {
|
||||
if err := batch.Delete(calcBlockMetaKey(h)); err != nil {
|
||||
return 0, -1, err
|
||||
}
|
||||
}
|
||||
if err := batch.Delete(calcBlockHashKey(meta.BlockID.Hash)); err != nil {
|
||||
return 0, err
|
||||
return 0, -1, err
|
||||
}
|
||||
if err := batch.Delete(calcBlockCommitKey(h)); err != nil {
|
||||
return 0, err
|
||||
// if height is beyond the evidence point we dont delete the commit data
|
||||
if h < evidencePoint {
|
||||
if err := batch.Delete(calcBlockCommitKey(h)); err != nil {
|
||||
return 0, -1, err
|
||||
}
|
||||
}
|
||||
if err := batch.Delete(calcSeenCommitKey(h)); err != nil {
|
||||
return 0, err
|
||||
return 0, -1, err
|
||||
}
|
||||
for p := 0; p < int(meta.BlockID.PartSetHeader.Total); p++ {
|
||||
if err := batch.Delete(calcBlockPartKey(h, p)); err != nil {
|
||||
return 0, err
|
||||
return 0, -1, err
|
||||
}
|
||||
}
|
||||
pruned++
|
||||
@@ -328,7 +346,7 @@ func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) {
|
||||
if pruned%1000 == 0 && pruned > 0 {
|
||||
err := flush(batch, h)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, -1, err
|
||||
}
|
||||
batch = bs.db.NewBatch()
|
||||
defer batch.Close()
|
||||
@@ -337,9 +355,9 @@ func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) {
|
||||
|
||||
err := flush(batch, height)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, -1, err
|
||||
}
|
||||
return pruned, nil
|
||||
return pruned, evidencePoint, nil
|
||||
}
|
||||
|
||||
// SaveBlock persists the given block, blockParts, and seenCommit to the underlying db.
|
||||
|
||||
@@ -381,7 +381,7 @@ func TestLoadBaseMeta(t *testing.T) {
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
}
|
||||
|
||||
_, err = bs.PruneBlocks(4)
|
||||
_, _, err = bs.PruneBlocks(4, state)
|
||||
require.NoError(t, err)
|
||||
|
||||
baseBlock := bs.LoadBaseMeta()
|
||||
@@ -440,10 +440,10 @@ func TestPruneBlocks(t *testing.T) {
|
||||
assert.EqualValues(t, 0, bs.Size())
|
||||
|
||||
// pruning an empty store should error, even when pruning to 0
|
||||
_, err = bs.PruneBlocks(1)
|
||||
_, _, err = bs.PruneBlocks(1, state)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = bs.PruneBlocks(0)
|
||||
_, _, err = bs.PruneBlocks(0, state)
|
||||
require.Error(t, err)
|
||||
|
||||
// make more than 1000 blocks, to test batch deletions
|
||||
@@ -459,27 +459,30 @@ func TestPruneBlocks(t *testing.T) {
|
||||
assert.EqualValues(t, 1500, bs.Height())
|
||||
assert.EqualValues(t, 1500, bs.Size())
|
||||
|
||||
prunedBlock := bs.LoadBlock(1199)
|
||||
state.LastBlockTime = time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC)
|
||||
state.LastBlockHeight = 1500
|
||||
|
||||
state.ConsensusParams.Evidence.MaxAgeNumBlocks = 400
|
||||
state.ConsensusParams.Evidence.MaxAgeDuration = 1 * time.Second
|
||||
|
||||
// Check that basic pruning works
|
||||
pruned, err := bs.PruneBlocks(1200)
|
||||
pruned, evidenceRetainHeight, err := bs.PruneBlocks(1200, state)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 1199, pruned)
|
||||
assert.EqualValues(t, 1200, bs.Base())
|
||||
assert.EqualValues(t, 1500, bs.Height())
|
||||
assert.EqualValues(t, 301, bs.Size())
|
||||
assert.EqualValues(t, tmstore.BlockStoreState{
|
||||
Base: 1200,
|
||||
Height: 1500,
|
||||
}, LoadBlockStoreState(db))
|
||||
assert.EqualValues(t, 1100, evidenceRetainHeight)
|
||||
|
||||
require.NotNil(t, bs.LoadBlock(1200))
|
||||
require.Nil(t, bs.LoadBlock(1199))
|
||||
require.Nil(t, bs.LoadBlockByHash(prunedBlock.Hash()))
|
||||
require.Nil(t, bs.LoadBlockCommit(1199))
|
||||
require.Nil(t, bs.LoadBlockMeta(1199))
|
||||
require.Nil(t, bs.LoadBlockMetaByHash(prunedBlock.Hash()))
|
||||
require.Nil(t, bs.LoadBlockPart(1199, 1))
|
||||
|
||||
// The header and commit for heights 1100 onwards
|
||||
// need to remain to verify evidence
|
||||
require.NotNil(t, bs.LoadBlockMeta(1100))
|
||||
require.Nil(t, bs.LoadBlockMeta(1099))
|
||||
require.NotNil(t, bs.LoadBlockCommit(1100))
|
||||
require.Nil(t, bs.LoadBlockCommit(1099))
|
||||
|
||||
for i := int64(1); i < 1200; i++ {
|
||||
require.Nil(t, bs.LoadBlock(i))
|
||||
@@ -489,26 +492,33 @@ func TestPruneBlocks(t *testing.T) {
|
||||
}
|
||||
|
||||
// Pruning below the current base should error
|
||||
_, err = bs.PruneBlocks(1199)
|
||||
_, _, err = bs.PruneBlocks(1199, state)
|
||||
require.Error(t, err)
|
||||
|
||||
// Pruning to the current base should work
|
||||
pruned, err = bs.PruneBlocks(1200)
|
||||
pruned, _, err = bs.PruneBlocks(1200, state)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 0, pruned)
|
||||
|
||||
// Pruning again should work
|
||||
pruned, err = bs.PruneBlocks(1300)
|
||||
pruned, _, err = bs.PruneBlocks(1300, state)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 100, pruned)
|
||||
assert.EqualValues(t, 1300, bs.Base())
|
||||
|
||||
// we should still have the header and the commit
|
||||
// as they're needed for evidence
|
||||
require.NotNil(t, bs.LoadBlockMeta(1100))
|
||||
require.Nil(t, bs.LoadBlockMeta(1099))
|
||||
require.NotNil(t, bs.LoadBlockCommit(1100))
|
||||
require.Nil(t, bs.LoadBlockCommit(1099))
|
||||
|
||||
// Pruning beyond the current height should error
|
||||
_, err = bs.PruneBlocks(1501)
|
||||
_, _, err = bs.PruneBlocks(1501, state)
|
||||
require.Error(t, err)
|
||||
|
||||
// Pruning to the current height should work
|
||||
pruned, err = bs.PruneBlocks(1500)
|
||||
pruned, _, err = bs.PruneBlocks(1500, state)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 200, pruned)
|
||||
assert.Nil(t, bs.LoadBlock(1499))
|
||||
|
||||
Reference in New Issue
Block a user