github.com/vipernet-xyz/tm@v0.34.24/state/store_test.go (about)

     1  package state_test
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	dbm "github.com/tendermint/tm-db"
    12  
    13  	abci "github.com/vipernet-xyz/tm/abci/types"
    14  	cfg "github.com/vipernet-xyz/tm/config"
    15  	"github.com/vipernet-xyz/tm/crypto"
    16  	"github.com/vipernet-xyz/tm/crypto/ed25519"
    17  	tmrand "github.com/vipernet-xyz/tm/libs/rand"
    18  	tmstate "github.com/vipernet-xyz/tm/proto/tendermint/state"
    19  	tmproto "github.com/vipernet-xyz/tm/proto/tendermint/types"
    20  	sm "github.com/vipernet-xyz/tm/state"
    21  	"github.com/vipernet-xyz/tm/types"
    22  )
    23  
    24  func TestStoreLoadValidators(t *testing.T) {
    25  	stateDB := dbm.NewMemDB()
    26  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
    27  		DiscardABCIResponses: false,
    28  	})
    29  	val, _ := types.RandValidator(true, 10)
    30  	vals := types.NewValidatorSet([]*types.Validator{val})
    31  
    32  	// 1) LoadValidators loads validators using a height where they were last changed
    33  	err := sm.SaveValidatorsInfo(stateDB, 1, 1, vals)
    34  	require.NoError(t, err)
    35  	err = sm.SaveValidatorsInfo(stateDB, 2, 1, vals)
    36  	require.NoError(t, err)
    37  	loadedVals, err := stateStore.LoadValidators(2)
    38  	require.NoError(t, err)
    39  	assert.NotZero(t, loadedVals.Size())
    40  
    41  	// 2) LoadValidators loads validators using a checkpoint height
    42  
    43  	err = sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval, 1, vals)
    44  	require.NoError(t, err)
    45  
    46  	loadedVals, err = stateStore.LoadValidators(sm.ValSetCheckpointInterval)
    47  	require.NoError(t, err)
    48  	assert.NotZero(t, loadedVals.Size())
    49  }
    50  
    51  func BenchmarkLoadValidators(b *testing.B) {
    52  	const valSetSize = 100
    53  
    54  	config := cfg.ResetTestRoot("state_")
    55  	defer os.RemoveAll(config.RootDir)
    56  	dbType := dbm.BackendType(config.DBBackend)
    57  	stateDB, err := dbm.NewDB("state", dbType, config.DBDir())
    58  	require.NoError(b, err)
    59  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
    60  		DiscardABCIResponses: false,
    61  	})
    62  	state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile())
    63  	if err != nil {
    64  		b.Fatal(err)
    65  	}
    66  
    67  	state.Validators = genValSet(valSetSize)
    68  	state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
    69  	err = stateStore.Save(state)
    70  	require.NoError(b, err)
    71  
    72  	for i := 10; i < 10000000000; i *= 10 { // 10, 100, 1000, ...
    73  		i := i
    74  		if err := sm.SaveValidatorsInfo(stateDB,
    75  			int64(i), state.LastHeightValidatorsChanged, state.NextValidators); err != nil {
    76  			b.Fatal(err)
    77  		}
    78  
    79  		b.Run(fmt.Sprintf("height=%d", i), func(b *testing.B) {
    80  			for n := 0; n < b.N; n++ {
    81  				_, err := stateStore.LoadValidators(int64(i))
    82  				if err != nil {
    83  					b.Fatal(err)
    84  				}
    85  			}
    86  		})
    87  	}
    88  }
    89  
    90  func TestPruneStates(t *testing.T) {
    91  	testcases := map[string]struct {
    92  		makeHeights  int64
    93  		pruneFrom    int64
    94  		pruneTo      int64
    95  		expectErr    bool
    96  		expectVals   []int64
    97  		expectParams []int64
    98  		expectABCI   []int64
    99  	}{
   100  		"error on pruning from 0":      {100, 0, 5, true, nil, nil, nil},
   101  		"error when from > to":         {100, 3, 2, true, nil, nil, nil},
   102  		"error when from == to":        {100, 3, 3, true, nil, nil, nil},
   103  		"error when to does not exist": {100, 1, 101, true, nil, nil, nil},
   104  		"prune all":                    {100, 1, 100, false, []int64{93, 100}, []int64{95, 100}, []int64{100}},
   105  		"prune some": {10, 2, 8, false, []int64{1, 3, 8, 9, 10},
   106  			[]int64{1, 5, 8, 9, 10}, []int64{1, 8, 9, 10}},
   107  		"prune across checkpoint": {100001, 1, 100001, false, []int64{99993, 100000, 100001},
   108  			[]int64{99995, 100001}, []int64{100001}},
   109  	}
   110  	for name, tc := range testcases {
   111  		tc := tc
   112  		t.Run(name, func(t *testing.T) {
   113  			db := dbm.NewMemDB()
   114  			stateStore := sm.NewStore(db, sm.StoreOptions{
   115  				DiscardABCIResponses: false,
   116  			})
   117  			pk := ed25519.GenPrivKey().PubKey()
   118  
   119  			// Generate a bunch of state data. Validators change for heights ending with 3, and
   120  			// parameters when ending with 5.
   121  			validator := &types.Validator{Address: tmrand.Bytes(crypto.AddressSize), VotingPower: 100, PubKey: pk}
   122  			validatorSet := &types.ValidatorSet{
   123  				Validators: []*types.Validator{validator},
   124  				Proposer:   validator,
   125  			}
   126  			valsChanged := int64(0)
   127  			paramsChanged := int64(0)
   128  
   129  			for h := int64(1); h <= tc.makeHeights; h++ {
   130  				if valsChanged == 0 || h%10 == 2 {
   131  					valsChanged = h + 1 // Have to add 1, since NextValidators is what's stored
   132  				}
   133  				if paramsChanged == 0 || h%10 == 5 {
   134  					paramsChanged = h
   135  				}
   136  
   137  				state := sm.State{
   138  					InitialHeight:   1,
   139  					LastBlockHeight: h - 1,
   140  					Validators:      validatorSet,
   141  					NextValidators:  validatorSet,
   142  					ConsensusParams: tmproto.ConsensusParams{
   143  						Block: tmproto.BlockParams{MaxBytes: 10e6},
   144  					},
   145  					LastHeightValidatorsChanged:      valsChanged,
   146  					LastHeightConsensusParamsChanged: paramsChanged,
   147  				}
   148  
   149  				if state.LastBlockHeight >= 1 {
   150  					state.LastValidators = state.Validators
   151  				}
   152  
   153  				err := stateStore.Save(state)
   154  				require.NoError(t, err)
   155  
   156  				err = stateStore.SaveABCIResponses(h, &tmstate.ABCIResponses{
   157  					DeliverTxs: []*abci.ResponseDeliverTx{
   158  						{Data: []byte{1}},
   159  						{Data: []byte{2}},
   160  						{Data: []byte{3}},
   161  					},
   162  				})
   163  				require.NoError(t, err)
   164  			}
   165  
   166  			// Test assertions
   167  			err := stateStore.PruneStates(tc.pruneFrom, tc.pruneTo)
   168  			if tc.expectErr {
   169  				require.Error(t, err)
   170  				return
   171  			}
   172  			require.NoError(t, err)
   173  
   174  			expectVals := sliceToMap(tc.expectVals)
   175  			expectParams := sliceToMap(tc.expectParams)
   176  			expectABCI := sliceToMap(tc.expectABCI)
   177  
   178  			for h := int64(1); h <= tc.makeHeights; h++ {
   179  				vals, err := stateStore.LoadValidators(h)
   180  				if expectVals[h] {
   181  					require.NoError(t, err, "validators height %v", h)
   182  					require.NotNil(t, vals)
   183  				} else {
   184  					require.Error(t, err, "validators height %v", h)
   185  					require.Equal(t, sm.ErrNoValSetForHeight{Height: h}, err)
   186  				}
   187  
   188  				params, err := stateStore.LoadConsensusParams(h)
   189  				if expectParams[h] {
   190  					require.NoError(t, err, "params height %v", h)
   191  					require.False(t, params.Equal(&tmproto.ConsensusParams{}))
   192  				} else {
   193  					require.Error(t, err, "params height %v", h)
   194  				}
   195  
   196  				abci, err := stateStore.LoadABCIResponses(h)
   197  				if expectABCI[h] {
   198  					require.NoError(t, err, "abci height %v", h)
   199  					require.NotNil(t, abci)
   200  				} else {
   201  					require.Error(t, err, "abci height %v", h)
   202  					require.Equal(t, sm.ErrNoABCIResponsesForHeight{Height: h}, err)
   203  				}
   204  			}
   205  		})
   206  	}
   207  }
   208  
   209  func TestABCIResponsesResultsHash(t *testing.T) {
   210  	responses := &tmstate.ABCIResponses{
   211  		BeginBlock: &abci.ResponseBeginBlock{},
   212  		DeliverTxs: []*abci.ResponseDeliverTx{
   213  			{Code: 32, Data: []byte("Hello"), Log: "Huh?"},
   214  		},
   215  		EndBlock: &abci.ResponseEndBlock{},
   216  	}
   217  
   218  	root := sm.ABCIResponsesResultsHash(responses)
   219  
   220  	// root should be Merkle tree root of DeliverTxs responses
   221  	results := types.NewResults(responses.DeliverTxs)
   222  	assert.Equal(t, root, results.Hash())
   223  
   224  	// test we can prove first DeliverTx
   225  	proof := results.ProveResult(0)
   226  	bz, err := results[0].Marshal()
   227  	require.NoError(t, err)
   228  	assert.NoError(t, proof.Verify(root, bz))
   229  }
   230  
   231  func sliceToMap(s []int64) map[int64]bool {
   232  	m := make(map[int64]bool, len(s))
   233  	for _, i := range s {
   234  		m[i] = true
   235  	}
   236  	return m
   237  }
   238  
   239  func TestLastABCIResponses(t *testing.T) {
   240  	// create an empty state store.
   241  	t.Run("Not persisting responses", func(t *testing.T) {
   242  		stateDB := dbm.NewMemDB()
   243  		stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   244  			DiscardABCIResponses: false,
   245  		})
   246  		responses, err := stateStore.LoadABCIResponses(1)
   247  		require.Error(t, err)
   248  		require.Nil(t, responses)
   249  		// stub the abciresponses.
   250  		response1 := &tmstate.ABCIResponses{
   251  			BeginBlock: &abci.ResponseBeginBlock{},
   252  			DeliverTxs: []*abci.ResponseDeliverTx{
   253  				{Code: 32, Data: []byte("Hello"), Log: "Huh?"},
   254  			},
   255  			EndBlock: &abci.ResponseEndBlock{},
   256  		}
   257  		// create new db and state store and set discard abciresponses to false.
   258  		stateDB = dbm.NewMemDB()
   259  		stateStore = sm.NewStore(stateDB, sm.StoreOptions{DiscardABCIResponses: false})
   260  		height := int64(10)
   261  		// save the last abci response.
   262  		err = stateStore.SaveABCIResponses(height, response1)
   263  		require.NoError(t, err)
   264  		// search for the last abciresponse and check if it has saved.
   265  		lastResponse, err := stateStore.LoadLastABCIResponse(height)
   266  		require.NoError(t, err)
   267  		// check to see if the saved response height is the same as the loaded height.
   268  		assert.Equal(t, lastResponse, response1)
   269  		// use an incorret height to make sure the state store errors.
   270  		_, err = stateStore.LoadLastABCIResponse(height + 1)
   271  		assert.Error(t, err)
   272  		// check if the abci response didnt save in the abciresponses.
   273  		responses, err = stateStore.LoadABCIResponses(height)
   274  		require.NoError(t, err, responses)
   275  		require.Equal(t, response1, responses)
   276  	})
   277  
   278  	t.Run("persisting responses", func(t *testing.T) {
   279  		stateDB := dbm.NewMemDB()
   280  		height := int64(10)
   281  		// stub the second abciresponse.
   282  		response2 := &tmstate.ABCIResponses{
   283  			BeginBlock: &abci.ResponseBeginBlock{},
   284  			DeliverTxs: []*abci.ResponseDeliverTx{
   285  				{Code: 44, Data: []byte("Hello again"), Log: "????"},
   286  			},
   287  			EndBlock: &abci.ResponseEndBlock{},
   288  		}
   289  		// create a new statestore with the responses on.
   290  		stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   291  			DiscardABCIResponses: true,
   292  		})
   293  		// save an additional response.
   294  		err := stateStore.SaveABCIResponses(height+1, response2)
   295  		require.NoError(t, err)
   296  		// check to see if the response saved by calling the last response.
   297  		lastResponse2, err := stateStore.LoadLastABCIResponse(height + 1)
   298  		require.NoError(t, err)
   299  		// check to see if the saved response height is the same as the loaded height.
   300  		assert.Equal(t, response2, lastResponse2)
   301  		// should error as we are no longer saving the response.
   302  		_, err = stateStore.LoadABCIResponses(height + 1)
   303  		assert.Equal(t, sm.ErrABCIResponsesNotPersisted, err)
   304  	})
   305  
   306  }