github.com/MetalBlockchain/subnet-evm@v0.6.3/core/evm.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2016 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package core 28 29 import ( 30 "math/big" 31 32 "github.com/MetalBlockchain/subnet-evm/consensus" 33 "github.com/MetalBlockchain/subnet-evm/core/types" 34 "github.com/MetalBlockchain/subnet-evm/core/vm" 35 "github.com/MetalBlockchain/subnet-evm/predicate" 36 "github.com/ethereum/go-ethereum/common" 37 "github.com/ethereum/go-ethereum/log" 38 //"github.com/ethereum/go-ethereum/log" 39 ) 40 41 // ChainContext supports retrieving headers and consensus parameters from the 42 // current blockchain to be used during transaction processing. 43 type ChainContext interface { 44 // Engine retrieves the chain's consensus engine. 45 Engine() consensus.Engine 46 47 // GetHeader returns the header corresponding to the hash/number argument pair. 48 GetHeader(common.Hash, uint64) *types.Header 49 } 50 51 // NewEVMBlockContext creates a new context for use in the EVM. 52 func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext { 53 predicateBytes, ok := predicate.GetPredicateResultBytes(header.Extra) 54 if !ok { 55 return newEVMBlockContext(header, chain, author, nil) 56 } 57 // Prior to Durango, the VM enforces the extra data is smaller than or 58 // equal to this size. After Durango, the VM pre-verifies the extra 59 // data past the dynamic fee rollup window is valid. 60 predicateResults, err := predicate.ParseResults(predicateBytes) 61 if err != nil { 62 log.Error("failed to parse predicate results creating new block context", "err", err, "extra", header.Extra) 63 // As mentioned above, we pre-verify the extra data to ensure this never happens. 64 // If we hit an error, construct a new block context rather than use a potentially half initialized value 65 // as defense in depth. 66 return newEVMBlockContext(header, chain, author, nil) 67 } 68 return newEVMBlockContext(header, chain, author, predicateResults) 69 } 70 71 // NewEVMBlockContextWithPredicateResults creates a new context for use in the EVM with an override for the predicate results that is not present 72 // in header.Extra. 73 // This function is used to create a BlockContext when the header Extra data is not fully formed yet and it's more efficient to pass in predicateResults 74 // directly rather than re-encode the latest results when executing each individaul transaction. 75 func NewEVMBlockContextWithPredicateResults(header *types.Header, chain ChainContext, author *common.Address, predicateResults *predicate.Results) vm.BlockContext { 76 return newEVMBlockContext(header, chain, author, predicateResults) 77 } 78 79 func newEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address, predicateResults *predicate.Results) vm.BlockContext { 80 var ( 81 beneficiary common.Address 82 baseFee *big.Int 83 ) 84 85 // If we don't have an explicit author (i.e. not mining), extract from the header 86 if author == nil { 87 beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation 88 } else { 89 beneficiary = *author 90 } 91 if header.BaseFee != nil { 92 baseFee = new(big.Int).Set(header.BaseFee) 93 } 94 return vm.BlockContext{ 95 CanTransfer: CanTransfer, 96 Transfer: Transfer, 97 GetHash: GetHashFn(header, chain), 98 PredicateResults: predicateResults, 99 Coinbase: beneficiary, 100 BlockNumber: new(big.Int).Set(header.Number), 101 Time: header.Time, 102 Difficulty: new(big.Int).Set(header.Difficulty), 103 BaseFee: baseFee, 104 GasLimit: header.GasLimit, 105 ExcessBlobGas: header.ExcessBlobGas, 106 } 107 } 108 109 // NewEVMTxContext creates a new transaction context for a single transaction. 110 func NewEVMTxContext(msg *Message) vm.TxContext { 111 return vm.TxContext{ 112 Origin: msg.From, 113 GasPrice: new(big.Int).Set(msg.GasPrice), 114 BlobHashes: msg.BlobHashes, 115 } 116 } 117 118 // GetHashFn returns a GetHashFunc which retrieves header hashes by number 119 func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash { 120 // Cache will initially contain [refHash.parent], 121 // Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...] 122 var cache []common.Hash 123 124 return func(n uint64) common.Hash { 125 if ref.Number.Uint64() <= n { 126 // This situation can happen if we're doing tracing and using 127 // block overrides. 128 return common.Hash{} 129 } 130 // If there's no hash cache yet, make one 131 if len(cache) == 0 { 132 cache = append(cache, ref.ParentHash) 133 } 134 if idx := ref.Number.Uint64() - n - 1; idx < uint64(len(cache)) { 135 return cache[idx] 136 } 137 // No luck in the cache, but we can start iterating from the last element we already know 138 lastKnownHash := cache[len(cache)-1] 139 lastKnownNumber := ref.Number.Uint64() - uint64(len(cache)) 140 141 for { 142 header := chain.GetHeader(lastKnownHash, lastKnownNumber) 143 if header == nil { 144 break 145 } 146 cache = append(cache, header.ParentHash) 147 lastKnownHash = header.ParentHash 148 lastKnownNumber = header.Number.Uint64() - 1 149 if n == lastKnownNumber { 150 return lastKnownHash 151 } 152 } 153 return common.Hash{} 154 } 155 } 156 157 // CanTransfer checks whether there are enough funds in the address' account to make a transfer. 158 // This does not take the necessary gas in to account to make the transfer valid. 159 func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { 160 return db.GetBalance(addr).Cmp(amount) >= 0 161 } 162 163 // Transfer subtracts amount from sender and adds amount to recipient using the given Db 164 func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { 165 db.SubBalance(sender, amount) 166 db.AddBalance(recipient, amount) 167 }