github.com/arcology-network/consensus-engine@v1.9.0/state/state_test.go (about) 1 package state_test 2 3 import ( 4 "bytes" 5 "fmt" 6 "math" 7 "math/big" 8 "os" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 dbm "github.com/tendermint/tm-db" 15 16 abci "github.com/arcology-network/consensus-engine/abci/types" 17 cfg "github.com/arcology-network/consensus-engine/config" 18 "github.com/arcology-network/consensus-engine/crypto/ed25519" 19 cryptoenc "github.com/arcology-network/consensus-engine/crypto/encoding" 20 tmrand "github.com/arcology-network/consensus-engine/libs/rand" 21 tmstate "github.com/arcology-network/consensus-engine/proto/tendermint/state" 22 tmproto "github.com/arcology-network/consensus-engine/proto/tendermint/types" 23 sm "github.com/arcology-network/consensus-engine/state" 24 "github.com/arcology-network/consensus-engine/types" 25 ) 26 27 // setupTestCase does setup common to all test cases. 28 func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, sm.State) { 29 config := cfg.ResetTestRoot("state_") 30 dbType := dbm.BackendType(config.DBBackend) 31 stateDB, err := dbm.NewDB("state", dbType, config.DBDir()) 32 stateStore := sm.NewStore(stateDB) 33 require.NoError(t, err) 34 state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile()) 35 assert.NoError(t, err, "expected no error on LoadStateFromDBOrGenesisFile") 36 err = stateStore.Save(state) 37 require.NoError(t, err) 38 39 tearDown := func(t *testing.T) { os.RemoveAll(config.RootDir) } 40 41 return tearDown, stateDB, state 42 } 43 44 // TestStateCopy tests the correct copying behaviour of State. 45 func TestStateCopy(t *testing.T) { 46 tearDown, _, state := setupTestCase(t) 47 defer tearDown(t) 48 assert := assert.New(t) 49 50 stateCopy := state.Copy() 51 52 assert.True(state.Equals(stateCopy), 53 fmt.Sprintf("expected state and its copy to be identical.\ngot: %v\nexpected: %v\n", 54 stateCopy, state)) 55 56 stateCopy.LastBlockHeight++ 57 stateCopy.LastValidators = state.Validators 58 assert.False(state.Equals(stateCopy), fmt.Sprintf(`expected states to be different. got same 59 %v`, state)) 60 } 61 62 // TestMakeGenesisStateNilValidators tests state's consistency when genesis file's validators field is nil. 63 func TestMakeGenesisStateNilValidators(t *testing.T) { 64 doc := types.GenesisDoc{ 65 ChainID: "dummy", 66 Validators: nil, 67 } 68 require.Nil(t, doc.ValidateAndComplete()) 69 state, err := sm.MakeGenesisState(&doc) 70 require.Nil(t, err) 71 require.Equal(t, 0, len(state.Validators.Validators)) 72 require.Equal(t, 0, len(state.NextValidators.Validators)) 73 } 74 75 // TestStateSaveLoad tests saving and loading State from a db. 76 func TestStateSaveLoad(t *testing.T) { 77 tearDown, stateDB, state := setupTestCase(t) 78 defer tearDown(t) 79 stateStore := sm.NewStore(stateDB) 80 assert := assert.New(t) 81 82 state.LastBlockHeight++ 83 state.LastValidators = state.Validators 84 err := stateStore.Save(state) 85 require.NoError(t, err) 86 87 loadedState, err := stateStore.Load() 88 require.NoError(t, err) 89 assert.True(state.Equals(loadedState), 90 fmt.Sprintf("expected state and its copy to be identical.\ngot: %v\nexpected: %v\n", 91 loadedState, state)) 92 } 93 94 // TestABCIResponsesSaveLoad tests saving and loading ABCIResponses. 95 func TestABCIResponsesSaveLoad1(t *testing.T) { 96 tearDown, stateDB, state := setupTestCase(t) 97 defer tearDown(t) 98 stateStore := sm.NewStore(stateDB) 99 assert := assert.New(t) 100 101 state.LastBlockHeight++ 102 103 // Build mock responses. 104 block := makeBlock(state, 2) 105 106 abciResponses := new(tmstate.ABCIResponses) 107 dtxs := make([]*abci.ResponseDeliverTx, 2) 108 abciResponses.DeliverTxs = dtxs 109 110 abciResponses.DeliverTxs[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Events: nil} 111 abciResponses.DeliverTxs[1] = &abci.ResponseDeliverTx{Data: []byte("bar"), Log: "ok", Events: nil} 112 abciResponses.EndBlock = &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{ 113 types.TM2PB.NewValidatorUpdate(ed25519.GenPrivKey().PubKey(), 10), 114 }} 115 116 err := stateStore.SaveABCIResponses(block.Height, abciResponses) 117 require.NoError(t, err) 118 loadedABCIResponses, err := stateStore.LoadABCIResponses(block.Height) 119 assert.Nil(err) 120 assert.Equal(abciResponses, loadedABCIResponses, 121 fmt.Sprintf("ABCIResponses don't match:\ngot: %v\nexpected: %v\n", 122 loadedABCIResponses, abciResponses)) 123 } 124 125 // TestResultsSaveLoad tests saving and loading ABCI results. 126 func TestABCIResponsesSaveLoad2(t *testing.T) { 127 tearDown, stateDB, _ := setupTestCase(t) 128 defer tearDown(t) 129 assert := assert.New(t) 130 131 stateStore := sm.NewStore(stateDB) 132 133 cases := [...]struct { 134 // Height is implied to equal index+2, 135 // as block 1 is created from genesis. 136 added []*abci.ResponseDeliverTx 137 expected []*abci.ResponseDeliverTx 138 }{ 139 0: { 140 nil, 141 nil, 142 }, 143 1: { 144 []*abci.ResponseDeliverTx{ 145 {Code: 32, Data: []byte("Hello"), Log: "Huh?"}, 146 }, 147 []*abci.ResponseDeliverTx{ 148 {Code: 32, Data: []byte("Hello")}, 149 }}, 150 2: { 151 []*abci.ResponseDeliverTx{ 152 {Code: 383}, 153 { 154 Data: []byte("Gotcha!"), 155 Events: []abci.Event{ 156 {Type: "type1", Attributes: []abci.EventAttribute{{Key: []byte("a"), Value: []byte("1")}}}, 157 {Type: "type2", Attributes: []abci.EventAttribute{{Key: []byte("build"), Value: []byte("stuff")}}}, 158 }, 159 }, 160 }, 161 []*abci.ResponseDeliverTx{ 162 {Code: 383, Data: nil}, 163 {Code: 0, Data: []byte("Gotcha!"), Events: []abci.Event{ 164 {Type: "type1", Attributes: []abci.EventAttribute{{Key: []byte("a"), Value: []byte("1")}}}, 165 {Type: "type2", Attributes: []abci.EventAttribute{{Key: []byte("build"), Value: []byte("stuff")}}}, 166 }}, 167 }}, 168 3: { 169 nil, 170 nil, 171 }, 172 4: { 173 []*abci.ResponseDeliverTx{nil}, 174 nil, 175 }, 176 } 177 178 // Query all before, this should return error. 179 for i := range cases { 180 h := int64(i + 1) 181 res, err := stateStore.LoadABCIResponses(h) 182 assert.Error(err, "%d: %#v", i, res) 183 } 184 185 // Add all cases. 186 for i, tc := range cases { 187 h := int64(i + 1) // last block height, one below what we save 188 responses := &tmstate.ABCIResponses{ 189 BeginBlock: &abci.ResponseBeginBlock{}, 190 DeliverTxs: tc.added, 191 EndBlock: &abci.ResponseEndBlock{}, 192 } 193 err := stateStore.SaveABCIResponses(h, responses) 194 require.NoError(t, err) 195 } 196 197 // Query all before, should return expected value. 198 for i, tc := range cases { 199 h := int64(i + 1) 200 res, err := stateStore.LoadABCIResponses(h) 201 if assert.NoError(err, "%d", i) { 202 t.Log(res) 203 responses := &tmstate.ABCIResponses{ 204 BeginBlock: &abci.ResponseBeginBlock{}, 205 DeliverTxs: tc.expected, 206 EndBlock: &abci.ResponseEndBlock{}, 207 } 208 assert.Equal(sm.ABCIResponsesResultsHash(responses), sm.ABCIResponsesResultsHash(res), "%d", i) 209 } 210 } 211 } 212 213 // TestValidatorSimpleSaveLoad tests saving and loading validators. 214 func TestValidatorSimpleSaveLoad(t *testing.T) { 215 tearDown, stateDB, state := setupTestCase(t) 216 defer tearDown(t) 217 assert := assert.New(t) 218 219 statestore := sm.NewStore(stateDB) 220 221 // Can't load anything for height 0. 222 _, err := statestore.LoadValidators(0) 223 assert.IsType(sm.ErrNoValSetForHeight{}, err, "expected err at height 0") 224 225 // Should be able to load for height 1. 226 v, err := statestore.LoadValidators(1) 227 assert.Nil(err, "expected no err at height 1") 228 assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match") 229 230 // Should be able to load for height 2. 231 v, err = statestore.LoadValidators(2) 232 assert.Nil(err, "expected no err at height 2") 233 assert.Equal(v.Hash(), state.NextValidators.Hash(), "expected validator hashes to match") 234 235 // Increment height, save; should be able to load for next & next next height. 236 state.LastBlockHeight++ 237 nextHeight := state.LastBlockHeight + 1 238 err = statestore.Save(state) 239 require.NoError(t, err) 240 vp0, err := statestore.LoadValidators(nextHeight + 0) 241 assert.Nil(err, "expected no err") 242 vp1, err := statestore.LoadValidators(nextHeight + 1) 243 assert.Nil(err, "expected no err") 244 assert.Equal(vp0.Hash(), state.Validators.Hash(), "expected validator hashes to match") 245 assert.Equal(vp1.Hash(), state.NextValidators.Hash(), "expected next validator hashes to match") 246 } 247 248 // TestValidatorChangesSaveLoad tests saving and loading a validator set with changes. 249 func TestOneValidatorChangesSaveLoad(t *testing.T) { 250 tearDown, stateDB, state := setupTestCase(t) 251 defer tearDown(t) 252 stateStore := sm.NewStore(stateDB) 253 254 // Change vals at these heights. 255 changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20} 256 N := len(changeHeights) 257 258 // Build the validator history by running updateState 259 // with the right validator set for each height. 260 highestHeight := changeHeights[N-1] + 5 261 changeIndex := 0 262 _, val := state.Validators.GetByIndex(0) 263 power := val.VotingPower 264 var err error 265 var validatorUpdates []*types.Validator 266 for i := int64(1); i < highestHeight; i++ { 267 // When we get to a change height, use the next pubkey. 268 if changeIndex < len(changeHeights) && i == changeHeights[changeIndex] { 269 changeIndex++ 270 power++ 271 } 272 header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, power) 273 validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates) 274 require.NoError(t, err) 275 state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates) 276 require.NoError(t, err) 277 err := stateStore.Save(state) 278 require.NoError(t, err) 279 } 280 281 // On each height change, increment the power by one. 282 testCases := make([]int64, highestHeight) 283 changeIndex = 0 284 power = val.VotingPower 285 for i := int64(1); i < highestHeight+1; i++ { 286 // We get to the height after a change height use the next pubkey (note 287 // our counter starts at 0 this time). 288 if changeIndex < len(changeHeights) && i == changeHeights[changeIndex]+1 { 289 changeIndex++ 290 power++ 291 } 292 testCases[i-1] = power 293 } 294 295 for i, power := range testCases { 296 v, err := stateStore.LoadValidators(int64(i + 1 + 1)) // +1 because vset changes delayed by 1 block. 297 assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", i)) 298 assert.Equal(t, v.Size(), 1, "validator set size is greater than 1: %d", v.Size()) 299 _, val := v.GetByIndex(0) 300 301 assert.Equal(t, val.VotingPower, power, fmt.Sprintf(`unexpected powerat 302 height %d`, i)) 303 } 304 } 305 306 func TestProposerFrequency(t *testing.T) { 307 308 // some explicit test cases 309 testCases := []struct { 310 powers []int64 311 }{ 312 // 2 vals 313 {[]int64{1, 1}}, 314 {[]int64{1, 2}}, 315 {[]int64{1, 100}}, 316 {[]int64{5, 5}}, 317 {[]int64{5, 100}}, 318 {[]int64{50, 50}}, 319 {[]int64{50, 100}}, 320 {[]int64{1, 1000}}, 321 322 // 3 vals 323 {[]int64{1, 1, 1}}, 324 {[]int64{1, 2, 3}}, 325 {[]int64{1, 2, 3}}, 326 {[]int64{1, 1, 10}}, 327 {[]int64{1, 1, 100}}, 328 {[]int64{1, 10, 100}}, 329 {[]int64{1, 1, 1000}}, 330 {[]int64{1, 10, 1000}}, 331 {[]int64{1, 100, 1000}}, 332 333 // 4 vals 334 {[]int64{1, 1, 1, 1}}, 335 {[]int64{1, 2, 3, 4}}, 336 {[]int64{1, 1, 1, 10}}, 337 {[]int64{1, 1, 1, 100}}, 338 {[]int64{1, 1, 1, 1000}}, 339 {[]int64{1, 1, 10, 100}}, 340 {[]int64{1, 1, 10, 1000}}, 341 {[]int64{1, 1, 100, 1000}}, 342 {[]int64{1, 10, 100, 1000}}, 343 } 344 345 for caseNum, testCase := range testCases { 346 // run each case 5 times to sample different 347 // initial priorities 348 for i := 0; i < 5; i++ { 349 valSet := genValSetWithPowers(testCase.powers) 350 testProposerFreq(t, caseNum, valSet) 351 } 352 } 353 354 // some random test cases with up to 100 validators 355 maxVals := 100 356 maxPower := 1000 357 nTestCases := 5 358 for i := 0; i < nTestCases; i++ { 359 N := tmrand.Int()%maxVals + 1 360 vals := make([]*types.Validator, N) 361 totalVotePower := int64(0) 362 for j := 0; j < N; j++ { 363 // make sure votePower > 0 364 votePower := int64(tmrand.Int()%maxPower) + 1 365 totalVotePower += votePower 366 privVal := types.NewMockPV() 367 pubKey, err := privVal.GetPubKey() 368 require.NoError(t, err) 369 val := types.NewValidator(pubKey, votePower) 370 val.ProposerPriority = tmrand.Int64() 371 vals[j] = val 372 } 373 valSet := types.NewValidatorSet(vals) 374 valSet.RescalePriorities(totalVotePower) 375 testProposerFreq(t, i, valSet) 376 } 377 } 378 379 // new val set with given powers and random initial priorities 380 func genValSetWithPowers(powers []int64) *types.ValidatorSet { 381 size := len(powers) 382 vals := make([]*types.Validator, size) 383 totalVotePower := int64(0) 384 for i := 0; i < size; i++ { 385 totalVotePower += powers[i] 386 val := types.NewValidator(ed25519.GenPrivKey().PubKey(), powers[i]) 387 val.ProposerPriority = tmrand.Int64() 388 vals[i] = val 389 } 390 valSet := types.NewValidatorSet(vals) 391 valSet.RescalePriorities(totalVotePower) 392 return valSet 393 } 394 395 // test a proposer appears as frequently as expected 396 func testProposerFreq(t *testing.T, caseNum int, valSet *types.ValidatorSet) { 397 N := valSet.Size() 398 totalPower := valSet.TotalVotingPower() 399 400 // run the proposer selection and track frequencies 401 runMult := 1 402 runs := int(totalPower) * runMult 403 freqs := make([]int, N) 404 for i := 0; i < runs; i++ { 405 prop := valSet.GetProposer() 406 idx, _ := valSet.GetByAddress(prop.Address) 407 freqs[idx]++ 408 valSet.IncrementProposerPriority(1) 409 } 410 411 // assert frequencies match expected (max off by 1) 412 for i, freq := range freqs { 413 _, val := valSet.GetByIndex(int32(i)) 414 expectFreq := int(val.VotingPower) * runMult 415 gotFreq := freq 416 abs := int(math.Abs(float64(expectFreq - gotFreq))) 417 418 // max bound on expected vs seen freq was proven 419 // to be 1 for the 2 validator case in 420 // https://github.com/cwgoes/tm-proposer-idris 421 // and inferred to generalize to N-1 422 bound := N - 1 423 require.True( 424 t, 425 abs <= bound, 426 fmt.Sprintf("Case %d val %d (%d): got %d, expected %d", caseNum, i, N, gotFreq, expectFreq), 427 ) 428 } 429 } 430 431 // TestProposerPriorityDoesNotGetResetToZero assert that we preserve accum when calling updateState 432 // see https://github.com/arcology-network/consensus-engine/issues/2718 433 func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { 434 tearDown, _, state := setupTestCase(t) 435 defer tearDown(t) 436 val1VotingPower := int64(10) 437 val1PubKey := ed25519.GenPrivKey().PubKey() 438 val1 := &types.Validator{Address: val1PubKey.Address(), PubKey: val1PubKey, VotingPower: val1VotingPower} 439 440 state.Validators = types.NewValidatorSet([]*types.Validator{val1}) 441 state.NextValidators = state.Validators 442 443 // NewValidatorSet calls IncrementProposerPriority but uses on a copy of val1 444 assert.EqualValues(t, 0, val1.ProposerPriority) 445 446 block := makeBlock(state, state.LastBlockHeight+1) 447 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 448 abciResponses := &tmstate.ABCIResponses{ 449 BeginBlock: &abci.ResponseBeginBlock{}, 450 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, 451 } 452 validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) 453 require.NoError(t, err) 454 updatedState, err := sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates) 455 assert.NoError(t, err) 456 curTotal := val1VotingPower 457 // one increment step and one validator: 0 + power - total_power == 0 458 assert.Equal(t, 0+val1VotingPower-curTotal, updatedState.NextValidators.Validators[0].ProposerPriority) 459 460 // add a validator 461 val2PubKey := ed25519.GenPrivKey().PubKey() 462 val2VotingPower := int64(100) 463 fvp, err := cryptoenc.PubKeyToProto(val2PubKey) 464 require.NoError(t, err) 465 466 updateAddVal := abci.ValidatorUpdate{PubKey: fvp, Power: val2VotingPower} 467 validatorUpdates, err = types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{updateAddVal}) 468 assert.NoError(t, err) 469 updatedState2, err := sm.UpdateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) 470 assert.NoError(t, err) 471 472 require.Equal(t, len(updatedState2.NextValidators.Validators), 2) 473 _, updatedVal1 := updatedState2.NextValidators.GetByAddress(val1PubKey.Address()) 474 _, addedVal2 := updatedState2.NextValidators.GetByAddress(val2PubKey.Address()) 475 476 // adding a validator should not lead to a ProposerPriority equal to zero (unless the combination of averaging and 477 // incrementing would cause so; which is not the case here) 478 // Steps from adding new validator: 479 // 0 - val1 prio is 0, TVP after add: 480 wantVal1Prio := int64(0) 481 totalPowerAfter := val1VotingPower + val2VotingPower 482 // 1. Add - Val2 should be initially added with (-123) => 483 wantVal2Prio := -(totalPowerAfter + (totalPowerAfter >> 3)) 484 // 2. Scale - noop 485 // 3. Center - with avg, resulting val2:-61, val1:62 486 avg := big.NewInt(0).Add(big.NewInt(wantVal1Prio), big.NewInt(wantVal2Prio)) 487 avg.Div(avg, big.NewInt(2)) 488 wantVal2Prio -= avg.Int64() // -61 489 wantVal1Prio -= avg.Int64() // 62 490 491 // 4. Steps from IncrementProposerPriority 492 wantVal1Prio += val1VotingPower // 72 493 wantVal2Prio += val2VotingPower // 39 494 wantVal1Prio -= totalPowerAfter // -38 as val1 is proposer 495 496 assert.Equal(t, wantVal1Prio, updatedVal1.ProposerPriority) 497 assert.Equal(t, wantVal2Prio, addedVal2.ProposerPriority) 498 499 // Updating a validator does not reset the ProposerPriority to zero: 500 // 1. Add - Val2 VotingPower change to 1 => 501 updatedVotingPowVal2 := int64(1) 502 updateVal := abci.ValidatorUpdate{PubKey: fvp, Power: updatedVotingPowVal2} 503 validatorUpdates, err = types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{updateVal}) 504 assert.NoError(t, err) 505 506 // this will cause the diff of priorities (77) 507 // to be larger than threshold == 2*totalVotingPower (22): 508 updatedState3, err := sm.UpdateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates) 509 assert.NoError(t, err) 510 511 require.Equal(t, len(updatedState3.NextValidators.Validators), 2) 512 _, prevVal1 := updatedState3.Validators.GetByAddress(val1PubKey.Address()) 513 _, prevVal2 := updatedState3.Validators.GetByAddress(val2PubKey.Address()) 514 _, updatedVal1 = updatedState3.NextValidators.GetByAddress(val1PubKey.Address()) 515 _, updatedVal2 := updatedState3.NextValidators.GetByAddress(val2PubKey.Address()) 516 517 // 2. Scale 518 // old prios: v1(10):-38, v2(1):39 519 wantVal1Prio = prevVal1.ProposerPriority 520 wantVal2Prio = prevVal2.ProposerPriority 521 // scale to diffMax = 22 = 2 * tvp, diff=39-(-38)=77 522 // new totalPower 523 totalPower := updatedVal1.VotingPower + updatedVal2.VotingPower 524 dist := wantVal2Prio - wantVal1Prio 525 // ratio := (dist + 2*totalPower - 1) / 2*totalPower = 98/22 = 4 526 ratio := (dist + 2*totalPower - 1) / (2 * totalPower) 527 // v1(10):-38/4, v2(1):39/4 528 wantVal1Prio /= ratio // -9 529 wantVal2Prio /= ratio // 9 530 531 // 3. Center - noop 532 // 4. IncrementProposerPriority() -> 533 // v1(10):-9+10, v2(1):9+1 -> v2 proposer so subsract tvp(11) 534 // v1(10):1, v2(1):-1 535 wantVal2Prio += updatedVal2.VotingPower // 10 -> prop 536 wantVal1Prio += updatedVal1.VotingPower // 1 537 wantVal2Prio -= totalPower // -1 538 539 assert.Equal(t, wantVal2Prio, updatedVal2.ProposerPriority) 540 assert.Equal(t, wantVal1Prio, updatedVal1.ProposerPriority) 541 } 542 543 func TestProposerPriorityProposerAlternates(t *testing.T) { 544 // Regression test that would fail if the inner workings of 545 // IncrementProposerPriority change. 546 // Additionally, make sure that same power validators alternate if both 547 // have the same voting power (and the 2nd was added later). 548 tearDown, _, state := setupTestCase(t) 549 defer tearDown(t) 550 val1VotingPower := int64(10) 551 val1PubKey := ed25519.GenPrivKey().PubKey() 552 val1 := &types.Validator{Address: val1PubKey.Address(), PubKey: val1PubKey, VotingPower: val1VotingPower} 553 554 // reset state validators to above validator 555 state.Validators = types.NewValidatorSet([]*types.Validator{val1}) 556 state.NextValidators = state.Validators 557 // we only have one validator: 558 assert.Equal(t, val1PubKey.Address(), state.Validators.Proposer.Address) 559 560 block := makeBlock(state, state.LastBlockHeight+1) 561 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 562 // no updates: 563 abciResponses := &tmstate.ABCIResponses{ 564 BeginBlock: &abci.ResponseBeginBlock{}, 565 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, 566 } 567 validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) 568 require.NoError(t, err) 569 570 updatedState, err := sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates) 571 assert.NoError(t, err) 572 573 // 0 + 10 (initial prio) - 10 (avg) - 10 (mostest - total) = -10 574 totalPower := val1VotingPower 575 wantVal1Prio := 0 + val1VotingPower - totalPower 576 assert.Equal(t, wantVal1Prio, updatedState.NextValidators.Validators[0].ProposerPriority) 577 assert.Equal(t, val1PubKey.Address(), updatedState.NextValidators.Proposer.Address) 578 579 // add a validator with the same voting power as the first 580 val2PubKey := ed25519.GenPrivKey().PubKey() 581 fvp, err := cryptoenc.PubKeyToProto(val2PubKey) 582 require.NoError(t, err) 583 updateAddVal := abci.ValidatorUpdate{PubKey: fvp, Power: val1VotingPower} 584 validatorUpdates, err = types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{updateAddVal}) 585 assert.NoError(t, err) 586 587 updatedState2, err := sm.UpdateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) 588 assert.NoError(t, err) 589 590 require.Equal(t, len(updatedState2.NextValidators.Validators), 2) 591 assert.Equal(t, updatedState2.Validators, updatedState.NextValidators) 592 593 // val1 will still be proposer as val2 just got added: 594 assert.Equal(t, val1PubKey.Address(), updatedState.NextValidators.Proposer.Address) 595 assert.Equal(t, updatedState2.Validators.Proposer.Address, updatedState2.NextValidators.Proposer.Address) 596 assert.Equal(t, updatedState2.Validators.Proposer.Address, val1PubKey.Address()) 597 assert.Equal(t, updatedState2.NextValidators.Proposer.Address, val1PubKey.Address()) 598 599 _, updatedVal1 := updatedState2.NextValidators.GetByAddress(val1PubKey.Address()) 600 _, oldVal1 := updatedState2.Validators.GetByAddress(val1PubKey.Address()) 601 _, updatedVal2 := updatedState2.NextValidators.GetByAddress(val2PubKey.Address()) 602 603 // 1. Add 604 val2VotingPower := val1VotingPower 605 totalPower = val1VotingPower + val2VotingPower // 20 606 v2PrioWhenAddedVal2 := -(totalPower + (totalPower >> 3)) // -22 607 // 2. Scale - noop 608 // 3. Center 609 avgSum := big.NewInt(0).Add(big.NewInt(v2PrioWhenAddedVal2), big.NewInt(oldVal1.ProposerPriority)) 610 avg := avgSum.Div(avgSum, big.NewInt(2)) // -11 611 expectedVal2Prio := v2PrioWhenAddedVal2 - avg.Int64() // -11 612 expectedVal1Prio := oldVal1.ProposerPriority - avg.Int64() // 11 613 // 4. Increment 614 expectedVal2Prio += val2VotingPower // -11 + 10 = -1 615 expectedVal1Prio += val1VotingPower // 11 + 10 == 21 616 expectedVal1Prio -= totalPower // 1, val1 proposer 617 618 assert.EqualValues(t, expectedVal1Prio, updatedVal1.ProposerPriority) 619 assert.EqualValues( 620 t, 621 expectedVal2Prio, 622 updatedVal2.ProposerPriority, 623 "unexpected proposer priority for validator: %v", 624 updatedVal2, 625 ) 626 627 validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) 628 require.NoError(t, err) 629 630 updatedState3, err := sm.UpdateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates) 631 assert.NoError(t, err) 632 633 assert.Equal(t, updatedState3.Validators.Proposer.Address, updatedState3.NextValidators.Proposer.Address) 634 635 assert.Equal(t, updatedState3.Validators, updatedState2.NextValidators) 636 _, updatedVal1 = updatedState3.NextValidators.GetByAddress(val1PubKey.Address()) 637 _, updatedVal2 = updatedState3.NextValidators.GetByAddress(val2PubKey.Address()) 638 639 // val1 will still be proposer: 640 assert.Equal(t, val1PubKey.Address(), updatedState3.NextValidators.Proposer.Address) 641 642 // check if expected proposer prio is matched: 643 // Increment 644 expectedVal2Prio2 := expectedVal2Prio + val2VotingPower // -1 + 10 = 9 645 expectedVal1Prio2 := expectedVal1Prio + val1VotingPower // 1 + 10 == 11 646 expectedVal1Prio2 -= totalPower // -9, val1 proposer 647 648 assert.EqualValues( 649 t, 650 expectedVal1Prio2, 651 updatedVal1.ProposerPriority, 652 "unexpected proposer priority for validator: %v", 653 updatedVal2, 654 ) 655 assert.EqualValues( 656 t, 657 expectedVal2Prio2, 658 updatedVal2.ProposerPriority, 659 "unexpected proposer priority for validator: %v", 660 updatedVal2, 661 ) 662 663 // no changes in voting power and both validators have same voting power 664 // -> proposers should alternate: 665 oldState := updatedState3 666 abciResponses = &tmstate.ABCIResponses{ 667 BeginBlock: &abci.ResponseBeginBlock{}, 668 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, 669 } 670 validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) 671 require.NoError(t, err) 672 673 oldState, err = sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) 674 assert.NoError(t, err) 675 expectedVal1Prio2 = 1 676 expectedVal2Prio2 = -1 677 expectedVal1Prio = -9 678 expectedVal2Prio = 9 679 680 for i := 0; i < 1000; i++ { 681 // no validator updates: 682 abciResponses := &tmstate.ABCIResponses{ 683 BeginBlock: &abci.ResponseBeginBlock{}, 684 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, 685 } 686 validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) 687 require.NoError(t, err) 688 689 updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) 690 assert.NoError(t, err) 691 // alternate (and cyclic priorities): 692 assert.NotEqual( 693 t, 694 updatedState.Validators.Proposer.Address, 695 updatedState.NextValidators.Proposer.Address, 696 "iter: %v", 697 i, 698 ) 699 assert.Equal(t, oldState.Validators.Proposer.Address, updatedState.NextValidators.Proposer.Address, "iter: %v", i) 700 701 _, updatedVal1 = updatedState.NextValidators.GetByAddress(val1PubKey.Address()) 702 _, updatedVal2 = updatedState.NextValidators.GetByAddress(val2PubKey.Address()) 703 704 if i%2 == 0 { 705 assert.Equal(t, updatedState.Validators.Proposer.Address, val2PubKey.Address()) 706 assert.Equal(t, expectedVal1Prio, updatedVal1.ProposerPriority) // -19 707 assert.Equal(t, expectedVal2Prio, updatedVal2.ProposerPriority) // 0 708 } else { 709 assert.Equal(t, updatedState.Validators.Proposer.Address, val1PubKey.Address()) 710 assert.Equal(t, expectedVal1Prio2, updatedVal1.ProposerPriority) // -9 711 assert.Equal(t, expectedVal2Prio2, updatedVal2.ProposerPriority) // -10 712 } 713 // update for next iteration: 714 oldState = updatedState 715 } 716 } 717 718 func TestLargeGenesisValidator(t *testing.T) { 719 tearDown, _, state := setupTestCase(t) 720 defer tearDown(t) 721 722 genesisVotingPower := types.MaxTotalVotingPower / 1000 723 genesisPubKey := ed25519.GenPrivKey().PubKey() 724 // fmt.Println("genesis addr: ", genesisPubKey.Address()) 725 genesisVal := &types.Validator{ 726 Address: genesisPubKey.Address(), 727 PubKey: genesisPubKey, 728 VotingPower: genesisVotingPower, 729 } 730 // reset state validators to above validator 731 state.Validators = types.NewValidatorSet([]*types.Validator{genesisVal}) 732 state.NextValidators = state.Validators 733 require.True(t, len(state.Validators.Validators) == 1) 734 735 // update state a few times with no validator updates 736 // asserts that the single validator's ProposerPrio stays the same 737 oldState := state 738 for i := 0; i < 10; i++ { 739 // no updates: 740 abciResponses := &tmstate.ABCIResponses{ 741 BeginBlock: &abci.ResponseBeginBlock{}, 742 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, 743 } 744 validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) 745 require.NoError(t, err) 746 747 block := makeBlock(oldState, oldState.LastBlockHeight+1) 748 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 749 750 updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) 751 require.NoError(t, err) 752 // no changes in voting power (ProposerPrio += VotingPower == Voting in 1st round; than shiftByAvg == 0, 753 // than -Total == -Voting) 754 // -> no change in ProposerPrio (stays zero): 755 assert.EqualValues(t, oldState.NextValidators, updatedState.NextValidators) 756 assert.EqualValues(t, 0, updatedState.NextValidators.Proposer.ProposerPriority) 757 758 oldState = updatedState 759 } 760 // add another validator, do a few iterations (create blocks), 761 // add more validators with same voting power as the 2nd 762 // let the genesis validator "unbond", 763 // see how long it takes until the effect wears off and both begin to alternate 764 // see: https://github.com/arcology-network/consensus-engine/issues/2960 765 firstAddedValPubKey := ed25519.GenPrivKey().PubKey() 766 firstAddedValVotingPower := int64(10) 767 fvp, err := cryptoenc.PubKeyToProto(firstAddedValPubKey) 768 require.NoError(t, err) 769 firstAddedVal := abci.ValidatorUpdate{PubKey: fvp, Power: firstAddedValVotingPower} 770 validatorUpdates, err := types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{firstAddedVal}) 771 assert.NoError(t, err) 772 abciResponses := &tmstate.ABCIResponses{ 773 BeginBlock: &abci.ResponseBeginBlock{}, 774 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{firstAddedVal}}, 775 } 776 block := makeBlock(oldState, oldState.LastBlockHeight+1) 777 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 778 updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) 779 require.NoError(t, err) 780 781 lastState := updatedState 782 for i := 0; i < 200; i++ { 783 // no updates: 784 abciResponses := &tmstate.ABCIResponses{ 785 BeginBlock: &abci.ResponseBeginBlock{}, 786 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, 787 } 788 validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) 789 require.NoError(t, err) 790 791 block := makeBlock(lastState, lastState.LastBlockHeight+1) 792 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 793 794 updatedStateInner, err := sm.UpdateState(lastState, blockID, &block.Header, abciResponses, validatorUpdates) 795 require.NoError(t, err) 796 lastState = updatedStateInner 797 } 798 // set state to last state of above iteration 799 state = lastState 800 801 // set oldState to state before above iteration 802 oldState = updatedState 803 _, oldGenesisVal := oldState.NextValidators.GetByAddress(genesisVal.Address) 804 _, newGenesisVal := state.NextValidators.GetByAddress(genesisVal.Address) 805 _, addedOldVal := oldState.NextValidators.GetByAddress(firstAddedValPubKey.Address()) 806 _, addedNewVal := state.NextValidators.GetByAddress(firstAddedValPubKey.Address()) 807 // expect large negative proposer priority for both (genesis validator decreased, 2nd validator increased): 808 assert.True(t, oldGenesisVal.ProposerPriority > newGenesisVal.ProposerPriority) 809 assert.True(t, addedOldVal.ProposerPriority < addedNewVal.ProposerPriority) 810 811 // add 10 validators with the same voting power as the one added directly after genesis: 812 for i := 0; i < 10; i++ { 813 addedPubKey := ed25519.GenPrivKey().PubKey() 814 ap, err := cryptoenc.PubKeyToProto(addedPubKey) 815 require.NoError(t, err) 816 addedVal := abci.ValidatorUpdate{PubKey: ap, Power: firstAddedValVotingPower} 817 validatorUpdates, err := types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{addedVal}) 818 assert.NoError(t, err) 819 820 abciResponses := &tmstate.ABCIResponses{ 821 BeginBlock: &abci.ResponseBeginBlock{}, 822 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{addedVal}}, 823 } 824 block := makeBlock(oldState, oldState.LastBlockHeight+1) 825 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 826 state, err = sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates) 827 require.NoError(t, err) 828 } 829 require.Equal(t, 10+2, len(state.NextValidators.Validators)) 830 831 // remove genesis validator: 832 gp, err := cryptoenc.PubKeyToProto(genesisPubKey) 833 require.NoError(t, err) 834 removeGenesisVal := abci.ValidatorUpdate{PubKey: gp, Power: 0} 835 abciResponses = &tmstate.ABCIResponses{ 836 BeginBlock: &abci.ResponseBeginBlock{}, 837 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{removeGenesisVal}}, 838 } 839 block = makeBlock(oldState, oldState.LastBlockHeight+1) 840 blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 841 validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) 842 require.NoError(t, err) 843 updatedState, err = sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates) 844 require.NoError(t, err) 845 // only the first added val (not the genesis val) should be left 846 assert.Equal(t, 11, len(updatedState.NextValidators.Validators)) 847 848 // call update state until the effect for the 3rd added validator 849 // being proposer for a long time after the genesis validator left wears off: 850 curState := updatedState 851 count := 0 852 isProposerUnchanged := true 853 for isProposerUnchanged { 854 abciResponses := &tmstate.ABCIResponses{ 855 BeginBlock: &abci.ResponseBeginBlock{}, 856 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, 857 } 858 validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) 859 require.NoError(t, err) 860 block = makeBlock(curState, curState.LastBlockHeight+1) 861 blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 862 curState, err = sm.UpdateState(curState, blockID, &block.Header, abciResponses, validatorUpdates) 863 require.NoError(t, err) 864 if !bytes.Equal(curState.Validators.Proposer.Address, curState.NextValidators.Proposer.Address) { 865 isProposerUnchanged = false 866 } 867 count++ 868 } 869 updatedState = curState 870 // the proposer changes after this number of blocks 871 firstProposerChangeExpectedAfter := 1 872 assert.Equal(t, firstProposerChangeExpectedAfter, count) 873 // store proposers here to see if we see them again in the same order: 874 numVals := len(updatedState.Validators.Validators) 875 proposers := make([]*types.Validator, numVals) 876 for i := 0; i < 100; i++ { 877 // no updates: 878 abciResponses := &tmstate.ABCIResponses{ 879 BeginBlock: &abci.ResponseBeginBlock{}, 880 EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, 881 } 882 validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) 883 require.NoError(t, err) 884 885 block := makeBlock(updatedState, updatedState.LastBlockHeight+1) 886 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 887 888 updatedState, err = sm.UpdateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) 889 require.NoError(t, err) 890 if i > numVals { // expect proposers to cycle through after the first iteration (of numVals blocks): 891 if proposers[i%numVals] == nil { 892 proposers[i%numVals] = updatedState.NextValidators.Proposer 893 } else { 894 assert.Equal(t, proposers[i%numVals], updatedState.NextValidators.Proposer) 895 } 896 } 897 } 898 } 899 900 func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) { 901 const valSetSize = 2 902 tearDown, stateDB, state := setupTestCase(t) 903 t.Cleanup(func() { tearDown(t) }) 904 stateStore := sm.NewStore(stateDB) 905 state.Validators = genValSet(valSetSize) 906 state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) 907 err := stateStore.Save(state) 908 require.NoError(t, err) 909 910 nextHeight := state.LastBlockHeight + 1 911 912 v0, err := stateStore.LoadValidators(nextHeight) 913 assert.Nil(t, err) 914 acc0 := v0.Validators[0].ProposerPriority 915 916 v1, err := stateStore.LoadValidators(nextHeight + 1) 917 assert.Nil(t, err) 918 acc1 := v1.Validators[0].ProposerPriority 919 920 assert.NotEqual(t, acc1, acc0, "expected ProposerPriority value to change between heights") 921 } 922 923 // TestValidatorChangesSaveLoad tests saving and loading a validator set with 924 // changes. 925 func TestManyValidatorChangesSaveLoad(t *testing.T) { 926 const valSetSize = 7 927 tearDown, stateDB, state := setupTestCase(t) 928 defer tearDown(t) 929 stateStore := sm.NewStore(stateDB) 930 require.Equal(t, int64(0), state.LastBlockHeight) 931 state.Validators = genValSet(valSetSize) 932 state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) 933 err := stateStore.Save(state) 934 require.NoError(t, err) 935 936 _, valOld := state.Validators.GetByIndex(0) 937 var pubkeyOld = valOld.PubKey 938 pubkey := ed25519.GenPrivKey().PubKey() 939 940 // Swap the first validator with a new one (validator set size stays the same). 941 header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(state, pubkey) 942 943 // Save state etc. 944 var validatorUpdates []*types.Validator 945 validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates) 946 require.NoError(t, err) 947 state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates) 948 require.Nil(t, err) 949 nextHeight := state.LastBlockHeight + 1 950 err = stateStore.Save(state) 951 require.NoError(t, err) 952 953 // Load nextheight, it should be the oldpubkey. 954 v0, err := stateStore.LoadValidators(nextHeight) 955 assert.Nil(t, err) 956 assert.Equal(t, valSetSize, v0.Size()) 957 index, val := v0.GetByAddress(pubkeyOld.Address()) 958 assert.NotNil(t, val) 959 if index < 0 { 960 t.Fatal("expected to find old validator") 961 } 962 963 // Load nextheight+1, it should be the new pubkey. 964 v1, err := stateStore.LoadValidators(nextHeight + 1) 965 assert.Nil(t, err) 966 assert.Equal(t, valSetSize, v1.Size()) 967 index, val = v1.GetByAddress(pubkey.Address()) 968 assert.NotNil(t, val) 969 if index < 0 { 970 t.Fatal("expected to find newly added validator") 971 } 972 } 973 974 func TestStateMakeBlock(t *testing.T) { 975 tearDown, _, state := setupTestCase(t) 976 defer tearDown(t) 977 978 proposerAddress := state.Validators.GetProposer().Address 979 stateVersion := state.Version.Consensus 980 block := makeBlock(state, 2) 981 982 // test we set some fields 983 assert.Equal(t, stateVersion, block.Version) 984 assert.Equal(t, proposerAddress, block.ProposerAddress) 985 } 986 987 // TestConsensusParamsChangesSaveLoad tests saving and loading consensus params 988 // with changes. 989 func TestConsensusParamsChangesSaveLoad(t *testing.T) { 990 tearDown, stateDB, state := setupTestCase(t) 991 defer tearDown(t) 992 993 stateStore := sm.NewStore(stateDB) 994 995 // Change vals at these heights. 996 changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20} 997 N := len(changeHeights) 998 999 // Each valset is just one validator. 1000 // create list of them. 1001 params := make([]tmproto.ConsensusParams, N+1) 1002 params[0] = state.ConsensusParams 1003 for i := 1; i < N+1; i++ { 1004 params[i] = *types.DefaultConsensusParams() 1005 params[i].Block.MaxBytes += int64(i) 1006 params[i].Block.TimeIotaMs = 10 1007 } 1008 1009 // Build the params history by running updateState 1010 // with the right params set for each height. 1011 highestHeight := changeHeights[N-1] + 5 1012 changeIndex := 0 1013 cp := params[changeIndex] 1014 var err error 1015 var validatorUpdates []*types.Validator 1016 for i := int64(1); i < highestHeight; i++ { 1017 // When we get to a change height, use the next params. 1018 if changeIndex < len(changeHeights) && i == changeHeights[changeIndex] { 1019 changeIndex++ 1020 cp = params[changeIndex] 1021 } 1022 header, blockID, responses := makeHeaderPartsResponsesParams(state, cp) 1023 validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates) 1024 require.NoError(t, err) 1025 state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates) 1026 1027 require.Nil(t, err) 1028 err := stateStore.Save(state) 1029 require.NoError(t, err) 1030 } 1031 1032 // Make all the test cases by using the same params until after the change. 1033 testCases := make([]paramsChangeTestCase, highestHeight) 1034 changeIndex = 0 1035 cp = params[changeIndex] 1036 for i := int64(1); i < highestHeight+1; i++ { 1037 // We get to the height after a change height use the next pubkey (note 1038 // our counter starts at 0 this time). 1039 if changeIndex < len(changeHeights) && i == changeHeights[changeIndex]+1 { 1040 changeIndex++ 1041 cp = params[changeIndex] 1042 } 1043 testCases[i-1] = paramsChangeTestCase{i, cp} 1044 } 1045 1046 for _, testCase := range testCases { 1047 p, err := stateStore.LoadConsensusParams(testCase.height) 1048 assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", testCase.height)) 1049 assert.EqualValues(t, testCase.params, p, fmt.Sprintf(`unexpected consensus params at 1050 height %d`, testCase.height)) 1051 } 1052 } 1053 1054 func TestStateProto(t *testing.T) { 1055 tearDown, _, state := setupTestCase(t) 1056 defer tearDown(t) 1057 1058 tc := []struct { 1059 testName string 1060 state *sm.State 1061 expPass1 bool 1062 expPass2 bool 1063 }{ 1064 {"empty state", &sm.State{}, true, false}, 1065 {"nil failure state", nil, false, false}, 1066 {"success state", &state, true, true}, 1067 } 1068 1069 for _, tt := range tc { 1070 tt := tt 1071 pbs, err := tt.state.ToProto() 1072 if !tt.expPass1 { 1073 assert.Error(t, err) 1074 } else { 1075 assert.NoError(t, err, tt.testName) 1076 } 1077 1078 smt, err := sm.StateFromProto(pbs) 1079 if tt.expPass2 { 1080 require.NoError(t, err, tt.testName) 1081 require.Equal(t, tt.state, smt, tt.testName) 1082 } else { 1083 require.Error(t, err, tt.testName) 1084 } 1085 } 1086 }