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
statedb = vmState.stateDB
call = CallParams(
vmState: vmState,
sender: SystemAddress,
gasLimit: 30_000_000.GasInt,
gasPrice: 0.GasInt,
to: BeaconRootsStorageAddress,
input: @(beaconRoot.data),
vmState : vmState,
sender : SystemAddress,
gasLimit : 30_000_000.GasInt,
gasPrice : 0.GasInt,
to : BeaconRootsStorageAddress,
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
if call.runComputation().isError:
return err("processBeaconBlockRoot: syscall error")
# We can choose to set SystemAddress nonce to 0
# 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)
statedb.persist(clearEmptyAccount = true, clearCache = false)
ok()
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.} =
# EIP161: We must check emptiness for the objects such that the account
# clearing (0,0,0 objects) can take effect.
if delta == 0.u256:
if delta.isZero:
let acc = ac.getAccount(address)
if acc.isEmpty:
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)
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)
proc setNonce*(ac: AccountsCache, address: EthAddress, nonce: AccountNonce) =

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,6 +42,7 @@ type
noAccessList*: bool # Don't initialise EIP-2929 access list.
noGasCharge*: bool # Don't charge sender account for gas.
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,
# 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
let cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(vmState, cMsg, code)
host.computation = newComputation(vmState, call.sysCall, cMsg, code)
shallowCopy(host.code, code)
@ -189,7 +190,7 @@ proc setupHost(call: CallParams): TransactionHost =
host.msg.input_data = host.input[0].addr
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,
call.isCreate, call.input,
@ -297,7 +298,10 @@ proc runComputation*(call: CallParams): CallResult
when defined(evmc_enabled):
doExecEvmc(host, call)
else:
execComputation(host.computation)
if host.computation.sysCall:
execSysCall(host.computation)
else:
execComputation(host.computation)
finishRunningComputation(host, call)

View File

@ -38,7 +38,10 @@ proc evmcExecute(vm: ptr evmc_vm, hostInterface: ptr evmc_host_interface,
# host.computation = c
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.
var output_data: ptr byte

View File

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

View File

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