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/[sequtils, os, algorithm, random],
|
|
|
|
unittest2,
|
2021-04-06 11:33:24 +00:00
|
|
|
stew/byteutils, nimcrypto/utils,
|
|
|
|
../../eth/trie/[hexary, db, trie_defs],
|
|
|
|
./testutils
|
2019-02-05 12:01:10 +00:00
|
|
|
|
2019-10-29 11:45:57 +00:00
|
|
|
from strutils import split
|
|
|
|
|
2019-02-05 12:01:10 +00:00
|
|
|
suite "hexary trie":
|
|
|
|
setup:
|
|
|
|
var
|
|
|
|
db = newMemoryDB()
|
2020-04-20 18:14:39 +00:00
|
|
|
tr {.used.} = initHexaryTrie(db)
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
test "ref-counted keys crash":
|
|
|
|
proc addKey(intKey: int) =
|
|
|
|
var key = newSeqWith(20, 0.byte)
|
|
|
|
key[19] = byte(intKey)
|
|
|
|
var data = newSeqWith(29, 1.byte)
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
var k = key
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
let v = tr.get(k)
|
|
|
|
doAssert(v.len == 0)
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
tr.put(k, data)
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
addKey(166)
|
|
|
|
addKey(193)
|
|
|
|
addKey(7)
|
|
|
|
addKey(101)
|
|
|
|
addKey(159)
|
|
|
|
addKey(187)
|
|
|
|
addKey(206)
|
|
|
|
addKey(242)
|
|
|
|
addKey(94)
|
|
|
|
addKey(171)
|
|
|
|
addKey(14)
|
|
|
|
addKey(143)
|
|
|
|
addKey(237)
|
|
|
|
addKey(148)
|
|
|
|
addKey(181)
|
|
|
|
addKey(147)
|
|
|
|
addKey(45)
|
|
|
|
addKey(81)
|
|
|
|
addKey(77)
|
|
|
|
addKey(123)
|
|
|
|
addKey(35)
|
|
|
|
addKey(24)
|
|
|
|
addKey(188)
|
|
|
|
addKey(136)
|
|
|
|
|
|
|
|
|
|
|
|
const genesisAccounts = "tests/cases/mainnet-genesis-accounts.txt"
|
|
|
|
if fileExists(genesisAccounts):
|
|
|
|
# This test is optional because it takes a while to run and the
|
|
|
|
# verification is already being part of Nimbus (see genesis.nim).
|
|
|
|
#
|
|
|
|
# On the other hand, it's useful to be able to debug just the trie
|
|
|
|
# code if problems arise. You can download the genesis-accounts file
|
|
|
|
# using the the following command at the root at the repo:
|
|
|
|
#
|
|
|
|
# wget https://gist.github.com/zah/f3a7d325a71d35df3c2606af05d30910/raw/d8bf8fed3d2760f0054cebf4de254a0564a87322/mainnet-genesis-accounts.txt -P tests/cases
|
|
|
|
test "genesis hash":
|
|
|
|
for line in lines(genesisAccounts):
|
|
|
|
var parts = line.split(" ")
|
|
|
|
var
|
|
|
|
key = fromHex(parts[0])
|
|
|
|
val = fromHex(parts[1])
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
SecureHexaryTrie(tr).put(key, val)
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
check tr.rootHashHex == "D7F8974FB5AC78D9AC099B9AD5018BEDC2CE0A72DAD1827A1709DA30580F0544"
|
|
|
|
|
|
|
|
# lexicographic comparison
|
2020-04-20 18:14:39 +00:00
|
|
|
proc lexComp(a, b: seq[byte]): bool =
|
2019-02-05 12:01:10 +00:00
|
|
|
var
|
|
|
|
x = 0
|
|
|
|
y = 0
|
|
|
|
xlen = a.len
|
|
|
|
ylen = b.len
|
|
|
|
|
|
|
|
while x != xlen:
|
|
|
|
if y == ylen or b[y] < a[x]: return false
|
|
|
|
elif a[x] < b[y]: return true
|
|
|
|
inc x
|
|
|
|
inc y
|
|
|
|
|
|
|
|
result = y != ylen
|
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc cmp(a, b: seq[byte]): int =
|
2019-02-05 12:01:10 +00:00
|
|
|
if a == b: return 0
|
|
|
|
if a.lexComp(b): return 1
|
|
|
|
return -1
|
|
|
|
|
|
|
|
test "get values and keys":
|
|
|
|
randomize()
|
|
|
|
var
|
|
|
|
memdb = newMemoryDB()
|
|
|
|
trie = initHexaryTrie(memdb)
|
|
|
|
keys = [
|
2020-04-20 18:14:39 +00:00
|
|
|
"key".toBytes,
|
|
|
|
"abc".toBytes,
|
|
|
|
"hola".toBytes,
|
|
|
|
"bubble".toBytes
|
2019-02-05 12:01:10 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
vals = [
|
2020-04-20 18:14:39 +00:00
|
|
|
"hello".toBytes,
|
|
|
|
"world".toBytes,
|
|
|
|
"block".toBytes,
|
|
|
|
"chain".toBytes
|
2019-02-05 12:01:10 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
for i in 0 ..< keys.len:
|
|
|
|
trie.put(keys[i], vals[i])
|
|
|
|
|
|
|
|
var values = trie.getValues()
|
|
|
|
values.sort(cmp)
|
|
|
|
vals.sort(cmp)
|
|
|
|
check values == vals
|
|
|
|
|
|
|
|
var paths = trie.getKeys()
|
|
|
|
paths.sort(cmp)
|
|
|
|
keys.sort(cmp)
|
|
|
|
check paths == keys
|
|
|
|
|
|
|
|
paths.setLen(0)
|
|
|
|
for k in trie.keys:
|
|
|
|
paths.add(k)
|
|
|
|
paths.sort(cmp)
|
|
|
|
keys.sort(cmp)
|
|
|
|
check paths == keys
|
|
|
|
|
|
|
|
values.setLen(0)
|
|
|
|
paths.setLen(0)
|
|
|
|
for k, v in trie:
|
|
|
|
paths.add k
|
|
|
|
values.add v
|
|
|
|
|
|
|
|
paths.sort(cmp)
|
|
|
|
values.sort(cmp)
|
|
|
|
check paths == keys
|
|
|
|
check values == vals
|
|
|
|
|
|
|
|
test "get values and keys with random data":
|
|
|
|
var
|
|
|
|
memdb = newMemoryDB()
|
|
|
|
trie = initHexaryTrie(memdb)
|
2020-04-20 18:14:39 +00:00
|
|
|
keys = randList(seq[byte], randGen(5, 32), randGen(10))
|
|
|
|
vals = randList(seq[byte], randGen(5, 7), randGen(10))
|
2019-02-05 12:01:10 +00:00
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
keys2 = randList(seq[byte], randGen(5, 30), randGen(15))
|
|
|
|
vals2 = randList(seq[byte], randGen(5, 7), randGen(15))
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
for i in 0 ..< keys.len:
|
|
|
|
trie.put(keys[i], vals[i])
|
|
|
|
|
|
|
|
for i in 0 ..< keys.len:
|
|
|
|
check trie.get(keys[i]) == vals[i]
|
|
|
|
|
|
|
|
var values = trie.getValues()
|
|
|
|
values.sort(cmp)
|
|
|
|
vals.sort(cmp)
|
|
|
|
check values == vals
|
|
|
|
|
|
|
|
let rootHash = trie.rootHash
|
|
|
|
for i in 0 ..< keys2.len:
|
|
|
|
trie.put(keys2[i], vals2[i])
|
|
|
|
var trie2 = initHexaryTrie(memdb, rootHash)
|
|
|
|
|
|
|
|
# because of pruning, equality become uncertain
|
|
|
|
values = trie2.getValues()
|
|
|
|
values.sort(cmp)
|
|
|
|
let
|
|
|
|
cmpResultA = values != vals
|
|
|
|
cmpResultB = values == vals
|
|
|
|
check cmpResultB or cmpResultA
|
|
|
|
|
|
|
|
var values2 = trie.getValues()
|
|
|
|
vals2.add vals
|
|
|
|
values2.sort(cmp)
|
|
|
|
vals2.sort(cmp)
|
|
|
|
check values2 == vals2
|
|
|
|
|
|
|
|
values2.setLen(0)
|
|
|
|
for k in trie.values:
|
|
|
|
values2.add(k)
|
|
|
|
values2.sort(cmp)
|
|
|
|
check values2 == vals2
|
|
|
|
|
|
|
|
var paths = trie.getKeys()
|
|
|
|
paths.sort(cmp)
|
|
|
|
keys2.add keys
|
|
|
|
keys2.sort(cmp)
|
|
|
|
check paths == keys2
|
|
|
|
|
|
|
|
paths.setLen(0)
|
|
|
|
for k in trie.keys:
|
|
|
|
paths.add(k)
|
|
|
|
paths.sort(cmp)
|
|
|
|
check paths == keys2
|
|
|
|
|
|
|
|
values.setLen(0)
|
|
|
|
paths.setLen(0)
|
|
|
|
for k, v in trie:
|
|
|
|
paths.add k
|
|
|
|
values.add v
|
|
|
|
|
|
|
|
paths.sort(cmp)
|
|
|
|
values.sort(cmp)
|
|
|
|
check paths == keys2
|
|
|
|
check values == vals2
|
|
|
|
|
|
|
|
test "non-pruning mode":
|
|
|
|
var
|
|
|
|
memdb = newMemoryDB()
|
|
|
|
nonPruningTrie = initHexaryTrie(memdb, false)
|
2020-04-20 18:14:39 +00:00
|
|
|
keys = randList(seq[byte], randGen(5, 77), randGen(30))
|
|
|
|
vals = randList(seq[byte], randGen(1, 57), randGen(30))
|
2019-02-05 12:01:10 +00:00
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
moreKeys = randList(seq[byte], randGen(5, 33), randGen(45))
|
|
|
|
moreVals = randList(seq[byte], randGen(1, 47), randGen(45))
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
for i in 0 ..< keys.len:
|
|
|
|
nonPruningTrie.put(keys[i], vals[i])
|
|
|
|
|
|
|
|
let rootHash = nonPruningTrie.rootHash
|
|
|
|
for i in 0 ..< moreKeys.len:
|
|
|
|
nonPruningTrie.put(moreKeys[i], moreVals[i])
|
|
|
|
|
|
|
|
var
|
|
|
|
readOnlyTrie = initHexaryTrie(memdb, rootHash)
|
|
|
|
secondaryTrie = initHexaryTrie(memdb, rootHash, false)
|
|
|
|
|
|
|
|
keys.sort(cmp)
|
|
|
|
vals.sort(cmp)
|
|
|
|
|
|
|
|
var
|
|
|
|
roKeys = readOnlyTrie.getKeys()
|
|
|
|
roValues = readOnlyTrie.getValues()
|
|
|
|
scKeys = secondaryTrie.getKeys()
|
|
|
|
scValues = secondaryTrie.getValues()
|
|
|
|
|
|
|
|
roKeys.sort(cmp)
|
|
|
|
roValues.sort(cmp)
|
|
|
|
scKeys.sort(cmp)
|
|
|
|
scValues.sort(cmp)
|
|
|
|
|
|
|
|
check keys == roKeys
|
|
|
|
check vals == roValues
|
|
|
|
check keys == scKeys
|
|
|
|
check vals == scValues
|
|
|
|
|
|
|
|
test "elaborate non-pruning test":
|
|
|
|
type
|
|
|
|
History = object
|
2020-04-20 18:14:39 +00:00
|
|
|
keys: seq[seq[byte]]
|
|
|
|
values: seq[seq[byte]]
|
2024-09-29 08:52:19 +00:00
|
|
|
rootHash: Hash32
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
const
|
|
|
|
listLength = 30
|
|
|
|
numLoop = 100
|
|
|
|
|
|
|
|
for iteration in 0 ..< numLoop:
|
|
|
|
var
|
|
|
|
memdb = newMemoryDB()
|
|
|
|
nonPruningTrie = initHexaryTrie(memdb, false)
|
2020-04-20 18:14:39 +00:00
|
|
|
keys = randList(seq[byte], randGen(3, 33), randGen(listLength))
|
|
|
|
values = randList(seq[byte], randGen(5, 77), randGen(listLength))
|
2019-02-05 12:01:10 +00:00
|
|
|
historyList = newSeq[History](listLength)
|
|
|
|
ok = true
|
|
|
|
|
|
|
|
for i, k in keys:
|
2020-04-20 18:14:39 +00:00
|
|
|
historyList[i].keys = newSeq[seq[byte]](i + 1)
|
|
|
|
historyList[i].values = newSeq[seq[byte]](i + 1)
|
2019-02-05 12:01:10 +00:00
|
|
|
for x in 0 ..< i + 1:
|
|
|
|
historyList[i].keys[x] = keys[x]
|
|
|
|
historyList[i].values[x] = values[x]
|
|
|
|
nonPruningTrie.put(keys[i], values[i])
|
|
|
|
historyList[i].rootHash = nonPruningTrie.rootHash
|
|
|
|
historyList[i].keys.sort(cmp)
|
|
|
|
historyList[i].values.sort(cmp)
|
|
|
|
|
|
|
|
for h in historyList:
|
|
|
|
var
|
|
|
|
trie = initHexaryTrie(memdb, h.rootHash)
|
2020-04-20 18:14:39 +00:00
|
|
|
pKeys: seq[seq[byte]] = @[]
|
2019-02-05 12:01:10 +00:00
|
|
|
pValues = trie.getValues()
|
|
|
|
|
|
|
|
for k in trie.keys:
|
|
|
|
pKeys.add k
|
|
|
|
pKeys.sort(cmp)
|
|
|
|
pValues.sort(cmp)
|
|
|
|
check pKeys.len == h.keys.len
|
|
|
|
check pValues.len == h.values.len
|
|
|
|
check pKeys == h.keys
|
|
|
|
check pValues == h.values
|
|
|
|
|
|
|
|
ok = ok and pKeys.len == h.keys.len
|
|
|
|
ok = ok and pValues.len == h.values.len
|
|
|
|
ok = ok and pKeys == h.keys
|
|
|
|
ok = ok and pValues == h.values
|
|
|
|
if not ok: break
|
|
|
|
|
|
|
|
if not ok:
|
|
|
|
echo "ITERATION: ", iteration
|
|
|
|
break
|
|
|
|
|
|
|
|
test "get branch with pruning trie":
|
|
|
|
var
|
|
|
|
memdb = newMemoryDB()
|
|
|
|
trie = initHexaryTrie(memdb)
|
2020-04-20 18:14:39 +00:00
|
|
|
keys = randList(seq[byte], randGen(5, 77), randGen(30))
|
|
|
|
vals = randList(seq[byte], randGen(1, 57), randGen(30))
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
for i in 0 ..< keys.len:
|
|
|
|
trie.put(keys[i], vals[i])
|
|
|
|
|
|
|
|
for i in 0 ..< keys.len:
|
|
|
|
var branch = trie.getBranch(keys[i])
|
|
|
|
check isValidBranch(branch, trie.rootHash, keys[i], vals[i])
|
|
|
|
|
|
|
|
test "get branch with non pruning trie":
|
|
|
|
const
|
|
|
|
numKeyVal = 30
|
|
|
|
|
|
|
|
var
|
|
|
|
memdb = newMemoryDB()
|
|
|
|
nonPruningTrie = initHexaryTrie(memdb, false)
|
2020-04-20 18:14:39 +00:00
|
|
|
keys = randList(seq[byte], randGen(5, 77), randGen(numKeyVal))
|
|
|
|
vals = randList(seq[byte], randGen(1, 57), randGen(numKeyVal))
|
2024-09-29 08:52:19 +00:00
|
|
|
roots = newSeq[Hash32](numKeyVal)
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
for i in 0 ..< keys.len:
|
|
|
|
nonPruningTrie.put(keys[i], vals[i])
|
|
|
|
roots[i] = nonPruningTrie.rootHash
|
|
|
|
|
|
|
|
for i in 0 ..< keys.len:
|
|
|
|
var trie = initHexaryTrie(memdb, roots[i], false)
|
|
|
|
for x in 0 ..< i+1:
|
|
|
|
var branch = trie.getBranch(keys[x])
|
|
|
|
check isValidBranch(branch, trie.rootHash, keys[x], vals[x])
|
|
|
|
|
|
|
|
test "isPruning getter":
|
|
|
|
var
|
|
|
|
memdb = newMemoryDB()
|
|
|
|
nonPruningTrie = initHexaryTrie(memdb, false)
|
|
|
|
pruningTrie = initHexaryTrie(memdb, true)
|
|
|
|
nonPruningSecureTrie = initSecureHexaryTrie(memdb, false)
|
|
|
|
pruningSecureTrie = initSecureHexaryTrie(memdb, true)
|
|
|
|
|
|
|
|
check nonPruningTrie.isPruning == false
|
|
|
|
check pruningTrie.isPruning == true
|
2019-11-28 10:02:32 +00:00
|
|
|
check nonPruningSecureTrie.isPruning == false
|
|
|
|
check pruningSecureTrie.isPruning == true
|
|
|
|
|
|
|
|
test "multi-roots pruning trie":
|
|
|
|
const
|
|
|
|
numKeyVal = 30
|
|
|
|
|
|
|
|
var
|
|
|
|
memdb = newMemoryDB()
|
|
|
|
pruningTrie = initHexaryTrie(memdb, isPruning = true)
|
|
|
|
|
|
|
|
let
|
2020-04-20 18:14:39 +00:00
|
|
|
keys = randList(seq[byte], randGen(5, 77), randGen(numKeyVal))
|
|
|
|
vals = randList(seq[byte], randGen(1, 57), randGen(numKeyVal))
|
|
|
|
newVals = randList(seq[byte], randGen(1, 63), randGen(numKeyVal))
|
2019-11-28 10:02:32 +00:00
|
|
|
|
|
|
|
var tx1 = memdb.beginTransaction()
|
|
|
|
for i in 0 ..< numKeyVal:
|
|
|
|
pruningTrie.put(keys[i], vals[i])
|
|
|
|
tx1.commit()
|
|
|
|
let rootHash1 = pruningTrie.rootHash
|
|
|
|
|
|
|
|
var tx2 = memdb.beginTransaction()
|
|
|
|
for i in 0 ..< numKeyVal:
|
|
|
|
pruningTrie.put(keys[i], newVals[i])
|
|
|
|
tx2.commit(applyDeletes = false)
|
|
|
|
let rootHash2 = pruningTrie.rootHash
|
|
|
|
|
|
|
|
check rootHash1 != rootHash2
|
|
|
|
|
|
|
|
var trie1 = initHexaryTrie(memdb, rootHash1, isPruning = true)
|
|
|
|
for x in 0 ..< numKeyVal:
|
|
|
|
var branch = trie1.getBranch(keys[x])
|
|
|
|
check isValidBranch(branch, trie1.rootHash, keys[x], vals[x])
|
|
|
|
|
|
|
|
var trie2 = initHexaryTrie(memdb, rootHash2, isPruning = true)
|
|
|
|
for x in 0 ..< numKeyVal:
|
|
|
|
var branch = trie2.getBranch(keys[x])
|
|
|
|
check isValidBranch(branch, trie2.rootHash, keys[x], newVals[x])
|
2019-12-11 07:50:10 +00:00
|
|
|
|
|
|
|
test "replicate iterator":
|
|
|
|
const
|
|
|
|
numKeyVal = 30
|
|
|
|
|
|
|
|
var
|
|
|
|
memdb = newMemoryDB()
|
|
|
|
repdb = newMemoryDB()
|
|
|
|
pruningTrie = initHexaryTrie(memdb, isPruning = true)
|
|
|
|
|
|
|
|
let
|
2020-04-20 18:14:39 +00:00
|
|
|
keys = randList(seq[byte], randGen(5, 77), randGen(numKeyVal))
|
|
|
|
vals = randList(seq[byte], randGen(1, 57), randGen(numKeyVal))
|
2019-12-11 07:50:10 +00:00
|
|
|
|
|
|
|
for i in 0 ..< numKeyVal:
|
|
|
|
pruningTrie.put(keys[i], vals[i])
|
|
|
|
|
|
|
|
let rootHash = pruningTrie.rootHash
|
|
|
|
for k, v in pruningTrie.replicate:
|
2020-04-20 18:14:39 +00:00
|
|
|
repdb.put(k, v)
|
2019-12-11 07:50:10 +00:00
|
|
|
|
|
|
|
var trie = initHexaryTrie(repdb, rootHash, isPruning = true)
|
|
|
|
var numPairs = 0
|
|
|
|
for k, v in trie.pairs:
|
|
|
|
check k in keys
|
|
|
|
check v in vals
|
|
|
|
inc numPairs
|
|
|
|
check numPairs == numKeyVal
|