github.com/iotexproject/iotex-core@v1.14.1-rc1/blockindex/contractstaking/dirty_cache.go (about) 1 // Copyright (c) 2023 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 contractstaking 7 8 import ( 9 "math/big" 10 "sync" 11 12 "github.com/pkg/errors" 13 14 "github.com/iotexproject/iotex-core/db/batch" 15 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 16 ) 17 18 const ( 19 // bucket related namespace in db 20 _StakingBucketInfoNS = "sbi" 21 _StakingBucketTypeNS = "sbt" 22 _StakingNS = "sns" 23 ) 24 25 type ( 26 // contractStakingDirty is the dirty data of contract staking 27 // main functions: 28 // 1. update bucket 29 // 2. get up-to-date bucket 30 // 3. store delta to merge to clean cache 31 contractStakingDirty struct { 32 clean *contractStakingCache // clean cache to get buckets of last block 33 delta *contractStakingDelta // delta for cache to store buckets of current block 34 batch batch.KVStoreBatch // batch for db to store buckets of current block 35 once sync.Once 36 } 37 ) 38 39 var ( 40 _stakingHeightKey = []byte("shk") 41 _stakingTotalBucketCountKey = []byte("stbck") 42 43 errBucketTypeNotExist = errors.New("bucket type does not exist") 44 ) 45 46 func newContractStakingDirty(clean *contractStakingCache) *contractStakingDirty { 47 return &contractStakingDirty{ 48 clean: clean, 49 delta: newContractStakingDelta(), 50 batch: batch.NewBatch(), 51 } 52 } 53 54 func (dirty *contractStakingDirty) addBucketInfo(id uint64, bi *bucketInfo) error { 55 dirty.batch.Put(_StakingBucketInfoNS, byteutil.Uint64ToBytesBigEndian(id), bi.Serialize(), "failed to put bucket info") 56 return dirty.delta.AddBucketInfo(id, bi) 57 } 58 59 func (dirty *contractStakingDirty) updateBucketInfo(id uint64, bi *bucketInfo) error { 60 dirty.batch.Put(_StakingBucketInfoNS, byteutil.Uint64ToBytesBigEndian(id), bi.Serialize(), "failed to put bucket info") 61 return dirty.delta.UpdateBucketInfo(id, bi) 62 } 63 64 func (dirty *contractStakingDirty) deleteBucketInfo(id uint64) error { 65 dirty.batch.Delete(_StakingBucketInfoNS, byteutil.Uint64ToBytesBigEndian(id), "failed to delete bucket info") 66 return dirty.delta.DeleteBucketInfo(id) 67 } 68 69 func (dirty *contractStakingDirty) putBucketType(bt *BucketType) error { 70 id, _, ok := dirty.matchBucketType(bt.Amount, bt.Duration) 71 if !ok { 72 id = dirty.getBucketTypeCount() 73 if err := dirty.addBucketType(id, bt); err != nil { 74 return err 75 } 76 } 77 return dirty.updateBucketType(id, bt) 78 } 79 80 func (dirty *contractStakingDirty) getBucketType(id uint64) (*BucketType, bool) { 81 bt, state := dirty.delta.GetBucketType(id) 82 switch state { 83 case deltaStateAdded, deltaStateModified: 84 return bt, true 85 default: 86 return dirty.clean.BucketType(id) 87 } 88 } 89 90 func (dirty *contractStakingDirty) getBucketInfo(id uint64) (*bucketInfo, bool) { 91 bi, state := dirty.delta.GetBucketInfo(id) 92 switch state { 93 case deltaStateAdded, deltaStateModified: 94 return bi, true 95 case deltaStateRemoved: 96 return nil, false 97 default: 98 return dirty.clean.BucketInfo(id) 99 } 100 } 101 102 func (dirty *contractStakingDirty) finalize() (batch.KVStoreBatch, *contractStakingDelta) { 103 return dirty.finalizeBatch(), dirty.delta 104 } 105 106 func (dirty *contractStakingDirty) finalizeBatch() batch.KVStoreBatch { 107 dirty.once.Do(func() { 108 tbc, _ := dirty.clean.TotalBucketCount(0) 109 total := tbc + dirty.delta.AddedBucketCnt() 110 dirty.batch.Put(_StakingNS, _stakingTotalBucketCountKey, byteutil.Uint64ToBytesBigEndian(total), "failed to put total bucket count") 111 }) 112 return dirty.batch 113 } 114 115 func (dirty *contractStakingDirty) addBucketType(id uint64, bt *BucketType) error { 116 dirty.batch.Put(_StakingBucketTypeNS, byteutil.Uint64ToBytesBigEndian(id), bt.Serialize(), "failed to put bucket type") 117 return dirty.delta.AddBucketType(id, bt) 118 } 119 120 func (dirty *contractStakingDirty) matchBucketType(amount *big.Int, duration uint64) (uint64, *BucketType, bool) { 121 id, bt, ok := dirty.delta.MatchBucketType(amount, duration) 122 if ok { 123 return id, bt, true 124 } 125 return dirty.clean.MatchBucketType(amount, duration) 126 } 127 128 func (dirty *contractStakingDirty) getBucketTypeCount() uint64 { 129 btc, _ := dirty.clean.BucketTypeCount(0) 130 return uint64(btc) + dirty.delta.AddedBucketTypeCnt() 131 } 132 133 func (dirty *contractStakingDirty) updateBucketType(id uint64, bt *BucketType) error { 134 dirty.batch.Put(_StakingBucketTypeNS, byteutil.Uint64ToBytesBigEndian(id), bt.Serialize(), "failed to put bucket type") 135 return dirty.delta.UpdateBucketType(id, bt) 136 }