2023-11-01 03:32:09 +00:00
|
|
|
# Nimbus
|
|
|
|
# Copyright (c) 2020-2023 Status Research & Development GmbH
|
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
|
|
# http://opensource.org/licenses/MIT)
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except
|
|
|
|
# according to those terms.
|
|
|
|
|
2020-04-22 11:04:19 +00:00
|
|
|
import
|
2020-05-24 04:41:20 +00:00
|
|
|
randutils, random, unittest2, stew/byteutils, os,
|
2023-08-04 11:10:09 +00:00
|
|
|
eth/[common, rlp], eth/trie/[trie_defs, nibbles],
|
2020-05-14 04:09:01 +00:00
|
|
|
faststreams/inputs, nimcrypto/sysrand,
|
2020-04-29 05:12:24 +00:00
|
|
|
../stateless/[witness_from_tree, tree_from_witness],
|
2023-08-04 11:10:09 +00:00
|
|
|
../nimbus/db/[core_db, distinct_tries, storage_types], ./witness_types, ./multi_keys
|
2020-04-22 11:04:19 +00:00
|
|
|
|
2020-04-28 09:58:16 +00:00
|
|
|
type
|
2023-08-04 11:10:09 +00:00
|
|
|
DB = CoreDbRef
|
2020-04-28 09:58:16 +00:00
|
|
|
|
2020-05-05 08:05:17 +00:00
|
|
|
StorageKeys = tuple[storageRoot: Hash256, keys: MultikeysRef]
|
2020-05-05 06:50:31 +00:00
|
|
|
|
|
|
|
AccountDef = object
|
|
|
|
storageKeys: MultiKeysRef
|
|
|
|
account: Account
|
2020-05-05 08:05:17 +00:00
|
|
|
codeTouched: bool
|
2020-05-05 06:50:31 +00:00
|
|
|
|
2020-04-28 02:30:10 +00:00
|
|
|
proc randU256(): UInt256 =
|
|
|
|
var bytes: array[32, byte]
|
|
|
|
discard randomBytes(bytes[0].addr, sizeof(result))
|
|
|
|
result = UInt256.fromBytesBE(bytes)
|
|
|
|
|
2020-05-05 06:50:31 +00:00
|
|
|
proc randStorageSlot(): StorageSlot =
|
|
|
|
discard randomBytes(result[0].addr, sizeof(result))
|
|
|
|
|
2020-04-28 02:30:10 +00:00
|
|
|
proc randNonce(): AccountNonce =
|
|
|
|
discard randomBytes(result.addr, sizeof(result))
|
|
|
|
|
2020-04-28 09:58:16 +00:00
|
|
|
proc randCode(db: DB): Hash256 =
|
|
|
|
if rand(0..1) == 0:
|
|
|
|
result = blankStringHash
|
|
|
|
else:
|
|
|
|
let codeLen = rand(1..150)
|
|
|
|
let code = randList(byte, rng(0, 255), codeLen, unique = false)
|
2022-09-03 18:15:35 +00:00
|
|
|
result = keccakHash(code)
|
2023-08-04 11:10:09 +00:00
|
|
|
db.kvt.put(contractHashKey(result).toOpenArray, code)
|
2020-04-28 09:58:16 +00:00
|
|
|
|
2020-05-05 06:50:31 +00:00
|
|
|
proc randStorage(db: DB): StorageKeys =
|
2020-04-28 14:06:37 +00:00
|
|
|
if rand(0..1) == 0:
|
2020-05-05 06:50:31 +00:00
|
|
|
result = (emptyRlpHash, MultikeysRef(nil))
|
2020-04-28 14:06:37 +00:00
|
|
|
else:
|
2023-03-10 18:42:37 +00:00
|
|
|
var trie = initStorageTrie(db)
|
2020-05-05 06:50:31 +00:00
|
|
|
let numPairs = rand(1..10)
|
|
|
|
var keys = newSeq[StorageSlot](numPairs)
|
2020-04-28 14:06:37 +00:00
|
|
|
|
|
|
|
for i in 0..<numPairs:
|
2020-05-05 06:50:31 +00:00
|
|
|
keys[i] = randStorageSlot()
|
2023-03-10 18:42:37 +00:00
|
|
|
trie.putSlotBytes(keys[i], rlp.encode(randU256()))
|
2020-05-05 06:50:31 +00:00
|
|
|
|
|
|
|
if rand(0..1) == 0:
|
|
|
|
result = (trie.rootHash, MultikeysRef(nil))
|
|
|
|
else:
|
|
|
|
var m = newMultikeys(keys)
|
|
|
|
result = (trie.rootHash, m)
|
2020-04-28 02:30:10 +00:00
|
|
|
|
2020-05-05 06:50:31 +00:00
|
|
|
proc randAccount(db: DB): AccountDef =
|
|
|
|
result.account.nonce = randNonce()
|
|
|
|
result.account.balance = randU256()
|
2020-05-05 08:05:17 +00:00
|
|
|
let z = randStorage(db)
|
2020-05-05 06:50:31 +00:00
|
|
|
result.account.codeHash = randCode(db)
|
2020-05-05 08:05:17 +00:00
|
|
|
result.account.storageRoot = z.storageRoot
|
|
|
|
result.storageKeys = z.keys
|
|
|
|
result.codeTouched = rand(0..1) == 0
|
2020-04-28 02:30:10 +00:00
|
|
|
|
2020-04-29 04:12:15 +00:00
|
|
|
proc randAddress(): EthAddress =
|
|
|
|
discard randomBytes(result.addr, sizeof(result))
|
|
|
|
|
2020-06-02 04:24:41 +00:00
|
|
|
proc runTest(numPairs: int, testStatusIMPL: var TestStatus,
|
|
|
|
addIdenticalKeys: bool = false, addInvalidKeys: static[bool] = false) =
|
|
|
|
|
2023-08-04 11:10:09 +00:00
|
|
|
var memDB = newCoreDbRef(LegacyDbMemory)
|
2023-03-10 18:42:37 +00:00
|
|
|
var trie = initAccountsTrie(memDB)
|
2020-05-05 06:50:31 +00:00
|
|
|
var addrs = newSeq[AccountKey](numPairs)
|
2020-05-03 01:11:04 +00:00
|
|
|
var accs = newSeq[Account](numPairs)
|
2020-04-22 11:04:19 +00:00
|
|
|
|
|
|
|
for i in 0..<numPairs:
|
2020-05-05 06:50:31 +00:00
|
|
|
let acc = randAccount(memDB)
|
2020-07-08 16:17:48 +00:00
|
|
|
addrs[i] = AccountKey(address: randAddress(), codeTouched: acc.codeTouched, storageKeys: acc.storageKeys)
|
2020-05-05 06:50:31 +00:00
|
|
|
accs[i] = acc.account
|
2023-03-10 18:42:37 +00:00
|
|
|
trie.putAccountBytes(addrs[i].address, rlp.encode(accs[i]))
|
2020-04-22 11:04:19 +00:00
|
|
|
|
2020-05-06 03:36:00 +00:00
|
|
|
when addInvalidKeys:
|
|
|
|
# invalidAddress should not end up in block witness
|
|
|
|
let invalidAddress = randAddress()
|
2020-07-08 16:17:48 +00:00
|
|
|
addrs.add(AccountKey(address: invalidAddress))
|
2020-05-05 08:05:17 +00:00
|
|
|
|
2020-06-02 04:24:41 +00:00
|
|
|
if addIdenticalKeys:
|
|
|
|
let invalidAddress = addrs[0].address
|
2020-07-08 16:17:48 +00:00
|
|
|
addrs.add(AccountKey(address: invalidAddress))
|
2020-06-02 04:24:41 +00:00
|
|
|
|
2020-05-05 06:50:31 +00:00
|
|
|
var mkeys = newMultiKeys(addrs)
|
2020-04-22 11:04:19 +00:00
|
|
|
let rootHash = trie.rootHash
|
|
|
|
|
2020-05-05 06:50:31 +00:00
|
|
|
var wb = initWitnessBuilder(memDB, rootHash, {wfEIP170})
|
|
|
|
var witness = wb.buildWitness(mkeys)
|
2023-08-04 11:10:09 +00:00
|
|
|
var db = newCoreDbRef(LegacyDbMemory)
|
2020-05-05 06:50:31 +00:00
|
|
|
when defined(useInputStream):
|
|
|
|
var input = memoryInput(witness)
|
|
|
|
var tb = initTreeBuilder(input, db, {wfEIP170})
|
|
|
|
else:
|
|
|
|
var tb = initTreeBuilder(witness, db, {wfEIP170})
|
|
|
|
let root = tb.buildTree()
|
|
|
|
check root.data == rootHash.data
|
2020-05-03 01:11:04 +00:00
|
|
|
|
2023-03-10 18:42:37 +00:00
|
|
|
let newTrie = initAccountsTrie(tb.getDB(), root)
|
2020-05-05 06:50:31 +00:00
|
|
|
for i in 0..<numPairs:
|
2023-03-10 18:42:37 +00:00
|
|
|
let recordFound = newTrie.getAccountBytes(addrs[i].address)
|
2020-05-03 01:11:04 +00:00
|
|
|
if recordFound.len > 0:
|
|
|
|
let acc = rlp.decode(recordFound, Account)
|
2020-05-05 06:50:31 +00:00
|
|
|
check acc == accs[i]
|
2020-05-03 01:11:04 +00:00
|
|
|
else:
|
2020-05-13 23:58:34 +00:00
|
|
|
debugEcho "BUG IN WITNESS/TREE BUILDER ", i
|
2020-05-05 06:50:31 +00:00
|
|
|
check false
|
2020-04-22 11:04:19 +00:00
|
|
|
|
2020-05-06 03:36:00 +00:00
|
|
|
when addInvalidKeys:
|
2020-05-05 08:05:17 +00:00
|
|
|
for kd in mkeys.keys:
|
2020-05-06 03:36:00 +00:00
|
|
|
if kd.address == invalidAddress:
|
2020-05-07 14:36:51 +00:00
|
|
|
check kd.visited == false
|
2020-05-05 08:05:17 +00:00
|
|
|
else:
|
|
|
|
check kd.visited == true
|
|
|
|
else:
|
|
|
|
for kd in mkeys.keys:
|
|
|
|
check kd.visited == true
|
|
|
|
|
2020-07-08 16:17:48 +00:00
|
|
|
proc initMultiKeys(keys: openArray[string], storageMode: bool = false): MultikeysRef =
|
2020-05-07 14:36:51 +00:00
|
|
|
result.new
|
2020-07-08 16:17:48 +00:00
|
|
|
if storageMode:
|
|
|
|
for i, x in keys:
|
|
|
|
result.keys.add KeyData(
|
|
|
|
storageMode: true,
|
|
|
|
hash: hexToByteArray[32](x)
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
for x in keys:
|
|
|
|
result.keys.add KeyData(
|
|
|
|
storageMode: false,
|
|
|
|
hash: hexToByteArray[32](x)
|
|
|
|
)
|
2020-05-07 14:36:51 +00:00
|
|
|
|
2020-05-24 04:41:20 +00:00
|
|
|
proc parseInvalidInput(payload: openArray[byte]): bool =
|
2023-08-04 11:10:09 +00:00
|
|
|
var db = newCoreDbRef(LegacyDbMemory)
|
2020-05-24 04:41:20 +00:00
|
|
|
try:
|
|
|
|
var tb = initTreeBuilder(payload, db, {wfEIP170})
|
|
|
|
discard tb.buildTree()
|
|
|
|
except ParsingError, ContractCodeError:
|
|
|
|
result = true
|
|
|
|
|
2020-05-06 04:47:33 +00:00
|
|
|
proc witnessKeysMain*() =
|
2020-05-05 06:50:31 +00:00
|
|
|
suite "random keys block witness roundtrip test":
|
|
|
|
randomize()
|
|
|
|
|
|
|
|
test "random multiple keys":
|
|
|
|
for i in 0..<100:
|
|
|
|
runTest(rand(1..30), testStatusIMPL)
|
2020-04-24 08:58:36 +00:00
|
|
|
|
2020-05-05 06:50:31 +00:00
|
|
|
test "there is no short node":
|
|
|
|
let acc = newAccount()
|
|
|
|
let rlpBytes = rlp.encode(acc)
|
|
|
|
check rlpBytes.len > 32
|
2020-04-22 11:04:19 +00:00
|
|
|
|
2020-05-06 03:36:00 +00:00
|
|
|
test "invalid address ignored":
|
2020-06-02 04:24:41 +00:00
|
|
|
runTest(rand(1..30), testStatusIMPL, false, addInvalidKeys = true)
|
|
|
|
|
|
|
|
test "multiple identical addresses rejected":
|
2023-01-31 12:38:08 +00:00
|
|
|
expect AssertionDefect:
|
2020-06-02 04:24:41 +00:00
|
|
|
runTest(rand(5..30), testStatusIMPL, true)
|
2020-05-05 08:05:17 +00:00
|
|
|
|
2020-05-07 14:36:51 +00:00
|
|
|
test "case 1: all keys is a match":
|
|
|
|
let keys = [
|
|
|
|
"0abc7124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
|
|
|
"0abccc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606",
|
|
|
|
"0abca163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6bb"
|
|
|
|
]
|
|
|
|
|
|
|
|
let m = initMultiKeys(keys)
|
|
|
|
let pg = m.initGroup()
|
|
|
|
let n = initNibbleRange(hexToByteArray[2]("0abc"))
|
|
|
|
let mg = m.groups(0, n, pg)
|
|
|
|
check:
|
|
|
|
mg.match == true
|
|
|
|
mg.group.first == 0
|
|
|
|
mg.group.last == 2
|
|
|
|
|
|
|
|
test "case 2: all keys is not a match":
|
|
|
|
let keys = [
|
|
|
|
"01237124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
|
|
|
"0890cc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606",
|
|
|
|
"0456a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6bb"
|
|
|
|
]
|
|
|
|
|
|
|
|
let m = initMultiKeys(keys)
|
|
|
|
let pg = m.initGroup()
|
|
|
|
let n = initNibbleRange(hexToByteArray[2]("0abc"))
|
|
|
|
let mg = m.groups(0, n, pg)
|
|
|
|
check:
|
|
|
|
mg.match == false
|
|
|
|
|
|
|
|
test "case 3: not match and match":
|
|
|
|
let keys = [
|
|
|
|
"01237124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
|
|
|
"0890cc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606",
|
|
|
|
"0abc6a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
|
|
|
"0abc7a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b"
|
|
|
|
]
|
|
|
|
|
|
|
|
let m = initMultiKeys(keys)
|
|
|
|
let pg = m.initGroup()
|
|
|
|
let n = initNibbleRange(hexToByteArray[2]("0abc"))
|
|
|
|
let mg = m.groups(0, n, pg)
|
|
|
|
check:
|
|
|
|
mg.match == true
|
|
|
|
mg.group.first == 2
|
|
|
|
mg.group.last == 3
|
|
|
|
|
|
|
|
test "case 4: match and not match":
|
|
|
|
let keys = [
|
|
|
|
"0abc6a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
|
|
|
"0abc7a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
|
|
|
"01237124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
2020-05-13 23:58:34 +00:00
|
|
|
"0890cc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606"
|
2020-05-07 14:36:51 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
let m = initMultiKeys(keys)
|
|
|
|
let pg = m.initGroup()
|
|
|
|
let n = initNibbleRange(hexToByteArray[2]("0abc"))
|
|
|
|
let mg = m.groups(0, n, pg)
|
|
|
|
check:
|
|
|
|
mg.match == true
|
|
|
|
mg.group.first == 0
|
|
|
|
mg.group.last == 1
|
2020-05-13 23:58:34 +00:00
|
|
|
|
2020-05-07 14:36:51 +00:00
|
|
|
test "case 5: not match, match and not match":
|
|
|
|
let keys = [
|
|
|
|
"01237124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
|
|
|
"0890cc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606",
|
|
|
|
"0abc6a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
|
|
|
"0abc7a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
|
|
|
"01237124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
2020-05-13 23:58:34 +00:00
|
|
|
"0890cc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606"
|
2020-05-07 14:36:51 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
let m = initMultiKeys(keys)
|
|
|
|
let pg = m.initGroup()
|
|
|
|
let n = initNibbleRange(hexToByteArray[2]("0abc"))
|
|
|
|
let mg = m.groups(0, n, pg)
|
|
|
|
check:
|
|
|
|
mg.match == true
|
|
|
|
mg.group.first == 2
|
|
|
|
mg.group.last == 3
|
2020-05-13 23:58:34 +00:00
|
|
|
|
2020-05-24 04:41:20 +00:00
|
|
|
test "parse invalid input":
|
|
|
|
for x in walkDirRec("stateless" / "invalidInput"):
|
|
|
|
let z = readFile(x)
|
|
|
|
check parseInvalidInput(z.toOpenArrayByte(0, z.len-1))
|
|
|
|
|
2020-07-08 16:17:48 +00:00
|
|
|
test "short rlp test":
|
|
|
|
let keys = [
|
|
|
|
"01234567abce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c325",
|
|
|
|
"01234567b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff5660",
|
|
|
|
"01234567c140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
|
|
|
]
|
|
|
|
let m = initMultiKeys(keys, true)
|
2023-08-04 11:10:09 +00:00
|
|
|
var memDB = newCoreDbRef(LegacyDbMemory)
|
2023-03-10 18:42:37 +00:00
|
|
|
var trie = initAccountsTrie(memDB)
|
2020-07-08 16:17:48 +00:00
|
|
|
var acc = randAccount(memDB)
|
|
|
|
|
2023-08-04 11:10:09 +00:00
|
|
|
var tt = memDB.mptPrune
|
2020-07-08 16:17:48 +00:00
|
|
|
for x in m.keys:
|
|
|
|
tt.put(x.hash, rlp.encode(1.u256))
|
|
|
|
acc.account.storageRoot = tt.rootHash
|
|
|
|
|
|
|
|
let addrs = @[AccountKey(address: randAddress(), codeTouched: acc.codeTouched, storageKeys: m)]
|
|
|
|
|
2023-03-10 18:42:37 +00:00
|
|
|
trie.putAccountBytes(addrs[0].address, rlp.encode(acc.account))
|
2020-07-08 16:17:48 +00:00
|
|
|
var mkeys = newMultiKeys(addrs)
|
|
|
|
let rootHash = trie.rootHash
|
|
|
|
|
|
|
|
var wb = initWitnessBuilder(memDB, rootHash, {wfEIP170})
|
|
|
|
var witness = wb.buildWitness(mkeys)
|
2023-08-04 11:10:09 +00:00
|
|
|
var db = newCoreDbRef(LegacyDbMemory)
|
2020-07-08 16:17:48 +00:00
|
|
|
var tb = initTreeBuilder(witness, db, {wfEIP170})
|
|
|
|
let root = tb.buildTree()
|
|
|
|
check root.data == rootHash.data
|
|
|
|
|
2020-05-05 13:46:17 +00:00
|
|
|
when isMainModule:
|
|
|
|
witnessKeysMain()
|