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: vmState.mutateStateDB:
let clearEmptyAccount = vmState.determineFork >= FkSpurious let clearEmptyAccount = vmState.determineFork >= FkSpurious
db.persist(clearEmptyAccount, ClearCache in vmState.flags) db.persist(clearEmptyAccount)
# `applyDeletes = false` # `applyDeletes = false`
# If the trie pruning activated, each of the block will have its own state # 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: vmState.mutateStateDB:
if vmState.generateWitness: if vmState.generateWitness:
db.collectWitnessData() db.collectWitnessData()
let clearEmptyAccount = vmState.determineFork >= FkSpurious
db.persist(clearEmptyAccount, ClearCache in vmState.flags) db.persist(clearEmptyAccount = vmState.determineFork >= FkSpurious)
let stateDb = vmState.stateDB let stateDb = vmState.stateDB
if header.stateRoot != stateDb.rootHash: if header.stateRoot != stateDb.rootHash:

View File

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

View File

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

View File

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

View File

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

View File

@ -268,10 +268,10 @@ proc makeMultiKeys*(ldg: LedgerRef): MultiKeysRef =
result = ldg.ac.makeMultiKeys() result = ldg.ac.makeMultiKeys()
ldg.ifTrackApi: debug apiTxt, api, elapsed ldg.ifTrackApi: debug apiTxt, api, elapsed
proc persist*(ldg: LedgerRef, clearEmptyAccount = false, clearCache = true) = proc persist*(ldg: LedgerRef, clearEmptyAccount = false) =
ldg.beginTrackApi LdgPersistFn ldg.beginTrackApi LdgPersistFn
ldg.ac.persist(clearEmptyAccount, clearCache) ldg.ac.persist(clearEmptyAccount)
ldg.ifTrackApi: debug apiTxt, api, elapsed, clearEmptyAccount, clearCache ldg.ifTrackApi: debug apiTxt, api, elapsed, clearEmptyAccount
proc ripemdSpecial*(ldg: LedgerRef) = proc ripemdSpecial*(ldg: LedgerRef) =
ldg.beginTrackApi LdgRipemdSpecialFn 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.} = proc contractCollision*(db: AccountStateDB, address: EthAddress): bool {.inline.} =
db.getNonce(address) != 0 or db.getNonce(address) != 0 or
db.getCodeHash(address) != EMPTY_SHA3 or db.getCodeHash(address) != EMPTY_CODE_HASH or
db.getStorageRoot(address) != EMPTY_ROOT_HASH db.getStorageRoot(address) != EMPTY_ROOT_HASH
proc dumpAccount*(db: AccountStateDB, addressS: string): string = proc dumpAccount*(db: AccountStateDB, addressS: string): string =
@ -238,19 +238,19 @@ proc isEmptyAccount*(db: AccountStateDB, address: EthAddress): bool =
assert(recordFound.len > 0) assert(recordFound.len > 0)
let account = rlp.decode(recordFound, Account) let account = rlp.decode(recordFound, Account)
result = account.codeHash == EMPTY_SHA3 and account.nonce == 0 and
account.balance.isZero and account.balance.isZero and
account.nonce == 0 account.codeHash == EMPTY_CODE_HASH
proc isDeadAccount*(db: AccountStateDB, address: EthAddress): bool = proc isDeadAccount*(db: AccountStateDB, address: EthAddress): bool =
let recordFound = db.trie.getAccountBytes(address) let recordFound = db.trie.getAccountBytes(address)
if recordFound.len > 0: if recordFound.len > 0:
let account = rlp.decode(recordFound, Account) let account = rlp.decode(recordFound, Account)
result = account.codeHash == EMPTY_SHA3 and account.nonce == 0 and
account.balance.isZero and account.balance.isZero and
account.nonce == 0 account.codeHash == EMPTY_CODE_HASH
else: else:
result = true true
proc removeEmptyRlpNode(branch: var seq[MptNodeRlpBytes]) = proc removeEmptyRlpNode(branch: var seq[MptNodeRlpBytes]) =
if branch.len() == 1 and branch[0] == emptyRlp: if branch.len() == 1 and branch[0] == emptyRlp:

View File

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

View File

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

View File

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

View File

@ -116,9 +116,9 @@ proc checkAndValidateWitnessAgainstProofs(
check stateDB.getBalance(address) == balance check stateDB.getBalance(address) == balance
check stateDB.getNonce(address) == nonce 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.getCode(address).len() == 0
check stateDB.getCodeHash(address) == EMPTY_SHA3 check stateDB.getCodeHash(address) == EMPTY_CODE_HASH
else: else:
check stateDB.getCodeHash(address) == codeHash check stateDB.getCodeHash(address) == codeHash

View File

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

View File

@ -1,5 +1,5 @@
# Nimbus # Nimbus
# Copyright (c) 2023 Status Research & Development GmbH # Copyright (c) 2023-2024 Status Research & Development GmbH
# Licensed under either of # Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0) # 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 # do not clear cache, we need the cache when constructing
# post state # post state
db.persist( db.persist(clearEmptyAccount = fork >= FkSpurious)
clearEmptyAccount = fork >= FkSpurious,
clearCache = false)

View File

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

View File

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