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  }