github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/staking/vote_reviser_test.go (about)

     1  package staking
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"math/big"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/golang/mock/gomock"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/iotexproject/iotex-address/address"
    14  
    15  	"github.com/iotexproject/iotex-core/action/protocol"
    16  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    17  	"github.com/iotexproject/iotex-core/pkg/unit"
    18  	"github.com/iotexproject/iotex-core/test/identityset"
    19  	"github.com/iotexproject/iotex-core/testutil/testdb"
    20  )
    21  
    22  func TestVoteReviser(t *testing.T) {
    23  	r := require.New(t)
    24  
    25  	ctrl := gomock.NewController(t)
    26  	sm := testdb.NewMockStateManagerWithoutHeightFunc(ctrl)
    27  	sm.EXPECT().Height().Return(uint64(0), nil).Times(4)
    28  	csm := newCandidateStateManager(sm)
    29  	csr := newCandidateStateReader(sm)
    30  	_, err := sm.PutState(
    31  		&totalBucketCount{count: 0},
    32  		protocol.NamespaceOption(_stakingNameSpace),
    33  		protocol.KeyOption(TotalBucketKey),
    34  	)
    35  	r.NoError(err)
    36  
    37  	tests := []struct {
    38  		cand     address.Address
    39  		owner    address.Address
    40  		amount   *big.Int
    41  		duration uint32
    42  		index    uint64
    43  	}{
    44  		{
    45  			identityset.Address(6),
    46  			identityset.Address(6),
    47  			unit.ConvertIotxToRau(1100000),
    48  			21,
    49  			0,
    50  		},
    51  		{
    52  			identityset.Address(1),
    53  			identityset.Address(1),
    54  			unit.ConvertIotxToRau(1200000),
    55  			21,
    56  			1,
    57  		},
    58  		{
    59  			identityset.Address(2),
    60  			identityset.Address(2),
    61  			unit.ConvertIotxToRau(1200000),
    62  			14,
    63  			2,
    64  		},
    65  		{
    66  			identityset.Address(3),
    67  			identityset.Address(3),
    68  			unit.ConvertIotxToRau(1200000),
    69  			25,
    70  			3,
    71  		},
    72  		{
    73  			identityset.Address(4),
    74  			identityset.Address(4),
    75  			unit.ConvertIotxToRau(1200000),
    76  			31,
    77  			4,
    78  		},
    79  		{
    80  			identityset.Address(5),
    81  			identityset.Address(5),
    82  			unit.ConvertIotxToRau(1199999),
    83  			31,
    84  			5,
    85  		},
    86  		{
    87  			identityset.Address(1),
    88  			identityset.Address(2),
    89  			big.NewInt(2100000000),
    90  			21,
    91  			6,
    92  		},
    93  		{
    94  			identityset.Address(2),
    95  			identityset.Address(3),
    96  			big.NewInt(1400000000),
    97  			14,
    98  			7,
    99  		},
   100  		{
   101  			identityset.Address(3),
   102  			identityset.Address(4),
   103  			big.NewInt(2500000000),
   104  			25,
   105  			8,
   106  		},
   107  		{
   108  			identityset.Address(4),
   109  			identityset.Address(1),
   110  			big.NewInt(3100000000),
   111  			31,
   112  			9,
   113  		},
   114  	}
   115  
   116  	// test loading with no candidate in stateDB
   117  	stk, err := NewProtocol(
   118  		nil,
   119  		&BuilderConfig{
   120  			Staking:                  genesis.Default.Staking,
   121  			PersistStakingPatchBlock: math.MaxUint64,
   122  		},
   123  		nil,
   124  		nil,
   125  		genesis.Default.OkhotskBlockHeight,
   126  		genesis.Default.HawaiiBlockHeight,
   127  		genesis.Default.GreenlandBlockHeight,
   128  	)
   129  	r.NotNil(stk)
   130  	r.NoError(err)
   131  
   132  	// write a number of buckets into stateDB
   133  	for _, e := range tests {
   134  		vb := NewVoteBucket(e.cand, e.owner, e.amount, e.duration, time.Now(), true)
   135  		index, err := csm.putBucketAndIndex(vb)
   136  		r.NoError(err)
   137  		r.Equal(index, vb.Index)
   138  	}
   139  
   140  	// load candidates from stateDB and verify
   141  	ctx := genesis.WithGenesisContext(context.Background(), genesis.Default)
   142  	ctx = protocol.WithFeatureWithHeightCtx(ctx)
   143  	v, err := stk.Start(ctx, sm)
   144  	sm.WriteView(_protocolID, v)
   145  	r.NoError(err)
   146  	_, ok := v.(*ViewData)
   147  	r.True(ok)
   148  
   149  	csm, err = NewCandidateStateManager(sm, false)
   150  	r.NoError(err)
   151  	oldCand := testCandidates[3].d.Clone()
   152  	oldCand.Name = "old name"
   153  	r.NoError(csm.Upsert(oldCand))
   154  	r.NoError(csm.Commit(ctx))
   155  	r.NotNil(csm.GetByName(oldCand.Name))
   156  	// load a number of candidates
   157  	updateCands := CandidateList{
   158  		testCandidates[3].d, testCandidates[4].d, testCandidates[2].d,
   159  		testCandidates[0].d, testCandidates[1].d, testCandidates[5].d,
   160  	}
   161  	for _, e := range updateCands {
   162  		r.NoError(csm.Upsert(e))
   163  	}
   164  	r.NoError(csm.Commit(ctx))
   165  	r.NotNil(csm.GetByName(oldCand.Name))
   166  	r.NotNil(csm.GetByName(testCandidates[3].d.Name))
   167  
   168  	// test revise
   169  	vr := stk.voteReviser
   170  	r.False(vr.isCacheExist(genesis.Default.GreenlandBlockHeight))
   171  	r.False(vr.isCacheExist(genesis.Default.HawaiiBlockHeight))
   172  	r.NoError(vr.Revise(csm, genesis.Default.HawaiiBlockHeight))
   173  	r.True(vr.isCacheExist(genesis.Default.HawaiiBlockHeight))
   174  	// simulate first revise attempt failed -- call Revise() again
   175  	r.True(vr.isCacheExist(genesis.Default.HawaiiBlockHeight))
   176  	r.NoError(vr.Revise(csm, genesis.Default.HawaiiBlockHeight))
   177  	sm.EXPECT().Height().DoAndReturn(
   178  		func() (uint64, error) {
   179  			return genesis.Default.HawaiiBlockHeight, nil
   180  		},
   181  	).Times(1)
   182  	r.NoError(csm.Commit(ctx))
   183  	r.NotNil(csm.GetByName(oldCand.Name))
   184  	// verify self-stake and total votes match
   185  	result, ok := vr.result(genesis.Default.HawaiiBlockHeight)
   186  	r.True(ok)
   187  	r.Equal(len(testCandidates), len(result))
   188  	cv := genesis.Default.Staking.VoteWeightCalConsts
   189  	for _, c := range result {
   190  		cand := csm.GetByOwner(c.Owner)
   191  		r.True(c.Equal(cand))
   192  		for _, cand := range testCandidates {
   193  			if address.Equal(cand.d.Owner, c.Owner) {
   194  				r.Equal(0, cand.d.SelfStake.Cmp(c.SelfStake))
   195  			}
   196  		}
   197  		for _, v := range tests {
   198  			if address.Equal(v.cand, c.Owner) && v.index != c.SelfStakeBucketIdx {
   199  				bucket, err := csr.getBucket(v.index)
   200  				r.NoError(err)
   201  				total := CalculateVoteWeight(cv, bucket, false)
   202  				bucket, err = csr.getBucket(c.SelfStakeBucketIdx)
   203  				r.NoError(err)
   204  				total.Add(total, CalculateVoteWeight(cv, bucket, true))
   205  				r.Equal(0, total.Cmp(c.Votes))
   206  				break
   207  			}
   208  		}
   209  	}
   210  	r.NoError(vr.Revise(csm, genesis.Default.OkhotskBlockHeight))
   211  	sm.EXPECT().Height().DoAndReturn(
   212  		func() (uint64, error) {
   213  			return genesis.Default.OkhotskBlockHeight, nil
   214  		},
   215  	).Times(1)
   216  	r.NoError(csm.Commit(ctx))
   217  	r.Nil(csm.GetByName(oldCand.Name))
   218  }