nimbus-eth1/nimbus/db/aristo/aristo_part/part_debug.nim

250 lines
7.4 KiB
Nim

# nimbus-eth1
# Copyright (c) 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)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed
# except according to those terms.
{.push raises: [].}
import
std/[algorithm, sequtils, strutils, sets, typetraits],
eth/common,
".."/[aristo_debug, aristo_desc, aristo_get],
./part_desc
export
algorithm,
sequtils,
aristo_debug
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
proc toPfx(indent: int; offset = 0): string =
if 0 < indent+offset: "\n" & " ".repeat(indent+offset) else: ""
proc pp(w: HashSet[HashKey]; ps: PartStateRef): string =
"{" & w.toSeq.sorted.mapIt(it.pp(ps.db)).join(",") & "}[#" & $w.len & "]"
# ------------------------------------------------------------------------------
# Public debugging stuff
# ------------------------------------------------------------------------------
proc pp*(n: PrfNode; ps: PartStateRef): string =
if n.isNil:
"(nil)"
elif n.prfType == isError:
"(" & $n.error & ")"
elif n.prfType == isExtension:
"X(" & n.vtx.pfx.pp & "," & n.key[0].pp(ps.db) & ")"
else:
"(" & NodeRef(n).pp(ps.db) & ")"
proc pp*(e: PrfExtension; ps: PartStateRef): string =
if e.isNil:
"(nil)"
else:
"(" & e.xPfx.pp & "," & e.xLink.pp(ps.db) & ")"
proc pp*(p: PrfPayload; ps: PartStateRef): string =
if p.prfType == isAccount:
p.acc.pp(ps.db)
elif p.prfType == isStoValue:
"(" & $p.num & ")"
else:
"(" & $p.error & ")"
proc pp*[T: PrfNode|PrfExtension](
q: seq[(HashKey,T)];
ps: PartStateRef;
indent = 4;
): string =
"{" & q.mapIt("(" & it[0].pp(ps.db) & "," & pp(it[1], ps) & ")")
.join("," & indent.toPfx(1)) & "}"
proc pp*[T: PrfNode|PrfExtension](
t: Table[HashKey,T];
ps: PartStateRef;
indent = 4;
): string =
var
t0: Table[RootedVertexID,(HashKey,T)]
t1: Table[HashKey,T]
for (key,val) in t.pairs:
ps.db.xMap.withValue(key,rv):
t0[rv[]] = (key,val)
do:
t1[key] = val
let
q0 = t0.keys.toSeq.sorted.mapIt(t0.getOrDefault it)
q1 = t1.keys.toSeq.sorted.mapIt((it, t1.getOrDefault it))
(q0 & q1).pp(ps,indent)
proc pp*(t: TableRef[HashKey,PrfNode]; ps: PartStateRef; indent = 4): string =
pp(t[], ps, indent)
proc pp*(t: Table[VertexID,HashKey]; ps: PartStateRef; indent = 4): string =
"{" & t.keys.toSeq.sorted
.mapIt((it,t.getOrDefault it))
.mapIt("(" & it[0].pp & "," & it[1].pp(ps.db) & ")")
.join("," & indent.toPfx(1)) & "}"
proc pp*(q: seq[HashKey]; ps: PartStateRef): string =
"(" & q.mapIt(it.pp ps.db).join("->") & ")[#" & $q.len & "]"
proc pp*(q: seq[seq[HashKey]]; ps: PartStateRef; indent = 4): string =
"{" & q.mapIt(it.pp ps).join("," & indent.toPfx(1)) & "}"
proc pp*(x: PartStateCtx): string =
if x.isNil:
"ø"
else:
"(" & x.location.pp & "," & $x.nibble & "," & x.fromVid.pp & ")"
proc pp*(
ps: PartStateRef;
indent = 4;
# ----------
dbOk = true;
coreOk = true;
byKeyOk = true;
byVidOk = true;
changedOk = true;
pureExtOk = true;
# ----------
backendOk = false;
balancerOk = true;
topOk = true;
stackOk = true;
kMapOk = true;
sTabOk = true;
limit = 100;
): string =
let
pfx0 = indent.toPfx()
pfx1 = indent.toPfx(1)
pfx2 = indent.toPfx(2)
var pfx = ""
if dbOk:
result &= pfx & "<db>" & pfx1 & ps.db.pp(
indent = indent + 1,
backendOk = backendOk,
balancerOk = balancerOk,
topOk = topOk,
stackOk = stackOk,
kMapOk = kMapOk,
sTabOk = sTabOk,
limit = limit)
pfx = pfx0
if coreOk:
let len = ps.core.len
result &= pfx & "<core(" & $len & ")>"
if 0 < len:
var qfx = ""
result &= pfx1 & "{"
for vid in ps.core.keys.toSeq.sorted:
let vLst = ps.core.getOrDefault vid
result &= qfx & "(" & vid.pp & ":" & vLst.pp(ps) & ")"
qfx = pfx2
result &= "}"
pfx = pfx0
if byKeyOk:
let len = ps.byKey.len
result &= pfx & "<byKey(" & $len & ")>"
if 0 < len:
result &= pfx1 & ps.byKey.pp(ps.db, indent+1)
pfx = pfx0
if byVidOk:
let len = ps.byVid.len
result &= pfx & "<byVid(" & $len & ")>"
if 0 < len:
result &= pfx1 & ps.byVid.pp(ps, indent+1)
pfx = pfx0
if changedOk:
let len = ps.changed.len
result &= pfx & "<changed(" & $len & ")>"
if 0 < len:
result &= pfx1 & ps.changed.pp(ps)
pfx = pfx0
if pureExtOk:
let len = ps.pureExt.len
result &= pfx & "<pureExt(" & $len & ")>"
if 0 < len:
result &= pfx1 & pp[PrfExtension](ps.pureExt, ps, indent+1)
pfx = pfx0
# ------------------
proc check*(ps: PartStateRef): Result[void,(VertexID,AristoError)] =
# Provide temporary lookup table
var
byVid: ptr Table[VertexID,HashKey]
t: Table[VertexID,HashKey] # use it in case `ps.byVid[]` is empty
# Lookup tables must match unless `ps.byVid[]` is empty
if ps.byVid.len == 0:
# Create ad-hoc table
for (key,rvid) in ps.byKey.pairs:
t[rvid.vid] = key
byVid = addr t
else:
if ps.byKey.len != ps.byVid.len:
return err((VertexID(0),PartChkVidKeyTabLengthsDiffer))
for (key,rvid) in ps.byKey.pairs:
ps.byVid.withValue(rvid.vid,vKey):
if key != vKey[]:
return err((rvid.vid,PartChkVidKeyTabKeyMismatch))
continue
return err((rvid.vid,PartChkVidTabVidMissing))
# Provide ad-hoc table
byVid = addr ps.byVid
# All `changed` keys must be listed in the `ps.byKey[]` tab and
# exist on the database
for key in ps.changed:
ps.byKey.withValue(key,rvid):
if not ps.db.getVtx(rvid[]).isValid:
return err((rvid.vid,PartChkChangedVtxMissing))
continue
return err((VertexID(0),PartChkChangedKeyNotInKeyTab))
# All vertices for `core` keys must exist on database
for (root,keys) in ps.core.pairs:
if root notin byVid[]:
return err((root,PartChkVidTabCoreRootMissing))
for key in keys:
ps.byKey.withValue(key,rvid):
if not ps.db.getVtx(rvid[]).isValid:
return err((rvid.vid,PartChkCoreVtxMissing))
# Verify lookup
if not ps.isCore key:
return err((rvid.vid,PartChkCoreKeyLookupFailed))
if not ps.isCore rvid[]:
return err((rvid.vid,PartChkCoreRVidLookupFailed))
if not ps.isCore rvid.vid:
return err((rvid.vid,PartChkCoreVidLookupFailed))
continue
return err((ps[key].vid,PartChkKeyTabCoreKeyMissing))
# All vertices for non-`core` and un-`changed` keys must not exist on database
for (key,rvid) in ps.byKey.pairs:
ps.core.withValue(rvid.root,keys):
if ps.db.getVtx(rvid).isValid and
key notin keys[] and
key notin ps.changed:
return err((rvid.vid,PartChkPerimeterVtxMustNotExist))
continue
return err((rvid.vid,PartChkKeyTabRootMissing))
ok()
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------