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