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