github.com/Finschia/ostracon@v1.1.5/state/rollback_test.go (about) 1 package state_test 2 3 import ( 4 "crypto/rand" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 tmstate "github.com/tendermint/tendermint/proto/tendermint/state" 9 tmversion "github.com/tendermint/tendermint/proto/tendermint/version" 10 dbm "github.com/tendermint/tm-db" 11 12 "github.com/Finschia/ostracon/crypto" 13 "github.com/Finschia/ostracon/crypto/tmhash" 14 "github.com/Finschia/ostracon/state" 15 "github.com/Finschia/ostracon/state/mocks" 16 "github.com/Finschia/ostracon/types" 17 "github.com/Finschia/ostracon/version" 18 ) 19 20 var ( 21 proofHash = []byte{0} 22 ) 23 24 func TestRollback(t *testing.T) { 25 var ( 26 height int64 = 100 27 nextHeight int64 = 101 28 ) 29 blockStore := &mocks.BlockStore{} 30 stateStore := setupStateStore(t, height) 31 initialState, err := stateStore.Load() 32 require.NoError(t, err) 33 34 // perform the rollback over a version bump 35 newParams := types.DefaultConsensusParams() 36 newParams.Version.AppVersion = 11 37 newParams.Block.MaxBytes = 1000 38 nextState := initialState.Copy() 39 nextState.LastBlockHeight = nextHeight 40 nextState.Version.Consensus.App = 11 41 nextState.LastBlockID = makeBlockIDRandom() 42 nextState.AppHash = tmhash.Sum([]byte("app_hash")) 43 nextState.LastValidators = initialState.Validators 44 nextState.Validators = initialState.NextValidators 45 nextState.NextValidators = initialState.NextValidators.CopyIncrementProposerPriority(1) 46 nextState.ConsensusParams = *newParams 47 nextState.LastHeightConsensusParamsChanged = nextHeight + 1 48 nextState.LastHeightValidatorsChanged = nextHeight + 1 49 50 // update the state 51 require.NoError(t, stateStore.Save(nextState)) 52 53 block := &types.BlockMeta{ 54 BlockID: initialState.LastBlockID, 55 Header: types.Header{ 56 Height: initialState.LastBlockHeight, 57 AppHash: crypto.CRandBytes(tmhash.Size), 58 LastBlockID: makeBlockIDRandom(), 59 LastResultsHash: initialState.LastResultsHash, 60 }, 61 } 62 nextBlock := &types.BlockMeta{ 63 BlockID: initialState.LastBlockID, 64 Header: types.Header{ 65 Height: nextState.LastBlockHeight, 66 AppHash: initialState.AppHash, 67 LastBlockID: block.BlockID, 68 LastResultsHash: nextState.LastResultsHash, 69 }, 70 } 71 blockStore.On("LoadBlockMeta", height).Return(block) 72 blockStore.On("LoadBlockMeta", nextHeight).Return(nextBlock) 73 blockStore.On("Height").Return(nextHeight) 74 75 // rollback the state 76 rollbackHeight, rollbackHash, err := state.Rollback(blockStore, stateStore) 77 require.NoError(t, err) 78 require.EqualValues(t, height, rollbackHeight) 79 require.EqualValues(t, initialState.AppHash, rollbackHash) 80 blockStore.AssertExpectations(t) 81 82 // assert that we've recovered the prior state 83 loadedState, err := stateStore.Load() 84 require.NoError(t, err) 85 require.EqualValues(t, initialState, loadedState) 86 } 87 88 func TestRollbackNoState(t *testing.T) { 89 stateStore := state.NewStore(dbm.NewMemDB(), 90 state.StoreOptions{ 91 DiscardABCIResponses: false, 92 }) 93 blockStore := &mocks.BlockStore{} 94 95 _, _, err := state.Rollback(blockStore, stateStore) 96 require.Error(t, err) 97 require.Contains(t, err.Error(), "no state found") 98 } 99 100 func TestRollbackNoBlocks(t *testing.T) { 101 const height = int64(100) 102 stateStore := setupStateStore(t, height) 103 blockStore := &mocks.BlockStore{} 104 blockStore.On("Height").Return(height) 105 blockStore.On("LoadBlockMeta", height-1).Once().Return(nil) 106 107 _, _, err := state.Rollback(blockStore, stateStore) 108 require.Error(t, err) 109 require.Contains(t, err.Error(), "block at RollbackHeight 99 not found") 110 111 blockStore.On("LoadBlockMeta", height-1).Once().Return(&types.BlockMeta{}) 112 blockStore.On("LoadBlockMeta", height).Return(nil) 113 114 _, _, err = state.Rollback(blockStore, stateStore) 115 require.Error(t, err) 116 require.Contains(t, err.Error(), "block at LastBlockHeight 100 not found") 117 } 118 119 func TestRollbackDifferentStateHeight(t *testing.T) { 120 const height = int64(100) 121 stateStore := setupStateStore(t, height) 122 blockStore := &mocks.BlockStore{} 123 blockStore.On("Height").Return(height + 2) 124 125 _, _, err := state.Rollback(blockStore, stateStore) 126 require.Error(t, err) 127 require.Equal(t, err.Error(), "statestore height (100) is not one below or equal to blockstore height (102)") 128 } 129 130 func setupStateStore(t *testing.T, height int64) state.Store { 131 stateStore := state.NewStore(dbm.NewMemDB(), state.StoreOptions{DiscardABCIResponses: false}) 132 133 previousValSet, _ := types.RandValidatorSet(5, 10) 134 lastValSet := previousValSet.CopyIncrementProposerPriority(1) 135 initialValSet := lastValSet.CopyIncrementProposerPriority(1) 136 nextValSet := initialValSet.CopyIncrementProposerPriority(1) 137 138 params := types.DefaultConsensusParams() 139 params.Version.AppVersion = 10 140 141 initialState := state.State{ 142 Version: tmstate.Version{ 143 Consensus: tmversion.Consensus{ 144 Block: version.BlockProtocol, 145 App: 10, 146 }, 147 Software: version.OCCoreSemVer, 148 }, 149 ChainID: "test-chain", 150 InitialHeight: 10, 151 LastBlockID: makeBlockIDRandom(), 152 AppHash: tmhash.Sum([]byte("app_hash")), 153 LastResultsHash: tmhash.Sum([]byte("last_results_hash")), 154 LastBlockHeight: height, 155 LastValidators: lastValSet, 156 Validators: initialValSet, 157 NextValidators: nextValSet, 158 LastHeightValidatorsChanged: height + 1, 159 ConsensusParams: *params, 160 LastHeightConsensusParamsChanged: height + 1, 161 LastProofHash: proofHash, 162 } 163 164 // Need to set previous initial state for VRF verify 165 previousState := initialState.Copy() 166 previousState.LastBlockHeight = initialState.LastBlockHeight - 1 167 previousState.LastHeightConsensusParamsChanged = initialState.LastHeightConsensusParamsChanged - 1 168 previousState.LastHeightValidatorsChanged = initialState.LastHeightValidatorsChanged - 1 169 previousState.LastValidators = previousValSet 170 previousState.Validators = lastValSet 171 previousState.NextValidators = initialValSet 172 require.NoError(t, stateStore.Bootstrap(previousState)) 173 174 require.NoError(t, stateStore.Bootstrap(initialState)) 175 return stateStore 176 } 177 178 func makeBlockIDRandom() types.BlockID { 179 var ( 180 blockHash = make([]byte, tmhash.Size) 181 partSetHash = make([]byte, tmhash.Size) 182 ) 183 rand.Read(blockHash) //nolint: errcheck // ignore errcheck for read 184 rand.Read(partSetHash) //nolint: errcheck // ignore errcheck for read 185 return types.BlockID{ 186 Hash: blockHash, 187 PartSetHeader: types.PartSetHeader{ 188 Total: 123, 189 Hash: partSetHash, 190 }, 191 } 192 }