nimbus-eth1/nimbus/evm/async_operations.nim

105 lines
4.0 KiB
Nim

# Nimbus
# Copyright (c) 2022-2023 Status Research & Development GmbH
# 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.
import
chronos,
stint,
json_rpc/rpcclient,
web3,
./computation,
./state,
./types,
../db/accounts_cache
# Used in synchronous mode.
proc noLazyDataSource*(): LazyDataSource =
LazyDataSource(
ifNecessaryGetStorage: (proc(c: Computation, slot: UInt256): Future[void] {.async.} =
discard
)
)
# Will be used in asynchronous on-demand-data-fetching mode, once
# that is implemented.
proc realLazyDataSource*(client: RpcClient): LazyDataSource =
LazyDataSource(
ifNecessaryGetStorage: (proc(c: Computation, slot: UInt256): Future[void] {.async.} =
# TODO: find some way to check whether we already have it.
# This is WRONG, but good enough for now, considering this
# code is unused except in a few tests. I'm working on
# doing this properly.
if not c.getStorage(slot).isZero: return
# FIXME-onDemandStorageNotImplementedYet
# (I sketched in this code, but haven't actually tried running it yet.)
echo("Attempting to for-real fetch slot " & $(slot))
# ethAddressStr("0xfff33a3bd36abdbd412707b8e310d6011454a7ae")
# check hexDataStr(0.u256).string == res.string
let ethAddress = c.msg.contractAddress
let address: Address = Address(ethAddress)
let quantity: int = slot.truncate(int) # this is probably wrong; what's the right way to convert this?
let blockId: BlockIdentifier = blockId(c.vmState.parent.blockNumber.truncate(uint64)) # ditto
let res = await client.eth_getStorageAt(address, quantity, blockId)
echo("Fetched slot " & $(slot) & ", result is " & $(res))
# let v = res # will res be the actual value, or do I need to convert or something?
# Before implementing this, see the note from Zahary here:
# https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r999669139
#
# c.vmState.mutateStateDB:
# db.setStorage(c.msg.contractAddress, slot, UInt256.fromBytesBE(v))
)
)
# Used for unit testing. Contains some prepopulated data.
proc fakeLazyDataSource*(fakePairs: seq[tuple[key, val: array[32, byte]]]): LazyDataSource =
LazyDataSource(
ifNecessaryGetStorage: (proc(c: Computation, slot: UInt256): Future[void] {.async.} =
# See the comment above.
if not c.getStorage(slot).isZero: return
# FIXME-writeAutomatedTestsToShowThatItCanRunConcurrently
# For now, until I've implemented some more automated way to
# capture and verify the fact that this can run concurrently,
# this is useful just to see in the console that the echo
# statements from multiple Computations can run at the same
# time and be interleaved.
# echo("Attempting to fake-fetch slot " & $(slot))
# await sleepAsync(2.seconds)
let slotBytes = toBytesBE(slot)
# The linear search is obviously slow, but doesn't matter
# for tests with only a few initialStorage entries. Fix
# this if we ever want to write tests with more.
for (k, v) in fakePairs:
if slotBytes == k:
c.vmState.mutateStateDB:
db.setStorage(c.msg.contractAddress, slot, UInt256.fromBytesBE(v))
break
# echo("Finished fake-fetch of slot " & $(slot))
)
)
# Gotta find the place where we're creating a Computation without setting
# its asyncFactory in the first place, but this is fine for now.
proc asyncFactory*(c: Computation): AsyncOperationFactory =
# Does Nim have an "ifNil" macro/template?
if isNil(c.vmState.asyncFactory):
AsyncOperationFactory(lazyDataSource: noLazyDataSource()) # AARDVARK - can I make a singleton?
else:
c.vmState.asyncFactory