nim-eth/eth/trie/sparse_proofs.nim

87 lines
2.6 KiB
Nim

import
ranges/[typedranges, bitranges],
trie_defs, db, trie_utils
const
treeHeight* = 160
pathByteLen* = treeHeight div 8
emptyLeafNodeHash* = blankStringHash
proc makeInitialEmptyTreeHash(H: static[int]): array[H, ByteRange] =
result[^1] = @(emptyLeafNodeHash.data).toRange
for i in countdown(H-1, 1):
result[i - 1] = keccakHash(result[i], result[i])
# cannot yet turn this into compile time constant
let emptyNodeHashes* = makeInitialEmptyTreeHash(treeHeight)
# VerifyProof verifies a Merkle proof.
proc verifyProofAux*(proof: seq[ByteRange], root, key, value: ByteRange): bool =
assert(root.len == 32)
assert(key.len == pathByteLen)
var
path = MutByteRange(key).bits
curHash = keccakHash(value)
if proof.len != treeHeight: return false
for i in countdown(treeHeight - 1, 0):
var node = proof[i]
if node.len != 32: return false
if path[i]: # right
# reuse curHash without more alloc
curHash.keccakHash(node, curHash)
else:
curHash.keccakHash(curHash, node)
result = curHash == root
template verifyProof*(proof: seq[ByteRange], root, key, value: distinct BytesContainer): bool =
verifyProofAux(proof, root.toRange, key.toRange, value.toRange)
proc count(b: BitRange, val: bool): int =
for c in b:
if c == val: inc result
# CompactProof compacts a proof, to reduce its size.
proc compactProof*(proof: seq[ByteRange]): seq[ByteRange] =
if proof.len != treeHeight: return
var
data = newRange[byte](pathByteLen)
bits = MutByteRange(data).bits
result = @[]
result.add data
for i in 0 ..< treeHeight:
var node = proof[i]
if node == emptyNodeHashes[i]:
bits[i] = true
else:
result.add node
# decompactProof decompacts a proof, so that it can be used for VerifyProof.
proc decompactProof*(proof: seq[ByteRange]): seq[ByteRange] =
if proof.len == 0: return
if proof[0].len != pathByteLen: return
var bits = MutByteRange(proof[0]).bits
if proof.len != bits.count(false) + 1: return
result = newSeq[ByteRange](treeHeight)
var pos = 1 # skip bits
for i in 0 ..< treeHeight:
if bits[i]:
result[i] = emptyNodeHashes[i]
else:
result[i] = proof[pos]
inc pos
# verifyCompactProof verifies a compacted Merkle proof.
proc verifyCompactProofAux*(proof: seq[ByteRange], root, key, value: ByteRange): bool =
var decompactedProof = decompactProof(proof)
if decompactedProof.len == 0: return false
verifyProofAux(decompactedProof, root, key, value)
template verifyCompactProof*(proof: seq[ByteRange], root, key, value: distinct BytesContainer): bool =
verifyCompactProofAux(proof, root.toRange, key.toRange, value.toRange)