github.com/klaytn/klaytn@v1.12.1/tests/klay_test_account_map_test.go (about)

     1  // Copyright 2018 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  package tests
    17  
    18  import (
    19  	"errors"
    20  	"fmt"
    21  	"math/big"
    22  
    23  	"github.com/klaytn/klaytn/blockchain/state"
    24  	"github.com/klaytn/klaytn/blockchain/types"
    25  	"github.com/klaytn/klaytn/common"
    26  	"github.com/klaytn/klaytn/contracts/reward/contract"
    27  	"github.com/klaytn/klaytn/crypto"
    28  )
    29  
    30  ////////////////////////////////////////////////////////////////////////////////
    31  // AddressBalanceMap
    32  ////////////////////////////////////////////////////////////////////////////////
    33  type AccountInfo struct {
    34  	balance *big.Int
    35  	nonce   uint64
    36  }
    37  
    38  type AccountMap struct {
    39  	m          map[common.Address]*AccountInfo
    40  	rewardbase common.Address
    41  }
    42  
    43  func NewAccountMap() *AccountMap {
    44  	return &AccountMap{
    45  		m: make(map[common.Address]*AccountInfo),
    46  	}
    47  }
    48  
    49  func (a *AccountMap) AddBalance(addr common.Address, v *big.Int) {
    50  	if acc, ok := a.m[addr]; ok {
    51  		acc.balance.Add(acc.balance, v)
    52  	} else {
    53  		// create an account
    54  		a.Set(addr, v, 0)
    55  	}
    56  }
    57  
    58  func (a *AccountMap) SubBalance(addr common.Address, v *big.Int) error {
    59  	if acc, ok := a.m[addr]; ok {
    60  		acc.balance.Sub(acc.balance, v)
    61  	} else {
    62  		return fmt.Errorf("trying to subtract balance from an uninitiailzed address (%s)", addr.Hex())
    63  	}
    64  
    65  	return nil
    66  }
    67  
    68  func (a *AccountMap) GetNonce(addr common.Address) uint64 {
    69  	if acc, ok := a.m[addr]; ok {
    70  		return acc.nonce
    71  	}
    72  	// 'StateDB.GetNonce' returns 0 when the address doesn't exist
    73  	return 0
    74  }
    75  
    76  func (a *AccountMap) IncNonce(addr common.Address) {
    77  	if acc, ok := a.m[addr]; ok {
    78  		acc.nonce++
    79  	}
    80  }
    81  
    82  func (a *AccountMap) Set(addr common.Address, v *big.Int, nonce uint64) {
    83  	a.m[addr] = &AccountInfo{new(big.Int).Set(v), nonce}
    84  }
    85  
    86  func (a *AccountMap) Initialize(bcdata *BCData) error {
    87  	statedb, err := bcdata.bc.State()
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	// NOTE-Klaytn-Issue973 Developing Klaytn token economy
    93  	// Add predefined accounts related to reward mechanism
    94  	rewardContractAddr := common.HexToAddress(contract.RewardContractAddress)
    95  	kcfContractAddr := common.HexToAddress(contract.KCFContractAddress)
    96  	kffContractAddr := common.HexToAddress(contract.KFFContractAddress)
    97  	addrs := append(bcdata.addrs, &rewardContractAddr, &kcfContractAddr, &kffContractAddr)
    98  
    99  	for _, addr := range addrs {
   100  		a.Set(*addr, statedb.GetBalance(*addr), statedb.GetNonce(*addr))
   101  	}
   102  
   103  	a.rewardbase = *bcdata.addrs[0]
   104  
   105  	return nil
   106  }
   107  
   108  func (a *AccountMap) Update(txs types.Transactions, signer types.Signer, picker types.AccountKeyPicker, currentBlockNumber uint64) error {
   109  	for _, tx := range txs {
   110  		to := tx.To()
   111  		v := tx.Value()
   112  
   113  		gasFrom, err := tx.ValidateSender(signer, picker, currentBlockNumber)
   114  		if err != nil {
   115  			return err
   116  		}
   117  		from := tx.ValidatedSender()
   118  
   119  		gasFeePayer := uint64(0)
   120  		feePayer := from
   121  		if tx.IsFeeDelegatedTransaction() {
   122  			gasFeePayer, err = tx.ValidateFeePayer(signer, picker, currentBlockNumber)
   123  			if err != nil {
   124  				return err
   125  			}
   126  			feePayer = tx.ValidatedFeePayer()
   127  		}
   128  		if to == nil {
   129  			nonce := a.GetNonce(from)
   130  			addr := crypto.CreateAddress(from, nonce)
   131  			to = &addr
   132  		}
   133  
   134  		a.AddBalance(*to, v)
   135  		a.SubBalance(from, v)
   136  
   137  		// TODO-Klaytn: This gas fee calculation is correct only if the transaction is a value transfer transaction.
   138  		// Calculate the correct transaction fee by checking the corresponding receipt.
   139  		intrinsicGas, err := tx.IntrinsicGas(currentBlockNumber)
   140  		if err != nil {
   141  			return err
   142  		}
   143  
   144  		intrinsicGas += gasFrom + gasFeePayer
   145  
   146  		fee := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(intrinsicGas))
   147  
   148  		feeRatio, ok := tx.FeeRatio()
   149  		if ok {
   150  			feeByFeePayer, feeBySender := types.CalcFeeWithRatio(feeRatio, fee)
   151  			a.SubBalance(feePayer, feeByFeePayer)
   152  			a.SubBalance(from, feeBySender)
   153  		} else {
   154  			a.SubBalance(feePayer, fee)
   155  		}
   156  
   157  		a.AddBalance(a.rewardbase, fee)
   158  
   159  		a.IncNonce(from)
   160  
   161  		if tx.IsEthereumTransaction() && tx.To() == nil {
   162  			a.IncNonce(*to)
   163  		}
   164  
   165  		if tx.Type() == types.TxTypeSmartContractDeploy || tx.Type() == types.TxTypeFeeDelegatedSmartContractDeploy || tx.Type() == types.TxTypeFeeDelegatedSmartContractDeployWithRatio {
   166  			a.IncNonce(*to)
   167  		}
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  func (a *AccountMap) Verify(statedb *state.StateDB) error {
   174  	for addr, acc := range a.m {
   175  		if acc.nonce != statedb.GetNonce(addr) {
   176  			return errors.New(fmt.Sprintf("[%s] nonce is different!! statedb(%d) != accountMap(%d).\n",
   177  				addr.Hex(), statedb.GetNonce(addr), acc.nonce))
   178  		}
   179  
   180  		if acc.balance.Cmp(statedb.GetBalance(addr)) != 0 {
   181  			return errors.New(fmt.Sprintf("[%s] balance is different!! statedb(%s) != accountMap(%s).\n",
   182  				addr.Hex(), statedb.GetBalance(addr).String(), acc.balance.String()))
   183  		}
   184  	}
   185  
   186  	return nil
   187  }