Added maybeGet, for working with incomplete DBs. (#595)

This commit is contained in:
Adam Spitz 2023-04-03 13:01:25 -04:00 committed by GitHub
parent 9e89f0dccc
commit 4754543605
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 110 additions and 2 deletions

View File

@ -1,7 +1,7 @@
{.push raises: [Defect].} {.push raises: [Defect].}
import import
std/[tables, hashes, sets], std/[options, tables, hashes, sets],
"."/[trie_defs, db_tracing] "."/[trie_defs, db_tracing]
type type
@ -234,6 +234,15 @@ proc contains*(db: TrieDatabaseRef, key: openArray[byte]): bool =
if db.containsProc != nil: if db.containsProc != nil:
result = db.containsProc(db.obj, key) result = db.containsProc(db.obj, key)
proc maybeGet*(db: TrieDatabaseRef, key: openArray[byte]): Option[seq[byte]] =
# FIXME-Adam: Could duplicate the structure of get, but for now let's just
# do this (I still don't know whether this overall approach makes any sense
# at all.)
if db.contains(key):
some(db.get(key))
else:
none[seq[byte]]()
# TransactionID imitate subset of JournalDB behaviour # TransactionID imitate subset of JournalDB behaviour
# but there is no need to rollback or dispose # but there is no need to rollback or dispose
# TransactionID, because it will be handled elsewhere # TransactionID, because it will be handled elsewhere

View File

@ -1,5 +1,5 @@
import import
std/tables, std/[options, tables],
nimcrypto/[keccak, hash], nimcrypto/[keccak, hash],
../rlp, ../rlp,
"."/[trie_defs, nibbles, db] "."/[trie_defs, nibbles, db]
@ -688,3 +688,102 @@ proc isValidBranch*(branch: seq[seq[byte]], rootHash: KeccakHash, key, value: se
var trie = initHexaryTrie(db, rootHash) var trie = initHexaryTrie(db, rootHash)
result = trie.get(key) == value result = trie.get(key) == value
# The code below has a lot of duplication with the code above; I needed
# versions of get/put/del that don't just assume that all the nodes exist.
# Maybe there's some way to eliminate the duplication without screwing
# up performance? But for now I don't want to meddle with the existing
# code, for fear of breaking it. --Adam, Nov. 2022
proc db*(self: SecureHexaryTrie): TrieDatabaseRef = HexaryTrie(self).db
template maybeKeyToLocalBytes(db: DB, k: TrieNodeKey): Option[seq[byte]] =
if k.len < 32:
some(k.getLocalBytes)
else:
db.maybeGet(k.asDbKey)
proc maybeGetAux(db: DB, nodeRlp: Rlp, path: NibblesSeq): Option[seq[byte]]
{.gcsafe, raises: [RlpError, Defect].}
proc maybeGetAuxByHash(db: DB, node: TrieNodeKey, path: NibblesSeq): Option[seq[byte]] =
let maybeBytes = maybeKeyToLocalBytes(db, node)
if maybeBytes.isNone:
return none[seq[byte]]()
else:
let bytes = maybeBytes.get
var nodeRlp = rlpFromBytes(bytes)
return maybeGetAux(db, nodeRlp, path)
proc maybeGetLookup(db: DB, elem: Rlp): Option[Rlp] =
if elem.isList:
some(elem)
else:
let h = elem.expectHash
let maybeBytes = db.maybeGet(h)
if maybeBytes.isNone:
none[Rlp]()
else:
let bytes = maybeBytes.get
some(rlpFromBytes(bytes))
proc maybeGetAux(db: DB, nodeRlp: Rlp, path: NibblesSeq): Option[seq[byte]]
{.gcsafe, raises: [RlpError, Defect].} =
# FIXME-Adam: do I need to distinguish between these two cases?
if not nodeRlp.hasData:
let zero: seq[byte] = @[]
return some(zero)
# return none[seq[byte]]()
if nodeRlp.isEmpty:
# FIXME-Adam: I am REALLY not sure this is the right thing to do. But toGenesisHeader
# failing is a pretty clear indication. So let's try this. I wonder whether the
# above case needs to do this too.
let zero: seq[byte] = @[]
return some(zero)
# return none[seq[byte]]()
case nodeRlp.listLen
of 2:
let (isLeaf, k) = nodeRlp.extensionNodeKey
let sharedNibbles = sharedPrefixLen(path, k)
if sharedNibbles == k.len:
let value = nodeRlp.listElem(1)
if sharedNibbles == path.len and isLeaf:
return some(value.toBytes)
elif not isLeaf:
let maybeNextLookup = maybeGetLookup(db, value)
if maybeNextLookup.isNone:
return none[seq[byte]]()
else:
return maybeGetAux(db, maybeNextLookup.get, path.slice(sharedNibbles))
else:
raise newException(RlpError, "isLeaf is true but the shared nibbles didn't exhaust the path?")
else:
let zero: seq[byte] = @[]
return some(zero)
of 17:
if path.len == 0:
return some(nodeRlp.listElem(16).toBytes)
var branch = nodeRlp.listElem(path[0].int)
if branch.isEmpty:
let zero: seq[byte] = @[]
return some(zero)
else:
let maybeNextLookup = maybeGetLookup(db, branch)
if maybeNextLookup.isNone:
return none[seq[byte]]()
else:
return maybeGetAux(db, maybeNextLookup.get, path.slice(1))
else:
raise newException(CorruptedTrieDatabase,
"HexaryTrie node with an unexpected number of children")
proc maybeGet*(self: HexaryTrie; key: openArray[byte]): Option[seq[byte]] =
return maybeGetAuxByHash(self.db, self.root, initNibbleRange(key))
proc maybeGet*(self: SecureHexaryTrie; key: openArray[byte]): Option[seq[byte]] =
return maybeGet(HexaryTrie(self), key.keccakHash.data)