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 }