diff --git a/nimbus/vm/computation.nim b/nimbus/vm/computation.nim index f6375cf76..fad89fe08 100644 --- a/nimbus/vm/computation.nim +++ b/nimbus/vm/computation.nim @@ -34,6 +34,9 @@ proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Me else: blockNumber.toFork.forkToSchedule result.forkOverride = forkOverride + # a dummy/terminus continuation proc + result.nextProc = proc() = + discard proc isOriginComputation*(c: BaseComputation): bool = # Is this computation the computation initiated by a transaction @@ -168,6 +171,8 @@ proc transferBalance(computation: BaseComputation, opCode: static[Op]) = db.addBalance(computation.msg.storageAddress, computation.msg.value) template continuation*(comp: BaseComputation, body: untyped) = + # this is a helper template to implement continuation + # passing and convert all recursion into tail call var tmpNext = comp.nextProc comp.nextProc = proc() = body @@ -189,21 +194,27 @@ proc executeOpcodes*(computation: BaseComputation) {.gcsafe.} proc applyMessage*(computation: BaseComputation, opCode: static[Op]) = computation.snapshot() - defer: computation.dispose() + defer: + computation.dispose() when opCode in {CallCode, Call, Create}: computation.transferBalance(opCode) if computation.isError(): computation.rollback() + computation.nextProc() return if computation.gasMeter.gasRemaining < 0: computation.commit() + computation.nextProc() return - executeOpcodes(computation) - postExecuteVM(computation) + continuation(computation): + postExecuteVM(computation) + executeOpcodes(computation) + computation.nextProc() + proc addChildComputation*(computation: BaseComputation, child: BaseComputation) = if child.isError: if child.msg.isCreate: diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index c7e3b8952..192ed26cc 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -593,13 +593,15 @@ op create, inline = false, value, startPosition, size: var childComp = setupCreate(computation, memPos, len, value) if childComp.isNil: return - childComp.applyMessage(Create) - computation.addChildComputation(childComp) + continuation(childComp): + computation.addChildComputation(childComp) - if childComp.isError: - push: 0 - else: - push: childComp.msg.storageAddress + if childComp.isError: + push: 0 + else: + push: childComp.msg.storageAddress + + childComp.applyMessage(Create) proc callParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, MsgFlags) = let gas = computation.stack.popInt() @@ -683,7 +685,7 @@ proc staticCallParams(computation: BaseComputation): (UInt256, UInt256, EthAddre emvcStatic) # is_static template genCall(callName: untyped, opCode: Op): untyped = - proc `callName Setup`(computation: BaseComputation, callNameStr: string): (BaseComputation, int, int) = + proc `callName Setup`(computation: BaseComputation, callNameStr: string): BaseComputation = let (gas, value, to, sender, codeAddress, memoryInputStartPosition, memoryInputSize, @@ -750,28 +752,32 @@ template genCall(callName: untyped, opCode: Op): untyped = childMsg, some(computation.getFork)) - result = (childComp, memOutPos, memOutLen) + computation.memOutPos = memOutPos + computation.memOutLen = memOutLen + result = childComp op callName, inline = false: ## CALL, 0xf1, Message-Call into an account ## CALLCODE, 0xf2, Message-call into this account with an alternative account's code. ## DELEGATECALL, 0xf4, Message-call into this account with an alternative account's code, but persisting the current values for sender and value. ## STATICCALL, 0xfa, Static message-call into an account. - var (childComp, memOutPos, memOutLen) = `callName Setup`(computation, callName.astToStr) + var childComp = `callName Setup`(computation, callName.astToStr) + + continuation(childComp): + addChildComputation(computation, childComp) + + if childComp.isError: + push: 0 + else: + push: 1 + + if not childComp.shouldEraseReturnData: + let actualOutputSize = min(computation.memOutLen, childComp.output.len) + computation.memory.write( + computation.memOutPos, + childComp.output.toOpenArray(0, actualOutputSize - 1)) childComp.applyMessage(opCode) - addChildComputation(computation, childComp) - - if childComp.isError: - push: 0 - else: - push: 1 - - if not childComp.shouldEraseReturnData: - let actualOutputSize = min(memOutLen, childComp.output.len) - computation.memory.write( - memOutPos, - childComp.output.toOpenArray(0, actualOutputSize - 1)) genCall(call, Call) genCall(callCode, CallCode) diff --git a/nimbus/vm_types.nim b/nimbus/vm_types.nim index 051577d56..b71ee91e9 100644 --- a/nimbus/vm_types.nim +++ b/nimbus/vm_types.nim @@ -67,7 +67,10 @@ type dbsnapshot*: Snapshot instr*: Op opIndex*: int + # continuation helpers nextProc*: proc() + memOutLen*: int + memOutPos*: int Error* = ref object info*: string