Simplify state root api (#2864)

`updateOk` is obsolete and always set to true - callers should not have
to care about this detail

also take the opportunity to clean up storage root naming
This commit is contained in:
Jacek Sieka 2024-11-22 14:15:35 +01:00 committed by GitHub
parent ac2f3a4358
commit 652539e628
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 58 additions and 84 deletions

View File

@ -93,13 +93,11 @@ type
{.noRaise.}
## Fetch an account record from the database indexed by `accPath`.
AristoApiFetchAccountStateRootFn* =
AristoApiFetchStateRootFn* =
proc(db: AristoDbRef;
updateOk: bool;
): Result[Hash32,AristoError]
{.noRaise.}
## Fetch the Merkle hash of the account root. Force update if the
## argument `updateOK` is set `true`.
## Fetch the Merkle hash of the account root.
AristoApiFetchStorageDataFn* =
proc(db: AristoDbRef;
@ -113,11 +111,9 @@ type
AristoApiFetchStorageRootFn* =
proc(db: AristoDbRef;
accPath: Hash32;
updateOk: bool;
): Result[Hash32,AristoError]
{.noRaise.}
## Fetch the Merkle hash of the storage root related to `accPath`. Force
## update if the argument `updateOK` is set `true`.
## Fetch the Merkle hash of the storage root related to `accPath`.
AristoApiFindTxFn* =
proc(db: AristoDbRef;
@ -425,7 +421,7 @@ type
fetchLastSavedState*: AristoApiFetchLastSavedStateFn
fetchAccountRecord*: AristoApiFetchAccountRecordFn
fetchAccountStateRoot*: AristoApiFetchAccountStateRootFn
fetchStateRoot*: AristoApiFetchStateRootFn
fetchStorageData*: AristoApiFetchStorageDataFn
fetchStorageRoot*: AristoApiFetchStorageRootFn
@ -472,7 +468,7 @@ type
AristoApiProfFetchLastSavedStateFn = "fetchLastSavedState"
AristoApiProfFetchAccountRecordFn = "fetchAccountRecord"
AristoApiProfFetchAccountStateRootFn = "fetchAccountStateRoot"
AristoApiProfFetchStateRootFn = "fetchStateRoot"
AristoApiProfFetchStorageDataFn = "fetchStorageData"
AristoApiProfFetchStorageRootFn = "fetchStorageRoot"
@ -534,7 +530,7 @@ when AutoValidateApiHooks:
doAssert not api.fetchLastSavedState.isNil
doAssert not api.fetchAccountRecord.isNil
doAssert not api.fetchAccountStateRoot.isNil
doAssert not api.fetchStateRoot.isNil
doAssert not api.fetchStorageData.isNil
doAssert not api.fetchStorageRoot.isNil
@ -601,7 +597,7 @@ func init*(api: var AristoApiObj) =
api.fetchLastSavedState = fetchLastSavedState
api.fetchAccountRecord = fetchAccountRecord
api.fetchAccountStateRoot = fetchAccountStateRoot
api.fetchStateRoot = fetchStateRoot
api.fetchStorageData = fetchStorageData
api.fetchStorageRoot = fetchStorageRoot
@ -650,7 +646,7 @@ func dup*(api: AristoApiRef): AristoApiRef =
fetchLastSavedState: api.fetchLastSavedState,
fetchAccountRecord: api.fetchAccountRecord,
fetchAccountStateRoot: api.fetchAccountStateRoot,
fetchStateRoot: api.fetchStateRoot,
fetchStorageData: api.fetchStorageData,
fetchStorageRoot: api.fetchStorageRoot,
@ -742,10 +738,10 @@ func init*(
AristoApiProfFetchAccountRecordFn.profileRunner:
result = api.fetchAccountRecord(a, b)
profApi.fetchAccountStateRoot =
profApi.fetchStateRoot =
proc(a: AristoDbRef; b: bool): auto =
AristoApiProfFetchAccountStateRootFn.profileRunner:
result = api.fetchAccountStateRoot(a, b)
AristoApiProfFetchStateRootFn.profileRunner:
result = api.fetchStateRoot(a, b)
profApi.fetchStorageData =
proc(a: AristoDbRef; b, stoPath: Hash32): auto =
@ -753,9 +749,9 @@ func init*(
result = api.fetchStorageData(a, b, stoPath)
profApi.fetchStorageRoot =
proc(a: AristoDbRef; b: Hash32; c: bool): auto =
proc(a: AristoDbRef; b: Hash32): auto =
AristoApiProfFetchStorageRootFn.profileRunner:
result = api.fetchStorageRoot(a, b, c)
result = api.fetchStorageRoot(a, b)
profApi.findTx =
proc(a: AristoDbRef; b: RootedVertexID; c: HashKey): auto =

View File

@ -80,20 +80,13 @@ proc retrieveAccountLeaf(
proc retrieveMerkleHash(
db: AristoDbRef;
root: VertexID;
updateOk: bool;
): Result[Hash32,AristoError] =
let key =
if updateOk:
db.computeKey((root, root)).valueOr:
if error == GetVtxNotFound:
return ok(EMPTY_ROOT_HASH)
return err(error)
else:
let (key, _) = db.getKeyRc((root, root)).valueOr:
if error == GetKeyNotFound:
return ok(EMPTY_ROOT_HASH) # empty sub-tree
return err(error)
key
db.computeKey((root, root)).valueOr:
if error == GetVtxNotFound:
return ok(EMPTY_ROOT_HASH)
return err(error)
ok key.to(Hash32)
proc hasAccountPayload(
@ -219,12 +212,11 @@ proc fetchAccountRecord*(
ok leafVtx.lData.account
proc fetchAccountStateRoot*(
proc fetchStateRoot*(
db: AristoDbRef;
updateOk: bool;
): Result[Hash32,AristoError] =
## Fetch the Merkle hash of the account root.
db.retrieveMerkleHash(VertexID(1), updateOk)
db.retrieveMerkleHash(VertexID(1))
proc hasPathAccount*(
db: AristoDbRef;
@ -248,14 +240,13 @@ proc fetchStorageData*(
proc fetchStorageRoot*(
db: AristoDbRef;
accPath: Hash32;
updateOk: bool;
): Result[Hash32,AristoError] =
## Fetch the Merkle hash of the storage root related to `accPath`.
let stoID = db.fetchStorageIdImpl(accPath).valueOr:
if error == FetchPathNotFound:
return ok(EMPTY_ROOT_HASH) # no sub-tree
return err(error)
db.retrieveMerkleHash(stoID, updateOk)
db.retrieveMerkleHash(stoID)
proc hasPathStorage*(
db: AristoDbRef;

View File

@ -243,7 +243,7 @@ proc jLogger(
func to(w: AristoApiProfNames; T: type TracePfx): T =
case w:
of AristoApiProfFetchAccountRecordFn,
AristoApiProfFetchAccountStateRootFn,
AristoApiProfFetchStateRootFn,
AristoApiProfDeleteAccountRecordFn,
AristoApiProfMergeAccountRecordFn:
return TrpAccounts
@ -466,26 +466,25 @@ proc ariTraceRecorder(tr: TraceRecorderRef) =
debug logTxt $info, level, accPath, accRec
ok accRec
tracerApi.fetchAccountStateRoot =
tracerApi.fetchStateRoot =
proc(mpt: AristoDbRef;
updateOk: bool;
): Result[Hash32,AristoError] =
const info = AristoApiProfFetchAccountStateRootFn
const info = AristoApiProfFetchStateRootFn
when CoreDbNoisyCaptJournal:
let level = tr.topLevel()
# Find entry on DB
let state = api.fetchAccountStateRoot(mpt, updateOk).valueOr:
let state = api.fetchStateRoot(mpt).valueOr:
when CoreDbNoisyCaptJournal:
debug logTxt $info, level, updateOk, error
debug logTxt $info, level, error
tr.jLogger logRecord(info, TrqFind, error)
return err(error)
tr.jLogger logRecord(info, TrqFind, state)
when CoreDbNoisyCaptJournal:
debug logTxt $info, level, updateOk, state
debug logTxt $info, level, state
ok state
tracerApi.fetchStorageData =
@ -514,7 +513,6 @@ proc ariTraceRecorder(tr: TraceRecorderRef) =
tracerApi.fetchStorageRoot =
proc(mpt: AristoDbRef;
accPath: Hash32;
updateOk: bool;
): Result[Hash32,AristoError] =
const info = AristoApiProfFetchStorageRootFn
@ -522,16 +520,16 @@ proc ariTraceRecorder(tr: TraceRecorderRef) =
let level = tr.topLevel()
# Find entry on DB
let state = api.fetchStorageRoot(mpt, accPath, updateOk).valueOr:
let state = api.fetchStorageRoot(mpt, accPath).valueOr:
when CoreDbNoisyCaptJournal:
debug logTxt $info, level, accPath, updateOk, error
debug logTxt $info, level, accPath, error
tr.jLogger(accPath, logRecord(info, TrqFind, error))
return err(error)
tr.jLogger(accPath, logRecord(info, TrqFind, state))
when CoreDbNoisyCaptJournal:
debug logTxt $info, level, accPath, updateOk, state
debug logTxt $info, level, accPath, state
ok state
tracerApi.deleteAccountRecord =

View File

@ -530,21 +530,17 @@ proc hasPath*(
acc.ifTrackNewApi:
debug logTxt, api, elapsed, accPath=($$accPath), result
proc stateRoot*(acc: CoreDbAccRef; updateOk = false): CoreDbRc[Hash32] =
proc getStateRoot*(acc: CoreDbAccRef): CoreDbRc[Hash32] =
## This function retrieves the Merkle state hash of the accounts
## column (if available.)
##
## If the argument `updateOk` is set `true`, the Merkle hashes of the
## database will be updated first (if needed, at all).
##
acc.setTrackNewApi AccStateFn
result = block:
let rc = acc.call(fetchAccountStateRoot, acc.mpt, updateOk)
let rc = acc.call(fetchStateRoot, acc.mpt)
if rc.isOk:
ok(rc.value)
else:
err(rc.error.toError $api)
acc.ifTrackNewApi: debug logTxt, api, elapsed, updateOK, result
acc.ifTrackNewApi: debug logTxt, api, elapsed, result
# ------------ storage ---------------
@ -647,36 +643,32 @@ proc slotMerge*(
debug logTxt, api, elapsed, accPath=($$accPath),
stoPath=($$stoPath), stoData, result
proc slotState*(
proc slotStorageRoot*(
acc: CoreDbAccRef;
accPath: Hash32;
updateOk = false;
): CoreDbRc[Hash32] =
## This function retrieves the Merkle state hash of the storage data
## column (if available) related to the account indexed by the key
## `accPath`.`.
##
## If the argument `updateOk` is set `true`, the Merkle hashes of the
## database will be updated first (if needed, at all).
##
acc.setTrackNewApi AccSlotStateFn
acc.setTrackNewApi AccSlotStorageRootFn
result = block:
let rc = acc.call(fetchStorageRoot, acc.mpt, accPath, updateOk)
let rc = acc.call(fetchStorageRoot, acc.mpt, accPath)
if rc.isOk:
ok(rc.value)
else:
err(rc.error.toError $api)
acc.ifTrackNewApi:
debug logTxt, api, elapsed, accPath=($$accPath), updateOk, result
debug logTxt, api, elapsed, accPath=($$accPath), result
proc slotStateEmpty*(
proc slotStorageEmpty*(
acc: CoreDbAccRef;
accPath: Hash32;
): CoreDbRc[bool] =
## This function returns `true` if the storage data column is empty or
## missing.
##
acc.setTrackNewApi AccSlotStateEmptyFn
acc.setTrackNewApi AccSlotStorageEmptyFn
result = block:
let rc = acc.call(hasStorageData, acc.mpt, accPath)
if rc.isOk:
@ -686,12 +678,12 @@ proc slotStateEmpty*(
acc.ifTrackNewApi:
debug logTxt, api, elapsed, accPath=($$accPath), result
proc slotStateEmptyOrVoid*(
proc slotStorageEmptyOrVoid*(
acc: CoreDbAccRef;
accPath: Hash32;
): bool =
## Convenience wrapper, returns `true` where `slotStateEmpty()` would fail.
acc.setTrackNewApi AccSlotStateEmptyOrVoidFn
## Convenience wrapper, returns `true` where `slotStorageEmpty()` would fail.
acc.setTrackNewApi AccSlotStorageEmptyOrVoidFn
result = block:
let rc = acc.call(hasStorageData, acc.mpt, accPath)
if rc.isOk:
@ -707,14 +699,13 @@ proc recast*(
acc: CoreDbAccRef;
accPath: Hash32;
accRec: CoreDbAccount;
updateOk = false;
): CoreDbRc[Account] =
## Complete the argument `accRec` to the portable Ethereum representation
## of an account statement. This conversion may fail if the storage colState
## hash (see `slotState()` above) is currently unavailable.
## hash (see `slotStorageRoot()` above) is currently unavailable.
##
acc.setTrackNewApi AccRecastFn
let rc = acc.call(fetchStorageRoot, acc.mpt, accPath, updateOk)
let rc = acc.call(fetchStorageRoot, acc.mpt, accPath)
result = block:
if rc.isOk:
ok Account(
@ -725,8 +716,8 @@ proc recast*(
else:
err(rc.error.toError $api)
acc.ifTrackNewApi:
let slotState = if rc.isOk: $$(rc.value) else: "n/a"
debug logTxt, api, elapsed, accPath=($$accPath), slotState, result
let storageRoot = if rc.isOk: $$(rc.value) else: "n/a"
debug logTxt, api, elapsed, accPath=($$accPath), storageRoot, result
# ------------------------------------------------------------------------------
# Public transaction related methods

View File

@ -45,9 +45,9 @@ type
AccSlotHasPathFn = "slotHasPath"
AccSlotMergeFn = "slotMerge"
AccSlotProofFn = "slotProof"
AccSlotStateFn = "slotState"
AccSlotStateEmptyFn = "slotStateEmpty"
AccSlotStateEmptyOrVoidFn = "slotStateEmptyOrVoid"
AccSlotStorageRootFn = "slotStorageRoot"
AccSlotStorageEmptyFn = "slotStorageEmpty"
AccSlotStorageEmptyOrVoidFn = "slotStorageEmptyOrVoid"
AccSlotPairsIt = "slotPairs"
BaseFinishFn = "finish"

View File

@ -371,13 +371,11 @@ proc init*(x: typedesc[LedgerRef], db: CoreDbRef): LedgerRef =
init(x, db, false)
proc getStateRoot*(ac: LedgerRef): Hash32 =
const info = "state(): "
# make sure all savepoint already committed
doAssert(ac.savePoint.parentSavepoint.isNil)
# make sure all cache already committed
doAssert(ac.isDirty == false)
ac.ledger.stateRoot(updateOk=true).valueOr:
raiseAssert info & $$error
ac.ledger.getStateRoot().expect("working database")
proc isTopLevelClean*(ac: LedgerRef): bool =
## Getter, returns `true` if all pending data have been commited.
@ -514,7 +512,7 @@ proc contractCollision*(ac: LedgerRef, address: Address): bool =
return
acc.statement.nonce != 0 or
acc.statement.codeHash != EMPTY_CODE_HASH or
not ac.ledger.slotStateEmptyOrVoid(acc.toAccountKey)
not ac.ledger.slotStorageEmptyOrVoid(acc.toAccountKey)
proc accountExists*(ac: LedgerRef, address: Address): bool =
let acc = ac.getAccount(address, false)
@ -600,7 +598,7 @@ proc clearStorage*(ac: LedgerRef, address: Address) =
let acc = ac.getAccount(address)
acc.flags.incl {Alive, NewlyCreated}
let empty = ac.ledger.slotStateEmpty(acc.toAccountKey).valueOr: return
let empty = ac.ledger.slotStorageEmpty(acc.toAccountKey).valueOr: return
if not empty:
# need to clear the storage from the database first
let acc = ac.makeDirty(address, cloneStorage = false)
@ -733,14 +731,14 @@ iterator accounts*(ac: LedgerRef): Account =
doAssert(ac.savePoint.parentSavepoint.isNil)
for _, acc in ac.savePoint.cache:
yield ac.ledger.recast(
acc.toAccountKey, acc.statement, updateOk=true).value
acc.toAccountKey, acc.statement).value
iterator pairs*(ac: LedgerRef): (Address, Account) =
# make sure all savepoint already committed
doAssert(ac.savePoint.parentSavepoint.isNil)
for address, acc in ac.savePoint.cache:
yield (address, ac.ledger.recast(
acc.toAccountKey, acc.statement, updateOk=true).value)
acc.toAccountKey, acc.statement).value)
iterator storage*(
ac: LedgerRef;
@ -771,7 +769,7 @@ proc getStorageRoot*(ac: LedgerRef, address: Address): Hash32 =
# the storage root will not be updated
let acc = ac.getAccount(address, false)
if acc.isNil: EMPTY_ROOT_HASH
else: ac.ledger.slotState(acc.toAccountKey).valueOr: EMPTY_ROOT_HASH
else: ac.ledger.slotStorageRoot(acc.toAccountKey).valueOr: EMPTY_ROOT_HASH
proc update(wd: var WitnessData, acc: AccountRef) =
# once the code is touched make sure it doesn't get reset back to false in another update

View File

@ -189,14 +189,14 @@ proc traceTransactionImpl(
before.captureAccount(stateDb, miner, minerName)
stateDb.persist()
stateDiff["beforeRoot"] = %(stateDb.getStateRoot().toHex)
discard com.db.ctx.getAccounts.stateRoot(updateOk=true) # lazy hashing!
discard com.db.ctx.getAccounts.getStateRoot() # lazy hashing!
stateCtx = CaptCtxRef.init(com, stateDb.getStateRoot())
let rc = vmState.processTransaction(tx, sender, header)
gasUsed = if rc.isOk: rc.value else: 0
if idx.uint64 == txIndex:
discard com.db.ctx.getAccounts.stateRoot(updateOk=true) # lazy hashing!
discard com.db.ctx.getAccounts.getStateRoot() # lazy hashing!
after.captureAccount(stateDb, sender, senderName)
after.captureAccount(stateDb, recipient, recipientName)
after.captureAccount(stateDb, miner, minerName)

View File

@ -59,7 +59,7 @@ proc parseEnv(node: JsonNode): TestEnv =
result.pre = node["pre"]
proc rootExists(db: CoreDbRef; root: Hash32): bool =
let state = db.ctx.getAccounts().stateRoot(updateOk=true).valueOr:
let state = db.ctx.getAccounts().getStateRoot().valueOr:
return false
state == root