refactor/relocate CALL/CREATE to reduce stack usage
This commit is contained in:
parent
4ed98e03d1
commit
039ab1ce71
|
@ -183,7 +183,7 @@ proc writeContract(fork: Fork, computation: var BaseComputation, opCode: static[
|
||||||
if fork < FkHomestead: computation.output = @[]
|
if fork < FkHomestead: computation.output = @[]
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc generateChildComputation*(fork: Fork, computation: var BaseComputation, childMsg: Message, opCode: static[Op]): BaseComputation =
|
proc generateChildComputation*(fork: Fork, computation: var BaseComputation, childMsg: Message): BaseComputation =
|
||||||
var childComp = newBaseComputation(
|
var childComp = newBaseComputation(
|
||||||
computation.vmState,
|
computation.vmState,
|
||||||
computation.vmState.blockNumber,
|
computation.vmState.blockNumber,
|
||||||
|
@ -193,20 +193,6 @@ proc generateChildComputation*(fork: Fork, computation: var BaseComputation, chi
|
||||||
# Copy the fork op code executor proc (assumes child computation is in the same fork)
|
# Copy the fork op code executor proc (assumes child computation is in the same fork)
|
||||||
childComp.opCodeExec = computation.opCodeExec
|
childComp.opCodeExec = computation.opCodeExec
|
||||||
|
|
||||||
var snapshot = computation.snapshot()
|
|
||||||
defer: snapshot.dispose()
|
|
||||||
var contractOK = true
|
|
||||||
|
|
||||||
if applyMessage(childComp, opCode):
|
|
||||||
if childMsg.isCreate:
|
|
||||||
contractOK = fork.writeContract(childComp, opCode)
|
|
||||||
|
|
||||||
if not contractOK and fork == FkHomestead:
|
|
||||||
# consume all gas
|
|
||||||
snapshot.revert(true)
|
|
||||||
else:
|
|
||||||
snapshot.commit()
|
|
||||||
|
|
||||||
return childComp
|
return childComp
|
||||||
|
|
||||||
proc addChildComputation(fork: Fork, computation: var BaseComputation, child: BaseComputation) =
|
proc addChildComputation(fork: Fork, computation: var BaseComputation, child: BaseComputation) =
|
||||||
|
@ -234,11 +220,25 @@ proc getFork*(computation: BaseComputation): Fork =
|
||||||
else:
|
else:
|
||||||
computation.vmState.blockNumber.toFork
|
computation.vmState.blockNumber.toFork
|
||||||
|
|
||||||
proc applyChildComputation*(computation: var BaseComputation, childMsg: Message, opCode: static[Op]): BaseComputation =
|
proc applyChildComputation*(parentComp, childComp: var BaseComputation, opCode: static[Op]) =
|
||||||
## Apply the vm message childMsg as a child computation.
|
## Apply the vm message childMsg as a child computation.
|
||||||
let fork = computation.getFork
|
let fork = parentComp.getFork
|
||||||
result = fork.generateChildComputation(computation, childMsg, opCode)
|
|
||||||
fork.addChildComputation(computation, result)
|
var snapshot = parentComp.snapshot()
|
||||||
|
defer: snapshot.dispose()
|
||||||
|
var contractOK = true
|
||||||
|
|
||||||
|
if applyMessage(childComp, opCode):
|
||||||
|
if childComp.msg.isCreate:
|
||||||
|
contractOK = fork.writeContract(childComp, opCode)
|
||||||
|
|
||||||
|
if not contractOK and fork == FkHomestead:
|
||||||
|
# consume all gas
|
||||||
|
snapshot.revert(true)
|
||||||
|
else:
|
||||||
|
snapshot.commit()
|
||||||
|
|
||||||
|
fork.addChildComputation(parentComp, childComp)
|
||||||
|
|
||||||
proc registerAccountForDeletion*(c: var BaseComputation, beneficiary: EthAddress) =
|
proc registerAccountForDeletion*(c: var BaseComputation, beneficiary: EthAddress) =
|
||||||
if c.msg.storageAddress in c.accountsToDelete:
|
if c.msg.storageAddress in c.accountsToDelete:
|
||||||
|
|
|
@ -506,11 +506,7 @@ genLog()
|
||||||
# ##########################################
|
# ##########################################
|
||||||
# f0s: System operations.
|
# f0s: System operations.
|
||||||
|
|
||||||
op create, inline = false, value, startPosition, size:
|
proc transferBalance(computation: BaseComputation, memPos, len: int, value: Uint256): bool =
|
||||||
## 0xf0, Create a new account with associated code.
|
|
||||||
# TODO: Forked create for Homestead
|
|
||||||
|
|
||||||
let (memPos, len) = (startPosition.cleanMemRef, size.cleanMemRef)
|
|
||||||
# tricky gasCost: 1,0,0 -> createCost. 0,0,x -> depositCost
|
# tricky gasCost: 1,0,0 -> createCost. 0,0,x -> depositCost
|
||||||
let gasCost = computation.gasCosts[Create].m_handler(1, 0, 0)
|
let gasCost = computation.gasCosts[Create].m_handler(1, 0, 0)
|
||||||
let reason = &"CREATE: GasCreate + {len} * memory expansion"
|
let reason = &"CREATE: GasCreate + {len} * memory expansion"
|
||||||
|
@ -527,30 +523,15 @@ op create, inline = false, value, startPosition, size:
|
||||||
|
|
||||||
if senderBalance < value:
|
if senderBalance < value:
|
||||||
debug "Computation Failure", reason = "Insufficient funds available to transfer", required = computation.msg.value, balance = senderBalance
|
debug "Computation Failure", reason = "Insufficient funds available to transfer", required = computation.msg.value, balance = senderBalance
|
||||||
push: 0
|
return false
|
||||||
return
|
|
||||||
|
|
||||||
if computation.msg.depth >= MaxCallDepth:
|
if computation.msg.depth >= MaxCallDepth:
|
||||||
debug "Computation Failure", reason = "Stack too deep", maximumDepth = MaxCallDepth, depth = computation.msg.depth
|
debug "Computation Failure", reason = "Stack too deep", maximumDepth = MaxCallDepth, depth = computation.msg.depth
|
||||||
push: 0
|
return false
|
||||||
return
|
|
||||||
|
|
||||||
##### getBalance type error: expression 'db' is of type: proc (vmState: untyped, readOnly: untyped, handler: untyped): untyped{.noSideEffect, gcsafe, locks: <unknown>.}
|
result = true
|
||||||
# computation.vmState.db(readOnly=true):
|
|
||||||
# when ForkName >= FkHomestead: # TODO this is done in Geth but not Parity and Py-EVM
|
|
||||||
# let insufficientFunds = db.getBalance(computation.msg.storageAddress) < value # TODO check gas balance rollover
|
|
||||||
# let stackTooDeep = computation.msg.depth >= MaxCallDepth
|
|
||||||
|
|
||||||
# # TODO: error message
|
|
||||||
# if insufficientFunds or stackTooDeep:
|
|
||||||
# push: 0
|
|
||||||
# return
|
|
||||||
# else:
|
|
||||||
# let stackTooDeep = computation.msg.depth >= MaxCallDepth
|
|
||||||
# if stackTooDeep:
|
|
||||||
# push: 0
|
|
||||||
# return
|
|
||||||
|
|
||||||
|
proc setupCreate(computation: var BaseComputation, memPos, len: int, value: Uint256): (BaseComputation, EthAddress) =
|
||||||
let
|
let
|
||||||
callData = computation.memory.read(memPos, len)
|
callData = computation.memory.read(memPos, len)
|
||||||
createMsgGas = computation.getGasRemaining()
|
createMsgGas = computation.getGasRemaining()
|
||||||
|
@ -588,15 +569,28 @@ op create, inline = false, value, startPosition, size:
|
||||||
)
|
)
|
||||||
|
|
||||||
childMsg.sender = computation.msg.storageAddress
|
childMsg.sender = computation.msg.storageAddress
|
||||||
let childComputation = computation.applyChildComputation(childMsg, Create)
|
var childComp = generateChildComputation(computation.getFork, computation, childMsg)
|
||||||
|
result = (childComp, contractAddress)
|
||||||
|
|
||||||
if childComputation.isError:
|
op create, inline = false, value, startPosition, size:
|
||||||
|
## 0xf0, Create a new account with associated code.
|
||||||
|
# TODO: Forked create for Homestead
|
||||||
|
|
||||||
|
let (memPos, len) = (startPosition.cleanMemRef, size.cleanMemRef)
|
||||||
|
if not computation.transferBalance(memPos, len, value):
|
||||||
|
push: 0
|
||||||
|
return
|
||||||
|
|
||||||
|
var (childComp, contractAddress) = setupCreate(computation, memPos, len, value)
|
||||||
|
computation.applyChildComputation(childComp, Create)
|
||||||
|
|
||||||
|
if childComp.isError:
|
||||||
push: 0
|
push: 0
|
||||||
else:
|
else:
|
||||||
push: contractAddress
|
push: contractAddress
|
||||||
|
|
||||||
if not childComputation.shouldBurnGas:
|
if not childComp.shouldBurnGas:
|
||||||
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
|
computation.gasMeter.returnGas(childComp.gasMeter.gasRemaining)
|
||||||
|
|
||||||
proc callParams(computation: var BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
|
proc callParams(computation: var BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
|
||||||
let gas = computation.stack.popInt()
|
let gas = computation.stack.popInt()
|
||||||
|
@ -680,11 +674,7 @@ proc staticCallParams(computation: var BaseComputation): (UInt256, UInt256, EthA
|
||||||
emvcStatic) # is_static
|
emvcStatic) # is_static
|
||||||
|
|
||||||
template genCall(callName: untyped, opCode: Op): untyped =
|
template genCall(callName: untyped, opCode: Op): untyped =
|
||||||
op callName, inline = false:
|
proc `callName Setup`(computation: var BaseComputation, callNameStr: string): (BaseComputation, int, int) =
|
||||||
## 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.
|
|
||||||
let (gas, value, to, sender,
|
let (gas, value, to, sender,
|
||||||
codeAddress,
|
codeAddress,
|
||||||
memoryInputStartPosition, memoryInputSize,
|
memoryInputStartPosition, memoryInputSize,
|
||||||
|
@ -704,7 +694,7 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
||||||
(memOutPos, memOutLen)
|
(memOutPos, memOutLen)
|
||||||
|
|
||||||
if gas > high(GasInt).u256:
|
if gas > high(GasInt).u256:
|
||||||
raise newException(TypeError, "GasInt Overflow (" & callName.astToStr & ")")
|
raise newException(TypeError, "GasInt Overflow (" & callNameStr & ")")
|
||||||
|
|
||||||
let (childGasFee, childGasLimit) = computation.gasCosts[opCode].c_handler(
|
let (childGasFee, childGasLimit) = computation.gasCosts[opCode].c_handler(
|
||||||
value,
|
value,
|
||||||
|
@ -718,12 +708,12 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
||||||
c_opCode: opCode
|
c_opCode: opCode
|
||||||
))
|
))
|
||||||
|
|
||||||
trace "Call (" & callName.astToStr & ")", childGasLimit, childGasFee
|
#trace "Call (" & callNameStr & ")", childGasLimit, childGasFee
|
||||||
if childGasFee >= 0:
|
if childGasFee >= 0:
|
||||||
computation.gasMeter.consumeGas(childGasFee, reason = $opCode)
|
computation.gasMeter.consumeGas(childGasFee, reason = $opCode)
|
||||||
|
|
||||||
if childGasFee < 0 and childGasLimit <= 0:
|
if childGasFee < 0 and childGasLimit <= 0:
|
||||||
raise newException(OutOfGas, "Gas not enough to perform calculation (" & callName.astToStr & ")")
|
raise newException(OutOfGas, "Gas not enough to perform calculation (" & callNameStr & ")")
|
||||||
|
|
||||||
computation.memory.extend(memInPos, memInLen)
|
computation.memory.extend(memInPos, memInLen)
|
||||||
computation.memory.extend(memOutPos, memOutLen)
|
computation.memory.extend(memOutPos, memOutLen)
|
||||||
|
@ -754,20 +744,30 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
||||||
when opCode == CallCode:
|
when opCode == CallCode:
|
||||||
childMsg.storageAddress = computation.msg.storageAddress
|
childMsg.storageAddress = computation.msg.storageAddress
|
||||||
|
|
||||||
var childComputation = applyChildComputation(computation, childMsg, opCode)
|
var childComp = generateChildComputation(computation.getFork, computation, childMsg)
|
||||||
|
result = (childComp, memOutPos, memOutLen)
|
||||||
|
|
||||||
if childComputation.isError:
|
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)
|
||||||
|
|
||||||
|
applyChildComputation(computation, childComp, opCode)
|
||||||
|
|
||||||
|
if childComp.isError:
|
||||||
push: 0
|
push: 0
|
||||||
else:
|
else:
|
||||||
push: 1
|
push: 1
|
||||||
|
|
||||||
if not childComputation.shouldEraseReturnData:
|
if not childComp.shouldEraseReturnData:
|
||||||
let actualOutputSize = min(memOutLen, childComputation.output.len)
|
let actualOutputSize = min(memOutLen, childComp.output.len)
|
||||||
computation.memory.write(
|
computation.memory.write(
|
||||||
memOutPos,
|
memOutPos,
|
||||||
childComputation.output.toOpenArray(0, actualOutputSize - 1))
|
childComp.output.toOpenArray(0, actualOutputSize - 1))
|
||||||
if not childComputation.shouldBurnGas:
|
if not childComp.shouldBurnGas:
|
||||||
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
|
computation.gasMeter.returnGas(childComp.gasMeter.gasRemaining)
|
||||||
|
|
||||||
if computation.gasMeter.gasRemaining <= 0:
|
if computation.gasMeter.gasRemaining <= 0:
|
||||||
raise newException(OutOfGas, "computation out of gas after contract call (" & callName.astToStr & ")")
|
raise newException(OutOfGas, "computation out of gas after contract call (" & callName.astToStr & ")")
|
||||||
|
|
Loading…
Reference in New Issue