Skip to content

Commit e80788b

Browse files
authored
Fluffy: Move AsyncEvmStateBackend into separate file and update documentation in comments (#3271)
* Move AsyncEvmStateBackend into separate file. * Update documentation/comments. * Use block header instead of state root in async evm backend.
1 parent 565d868 commit e80788b

File tree

4 files changed

+65
-54
lines changed

4 files changed

+65
-54
lines changed

fluffy/evm/async_evm.nim

Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,30 @@ import
1414
chronicles,
1515
stint,
1616
results,
17-
eth/common/[base, hashes, addresses, accounts, headers, transactions],
17+
eth/common/[base, addresses, accounts, headers, transactions],
1818
../../execution_chain/db/[ledger, access_list],
1919
../../execution_chain/common/common,
2020
../../execution_chain/transaction/call_evm,
21-
../../execution_chain/evm/[types, state, evm_errors]
21+
../../execution_chain/evm/[types, state, evm_errors],
22+
./async_evm_backend
2223

23-
from web3/eth_api_types import TransactionArgs
24-
from web3/eth_api_types import Quantity
24+
from web3/eth_api_types import TransactionArgs, Quantity
2525

2626
export
27-
results, chronos, hashes, addresses, accounts, headers, TransactionArgs, CallResult,
27+
async_evm_backend, results, chronos, headers, TransactionArgs, CallResult,
2828
transactions.AccessList, GasInt
2929

3030
logScope:
3131
topics = "async_evm"
3232

33-
# The Portal EVM uses the Nimbus in-memory EVM to execute transactions using the
34-
# portal state network state data. Currently only call is supported.
33+
# The Async EVM uses the Nimbus in-memory EVM to execute transactions using state
34+
# data fetched asyncronously from a supplied state backend.
3535
#
36-
# Rather than wire in the portal state lookups into the EVM directly, the approach
36+
# Rather than wire in the async state lookups into the EVM directly, the approach
3737
# taken here is to optimistically execute the transaction multiple times with the
3838
# goal of building the correct access list so that we can then lookup the accessed
39-
# state from the portal network, store the state in the in-memory EVM and then
40-
# finally execute the transaction using the correct state. The Portal EVM makes
39+
# state from the async state backend, store the state in the in-memory EVM and then
40+
# finally execute the transaction using the correct state. The Async EVM makes
4141
# use of data in memory during the call and therefore each piece of state is never
4242
# fetched more than once. We know we have found the correct access list if it
4343
# doesn't change after another execution of the transaction.
@@ -48,9 +48,8 @@ logScope:
4848
# call given that we gain the ability to fetch the state concurrently.
4949
#
5050
# There are multiple reasons for choosing this approach:
51-
# - Firstly updating the existing Nimbus EVM to support using a different state
52-
# backend (portal state in this case) is difficult and would require making
53-
# non-trivial changes to the EVM.
51+
# - Firstly updating the existing Nimbus EVM to support using different state
52+
# backends is difficult and would require making non-trivial changes to the EVM.
5453
# - This new approach allows us to look up the state concurrently in the event that
5554
# multiple new state keys are discovered after executing the transaction. This
5655
# should in theory result in improved performance for certain scenarios. The
@@ -82,23 +81,6 @@ type
8281
address: Address
8382
codeFut: Future[Opt[seq[byte]]]
8483

85-
GetAccountProc* = proc(stateRoot: Hash32, address: Address): Future[Opt[Account]] {.
86-
async: (raises: [CancelledError])
87-
.}
88-
89-
GetStorageProc* = proc(
90-
stateRoot: Hash32, address: Address, slotKey: UInt256
91-
): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).}
92-
93-
GetCodeProc* = proc(stateRoot: Hash32, address: Address): Future[Opt[seq[byte]]] {.
94-
async: (raises: [CancelledError])
95-
.}
96-
97-
AsyncEvmStateBackend* = object
98-
getAccount: GetAccountProc
99-
getStorage: GetStorageProc
100-
getCode: GetCodeProc
101-
10284
AsyncEvm* = ref object
10385
com: CommonRef
10486
backend: AsyncEvmStateBackend
@@ -114,14 +96,6 @@ func init(
11496
func init(T: type CodeQuery, adr: Address, fut: Future[Opt[seq[byte]]]): T =
11597
T(address: adr, codeFut: fut)
11698

117-
proc init*(
118-
T: type AsyncEvmStateBackend,
119-
accProc: GetAccountProc,
120-
storageProc: GetStorageProc,
121-
codeProc: GetCodeProc,
122-
): T =
123-
AsyncEvmStateBackend(getAccount: accProc, getStorage: storageProc, getCode: codeProc)
124-
12599
proc init*(
126100
T: type AsyncEvm, backend: AsyncEvmStateBackend, networkId: NetworkId = MainNet
127101
): T =
@@ -165,7 +139,7 @@ proc callFetchingState(
165139
fetchedCode = initHashSet[Address]()
166140
167141
# Set code of the 'to' address in the EVM so that we can execute the transaction
168-
let code = (await evm.backend.getCode(header.stateRoot, to)).valueOr:
142+
let code = (await evm.backend.getCode(header, to)).valueOr:
169143
return err("Unable to get code")
170144
vmState.ledger.setCode(to, code)
171145
fetchedCode.incl(to)
@@ -215,8 +189,7 @@ proc callFetchingState(
215189
let slotIdx = (adr, v.storageSlot)
216190
if slotIdx notin fetchedStorage:
217191
debug "Fetching storage slot", address = adr, slotKey = v.storageSlot
218-
let storageFut =
219-
evm.backend.getStorage(header.stateRoot, adr, v.storageSlot)
192+
let storageFut = evm.backend.getStorage(header, adr, v.storageSlot)
220193
if not stateFetchDone:
221194
storageQueries.add(StorageQuery.init(adr, v.storageSlot, storageFut))
222195
if not optimisticStateFetch:
@@ -226,15 +199,15 @@ proc callFetchingState(
226199
227200
if adr notin fetchedAccounts:
228201
debug "Fetching account", address = adr
229-
let accFut = evm.backend.getAccount(header.stateRoot, adr)
202+
let accFut = evm.backend.getAccount(header, adr)
230203
if not stateFetchDone:
231204
accountQueries.add(AccountQuery.init(adr, accFut))
232205
if not optimisticStateFetch:
233206
stateFetchDone = true
234207
235208
if v.codeTouched and adr notin fetchedCode:
236209
debug "Fetching code", address = adr
237-
let codeFut = evm.backend.getCode(header.stateRoot, adr)
210+
let codeFut = evm.backend.getCode(header, adr)
238211
if not stateFetchDone:
239212
codeQueries.add(CodeQuery.init(adr, codeFut))
240213
if not optimisticStateFetch:

fluffy/evm/async_evm_backend.nim

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Fluffy
2+
# Copyright (c) 2025 Status Research & Development GmbH
3+
# Licensed and distributed under either of
4+
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
5+
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
6+
# at your option. This file may not be copied, modified, or distributed except according to those terms.
7+
8+
{.push raises: [].}
9+
10+
import chronos, stint, eth/common/[headers, addresses, accounts]
11+
12+
export chronos, stint, headers, addresses, accounts
13+
14+
type
15+
GetAccountProc* = proc(header: Header, address: Address): Future[Opt[Account]] {.
16+
async: (raises: [CancelledError])
17+
.}
18+
19+
GetStorageProc* = proc(
20+
header: Header, address: Address, slotKey: UInt256
21+
): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).}
22+
23+
GetCodeProc* = proc(header: Header, address: Address): Future[Opt[seq[byte]]] {.
24+
async: (raises: [CancelledError])
25+
.}
26+
27+
AsyncEvmStateBackend* = ref object
28+
getAccount*: GetAccountProc
29+
getStorage*: GetStorageProc
30+
getCode*: GetCodeProc
31+
32+
proc init*(
33+
T: type AsyncEvmStateBackend,
34+
accProc: GetAccountProc,
35+
storageProc: GetStorageProc,
36+
codeProc: GetCodeProc,
37+
): T =
38+
AsyncEvmStateBackend(getAccount: accProc, getStorage: storageProc, getCode: codeProc)

fluffy/evm/async_evm_portal_backend.nim

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@
77

88
{.push raises: [].}
99

10-
import ../network/state/state_endpoints, ./async_evm
10+
import ../network/state/state_endpoints, ./async_evm_backend
1111

1212
proc toAsyncEvmStateBackend*(stateNetwork: StateNetwork): AsyncEvmStateBackend =
1313
let
1414
accProc = proc(
15-
stateRoot: Hash32, address: Address
15+
header: Header, address: Address
1616
): Future[Opt[Account]] {.async: (raw: true, raises: [CancelledError]).} =
17-
stateNetwork.getAccount(stateRoot, address)
17+
stateNetwork.getAccount(header.stateRoot, address)
1818
storageProc = proc(
19-
stateRoot: Hash32, address: Address, slotKey: UInt256
19+
header: Header, address: Address, slotKey: UInt256
2020
): Future[Opt[UInt256]] {.async: (raw: true, raises: [CancelledError]).} =
21-
stateNetwork.getStorageAtByStateRoot(stateRoot, address, slotKey)
21+
stateNetwork.getStorageAtByStateRoot(header.stateRoot, address, slotKey)
2222
codeProc = proc(
23-
stateRoot: Hash32, address: Address
23+
header: Header, address: Address
2424
): Future[Opt[seq[byte]]] {.async: (raw: true, raises: [CancelledError]).} =
25-
stateNetwork.getCodeByStateRoot(stateRoot, address)
25+
stateNetwork.getCodeByStateRoot(header.stateRoot, address)
2626

2727
AsyncEvmStateBackend.init(accProc, storageProc, codeProc)

fluffy/tests/evm/async_evm_test_backend.nim

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
{.used.}
99

10-
import std/tables, ../../evm/async_evm
10+
import std/tables, ../../evm/async_evm_backend
1111

1212
type TestEvmState* = ref object
1313
accounts: Table[Address, Account]
@@ -43,15 +43,15 @@ proc toAsyncEvmStateBackend*(testState: TestEvmState): AsyncEvmStateBackend =
4343
# State root is ignored because TestEvmState only stores a single state
4444
let
4545
accProc = proc(
46-
stateRoot: Hash32, address: Address
46+
header: Header, address: Address
4747
): Future[Opt[Account]] {.async: (raises: [CancelledError]).} =
4848
Opt.some(testState.getAccount(address))
4949
storageProc = proc(
50-
stateRoot: Hash32, address: Address, slotKey: UInt256
50+
header: Header, address: Address, slotKey: UInt256
5151
): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} =
5252
Opt.some(testState.getStorage(address, slotKey))
5353
codeProc = proc(
54-
stateRoot: Hash32, address: Address
54+
header: Header, address: Address
5555
): Future[Opt[seq[byte]]] {.async: (raises: [CancelledError]).} =
5656
Opt.some(testState.getCode(address))
5757

0 commit comments

Comments
 (0)