binary tries: remove (#684)

not used anywhere
This commit is contained in:
Jacek Sieka 2024-05-26 09:57:59 +02:00 committed by GitHub
parent c482b4c5b6
commit 0addffcc6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 2 additions and 497 deletions

View File

@ -1,6 +1,5 @@
import import
./trie/[hexary, sparse_binary] ./trie/[hexary, trie_defs]
export export
hexary, sparse_binary hexary, trie_defs

View File

@ -1,177 +0,0 @@
import
"."/[trie_bitseq, trie_defs, trie_utils, db, sparse_proofs]
export
trie_utils, trie_bitseq,
sparse_proofs.verifyProof
type
DB = TrieDatabaseRef
SparseBinaryTrie* = object
db: DB
rootHash: seq[byte]
type
# 256 * 2 div 8
DoubleHash = array[64, byte]
proc initDoubleHash(a, b: openArray[byte]): DoubleHash =
doAssert(a.len == 32, $a.len)
doAssert(b.len == 32, $b.len)
result[0..31] = a
result[32..^1] = b
proc initDoubleHash(x: openArray[byte]): DoubleHash =
initDoubleHash(x, x)
proc init*(x: typedesc[SparseBinaryTrie], db: DB): SparseBinaryTrie =
result.db = db
# Initialize an empty tree with one branch
var value = initDoubleHash(emptyNodeHashes[0].data)
result.rootHash = @(keccakHash(value).data)
result.db.put(result.rootHash, value)
for i in 0..<treeHeight - 1:
value = initDoubleHash(emptyNodeHashes[i+1].data)
result.db.put(emptyNodeHashes[i].data, value)
result.db.put(emptyLeafNodeHash.data, [])
proc initSparseBinaryTrie*(db: DB): SparseBinaryTrie =
init(SparseBinaryTrie, db)
proc init*(x: typedesc[SparseBinaryTrie], db: DB,
rootHash: openArray[byte]): SparseBinaryTrie =
checkValidHashZ(rootHash)
result.db = db
result.rootHash = @rootHash
proc initSparseBinaryTrie*(db: DB, rootHash: openArray[byte]): SparseBinaryTrie =
init(SparseBinaryTrie, db, rootHash)
proc getDB*(t: SparseBinaryTrie): auto = t.db
proc getRootHash*(self: SparseBinaryTrie): seq[byte] {.inline.} =
self.rootHash
proc getAux(self: SparseBinaryTrie, path: TrieBitSeq, rootHash: openArray[byte]): seq[byte] =
var nodeHash = @rootHash
for targetBit in path:
let value = self.db.get(nodeHash)
if value.len == 0: return
if targetBit: nodeHash = value[32..^1]
else: nodeHash = value[0..31]
if nodeHash == emptyLeafNodeHash.data:
result = @[]
else:
result = self.db.get(nodeHash)
proc get*(self: SparseBinaryTrie, key: openArray[byte]): seq[byte] =
## gets a key from the tree.
doAssert(key.len == pathByteLen)
let path = bits key
self.getAux(path, self.rootHash)
proc get*(self: SparseBinaryTrie, key, rootHash: openArray[byte]): seq[byte] =
## gets a key from the tree at a specific root.
doAssert(key.len == pathByteLen)
let path = bits key
self.getAux(path, rootHash)
proc hashAndSave*(self: SparseBinaryTrie, node: openArray[byte]): seq[byte] =
result = @(keccakHash(node).data)
self.db.put(result, node)
proc hashAndSave*(self: SparseBinaryTrie, a, b: openArray[byte]): seq[byte] =
let value = initDoubleHash(a, b)
result = @(keccakHash(value).data)
self.db.put(result, value)
proc setAux(self: var SparseBinaryTrie, value: openArray[byte],
path: TrieBitSeq, depth: int, nodeHash: openArray[byte]): seq[byte] =
if depth == treeHeight:
result = self.hashAndSave(value)
else:
let
node = self.db.get(nodeHash)
leftNode = node[0..31]
rightNode = node[32..^1]
if path[depth]:
result = self.hashAndSave(leftNode, self.setAux(value, path, depth+1, rightNode))
else:
result = self.hashAndSave(self.setAux(value, path, depth+1, leftNode), rightNode)
proc set*(self: var SparseBinaryTrie, key, value: openArray[byte]) =
## sets a new value for a key in the tree, returns the new root,
## and sets the new current root of the tree.
doAssert(key.len == pathByteLen)
let path = bits key
self.rootHash = self.setAux(value, path, 0, self.rootHash)
proc set*(self: var SparseBinaryTrie, key, value, rootHash: openArray[byte]): seq[byte] =
## sets a new value for a key in the tree at a specific root,
## and returns the new root.
doAssert(key.len == pathByteLen)
let path = bits key
self.setAux(value, path, 0, rootHash)
template exists*(self: SparseBinaryTrie, key: openArray[byte]): bool =
self.get(key) != []
proc del*(self: var SparseBinaryTrie, key: openArray[byte]) =
## Equals to setting the value to zeroBytesRange
doAssert(key.len == pathByteLen)
self.set(key, [])
# Dictionary API
template `[]`*(self: SparseBinaryTrie, key: openArray[byte]): seq[byte] =
self.get(key)
template `[]=`*(self: var SparseBinaryTrie, key, value: openArray[byte]) =
self.set(key, value)
template contains*(self: SparseBinaryTrie, key: openArray[byte]): bool =
self.exists(key)
proc proveAux(self: SparseBinaryTrie, key, rootHash: openArray[byte], output: var seq[seq[byte]]): bool =
doAssert(key.len == pathByteLen)
var currVal = self.db.get(rootHash)
if currVal.len == 0: return false
let path = bits key
for i, bit in path:
if bit:
# right side
output[i] = currVal[0..31]
currVal = self.db.get(currVal[32..^1])
if currVal.len == 0: return false
else:
output[i] = currVal[32..^1]
currVal = self.db.get(currVal[0..31])
if currVal.len == 0: return false
result = true
# prove generates a Merkle proof for a key.
proc prove*(self: SparseBinaryTrie, key: openArray[byte]): seq[seq[byte]] =
result = newSeq[seq[byte]](treeHeight)
if not self.proveAux(key, self.rootHash, result):
result = @[]
# prove generates a Merkle proof for a key, at a specific root.
proc prove*(self: SparseBinaryTrie, key, rootHash: openArray[byte]): seq[seq[byte]] =
result = newSeq[seq[byte]](treeHeight)
if not self.proveAux(key, rootHash, result):
result = @[]
# proveCompact generates a compacted Merkle proof for a key.
proc proveCompact*(self: SparseBinaryTrie, key: openArray[byte]): seq[seq[byte]] =
var temp = self.prove(key)
temp.compactProof
# proveCompact generates a compacted Merkle proof for a key, at a specific root.
proc proveCompact*(self: SparseBinaryTrie, key, rootHash: openArray[byte]): seq[seq[byte]] =
var temp = self.prove(key, rootHash)
temp.compactProof

View File

@ -1,86 +0,0 @@
import
"."/[trie_bitseq, trie_defs, trie_utils]
const
treeHeight* = 160
pathByteLen* = treeHeight div 8
emptyLeafNodeHash* = blankStringHash
proc makeInitialEmptyTreeHash(H: static[int]): array[H, KeccakHash] =
result[^1] = emptyLeafNodeHash
for i in countdown(H-1, 1):
result[i - 1] = keccakHash(result[i].data, result[i].data)
# cannot yet turn this into compile time constant
let emptyNodeHashes* = makeInitialEmptyTreeHash(treeHeight)
# VerifyProof verifies a Merkle proof.
proc verifyProofAux*(proof: seq[seq[byte]], root, key, value: openArray[byte]): bool =
doAssert(root.len == 32)
doAssert(key.len == pathByteLen)
var
path = bits key
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.data)
else:
curHash = keccakHash(curHash.data, node)
result = curHash.data == root
template verifyProof*(proof: seq[seq[byte]], root, key, value: openArray[byte]): bool =
verifyProofAux(proof, root, key, value)
proc count(b: TrieBitSeq, val: bool): int =
for c in b:
if c == val: inc result
# CompactProof compacts a proof, to reduce its size.
proc compactProof*(proof: seq[seq[byte]]): seq[seq[byte]] =
if proof.len != treeHeight: return
var
data = newSeq[byte](pathByteLen)
bits = bits data
result = @[]
result.add @[]
for i in 0 ..< treeHeight:
var node = proof[i]
if node == emptyNodeHashes[i].data:
bits[i] = true
else:
result.add node
result[0] = bits.toBytes
# decompactProof decompacts a proof, so that it can be used for VerifyProof.
proc decompactProof*(proof: seq[seq[byte]]): seq[seq[byte]] =
if proof.len == 0: return
if proof[0].len != pathByteLen: return
let bits = bits proof[0]
if proof.len != bits.count(false) + 1: return
result = newSeq[seq[byte]](treeHeight)
var pos = 1 # skip bits
for i in 0 ..< treeHeight:
if bits[i]:
result[i] = @(emptyNodeHashes[i].data)
else:
result[i] = proof[pos]
inc pos
# verifyCompactProof verifies a compacted Merkle proof.
proc verifyCompactProofAux*(proof: seq[seq[byte]], root, key, value: openArray[byte]): bool =
var decompactedProof = decompactProof(proof)
if decompactedProof.len == 0: return false
verifyProofAux(decompactedProof, root, key, value)
template verifyCompactProof*(proof: seq[seq[byte]], root, key, value: openArray[byte]): bool =
verifyCompactProofAux(proof, root, key, value)

View File

@ -5,7 +5,6 @@ import
./test_examples, ./test_examples,
./test_hexary_trie, ./test_hexary_trie,
./test_json_suite, ./test_json_suite,
./test_sparse_binary_trie,
./test_transaction_db, ./test_transaction_db,
./test_trie_bitseq, ./test_trie_bitseq,
./test_hexary_proof ./test_hexary_proof

View File

@ -1,230 +0,0 @@
{.used.}
import
std/random,
unittest2,
stew/byteutils,
../../eth/trie/[db, sparse_binary, sparse_proofs],
./testutils
suite "sparse binary trie":
randomize()
var kv_pairs = randKVPair(20)
var numbers = randList(int, randGen(1, 99), randGen(50, 100))
var db = newMemoryDB()
var trie = initSparseBinaryTrie(db)
test "basic set":
for c in kv_pairs:
check trie.exists(c.key) == false
trie.set(c.key, c.value)
let prevRoot = trie.getRootHash()
test "basic get":
for c in kv_pairs:
let x = trie.get(c.key)
let y = c.value
check x == y
trie.del(c.key)
for c in kv_pairs:
check trie.exists(c.key) == false
check trie.getRootHash() == keccakHash(emptyNodeHashes[0].data, emptyNodeHashes[0].data).data
test "single update set":
random.shuffle(kv_pairs)
for c in kv_pairs:
trie.set(c.key, c.value)
# Check trie root remains the same even in different insert order
check trie.getRootHash() == prevRoot
let prior_to_update_root = trie.getRootHash()
test "single update get":
for i in numbers:
# If new value is the same as current value, skip the update
if toBytes($i) == trie.get(kv_pairs[i].key):
continue
# Update
trie.set(kv_pairs[i].key, toBytes($i))
check trie.get(kv_pairs[i].key) == toBytes($i)
check trie.getRootHash() != prior_to_update_root
# Un-update
trie.set(kv_pairs[i].key, kv_pairs[i].value)
check trie.getRootHash == prior_to_update_root
test "batch update with different update order":
# First batch update
for i in numbers:
trie.set(kv_pairs[i].key, toBytes($i))
let batch_updated_root = trie.getRootHash()
# Un-update
random.shuffle(numbers)
for i in numbers:
trie.set(kv_pairs[i].key, kv_pairs[i].value)
check trie.getRootHash() == prior_to_update_root
# Second batch update
random.shuffle(numbers)
for i in numbers:
trie.set(kv_pairs[i].key, toBytes($i))
check trie.getRootHash() == batch_updated_root
test "dictionary API":
trie[kv_pairs[0].key] = kv_pairs[0].value
let x = trie[kv_pairs[0].key]
let y = kv_pairs[0].value
check x == y
check kv_pairs[0].key in trie
test "get/set for specific root":
db = newMemoryDB()
trie = initSparseBinaryTrie(db)
let
testKey = kv_pairs[0].key
testValue = kv_pairs[0].value
testKey2 = kv_pairs[1].key
testValue2 = kv_pairs[1].value
trie.set(testKey, testValue)
var root = trie.getRootHash()
var value = trie.get(testKey, root)
check value == testValue
root = trie.set(testKey2, testValue2, root)
value = trie.get(testKey2, root)
check value == testValue2
value = trie.get(testKey, root)
check value == testValue
proc makeBadProof(size: int, width = 32): seq[seq[byte]] =
let badProofStr = randList(seq[byte], randGen(width, width), randGen(size, size))
result = newSeq[seq[byte]](size)
for i in 0 ..< result.len:
result[i] = badProofStr[i]
test "proofs":
const
MaxBadProof = 32 * 8
let
testKey = kv_pairs[0].key
badKey = kv_pairs[1].key
testValue = "testValue".toBytes
testValue2 = "testValue2".toBytes
badValue = "badValue".toBytes
badProof = makeBadProof(MaxBadProof)
trie[testKey] = testValue
var proof = trie.prove(testKey)
check proof.len == treeHeight
check verifyProof(proof, trie.getRootHash(), testKey, testValue) == true
check verifyProof(proof, trie.getRootHash(), testKey, badValue) == false
check verifyProof(proof, trie.getRootHash(), badKey, testValue) == false
check verifyProof(badProof, trie.getRootHash(), testKey, testValue) == false
let
testKey2 = kv_pairs[2].key
testKey3 = kv_pairs[3].key
defaultValue = default(seq[byte])
trie.set(testKey2, testValue)
proof = trie.prove(testKey)
check verifyProof(proof, trie.getRootHash(), testKey, testValue) == true
check verifyProof(proof, trie.getRootHash(), testKey, badValue) == false
check verifyProof(proof, trie.getRootHash(), testKey2, testValue) == false
check verifyProof(badProof, trie.getRootHash(), testKey, testValue) == false
proof = trie.prove(testKey2)
check verifyProof(proof, trie.getRootHash(), testKey2, testValue) == true
check verifyProof(proof, trie.getRootHash(), testKey2, badValue) == false
check verifyProof(proof, trie.getRootHash(), testKey3, testValue) == false
check verifyProof(badProof, trie.getRootHash(), testKey, testValue) == false
var compactProof = compactProof(proof)
var decompactedProof = decompactProof(compactProof)
check decompactedProof.len == proof.len
for i, c in proof:
check decompactedProof[i] == c
let
badProof2 = makeBadProof(MaxBadProof + 1)
badProof3 = makeBadProof(MaxBadProof - 1)
badProof4 = makeBadProof(MaxBadProof, 31)
badProof5 = makeBadProof(MaxBadProof, 33)
badProof6 = makeBadProof(MaxBadProof, 1)
check verifyProof(badProof2, trie.getRootHash(), testKey3, defaultValue) == false
check verifyProof(badProof3, trie.getRootHash(), testKey3, defaultValue) == false
check verifyProof(badProof4, trie.getRootHash(), testKey3, defaultValue) == false
check verifyProof(badProof5, trie.getRootHash(), testKey3, defaultValue) == false
check verifyProof(badProof6, trie.getRootHash(), testKey3, defaultValue) == false
check compactProof(badProof2).len == 0
check compactProof(badProof3).len == 0
check decompactProof(badProof3).len == 0
var zeroProof: seq[seq[byte]]
check decompactProof(zeroProof).len == 0
proof = trie.proveCompact(testKey2)
check verifyCompactProof(proof, trie.getRootHash(), testKey2, testValue) == true
check verifyCompactProof(proof, trie.getRootHash(), testKey2, badValue) == false
check verifyCompactProof(proof, trie.getRootHash(), testKey3, testValue) == false
check verifyCompactProof(badProof, trie.getRootHash(), testKey, testValue) == false
var root = trie.getRootHash()
trie.set(testKey2, testValue2)
proof = trie.proveCompact(testKey2, root)
check verifyCompactProof(proof, root, testKey2, testValue) == true
check verifyCompactProof(proof, root, testKey2, badValue) == false
check verifyCompactProof(proof, root, testKey3, testValue) == false
check verifyCompactProof(badProof, root, testKey, testValue) == false
proof = trie.prove(testKey2, root)
check verifyProof(proof, root, testKey2, testValue) == true
check verifyProof(proof, root, testKey2, badValue) == false
check verifyProof(proof, root, testKey3, testValue) == false
check verifyProof(badProof, root, testKey, testValue) == false
proof = trie.prove(testKey3)
check proof.len == 0
check verifyProof(proof, trie.getRootHash(), testKey3, defaultValue) == false
check verifyProof(proof, trie.getRootHash(), testKey3, badValue) == false
check verifyProof(proof, trie.getRootHash(), testKey2, defaultValue) == false
check verifyProof(badProof, trie.getRootHash(), testKey, testValue) == false
test "examples":
let
key1 = "01234567890123456789".toBytes
key2 = "abcdefghijklmnopqrst".toBytes
trie.set(key1, "value1".toBytes)
trie.set(key2, "value2".toBytes)
check trie.get(key1) == "value1".toBytes
check trie.get(key2) == "value2".toBytes
trie.del(key1)
check trie.get(key1) == []
trie.del(key2)
check trie[key2] == []
let
value1 = "hello world".toBytes
badValue = "bad value".toBytes
trie[key1] = value1
var proof = trie.prove(key1)
check verifyProof(proof, trie.getRootHash(), key1, value1) == true
check verifyProof(proof, trie.getRootHash(), key1, badValue) == false
check verifyProof(proof, trie.getRootHash(), key2, value1) == false