github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/poll/lifelong_protocol.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  	"go.uber.org/zap"
    15  
    16  	"github.com/iotexproject/iotex-core/action"
    17  	"github.com/iotexproject/iotex-core/action/protocol"
    18  	"github.com/iotexproject/iotex-core/action/protocol/rolldpos"
    19  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    20  	"github.com/iotexproject/iotex-core/crypto"
    21  	"github.com/iotexproject/iotex-core/pkg/log"
    22  	"github.com/iotexproject/iotex-core/state"
    23  )
    24  
    25  type lifeLongDelegatesProtocol struct {
    26  	delegates state.CandidateList
    27  	addr      address.Address
    28  }
    29  
    30  // NewLifeLongDelegatesProtocol creates a poll protocol with life long delegates
    31  func NewLifeLongDelegatesProtocol(delegates []genesis.Delegate) Protocol {
    32  	var l state.CandidateList
    33  	for _, delegate := range delegates {
    34  		rewardAddress := delegate.RewardAddr()
    35  		if rewardAddress == nil {
    36  			rewardAddress = delegate.OperatorAddr()
    37  		}
    38  		l = append(l, &state.Candidate{
    39  			Address: delegate.OperatorAddr().String(),
    40  			// TODO: load votes from genesis
    41  			Votes:         delegate.Votes(),
    42  			RewardAddress: rewardAddress.String(),
    43  		})
    44  	}
    45  	h := hash.Hash160b([]byte(_protocolID))
    46  	addr, err := address.FromBytes(h[:])
    47  	if err != nil {
    48  		log.L().Panic("Error when constructing the address of poll protocol", zap.Error(err))
    49  	}
    50  	return &lifeLongDelegatesProtocol{delegates: l, addr: addr}
    51  }
    52  
    53  func (p *lifeLongDelegatesProtocol) CreateGenesisStates(
    54  	ctx context.Context,
    55  	sm protocol.StateManager,
    56  ) (err error) {
    57  	blkCtx := protocol.MustGetBlockCtx(ctx)
    58  	if blkCtx.BlockHeight != 0 {
    59  		return errors.Errorf("Cannot create genesis state for height %d", blkCtx.BlockHeight)
    60  	}
    61  	log.L().Info("Creating genesis states for lifelong delegates protocol")
    62  	return setCandidates(ctx, sm, nil, p.delegates, uint64(1))
    63  }
    64  
    65  func (p *lifeLongDelegatesProtocol) Handle(ctx context.Context, act action.Action, sm protocol.StateManager) (*action.Receipt, error) {
    66  	if err := validate(ctx, sm, p, act); err != nil {
    67  		return nil, err
    68  	}
    69  	return handle(ctx, act, sm, nil, p.addr.String())
    70  }
    71  
    72  func (p *lifeLongDelegatesProtocol) Validate(ctx context.Context, act action.Action, sr protocol.StateReader) error {
    73  	return validate(ctx, sr, p, act)
    74  }
    75  
    76  func (p *lifeLongDelegatesProtocol) CalculateCandidatesByHeight(ctx context.Context, _ protocol.StateReader, _ uint64) (state.CandidateList, error) {
    77  	return p.delegates, nil
    78  }
    79  
    80  func (p *lifeLongDelegatesProtocol) Delegates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) {
    81  	return p.readActiveBlockProducers(ctx, sr, false)
    82  }
    83  
    84  func (p *lifeLongDelegatesProtocol) NextDelegates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) {
    85  	return p.readActiveBlockProducers(ctx, sr, true)
    86  }
    87  
    88  func (p *lifeLongDelegatesProtocol) Candidates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) {
    89  	return p.delegates, nil
    90  }
    91  
    92  func (p *lifeLongDelegatesProtocol) NextCandidates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) {
    93  	return p.delegates, nil
    94  }
    95  
    96  func (p *lifeLongDelegatesProtocol) CalculateUnproductiveDelegates(
    97  	ctx context.Context,
    98  	sr protocol.StateReader,
    99  ) ([]string, error) {
   100  	return nil, nil
   101  }
   102  
   103  func (p *lifeLongDelegatesProtocol) ReadState(
   104  	ctx context.Context,
   105  	sr protocol.StateReader,
   106  	method []byte,
   107  	args ...[]byte,
   108  ) ([]byte, uint64, error) {
   109  	height, err := sr.Height()
   110  	if err != nil {
   111  		return nil, uint64(0), err
   112  	}
   113  	switch string(method) {
   114  	case "CandidatesByEpoch":
   115  		fallthrough
   116  	case "BlockProducersByEpoch":
   117  		fallthrough
   118  	case "ActiveBlockProducersByEpoch":
   119  		bp, err := p.readBlockProducers()
   120  		return bp, height, err
   121  	case "GetGravityChainStartHeight":
   122  		if len(args) != 1 {
   123  			return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args))
   124  		}
   125  		return args[0], height, nil
   126  	default:
   127  		return nil, uint64(0), errors.New("corresponding method isn't found")
   128  	}
   129  }
   130  
   131  // Register registers the protocol with a unique ID
   132  func (p *lifeLongDelegatesProtocol) Register(r *protocol.Registry) error {
   133  	return r.Register(_protocolID, p)
   134  }
   135  
   136  // ForceRegister registers the protocol with a unique ID and force replacing the previous protocol if it exists
   137  func (p *lifeLongDelegatesProtocol) ForceRegister(r *protocol.Registry) error {
   138  	return r.ForceRegister(_protocolID, p)
   139  }
   140  
   141  // Name returns the name of protocol
   142  func (p *lifeLongDelegatesProtocol) Name() string {
   143  	return _protocolID
   144  }
   145  
   146  func (p *lifeLongDelegatesProtocol) readBlockProducers() ([]byte, error) {
   147  	return p.delegates.Serialize()
   148  }
   149  
   150  func (p *lifeLongDelegatesProtocol) readActiveBlockProducers(ctx context.Context, sr protocol.StateReader, readFromNext bool) (state.CandidateList, error) {
   151  	var blockProducerList []string
   152  	rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx))
   153  	blockProducerMap := make(map[string]*state.Candidate)
   154  	delegates := p.delegates
   155  	if len(p.delegates) > int(rp.NumCandidateDelegates()) {
   156  		delegates = p.delegates[:rp.NumCandidateDelegates()]
   157  	}
   158  	for _, bp := range delegates {
   159  		blockProducerList = append(blockProducerList, bp.Address)
   160  		blockProducerMap[bp.Address] = bp
   161  	}
   162  	targetHeight, err := sr.Height()
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	// make sure it's epochStartHeight
   167  	targetEpochStartHeight := rp.GetEpochHeight(rp.GetEpochNum(targetHeight))
   168  	if readFromNext {
   169  		targetEpochNum := rp.GetEpochNum(targetEpochStartHeight) + 1
   170  		targetEpochStartHeight = rp.GetEpochHeight(targetEpochNum) // next epoch start height
   171  	}
   172  	crypto.SortCandidates(blockProducerList, targetEpochStartHeight, crypto.CryptoSeed)
   173  	length := int(rp.NumDelegates())
   174  	if len(blockProducerList) < length {
   175  		// TODO: if the number of delegates is smaller than expected, should it return error or not?
   176  		length = len(blockProducerList)
   177  	}
   178  
   179  	var activeBlockProducers state.CandidateList
   180  	for i := 0; i < length; i++ {
   181  		activeBlockProducers = append(activeBlockProducers, blockProducerMap[blockProducerList[i]])
   182  	}
   183  	return activeBlockProducers, nil
   184  }