added tests for RLP encoding and fixed edge cases
This commit is contained in:
parent
75046ccabc
commit
329fe8d85a
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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", ""))
|
Loading…
Reference in New Issue