integrate evmc 'setStorage'
This commit is contained in:
parent
62f96e9bd4
commit
4aa209113a
|
@ -57,7 +57,8 @@ proc setStorage*(ctx: HostContext, address: EthAddress,
|
|||
address = toEvmc(address)
|
||||
key = toEvmc(key)
|
||||
value = toEvmc(value)
|
||||
ctx.host.set_storage(ctx.context, address.addr, key.addr, value.addr)
|
||||
{.gcsafe.}:
|
||||
ctx.host.set_storage(ctx.context, address.addr, key.addr, value.addr)
|
||||
|
||||
proc getBalance*(ctx: HostContext, address: EthAddress): Uint256 =
|
||||
var address = toEvmc(address)
|
||||
|
|
|
@ -49,28 +49,37 @@ proc hostSetStorageImpl(ctx: Computation, address: var evmc_address,
|
|||
if newValue == currValue:
|
||||
return EVMC_STORAGE_UNCHANGED
|
||||
|
||||
var
|
||||
status = EVMC_STORAGE_MODIFIED
|
||||
let
|
||||
origValue = statedb.getCommittedStorage(storageAddr, slot)
|
||||
InitRefundEIP2200 = gasFees[ctx.fork][GasSset] - gasFees[ctx.fork][GasSload]
|
||||
CleanRefundEIP2200 = gasFees[ctx.fork][GasSreset] - gasFees[ctx.fork][GasSload]
|
||||
ClearRefundEIP2200 = gasFees[ctx.fork][RefundsClear]
|
||||
|
||||
if origValue == currValue or ctx.fork >= FkIstanbul:
|
||||
var
|
||||
gasRefund = 0.GasInt
|
||||
status = EVMC_STORAGE_MODIFIED
|
||||
|
||||
if origValue == currValue or ctx.fork < FkIstanbul:
|
||||
if currValue == 0:
|
||||
status = EVMC_STORAGE_ADDED
|
||||
elif newValue == 0:
|
||||
status = EVMC_STORAGE_DELETED
|
||||
# refunds += sstoreRefundGas
|
||||
gasRefund += ClearRefundEIP2200
|
||||
else:
|
||||
status = EVMC_STORAGE_MODIFIED_AGAIN
|
||||
#if origValue != 0:
|
||||
# if currValue == 0:
|
||||
# refunds -= sstoreRefundGas # Can go negative
|
||||
# if newValue == 0:
|
||||
# refunds += sstoreRefundGas
|
||||
#if origValue == newValue:
|
||||
# if origValue == 0:
|
||||
# refunds += sstoreSetGas - sstoreUnchangedGas
|
||||
# else:
|
||||
# refunds += sstoreResetGas - sstoreUnchangedGas
|
||||
if origValue != 0:
|
||||
if currValue == 0:
|
||||
gasRefund -= ClearRefundEIP2200 # Can go negative
|
||||
if newValue == 0:
|
||||
gasRefund += ClearRefundEIP2200
|
||||
if origValue == newValue:
|
||||
if origValue == 0:
|
||||
gasRefund += InitRefundEIP2200
|
||||
else:
|
||||
gasRefund += CleanRefundEIP2200
|
||||
|
||||
if gasRefund > 0:
|
||||
ctx.gasMeter.refundGas(gasRefund)
|
||||
|
||||
ctx.vmState.mutateStateDB:
|
||||
db.setStorage(storageAddr, slot, newValue)
|
||||
|
|
|
@ -10,6 +10,9 @@ import
|
|||
./utils/[macros_gen_opcodes, utils_numeric],
|
||||
./opcode_values, ./vm_forks, ../../errors
|
||||
|
||||
when defined(evmc_enabled):
|
||||
import evmc/evmc
|
||||
|
||||
# Gas Fee Schedule
|
||||
# Yellow Paper Appendix G - https://ethereum.github.io/yellowpaper/paper.pdf
|
||||
type
|
||||
|
@ -64,9 +67,12 @@ type
|
|||
|
||||
case kind*: Op
|
||||
of Sstore:
|
||||
s_isStorageEmpty*: bool
|
||||
s_currentValue*: Uint256
|
||||
s_originalValue*: Uint256
|
||||
when defined(evmc_enabled):
|
||||
s_status*: evmc_storage_status
|
||||
else:
|
||||
s_isStorageEmpty*: bool
|
||||
s_currentValue*: Uint256
|
||||
s_originalValue*: Uint256
|
||||
of Call, CallCode, DelegateCall, StaticCall:
|
||||
c_isNewAccount*: bool
|
||||
c_gasBalance*: GasInt
|
||||
|
@ -211,73 +217,86 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
|||
func `prefix gasSstore`(value: Uint256, gasParams: Gasparams): GasResult {.nimcall.} =
|
||||
## Value is word to save
|
||||
|
||||
when fork < FkIstanbul:
|
||||
# workaround for static evaluation not working for if expression
|
||||
when defined(evmc_enabled):
|
||||
const
|
||||
gSet = FeeSchedule[GasSset]
|
||||
gSreset = FeeSchedule[GasSreset]
|
||||
sstoreLoad = FeeSchedule[GasSload]
|
||||
sstoreSet = FeeSchedule[GasSset]
|
||||
sstoreReset= FeeSchedule[GasSreset]
|
||||
|
||||
# Gas cost - literal translation of Yellow Paper
|
||||
result.gasCost = if value.isZero.not and gasParams.s_isStorageEmpty:
|
||||
gSet
|
||||
else:
|
||||
gSreset
|
||||
case gasParams.s_status
|
||||
of EVMC_STORAGE_ADDED: result.gasCost = sstoreSet
|
||||
of EVMC_STORAGE_MODIFIED, EVMC_STORAGE_DELETED: result.gasCost = sstoreReset
|
||||
of EVMC_STORAGE_UNCHANGED, EVMC_STORAGE_MODIFIED_AGAIN:
|
||||
result.gasCost = if fork >= FkIstanbul: sstoreLoad else: sstoreReset
|
||||
|
||||
# Refund
|
||||
if value.isZero and not gasParams.s_isStorageEmpty:
|
||||
result.gasRefund = static(FeeSchedule[RefundSclear])
|
||||
else:
|
||||
# 0. If *gasleft* is less than or equal to 2300, fail the current call.
|
||||
# 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted.
|
||||
# 2. If current value does not equal new value:
|
||||
# 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
|
||||
# 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted.
|
||||
# 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter.
|
||||
# 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses:
|
||||
# 2.2.1. If original value is not 0:
|
||||
# 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0.
|
||||
# 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter.
|
||||
# 2.2.2. If original value equals new value (this storage slot is reset):
|
||||
# 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter.
|
||||
# 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter.
|
||||
const
|
||||
NoopGasEIP2200 = FeeSchedule[GasSload] # if the value doesn't change.
|
||||
DirtyGasEIP2200 = FeeSchedule[GasSload] # if a dirty value is changed.
|
||||
InitGasEIP2200 = FeeSchedule[GasSset] # from clean zero to non-zero
|
||||
InitRefundEIP2200 = FeeSchedule[GasSset] - FeeSchedule[GasSload] # resetting to the original zero value
|
||||
CleanGasEIP2200 = FeeSchedule[GasSreset]# from clean non-zero to something else
|
||||
CleanRefundEIP2200 = FeeSchedule[GasSreset] - FeeSchedule[GasSload] # resetting to the original non-zero value
|
||||
ClearRefundEIP2200 = FeeSchedule[RefundSclear]# clearing an originally existing storage slot
|
||||
when fork < FkIstanbul:
|
||||
# workaround for static evaluation not working for if expression
|
||||
const
|
||||
gSet = FeeSchedule[GasSset]
|
||||
gSreset = FeeSchedule[GasSreset]
|
||||
|
||||
# Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
if gasParams.s_currentValue == value: # noop (1)
|
||||
result.gasCost = NoopGasEIP2200
|
||||
return
|
||||
# Gas cost - literal translation of Yellow Paper
|
||||
result.gasCost = if value.isZero.not and gasParams.s_isStorageEmpty:
|
||||
gSet
|
||||
else:
|
||||
gSreset
|
||||
|
||||
if gasParams.s_originalValue == gasParams.s_currentValue:
|
||||
if gasParams.s_originalValue.isZero: # create slot (2.1.1)
|
||||
result.gasCost = InitGasEIP2200
|
||||
# Refund
|
||||
if value.isZero and not gasParams.s_isStorageEmpty:
|
||||
result.gasRefund = static(FeeSchedule[RefundsClear])
|
||||
else:
|
||||
# 0. If *gasleft* is less than or equal to 2300, fail the current call.
|
||||
# 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted.
|
||||
# 2. If current value does not equal new value:
|
||||
# 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
|
||||
# 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted.
|
||||
# 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter.
|
||||
# 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses:
|
||||
# 2.2.1. If original value is not 0:
|
||||
# 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0.
|
||||
# 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter.
|
||||
# 2.2.2. If original value equals new value (this storage slot is reset):
|
||||
# 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter.
|
||||
# 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter.
|
||||
const
|
||||
NoopGasEIP2200 = FeeSchedule[GasSload] # if the value doesn't change.
|
||||
DirtyGasEIP2200 = FeeSchedule[GasSload] # if a dirty value is changed.
|
||||
InitGasEIP2200 = FeeSchedule[GasSset] # from clean zero to non-zero
|
||||
InitRefundEIP2200 = FeeSchedule[GasSset] - FeeSchedule[GasSload] # resetting to the original zero value
|
||||
CleanGasEIP2200 = FeeSchedule[GasSreset]# from clean non-zero to something else
|
||||
CleanRefundEIP2200 = FeeSchedule[GasSreset] - FeeSchedule[GasSload] # resetting to the original non-zero value
|
||||
ClearRefundEIP2200 = FeeSchedule[RefundsClear]# clearing an originally existing storage slot
|
||||
|
||||
# Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
if gasParams.s_currentValue == value: # noop (1)
|
||||
result.gasCost = NoopGasEIP2200
|
||||
return
|
||||
|
||||
if value.isZero: # delete slot (2.1.2b)
|
||||
result.gasRefund = ClearRefundEIP2200
|
||||
if gasParams.s_originalValue == gasParams.s_currentValue:
|
||||
if gasParams.s_originalValue.isZero: # create slot (2.1.1)
|
||||
result.gasCost = InitGasEIP2200
|
||||
return
|
||||
|
||||
result.gasCost = CleanGasEIP2200 # write existing slot (2.1.2)
|
||||
return
|
||||
if value.isZero: # delete slot (2.1.2b)
|
||||
result.gasRefund = ClearRefundEIP2200
|
||||
|
||||
if not gasParams.s_originalValue.isZero:
|
||||
if gasParams.s_currentValue.isZero: # recreate slot (2.2.1.1)
|
||||
result.gasRefund -= ClearRefundEIP2200
|
||||
if value.isZero: # delete slot (2.2.1.2)
|
||||
result.gasRefund += ClearRefundEIP2200
|
||||
result.gasCost = CleanGasEIP2200 # write existing slot (2.1.2)
|
||||
return
|
||||
|
||||
if gasParams.s_originalValue == value:
|
||||
if gasParams.s_originalValue.isZero: # reset to original inexistent slot (2.2.2.1)
|
||||
result.gasRefund += InitRefundEIP2200
|
||||
else: # reset to original existing slot (2.2.2.2)
|
||||
result.gasRefund += CleanRefundEIP2200
|
||||
if not gasParams.s_originalValue.isZero:
|
||||
if gasParams.s_currentValue.isZero: # recreate slot (2.2.1.1)
|
||||
result.gasRefund -= ClearRefundEIP2200
|
||||
if value.isZero: # delete slot (2.2.1.2)
|
||||
result.gasRefund += ClearRefundEIP2200
|
||||
|
||||
result.gasCost = DirtyGasEIP2200 # dirty update (2.2)
|
||||
if gasParams.s_originalValue == value:
|
||||
if gasParams.s_originalValue.isZero: # reset to original inexistent slot (2.2.2.1)
|
||||
result.gasRefund += InitRefundEIP2200
|
||||
else: # reset to original existing slot (2.2.2.2)
|
||||
result.gasRefund += CleanRefundEIP2200
|
||||
|
||||
result.gasCost = DirtyGasEIP2200 # dirty update (2.2)
|
||||
|
||||
func `prefix gasLog0`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} =
|
||||
result = `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength)
|
||||
|
|
|
@ -426,23 +426,69 @@ op sload, inline = true, slot:
|
|||
## 0x54, Load word from storage.
|
||||
push: c.getStorage(slot)
|
||||
|
||||
op sstore, inline = false, slot, value:
|
||||
## 0x55, Save word to storage.
|
||||
checkInStaticContext(c)
|
||||
|
||||
let currentValue = c.getStorage(slot)
|
||||
template sstoreImpl(c: Computation, slot, newValue: Uint256) =
|
||||
let currentValue {.inject.} = c.getStorage(slot)
|
||||
|
||||
let
|
||||
gasParam = GasParams(kind: Op.Sstore, s_isStorageEmpty: currentValue.isZero)
|
||||
(gasCost, gasRefund) = c.gasCosts[Sstore].c_handler(value, gasParam)
|
||||
(gasCost, gasRefund) = c.gasCosts[Sstore].c_handler(newValue, gasParam)
|
||||
|
||||
c.gasMeter.consumeGas(gasCost, &"SSTORE: {c.msg.contractAddress}[{slot}] -> {value} ({currentValue})")
|
||||
c.gasMeter.consumeGas(gasCost, &"SSTORE: {c.msg.contractAddress}[{slot}] -> {newValue} ({currentValue})")
|
||||
|
||||
if gasRefund > 0:
|
||||
c.gasMeter.refundGas(gasRefund)
|
||||
|
||||
c.vmState.mutateStateDB:
|
||||
db.setStorage(c.msg.contractAddress, slot, value)
|
||||
db.setStorage(c.msg.contractAddress, slot, newValue)
|
||||
|
||||
template sstoreEvmc(c: Computation, slot, newValue: Uint256) =
|
||||
let
|
||||
status = c.host.setStorage(c.msg.contractAddress, slot, newValue)
|
||||
gasParam = GasParams(kind: Op.Sstore, s_status: status)
|
||||
gasCost = c.gasCosts[Sstore].c_handler(newValue, gasParam)[0]
|
||||
|
||||
c.gasMeter.consumeGas(gasCost, &"SSTORE: {c.msg.contractAddress}[{slot}] -> {newValue}")
|
||||
|
||||
op sstore, inline = false, slot, newValue:
|
||||
## 0x55, Save word to storage.
|
||||
checkInStaticContext(c)
|
||||
|
||||
when evmc_enabled:
|
||||
sstoreEvmc(c, slot, newValue)
|
||||
else:
|
||||
sstoreImpl(c, slot, newValue)
|
||||
|
||||
template sstoreEIP2200Impl(c: Computation, slot, newValue: Uint256) =
|
||||
let stateDB = c.vmState.readOnlyStateDB
|
||||
let currentValue {.inject.} = c.getStorage(slot)
|
||||
|
||||
let
|
||||
gasParam = GasParams(kind: Op.Sstore,
|
||||
s_isStorageEmpty: currentValue.isZero,
|
||||
s_currentValue: currentValue,
|
||||
s_originalValue: stateDB.getCommittedStorage(c.msg.contractAddress, slot)
|
||||
)
|
||||
(gasCost, gasRefund) = c.gasCosts[Sstore].c_handler(newValue, gasParam)
|
||||
|
||||
c.gasMeter.consumeGas(gasCost, &"SSTORE EIP2200: {c.msg.contractAddress}[{slot}] -> {newValue} ({currentValue})")
|
||||
|
||||
if gasRefund != 0:
|
||||
c.gasMeter.refundGas(gasRefund)
|
||||
|
||||
c.vmState.mutateStateDB:
|
||||
db.setStorage(c.msg.contractAddress, slot, newValue)
|
||||
|
||||
op sstoreEIP2200, inline = false, slot, newValue:
|
||||
checkInStaticContext(c)
|
||||
const SentryGasEIP2200 = 2300 # Minimum gas required to be present for an SSTORE call, not consumed
|
||||
|
||||
if c.gasMeter.gasRemaining <= SentryGasEIP2200:
|
||||
raise newException(OutOfGas, "Gas not enough to perform EIP2200 SSTORE")
|
||||
|
||||
when evmc_enabled:
|
||||
sstoreEvmc(c, slot, newValue)
|
||||
else:
|
||||
sstoreEIP2200Impl(c, slot, newValue)
|
||||
|
||||
proc jumpImpl(c: Computation, jumpTarget: UInt256) =
|
||||
if jumpTarget >= c.code.len.u256:
|
||||
|
@ -874,29 +920,3 @@ op sarOp, inline = true:
|
|||
op extCodeHash, inline = true:
|
||||
let address = c.stack.popAddress()
|
||||
push: c.getCodeHash(address)
|
||||
|
||||
op sstoreEIP2200, inline = false, slot, value:
|
||||
checkInStaticContext(c)
|
||||
const SentryGasEIP2200 = 2300 # Minimum gas required to be present for an SSTORE call, not consumed
|
||||
|
||||
if c.gasMeter.gasRemaining <= SentryGasEIP2200:
|
||||
raise newException(OutOfGas, "Gas not enough to perform EIP2200 SSTORE")
|
||||
|
||||
let stateDB = c.vmState.readOnlyStateDB
|
||||
let currentValue = c.getStorage(slot)
|
||||
|
||||
let
|
||||
gasParam = GasParams(kind: Op.Sstore,
|
||||
s_isStorageEmpty: currentValue.isZero,
|
||||
s_currentValue: currentValue,
|
||||
s_originalValue: stateDB.getCommittedStorage(c.msg.contractAddress, slot)
|
||||
)
|
||||
(gasCost, gasRefund) = c.gasCosts[Sstore].c_handler(value, gasParam)
|
||||
|
||||
c.gasMeter.consumeGas(gasCost, &"SSTORE EIP2200: {c.msg.contractAddress}[{slot}] -> {value} ({currentValue})")
|
||||
|
||||
if gasRefund != 0:
|
||||
c.gasMeter.refundGas(gasRefund)
|
||||
|
||||
c.vmState.mutateStateDB:
|
||||
db.setStorage(c.msg.contractAddress, slot, value)
|
||||
|
|
Loading…
Reference in New Issue