github.com/arcology-network/consensus-engine@v1.9.0/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/arcology-network/consensus-engine/abci/types"
    14  	cfg "github.com/arcology-network/consensus-engine/config"
    15  	"github.com/arcology-network/consensus-engine/crypto"
    16  	"github.com/arcology-network/consensus-engine/crypto/ed25519"
    17  	tmrand "github.com/arcology-network/consensus-engine/libs/rand"
    18  	tmstate "github.com/arcology-network/consensus-engine/proto/tendermint/state"
    19  	tmproto "github.com/arcology-network/consensus-engine/proto/tendermint/types"
    20  	sm "github.com/arcology-network/consensus-engine/state"
    21  	"github.com/arcology-network/consensus-engine/types"
    22  )
    23  
    24  func TestStoreLoadValidators(t *testing.T) {
    25  	stateDB := dbm.NewMemDB()
    26  	stateStore := sm.NewStore(stateDB)
    27  	val, _ := types.RandValidator(true, 10)
    28  	vals := types.NewValidatorSet([]*types.Validator{val})
    29  
    30  	// 1) LoadValidators loads validators using a height where they were last changed
    31  	err := sm.SaveValidatorsInfo(stateDB, 1, 1, vals)
    32  	require.NoError(t, err)
    33  	err = sm.SaveValidatorsInfo(stateDB, 2, 1, vals)
    34  	require.NoError(t, err)
    35  	loadedVals, err := stateStore.LoadValidators(2)
    36  	require.NoError(t, err)
    37  	assert.NotZero(t, loadedVals.Size())
    38  
    39  	// 2) LoadValidators loads validators using a checkpoint height
    40  
    41  	err = sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval, 1, vals)
    42  	require.NoError(t, err)
    43  
    44  	loadedVals, err = stateStore.LoadValidators(sm.ValSetCheckpointInterval)
    45  	require.NoError(t, err)
    46  	assert.NotZero(t, loadedVals.Size())
    47  }
    48  
    49  func BenchmarkLoadValidators(b *testing.B) {
    50  	const valSetSize = 100
    51  
    52  	config := cfg.ResetTestRoot("state_")
    53  	defer os.RemoveAll(config.RootDir)
    54  	dbType := dbm.BackendType(config.DBBackend)
    55  	stateDB, err := dbm.NewDB("state", dbType, config.DBDir())
    56  	require.NoError(b, err)
    57  	stateStore := sm.NewStore(stateDB)
    58  	state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile())
    59  	if err != nil {
    60  		b.Fatal(err)
    61  	}
    62  
    63  	state.Validators = genValSet(valSetSize)
    64  	state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
    65  	err = stateStore.Save(state)
    66  	require.NoError(b, err)
    67  
    68  	for i := 10; i < 10000000000; i *= 10 { // 10, 100, 1000, ...
    69  		i := i
    70  		if err := sm.SaveValidatorsInfo(stateDB,
    71  			int64(i), state.LastHeightValidatorsChanged, state.NextValidators); err != nil {
    72  			b.Fatal(err)
    73  		}
    74  
    75  		b.Run(fmt.Sprintf("height=%d", i), func(b *testing.B) {
    76  			for n := 0; n < b.N; n++ {
    77  				_, err := stateStore.LoadValidators(int64(i))
    78  				if err != nil {
    79  					b.Fatal(err)
    80  				}
    81  			}
    82  		})
    83  	}
    84  }
    85  
    86  func TestPruneStates(t *testing.T) {
    87  	testcases := map[string]struct {
    88  		makeHeights  int64
    89  		pruneFrom    int64
    90  		pruneTo      int64
    91  		expectErr    bool
    92  		expectVals   []int64
    93  		expectParams []int64
    94  		expectABCI   []int64
    95  	}{
    96  		"error on pruning from 0":      {100, 0, 5, true, nil, nil, nil},
    97  		"error when from > to":         {100, 3, 2, true, nil, nil, nil},
    98  		"error when from == to":        {100, 3, 3, true, nil, nil, nil},
    99  		"error when to does not exist": {100, 1, 101, true, nil, nil, nil},
   100  		"prune all":                    {100, 1, 100, false, []int64{93, 100}, []int64{95, 100}, []int64{100}},
   101  		"prune some": {10, 2, 8, false, []int64{1, 3, 8, 9, 10},
   102  			[]int64{1, 5, 8, 9, 10}, []int64{1, 8, 9, 10}},
   103  		"prune across checkpoint": {100001, 1, 100001, false, []int64{99993, 100000, 100001},
   104  			[]int64{99995, 100001}, []int64{100001}},
   105  	}
   106  	for name, tc := range testcases {
   107  		tc := tc
   108  		t.Run(name, func(t *testing.T) {
   109  			db := dbm.NewMemDB()
   110  			stateStore := sm.NewStore(db)
   111  			pk := ed25519.GenPrivKey().PubKey()
   112  
   113  			// Generate a bunch of state data. Validators change for heights ending with 3, and
   114  			// parameters when ending with 5.
   115  			validator := &types.Validator{Address: tmrand.Bytes(crypto.AddressSize), VotingPower: 100, PubKey: pk}
   116  			validatorSet := &types.ValidatorSet{
   117  				Validators: []*types.Validator{validator},
   118  				Proposer:   validator,
   119  			}
   120  			valsChanged := int64(0)
   121  			paramsChanged := int64(0)
   122  
   123  			for h := int64(1); h <= tc.makeHeights; h++ {
   124  				if valsChanged == 0 || h%10 == 2 {
   125  					valsChanged = h + 1 // Have to add 1, since NextValidators is what's stored
   126  				}
   127  				if paramsChanged == 0 || h%10 == 5 {
   128  					paramsChanged = h
   129  				}
   130  
   131  				state := sm.State{
   132  					InitialHeight:   1,
   133  					LastBlockHeight: h - 1,
   134  					Validators:      validatorSet,
   135  					NextValidators:  validatorSet,
   136  					ConsensusParams: tmproto.ConsensusParams{
   137  						Block: tmproto.BlockParams{MaxBytes: 10e6},
   138  					},
   139  					LastHeightValidatorsChanged:      valsChanged,
   140  					LastHeightConsensusParamsChanged: paramsChanged,
   141  				}
   142  
   143  				if state.LastBlockHeight >= 1 {
   144  					state.LastValidators = state.Validators
   145  				}
   146  
   147  				err := stateStore.Save(state)
   148  				require.NoError(t, err)
   149  
   150  				err = stateStore.SaveABCIResponses(h, &tmstate.ABCIResponses{
   151  					DeliverTxs: []*abci.ResponseDeliverTx{
   152  						{Data: []byte{1}},
   153  						{Data: []byte{2}},
   154  						{Data: []byte{3}},
   155  					},
   156  				})
   157  				require.NoError(t, err)
   158  			}
   159  
   160  			// Test assertions
   161  			err := stateStore.PruneStates(tc.pruneFrom, tc.pruneTo)
   162  			if tc.expectErr {
   163  				require.Error(t, err)
   164  				return
   165  			}
   166  			require.NoError(t, err)
   167  
   168  			expectVals := sliceToMap(tc.expectVals)
   169  			expectParams := sliceToMap(tc.expectParams)
   170  			expectABCI := sliceToMap(tc.expectABCI)
   171  
   172  			for h := int64(1); h <= tc.makeHeights; h++ {
   173  				vals, err := stateStore.LoadValidators(h)
   174  				if expectVals[h] {
   175  					require.NoError(t, err, "validators height %v", h)
   176  					require.NotNil(t, vals)
   177  				} else {
   178  					require.Error(t, err, "validators height %v", h)
   179  					require.Equal(t, sm.ErrNoValSetForHeight{Height: h}, err)
   180  				}
   181  
   182  				params, err := stateStore.LoadConsensusParams(h)
   183  				if expectParams[h] {
   184  					require.NoError(t, err, "params height %v", h)
   185  					require.False(t, params.Equal(&tmproto.ConsensusParams{}))
   186  				} else {
   187  					require.Error(t, err, "params height %v", h)
   188  				}
   189  
   190  				abci, err := stateStore.LoadABCIResponses(h)
   191  				if expectABCI[h] {
   192  					require.NoError(t, err, "abci height %v", h)
   193  					require.NotNil(t, abci)
   194  				} else {
   195  					require.Error(t, err, "abci height %v", h)
   196  					require.Equal(t, sm.ErrNoABCIResponsesForHeight{Height: h}, err)
   197  				}
   198  			}
   199  		})
   200  	}
   201  }
   202  
   203  func TestABCIResponsesResultsHash(t *testing.T) {
   204  	responses := &tmstate.ABCIResponses{
   205  		BeginBlock: &abci.ResponseBeginBlock{},
   206  		DeliverTxs: []*abci.ResponseDeliverTx{
   207  			{Code: 32, Data: []byte("Hello"), Log: "Huh?"},
   208  		},
   209  		EndBlock: &abci.ResponseEndBlock{},
   210  	}
   211  
   212  	root := sm.ABCIResponsesResultsHash(responses)
   213  
   214  	// root should be Merkle tree root of DeliverTxs responses
   215  	results := types.NewResults(responses.DeliverTxs)
   216  	assert.Equal(t, root, results.Hash())
   217  
   218  	// test we can prove first DeliverTx
   219  	proof := results.ProveResult(0)
   220  	bz, err := results[0].Marshal()
   221  	require.NoError(t, err)
   222  	assert.NoError(t, proof.Verify(root, bz))
   223  }
   224  
   225  func sliceToMap(s []int64) map[int64]bool {
   226  	m := make(map[int64]bool, len(s))
   227  	for _, i := range s {
   228  		m[i] = true
   229  	}
   230  	return m
   231  }