github.com/PikeEcosystem/tendermint@v0.0.4/state/execution_test.go (about) 1 package state_test 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/mock" 10 "github.com/stretchr/testify/require" 11 12 abci "github.com/tendermint/tendermint/abci/types" 13 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 14 tmversion "github.com/tendermint/tendermint/proto/tendermint/version" 15 16 "github.com/PikeEcosystem/tendermint/crypto" 17 "github.com/PikeEcosystem/tendermint/crypto/ed25519" 18 cryptoenc "github.com/PikeEcosystem/tendermint/crypto/encoding" 19 "github.com/PikeEcosystem/tendermint/crypto/tmhash" 20 "github.com/PikeEcosystem/tendermint/libs/bytes" 21 "github.com/PikeEcosystem/tendermint/libs/log" 22 mmock "github.com/PikeEcosystem/tendermint/mempool/mock" 23 "github.com/PikeEcosystem/tendermint/proxy" 24 sm "github.com/PikeEcosystem/tendermint/state" 25 "github.com/PikeEcosystem/tendermint/state/mocks" 26 "github.com/PikeEcosystem/tendermint/types" 27 tmtime "github.com/PikeEcosystem/tendermint/types/time" 28 "github.com/PikeEcosystem/tendermint/version" 29 ) 30 31 var ( 32 chainID = "execution_chain" 33 testPartSize uint32 = 65536 34 nTxsPerBlock = 10 35 ) 36 37 func TestApplyBlock(t *testing.T) { 38 app := &testApp{} 39 cc := proxy.NewLocalClientCreator(app) 40 proxyApp := proxy.NewAppConns(cc) 41 err := proxyApp.Start() 42 require.Nil(t, err) 43 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 44 45 state, stateDB, privVals := makeState(1, 1) 46 stateStore := sm.NewStore(stateDB) 47 48 blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), 49 mmock.Mempool{}, sm.EmptyEvidencePool{}) 50 51 block := makeBlockWithPrivVal(state, privVals[state.Validators.Validators[0].Address.String()], 1) 52 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 53 54 state, retainHeight, err := blockExec.ApplyBlock(state, blockID, block, nil) 55 require.Nil(t, err) 56 assert.EqualValues(t, retainHeight, 1) 57 58 // TODO check state and mempool 59 assert.EqualValues(t, TestAppVersion, state.Version.Consensus.App, "App version wasn't updated") 60 } 61 62 // TestBeginBlockValidators ensures we send absent validators list. 63 func TestBeginBlockValidators(t *testing.T) { 64 app := &testApp{} 65 cc := proxy.NewLocalClientCreator(app) 66 proxyApp := proxy.NewAppConns(cc) 67 err := proxyApp.Start() 68 require.Nil(t, err) 69 defer proxyApp.Stop() //nolint:errcheck // no need to check error again 70 71 state, stateDB, privVals := makeState(2, 2) 72 stateStore := sm.NewStore(stateDB) 73 74 prevHash := state.LastBlockID.Hash 75 prevParts := types.PartSetHeader{} 76 prevBlockID := types.BlockID{Hash: prevHash, PartSetHeader: prevParts} 77 78 var ( 79 now = tmtime.Now() 80 commitSig0 = types.NewCommitSigForBlock( 81 []byte("Signature1"), 82 state.Validators.Validators[0].Address, 83 now) 84 commitSig1 = types.NewCommitSigForBlock( 85 []byte("Signature2"), 86 state.Validators.Validators[1].Address, 87 now) 88 absentSig = types.NewCommitSigAbsent() 89 ) 90 91 testCases := []struct { 92 desc string 93 lastCommitSigs []types.CommitSig 94 expectedAbsentValidators []int 95 }{ 96 {"none absent", []types.CommitSig{commitSig0, commitSig1}, []int{}}, 97 {"one absent", []types.CommitSig{commitSig0, absentSig}, []int{1}}, 98 {"multiple absent", []types.CommitSig{absentSig, absentSig}, []int{0, 1}}, 99 } 100 101 for _, tc := range testCases { 102 lastCommit := types.NewCommit(1, 0, prevBlockID, tc.lastCommitSigs) 103 104 proposer := state.Validators.SelectProposer(state.LastProofHash, 1, 0) 105 message := state.MakeHashMessage(0) 106 proof, _ := privVals[proposer.Address.String()].GenerateVRFProof(message) 107 108 // block for height 2 109 block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, proposer.Address, 0, proof) 110 111 _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1) 112 require.Nil(t, err, tc.desc) 113 114 // -> app receives a list of validators with a bool indicating if they signed 115 ctr := 0 116 for i, v := range app.CommitVotes { 117 if ctr < len(tc.expectedAbsentValidators) && 118 tc.expectedAbsentValidators[ctr] == i { 119 120 assert.False(t, v.SignedLastBlock) 121 ctr++ 122 } else { 123 assert.True(t, v.SignedLastBlock) 124 } 125 } 126 } 127 } 128 129 // TestBeginBlockByzantineValidators ensures we send byzantine validators list. 130 func TestBeginBlockByzantineValidators(t *testing.T) { 131 app := &testApp{} 132 cc := proxy.NewLocalClientCreator(app) 133 proxyApp := proxy.NewAppConns(cc) 134 err := proxyApp.Start() 135 require.Nil(t, err) 136 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 137 138 state, stateDB, privVals := makeState(2, 12) 139 stateStore := sm.NewStore(stateDB) 140 141 defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 142 privVal := privVals[state.Validators.Validators[0].Address.String()] 143 blockID := makeBlockID([]byte("headerhash"), 1000, []byte("partshash")) 144 header := &types.Header{ 145 Version: tmversion.Consensus{Block: version.BlockProtocol, App: version.AppProtocol}, 146 ChainID: state.ChainID, 147 Height: 10, 148 Time: defaultEvidenceTime, 149 LastBlockID: blockID, 150 LastCommitHash: crypto.CRandBytes(tmhash.Size), 151 DataHash: crypto.CRandBytes(tmhash.Size), 152 ValidatorsHash: state.Validators.Hash(), 153 NextValidatorsHash: state.Validators.Hash(), 154 ConsensusHash: crypto.CRandBytes(tmhash.Size), 155 AppHash: crypto.CRandBytes(tmhash.Size), 156 LastResultsHash: crypto.CRandBytes(tmhash.Size), 157 EvidenceHash: crypto.CRandBytes(tmhash.Size), 158 ProposerAddress: crypto.CRandBytes(crypto.AddressSize), 159 } 160 161 // we don't need to worry about validating the evidence as long as they pass validate basic 162 dve := types.NewMockDuplicateVoteEvidenceWithValidator(3, defaultEvidenceTime, privVal, state.ChainID) 163 dve.ValidatorPower = 1000 164 lcae := &types.LightClientAttackEvidence{ 165 ConflictingBlock: &types.LightBlock{ 166 SignedHeader: &types.SignedHeader{ 167 Header: header, 168 Commit: types.NewCommit(10, 0, makeBlockID(header.Hash(), 100, []byte("partshash")), []types.CommitSig{{ 169 BlockIDFlag: types.BlockIDFlagNil, 170 ValidatorAddress: crypto.AddressHash([]byte("validator_address")), 171 Timestamp: defaultEvidenceTime, 172 Signature: crypto.CRandBytes(types.MaxSignatureSize), 173 }}), 174 }, 175 ValidatorSet: state.Validators, 176 }, 177 CommonHeight: 8, 178 ByzantineValidators: []*types.Validator{state.Validators.Validators[0]}, 179 TotalVotingPower: 12, 180 Timestamp: defaultEvidenceTime, 181 } 182 183 ev := []types.Evidence{dve, lcae} 184 185 abciEv := []abci.Evidence{ 186 { 187 Type: abci.EvidenceType_DUPLICATE_VOTE, 188 Height: 3, 189 Time: defaultEvidenceTime, 190 Validator: types.OC2PB.Validator(state.Validators.Validators[0]), 191 TotalVotingPower: 10, 192 }, 193 { 194 Type: abci.EvidenceType_LIGHT_CLIENT_ATTACK, 195 Height: 8, 196 Time: defaultEvidenceTime, 197 Validator: types.OC2PB.Validator(state.Validators.Validators[0]), 198 TotalVotingPower: 12, 199 }, 200 } 201 202 evpool := &mocks.EvidencePool{} 203 evpool.On("PendingEvidence", mock.AnythingOfType("int64")).Return(ev, int64(100)) 204 evpool.On("Update", mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return() 205 evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil) 206 207 blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), 208 mmock.Mempool{}, evpool) 209 210 now := tmtime.Now() 211 state.LastBlockID = blockID 212 state.LastBlockTime = now.Add(-1 * time.Second) 213 214 prevHash := state.LastBlockID.Hash 215 prevParts := types.PartSetHeader{} 216 prevBlockID := types.BlockID{Hash: prevHash, PartSetHeader: prevParts} 217 218 ev1 := types.DuplicateVoteEvidence{ 219 VoteA: &types.Vote{}, 220 VoteB: &types.Vote{}, 221 TotalVotingPower: 100, 222 ValidatorPower: 10, 223 Timestamp: time.Now(), 224 } 225 ev2 := types.LightClientAttackEvidence{} 226 227 testCases := []struct { 228 desc string 229 evidence []types.Evidence 230 expectedByzantineValidators []abci.Evidence 231 }{ 232 {"none byzantine", []types.Evidence{}, []abci.Evidence{}}, 233 {"one byzantine", []types.Evidence{&ev1}, ev1.ABCI()}, 234 {"multiple byzantine", []types.Evidence{&ev1, &ev2}, append(ev1.ABCI(), ev2.ABCI()...)}, 235 } 236 237 var ( 238 commitSig0 = types.NewCommitSigForBlock( 239 []byte("Signature1"), 240 state.Validators.Validators[0].Address, 241 now) 242 commitSig1 = types.NewCommitSigForBlock( 243 []byte("Signature2"), 244 state.Validators.Validators[1].Address, 245 now) 246 ) 247 commitSigs := []types.CommitSig{commitSig0, commitSig1} 248 lastCommit := types.NewCommit(9, 0, prevBlockID, commitSigs) 249 for _, tc := range testCases { 250 message := state.MakeHashMessage(0) 251 proposer := state.Validators.SelectProposer(state.LastProofHash, 1, 0) 252 proof, _ := privVals[proposer.Address.String()].GenerateVRFProof(message) 253 block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, proposer.Address, 0, proof) 254 block.Time = now 255 block.Evidence.Evidence = tc.evidence 256 _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 10) 257 require.Nil(t, err, tc.desc) 258 } 259 260 proposer := state.Validators.SelectProposer(state.LastProofHash, 12, 0) 261 privVal = privVals[proposer.Address.String()] 262 263 block := makeBlockWithPrivVal(state, privVal, 12) 264 block.Evidence = types.EvidenceData{Evidence: ev} 265 block.Header.EvidenceHash = block.Evidence.Hash() 266 blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 267 block.LastCommit, _ = makeValidCommit(11, state.LastBlockID, state.Validators, privVals) 268 block.LastCommitHash = block.LastCommit.Hash() 269 block.Time = sm.MedianTime(block.LastCommit, state.LastValidators) 270 message := state.MakeHashMessage(block.Round) 271 proof, _ := privVal.GenerateVRFProof(message) 272 block.Proof = bytes.HexBytes(proof) 273 274 state, retainHeight, err := blockExec.ApplyBlock(state, blockID, block, nil) 275 require.Nil(t, err) 276 assert.EqualValues(t, retainHeight, 1) 277 278 // TODO check state and mempool 279 assert.Equal(t, abciEv, app.ByzantineValidators) 280 } 281 282 func TestValidateValidatorUpdates(t *testing.T) { 283 pubkey1 := ed25519.GenPrivKey().PubKey() 284 pubkey2 := ed25519.GenPrivKey().PubKey() 285 pk1, err := cryptoenc.PubKeyToProto(pubkey1) 286 assert.NoError(t, err) 287 pk2, err := cryptoenc.PubKeyToProto(pubkey2) 288 assert.NoError(t, err) 289 290 defaultValidatorParams := tmproto.ValidatorParams{PubKeyTypes: []string{types.ABCIPubKeyTypeEd25519}} 291 292 testCases := []struct { 293 name string 294 295 abciUpdates []abci.ValidatorUpdate 296 validatorParams tmproto.ValidatorParams 297 298 shouldErr bool 299 }{ 300 { 301 "adding a validator is OK", 302 []abci.ValidatorUpdate{{PubKey: pk2, Power: 20}}, 303 defaultValidatorParams, 304 false, 305 }, 306 { 307 "updating a validator is OK", 308 []abci.ValidatorUpdate{{PubKey: pk1, Power: 20}}, 309 defaultValidatorParams, 310 false, 311 }, 312 { 313 "removing a validator is OK", 314 []abci.ValidatorUpdate{{PubKey: pk2, Power: 0}}, 315 defaultValidatorParams, 316 false, 317 }, 318 { 319 "adding a validator with negative power results in error", 320 []abci.ValidatorUpdate{{PubKey: pk2, Power: -100}}, 321 defaultValidatorParams, 322 true, 323 }, 324 } 325 326 for _, tc := range testCases { 327 tc := tc 328 t.Run(tc.name, func(t *testing.T) { 329 err := sm.ValidateValidatorUpdates(tc.abciUpdates, tc.validatorParams) 330 if tc.shouldErr { 331 assert.Error(t, err) 332 } else { 333 assert.NoError(t, err) 334 } 335 }) 336 } 337 } 338 339 func TestUpdateValidators(t *testing.T) { 340 pubkey1 := ed25519.GenPrivKey().PubKey() 341 val1 := types.NewValidator(pubkey1, 10) 342 pubkey2 := ed25519.GenPrivKey().PubKey() 343 val2 := types.NewValidator(pubkey2, 20) 344 345 pk, err := cryptoenc.PubKeyToProto(pubkey1) 346 require.NoError(t, err) 347 pk2, err := cryptoenc.PubKeyToProto(pubkey2) 348 require.NoError(t, err) 349 350 testCases := []struct { 351 name string 352 353 currentSet *types.ValidatorSet 354 abciUpdates []abci.ValidatorUpdate 355 356 resultingSet *types.ValidatorSet 357 shouldErr bool 358 }{ 359 { 360 "adding a validator is OK", 361 types.NewValidatorSet([]*types.Validator{val1}), 362 []abci.ValidatorUpdate{{PubKey: pk2, Power: 20}}, 363 types.NewValidatorSet([]*types.Validator{val1, val2}), 364 false, 365 }, 366 { 367 "updating a validator is OK", 368 types.NewValidatorSet([]*types.Validator{val1}), 369 []abci.ValidatorUpdate{{PubKey: pk, Power: 20}}, 370 types.NewValidatorSet([]*types.Validator{types.NewValidator(pubkey1, 20)}), 371 false, 372 }, 373 { 374 "removing a validator is OK", 375 types.NewValidatorSet([]*types.Validator{val1, val2}), 376 []abci.ValidatorUpdate{{PubKey: pk2, Power: 0}}, 377 types.NewValidatorSet([]*types.Validator{val1}), 378 false, 379 }, 380 { 381 "removing a non-existing validator results in error", 382 types.NewValidatorSet([]*types.Validator{val1}), 383 []abci.ValidatorUpdate{{PubKey: pk2, Power: 0}}, 384 types.NewValidatorSet([]*types.Validator{val1}), 385 true, 386 }, 387 } 388 389 for _, tc := range testCases { 390 tc := tc 391 t.Run(tc.name, func(t *testing.T) { 392 updates, err := types.PB2OC.ValidatorUpdates(tc.abciUpdates) 393 assert.NoError(t, err) 394 err = tc.currentSet.UpdateWithChangeSet(updates) 395 if tc.shouldErr { 396 assert.Error(t, err) 397 } else { 398 assert.NoError(t, err) 399 require.Equal(t, tc.resultingSet.Size(), tc.currentSet.Size()) 400 401 assert.Equal(t, tc.resultingSet.TotalVotingPower(), tc.currentSet.TotalVotingPower()) 402 403 assert.Equal(t, tc.resultingSet.Validators[0].Address, tc.currentSet.Validators[0].Address) 404 if tc.resultingSet.Size() > 1 { 405 assert.Equal(t, tc.resultingSet.Validators[1].Address, tc.currentSet.Validators[1].Address) 406 } 407 } 408 }) 409 } 410 } 411 412 // TestEndBlockValidatorUpdates ensures we update validator set and send an event. 413 func TestEndBlockValidatorUpdates(t *testing.T) { 414 app := &testApp{} 415 cc := proxy.NewLocalClientCreator(app) 416 proxyApp := proxy.NewAppConns(cc) 417 err := proxyApp.Start() 418 require.Nil(t, err) 419 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 420 421 state, stateDB, privVals := makeState(1, 1) 422 stateStore := sm.NewStore(stateDB) 423 424 blockExec := sm.NewBlockExecutor( 425 stateStore, 426 log.TestingLogger(), 427 proxyApp.Consensus(), 428 mmock.Mempool{}, 429 sm.EmptyEvidencePool{}, 430 ) 431 432 eventBus := types.NewEventBus() 433 err = eventBus.Start() 434 require.NoError(t, err) 435 defer eventBus.Stop() //nolint:errcheck // ignore for tests 436 437 blockExec.SetEventBus(eventBus) 438 439 updatesSub, err := eventBus.Subscribe( 440 context.Background(), 441 "TestEndBlockValidatorUpdates", 442 types.EventQueryValidatorSetUpdates, 443 ) 444 require.NoError(t, err) 445 446 block := makeBlockWithPrivVal(state, privVals[state.Validators.Validators[0].Address.String()], 1) 447 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 448 449 pubkey := ed25519.GenPrivKey().PubKey() 450 pk, err := cryptoenc.PubKeyToProto(pubkey) 451 require.NoError(t, err) 452 app.ValidatorUpdates = []abci.ValidatorUpdate{ 453 {PubKey: pk, Power: 10}, 454 } 455 456 state, _, err = blockExec.ApplyBlock(state, blockID, block, nil) 457 require.Nil(t, err) 458 // test new validator was added to NextValidators 459 if assert.Equal(t, state.Validators.Size()+1, state.NextValidators.Size()) { 460 idx, _ := state.NextValidators.GetByAddress(pubkey.Address()) 461 if idx < 0 { 462 t.Fatalf("can't find address %v in the set %v", pubkey.Address(), state.NextValidators) 463 } 464 } 465 466 // test we threw an event 467 select { 468 case msg := <-updatesSub.Out(): 469 event, ok := msg.Data().(types.EventDataValidatorSetUpdates) 470 require.True(t, ok, "Expected event of type EventDataValidatorSetUpdates, got %T", msg.Data()) 471 if assert.NotEmpty(t, event.ValidatorUpdates) { 472 assert.Equal(t, pubkey, event.ValidatorUpdates[0].PubKey) 473 assert.EqualValues(t, 10, event.ValidatorUpdates[0].VotingPower) 474 } 475 case <-updatesSub.Cancelled(): 476 t.Fatalf("updatesSub was cancelled (reason: %v)", updatesSub.Err()) 477 case <-time.After(1 * time.Second): 478 t.Fatal("Did not receive EventValidatorSetUpdates within 1 sec.") 479 } 480 } 481 482 // TestEndBlockValidatorUpdatesResultingInEmptySet checks that processing validator updates that 483 // would result in empty set causes no panic, an error is raised and NextValidators is not updated 484 func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { 485 app := &testApp{} 486 cc := proxy.NewLocalClientCreator(app) 487 proxyApp := proxy.NewAppConns(cc) 488 err := proxyApp.Start() 489 require.Nil(t, err) 490 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 491 492 state, stateDB, _ := makeState(1, 1) 493 stateStore := sm.NewStore(stateDB) 494 blockExec := sm.NewBlockExecutor( 495 stateStore, 496 log.TestingLogger(), 497 proxyApp.Consensus(), 498 mmock.Mempool{}, 499 sm.EmptyEvidencePool{}, 500 ) 501 502 block := makeBlock(state, 1) 503 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} 504 505 vp, err := cryptoenc.PubKeyToProto(state.Validators.Validators[0].PubKey) 506 require.NoError(t, err) 507 // Remove the only validator 508 app.ValidatorUpdates = []abci.ValidatorUpdate{ 509 {PubKey: vp, Power: 0}, 510 } 511 512 assert.NotPanics(t, func() { state, _, err = blockExec.ApplyBlock(state, blockID, block, nil) }) 513 assert.NotNil(t, err) 514 assert.NotEmpty(t, state.NextValidators.Validators) 515 } 516 517 func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID { 518 var ( 519 h = make([]byte, tmhash.Size) 520 psH = make([]byte, tmhash.Size) 521 ) 522 copy(h, hash) 523 copy(psH, partSetHash) 524 return types.BlockID{ 525 Hash: h, 526 PartSetHeader: types.PartSetHeader{ 527 Total: partSetSize, 528 Hash: psH, 529 }, 530 } 531 }