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:
parent
8b658343f6
commit
7f76586214
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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*(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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])
|
||||||
|
|
||||||
|
|
|
@ -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,9 +362,7 @@ 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
|
|
||||||
# to originalStorage, related to EIP2200, EIP1283
|
|
||||||
for slot, value in acc.overlayStorage:
|
for slot, value in acc.overlayStorage:
|
||||||
if value > 0:
|
if value > 0:
|
||||||
acc.originalStorage[slot] = value
|
acc.originalStorage[slot] = value
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -39,7 +39,6 @@ type
|
||||||
VMFlag* = enum
|
VMFlag* = enum
|
||||||
ExecutionOK
|
ExecutionOK
|
||||||
GenerateWitness
|
GenerateWitness
|
||||||
ClearCache
|
|
||||||
|
|
||||||
BlockContext* = object
|
BlockContext* = object
|
||||||
timestamp* : EthTime
|
timestamp* : EthTime
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue