github.com/klaytn/klaytn@v1.10.2/governance/contract_connector.go (about) 1 // Copyright 2022 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 "math/big" 22 "strings" 23 24 "github.com/klaytn/klaytn/accounts/abi" 25 "github.com/klaytn/klaytn/blockchain" 26 "github.com/klaytn/klaytn/blockchain/types" 27 "github.com/klaytn/klaytn/blockchain/vm" 28 "github.com/klaytn/klaytn/common" 29 govcontract "github.com/klaytn/klaytn/contracts/gov" 30 "github.com/klaytn/klaytn/params" 31 ) 32 33 type contractCaller struct { 34 chain blockChain 35 contractAddr common.Address 36 } 37 38 var govParamAbi, _ = abi.JSON(strings.NewReader(govcontract.GovParamABI)) 39 40 const govparamfunc = "getAllParamsAt" 41 42 func (c *contractCaller) getAllParamsAt(num *big.Int) (*params.GovParamSet, error) { 43 // respect error; some nodes can succeed without error while others do not 44 tx, evm, err := c.prepareCall(govParamAbi, govparamfunc, num) 45 if err != nil { 46 return nil, err 47 } 48 49 // ignore error; if error, all nodes will have the same error 50 res, err := c.callTx(tx, evm) 51 if err != nil { 52 return params.NewGovParamSet(), nil 53 } 54 55 // ignore error; if error, all nodes will have the same error 56 pset, err := c.parseGetAllParamsAt(res) 57 if err != nil { 58 return params.NewGovParamSet(), nil 59 } 60 return pset, nil 61 } 62 63 func (c *contractCaller) prepareCall(contractAbi abi.ABI, fn string, args ...interface{}) (*types.Transaction, *vm.EVM, error) { 64 tx, err := c.makeTx(contractAbi, fn, args...) 65 if err != nil { 66 return nil, nil, err 67 } 68 69 evm, err := c.makeEVM(tx) 70 if err != nil { 71 return nil, nil, err 72 } 73 74 return tx, evm, nil 75 } 76 77 // makeTx makes a contract execution transaction 78 func (c *contractCaller) makeTx(contractAbi abi.ABI, fn string, args ...interface{}, 79 ) (*types.Transaction, error) { 80 calldata, err := contractAbi.Pack(fn, args...) 81 if err != nil { 82 logger.Error("Could not pack ABI", "err", err) 83 return nil, err 84 } 85 86 rules := c.chain.Config().Rules(c.chain.CurrentBlock().Number()) 87 intrinsicGas, err := types.IntrinsicGas(calldata, nil, false, rules) 88 if err != nil { 89 logger.Error("Could not fetch intrinsicGas", "err", err) 90 return nil, err 91 } 92 93 var ( 94 from = common.Address{} 95 to = &c.contractAddr 96 nonce = uint64(0) 97 amount = big.NewInt(0) 98 gasLimit = uint64(1e8) 99 gasPrice = big.NewInt(0) 100 checkNonce = false 101 ) 102 103 tx := types.NewMessage(from, to, nonce, amount, gasLimit, gasPrice, calldata, 104 checkNonce, intrinsicGas) 105 return tx, nil 106 } 107 108 // makeEVM makes an EVM for the tx execution 109 func (c *contractCaller) makeEVM(tx *types.Transaction) (*vm.EVM, error) { 110 // Load the latest state 111 block := c.chain.CurrentBlock() 112 if block == nil { 113 logger.Error("Could not find the latest block", "num", c.chain.CurrentBlock().NumberU64()) 114 return nil, errors.New("no block") 115 } 116 117 statedb, err := c.chain.StateAt(block.Root()) 118 if err != nil { 119 logger.Error("Could not find the state", "err", err, "num", c.chain.CurrentBlock().NumberU64()) 120 return nil, err 121 } 122 123 // Run EVM at given states 124 evmCtx := blockchain.NewEVMContext(tx, block.Header(), c.chain, nil) 125 // EVM demands the sender to have enough KLAY balance (gasPrice * gasLimit) in buyGas() 126 // After KIP-71, gasPrice is baseFee (=nonzero), regardless of the msg.gasPrice (=zero) 127 // But our sender (0x0) won't have enough balance. Instead we override gasPrice = 0 here 128 evmCtx.GasPrice = big.NewInt(0) 129 evm := vm.NewEVM(evmCtx, statedb, c.chain.Config(), &vm.Config{}) 130 return evm, nil 131 } 132 133 // callTx executes contract call at the latest block context 134 func (c *contractCaller) callTx(tx *types.Transaction, evm *vm.EVM) ([]byte, error) { 135 res, _, kerr := blockchain.ApplyMessage(evm, tx) 136 if kerr.ErrTxInvalid != nil { 137 logger.Warn("Invalid tx") 138 return nil, kerr.ErrTxInvalid 139 } 140 141 return res, nil 142 } 143 144 func (c *contractCaller) parseGetAllParamsAt(b []byte) (*params.GovParamSet, error) { 145 if len(b) == 0 { 146 return params.NewGovParamSet(), nil 147 } 148 149 var ( // c.f. contracts/gov/GovParam.go:GetAllParamsAt() 150 pNames = new([]string) // *[]string = nil 151 pValues = new([][]byte) // *[][]byte = nil 152 out = &[]interface{}{pNames, pValues} // array of pointers 153 ) 154 if err := govParamAbi.Unpack(out, govparamfunc, b); err != nil { 155 return nil, err 156 } 157 var ( // Retrieve the slices allocated inside Unpack(). 158 names = *pNames 159 values = *pValues 160 ) 161 162 // verification 163 if len(names) != len(values) { 164 logger.Warn("Malformed contract.getAllParams result", 165 "len(names)", len(names), "len(values)", len(values)) 166 return nil, errors.New("malformed contract.getAllParams result") 167 } 168 169 bytesMap := make(map[string][]byte) 170 for i := 0; i < len(names); i++ { 171 bytesMap[names[i]] = values[i] 172 } 173 return params.NewGovParamSetBytesMapTolerant(bytesMap), nil 174 }