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