github.com/klaytn/klaytn@v1.10.2/reward/addressbook_connector.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 reward 18 19 import ( 20 "errors" 21 "fmt" 22 "math/big" 23 "strings" 24 25 "github.com/klaytn/klaytn/accounts/abi" 26 "github.com/klaytn/klaytn/blockchain" 27 "github.com/klaytn/klaytn/blockchain/types" 28 "github.com/klaytn/klaytn/blockchain/vm" 29 "github.com/klaytn/klaytn/common" 30 "github.com/klaytn/klaytn/contracts/reward/contract" 31 "github.com/klaytn/klaytn/params" 32 ) 33 34 // addressType defined in AddressBook 35 const ( 36 addressTypeNodeID = iota 37 addressTypeStakingAddr 38 addressTypeRewardAddr 39 addressTypePoCAddr // TODO-klaytn: PoC should be changed to KFF after changing AddressBook contract 40 addressTypeKIRAddr // TODO-klaytn: KIR should be changed to KCF after changing AddressBook contract 41 ) 42 43 var errAddressBookIncomplete = errors.New("incomplete node information from AddressBook") 44 45 var addressBookContractAddress = contract.AddressBookContractAddress 46 47 type addressBookConnector struct { 48 bc blockChain 49 gh governanceHelper 50 abi string 51 contractAddress common.Address 52 } 53 54 // create and return addressBookConnector 55 func newAddressBookConnector(bc blockChain, gh governanceHelper) *addressBookConnector { 56 return &addressBookConnector{ 57 bc: bc, 58 gh: gh, 59 abi: contract.AddressBookABI, 60 contractAddress: common.HexToAddress(addressBookContractAddress), 61 } 62 } 63 64 // make a message to the addressBook contract for executing getAllAddress function of the addressBook contract 65 func (ac *addressBookConnector) makeMsgToAddressBook(r params.Rules) (*types.Transaction, error) { 66 abiInstance, err := abi.JSON(strings.NewReader(ac.abi)) 67 if err != nil { 68 return nil, err 69 } 70 71 data, err := abiInstance.Pack("getAllAddress") 72 if err != nil { 73 return nil, err 74 } 75 76 intrinsicGas, err := types.IntrinsicGas(data, nil, false, r) 77 if err != nil { 78 return nil, err 79 } 80 81 // Create new call message 82 // TODO-Klaytn-Issue1166 Decide who will be sender(i.e. from) 83 var ( 84 from = common.Address{} 85 to = &ac.contractAddress 86 nonce = uint64(0) 87 amount = big.NewInt(0) 88 gasLimit = uint64(10000000) 89 gasPrice = big.NewInt(0) 90 checkNonce = false 91 ) 92 msg := types.NewMessage(from, to, nonce, amount, gasLimit, gasPrice, 93 data, checkNonce, intrinsicGas) 94 return msg, nil 95 } 96 97 // It parses the result bytes of calling addressBook to addresses. 98 func (ac *addressBookConnector) parseAllAddresses(result []byte) (nodeIds []common.Address, stakingAddrs []common.Address, rewardAddrs []common.Address, pocAddr common.Address, kirAddr common.Address, err error) { 99 nodeIds = []common.Address{} 100 stakingAddrs = []common.Address{} 101 rewardAddrs = []common.Address{} 102 pocAddr = common.Address{} 103 kirAddr = common.Address{} 104 105 if result == nil { 106 err = errAddressBookIncomplete 107 return 108 } 109 110 abiInstance, err := abi.JSON(strings.NewReader(ac.abi)) 111 if err != nil { 112 return 113 } 114 115 var ( 116 allTypeList = new([]uint8) 117 allAddressList = new([]common.Address) 118 ) 119 out := &[]interface{}{ 120 allTypeList, 121 allAddressList, 122 } 123 124 err = abiInstance.Unpack(out, "getAllAddress", result) 125 if err != nil { 126 logger.Trace("abiInstance.Unpack failed for getAllAddress in AddressBook") 127 return 128 } 129 130 if len(*allTypeList) != len(*allAddressList) { 131 err = errors.New(fmt.Sprintf("length of type list and address list differ. len(type)=%d, len(addrs)=%d", len(*allTypeList), len(*allAddressList))) 132 return 133 } 134 135 // Parse and construct node information 136 for i, addrType := range *allTypeList { 137 switch addrType { 138 case addressTypeNodeID: 139 nodeIds = append(nodeIds, (*allAddressList)[i]) 140 case addressTypeStakingAddr: 141 stakingAddrs = append(stakingAddrs, (*allAddressList)[i]) 142 case addressTypeRewardAddr: 143 rewardAddrs = append(rewardAddrs, (*allAddressList)[i]) 144 case addressTypePoCAddr: 145 pocAddr = (*allAddressList)[i] 146 case addressTypeKIRAddr: 147 kirAddr = (*allAddressList)[i] 148 default: 149 err = errors.New(fmt.Sprintf("invalid type from AddressBook: %d", addrType)) 150 return 151 } 152 } 153 154 // validate parsed node information 155 if len(nodeIds) != len(stakingAddrs) || 156 len(nodeIds) != len(rewardAddrs) || 157 common.EmptyAddress(pocAddr) || 158 common.EmptyAddress(kirAddr) { 159 err = errAddressBookIncomplete 160 return 161 } 162 163 return 164 } 165 166 // getStakingInfoFromAddressBook returns stakingInfo when calling AddressBook succeeded. 167 // If addressBook is not activated, emptyStakingInfo is returned. 168 // After addressBook is activated, it returns stakingInfo with addresses and stakingAmount. 169 // Otherwise, it returns an error. 170 func (ac *addressBookConnector) getStakingInfoFromAddressBook(blockNum uint64) (*StakingInfo, error) { 171 if !params.IsStakingUpdateInterval(blockNum) { 172 return nil, errors.New(fmt.Sprintf("not staking block number. blockNum: %d", blockNum)) 173 } 174 175 // Prepare a message 176 msg, err := ac.makeMsgToAddressBook(ac.bc.Config().Rules(new(big.Int).SetUint64(blockNum))) 177 if err != nil { 178 return nil, errors.New(fmt.Sprintf("failed to make message for AddressBook. root err: %s", err)) 179 } 180 181 intervalBlock := ac.bc.GetBlockByNumber(blockNum) 182 if intervalBlock == nil { 183 return nil, errors.New("stateDB is not ready for staking info") 184 } 185 statedb, err := ac.bc.StateAt(intervalBlock.Root()) 186 if err != nil { 187 return nil, errors.New(fmt.Sprintf("failed to make a state for interval block. blockNum: %d, root err: %s", blockNum, err)) 188 } 189 190 // Create a new context to be used in the EVM environment 191 context := blockchain.NewEVMContext(msg, intervalBlock.Header(), ac.bc, nil) 192 // EVM demands the sender to have enough KLAY balance (gasPrice * gasLimit) in buyGas() 193 // After KIP-71, gasPrice is baseFee (=nonzero), regardless of the msg.gasPrice (=zero) 194 // But our sender (0x0) won't have enough balance. Instead we override gasPrice = 0 here 195 context.GasPrice = big.NewInt(0) 196 evm := vm.NewEVM(context, statedb, ac.bc.Config(), &vm.Config{}) 197 198 res, gas, kerr := blockchain.ApplyMessage(evm, msg) 199 logger.Trace("Call AddressBook contract", "used gas", gas, "kerr", kerr) 200 err = kerr.ErrTxInvalid 201 if err != nil { 202 return nil, errors.New(fmt.Sprintf("failed to call AddressBook contract. root err: %s", err)) 203 } 204 205 nodeAddrs, stakingAddrs, rewardAddrs, PoCAddr, KIRAddr, err := ac.parseAllAddresses(res) 206 if err != nil { 207 if err == errAddressBookIncomplete { 208 // This is an expected behavior when the addressBook contract is not activated yet. 209 logger.Info("The addressBook is not yet activated. Use empty stakingInfo", "reason", err) 210 } else { 211 logger.Error("Fail while parsing a result from the addressBook. Use empty staking info", "err", err) 212 } 213 return newEmptyStakingInfo(blockNum), nil 214 } 215 216 return newStakingInfo(ac.bc, ac.gh, blockNum, nodeAddrs, stakingAddrs, rewardAddrs, KIRAddr, PoCAddr) 217 } 218 219 // Only for testing purpose. 220 func SetTestAddressBookAddress(addr common.Address) { 221 addressBookContractAddress = addr.Hex() 222 }