Speed up account ledger a little (#2279)

`persist` is a hotspot when processing blocks because it is run at least
once per transaction and loops over the entire account cache every time.

Here, we introduce an extra `dirty` map that keeps track of all accounts
that need checking during `persist` which fixes the immediate
inefficiency, though probably this could benefit from a more thorough
review - we also get rid of the unused clearCache flag - we start with
a fresh cache on every fresh vmState.

* avoid unnecessary code hash comparisons
* avoid unnecessary copies when iterating
* use EMPTY_CODE_HASH throughout for code hash comparison
This commit is contained in:
Jacek Sieka 2024-06-02 21:21:29 +02:00 committed by GitHub
parent 8b658343f6
commit 7f76586214
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 106 additions and 103 deletions

View File

@ -74,7 +74,7 @@ proc processBlock(
vmState.mutateStateDB:
let clearEmptyAccount = vmState.determineFork >= FkSpurious
db.persist(clearEmptyAccount, ClearCache in vmState.flags)
db.persist(clearEmptyAccount)
# `applyDeletes = false`
# If the trie pruning activated, each of the block will have its own state

View File

@ -117,8 +117,8 @@ proc procBlkEpilogue(vmState: BaseVMState;
vmState.mutateStateDB:
if vmState.generateWitness:
db.collectWitnessData()
let clearEmptyAccount = vmState.determineFork >= FkSpurious
db.persist(clearEmptyAccount, ClearCache in vmState.flags)
db.persist(clearEmptyAccount = vmState.determineFork >= FkSpurious)
let stateDb = vmState.stateDB
if header.stateRoot != stateDb.rootHash:

View File

@ -118,9 +118,7 @@ proc processTransactionImpl(
if vmState.generateWitness:
vmState.stateDB.collectWitnessData()
vmState.stateDB.persist(
clearEmptyAccount = fork >= FkSpurious,
clearCache = false)
vmState.stateDB.persist(clearEmptyAccount = fork >= FkSpurious)
return res
@ -157,7 +155,7 @@ proc processBeaconBlockRoot*(vmState: BaseVMState, beaconRoot: Hash256):
if res.isError:
return err("processBeaconBlockRoot: " & res.error)
statedb.persist(clearEmptyAccount = true, clearCache = false)
statedb.persist(clearEmptyAccount = true)
ok()
proc processTransaction*(

View File

@ -71,9 +71,7 @@ proc persist(pst: TxPackerStateRef)
## Smart wrapper
if not pst.cleanState:
let fork = pst.xp.chain.nextFork
pst.xp.chain.vmState.stateDB.persist(
clearEmptyAccount = fork >= FkSpurious,
clearCache = false)
pst.xp.chain.vmState.stateDB.persist(clearEmptyAccount = fork >= FkSpurious)
pst.cleanState = true
# ------------------------------------------------------------------------------
@ -227,9 +225,7 @@ proc vmExecGrabItem(pst: TxPackerStateRef; item: TxItemRef): Result[bool,void]
# Commit account state DB
vmState.stateDB.commit(accTx)
vmState.stateDB.persist(
clearEmptyAccount = xp.chain.nextFork >= FkSpurious,
clearCache = false)
vmState.stateDB.persist(clearEmptyAccount = xp.chain.nextFork >= FkSpurious)
# let midRoot = vmState.stateDB.rootHash -- notused
# Finish book-keeping and move item to `packed` bucket
@ -254,9 +250,7 @@ proc vmExecCommit(pst: TxPackerStateRef)
if vmState.generateWitness:
db.collectWitnessData()
# Finish up, then vmState.stateDB.rootHash may be accessed
db.persist(
clearEmptyAccount = xp.chain.nextFork >= FkSpurious,
clearCache = ClearCache in vmState.flags)
db.persist(clearEmptyAccount = xp.chain.nextFork >= FkSpurious)
# Update flexi-array, set proper length
let nItems = xp.txDB.byStatus.eq(txItemPacked).nItems

View File

@ -354,7 +354,7 @@ proc validateTransaction*(
# `eth_call` and `eth_estimateGas`
# EOA = Externally Owned Account
let codeHash = roDB.getCodeHash(sender)
if codeHash != EMPTY_SHA3:
if codeHash != EMPTY_CODE_HASH:
return err("invalid tx: sender is not an EOA. sender=$1, codeHash=$2" % [
sender.toHex, codeHash.data.toHex])

View File

@ -80,6 +80,7 @@ type
LedgerSavePoint* = ref object
parentSavepoint: LedgerSavePoint
cache: Table[EthAddress, AccountRef]
dirty: Table[EthAddress, AccountRef]
selfDestruct: HashSet[EthAddress]
logEntries: seq[Log]
accessList: ac_access_list.AccessList
@ -129,6 +130,12 @@ func newCoreDbAccount(address: EthAddress): CoreDbAccount =
codeHash: emptyEthAccount.codeHash,
storage: CoreDbColRef(nil))
func resetCoreDbAccount(v: var CoreDbAccount) =
v.nonce = emptyEthAccount.nonce
v.balance = emptyEthAccount.balance
v.codeHash = emptyEthAccount.codeHash
v.storage = nil
template noRlpException(info: static[string]; code: untyped) =
try:
code
@ -196,6 +203,9 @@ proc commit*(ac: AccountsLedgerRef, sp: LedgerSavePoint) =
for k, v in sp.cache:
sp.parentSavepoint.cache[k] = v
for k, v in sp.dirty:
sp.parentSavepoint.dirty[k] = v
ac.savePoint.transientStorage.merge(sp.transientStorage)
ac.savePoint.accessList.merge(sp.accessList)
ac.savePoint.selfDestruct.incl sp.selfDestruct
@ -242,7 +252,7 @@ proc getAccount(
# cache the account
ac.savePoint.cache[address] = result
ac.savePoint.dirty[address] = result
proc clone(acc: AccountRef, cloneStorage: bool): AccountRef =
result = AccountRef(
@ -256,9 +266,9 @@ proc clone(acc: AccountRef, cloneStorage: bool): AccountRef =
result.overlayStorage = acc.overlayStorage
proc isEmpty(acc: AccountRef): bool =
result = acc.statement.codeHash == EMPTY_SHA3 and
acc.statement.nonce == 0 and
acc.statement.balance.isZero and
acc.statement.nonce == 0
acc.statement.codeHash == EMPTY_CODE_HASH
template exists(acc: AccountRef): bool =
Alive in acc.flags
@ -294,12 +304,12 @@ proc storageValue(
do:
result = acc.originalStorageValue(slot, ac)
proc kill(acc: AccountRef, address: EthAddress) =
proc kill(acc: AccountRef) =
acc.flags.excl Alive
acc.overlayStorage.clear()
acc.originalStorage = nil
acc.statement = address.newCoreDbAccount()
acc.code = default(seq[byte])
acc.statement.resetCoreDbAccount()
acc.code.reset()
type
PersistMode = enum
@ -324,13 +334,13 @@ proc persistCode(acc: AccountRef, ac: AccountsLedgerRef) =
warn logTxt "persistCode()",
codeHash=acc.statement.codeHash, error=($$rc.error)
proc persistStorage(acc: AccountRef, ac: AccountsLedgerRef, clearCache: bool) =
proc persistStorage(acc: AccountRef, ac: AccountsLedgerRef) =
if acc.overlayStorage.len == 0:
# TODO: remove the storage too if we figure out
# how to create 'virtual' storage room for each account
return
if not clearCache and acc.originalStorage.isNil:
if acc.originalStorage.isNil:
acc.originalStorage = newTable[UInt256, UInt256]()
# Make sure that there is an account column on the database. This is needed
@ -352,15 +362,13 @@ proc persistStorage(acc: AccountRef, ac: AccountsLedgerRef, clearCache: bool) =
if rc.isErr:
warn logTxt "persistStorage()", slot, error=($$rc.error)
if not clearCache:
# if we preserve cache, move the overlayStorage
# to originalStorage, related to EIP2200, EIP1283
for slot, value in acc.overlayStorage:
if value > 0:
acc.originalStorage[slot] = value
else:
acc.originalStorage.del(slot)
acc.overlayStorage.clear()
# move the overlayStorage to originalStorage, related to EIP2200, EIP1283
for slot, value in acc.overlayStorage:
if value > 0:
acc.originalStorage[slot] = value
else:
acc.originalStorage.del(slot)
acc.overlayStorage.clear()
# Changing the storage trie might also change the `storage` descriptor when
# the trie changes from empty to exixting or v.v.
@ -378,12 +386,14 @@ proc makeDirty(ac: AccountsLedgerRef, address: EthAddress, cloneStorage = true):
if address in ac.savePoint.cache:
# it's already in latest savepoint
result.flags.incl Dirty
ac.savePoint.dirty[address] = result
return
# put a copy into latest savepoint
result = result.clone(cloneStorage)
result.flags.incl Dirty
ac.savePoint.cache[address] = result
ac.savePoint.dirty[address] = result
proc getCodeHash*(ac: AccountsLedgerRef, address: EthAddress): Hash256 =
let acc = ac.getAccount(address, false)
@ -400,24 +410,33 @@ proc getNonce*(ac: AccountsLedgerRef, address: EthAddress): AccountNonce =
if acc.isNil: emptyEthAccount.nonce
else: acc.statement.nonce
proc getCode(acc: AccountRef, kvt: CoreDxKvtRef): lent seq[byte] =
if CodeLoaded notin acc.flags and CodeChanged notin acc.flags:
if acc.statement.codeHash != EMPTY_CODE_HASH:
var rc = kvt.get(contractHashKey(acc.statement.codeHash).toOpenArray)
if rc.isErr:
warn logTxt "getCode()", codeHash=acc.statement.codeHash, error=($$rc.error)
else:
acc.code = move(rc.value)
acc.flags.incl CodeLoaded
else:
acc.flags.incl CodeLoaded # avoid hash comparisons
acc.code
proc getCode*(ac: AccountsLedgerRef, address: EthAddress): seq[byte] =
let acc = ac.getAccount(address, false)
if acc.isNil:
return
if CodeLoaded in acc.flags or CodeChanged in acc.flags:
result = acc.code
elif acc.statement.codeHash != EMPTY_CODE_HASH:
let rc = ac.kvt.get(contractHashKey(acc.statement.codeHash).toOpenArray)
if rc.isErr:
warn logTxt "getCode()", codeHash=acc.statement.codeHash, error=($$rc.error)
else:
acc.code = rc.value
acc.flags.incl CodeLoaded
result = acc.code
acc.getCode(ac.kvt)
proc getCodeSize*(ac: AccountsLedgerRef, address: EthAddress): int =
ac.getCode(address).len
let acc = ac.getAccount(address, false)
if acc.isNil:
return
acc.getCode(ac.kvt).len
proc getCommittedStorage*(ac: AccountsLedgerRef, address: EthAddress, slot: UInt256): UInt256 =
let acc = ac.getAccount(address, false)
@ -436,7 +455,7 @@ proc contractCollision*(ac: AccountsLedgerRef, address: EthAddress): bool =
if acc.isNil:
return
acc.statement.nonce != 0 or
acc.statement.codeHash != EMPTY_SHA3 or
acc.statement.codeHash != EMPTY_CODE_HASH or
acc.statement.storage.stateOrVoid != EMPTY_ROOT_HASH
proc accountExists*(ac: AccountsLedgerRef, address: EthAddress): bool =
@ -534,7 +553,8 @@ proc deleteAccount*(ac: AccountsLedgerRef, address: EthAddress) =
# make sure all savepoints already committed
doAssert(ac.savePoint.parentSavepoint.isNil)
let acc = ac.getAccount(address)
acc.kill(address)
ac.savePoint.dirty[address] = acc
acc.kill()
proc selfDestruct*(ac: AccountsLedgerRef, address: EthAddress) =
ac.setBalance(address, 0.u256)
@ -572,13 +592,16 @@ proc deleteEmptyAccount(ac: AccountsLedgerRef, address: EthAddress) =
return
if not acc.exists:
return
acc.kill(address)
ac.savePoint.dirty[address] = acc
acc.kill()
proc clearEmptyAccounts(ac: AccountsLedgerRef) =
for address, acc in ac.savePoint.cache:
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md
for acc in ac.savePoint.dirty.values():
if Touched in acc.flags and
acc.isEmpty and acc.exists:
acc.kill(address)
acc.kill()
# https://github.com/ethereum/EIPs/issues/716
if ac.ripemdSpecial:
@ -586,11 +609,9 @@ proc clearEmptyAccounts(ac: AccountsLedgerRef) =
ac.ripemdSpecial = false
proc persist*(ac: AccountsLedgerRef,
clearEmptyAccount: bool = false,
clearCache: bool = true) =
clearEmptyAccount: bool = false) =
# make sure all savepoint already committed
doAssert(ac.savePoint.parentSavepoint.isNil)
var cleanAccounts = initHashSet[EthAddress]()
if clearEmptyAccount:
ac.clearEmptyAccounts()
@ -598,8 +619,7 @@ proc persist*(ac: AccountsLedgerRef,
for address in ac.savePoint.selfDestruct:
ac.deleteAccount(address)
for address, acc in ac.savePoint.cache:
assert address == acc.statement.address # debugging only
for acc in ac.savePoint.dirty.values(): # This is a hotspot in block processing
case acc.persistMode()
of Update:
if CodeChanged in acc.flags:
@ -607,25 +627,19 @@ proc persist*(ac: AccountsLedgerRef,
if StorageChanged in acc.flags:
# storageRoot must be updated first
# before persisting account into merkle trie
acc.persistStorage(ac, clearCache)
acc.persistStorage(ac)
ac.ledger.merge(acc.statement)
of Remove:
ac.ledger.delete address
if not clearCache:
cleanAccounts.incl address
ac.ledger.delete acc.statement.address
ac.savePoint.cache.del acc.statement.address
of DoNothing:
# dead man tell no tales
# remove touched dead account from cache
if not clearCache and Alive notin acc.flags:
cleanAccounts.incl address
if Alive notin acc.flags:
ac.savePoint.cache.del acc.statement.address
acc.flags = acc.flags - resetFlags
if clearCache:
ac.savePoint.cache.clear()
else:
for x in cleanAccounts:
ac.savePoint.cache.del x
ac.savePoint.dirty.clear()
ac.savePoint.selfDestruct.clear()

View File

@ -268,10 +268,10 @@ proc makeMultiKeys*(ldg: LedgerRef): MultiKeysRef =
result = ldg.ac.makeMultiKeys()
ldg.ifTrackApi: debug apiTxt, api, elapsed
proc persist*(ldg: LedgerRef, clearEmptyAccount = false, clearCache = true) =
proc persist*(ldg: LedgerRef, clearEmptyAccount = false) =
ldg.beginTrackApi LdgPersistFn
ldg.ac.persist(clearEmptyAccount, clearCache)
ldg.ifTrackApi: debug apiTxt, api, elapsed, clearEmptyAccount, clearCache
ldg.ac.persist(clearEmptyAccount)
ldg.ifTrackApi: debug apiTxt, api, elapsed, clearEmptyAccount
proc ripemdSpecial*(ldg: LedgerRef) =
ldg.beginTrackApi LdgRipemdSpecialFn

View File

@ -223,7 +223,7 @@ proc getCode*(db: AccountStateDB, address: EthAddress): seq[byte] =
proc contractCollision*(db: AccountStateDB, address: EthAddress): bool {.inline.} =
db.getNonce(address) != 0 or
db.getCodeHash(address) != EMPTY_SHA3 or
db.getCodeHash(address) != EMPTY_CODE_HASH or
db.getStorageRoot(address) != EMPTY_ROOT_HASH
proc dumpAccount*(db: AccountStateDB, addressS: string): string =
@ -238,19 +238,19 @@ proc isEmptyAccount*(db: AccountStateDB, address: EthAddress): bool =
assert(recordFound.len > 0)
let account = rlp.decode(recordFound, Account)
result = account.codeHash == EMPTY_SHA3 and
account.nonce == 0 and
account.balance.isZero and
account.nonce == 0
account.codeHash == EMPTY_CODE_HASH
proc isDeadAccount*(db: AccountStateDB, address: EthAddress): bool =
let recordFound = db.trie.getAccountBytes(address)
if recordFound.len > 0:
let account = rlp.decode(recordFound, Account)
result = account.codeHash == EMPTY_SHA3 and
account.nonce == 0 and
account.balance.isZero and
account.nonce == 0
account.codeHash == EMPTY_CODE_HASH
else:
result = true
true
proc removeEmptyRlpNode(branch: var seq[MptNodeRlpBytes]) =
if branch.len() == 1 and branch[0] == emptyRlp:

View File

@ -39,7 +39,6 @@ type
VMFlag* = enum
ExecutionOK
GenerateWitness
ClearCache
BlockContext* = object
timestamp* : EthTime

View File

@ -99,12 +99,12 @@ proc snapRead*(
if rlp.blobLen != 0 or not rlp.isBlob:
result.codeHash = rlp.read(typeof(result.codeHash))
when strict:
if result.codeHash == EMPTY_SHA3:
if result.codeHash == EMPTY_CODE_HASH:
raise newException(RlpTypeMismatch,
"EMPTY_SHA3 not encoded as empty string in Snap protocol")
else:
rlp.skipElem()
result.codeHash = EMPTY_SHA3
result.codeHash = EMPTY_CODE_HASH
proc snapAppend*(
writer: var RlpWriter;
@ -121,7 +121,7 @@ proc snapAppend*(
writer.append("")
else:
writer.append(account.storageRoot)
if account.codeHash == EMPTY_SHA3:
if account.codeHash == EMPTY_CODE_HASH:
writer.append("")
else:
writer.append(account.codeHash)

View File

@ -135,7 +135,7 @@ proc runTrial2ok(vmState: BaseVMState; inx: int) =
vmState.stateDB.modBalance(eAddr)
vmState.stateDB.commit(accTx)
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
proc runTrial3(vmState: BaseVMState; inx: int; rollback: bool) =
@ -146,7 +146,7 @@ proc runTrial3(vmState: BaseVMState; inx: int; rollback: bool) =
let accTx = vmState.stateDB.beginSavepoint
vmState.stateDB.modBalance(eAddr)
vmState.stateDB.commit(accTx)
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
block:
let accTx = vmState.stateDB.beginSavepoint
@ -157,13 +157,13 @@ proc runTrial3(vmState: BaseVMState; inx: int; rollback: bool) =
break
vmState.stateDB.commit(accTx)
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
block:
let accTx = vmState.stateDB.beginSavepoint
vmState.stateDB.modBalance(eAddr)
vmState.stateDB.commit(accTx)
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
proc runTrial3crash(vmState: BaseVMState; inx: int; noisy = false) =
@ -177,7 +177,7 @@ proc runTrial3crash(vmState: BaseVMState; inx: int; noisy = false) =
let accTx = vmState.stateDB.beginSavepoint
vmState.stateDB.modBalance(eAddr)
vmState.stateDB.commit(accTx)
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
block:
let accTx = vmState.stateDB.beginSavepoint
@ -216,7 +216,7 @@ proc runTrial3crash(vmState: BaseVMState; inx: int; noisy = false) =
vmState.stateDB.commit(accTx)
try:
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
except AssertionDefect as e:
if noisy:
let msg = e.msg.rsplit($DirSep,1)[^1]
@ -224,7 +224,7 @@ proc runTrial3crash(vmState: BaseVMState; inx: int; noisy = false) =
dbTx.dispose()
raise e
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
dbTx.commit()
@ -240,13 +240,13 @@ proc runTrial4(vmState: BaseVMState; inx: int; rollback: bool) =
let accTx = vmState.stateDB.beginSavepoint
vmState.stateDB.modBalance(eAddr)
vmState.stateDB.commit(accTx)
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
block:
let accTx = vmState.stateDB.beginSavepoint
vmState.stateDB.modBalance(eAddr)
vmState.stateDB.commit(accTx)
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
block:
let accTx = vmState.stateDB.beginSavepoint
@ -257,7 +257,7 @@ proc runTrial4(vmState: BaseVMState; inx: int; rollback: bool) =
break
vmState.stateDB.commit(accTx)
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
# There must be no dbTx.rollback() here unless `vmState.stateDB` is
# discarded and/or re-initialised.
@ -270,7 +270,7 @@ proc runTrial4(vmState: BaseVMState; inx: int; rollback: bool) =
let accTx = vmState.stateDB.beginSavepoint
vmState.stateDB.modBalance(eAddr)
vmState.stateDB.commit(accTx)
vmState.stateDB.persist(clearCache = false)
vmState.stateDB.persist()
dbTx.commit()

View File

@ -116,9 +116,9 @@ proc checkAndValidateWitnessAgainstProofs(
check stateDB.getBalance(address) == balance
check stateDB.getNonce(address) == nonce
if codeHash == ZERO_HASH256 or codeHash == EMPTY_SHA3:
if codeHash == ZERO_HASH256 or codeHash == EMPTY_CODE_HASH:
check stateDB.getCode(address).len() == 0
check stateDB.getCodeHash(address) == EMPTY_SHA3
check stateDB.getCodeHash(address) == EMPTY_CODE_HASH
else:
check stateDB.getCodeHash(address) == codeHash

View File

@ -103,11 +103,11 @@ proc updateStateUsingProofsAndCheckStateRoot(
stateDB.setCode(address, @[])
stateDB.clearStorage(address)
stateDB.deleteAccount(address)
elif (balance == 0 and nonce == 0 and codeHash == EMPTY_SHA3 and storageHash == EMPTY_ROOT_HASH):
elif (balance == 0 and nonce == 0 and codeHash == EMPTY_CODE_HASH and storageHash == EMPTY_ROOT_HASH):
# Account exists but is empty:
# The account was deleted due to a self destruct or the storage was cleared/set to zero
# and the bytecode is empty.
# The RPC API correctly returns codeHash == EMPTY_SHA3 and storageHash == EMPTY_ROOT_HASH
# The RPC API correctly returns codeHash == EMPTY_CODE_HASH and storageHash == EMPTY_ROOT_HASH
# in this scenario which is the same behavior implemented by geth.
stateDB.setCode(address, @[])
stateDB.clearStorage(address)
@ -123,9 +123,9 @@ proc updateStateUsingProofsAndCheckStateRoot(
check stateDB.getBalance(address) == balance
check stateDB.getNonce(address) == nonce
if codeHash == ZERO_HASH256 or codeHash == EMPTY_SHA3:
if codeHash == ZERO_HASH256 or codeHash == EMPTY_CODE_HASH:
check stateDB.getCode(address).len() == 0
check stateDB.getCodeHash(address) == EMPTY_SHA3
check stateDB.getCodeHash(address) == EMPTY_CODE_HASH
else:
check stateDB.getCodeHash(address) == codeHash

View File

@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2023 Status Research & Development GmbH
# Copyright (c) 2023-2024 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)
@ -38,6 +38,4 @@ proc coinbaseStateClearing*(vmState: BaseVMState,
# do not clear cache, we need the cache when constructing
# post state
db.persist(
clearEmptyAccount = fork >= FkSpurious,
clearCache = false)
db.persist(clearEmptyAccount = fork >= FkSpurious)

View File

@ -157,7 +157,7 @@ proc runExecution(ctx: var StateContext, conf: StateConf, pre: JsonNode): StateR
vmState.mutateStateDB:
setupStateDB(pre, db)
db.persist(clearEmptyAccount = false, clearCache = false) # settle accounts storage
db.persist(clearEmptyAccount = false) # settle accounts storage
defer:
ctx.verifyResult(vmState)

View File

@ -481,7 +481,7 @@ proc transitionAction*(ctx: var TransContext, conf: T8NConf) =
vmState.mutateStateDB:
db.setupAlloc(ctx.alloc)
db.persist(clearEmptyAccount = false, clearCache = false)
db.persist(clearEmptyAccount = false)
let res = exec(ctx, vmState, conf.stateReward, header, conf)