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 // }