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  }