avoid computing state root just to know if storage is empty (#2380)

The state root computation here is one of the major hotspots in block
processing - in the cases the code only needs to know if it's empty or
not, it can be done a lot faster.

Adding a separate function for this looks fragile and should probably be
revisited.
This commit is contained in:
Jacek Sieka 2024-06-17 15:29:07 +02:00 committed by GitHub
parent 9cf7e6aea3
commit 9c6fd46a51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 43 additions and 5 deletions

View File

@ -150,6 +150,9 @@ proc baseMethods(db: AristoCoreDbRef): CoreDbBaseFns =
levelFn: proc(): int =
aBase.getLevel,
colStateEmptyFn: proc(col: CoreDbColRef): CoreDbRc[bool] =
aBase.rootHashEmpty(col, "rootHashFn()"),
colStateFn: proc(col: CoreDbColRef): CoreDbRc[Hash256] =
aBase.rootHash(col, "rootHashFn()"),

View File

@ -645,6 +645,20 @@ proc colPrint*(
result &= "$?"
proc rootHashEmpty*(
base: AristoBaseRef;
col: CoreDbColRef;
info: static[string];
): CoreDbRc[bool] =
let col = AristoColRef(col)
if not col.isValid:
return err(TrieInvalid.toError(base, info, HashNotAvailable))
let root = col.to(VertexID)
if not root.isValid:
return ok(true)
return ok(false)
proc rootHash*(
base: AristoBaseRef;
col: CoreDbColRef;

View File

@ -445,6 +445,23 @@ proc `$$`*(col: CoreDbColRef): string =
result = col.prettyText()
#col.ifTrackNewApi: debug newApiTxt, api, elapsed, result
proc stateEmpty*(col: CoreDbColRef): CoreDbRc[bool] =
## Getter (well, sort of). It retrieves the column state hash for the
## argument `col` descriptor. The function might fail unless the current
## state is available (e.g. on `Aristo`.)
##
## The value `EMPTY_ROOT_HASH` is returned on the void `col` descriptor
## argument `CoreDbColRef(nil)`.
##
col.setTrackNewApi BaseColStateEmptyFn
result = block:
if not col.isNil and col.ready:
col.parent.methods.colStateEmptyFn col
else:
ok true
# Note: tracker will be silent if `vid` is NIL
col.ifTrackNewApi: debug newApiTxt, api, elapsed, col, result
proc state*(col: CoreDbColRef): CoreDbRc[Hash256] =
## Getter (well, sort of). It retrieves the column state hash for the
## argument `col` descriptor. The function might fail unless the current

View File

@ -41,6 +41,7 @@ type
AnyBackendFn = "any/backend"
BaseColPrintFn = "$$"
BaseColStateEmptyFn = "stateEmpty"
BaseColStateFn = "state"
BaseDbTypeFn = "dbType"
BaseFinishFn = "finish"

View File

@ -92,6 +92,8 @@ type
CoreDbBaseDestroyFn* = proc(eradicate = true) {.noRaise.}
CoreDbBaseColStateFn* = proc(
col: CoreDbColRef): CoreDbRc[Hash256] {.noRaise.}
CoreDbBaseColStateEmptyFn* = proc(
col: CoreDbColRef): CoreDbRc[bool] {.noRaise.}
CoreDbBaseColPrintFn* = proc(vid: CoreDbColRef): string {.noRaise.}
CoreDbBaseErrorPrintFn* = proc(e: CoreDbErrorRef): string {.noRaise.}
CoreDbBaseLevelFn* = proc(): int {.noRaise.}
@ -110,6 +112,7 @@ type
CoreDbBaseFns* = object
destroyFn*: CoreDbBaseDestroyFn
colStateFn*: CoreDbBaseColStateFn
colStateEmptyFn*: CoreDbBaseColStateEmptyFn
colPrintFn*: CoreDbBaseColPrintFn
errorPrintFn*: CoreDbBaseErrorPrintFn
levelFn*: CoreDbBaseLevelFn

View File

@ -376,9 +376,9 @@ proc persistStorage(acc: AccountRef, ac: AccountsLedgerRef) =
acc.statement.storage = storageLedger.getColumn()
# No need to hold descriptors for longer than needed
let state = acc.statement.storage.state.valueOr:
let stateEmpty = acc.statement.storage.stateEmpty.valueOr:
raiseAssert "Storage column state error: " & $$error
if state == EMPTY_ROOT_HASH:
if stateEmpty:
acc.statement.storage = CoreDbColRef(nil)
@ -540,8 +540,8 @@ proc clearStorage*(ac: AccountsLedgerRef, address: EthAddress) =
let acc = ac.getAccount(address)
acc.flags.incl {Alive, NewlyCreated}
let accHash = acc.statement.storage.state.valueOr: return
if accHash != EMPTY_ROOT_HASH:
let empty = acc.statement.storage.stateEmpty.valueOr: return
if not empty:
# need to clear the storage from the database first
let acc = ac.makeDirty(address, cloneStorage = false)
ac.ledger.freeStorage address
@ -613,7 +613,7 @@ proc clearEmptyAccounts(ac: AccountsLedgerRef) =
ac.ripemdSpecial = false
proc persist*(ac: AccountsLedgerRef,
clearEmptyAccount: bool = false) =
clearEmptyAccount: bool = false) {.deprecated.} =
# make sure all savepoint already committed
doAssert(ac.savePoint.parentSavepoint.isNil)