EIP-4788: Make it clear what is an EVM system call

This commit is contained in:
jangko 2023-09-20 20:10:16 +07:00
parent 2d2def9a8d
commit cc7a7db74b
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
11 changed files with 47 additions and 32 deletions

View File

@ -140,32 +140,26 @@ proc processBeaconBlockRoot*(vmState: BaseVMState, beaconRoot: Hash256):
let let
statedb = vmState.stateDB statedb = vmState.stateDB
call = CallParams( call = CallParams(
vmState: vmState, vmState : vmState,
sender: SystemAddress, sender : SystemAddress,
gasLimit: 30_000_000.GasInt, gasLimit : 30_000_000.GasInt,
gasPrice: 0.GasInt, gasPrice : 0.GasInt,
to: BeaconRootsStorageAddress, to : BeaconRootsStorageAddress,
input: @(beaconRoot.data), input : @(beaconRoot.data),
# It's a systemCall, no need for other knicks knacks
sysCall : true,
noAccessList: true,
noIntrinsic : true,
noGasCharge : true,
noRefund : true,
) )
# runComputation a.k.a syscall/evm.call # runComputation a.k.a syscall/evm.call
if call.runComputation().isError: if call.runComputation().isError:
return err("processBeaconBlockRoot: syscall error") return err("processBeaconBlockRoot: syscall error")
# We can choose to set SystemAddress nonce to 0 statedb.persist(clearEmptyAccount = true, clearCache = false)
# like erigon or geth(their EVM have explicit nonce set)
# or we delete the account manually instead of let it deleted
# by AccountsCache.persist.
statedb.deleteAccount(SystemAddress)
when false:
# nimbus EVM automatically increase sender nonce by one
# for each call/create.
statedb.setNonce(SystemAddress, 0)
# statedb.persist probably not needed as each processTransaction
# will call it.
statedb.persist(clearEmptyAccount = true, clearCache = false)
ok() ok()
proc asyncProcessTransaction*( proc asyncProcessTransaction*(

View File

@ -448,7 +448,7 @@ proc setBalance*(ac: AccountsCache, address: EthAddress, balance: UInt256) =
proc addBalance*(ac: AccountsCache, address: EthAddress, delta: UInt256) {.inline.} = proc addBalance*(ac: AccountsCache, address: EthAddress, delta: UInt256) {.inline.} =
# EIP161: We must check emptiness for the objects such that the account # EIP161: We must check emptiness for the objects such that the account
# clearing (0,0,0 objects) can take effect. # clearing (0,0,0 objects) can take effect.
if delta == 0.u256: if delta.isZero:
let acc = ac.getAccount(address) let acc = ac.getAccount(address)
if acc.isEmpty: if acc.isEmpty:
ac.makeDirty(address).flags.incl Touched ac.makeDirty(address).flags.incl Touched
@ -456,6 +456,12 @@ proc addBalance*(ac: AccountsCache, address: EthAddress, delta: UInt256) {.inlin
ac.setBalance(address, ac.getBalance(address) + delta) ac.setBalance(address, ac.getBalance(address) + delta)
proc subBalance*(ac: AccountsCache, address: EthAddress, delta: UInt256) {.inline.} = proc subBalance*(ac: AccountsCache, address: EthAddress, delta: UInt256) {.inline.} =
if delta.isZero:
# This zero delta early exit is important as shown in EIP-4788.
# If the account is created, it will change the state.
# But early exit will prevent the account creation.
# In this case, the SystemAddress
return
ac.setBalance(address, ac.getBalance(address) - delta) ac.setBalance(address, ac.getBalance(address) - delta)
proc setNonce*(ac: AccountsCache, address: EthAddress, nonce: AccountNonce) = proc setNonce*(ac: AccountsCache, address: EthAddress, nonce: AccountNonce) =

View File

@ -212,7 +212,7 @@ template getTransientStorage*(c: Computation, slot: UInt256): UInt256 =
c.vmState.readOnlyStateDB. c.vmState.readOnlyStateDB.
getTransientStorage(c.msg.contractAddress, slot) getTransientStorage(c.msg.contractAddress, slot)
proc newComputation*(vmState: BaseVMState, message: Message, proc newComputation*(vmState: BaseVMState, sysCall: bool, message: Message,
salt: ContractSalt = ZERO_CONTRACTSALT): Computation = salt: ContractSalt = ZERO_CONTRACTSALT): Computation =
new result new result
result.vmState = vmState result.vmState = vmState
@ -221,6 +221,7 @@ proc newComputation*(vmState: BaseVMState, message: Message,
result.stack = newStack() result.stack = newStack()
result.returnStack = @[] result.returnStack = @[]
result.gasMeter.init(message.gas) result.gasMeter.init(message.gas)
result.sysCall = sysCall
if result.msg.isCreate(): if result.msg.isCreate():
result.msg.contractAddress = result.generateContractAddress(salt) result.msg.contractAddress = result.generateContractAddress(salt)
@ -230,7 +231,8 @@ proc newComputation*(vmState: BaseVMState, message: Message,
result.code = newCodeStream( result.code = newCodeStream(
vmState.readOnlyStateDB.getCode(message.codeAddress)) vmState.readOnlyStateDB.getCode(message.codeAddress))
proc newComputation*(vmState: BaseVMState, message: Message, code: seq[byte]): Computation = proc newComputation*(vmState: BaseVMState, sysCall: bool,
message: Message, code: seq[byte]): Computation =
new result new result
result.vmState = vmState result.vmState = vmState
result.msg = message result.msg = message
@ -239,6 +241,7 @@ proc newComputation*(vmState: BaseVMState, message: Message, code: seq[byte]): C
result.returnStack = @[] result.returnStack = @[]
result.gasMeter.init(message.gas) result.gasMeter.init(message.gas)
result.code = newCodeStream(code) result.code = newCodeStream(code)
result.sysCall = sysCall
template gasCosts*(c: Computation): untyped = template gasCosts*(c: Computation): untyped =
c.vmState.gasCosts c.vmState.gasCosts

View File

@ -172,7 +172,7 @@ else:
# need to provide explicit <c> and <child> for capturing in chainTo proc() # need to provide explicit <c> and <child> for capturing in chainTo proc()
# <memPos> and <memLen> are provided by value and need not be captured # <memPos> and <memLen> are provided by value and need not be captured
var var
child = newComputation(c.vmState, childMsg) child = newComputation(c.vmState, false, childMsg)
c.chainTo(child): c.chainTo(child):
if not child.shouldBurnGas: if not child.shouldBurnGas:

View File

@ -63,7 +63,7 @@ else:
# need to provide explicit <c> and <child> for capturing in chainTo proc() # need to provide explicit <c> and <child> for capturing in chainTo proc()
var var
child = newComputation(c.vmState, childMsg, salt) child = newComputation(c.vmState, false, childMsg, salt)
c.chainTo(child): c.chainTo(child):
if not child.shouldBurnGas: if not child.shouldBurnGas:

View File

@ -63,6 +63,9 @@ proc execComputation*(c: Computation)
c.execCallOrCreate() c.execCallOrCreate()
c.postExecComputation() c.postExecComputation()
template execSysCall*(c: Computation) =
c.execCallOrCreate()
# FIXME-duplicatedForAsync # FIXME-duplicatedForAsync
proc asyncExecComputation*(c: Computation): Future[void] {.async.} = proc asyncExecComputation*(c: Computation): Future[void] {.async.} =
c.preExecComputation() c.preExecComputation()

View File

@ -87,6 +87,7 @@ type
parent*, child*: Computation parent*, child*: Computation
pendingAsyncOperation*: Future[void] pendingAsyncOperation*: Future[void]
continuation*: proc() {.gcsafe, raises: [CatchableError].} continuation*: proc() {.gcsafe, raises: [CatchableError].}
sysCall*: bool
Error* = ref object Error* = ref object
statusCode*: evmc_status_code statusCode*: evmc_status_code

View File

@ -42,6 +42,7 @@ type
noAccessList*: bool # Don't initialise EIP-2929 access list. noAccessList*: bool # Don't initialise EIP-2929 access list.
noGasCharge*: bool # Don't charge sender account for gas. noGasCharge*: bool # Don't charge sender account for gas.
noRefund*: bool # Don't apply gas refund/burn rule. noRefund*: bool # Don't apply gas refund/burn rule.
sysCall*: bool # System call or ordinary call
# Standard call result. (Some fields are beyond what EVMC can return, # Standard call result. (Some fields are beyond what EVMC can return,
# and must only be used from tests because they will not always be set). # and must only be used from tests because they will not always be set).
@ -176,7 +177,7 @@ proc setupHost(call: CallParams): TransactionHost =
host.msg.input_data = host.input[0].addr host.msg.input_data = host.input[0].addr
let cMsg = hostToComputationMessage(host.msg) let cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(vmState, cMsg, code) host.computation = newComputation(vmState, call.sysCall, cMsg, code)
shallowCopy(host.code, code) shallowCopy(host.code, code)
@ -189,7 +190,7 @@ proc setupHost(call: CallParams): TransactionHost =
host.msg.input_data = host.input[0].addr host.msg.input_data = host.input[0].addr
let cMsg = hostToComputationMessage(host.msg) let cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(vmState, cMsg) host.computation = newComputation(vmState, call.sysCall, cMsg)
vmState.captureStart(host.computation, call.sender, call.to, vmState.captureStart(host.computation, call.sender, call.to,
call.isCreate, call.input, call.isCreate, call.input,
@ -297,7 +298,10 @@ proc runComputation*(call: CallParams): CallResult
when defined(evmc_enabled): when defined(evmc_enabled):
doExecEvmc(host, call) doExecEvmc(host, call)
else: else:
execComputation(host.computation) if host.computation.sysCall:
execSysCall(host.computation)
else:
execComputation(host.computation)
finishRunningComputation(host, call) finishRunningComputation(host, call)

View File

@ -38,7 +38,10 @@ proc evmcExecute(vm: ptr evmc_vm, hostInterface: ptr evmc_host_interface,
# host.computation = c # host.computation = c
c.host.init(cast[ptr nimbus_host_interface](hostInterface), hostContext) c.host.init(cast[ptr nimbus_host_interface](hostInterface), hostContext)
execComputation(c) if c.sysCall:
execSysCall(c)
else:
execComputation(c)
# When output size is zero, output data pointer may be null. # When output size is zero, output data pointer may be null.
var output_data: ptr byte var output_data: ptr byte

View File

@ -30,7 +30,7 @@ proc beforeExecCreateEvmcNested(host: TransactionHost,
value: m.value.fromEvmc, value: m.value.fromEvmc,
data: @(makeOpenArray(m.inputData, m.inputSize.int)) data: @(makeOpenArray(m.inputData, m.inputSize.int))
) )
return newComputation(host.vmState, childMsg, return newComputation(host.vmState, false, childMsg,
cast[ContractSalt](m.create2_salt)) cast[ContractSalt](m.create2_salt))
proc afterExecCreateEvmcNested(host: TransactionHost, child: Computation, proc afterExecCreateEvmcNested(host: TransactionHost, child: Computation,
@ -67,7 +67,7 @@ proc beforeExecCallEvmcNested(host: TransactionHost,
data: @(makeOpenArray(m.inputData, m.inputSize.int)), data: @(makeOpenArray(m.inputData, m.inputSize.int)),
flags: m.flags, flags: m.flags,
) )
return newComputation(host.vmState, childMsg) return newComputation(host.vmState, false, childMsg)
proc afterExecCallEvmcNested(host: TransactionHost, child: Computation, proc afterExecCallEvmcNested(host: TransactionHost, child: Computation,
res: var EvmcResult) {.inline.} = res: var EvmcResult) {.inline.} =

View File

@ -13,6 +13,7 @@ import
export export
vmx.asyncExecComputation, vmx.asyncExecComputation,
vmx.execComputation vmx.execComputation,
vmx.execSysCall
# End # End