Browse Source

state: add height assertion to rollback function (#7143) (#7148)

(cherry picked from commit a8ff617773)

Co-authored-by: Callum Waters <cmwaters19@gmail.com>
pull/7151/head
mergify[bot] 3 years ago
committed by GitHub
parent
commit
e62a75b627
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 50 additions and 46 deletions
  1. +17
    -0
      internal/state/rollback.go
  2. +33
    -46
      internal/state/rollback_test.go

+ 17
- 0
internal/state/rollback.go View File

@ -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 {


+ 33
- 46
internal/state/rollback_test.go View File

@ -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
}

Loading…
Cancel
Save