gitlab.com/flarenetwork/coreth@v0.1.1/core/vm/operations_acl.go (about)

     1  // (c) 2019-2021, 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 2020 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 vm
    28  
    29  import (
    30  	"errors"
    31  
    32  	"github.com/ethereum/go-ethereum/common"
    33  	"github.com/ethereum/go-ethereum/common/math"
    34  	"gitlab.com/flarenetwork/coreth/params"
    35  )
    36  
    37  // gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
    38  //
    39  // When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
    40  // If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
    41  // Additionally, modify the parameters defined in EIP 2200 as follows:
    42  //
    43  // Parameter 	Old value 	New value
    44  // SLOAD_GAS 	800 	= WARM_STORAGE_READ_COST
    45  // SSTORE_RESET_GAS 	5000 	5000 - COLD_SLOAD_COST
    46  //
    47  //The other parameters defined in EIP 2200 are unchanged.
    48  // see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
    49  func gasSStoreEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
    50  	// If we fail the minimum gas availability invariant, fail (0)
    51  	if contract.Gas <= params.SstoreSentryGasEIP2200 {
    52  		return 0, errors.New("not enough gas for reentrancy sentry")
    53  	}
    54  	// Gas sentry honoured, do the actual gas calculation based on the stored value
    55  	var (
    56  		y, x    = stack.Back(1), stack.peek()
    57  		slot    = common.Hash(x.Bytes32())
    58  		current = evm.StateDB.GetState(contract.Address(), slot)
    59  		cost    = uint64(0)
    60  	)
    61  	// Check slot presence in the access list
    62  	if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
    63  		cost = params.ColdSloadCostEIP2929
    64  		// If the caller cannot afford the cost, this change will be rolled back
    65  		evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
    66  		if !addrPresent {
    67  			// Once we're done with YOLOv2 and schedule this for mainnet, might
    68  			// be good to remove this panic here, which is just really a
    69  			// canary to have during testing
    70  			panic("impossible case: address was not present in access list during sstore op")
    71  		}
    72  	}
    73  	value := common.Hash(y.Bytes32())
    74  
    75  	if current == value { // noop (1)
    76  		// EIP 2200 original clause:
    77  		//		return params.SloadGasEIP2200, nil
    78  		return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
    79  	}
    80  	original := evm.StateDB.GetCommittedStateAP1(contract.Address(), x.Bytes32())
    81  	if original == current {
    82  		if original == (common.Hash{}) { // create slot (2.1.1)
    83  			return cost + params.SstoreSetGasEIP2200, nil
    84  		}
    85  		// EIP-2200 original clause:
    86  		//		return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
    87  		return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
    88  	}
    89  
    90  	// EIP-2200 original clause:
    91  	//return params.SloadGasEIP2200, nil // dirty update (2.2)
    92  	return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
    93  }
    94  
    95  // gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
    96  // For SLOAD, if the (address, storage_key) pair (where address is the address of the contract
    97  // whose storage is being read) is not yet in accessed_storage_keys,
    98  // charge 2100 gas and add the pair to accessed_storage_keys.
    99  // If the pair is already in accessed_storage_keys, charge 100 gas.
   100  func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   101  	loc := stack.peek()
   102  	slot := common.Hash(loc.Bytes32())
   103  	// Check slot presence in the access list
   104  	if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
   105  		// If the caller cannot afford the cost, this change will be rolled back
   106  		// If he does afford it, we can skip checking the same thing later on, during execution
   107  		evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
   108  		return params.ColdSloadCostEIP2929, nil
   109  	}
   110  	return params.WarmStorageReadCostEIP2929, nil
   111  }
   112  
   113  // gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
   114  // EIP spec:
   115  // > If the target is not in accessed_addresses,
   116  // > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
   117  // > Otherwise, charge WARM_STORAGE_READ_COST gas.
   118  func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   119  	// memory expansion first (dynamic part of pre-2929 implementation)
   120  	gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
   121  	if err != nil {
   122  		return 0, err
   123  	}
   124  	addr := common.Address(stack.peek().Bytes20())
   125  	// Check slot presence in the access list
   126  	if !evm.StateDB.AddressInAccessList(addr) {
   127  		evm.StateDB.AddAddressToAccessList(addr)
   128  		var overflow bool
   129  		// We charge (cold-warm), since 'warm' is already charged as constantGas
   130  		if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
   131  			return 0, ErrGasUintOverflow
   132  		}
   133  		return gas, nil
   134  	}
   135  	return gas, nil
   136  }
   137  
   138  // gasEip2929AccountCheck checks whether the first stack item (as address) is present in the access list.
   139  // If it is, this method returns '0', otherwise 'cold-warm' gas, presuming that the opcode using it
   140  // is also using 'warm' as constant factor.
   141  // This method is used by:
   142  // - extcodehash,
   143  // - extcodesize,
   144  // - (ext) balance
   145  func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   146  	addr := common.Address(stack.peek().Bytes20())
   147  	// Check slot presence in the access list
   148  	if !evm.StateDB.AddressInAccessList(addr) {
   149  		// If the caller cannot afford the cost, this change will be rolled back
   150  		evm.StateDB.AddAddressToAccessList(addr)
   151  		// The warm storage read cost is already charged as constantGas
   152  		return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
   153  	}
   154  	return 0, nil
   155  }
   156  
   157  func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
   158  	return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   159  		addr := common.Address(stack.Back(1).Bytes20())
   160  		// Check slot presence in the access list
   161  		warmAccess := evm.StateDB.AddressInAccessList(addr)
   162  		// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
   163  		// the cost to charge for cold access, if any, is Cold - Warm
   164  		coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
   165  		if !warmAccess {
   166  			evm.StateDB.AddAddressToAccessList(addr)
   167  			// Charge the remaining difference here already, to correctly calculate available
   168  			// gas for call
   169  			if !contract.UseGas(coldCost) {
   170  				return 0, ErrOutOfGas
   171  			}
   172  		}
   173  		// Now call the old calculator, which takes into account
   174  		// - create new account
   175  		// - transfer value
   176  		// - memory expansion
   177  		// - 63/64ths rule
   178  		gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
   179  		if warmAccess || err != nil {
   180  			return gas, err
   181  		}
   182  		// In case of a cold access, we temporarily add the cold charge back, and also
   183  		// add it to the returned gas. By adding it to the return, it will be charged
   184  		// outside of this function, as part of the dynamic gas, and that will make it
   185  		// also become correctly reported to tracers.
   186  		contract.Gas += coldCost
   187  		return gas + coldCost, nil
   188  	}
   189  }
   190  
   191  var (
   192  	gasCallEIP2929         = makeCallVariantGasCallEIP2929(gasCall)
   193  	gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
   194  	gasStaticCallEIP2929   = makeCallVariantGasCallEIP2929(gasStaticCall)
   195  	gasCallCodeEIP2929     = makeCallVariantGasCallEIP2929(gasCallCode)
   196  )
   197  
   198  func gasSelfdestructEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   199  	var (
   200  		gas     uint64
   201  		address = common.Address(stack.peek().Bytes20())
   202  	)
   203  	if !evm.StateDB.AddressInAccessList(address) {
   204  		// If the caller cannot afford the cost, this change will be rolled back
   205  		evm.StateDB.AddAddressToAccessList(address)
   206  		gas = params.ColdAccountAccessCostEIP2929
   207  	}
   208  	// if empty and transfers value
   209  	if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
   210  		gas += params.CreateBySelfdestructGas
   211  	}
   212  
   213  	return gas, nil
   214  }