added tests for RLP encoding and fixed edge cases

This commit is contained in:
Daniel Lamberger 2024-05-17 18:57:25 +03:00
parent 75046ccabc
commit 329fe8d85a
6 changed files with 274 additions and 77 deletions

View File

@ -97,7 +97,7 @@ type
MptExtension* = ref object of MptNode
## An MPT Extension node. Guaranteed to have a non-nil, loaded child branch.
remainderPath*: Nibbles62
remainderPath*: Nibbles
child*: MptBranch
@ -139,11 +139,13 @@ type
func `$`*(buffer: Buffer32): string =
buffer.bytes[0..<buffer.len].toHex
if buffer.len > 0:
result = buffer.bytes[0..<buffer.len].toHex
func toSeq*(buffer: Buffer32): seq[byte] =
buffer.bytes[0..<buffer.len]
if buffer.len > 0:
return buffer.bytes[0..<buffer.len]
func remainderPath*(leaf: MptLeaf, logicalDepth: uint8): Nibbles =
@ -246,7 +248,7 @@ proc printTree*(node: MptNode, stream: Stream, justTopTree: bool) =
elif node of MptExtension:
for nibble in node.MptExtension.remainderPath.enumerate:
stream.write nibble.bitsToHex
for _ in path.len.int + node.MptExtension.remainderPath.len ..< 66:
for _ in path.len + node.MptExtension.remainderPath.len ..< 66:
stream.write " "
stream.write "Extension "
elif node of MptLeaf:
@ -270,7 +272,8 @@ proc printTree*(node: MptNode, stream: Stream, justTopTree: bool) =
if node of MptLeaf:
stream.write " Value: "
stream.writeAsHex node.MptLeaf.value.bytes[0..<node.MptLeaf.value.len]
if node.MptLeaf.value.len > 0:
stream.writeAsHex node.MptLeaf.value.bytes[0..<node.MptLeaf.value.len]
elif node of MptAccount:
stream.write " Balance: "
stream.write node.MptAccount.balance

View File

@ -139,20 +139,6 @@ func `[]=`*(nibbles: var Nibbles62, pos: range[0..61], nibble: range[0..15]) =
else: nibbles.bytes[(pos+1) div 2] = (nibbles.bytes[(pos+1) div 2] and 0xf) or (nibble.byte shl 4)
func slice*(nibbles: Nibbles64, start: range[0..61], length: range[1..62]): Nibbles62 =
##[ Initializes a `Nibbles62` instance from a range within a `Nibbles64` instance, denoted by
`start` and `length`. The nibbles are copied over, not referenced. In case `start` + `length`
exceed 64, a `RangeDefect` exception is raised. ]##
# PERF TODO: This can probably be optimized
if start + length > 64:
raise newException(RangeDefect, "Can't initialize nibbles slice with start=" & $start & " and length=" & $length & "; exceeds 64")
result.bytes[31] = length.uint8
var pos = 0
for i in start.int ..< start + length:
result[pos] = nibbles[i]
inc pos
func slice*(nibbles: Nibbles62, start: range[0..61], length: range[1..62]): Nibbles62 =
##[ Initializes a `Nibbles62` instance from a range within another instance, denoted by `start`
and `length`. The nibbles are copied over, not referenced. In case `start` + `length` exceed
@ -197,14 +183,18 @@ func append*(nibbles: Nibbles, nibble: range[0..15]): Nibbles {.noinit.} =
result[nibbles.len] = nibble
func append*(nibbles: Nibbles, nibbles62: Nibbles62): Nibbles {.noinit.} =
## Returns a clone of `nibbles` with the content of `nibbles62` appended to it.
if nibbles.len + nibbles62.len.uint8 > 64:
raise newException(RangeDefect, "Can't append a nibbles62; exceeds 64")
result = nibbles
for n in nibbles62.enumerate():
inc result.len
result[result.len - 1] = n
func slice*(nibbles: Nibbles64, start: range[0..63], length: range[0..64]): Nibbles =
##[ Initializes a `Nibbles` instance from a range within a `Nibbles64` instance, denoted by
`start` and `length`. The nibbles are copied over, not referenced. In case `start` + `length`
exceed 64, a `RangeDefect` exception is raised. ]##
# PERF TODO: This can probably be optimized
if start + length > 64:
raise newException(RangeDefect, "Can't initialize nibbles slice with start=" & $start & " and length=" & $length & "; exceeds 64")
result.len = length.uint8
var pos = 0
for i in start.int ..< start + length:
result[pos] = nibbles[i]
inc pos
iterator enumerate*(nibbles: Nibbles): uint8 =
@ -220,3 +210,27 @@ func `$`*(nibbles: Nibbles): string =
## Hex encoded nibbles, excluding `0x` prefix
for nibble in nibbles.enumerate():
result.add nibble.bitsToHex
func append*(nibbles: Nibbles, otherNibs: Nibbles): Nibbles {.noinit.} =
## Returns a clone of `nibbles` with the content of `otherNibs` appended to it.
if nibbles.len + otherNibs.len.uint8 > 64:
raise newException(RangeDefect, "Can't append a nibbles62; exceeds 64")
result = nibbles
for n in otherNibs.enumerate():
inc result.len
result[result.len - 1] = n
func slice*(nibbles: Nibbles, start: range[0..63], length: range[0..64]): Nibbles =
##[ Initializes a `Nibbles` instance from a range within another `Nibbles` instance, denoted by
`start` and `length`. The nibbles are copied over, not referenced. In case `start` + `length`
exceed 64, a `RangeDefect` exception is raised. ]##
# PERF TODO: This can probably be optimized
if start + length > 64:
raise newException(RangeDefect, "Can't initialize nibbles slice with start=" & $start & " and length=" & $length & "; exceeds 64")
result.len = length.uint8
var pos = 0
for i in start.int ..< start + length:
result[pos] = nibbles[i]
inc pos

View File

@ -47,7 +47,7 @@ func newAccount(diffHeight: uint64, path: Nibbles64, balance: UInt256,
codeHash: codeHash)
func newExtension(diffHeight: uint64, remainderPath: Nibbles62, child: MptBranch): MptExtension =
func newExtension(diffHeight: uint64, remainderPath: Nibbles, child: MptBranch): MptExtension =
MptExtension(
diffHeight: diffHeight,
hashOrRlp: emptyBuffer32,
@ -244,7 +244,7 @@ method put(ext: MptExtension, diffHeight: uint64, logicalDepth: uint8, key: Nibb
# Check if the key matches the (remainder of) the extension path
var divergence = logicalDepth
var offset = 0
var offset = 0'u8
while offset < ext.remainderPath.len and ext.remainderPath[offset] == key[divergence]:
inc divergence
inc offset
@ -258,7 +258,7 @@ method put(ext: MptExtension, diffHeight: uint64, logicalDepth: uint8, key: Nibb
else: newExtension(diffHeight, ext.remainderPath, ext.child)
# Call put() on the child branch, and update it afterwards in case it was cloned
let res = clone.child.put(diffHeight, logicalDepth + ext.remainderPath.len.uint8, key, value)
let res = clone.child.put(diffHeight, logicalDepth + ext.remainderPath.len, key, value)
clone.child = cast[MptBranch](res.updatedNode)
return (clone, res.missing)

View File

@ -35,19 +35,8 @@ when TraceLogs:
buffer.bytes[0..<buffer.len].toHex
func rlpLegthOfLength(length: int): int {.inline.} =
if length <= 1: 0
elif length <= 55: 1
elif length < 256: 2
elif length < 65536: 3
elif length < 16777216: 4
else: 5
func rlpAppendStringLength(buffer: var Buffer, length: int) {.inline.} =
if length <= 1:
discard
elif length <= 55:
if length <= 55:
buffer.add 128+length.byte
elif length < 256:
buffer.add 184
@ -111,18 +100,28 @@ method computeHashOrRlpIfNeeded(leaf: MptLeaf, logicalDepth: uint8) =
#[ A leaf is encoded as a RLP list with two items: [path, value]. In case the path's length is
even, it's prefixed with 0x20 to discern it from an extension node. In case its length is odd,
it's prefixed with 0x3, to complement to a full byte. Both the path and the value are prefixed
by their length if it's greater than 1, which is enocded as one additional byte. ]#
it's prefixed with 0x3, to complement to a full byte.
PERF TODO: if we know the leaf is going to encode into 31 bytes or less, we could serialize it
straight into node's buffer instead of into the temaporary buffer and then copying it. ]#
let pathPrefixLen =
if logicalDepth >= 63: 0
else: 1
let pathLen = 1 + (64 - logicalDepth.int) div 2
let blobLen = rlpLegthOfLength(pathLen) + pathLen + rlpLegthOfLength(leaf.value.len.int) + leaf.value.len.int
let leafPrefixLen =
if leaf.value.len == 1 and leaf.value.bytes[0] < 128: 0
else: 1
let blobLen = pathPrefixLen + pathLen + leafPrefixLen + leaf.value.len.int
var buffer {.noinit.}: Buffer
buffer.len = 0
buffer.rlpAppendListLength blobLen
buffer.rlpAppendStringLength pathLen
if pathPrefixLen > 0:
buffer.rlpAppendStringLength pathLen
# TODO PERF: copy memory in bulk
# Serialize path
if logicalDepth mod 2 == 0: # Even-length path
buffer.add 0x20
for i in logicalDepth div 2 ..< 32:
@ -132,15 +131,22 @@ method computeHashOrRlpIfNeeded(leaf: MptLeaf, logicalDepth: uint8) =
for i in logicalDepth div 2 + 1 ..< 32:
buffer.add leaf.path.bytes[i]
buffer.rlpAppendStringLength leaf.value.len.int
for i in 0 ..< leaf.value.len.int:
buffer.add leaf.value.bytes[i]
# Serialize value. In case the value is a single ASCII byte, we don't need a length prefix
if leaf.value.len == 1 and leaf.value.bytes[0] < 128:
buffer.add leaf.value.bytes[0]
else:
buffer.rlpAppendStringLength leaf.value.len.int
for i in 0 ..< leaf.value.len.int:
buffer.add leaf.value.bytes[i]
when TraceLogs: echo &"Leaf {$leaf.path} RLP: {$buffer}"
when TraceLogs: echo &"Leaf {$leaf.path} at depth {logicalDepth:2} RLP: {$buffer}"
# Encoded RLP is less than 32 bytes long? Store it as-is
if buffer.len < 32:
leaf.hashOrRlp.bytes[0..<buffer.len] = buffer.bytes[0..<buffer.len]
leaf.hashOrRlp.len = buffer.len.uint8
# Encoded RLP is 32 bytes long or more? Keccak-hash it and store the 32-bytes hash
else:
leaf.hashOrRlp.bytes[0..<32] = keccak256.digest(addr buffer.bytes[0], buffer.len.uint).data
leaf.hashOrRlp.len = 32
@ -161,32 +167,36 @@ method computeHashOrRlpIfNeeded(ext: MptExtension, logicalDepth: uint8) =
# of them are less than that.
ext.child.computeHashOrRlpIfNeeded logicalDepth + ext.remainderPath.len.uint8
let pathPrefixLen =
if ext.remainderPath.len == 1: 0'u8
else: 1
let pathLen = 1 + ext.remainderPath.len div 2
var blobLen = rlpLegthOfLength(pathLen) + pathLen + ext.child.hashOrRlp.len.int
var blobLen = pathPrefixLen + pathLen + ext.child.hashOrRlp.len
if ext.child.hashOrRlp.len == 32: # It's a hash; need a length prefix
inc blobLen
var buffer {.noinit.}: Buffer
buffer.len = 0
buffer.rlpAppendListLength blobLen
buffer.rlpAppendStringLength pathLen
buffer.rlpAppendListLength blobLen.int
if pathPrefixLen > 0:
buffer.rlpAppendStringLength pathLen.int
# TODO PERF: copy memory in bulk
if ext.remainderPath.len mod 2 == 0: # Even-length path
buffer.add 0x00
for i in 0 ..< ext.remainderPath.len div 2:
buffer.add ext.remainderPath.bytes[i]
for i in 0 ..< ext.remainderPath.len.int div 2:
buffer.add (ext.remainderPath[i*2] shl 4 or ext.remainderPath[i*2+1]).byte
else: # odd-length path
buffer.add (0x1 shl 4).byte or ext.remainderPath.bytes[0]
for i in 1 .. ext.remainderPath.len div 2:
buffer.add ext.remainderPath.bytes[i]
buffer.add (0x1 shl 4).byte or ext.remainderPath[0].byte
for i in 0 ..< ext.remainderPath.len.int div 2:
buffer.add (ext.remainderPath[i*2+1] shl 4 or ext.remainderPath[(i+1)*2]).byte
if ext.child.hashOrRlp.len == 32:
buffer.add 0xa0 # RLP code for 32-bytes string
for i in 0 ..< ext.child.hashOrRlp.len.int: # No need for length prefix; included in RLP blob
buffer.add ext.child.hashOrRlp.bytes[i]
when TraceLogs: echo &"Extension {$ext.remainderPath} RLP: {$buffer}"
when TraceLogs: echo &"Extend {$ext.remainderPath:62} at depth {logicalDepth:2} RLP: {$buffer}"
if buffer.len < 32:
ext.hashOrRlp.bytes[0..<buffer.len] = buffer.bytes[0..<buffer.len]
@ -230,7 +240,7 @@ method computeHashOrRlpIfNeeded(branch: MptBranch, logicalDepth: uint8) =
else: buffer.add 0x80 # empty string
buffer.add 0x80 # empty string
when TraceLogs: echo &"Branch RLP: {$buffer}"
when TraceLogs: echo &"Branch at depth {logicalDepth:2} RLP: {$buffer}"
if buffer.len < 32:
branch.hashOrRlp.bytes[0..<buffer.len] = buffer.bytes[0..<buffer.len]

View File

@ -84,8 +84,8 @@ suite "Legacy compatibility":
test "Populate trees and compare":
const sampleKvps = @[
("20001ab975821a408aa3fabe8132f5915cd05054652f879f0aedf0c573dfb33", "2d9e782d37eec375ab9950fbdd1e9a9b983bbfe71ceb4b073411a3c821927f07"),
("ed812738fb4aec6f6b8db0b372e5f8039aa85d47fe2845edb219301acec34ad", "74e931d10d7e1b1ca9f0811cdf80999254971c029981ceaebde2924a6f97a17c"),
("20001ab975821a408aa3fabe8132f5915cd05054652f879f0aedf0c573dfb336", "2d9e782d37eec375ab9950fbdd1e9a9b983bbfe71ceb4b073411a3c821927f07"),
("ed812738fb4aec6f6b8db0b372e5f8039aa85d47fe2845edb219301acec34ada", "74e931d10d7e1b1ca9f0811cdf80999254971c029981ceaebde2924a6f97a17c"),
]
var db = newMemoryDB()
@ -117,10 +117,10 @@ suite "Legacy compatibility":
test "Fuzzing":
const numRuns = 10
const numRuns = 5
const maxBlocksPerRun = 200
const maxNewKeysPerBlock = 200
const maxModifiedOldKeysPerBlock = 50
const maxNewKeysPerBlock = 2000
const maxModifiedOldKeysPerBlock = 500
const oldChainsRatio = 0.1 # 0.1 The probability to base a block on top of another block that's earlier than the last one (0.0 - 1.0)
@ -163,7 +163,7 @@ suite "Legacy compatibility":
# Add some random number of random key-values to that "block"
for _ in 0 ..< 1 + randomGenerator.rand(maxNewKeysPerBlock-1):
let key = makeRandomBytes32()
let randomLength = 32 #1 + randomGenerator.rand(31)
let randomLength = 1 + randomGenerator.rand(31)
let value = makeRandomBytes32()[0..<randomLength]
state.newKvps[key] = value
state.newKeys.add key
@ -175,7 +175,7 @@ suite "Legacy compatibility":
for _ in 0 ..< randomGenerator.rand(maxModifiedOldKeysPerBlock):
let randomBlock = blocks[randomGenerator.rand(blocks.len-1)]
let oldKey = randomBlock.newKeys[randomGenerator.rand(randomBlock.newKeys.len-1)]
let randomLength = 32 #1 + randomGenerator.rand(31)
let randomLength = 1 + randomGenerator.rand(31)
let value = makeRandomBytes32()[0..<randomLength]
state.modifiedKvps[oldKey] = value
state.legacyTrie[].put(oldKey, value)
@ -183,14 +183,20 @@ suite "Legacy compatibility":
# Compare the hashes of legacy and DiffLayer
if state.legacyTrie[].rootHash.data != state.tree.rootHash:
var hashes: Table[string, bool]
echo &"Error at run iteration #{runIteration}, block #{blockNumber}"
echo &"Legacy root hash: {state.legacyTrie[].rootHash.data.toHex}"
echo "\nDumping kvps in Legacy DB"
for kvp in db.pairsInMemoryDB():
if kvp[0][0..^1] != emptyRlpHash[0..^1]:
echo &"{kvp[0].toHex} => {kvp[1].toHex}"
hashes[kvp[0].toHex] = true
echo "\nDumping tree:\n"
state.tree.root.printTree(newFileStream(stdout), false)
for node, _, _ in state.tree.root.enumerateTree(false):
let hash = node.hashOrRlp.bytes[0..<node.hashOrRlp.len].toHex
if not hashes.hasKey(hash):
echo "Hash in tree not found in Legacy: " & hash
doAssert false
state.legacyTrieHash = state.legacyTrie[].rootHash
@ -198,17 +204,17 @@ suite "Legacy compatibility":
# Print state
when false:
echo &"\n\nRun iteration #{runIteration}, block #{blockNumber}. Tree:\n"
echo &"\n\nRun iteration #{runIteration}, block #{blockNumber}, height {state.tree.diffHeight}. Tree:\n"
state.tree.root.printTree(newFileStream(stdout), justTopTree=false)
echo &"\n\nRun iteration #{runIteration}, block #{blockNumber}. Top tree:\n"
echo &"\n\nRun iteration #{runIteration}, block #{blockNumber}, height {state.tree.diffHeight}. Top tree:\n"
state.tree.root.printTree(newFileStream(stdout), justTopTree=true)
echo &"\n\nRun iteration #{runIteration}, block #{blockNumber}. Expected new key-values:\n"
echo &"\n\nRun iteration #{runIteration}, block #{blockNumber}, height {state.tree.diffHeight}. Expected new key-values:\n"
for key, value in state.newKvps:
echo &"{key.toHex} --> {value.toHex}"
echo &"\n\nRun iteration #{runIteration}, block #{blockNumber}. Found key-values in top tree:\n"
echo &"\n\nRun iteration #{runIteration}, block #{blockNumber}, height {state.tree.diffHeight}. Found key-values in top tree:\n"
for node, path, _ in state.tree.root.enumerateTree(justTopTree = true):
if node of MptLeaf:
echo &"{node.MptLeaf.path.bytes.toHex} --> {$node.MptLeaf.value}"
@ -218,11 +224,14 @@ suite "Legacy compatibility":
for key, value in state.newKvps:
let (leaf, _) = state.tree.tryGet Nibbles64(bytes: key)
if leaf == nil:
echo &"\nRun iteration #{runIteration}, block #{blockNumber}. Key not found: {key.toHex}"
echo &"\nRun iteration #{runIteration}, block #{blockNumber}, height {state.tree.diffHeight}. New key not found: {key.toHex}"
check leaf != nil
elif leaf.value.toSeq != value:
echo &"\nRun iteration #{runIteration}, block #{blockNumber}. Unexpeced key-value: {key.toHex} --> {value.toHex}"
echo &"\nRun iteration #{runIteration}, block #{blockNumber}, height {state.tree.diffHeight}. Unexpeced new key-value: {key.toHex} --> {value.toHex}"
check leaf.value.toSeq == value
elif leaf.diffHeight != state.tree.diffHeight:
echo &"\nRun iteration #{runIteration}, block #{blockNumber}. Key has wrong diff height: {key.toHex}"
echo &"\nRun iteration #{runIteration}, block #{blockNumber}, height {state.tree.diffHeight}. New key has wrong diff height: {key.toHex}. Got: {leaf.diffHeight}, expected: {state.tree.diffHeight}"
check leaf.diffHeight == state.tree.diffHeight
check leaf != nil and leaf.value.toSeq == value
# Verify the modified key-values in all blocks in that run
@ -230,11 +239,14 @@ suite "Legacy compatibility":
for key, value in state.modifiedKvps:
let (leaf, _) = state.tree.tryGet Nibbles64(bytes: key)
if leaf == nil:
echo &"\nRun iteration #{runIteration}, block #{blockNumber}. Key not found: {key.toHex}"
echo &"\nRun iteration #{runIteration}, block #{blockNumber}, height {state.tree.diffHeight}. Modified key not found: {key.toHex}"
check leaf != nil
elif leaf.value.toSeq != value:
echo &"\nRun iteration #{runIteration}, block #{blockNumber}. Unexpeced key-value: {key.toHex} --> {value.toHex}"
echo &"\nRun iteration #{runIteration}, block #{blockNumber}, height {state.tree.diffHeight}. Unexpeced modified key-value: {key.toHex} --> {value.toHex}"
check leaf.value.toSeq == value
elif leaf.diffHeight != state.tree.diffHeight:
echo &"\nRun iteration #{runIteration}, block #{blockNumber}. Key has wrong diff height: {key.toHex}"
echo &"\nRun iteration #{runIteration}, block #{blockNumber}, height {state.tree.diffHeight}. Modified key has wrong diff height: {key.toHex}. Got: {leaf.diffHeight}, expected: {state.tree.diffHeight}"
check leaf.diffHeight == state.tree.diffHeight
check leaf != nil and leaf.value.toSeq == value
# Nullify the hashes of all nodes, recompute them in random blocks order and compare them again with legacy hashes

View File

@ -0,0 +1,158 @@
#[ Nimbus
Copyright (c) 2021-2024 Status Research & Development GmbH
Licensed and distributed under either of
* MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
* Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
at your option. This file may not be copied, modified, or distributed except according to those terms. ]#
import
std/[streams, strformat, os, random, times, tables],
stint,
unittest2,
nimcrypto/hash,
../../../vendor/nim-eth/eth/trie/hexary,
../../../vendor/nim-eth/eth/trie/db,
../../../vendor/nim-eth/eth/common/eth_hash,
../[mpt, mpt_rlp_hash, mpt_nibbles, mpt_operations, utils, config]
from ../../../vendor/nimcrypto/nimcrypto/utils import fromHex
proc compareWithLegacy(kvps: varargs[tuple[key:string, value:string]]) =
var diff = DiffLayer()
var db = newMemoryDB()
var trie = initHexaryTrie(db)
for kvp in kvps:
when TraceLogs:
echo &"Adding {kvp.key} => '{kvp.value}'"
let key = hexToBytesArray[32](kvp.key)
if kvp.value.len mod 2 == 1:
raise newException(ValueError, "odd length hex string")
let value = kvp.value.fromHex
discard diff.put(Nibbles64(bytes: key), value.toBuffer32)
trie.put(key, value)
when TraceLogs:
for kvp in db.pairsInMemoryDB():
if kvp[0][0..^1] != emptyRlpHash[0..^1]:
echo &"Legacy {kvp[0].toHex} => {kvp[1].toHex}"
discard diff.rootHash
echo "Tree:"
diff.root.printTree(newFileStream(stdout), justTopTree=false)
check diff.rootHash == trie.rootHash.data
suite "Merkle hashes":
let emptyValue = hexToBytesArray[32]("0000000000000000000000000000000000000000000000000000000000000000").toBuffer32
func makeKey(hex: string): Nibbles64 =
Nibbles64(bytes: hexToBytesArray[32](hex))
echo ""
test "Leaf":
when TraceLogs: echo "\nLeaf with empty value:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000000", ""))
when TraceLogs: echo "\nLeaf with 1-byte ASCII value:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000000", "12"))
when TraceLogs: echo "\nLeaf with 1-byte non-ASCII value:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000000", "ff"))
when TraceLogs: echo "\nLeaf with 2-bytes value:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000000", "1234"))
when TraceLogs: echo "\nLeaf with 32-bytes value:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000000", "0123456789abcdeffedcba9876543210ffeeddccbbaa99887766554433221100"))
when TraceLogs: echo "\nLeaves with path length 63:"
compareWithLegacy(("1000000000000000000000000000000000000000000000000000000000000000", "11"),
("2000000000000000000000000000000000000000000000000000000000000000", "22"))
when TraceLogs: echo "\nLeaves with path length 62:"
compareWithLegacy(("0100000000000000000000000000000000000000000000000000000000000000", "11"),
("0200000000000000000000000000000000000000000000000000000000000000", "22"))
when TraceLogs: echo "\nLeaves with path length 2:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000000", ""),
("0000000000000000000000000000000000000000000000000000000000000100", "12"))
when TraceLogs: echo "\nLeaves with path length 1:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000001", ""),
("0000000000000000000000000000000000000000000000000000000000000023", "12"))
when TraceLogs: echo "\nLeaves with empty path:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000001", ""),
("0000000000000000000000000000000000000000000000000000000000000002", "12"))
test "Extension":
when TraceLogs: echo "\nExtension with length 63, ascii prefix:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000001", ""),
("0000000000000000000000000000000000000000000000000000000000000002", ""))
when TraceLogs: echo "\nExtension with length 63, non-ascii prefix:"
compareWithLegacy(("f000000000000000000000000000000000000000000000000000000000000001", ""),
("f000000000000000000000000000000000000000000000000000000000000002", ""))
when TraceLogs: echo "\nExtension with length 62, ascii prefix:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000010", ""),
("0000000000000000000000000000000000000000000000000000000000000020", ""))
when TraceLogs: echo "\nExtension with length 62, non-ascii prefix:"
compareWithLegacy(("f000000000000000000000000000000000000000000000000000000000000010", ""),
("f000000000000000000000000000000000000000000000000000000000000020", ""))
when TraceLogs: echo "\nExtension with length 61, ascii prefix:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000000100", ""),
("0000000000000000000000000000000000000000000000000000000000000200", ""))
when TraceLogs: echo "\nExtension with length 61, non-ascii prefix:"
compareWithLegacy(("f000000000000000000000000000000000000000000000000000000000000100", ""),
("f000000000000000000000000000000000000000000000000000000000000200", ""))
when TraceLogs: echo "\nExtension with length 60, ascii prefix:"
compareWithLegacy(("0000000000000000000000000000000000000000000000000000000000001000", ""),
("0000000000000000000000000000000000000000000000000000000000002000", ""))
when TraceLogs: echo "\nExtension with length 60, non-ascii prefix:"
compareWithLegacy(("f000000000000000000000000000000000000000000000000000000000001000", ""),
("f000000000000000000000000000000000000000000000000000000000002000", ""))
when TraceLogs: echo "\nExtension with length 4, ascii prefix:"
compareWithLegacy(("0000100000000000000000000000000000000000000000000000000000000000", ""),
("0000200000000000000000000000000000000000000000000000000000000000", ""))
when TraceLogs: echo "\nExtension with length 4, non-ascii prefix:"
compareWithLegacy(("f000100000000000000000000000000000000000000000000000000000000000", ""),
("f000200000000000000000000000000000000000000000000000000000000000", ""))
when TraceLogs: echo "\nExtension with length 3, ascii prefix:"
compareWithLegacy(("0001000000000000000000000000000000000000000000000000000000000000", ""),
("0002000000000000000000000000000000000000000000000000000000000000", ""))
when TraceLogs: echo "\nExtension with length 3, non-ascii prefix:"
compareWithLegacy(("f001000000000000000000000000000000000000000000000000000000000000", ""),
("f002000000000000000000000000000000000000000000000000000000000000", ""))
when TraceLogs: echo "\nExtension with length 2, ascii prefix:"
compareWithLegacy(("0010000000000000000000000000000000000000000000000000000000000000", ""),
("0020000000000000000000000000000000000000000000000000000000000000", ""))
when TraceLogs: echo "\nExtension with length 2, non-ascii prefix:"
compareWithLegacy(("f010000000000000000000000000000000000000000000000000000000000000", ""),
("f020000000000000000000000000000000000000000000000000000000000000", ""))
when TraceLogs: echo "\nExtension with length 1, ascii prefix:"
compareWithLegacy(("0100000000000000000000000000000000000000000000000000000000000000", ""),
("0200000000000000000000000000000000000000000000000000000000000000", ""))
when TraceLogs: echo "\nExtension with length 1, non-ascii prefix:"
compareWithLegacy(("f100000000000000000000000000000000000000000000000000000000000000", ""),
("f200000000000000000000000000000000000000000000000000000000000000", ""))
when TraceLogs: echo "\nExtension 810:"
compareWithLegacy(("810a000000000000000000000000000000000000000000000000000000000000", ""),
("810b000000000000000000000000000000000000000000000000000000000000", ""))