github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/staking/candidate_statemanager.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  	"context"
    10  	"math/big"
    11  
    12  	"github.com/pkg/errors"
    13  
    14  	"github.com/iotexproject/iotex-address/address"
    15  
    16  	"github.com/iotexproject/iotex-core/action/protocol"
    17  	"github.com/iotexproject/iotex-core/state"
    18  )
    19  
    20  // const
    21  const (
    22  	_stakingCandCenter = "candCenter"
    23  )
    24  
    25  type (
    26  	// BucketSet related to setting bucket
    27  	BucketSet interface {
    28  		updateBucket(index uint64, bucket *VoteBucket) error
    29  		putBucket(bucket *VoteBucket) (uint64, error)
    30  		delBucket(index uint64) error
    31  		putBucketAndIndex(bucket *VoteBucket) (uint64, error)
    32  		delBucketAndIndex(owner, cand address.Address, index uint64) error
    33  	}
    34  	// CandidateSet related to setting candidates
    35  	CandidateSet interface {
    36  		putCandidate(*Candidate) error
    37  		delCandidate(address.Address) error
    38  		putVoterBucketIndex(address.Address, uint64) error
    39  		delVoterBucketIndex(address.Address, uint64) error
    40  		putCandBucketIndex(address.Address, uint64) error
    41  		delCandBucketIndex(address.Address, uint64) error
    42  	}
    43  	// CandidateStateManager is candidate state manager on top of StateManager
    44  	CandidateStateManager interface {
    45  		BucketSet
    46  		BucketGetByIndex
    47  		CandidateSet
    48  		// candidate and bucket pool related
    49  		DirtyView() *ViewData
    50  		ContainsName(string) bool
    51  		ContainsOwner(address.Address) bool
    52  		ContainsOperator(address.Address) bool
    53  		ContainsSelfStakingBucket(uint64) bool
    54  		GetByName(string) *Candidate
    55  		GetByOwner(address.Address) *Candidate
    56  		Upsert(*Candidate) error
    57  		CreditBucketPool(*big.Int) error
    58  		DebitBucketPool(*big.Int, bool) error
    59  		Commit(context.Context) error
    60  		SM() protocol.StateManager
    61  		SR() protocol.StateReader
    62  	}
    63  
    64  	// CandidiateStateCommon is the common interface for candidate state manager and reader
    65  	CandidiateStateCommon interface {
    66  		ContainsSelfStakingBucket(uint64) bool
    67  		SR() protocol.StateReader
    68  	}
    69  
    70  	candSM struct {
    71  		protocol.StateManager
    72  		candCenter *CandidateCenter
    73  		bucketPool *BucketPool
    74  	}
    75  )
    76  
    77  // NewCandidateStateManager returns a new CandidateStateManager instance
    78  func NewCandidateStateManager(sm protocol.StateManager, enableSMStorage bool) (CandidateStateManager, error) {
    79  	// TODO: we can store csm in a local cache, just as how statedb store the workingset
    80  	// b/c most time the sm is used before, no need to create another clone
    81  	csr, err := ConstructBaseView(sm)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	// make a copy of candidate center and bucket pool, so they can be modified by csm
    87  	// and won't affect base view until being committed
    88  	view := csr.BaseView()
    89  	csm := &candSM{
    90  		StateManager: sm,
    91  		candCenter:   view.candCenter.Base(),
    92  		bucketPool:   view.bucketPool.Copy(enableSMStorage),
    93  	}
    94  
    95  	// extract view change from SM
    96  	if err := csm.bucketPool.Sync(sm); err != nil {
    97  		return nil, errors.Wrap(err, "failed to sync bucket pool")
    98  	}
    99  
   100  	if err := csm.candCenter.Sync(sm); err != nil {
   101  		return nil, errors.Wrap(err, "failed to sync candidate center")
   102  	}
   103  	return csm, nil
   104  }
   105  
   106  func newCandidateStateManager(sm protocol.StateManager) CandidateStateManager {
   107  	return &candSM{
   108  		StateManager: sm,
   109  	}
   110  }
   111  
   112  func (csm *candSM) SM() protocol.StateManager {
   113  	return csm.StateManager
   114  }
   115  
   116  func (csm *candSM) SR() protocol.StateReader {
   117  	return csm.StateManager
   118  }
   119  
   120  // DirtyView is csm's current state, which reflects base view + applying delta saved in csm's dock
   121  func (csm *candSM) DirtyView() *ViewData {
   122  	return &ViewData{
   123  		candCenter: csm.candCenter,
   124  		bucketPool: csm.bucketPool,
   125  	}
   126  }
   127  
   128  func (csm *candSM) ContainsName(name string) bool {
   129  	return csm.candCenter.ContainsName(name)
   130  }
   131  
   132  func (csm *candSM) ContainsOwner(addr address.Address) bool {
   133  	return csm.candCenter.ContainsOwner(addr)
   134  }
   135  
   136  func (csm *candSM) ContainsOperator(addr address.Address) bool {
   137  	return csm.candCenter.ContainsOperator(addr)
   138  }
   139  
   140  func (csm *candSM) ContainsSelfStakingBucket(index uint64) bool {
   141  	return csm.candCenter.ContainsSelfStakingBucket(index)
   142  }
   143  
   144  func (csm *candSM) GetByName(name string) *Candidate {
   145  	return csm.candCenter.GetByName(name)
   146  }
   147  
   148  func (csm *candSM) GetByOwner(addr address.Address) *Candidate {
   149  	return csm.candCenter.GetByOwner(addr)
   150  }
   151  
   152  // Upsert writes the candidate into state manager and cand center
   153  func (csm *candSM) Upsert(d *Candidate) error {
   154  	if err := csm.candCenter.Upsert(d); err != nil {
   155  		return err
   156  	}
   157  
   158  	if err := csm.putCandidate(d); err != nil {
   159  		return err
   160  	}
   161  
   162  	delta := csm.candCenter.Delta()
   163  	if len(delta) == 0 {
   164  		return nil
   165  	}
   166  
   167  	// load change to sm
   168  	return csm.StateManager.Load(_protocolID, _stakingCandCenter, &delta)
   169  }
   170  
   171  func (csm *candSM) CreditBucketPool(amount *big.Int) error {
   172  	return csm.bucketPool.CreditPool(csm.StateManager, amount)
   173  }
   174  
   175  func (csm *candSM) DebitBucketPool(amount *big.Int, newBucket bool) error {
   176  	return csm.bucketPool.DebitPool(csm, amount, newBucket)
   177  }
   178  
   179  func (csm *candSM) Commit(ctx context.Context) error {
   180  	height, err := csm.Height()
   181  	if err != nil {
   182  		return err
   183  	}
   184  	if featureWithHeightCtx, ok := protocol.GetFeatureWithHeightCtx(ctx); ok && featureWithHeightCtx.CandCenterHasAlias(height) {
   185  		if err := csm.candCenter.LegacyCommit(); err != nil {
   186  			return err
   187  		}
   188  	} else {
   189  		if err := csm.candCenter.Commit(); err != nil {
   190  			return err
   191  		}
   192  	}
   193  
   194  	if err := csm.bucketPool.Commit(csm); err != nil {
   195  		return err
   196  	}
   197  
   198  	// write updated view back to state factory
   199  	return csm.WriteView(_protocolID, csm.DirtyView())
   200  }
   201  
   202  func (csm *candSM) getBucket(index uint64) (*VoteBucket, error) {
   203  	return newCandidateStateReader(csm).getBucket(index)
   204  }
   205  
   206  func (csm *candSM) updateBucket(index uint64, bucket *VoteBucket) error {
   207  	if _, err := csm.getBucket(index); err != nil {
   208  		return err
   209  	}
   210  
   211  	_, err := csm.PutState(
   212  		bucket,
   213  		protocol.NamespaceOption(_stakingNameSpace),
   214  		protocol.KeyOption(bucketKey(index)))
   215  	return err
   216  }
   217  
   218  func (csm *candSM) putBucket(bucket *VoteBucket) (uint64, error) {
   219  	var tc totalBucketCount
   220  	if _, err := csm.State(
   221  		&tc,
   222  		protocol.NamespaceOption(_stakingNameSpace),
   223  		protocol.KeyOption(TotalBucketKey)); err != nil && errors.Cause(err) != state.ErrStateNotExist {
   224  		return 0, err
   225  	}
   226  
   227  	index := tc.Count()
   228  	// Add index inside bucket
   229  	bucket.Index = index
   230  	if _, err := csm.PutState(
   231  		bucket,
   232  		protocol.NamespaceOption(_stakingNameSpace),
   233  		protocol.KeyOption(bucketKey(index))); err != nil {
   234  		return 0, err
   235  	}
   236  	tc.count++
   237  	_, err := csm.PutState(
   238  		&tc,
   239  		protocol.NamespaceOption(_stakingNameSpace),
   240  		protocol.KeyOption(TotalBucketKey))
   241  	return index, err
   242  }
   243  
   244  func (csm *candSM) delBucket(index uint64) error {
   245  	_, err := csm.DelState(
   246  		protocol.NamespaceOption(_stakingNameSpace),
   247  		protocol.KeyOption(bucketKey(index)))
   248  	return err
   249  }
   250  
   251  func (csm *candSM) putBucketAndIndex(bucket *VoteBucket) (uint64, error) {
   252  	index, err := csm.putBucket(bucket)
   253  	if err != nil {
   254  		return 0, errors.Wrap(err, "failed to put bucket")
   255  	}
   256  
   257  	if err := csm.putVoterBucketIndex(bucket.Owner, index); err != nil {
   258  		return 0, errors.Wrap(err, "failed to put bucket index")
   259  	}
   260  
   261  	if err := csm.putCandBucketIndex(bucket.Candidate, index); err != nil {
   262  		return 0, errors.Wrap(err, "failed to put candidate index")
   263  	}
   264  	return index, nil
   265  }
   266  
   267  func (csm *candSM) delBucketAndIndex(owner, cand address.Address, index uint64) error {
   268  	if err := csm.delBucket(index); err != nil {
   269  		return errors.Wrap(err, "failed to delete bucket")
   270  	}
   271  
   272  	if err := csm.delVoterBucketIndex(owner, index); err != nil {
   273  		return errors.Wrap(err, "failed to delete bucket index")
   274  	}
   275  
   276  	if err := csm.delCandBucketIndex(cand, index); err != nil {
   277  		return errors.Wrap(err, "failed to delete candidate index")
   278  	}
   279  	return nil
   280  }
   281  
   282  func (csm *candSM) putBucketIndex(addr address.Address, prefix byte, index uint64) error {
   283  	var (
   284  		bis BucketIndices
   285  		key = AddrKeyWithPrefix(addr, prefix)
   286  	)
   287  	if _, err := csm.State(
   288  		&bis,
   289  		protocol.NamespaceOption(_stakingNameSpace),
   290  		protocol.KeyOption(key)); err != nil && errors.Cause(err) != state.ErrStateNotExist {
   291  		return err
   292  	}
   293  	bis.addBucketIndex(index)
   294  	_, err := csm.PutState(
   295  		&bis,
   296  		protocol.NamespaceOption(_stakingNameSpace),
   297  		protocol.KeyOption(key))
   298  	return err
   299  }
   300  
   301  func (csm *candSM) putVoterBucketIndex(addr address.Address, index uint64) error {
   302  	return csm.putBucketIndex(addr, _voterIndex, index)
   303  }
   304  
   305  func (csm *candSM) delBucketIndex(addr address.Address, prefix byte, index uint64) error {
   306  	var (
   307  		bis BucketIndices
   308  		key = AddrKeyWithPrefix(addr, prefix)
   309  	)
   310  	if _, err := csm.State(
   311  		&bis,
   312  		protocol.NamespaceOption(_stakingNameSpace),
   313  		protocol.KeyOption(key)); err != nil {
   314  		return err
   315  	}
   316  	bis.deleteBucketIndex(index)
   317  
   318  	var err error
   319  	if len(bis) == 0 {
   320  		_, err = csm.DelState(
   321  			protocol.NamespaceOption(_stakingNameSpace),
   322  			protocol.KeyOption(key))
   323  	} else {
   324  		_, err = csm.PutState(
   325  			&bis,
   326  			protocol.NamespaceOption(_stakingNameSpace),
   327  			protocol.KeyOption(key))
   328  	}
   329  	return err
   330  }
   331  
   332  func (csm *candSM) delVoterBucketIndex(addr address.Address, index uint64) error {
   333  	return csm.delBucketIndex(addr, _voterIndex, index)
   334  }
   335  
   336  func (csm *candSM) putCandidate(d *Candidate) error {
   337  	_, err := csm.PutState(d, protocol.NamespaceOption(_candidateNameSpace), protocol.KeyOption(d.Owner.Bytes()))
   338  	return err
   339  }
   340  
   341  func (csm *candSM) putCandBucketIndex(addr address.Address, index uint64) error {
   342  	return csm.putBucketIndex(addr, _candIndex, index)
   343  }
   344  
   345  func (csm *candSM) delCandidate(name address.Address) error {
   346  	_, err := csm.DelState(protocol.NamespaceOption(_candidateNameSpace), protocol.KeyOption(name.Bytes()))
   347  	return err
   348  }
   349  
   350  func (csm *candSM) delCandBucketIndex(addr address.Address, index uint64) error {
   351  	return csm.delBucketIndex(addr, _candIndex, index)
   352  }