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  }