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

     1  // Copyright (c) 2019 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 poll
     7  
     8  import (
     9  	"context"
    10  
    11  	"github.com/iotexproject/go-pkgs/hash"
    12  	"github.com/iotexproject/iotex-address/address"
    13  	"github.com/pkg/errors"
    14  
    15  	"github.com/iotexproject/iotex-core/action"
    16  	"github.com/iotexproject/iotex-core/action/protocol"
    17  	"github.com/iotexproject/iotex-core/state"
    18  )
    19  
    20  type stakingCommand struct {
    21  	addr      address.Address
    22  	stakingV1 Protocol
    23  	stakingV2 Protocol
    24  }
    25  
    26  // NewStakingCommand creates a staking command center to manage staking committee and new native staking
    27  func NewStakingCommand(stkV1 Protocol, stkV2 Protocol) (Protocol, error) {
    28  	if stkV1 == nil && stkV2 == nil {
    29  		return nil, errors.New("empty staking protocol")
    30  	}
    31  
    32  	h := hash.Hash160b([]byte(_protocolID))
    33  	addr, err := address.FromBytes(h[:])
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	return &stakingCommand{
    39  		addr:      addr,
    40  		stakingV1: stkV1,
    41  		stakingV2: stkV2,
    42  	}, nil
    43  }
    44  
    45  func (sc *stakingCommand) CreateGenesisStates(ctx context.Context, sm protocol.StateManager) error {
    46  	// if v1 exists, bootstrap from v1 only
    47  	if sc.stakingV1 != nil {
    48  		return sc.stakingV1.CreateGenesisStates(ctx, sm)
    49  	}
    50  	return sc.stakingV2.CreateGenesisStates(ctx, sm)
    51  }
    52  
    53  func (sc *stakingCommand) Start(ctx context.Context, sr protocol.StateReader) (interface{}, error) {
    54  	if sc.stakingV1 != nil {
    55  		if starter, ok := sc.stakingV1.(protocol.Starter); ok {
    56  			if _, err := starter.Start(ctx, sr); err != nil {
    57  				return nil, err
    58  			}
    59  		}
    60  	}
    61  
    62  	if sc.stakingV2 != nil {
    63  		if starter, ok := sc.stakingV2.(protocol.Starter); ok {
    64  			return starter.Start(ctx, sr)
    65  		}
    66  	}
    67  	return nil, nil
    68  }
    69  
    70  func (sc *stakingCommand) CreatePreStates(ctx context.Context, sm protocol.StateManager) error {
    71  	if sc.useV2(ctx, sm) {
    72  		if p, ok := sc.stakingV2.(protocol.PreStatesCreator); ok {
    73  			return p.CreatePreStates(ctx, sm)
    74  		}
    75  	}
    76  	if p, ok := sc.stakingV1.(protocol.PreStatesCreator); ok {
    77  		return p.CreatePreStates(ctx, sm)
    78  	}
    79  	return nil
    80  }
    81  
    82  func (sc *stakingCommand) CreatePostSystemActions(ctx context.Context, sr protocol.StateReader) ([]action.Envelope, error) {
    83  	// no height here,  v1 v2 has the same createPostSystemActions method, so directly use common one
    84  	return createPostSystemActions(ctx, sr, sc)
    85  }
    86  
    87  func (sc *stakingCommand) Handle(ctx context.Context, act action.Action, sm protocol.StateManager) (*action.Receipt, error) {
    88  	if sc.useV2(ctx, sm) {
    89  		return sc.stakingV2.Handle(ctx, act, sm)
    90  	}
    91  	return sc.stakingV1.Handle(ctx, act, sm)
    92  }
    93  
    94  func (sc *stakingCommand) Validate(ctx context.Context, act action.Action, sr protocol.StateReader) error {
    95  	// no height here,  v1 v2 has the same validate method, so directly use common one
    96  	return validate(ctx, sr, sc, act)
    97  }
    98  
    99  func (sc *stakingCommand) CalculateCandidatesByHeight(ctx context.Context, sr protocol.StateReader, height uint64) (state.CandidateList, error) {
   100  	if sc.useV2ByHeight(ctx, height) {
   101  		return sc.stakingV2.CalculateCandidatesByHeight(ctx, sr, height)
   102  	}
   103  	return sc.stakingV1.CalculateCandidatesByHeight(ctx, sr, height)
   104  }
   105  
   106  func (sc *stakingCommand) CalculateUnproductiveDelegates(
   107  	ctx context.Context,
   108  	sr protocol.StateReader,
   109  ) ([]string, error) {
   110  	if sc.useV2(ctx, sr) && protocol.MustGetFeatureCtx(ctx).FixUnproductiveDelegates {
   111  		return sc.stakingV2.CalculateUnproductiveDelegates(ctx, sr)
   112  	}
   113  	return sc.stakingV1.CalculateUnproductiveDelegates(ctx, sr)
   114  }
   115  
   116  // Delegates returns exact number of delegates of current epoch
   117  func (sc *stakingCommand) Delegates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) {
   118  	if sc.useV2(ctx, sr) {
   119  		return sc.stakingV2.Delegates(ctx, sr)
   120  	}
   121  	return sc.stakingV1.Delegates(ctx, sr)
   122  }
   123  
   124  // NextDelegates returns exact number of delegates of next epoch
   125  func (sc *stakingCommand) NextDelegates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) {
   126  	if sc.useV2(ctx, sr) {
   127  		return sc.stakingV2.NextDelegates(ctx, sr)
   128  	}
   129  	return sc.stakingV1.NextDelegates(ctx, sr)
   130  }
   131  
   132  // Candidates returns candidate list from state factory of current epoch
   133  func (sc *stakingCommand) Candidates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) {
   134  	if sc.useV2(ctx, sr) {
   135  		return sc.stakingV2.Candidates(ctx, sr)
   136  	}
   137  	return sc.stakingV1.Candidates(ctx, sr)
   138  }
   139  
   140  // NextCandidates returns candidate list from state factory of next epoch
   141  func (sc *stakingCommand) NextCandidates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) {
   142  	if sc.useV2(ctx, sr) {
   143  		return sc.stakingV2.NextCandidates(ctx, sr)
   144  	}
   145  	return sc.stakingV1.NextCandidates(ctx, sr)
   146  }
   147  
   148  func (sc *stakingCommand) ReadState(ctx context.Context, sr protocol.StateReader, method []byte, args ...[]byte) ([]byte, uint64, error) {
   149  	if sc.useV2(ctx, sr) {
   150  		res, height, err := sc.stakingV2.ReadState(ctx, sr, method, args...)
   151  		if err != nil && sc.stakingV1 != nil {
   152  			// check if reading from v1 only method
   153  			return sc.stakingV1.ReadState(ctx, sr, method, args...)
   154  		}
   155  		return res, height, nil
   156  	}
   157  	return sc.stakingV1.ReadState(ctx, sr, method, args...)
   158  }
   159  
   160  // Register registers the protocol with a unique ID
   161  func (sc *stakingCommand) Register(r *protocol.Registry) error {
   162  	return r.Register(_protocolID, sc)
   163  }
   164  
   165  // ForceRegister registers the protocol with a unique ID and force replacing the previous protocol if it exists
   166  func (sc *stakingCommand) ForceRegister(r *protocol.Registry) error {
   167  	return r.ForceRegister(_protocolID, sc)
   168  }
   169  
   170  func (sc *stakingCommand) Name() string {
   171  	return _protocolID
   172  }
   173  
   174  func (sc *stakingCommand) useV2(ctx context.Context, sr protocol.StateReader) bool {
   175  	height, err := sr.Height()
   176  	if err != nil {
   177  		panic("failed to return out height from state reader")
   178  	}
   179  	return sc.useV2ByHeight(ctx, height)
   180  }
   181  
   182  func (sc *stakingCommand) useV2ByHeight(ctx context.Context, height uint64) bool {
   183  	featureCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
   184  	if sc.stakingV1 == nil || featureCtx.UseV2Staking(height) {
   185  		return true
   186  	}
   187  	return false
   188  }