github.com/ethereum/go-ethereum@v1.16.1/core/vm/operations_acl.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package vm 18 19 import ( 20 "errors" 21 22 "github.com/ethereum/go-ethereum/common" 23 "github.com/ethereum/go-ethereum/common/math" 24 "github.com/ethereum/go-ethereum/core/tracing" 25 "github.com/ethereum/go-ethereum/core/types" 26 "github.com/ethereum/go-ethereum/params" 27 ) 28 29 func makeGasSStoreFunc(clearingRefund uint64) gasFunc { 30 return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { 31 // If we fail the minimum gas availability invariant, fail (0) 32 if contract.Gas <= params.SstoreSentryGasEIP2200 { 33 return 0, errors.New("not enough gas for reentrancy sentry") 34 } 35 // Gas sentry honoured, do the actual gas calculation based on the stored value 36 var ( 37 y, x = stack.Back(1), stack.peek() 38 slot = common.Hash(x.Bytes32()) 39 current = evm.StateDB.GetState(contract.Address(), slot) 40 cost = uint64(0) 41 ) 42 // Check slot presence in the access list 43 if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { 44 cost = params.ColdSloadCostEIP2929 45 // If the caller cannot afford the cost, this change will be rolled back 46 evm.StateDB.AddSlotToAccessList(contract.Address(), slot) 47 } 48 value := common.Hash(y.Bytes32()) 49 50 if current == value { // noop (1) 51 // EIP 2200 original clause: 52 // return params.SloadGasEIP2200, nil 53 return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS 54 } 55 original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32()) 56 if original == current { 57 if original == (common.Hash{}) { // create slot (2.1.1) 58 return cost + params.SstoreSetGasEIP2200, nil 59 } 60 if value == (common.Hash{}) { // delete slot (2.1.2b) 61 evm.StateDB.AddRefund(clearingRefund) 62 } 63 // EIP-2200 original clause: 64 // return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) 65 return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2) 66 } 67 if original != (common.Hash{}) { 68 if current == (common.Hash{}) { // recreate slot (2.2.1.1) 69 evm.StateDB.SubRefund(clearingRefund) 70 } else if value == (common.Hash{}) { // delete slot (2.2.1.2) 71 evm.StateDB.AddRefund(clearingRefund) 72 } 73 } 74 if original == value { 75 if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) 76 // EIP 2200 Original clause: 77 //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) 78 evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929) 79 } else { // reset to original existing slot (2.2.2.2) 80 // EIP 2200 Original clause: 81 // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) 82 // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST) 83 // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST 84 // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST 85 evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929) 86 } 87 } 88 // EIP-2200 original clause: 89 //return params.SloadGasEIP2200, nil // dirty update (2.2) 90 return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2) 91 } 92 } 93 94 // gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929 95 // For SLOAD, if the (address, storage_key) pair (where address is the address of the contract 96 // whose storage is being read) is not yet in accessed_storage_keys, 97 // charge 2100 gas and add the pair to accessed_storage_keys. 98 // If the pair is already in accessed_storage_keys, charge 100 gas. 99 func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { 100 loc := stack.peek() 101 slot := common.Hash(loc.Bytes32()) 102 // Check slot presence in the access list 103 if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { 104 // If the caller cannot afford the cost, this change will be rolled back 105 // If he does afford it, we can skip checking the same thing later on, during execution 106 evm.StateDB.AddSlotToAccessList(contract.Address(), slot) 107 return params.ColdSloadCostEIP2929, nil 108 } 109 return params.WarmStorageReadCostEIP2929, nil 110 } 111 112 // gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929 113 // EIP spec: 114 // > If the target is not in accessed_addresses, 115 // > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses. 116 // > Otherwise, charge WARM_STORAGE_READ_COST gas. 117 func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { 118 // memory expansion first (dynamic part of pre-2929 implementation) 119 gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize) 120 if err != nil { 121 return 0, err 122 } 123 addr := common.Address(stack.peek().Bytes20()) 124 // Check slot presence in the access list 125 if !evm.StateDB.AddressInAccessList(addr) { 126 evm.StateDB.AddAddressToAccessList(addr) 127 var overflow bool 128 // We charge (cold-warm), since 'warm' is already charged as constantGas 129 if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow { 130 return 0, ErrGasUintOverflow 131 } 132 return gas, nil 133 } 134 return gas, nil 135 } 136 137 // gasEip2929AccountCheck checks whether the first stack item (as address) is present in the access list. 138 // If it is, this method returns '0', otherwise 'cold-warm' gas, presuming that the opcode using it 139 // is also using 'warm' as constant factor. 140 // This method is used by: 141 // - extcodehash, 142 // - extcodesize, 143 // - (ext) balance 144 func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { 145 addr := common.Address(stack.peek().Bytes20()) 146 // Check slot presence in the access list 147 if !evm.StateDB.AddressInAccessList(addr) { 148 // If the caller cannot afford the cost, this change will be rolled back 149 evm.StateDB.AddAddressToAccessList(addr) 150 // The warm storage read cost is already charged as constantGas 151 return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil 152 } 153 return 0, nil 154 } 155 156 func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc { 157 return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { 158 addr := common.Address(stack.Back(addressPosition).Bytes20()) 159 // Check slot presence in the access list 160 warmAccess := evm.StateDB.AddressInAccessList(addr) 161 // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so 162 // the cost to charge for cold access, if any, is Cold - Warm 163 coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 164 if !warmAccess { 165 evm.StateDB.AddAddressToAccessList(addr) 166 // Charge the remaining difference here already, to correctly calculate available 167 // gas for call 168 if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { 169 return 0, ErrOutOfGas 170 } 171 } 172 // Now call the old calculator, which takes into account 173 // - create new account 174 // - transfer value 175 // - memory expansion 176 // - 63/64ths rule 177 gas, err := oldCalculator(evm, contract, stack, mem, memorySize) 178 if warmAccess || err != nil { 179 return gas, err 180 } 181 // In case of a cold access, we temporarily add the cold charge back, and also 182 // add it to the returned gas. By adding it to the return, it will be charged 183 // outside of this function, as part of the dynamic gas, and that will make it 184 // also become correctly reported to tracers. 185 contract.Gas += coldCost 186 187 var overflow bool 188 if gas, overflow = math.SafeAdd(gas, coldCost); overflow { 189 return 0, ErrGasUintOverflow 190 } 191 return gas, nil 192 } 193 } 194 195 var ( 196 gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall, 1) 197 gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall, 1) 198 gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall, 1) 199 gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode, 1) 200 gasSelfdestructEIP2929 = makeSelfdestructGasFn(true) 201 // gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds) 202 gasSelfdestructEIP3529 = makeSelfdestructGasFn(false) 203 204 // gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929 205 // 206 // When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys. 207 // If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys. 208 // Additionally, modify the parameters defined in EIP 2200 as follows: 209 // 210 // Parameter Old value New value 211 // SLOAD_GAS 800 = WARM_STORAGE_READ_COST 212 // SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST 213 // 214 //The other parameters defined in EIP 2200 are unchanged. 215 // see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified 216 gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200) 217 218 // gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529 219 // Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800) 220 gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529) 221 ) 222 223 // makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529 224 func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { 225 gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { 226 var ( 227 gas uint64 228 address = common.Address(stack.peek().Bytes20()) 229 ) 230 if !evm.StateDB.AddressInAccessList(address) { 231 // If the caller cannot afford the cost, this change will be rolled back 232 evm.StateDB.AddAddressToAccessList(address) 233 gas = params.ColdAccountAccessCostEIP2929 234 } 235 // if empty and transfers value 236 if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 { 237 gas += params.CreateBySelfdestructGas 238 } 239 if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) { 240 evm.StateDB.AddRefund(params.SelfdestructRefundGas) 241 } 242 return gas, nil 243 } 244 return gasFunc 245 } 246 247 var ( 248 gasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCall) 249 gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCall) 250 gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCall) 251 gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCode) 252 ) 253 254 func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc { 255 return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { 256 var ( 257 total uint64 // total dynamic gas used 258 addr = common.Address(stack.Back(1).Bytes20()) 259 ) 260 261 // Check slot presence in the access list 262 if !evm.StateDB.AddressInAccessList(addr) { 263 evm.StateDB.AddAddressToAccessList(addr) 264 // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so 265 // the cost to charge for cold access, if any, is Cold - Warm 266 coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 267 // Charge the remaining difference here already, to correctly calculate available 268 // gas for call 269 if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { 270 return 0, ErrOutOfGas 271 } 272 total += coldCost 273 } 274 275 // Check if code is a delegation and if so, charge for resolution. 276 if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok { 277 var cost uint64 278 if evm.StateDB.AddressInAccessList(target) { 279 cost = params.WarmStorageReadCostEIP2929 280 } else { 281 evm.StateDB.AddAddressToAccessList(target) 282 cost = params.ColdAccountAccessCostEIP2929 283 } 284 if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { 285 return 0, ErrOutOfGas 286 } 287 total += cost 288 } 289 290 // Now call the old calculator, which takes into account 291 // - create new account 292 // - transfer value 293 // - memory expansion 294 // - 63/64ths rule 295 old, err := oldCalculator(evm, contract, stack, mem, memorySize) 296 if err != nil { 297 return old, err 298 } 299 300 // Temporarily add the gas charge back to the contract and return value. By 301 // adding it to the return, it will be charged outside of this function, as 302 // part of the dynamic gas. This will ensure it is correctly reported to 303 // tracers. 304 contract.Gas += total 305 306 var overflow bool 307 if total, overflow = math.SafeAdd(old, total); overflow { 308 return 0, ErrGasUintOverflow 309 } 310 return total, nil 311 } 312 }