2021-05-17 11:39:52 +00:00
|
|
|
# Nimbus - Common entry point to the EVM from all different callers
|
|
|
|
#
|
2024-02-20 03:07:38 +00:00
|
|
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
2021-05-17 11:39:52 +00:00
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2023-02-14 20:27:17 +00:00
|
|
|
{.push raises: [].}
|
|
|
|
|
2021-05-17 11:39:52 +00:00
|
|
|
import
|
2024-06-14 07:31:08 +00:00
|
|
|
eth/common/eth_types, stint, stew/ptrops,
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
chronos,
|
2024-06-14 07:31:08 +00:00
|
|
|
results,
|
2024-07-07 06:52:11 +00:00
|
|
|
stew/saturation_arith,
|
|
|
|
../evm/[types, state],
|
2024-12-11 13:56:41 +00:00
|
|
|
../evm/[message, precompiles, internals, interpreter_dispatch],
|
2024-06-17 07:56:39 +00:00
|
|
|
../db/ledger,
|
2022-12-02 04:39:12 +00:00
|
|
|
../common/evmforks,
|
2023-06-24 13:56:44 +00:00
|
|
|
../core/eip4844,
|
2024-11-27 07:59:42 +00:00
|
|
|
../core/eip7702,
|
2024-10-26 07:19:48 +00:00
|
|
|
./host_types,
|
|
|
|
./call_types
|
2021-08-11 19:37:00 +00:00
|
|
|
|
2024-06-17 07:56:39 +00:00
|
|
|
import ../evm/computation except fromEvmc, toEvmc
|
|
|
|
|
2021-08-11 19:37:00 +00:00
|
|
|
when defined(evmc_enabled):
|
2024-07-07 06:52:11 +00:00
|
|
|
import
|
|
|
|
../utils/utils,
|
|
|
|
./host_services
|
2021-05-17 11:39:52 +00:00
|
|
|
|
2024-10-26 07:19:48 +00:00
|
|
|
export
|
|
|
|
call_types
|
2023-10-31 05:54:57 +00:00
|
|
|
|
2021-05-18 22:53:14 +00:00
|
|
|
proc hostToComputationMessage*(msg: EvmcMessage): Message =
|
2021-05-17 11:39:52 +00:00
|
|
|
Message(
|
2023-01-31 01:32:17 +00:00
|
|
|
kind: CallKind(msg.kind.ord),
|
2021-05-17 11:39:52 +00:00
|
|
|
depth: msg.depth,
|
2024-06-14 07:31:08 +00:00
|
|
|
gas: GasInt msg.gas,
|
2021-05-17 11:39:52 +00:00
|
|
|
sender: msg.sender.fromEvmc,
|
2022-09-26 12:23:54 +00:00
|
|
|
contractAddress: msg.recipient.fromEvmc,
|
|
|
|
codeAddress: msg.code_address.fromEvmc,
|
2021-05-17 11:39:52 +00:00
|
|
|
value: msg.value.fromEvmc,
|
|
|
|
# When input size is zero, input data pointer may be null.
|
|
|
|
data: if msg.input_size <= 0: @[]
|
|
|
|
else: @(makeOpenArray(msg.input_data, msg.input_size.int)),
|
2023-08-28 12:10:31 +00:00
|
|
|
flags: msg.flags
|
2021-05-17 11:39:52 +00:00
|
|
|
)
|
|
|
|
|
2021-05-27 07:45:55 +00:00
|
|
|
proc initialAccessListEIP2929(call: CallParams) =
|
|
|
|
# EIP2929 initial access list.
|
|
|
|
let vmState = call.vmState
|
|
|
|
if vmState.fork < FkBerlin:
|
|
|
|
return
|
|
|
|
|
|
|
|
vmState.mutateStateDB:
|
|
|
|
db.accessList(call.sender)
|
|
|
|
# For contract creations the EVM will add the contract address to the
|
|
|
|
# access list itself, after calculating the new contract address.
|
|
|
|
if not call.isCreate:
|
|
|
|
db.accessList(call.to)
|
2024-11-27 07:59:42 +00:00
|
|
|
# If the `call.to` has a delegation, also warm its target.
|
|
|
|
let target = parseDelegationAddress(db.getCode(call.to))
|
|
|
|
if target.isSome:
|
|
|
|
db.accessList(target[])
|
2023-01-04 13:11:33 +00:00
|
|
|
|
|
|
|
# EIP3651 adds coinbase to the list of addresses that should start warm.
|
|
|
|
if vmState.fork >= FkShanghai:
|
|
|
|
db.accessList(vmState.coinbase)
|
|
|
|
|
2023-06-24 13:56:44 +00:00
|
|
|
# Adds the correct subset of precompiles.
|
|
|
|
for c in activePrecompiles(vmState.fork):
|
2021-05-27 07:45:55 +00:00
|
|
|
db.accessList(c)
|
|
|
|
|
2021-05-26 15:05:18 +00:00
|
|
|
# EIP2930 optional access list.
|
|
|
|
for account in call.accessList:
|
|
|
|
db.accessList(account.address)
|
|
|
|
for key in account.storageKeys:
|
2024-09-29 12:37:09 +00:00
|
|
|
db.accessList(account.address, key.to(UInt256))
|
2021-05-26 15:05:18 +00:00
|
|
|
|
2024-11-27 07:59:42 +00:00
|
|
|
proc preExecComputation(vmState: BaseVMState, call: CallParams): int64 =
|
|
|
|
var gasRefund = 0
|
|
|
|
let ledger = vmState.stateDB
|
|
|
|
|
|
|
|
if not call.isCreate:
|
|
|
|
ledger.incNonce(call.sender)
|
|
|
|
|
|
|
|
# EIP-7702
|
|
|
|
for auth in call.authorizationList:
|
|
|
|
# 1. Verify the chain id is either 0 or the chain's current ID.
|
|
|
|
if not(auth.chainId == 0.ChainId or auth.chainId == vmState.com.chainId):
|
|
|
|
continue
|
|
|
|
|
|
|
|
# 2. authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]
|
|
|
|
let authority = authority(auth).valueOr:
|
|
|
|
continue
|
|
|
|
|
|
|
|
# 3. Add authority to accessed_addresses (as defined in EIP-2929.)
|
|
|
|
ledger.accessList(authority)
|
|
|
|
|
|
|
|
# 4. Verify the code of authority is either empty or already delegated.
|
|
|
|
let code = ledger.getCode(authority)
|
|
|
|
if code.len > 0:
|
|
|
|
if not parseDelegation(code):
|
|
|
|
continue
|
|
|
|
|
|
|
|
# 5. Verify the nonce of authority is equal to nonce.
|
|
|
|
if ledger.getNonce(authority) != auth.nonce:
|
|
|
|
continue
|
|
|
|
|
|
|
|
# 6. Add PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST gas to the global refund counter if authority exists in the trie.
|
|
|
|
if ledger.accountExists(authority):
|
|
|
|
gasRefund += PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST
|
|
|
|
|
|
|
|
# 7. Set the code of authority to be 0xef0100 || address. This is a delegation designation.
|
|
|
|
if auth.address == default(eth_types.Address):
|
|
|
|
ledger.setCode(authority, @[])
|
|
|
|
else:
|
|
|
|
ledger.setCode(authority, @(addressToDelegation(auth.address)))
|
|
|
|
|
|
|
|
# 8. Increase the nonce of authority by one.
|
|
|
|
ledger.setNonce(authority, auth.nonce + 1)
|
|
|
|
|
|
|
|
# Usually the transaction destination and delegation target are added to
|
|
|
|
# the access list in initialAccessListEIP2929, however if the delegation is in
|
|
|
|
# the same transaction we need add here as to reduce calling slow ecrecover.
|
|
|
|
if call.to == authority:
|
|
|
|
ledger.accessList(auth.address)
|
|
|
|
|
|
|
|
gasRefund
|
|
|
|
|
2024-12-11 13:56:41 +00:00
|
|
|
proc setupHost(call: CallParams, keepStack: bool): TransactionHost =
|
2021-05-17 11:39:52 +00:00
|
|
|
let vmState = call.vmState
|
2024-07-04 13:48:36 +00:00
|
|
|
vmState.txCtx = TxContext(
|
|
|
|
origin : call.origin.get(call.sender),
|
|
|
|
gasPrice : call.gasPrice,
|
|
|
|
versionedHashes: call.versionedHashes,
|
|
|
|
blobBaseFee : getBlobBaseFee(vmState.blockCtx.excessBlobGas),
|
2021-05-17 11:39:52 +00:00
|
|
|
)
|
|
|
|
|
2024-12-05 06:00:47 +00:00
|
|
|
# reset global gasRefund counter each time
|
|
|
|
# EVM called for a new transaction
|
|
|
|
vmState.gasRefunded = 0
|
2024-12-11 13:56:41 +00:00
|
|
|
|
|
|
|
let
|
|
|
|
intrinsicGas = if call.noIntrinsic: 0.GasInt
|
|
|
|
else: intrinsicGas(call, vmState.fork)
|
|
|
|
host = TransactionHost(
|
|
|
|
vmState: vmState,
|
|
|
|
sysCall: call.sysCall,
|
|
|
|
msg: EvmcMessage(
|
|
|
|
kind: if call.isCreate: EVMC_CREATE else: EVMC_CALL,
|
|
|
|
# Default: flags: {},
|
|
|
|
# Default: depth: 0,
|
|
|
|
gas: int64.saturate(call.gasLimit - intrinsicGas),
|
|
|
|
recipient: call.to.toEvmc,
|
|
|
|
code_address: call.to.toEvmc,
|
|
|
|
sender: call.sender.toEvmc,
|
|
|
|
value: call.value.toEvmc,
|
|
|
|
)
|
|
|
|
# All other defaults in `TransactionHost` are fine.
|
|
|
|
)
|
|
|
|
gasRefund = if call.sysCall: 0
|
|
|
|
else: preExecComputation(vmState, call)
|
|
|
|
code = if call.isCreate:
|
|
|
|
let contractAddress = generateContractAddress(call.vmState, EVMC_CREATE, call.sender)
|
|
|
|
host.msg.recipient = contractAddress.toEvmc
|
|
|
|
host.msg.input_size = 0
|
|
|
|
host.msg.input_data = nil
|
|
|
|
CodeBytesRef.init(call.input)
|
|
|
|
else:
|
|
|
|
if call.input.len > 0:
|
|
|
|
host.msg.input_size = call.input.len.csize_t
|
|
|
|
# Must copy the data so the `host.msg.input_data` pointer
|
|
|
|
# remains valid after the end of `call` lifetime.
|
|
|
|
host.input = call.input
|
|
|
|
host.msg.input_data = host.input[0].addr
|
|
|
|
getCallCode(host.vmState, host.msg.code_address.fromEvmc)
|
|
|
|
cMsg = hostToComputationMessage(host.msg)
|
|
|
|
|
|
|
|
host.computation = newComputation(vmState, keepStack, cMsg, code)
|
|
|
|
host.code = code
|
2021-05-17 11:39:52 +00:00
|
|
|
|
2024-12-05 06:00:47 +00:00
|
|
|
host.computation.addRefund(gasRefund)
|
2023-08-02 10:17:40 +00:00
|
|
|
vmState.captureStart(host.computation, call.sender, call.to,
|
|
|
|
call.isCreate, call.input,
|
|
|
|
call.gasLimit, call.value)
|
2023-08-25 09:07:20 +00:00
|
|
|
|
2021-05-17 11:39:52 +00:00
|
|
|
return host
|
|
|
|
|
2021-12-10 13:57:03 +00:00
|
|
|
when defined(evmc_enabled):
|
2024-06-07 08:24:32 +00:00
|
|
|
proc doExecEvmc(host: TransactionHost, call: CallParams) =
|
2021-12-10 13:57:03 +00:00
|
|
|
var callResult = evmcExecComputation(host)
|
|
|
|
let c = host.computation
|
|
|
|
|
|
|
|
if callResult.status_code == EVMC_SUCCESS:
|
|
|
|
c.error = nil
|
|
|
|
elif callResult.status_code == EVMC_REVERT:
|
2023-08-28 12:10:31 +00:00
|
|
|
c.setError(EVMC_REVERT, false)
|
2021-12-10 13:57:03 +00:00
|
|
|
else:
|
2023-08-28 12:10:31 +00:00
|
|
|
c.setError(callResult.status_code, true)
|
2021-12-10 13:57:03 +00:00
|
|
|
|
2024-06-14 07:31:08 +00:00
|
|
|
c.gasMeter.gasRemaining = GasInt callResult.gas_left
|
2021-12-10 13:57:03 +00:00
|
|
|
c.msg.contractAddress = callResult.create_address.fromEvmc
|
|
|
|
c.output = if callResult.output_size <= 0: @[]
|
|
|
|
else: @(makeOpenArray(callResult.output_data,
|
|
|
|
callResult.output_size.int))
|
|
|
|
if not callResult.release.isNil:
|
|
|
|
{.gcsafe.}:
|
2024-02-24 02:38:50 +00:00
|
|
|
callResult.release(callResult)
|
2021-12-10 13:57:03 +00:00
|
|
|
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
# FIXME-awkwardFactoring: the factoring out of the pre and
|
|
|
|
# post parts feels awkward to me, but for now I'd really like
|
|
|
|
# not to have too much duplicated code between sync and async.
|
|
|
|
# --Adam
|
2021-05-17 12:54:17 +00:00
|
|
|
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
proc prepareToRunComputation(host: TransactionHost, call: CallParams) =
|
2021-05-17 17:01:32 +00:00
|
|
|
# Must come after `setupHost` for correct fork.
|
2021-05-17 14:16:44 +00:00
|
|
|
if not call.noAccessList:
|
|
|
|
initialAccessListEIP2929(call)
|
2021-05-27 07:45:55 +00:00
|
|
|
|
2021-05-17 12:54:17 +00:00
|
|
|
# Charge for gas.
|
2021-05-17 14:16:44 +00:00
|
|
|
if not call.noGasCharge:
|
2023-07-20 23:34:56 +00:00
|
|
|
let
|
|
|
|
vmState = host.vmState
|
|
|
|
fork = vmState.fork
|
|
|
|
|
|
|
|
vmState.mutateStateDB:
|
2021-05-17 14:16:44 +00:00
|
|
|
db.subBalance(call.sender, call.gasLimit.u256 * call.gasPrice.u256)
|
2021-05-17 12:54:17 +00:00
|
|
|
|
2023-07-20 23:34:56 +00:00
|
|
|
# EIP-4844
|
|
|
|
if fork >= FkCancun:
|
|
|
|
let blobFee = calcDataFee(call.versionedHashes.len,
|
2023-09-24 15:25:41 +00:00
|
|
|
vmState.blockCtx.excessBlobGas)
|
|
|
|
db.subBalance(call.sender, blobFee)
|
2023-07-20 23:34:56 +00:00
|
|
|
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
proc calculateAndPossiblyRefundGas(host: TransactionHost, call: CallParams): GasInt =
|
|
|
|
let c = host.computation
|
2022-12-02 04:39:12 +00:00
|
|
|
|
2021-06-28 13:12:14 +00:00
|
|
|
# EIP-3529: Reduction in refunds
|
|
|
|
let MaxRefundQuotient = if host.vmState.fork >= FkLondon:
|
|
|
|
5.GasInt
|
|
|
|
else:
|
|
|
|
2.GasInt
|
|
|
|
|
2021-05-17 12:54:17 +00:00
|
|
|
# Calculated gas used, taking into account refund rules.
|
2021-05-17 14:16:44 +00:00
|
|
|
if call.noRefund:
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
result = c.gasMeter.gasRemaining
|
2024-12-05 06:00:47 +00:00
|
|
|
else:
|
|
|
|
if c.shouldBurnGas:
|
|
|
|
c.gasMeter.gasRemaining = 0
|
2021-06-28 13:12:14 +00:00
|
|
|
let maxRefund = (call.gasLimit - c.gasMeter.gasRemaining) div MaxRefundQuotient
|
2024-07-07 06:52:11 +00:00
|
|
|
let refund = min(c.getGasRefund(), maxRefund)
|
2021-05-17 12:54:17 +00:00
|
|
|
c.gasMeter.returnGas(refund)
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
result = c.gasMeter.gasRemaining
|
2021-05-17 12:54:17 +00:00
|
|
|
|
|
|
|
# Refund for unused gas.
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
if result > 0 and not call.noGasCharge:
|
2021-05-17 12:54:17 +00:00
|
|
|
host.vmState.mutateStateDB:
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
db.addBalance(call.sender, result.u256 * call.gasPrice.u256)
|
|
|
|
|
2024-07-13 18:42:49 +00:00
|
|
|
proc finishRunningComputation(
|
|
|
|
host: TransactionHost, call: CallParams, T: type): T =
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
let c = host.computation
|
2022-12-02 04:39:12 +00:00
|
|
|
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
let gasRemaining = calculateAndPossiblyRefundGas(host, call)
|
2022-12-14 09:42:55 +00:00
|
|
|
# evm gas used without intrinsic gas
|
2024-06-14 07:31:08 +00:00
|
|
|
let gasUsed = host.msg.gas.GasInt - gasRemaining
|
2023-08-28 12:10:31 +00:00
|
|
|
host.vmState.captureEnd(c, c.output, gasUsed, c.errorOpt)
|
2021-05-17 12:54:17 +00:00
|
|
|
|
2024-11-30 09:07:10 +00:00
|
|
|
when T is CallResult|DebugCallResult:
|
2024-07-13 18:42:49 +00:00
|
|
|
# Collecting the result can be unnecessarily expensive when (re)-processing
|
|
|
|
# transactions
|
|
|
|
if c.isError:
|
|
|
|
result.error = c.error.info
|
|
|
|
result.gasUsed = call.gasLimit - gasRemaining
|
|
|
|
result.output = system.move(c.output)
|
|
|
|
result.contractAddress = if call.isCreate: c.msg.contractAddress
|
|
|
|
else: default(HostAddress)
|
2024-11-30 09:07:10 +00:00
|
|
|
|
|
|
|
when T is DebugCallResult:
|
|
|
|
result.stack = move(c.finalStack)
|
|
|
|
result.memory = move(c.memory)
|
2024-07-13 18:42:49 +00:00
|
|
|
elif T is GasInt:
|
|
|
|
result = call.gasLimit - gasRemaining
|
|
|
|
elif T is string:
|
|
|
|
if c.isError:
|
|
|
|
result = c.error.info
|
2024-10-16 01:34:12 +00:00
|
|
|
elif T is seq[byte]:
|
2024-09-12 06:56:13 +00:00
|
|
|
result = move(c.output)
|
2024-07-13 18:42:49 +00:00
|
|
|
else:
|
|
|
|
{.error: "Unknown computation output".}
|
|
|
|
|
|
|
|
proc runComputation*(call: CallParams, T: type): T =
|
2024-11-30 09:07:10 +00:00
|
|
|
let host = setupHost(call, keepStack = T is DebugCallResult)
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
prepareToRunComputation(host, call)
|
|
|
|
|
|
|
|
when defined(evmc_enabled):
|
|
|
|
doExecEvmc(host, call)
|
|
|
|
else:
|
2024-12-11 13:56:41 +00:00
|
|
|
host.computation.execCallOrCreate()
|
|
|
|
if not call.sysCall:
|
|
|
|
host.computation.postExecComputation()
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
|
2024-07-13 18:42:49 +00:00
|
|
|
finishRunningComputation(host, call, T)
|