github.com/aakash4dev/cometbft@v0.38.2/state/execution_test.go (about) 1 package state_test 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/mock" 11 "github.com/stretchr/testify/require" 12 13 dbm "github.com/aakash4dev/cometbft-db" 14 15 abciclientmocks "github.com/aakash4dev/cometbft/abci/client/mocks" 16 abci "github.com/aakash4dev/cometbft/abci/types" 17 abcimocks "github.com/aakash4dev/cometbft/abci/types/mocks" 18 "github.com/aakash4dev/cometbft/crypto" 19 "github.com/aakash4dev/cometbft/crypto/ed25519" 20 cryptoenc "github.com/aakash4dev/cometbft/crypto/encoding" 21 "github.com/aakash4dev/cometbft/crypto/tmhash" 22 "github.com/aakash4dev/cometbft/internal/test" 23 "github.com/aakash4dev/cometbft/libs/log" 24 mpmocks "github.com/aakash4dev/cometbft/mempool/mocks" 25 cmtproto "github.com/aakash4dev/cometbft/proto/tendermint/types" 26 cmtversion "github.com/aakash4dev/cometbft/proto/tendermint/version" 27 "github.com/aakash4dev/cometbft/proxy" 28 pmocks "github.com/aakash4dev/cometbft/proxy/mocks" 29 sm "github.com/aakash4dev/cometbft/state" 30 "github.com/aakash4dev/cometbft/state/mocks" 31 "github.com/aakash4dev/cometbft/store" 32 "github.com/aakash4dev/cometbft/types" 33 cmttime "github.com/aakash4dev/cometbft/types/time" 34 "github.com/aakash4dev/cometbft/version" 35 ) 36 37 var ( 38 chainID = "execution_chain" 39 testPartSize uint32 = 65536 40 ) 41 42 func TestApplyBlock(t *testing.T) { 43 app := &testApp{} 44 cc := proxy.NewLocalClientCreator(app) 45 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 46 err := proxyApp.Start() 47 require.Nil(t, err) 48 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 49 50 state, stateDB, _ := makeState(1, 1) 51 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 52 DiscardABCIResponses: false, 53 }) 54 blockStore := store.NewBlockStore(dbm.NewMemDB()) 55 56 mp := &mpmocks.Mempool{} 57 mp.On("Lock").Return() 58 mp.On("Unlock").Return() 59 mp.On("FlushAppConn", mock.Anything).Return(nil) 60 mp.On("Update", 61 mock.Anything, 62 mock.Anything, 63 mock.Anything, 64 mock.Anything, 65 mock.Anything, 66 mock.Anything).Return(nil) 67 blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), 68 mp, sm.EmptyEvidencePool{}, blockStore) 69 70 block := makeBlock(state, 1, new(types.Commit)) 71 bps, err := block.MakePartSet(testPartSize) 72 require.NoError(t, err) 73 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} 74 75 state, err = blockExec.ApplyBlock(state, blockID, block) 76 require.Nil(t, err) 77 78 // TODO check state and mempool 79 assert.EqualValues(t, 1, state.Version.Consensus.App, "App version wasn't updated") 80 } 81 82 // TestFinalizeBlockDecidedLastCommit ensures we correctly send the 83 // DecidedLastCommit to the application. The test ensures that the 84 // DecidedLastCommit properly reflects which validators signed the preceding 85 // block. 86 func TestFinalizeBlockDecidedLastCommit(t *testing.T) { 87 app := &testApp{} 88 baseTime := time.Now() 89 cc := proxy.NewLocalClientCreator(app) 90 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 91 err := proxyApp.Start() 92 require.NoError(t, err) 93 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 94 95 state, stateDB, privVals := makeState(7, 1) 96 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 97 DiscardABCIResponses: false, 98 }) 99 absentSig := types.NewExtendedCommitSigAbsent() 100 101 testCases := []struct { 102 name string 103 absentCommitSigs map[int]bool 104 }{ 105 {"none absent", map[int]bool{}}, 106 {"one absent", map[int]bool{1: true}}, 107 {"multiple absent", map[int]bool{1: true, 3: true}}, 108 } 109 110 for _, tc := range testCases { 111 t.Run(tc.name, func(t *testing.T) { 112 blockStore := store.NewBlockStore(dbm.NewMemDB()) 113 evpool := &mocks.EvidencePool{} 114 evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, 0) 115 evpool.On("Update", mock.Anything, mock.Anything).Return() 116 evpool.On("CheckEvidence", mock.Anything).Return(nil) 117 mp := &mpmocks.Mempool{} 118 mp.On("Lock").Return() 119 mp.On("Unlock").Return() 120 mp.On("FlushAppConn", mock.Anything).Return(nil) 121 mp.On("Update", 122 mock.Anything, 123 mock.Anything, 124 mock.Anything, 125 mock.Anything, 126 mock.Anything, 127 mock.Anything, 128 mock.Anything).Return(nil) 129 130 eventBus := types.NewEventBus() 131 require.NoError(t, eventBus.Start()) 132 133 blockExec := sm.NewBlockExecutor(stateStore, log.NewNopLogger(), proxyApp.Consensus(), mp, evpool, blockStore) 134 state, _, lastCommit, err := makeAndCommitGoodBlock(state, 1, new(types.Commit), state.NextValidators.Validators[0].Address, blockExec, privVals, nil) 135 require.NoError(t, err) 136 137 for idx, isAbsent := range tc.absentCommitSigs { 138 if isAbsent { 139 lastCommit.ExtendedSignatures[idx] = absentSig 140 } 141 } 142 143 // block for height 2 144 block := makeBlock(state, 2, lastCommit.ToCommit()) 145 bps, err := block.MakePartSet(testPartSize) 146 require.NoError(t, err) 147 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} 148 _, err = blockExec.ApplyBlock(state, blockID, block) 149 require.NoError(t, err) 150 require.True(t, app.LastTime.After(baseTime)) 151 152 // -> app receives a list of validators with a bool indicating if they signed 153 for i, v := range app.CommitVotes { 154 _, absent := tc.absentCommitSigs[i] 155 assert.Equal(t, !absent, v.BlockIdFlag != cmtproto.BlockIDFlagAbsent) 156 } 157 }) 158 } 159 } 160 161 // TestFinalizeBlockValidators ensures we send absent validators list. 162 func TestFinalizeBlockValidators(t *testing.T) { 163 app := &testApp{} 164 cc := proxy.NewLocalClientCreator(app) 165 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 166 err := proxyApp.Start() 167 require.NoError(t, err) 168 defer proxyApp.Stop() //nolint:errcheck // no need to check error again 169 170 state, stateDB, _ := makeState(2, 2) 171 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 172 DiscardABCIResponses: false, 173 }) 174 175 prevHash := state.LastBlockID.Hash 176 prevParts := types.PartSetHeader{} 177 prevBlockID := types.BlockID{Hash: prevHash, PartSetHeader: prevParts} 178 179 var ( 180 now = cmttime.Now() 181 commitSig0 = types.ExtendedCommitSig{ 182 CommitSig: types.CommitSig{ 183 BlockIDFlag: types.BlockIDFlagCommit, 184 ValidatorAddress: state.Validators.Validators[0].Address, 185 Timestamp: now, 186 Signature: []byte("Signature1"), 187 }, 188 Extension: []byte("extension1"), 189 ExtensionSignature: []byte("extensionSig1"), 190 } 191 192 commitSig1 = types.ExtendedCommitSig{ 193 CommitSig: types.CommitSig{ 194 BlockIDFlag: types.BlockIDFlagCommit, 195 ValidatorAddress: state.Validators.Validators[1].Address, 196 Timestamp: now, 197 Signature: []byte("Signature2"), 198 }, 199 Extension: []byte("extension2"), 200 ExtensionSignature: []byte("extensionSig2"), 201 } 202 absentSig = types.NewExtendedCommitSigAbsent() 203 ) 204 205 testCases := []struct { 206 desc string 207 lastCommitSigs []types.ExtendedCommitSig 208 expectedAbsentValidators []int 209 shouldHaveTime bool 210 }{ 211 {"none absent", []types.ExtendedCommitSig{commitSig0, commitSig1}, []int{}, true}, 212 {"one absent", []types.ExtendedCommitSig{commitSig0, absentSig}, []int{1}, true}, 213 {"multiple absent", []types.ExtendedCommitSig{absentSig, absentSig}, []int{0, 1}, false}, 214 } 215 216 for _, tc := range testCases { 217 lastCommit := &types.ExtendedCommit{ 218 Height: 1, 219 BlockID: prevBlockID, 220 ExtendedSignatures: tc.lastCommitSigs, 221 } 222 223 // block for height 2 224 block := makeBlock(state, 2, lastCommit.ToCommit()) 225 226 _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1) 227 require.NoError(t, err, tc.desc) 228 require.True(t, 229 !tc.shouldHaveTime || 230 app.LastTime.Equal(now) || app.LastTime.After(now), 231 "'last_time' should be at or after 'now'; tc %v, last_time %v, now %v", tc.desc, app.LastTime, now, 232 ) 233 234 // -> app receives a list of validators with a bool indicating if they signed 235 ctr := 0 236 for i, v := range app.CommitVotes { 237 if ctr < len(tc.expectedAbsentValidators) && 238 tc.expectedAbsentValidators[ctr] == i { 239 240 assert.Equal(t, v.BlockIdFlag, cmtproto.BlockIDFlagAbsent) 241 ctr++ 242 } else { 243 assert.NotEqual(t, v.BlockIdFlag, cmtproto.BlockIDFlagAbsent) 244 } 245 } 246 } 247 } 248 249 // TestFinalizeBlockMisbehavior ensures we send misbehavior list. 250 func TestFinalizeBlockMisbehavior(t *testing.T) { 251 app := &testApp{} 252 cc := proxy.NewLocalClientCreator(app) 253 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 254 err := proxyApp.Start() 255 require.NoError(t, err) 256 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 257 258 state, stateDB, privVals := makeState(1, 1) 259 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 260 DiscardABCIResponses: false, 261 }) 262 263 defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 264 privVal := privVals[state.Validators.Validators[0].Address.String()] 265 blockID := makeBlockID([]byte("headerhash"), 1000, []byte("partshash")) 266 header := &types.Header{ 267 Version: cmtversion.Consensus{Block: version.BlockProtocol, App: 1}, 268 ChainID: state.ChainID, 269 Height: 10, 270 Time: defaultEvidenceTime, 271 LastBlockID: blockID, 272 LastCommitHash: crypto.CRandBytes(tmhash.Size), 273 DataHash: crypto.CRandBytes(tmhash.Size), 274 ValidatorsHash: state.Validators.Hash(), 275 NextValidatorsHash: state.Validators.Hash(), 276 ConsensusHash: crypto.CRandBytes(tmhash.Size), 277 AppHash: crypto.CRandBytes(tmhash.Size), 278 LastResultsHash: crypto.CRandBytes(tmhash.Size), 279 EvidenceHash: crypto.CRandBytes(tmhash.Size), 280 ProposerAddress: crypto.CRandBytes(crypto.AddressSize), 281 } 282 283 // we don't need to worry about validating the evidence as long as they pass validate basic 284 dve, err := types.NewMockDuplicateVoteEvidenceWithValidator(3, defaultEvidenceTime, privVal, state.ChainID) 285 require.NoError(t, err) 286 dve.ValidatorPower = 1000 287 lcae := &types.LightClientAttackEvidence{ 288 ConflictingBlock: &types.LightBlock{ 289 SignedHeader: &types.SignedHeader{ 290 Header: header, 291 Commit: &types.Commit{ 292 Height: 10, 293 BlockID: makeBlockID(header.Hash(), 100, []byte("partshash")), 294 Signatures: []types.CommitSig{{ 295 BlockIDFlag: types.BlockIDFlagNil, 296 ValidatorAddress: crypto.AddressHash([]byte("validator_address")), 297 Timestamp: defaultEvidenceTime, 298 Signature: crypto.CRandBytes(types.MaxSignatureSize)}}, 299 }, 300 }, 301 ValidatorSet: state.Validators, 302 }, 303 CommonHeight: 8, 304 ByzantineValidators: []*types.Validator{state.Validators.Validators[0]}, 305 TotalVotingPower: 12, 306 Timestamp: defaultEvidenceTime, 307 } 308 309 ev := []types.Evidence{dve, lcae} 310 311 abciMb := []abci.Misbehavior{ 312 { 313 Type: abci.MisbehaviorType_DUPLICATE_VOTE, 314 Height: 3, 315 Time: defaultEvidenceTime, 316 Validator: types.TM2PB.Validator(state.Validators.Validators[0]), 317 TotalVotingPower: 10, 318 }, 319 { 320 Type: abci.MisbehaviorType_LIGHT_CLIENT_ATTACK, 321 Height: 8, 322 Time: defaultEvidenceTime, 323 Validator: types.TM2PB.Validator(state.Validators.Validators[0]), 324 TotalVotingPower: 12, 325 }, 326 } 327 328 evpool := &mocks.EvidencePool{} 329 evpool.On("PendingEvidence", mock.AnythingOfType("int64")).Return(ev, int64(100)) 330 evpool.On("Update", mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return() 331 evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil) 332 mp := &mpmocks.Mempool{} 333 mp.On("Lock").Return() 334 mp.On("Unlock").Return() 335 mp.On("FlushAppConn", mock.Anything).Return(nil) 336 mp.On("Update", 337 mock.Anything, 338 mock.Anything, 339 mock.Anything, 340 mock.Anything, 341 mock.Anything, 342 mock.Anything).Return(nil) 343 344 blockStore := store.NewBlockStore(dbm.NewMemDB()) 345 346 blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), 347 mp, evpool, blockStore) 348 349 block := makeBlock(state, 1, new(types.Commit)) 350 block.Evidence = types.EvidenceData{Evidence: ev} 351 block.Header.EvidenceHash = block.Evidence.Hash() 352 bps, err := block.MakePartSet(testPartSize) 353 require.NoError(t, err) 354 355 blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} 356 357 _, err = blockExec.ApplyBlock(state, blockID, block) 358 require.NoError(t, err) 359 360 // TODO check state and mempool 361 assert.Equal(t, abciMb, app.Misbehavior) 362 } 363 364 func TestProcessProposal(t *testing.T) { 365 const height = 2 366 txs := test.MakeNTxs(height, 10) 367 368 logger := log.NewNopLogger() 369 app := &abcimocks.Application{} 370 app.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil) 371 372 cc := proxy.NewLocalClientCreator(app) 373 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 374 err := proxyApp.Start() 375 require.NoError(t, err) 376 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 377 378 state, stateDB, privVals := makeState(1, height) 379 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 380 DiscardABCIResponses: false, 381 }) 382 blockStore := store.NewBlockStore(dbm.NewMemDB()) 383 eventBus := types.NewEventBus() 384 err = eventBus.Start() 385 require.NoError(t, err) 386 387 blockExec := sm.NewBlockExecutor( 388 stateStore, 389 logger, 390 proxyApp.Consensus(), 391 new(mpmocks.Mempool), 392 sm.EmptyEvidencePool{}, 393 blockStore, 394 ) 395 396 block0 := makeBlock(state, height-1, new(types.Commit)) 397 lastCommitSig := []types.CommitSig{} 398 partSet, err := block0.MakePartSet(types.BlockPartSizeBytes) 399 require.NoError(t, err) 400 blockID := types.BlockID{Hash: block0.Hash(), PartSetHeader: partSet.Header()} 401 voteInfos := []abci.VoteInfo{} 402 for _, privVal := range privVals { 403 pk, err := privVal.GetPubKey() 404 require.NoError(t, err) 405 idx, _ := state.Validators.GetByAddress(pk.Address()) 406 vote := types.MakeVoteNoError(t, privVal, block0.Header.ChainID, idx, height-1, 0, 2, blockID, time.Now()) 407 addr := pk.Address() 408 voteInfos = append(voteInfos, 409 abci.VoteInfo{ 410 BlockIdFlag: cmtproto.BlockIDFlagCommit, 411 Validator: abci.Validator{ 412 Address: addr, 413 Power: 1000, 414 }, 415 }) 416 lastCommitSig = append(lastCommitSig, vote.CommitSig()) 417 } 418 419 block1 := makeBlock(state, height, &types.Commit{ 420 Height: height - 1, 421 Signatures: lastCommitSig, 422 }) 423 424 block1.Txs = txs 425 426 expectedRpp := &abci.RequestProcessProposal{ 427 Txs: block1.Txs.ToSliceOfBytes(), 428 Hash: block1.Hash(), 429 Height: block1.Header.Height, 430 Time: block1.Header.Time, 431 Misbehavior: block1.Evidence.Evidence.ToABCI(), 432 ProposedLastCommit: abci.CommitInfo{ 433 Round: 0, 434 Votes: voteInfos, 435 }, 436 NextValidatorsHash: block1.NextValidatorsHash, 437 ProposerAddress: block1.ProposerAddress, 438 } 439 440 acceptBlock, err := blockExec.ProcessProposal(block1, state) 441 require.NoError(t, err) 442 require.True(t, acceptBlock) 443 app.AssertExpectations(t) 444 app.AssertCalled(t, "ProcessProposal", context.TODO(), expectedRpp) 445 } 446 447 func TestValidateValidatorUpdates(t *testing.T) { 448 pubkey1 := ed25519.GenPrivKey().PubKey() 449 pubkey2 := ed25519.GenPrivKey().PubKey() 450 pk1, err := cryptoenc.PubKeyToProto(pubkey1) 451 assert.NoError(t, err) 452 pk2, err := cryptoenc.PubKeyToProto(pubkey2) 453 assert.NoError(t, err) 454 455 defaultValidatorParams := types.ValidatorParams{PubKeyTypes: []string{types.ABCIPubKeyTypeEd25519}} 456 457 testCases := []struct { 458 name string 459 460 abciUpdates []abci.ValidatorUpdate 461 validatorParams types.ValidatorParams 462 463 shouldErr bool 464 }{ 465 { 466 "adding a validator is OK", 467 []abci.ValidatorUpdate{{PubKey: pk2, Power: 20}}, 468 defaultValidatorParams, 469 false, 470 }, 471 { 472 "updating a validator is OK", 473 []abci.ValidatorUpdate{{PubKey: pk1, Power: 20}}, 474 defaultValidatorParams, 475 false, 476 }, 477 { 478 "removing a validator is OK", 479 []abci.ValidatorUpdate{{PubKey: pk2, Power: 0}}, 480 defaultValidatorParams, 481 false, 482 }, 483 { 484 "adding a validator with negative power results in error", 485 []abci.ValidatorUpdate{{PubKey: pk2, Power: -100}}, 486 defaultValidatorParams, 487 true, 488 }, 489 } 490 491 for _, tc := range testCases { 492 tc := tc 493 t.Run(tc.name, func(t *testing.T) { 494 err := sm.ValidateValidatorUpdates(tc.abciUpdates, tc.validatorParams) 495 if tc.shouldErr { 496 assert.Error(t, err) 497 } else { 498 assert.NoError(t, err) 499 } 500 }) 501 } 502 } 503 504 func TestUpdateValidators(t *testing.T) { 505 pubkey1 := ed25519.GenPrivKey().PubKey() 506 val1 := types.NewValidator(pubkey1, 10) 507 pubkey2 := ed25519.GenPrivKey().PubKey() 508 val2 := types.NewValidator(pubkey2, 20) 509 510 pk, err := cryptoenc.PubKeyToProto(pubkey1) 511 require.NoError(t, err) 512 pk2, err := cryptoenc.PubKeyToProto(pubkey2) 513 require.NoError(t, err) 514 515 testCases := []struct { 516 name string 517 518 currentSet *types.ValidatorSet 519 abciUpdates []abci.ValidatorUpdate 520 521 resultingSet *types.ValidatorSet 522 shouldErr bool 523 }{ 524 { 525 "adding a validator is OK", 526 types.NewValidatorSet([]*types.Validator{val1}), 527 []abci.ValidatorUpdate{{PubKey: pk2, Power: 20}}, 528 types.NewValidatorSet([]*types.Validator{val1, val2}), 529 false, 530 }, 531 { 532 "updating a validator is OK", 533 types.NewValidatorSet([]*types.Validator{val1}), 534 []abci.ValidatorUpdate{{PubKey: pk, Power: 20}}, 535 types.NewValidatorSet([]*types.Validator{types.NewValidator(pubkey1, 20)}), 536 false, 537 }, 538 { 539 "removing a validator is OK", 540 types.NewValidatorSet([]*types.Validator{val1, val2}), 541 []abci.ValidatorUpdate{{PubKey: pk2, Power: 0}}, 542 types.NewValidatorSet([]*types.Validator{val1}), 543 false, 544 }, 545 { 546 "removing a non-existing validator results in error", 547 types.NewValidatorSet([]*types.Validator{val1}), 548 []abci.ValidatorUpdate{{PubKey: pk2, Power: 0}}, 549 types.NewValidatorSet([]*types.Validator{val1}), 550 true, 551 }, 552 } 553 554 for _, tc := range testCases { 555 tc := tc 556 t.Run(tc.name, func(t *testing.T) { 557 updates, err := types.PB2TM.ValidatorUpdates(tc.abciUpdates) 558 assert.NoError(t, err) 559 err = tc.currentSet.UpdateWithChangeSet(updates) 560 if tc.shouldErr { 561 assert.Error(t, err) 562 } else { 563 assert.NoError(t, err) 564 require.Equal(t, tc.resultingSet.Size(), tc.currentSet.Size()) 565 566 assert.Equal(t, tc.resultingSet.TotalVotingPower(), tc.currentSet.TotalVotingPower()) 567 568 assert.Equal(t, tc.resultingSet.Validators[0].Address, tc.currentSet.Validators[0].Address) 569 if tc.resultingSet.Size() > 1 { 570 assert.Equal(t, tc.resultingSet.Validators[1].Address, tc.currentSet.Validators[1].Address) 571 } 572 } 573 }) 574 } 575 } 576 577 // TestFinalizeBlockValidatorUpdates ensures we update validator set and send an event. 578 func TestFinalizeBlockValidatorUpdates(t *testing.T) { 579 app := &testApp{} 580 cc := proxy.NewLocalClientCreator(app) 581 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 582 err := proxyApp.Start() 583 require.NoError(t, err) 584 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 585 586 state, stateDB, _ := makeState(1, 1) 587 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 588 DiscardABCIResponses: false, 589 }) 590 mp := &mpmocks.Mempool{} 591 mp.On("Lock").Return() 592 mp.On("Unlock").Return() 593 mp.On("FlushAppConn", mock.Anything).Return(nil) 594 mp.On("Update", 595 mock.Anything, 596 mock.Anything, 597 mock.Anything, 598 mock.Anything, 599 mock.Anything, 600 mock.Anything).Return(nil) 601 mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{}) 602 603 blockStore := store.NewBlockStore(dbm.NewMemDB()) 604 blockExec := sm.NewBlockExecutor( 605 stateStore, 606 log.TestingLogger(), 607 proxyApp.Consensus(), 608 mp, 609 sm.EmptyEvidencePool{}, 610 blockStore, 611 ) 612 613 eventBus := types.NewEventBus() 614 err = eventBus.Start() 615 require.NoError(t, err) 616 defer eventBus.Stop() //nolint:errcheck // ignore for tests 617 618 blockExec.SetEventBus(eventBus) 619 620 updatesSub, err := eventBus.Subscribe( 621 context.Background(), 622 "TestEndBlockValidatorUpdates", 623 types.EventQueryValidatorSetUpdates, 624 ) 625 require.NoError(t, err) 626 627 block := makeBlock(state, 1, new(types.Commit)) 628 bps, err := block.MakePartSet(testPartSize) 629 require.NoError(t, err) 630 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} 631 632 pubkey := ed25519.GenPrivKey().PubKey() 633 pk, err := cryptoenc.PubKeyToProto(pubkey) 634 require.NoError(t, err) 635 app.ValidatorUpdates = []abci.ValidatorUpdate{ 636 {PubKey: pk, Power: 10}, 637 } 638 639 state, err = blockExec.ApplyBlock(state, blockID, block) 640 require.NoError(t, err) 641 // test new validator was added to NextValidators 642 if assert.Equal(t, state.Validators.Size()+1, state.NextValidators.Size()) { 643 idx, _ := state.NextValidators.GetByAddress(pubkey.Address()) 644 if idx < 0 { 645 t.Fatalf("can't find address %v in the set %v", pubkey.Address(), state.NextValidators) 646 } 647 } 648 649 // test we threw an event 650 select { 651 case msg := <-updatesSub.Out(): 652 event, ok := msg.Data().(types.EventDataValidatorSetUpdates) 653 require.True(t, ok, "Expected event of type EventDataValidatorSetUpdates, got %T", msg.Data()) 654 if assert.NotEmpty(t, event.ValidatorUpdates) { 655 assert.Equal(t, pubkey, event.ValidatorUpdates[0].PubKey) 656 assert.EqualValues(t, 10, event.ValidatorUpdates[0].VotingPower) 657 } 658 case <-updatesSub.Canceled(): 659 t.Fatalf("updatesSub was canceled (reason: %v)", updatesSub.Err()) 660 case <-time.After(1 * time.Second): 661 t.Fatal("Did not receive EventValidatorSetUpdates within 1 sec.") 662 } 663 } 664 665 // TestFinalizeBlockValidatorUpdatesResultingInEmptySet checks that processing validator updates that 666 // would result in empty set causes no panic, an error is raised and NextValidators is not updated 667 func TestFinalizeBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { 668 app := &testApp{} 669 cc := proxy.NewLocalClientCreator(app) 670 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 671 err := proxyApp.Start() 672 require.NoError(t, err) 673 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 674 675 state, stateDB, _ := makeState(1, 1) 676 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 677 DiscardABCIResponses: false, 678 }) 679 blockStore := store.NewBlockStore(dbm.NewMemDB()) 680 blockExec := sm.NewBlockExecutor( 681 stateStore, 682 log.TestingLogger(), 683 proxyApp.Consensus(), 684 new(mpmocks.Mempool), 685 sm.EmptyEvidencePool{}, 686 blockStore, 687 ) 688 689 block := makeBlock(state, 1, new(types.Commit)) 690 bps, err := block.MakePartSet(testPartSize) 691 require.NoError(t, err) 692 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} 693 694 vp, err := cryptoenc.PubKeyToProto(state.Validators.Validators[0].PubKey) 695 require.NoError(t, err) 696 // Remove the only validator 697 app.ValidatorUpdates = []abci.ValidatorUpdate{ 698 {PubKey: vp, Power: 0}, 699 } 700 701 assert.NotPanics(t, func() { state, err = blockExec.ApplyBlock(state, blockID, block) }) 702 assert.Error(t, err) 703 assert.NotEmpty(t, state.NextValidators.Validators) 704 } 705 706 func TestEmptyPrepareProposal(t *testing.T) { 707 const height = 2 708 ctx, cancel := context.WithCancel(context.Background()) 709 defer cancel() 710 711 app := &abci.BaseApplication{} 712 cc := proxy.NewLocalClientCreator(app) 713 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 714 err := proxyApp.Start() 715 require.NoError(t, err) 716 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 717 718 state, stateDB, privVals := makeState(1, height) 719 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 720 DiscardABCIResponses: false, 721 }) 722 mp := &mpmocks.Mempool{} 723 mp.On("Lock").Return() 724 mp.On("Unlock").Return() 725 mp.On("FlushAppConn", mock.Anything).Return(nil) 726 mp.On("Update", 727 mock.Anything, 728 mock.Anything, 729 mock.Anything, 730 mock.Anything, 731 mock.Anything, 732 mock.Anything).Return(nil) 733 mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{}) 734 735 blockStore := store.NewBlockStore(dbm.NewMemDB()) 736 blockExec := sm.NewBlockExecutor( 737 stateStore, 738 log.TestingLogger(), 739 proxyApp.Consensus(), 740 mp, 741 sm.EmptyEvidencePool{}, 742 blockStore, 743 ) 744 pa, _ := state.Validators.GetByIndex(0) 745 commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) 746 require.NoError(t, err) 747 _, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa) 748 require.NoError(t, err) 749 } 750 751 // TestPrepareProposalTxsAllIncluded tests that any transactions included in 752 // the prepare proposal response are included in the block. 753 func TestPrepareProposalTxsAllIncluded(t *testing.T) { 754 const height = 2 755 ctx, cancel := context.WithCancel(context.Background()) 756 defer cancel() 757 758 state, stateDB, privVals := makeState(1, height) 759 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 760 DiscardABCIResponses: false, 761 }) 762 763 evpool := &mocks.EvidencePool{} 764 evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0)) 765 766 txs := test.MakeNTxs(height, 10) 767 mp := &mpmocks.Mempool{} 768 mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs[2:]) 769 770 app := &abcimocks.Application{} 771 app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{ 772 Txs: txs.ToSliceOfBytes(), 773 }, nil) 774 cc := proxy.NewLocalClientCreator(app) 775 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 776 err := proxyApp.Start() 777 require.NoError(t, err) 778 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 779 780 blockStore := store.NewBlockStore(dbm.NewMemDB()) 781 blockExec := sm.NewBlockExecutor( 782 stateStore, 783 log.TestingLogger(), 784 proxyApp.Consensus(), 785 mp, 786 evpool, 787 blockStore, 788 ) 789 pa, _ := state.Validators.GetByIndex(0) 790 commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) 791 require.NoError(t, err) 792 block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa) 793 require.NoError(t, err) 794 795 for i, tx := range block.Data.Txs { 796 require.Equal(t, txs[i], tx) 797 } 798 799 mp.AssertExpectations(t) 800 } 801 802 // TestPrepareProposalReorderTxs tests that CreateBlock produces a block with transactions 803 // in the order matching the order they are returned from PrepareProposal. 804 func TestPrepareProposalReorderTxs(t *testing.T) { 805 const height = 2 806 ctx, cancel := context.WithCancel(context.Background()) 807 defer cancel() 808 809 state, stateDB, privVals := makeState(1, height) 810 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 811 DiscardABCIResponses: false, 812 }) 813 814 evpool := &mocks.EvidencePool{} 815 evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0)) 816 817 txs := test.MakeNTxs(height, 10) 818 mp := &mpmocks.Mempool{} 819 mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs) 820 821 txs = txs[2:] 822 txs = append(txs[len(txs)/2:], txs[:len(txs)/2]...) 823 824 app := &abcimocks.Application{} 825 app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{ 826 Txs: txs.ToSliceOfBytes(), 827 }, nil) 828 829 cc := proxy.NewLocalClientCreator(app) 830 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 831 err := proxyApp.Start() 832 require.NoError(t, err) 833 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 834 835 blockStore := store.NewBlockStore(dbm.NewMemDB()) 836 blockExec := sm.NewBlockExecutor( 837 stateStore, 838 log.TestingLogger(), 839 proxyApp.Consensus(), 840 mp, 841 evpool, 842 blockStore, 843 ) 844 pa, _ := state.Validators.GetByIndex(0) 845 commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) 846 require.NoError(t, err) 847 block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa) 848 require.NoError(t, err) 849 for i, tx := range block.Data.Txs { 850 require.Equal(t, txs[i], tx) 851 } 852 853 mp.AssertExpectations(t) 854 } 855 856 // TestPrepareProposalErrorOnTooManyTxs tests that the block creation logic returns 857 // an error if the ResponsePrepareProposal returned from the application is invalid. 858 func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) { 859 const height = 2 860 ctx, cancel := context.WithCancel(context.Background()) 861 defer cancel() 862 863 state, stateDB, privVals := makeState(1, height) 864 // limit max block size 865 state.ConsensusParams.Block.MaxBytes = 60 * 1024 866 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 867 DiscardABCIResponses: false, 868 }) 869 870 evpool := &mocks.EvidencePool{} 871 evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0)) 872 873 const nValidators = 1 874 var bytesPerTx int64 = 3 875 maxDataBytes := types.MaxDataBytes(state.ConsensusParams.Block.MaxBytes, 0, nValidators) 876 txs := test.MakeNTxs(height, maxDataBytes/bytesPerTx+2) // +2 so that tx don't fit 877 mp := &mpmocks.Mempool{} 878 mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs) 879 880 app := &abcimocks.Application{} 881 app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{ 882 Txs: txs.ToSliceOfBytes(), 883 }, nil) 884 885 cc := proxy.NewLocalClientCreator(app) 886 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 887 err := proxyApp.Start() 888 require.NoError(t, err) 889 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 890 891 blockStore := store.NewBlockStore(dbm.NewMemDB()) 892 blockExec := sm.NewBlockExecutor( 893 stateStore, 894 log.NewNopLogger(), 895 proxyApp.Consensus(), 896 mp, 897 evpool, 898 blockStore, 899 ) 900 pa, _ := state.Validators.GetByIndex(0) 901 commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) 902 require.NoError(t, err) 903 block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa) 904 require.Nil(t, block) 905 require.ErrorContains(t, err, "transaction data size exceeds maximum") 906 907 mp.AssertExpectations(t) 908 } 909 910 // TestPrepareProposalErrorOnPrepareProposalError tests when the client returns an error 911 // upon calling PrepareProposal on it. 912 func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) { 913 const height = 2 914 ctx, cancel := context.WithCancel(context.Background()) 915 defer cancel() 916 917 state, stateDB, privVals := makeState(1, height) 918 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 919 DiscardABCIResponses: false, 920 }) 921 922 evpool := &mocks.EvidencePool{} 923 evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0)) 924 925 txs := test.MakeNTxs(height, 10) 926 mp := &mpmocks.Mempool{} 927 mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs) 928 929 cm := &abciclientmocks.Client{} 930 cm.On("SetLogger", mock.Anything).Return() 931 cm.On("Start").Return(nil) 932 cm.On("Quit").Return(nil) 933 cm.On("PrepareProposal", mock.Anything, mock.Anything).Return(nil, errors.New("an injected error")).Once() 934 cm.On("Stop").Return(nil) 935 cc := &pmocks.ClientCreator{} 936 cc.On("NewABCIClient").Return(cm, nil) 937 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 938 err := proxyApp.Start() 939 require.NoError(t, err) 940 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 941 942 blockStore := store.NewBlockStore(dbm.NewMemDB()) 943 blockExec := sm.NewBlockExecutor( 944 stateStore, 945 log.NewNopLogger(), 946 proxyApp.Consensus(), 947 mp, 948 evpool, 949 blockStore, 950 ) 951 pa, _ := state.Validators.GetByIndex(0) 952 commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) 953 require.NoError(t, err) 954 block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa) 955 require.Nil(t, block) 956 require.ErrorContains(t, err, "an injected error") 957 958 mp.AssertExpectations(t) 959 } 960 961 // TestCreateProposalBlockPanicOnAbsentVoteExtensions ensures that the CreateProposalBlock 962 // call correctly panics when the vote extension data is missing from the extended commit 963 // data that the method receives. 964 func TestCreateProposalAbsentVoteExtensions(t *testing.T) { 965 for _, testCase := range []struct { 966 name string 967 968 // The height that is about to be proposed 969 height int64 970 971 // The first height during which vote extensions will be required for consensus to proceed. 972 extensionEnableHeight int64 973 expectPanic bool 974 }{ 975 { 976 name: "missing extension data on first required height", 977 height: 2, 978 extensionEnableHeight: 1, 979 expectPanic: true, 980 }, 981 { 982 name: "missing extension during before required height", 983 height: 2, 984 extensionEnableHeight: 2, 985 expectPanic: false, 986 }, 987 { 988 name: "missing extension data and not required", 989 height: 2, 990 extensionEnableHeight: 0, 991 expectPanic: false, 992 }, 993 { 994 name: "missing extension data and required in two heights", 995 height: 2, 996 extensionEnableHeight: 3, 997 expectPanic: false, 998 }, 999 } { 1000 t.Run(testCase.name, func(t *testing.T) { 1001 ctx, cancel := context.WithCancel(context.Background()) 1002 defer cancel() 1003 1004 app := abcimocks.NewApplication(t) 1005 if !testCase.expectPanic { 1006 app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil) 1007 } 1008 cc := proxy.NewLocalClientCreator(app) 1009 proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics()) 1010 err := proxyApp.Start() 1011 require.NoError(t, err) 1012 1013 state, stateDB, privVals := makeState(1, int(testCase.height-1)) 1014 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 1015 DiscardABCIResponses: false, 1016 }) 1017 state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.extensionEnableHeight 1018 mp := &mpmocks.Mempool{} 1019 mp.On("Lock").Return() 1020 mp.On("Unlock").Return() 1021 mp.On("FlushAppConn", mock.Anything).Return(nil) 1022 mp.On("Update", 1023 mock.Anything, 1024 mock.Anything, 1025 mock.Anything, 1026 mock.Anything, 1027 mock.Anything, 1028 mock.Anything).Return(nil) 1029 mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{}) 1030 1031 blockStore := store.NewBlockStore(dbm.NewMemDB()) 1032 blockExec := sm.NewBlockExecutor( 1033 stateStore, 1034 log.NewNopLogger(), 1035 proxyApp.Consensus(), 1036 mp, 1037 sm.EmptyEvidencePool{}, 1038 blockStore, 1039 ) 1040 block := makeBlock(state, testCase.height, new(types.Commit)) 1041 1042 bps, err := block.MakePartSet(testPartSize) 1043 require.NoError(t, err) 1044 blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} 1045 pa, _ := state.Validators.GetByIndex(0) 1046 lastCommit, _, _ := makeValidCommit(testCase.height-1, blockID, state.Validators, privVals) 1047 stripSignatures(lastCommit) 1048 if testCase.expectPanic { 1049 require.Panics(t, func() { 1050 blockExec.CreateProposalBlock(ctx, testCase.height, state, lastCommit, pa) //nolint:errcheck 1051 }) 1052 } else { 1053 _, err = blockExec.CreateProposalBlock(ctx, testCase.height, state, lastCommit, pa) 1054 require.NoError(t, err) 1055 } 1056 }) 1057 } 1058 } 1059 1060 func stripSignatures(ec *types.ExtendedCommit) { 1061 for i, commitSig := range ec.ExtendedSignatures { 1062 commitSig.Extension = nil 1063 commitSig.ExtensionSignature = nil 1064 ec.ExtendedSignatures[i] = commitSig 1065 } 1066 } 1067 1068 func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID { 1069 var ( 1070 h = make([]byte, tmhash.Size) 1071 psH = make([]byte, tmhash.Size) 1072 ) 1073 copy(h, hash) 1074 copy(psH, partSetHash) 1075 return types.BlockID{ 1076 Hash: h, 1077 PartSetHeader: types.PartSetHeader{ 1078 Total: partSetSize, 1079 Hash: psH, 1080 }, 1081 } 1082 }