implement evmc call

This commit is contained in:
andri lim 2020-02-03 12:37:33 +07:00 committed by zah
parent 9477990897
commit dc3a897851
3 changed files with 166 additions and 29 deletions

View File

@ -306,6 +306,23 @@ proc addChildComputation*(c, child: Computation) =
if not child.shouldBurnGas: if not child.shouldBurnGas:
c.gasMeter.returnGas(child.gasMeter.gasRemaining) c.gasMeter.returnGas(child.gasMeter.gasRemaining)
proc execCall*(c: Computation) =
c.snapshot()
defer:
c.dispose()
if c.msg.kind == evmcCall:
c.vmState.mutateStateDb:
db.subBalance(c.msg.sender, c.msg.value)
db.addBalance(c.msg.contractAddress, c.msg.value)
executeOpcodes(c)
if c.isSuccess:
c.commit()
else:
c.rollback()
proc execCreate*(c: Computation) = proc execCreate*(c: Computation) =
c.vmState.mutateStateDB: c.vmState.mutateStateDB:
db.incNonce(c.msg.sender) db.incNonce(c.msg.sender)

View File

@ -126,7 +126,7 @@ proc hostEmitLogImpl(ctx: Computation, address: EthAddress,
template createImpl(c: Computation, m: nimbus_message, res: nimbus_result) = template createImpl(c: Computation, m: nimbus_message, res: nimbus_result) =
# TODO: use evmc_message to evoid copy # TODO: use evmc_message to evoid copy
var childMsg = Message( var childMsg = Message(
kind: CallKind(m.kind.ord), kind: CallKind(m.kind),
depth: m.depth, depth: m.depth,
gas: m.gas, gas: m.gas,
sender: m.sender, sender: m.sender,
@ -154,8 +154,44 @@ template createImpl(c: Computation, m: nimbus_message, res: nimbus_result) =
copyMem(res.output_data, child.output[0].addr, child.output.len) copyMem(res.output_data, child.output[0].addr, child.output.len)
res.release = hostReleaseResultImpl res.release = hostReleaseResultImpl
template callImpl(c: Computation, msg: nimbus_message, res: nimbus_result) = template callImpl(c: Computation, m: nimbus_message, res: nimbus_result) =
discard var childMsg = Message(
kind: CallKind(m.kind),
depth: m.depth,
gas: m.gas,
sender: m.sender,
codeAddress: m.destination,
contractAddress: if m.kind == EVMC_CALL: m.destination else: c.msg.contractAddress,
value: Uint256.fromEvmc(m.value),
flags: MsgFlags(m.flags)
)
if m.input_size.int > 0:
childMsg.data = newSeq[byte](m.input_size.int)
copyMem(childMsg.data[0].addr, m.input_data, m.input_size.int)
let child = newComputation(c.vmState, childMsg)
child.execCall()
if child.isError or c.fork == FKIstanbul:
if child.msg.contractAddress == ripemdAddr:
child.vmState.touchedAccounts.incl child.msg.contractAddress
if not child.shouldBurnGas:
res.gas_left = child.gasMeter.gasRemaining
if child.isSuccess:
c.touchedAccounts.incl child.msg.contractAddress
c.merge(child)
res.status_code = EVMC_SUCCESS
else:
res.status_code = if child.shouldBurnGas: EVMC_FAILURE else: EVMC_REVERT
if child.output.len > 0:
res.output_size = child.output.len.uint
res.output_data = cast[ptr byte](alloc(child.output.len))
copyMem(res.output_data, child.output[0].addr, child.output.len)
res.release = hostReleaseResultImpl
proc hostCallImpl(ctx: Computation, msg: var nimbus_message): nimbus_result {.cdecl.} = proc hostCallImpl(ctx: Computation, msg: var nimbus_message): nimbus_result {.cdecl.} =
if msg.kind == EVMC_CREATE or msg.kind == EVMC_CREATE2: if msg.kind == EVMC_CREATE or msg.kind == EVMC_CREATE2:

View File

@ -641,16 +641,15 @@ template genCreate(callName: untyped, opCode: Op): untyped =
genCreate(create, Create) genCreate(create, Create)
genCreate(create2, Create2) genCreate(create2, Create2)
proc callParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) = proc callParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) =
let gas = c.stack.popInt() let gas = c.stack.popInt()
let codeAddress = c.stack.popAddress() let destination = c.stack.popAddress()
let value = c.stack.popInt() let value = c.stack.popInt()
result = (gas, result = (gas,
value, value,
codeAddress, # contractAddress destination,
c.msg.contractAddress, # sender c.msg.contractAddress, # sender
codeAddress,
evmcCall, evmcCall,
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
@ -658,16 +657,15 @@ proc callParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, EthA
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
c.msg.flags) c.msg.flags)
proc callCodeParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) = proc callCodeParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) =
let gas = c.stack.popInt() let gas = c.stack.popInt()
let codeAddress = c.stack.popAddress() let destination = c.stack.popAddress()
let value = c.stack.popInt() let value = c.stack.popInt()
result = (gas, result = (gas,
value, value,
c.msg.contractAddress, # contractAddress destination,
c.msg.contractAddress, # sender c.msg.contractAddress, # sender
codeAddress,
evmcCallCode, evmcCallCode,
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
@ -675,15 +673,14 @@ proc callCodeParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress,
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
c.msg.flags) c.msg.flags)
proc delegateCallParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) = proc delegateCallParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) =
let gas = c.stack.popInt() let gas = c.stack.popInt()
let codeAddress = c.stack.popAddress() let destination = c.stack.popAddress()
result = (gas, result = (gas,
c.msg.value, # value c.msg.value, # value
c.msg.contractAddress, # contractAddress destination,
c.msg.sender, # sender c.msg.sender, # sender
codeAddress,
evmcDelegateCall, evmcDelegateCall,
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
@ -691,15 +688,14 @@ proc delegateCallParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddre
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
c.msg.flags) c.msg.flags)
proc staticCallParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) = proc staticCallParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) =
let gas = c.stack.popInt() let gas = c.stack.popInt()
let codeAddress = c.stack.popAddress() let destination = c.stack.popAddress()
result = (gas, result = (gas,
0.u256, # value 0.u256, # value
codeAddress, # contractAddress destination,
c.msg.contractAddress, # sender c.msg.contractAddress, # sender
codeAddress,
evmcCall, evmcCall,
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
c.stack.popInt().cleanMemRef, c.stack.popInt().cleanMemRef,
@ -709,10 +705,8 @@ proc staticCallParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress
template genCall(callName: untyped, opCode: Op): untyped = template genCall(callName: untyped, opCode: Op): untyped =
proc `callName Setup`(c: Computation, callNameStr: string): Computation = proc `callName Setup`(c: Computation, callNameStr: string): Computation =
let (gas, value, contractAddress, sender, let (gas, value, destination, sender, callKind,
codeAddress, callKind, memInPos, memInLen, memOutPos, memOutLen,
memInPos, memInLen,
memOutPos, memOutLen,
flags) = `callName Params`(c) flags) = `callName Params`(c)
let (memOffset, memLength) = if calcMemSize(memInPos, memInLen) > calcMemSize(memOutPos, memOutLen): let (memOffset, memLength) = if calcMemSize(memInPos, memInLen) > calcMemSize(memOutPos, memOutLen):
@ -720,6 +714,7 @@ template genCall(callName: untyped, opCode: Op): untyped =
else: else:
(memOutPos, memOutLen) (memOutPos, memOutLen)
let contractAddress = when opCode in {Call, StaticCall}: destination else: c.msg.contractAddress
let (childGasFee, childGasLimit) = c.gasCosts[opCode].c_handler( let (childGasFee, childGasLimit) = c.gasCosts[opCode].c_handler(
value, value,
GasParams(kind: opCode, GasParams(kind: opCode,
@ -762,7 +757,7 @@ template genCall(callName: untyped, opCode: Op): untyped =
gas: childGasLimit, gas: childGasLimit,
sender: sender, sender: sender,
contractAddress: contractAddress, contractAddress: contractAddress,
codeAddress: codeAddress, codeAddress: destination,
value: value, value: value,
data: c.memory.read(memInPos, memInLen), data: c.memory.read(memInPos, memInLen),
flags: flags) flags: flags)
@ -804,6 +799,95 @@ template genCall(callName: untyped, opCode: Op): untyped =
child.applyMessage(opCode) child.applyMessage(opCode)
template genCallEvmc(callName: untyped, opCode: Op): untyped =
op callName, inline = false:
when opCode == Call:
if emvcStatic == c.msg.flags and c.stack[^3, Uint256] > 0.u256:
raise newException(StaticContextError, "Cannot modify state while inside of a STATICCALL context")
let (gas, value, destination, sender, callKind,
memInPos, memInLen, memOutPos, memOutLen, flags) = `callName Params`(c)
push: 0
let (memOffset, memLength) = if calcMemSize(memInPos, memInLen) > calcMemSize(memOutPos, memOutLen):
(memInPos, memInLen)
else:
(memOutPos, memOutLen)
let contractAddress = when opCode in {Call, StaticCall}: destination else: c.msg.contractAddress
let (childGasFee, childGasLimit) = c.gasCosts[opCode].c_handler(
value,
GasParams(kind: opCode,
c_isNewAccount: not c.accountExists(contractAddress),
c_gasBalance: c.gasMeter.gasRemaining,
c_contractGas: gas,
c_currentMemSize: c.memory.len,
c_memOffset: memOffset,
c_memLength: memLength
))
if childGasFee >= 0:
c.gasMeter.consumeGas(childGasFee, reason = $opCode)
c.returnData.setLen(0)
if c.msg.depth >= MaxCallDepth:
debug "Computation Failure", reason = "Stack too deep", maximumDepth = MaxCallDepth, depth = c.msg.depth
# return unused gas
c.gasMeter.returnGas(childGasLimit)
return
if childGasFee < 0 and childGasLimit <= 0:
raise newException(OutOfGas, "Gas not enough to perform calculation (" & callName.astToStr & ")")
c.memory.extend(memInPos, memInLen)
c.memory.extend(memOutPos, memOutLen)
when opCode in {CallCode, Call}:
let senderBalance = c.getBalance(sender)
if senderBalance < value:
debug "Insufficient funds", available = senderBalance, needed = c.msg.value
# return unused gas
c.gasMeter.returnGas(childGasLimit)
return
let msg = nimbus_message(
kind: callKind.evmc_call_kind,
depth: (c.msg.depth + 1).int32,
gas: childGasLimit,
sender: sender,
destination: destination,
input_data: c.memory.readPtr(memInPos),
input_size: memInLen.uint,
value: toEvmc(value),
flags: flags.uint32
)
var res = c.host.call(msg)
if res.output_size.int > 0:
c.returnData = newSeq[byte](res.output_size.int)
copyMem(c.returnData[0].addr, res.output_data, c.returnData.len)
let actualOutputSize = min(memOutLen, c.returnData.len)
if actualOutputSize > 0:
c.memory.write(memOutPos,
c.returnData.toOpenArray(0, actualOutputSize - 1))
c.gasMeter.returnGas(res.gas_left)
if res.status_code == EVMC_SUCCESS:
c.stack.top(1)
if not res.release.isNil:
res.release(res)
when evmc_enabled:
genCallEvmc(call, Call)
genCallEvmc(callCode, CallCode)
genCallEvmc(delegateCall, DelegateCall)
genCallEvmc(staticCall, StaticCall)
else:
genCall(call, Call) genCall(call, Call)
genCall(callCode, CallCode) genCall(callCode, CallCode)
genCall(delegateCall, DelegateCall) genCall(delegateCall, DelegateCall)