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 }