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 }