integrate evmc 'setStorage'

This commit is contained in:
andri lim 2020-01-17 21:49:22 +07:00 committed by zah
parent 62f96e9bd4
commit 4aa209113a
4 changed files with 156 additions and 107 deletions

View File

@ -57,6 +57,7 @@ proc setStorage*(ctx: HostContext, address: EthAddress,
address = toEvmc(address)
key = toEvmc(key)
value = toEvmc(value)
{.gcsafe.}:
ctx.host.set_storage(ctx.context, address.addr, key.addr, value.addr)
proc getBalance*(ctx: HostContext, address: EthAddress): Uint256 =

View File

@ -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)

View File

@ -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,6 +67,9 @@ type
case kind*: Op
of Sstore:
when defined(evmc_enabled):
s_status*: evmc_storage_status
else:
s_isStorageEmpty*: bool
s_currentValue*: Uint256
s_originalValue*: Uint256
@ -211,6 +217,19 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
func `prefix gasSstore`(value: Uint256, gasParams: Gasparams): GasResult {.nimcall.} =
## Value is word to save
when defined(evmc_enabled):
const
sstoreLoad = FeeSchedule[GasSload]
sstoreSet = FeeSchedule[GasSset]
sstoreReset= FeeSchedule[GasSreset]
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
else:
when fork < FkIstanbul:
# workaround for static evaluation not working for if expression
const
@ -225,7 +244,7 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
# Refund
if value.isZero and not gasParams.s_isStorageEmpty:
result.gasRefund = static(FeeSchedule[RefundSclear])
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.
@ -247,7 +266,7 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
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
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)

View File

@ -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)