mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-09 14:43:19 +00:00
(cherry picked from commit a8ff617773)
Co-authored-by: Callum Waters <cmwaters19@gmail.com>
This commit is contained in:
@@ -19,6 +19,23 @@ func Rollback(bs BlockStore, ss Store) (int64, []byte, error) {
|
||||
return -1, nil, errors.New("no state found")
|
||||
}
|
||||
|
||||
height := bs.Height()
|
||||
|
||||
// NOTE: persistence of state and blocks don't happen atomically. Therefore it is possible that
|
||||
// when the user stopped the node the state wasn't updated but the blockstore was. In this situation
|
||||
// we don't need to rollback any state and can just return early
|
||||
if height == invalidState.LastBlockHeight+1 {
|
||||
return invalidState.LastBlockHeight, invalidState.AppHash, nil
|
||||
}
|
||||
|
||||
// If the state store isn't one below nor equal to the blockstore height than this violates the
|
||||
// invariant
|
||||
if height != invalidState.LastBlockHeight {
|
||||
return -1, nil, fmt.Errorf("statestore height (%d) is not one below or equal to blockstore height (%d)",
|
||||
invalidState.LastBlockHeight, height)
|
||||
}
|
||||
|
||||
// state store height is equal to blockstore height. We're good to proceed with rolling back state
|
||||
rollbackHeight := invalidState.LastBlockHeight
|
||||
rollbackBlock := bs.LoadBlockMeta(rollbackHeight)
|
||||
if rollbackBlock == nil {
|
||||
|
||||
@@ -14,42 +14,14 @@ import (
|
||||
)
|
||||
|
||||
func TestRollback(t *testing.T) {
|
||||
stateStore := state.NewStore(dbm.NewMemDB())
|
||||
blockStore := &mocks.BlockStore{}
|
||||
var (
|
||||
height int64 = 100
|
||||
appVersion uint64 = 10
|
||||
)
|
||||
|
||||
valSet, _ := factory.RandValidatorSet(5, 10)
|
||||
|
||||
params := types.DefaultConsensusParams()
|
||||
params.Version.AppVersion = appVersion
|
||||
newParams := types.DefaultConsensusParams()
|
||||
newParams.Block.MaxBytes = 10000
|
||||
|
||||
initialState := state.State{
|
||||
Version: state.Version{
|
||||
Consensus: version.Consensus{
|
||||
Block: version.BlockProtocol,
|
||||
App: 10,
|
||||
},
|
||||
Software: version.TMVersion,
|
||||
},
|
||||
ChainID: factory.DefaultTestChainID,
|
||||
InitialHeight: 10,
|
||||
LastBlockID: factory.MakeBlockID(),
|
||||
AppHash: factory.RandomHash(),
|
||||
LastResultsHash: factory.RandomHash(),
|
||||
LastBlockHeight: height,
|
||||
LastValidators: valSet,
|
||||
Validators: valSet.CopyIncrementProposerPriority(1),
|
||||
NextValidators: valSet.CopyIncrementProposerPriority(2),
|
||||
LastHeightValidatorsChanged: height + 1,
|
||||
ConsensusParams: *params,
|
||||
LastHeightConsensusParamsChanged: height + 1,
|
||||
}
|
||||
require.NoError(t, stateStore.Bootstrap(initialState))
|
||||
blockStore := &mocks.BlockStore{}
|
||||
stateStore := setupStateStore(t, height)
|
||||
initialState, err := stateStore.Load()
|
||||
require.NoError(t, err)
|
||||
|
||||
height++
|
||||
block := &types.BlockMeta{
|
||||
@@ -61,9 +33,13 @@ func TestRollback(t *testing.T) {
|
||||
},
|
||||
}
|
||||
blockStore.On("LoadBlockMeta", height).Return(block)
|
||||
blockStore.On("Height").Return(height)
|
||||
|
||||
// perform the rollback over a version bump
|
||||
appVersion++
|
||||
newParams := types.DefaultConsensusParams()
|
||||
newParams.Version.AppVersion = appVersion
|
||||
newParams.Block.MaxBytes = 1000
|
||||
nextState := initialState.Copy()
|
||||
nextState.LastBlockHeight = height
|
||||
nextState.Version.Consensus.App = appVersion
|
||||
@@ -102,19 +78,34 @@ func TestRollbackNoState(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRollbackNoBlocks(t *testing.T) {
|
||||
stateStore := state.NewStore(dbm.NewMemDB())
|
||||
const height = int64(100)
|
||||
stateStore := setupStateStore(t, height)
|
||||
blockStore := &mocks.BlockStore{}
|
||||
var (
|
||||
height int64 = 100
|
||||
appVersion uint64 = 10
|
||||
)
|
||||
blockStore.On("Height").Return(height)
|
||||
blockStore.On("LoadBlockMeta", height).Return(nil)
|
||||
|
||||
_, _, err := state.Rollback(blockStore, stateStore)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "block at height 100 not found")
|
||||
}
|
||||
|
||||
func TestRollbackDifferentStateHeight(t *testing.T) {
|
||||
const height = int64(100)
|
||||
stateStore := setupStateStore(t, height)
|
||||
blockStore := &mocks.BlockStore{}
|
||||
blockStore.On("Height").Return(height + 2)
|
||||
|
||||
_, _, err := state.Rollback(blockStore, stateStore)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, err.Error(), "statestore height (100) is not one below or equal to blockstore height (102)")
|
||||
}
|
||||
|
||||
func setupStateStore(t *testing.T, height int64) state.Store {
|
||||
stateStore := state.NewStore(dbm.NewMemDB())
|
||||
valSet, _ := factory.RandValidatorSet(5, 10)
|
||||
|
||||
params := types.DefaultConsensusParams()
|
||||
params.Version.AppVersion = appVersion
|
||||
newParams := types.DefaultConsensusParams()
|
||||
newParams.Block.MaxBytes = 10000
|
||||
params.Version.AppVersion = 10
|
||||
|
||||
initialState := state.State{
|
||||
Version: state.Version{
|
||||
@@ -137,10 +128,6 @@ func TestRollbackNoBlocks(t *testing.T) {
|
||||
ConsensusParams: *params,
|
||||
LastHeightConsensusParamsChanged: height + 1,
|
||||
}
|
||||
require.NoError(t, stateStore.Save(initialState))
|
||||
blockStore.On("LoadBlockMeta", height).Return(nil)
|
||||
|
||||
_, _, err := state.Rollback(blockStore, stateStore)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "block at height 100 not found")
|
||||
require.NoError(t, stateStore.Bootstrap(initialState))
|
||||
return stateStore
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user