github.com/amazechain/amc@v0.1.3/internal/vm/operations_acl.go (about) 1 // Copyright 2023 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain 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 AmazeChain 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 AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package vm 18 19 import ( 20 "errors" 21 "github.com/amazechain/amc/common/math" 22 "github.com/amazechain/amc/common/types" 23 "github.com/amazechain/amc/internal/vm/stack" 24 "github.com/amazechain/amc/params" 25 26 "github.com/holiman/uint256" 27 ) 28 29 func makeGasSStoreFunc(clearingRefund uint64) gasFunc { 30 return func(evm VMInterpreter, contract *Contract, stack *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 = types.Hash(x.Bytes32()) 39 current uint256.Int 40 cost = uint64(0) 41 ) 42 evm.IntraBlockState().GetState(contract.Address(), &slot, ¤t) 43 // Check slot presence in the access list 44 if addrPresent, slotPresent := evm.IntraBlockState().SlotInAccessList(contract.Address(), slot); !slotPresent { 45 cost = params.ColdSloadCostEIP2929 46 // If the caller cannot afford the cost, this change will be rolled back 47 evm.IntraBlockState().AddSlotToAccessList(contract.Address(), slot) 48 if !addrPresent { 49 // Once we're done with YOLOv2 and schedule this for mainnet, might 50 // be good to remove this panic here, which is just really a 51 // canary to have during testing 52 panic("impossible case: address was not present in access list during sstore op") 53 } 54 } 55 var value uint256.Int 56 value.Set(y) 57 58 if current.Eq(&value) { // noop (1) 59 // EIP 2200 original clause: 60 // return params.SloadGasEIP2200, nil 61 return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS 62 } 63 var original uint256.Int 64 slotCommited := types.Hash(x.Bytes32()) 65 evm.IntraBlockState().GetCommittedState(contract.Address(), &slotCommited, &original) 66 if original.Eq(¤t) { 67 if original.IsZero() { // create slot (2.1.1) 68 return cost + params.SstoreSetGasEIP2200, nil 69 } 70 if value.IsZero() { // delete slot (2.1.2b) 71 evm.IntraBlockState().AddRefund(clearingRefund) 72 } 73 // EIP-2200 original clause: 74 // return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) 75 return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2) 76 } 77 if !original.IsZero() { 78 if current.IsZero() { // recreate slot (2.2.1.1) 79 evm.IntraBlockState().SubRefund(clearingRefund) 80 } else if value.IsZero() { // delete slot (2.2.1.2) 81 evm.IntraBlockState().AddRefund(clearingRefund) 82 } 83 } 84 if original.Eq(&value) { 85 if original.IsZero() { // reset to original inexistent slot (2.2.2.1) 86 // EIP 2200 Original clause: 87 //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) 88 evm.IntraBlockState().AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929) 89 } else { // reset to original existing slot (2.2.2.2) 90 // EIP 2200 Original clause: 91 // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) 92 // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST) 93 // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST 94 // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST 95 evm.IntraBlockState().AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929) 96 } 97 } 98 // EIP-2200 original clause: 99 //return params.SloadGasEIP2200, nil // dirty update (2.2) 100 return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2) 101 } 102 } 103 104 // gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929 105 // For SLOAD, if the (address, storage_key) pair (where address is the address of the contract 106 // whose storage is being read) is not yet in accessed_storage_keys, 107 // charge 2100 gas and add the pair to accessed_storage_keys. 108 // If the pair is already in accessed_storage_keys, charge 100 gas. 109 func gasSLoadEIP2929(evm VMInterpreter, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) { 110 loc := stack.Peek() 111 slot := types.Hash(loc.Bytes32()) 112 // Check slot presence in the access list 113 if _, slotPresent := evm.IntraBlockState().SlotInAccessList(contract.Address(), slot); !slotPresent { 114 // If the caller cannot afford the cost, this change will be rolled back 115 // If he does afford it, we can skip checking the same thing later on, during execution 116 evm.IntraBlockState().AddSlotToAccessList(contract.Address(), slot) 117 return params.ColdSloadCostEIP2929, nil 118 } 119 return params.WarmStorageReadCostEIP2929, nil 120 } 121 122 // gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929 123 // EIP spec: 124 // > If the target is not in accessed_addresses, 125 // > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses. 126 // > Otherwise, charge WARM_STORAGE_READ_COST gas. 127 func gasExtCodeCopyEIP2929(evm VMInterpreter, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) { 128 // memory expansion first (dynamic part of pre-2929 implementation) 129 gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize) 130 if err != nil { 131 return 0, err 132 } 133 addr := types.Address(stack.Peek().Bytes20()) 134 // Check slot presence in the access list 135 if !evm.IntraBlockState().AddressInAccessList(addr) { 136 evm.IntraBlockState().AddAddressToAccessList(addr) 137 var overflow bool 138 // We charge (cold-warm), since 'warm' is already charged as constantGas 139 if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow { 140 return 0, ErrGasUintOverflow 141 } 142 return gas, nil 143 } 144 return gas, nil 145 } 146 147 // gasEip2929AccountCheck checks whether the first stack item (as address) is present in the access list. 148 // If it is, this method returns '0', otherwise 'cold-warm' gas, presuming that the opcode using it 149 // is also using 'warm' as constant factor. 150 // This method is used by: 151 // - extcodehash, 152 // - extcodesize, 153 // - (ext) balance 154 func gasEip2929AccountCheck(evm VMInterpreter, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) { 155 addr := types.Address(stack.Peek().Bytes20()) 156 // Check slot presence in the access list 157 if !evm.IntraBlockState().AddressInAccessList(addr) { 158 // If the caller cannot afford the cost, this change will be rolled back 159 evm.IntraBlockState().AddAddressToAccessList(addr) 160 // The warm storage read cost is already charged as constantGas 161 return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil 162 } 163 return 0, nil 164 } 165 166 func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { 167 return func(evm VMInterpreter, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) { 168 addr := types.Address(stack.Back(1).Bytes20()) 169 // Check slot presence in the access list 170 warmAccess := evm.IntraBlockState().AddressInAccessList(addr) 171 // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so 172 // the cost to charge for cold access, if any, is Cold - Warm 173 coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 174 if !warmAccess { 175 evm.IntraBlockState().AddAddressToAccessList(addr) 176 // Charge the remaining difference here already, to correctly calculate available 177 // gas for call 178 if !contract.UseGas(coldCost) { 179 return 0, ErrOutOfGas 180 } 181 } 182 // Now call the old calculator, which takes into account 183 // - create new account 184 // - transfer value 185 // - memory expansion 186 // - 63/64ths rule 187 gas, err := oldCalculator(evm, contract, stack, mem, memorySize) 188 if warmAccess || err != nil { 189 return gas, err 190 } 191 // In case of a cold access, we temporarily add the cold charge back, and also 192 // add it to the returned gas. By adding it to the return, it will be charged 193 // outside of this function, as part of the dynamic gas, and that will make it 194 // also become correctly reported to tracers. 195 contract.Gas += coldCost 196 return gas + coldCost, nil 197 } 198 } 199 200 var ( 201 gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall) 202 gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall) 203 gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall) 204 gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode) 205 gasSelfdestructEIP2929 = makeSelfdestructGasFn(true) 206 // gasSelfdestructEIP3529 implements the changes in EIP-2539 (no refunds) 207 gasSelfdestructEIP3529 = makeSelfdestructGasFn(false) 208 209 // gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929 210 // 211 // When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys. 212 // If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys. 213 // Additionally, modify the parameters defined in EIP 2200 as follows: 214 // 215 // Parameter Old value New value 216 // SLOAD_GAS 800 = WARM_STORAGE_READ_COST 217 // SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST 218 // 219 //The other parameters defined in EIP 2200 are unchanged. 220 // see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified 221 gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200) 222 223 // gasSStoreEIP2539 implements gas cost for SSTORE according to EPI-2539 224 // Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800) 225 gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529) 226 ) 227 228 // makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539 229 func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { 230 gasFunc := func(evm VMInterpreter, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) { 231 var ( 232 gas uint64 233 address = types.Address(stack.Peek().Bytes20()) 234 ) 235 if !evm.IntraBlockState().AddressInAccessList(address) { 236 // If the caller cannot afford the cost, this change will be rolled back 237 evm.IntraBlockState().AddAddressToAccessList(address) 238 gas = params.ColdAccountAccessCostEIP2929 239 } 240 // if empty and transfers value 241 if evm.IntraBlockState().Empty(address) && !evm.IntraBlockState().GetBalance(contract.Address()).IsZero() { 242 gas += params.CreateBySelfdestructGas 243 } 244 if refundsEnabled && !evm.IntraBlockState().HasSelfdestructed(contract.Address()) { 245 evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas) 246 } 247 return gas, nil 248 } 249 return gasFunc 250 }