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