github.com/klaytn/klaytn@v1.12.1/governance/api.go (about)

     1  // Copyright 2019 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn 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 klaytn 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 klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package governance
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"math/big"
    23  	"runtime"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  
    28  	"github.com/klaytn/klaytn/common"
    29  	"github.com/klaytn/klaytn/networks/rpc"
    30  	"github.com/klaytn/klaytn/params"
    31  	"github.com/klaytn/klaytn/reward"
    32  )
    33  
    34  type GovernanceAPI struct {
    35  	governance Engine // Node interfaced by this API
    36  }
    37  
    38  type returnTally struct {
    39  	Key                string
    40  	Value              interface{}
    41  	ApprovalPercentage float64
    42  }
    43  
    44  func NewGovernanceAPI(gov Engine) *GovernanceAPI {
    45  	return &GovernanceAPI{governance: gov}
    46  }
    47  
    48  type GovernanceKlayAPI struct {
    49  	governance Engine
    50  	chain      blockChain
    51  }
    52  
    53  func NewGovernanceKlayAPI(gov Engine, chain blockChain) *GovernanceKlayAPI {
    54  	return &GovernanceKlayAPI{governance: gov, chain: chain}
    55  }
    56  
    57  var (
    58  	errUnknownBlock           = errors.New("Unknown block")
    59  	errNotAvailableInThisMode = errors.New("In current governance mode, voting power is not available")
    60  	errSetDefaultFailure      = errors.New("Failed to set a default value")
    61  	errPermissionDenied       = errors.New("You don't have the right to vote")
    62  	errRemoveSelf             = errors.New("You can't vote on removing yourself")
    63  	errInvalidKeyValue        = errors.New("Your vote couldn't be placed. Please check your vote's key and value")
    64  	errInvalidLowerBound      = errors.New("lowerboundbasefee cannot be set exceeding upperboundbasefee")
    65  	errInvalidUpperBound      = errors.New("upperboundbasefee cannot be set lower than lowerboundbasefee")
    66  )
    67  
    68  func (api *GovernanceKlayAPI) GetChainConfig(num *rpc.BlockNumber) *params.ChainConfig {
    69  	return getChainConfig(api.governance, num)
    70  }
    71  
    72  func (api *GovernanceKlayAPI) GetStakingInfo(num *rpc.BlockNumber) (*reward.StakingInfo, error) {
    73  	return getStakingInfo(api.governance, num)
    74  }
    75  
    76  func (api *GovernanceKlayAPI) GetParams(num *rpc.BlockNumber) (map[string]interface{}, error) {
    77  	return getParams(api.governance, num)
    78  }
    79  
    80  func (api *GovernanceKlayAPI) NodeAddress() common.Address {
    81  	return api.governance.NodeAddress()
    82  }
    83  
    84  // GetRewards returns detailed information of the block reward at a given block number.
    85  func (api *GovernanceKlayAPI) GetRewards(num *rpc.BlockNumber) (*reward.RewardSpec, error) {
    86  	blockNumber := uint64(0)
    87  	if num == nil || *num == rpc.LatestBlockNumber {
    88  		blockNumber = api.chain.CurrentBlock().NumberU64()
    89  	} else {
    90  		blockNumber = uint64(num.Int64())
    91  	}
    92  
    93  	header := api.chain.GetHeaderByNumber(blockNumber)
    94  	if header == nil {
    95  		return nil, fmt.Errorf("the block does not exist (block number: %d)", blockNumber)
    96  	}
    97  
    98  	rules := api.chain.Config().Rules(new(big.Int).SetUint64(blockNumber))
    99  	pset, err := api.governance.EffectiveParams(blockNumber)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	rewardParamNum := reward.CalcRewardParamBlock(header.Number.Uint64(), pset.Epoch(), rules)
   104  	rewardParamSet, err := api.governance.EffectiveParams(rewardParamNum)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	return reward.GetBlockReward(header, rules, rewardParamSet)
   110  }
   111  
   112  type AccumulatedRewards struct {
   113  	FirstBlockTime string   `json:"firstBlockTime"`
   114  	LastBlockTime  string   `json:"lastBlockTime"`
   115  	FirstBlock     *big.Int `json:"firstBlock"`
   116  	LastBlock      *big.Int `json:"lastBlock"`
   117  
   118  	// TotalMinted + TotalTxFee - TotalBurntTxFee = TotalProposerRewards + TotalStakingRewards + TotalKFFRewards + TotalKCFRewards
   119  	TotalMinted          *big.Int                    `json:"totalMinted"`
   120  	TotalTxFee           *big.Int                    `json:"totalTxFee"`
   121  	TotalBurntTxFee      *big.Int                    `json:"totalBurntTxFee"`
   122  	TotalProposerRewards *big.Int                    `json:"totalProposerRewards"`
   123  	TotalStakingRewards  *big.Int                    `json:"totalStakingRewards"`
   124  	TotalKFFRewards      *big.Int                    `json:"totalKFFRewards"`
   125  	TotalKCFRewards      *big.Int                    `json:"totalKCFRewards"`
   126  	Rewards              map[common.Address]*big.Int `json:"rewards"`
   127  }
   128  
   129  // GetRewardsAccumulated returns accumulated rewards data in the block range of [first, last].
   130  func (api *GovernanceAPI) GetRewardsAccumulated(first rpc.BlockNumber, last rpc.BlockNumber) (*AccumulatedRewards, error) {
   131  	blockchain := api.governance.BlockChain()
   132  	govKlayAPI := NewGovernanceKlayAPI(api.governance, blockchain)
   133  
   134  	currentBlock := blockchain.CurrentBlock().NumberU64()
   135  
   136  	firstBlock := currentBlock
   137  	if first >= rpc.EarliestBlockNumber {
   138  		firstBlock = uint64(first.Int64())
   139  	}
   140  
   141  	lastBlock := currentBlock
   142  	if last >= rpc.EarliestBlockNumber {
   143  		lastBlock = uint64(last.Int64())
   144  	}
   145  
   146  	if firstBlock > lastBlock {
   147  		return nil, errors.New("the last block number should be equal or larger the first block number")
   148  	}
   149  
   150  	if lastBlock > currentBlock {
   151  		return nil, errors.New("the last block number should be equal or less than the current block number")
   152  	}
   153  
   154  	blockCount := lastBlock - firstBlock + 1
   155  	if blockCount > 604800 { // 7 days. naive resource protection
   156  		return nil, errors.New("block range should be equal or less than 604800")
   157  	}
   158  
   159  	// initialize structures before request a job
   160  	accumRewards := &AccumulatedRewards{}
   161  	blockRewards := reward.NewRewardSpec()
   162  	mu := sync.Mutex{} // protect blockRewards
   163  
   164  	numWorkers := runtime.NumCPU()
   165  	reqCh := make(chan uint64, numWorkers)
   166  	errCh := make(chan error, numWorkers+1)
   167  	wg := sync.WaitGroup{}
   168  
   169  	// introduce the worker pattern to prevent resource exhaustion
   170  	for i := 0; i < numWorkers; i++ {
   171  		go func() {
   172  			// the minimum digit of request is period to avoid current access to an accArray item
   173  			for num := range reqCh {
   174  				bn := rpc.BlockNumber(num)
   175  				blockReward, err := govKlayAPI.GetRewards(&bn)
   176  				if err != nil {
   177  					errCh <- err
   178  					wg.Done()
   179  					return
   180  				}
   181  
   182  				mu.Lock()
   183  				blockRewards.Add(blockReward)
   184  				mu.Unlock()
   185  
   186  				wg.Done()
   187  			}
   188  		}()
   189  	}
   190  
   191  	// write the information of the first block
   192  	header := blockchain.GetHeaderByNumber(firstBlock)
   193  	if header == nil {
   194  		return nil, fmt.Errorf("the block does not exist (block number: %d)", firstBlock)
   195  	}
   196  	accumRewards.FirstBlock = header.Number
   197  	accumRewards.FirstBlockTime = time.Unix(header.Time.Int64(), 0).String()
   198  
   199  	// write the information of the last block
   200  	header = blockchain.GetHeaderByNumber(lastBlock)
   201  	if header == nil {
   202  		return nil, fmt.Errorf("the block does not exist (block number: %d)", lastBlock)
   203  	}
   204  	accumRewards.LastBlock = header.Number
   205  	accumRewards.LastBlockTime = time.Unix(header.Time.Int64(), 0).String()
   206  
   207  	for num := firstBlock; num <= lastBlock; num++ {
   208  		wg.Add(1)
   209  		reqCh <- num
   210  	}
   211  
   212  	// generate a goroutine to return error early
   213  	go func() {
   214  		wg.Wait()
   215  		errCh <- nil
   216  	}()
   217  
   218  	if err := <-errCh; err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	// collect the accumulated rewards information
   223  	accumRewards.Rewards = blockRewards.Rewards
   224  	accumRewards.TotalMinted = blockRewards.Minted
   225  	accumRewards.TotalTxFee = blockRewards.TotalFee
   226  	accumRewards.TotalBurntTxFee = blockRewards.BurntFee
   227  	accumRewards.TotalProposerRewards = blockRewards.Proposer
   228  	accumRewards.TotalStakingRewards = blockRewards.Stakers
   229  	accumRewards.TotalKFFRewards = blockRewards.KFF
   230  	accumRewards.TotalKCFRewards = blockRewards.KCF
   231  
   232  	return accumRewards, nil
   233  }
   234  
   235  // Vote injects a new vote for governance targets such as unitprice and governingnode.
   236  func (api *GovernanceAPI) Vote(key string, val interface{}) (string, error) {
   237  	blockNumber := api.governance.BlockChain().CurrentBlock().NumberU64()
   238  	pset, err := api.governance.EffectiveParams(blockNumber + 1)
   239  	if err != nil {
   240  		return "", err
   241  	}
   242  	gMode := pset.GovernanceModeInt()
   243  	gNode := pset.GoverningNode()
   244  
   245  	if gMode == params.GovernanceMode_Single && gNode != api.governance.NodeAddress() {
   246  		return "", errPermissionDenied
   247  	}
   248  	vote, ok := api.governance.ValidateVote(&GovernanceVote{Key: strings.ToLower(key), Value: val})
   249  	if !ok {
   250  		return "", errInvalidKeyValue
   251  	}
   252  	if vote.Key == "governance.removevalidator" {
   253  		if api.isRemovingSelf(val.(string)) {
   254  			return "", errRemoveSelf
   255  		}
   256  	}
   257  	if vote.Key == "kip71.lowerboundbasefee" {
   258  		if vote.Value.(uint64) > pset.UpperBoundBaseFee() {
   259  			return "", errInvalidLowerBound
   260  		}
   261  	}
   262  	if vote.Key == "kip71.upperboundbasefee" {
   263  		if vote.Value.(uint64) < pset.LowerBoundBaseFee() {
   264  			return "", errInvalidUpperBound
   265  		}
   266  	}
   267  	if api.governance.AddVote(key, val) {
   268  		return "Your vote is prepared. It will be put into the block header or applied when your node generates a block as a proposer. Note that your vote may be duplicate.", nil
   269  	}
   270  	return "", errInvalidKeyValue
   271  }
   272  
   273  func (api *GovernanceAPI) isRemovingSelf(val string) bool {
   274  	for _, str := range strings.Split(val, ",") {
   275  		str = strings.Trim(str, " ")
   276  		if common.HexToAddress(str) == api.governance.NodeAddress() {
   277  			return true
   278  		}
   279  	}
   280  	return false
   281  }
   282  
   283  func (api *GovernanceAPI) ShowTally() []*returnTally {
   284  	ret := []*returnTally{}
   285  
   286  	for _, val := range api.governance.GetGovernanceTalliesCopy() {
   287  		item := &returnTally{
   288  			Key:                val.Key,
   289  			Value:              val.Value,
   290  			ApprovalPercentage: float64(val.Votes) / float64(api.governance.TotalVotingPower()) * 100,
   291  		}
   292  		ret = append(ret, item)
   293  	}
   294  
   295  	return ret
   296  }
   297  
   298  func (api *GovernanceAPI) TotalVotingPower() (float64, error) {
   299  	if !api.isGovernanceModeBallot() {
   300  		return 0, errNotAvailableInThisMode
   301  	}
   302  	return float64(api.governance.TotalVotingPower()) / 1000.0, nil
   303  }
   304  
   305  func (api *GovernanceAPI) GetParams(num *rpc.BlockNumber) (map[string]interface{}, error) {
   306  	return getParams(api.governance, num)
   307  }
   308  
   309  func getParams(governance Engine, num *rpc.BlockNumber) (map[string]interface{}, error) {
   310  	blockNumber := uint64(0)
   311  	if num == nil || *num == rpc.LatestBlockNumber || *num == rpc.PendingBlockNumber {
   312  		blockNumber = governance.BlockChain().CurrentBlock().NumberU64()
   313  	} else {
   314  		blockNumber = uint64(num.Int64())
   315  	}
   316  
   317  	pset, err := governance.EffectiveParams(blockNumber)
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  	return pset.StrMap(), nil
   322  }
   323  
   324  func (api *GovernanceAPI) GetStakingInfo(num *rpc.BlockNumber) (*reward.StakingInfo, error) {
   325  	return getStakingInfo(api.governance, num)
   326  }
   327  
   328  func getStakingInfo(governance Engine, num *rpc.BlockNumber) (*reward.StakingInfo, error) {
   329  	blockNumber := uint64(0)
   330  	if num == nil || *num == rpc.LatestBlockNumber || *num == rpc.PendingBlockNumber {
   331  		blockNumber = governance.BlockChain().CurrentBlock().NumberU64()
   332  	} else {
   333  		blockNumber = uint64(num.Int64())
   334  	}
   335  	return reward.GetStakingInfo(blockNumber), nil
   336  }
   337  
   338  func (api *GovernanceAPI) PendingChanges() map[string]interface{} {
   339  	return api.governance.PendingChanges()
   340  }
   341  
   342  func (api *GovernanceAPI) Votes() []GovernanceVote {
   343  	return api.governance.Votes()
   344  }
   345  
   346  func (api *GovernanceAPI) IdxCache() []uint64 {
   347  	return api.governance.IdxCache()
   348  }
   349  
   350  func (api *GovernanceAPI) IdxCacheFromDb() []uint64 {
   351  	return api.governance.IdxCacheFromDb()
   352  }
   353  
   354  // TODO-Klaytn: Return error if invalid input is given such as pending or a too big number
   355  func (api *GovernanceAPI) ItemCacheFromDb(num *rpc.BlockNumber) map[string]interface{} {
   356  	blockNumber := uint64(0)
   357  	if num == nil || *num == rpc.LatestBlockNumber || *num == rpc.PendingBlockNumber {
   358  		blockNumber = api.governance.BlockChain().CurrentBlock().NumberU64()
   359  	} else {
   360  		blockNumber = uint64(num.Int64())
   361  	}
   362  	ret, _ := api.governance.DB().ReadGovernance(blockNumber)
   363  	return ret
   364  }
   365  
   366  type VoteList struct {
   367  	Key      string
   368  	Value    interface{}
   369  	Casted   bool
   370  	BlockNum uint64
   371  }
   372  
   373  func (api *GovernanceAPI) MyVotes() []*VoteList {
   374  	ret := []*VoteList{}
   375  
   376  	for k, v := range api.governance.GetVoteMapCopy() {
   377  		item := &VoteList{
   378  			Key:      k,
   379  			Value:    v.Value,
   380  			Casted:   v.Casted,
   381  			BlockNum: v.Num,
   382  		}
   383  		ret = append(ret, item)
   384  	}
   385  
   386  	return ret
   387  }
   388  
   389  func (api *GovernanceAPI) MyVotingPower() (float64, error) {
   390  	if !api.isGovernanceModeBallot() {
   391  		return 0, errNotAvailableInThisMode
   392  	}
   393  	return float64(api.governance.MyVotingPower()) / 1000.0, nil
   394  }
   395  
   396  func (api *GovernanceAPI) GetChainConfig(num *rpc.BlockNumber) *params.ChainConfig {
   397  	return getChainConfig(api.governance, num)
   398  }
   399  
   400  func getChainConfig(governance Engine, num *rpc.BlockNumber) *params.ChainConfig {
   401  	var blocknum uint64
   402  	if num == nil || *num == rpc.LatestBlockNumber || *num == rpc.PendingBlockNumber {
   403  		blocknum = governance.BlockChain().CurrentBlock().NumberU64()
   404  	} else {
   405  		blocknum = num.Uint64()
   406  	}
   407  
   408  	pset, err := governance.EffectiveParams(blocknum)
   409  	if err != nil {
   410  		return nil
   411  	}
   412  
   413  	latestConfig := governance.BlockChain().Config()
   414  	config := pset.ToChainConfig()
   415  	config.ChainID = latestConfig.ChainID
   416  	config.IstanbulCompatibleBlock = latestConfig.IstanbulCompatibleBlock
   417  	config.LondonCompatibleBlock = latestConfig.LondonCompatibleBlock
   418  	config.EthTxTypeCompatibleBlock = latestConfig.EthTxTypeCompatibleBlock
   419  	config.MagmaCompatibleBlock = latestConfig.MagmaCompatibleBlock
   420  	config.KoreCompatibleBlock = latestConfig.KoreCompatibleBlock
   421  	config.ShanghaiCompatibleBlock = latestConfig.ShanghaiCompatibleBlock
   422  	config.CancunCompatibleBlock = latestConfig.CancunCompatibleBlock
   423  	config.Kip103CompatibleBlock = latestConfig.Kip103CompatibleBlock
   424  	config.Kip103ContractAddress = latestConfig.Kip103ContractAddress
   425  	config.RandaoCompatibleBlock = latestConfig.RandaoCompatibleBlock
   426  
   427  	return config
   428  }
   429  
   430  func (api *GovernanceAPI) NodeAddress() common.Address {
   431  	return api.governance.NodeAddress()
   432  }
   433  
   434  func (api *GovernanceAPI) isGovernanceModeBallot() bool {
   435  	blockNumber := api.governance.BlockChain().CurrentBlock().NumberU64()
   436  	pset, err := api.governance.EffectiveParams(blockNumber + 1)
   437  	if err != nil {
   438  		return false
   439  	}
   440  	gMode := pset.GovernanceModeInt()
   441  	return gMode == params.GovernanceMode_Ballot
   442  }
   443  
   444  // Disabled APIs
   445  // func (api *GovernanceKlayAPI) GetTxGasHumanReadable(num *rpc.BlockNumber) (uint64, error) {
   446  // 	if num == nil || *num == rpc.LatestBlockNumber || *num == rpc.PendingBlockNumber {
   447  // 		// If the value hasn't been set in governance, set it with default value
   448  // 		if ret := api.governance.GetGovernanceValue(params.ConstTxGasHumanReadable); ret == nil {
   449  // 			return api.setDefaultTxGasHumanReadable()
   450  // 		} else {
   451  // 			return ret.(uint64), nil
   452  // 		}
   453  // 	} else {
   454  // 		blockNum := num.Int64()
   455  //
   456  // 		if blockNum > api.chain.CurrentBlock().NumberU64() {
   457  // 			return 0, errUnknownBlock
   458  // 		}
   459  //
   460  // 		if ret, err := api.governance.GetGovernanceItemAtNumber(uint64(blockNum), GovernanceKeyMapReverse[params.ConstTxGasHumanReadable]); err == nil && ret != nil {
   461  // 			return ret.(uint64), nil
   462  // 		} else {
   463  // 			logger.Error("Failed to retrieve TxGasHumanReadable, sending default value", "err", err)
   464  // 			return api.setDefaultTxGasHumanReadable()
   465  // 		}
   466  // 	}
   467  // }
   468  //
   469  // func (api *GovernanceKlayAPI) setDefaultTxGasHumanReadable() (uint64, error) {
   470  // 	err := api.governance.currentSet.SetValue(params.ConstTxGasHumanReadable, params.TxGasHumanReadable)
   471  // 	if err != nil {
   472  // 		return 0, errSetDefaultFailure
   473  // 	} else {
   474  // 		return params.TxGasHumanReadable, nil
   475  // 	}
   476  // }