github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/rolldpos/epoch.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 rolldpos 7 8 import ( 9 "context" 10 "strconv" 11 12 "github.com/pkg/errors" 13 14 "github.com/iotexproject/iotex-address/address" 15 "github.com/iotexproject/iotex-core/action" 16 "github.com/iotexproject/iotex-core/action/protocol" 17 "github.com/iotexproject/iotex-core/pkg/log" 18 ) 19 20 const protocolID = "rolldpos" 21 22 // Protocol defines an epoch protocol 23 type Protocol struct { 24 numCandidateDelegates uint64 25 numDelegates uint64 26 numSubEpochs uint64 27 numSubEpochsDardanelles uint64 28 dardanellesHeight uint64 29 dardanellesOn bool 30 } 31 32 // FindProtocol return a registered protocol from registry 33 func FindProtocol(registry *protocol.Registry) *Protocol { 34 if registry == nil { 35 return nil 36 } 37 p, ok := registry.Find(protocolID) 38 if !ok { 39 return nil 40 } 41 rp, ok := p.(*Protocol) 42 if !ok { 43 log.S().Panic("fail to cast rolldpos protocol") 44 } 45 return rp 46 } 47 48 // MustGetProtocol return a registered protocol from registry 49 func MustGetProtocol(registry *protocol.Registry) *Protocol { 50 if registry == nil { 51 log.S().Panic("registry cannot be nil") 52 } 53 p, ok := registry.Find(protocolID) 54 if !ok { 55 log.S().Panic("rolldpos protocol is not registered") 56 } 57 rp, ok := p.(*Protocol) 58 if !ok { 59 log.S().Panic("fail to cast to rolldpos protocol") 60 } 61 return rp 62 } 63 64 // Option is optional setting for epoch protocol 65 type Option func(*Protocol) error 66 67 // EnableDardanellesSubEpoch will set give numSubEpochs at give height. 68 func EnableDardanellesSubEpoch(height, numSubEpochs uint64) Option { 69 return func(p *Protocol) error { 70 p.dardanellesOn = true 71 p.numSubEpochsDardanelles = numSubEpochs 72 p.dardanellesHeight = height 73 return nil 74 } 75 } 76 77 // NewProtocol returns a new rolldpos protocol 78 func NewProtocol(numCandidateDelegates, numDelegates, numSubEpochs uint64, opts ...Option) *Protocol { 79 if numCandidateDelegates < numDelegates { 80 numCandidateDelegates = numDelegates 81 } 82 p := &Protocol{ 83 numCandidateDelegates: numCandidateDelegates, 84 numDelegates: numDelegates, 85 numSubEpochs: numSubEpochs, 86 } 87 for _, opt := range opts { 88 if err := opt(p); err != nil { 89 log.S().Panicf("Failed to execute epoch protocol creation option %p: %v", opt, err) 90 } 91 } 92 return p 93 } 94 95 // ProtocolAddr returns the address generated from protocol id 96 func ProtocolAddr() address.Address { 97 return protocol.HashStringToAddress(protocolID) 98 } 99 100 // Handle handles a modification 101 func (p *Protocol) Handle(context.Context, action.Action, protocol.StateManager) (*action.Receipt, error) { 102 return nil, nil 103 } 104 105 // ReadState read the state on blockchain via protocol 106 func (p *Protocol) ReadState(ctx context.Context, sr protocol.StateReader, method []byte, args ...[]byte) ([]byte, uint64, error) { 107 tipHeight, err := sr.Height() 108 if err != nil { 109 return nil, uint64(0), err 110 } 111 switch string(method) { 112 case "NumCandidateDelegates": 113 return []byte(strconv.FormatUint(p.numCandidateDelegates, 10)), tipHeight, nil 114 case "NumDelegates": 115 return []byte(strconv.FormatUint(p.numDelegates, 10)), tipHeight, nil 116 case "NumSubEpochs": 117 if len(args) != 1 { 118 return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args)) 119 } 120 height, err := strconv.ParseUint(string(args[0]), 10, 64) 121 if err != nil { 122 return nil, uint64(0), err 123 } 124 numSubEpochs := p.NumSubEpochs(height) 125 return []byte(strconv.FormatUint(numSubEpochs, 10)), tipHeight, nil 126 case "EpochNumber": 127 if len(args) != 1 { 128 return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args)) 129 } 130 height, err := strconv.ParseUint(string(args[0]), 10, 64) 131 if err != nil { 132 return nil, uint64(0), err 133 } 134 epochNumber := p.GetEpochNum(height) 135 return []byte(strconv.FormatUint(epochNumber, 10)), tipHeight, nil 136 case "EpochHeight": 137 if len(args) != 1 { 138 return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args)) 139 } 140 epochNumber, err := strconv.ParseUint(string(args[0]), 10, 64) 141 if err != nil { 142 return nil, uint64(0), err 143 } 144 epochHeight := p.GetEpochHeight(epochNumber) 145 return []byte(strconv.FormatUint(epochHeight, 10)), tipHeight, nil 146 case "EpochLastHeight": 147 if len(args) != 1 { 148 return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args)) 149 } 150 epochNumber, err := strconv.ParseUint(string(args[0]), 10, 64) 151 if err != nil { 152 return nil, uint64(0), err 153 } 154 epochLastHeight := p.GetEpochLastBlockHeight(epochNumber) 155 return []byte(strconv.FormatUint(epochLastHeight, 10)), tipHeight, nil 156 case "SubEpochNumber": 157 if len(args) != 1 { 158 return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args)) 159 } 160 height, err := strconv.ParseUint(string(args[0]), 10, 64) 161 if err != nil { 162 return nil, uint64(0), err 163 } 164 subEpochNumber := p.GetSubEpochNum(height) 165 return []byte(strconv.FormatUint(subEpochNumber, 10)), tipHeight, nil 166 default: 167 return nil, tipHeight, errors.New("corresponding method isn't found") 168 } 169 } 170 171 // Register registers the protocol with a unique ID 172 func (p *Protocol) Register(r *protocol.Registry) error { 173 return r.Register(protocolID, p) 174 } 175 176 // ForceRegister registers the protocol with a unique ID and force replacing the previous protocol if it exists 177 func (p *Protocol) ForceRegister(r *protocol.Registry) error { 178 return r.ForceRegister(protocolID, p) 179 } 180 181 // Name returns the name of protocol 182 func (p *Protocol) Name() string { 183 return protocolID 184 } 185 186 // NumCandidateDelegates returns the number of delegate candidates for an epoch 187 func (p *Protocol) NumCandidateDelegates() uint64 { 188 return p.numCandidateDelegates 189 } 190 191 // NumDelegates returns the number of delegates in an epoch 192 func (p *Protocol) NumDelegates() uint64 { 193 return p.numDelegates 194 } 195 196 // NumSubEpochs returns the number of subEpochs given a block height 197 func (p *Protocol) NumSubEpochs(height uint64) uint64 { 198 if !p.dardanellesOn || height < p.dardanellesHeight { 199 return p.numSubEpochs 200 } 201 return p.numSubEpochsDardanelles 202 } 203 204 // GetEpochNum returns the number of the epoch for a given height 205 func (p *Protocol) GetEpochNum(height uint64) uint64 { 206 if height == 0 { 207 return 0 208 } 209 if !p.dardanellesOn || height <= p.dardanellesHeight { 210 return (height-1)/p.numDelegates/p.numSubEpochs + 1 211 } 212 dardanellesEpoch := p.GetEpochNum(p.dardanellesHeight) 213 dardanellesEpochHeight := p.GetEpochHeight(dardanellesEpoch) 214 return dardanellesEpoch + (height-dardanellesEpochHeight)/p.numDelegates/p.numSubEpochsDardanelles 215 } 216 217 // GetEpochHeight returns the start height of an epoch 218 func (p *Protocol) GetEpochHeight(epochNum uint64) uint64 { 219 if epochNum == 0 { 220 return 0 221 } 222 dardanellesEpoch := p.GetEpochNum(p.dardanellesHeight) 223 if !p.dardanellesOn || epochNum <= dardanellesEpoch { 224 return (epochNum-1)*p.numDelegates*p.numSubEpochs + 1 225 } 226 dardanellesEpochHeight := p.GetEpochHeight(dardanellesEpoch) 227 return dardanellesEpochHeight + (epochNum-dardanellesEpoch)*p.numDelegates*p.numSubEpochsDardanelles 228 } 229 230 // GetEpochLastBlockHeight returns the last height of an epoch 231 func (p *Protocol) GetEpochLastBlockHeight(epochNum uint64) uint64 { 232 return p.GetEpochHeight(epochNum+1) - 1 233 } 234 235 // GetSubEpochNum returns the sub epoch number of a block height 236 func (p *Protocol) GetSubEpochNum(height uint64) uint64 { 237 return (height - p.GetEpochHeight(p.GetEpochNum(height))) / p.numDelegates 238 } 239 240 // ProductivityByEpoch read the productivity in an epoch 241 func (p *Protocol) ProductivityByEpoch( 242 epochNum uint64, 243 tipHeight uint64, 244 productivity func(uint64, uint64) (map[string]uint64, error), 245 ) (uint64, map[string]uint64, error) { 246 if tipHeight == 0 { 247 return 0, map[string]uint64{}, nil 248 } 249 currentEpochNum := p.GetEpochNum(tipHeight) 250 if epochNum > currentEpochNum { 251 return 0, nil, errors.Errorf("epoch number %d is larger than current epoch number %d", epochNum, currentEpochNum) 252 } 253 epochStartHeight := p.GetEpochHeight(epochNum) 254 var epochEndHeight uint64 255 if epochNum == currentEpochNum { 256 epochEndHeight = tipHeight 257 } else { 258 epochEndHeight = p.GetEpochLastBlockHeight(epochNum) 259 } 260 produce, err := productivity(epochStartHeight, epochEndHeight) 261 return epochEndHeight - epochStartHeight + 1, produce, err 262 }