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