implement EIP2200's SSTORE and gas cost

This commit is contained in:
andri lim 2019-11-12 20:24:52 +07:00 committed by zah
parent c0c62b94b8
commit 61f340ae87
3 changed files with 58 additions and 1 deletions

View File

@ -208,6 +208,10 @@ proc isDeadAccount*(db: AccountStateDB, address: EthAddress): bool =
else:
result = true
proc getCommittedStorage*(db: AccountStateDB, address: EthAddress, slot: UInt256): UInt256 =
discard
# TODO: stub
proc rootHash*(db: ReadOnlyStateDB): KeccakHash {.borrow.}
proc getAccount*(db: ReadOnlyStateDB, address: EthAddress): Account {.borrow.}
proc getCodeHash*(db: ReadOnlyStateDB, address: EthAddress): Hash256 {.borrow.}
@ -220,3 +224,4 @@ proc hasCodeOrNonce*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc accountExists*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc isDeadAccount*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc isEmptyAccount*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc getCommittedStorage*(db: ReadOnlyStateDB, address: EthAddress, slot: UInt256): UInt256 {.borrow.}

View File

@ -65,6 +65,8 @@ type
case kind*: Op
of Sstore:
s_isStorageEmpty*: bool
s_currentValue*: Uint256
s_originalValue*: Uint256
of Call, CallCode, DelegateCall, StaticCall:
c_isNewAccount*: bool
c_gasBalance*: GasInt
@ -247,6 +249,34 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
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 gasParams.s_originalValue == gasParams.s_currentValue:
if gasParams.s_originalValue.isZero: # create slot (2.1.1)
result.gasCost = InitGasEIP2200
return
if value.isZero: # delete slot (2.1.2b)
result.gasRefund = ClearRefundEIP2200
result.gasCost = CleanGasEIP2200 # write existing slot (2.1.2)
return
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
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)

View File

@ -933,4 +933,26 @@ op extCodeHash, inline = true:
op sstoreEIP2200, inline = false, slot, value:
checkInStaticContext(computation)
# TODO: stub
const SentryGasEIP2200 = 2300 # Minimum gas required to be present for an SSTORE call, not consumed
if computation.gasMeter.gasRemaining < SentryGasEIP2200:
raise newException(OutOfGas, "Gas not enough to perform EIP2200 SSTORE")
let stateDB = computation.vmState.readOnlyStateDB
let (currentValue, existing) = stateDB.getStorage(computation.msg.storageAddress, slot)
let
gasParam = GasParams(kind: Op.Sstore,
s_isStorageEmpty: currentValue.isZero,
s_currentValue: currentValue,
s_originalValue: stateDB.getCommittedStorage(computation.msg.storageAddress, slot)
)
(gasCost, gasRefund) = computation.gasCosts[Sstore].c_handler(value, gasParam)
computation.gasMeter.consumeGas(gasCost, &"SSTORE EIP2200: {computation.msg.storageAddress}[{slot}] -> {value} ({currentValue})")
if gasRefund > 0:
computation.gasMeter.refundGas(gasRefund)
computation.vmState.mutateStateDB:
db.setStorage(computation.msg.storageAddress, slot, value)