import stew/[byteutils, endians2], nimcrypto/[keccak, hash], eth/rlp, eth/trie/[trie_defs, nibbles, db], faststreams/output_stream, ./witness_types type DB = TrieDatabaseRef WitnessBuilder* = object db*: DB root: KeccakHash output: OutputStream proc initWitnessBuilder*(db: DB, rootHash: KeccakHash = emptyRlpHash): WitnessBuilder = result.db = db result.root = rootHash result.output = memoryOutput().s template extensionNodeKey(r: Rlp): auto = hexPrefixDecode r.listElem(0).toBytes proc expectHash(r: Rlp): seq[byte] = result = r.toBytes if result.len != 32: raise newException(RlpTypeMismatch, "RLP expected to be a Keccak hash value, but has an incorrect length") template getNode(elem: untyped): untyped = if elem.isList: @(elem.rawData) else: get(wb.db, elem.expectHash) proc rlpListToBitmask(r: var Rlp): uint = var i = 0 for branch in r: if not branch.isEmpty: result.setBranchMaskBit(i) inc i r.position = 0 proc writeNibbles(wb: var WitnessBuilder; n: NibblesSeq) = let nibblesLen = n.len let numBytes = nibblesLen div 2 + nibblesLen mod 2 var bytes: array[32, byte] doAssert(nibblesLen >= 1) doAssert(numBytes >= 0 and numBytes <= 64) for pos in 0.. 0: let (node, path) = stack.pop() if node.len == 0: continue var nodeRlp = rlpFromBytes node 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 not isLeaf: let nextLookup = value.getNode stack.add((nextLookup, path.slice(sharedNibbles))) result.add nextLookup.toHex of 17: if path.len != 0: var branch = nodeRlp.listElem(path[0].int) if not branch.isEmpty: let nextLookup = branch.getNode stack.add((nextLookup, path.slice(1))) result.add nextLookup.toHex else: raise newException(CorruptedTrieDatabase, "HexaryTrie node with an unexpected number of children") proc getBranch*(wb: WitnessBuilder; key: openArray[byte]): seq[seq[byte]] = var node = wb.db.get(wb.root.data) stack = @[(node, initNibbleRange(key))] result.add node while stack.len > 0: let (node, path) = stack.pop() if node.len == 0: continue var nodeRlp = rlpFromBytes node 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 not isLeaf: let nextLookup = value.getNode stack.add((nextLookup, path.slice(sharedNibbles))) result.add nextLookup of 17: if path.len != 0: var branch = nodeRlp.listElem(path[0].int) if not branch.isEmpty: let nextLookup = branch.getNode stack.add((nextLookup, path.slice(1))) result.add nextLookup else: raise newException(CorruptedTrieDatabase, "HexaryTrie node with an unexpected number of children") proc buildWitness*(wb: var WitnessBuilder; key: openArray[byte]) = var node = wb.db.get(wb.root.data) stack = @[(node, initNibbleRange(key))] while stack.len > 0: let (node, path) = stack.pop() if node.len == 0: continue var nodeRlp = rlpFromBytes node 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 not isLeaf: let nextLookup = value.getNode stack.add((nextLookup, path.slice(sharedNibbles))) of 17: if path.len != 0: var branch = nodeRlp.listElem(path[0].int) if not branch.isEmpty: let nextLookup = branch.getNode stack.add((nextLookup, path.slice(1))) else: raise newException(CorruptedTrieDatabase, "HexaryTrie node with an unexpected number of children")