github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/staking/protocol.go (about)

     1  // Copyright (c) 2020 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  	"encoding/hex"
    11  	"math/big"
    12  	"time"
    13  
    14  	"github.com/pkg/errors"
    15  	"go.uber.org/zap"
    16  	"google.golang.org/protobuf/proto"
    17  
    18  	"github.com/iotexproject/go-pkgs/hash"
    19  	"github.com/iotexproject/iotex-address/address"
    20  	"github.com/iotexproject/iotex-proto/golang/iotexapi"
    21  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    22  
    23  	"github.com/iotexproject/iotex-core/action"
    24  	"github.com/iotexproject/iotex-core/action/protocol"
    25  	accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util"
    26  	"github.com/iotexproject/iotex-core/action/protocol/rolldpos"
    27  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    28  	"github.com/iotexproject/iotex-core/pkg/log"
    29  	"github.com/iotexproject/iotex-core/state"
    30  )
    31  
    32  const (
    33  	// _protocolID is the protocol ID
    34  	_protocolID = "staking"
    35  
    36  	// _stakingNameSpace is the bucket name for staking state
    37  	_stakingNameSpace = "Staking"
    38  
    39  	// _candidateNameSpace is the bucket name for candidate state
    40  	_candidateNameSpace = "Candidate"
    41  
    42  	// CandsMapNS is the bucket name to store candidate map
    43  	CandsMapNS = "CandsMap"
    44  )
    45  
    46  const (
    47  	// keys in the namespace StakingNameSpace are prefixed with 1-byte tag, which serves 2 purposes:
    48  	// 1. to be able to store multiple objects under the same key (like bucket index for voter and candidate)
    49  	// 2. can call underlying KVStore's Filter() to retrieve a certain type of objects
    50  	_const = byte(iota)
    51  	_bucket
    52  	_voterIndex
    53  	_candIndex
    54  	_endorsement
    55  )
    56  
    57  // Errors
    58  var (
    59  	ErrWithdrawnBucket     = errors.New("the bucket is already withdrawn")
    60  	ErrEndorsementNotExist = errors.New("the endorsement does not exist")
    61  	TotalBucketKey         = append([]byte{_const}, []byte("totalBucket")...)
    62  )
    63  
    64  var (
    65  	_nameKey     = []byte("name")
    66  	_operatorKey = []byte("operator")
    67  	_ownerKey    = []byte("owner")
    68  )
    69  
    70  type (
    71  	// ReceiptError indicates a non-critical error with corresponding receipt status
    72  	ReceiptError interface {
    73  		Error() string
    74  		ReceiptStatus() uint64
    75  	}
    76  
    77  	// Protocol defines the protocol of handling staking
    78  	Protocol struct {
    79  		addr                   address.Address
    80  		depositGas             DepositGas
    81  		config                 Configuration
    82  		candBucketsIndexer     *CandidatesBucketsIndexer
    83  		contractStakingIndexer ContractStakingIndexer
    84  		voteReviser            *VoteReviser
    85  		patch                  *PatchStore
    86  	}
    87  
    88  	// Configuration is the staking protocol configuration.
    89  	Configuration struct {
    90  		VoteWeightCalConsts              genesis.VoteWeightCalConsts
    91  		RegistrationConsts               RegistrationConsts
    92  		WithdrawWaitingPeriod            time.Duration
    93  		MinStakeAmount                   *big.Int
    94  		BootstrapCandidates              []genesis.BootstrapCandidate
    95  		PersistStakingPatchBlock         uint64
    96  		EndorsementWithdrawWaitingBlocks uint64
    97  	}
    98  
    99  	// DepositGas deposits gas to some pool
   100  	DepositGas func(ctx context.Context, sm protocol.StateManager, amount *big.Int) (*action.TransactionLog, error)
   101  )
   102  
   103  // FindProtocol return a registered protocol from registry
   104  func FindProtocol(registry *protocol.Registry) *Protocol {
   105  	if registry == nil {
   106  		return nil
   107  	}
   108  	p, ok := registry.Find(_protocolID)
   109  	if !ok {
   110  		return nil
   111  	}
   112  	rp, ok := p.(*Protocol)
   113  	if !ok {
   114  		log.S().Panic("fail to cast rolldpos protocol")
   115  	}
   116  	return rp
   117  }
   118  
   119  // NewProtocol instantiates the protocol of staking
   120  func NewProtocol(
   121  	depositGas DepositGas,
   122  	cfg *BuilderConfig,
   123  	candBucketsIndexer *CandidatesBucketsIndexer,
   124  	contractStakingIndexer ContractStakingIndexer,
   125  	correctCandsHeight uint64,
   126  	reviseHeights ...uint64,
   127  ) (*Protocol, error) {
   128  	h := hash.Hash160b([]byte(_protocolID))
   129  	addr, err := address.FromBytes(h[:])
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	minStakeAmount, ok := new(big.Int).SetString(cfg.Staking.MinStakeAmount, 10)
   135  	if !ok {
   136  		return nil, action.ErrInvalidAmount
   137  	}
   138  
   139  	regFee, ok := new(big.Int).SetString(cfg.Staking.RegistrationConsts.Fee, 10)
   140  	if !ok {
   141  		return nil, action.ErrInvalidAmount
   142  	}
   143  
   144  	minSelfStake, ok := new(big.Int).SetString(cfg.Staking.RegistrationConsts.MinSelfStake, 10)
   145  	if !ok {
   146  		return nil, action.ErrInvalidAmount
   147  	}
   148  
   149  	// new vote reviser, revise ate greenland
   150  	voteReviser := NewVoteReviser(cfg.Staking.VoteWeightCalConsts, correctCandsHeight, reviseHeights...)
   151  
   152  	return &Protocol{
   153  		addr: addr,
   154  		config: Configuration{
   155  			VoteWeightCalConsts: cfg.Staking.VoteWeightCalConsts,
   156  			RegistrationConsts: RegistrationConsts{
   157  				Fee:          regFee,
   158  				MinSelfStake: minSelfStake,
   159  			},
   160  			WithdrawWaitingPeriod:            cfg.Staking.WithdrawWaitingPeriod,
   161  			MinStakeAmount:                   minStakeAmount,
   162  			BootstrapCandidates:              cfg.Staking.BootstrapCandidates,
   163  			PersistStakingPatchBlock:         cfg.PersistStakingPatchBlock,
   164  			EndorsementWithdrawWaitingBlocks: cfg.Staking.EndorsementWithdrawWaitingBlocks,
   165  		},
   166  		depositGas:             depositGas,
   167  		candBucketsIndexer:     candBucketsIndexer,
   168  		voteReviser:            voteReviser,
   169  		patch:                  NewPatchStore(cfg.StakingPatchDir),
   170  		contractStakingIndexer: contractStakingIndexer,
   171  	}, nil
   172  }
   173  
   174  // ProtocolAddr returns the address generated from protocol id
   175  func ProtocolAddr() address.Address {
   176  	return protocol.HashStringToAddress(_protocolID)
   177  }
   178  
   179  // Start starts the protocol
   180  func (p *Protocol) Start(ctx context.Context, sr protocol.StateReader) (interface{}, error) {
   181  	featureCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
   182  	height, err := sr.Height()
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  
   187  	// load view from SR
   188  	c, _, err := CreateBaseView(sr, featureCtx.ReadStateFromDB(height))
   189  	if err != nil {
   190  		return nil, errors.Wrap(err, "failed to start staking protocol")
   191  	}
   192  
   193  	if p.needToReadCandsMap(ctx, height) {
   194  		name, operator, owners, err := readCandCenterStateFromStateDB(sr)
   195  		if err != nil {
   196  			// stateDB does not have name/operator map yet
   197  			if name, operator, owners, err = p.patch.Read(height); err != nil {
   198  				return nil, errors.Wrap(err, "failed to read name/operator map")
   199  			}
   200  		}
   201  		if err = c.candCenter.base.loadNameOperatorMapOwnerList(name, operator, owners); err != nil {
   202  			return nil, errors.Wrap(err, "failed to load name/operator map to cand center")
   203  		}
   204  	}
   205  	return c, nil
   206  }
   207  
   208  // CreateGenesisStates is used to setup BootstrapCandidates from genesis config.
   209  func (p *Protocol) CreateGenesisStates(
   210  	ctx context.Context,
   211  	sm protocol.StateManager,
   212  ) error {
   213  	if len(p.config.BootstrapCandidates) == 0 {
   214  		return nil
   215  	}
   216  	// TODO: set init values based on ctx
   217  	csm, err := NewCandidateStateManager(sm, false)
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	for _, bc := range p.config.BootstrapCandidates {
   223  		owner, err := address.FromString(bc.OwnerAddress)
   224  		if err != nil {
   225  			return err
   226  		}
   227  
   228  		operator, err := address.FromString(bc.OperatorAddress)
   229  		if err != nil {
   230  			return err
   231  		}
   232  
   233  		reward, err := address.FromString(bc.RewardAddress)
   234  		if err != nil {
   235  			return err
   236  		}
   237  
   238  		selfStake, ok := new(big.Int).SetString(bc.SelfStakingTokens, 10)
   239  		if !ok {
   240  			return action.ErrInvalidAmount
   241  		}
   242  		bucket := NewVoteBucket(owner, owner, selfStake, 7, time.Now(), true)
   243  		bucketIdx, err := csm.putBucketAndIndex(bucket)
   244  		if err != nil {
   245  			return err
   246  		}
   247  		c := &Candidate{
   248  			Owner:              owner,
   249  			Operator:           operator,
   250  			Reward:             reward,
   251  			Name:               bc.Name,
   252  			Votes:              p.calculateVoteWeight(bucket, true),
   253  			SelfStakeBucketIdx: bucketIdx,
   254  			SelfStake:          selfStake,
   255  		}
   256  
   257  		// put in statedb and cand center
   258  		if err := csm.Upsert(c); err != nil {
   259  			return err
   260  		}
   261  		if err := csm.DebitBucketPool(selfStake, true); err != nil {
   262  			return err
   263  		}
   264  	}
   265  
   266  	// commit updated view
   267  	return errors.Wrap(csm.Commit(ctx), "failed to commit candidate change in CreateGenesisStates")
   268  }
   269  
   270  // CreatePreStates updates state manager
   271  func (p *Protocol) CreatePreStates(ctx context.Context, sm protocol.StateManager) error {
   272  	g := genesis.MustExtractGenesisContext(ctx)
   273  	blkCtx := protocol.MustGetBlockCtx(ctx)
   274  	featureCtx := protocol.MustGetFeatureCtx(ctx)
   275  	featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
   276  	if blkCtx.BlockHeight == g.GreenlandBlockHeight {
   277  		csr, err := ConstructBaseView(sm)
   278  		if err != nil {
   279  			return err
   280  		}
   281  		if _, err = sm.PutState(csr.BaseView().bucketPool.total, protocol.NamespaceOption(_stakingNameSpace), protocol.KeyOption(_bucketPoolAddrKey)); err != nil {
   282  			return err
   283  		}
   284  	}
   285  
   286  	if p.voteReviser.NeedRevise(blkCtx.BlockHeight) {
   287  		csm, err := NewCandidateStateManager(sm, featureWithHeightCtx.ReadStateFromDB(blkCtx.BlockHeight))
   288  		if err != nil {
   289  			return err
   290  		}
   291  		if err := p.voteReviser.Revise(csm, blkCtx.BlockHeight); err != nil {
   292  			return err
   293  		}
   294  	}
   295  	if p.candBucketsIndexer == nil {
   296  		return nil
   297  	}
   298  	rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx))
   299  	currentEpochNum := rp.GetEpochNum(blkCtx.BlockHeight)
   300  	if currentEpochNum == 0 {
   301  		return nil
   302  	}
   303  	epochStartHeight := rp.GetEpochHeight(currentEpochNum)
   304  	if epochStartHeight != blkCtx.BlockHeight || featureCtx.SkipStakingIndexer {
   305  		return nil
   306  	}
   307  
   308  	return p.handleStakingIndexer(rp.GetEpochHeight(currentEpochNum-1), sm)
   309  }
   310  
   311  func (p *Protocol) handleStakingIndexer(epochStartHeight uint64, sm protocol.StateManager) error {
   312  	csr := newCandidateStateReader(sm)
   313  	allBuckets, _, err := csr.getAllBuckets()
   314  	if err != nil && errors.Cause(err) != state.ErrStateNotExist {
   315  		return err
   316  	}
   317  	buckets, err := toIoTeXTypesVoteBucketList(sm, allBuckets)
   318  	if err != nil {
   319  		return err
   320  	}
   321  	err = p.candBucketsIndexer.PutBuckets(epochStartHeight, buckets)
   322  	if err != nil {
   323  		return err
   324  	}
   325  	all, _, err := csr.getAllCandidates()
   326  	if err != nil && errors.Cause(err) != state.ErrStateNotExist {
   327  		return err
   328  	}
   329  	candidateList := toIoTeXTypesCandidateListV2(all)
   330  	return p.candBucketsIndexer.PutCandidates(epochStartHeight, candidateList)
   331  }
   332  
   333  // PreCommit preforms pre-commit
   334  func (p *Protocol) PreCommit(ctx context.Context, sm protocol.StateManager) error {
   335  	height, err := sm.Height()
   336  	if err != nil {
   337  		return err
   338  	}
   339  	if !p.needToWriteCandsMap(ctx, height) {
   340  		return nil
   341  	}
   342  
   343  	featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
   344  	csm, err := NewCandidateStateManager(sm, featureWithHeightCtx.ReadStateFromDB(height))
   345  	if err != nil {
   346  		return err
   347  	}
   348  	cc := csm.DirtyView().candCenter
   349  	base := cc.base.clone()
   350  	if _, err = base.commit(cc.change, featureWithHeightCtx.CandCenterHasAlias(height)); err != nil {
   351  		return errors.Wrap(err, "failed to apply candidate change in pre-commit")
   352  	}
   353  	// persist nameMap/operatorMap and ownerList to stateDB
   354  	name := base.candsInNameMap()
   355  	op := base.candsInOperatorMap()
   356  	owners := base.ownersList()
   357  	if len(name) == 0 || len(op) == 0 {
   358  		return ErrNilParameters
   359  	}
   360  	if err := writeCandCenterStateToStateDB(sm, name, op, owners); err != nil {
   361  		return errors.Wrap(err, "failed to write name/operator map to stateDB")
   362  	}
   363  	return nil
   364  }
   365  
   366  // Commit commits the last change
   367  func (p *Protocol) Commit(ctx context.Context, sm protocol.StateManager) error {
   368  	featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
   369  	height, err := sm.Height()
   370  	if err != nil {
   371  		return err
   372  	}
   373  	csm, err := NewCandidateStateManager(sm, featureWithHeightCtx.ReadStateFromDB(height))
   374  	if err != nil {
   375  		return err
   376  	}
   377  
   378  	// commit updated view
   379  	return errors.Wrap(csm.Commit(ctx), "failed to commit candidate change in Commit")
   380  }
   381  
   382  // Handle handles a staking message
   383  func (p *Protocol) Handle(ctx context.Context, act action.Action, sm protocol.StateManager) (*action.Receipt, error) {
   384  	featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
   385  	height, err := sm.Height()
   386  	if err != nil {
   387  		return nil, err
   388  	}
   389  	csm, err := NewCandidateStateManager(sm, featureWithHeightCtx.ReadStateFromDB(height))
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  
   394  	return p.handle(ctx, act, csm)
   395  }
   396  
   397  func (p *Protocol) handle(ctx context.Context, act action.Action, csm CandidateStateManager) (*action.Receipt, error) {
   398  	var (
   399  		rLog  *receiptLog
   400  		tLogs []*action.TransactionLog
   401  		err   error
   402  		logs  []*action.Log
   403  	)
   404  
   405  	switch act := act.(type) {
   406  	case *action.CreateStake:
   407  		rLog, tLogs, err = p.handleCreateStake(ctx, act, csm)
   408  	case *action.Unstake:
   409  		rLog, err = p.handleUnstake(ctx, act, csm)
   410  	case *action.WithdrawStake:
   411  		rLog, tLogs, err = p.handleWithdrawStake(ctx, act, csm)
   412  	case *action.ChangeCandidate:
   413  		rLog, err = p.handleChangeCandidate(ctx, act, csm)
   414  	case *action.TransferStake:
   415  		rLog, err = p.handleTransferStake(ctx, act, csm)
   416  	case *action.DepositToStake:
   417  		rLog, tLogs, err = p.handleDepositToStake(ctx, act, csm)
   418  	case *action.Restake:
   419  		rLog, err = p.handleRestake(ctx, act, csm)
   420  	case *action.CandidateRegister:
   421  		rLog, tLogs, err = p.handleCandidateRegister(ctx, act, csm)
   422  	case *action.CandidateUpdate:
   423  		rLog, err = p.handleCandidateUpdate(ctx, act, csm)
   424  	case *action.CandidateActivate:
   425  		rLog, tLogs, err = p.handleCandidateActivate(ctx, act, csm)
   426  	case *action.CandidateEndorsement:
   427  		rLog, tLogs, err = p.handleCandidateEndorsement(ctx, act, csm)
   428  	default:
   429  		return nil, nil
   430  	}
   431  
   432  	if l := rLog.Build(ctx, err); l != nil {
   433  		logs = append(logs, l)
   434  	}
   435  	if err == nil {
   436  		return p.settleAction(ctx, csm.SM(), uint64(iotextypes.ReceiptStatus_Success), logs, tLogs)
   437  	}
   438  
   439  	if receiptErr, ok := err.(ReceiptError); ok {
   440  		actionCtx := protocol.MustGetActionCtx(ctx)
   441  		log.L().With(
   442  			zap.String("actionHash", hex.EncodeToString(actionCtx.ActionHash[:]))).Debug("Failed to commit staking action", zap.Error(err))
   443  		return p.settleAction(ctx, csm.SM(), receiptErr.ReceiptStatus(), logs, tLogs)
   444  	}
   445  	return nil, err
   446  }
   447  
   448  // Validate validates a staking message
   449  func (p *Protocol) Validate(ctx context.Context, act action.Action, sr protocol.StateReader) error {
   450  	if act == nil {
   451  		return action.ErrNilAction
   452  	}
   453  	switch act := act.(type) {
   454  	case *action.CreateStake:
   455  		return p.validateCreateStake(ctx, act)
   456  	case *action.Unstake:
   457  		return p.validateUnstake(ctx, act)
   458  	case *action.WithdrawStake:
   459  		return p.validateWithdrawStake(ctx, act)
   460  	case *action.ChangeCandidate:
   461  		return p.validateChangeCandidate(ctx, act)
   462  	case *action.TransferStake:
   463  		return p.validateTransferStake(ctx, act)
   464  	case *action.DepositToStake:
   465  		return p.validateDepositToStake(ctx, act)
   466  	case *action.Restake:
   467  		return p.validateRestake(ctx, act)
   468  	case *action.CandidateRegister:
   469  		return p.validateCandidateRegister(ctx, act)
   470  	case *action.CandidateUpdate:
   471  		return p.validateCandidateUpdate(ctx, act)
   472  	case *action.CandidateActivate:
   473  		return p.validateCandidateActivate(ctx, act)
   474  	case *action.CandidateEndorsement:
   475  		return p.validateCandidateEndorsement(ctx, act)
   476  	}
   477  	return nil
   478  }
   479  
   480  func (p *Protocol) isActiveCandidate(ctx context.Context, csr CandidateStateReader, cand *Candidate) (bool, error) {
   481  	if cand.SelfStake.Cmp(p.config.RegistrationConsts.MinSelfStake) < 0 {
   482  		return false, nil
   483  	}
   484  	featureCtx := protocol.MustGetFeatureCtx(ctx)
   485  	if featureCtx.DisableDelegateEndorsement {
   486  		// before endorsement feature, candidates with enough amount must be active
   487  		return true, nil
   488  	}
   489  	bucket, err := csr.getBucket(cand.SelfStakeBucketIdx)
   490  	switch {
   491  	case errors.Cause(err) == state.ErrStateNotExist:
   492  		// endorse bucket has been withdrawn
   493  		return false, nil
   494  	case err != nil:
   495  		return false, errors.Wrapf(err, "failed to get bucket %d", cand.SelfStakeBucketIdx)
   496  	default:
   497  	}
   498  	selfStake, err := isSelfStakeBucket(featureCtx, csr, bucket)
   499  	if err != nil {
   500  		return false, errors.Wrapf(err, "failed to check self-stake bucket %d", cand.SelfStakeBucketIdx)
   501  	}
   502  	return selfStake, nil
   503  }
   504  
   505  // ActiveCandidates returns all active candidates in candidate center
   506  func (p *Protocol) ActiveCandidates(ctx context.Context, sr protocol.StateReader, height uint64) (state.CandidateList, error) {
   507  	srHeight, err := sr.Height()
   508  	if err != nil {
   509  		return nil, errors.Wrap(err, "failed to get StateReader height")
   510  	}
   511  	c, err := ConstructBaseView(sr)
   512  	if err != nil {
   513  		return nil, errors.Wrap(err, "failed to get ActiveCandidates")
   514  	}
   515  	list := c.AllCandidates()
   516  	cand := make(CandidateList, 0, len(list))
   517  	featureCtx := protocol.MustGetFeatureCtx(ctx)
   518  	for i := range list {
   519  		if p.contractStakingIndexer != nil && featureCtx.AddContractStakingVotes {
   520  			// specifying the height param instead of query latest from indexer directly, aims to cause error when indexer falls behind
   521  			// currently there are two possible sr (i.e. factory or workingSet), it means the height could be chain height or current block height
   522  			// using height-1 will cover the two scenario while detect whether the indexer is lagging behind
   523  			contractVotes, err := p.contractStakingIndexer.CandidateVotes(ctx, list[i].Owner, srHeight-1)
   524  			if err != nil {
   525  				return nil, errors.Wrap(err, "failed to get CandidateVotes from contractStakingIndexer")
   526  			}
   527  			list[i].Votes.Add(list[i].Votes, contractVotes)
   528  		}
   529  		active, err := p.isActiveCandidate(ctx, c, list[i])
   530  		if err != nil {
   531  			return nil, err
   532  		}
   533  		if active {
   534  			cand = append(cand, list[i])
   535  		}
   536  	}
   537  	return cand.toStateCandidateList()
   538  }
   539  
   540  // ReadState read the state on blockchain via protocol
   541  func (p *Protocol) ReadState(ctx context.Context, sr protocol.StateReader, method []byte, args ...[]byte) ([]byte, uint64, error) {
   542  	m := iotexapi.ReadStakingDataMethod{}
   543  	if err := proto.Unmarshal(method, &m); err != nil {
   544  		return nil, uint64(0), errors.Wrap(err, "failed to unmarshal method name")
   545  	}
   546  	if len(args) != 1 {
   547  		return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args))
   548  	}
   549  	r := iotexapi.ReadStakingDataRequest{}
   550  	if err := proto.Unmarshal(args[0], &r); err != nil {
   551  		return nil, uint64(0), errors.Wrap(err, "failed to unmarshal request")
   552  	}
   553  
   554  	// stakeSR is the stake state reader including native and contract staking
   555  	stakeSR, err := newCompositeStakingStateReader(p.contractStakingIndexer, p.candBucketsIndexer, sr)
   556  	if err != nil {
   557  		return nil, 0, err
   558  	}
   559  
   560  	// get height arg
   561  	inputHeight, err := sr.Height()
   562  	if err != nil {
   563  		return nil, 0, err
   564  	}
   565  	rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx))
   566  	epochStartHeight := rp.GetEpochHeight(rp.GetEpochNum(inputHeight))
   567  	nativeSR, err := ConstructBaseView(sr)
   568  	if err != nil {
   569  		return nil, 0, err
   570  	}
   571  
   572  	var (
   573  		height uint64
   574  		resp   proto.Message
   575  	)
   576  	switch m.GetMethod() {
   577  	case iotexapi.ReadStakingDataMethod_BUCKETS:
   578  		if epochStartHeight != 0 && p.candBucketsIndexer != nil {
   579  			resp, height, err = p.candBucketsIndexer.GetBuckets(epochStartHeight, r.GetBuckets().GetPagination().GetOffset(), r.GetBuckets().GetPagination().GetLimit())
   580  		} else {
   581  			resp, height, err = nativeSR.readStateBuckets(ctx, r.GetBuckets())
   582  		}
   583  	case iotexapi.ReadStakingDataMethod_BUCKETS_BY_VOTER:
   584  		resp, height, err = nativeSR.readStateBucketsByVoter(ctx, r.GetBucketsByVoter())
   585  	case iotexapi.ReadStakingDataMethod_BUCKETS_BY_CANDIDATE:
   586  		resp, height, err = nativeSR.readStateBucketsByCandidate(ctx, r.GetBucketsByCandidate())
   587  	case iotexapi.ReadStakingDataMethod_BUCKETS_BY_INDEXES:
   588  		resp, height, err = nativeSR.readStateBucketByIndices(ctx, r.GetBucketsByIndexes())
   589  	case iotexapi.ReadStakingDataMethod_BUCKETS_COUNT:
   590  		resp, height, err = nativeSR.readStateBucketCount(ctx, r.GetBucketsCount())
   591  	case iotexapi.ReadStakingDataMethod_CANDIDATES:
   592  		resp, height, err = stakeSR.readStateCandidates(ctx, r.GetCandidates())
   593  	case iotexapi.ReadStakingDataMethod_CANDIDATE_BY_NAME:
   594  		resp, height, err = stakeSR.readStateCandidateByName(ctx, r.GetCandidateByName())
   595  	case iotexapi.ReadStakingDataMethod_CANDIDATE_BY_ADDRESS:
   596  		resp, height, err = stakeSR.readStateCandidateByAddress(ctx, r.GetCandidateByAddress())
   597  	case iotexapi.ReadStakingDataMethod_TOTAL_STAKING_AMOUNT:
   598  		resp, height, err = nativeSR.readStateTotalStakingAmount(ctx, r.GetTotalStakingAmount())
   599  	case iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS:
   600  		resp, height, err = stakeSR.readStateBuckets(ctx, r.GetBuckets())
   601  	case iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS_BY_VOTER:
   602  		resp, height, err = stakeSR.readStateBucketsByVoter(ctx, r.GetBucketsByVoter())
   603  	case iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS_BY_CANDIDATE:
   604  		resp, height, err = stakeSR.readStateBucketsByCandidate(ctx, r.GetBucketsByCandidate())
   605  	case iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS_BY_INDEXES:
   606  		resp, height, err = stakeSR.readStateBucketByIndices(ctx, r.GetBucketsByIndexes())
   607  	case iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS_COUNT:
   608  		resp, height, err = stakeSR.readStateBucketCount(ctx, r.GetBucketsCount())
   609  	case iotexapi.ReadStakingDataMethod_COMPOSITE_TOTAL_STAKING_AMOUNT:
   610  		resp, height, err = stakeSR.readStateTotalStakingAmount(ctx, r.GetTotalStakingAmount())
   611  	case iotexapi.ReadStakingDataMethod_CONTRACT_STAKING_BUCKET_TYPES:
   612  		resp, height, err = stakeSR.readStateContractStakingBucketTypes(ctx, r.GetContractStakingBucketTypes())
   613  	default:
   614  		err = errors.New("corresponding method isn't found")
   615  	}
   616  	if err != nil {
   617  		return nil, height, err
   618  	}
   619  	data, err := proto.Marshal(resp)
   620  	if err != nil {
   621  		return nil, height, err
   622  	}
   623  	return data, height, nil
   624  }
   625  
   626  // Register registers the protocol with a unique ID
   627  func (p *Protocol) Register(r *protocol.Registry) error {
   628  	return r.Register(_protocolID, p)
   629  }
   630  
   631  // ForceRegister registers the protocol with a unique ID and force replacing the previous protocol if it exists
   632  func (p *Protocol) ForceRegister(r *protocol.Registry) error {
   633  	return r.ForceRegister(_protocolID, p)
   634  }
   635  
   636  // Name returns the name of protocol
   637  func (p *Protocol) Name() string {
   638  	return _protocolID
   639  }
   640  
   641  func (p *Protocol) calculateVoteWeight(v *VoteBucket, selfStake bool) *big.Int {
   642  	return CalculateVoteWeight(p.config.VoteWeightCalConsts, v, selfStake)
   643  }
   644  
   645  // settleAccount deposits gas fee and updates caller's nonce
   646  func (p *Protocol) settleAction(
   647  	ctx context.Context,
   648  	sm protocol.StateManager,
   649  	status uint64,
   650  	logs []*action.Log,
   651  	tLogs []*action.TransactionLog,
   652  ) (*action.Receipt, error) {
   653  	actionCtx := protocol.MustGetActionCtx(ctx)
   654  	blkCtx := protocol.MustGetBlockCtx(ctx)
   655  	gasFee := big.NewInt(0).Mul(actionCtx.GasPrice, big.NewInt(0).SetUint64(actionCtx.IntrinsicGas))
   656  	depositLog, err := p.depositGas(ctx, sm, gasFee)
   657  	if err != nil {
   658  		return nil, errors.Wrap(err, "failed to deposit gas")
   659  	}
   660  	accountCreationOpts := []state.AccountCreationOption{}
   661  	if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount {
   662  		accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption())
   663  	}
   664  	acc, err := accountutil.LoadAccount(sm, actionCtx.Caller, accountCreationOpts...)
   665  	if err != nil {
   666  		return nil, err
   667  	}
   668  	if err := acc.SetPendingNonce(actionCtx.Nonce + 1); err != nil {
   669  		return nil, errors.Wrap(err, "failed to set nonce")
   670  	}
   671  	if err := accountutil.StoreAccount(sm, actionCtx.Caller, acc); err != nil {
   672  		return nil, errors.Wrap(err, "failed to update nonce")
   673  	}
   674  	r := action.Receipt{
   675  		Status:          status,
   676  		BlockHeight:     blkCtx.BlockHeight,
   677  		ActionHash:      actionCtx.ActionHash,
   678  		GasConsumed:     actionCtx.IntrinsicGas,
   679  		ContractAddress: p.addr.String(),
   680  	}
   681  	r.AddLogs(logs...).AddTransactionLogs(depositLog).AddTransactionLogs(tLogs...)
   682  	return &r, nil
   683  }
   684  
   685  func (p *Protocol) needToReadCandsMap(ctx context.Context, height uint64) bool {
   686  	fCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
   687  	return height > p.config.PersistStakingPatchBlock && fCtx.CandCenterHasAlias(height)
   688  }
   689  
   690  func (p *Protocol) needToWriteCandsMap(ctx context.Context, height uint64) bool {
   691  	fCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
   692  	return height >= p.config.PersistStakingPatchBlock && fCtx.CandCenterHasAlias(height)
   693  }
   694  
   695  func readCandCenterStateFromStateDB(sr protocol.StateReader) (CandidateList, CandidateList, CandidateList, error) {
   696  	var (
   697  		name, operator, owner CandidateList
   698  	)
   699  	if _, err := sr.State(&name, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_nameKey)); err != nil {
   700  		return nil, nil, nil, err
   701  	}
   702  	if _, err := sr.State(&operator, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_operatorKey)); err != nil {
   703  		return nil, nil, nil, err
   704  	}
   705  	if _, err := sr.State(&owner, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_ownerKey)); err != nil {
   706  		return nil, nil, nil, err
   707  	}
   708  	return name, operator, owner, nil
   709  }
   710  
   711  func writeCandCenterStateToStateDB(sm protocol.StateManager, name, op, owners CandidateList) error {
   712  	if _, err := sm.PutState(name, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_nameKey)); err != nil {
   713  		return err
   714  	}
   715  	if _, err := sm.PutState(op, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_operatorKey)); err != nil {
   716  		return err
   717  	}
   718  	_, err := sm.PutState(owners, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_ownerKey))
   719  	return err
   720  }
   721  
   722  // isSelfStakeBucket returns true if the bucket is self-stake bucket and not expired
   723  func isSelfStakeBucket(featureCtx protocol.FeatureCtx, csc CandidiateStateCommon, bucket *VoteBucket) (bool, error) {
   724  	// bucket index should be settled in one of candidates
   725  	selfStake := csc.ContainsSelfStakingBucket(bucket.Index)
   726  	if featureCtx.DisableDelegateEndorsement || !selfStake {
   727  		return selfStake, nil
   728  	}
   729  
   730  	// bucket should not be unstaked if it is self-owned
   731  	if address.Equal(bucket.Owner, bucket.Candidate) {
   732  		return !bucket.isUnstaked(), nil
   733  	}
   734  	// otherwise bucket should be an endorse bucket which is not expired
   735  	esm := NewEndorsementStateReader(csc.SR())
   736  	height, err := esm.Height()
   737  	if err != nil {
   738  		return false, err
   739  	}
   740  	endorse, err := esm.Get(bucket.Index)
   741  	if err != nil {
   742  		return false, err
   743  	}
   744  	return endorse.Status(height) != EndorseExpired, nil
   745  }