github.com/klaytn/klaytn@v1.10.2/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 "strings" 24 25 "github.com/klaytn/klaytn/common/hexutil" 26 27 "github.com/klaytn/klaytn/common" 28 "github.com/klaytn/klaytn/networks/rpc" 29 "github.com/klaytn/klaytn/params" 30 "github.com/klaytn/klaytn/reward" 31 ) 32 33 type PublicGovernanceAPI struct { 34 governance Engine // Node interfaced by this API 35 } 36 37 type returnTally struct { 38 Key string 39 Value interface{} 40 ApprovalPercentage float64 41 } 42 43 func NewGovernanceAPI(gov Engine) *PublicGovernanceAPI { 44 return &PublicGovernanceAPI{governance: gov} 45 } 46 47 type GovernanceKlayAPI struct { 48 governance Engine 49 chain blockChain 50 } 51 52 func NewGovernanceKlayAPI(gov Engine, chain blockChain) *GovernanceKlayAPI { 53 return &GovernanceKlayAPI{governance: gov, chain: chain} 54 } 55 56 var ( 57 errUnknownBlock = errors.New("Unknown block") 58 errNotAvailableInThisMode = errors.New("In current governance mode, voting power is not available") 59 errSetDefaultFailure = errors.New("Failed to set a default value") 60 errPermissionDenied = errors.New("You don't have the right to vote") 61 errRemoveSelf = errors.New("You can't vote on removing yourself") 62 errInvalidKeyValue = errors.New("Your vote couldn't be placed. Please check your vote's key and value") 63 errInvalidLowerBound = errors.New("lowerboundbasefee cannot be set exceeding upperboundbasefee") 64 errInvalidUpperBound = errors.New("upperboundbasefee cannot be set lower than lowerboundbasefee") 65 ) 66 67 func (api *GovernanceKlayAPI) GetStakingInfo(num *rpc.BlockNumber) (*reward.StakingInfo, error) { 68 return getStakingInfo(api.governance, num) 69 } 70 71 // TODO-Klaytn-Mantle: deprecate this 72 func (api *GovernanceKlayAPI) GovParamsAt(num *rpc.BlockNumber) (map[string]interface{}, error) { 73 return getParams(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 // GasPriceAt returns the base fee of the given block in peb, 85 // or returns unit price by using governance if there is no base fee set in header, 86 // or returns gas price of txpool if the block is pending block. 87 func (api *GovernanceKlayAPI) GasPriceAt(num *rpc.BlockNumber) (*hexutil.Big, error) { 88 if num == nil || *num == rpc.LatestBlockNumber { 89 header := api.chain.CurrentBlock().Header() 90 if header.BaseFee == nil { 91 pset, err := api.governance.EffectiveParams(header.Number.Uint64() + 1) 92 if err != nil { 93 return nil, err 94 } 95 return (*hexutil.Big)(new(big.Int).SetUint64(pset.UnitPrice())), nil 96 } 97 return (*hexutil.Big)(header.BaseFee), nil 98 } else if *num == rpc.PendingBlockNumber { 99 txpool := api.governance.GetTxPool() 100 return (*hexutil.Big)(txpool.GasPrice()), nil 101 } else { 102 blockNum := num.Uint64() 103 104 // Return the BaseFee in header at the block number 105 header := api.chain.GetHeaderByNumber(blockNum) 106 if blockNum > api.chain.CurrentBlock().NumberU64() || header == nil { 107 return nil, errUnknownBlock 108 } else if header.BaseFee != nil { 109 return (*hexutil.Big)(header.BaseFee), nil 110 } 111 112 // Return the UnitPrice in governance data at the block number 113 if ret, err := api.GasPriceAtNumber(blockNum); err != nil { 114 return nil, err 115 } else { 116 return (*hexutil.Big)(new(big.Int).SetUint64(ret)), nil 117 } 118 } 119 } 120 121 // GetRewards returns detailed information of the block reward at a given block number. 122 func (api *GovernanceKlayAPI) GetRewards(num *rpc.BlockNumber) (*reward.RewardSpec, error) { 123 blockNumber := uint64(0) 124 if num == nil || *num == rpc.LatestBlockNumber { 125 blockNumber = api.chain.CurrentBlock().NumberU64() 126 } else { 127 blockNumber = uint64(num.Int64()) 128 } 129 130 header := api.chain.GetHeaderByNumber(blockNumber) 131 if header == nil { 132 return nil, fmt.Errorf("the block does not exist (block number: %d)", blockNumber) 133 } 134 135 rules := api.chain.Config().Rules(new(big.Int).SetUint64(blockNumber)) 136 pset, err := api.governance.EffectiveParams(blockNumber) 137 if err != nil { 138 return nil, err 139 } 140 rewardParamNum := reward.CalcRewardParamBlock(header.Number.Uint64(), pset.Epoch(), rules) 141 rewardParamSet, err := api.governance.EffectiveParams(rewardParamNum) 142 if err != nil { 143 return nil, err 144 } 145 146 return reward.GetBlockReward(header, rules, rewardParamSet) 147 } 148 149 func (api *GovernanceKlayAPI) ChainConfig() *params.ChainConfig { 150 num := rpc.LatestBlockNumber 151 return getChainConfig(api.governance, &num) 152 } 153 154 // TODO-Klaytn-Mantle: deprecate this 155 func (api *GovernanceKlayAPI) ChainConfigAt(num *rpc.BlockNumber) *params.ChainConfig { 156 return getChainConfig(api.governance, num) 157 } 158 159 func (api *GovernanceKlayAPI) GetChainConfig(num *rpc.BlockNumber) *params.ChainConfig { 160 return getChainConfig(api.governance, num) 161 } 162 163 // Vote injects a new vote for governance targets such as unitprice and governingnode. 164 func (api *PublicGovernanceAPI) Vote(key string, val interface{}) (string, error) { 165 blockNumber := api.governance.BlockChain().CurrentBlock().NumberU64() 166 pset, err := api.governance.EffectiveParams(blockNumber + 1) 167 if err != nil { 168 return "", err 169 } 170 gMode := pset.GovernanceModeInt() 171 gNode := pset.GoverningNode() 172 173 if gMode == params.GovernanceMode_Single && gNode != api.governance.NodeAddress() { 174 return "", errPermissionDenied 175 } 176 vote, ok := api.governance.ValidateVote(&GovernanceVote{Key: strings.ToLower(key), Value: val}) 177 if !ok { 178 return "", errInvalidKeyValue 179 } 180 if vote.Key == "governance.removevalidator" { 181 if api.isRemovingSelf(val.(string)) { 182 return "", errRemoveSelf 183 } 184 } 185 if vote.Key == "kip71.lowerboundbasefee" { 186 if vote.Value.(uint64) > pset.UpperBoundBaseFee() { 187 return "", errInvalidLowerBound 188 } 189 } 190 if vote.Key == "kip71.upperboundbasefee" { 191 if vote.Value.(uint64) < pset.LowerBoundBaseFee() { 192 return "", errInvalidUpperBound 193 } 194 } 195 if api.governance.AddVote(key, val) { 196 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 197 } 198 return "", errInvalidKeyValue 199 } 200 201 func (api *PublicGovernanceAPI) isRemovingSelf(val string) bool { 202 for _, str := range strings.Split(val, ",") { 203 str = strings.Trim(str, " ") 204 if common.HexToAddress(str) == api.governance.NodeAddress() { 205 return true 206 } 207 } 208 return false 209 } 210 211 func (api *PublicGovernanceAPI) ShowTally() []*returnTally { 212 ret := []*returnTally{} 213 214 for _, val := range api.governance.GetGovernanceTalliesCopy() { 215 item := &returnTally{ 216 Key: val.Key, 217 Value: val.Value, 218 ApprovalPercentage: float64(val.Votes) / float64(api.governance.TotalVotingPower()) * 100, 219 } 220 ret = append(ret, item) 221 } 222 223 return ret 224 } 225 226 func (api *PublicGovernanceAPI) TotalVotingPower() (float64, error) { 227 if !api.isGovernanceModeBallot() { 228 return 0, errNotAvailableInThisMode 229 } 230 return float64(api.governance.TotalVotingPower()) / 1000.0, nil 231 } 232 233 // TODO-Klaytn-Mantle: deprecate this 234 func (api *PublicGovernanceAPI) ItemsAt(num *rpc.BlockNumber) (map[string]interface{}, error) { 235 return getParams(api.governance, num) 236 } 237 238 func (api *PublicGovernanceAPI) GetParams(num *rpc.BlockNumber) (map[string]interface{}, error) { 239 return getParams(api.governance, num) 240 } 241 242 func getParams(governance Engine, num *rpc.BlockNumber) (map[string]interface{}, error) { 243 blockNumber := uint64(0) 244 if num == nil || *num == rpc.LatestBlockNumber || *num == rpc.PendingBlockNumber { 245 blockNumber = governance.BlockChain().CurrentBlock().NumberU64() 246 } else { 247 blockNumber = uint64(num.Int64()) 248 } 249 250 pset, err := governance.EffectiveParams(blockNumber) 251 if err != nil { 252 return nil, err 253 } 254 return pset.StrMap(), nil 255 } 256 257 func (api *PublicGovernanceAPI) GetStakingInfo(num *rpc.BlockNumber) (*reward.StakingInfo, error) { 258 return getStakingInfo(api.governance, num) 259 } 260 261 func getStakingInfo(governance Engine, num *rpc.BlockNumber) (*reward.StakingInfo, error) { 262 blockNumber := uint64(0) 263 if num == nil || *num == rpc.LatestBlockNumber || *num == rpc.PendingBlockNumber { 264 blockNumber = governance.BlockChain().CurrentBlock().NumberU64() 265 } else { 266 blockNumber = uint64(num.Int64()) 267 } 268 return reward.GetStakingInfo(blockNumber), nil 269 } 270 271 func (api *PublicGovernanceAPI) PendingChanges() map[string]interface{} { 272 return api.governance.PendingChanges() 273 } 274 275 func (api *PublicGovernanceAPI) Votes() []GovernanceVote { 276 return api.governance.Votes() 277 } 278 279 func (api *PublicGovernanceAPI) IdxCache() []uint64 { 280 return api.governance.IdxCache() 281 } 282 283 func (api *PublicGovernanceAPI) IdxCacheFromDb() []uint64 { 284 return api.governance.IdxCacheFromDb() 285 } 286 287 // TODO-Klaytn: Return error if invalid input is given such as pending or a too big number 288 func (api *PublicGovernanceAPI) ItemCacheFromDb(num *rpc.BlockNumber) map[string]interface{} { 289 blockNumber := uint64(0) 290 if num == nil || *num == rpc.LatestBlockNumber || *num == rpc.PendingBlockNumber { 291 blockNumber = api.governance.BlockChain().CurrentBlock().NumberU64() 292 } else { 293 blockNumber = uint64(num.Int64()) 294 } 295 ret, _ := api.governance.DB().ReadGovernance(blockNumber) 296 return ret 297 } 298 299 type VoteList struct { 300 Key string 301 Value interface{} 302 Casted bool 303 BlockNum uint64 304 } 305 306 func (api *PublicGovernanceAPI) MyVotes() []*VoteList { 307 ret := []*VoteList{} 308 309 for k, v := range api.governance.GetVoteMapCopy() { 310 item := &VoteList{ 311 Key: k, 312 Value: v.Value, 313 Casted: v.Casted, 314 BlockNum: v.Num, 315 } 316 ret = append(ret, item) 317 } 318 319 return ret 320 } 321 322 func (api *PublicGovernanceAPI) MyVotingPower() (float64, error) { 323 if !api.isGovernanceModeBallot() { 324 return 0, errNotAvailableInThisMode 325 } 326 return float64(api.governance.MyVotingPower()) / 1000.0, nil 327 } 328 329 func (api *PublicGovernanceAPI) ChainConfig() *params.ChainConfig { 330 num := rpc.LatestBlockNumber 331 return getChainConfig(api.governance, &num) 332 } 333 334 // TODO-Klaytn-Mantle: deprecate this 335 func (api *PublicGovernanceAPI) ChainConfigAt(num *rpc.BlockNumber) *params.ChainConfig { 336 return getChainConfig(api.governance, num) 337 } 338 339 func (api *PublicGovernanceAPI) GetChainConfig(num *rpc.BlockNumber) *params.ChainConfig { 340 return getChainConfig(api.governance, num) 341 } 342 343 func getChainConfig(governance Engine, num *rpc.BlockNumber) *params.ChainConfig { 344 var blocknum uint64 345 if num == nil || *num == rpc.LatestBlockNumber || *num == rpc.PendingBlockNumber { 346 blocknum = governance.BlockChain().CurrentBlock().NumberU64() 347 } else { 348 blocknum = num.Uint64() 349 } 350 351 pset, err := governance.EffectiveParams(blocknum) 352 if err != nil { 353 return nil 354 } 355 356 latestConfig := governance.BlockChain().Config() 357 config := pset.ToChainConfig() 358 config.ChainID = latestConfig.ChainID 359 config.IstanbulCompatibleBlock = latestConfig.IstanbulCompatibleBlock 360 config.LondonCompatibleBlock = latestConfig.LondonCompatibleBlock 361 config.EthTxTypeCompatibleBlock = latestConfig.EthTxTypeCompatibleBlock 362 config.MagmaCompatibleBlock = latestConfig.MagmaCompatibleBlock 363 config.KoreCompatibleBlock = latestConfig.KoreCompatibleBlock 364 config.Kip103CompatibleBlock = latestConfig.Kip103CompatibleBlock 365 config.Kip103ContractAddress = latestConfig.Kip103ContractAddress 366 367 return config 368 } 369 370 func (api *PublicGovernanceAPI) NodeAddress() common.Address { 371 return api.governance.NodeAddress() 372 } 373 374 func (api *PublicGovernanceAPI) isGovernanceModeBallot() bool { 375 blockNumber := api.governance.BlockChain().CurrentBlock().NumberU64() 376 pset, err := api.governance.EffectiveParams(blockNumber + 1) 377 if err != nil { 378 return false 379 } 380 gMode := pset.GovernanceModeInt() 381 return gMode == params.GovernanceMode_Ballot 382 } 383 384 func (api *GovernanceKlayAPI) GasPriceAtNumber(num uint64) (uint64, error) { 385 pset, err := api.governance.EffectiveParams(num) 386 if err != nil { 387 logger.Error("Failed to retrieve unit price", "err", err) 388 return 0, err 389 } 390 return pset.UnitPrice(), nil 391 } 392 393 // Disabled APIs 394 // func (api *GovernanceKlayAPI) GetTxGasHumanReadable(num *rpc.BlockNumber) (uint64, error) { 395 // if num == nil || *num == rpc.LatestBlockNumber || *num == rpc.PendingBlockNumber { 396 // // If the value hasn't been set in governance, set it with default value 397 // if ret := api.governance.GetGovernanceValue(params.ConstTxGasHumanReadable); ret == nil { 398 // return api.setDefaultTxGasHumanReadable() 399 // } else { 400 // return ret.(uint64), nil 401 // } 402 // } else { 403 // blockNum := num.Int64() 404 // 405 // if blockNum > api.chain.CurrentBlock().NumberU64() { 406 // return 0, errUnknownBlock 407 // } 408 // 409 // if ret, err := api.governance.GetGovernanceItemAtNumber(uint64(blockNum), GovernanceKeyMapReverse[params.ConstTxGasHumanReadable]); err == nil && ret != nil { 410 // return ret.(uint64), nil 411 // } else { 412 // logger.Error("Failed to retrieve TxGasHumanReadable, sending default value", "err", err) 413 // return api.setDefaultTxGasHumanReadable() 414 // } 415 // } 416 // } 417 // 418 // func (api *GovernanceKlayAPI) setDefaultTxGasHumanReadable() (uint64, error) { 419 // err := api.governance.currentSet.SetValue(params.ConstTxGasHumanReadable, params.TxGasHumanReadable) 420 // if err != nil { 421 // return 0, errSetDefaultFailure 422 // } else { 423 // return params.TxGasHumanReadable, nil 424 // } 425 // }