2020-04-18 08:17:59 +00:00
|
|
|
{.used.}
|
|
|
|
|
2019-02-05 12:01:10 +00:00
|
|
|
import
|
2021-12-11 18:12:55 +00:00
|
|
|
std/random,
|
|
|
|
unittest2,
|
2021-04-06 11:33:24 +00:00
|
|
|
stew/byteutils,
|
|
|
|
../../eth/trie/[db, sparse_binary, sparse_proofs],
|
2019-02-14 22:40:23 +00:00
|
|
|
./testutils
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
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)
|
2020-04-20 18:14:39 +00:00
|
|
|
let y = c.value
|
2019-02-05 12:01:10 +00:00
|
|
|
check x == y
|
|
|
|
trie.del(c.key)
|
|
|
|
|
|
|
|
for c in kv_pairs:
|
|
|
|
check trie.exists(c.key) == false
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
check trie.getRootHash() == keccakHash(emptyNodeHashes[0].data, emptyNodeHashes[0].data).data
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
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
|
2020-04-20 18:14:39 +00:00
|
|
|
if toBytes($i) == trie.get(kv_pairs[i].key):
|
2019-02-05 12:01:10 +00:00
|
|
|
continue
|
|
|
|
# Update
|
2020-04-20 18:14:39 +00:00
|
|
|
trie.set(kv_pairs[i].key, toBytes($i))
|
|
|
|
check trie.get(kv_pairs[i].key) == toBytes($i)
|
2019-02-05 12:01:10 +00:00
|
|
|
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:
|
2020-04-20 18:14:39 +00:00
|
|
|
trie.set(kv_pairs[i].key, toBytes($i))
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
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:
|
2020-04-20 18:14:39 +00:00
|
|
|
trie.set(kv_pairs[i].key, toBytes($i))
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
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]
|
2020-04-20 18:14:39 +00:00
|
|
|
let y = kv_pairs[0].value
|
2019-02-05 12:01:10 +00:00
|
|
|
check x == y
|
|
|
|
check kv_pairs[0].key in trie
|
|
|
|
|
|
|
|
test "get/set for specific root":
|
|
|
|
db = newMemoryDB()
|
|
|
|
trie = initSparseBinaryTrie(db)
|
|
|
|
let
|
2020-04-20 18:14:39 +00:00
|
|
|
testKey = kv_pairs[0].key
|
|
|
|
testValue = kv_pairs[0].value
|
|
|
|
testKey2 = kv_pairs[1].key
|
|
|
|
testValue2 = kv_pairs[1].value
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
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)
|
2019-02-05 12:01:10 +00:00
|
|
|
for i in 0 ..< result.len:
|
2020-04-20 18:14:39 +00:00
|
|
|
result[i] = badProofStr[i]
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
test "proofs":
|
|
|
|
const
|
|
|
|
MaxBadProof = 32 * 8
|
|
|
|
|
|
|
|
let
|
|
|
|
testKey = kv_pairs[0].key
|
|
|
|
badKey = kv_pairs[1].key
|
2020-04-20 18:14:39 +00:00
|
|
|
testValue = "testValue".toBytes
|
|
|
|
testValue2 = "testValue2".toBytes
|
|
|
|
badValue = "badValue".toBytes
|
2019-02-05 12:01:10 +00:00
|
|
|
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
|
2020-04-20 18:14:39 +00:00
|
|
|
defaultValue = default(seq[byte])
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
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
|
2020-04-20 18:14:39 +00:00
|
|
|
var zeroProof: seq[seq[byte]]
|
2019-02-05 12:01:10 +00:00
|
|
|
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
|
2020-04-20 18:14:39 +00:00
|
|
|
key1 = "01234567890123456789".toBytes
|
|
|
|
key2 = "abcdefghijklmnopqrst".toBytes
|
2019-02-05 12:01:10 +00:00
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
trie.set(key1, "value1".toBytes)
|
|
|
|
trie.set(key2, "value2".toBytes)
|
|
|
|
check trie.get(key1) == "value1".toBytes
|
|
|
|
check trie.get(key2) == "value2".toBytes
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
trie.del(key1)
|
2020-04-20 18:14:39 +00:00
|
|
|
check trie.get(key1) == []
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
trie.del(key2)
|
2020-04-20 18:14:39 +00:00
|
|
|
check trie[key2] == []
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
let
|
2020-04-20 18:14:39 +00:00
|
|
|
value1 = "hello world".toBytes
|
|
|
|
badValue = "bad value".toBytes
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
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
|