github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/staking/bucket_pool.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 "math/big" 10 11 "github.com/iotexproject/go-pkgs/hash" 12 "google.golang.org/protobuf/proto" 13 14 "github.com/iotexproject/iotex-core/action/protocol" 15 "github.com/iotexproject/iotex-core/action/protocol/staking/stakingpb" 16 "github.com/iotexproject/iotex-core/state" 17 ) 18 19 // const 20 const ( 21 _stakingBucketPool = "bucketPool" 22 ) 23 24 var ( 25 _bucketPoolAddr = hash.Hash160b([]byte(_stakingBucketPool)) 26 _bucketPoolAddrKey = append([]byte{_const}, _bucketPoolAddr[:]...) 27 ) 28 29 // when a bucket is created, the amount of staked IOTX token is deducted from user, but does not transfer to any address 30 // in the same way when a bucket are withdrawn, bucket amount is added back to user, but does not come out of any address 31 // 32 // for better accounting/auditing, we take protocol's address as the 'bucket pool' address 33 // 1. at Greenland height we sum up all existing bucket's amount and set the total amount to bucket pool address 34 // 2. for future bucket creation/deposit/registration, the amount of staked IOTX token will be added to bucket pool (so 35 // the pool is 'receiving' token) 36 // 3. for future bucket withdrawal, the bucket amount will be deducted from bucket pool (so the pool is 'releasing' token) 37 38 type ( 39 // BucketPool implements the bucket pool 40 BucketPool struct { 41 enableSMStorage bool 42 total *totalAmount 43 } 44 45 totalAmount struct { 46 amount *big.Int 47 count uint64 48 } 49 ) 50 51 func (t *totalAmount) Serialize() ([]byte, error) { 52 gen := stakingpb.TotalAmount{ 53 Amount: t.amount.String(), 54 Count: t.count, 55 } 56 return proto.Marshal(&gen) 57 } 58 59 func (t *totalAmount) Deserialize(data []byte) error { 60 gen := stakingpb.TotalAmount{} 61 if err := proto.Unmarshal(data, &gen); err != nil { 62 return err 63 } 64 65 var ok bool 66 if t.amount, ok = new(big.Int).SetString(gen.Amount, 10); !ok { 67 return state.ErrStateDeserialization 68 } 69 70 if t.amount.Cmp(big.NewInt(0)) == -1 { 71 return state.ErrNotEnoughBalance 72 } 73 t.count = gen.Count 74 return nil 75 } 76 77 func (t *totalAmount) AddBalance(amount *big.Int, newBucket bool) { 78 t.amount.Add(t.amount, amount) 79 if newBucket { 80 t.count++ 81 } 82 } 83 84 func (t *totalAmount) SubBalance(amount *big.Int) error { 85 if amount.Cmp(t.amount) == 1 || t.count == 0 { 86 return state.ErrNotEnoughBalance 87 } 88 t.amount.Sub(t.amount, amount) 89 t.count-- 90 return nil 91 } 92 93 // Total returns the total amount staked in bucket pool 94 func (bp *BucketPool) Total() *big.Int { 95 return new(big.Int).Set(bp.total.amount) 96 } 97 98 // Count returns the total number of buckets in bucket pool 99 func (bp *BucketPool) Count() uint64 { 100 return bp.total.count 101 } 102 103 // Copy returns a copy of the bucket pool 104 func (bp *BucketPool) Copy(enableSMStorage bool) *BucketPool { 105 pool := BucketPool{} 106 pool.enableSMStorage = enableSMStorage 107 pool.total = &totalAmount{ 108 amount: new(big.Int).Set(bp.total.amount), 109 count: bp.total.count, 110 } 111 return &pool 112 } 113 114 // Sync syncs the data from state manager 115 func (bp *BucketPool) Sync(sm protocol.StateManager) error { 116 if bp.enableSMStorage { 117 _, err := sm.State(bp.total, protocol.NamespaceOption(_stakingNameSpace), protocol.KeyOption(_bucketPoolAddrKey)) 118 return err 119 } 120 // get stashed total amount 121 err := sm.Unload(_protocolID, _stakingBucketPool, bp.total) 122 if err != nil && err != protocol.ErrNoName { 123 return err 124 } 125 return nil 126 } 127 128 // Commit is called upon workingset commit 129 func (bp *BucketPool) Commit(sr protocol.StateReader) error { 130 return nil 131 } 132 133 // CreditPool subtracts staked amount out of the pool 134 func (bp *BucketPool) CreditPool(sm protocol.StateManager, amount *big.Int) error { 135 if err := bp.total.SubBalance(amount); err != nil { 136 return err 137 } 138 139 if bp.enableSMStorage { 140 _, err := sm.PutState(bp.total, protocol.NamespaceOption(_stakingNameSpace), protocol.KeyOption(_bucketPoolAddrKey)) 141 return err 142 } 143 return sm.Load(_protocolID, _stakingBucketPool, bp.total) 144 } 145 146 // DebitPool adds staked amount into the pool 147 func (bp *BucketPool) DebitPool(sm protocol.StateManager, amount *big.Int, newBucket bool) error { 148 bp.total.AddBalance(amount, newBucket) 149 if bp.enableSMStorage { 150 _, err := sm.PutState(bp.total, protocol.NamespaceOption(_stakingNameSpace), protocol.KeyOption(_bucketPoolAddrKey)) 151 return err 152 } 153 return sm.Load(_protocolID, _stakingBucketPool, bp.total) 154 }