mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-02 15:24:01 +00:00
parent
94ec3349a4
commit
4b142ac52d
@ -18,3 +18,6 @@ type
|
|||||||
FkIstanbul = "Istanbul"
|
FkIstanbul = "Istanbul"
|
||||||
FkBerlin = "Berlin"
|
FkBerlin = "Berlin"
|
||||||
FkLondon = "London"
|
FkLondon = "London"
|
||||||
|
FkParis = "Paris"
|
||||||
|
FkShanghai = "Shanghai"
|
||||||
|
FkCancun = "Cancun"
|
||||||
|
@ -38,7 +38,10 @@ const
|
|||||||
eth2, # FkPetersburg
|
eth2, # FkPetersburg
|
||||||
eth2, # FkIstanbul
|
eth2, # FkIstanbul
|
||||||
eth2, # FkBerlin
|
eth2, # FkBerlin
|
||||||
eth2 # FkLondon
|
eth2, # FkLondon
|
||||||
|
eth0, # FkParis
|
||||||
|
eth0, # FkShanghai
|
||||||
|
eth0, # FkCancun
|
||||||
]
|
]
|
||||||
|
|
||||||
{.push raises: [Defect].}
|
{.push raises: [Defect].}
|
||||||
|
@ -53,8 +53,8 @@ proc hostToComputationMessage*(msg: EvmcMessage): Message =
|
|||||||
depth: msg.depth,
|
depth: msg.depth,
|
||||||
gas: msg.gas,
|
gas: msg.gas,
|
||||||
sender: msg.sender.fromEvmc,
|
sender: msg.sender.fromEvmc,
|
||||||
contractAddress: msg.destination.fromEvmc,
|
contractAddress: msg.recipient.fromEvmc,
|
||||||
codeAddress: msg.destination.fromEvmc,
|
codeAddress: msg.code_address.fromEvmc,
|
||||||
value: msg.value.fromEvmc,
|
value: msg.value.fromEvmc,
|
||||||
# When input size is zero, input data pointer may be null.
|
# When input size is zero, input data pointer may be null.
|
||||||
data: if msg.input_size <= 0: @[]
|
data: if msg.input_size <= 0: @[]
|
||||||
@ -122,18 +122,19 @@ proc setupHost(call: CallParams): TransactionHost =
|
|||||||
let host = TransactionHost(
|
let host = TransactionHost(
|
||||||
vmState: vmState,
|
vmState: vmState,
|
||||||
msg: EvmcMessage(
|
msg: EvmcMessage(
|
||||||
kind: if call.isCreate: EVMC_CREATE else: EVMC_CALL,
|
kind: if call.isCreate: EVMC_CREATE else: EVMC_CALL,
|
||||||
# Default: flags: {},
|
# Default: flags: {},
|
||||||
# Default: depth: 0,
|
# Default: depth: 0,
|
||||||
gas: call.gasLimit - intrinsicGas,
|
gas: call.gasLimit - intrinsicGas,
|
||||||
destination: call.to.toEvmc,
|
recipient: call.to.toEvmc,
|
||||||
sender: call.sender.toEvmc,
|
code_address: call.to.toEvmc,
|
||||||
value: call.value.toEvmc,
|
sender: call.sender.toEvmc,
|
||||||
|
value: call.value.toEvmc,
|
||||||
)
|
)
|
||||||
# All other defaults in `TransactionHost` are fine.
|
# All other defaults in `TransactionHost` are fine.
|
||||||
)
|
)
|
||||||
|
|
||||||
# Generate new contract address, prepare code, and update message `destination`
|
# Generate new contract address, prepare code, and update message `recipient`
|
||||||
# with the contract address. This differs from the previous Nimbus EVM API.
|
# with the contract address. This differs from the previous Nimbus EVM API.
|
||||||
# Guarded under `evmc_enabled` for now so it doesn't break vm2.
|
# Guarded under `evmc_enabled` for now so it doesn't break vm2.
|
||||||
when defined(evmc_enabled):
|
when defined(evmc_enabled):
|
||||||
@ -142,14 +143,14 @@ proc setupHost(call: CallParams): TransactionHost =
|
|||||||
let sender = call.sender
|
let sender = call.sender
|
||||||
let contractAddress =
|
let contractAddress =
|
||||||
generateAddress(sender, call.vmState.readOnlyStateDB.getNonce(sender))
|
generateAddress(sender, call.vmState.readOnlyStateDB.getNonce(sender))
|
||||||
host.msg.destination = contractAddress.toEvmc
|
host.msg.recipient = contractAddress.toEvmc
|
||||||
host.msg.input_size = 0
|
host.msg.input_size = 0
|
||||||
host.msg.input_data = nil
|
host.msg.input_data = nil
|
||||||
code = call.input
|
code = call.input
|
||||||
else:
|
else:
|
||||||
# TODO: Share the underlying data, but only after checking this does not
|
# TODO: Share the underlying data, but only after checking this does not
|
||||||
# cause problems with the database.
|
# cause problems with the database.
|
||||||
code = host.vmState.readOnlyStateDB.getCode(host.msg.destination.fromEvmc)
|
code = host.vmState.readOnlyStateDB.getCode(host.msg.code_address.fromEvmc)
|
||||||
if call.input.len > 0:
|
if call.input.len > 0:
|
||||||
host.msg.input_size = call.input.len.csize_t
|
host.msg.input_size = call.input.len.csize_t
|
||||||
# Must copy the data so the `host.msg.input_data` pointer
|
# Must copy the data so the `host.msg.input_data` pointer
|
||||||
|
@ -132,8 +132,8 @@ proc evmcExecComputation*(host: TransactionHost): EvmcResult {.inline.} =
|
|||||||
|
|
||||||
host.showCallReturn(result)
|
host.showCallReturn(result)
|
||||||
|
|
||||||
# This code assumes fields, methods and types of ABI version 9, and must be
|
# This code assumes fields, methods and types of ABI version 10, and must be
|
||||||
# checked for compatibility if the `import evmc/evmc` major version is updated.
|
# checked for compatibility if the `import evmc/evmc` major version is updated.
|
||||||
when EVMC_ABI_VERSION != 9:
|
when EVMC_ABI_VERSION != 10:
|
||||||
{.error: ("This code assumes EVMC_ABI_VERSION 9;" &
|
{.error: ("This code assumes EVMC_ABI_VERSION 10;" &
|
||||||
" update the code to use EVMC_ABI_VERSION " & $EVMC_ABI_VERSION).}
|
" update the code to use EVMC_ABI_VERSION " & $EVMC_ABI_VERSION).}
|
||||||
|
@ -64,6 +64,8 @@ proc evmcExecute(vm: ptr evmc_vm, hostInterface: ptr evmc_host_interface,
|
|||||||
# Gas left is required to be zero when not `EVMC_SUCCESS` or `EVMC_REVERT`.
|
# Gas left is required to be zero when not `EVMC_SUCCESS` or `EVMC_REVERT`.
|
||||||
gas_left: if result.status_code notin {EVMC_SUCCESS, EVMC_REVERT}: 0'i64
|
gas_left: if result.status_code notin {EVMC_SUCCESS, EVMC_REVERT}: 0'i64
|
||||||
else: c.gasMeter.gasRemaining.int64,
|
else: c.gasMeter.gasRemaining.int64,
|
||||||
|
gas_refund: if result.status_code == EVMC_SUCCESS: c.gasMeter.gasRefunded.int64
|
||||||
|
else: 0'i64,
|
||||||
output_data: output_data,
|
output_data: output_data,
|
||||||
output_size: output_size.csize_t,
|
output_size: output_size.csize_t,
|
||||||
release: if output_data.isNil: nil
|
release: if output_data.isNil: nil
|
||||||
@ -100,8 +102,8 @@ proc evmc_create_nimbus_evm(): ptr evmc_vm {.cdecl, exportc.} =
|
|||||||
GC_ref(vm)
|
GC_ref(vm)
|
||||||
return cast[ptr evmc_vm](vm)
|
return cast[ptr evmc_vm](vm)
|
||||||
|
|
||||||
# This code assumes fields, methods and types of ABI version 9, and must be
|
# This code assumes fields, methods and types of ABI version 10, and must be
|
||||||
# checked for compatibility if the `import evmc/evmc` major version is updated.
|
# checked for compatibility if the `import evmc/evmc` major version is updated.
|
||||||
when EVMC_ABI_VERSION != 9:
|
when EVMC_ABI_VERSION != 10:
|
||||||
{.error: ("This code assumes EVMC_ABI_VERSION 9;" &
|
{.error: ("This code assumes EVMC_ABI_VERSION 10;" &
|
||||||
" update the code to use EVMC_ABI_VERSION " & $EVMC_ABI_VERSION).}
|
" update the code to use EVMC_ABI_VERSION " & $EVMC_ABI_VERSION).}
|
||||||
|
@ -56,9 +56,9 @@ proc beforeExecCallEvmcNested(host: TransactionHost,
|
|||||||
depth: m.depth,
|
depth: m.depth,
|
||||||
gas: m.gas,
|
gas: m.gas,
|
||||||
sender: m.sender.fromEvmc,
|
sender: m.sender.fromEvmc,
|
||||||
codeAddress: m.destination.fromEvmc,
|
codeAddress: m.code_address.fromEvmc,
|
||||||
contractAddress: if m.kind == EVMC_CALL:
|
contractAddress: if m.kind == EVMC_CALL:
|
||||||
m.destination.fromEvmc
|
m.recipient.fromEvmc
|
||||||
else:
|
else:
|
||||||
host.computation.msg.contractAddress,
|
host.computation.msg.contractAddress,
|
||||||
value: m.value.fromEvmc,
|
value: m.value.fromEvmc,
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
import
|
import
|
||||||
sets, times, stint, chronicles,
|
sets, times, stint, chronicles,
|
||||||
eth/common/eth_types, ../db/accounts_cache, ../forks,
|
eth/common/eth_types, ../db/accounts_cache, ../forks,
|
||||||
".."/[vm_state, vm_computation, vm_internals],
|
".."/[vm_state, vm_computation, vm_internals, vm_gas_costs],
|
||||||
./host_types, ./host_trace, ./host_call_nested
|
./host_types, ./host_trace, ./host_call_nested
|
||||||
|
|
||||||
proc setupTxContext(host: TransactionHost) =
|
proc setupTxContext(host: TransactionHost) =
|
||||||
@ -45,7 +45,7 @@ proc setupTxContext(host: TransactionHost) =
|
|||||||
# [EIP-1985](https://eips.ethereum.org/EIPS/eip-1985) although it's not
|
# [EIP-1985](https://eips.ethereum.org/EIPS/eip-1985) although it's not
|
||||||
# officially accepted), and `vmState.gasLimit` is too (`GasInt`).
|
# officially accepted), and `vmState.gasLimit` is too (`GasInt`).
|
||||||
#
|
#
|
||||||
# `txContext.block_difficulty` is 256-bit, and this one can genuinely take
|
# `txContext.block_prev_randao` is 256-bit, and this one can genuinely take
|
||||||
# values over much of the 256-bit range.
|
# values over much of the 256-bit range.
|
||||||
|
|
||||||
let vmState = host.vmState
|
let vmState = host.vmState
|
||||||
@ -73,7 +73,7 @@ proc setupTxContext(host: TransactionHost) =
|
|||||||
# EIP-4399
|
# EIP-4399
|
||||||
# Transfer block randomness to difficulty OPCODE
|
# Transfer block randomness to difficulty OPCODE
|
||||||
let difficulty = vmState.difficulty.toEvmc
|
let difficulty = vmState.difficulty.toEvmc
|
||||||
host.txContext.block_difficulty = flip256(difficulty)
|
host.txContext.block_prev_randao = flip256(difficulty)
|
||||||
|
|
||||||
host.cachedTxContext = true
|
host.cachedTxContext = true
|
||||||
|
|
||||||
@ -100,80 +100,66 @@ proc accountExists(host: TransactionHost, address: HostAddress): bool {.show.} =
|
|||||||
proc getStorage(host: TransactionHost, address: HostAddress, key: HostKey): HostValue {.show.} =
|
proc getStorage(host: TransactionHost, address: HostAddress, key: HostKey): HostValue {.show.} =
|
||||||
host.vmState.readOnlyStateDB.getStorage(address, key)
|
host.vmState.readOnlyStateDB.getStorage(address, key)
|
||||||
|
|
||||||
const
|
proc setStorageStatus(host: TransactionHost, address: HostAddress,
|
||||||
# EIP-1283
|
key: HostKey, newVal: HostValue): EvmcStorageStatus {.show.} =
|
||||||
SLOAD_GAS_CONSTANTINOPLE = 200
|
let
|
||||||
# EIP-2200
|
db = host.vmState.readOnlyStateDB
|
||||||
SSTORE_SET_GAS = 20000
|
currentVal = db.getStorage(address, key)
|
||||||
SSTORE_RESET_GAS = 5000
|
|
||||||
SLOAD_GAS_ISTANBUL = 800
|
|
||||||
# EIP-2929
|
|
||||||
WARM_STORAGE_READ_COST = 100
|
|
||||||
COLD_SLOAD_COST = 2100
|
|
||||||
COLD_ACCOUNT_ACCESS_COST = 2600
|
|
||||||
|
|
||||||
SSTORE_CLEARS_SCHEDULE_EIP2200 = 15000
|
if currentVal == newVal:
|
||||||
SSTORE_CLEARS_SCHEDULE_EIP3529 = 4800
|
return EVMC_STORAGE_ASSIGNED
|
||||||
|
|
||||||
func storageModifiedAgainRefund(originalValue, oldValue, value: HostValue,
|
|
||||||
fork: Fork): int {.inline.} =
|
|
||||||
# Calculate `SSTORE` refund according to EIP-2929 (Berlin),
|
|
||||||
# EIP-2200 (Istanbul) or EIP-1283 (Constantinople only).
|
|
||||||
result = 0
|
|
||||||
if value == originalValue:
|
|
||||||
result = if value.isZero: SSTORE_SET_GAS
|
|
||||||
elif fork >= FkBerlin: SSTORE_RESET_GAS - COLD_SLOAD_COST
|
|
||||||
else: SSTORE_RESET_GAS
|
|
||||||
let SLOAD_GAS = if fork >= FkBerlin: WARM_STORAGE_READ_COST
|
|
||||||
elif fork >= FkIstanbul: SLOAD_GAS_ISTANBUL
|
|
||||||
else: SLOAD_GAS_CONSTANTINOPLE
|
|
||||||
result -= SLOAD_GAS
|
|
||||||
|
|
||||||
let SSTORE_CLEARS_SCHEDULE = if fork >= FkLondon:
|
|
||||||
SSTORE_CLEARS_SCHEDULE_EIP3529
|
|
||||||
else:
|
|
||||||
SSTORE_CLEARS_SCHEDULE_EIP2200
|
|
||||||
if not originalValue.isZero:
|
|
||||||
if value.isZero:
|
|
||||||
result += SSTORE_CLEARS_SCHEDULE
|
|
||||||
elif oldValue.isZero:
|
|
||||||
result -= SSTORE_CLEARS_SCHEDULE
|
|
||||||
|
|
||||||
proc setStorage(host: TransactionHost, address: HostAddress,
|
|
||||||
key: HostKey, value: HostValue): EvmcStorageStatus {.show.} =
|
|
||||||
let db = host.vmState.readOnlyStateDB
|
|
||||||
let oldValue = db.getStorage(address, key)
|
|
||||||
|
|
||||||
if oldValue == value:
|
|
||||||
return EVMC_STORAGE_UNCHANGED
|
|
||||||
|
|
||||||
host.vmState.mutateStateDB:
|
host.vmState.mutateStateDB:
|
||||||
db.setStorage(address, key, value)
|
db.setStorage(address, key, newVal)
|
||||||
|
|
||||||
if host.vmState.fork >= FkIstanbul or host.vmState.fork == FkConstantinople:
|
# https://eips.ethereum.org/EIPS/eip-1283
|
||||||
let originalValue = db.getCommittedStorage(address, key)
|
let originalVal = db.getCommittedStorage(address, key)
|
||||||
if oldValue != originalValue:
|
if originalVal == currentVal:
|
||||||
# Gas refund for `MODIFIED_AGAIN` (EIP-1283/2200/2929 only).
|
if originalVal.isZero:
|
||||||
let refund = storageModifiedAgainRefund(originalValue, oldValue, value,
|
return EVMC_STORAGE_ADDED
|
||||||
host.vmState.fork)
|
|
||||||
# TODO: Refund depends on `Computation` at the moment.
|
|
||||||
if refund != 0:
|
|
||||||
host.computation.gasMeter.refundGas(refund)
|
|
||||||
return EVMC_STORAGE_MODIFIED_AGAIN
|
|
||||||
|
|
||||||
if oldValue.isZero:
|
# !is_zero(original_val)
|
||||||
return EVMC_STORAGE_ADDED
|
if newVal.isZero:
|
||||||
elif value.isZero:
|
return EVMC_STORAGE_DELETED
|
||||||
# Gas refund for `DELETED` (all forks).
|
else:
|
||||||
# TODO: Refund depends on `Computation` at the moment.
|
return EVMC_STORAGE_MODIFIED
|
||||||
let SSTORE_CLEARS_SCHEDULE = if host.vmState.fork >= FkLondon:
|
|
||||||
SSTORE_CLEARS_SCHEDULE_EIP3529
|
# originalVal != currentVal
|
||||||
else:
|
if originalVal.isZero.not:
|
||||||
SSTORE_CLEARS_SCHEDULE_EIP2200
|
if currentVal.isZero:
|
||||||
host.computation.gasMeter.refundGas(SSTORE_CLEARS_SCHEDULE)
|
if originalVal == newVal:
|
||||||
return EVMC_STORAGE_DELETED
|
return EVMC_STORAGE_DELETED_RESTORED
|
||||||
|
else:
|
||||||
|
return EVMC_STORAGE_DELETED_ADDED
|
||||||
|
|
||||||
|
# !is_zero(current_val)
|
||||||
|
if newVal.isZero:
|
||||||
|
return EVMC_STORAGE_MODIFIED_DELETED
|
||||||
|
|
||||||
|
# !is_zero(new_val)
|
||||||
|
if originalVal == newVal:
|
||||||
|
return EVMC_STORAGE_MODIFIED_RESTORED
|
||||||
|
else:
|
||||||
|
return EVMC_STORAGE_ASSIGNED
|
||||||
|
|
||||||
|
# is_zero(original_val)
|
||||||
|
if originalVal == newVal:
|
||||||
|
return EVMC_STORAGE_ADDED_DELETED
|
||||||
else:
|
else:
|
||||||
return EVMC_STORAGE_MODIFIED
|
return EVMC_STORAGE_ASSIGNED
|
||||||
|
|
||||||
|
proc setStorage(host: TransactionHost, address: HostAddress,
|
||||||
|
key: HostKey, newVal: HostValue): EvmcStorageStatus {.show.} =
|
||||||
|
let
|
||||||
|
status = setStorageStatus(host, address, key, newVal)
|
||||||
|
fork = host.vmState.fork
|
||||||
|
refund = SstoreCost[fork][status].gasRefund
|
||||||
|
|
||||||
|
if refund != 0:
|
||||||
|
# TODO: Refund depends on `Computation` at the moment.
|
||||||
|
host.computation.gasMeter.refundGas(refund)
|
||||||
|
|
||||||
|
status
|
||||||
|
|
||||||
proc getBalance(host: TransactionHost, address: HostAddress): HostBalance {.show.} =
|
proc getBalance(host: TransactionHost, address: HostAddress): HostBalance {.show.} =
|
||||||
host.vmState.readOnlyStateDB.getBalance(address)
|
host.vmState.readOnlyStateDB.getBalance(address)
|
||||||
|
@ -58,7 +58,8 @@ proc showEvmcMessage(msg: EvmcMessage): string =
|
|||||||
&" gas={$msg.gas}" &
|
&" gas={$msg.gas}" &
|
||||||
&" value={$msg.value.fromEvmc}" &
|
&" value={$msg.value.fromEvmc}" &
|
||||||
&" sender={$msg.sender.fromEvmc}" &
|
&" sender={$msg.sender.fromEvmc}" &
|
||||||
&" destination={$msg.destination.fromEvmc}" &
|
&" recipient={$msg.recipient.fromEvmc}" &
|
||||||
|
&" code_address={$msg.code_address.fromEvmc}" &
|
||||||
&" input_data={inputStr}"
|
&" input_data={inputStr}"
|
||||||
if msg.kind == EVMC_CREATE2:
|
if msg.kind == EVMC_CREATE2:
|
||||||
result.add &" create2_salt={$msg.create2_salt.fromEvmc}"
|
result.add &" create2_salt={$msg.create2_salt.fromEvmc}"
|
||||||
@ -88,7 +89,7 @@ proc showEvmcTxContext(txc: EvmcTxContext): string =
|
|||||||
&" block_number={$txc.block_number}" &
|
&" block_number={$txc.block_number}" &
|
||||||
&" block_timestamp={$txc.block_timestamp}" &
|
&" block_timestamp={$txc.block_timestamp}" &
|
||||||
&" block_gas_limit={$txc.block_gas_limit}" &
|
&" block_gas_limit={$txc.block_gas_limit}" &
|
||||||
&" block_difficulty={$txc.block_difficulty.fromEvmc}" &
|
&" block_prev_randao={$txc.block_prev_randao.fromEvmc}" &
|
||||||
&" chain_id={$txc.chain_id.fromEvmc}" &
|
&" chain_id={$txc.chain_id.fromEvmc}" &
|
||||||
&" block_base_fee={$txc.block_base_fee.fromEvmc}"
|
&" block_base_fee={$txc.block_base_fee.fromEvmc}"
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ template getBlockNumber*(c: Computation): UInt256 =
|
|||||||
|
|
||||||
template getDifficulty*(c: Computation): DifficultyInt =
|
template getDifficulty*(c: Computation): DifficultyInt =
|
||||||
when evmc_enabled:
|
when evmc_enabled:
|
||||||
UInt256.fromEvmc c.host.getTxContext().block_difficulty
|
UInt256.fromEvmc c.host.getTxContext().block_prev_randao
|
||||||
else:
|
else:
|
||||||
c.vmState.difficulty
|
c.vmState.difficulty
|
||||||
|
|
||||||
@ -192,12 +192,6 @@ proc newComputation*(vmState: BaseVMState, message: Message,
|
|||||||
result.code = newCodeStream(
|
result.code = newCodeStream(
|
||||||
vmState.readOnlyStateDB.getCode(message.codeAddress))
|
vmState.readOnlyStateDB.getCode(message.codeAddress))
|
||||||
|
|
||||||
when evmc_enabled:
|
|
||||||
result.host.init(
|
|
||||||
nim_host_get_interface(),
|
|
||||||
cast[evmc_host_context](result)
|
|
||||||
)
|
|
||||||
|
|
||||||
proc newComputation*(vmState: BaseVMState, message: Message, code: seq[byte]): Computation =
|
proc newComputation*(vmState: BaseVMState, message: Message, code: seq[byte]): Computation =
|
||||||
new result
|
new result
|
||||||
result.vmState = vmState
|
result.vmState = vmState
|
||||||
@ -210,12 +204,6 @@ proc newComputation*(vmState: BaseVMState, message: Message, code: seq[byte]): C
|
|||||||
result.selfDestructs = initHashSet[EthAddress]()
|
result.selfDestructs = initHashSet[EthAddress]()
|
||||||
result.code = newCodeStream(code)
|
result.code = newCodeStream(code)
|
||||||
|
|
||||||
when evmc_enabled:
|
|
||||||
result.host.init(
|
|
||||||
nim_host_get_interface(),
|
|
||||||
cast[evmc_host_context](result)
|
|
||||||
)
|
|
||||||
|
|
||||||
template gasCosts*(c: Computation): untyped =
|
template gasCosts*(c: Computation): untyped =
|
||||||
c.vmState.gasCosts
|
c.vmState.gasCosts
|
||||||
|
|
||||||
|
@ -23,30 +23,32 @@ type
|
|||||||
block_number* : int64 # The block number.
|
block_number* : int64 # The block number.
|
||||||
block_timestamp* : int64 # The block timestamp.
|
block_timestamp* : int64 # The block timestamp.
|
||||||
block_gas_limit* : int64 # The block gas limit.
|
block_gas_limit* : int64 # The block gas limit.
|
||||||
block_difficulty*: evmc_uint256be # The block difficulty.
|
block_prev_randao*: evmc_uint256be # The block difficulty.
|
||||||
chain_id* : evmc_uint256be # The blockchain's ChainID.
|
chain_id* : evmc_uint256be # The blockchain's ChainID.
|
||||||
block_base_fee* : evmc_uint256be # The block base fee.
|
block_base_fee* : evmc_uint256be # The block base fee.
|
||||||
|
|
||||||
nimbus_message* = object
|
nimbus_message* = object
|
||||||
kind*: evmc_call_kind
|
kind* : evmc_call_kind
|
||||||
flags*: uint32
|
flags* : uint32
|
||||||
depth*: int32
|
depth* : int32
|
||||||
gas*: int64
|
gas* : int64
|
||||||
destination*: EthAddress
|
recipient* : EthAddress
|
||||||
sender*: EthAddress
|
sender* : EthAddress
|
||||||
input_data*: ptr byte
|
input_data* : ptr byte
|
||||||
input_size*: uint
|
input_size* : uint
|
||||||
value*: evmc_uint256be
|
value* : evmc_uint256be
|
||||||
create2_salt*: evmc_bytes32
|
create2_salt*: evmc_bytes32
|
||||||
|
code_address*: EthAddress
|
||||||
|
|
||||||
nimbus_result* = object
|
nimbus_result* = object
|
||||||
status_code*: evmc_status_code
|
status_code* : evmc_status_code
|
||||||
gas_left*: int64
|
gas_left* : int64
|
||||||
output_data*: ptr byte
|
gas_refund* : int64
|
||||||
output_size*: uint
|
output_data* : ptr byte
|
||||||
release*: proc(result: var nimbus_result) {.cdecl, gcsafe.}
|
output_size* : uint
|
||||||
|
release* : proc(result: var nimbus_result) {.cdecl, gcsafe.}
|
||||||
create_address*: EthAddress
|
create_address*: EthAddress
|
||||||
padding*: array[4, byte]
|
padding* : array[4, byte]
|
||||||
|
|
||||||
nimbus_host_interface* = object
|
nimbus_host_interface* = object
|
||||||
account_exists*: proc(context: evmc_host_context, address: EthAddress): bool {.cdecl, gcsafe.}
|
account_exists*: proc(context: evmc_host_context, address: EthAddress): bool {.cdecl, gcsafe.}
|
||||||
@ -151,11 +153,3 @@ proc accessStorage*(ctx: HostContext, address: EthAddress,
|
|||||||
key: UInt256): evmc_access_status {.inline.} =
|
key: UInt256): evmc_access_status {.inline.} =
|
||||||
var key = toEvmc(key)
|
var key = toEvmc(key)
|
||||||
ctx.host.access_storage(ctx.context, address, key)
|
ctx.host.access_storage(ctx.context, address, key)
|
||||||
|
|
||||||
#proc vmHost*(vmState: BaseVMState, gasPrice: GasInt, origin: EthAddress): HostContext =
|
|
||||||
# let host = nim_host_get_interface()
|
|
||||||
# let context = nim_host_create_context(cast[pointer](vmState), gasPrice, toEvmc(origin))
|
|
||||||
# result.init(host, context)
|
|
||||||
#
|
|
||||||
#proc destroy*(hc: HostContext) =
|
|
||||||
# nim_host_destroy_context(hc.context)
|
|
||||||
|
@ -1,288 +0,0 @@
|
|||||||
# Nimbus
|
|
||||||
# Copyright (c) 2019 Status Research & Development GmbH
|
|
||||||
# Licensed under either of
|
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
||||||
|
|
||||||
proc hostReleaseResultImpl(res: var nimbus_result) {.cdecl, gcsafe.} =
|
|
||||||
dealloc(res.output_data)
|
|
||||||
|
|
||||||
proc hostGetTxContextImpl(ctx: Computation): nimbus_tx_context {.cdecl.} =
|
|
||||||
let vmstate = ctx.vmState
|
|
||||||
result.tx_gas_price = toEvmc(vmstate.txGasPrice.u256)
|
|
||||||
result.tx_origin = vmstate.txOrigin
|
|
||||||
result.block_coinbase = vmstate.coinbase
|
|
||||||
result.block_number = vmstate.blockNumber.truncate(int64)
|
|
||||||
result.block_timestamp = vmstate.timestamp.toUnix()
|
|
||||||
result.block_gas_limit = int64(vmstate.gasLimit)
|
|
||||||
|
|
||||||
# EIP-4399
|
|
||||||
# Transfer block randomness to difficulty OPCODE
|
|
||||||
let difficulty = toEvmc(vmstate.difficulty)
|
|
||||||
result.block_difficulty = difficulty
|
|
||||||
|
|
||||||
result.chain_id = toEvmc(vmstate.chaindb.config.chainId.uint.u256)
|
|
||||||
result.block_base_fee = toEvmc(vmstate.baseFee)
|
|
||||||
|
|
||||||
proc hostGetBlockHashImpl(ctx: Computation, number: int64): Hash256 {.cdecl.} =
|
|
||||||
ctx.vmState.getAncestorHash(number.u256)
|
|
||||||
|
|
||||||
proc hostAccountExistsImpl(ctx: Computation, address: EthAddress): bool {.cdecl.} =
|
|
||||||
let db = ctx.vmState.readOnlyStateDB
|
|
||||||
if ctx.fork >= FkSpurious:
|
|
||||||
not db.isDeadAccount(address)
|
|
||||||
else:
|
|
||||||
db.accountExists(address)
|
|
||||||
|
|
||||||
proc hostGetStorageImpl(ctx: Computation, address: EthAddress, key: var evmc_bytes32): evmc_bytes32 {.cdecl.} =
|
|
||||||
ctx.vmState.stateDB.getStorage(address, UInt256.fromEvmc(key)).toEvmc()
|
|
||||||
|
|
||||||
proc sstoreNetGasMetering(ctx: Computation): bool {.inline.} =
|
|
||||||
ctx.fork in {FkConstantinople, FkIstanbul, FkBerlin, FkLondon}
|
|
||||||
|
|
||||||
proc hostSetStorageImpl(ctx: Computation, address: EthAddress,
|
|
||||||
key, value: var evmc_bytes32): evmc_storage_status {.cdecl.} =
|
|
||||||
let
|
|
||||||
slot = UInt256.fromEvmc(key)
|
|
||||||
newValue = UInt256.fromEvmc(value)
|
|
||||||
statedb = ctx.vmState.readOnlyStateDB
|
|
||||||
currValue = statedb.getStorage(address, slot)
|
|
||||||
|
|
||||||
assert address == ctx.msg.contractAddress
|
|
||||||
|
|
||||||
var
|
|
||||||
status = EVMC_STORAGE_MODIFIED
|
|
||||||
gasRefund = 0.GasInt
|
|
||||||
origValue = 0.u256
|
|
||||||
|
|
||||||
if newValue == currValue:
|
|
||||||
status = EVMC_STORAGE_UNCHANGED
|
|
||||||
else:
|
|
||||||
origValue = statedb.getCommittedStorage(address, slot)
|
|
||||||
if origValue == currValue or not ctx.sstoreNetGasMetering():
|
|
||||||
if currValue == 0:
|
|
||||||
status = EVMC_STORAGE_ADDED
|
|
||||||
elif newValue == 0:
|
|
||||||
status = EVMC_STORAGE_DELETED
|
|
||||||
else:
|
|
||||||
status = EVMC_STORAGE_MODIFIED_AGAIN
|
|
||||||
ctx.vmState.mutateStateDB:
|
|
||||||
db.setStorage(address, slot, newValue)
|
|
||||||
|
|
||||||
let gasParam = GasParams(kind: Op.Sstore,
|
|
||||||
s_status: status,
|
|
||||||
s_currentValue: currValue,
|
|
||||||
s_originalValue: origValue
|
|
||||||
)
|
|
||||||
gasRefund = ctx.gasCosts[Sstore].c_handler(newValue, gasParam)[1]
|
|
||||||
|
|
||||||
if gasRefund != 0:
|
|
||||||
ctx.gasMeter.refundGas(gasRefund)
|
|
||||||
|
|
||||||
result = status
|
|
||||||
|
|
||||||
proc hostGetBalanceImpl(ctx: Computation, address: EthAddress): evmc_bytes32 {.cdecl.} =
|
|
||||||
ctx.vmState.readOnlyStateDB.getBalance(address).toEvmc()
|
|
||||||
|
|
||||||
proc hostGetCodeSizeImpl(ctx: Computation, address: EthAddress): uint {.cdecl.} =
|
|
||||||
ctx.vmState.readOnlyStateDB.getCode(address).len.uint
|
|
||||||
|
|
||||||
proc hostGetCodeHashImpl(ctx: Computation, address: EthAddress): Hash256 {.cdecl.} =
|
|
||||||
let db = ctx.vmstate.readOnlyStateDB
|
|
||||||
if not db.accountExists(address):
|
|
||||||
return
|
|
||||||
if db.isEmptyAccount(address):
|
|
||||||
return
|
|
||||||
db.getCodeHash(address)
|
|
||||||
|
|
||||||
proc hostCopyCodeImpl(ctx: Computation, address: EthAddress,
|
|
||||||
codeOffset: int, bufferData: ptr byte,
|
|
||||||
bufferSize: int): int {.cdecl.} =
|
|
||||||
|
|
||||||
var code = ctx.vmState.readOnlyStateDB.getCode(address)
|
|
||||||
|
|
||||||
# Handle "big offset" edge case.
|
|
||||||
if codeOffset > code.len:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
let maxToCopy = code.len - codeOffset
|
|
||||||
let numToCopy = min(maxToCopy, bufferSize)
|
|
||||||
if numToCopy > 0:
|
|
||||||
copyMem(bufferData, code[codeOffset].addr, numToCopy)
|
|
||||||
result = numToCopy
|
|
||||||
|
|
||||||
proc hostSelfdestructImpl(ctx: Computation, address, beneficiary: EthAddress) {.cdecl.} =
|
|
||||||
assert address == ctx.msg.contractAddress
|
|
||||||
ctx.execSelfDestruct(beneficiary)
|
|
||||||
|
|
||||||
proc hostEmitLogImpl(ctx: Computation, address: EthAddress,
|
|
||||||
data: ptr byte, dataSize: int,
|
|
||||||
topics: UncheckedArray[evmc_bytes32], topicsCount: int) {.cdecl.} =
|
|
||||||
var log: Log
|
|
||||||
if topicsCount > 0:
|
|
||||||
log.topics = newSeq[Topic](topicsCount)
|
|
||||||
for i in 0 ..< topicsCount:
|
|
||||||
log.topics[i] = topics[i].bytes
|
|
||||||
|
|
||||||
log.data = @(makeOpenArray(data, dataSize))
|
|
||||||
log.address = address
|
|
||||||
ctx.addLogEntry(log)
|
|
||||||
|
|
||||||
proc hostAccessAccountImpl(ctx: Computation, address: EthAddress): evmc_access_status {.cdecl.} =
|
|
||||||
ctx.vmState.mutateStateDB:
|
|
||||||
if not db.inAccessList(address):
|
|
||||||
db.accessList(address)
|
|
||||||
return EVMC_ACCESS_COLD
|
|
||||||
else:
|
|
||||||
return EVMC_ACCESS_WARM
|
|
||||||
|
|
||||||
proc hostAccessStorageImpl(ctx: Computation, address: EthAddress,
|
|
||||||
key: var evmc_bytes32): evmc_access_status {.cdecl.} =
|
|
||||||
let slot = UInt256.fromEvmc(key)
|
|
||||||
ctx.vmState.mutateStateDB:
|
|
||||||
if not db.inAccessList(address, slot):
|
|
||||||
db.accessList(address, slot)
|
|
||||||
return EVMC_ACCESS_COLD
|
|
||||||
else:
|
|
||||||
return EVMC_ACCESS_WARM
|
|
||||||
|
|
||||||
proc enterCreateImpl(c: Computation, m: nimbus_message): Computation =
|
|
||||||
# TODO: use evmc_message to evoid copy
|
|
||||||
let childMsg = Message(
|
|
||||||
kind: CallKind(m.kind),
|
|
||||||
depth: m.depth,
|
|
||||||
gas: m.gas,
|
|
||||||
sender: m.sender,
|
|
||||||
value: UInt256.fromEvmc(m.value),
|
|
||||||
data: @(makeOpenArray(m.inputData, m.inputSize.int))
|
|
||||||
)
|
|
||||||
return newComputation(c.vmState, childMsg,
|
|
||||||
ContractSalt.fromEvmc(m.create2_salt))
|
|
||||||
|
|
||||||
template leaveCreateImpl(c, child: Computation, res: nimbus_result) =
|
|
||||||
if not child.shouldBurnGas:
|
|
||||||
res.gas_left = child.gasMeter.gasRemaining
|
|
||||||
|
|
||||||
if child.isSuccess:
|
|
||||||
c.merge(child)
|
|
||||||
res.status_code = EVMC_SUCCESS
|
|
||||||
res.create_address = child.msg.contractAddress
|
|
||||||
else:
|
|
||||||
res.status_code = if child.shouldBurnGas: EVMC_FAILURE else: EVMC_REVERT
|
|
||||||
if child.output.len > 0:
|
|
||||||
# TODO: can we move the ownership of seq to raw pointer?
|
|
||||||
res.output_size = child.output.len.uint
|
|
||||||
res.output_data = cast[ptr byte](alloc(child.output.len))
|
|
||||||
copyMem(res.output_data, child.output[0].addr, child.output.len)
|
|
||||||
res.release = hostReleaseResultImpl
|
|
||||||
|
|
||||||
template enterCallImpl(c: Computation, m: nimbus_message): Computation =
|
|
||||||
let childMsg = Message(
|
|
||||||
kind: CallKind(m.kind),
|
|
||||||
depth: m.depth,
|
|
||||||
gas: m.gas,
|
|
||||||
sender: m.sender,
|
|
||||||
codeAddress: m.destination,
|
|
||||||
contractAddress: if m.kind == EVMC_CALL: m.destination else: c.msg.contractAddress,
|
|
||||||
value: UInt256.fromEvmc(m.value),
|
|
||||||
data: @(makeOpenArray(m.inputData, m.inputSize.int)),
|
|
||||||
flags: MsgFlags(m.flags)
|
|
||||||
)
|
|
||||||
newComputation(c.vmState, childMsg)
|
|
||||||
|
|
||||||
template leaveCallImpl(c, child: Computation, res: nimbus_result) =
|
|
||||||
if not child.shouldBurnGas:
|
|
||||||
res.gas_left = child.gasMeter.gasRemaining
|
|
||||||
|
|
||||||
if child.isSuccess:
|
|
||||||
c.merge(child)
|
|
||||||
res.status_code = EVMC_SUCCESS
|
|
||||||
else:
|
|
||||||
res.status_code = if child.shouldBurnGas: EVMC_FAILURE else: EVMC_REVERT
|
|
||||||
|
|
||||||
if child.output.len > 0:
|
|
||||||
# TODO: can we move the ownership of seq to raw pointer?
|
|
||||||
res.output_size = child.output.len.uint
|
|
||||||
res.output_data = cast[ptr byte](alloc(child.output.len))
|
|
||||||
copyMem(res.output_data, child.output[0].addr, child.output.len)
|
|
||||||
res.release = hostReleaseResultImpl
|
|
||||||
|
|
||||||
proc enterHostCall(c: Computation, msg: var nimbus_message): Computation {.noinline.} =
|
|
||||||
if msg.kind == EVMC_CREATE or msg.kind == EVMC_CREATE2:
|
|
||||||
enterCreateImpl(c, msg)
|
|
||||||
else:
|
|
||||||
enterCallImpl(c, msg)
|
|
||||||
|
|
||||||
proc leaveHostCall(c, child: Computation, kind: evmc_call_kind): nimbus_result {.noinline.} =
|
|
||||||
if kind == EVMC_CREATE or kind == EVMC_CREATE2:
|
|
||||||
leaveCreateImpl(c, child, result)
|
|
||||||
else:
|
|
||||||
leaveCallImpl(c, child, result)
|
|
||||||
|
|
||||||
proc hostCallImpl(ctx: Computation, msg: var nimbus_message): nimbus_result {.cdecl.} =
|
|
||||||
let child = enterHostCall(ctx, msg)
|
|
||||||
child.execCallOrCreate()
|
|
||||||
leaveHostCall(ctx, child, msg.kind)
|
|
||||||
|
|
||||||
proc initHostInterface(): evmc_host_interface =
|
|
||||||
result.account_exists = cast[evmc_account_exists_fn](hostAccountExistsImpl)
|
|
||||||
result.get_storage = cast[evmc_get_storage_fn](hostGetStorageImpl)
|
|
||||||
result.set_storage = cast[evmc_set_storage_fn](hostSetStorageImpl)
|
|
||||||
result.get_balance = cast[evmc_get_balance_fn](hostGetBalanceImpl)
|
|
||||||
result.get_code_size = cast[evmc_get_code_size_fn](hostGetCodeSizeImpl)
|
|
||||||
result.get_code_hash = cast[evmc_get_code_hash_fn](hostGetCodeHashImpl)
|
|
||||||
result.copy_code = cast[evmc_copy_code_fn](hostCopyCodeImpl)
|
|
||||||
result.selfdestruct = cast[evmc_selfdestruct_fn](hostSelfdestructImpl)
|
|
||||||
result.call = cast[evmc_call_fn](hostCallImpl)
|
|
||||||
result.get_tx_context = cast[evmc_get_tx_context_fn](hostGetTxContextImpl)
|
|
||||||
result.get_block_hash = cast[evmc_get_block_hash_fn](hostGetBlockHashImpl)
|
|
||||||
result.emit_log = cast[evmc_emit_log_fn](hostEmitLogImpl)
|
|
||||||
result.access_account = cast[evmc_access_account_fn](hostAccessAccountImpl)
|
|
||||||
result.access_storage = cast[evmc_access_storage_fn](hostAccessStorageImpl)
|
|
||||||
|
|
||||||
proc vmSetOptionImpl(vm: ptr evmc_vm, name, value: cstring): evmc_set_option_result {.cdecl.} =
|
|
||||||
return EVMC_SET_OPTION_INVALID_NAME
|
|
||||||
|
|
||||||
proc vmExecuteImpl(vm: ptr evmc_vm, host: ptr evmc_host_interface,
|
|
||||||
ctx: Computation, rev: evmc_revision,
|
|
||||||
msg: evmc_message, code: ptr byte, code_size: uint): evmc_result {.cdecl.} =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc vmGetCapabilitiesImpl(vm: ptr evmc_vm): evmc_capabilities {.cdecl.} =
|
|
||||||
result.incl(EVMC_CAPABILITY_EVM1)
|
|
||||||
|
|
||||||
proc vmDestroyImpl(vm: ptr evmc_vm) {.cdecl.} =
|
|
||||||
dealloc(vm)
|
|
||||||
|
|
||||||
const
|
|
||||||
EVMC_HOST_NAME = "nimbus_vm"
|
|
||||||
EVMC_VM_VERSION = "0.0.1"
|
|
||||||
|
|
||||||
proc init(vm: var evmc_vm) =
|
|
||||||
vm.abi_version = EVMC_ABI_VERSION
|
|
||||||
vm.name = EVMC_HOST_NAME
|
|
||||||
vm.version = EVMC_VM_VERSION
|
|
||||||
vm.destroy = vmDestroyImpl
|
|
||||||
vm.execute = cast[evmc_execute_fn](vmExecuteImpl)
|
|
||||||
vm.get_capabilities = vmGetCapabilitiesImpl
|
|
||||||
vm.set_option = vmSetOptionImpl
|
|
||||||
|
|
||||||
let gHost = initHostInterface()
|
|
||||||
proc nim_host_get_interface(): ptr nimbus_host_interface {.exportc, cdecl.} =
|
|
||||||
result = cast[ptr nimbus_host_interface](gHost.unsafeAddr)
|
|
||||||
|
|
||||||
proc nim_host_create_context(vmstate: BaseVmState, msg: ptr evmc_message): Computation {.exportc, cdecl.} =
|
|
||||||
#result = HostContext(
|
|
||||||
# vmState: vmstate,
|
|
||||||
# gasPrice: GasInt(gasPrice),
|
|
||||||
# origin: fromEvmc(origin)
|
|
||||||
#)
|
|
||||||
GC_ref(result)
|
|
||||||
|
|
||||||
proc nim_host_destroy_context(ctx: Computation) {.exportc, cdecl.} =
|
|
||||||
GC_unref(ctx)
|
|
||||||
|
|
||||||
proc nim_create_nimbus_vm(): ptr evmc_vm {.exportc, cdecl.} =
|
|
||||||
result = create(evmc_vm)
|
|
||||||
init(result[])
|
|
@ -125,6 +125,87 @@ const
|
|||||||
ACCESS_LIST_STORAGE_KEY_COST* = 1900.GasInt
|
ACCESS_LIST_STORAGE_KEY_COST* = 1900.GasInt
|
||||||
ACCESS_LIST_ADDRESS_COST* = 2400.GasInt
|
ACCESS_LIST_ADDRESS_COST* = 2400.GasInt
|
||||||
|
|
||||||
|
|
||||||
|
when defined(evmc_enabled):
|
||||||
|
type
|
||||||
|
# The gas cost specification for storage instructions.
|
||||||
|
StorageCostSpec = object
|
||||||
|
netCost : bool # Is this net gas cost metering schedule?
|
||||||
|
warmAccess: int16 # Storage warm access cost, YP: G_{warmaccess}
|
||||||
|
sset : int16 # Storage addition cost, YP: G_{sset}
|
||||||
|
reset : int16 # Storage modification cost, YP: G_{sreset}
|
||||||
|
clear : int16 # Storage deletion refund, YP: R_{sclear}
|
||||||
|
|
||||||
|
StorageStoreCost* = object
|
||||||
|
gasCost* : int16
|
||||||
|
gasRefund*: int16
|
||||||
|
|
||||||
|
# Table of gas cost specification for storage instructions per EVM revision.
|
||||||
|
func storageCostSpec(): array[Fork, StorageCostSpec] {.compileTime.} =
|
||||||
|
# Legacy cost schedule.
|
||||||
|
const revs = [
|
||||||
|
FkFrontier, FkHomestead, FkTangerine,
|
||||||
|
FkSpurious, FkByzantium, FkPetersburg]
|
||||||
|
|
||||||
|
for rev in revs:
|
||||||
|
result[rev] = StorageCostSpec(
|
||||||
|
netCost: false, warmAccess: 200, sset: 20000, reset: 5000, clear: 15000)
|
||||||
|
|
||||||
|
# Net cost schedule.
|
||||||
|
result[FkConstantinople] = StorageCostSpec(
|
||||||
|
netCost: true, warmAccess: 200, sset: 20000, reset: 5000, clear: 15000)
|
||||||
|
result[FkIstanbul] = StorageCostSpec(
|
||||||
|
netCost: true, warmAccess: 800, sset: 20000, reset: 5000, clear: 15000)
|
||||||
|
result[FkBerlin] = StorageCostSpec(
|
||||||
|
netCost: true, warmAccess: WarmStorageReadCost, sset: 20000,
|
||||||
|
reset: 5000 - ColdSloadCost, clear: 15000)
|
||||||
|
result[FkLondon] = StorageCostSpec(
|
||||||
|
netCost: true, warmAccess: WarmStorageReadCost, sset: 20000,
|
||||||
|
reset: 5000 - ColdSloadCost, clear: 4800)
|
||||||
|
|
||||||
|
result[FkParis] = result[FkLondon]
|
||||||
|
result[FkShanghai] = result[FkLondon]
|
||||||
|
result[FkCancun] = result[FkLondon]
|
||||||
|
|
||||||
|
proc legacySStoreCost(e: var array[evmc_storage_status, StorageStoreCost],
|
||||||
|
c: StorageCostSpec) {.compileTime.} =
|
||||||
|
e[EVMC_STORAGE_ADDED] = StorageStoreCost(gasCost: c.sset , gasRefund: 0)
|
||||||
|
e[EVMC_STORAGE_DELETED] = StorageStoreCost(gasCost: c.reset, gasRefund: c.clear)
|
||||||
|
e[EVMC_STORAGE_MODIFIED] = StorageStoreCost(gasCost: c.reset, gasRefund: 0)
|
||||||
|
e[EVMC_STORAGE_ASSIGNED] = e[EVMC_STORAGE_MODIFIED]
|
||||||
|
e[EVMC_STORAGE_DELETED_ADDED] = e[EVMC_STORAGE_ADDED]
|
||||||
|
e[EVMC_STORAGE_MODIFIED_DELETED] = e[EVMC_STORAGE_DELETED]
|
||||||
|
e[EVMC_STORAGE_DELETED_RESTORED] = e[EVMC_STORAGE_ADDED]
|
||||||
|
e[EVMC_STORAGE_ADDED_DELETED] = e[EVMC_STORAGE_DELETED]
|
||||||
|
e[EVMC_STORAGE_MODIFIED_RESTORED] = e[EVMC_STORAGE_MODIFIED]
|
||||||
|
|
||||||
|
proc netSStoreCost(e: var array[evmc_storage_status, StorageStoreCost],
|
||||||
|
c: StorageCostSpec) {.compileTime.} =
|
||||||
|
e[EVMC_STORAGE_ASSIGNED] = StorageStoreCost(gasCost: c.warmAccess, gasRefund: 0)
|
||||||
|
e[EVMC_STORAGE_ADDED] = StorageStoreCost(gasCost: c.sset , gasRefund: 0)
|
||||||
|
e[EVMC_STORAGE_DELETED] = StorageStoreCost(gasCost: c.reset , gasRefund: c.clear)
|
||||||
|
e[EVMC_STORAGE_MODIFIED] = StorageStoreCost(gasCost: c.reset , gasRefund: 0)
|
||||||
|
e[EVMC_STORAGE_DELETED_ADDED] = StorageStoreCost(gasCost: c.warmAccess, gasRefund: -c.clear)
|
||||||
|
e[EVMC_STORAGE_MODIFIED_DELETED] = StorageStoreCost(gasCost: c.warmAccess, gasRefund: c.clear)
|
||||||
|
e[EVMC_STORAGE_DELETED_RESTORED] = StorageStoreCost(gasCost: c.warmAccess,
|
||||||
|
gasRefund: c.reset - c.warmAccess - c.clear)
|
||||||
|
e[EVMC_STORAGE_ADDED_DELETED] = StorageStoreCost(gasCost: c.warmAccess,
|
||||||
|
gasRefund: c.sset - c.warmAccess)
|
||||||
|
e[EVMC_STORAGE_MODIFIED_RESTORED] = StorageStoreCost(gasCost: c.warmAccess,
|
||||||
|
gasRefund: c.reset - c.warm_access)
|
||||||
|
|
||||||
|
proc storageStoreCost(): array[Fork, array[evmc_storage_status, StorageStoreCost]] {.compileTime.} =
|
||||||
|
const tbl = storageCostSpec()
|
||||||
|
for rev in Fork:
|
||||||
|
let c = tbl[rev]
|
||||||
|
if not c.netCost: # legacy
|
||||||
|
legacySStoreCost(result[rev], c)
|
||||||
|
else: # net cost
|
||||||
|
netSStoreCost(result[rev], c)
|
||||||
|
|
||||||
|
const
|
||||||
|
SstoreCost* = storageStoreCost()
|
||||||
|
|
||||||
template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
||||||
|
|
||||||
## Generate the gas cost for each forks and store them in a const
|
## Generate the gas cost for each forks and store them in a const
|
||||||
@ -224,52 +305,31 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
|||||||
|
|
||||||
func `prefix gasSstore`(value: UInt256, gasParams: GasParams): GasResult {.nimcall.} =
|
func `prefix gasSstore`(value: UInt256, gasParams: GasParams): GasResult {.nimcall.} =
|
||||||
## Value is word to save
|
## Value is word to save
|
||||||
|
|
||||||
when fork >= FkBerlin:
|
|
||||||
# EIP2929
|
|
||||||
const
|
|
||||||
SLOAD_GAS = WarmStorageReadCost
|
|
||||||
SSTORE_RESET_GAS = 5000 - ColdSloadCost
|
|
||||||
else:
|
|
||||||
const
|
|
||||||
SLOAD_GAS = FeeSchedule[GasSload]
|
|
||||||
SSTORE_RESET_GAS = FeeSchedule[GasSreset]
|
|
||||||
|
|
||||||
const
|
|
||||||
NoopGas = SLOAD_GAS # if the value doesn't change.
|
|
||||||
DirtyGas = SLOAD_GAS # if a dirty value is changed.
|
|
||||||
InitGas = FeeSchedule[GasSset] # from clean zero to non-zero
|
|
||||||
InitRefund = FeeSchedule[GasSset] - SLOAD_GAS # resetting to the original zero value
|
|
||||||
CleanGas = SSTORE_RESET_GAS # from clean non-zero to something else
|
|
||||||
CleanRefund = SSTORE_RESET_GAS - SLOAD_GAS # resetting to the original non-zero value
|
|
||||||
ClearRefund = FeeSchedule[RefundsClear]# clearing an originally existing storage slot
|
|
||||||
|
|
||||||
when defined(evmc_enabled):
|
when defined(evmc_enabled):
|
||||||
const
|
const c = SStoreCost[fork]
|
||||||
sstoreDirty = when fork < FkConstantinople or fork == FkPetersburg: CleanGas
|
let sc = c[gasParams.s_status]
|
||||||
else: DirtyGas
|
result.gasCost = sc.gasCost
|
||||||
|
result.gasRefund = sc.gasRefund
|
||||||
case gasParams.s_status
|
|
||||||
of EVMC_STORAGE_ADDED: result.gasCost = InitGas
|
|
||||||
of EVMC_STORAGE_MODIFIED: result.gasCost = CleanGas
|
|
||||||
of EVMC_STORAGE_DELETED:
|
|
||||||
result.gasCost = CleanGas
|
|
||||||
result.gasRefund += ClearRefund
|
|
||||||
of EVMC_STORAGE_UNCHANGED: result.gasCost = sstoreDirty
|
|
||||||
of EVMC_STORAGE_MODIFIED_AGAIN:
|
|
||||||
result.gasCost = sstoreDirty
|
|
||||||
if not gasParams.s_originalValue.isZero:
|
|
||||||
if gasParams.s_currentValue.isZero:
|
|
||||||
result.gasRefund -= ClearRefund
|
|
||||||
if value.isZero:
|
|
||||||
result.gasRefund += ClearRefund
|
|
||||||
|
|
||||||
if gasParams.s_originalValue == value:
|
|
||||||
if gasParams.s_originalValue.isZero:
|
|
||||||
result.gasRefund += InitRefund
|
|
||||||
else:
|
|
||||||
result.gasRefund += CleanRefund
|
|
||||||
else:
|
else:
|
||||||
|
when fork >= FkBerlin:
|
||||||
|
# EIP2929
|
||||||
|
const
|
||||||
|
SLOAD_GAS = WarmStorageReadCost
|
||||||
|
SSTORE_RESET_GAS = 5000 - ColdSloadCost
|
||||||
|
else:
|
||||||
|
const
|
||||||
|
SLOAD_GAS = FeeSchedule[GasSload]
|
||||||
|
SSTORE_RESET_GAS = FeeSchedule[GasSreset]
|
||||||
|
|
||||||
|
const
|
||||||
|
NoopGas = SLOAD_GAS # if the value doesn't change.
|
||||||
|
DirtyGas = SLOAD_GAS # if a dirty value is changed.
|
||||||
|
InitGas = FeeSchedule[GasSset] # from clean zero to non-zero
|
||||||
|
InitRefund = FeeSchedule[GasSset] - SLOAD_GAS # resetting to the original zero value
|
||||||
|
CleanGas = SSTORE_RESET_GAS # from clean non-zero to something else
|
||||||
|
CleanRefund = SSTORE_RESET_GAS - SLOAD_GAS # resetting to the original non-zero value
|
||||||
|
ClearRefund = FeeSchedule[RefundsClear]# clearing an originally existing storage slot
|
||||||
|
|
||||||
when fork < FkConstantinople or fork == FkPetersburg:
|
when fork < FkConstantinople or fork == FkPetersburg:
|
||||||
let isStorageEmpty = gasParams.s_currentValue.isZero
|
let isStorageEmpty = gasParams.s_currentValue.isZero
|
||||||
|
|
||||||
@ -760,7 +820,10 @@ const
|
|||||||
FkPetersburg: SpuriousGasFees,
|
FkPetersburg: SpuriousGasFees,
|
||||||
FkIstanbul: IstanbulGasFees,
|
FkIstanbul: IstanbulGasFees,
|
||||||
FkBerlin: BerlinGasFees,
|
FkBerlin: BerlinGasFees,
|
||||||
FkLondon: LondonGasFees
|
FkLondon: LondonGasFees,
|
||||||
|
FkParis: LondonGasFees,
|
||||||
|
FkShanghai: LondonGasFees,
|
||||||
|
FkCancun: LondonGasFees,
|
||||||
]
|
]
|
||||||
|
|
||||||
gasCosts(FkFrontier, base, BaseGasCosts)
|
gasCosts(FkFrontier, base, BaseGasCosts)
|
||||||
|
@ -40,7 +40,7 @@ type
|
|||||||
LocalParams = tuple
|
LocalParams = tuple
|
||||||
gas: UInt256
|
gas: UInt256
|
||||||
value: UInt256
|
value: UInt256
|
||||||
destination: EthAddress
|
codeAddress: EthAddress
|
||||||
sender: EthAddress
|
sender: EthAddress
|
||||||
memInPos: int
|
memInPos: int
|
||||||
memInLen: int
|
memInLen: int
|
||||||
@ -72,14 +72,14 @@ proc updateStackAndParams(q: var LocalParams; c: Computation) =
|
|||||||
# and further `childGasLimit`
|
# and further `childGasLimit`
|
||||||
if FkBerlin <= c.fork:
|
if FkBerlin <= c.fork:
|
||||||
when evmc_enabled:
|
when evmc_enabled:
|
||||||
if c.host.accessAccount(q.destination) == EVMC_ACCESS_COLD:
|
if c.host.accessAccount(q.codeAddress) == EVMC_ACCESS_COLD:
|
||||||
c.gasMeter.consumeGas(
|
c.gasMeter.consumeGas(
|
||||||
ColdAccountAccessCost - WarmStorageReadCost,
|
ColdAccountAccessCost - WarmStorageReadCost,
|
||||||
reason = "EIP2929 gasCall")
|
reason = "EIP2929 gasCall")
|
||||||
else:
|
else:
|
||||||
c.vmState.mutateStateDB:
|
c.vmState.mutateStateDB:
|
||||||
if not db.inAccessList(q.destination):
|
if not db.inAccessList(q.codeAddress):
|
||||||
db.accessList(q.destination)
|
db.accessList(q.codeAddress)
|
||||||
|
|
||||||
# The WarmStorageReadCostEIP2929 (100) is already deducted in
|
# The WarmStorageReadCostEIP2929 (100) is already deducted in
|
||||||
# the form of a constant `gasCall`
|
# the form of a constant `gasCall`
|
||||||
@ -91,7 +91,7 @@ proc updateStackAndParams(q: var LocalParams; c: Computation) =
|
|||||||
proc callParams(c: Computation): LocalParams =
|
proc callParams(c: Computation): LocalParams =
|
||||||
## Helper for callOp()
|
## Helper for callOp()
|
||||||
result.gas = c.stack.popInt()
|
result.gas = c.stack.popInt()
|
||||||
result.destination = c.stack.popAddress()
|
result.codeAddress = c.stack.popAddress()
|
||||||
result.value = c.stack.popInt()
|
result.value = c.stack.popInt()
|
||||||
result.memInPos = c.stack.popInt().cleanMemRef
|
result.memInPos = c.stack.popInt().cleanMemRef
|
||||||
result.memInLen = c.stack.popInt().cleanMemRef
|
result.memInLen = c.stack.popInt().cleanMemRef
|
||||||
@ -100,7 +100,7 @@ proc callParams(c: Computation): LocalParams =
|
|||||||
|
|
||||||
result.sender = c.msg.contractAddress
|
result.sender = c.msg.contractAddress
|
||||||
result.flags = c.msg.flags
|
result.flags = c.msg.flags
|
||||||
result.contractAddress = result.destination
|
result.contractAddress = result.codeAddress
|
||||||
|
|
||||||
result.updateStackAndParams(c)
|
result.updateStackAndParams(c)
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ proc callCodeParams(c: Computation): LocalParams =
|
|||||||
proc delegateCallParams(c: Computation): LocalParams =
|
proc delegateCallParams(c: Computation): LocalParams =
|
||||||
## Helper for delegateCall()
|
## Helper for delegateCall()
|
||||||
result.gas = c.stack.popInt()
|
result.gas = c.stack.popInt()
|
||||||
result.destination = c.stack.popAddress()
|
result.codeAddress = c.stack.popAddress()
|
||||||
result.memInPos = c.stack.popInt().cleanMemRef
|
result.memInPos = c.stack.popInt().cleanMemRef
|
||||||
result.memInLen = c.stack.popInt().cleanMemRef
|
result.memInLen = c.stack.popInt().cleanMemRef
|
||||||
result.memOutPos = c.stack.popInt().cleanMemRef
|
result.memOutPos = c.stack.popInt().cleanMemRef
|
||||||
@ -131,7 +131,7 @@ proc delegateCallParams(c: Computation): LocalParams =
|
|||||||
proc staticCallParams(c: Computation): LocalParams =
|
proc staticCallParams(c: Computation): LocalParams =
|
||||||
## Helper for staticCall()
|
## Helper for staticCall()
|
||||||
result.gas = c.stack.popInt()
|
result.gas = c.stack.popInt()
|
||||||
result.destination = c.stack.popAddress()
|
result.codeAddress = c.stack.popAddress()
|
||||||
result.memInPos = c.stack.popInt().cleanMemRef
|
result.memInPos = c.stack.popInt().cleanMemRef
|
||||||
result.memInLen = c.stack.popInt().cleanMemRef
|
result.memInLen = c.stack.popInt().cleanMemRef
|
||||||
result.memOutPos = c.stack.popInt().cleanMemRef
|
result.memOutPos = c.stack.popInt().cleanMemRef
|
||||||
@ -140,7 +140,7 @@ proc staticCallParams(c: Computation): LocalParams =
|
|||||||
result.value = 0.u256
|
result.value = 0.u256
|
||||||
result.sender = c.msg.contractAddress
|
result.sender = c.msg.contractAddress
|
||||||
result.flags = emvcStatic
|
result.flags = emvcStatic
|
||||||
result.contractAddress = result.destination
|
result.contractAddress = result.codeAddress
|
||||||
|
|
||||||
result.updateStackAndParams(c)
|
result.updateStackAndParams(c)
|
||||||
|
|
||||||
@ -246,15 +246,16 @@ const
|
|||||||
msg = new(nimbus_message)
|
msg = new(nimbus_message)
|
||||||
c = k.cpt
|
c = k.cpt
|
||||||
msg[] = nimbus_message(
|
msg[] = nimbus_message(
|
||||||
kind : evmcCall.evmc_call_kind,
|
kind : evmcCall.evmc_call_kind,
|
||||||
depth : (k.cpt.msg.depth + 1).int32,
|
depth : (k.cpt.msg.depth + 1).int32,
|
||||||
gas : childGasLimit,
|
gas : childGasLimit,
|
||||||
sender : p.sender,
|
sender : p.sender,
|
||||||
destination: p.destination,
|
recipient : p.contractAddress,
|
||||||
input_data : k.cpt.memory.readPtr(p.memInPos),
|
code_address: p.codeAddress,
|
||||||
input_size : p.memInLen.uint,
|
input_data : k.cpt.memory.readPtr(p.memInPos),
|
||||||
value : toEvmc(p.value),
|
input_size : p.memInLen.uint,
|
||||||
flags : p.flags.uint32
|
value : toEvmc(p.value),
|
||||||
|
flags : p.flags.uint32
|
||||||
)
|
)
|
||||||
c.execSubCall(msg, p)
|
c.execSubCall(msg, p)
|
||||||
else:
|
else:
|
||||||
@ -267,7 +268,7 @@ const
|
|||||||
gas: childGasLimit,
|
gas: childGasLimit,
|
||||||
sender: p.sender,
|
sender: p.sender,
|
||||||
contractAddress: p.contractAddress,
|
contractAddress: p.contractAddress,
|
||||||
codeAddress: p.destination,
|
codeAddress: p.codeAddress,
|
||||||
value: p.value,
|
value: p.value,
|
||||||
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
||||||
flags: p.flags))
|
flags: p.flags))
|
||||||
@ -329,15 +330,16 @@ const
|
|||||||
msg = new(nimbus_message)
|
msg = new(nimbus_message)
|
||||||
c = k.cpt
|
c = k.cpt
|
||||||
msg[] = nimbus_message(
|
msg[] = nimbus_message(
|
||||||
kind : evmcCallCode.evmc_call_kind,
|
kind : evmcCallCode.evmc_call_kind,
|
||||||
depth : (k.cpt.msg.depth + 1).int32,
|
depth : (k.cpt.msg.depth + 1).int32,
|
||||||
gas : childGasLimit,
|
gas : childGasLimit,
|
||||||
sender : p.sender,
|
sender : p.sender,
|
||||||
destination: p.destination,
|
recipient : p.contractAddress,
|
||||||
input_data : k.cpt.memory.readPtr(p.memInPos),
|
code_address: p.codeAddress,
|
||||||
input_size : p.memInLen.uint,
|
input_data : k.cpt.memory.readPtr(p.memInPos),
|
||||||
value : toEvmc(p.value),
|
input_size : p.memInLen.uint,
|
||||||
flags : p.flags.uint32
|
value : toEvmc(p.value),
|
||||||
|
flags : p.flags.uint32
|
||||||
)
|
)
|
||||||
c.execSubCall(msg, p)
|
c.execSubCall(msg, p)
|
||||||
else:
|
else:
|
||||||
@ -350,7 +352,7 @@ const
|
|||||||
gas: childGasLimit,
|
gas: childGasLimit,
|
||||||
sender: p.sender,
|
sender: p.sender,
|
||||||
contractAddress: p.contractAddress,
|
contractAddress: p.contractAddress,
|
||||||
codeAddress: p.destination,
|
codeAddress: p.codeAddress,
|
||||||
value: p.value,
|
value: p.value,
|
||||||
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
||||||
flags: p.flags))
|
flags: p.flags))
|
||||||
@ -401,15 +403,16 @@ const
|
|||||||
msg = new(nimbus_message)
|
msg = new(nimbus_message)
|
||||||
c = k.cpt
|
c = k.cpt
|
||||||
msg[] = nimbus_message(
|
msg[] = nimbus_message(
|
||||||
kind : evmcDelegateCall.evmc_call_kind,
|
kind : evmcDelegateCall.evmc_call_kind,
|
||||||
depth : (k.cpt.msg.depth + 1).int32,
|
depth : (k.cpt.msg.depth + 1).int32,
|
||||||
gas : childGasLimit,
|
gas : childGasLimit,
|
||||||
sender : p.sender,
|
sender : p.sender,
|
||||||
destination: p.destination,
|
recipient : p.contractAddress,
|
||||||
input_data : k.cpt.memory.readPtr(p.memInPos),
|
code_address: p.codeAddress,
|
||||||
input_size : p.memInLen.uint,
|
input_data : k.cpt.memory.readPtr(p.memInPos),
|
||||||
value : toEvmc(p.value),
|
input_size : p.memInLen.uint,
|
||||||
flags : p.flags.uint32
|
value : toEvmc(p.value),
|
||||||
|
flags : p.flags.uint32
|
||||||
)
|
)
|
||||||
c.execSubCall(msg, p)
|
c.execSubCall(msg, p)
|
||||||
else:
|
else:
|
||||||
@ -422,7 +425,7 @@ const
|
|||||||
gas: childGasLimit,
|
gas: childGasLimit,
|
||||||
sender: p.sender,
|
sender: p.sender,
|
||||||
contractAddress: p.contractAddress,
|
contractAddress: p.contractAddress,
|
||||||
codeAddress: p.destination,
|
codeAddress: p.codeAddress,
|
||||||
value: p.value,
|
value: p.value,
|
||||||
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
||||||
flags: p.flags))
|
flags: p.flags))
|
||||||
@ -451,7 +454,7 @@ const
|
|||||||
# from 700 to 40
|
# from 700 to 40
|
||||||
#
|
#
|
||||||
# when opCode == StaticCall:
|
# when opCode == StaticCall:
|
||||||
# if k.cpt.fork >= FkBerlin and destination.toInt <= MaxPrecompilesAddr:
|
# if k.cpt.fork >= FkBerlin and codeAddress.toInt <= MaxPrecompilesAddr:
|
||||||
# gasCost = gasCost - 660.GasInt
|
# gasCost = gasCost - 660.GasInt
|
||||||
if gasCost >= 0:
|
if gasCost >= 0:
|
||||||
k.cpt.gasMeter.consumeGas(gasCost, reason = $StaticCall)
|
k.cpt.gasMeter.consumeGas(gasCost, reason = $StaticCall)
|
||||||
@ -478,15 +481,16 @@ const
|
|||||||
msg = new(nimbus_message)
|
msg = new(nimbus_message)
|
||||||
c = k.cpt
|
c = k.cpt
|
||||||
msg[] = nimbus_message(
|
msg[] = nimbus_message(
|
||||||
kind : evmcCall.evmc_call_kind,
|
kind : evmcCall.evmc_call_kind,
|
||||||
depth : (k.cpt.msg.depth + 1).int32,
|
depth : (k.cpt.msg.depth + 1).int32,
|
||||||
gas : childGasLimit,
|
gas : childGasLimit,
|
||||||
sender : p.sender,
|
sender : p.sender,
|
||||||
destination: p.destination,
|
recipient : p.contractAddress,
|
||||||
input_data : k.cpt.memory.readPtr(p.memInPos),
|
code_address: p.codeAddress,
|
||||||
input_size : p.memInLen.uint,
|
input_data : k.cpt.memory.readPtr(p.memInPos),
|
||||||
value : toEvmc(p.value),
|
input_size : p.memInLen.uint,
|
||||||
flags : p.flags.uint32
|
value : toEvmc(p.value),
|
||||||
|
flags : p.flags.uint32
|
||||||
)
|
)
|
||||||
c.execSubCall(msg, p)
|
c.execSubCall(msg, p)
|
||||||
else:
|
else:
|
||||||
@ -499,7 +503,7 @@ const
|
|||||||
gas: childGasLimit,
|
gas: childGasLimit,
|
||||||
sender: p.sender,
|
sender: p.sender,
|
||||||
contractAddress: p.contractAddress,
|
contractAddress: p.contractAddress,
|
||||||
codeAddress: p.destination,
|
codeAddress: p.codeAddress,
|
||||||
value: p.value,
|
value: p.value,
|
||||||
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
||||||
flags: p.flags))
|
flags: p.flags))
|
||||||
|
@ -256,6 +256,3 @@ else:
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# End
|
# End
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
when defined(evmc_enabled):
|
|
||||||
include evmc_host
|
|
||||||
|
@ -48,4 +48,8 @@ export
|
|||||||
vmg.ACCESS_LIST_STORAGE_KEY_COST,
|
vmg.ACCESS_LIST_STORAGE_KEY_COST,
|
||||||
vmg.ACCESS_LIST_ADDRESS_COST
|
vmg.ACCESS_LIST_ADDRESS_COST
|
||||||
|
|
||||||
|
when defined(evmc_enabled):
|
||||||
|
export
|
||||||
|
vmg.SstoreCost
|
||||||
|
|
||||||
# End
|
# End
|
||||||
|
Loading…
x
Reference in New Issue
Block a user