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  }