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