github.com/klaytn/klaytn@v1.12.1/blockchain/evm.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2016 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from core/evm.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package blockchain 22 23 import ( 24 "context" 25 "errors" 26 "fmt" 27 "math/big" 28 29 "github.com/klaytn/klaytn/accounts/abi" 30 "github.com/klaytn/klaytn/blockchain/types" 31 "github.com/klaytn/klaytn/blockchain/vm" 32 "github.com/klaytn/klaytn/common" 33 "github.com/klaytn/klaytn/common/hexutil" 34 "github.com/klaytn/klaytn/consensus" 35 "github.com/klaytn/klaytn/params" 36 ) 37 38 // ChainContext supports retrieving headers and consensus parameters from the 39 // current blockchain to be used during transaction processing. 40 type ChainContext interface { 41 // Engine retrieves the chain's consensus engine. 42 Engine() consensus.Engine 43 44 // GetHeader returns the hash corresponding to their hash. 45 GetHeader(common.Hash, uint64) *types.Header 46 } 47 48 // NewEVMBlockContext creates a new context for use in the EVM. 49 func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext { 50 // If we don't have an explicit author (i.e. not mining), extract from the header 51 var ( 52 beneficiary common.Address 53 rewardBase common.Address 54 baseFee *big.Int 55 random common.Hash 56 ) 57 58 if author == nil { 59 beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation 60 } else { 61 beneficiary = *author 62 } 63 64 rewardBase = header.Rewardbase 65 66 if header.BaseFee != nil { 67 baseFee = header.BaseFee 68 } else { // Before Magma hardfork, BASEFEE (48) returns 0 69 baseFee = new(big.Int).SetUint64(params.ZeroBaseFee) 70 } 71 72 if header.MixHash != nil { 73 random = common.BytesToHash(header.MixHash) 74 } else { // Before Randao hardfork, RANDOM (44) returns last block hash 75 random = header.ParentHash 76 } 77 78 return vm.BlockContext{ 79 CanTransfer: CanTransfer, 80 Transfer: Transfer, 81 GetHash: GetHashFn(header, chain), 82 Coinbase: beneficiary, 83 Rewardbase: rewardBase, 84 BlockNumber: new(big.Int).Set(header.Number), 85 Time: new(big.Int).Set(header.Time), 86 BlockScore: new(big.Int).Set(header.BlockScore), 87 BaseFee: baseFee, 88 Random: random, 89 } 90 } 91 92 // NewEVMTxContext creates a new transaction context for a single transaction. 93 func NewEVMTxContext(msg Message, header *types.Header) vm.TxContext { 94 effectiveGasPrice := msg.GasPrice() 95 if header.BaseFee != nil { 96 effectiveGasPrice = header.BaseFee 97 } 98 99 return vm.TxContext{ 100 Origin: msg.ValidatedSender(), 101 GasPrice: new(big.Int).Set(effectiveGasPrice), 102 } 103 } 104 105 // GetHashFn returns a GetHashFunc which retrieves header hashes by number 106 func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash { 107 // Cache will initially contain [refHash.parent], 108 // Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...] 109 var cache []common.Hash 110 111 return func(n uint64) common.Hash { 112 // If there's no hash cache yet, make one 113 if len(cache) == 0 { 114 cache = append(cache, ref.ParentHash) 115 } 116 if idx := ref.Number.Uint64() - n - 1; idx < uint64(len(cache)) { 117 return cache[idx] 118 } 119 // No luck in the cache, but we can start iterating from the last element we already know 120 lastKnownHash := cache[len(cache)-1] 121 122 lastKnownNumber := ref.Number.Uint64() - uint64(len(cache)) 123 124 for { 125 header := chain.GetHeader(lastKnownHash, lastKnownNumber) 126 if header == nil { 127 break 128 } 129 cache = append(cache, header.ParentHash) 130 lastKnownHash = header.ParentHash 131 lastKnownNumber = header.Number.Uint64() - 1 132 if n == lastKnownNumber { 133 return lastKnownHash 134 } 135 } 136 return common.Hash{} 137 } 138 } 139 140 // CanTransfer checks whether there are enough funds in the address' account to make a transfer. 141 // This does not take the necessary gas in to account to make the transfer valid. 142 func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { 143 return db.GetBalance(addr).Cmp(amount) >= 0 144 } 145 146 // Transfer subtracts amount from sender and adds amount to recipient using the given Db 147 func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { 148 db.SubBalance(sender, amount) 149 db.AddBalance(recipient, amount) 150 } 151 152 func DoEstimateGas(ctx context.Context, gasLimit, rpcGasCap uint64, txValue, gasPrice, balance *big.Int, test func(gas uint64) (bool, *ExecutionResult, error)) (hexutil.Uint64, error) { 153 // Binary search the gas requirement, as it may be higher than the amount used 154 var ( 155 lo uint64 = params.TxGas - 1 156 hi uint64 = params.UpperGasLimit 157 cap uint64 158 ) 159 160 // Initialize nil params 161 if txValue == nil { 162 txValue = big.NewInt(0) 163 } 164 if gasPrice == nil { 165 gasPrice = big.NewInt(0) 166 } 167 if balance == nil { 168 balance = big.NewInt(0) 169 } 170 171 if gasLimit >= params.TxGas { 172 hi = gasLimit 173 } 174 175 // recap the highest gas limit with account's available balance. 176 if gasPrice.BitLen() != 0 { 177 available := new(big.Int).Set(balance) 178 if txValue.Cmp(available) >= 0 { 179 return 0, errors.New("insufficient funds for transfer") 180 } 181 available.Sub(available, txValue) 182 allowance := new(big.Int).Div(available, gasPrice) 183 184 // If the allowance is larger than maximum uint64, skip checking 185 if allowance.IsUint64() && hi > allowance.Uint64() { 186 logger.Trace("Gas estimation capped by limited funds", "original", hi, "balance", balance, 187 "sent", txValue, "maxFeePerGas", gasPrice, "fundable", allowance) 188 hi = allowance.Uint64() 189 } 190 } 191 // Recap the highest gas allowance with specified gascap. 192 if rpcGasCap != 0 && hi > rpcGasCap { 193 logger.Trace("Caller gas above allowance, capping", "requested", hi, "cap", rpcGasCap) 194 hi = rpcGasCap 195 } 196 cap = hi 197 198 // Execute the binary search and hone in on an executable gas limit 199 for lo+1 < hi { 200 mid := (hi + lo) / 2 201 failed, _, err := test(mid) 202 if err != nil { 203 return 0, err 204 } 205 206 if failed { 207 lo = mid 208 } else { 209 hi = mid 210 } 211 } 212 // Reject the transaction as invalid if it still fails at the highest allowance 213 if hi == cap { 214 failed, result, err := test(hi) 215 if err != nil { 216 return 0, err 217 } 218 if failed { 219 if result != nil && result.VmExecutionStatus != types.ReceiptStatusErrOutOfGas { 220 if len(result.Revert()) > 0 { 221 return 0, NewRevertError(result) 222 } 223 return 0, result.Unwrap() 224 } 225 // Otherwise, the specified gas cap is too low 226 return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap) 227 } 228 } 229 return hexutil.Uint64(hi), nil 230 } 231 232 func NewRevertError(result *ExecutionResult) *RevertError { 233 reason, errUnpack := abi.UnpackRevert(result.Revert()) 234 err := errors.New("execution reverted") 235 if errUnpack == nil { 236 err = fmt.Errorf("execution reverted: %v", reason) 237 } 238 return &RevertError{ 239 error: err, 240 reason: hexutil.Encode(result.Revert()), 241 } 242 } 243 244 // RevertError is an API error that encompassas an EVM revertal with JSON error 245 // code and a binary data blob. 246 type RevertError struct { 247 error 248 reason string // revert reason hex encoded 249 } 250 251 // ErrorCode returns the JSON error code for a revertal. 252 // See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal 253 func (e *RevertError) ErrorCode() int { 254 return 3 255 } 256 257 // ErrorData returns the hex encoded revert reason. 258 func (e *RevertError) ErrorData() interface{} { 259 return e.reason 260 }