github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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/kisexp/xdchain/common" 23 "github.com/kisexp/xdchain/common/math" 24 "github.com/kisexp/xdchain/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 }