Transaction: Unified runner `runComputation` for all EVM call types
New entry point `runComputation`, for all EVM calls.
(Later the intent is `runComputationAsync`.)
As noted in commit 297d789
, there are six entry points calling EVM computation,
with different parameters and expecting different behaviours. Parameters were
dealt with in `setupComputation`. Behaviours are unified in `runComputation`,
with options passed via `CallParams`.
This code performs the steps used when validating a transaction. Options for
non-standard behaviour for RPC, GraphQL and tests to be added as required.
This replaces `setupComputation`, `execComputation` and `executeOpcodes`
(other than its own calls). As a result `Computation` and other EVM types are
no longer referenced in the main program, and many imports can be dropped.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
This commit is contained in:
parent
94f95efd9e
commit
c6d50a0ef7
|
@ -27,6 +27,18 @@ type
|
||||||
value*: HostValue # Value sent from sender to recipient.
|
value*: HostValue # Value sent from sender to recipient.
|
||||||
input*: seq[byte] # Input data.
|
input*: seq[byte] # Input data.
|
||||||
|
|
||||||
|
# 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).
|
||||||
|
CallResult* = object
|
||||||
|
isError*: bool # True if the call failed.
|
||||||
|
gasUsed*: GasInt # Gas used by the call.
|
||||||
|
contractAddress*: EthAddress # Created account (when `isCreate`).
|
||||||
|
output*: seq[byte] # Output data.
|
||||||
|
logEntries*: seq[Log] # Output logs.
|
||||||
|
stack*: Stack # EVM stack on return (for test only).
|
||||||
|
memory*: Memory # EVM memory on return (for test only).
|
||||||
|
error*: Error # Error if `isError` (for test only).
|
||||||
|
|
||||||
proc hostToComputationMessage(msg: EvmcMessage): Message =
|
proc hostToComputationMessage(msg: EvmcMessage): Message =
|
||||||
Message(
|
Message(
|
||||||
kind: CallKind(msg.kind),
|
kind: CallKind(msg.kind),
|
||||||
|
@ -42,7 +54,7 @@ proc hostToComputationMessage(msg: EvmcMessage): Message =
|
||||||
flags: if msg.isStatic: emvcStatic else: emvcNoFlags
|
flags: if msg.isStatic: emvcStatic else: emvcNoFlags
|
||||||
)
|
)
|
||||||
|
|
||||||
proc setupCall(call: CallParams): TransactionHost =
|
proc setupCall(call: CallParams, useIntrinsic: bool): TransactionHost =
|
||||||
let vmState = call.vmState
|
let vmState = call.vmState
|
||||||
vmState.setupTxContext(
|
vmState.setupTxContext(
|
||||||
origin = call.origin.get(call.sender),
|
origin = call.origin.get(call.sender),
|
||||||
|
@ -50,13 +62,19 @@ proc setupCall(call: CallParams): TransactionHost =
|
||||||
forkOverride = call.forkOverride
|
forkOverride = call.forkOverride
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var intrinsicGas: GasInt = 0
|
||||||
|
if useIntrinsic:
|
||||||
|
intrinsicGas = intrinsicGas(call.input, vmState.fork)
|
||||||
|
if call.isCreate:
|
||||||
|
intrinsicGas += gasFees[vmState.fork][GasTXCreate]
|
||||||
|
|
||||||
let host = TransactionHost(
|
let host = TransactionHost(
|
||||||
vmState: vmState,
|
vmState: vmState,
|
||||||
msg: EvmcMessage(
|
msg: EvmcMessage(
|
||||||
kind: if call.isCreate: EVMC_CREATE else: EVMC_CALL,
|
kind: if call.isCreate: EVMC_CREATE else: EVMC_CALL,
|
||||||
# Default: flags: {},
|
# Default: flags: {},
|
||||||
# Default: depth: 0,
|
# Default: depth: 0,
|
||||||
gas: call.gasLimit,
|
gas: call.gasLimit - intrinsicGas,
|
||||||
destination: call.to.toEvmc,
|
destination: call.to.toEvmc,
|
||||||
sender: call.sender.toEvmc,
|
sender: call.sender.toEvmc,
|
||||||
value: call.value.toEvmc,
|
value: call.value.toEvmc,
|
||||||
|
@ -76,4 +94,37 @@ proc setupCall(call: CallParams): TransactionHost =
|
||||||
return host
|
return host
|
||||||
|
|
||||||
proc setupComputation*(call: CallParams): Computation =
|
proc setupComputation*(call: CallParams): Computation =
|
||||||
return setupCall(call).computation
|
return setupCall(call, false).computation
|
||||||
|
|
||||||
|
proc runComputation*(call: CallParams): CallResult =
|
||||||
|
let host = setupCall(call, true)
|
||||||
|
let c = host.computation
|
||||||
|
|
||||||
|
# Charge for gas.
|
||||||
|
host.vmState.mutateStateDB:
|
||||||
|
db.subBalance(call.sender, call.gasLimit.u256 * call.gasPrice.u256)
|
||||||
|
|
||||||
|
execComputation(c)
|
||||||
|
|
||||||
|
# Calculated gas used, taking into account refund rules.
|
||||||
|
var gasRemaining: GasInt = 0
|
||||||
|
if not c.shouldBurnGas:
|
||||||
|
let maxRefund = (call.gasLimit - c.gasMeter.gasRemaining) div 2
|
||||||
|
let refund = min(c.getGasRefund(), maxRefund)
|
||||||
|
c.gasMeter.returnGas(refund)
|
||||||
|
gasRemaining = c.gasMeter.gasRemaining
|
||||||
|
|
||||||
|
# Refund for unused gas.
|
||||||
|
if gasRemaining > 0:
|
||||||
|
host.vmState.mutateStateDB:
|
||||||
|
db.addBalance(call.sender, gasRemaining.u256 * call.gasPrice.u256)
|
||||||
|
|
||||||
|
result.isError = c.isError
|
||||||
|
result.gasUsed = call.gasLimit - gasRemaining
|
||||||
|
shallowCopy(result.output, c.output)
|
||||||
|
result.contractAddress = if call.isCreate: c.msg.contractAddress
|
||||||
|
else: default(HostAddress)
|
||||||
|
shallowCopy(result.logEntries, c.logEntries)
|
||||||
|
result.stack = c.stack
|
||||||
|
result.memory = c.memory
|
||||||
|
result.error = c.error
|
||||||
|
|
Loading…
Reference in New Issue