diff --git a/nimbus/core/executor/process_transaction.nim b/nimbus/core/executor/process_transaction.nim index 2b68e1ada..028ed79b6 100644 --- a/nimbus/core/executor/process_transaction.nim +++ b/nimbus/core/executor/process_transaction.nim @@ -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*( diff --git a/nimbus/db/accounts_cache.nim b/nimbus/db/accounts_cache.nim index 8d7e3afbd..5a83c4e43 100644 --- a/nimbus/db/accounts_cache.nim +++ b/nimbus/db/accounts_cache.nim @@ -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) = diff --git a/nimbus/evm/computation.nim b/nimbus/evm/computation.nim index bba589e98..3edd881ef 100644 --- a/nimbus/evm/computation.nim +++ b/nimbus/evm/computation.nim @@ -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 diff --git a/nimbus/evm/interpreter/op_handlers/oph_call.nim b/nimbus/evm/interpreter/op_handlers/oph_call.nim index 517019f05..8b8b744ab 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_call.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_call.nim @@ -172,7 +172,7 @@ else: # need to provide explicit and for capturing in chainTo proc() # and 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: diff --git a/nimbus/evm/interpreter/op_handlers/oph_create.nim b/nimbus/evm/interpreter/op_handlers/oph_create.nim index a7749abc9..3436e4ec6 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_create.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_create.nim @@ -63,7 +63,7 @@ else: # need to provide explicit and 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: diff --git a/nimbus/evm/state_transactions.nim b/nimbus/evm/state_transactions.nim index aa2a8305d..aaf7c0346 100644 --- a/nimbus/evm/state_transactions.nim +++ b/nimbus/evm/state_transactions.nim @@ -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() diff --git a/nimbus/evm/types.nim b/nimbus/evm/types.nim index e0d01fa8d..627b7bd4a 100644 --- a/nimbus/evm/types.nim +++ b/nimbus/evm/types.nim @@ -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 diff --git a/nimbus/transaction/call_common.nim b/nimbus/transaction/call_common.nim index fb42f90d5..e34df80cd 100644 --- a/nimbus/transaction/call_common.nim +++ b/nimbus/transaction/call_common.nim @@ -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) diff --git a/nimbus/transaction/evmc_vm_glue.nim b/nimbus/transaction/evmc_vm_glue.nim index c7577d4e9..7df2b527b 100644 --- a/nimbus/transaction/evmc_vm_glue.nim +++ b/nimbus/transaction/evmc_vm_glue.nim @@ -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 diff --git a/nimbus/transaction/host_call_nested.nim b/nimbus/transaction/host_call_nested.nim index 9f65c67b8..aa1e1bc75 100644 --- a/nimbus/transaction/host_call_nested.nim +++ b/nimbus/transaction/host_call_nested.nim @@ -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.} = diff --git a/nimbus/vm_state_transactions.nim b/nimbus/vm_state_transactions.nim index a8539d7ba..24ff655f1 100644 --- a/nimbus/vm_state_transactions.nim +++ b/nimbus/vm_state_transactions.nim @@ -13,6 +13,7 @@ import export vmx.asyncExecComputation, - vmx.execComputation + vmx.execComputation, + vmx.execSysCall # End