- package state_test
-
- import (
- "crypto/rand"
- "testing"
-
- "github.com/stretchr/testify/require"
- dbm "github.com/tendermint/tm-db"
-
- "github.com/tendermint/tendermint/crypto"
- "github.com/tendermint/tendermint/crypto/tmhash"
- tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
- tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
- "github.com/tendermint/tendermint/state"
- "github.com/tendermint/tendermint/state/mocks"
- "github.com/tendermint/tendermint/types"
- "github.com/tendermint/tendermint/version"
- )
-
- func TestRollback(t *testing.T) {
- var (
- height int64 = 100
- nextHeight int64 = 101
- )
- blockStore := &mocks.BlockStore{}
- stateStore := setupStateStore(t, height)
- initialState, err := stateStore.Load()
- require.NoError(t, err)
-
- // perform the rollback over a version bump
- newParams := types.DefaultConsensusParams()
- newParams.Version.AppVersion = 11
- newParams.Block.MaxBytes = 1000
- nextState := initialState.Copy()
- nextState.LastBlockHeight = nextHeight
- nextState.Version.Consensus.App = 11
- nextState.LastBlockID = makeBlockIDRandom()
- nextState.AppHash = tmhash.Sum([]byte("app_hash"))
- nextState.LastValidators = initialState.Validators
- nextState.Validators = initialState.NextValidators
- nextState.NextValidators = initialState.NextValidators.CopyIncrementProposerPriority(1)
- nextState.ConsensusParams = *newParams
- nextState.LastHeightConsensusParamsChanged = nextHeight + 1
- nextState.LastHeightValidatorsChanged = nextHeight + 1
-
- // update the state
- require.NoError(t, stateStore.Save(nextState))
-
- block := &types.BlockMeta{
- BlockID: initialState.LastBlockID,
- Header: types.Header{
- Height: initialState.LastBlockHeight,
- AppHash: crypto.CRandBytes(tmhash.Size),
- LastBlockID: makeBlockIDRandom(),
- LastResultsHash: initialState.LastResultsHash,
- },
- }
- nextBlock := &types.BlockMeta{
- BlockID: initialState.LastBlockID,
- Header: types.Header{
- Height: nextState.LastBlockHeight,
- AppHash: initialState.AppHash,
- LastBlockID: block.BlockID,
- LastResultsHash: nextState.LastResultsHash,
- },
- }
- blockStore.On("LoadBlockMeta", height).Return(block)
- blockStore.On("LoadBlockMeta", nextHeight).Return(nextBlock)
- blockStore.On("Height").Return(nextHeight)
-
- // rollback the state
- rollbackHeight, rollbackHash, err := state.Rollback(blockStore, stateStore)
- require.NoError(t, err)
- require.EqualValues(t, height, rollbackHeight)
- require.EqualValues(t, initialState.AppHash, rollbackHash)
- blockStore.AssertExpectations(t)
-
- // assert that we've recovered the prior state
- loadedState, err := stateStore.Load()
- require.NoError(t, err)
- require.EqualValues(t, initialState, loadedState)
- }
-
- func TestRollbackNoState(t *testing.T) {
- stateStore := state.NewStore(dbm.NewMemDB())
- blockStore := &mocks.BlockStore{}
-
- _, _, err := state.Rollback(blockStore, stateStore)
- require.Error(t, err)
- require.Contains(t, err.Error(), "no state found")
- }
-
- func TestRollbackNoBlocks(t *testing.T) {
- const height = int64(100)
- stateStore := setupStateStore(t, height)
- blockStore := &mocks.BlockStore{}
- blockStore.On("Height").Return(height)
- blockStore.On("LoadBlockMeta", height).Return(nil)
- blockStore.On("LoadBlockMeta", height-1).Return(nil)
-
- _, _, err := state.Rollback(blockStore, stateStore)
- require.Error(t, err)
- require.Contains(t, err.Error(), "block at height 99 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, _ := types.RandValidatorSet(5, 10)
-
- params := types.DefaultConsensusParams()
- params.Version.AppVersion = 10
-
- initialState := state.State{
- Version: tmstate.Version{
- Consensus: tmversion.Consensus{
- Block: version.BlockProtocol,
- App: 10,
- },
- Software: version.TMCoreSemVer,
- },
- ChainID: "test-chain",
- InitialHeight: 10,
- LastBlockID: makeBlockIDRandom(),
- AppHash: tmhash.Sum([]byte("app_hash")),
- LastResultsHash: tmhash.Sum([]byte("last_results_hash")),
- 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))
- return stateStore
- }
-
- func makeBlockIDRandom() types.BlockID {
- var (
- blockHash = make([]byte, tmhash.Size)
- partSetHash = make([]byte, tmhash.Size)
- )
- rand.Read(blockHash) //nolint: errcheck // ignore errcheck for read
- rand.Read(partSetHash) //nolint: errcheck // ignore errcheck for read
- return types.BlockID{
- Hash: blockHash,
- PartSetHeader: types.PartSetHeader{
- Total: 123,
- Hash: partSetHash,
- },
- }
- }
|