github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/poll/governance_protocol_test.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package poll 7 8 import ( 9 "context" 10 "math/big" 11 "testing" 12 "time" 13 14 "github.com/golang/mock/gomock" 15 "github.com/stretchr/testify/require" 16 17 "github.com/iotexproject/go-pkgs/hash" 18 "github.com/iotexproject/iotex-address/address" 19 "github.com/iotexproject/iotex-election/test/mock/mock_committee" 20 "github.com/iotexproject/iotex-election/types" 21 22 "github.com/iotexproject/iotex-core/action" 23 "github.com/iotexproject/iotex-core/action/protocol" 24 "github.com/iotexproject/iotex-core/action/protocol/rolldpos" 25 "github.com/iotexproject/iotex-core/action/protocol/vote" 26 "github.com/iotexproject/iotex-core/action/protocol/vote/candidatesutil" 27 "github.com/iotexproject/iotex-core/blockchain" 28 "github.com/iotexproject/iotex-core/blockchain/genesis" 29 "github.com/iotexproject/iotex-core/db" 30 "github.com/iotexproject/iotex-core/db/batch" 31 "github.com/iotexproject/iotex-core/state" 32 "github.com/iotexproject/iotex-core/test/identityset" 33 "github.com/iotexproject/iotex-core/test/mock/mock_chainmanager" 34 ) 35 36 func initConstruct(ctrl *gomock.Controller) (Protocol, context.Context, protocol.StateManager, *types.ElectionResult, error) { 37 cfg := struct { 38 Genesis genesis.Genesis 39 Chain blockchain.Config 40 }{ 41 Genesis: genesis.Default, 42 Chain: blockchain.DefaultConfig, 43 } 44 cfg.Genesis.EasterBlockHeight = 1 // set up testing after Easter Height 45 cfg.Genesis.ProbationIntensityRate = 90 46 cfg.Genesis.ProbationEpochPeriod = 2 47 cfg.Genesis.ProductivityThreshold = 75 48 ctx := protocol.WithBlockCtx( 49 context.Background(), 50 protocol.BlockCtx{ 51 BlockHeight: 0, 52 }, 53 ) 54 registry := protocol.NewRegistry() 55 rp := rolldpos.NewProtocol(36, 6, 5) 56 err := registry.Register("rolldpos", rp) 57 if err != nil { 58 return nil, nil, nil, nil, err 59 } 60 epochStartHeight := rp.GetEpochHeight(2) 61 ctx = genesis.WithGenesisContext( 62 protocol.WithBlockchainCtx( 63 protocol.WithRegistry(ctx, registry), 64 protocol.BlockchainCtx{ 65 Tip: protocol.TipInfo{ 66 Height: epochStartHeight - 1, 67 }, 68 }, 69 ), 70 cfg.Genesis, 71 ) 72 ctx = protocol.WithActionCtx( 73 ctx, 74 protocol.ActionCtx{}, 75 ) 76 ctx = protocol.WithFeatureCtx(ctx) 77 78 sm := mock_chainmanager.NewMockStateManager(ctrl) 79 committee := mock_committee.NewMockCommittee(ctrl) 80 cb := batch.NewCachedBatch() 81 sm.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn( 82 func(account interface{}, opts ...protocol.StateOption) (uint64, error) { 83 cfg, err := protocol.CreateStateConfig(opts...) 84 if err != nil { 85 return 0, err 86 } 87 val, err := cb.Get(cfg.Namespace, cfg.Key) 88 if err != nil { 89 return 0, state.ErrStateNotExist 90 } 91 return 0, state.Deserialize(account, val) 92 }).AnyTimes() 93 sm.EXPECT().PutState(gomock.Any(), gomock.Any()).DoAndReturn( 94 func(account interface{}, opts ...protocol.StateOption) (uint64, error) { 95 cfg, err := protocol.CreateStateConfig(opts...) 96 if err != nil { 97 return 0, err 98 } 99 ss, err := state.Serialize(account) 100 if err != nil { 101 return 0, err 102 } 103 cb.Put(cfg.Namespace, cfg.Key, ss, "failed to put state") 104 return 0, nil 105 }).AnyTimes() 106 sm.EXPECT().DelState(gomock.Any()).DoAndReturn( 107 func(opts ...protocol.StateOption) (uint64, error) { 108 cfg, err := protocol.CreateStateConfig(opts...) 109 if err != nil { 110 return 0, err 111 } 112 cb.Delete(cfg.Namespace, cfg.Key, "failed to delete state") 113 return 0, nil 114 }).AnyTimes() 115 sm.EXPECT().Snapshot().Return(1).AnyTimes() 116 sm.EXPECT().Height().Return(epochStartHeight-1, nil).AnyTimes() 117 r := types.NewElectionResultForTest(time.Now()) 118 committee.EXPECT().ResultByHeight(uint64(123456)).Return(r, nil).AnyTimes() 119 committee.EXPECT().HeightByTime(gomock.Any()).Return(uint64(123456), nil).AnyTimes() 120 candidates := []*state.Candidate{ 121 { 122 Address: identityset.Address(1).String(), 123 Votes: big.NewInt(30), 124 RewardAddress: "rewardAddress1", 125 }, 126 { 127 Address: identityset.Address(2).String(), 128 Votes: big.NewInt(22), 129 RewardAddress: "rewardAddress2", 130 }, 131 { 132 Address: identityset.Address(3).String(), 133 Votes: big.NewInt(20), 134 RewardAddress: "rewardAddress3", 135 }, 136 { 137 Address: identityset.Address(4).String(), 138 Votes: big.NewInt(10), 139 RewardAddress: "rewardAddress4", 140 }, 141 { 142 Address: identityset.Address(5).String(), 143 Votes: big.NewInt(5), 144 RewardAddress: "rewardAddress5", 145 }, 146 { 147 Address: identityset.Address(6).String(), 148 Votes: big.NewInt(3), 149 RewardAddress: "rewardAddress6", 150 }, 151 } 152 indexer, err := NewCandidateIndexer(db.NewMemKVStore()) 153 if err != nil { 154 return nil, nil, nil, nil, err 155 } 156 slasher, err := NewSlasher( 157 func(start, end uint64) (map[string]uint64, error) { 158 switch start { 159 case 1: 160 return map[string]uint64{ // [A, B, C] 161 identityset.Address(1).String(): 1, // underperformance 162 identityset.Address(2).String(): 1, // underperformance 163 identityset.Address(3).String(): 1, // underperformance 164 identityset.Address(4).String(): 5, 165 identityset.Address(5).String(): 5, 166 identityset.Address(6).String(): 5, 167 }, nil 168 case 31: 169 return map[string]uint64{ // [B, D] 170 identityset.Address(1).String(): 5, 171 identityset.Address(2).String(): 1, // underperformance 172 identityset.Address(3).String(): 5, 173 identityset.Address(4).String(): 1, // underperformance 174 identityset.Address(5).String(): 4, 175 identityset.Address(6).String(): 4, 176 }, nil 177 case 61: 178 return map[string]uint64{ // [E, F] 179 identityset.Address(1).String(): 5, 180 identityset.Address(2).String(): 5, 181 identityset.Address(3).String(): 5, 182 identityset.Address(4).String(): 5, 183 identityset.Address(5).String(): 1, // underperformance 184 identityset.Address(6).String(): 1, // underperformance 185 }, nil 186 default: 187 return nil, nil 188 } 189 }, 190 candidatesutil.CandidatesFromDB, 191 candidatesutil.ProbationListFromDB, 192 candidatesutil.UnproductiveDelegateFromDB, 193 indexer, 194 2, 195 2, 196 cfg.Genesis.DardanellesNumSubEpochs, 197 cfg.Genesis.ProductivityThreshold, 198 cfg.Genesis.ProbationEpochPeriod, 199 cfg.Genesis.UnproductiveDelegateMaxCacheSize, 200 cfg.Genesis.ProbationIntensityRate) 201 if err != nil { 202 return nil, nil, nil, nil, err 203 } 204 p, err := NewGovernanceChainCommitteeProtocol( 205 indexer, 206 committee, 207 uint64(123456), 208 func(uint64) (time.Time, error) { return time.Now(), nil }, 209 cfg.Chain.PollInitialCandidatesInterval, 210 slasher) 211 if err != nil { 212 return nil, nil, nil, nil, err 213 } 214 ctx = protocol.WithFeatureWithHeightCtx(ctx) 215 if err := setCandidates(ctx, sm, indexer, candidates, 1); err != nil { 216 return nil, nil, nil, nil, err 217 } 218 if err := setNextEpochProbationList(sm, indexer, 1, vote.NewProbationList(cfg.Genesis.ProbationIntensityRate)); err != nil { 219 return nil, nil, nil, nil, err 220 } 221 return p, ctx, sm, r, err 222 } 223 224 func TestCreateGenesisStates(t *testing.T) { 225 require := require.New(t) 226 ctrl := gomock.NewController(t) 227 p, ctx, sm, r, err := initConstruct(ctrl) 228 require.NoError(err) 229 require.NoError(p.CreateGenesisStates(ctx, sm)) 230 var sc state.CandidateList 231 candKey := candidatesutil.ConstructKey(candidatesutil.NxtCandidateKey) 232 _, err = sm.State(&sc, protocol.KeyOption(candKey[:]), protocol.NamespaceOption(protocol.SystemNamespace)) 233 require.NoError(err) 234 candidates, err := state.CandidatesToMap(sc) 235 require.NoError(err) 236 require.Equal(2, len(candidates)) 237 for _, d := range r.Delegates() { 238 operator := string(d.OperatorAddress()) 239 addr, err := address.FromString(operator) 240 require.NoError(err) 241 c, ok := candidates[hash.BytesToHash160(addr.Bytes())] 242 require.True(ok) 243 require.Equal(addr.String(), c.Address) 244 } 245 } 246 247 func TestCreatePostSystemActions(t *testing.T) { 248 require := require.New(t) 249 ctrl := gomock.NewController(t) 250 p, ctx, sm, r, err := initConstruct(ctrl) 251 require.NoError(err) 252 _, err = shiftCandidates(sm) 253 require.NoError(err) 254 psac, ok := p.(protocol.PostSystemActionsCreator) 255 require.True(ok) 256 elp, err := psac.CreatePostSystemActions(ctx, sm) 257 require.NoError(err) 258 require.Equal(1, len(elp)) 259 act, ok := elp[0].Action().(*action.PutPollResult) 260 require.True(ok) 261 require.Equal(uint64(1), act.Height()) 262 require.Equal(uint64(0), act.AbstractAction.Nonce()) 263 delegates := r.Delegates() 264 require.Equal(len(act.Candidates()), len(delegates)) 265 for _, can := range act.Candidates() { 266 d := r.DelegateByName(can.CanName) 267 require.NotNil(d) 268 } 269 } 270 271 func TestCreatePreStates(t *testing.T) { 272 require := require.New(t) 273 ctrl := gomock.NewController(t) 274 p, ctx, sm, _, err := initConstruct(ctrl) 275 require.NoError(err) 276 277 psc, ok := p.(protocol.PreStatesCreator) 278 require.True(ok) 279 bcCtx := protocol.MustGetBlockchainCtx(ctx) 280 rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx)) 281 282 test := make(map[uint64](map[string]uint32)) 283 test[1] = map[string]uint32{} 284 test[2] = map[string]uint32{ 285 identityset.Address(1).String(): 1, // [A, B, C] 286 identityset.Address(2).String(): 1, 287 identityset.Address(3).String(): 1, 288 } 289 test[3] = map[string]uint32{ 290 identityset.Address(1).String(): 1, // [A, B, C, D] 291 identityset.Address(2).String(): 2, 292 identityset.Address(3).String(): 1, 293 identityset.Address(4).String(): 1, 294 } 295 test[4] = map[string]uint32{ 296 identityset.Address(2).String(): 1, // [B, D, E, F] 297 identityset.Address(4).String(): 1, 298 identityset.Address(5).String(): 1, 299 identityset.Address(6).String(): 1, 300 } 301 302 // testing for probation slashing 303 var epochNum uint64 304 for epochNum = 1; epochNum <= 3; epochNum++ { 305 // at first of epoch 306 epochStartHeight := rp.GetEpochHeight(epochNum) 307 bcCtx.Tip.Height = epochStartHeight - 1 308 ctx = protocol.WithBlockchainCtx(ctx, bcCtx) 309 ctx = protocol.WithBlockCtx( 310 ctx, 311 protocol.BlockCtx{ 312 BlockHeight: epochStartHeight, 313 Producer: identityset.Address(1), 314 }, 315 ) 316 ctx = protocol.WithFeatureCtx(protocol.WithFeatureWithHeightCtx(ctx)) 317 require.NoError(psc.CreatePreStates(ctx, sm)) // shift 318 bl := &vote.ProbationList{} 319 key := candidatesutil.ConstructKey(candidatesutil.CurProbationKey) 320 _, err := sm.State(bl, protocol.KeyOption(key[:]), protocol.NamespaceOption(protocol.SystemNamespace)) 321 require.NoError(err) 322 expected := test[epochNum] 323 require.Equal(len(expected), len(bl.ProbationInfo)) 324 for addr, count := range bl.ProbationInfo { 325 val, ok := expected[addr] 326 require.True(ok) 327 require.Equal(val, count) 328 } 329 330 // mid of epoch, set candidatelist into next candidate key 331 nextEpochStartHeight := rp.GetEpochHeight(epochNum + 1) 332 candidates, err := p.Candidates(ctx, sm) 333 require.Equal(len(candidates), 6) 334 require.NoError(err) 335 require.NoError(setCandidates(ctx, sm, nil, candidates, nextEpochStartHeight)) // set next candidate 336 337 // at last of epoch, set probationList into next probation key 338 epochLastHeight := rp.GetEpochLastBlockHeight(epochNum) 339 bcCtx.Tip.Height = epochLastHeight - 1 340 ctx = protocol.WithBlockchainCtx(ctx, bcCtx) 341 ctx = protocol.WithBlockCtx( 342 ctx, 343 protocol.BlockCtx{ 344 BlockHeight: epochLastHeight, 345 Producer: identityset.Address(1), 346 }, 347 ) 348 require.NoError(psc.CreatePreStates(ctx, sm)) // calculate probation list and set next probationlist 349 350 bl = &vote.ProbationList{} 351 key = candidatesutil.ConstructKey(candidatesutil.NxtProbationKey) 352 _, err = sm.State(bl, protocol.KeyOption(key[:]), protocol.NamespaceOption(protocol.SystemNamespace)) 353 require.NoError(err) 354 expected = test[epochNum+1] 355 require.Equal(len(expected), len(bl.ProbationInfo)) 356 for addr, count := range bl.ProbationInfo { 357 val, ok := expected[addr] 358 require.True(ok) 359 require.Equal(val, count) 360 } 361 } 362 } 363 364 func TestHandle(t *testing.T) { 365 require := require.New(t) 366 ctrl := gomock.NewController(t) 367 368 p, ctx, sm, _, err := initConstruct(ctrl) 369 require.NoError(err) 370 require.NoError(p.CreateGenesisStates(ctx, sm)) 371 recipientAddr := identityset.Address(28) 372 senderKey := identityset.PrivateKey(27) 373 374 t.Run("wrong action", func(t *testing.T) { 375 tsf, err := action.NewTransfer(0, big.NewInt(10), recipientAddr.String(), []byte{}, uint64(100000), big.NewInt(10)) 376 require.NoError(err) 377 bd := &action.EnvelopeBuilder{} 378 elp := bd.SetGasLimit(uint64(100000)). 379 SetGasPrice(big.NewInt(10)). 380 SetAction(tsf).Build() 381 selp, err := action.Sign(elp, senderKey) 382 require.NoError(err) 383 require.NotNil(selp) 384 receipt, err := p.Handle(ctx, selp.Action(), nil) 385 require.NoError(err) 386 require.Nil(receipt) 387 }) 388 candKey := candidatesutil.ConstructKey(candidatesutil.NxtCandidateKey) 389 t.Run("All right", func(t *testing.T) { 390 p2, ctx2, sm2, _, err := initConstruct(ctrl) 391 require.NoError(err) 392 require.NoError(p2.CreateGenesisStates(ctx2, sm2)) 393 var sc2 state.CandidateList 394 _, err = sm2.State(&sc2, protocol.KeyOption(candKey[:]), protocol.NamespaceOption(protocol.SystemNamespace)) 395 require.NoError(err) 396 act2 := action.NewPutPollResult(1, 1, sc2) 397 bd := &action.EnvelopeBuilder{} 398 elp := bd.SetGasLimit(uint64(100000)). 399 SetGasPrice(big.NewInt(10)). 400 SetAction(act2).Build() 401 selp2, err := action.Sign(elp, senderKey) 402 require.NoError(err) 403 require.NotNil(selp2) 404 caller := selp2.SenderAddress() 405 require.NotNil(caller) 406 ctx2 = protocol.WithBlockCtx( 407 ctx2, 408 protocol.BlockCtx{ 409 BlockHeight: 1, 410 Producer: caller, 411 }, 412 ) 413 ctx2 = protocol.WithActionCtx( 414 ctx2, 415 protocol.ActionCtx{ 416 Caller: caller, 417 }, 418 ) 419 receipt, err := p.Handle(ctx2, selp2.Action(), sm2) 420 require.NoError(err) 421 require.NotNil(receipt) 422 423 _, err = shiftCandidates(sm2) 424 require.NoError(err) 425 _, _, err = candidatesutil.CandidatesFromDB(sm2, 1, false, true) 426 require.Error(err) // should return stateNotExist error 427 candidates, _, err := candidatesutil.CandidatesFromDB(sm2, 1, false, false) 428 require.NoError(err) 429 require.Equal(2, len(candidates)) 430 require.Equal(candidates[0].Address, sc2[0].Address) 431 require.Equal(candidates[0].Votes, sc2[0].Votes) 432 require.Equal(candidates[1].Address, sc2[1].Address) 433 require.Equal(candidates[1].Votes, sc2[1].Votes) 434 }) 435 436 t.Run("Only producer could create this protocol", func(t *testing.T) { 437 p2, ctx2, sm2, _, err := initConstruct(ctrl) 438 require.NoError(err) 439 require.NoError(p2.CreateGenesisStates(ctx2, sm2)) 440 var sc2 state.CandidateList 441 _, err = sm2.State(&sc2, protocol.KeyOption(candKey[:]), protocol.NamespaceOption(protocol.SystemNamespace)) 442 require.NoError(err) 443 act2 := action.NewPutPollResult(1, 1, sc2) 444 bd := &action.EnvelopeBuilder{} 445 elp := bd.SetGasLimit(uint64(100000)). 446 SetGasPrice(big.NewInt(10)). 447 SetAction(act2).Build() 448 selp2, err := action.Sign(elp, senderKey) 449 require.NoError(err) 450 require.NotNil(selp2) 451 caller := selp2.SenderAddress() 452 require.NotNil(caller) 453 ctx2 = protocol.WithBlockCtx( 454 ctx2, 455 protocol.BlockCtx{ 456 BlockHeight: 1, 457 Producer: recipientAddr, 458 }, 459 ) 460 ctx2 = protocol.WithActionCtx( 461 ctx2, 462 protocol.ActionCtx{ 463 Caller: caller, 464 }, 465 ) 466 err = p.Validate(ctx2, selp2.Action(), sm2) 467 require.Contains(err.Error(), "Only producer could create this protocol") 468 }) 469 t.Run("Duplicate candidate", func(t *testing.T) { 470 p3, ctx3, sm3, _, err := initConstruct(ctrl) 471 require.NoError(err) 472 require.NoError(p3.CreateGenesisStates(ctx3, sm3)) 473 var sc3 state.CandidateList 474 _, err = sm3.State(&sc3, protocol.KeyOption(candKey[:]), protocol.NamespaceOption(protocol.SystemNamespace)) 475 require.NoError(err) 476 sc3 = append(sc3, &state.Candidate{Address: "1", Votes: big.NewInt(10), RewardAddress: "2", CanName: nil}) 477 sc3 = append(sc3, &state.Candidate{Address: "1", Votes: big.NewInt(10), RewardAddress: "2", CanName: nil}) 478 act3 := action.NewPutPollResult(1, 1, sc3) 479 bd := &action.EnvelopeBuilder{} 480 elp := bd.SetGasLimit(uint64(100000)). 481 SetGasPrice(big.NewInt(10)). 482 SetAction(act3).Build() 483 selp3, err := action.Sign(elp, senderKey) 484 require.NoError(err) 485 require.NotNil(selp3) 486 caller := selp3.SenderAddress() 487 require.NotNil(caller) 488 ctx3 = protocol.WithBlockCtx( 489 ctx3, 490 protocol.BlockCtx{ 491 BlockHeight: 1, 492 Producer: identityset.Address(27), 493 }, 494 ) 495 ctx3 = protocol.WithActionCtx( 496 ctx3, 497 protocol.ActionCtx{ 498 Caller: caller, 499 }, 500 ) 501 err = p.Validate(ctx3, selp3.Action(), sm3) 502 require.Contains(err.Error(), "duplicate candidate") 503 }) 504 t.Run("Delegate's length is not equal", func(t *testing.T) { 505 p4, ctx4, sm4, _, err := initConstruct(ctrl) 506 require.NoError(err) 507 require.NoError(p4.CreateGenesisStates(ctx4, sm4)) 508 var sc4 state.CandidateList 509 _, err = sm4.State(&sc4, protocol.KeyOption(candKey[:]), protocol.NamespaceOption(protocol.SystemNamespace)) 510 require.NoError(err) 511 sc4 = append(sc4, &state.Candidate{Address: "1", Votes: big.NewInt(10), RewardAddress: "2", CanName: nil}) 512 act4 := action.NewPutPollResult(1, 1, sc4) 513 bd4 := &action.EnvelopeBuilder{} 514 elp4 := bd4.SetGasLimit(uint64(100000)). 515 SetGasPrice(big.NewInt(10)). 516 SetAction(act4).Build() 517 selp4, err := action.Sign(elp4, senderKey) 518 require.NoError(err) 519 require.NotNil(selp4) 520 caller := selp4.SenderAddress() 521 require.NotNil(caller) 522 ctx4 = protocol.WithBlockCtx( 523 ctx4, 524 protocol.BlockCtx{ 525 BlockHeight: 1, 526 Producer: identityset.Address(27), 527 }, 528 ) 529 ctx4 = protocol.WithActionCtx( 530 ctx4, 531 protocol.ActionCtx{ 532 Caller: caller, 533 }, 534 ) 535 err = p4.Validate(ctx4, selp4.Action(), sm4) 536 require.Contains(err.Error(), "the proposed delegate list length") 537 }) 538 t.Run("Candidate's vote is not equal", func(t *testing.T) { 539 p5, ctx5, sm5, _, err := initConstruct(ctrl) 540 require.NoError(err) 541 require.NoError(p5.CreateGenesisStates(ctx5, sm5)) 542 var sc5 state.CandidateList 543 _, err = sm5.State(&sc5, protocol.KeyOption(candKey[:]), protocol.NamespaceOption(protocol.SystemNamespace)) 544 require.NoError(err) 545 sc5[0].Votes = big.NewInt(10) 546 act5 := action.NewPutPollResult(1, 1, sc5) 547 bd5 := &action.EnvelopeBuilder{} 548 elp5 := bd5.SetGasLimit(uint64(100000)). 549 SetGasPrice(big.NewInt(10)). 550 SetAction(act5).Build() 551 selp5, err := action.Sign(elp5, senderKey) 552 require.NoError(err) 553 require.NotNil(selp5) 554 caller := selp5.SenderAddress() 555 require.NotNil(caller) 556 ctx5 = protocol.WithBlockCtx( 557 ctx5, 558 protocol.BlockCtx{ 559 BlockHeight: 1, 560 Producer: identityset.Address(27), 561 }, 562 ) 563 ctx5 = protocol.WithActionCtx( 564 ctx5, 565 protocol.ActionCtx{ 566 Caller: caller, 567 }, 568 ) 569 err = p5.Validate(ctx5, selp5.Action(), sm5) 570 require.Contains(err.Error(), "delegates are not as expected") 571 }) 572 } 573 574 func TestNextCandidates(t *testing.T) { 575 require := require.New(t) 576 ctrl := gomock.NewController(t) 577 p, ctx, sm, _, err := initConstruct(ctrl) 578 require.NoError(err) 579 probationListMap := map[string]uint32{ 580 identityset.Address(1).String(): 1, 581 identityset.Address(2).String(): 1, 582 } 583 584 probationList := &vote.ProbationList{ 585 ProbationInfo: probationListMap, 586 IntensityRate: 50, 587 } 588 require.NoError(setNextEpochProbationList(sm, nil, 721, probationList)) 589 ctx = protocol.WithFeatureWithHeightCtx(ctx) 590 filteredCandidates, err := p.NextCandidates(ctx, sm) 591 require.NoError(err) 592 require.Equal(6, len(filteredCandidates)) 593 594 for _, cand := range filteredCandidates { 595 if cand.Address == identityset.Address(1).String() { 596 require.Equal(0, cand.Votes.Cmp(big.NewInt(15))) 597 } 598 if cand.Address == identityset.Address(2).String() { 599 require.Equal(0, cand.Votes.Cmp(big.NewInt(11))) 600 } 601 } 602 603 // change intensity rate to be 0 604 probationList = &vote.ProbationList{ 605 ProbationInfo: probationListMap, 606 IntensityRate: 0, 607 } 608 require.NoError(setNextEpochProbationList(sm, nil, 721, probationList)) 609 filteredCandidates, err = p.NextCandidates(ctx, sm) 610 require.NoError(err) 611 require.Equal(6, len(filteredCandidates)) 612 613 for _, cand := range filteredCandidates { 614 if cand.Address == identityset.Address(1).String() { 615 require.Equal(0, cand.Votes.Cmp(big.NewInt(30))) 616 } 617 if cand.Address == identityset.Address(2).String() { 618 require.Equal(0, cand.Votes.Cmp(big.NewInt(22))) 619 } 620 } 621 622 } 623 624 func TestDelegatesAndNextDelegates(t *testing.T) { 625 require := require.New(t) 626 ctrl := gomock.NewController(t) 627 p, ctx, sm, _, err := initConstruct(ctrl) 628 require.NoError(err) 629 630 // 1: empty probationList NextDelegates() 631 probationListMap := map[string]uint32{} 632 probationList := &vote.ProbationList{ 633 ProbationInfo: probationListMap, 634 IntensityRate: 90, 635 } 636 require.NoError(setNextEpochProbationList(sm, nil, 31, probationList)) 637 638 ctx = protocol.WithFeatureWithHeightCtx(ctx) 639 delegates, err := p.NextDelegates(ctx, sm) 640 require.NoError(err) 641 require.Equal(2, len(delegates)) 642 require.Equal(identityset.Address(1).String(), delegates[0].Address) 643 require.Equal(identityset.Address(2).String(), delegates[1].Address) 644 645 // 2: not empty probationList NextDelegates() 646 probationListMap2 := map[string]uint32{ 647 identityset.Address(1).String(): 1, 648 identityset.Address(2).String(): 1, 649 } 650 probationList2 := &vote.ProbationList{ 651 ProbationInfo: probationListMap2, 652 IntensityRate: 90, 653 } 654 require.NoError(setNextEpochProbationList(sm, nil, 721, probationList2)) 655 delegates2, err := p.NextDelegates(ctx, sm) 656 require.NoError(err) 657 require.Equal(2, len(delegates2)) 658 // even though the address 1, 2 have larger amount of votes, it got probated because it's on probation list 659 require.Equal(identityset.Address(3).String(), delegates2[0].Address) 660 require.Equal(identityset.Address(4).String(), delegates2[1].Address) 661 662 // 3: probation with different probationList 663 probationListMap3 := map[string]uint32{ 664 identityset.Address(1).String(): 1, 665 identityset.Address(3).String(): 2, 666 } 667 probationList3 := &vote.ProbationList{ 668 ProbationInfo: probationListMap3, 669 IntensityRate: 90, 670 } 671 require.NoError(setNextEpochProbationList(sm, nil, 721, probationList3)) 672 673 delegates3, err := p.NextDelegates(ctx, sm) 674 require.NoError(err) 675 676 require.Equal(2, len(delegates3)) 677 require.Equal(identityset.Address(2).String(), delegates3[0].Address) 678 require.Equal(identityset.Address(4).String(), delegates3[1].Address) 679 680 // 4: test hard probation 681 probationListMap4 := map[string]uint32{ 682 identityset.Address(1).String(): 1, 683 identityset.Address(2).String(): 2, 684 identityset.Address(3).String(): 2, 685 identityset.Address(4).String(): 2, 686 identityset.Address(5).String(): 2, 687 } 688 probationList4 := &vote.ProbationList{ 689 ProbationInfo: probationListMap4, 690 IntensityRate: 100, // hard probation 691 } 692 require.NoError(setNextEpochProbationList(sm, nil, 721, probationList4)) 693 694 delegates4, err := p.NextDelegates(ctx, sm) 695 require.NoError(err) 696 697 require.Equal(1, len(delegates4)) // exclude all of them 698 require.Equal(identityset.Address(6).String(), delegates4[0].Address) 699 700 // 5: shift probation list and Delegates() 701 _, err = shiftCandidates(sm) 702 require.NoError(err) 703 _, err = shiftProbationList(sm) 704 require.NoError(err) 705 delegates5, err := p.Delegates(ctx, sm) 706 require.NoError(err) 707 require.Equal(len(delegates5), len(delegates4)) 708 for i, d := range delegates4 { 709 require.True(d.Equal(delegates5[i])) 710 } 711 }