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 }