github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/poll/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 "math/big" 11 "time" 12 13 "github.com/iotexproject/iotex-address/address" 14 "github.com/iotexproject/iotex-election/committee" 15 "github.com/pkg/errors" 16 17 "github.com/iotexproject/iotex-core/action/protocol" 18 "github.com/iotexproject/iotex-core/action/protocol/execution/evm" 19 "github.com/iotexproject/iotex-core/action/protocol/staking" 20 "github.com/iotexproject/iotex-core/action/protocol/vote" 21 "github.com/iotexproject/iotex-core/blockchain" 22 "github.com/iotexproject/iotex-core/blockchain/genesis" 23 "github.com/iotexproject/iotex-core/pkg/log" 24 "github.com/iotexproject/iotex-core/state" 25 ) 26 27 const ( 28 _protocolID = "poll" 29 _rollDPoSScheme = "ROLLDPOS" 30 ) 31 32 const ( 33 _modeLifeLong = "lifeLong" 34 _modeGovernanceMix = "governanceMix" // mix governance with native staking contract 35 _modeNative = "native" // only use go naitve staking 36 _modeNativeMix = "nativeMix" // native with backward compatibility for governanceMix before fairbank 37 _modeConsortium = "consortium" 38 39 _blockMetaPrefix = "BlockMeta." 40 ) 41 42 // ErrInconsistentHeight is an error that result of "readFromStateDB" is not consistent with others 43 var ErrInconsistentHeight = errors.New("data is inconsistent because the state height has been changed") 44 45 // ErrNoElectionCommittee is an error that the election committee is not specified 46 var ErrNoElectionCommittee = errors.New("no election committee specified") 47 48 // ErrProposedDelegatesLength is an error that the proposed delegate list length is not right 49 var ErrProposedDelegatesLength = errors.New("the proposed delegate list length") 50 51 // ErrDelegatesNotAsExpected is an error that the delegates are not as expected 52 var ErrDelegatesNotAsExpected = errors.New("delegates are not as expected") 53 54 // ErrDelegatesNotExist is an error that the delegates cannot be prepared 55 var ErrDelegatesNotExist = errors.New("delegates cannot be found") 56 57 type ( 58 // GetCandidates returns the current candidates 59 GetCandidates func(protocol.StateReader, uint64, bool, bool) ([]*state.Candidate, uint64, error) 60 61 // GetProbationList returns current the ProbationList 62 GetProbationList func(protocol.StateReader, bool) (*vote.ProbationList, uint64, error) 63 64 // GetUnproductiveDelegate returns unproductiveDelegate struct which contains a cache of upd info by epochs 65 GetUnproductiveDelegate func(protocol.StateReader) (*vote.UnproductiveDelegate, error) 66 67 // GetBlockTime defines a function to get block creation time 68 GetBlockTime func(uint64) (time.Time, error) 69 70 // Productivity returns the number of produced blocks per producer 71 Productivity func(uint64, uint64) (map[string]uint64, error) 72 73 // Protocol defines the protocol of handling votes 74 Protocol interface { 75 protocol.Protocol 76 protocol.ActionValidator 77 protocol.GenesisStateCreator 78 Delegates(context.Context, protocol.StateReader) (state.CandidateList, error) 79 NextDelegates(context.Context, protocol.StateReader) (state.CandidateList, error) 80 Candidates(context.Context, protocol.StateReader) (state.CandidateList, error) 81 NextCandidates(context.Context, protocol.StateReader) (state.CandidateList, error) 82 // CalculateCandidatesByHeight calculates candidate and returns candidates by chain height 83 // TODO: remove height, and read it from state reader 84 CalculateCandidatesByHeight(context.Context, protocol.StateReader, uint64) (state.CandidateList, error) 85 // CalculateUnproductiveDelegates calculates unproductive delegate on current epoch 86 CalculateUnproductiveDelegates(context.Context, protocol.StateReader) ([]string, error) 87 } 88 ) 89 90 // FindProtocol finds the registered protocol from registry 91 func FindProtocol(registry *protocol.Registry) Protocol { 92 if registry == nil { 93 return nil 94 } 95 p, ok := registry.Find(_protocolID) 96 if !ok { 97 return nil 98 } 99 pp, ok := p.(Protocol) 100 if !ok { 101 log.S().Panic("fail to cast poll protocol") 102 } 103 return pp 104 } 105 106 // MustGetProtocol return a registered protocol from registry 107 func MustGetProtocol(registry *protocol.Registry) Protocol { 108 if registry == nil { 109 log.S().Panic("registry cannot be nil") 110 } 111 p, ok := registry.Find(_protocolID) 112 if !ok { 113 log.S().Panic("poll protocol is not registered") 114 } 115 116 pp, ok := p.(Protocol) 117 if !ok { 118 log.S().Panic("fail to cast poll protocol") 119 } 120 121 return pp 122 } 123 124 // NewProtocol instantiates a rewarding protocol instance. 125 func NewProtocol( 126 scheme string, 127 chainConfig blockchain.Config, 128 genesisConfig genesis.Genesis, 129 candidateIndexer *CandidateIndexer, 130 readContract ReadContract, 131 getCandidates GetCandidates, 132 getprobationList GetProbationList, 133 getUnproductiveDelegate GetUnproductiveDelegate, 134 electionCommittee committee.Committee, 135 stakingProto *staking.Protocol, 136 getBlockTimeFunc GetBlockTime, 137 productivity Productivity, 138 getBlockHash evm.GetBlockHash, 139 getBlockTime evm.GetBlockTime, 140 ) (Protocol, error) { 141 if scheme != _rollDPoSScheme { 142 return nil, nil 143 } 144 145 var ( 146 slasher *Slasher 147 scoreThreshold *big.Int 148 ) 149 switch genesisConfig.PollMode { 150 case _modeGovernanceMix, _modeNative, _modeNativeMix: 151 var ( 152 err error 153 ok bool 154 ) 155 slasher, err = NewSlasher( 156 productivity, 157 getCandidates, 158 getprobationList, 159 getUnproductiveDelegate, 160 candidateIndexer, 161 genesisConfig.NumCandidateDelegates, 162 genesisConfig.NumDelegates, 163 genesisConfig.DardanellesNumSubEpochs, 164 genesisConfig.ProductivityThreshold, 165 genesisConfig.ProbationEpochPeriod, 166 genesisConfig.UnproductiveDelegateMaxCacheSize, 167 genesisConfig.ProbationIntensityRate) 168 if err != nil { 169 return nil, err 170 } 171 scoreThreshold, ok = new(big.Int).SetString(genesisConfig.ScoreThreshold, 10) 172 if !ok { 173 return nil, errors.Errorf("failed to parse score threshold %s", genesisConfig.ScoreThreshold) 174 } 175 } 176 177 var stakingV1 Protocol 178 switch genesisConfig.PollMode { 179 case _modeGovernanceMix, _modeNativeMix: 180 if !genesisConfig.EnableGravityChainVoting || electionCommittee == nil { 181 return nil, errors.New("gravity chain voting is not enabled") 182 } 183 governance, err := NewGovernanceChainCommitteeProtocol( 184 candidateIndexer, 185 electionCommittee, 186 genesisConfig.GravityChainStartHeight, 187 getBlockTimeFunc, 188 chainConfig.PollInitialCandidatesInterval, 189 slasher, 190 ) 191 if err != nil { 192 return nil, err 193 } 194 stakingV1, err = NewStakingCommittee( 195 electionCommittee, 196 governance, 197 readContract, 198 genesisConfig.NativeStakingContractAddress, 199 genesisConfig.NativeStakingContractCode, 200 scoreThreshold, 201 ) 202 if err != nil { 203 return nil, err 204 } 205 } 206 207 switch genesisConfig.PollMode { 208 case _modeLifeLong: 209 delegates := genesisConfig.Delegates 210 if uint64(len(delegates)) < genesisConfig.NumDelegates { 211 return nil, errors.New("invalid delegate address in genesis block") 212 } 213 return NewLifeLongDelegatesProtocol(delegates), nil 214 case _modeGovernanceMix: 215 return stakingV1, nil 216 case _modeNativeMix, _modeNative: 217 stakingV2, err := newNativeStakingV2(candidateIndexer, slasher, scoreThreshold, stakingProto) 218 if err != nil { 219 return nil, err 220 } 221 return NewStakingCommand(stakingV1, stakingV2) 222 case _modeConsortium: 223 return NewConsortiumCommittee(candidateIndexer, readContract, getBlockHash) 224 default: 225 return nil, errors.Errorf("unsupported poll mode %s", genesisConfig.PollMode) 226 } 227 } 228 229 // ProtocolAddr returns the address generated from protocol id 230 func ProtocolAddr() address.Address { 231 return protocol.HashStringToAddress(_protocolID) 232 }