github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/backend/pos.go (about) 1 // Copyright 2017 The celo Authors 2 // This file is part of the celo library. 3 // 4 // The celo library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The celo library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the celo library. If not, see <http://www.gnu.org/licenses/>. 16 17 package backend 18 19 import ( 20 "errors" 21 "fmt" 22 "math/big" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/consensus/istanbul" 27 "github.com/ethereum/go-ethereum/contract_comm" 28 "github.com/ethereum/go-ethereum/contract_comm/currency" 29 "github.com/ethereum/go-ethereum/contract_comm/election" 30 "github.com/ethereum/go-ethereum/contract_comm/epoch_rewards" 31 "github.com/ethereum/go-ethereum/contract_comm/gold_token" 32 "github.com/ethereum/go-ethereum/contract_comm/validators" 33 "github.com/ethereum/go-ethereum/core" 34 "github.com/ethereum/go-ethereum/core/rawdb" 35 "github.com/ethereum/go-ethereum/core/state" 36 "github.com/ethereum/go-ethereum/core/types" 37 "github.com/ethereum/go-ethereum/log" 38 "github.com/ethereum/go-ethereum/params" 39 ) 40 41 func (sb *Backend) distributeEpochPaymentsAndRewards(header *types.Header, state *state.StateDB) error { 42 start := time.Now() 43 defer sb.rewardDistributionTimer.UpdateSince(start) 44 45 err := epoch_rewards.UpdateTargetVotingYield(header, state) 46 if err != nil { 47 return err 48 } 49 validatorEpochPayment, totalVoterRewards, err := epoch_rewards.CalculateTargetEpochPaymentAndRewards(header, state) 50 if err != nil { 51 return err 52 } 53 log.Debug("Calculated target epoch payment and rewards", "validatorEpochPayment", validatorEpochPayment, "totalVoterRewards", totalVoterRewards) 54 55 // The validator set that signs off on the last block of the epoch is the one that we need to 56 // iterate over. 57 valSet := sb.GetValidators(big.NewInt(header.Number.Int64()-1), header.ParentHash) 58 if len(valSet) == 0 { 59 err := errors.New("Unable to fetch validator set to update scores and distribute payments and rewards") 60 sb.logger.Error(err.Error()) 61 return err 62 } 63 64 uptimes, err := sb.updateValidatorScores(header, state, valSet) 65 if err != nil { 66 return err 67 } 68 69 totalEpochPayments, err := sb.distributeEpochPayments(header, state, valSet, validatorEpochPayment) 70 if err != nil { 71 return err 72 } 73 74 totalEpochRewards, err := sb.distributeEpochRewards(header, state, valSet, totalVoterRewards, uptimes) 75 if err != nil { 76 return err 77 } 78 79 stableTokenAddress, err := contract_comm.GetRegisteredAddress(params.StableTokenRegistryId, header, state) 80 if err != nil { 81 return err 82 } 83 totalEpochPaymentsConvertedToGold, err := currency.Convert(totalEpochPayments, stableTokenAddress, nil) 84 85 reserveAddress, err := contract_comm.GetRegisteredAddress(params.ReserveRegistryId, header, state) 86 if err != nil { 87 return err 88 } 89 if reserveAddress != nil { 90 state.AddBalance(*reserveAddress, totalEpochPaymentsConvertedToGold) 91 } else { 92 return errors.New("Unable to fetch reserve address for epoch rewards distribution") 93 } 94 95 return sb.increaseGoldTokenTotalSupply(header, state, big.NewInt(0).Add(totalEpochRewards, totalEpochPaymentsConvertedToGold)) 96 } 97 98 func (sb *Backend) updateValidatorScores(header *types.Header, state *state.StateDB, valSet []istanbul.Validator) ([]*big.Int, error) { 99 epoch := istanbul.GetEpochNumber(header.Number.Uint64(), sb.EpochSize()) 100 logger := sb.logger.New("func", "Backend.updateValidatorScores", "blocknum", header.Number.Uint64(), "epoch", epoch, "epochsize", sb.EpochSize(), "window", sb.LookbackWindow()) 101 logger.Trace("Updating validator scores") 102 103 // The denominator is the (last block - first block + 1) of the val score tally window 104 denominator := istanbul.GetValScoreTallyLastBlockNumber(epoch, sb.EpochSize()) - istanbul.GetValScoreTallyFirstBlockNumber(epoch, sb.EpochSize(), sb.LookbackWindow()) + 1 105 106 uptimes := make([]*big.Int, 0, len(valSet)) 107 for i, entry := range rawdb.ReadAccumulatedEpochUptime(sb.db, epoch).Entries { 108 if i >= len(valSet) { 109 break 110 } 111 val_logger := logger.New("scoreTally", entry.ScoreTally, "denominator", denominator, "index", i, "address", valSet[i].Address()) 112 113 if entry.ScoreTally > denominator { 114 val_logger.Error("ScoreTally exceeds max possible") 115 uptimes = append(uptimes, params.Fixidity1) 116 continue 117 } 118 119 numerator := big.NewInt(0).Mul(big.NewInt(int64(entry.ScoreTally)), params.Fixidity1) 120 uptimes = append(uptimes, big.NewInt(0).Div(numerator, big.NewInt(int64(denominator)))) 121 } 122 123 if len(uptimes) < len(valSet) { 124 err := fmt.Errorf("%d accumulated uptimes found, cannot update validator scores", len(uptimes)) 125 logger.Error(err.Error()) 126 return nil, err 127 } 128 129 for i, val := range valSet { 130 val_logger := logger.New("uptime", uptimes[i], "address", val.Address()) 131 val_logger.Trace("Updating validator score") 132 err := validators.UpdateValidatorScore(header, state, val.Address(), uptimes[i]) 133 if err != nil { 134 return nil, err 135 } 136 } 137 return uptimes, nil 138 } 139 140 func (sb *Backend) distributeEpochPayments(header *types.Header, state *state.StateDB, valSet []istanbul.Validator, maxPayment *big.Int) (*big.Int, error) { 141 totalEpochPayments := big.NewInt(0) 142 for _, val := range valSet { 143 sb.logger.Debug("Distributing epoch payment for address", "address", val.Address()) 144 epochPayment, err := validators.DistributeEpochPayment(header, state, val.Address(), maxPayment) 145 if err != nil { 146 return totalEpochPayments, nil 147 } 148 totalEpochPayments.Add(totalEpochPayments, epochPayment) 149 } 150 return totalEpochPayments, nil 151 } 152 153 func (sb *Backend) distributeEpochRewards(header *types.Header, state *state.StateDB, valSet []istanbul.Validator, maxTotalRewards *big.Int, uptimes []*big.Int) (*big.Int, error) { 154 totalEpochRewards := big.NewInt(0) 155 156 // Fixed epoch reward to the community fund. 157 // TODO(asa): This should be a fraction of the overall reward to stakers. 158 communityEpochReward := big.NewInt(params.Ether) 159 governanceAddress, err := contract_comm.GetRegisteredAddress(params.GovernanceRegistryId, header, state) 160 if err != nil { 161 return totalEpochRewards, err 162 } 163 164 if governanceAddress != nil { 165 state.AddBalance(*governanceAddress, communityEpochReward) 166 totalEpochRewards.Add(totalEpochRewards, communityEpochReward) 167 } 168 169 // Select groups that elected at least one validator aggregate their uptimes. 170 var groups []common.Address 171 groupUptimes := make(map[common.Address][]*big.Int) 172 groupElectedValidator := make(map[common.Address]bool) 173 for i, val := range valSet { 174 group, err := validators.GetMembershipInLastEpoch(header, state, val.Address()) 175 if err != nil { 176 return totalEpochRewards, err 177 } 178 if _, ok := groupElectedValidator[group]; !ok { 179 groups = append(groups, group) 180 sb.logger.Debug("Group elected validator", "group", group.String()) 181 } 182 groupElectedValidator[group] = true 183 groupUptimes[group] = append(groupUptimes[group], uptimes[i]) 184 } 185 186 electionRewards, err := election.DistributeEpochRewards(header, state, groups, maxTotalRewards, groupUptimes) 187 if err != nil { 188 return totalEpochRewards, err 189 } 190 lockedGoldAddress, err := contract_comm.GetRegisteredAddress(params.LockedGoldRegistryId, header, state) 191 if err != nil { 192 return totalEpochRewards, err 193 } 194 if lockedGoldAddress != nil { 195 state.AddBalance(*lockedGoldAddress, electionRewards) 196 totalEpochRewards.Add(totalEpochRewards, electionRewards) 197 } else { 198 return totalEpochRewards, errors.New("Unable to fetch locked gold address for epoch rewards distribution") 199 } 200 return totalEpochRewards, err 201 } 202 203 func (sb *Backend) setInitialGoldTokenTotalSupplyIfUnset(header *types.Header, state *state.StateDB) error { 204 totalSupply, err := gold_token.GetTotalSupply(header, state) 205 if err != nil { 206 return err 207 } 208 // totalSupply not yet initialized. 209 if totalSupply.Cmp(common.Big0) == 0 { 210 data, err := sb.db.Get(core.DBGenesisSupplyKey) 211 if err != nil { 212 log.Error("Unable to fetch genesisSupply from db", "err", err) 213 return err 214 } 215 genesisSupply := new(big.Int) 216 genesisSupply.SetBytes(data) 217 218 err = sb.increaseGoldTokenTotalSupply(header, state, genesisSupply) 219 if err != nil { 220 return err 221 } 222 } 223 return nil 224 } 225 226 func (sb *Backend) increaseGoldTokenTotalSupply(header *types.Header, state *state.StateDB, increase *big.Int) error { 227 if increase.Cmp(common.Big0) > 0 { 228 return gold_token.IncreaseSupply(header, state, increase) 229 } 230 return nil 231 }