You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
4.8 KiB

  1. package state_test
  2. import (
  3. "context"
  4. "testing"
  5. "github.com/stretchr/testify/require"
  6. dbm "github.com/tendermint/tm-db"
  7. "github.com/tendermint/tendermint/internal/state"
  8. "github.com/tendermint/tendermint/internal/state/mocks"
  9. "github.com/tendermint/tendermint/internal/test/factory"
  10. "github.com/tendermint/tendermint/types"
  11. "github.com/tendermint/tendermint/version"
  12. )
  13. func TestRollback(t *testing.T) {
  14. var (
  15. height int64 = 100
  16. nextHeight int64 = 101
  17. )
  18. blockStore := &mocks.BlockStore{}
  19. stateStore := setupStateStore(t, height)
  20. initialState, err := stateStore.Load()
  21. require.NoError(t, err)
  22. // perform the rollback over a version bump
  23. newParams := types.DefaultConsensusParams()
  24. newParams.Version.AppVersion = 11
  25. newParams.Block.MaxBytes = 1000
  26. nextState := initialState.Copy()
  27. nextState.LastBlockHeight = nextHeight
  28. nextState.Version.Consensus.App = 11
  29. nextState.LastBlockID = factory.MakeBlockID()
  30. nextState.AppHash = factory.RandomHash()
  31. nextState.LastValidators = initialState.Validators
  32. nextState.Validators = initialState.NextValidators
  33. nextState.NextValidators = initialState.NextValidators.CopyIncrementProposerPriority(1)
  34. nextState.ConsensusParams = *newParams
  35. nextState.LastHeightConsensusParamsChanged = nextHeight + 1
  36. nextState.LastHeightValidatorsChanged = nextHeight + 1
  37. // update the state
  38. require.NoError(t, stateStore.Save(nextState))
  39. block := &types.BlockMeta{
  40. BlockID: initialState.LastBlockID,
  41. Header: types.Header{
  42. Height: initialState.LastBlockHeight,
  43. AppHash: factory.RandomHash(),
  44. LastBlockID: factory.MakeBlockID(),
  45. LastResultsHash: initialState.LastResultsHash,
  46. },
  47. }
  48. nextBlock := &types.BlockMeta{
  49. BlockID: initialState.LastBlockID,
  50. Header: types.Header{
  51. Height: nextState.LastBlockHeight,
  52. AppHash: initialState.AppHash,
  53. LastBlockID: block.BlockID,
  54. LastResultsHash: nextState.LastResultsHash,
  55. },
  56. }
  57. blockStore.On("LoadBlockMeta", height).Return(block)
  58. blockStore.On("LoadBlockMeta", nextHeight).Return(nextBlock)
  59. blockStore.On("Height").Return(nextHeight)
  60. // rollback the state
  61. rollbackHeight, rollbackHash, err := state.Rollback(blockStore, stateStore)
  62. require.NoError(t, err)
  63. require.EqualValues(t, height, rollbackHeight)
  64. require.EqualValues(t, initialState.AppHash, rollbackHash)
  65. blockStore.AssertExpectations(t)
  66. // assert that we've recovered the prior state
  67. loadedState, err := stateStore.Load()
  68. require.NoError(t, err)
  69. require.EqualValues(t, initialState, loadedState)
  70. }
  71. func TestRollbackNoState(t *testing.T) {
  72. stateStore := state.NewStore(dbm.NewMemDB())
  73. blockStore := &mocks.BlockStore{}
  74. _, _, err := state.Rollback(blockStore, stateStore)
  75. require.Error(t, err)
  76. require.Contains(t, err.Error(), "no state found")
  77. }
  78. func TestRollbackNoBlocks(t *testing.T) {
  79. const height = int64(100)
  80. stateStore := setupStateStore(t, height)
  81. blockStore := &mocks.BlockStore{}
  82. blockStore.On("Height").Return(height)
  83. blockStore.On("LoadBlockMeta", height).Return(nil)
  84. blockStore.On("LoadBlockMeta", height-1).Return(nil)
  85. _, _, err := state.Rollback(blockStore, stateStore)
  86. require.Error(t, err)
  87. require.Contains(t, err.Error(), "block at height 99 not found")
  88. }
  89. func TestRollbackDifferentStateHeight(t *testing.T) {
  90. const height = int64(100)
  91. stateStore := setupStateStore(t, height)
  92. blockStore := &mocks.BlockStore{}
  93. blockStore.On("Height").Return(height + 2)
  94. _, _, err := state.Rollback(blockStore, stateStore)
  95. require.Error(t, err)
  96. require.Equal(t, err.Error(), "statestore height (100) is not one below or equal to blockstore height (102)")
  97. }
  98. func setupStateStore(t *testing.T, height int64) state.Store {
  99. stateStore := state.NewStore(dbm.NewMemDB())
  100. ctx, cancel := context.WithCancel(context.Background())
  101. defer cancel()
  102. valSet, _ := factory.ValidatorSet(ctx, t, 5, 10)
  103. params := types.DefaultConsensusParams()
  104. params.Version.AppVersion = 10
  105. initialState := state.State{
  106. Version: state.Version{
  107. Consensus: version.Consensus{
  108. Block: version.BlockProtocol,
  109. App: 10,
  110. },
  111. Software: version.TMVersion,
  112. },
  113. ChainID: factory.DefaultTestChainID,
  114. InitialHeight: 10,
  115. LastBlockID: factory.MakeBlockID(),
  116. AppHash: factory.RandomHash(),
  117. LastResultsHash: factory.RandomHash(),
  118. LastBlockHeight: height,
  119. LastValidators: valSet,
  120. Validators: valSet.CopyIncrementProposerPriority(1),
  121. NextValidators: valSet.CopyIncrementProposerPriority(2),
  122. LastHeightValidatorsChanged: height + 1,
  123. ConsensusParams: *params,
  124. LastHeightConsensusParamsChanged: height + 1,
  125. }
  126. require.NoError(t, stateStore.Bootstrap(initialState))
  127. return stateStore
  128. }