github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/poll/governance_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 "strconv" 11 "time" 12 13 "github.com/iotexproject/go-pkgs/hash" 14 "github.com/iotexproject/iotex-address/address" 15 "github.com/iotexproject/iotex-election/committee" 16 "github.com/iotexproject/iotex-election/db" 17 "github.com/pkg/errors" 18 "go.uber.org/zap" 19 20 "github.com/iotexproject/iotex-core/action" 21 "github.com/iotexproject/iotex-core/action/protocol" 22 "github.com/iotexproject/iotex-core/action/protocol/rolldpos" 23 "github.com/iotexproject/iotex-core/pkg/log" 24 "github.com/iotexproject/iotex-core/state" 25 ) 26 27 type governanceChainCommitteeProtocol struct { 28 getBlockTime GetBlockTime 29 electionCommittee committee.Committee 30 initGravityChainHeight uint64 31 addr address.Address 32 initialCandidatesInterval time.Duration 33 sh *Slasher 34 indexer *CandidateIndexer 35 } 36 37 // NewGovernanceChainCommitteeProtocol creates a Poll Protocol which fetch result from governance chain 38 func NewGovernanceChainCommitteeProtocol( 39 candidatesIndexer *CandidateIndexer, 40 electionCommittee committee.Committee, 41 initGravityChainHeight uint64, 42 getBlockTime GetBlockTime, 43 initialCandidatesInterval time.Duration, 44 sh *Slasher, 45 ) (Protocol, error) { 46 if electionCommittee == nil { 47 return nil, ErrNoElectionCommittee 48 } 49 if getBlockTime == nil { 50 return nil, errors.New("getBlockTime api is not provided") 51 } 52 53 h := hash.Hash160b([]byte(_protocolID)) 54 addr, err := address.FromBytes(h[:]) 55 if err != nil { 56 log.L().Panic("Error when constructing the address of poll protocol", zap.Error(err)) 57 } 58 59 return &governanceChainCommitteeProtocol{ 60 electionCommittee: electionCommittee, 61 initGravityChainHeight: initGravityChainHeight, 62 getBlockTime: getBlockTime, 63 addr: addr, 64 initialCandidatesInterval: initialCandidatesInterval, 65 sh: sh, 66 indexer: candidatesIndexer, 67 }, nil 68 } 69 70 func (p *governanceChainCommitteeProtocol) CreateGenesisStates( 71 ctx context.Context, 72 sm protocol.StateManager, 73 ) (err error) { 74 blkCtx := protocol.MustGetBlockCtx(ctx) 75 if blkCtx.BlockHeight != 0 { 76 return errors.Errorf("Cannot create genesis state for height %d", blkCtx.BlockHeight) 77 } 78 log.L().Info("Initialize poll protocol", zap.Uint64("height", p.initGravityChainHeight)) 79 if err = p.sh.CreateGenesisStates(ctx, sm, p.indexer); err != nil { 80 return 81 } 82 var ds state.CandidateList 83 for { 84 ds, err = p.candidatesByGravityChainHeight(p.initGravityChainHeight) 85 if err == nil || errors.Cause(err) != db.ErrNotExist { 86 break 87 } 88 log.L().Debug("calling committee,waiting for a while", zap.Int64("duration", int64(p.initialCandidatesInterval.Seconds())), zap.String("unit", " seconds")) 89 time.Sleep(p.initialCandidatesInterval) 90 } 91 if err != nil { 92 return 93 } 94 log.L().Info("Validating delegates from gravity chain", zap.Any("delegates", ds)) 95 if err = validateDelegates(ds); err != nil { 96 return 97 } 98 return setCandidates(ctx, sm, p.indexer, ds, uint64(1)) 99 } 100 101 func (p *governanceChainCommitteeProtocol) CreatePostSystemActions(ctx context.Context, sr protocol.StateReader) ([]action.Envelope, error) { 102 return createPostSystemActions(ctx, sr, p) 103 } 104 105 func (p *governanceChainCommitteeProtocol) CreatePreStates(ctx context.Context, sm protocol.StateManager) error { 106 return p.sh.CreatePreStates(ctx, sm, p.indexer) 107 } 108 109 func (p *governanceChainCommitteeProtocol) Handle(ctx context.Context, act action.Action, sm protocol.StateManager) (*action.Receipt, error) { 110 return handle(ctx, act, sm, p.indexer, p.addr.String()) 111 } 112 113 func (p *governanceChainCommitteeProtocol) Validate(ctx context.Context, act action.Action, sr protocol.StateReader) error { 114 return validate(ctx, sr, p, act) 115 } 116 117 func (p *governanceChainCommitteeProtocol) candidatesByGravityChainHeight(height uint64) (state.CandidateList, error) { 118 r, err := p.electionCommittee.ResultByHeight(height) 119 if err != nil { 120 return nil, err 121 } 122 l := state.CandidateList{} 123 for _, c := range r.Delegates() { 124 operatorAddress := string(c.OperatorAddress()) 125 if _, err := address.FromString(operatorAddress); err != nil { 126 log.L().Debug( 127 "candidate's operator address is invalid", 128 zap.String("operatorAddress", operatorAddress), 129 zap.String("name", string(c.Name())), 130 zap.Error(err), 131 ) 132 continue 133 } 134 rewardAddress := string(c.RewardAddress()) 135 if _, err := address.FromString(rewardAddress); err != nil { 136 log.L().Debug( 137 "candidate's reward address is invalid", 138 zap.String("name", string(c.Name())), 139 zap.String("rewardAddress", rewardAddress), 140 zap.Error(err), 141 ) 142 continue 143 } 144 l = append(l, &state.Candidate{ 145 Address: operatorAddress, 146 Votes: c.Score(), 147 RewardAddress: rewardAddress, 148 CanName: c.Name(), 149 }) 150 } 151 return l, nil 152 } 153 154 func (p *governanceChainCommitteeProtocol) CalculateCandidatesByHeight(ctx context.Context, _ protocol.StateReader, height uint64) (state.CandidateList, error) { 155 gravityHeight, err := p.getGravityHeight(ctx, height) 156 if err != nil { 157 return nil, errors.Wrap(err, "failed to get gravity chain height") 158 } 159 log.L().Debug( 160 "fetch delegates from gravity chain", 161 zap.Uint64("gravityChainHeight", gravityHeight), 162 ) 163 return p.candidatesByGravityChainHeight(gravityHeight) 164 } 165 166 func (p *governanceChainCommitteeProtocol) CalculateUnproductiveDelegates( 167 ctx context.Context, 168 sr protocol.StateReader, 169 ) ([]string, error) { 170 return p.sh.calculateUnproductiveDelegates(ctx, sr) 171 } 172 173 func (p *governanceChainCommitteeProtocol) Delegates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) { 174 delegates, _, err := p.sh.GetActiveBlockProducers(ctx, sr, false) 175 return delegates, err 176 } 177 178 func (p *governanceChainCommitteeProtocol) NextDelegates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) { 179 nextDelegates, _, err := p.sh.GetActiveBlockProducers(ctx, sr, true) 180 return nextDelegates, err 181 } 182 183 func (p *governanceChainCommitteeProtocol) Candidates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) { 184 candidates, _, err := p.sh.GetCandidates(ctx, sr, false) 185 return candidates, err 186 } 187 188 func (p *governanceChainCommitteeProtocol) NextCandidates(ctx context.Context, sr protocol.StateReader) (state.CandidateList, error) { 189 candidates, _, err := p.sh.GetCandidates(ctx, sr, true) 190 return candidates, err 191 } 192 193 func (p *governanceChainCommitteeProtocol) ReadState( 194 ctx context.Context, 195 sr protocol.StateReader, 196 method []byte, 197 args ...[]byte, 198 ) ([]byte, uint64, error) { 199 switch string(method) { 200 case "GetGravityChainStartHeight": 201 if len(args) != 1 { 202 return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args)) 203 } 204 nativeHeight, err := strconv.ParseUint(string(args[0]), 10, 64) 205 if err != nil { 206 return nil, uint64(0), err 207 } 208 gravityStartheight, err := p.getGravityHeight(ctx, nativeHeight) 209 if err != nil { 210 return nil, uint64(0), err 211 } 212 return []byte(strconv.FormatUint(gravityStartheight, 10)), nativeHeight, nil 213 default: 214 return p.sh.ReadState(ctx, sr, p.indexer, method, args...) 215 } 216 } 217 218 // Register registers the protocol with a unique ID 219 func (p *governanceChainCommitteeProtocol) Register(r *protocol.Registry) error { 220 return r.Register(_protocolID, p) 221 } 222 223 // ForceRegister registers the protocol with a unique ID and force replacing the previous protocol if it exists 224 func (p *governanceChainCommitteeProtocol) ForceRegister(r *protocol.Registry) error { 225 return r.ForceRegister(_protocolID, p) 226 } 227 228 // Name returns the name of protocol 229 func (p *governanceChainCommitteeProtocol) Name() string { 230 return _protocolID 231 } 232 233 func (p *governanceChainCommitteeProtocol) getGravityHeight(ctx context.Context, height uint64) (uint64, error) { 234 rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx)) 235 epochNumber := rp.GetEpochNum(height) 236 epochHeight := rp.GetEpochHeight(epochNumber) 237 blkTime, err := p.getBlockTime(epochHeight) 238 if err != nil { 239 return 0, err 240 } 241 log.L().Debug( 242 "get gravity chain height by time", 243 zap.Time("time", blkTime), 244 ) 245 return p.electionCommittee.HeightByTime(blkTime) 246 }