github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/contract_comm/election/election.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 package election 17 18 import ( 19 "math/big" 20 "sort" 21 "strings" 22 23 "github.com/ethereum/go-ethereum/accounts/abi" 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/contract_comm" 26 "github.com/ethereum/go-ethereum/core/types" 27 "github.com/ethereum/go-ethereum/core/vm" 28 "github.com/ethereum/go-ethereum/log" 29 "github.com/ethereum/go-ethereum/params" 30 ) 31 32 // This is taken from celo-monorepo/packages/protocol/build/<env>/contracts/Election.json 33 const electionABIString string = `[ 34 {"constant": true, 35 "inputs": [], 36 "name": "electValidatorSigners", 37 "outputs": [ 38 { 39 "name": "", 40 "type": "address[]" 41 } 42 ], 43 "payable": false, 44 "stateMutability": "view", 45 "type": "function" 46 }, 47 { 48 "constant": true, 49 "inputs": [], 50 "name": "getTotalVotesForEligibleValidatorGroups", 51 "outputs": [ 52 { 53 "name": "groups", 54 "type": "address[]" 55 }, 56 { 57 "name": "values", 58 "type": "uint256[]" 59 } 60 ], 61 "payable": false, 62 "stateMutability": "view", 63 "type": "function" 64 }, 65 { 66 "constant": false, 67 "inputs": [ 68 { 69 "name": "group", 70 "type": "address" 71 }, 72 { 73 "name": "value", 74 "type": "uint256" 75 }, 76 { 77 "name": "lesser", 78 "type": "address" 79 }, 80 { 81 "name": "greater", 82 "type": "address" 83 } 84 ], 85 "name": "distributeEpochRewards", 86 "outputs": [], 87 "payable": false, 88 "stateMutability": "nonpayable", 89 "type": "function" 90 }, 91 { 92 "constant": true, 93 "inputs": [ 94 { 95 "name": "group", 96 "type": "address" 97 }, 98 { 99 "name": "maxTotalRewards", 100 "type": "uint256" 101 }, 102 { 103 "name": "uptimes", 104 "type": "uint256[]" 105 } 106 ], 107 "name": "getGroupEpochRewards", 108 "outputs": [ 109 { 110 "name": "", 111 "type": "uint256" 112 } 113 ], 114 "payable": false, 115 "stateMutability": "view", 116 "type": "function" 117 } 118 ]` 119 120 var electionABI, _ = abi.JSON(strings.NewReader(electionABIString)) 121 122 func GetElectedValidators(header *types.Header, state vm.StateDB) ([]common.Address, error) { 123 var newValSet []common.Address 124 // Get the new epoch's validator set 125 _, err := contract_comm.MakeStaticCall(params.ElectionRegistryId, electionABI, "electValidatorSigners", []interface{}{}, &newValSet, params.MaxGasForElectValidators, header, state) 126 if err != nil { 127 return nil, err 128 } 129 return newValSet, nil 130 } 131 132 type voteTotal struct { 133 Group common.Address 134 Value *big.Int 135 } 136 137 func getTotalVotesForEligibleValidatorGroups(header *types.Header, state vm.StateDB) ([]voteTotal, error) { 138 var groups []common.Address 139 var values []*big.Int 140 _, err := contract_comm.MakeStaticCall(params.ElectionRegistryId, electionABI, "getTotalVotesForEligibleValidatorGroups", []interface{}{}, &[]interface{}{&groups, &values}, params.MaxGasForGetEligibleValidatorGroupsVoteTotals, header, state) 141 if err != nil { 142 return nil, err 143 } 144 145 voteTotals := make([]voteTotal, len(groups)) 146 for i, group := range groups { 147 log.Trace("Got group vote total", "group", group, "value", values[i]) 148 voteTotals[i].Group = group 149 voteTotals[i].Value = values[i] 150 } 151 return voteTotals, err 152 } 153 154 func getGroupEpochRewards(header *types.Header, state vm.StateDB, group common.Address, maxRewards *big.Int, uptimes []*big.Int) (*big.Int, error) { 155 var groupEpochRewards *big.Int 156 _, err := contract_comm.MakeStaticCall(params.ElectionRegistryId, electionABI, "getGroupEpochRewards", []interface{}{group, maxRewards, uptimes}, &groupEpochRewards, params.MaxGasForGetGroupEpochRewards, header, state) 157 if err != nil { 158 return nil, err 159 } 160 return groupEpochRewards, nil 161 } 162 163 func DistributeEpochRewards(header *types.Header, state vm.StateDB, groups []common.Address, maxTotalRewards *big.Int, uptimes map[common.Address][]*big.Int) (*big.Int, error) { 164 totalRewards := big.NewInt(0) 165 voteTotals, err := getTotalVotesForEligibleValidatorGroups(header, state) 166 if err != nil { 167 return totalRewards, err 168 } 169 170 rewards := make([]*big.Int, len(groups)) 171 for i, group := range groups { 172 reward, err := getGroupEpochRewards(header, state, group, maxTotalRewards, uptimes[group]) 173 if err != nil { 174 return totalRewards, err 175 } 176 rewards[i] = reward 177 log.Debug("Reward for group voters", "reward", reward, "group", group.String()) 178 } 179 180 for i, group := range groups { 181 reward := rewards[i] 182 for _, voteTotal := range voteTotals { 183 if voteTotal.Group == group { 184 voteTotal.Value.Add(voteTotal.Value, reward) 185 break 186 } 187 } 188 189 // Sorting in descending order is necessary to match the order on-chain. 190 // TODO: We could make this more efficient by only moving the newly vote member. 191 sort.SliceStable(voteTotals, func(j, k int) bool { 192 return voteTotals[j].Value.Cmp(voteTotals[k].Value) > 0 193 }) 194 195 lesser := common.ZeroAddress 196 greater := common.ZeroAddress 197 for j, voteTotal := range voteTotals { 198 if voteTotal.Group == group { 199 if j > 0 { 200 greater = voteTotals[j-1].Group 201 } 202 if j+1 < len(voteTotals) { 203 lesser = voteTotals[j+1].Group 204 } 205 break 206 } 207 } 208 _, err := contract_comm.MakeCall(params.ElectionRegistryId, electionABI, "distributeEpochRewards", []interface{}{group, reward, lesser, greater}, nil, params.MaxGasForDistributeEpochRewards, common.Big0, header, state, false) 209 if err != nil { 210 return totalRewards, err 211 } 212 totalRewards.Add(totalRewards, reward) 213 } 214 return totalRewards, nil 215 }