nimbus-eth1/nimbus/db/aristo/aristo_debug.nim

467 lines
13 KiB
Nim

# nimbus-eth1
# Copyright (c) 2021 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, sets, strutils, tables],
eth/[common, trie/nibbles],
stew/byteutils,
"."/[aristo_constants, aristo_desc, aristo_error, aristo_hike, aristo_vid]
# ------------------------------------------------------------------------------
# Ptivate functions
# ------------------------------------------------------------------------------
proc toPfx(indent: int): string =
"\n" & " ".repeat(indent)
proc keyVidUpdate(db: AristoDbRef, key: NodeKey, vid: VertexID): string =
if not key.isEmpty and
not vid.isZero and
not db.isNil:
block:
let keyVid = db.pAmk.getOrDefault(key, VertexID(0))
if keyVid != VertexID(0):
if keyVid != vid:
result = "(!)"
return
block:
let keyVid = db.xMap.getOrDefault(key, VertexID(0))
if keyVid != VertexID(0):
if keyVid != vid:
result = "(!)"
return
db.xMap[key] = vid
proc squeeze(s: string; hex = false; ignLen = false): string =
## For long strings print `begin..end` only
if hex:
let n = (s.len + 1) div 2
result = if s.len < 20: s else: s[0 .. 5] & ".." & s[s.len-8 .. ^1]
if not ignLen:
result &= "[" & (if 0 < n: "#" & $n else: "") & "]"
elif s.len <= 30:
result = s
else:
result = if (s.len and 1) == 0: s[0 ..< 8] else: "0" & s[0 ..< 7]
if not ignLen:
result &= "..(" & $s.len & ")"
result &= ".." & s[s.len-16 .. ^1]
proc stripZeros(a: string): string =
for n in 0 ..< a.len:
if a[n] != '0':
return a[n .. ^1]
return a
proc ppVid(vid: VertexID): string =
if vid.isZero: "ø" else: "$" & vid.uint64.toHex.stripZeros.toLowerAscii
proc vidCode(key: NodeKey, db: AristoDbRef): uint64 =
if not db.isNil and
key != EMPTY_ROOT_KEY and
key != EMPTY_CODE_KEY:
block:
let vid = db.pAmk.getOrDefault(key, VertexID(0))
if vid != VertexID(0):
return vid.uint64
block:
let vid = db.xMap.getOrDefault(key, VertexID(0))
if vid != VertexID(0):
return vid.uint64
proc ppKey(key: NodeKey, db: AristoDbRef): string =
if key == NodeKey.default:
return "£ø"
if key == EMPTY_ROOT_KEY:
return "£r"
if key == EMPTY_CODE_KEY:
return "£c"
if not db.isNil:
block:
let vid = db.pAmk.getOrDefault(key, VertexID(0))
if vid != VertexID(0):
return "£" & vid.uint64.toHex.stripZeros.toLowerAscii
block:
let vid = db.xMap.getOrDefault(key, VertexID(0))
if vid != VertexID(0):
return "£" & vid.uint64.toHex.stripZeros.toLowerAscii
"%" & key.ByteArray32
.mapIt(it.toHex(2)).join.tolowerAscii
.squeeze(hex=true,ignLen=true)
proc ppRootKey(a: NodeKey, db: AristoDbRef): string =
if a != EMPTY_ROOT_KEY:
return a.ppKey(db)
proc ppCodeKey(a: NodeKey, db: AristoDbRef): string =
if a != EMPTY_CODE_KEY:
return a.ppKey(db)
proc ppPathTag(tag: NodeTag, db: AristoDbRef): string =
## Raw key, for referenced key dump use `key.pp(db)` below
if not db.isNil:
let vid = db.lTab.getOrDefault(tag, VertexID(0))
if vid != VertexID(0):
return "@" & vid.ppVid
"@" & tag.to(NodeKey).ByteArray32
.mapIt(it.toHex(2)).join.toLowerAscii
.squeeze(hex=true,ignLen=true)
proc ppPathPfx(pfx: NibblesSeq): string =
let s = $pfx
if s.len < 20: s else: s[0 .. 5] & ".." & s[s.len-8 .. ^1] & ":" & $s.len
proc ppNibble(n: int8): string =
if n < 0: "ø" elif n < 10: $n else: n.toHex(1).toLowerAscii
proc ppPayload(p: PayloadRef, db: AristoDbRef): string =
if p.isNil:
result = "n/a"
else:
case p.pType:
of BlobData:
result &= p.blob.toHex.squeeze(hex=true)
of AccountData:
result = "("
result &= $p.account.nonce & ","
result &= $p.account.balance & ","
result &= p.account.storageRoot.to(NodeKey).ppRootKey(db) & ","
result &= p.account.codeHash.to(NodeKey).ppCodeKey(db) & ")"
proc ppVtx(nd: VertexRef, db: AristoDbRef, vid: VertexID): string =
if nd.isNil:
result = "n/a"
else:
if db.isNil or vid.isZero or vid in db.pPrf:
result = ["l(", "x(", "b("][nd.vType.ord]
else:
result = ["ł(", "€(", "þ("][nd.vType.ord]
case nd.vType:
of Leaf:
result &= nd.lPfx.ppPathPfx & "," & nd.lData.ppPayload(db)
of Extension:
result &= nd.ePfx.ppPathPfx & "," & nd.eVid.ppVid
of Branch:
for n in 0..15:
if not nd.bVid[n].isZero:
result &= nd.bVid[n].ppVid
if n < 15:
result &= ","
result &= ")"
proc ppXMap*(
db: AristoDbRef;
kMap: Table[VertexID,NodeKey];
pAmk: Table[NodeKey,VertexID];
indent: int;
): string =
let dups = pAmk.values.toSeq.toCountTable.pairs.toSeq
.filterIt(1 < it[1]).toTable
proc ppNtry(n: uint64): string =
let
vid = n.VertexID
key = kMap.getOrDefault(vid, EMPTY_ROOT_KEY)
var s = "(" & vid.ppVid & ","
if key != EMPTY_ROOT_KEY:
s &= key.ppKey(db)
let keyVid = pAmk.getOrDefault(key, VertexID(0))
if keyVid == VertexID(0):
s &= ""
elif keyVid != vid:
s &= "," & keyVid.ppVid
let count = dups.getOrDefault(vid, 0)
if 0 < count:
s &= ",*" & $count
else:
s &= "£r(!)"
s & "),"
var cache: seq[(uint64,uint64,bool)]
for vid in toSeq(kMap.keys).mapIt(it.uint64).sorted.mapIt(it.VertexID):
let key = kMap.getOrDefault(vid, EMPTY_ROOT_KEY)
if key != EMPTY_ROOT_KEY:
cache.add (vid.uint64, key.vidCode(db), 0 < dups.getOrDefault(vid, 0))
let keyVid = pAmk.getOrDefault(key, VertexID(0))
if keyVid != VertexID(0) and keyVid != vid:
cache[^1][2] = true
else:
cache.add (vid.uint64, 0u64, true)
result = "{"
if 0 < cache.len:
let
pfx = indent.toPfx
var
(i, r) = (0, cache[0])
result &= cache[i][0].ppNtry
for n in 1 ..< cache.len:
let w = cache[n]
r[0].inc
r[1].inc
if r != w or w[2]:
if i+1 != n:
result &= ".. " & cache[n-1][0].ppNtry
result &= pfx & " " & cache[n][0].ppNtry
(i, r) = (n, w)
if i < cache.len - 1:
if i+1 != cache.len - 1:
result &= ".. "
else:
result &= pfx & " "
result &= cache[^1][0].ppNtry
result[^1] = '}'
else:
result &= "}"
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc keyToVtxID*(db: AristoDbRef, key: NodeKey): VertexID =
## Associate a vertex ID with the argument `key` for pretty printing.
if not db.isNil and
key != EMPTY_ROOT_KEY and
key != EMPTY_CODE_KEY:
let vid = db.xMap.getOrDefault(key, VertexID(0))
if vid != VertexID(0):
result = vid
else:
result = db.vidFetch()
db.xMap[key] = result
proc pp*(vid: NodeKey, db = AristoDbRef(nil)): string =
vid.ppKey(db)
proc pp*(tag: NodeTag, db = AristoDbRef(nil)): string =
tag.ppPathTag(db)
proc pp*(vid: VertexID): string =
vid.ppVid
proc pp*(vid: openArray[VertexID]): string =
"[" & vid.mapIt(it.ppVid).join(",") & "]"
proc pp*(p: PayloadRef, db = AristoDbRef(nil)): string =
p.ppPayload(db)
proc pp*(nd: VertexRef, db = AristoDbRef(nil)): string =
nd.ppVtx(db, VertexID(0))
proc pp*(nd: NodeRef, db = AristoDbRef(nil)): string =
if nd.isNil:
result = "n/a"
elif nd.isError:
result = "(!" & $nd.error
else:
result = ["L(", "X(", "B("][nd.vType.ord]
case nd.vType:
of Leaf:
result &= $nd.lPfx.ppPathPfx & "," & nd.lData.pp(db)
of Extension:
result &= $nd.ePfx.ppPathPfx & "," & nd.eVid.ppVid & ","
result &= nd.key[0].ppKey(db)
result &= db.keyVidUpdate(nd.key[0], nd.eVid)
of Branch:
result &= "["
for n in 0..15:
if not nd.bVid[n].isZero or nd.key[n] != EMPTY_ROOT_KEY:
result &= nd.bVid[n].ppVid
result &= db.keyVidUpdate(nd.key[n], nd.bVid[n]) & ","
result[^1] = ']'
result &= ",["
for n in 0..15:
if not nd.bVid[n].isZero or nd.key[n] != EMPTY_ROOT_KEY:
result &= nd.key[n].ppKey(db)
result &= ","
result[^1] = ']'
result &= ")"
proc pp*(
sTab: Table[VertexID,VertexRef];
db = AristoDbRef(nil);
indent = 4;
): string =
let pfx = indent.toPfx
var first = true
result = "{"
for vid in toSeq(sTab.keys).mapIt(it.uint64).sorted.mapIt(it.VertexID):
let vtx = sTab.getOrDefault(vid, VertexRef(nil))
if vtx != VertexRef(nil):
if first:
first = false
else:
result &= pfx & " "
result &= "(" & vid.ppVid & "," & vtx.ppVtx(db,vid) & ")"
result &= "}"
proc pp*(
lTab: Table[NodeTag,VertexID];
indent = 4;
): string =
let pfx = indent.toPfx
var first = true
result = "{"
for tag in toSeq(lTab.keys).mapIt(it.UInt256).sorted.mapIt(it.NodeTag):
let vid = lTab.getOrDefault(tag, VertexID(0))
if vid != VertexID(0):
if first:
first = false
else:
result &= pfx & " "
result &= "(" & tag.ppPathTag(nil) & "," & vid.ppVid & ")"
result &= "}"
proc pp*(vGen: seq[VertexID]): string =
result = "["
for vid in vGen:
result &= vid.ppVid & ","
if result[^1] == ',':
result[^1] = ']'
else:
result &= "]"
proc pp*(pPrf: HashSet[VertexID]): string =
result = "{"
for vid in pPrf.toSeq.mapIt(it.uint64).sorted.mapIt(it.VertexID):
result &= vid.ppVid & ","
if result[^1] == ',':
result[^1] = '}'
else:
result &= "}"
proc pp*(
leg: Leg;
db = AristoDbRef(nil);
): string =
result = " (" & leg.wp.vid.ppVid & ","
if not db.isNil:
let key = db.kMap.getOrDefault(leg.wp.vid, EMPTY_ROOT_KEY)
if key != EMPTY_ROOT_KEY:
result &= key.ppKey(db)
else:
result &= "ø"
result &= "," & $leg.nibble.ppNibble & "," & leg.wp.vtx.pp(db) & ")"
proc pp*(
hike: Hike;
db = AristoDbRef(nil);
indent = 4;
): string =
let pfx = indent.toPfx
var first = true
result = "[(" & hike.root.ppVid & ")"
for leg in hike.legs:
result &= "," & pfx & leg.pp(db)
result &= "," & pfx & " (" & hike.tail.ppPathPfx & ")"
if hike.error != AristoError(0):
result &= "," & pfx & " (" & $hike.error & ")"
result &= "]"
proc pp*(
kMap: Table[VertexID,NodeKey];
db = AristoDbRef(nil);
indent = 4;
): string =
let pfx = indent.toPfx
var first = true
result = "{"
for vid in toSeq(kMap.keys).mapIt(it.uint64).sorted.mapIt(it.VertexID):
let key = kMap.getOrDefault(vid, EMPTY_ROOT_KEY)
if key != EMPTY_ROOT_KEY:
if first:
first = false
else:
result &= pfx & " "
result &= "(" & vid.ppVid & "," & key.ppKey(db) & "),"
if result[^1] == ',':
result[^1] = '}'
else:
result &= "}"
proc pp*(
pAmk: Table[NodeKey,VertexID];
db = AristoDbRef(nil);
indent = 4;
): string =
let pfx = indent.toPfx
var
rev = pAmk.pairs.toSeq.mapIt((it[1],it[0])).toTable
first = true
result = "{"
for vid in rev.keys.toSeq.mapIt(it.uint64).sorted.mapIt(it.VertexID):
let key = rev.getOrDefault(vid, EMPTY_ROOT_KEY)
if key != EMPTY_ROOT_KEY:
if first:
first = false
else:
result &= pfx & " "
result &= "(" & key.ppKey(db) & "," & vid.ppVid & "),"
if result[^1] == ',':
result[^1] = '}'
else:
result &= "}"
# ---------------------
proc pp*(
db: AristoDbRef;
sTabOk = true;
lTabOk = true;
kMapOk = true;
pPrfOk = true;
indent = 4;
): string =
let
pfx1 = max(indent-1,0).toPfx
pfx2 = indent.toPfx
labelOk = 1 < sTabOk.ord + lTabOk.ord + kMapOk.ord + pPrfOk.ord
var
pfy1 = ""
pfy2 = ""
proc doPrefix(s: string): string =
var rc: string
if labelOk:
rc = pfy1 & s & pfx2
pfy1 = pfx1
else:
rc = pfy2
pfy2 = pfx2
rc
if sTabOk:
let info = "sTab(" & $db.sTab.len & ")"
result &= info.doPrefix & db.sTab.pp(db,indent)
if lTabOk:
let info = "lTab(" & $db.lTab.len & "),root=" & db.lRoot.ppVid
result &= info.doPrefix & db.lTab.pp(indent)
if kMapOk:
let info = "kMap(" & $db.kMap.len & "," & $db.pAmk.len & ")"
result &= info.doPrefix & db.ppXMap(db.kMap,db.pAmk,indent)
if pPrfOk:
let info = "pPrf(" & $db.pPrf.len & ")"
result &= info.doPrefix & db.pPrf.pp
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------