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  }