github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/staking/bucket_pool_test.go (about) 1 // Copyright (c) 2022 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 staking 7 8 import ( 9 "bytes" 10 "context" 11 "math/big" 12 "testing" 13 "time" 14 15 "github.com/golang/mock/gomock" 16 "github.com/stretchr/testify/require" 17 18 "github.com/iotexproject/iotex-core/action/protocol" 19 "github.com/iotexproject/iotex-core/blockchain/genesis" 20 "github.com/iotexproject/iotex-core/state" 21 "github.com/iotexproject/iotex-core/test/identityset" 22 "github.com/iotexproject/iotex-core/testutil/testdb" 23 ) 24 25 func TestTotalAmount(t *testing.T) { 26 r := require.New(t) 27 28 a := totalAmount{ 29 count: 1, 30 } 31 ser, err := a.Serialize() 32 r.NoError(err) 33 b := totalAmount{} 34 // amount = nil leads to unmarshal error 35 r.Error(b.Deserialize(ser)) 36 37 a.amount = big.NewInt(10) 38 ser, err = a.Serialize() 39 r.NoError(err) 40 r.NoError(b.Deserialize(ser)) 41 r.Equal(a, b) 42 43 // test sub balance 44 r.Equal(state.ErrNotEnoughBalance, a.SubBalance(big.NewInt(11))) 45 r.NoError(a.SubBalance(big.NewInt(4))) 46 r.Equal(big.NewInt(6), a.amount) 47 r.EqualValues(0, a.count) 48 r.Equal(state.ErrNotEnoughBalance, a.SubBalance(big.NewInt(1))) 49 50 // test add balance 51 a.AddBalance(big.NewInt(1), true) 52 r.Equal(big.NewInt(7), a.amount) 53 r.EqualValues(1, a.count) 54 a.AddBalance(big.NewInt(0), false) 55 r.Equal(big.NewInt(7), a.amount) 56 r.EqualValues(1, a.count) 57 } 58 59 func TestBucketPool(t *testing.T) { 60 r := require.New(t) 61 62 // bucket pool address does not interfere with buckets data 63 r.Equal(-1, bytes.Compare(_bucketPoolAddrKey, bucketKey(0))) 64 65 ctrl := gomock.NewController(t) 66 sm := testdb.NewMockStateManager(ctrl) 67 68 pool, err := newCandidateStateReader(sm).NewBucketPool(false) 69 r.NoError(err) 70 r.Equal(big.NewInt(0), pool.Total()) 71 r.EqualValues(0, pool.Count()) 72 r.Equal(false, pool.enableSMStorage) 73 74 // add 4 buckets 75 addr := identityset.Address(1) 76 for i := 0; i < 4; i++ { 77 _, err = newCandidateStateManager(sm).putBucket(NewVoteBucket(addr, addr, big.NewInt(10000), 21, time.Now(), true)) 78 r.NoError(err) 79 } 80 81 view, _, err := CreateBaseView(sm, false) 82 r.NoError(err) 83 sm.WriteView(_protocolID, view) 84 pool = view.bucketPool 85 total := big.NewInt(40000) 86 count := uint64(4) 87 r.Equal(total, pool.Total()) 88 r.Equal(count, pool.Count()) 89 r.Equal(false, pool.enableSMStorage) 90 91 tests := []struct { 92 debit, newBucket, postGreenland, commit bool 93 amount *big.Int 94 expected error 95 }{ 96 {true, true, false, false, big.NewInt(1000), nil}, 97 {false, true, false, false, big.NewInt(200), nil}, 98 {true, true, false, true, big.NewInt(300), nil}, 99 {false, true, false, false, big.NewInt(22200), nil}, 100 {false, true, false, false, big.NewInt(60000), state.ErrNotEnoughBalance}, 101 {true, false, false, true, big.NewInt(400), nil}, 102 // below test created staking bucket pool 103 {true, false, true, true, big.NewInt(500), nil}, 104 {false, false, true, false, big.NewInt(1000), nil}, 105 {true, false, true, true, big.NewInt(600), nil}, 106 } 107 108 // simulate bucket pool operation success, but sm did not commit (Snapshot() implements workingset.Reset(), clearing data stored in Dock()) 109 csm, err := NewCandidateStateManager(sm, false) 110 r.NoError(err) 111 r.NoError(csm.DebitBucketPool(tests[0].amount, true)) 112 sm.Snapshot() 113 114 // after that, the base view should not change 115 c, err := ConstructBaseView(sm) 116 r.NoError(err) 117 pool = c.BaseView().bucketPool 118 r.Equal(total, pool.Total()) 119 r.Equal(count, pool.Count()) 120 121 var testGreenland bool 122 ctx := protocol.WithFeatureWithHeightCtx(genesis.WithGenesisContext(context.Background(), genesis.Default)) 123 for _, v := range tests { 124 csm, err = NewCandidateStateManager(sm, v.postGreenland && testGreenland) 125 r.NoError(err) 126 // dirty view always follows the latest change 127 pool = csm.DirtyView().bucketPool 128 r.Equal(total, pool.Total()) 129 r.Equal(count, pool.Count()) 130 if v.debit { 131 err = csm.DebitBucketPool(v.amount, v.newBucket) 132 } else { 133 err = csm.CreditBucketPool(v.amount) 134 } 135 r.Equal(v.expected, err) 136 137 if v.expected != nil { 138 continue 139 } 140 141 if v.debit { 142 total.Add(total, v.amount) 143 if v.newBucket { 144 count++ 145 } 146 } else { 147 total.Sub(total, v.amount) 148 count-- 149 } 150 151 if v.commit { 152 r.NoError(csm.Commit(ctx)) 153 // after commit, value should reflect in base view 154 c, err = ConstructBaseView(sm) 155 r.NoError(err) 156 pool = c.BaseView().bucketPool 157 r.Equal(total, pool.Total()) 158 r.Equal(count, pool.Count()) 159 } 160 161 if !testGreenland && v.postGreenland { 162 _, err = sm.PutState(c.BaseView().bucketPool.total, protocol.NamespaceOption(_stakingNameSpace), protocol.KeyOption(_bucketPoolAddrKey)) 163 r.NoError(err) 164 testGreenland = true 165 } 166 } 167 168 // verify state has been created successfully 169 var b totalAmount 170 _, err = sm.State(&b, protocol.NamespaceOption(_stakingNameSpace), protocol.KeyOption(_bucketPoolAddrKey)) 171 r.NoError(err) 172 r.Equal(total, b.amount) 173 r.Equal(count, b.count) 174 175 // test again bucket pool operation success but sm did not commit 176 csm, err = NewCandidateStateManager(sm, true) 177 r.NoError(err) 178 r.NoError(csm.DebitBucketPool(tests[0].amount, true)) 179 sm.Snapshot() 180 181 c, err = ConstructBaseView(sm) 182 r.NoError(err) 183 pool = c.BaseView().bucketPool 184 r.Equal(total, pool.Total()) 185 r.Equal(count, pool.Count()) 186 }