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  }