nimbus-eth1/fluffy/tests/state_network_tests/test_state_validation.nim

567 lines
20 KiB
Nim

# Fluffy
# Copyright (c) 2023-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/[os, strutils],
results,
unittest2,
stew/byteutils,
eth/common,
../../common/common_utils,
../../network/state/state_content,
../../network/state/state_validation,
../../eth_data/yaml_utils
const testVectorDir = "./vendor/portal-spec-tests/tests/mainnet/state/validation/"
type YamlTrieNodeRecursiveGossipKV = ref object
content_key: string
content_value_offer: string
content_value_retrieval: string
type YamlTrieNodeKV = object
state_root: string
content_key: string
content_value_offer: string
content_value_retrieval: string
recursive_gossip: YamlTrieNodeRecursiveGossipKV
type YamlTrieNodeKVs = seq[YamlTrieNodeKV]
type YamlContractBytecodeKV = object
state_root: string
content_key: string
content_value_offer: string
content_value_retrieval: string
type YamlContractBytecodeKVs = seq[YamlContractBytecodeKV]
type YamlRecursiveGossipKV = object
content_key: string
content_value: string
type YamlRecursiveGossipData = object
state_root: string
recursive_gossip: seq[YamlRecursiveGossipKV]
type YamlRecursiveGossipKVs = seq[YamlRecursiveGossipData]
suite "State Validation":
# Retrieval validation tests
test "Validate valid AccountTrieNodeRetrieval nodes":
const file = testVectorDir / "account_trie_node.yaml"
let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for testData in testCase:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueRetrieval = AccountTrieNodeRetrieval
.decode(testData.content_value_retrieval.hexToSeqByte())
.get()
check:
validateRetrieval(contentKey.accountTrieNodeKey, contentValueRetrieval).isOk()
test "Validate invalid AccountTrieNodeRetrieval nodes":
const file = testVectorDir / "account_trie_node.yaml"
let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for testData in testCase:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueRetrieval = AccountTrieNodeRetrieval
.decode(testData.content_value_retrieval.hexToSeqByte())
.get()
contentValueRetrieval.node[^1] += 1 # Modify node hash
let res = validateRetrieval(contentKey.accountTrieNodeKey, contentValueRetrieval)
check:
res.isErr()
res.error() ==
"hash of fetched account trie node doesn't match the expected node hash"
test "Validate valid ContractTrieNodeRetrieval nodes":
const file = testVectorDir / "contract_storage_trie_node.yaml"
let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for testData in testCase:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueRetrieval = ContractTrieNodeRetrieval
.decode(testData.content_value_retrieval.hexToSeqByte())
.get()
check:
validateRetrieval(contentKey.contractTrieNodeKey, contentValueRetrieval).isOk()
test "Validate invalid ContractTrieNodeRetrieval nodes":
const file = testVectorDir / "contract_storage_trie_node.yaml"
let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for testData in testCase:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueRetrieval = ContractTrieNodeRetrieval
.decode(testData.content_value_retrieval.hexToSeqByte())
.get()
contentValueRetrieval.node[^1] += 1 # Modify node hash
let res = validateRetrieval(contentKey.contractTrieNodeKey, contentValueRetrieval)
check:
res.isErr()
res.error() ==
"hash of fetched contract trie node doesn't match the expected node hash"
test "Validate valid ContractCodeRetrieval nodes":
const file = testVectorDir / "contract_bytecode.yaml"
let testCase = YamlContractBytecodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for testData in testCase:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueRetrieval = ContractCodeRetrieval
.decode(testData.content_value_retrieval.hexToSeqByte())
.get()
check:
validateRetrieval(contentKey.contractCodeKey, contentValueRetrieval).isOk()
test "Validate invalid ContractCodeRetrieval nodes":
const file = testVectorDir / "contract_bytecode.yaml"
let testCase = YamlContractBytecodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for testData in testCase:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueRetrieval = ContractCodeRetrieval
.decode(testData.content_value_retrieval.hexToSeqByte())
.get()
contentValueRetrieval.code[^1] += 1 # Modify node hash
let res = validateRetrieval(contentKey.contractCodeKey, contentValueRetrieval)
check:
res.isErr()
res.error() == "hash of fetched bytecode doesn't match the expected code hash"
# Account offer validation tests
test "Validate valid AccountTrieNodeOffer nodes":
const file = testVectorDir / "account_trie_node.yaml"
let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte())
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueOffer =
AccountTrieNodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
check:
validateOffer(stateRoot, contentKey.accountTrieNodeKey, contentValueOffer)
.isOk()
if i == 1:
continue # second test case only has root node and no recursive gossip
let contentKey = ContentKey
.decode(testData.recursive_gossip.content_key.hexToSeqByte().ByteList)
.get()
let contentValueOffer = AccountTrieNodeOffer
.decode(testData.recursive_gossip.content_value_offer.hexToSeqByte())
.get()
check:
validateOffer(stateRoot, contentKey.accountTrieNodeKey, contentValueOffer).isOk()
test "Validate invalid AccountTrieNodeOffer nodes - bad state roots":
const file = testVectorDir / "account_trie_node.yaml"
const stateRoots = [
"0xBAD7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61",
"0xBAD7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61",
"0xBAD8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544",
]
let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
var stateRoot = KeccakHash.fromBytes(stateRoots[i].hexToSeqByte())
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueOffer =
AccountTrieNodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
let res =
validateOffer(stateRoot, contentKey.accountTrieNodeKey, contentValueOffer)
check:
res.isErr()
res.error() == "hash of proof root node doesn't match the expected root hash"
test "Validate invalid AccountTrieNodeOffer nodes - bad nodes":
const file = testVectorDir / "account_trie_node.yaml"
let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte())
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer =
AccountTrieNodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
contentValueOffer.proof[0][0] += 1.byte
let res =
validateOffer(stateRoot, contentKey.accountTrieNodeKey, contentValueOffer)
check:
res.isErr()
res.error() == "hash of proof root node doesn't match the expected root hash"
for i, testData in testCase:
if i == 1:
continue # second test case only has root node
var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte())
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer =
AccountTrieNodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
contentValueOffer.proof[^2][^2] += 1.byte
let res =
validateOffer(stateRoot, contentKey.accountTrieNodeKey, contentValueOffer)
check:
res.isErr()
"hash of next node doesn't match the expected" in res.error()
for i, testData in testCase:
var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte())
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer =
AccountTrieNodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
contentValueOffer.proof[^1][^1] += 1.byte
let res =
validateOffer(stateRoot, contentKey.accountTrieNodeKey, contentValueOffer)
check:
res.isErr()
# Contract storage offer validation tests
test "Validate valid ContractTrieNodeOffer nodes":
const file = testVectorDir / "contract_storage_trie_node.yaml"
let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte())
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueOffer = ContractTrieNodeOffer
.decode(testData.content_value_offer.hexToSeqByte())
.get()
check:
validateOffer(stateRoot, contentKey.contractTrieNodeKey, contentValueOffer)
.isOk()
if i == 1:
continue # second test case has no recursive gossip
let contentKey = ContentKey
.decode(testData.recursive_gossip.content_key.hexToSeqByte().ByteList)
.get()
let contentValueOffer = ContractTrieNodeOffer
.decode(testData.recursive_gossip.content_value_offer.hexToSeqByte())
.get()
check:
validateOffer(stateRoot, contentKey.contractTrieNodeKey, contentValueOffer).isOk()
test "Validate invalid ContractTrieNodeOffer nodes - bad state roots":
const file = testVectorDir / "contract_storage_trie_node.yaml"
const stateRoots = [
"0xBAD7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61",
"0xBAD7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61",
]
let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
var stateRoot = KeccakHash.fromBytes(stateRoots[i].hexToSeqByte())
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueOffer =
ContractTrieNodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
let res =
validateOffer(stateRoot, contentKey.contractTrieNodeKey, contentValueOffer)
check:
res.isErr()
res.error() == "hash of proof root node doesn't match the expected root hash"
test "Validate invalid ContractTrieNodeOffer nodes - bad nodes":
const file = testVectorDir / "contract_storage_trie_node.yaml"
let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte())
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer = ContractTrieNodeOffer
.decode(testData.content_value_offer.hexToSeqByte())
.get()
contentValueOffer.accountProof[0][0] += 1.byte
let res =
validateOffer(stateRoot, contentKey.contractTrieNodeKey, contentValueOffer)
check:
res.isErr()
res.error() == "hash of proof root node doesn't match the expected root hash"
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer = ContractTrieNodeOffer
.decode(testData.content_value_offer.hexToSeqByte())
.get()
contentValueOffer.storageProof[0][0] += 1.byte
let res =
validateOffer(stateRoot, contentKey.contractTrieNodeKey, contentValueOffer)
check:
res.isErr()
res.error() == "hash of proof root node doesn't match the expected root hash"
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer = ContractTrieNodeOffer
.decode(testData.content_value_offer.hexToSeqByte())
.get()
contentValueOffer.accountProof[^1][^1] += 1.byte
check:
validateOffer(stateRoot, contentKey.contractTrieNodeKey, contentValueOffer)
.isErr()
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer = ContractTrieNodeOffer
.decode(testData.content_value_offer.hexToSeqByte())
.get()
contentValueOffer.storageProof[^1][^1] += 1.byte
check:
validateOffer(stateRoot, contentKey.contractTrieNodeKey, contentValueOffer)
.isErr()
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer = ContractTrieNodeOffer
.decode(testData.content_value_offer.hexToSeqByte())
.get()
contentValueOffer.accountProof[^2][^2] += 1.byte
check:
validateOffer(stateRoot, contentKey.contractTrieNodeKey, contentValueOffer)
.isErr()
# Contract bytecode offer validation tests
test "Validate valid ContractCodeOffer nodes":
const file = testVectorDir / "contract_bytecode.yaml"
let testCase = YamlContractBytecodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte())
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueOffer =
ContractCodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
check:
validateOffer(stateRoot, contentKey.contractCodeKey, contentValueOffer).isOk()
test "Validate invalid ContractCodeOffer nodes - bad state root":
const file = testVectorDir / "contract_bytecode.yaml"
const stateRoots =
["0xBAD7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61"]
let testCase = YamlContractBytecodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
var stateRoot = KeccakHash.fromBytes(stateRoots[i].hexToSeqByte())
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueOffer =
ContractCodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
let res = validateOffer(stateRoot, contentKey.contractCodeKey, contentValueOffer)
check:
res.isErr()
res.error() == "hash of proof root node doesn't match the expected root hash"
test "Validate invalid ContractCodeOffer nodes - bad nodes and bytecode":
const file = testVectorDir / "contract_bytecode.yaml"
let testCase = YamlContractBytecodeKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte())
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer =
ContractCodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
contentValueOffer.accountProof[0][0] += 1.byte
let res =
validateOffer(stateRoot, contentKey.contractCodeKey, contentValueOffer)
check:
res.isErr()
res.error() == "hash of proof root node doesn't match the expected root hash"
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer =
ContractCodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
contentValueOffer.code[0] += 1.byte
let res =
validateOffer(stateRoot, contentKey.contractCodeKey, contentValueOffer)
check:
res.isErr()
res.error() == "hash of offered bytecode doesn't match the expected code hash"
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer =
ContractCodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
contentValueOffer.accountProof[^1][^1] += 1.byte
check:
validateOffer(stateRoot, contentKey.contractCodeKey, contentValueOffer).isErr()
block:
let contentKey =
ContentKey.decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueOffer =
ContractCodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get()
contentValueOffer.code[^1] += 1.byte
let res =
validateOffer(stateRoot, contentKey.contractCodeKey, contentValueOffer)
check:
res.isErr()
res.error() == "hash of offered bytecode doesn't match the expected code hash"
# Recursive gossip offer validation tests
test "Validate valid AccountTrieNodeOffer recursive gossip nodes":
const file = testVectorDir / "recursive_gossip.yaml"
const stateRoots = [
"0x1ad7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61",
"0x1ad7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61",
"0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544",
]
let testCase = YamlRecursiveGossipKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
if i == 1:
continue
var stateRoot = KeccakHash.fromBytes(stateRoots[i].hexToSeqByte())
for kv in testData.recursive_gossip:
let contentKey = ContentKey.decode(kv.content_key.hexToSeqByte().ByteList).get()
let contentValueOffer =
AccountTrieNodeOffer.decode(kv.content_value.hexToSeqByte()).get()
check:
validateOffer(stateRoot, contentKey.accountTrieNodeKey, contentValueOffer)
.isOk()
test "Validate valid ContractTrieNodeOffer recursive gossip nodes":
const file = testVectorDir / "recursive_gossip.yaml"
let testCase = YamlRecursiveGossipKVs.loadFromYaml(file).valueOr:
raiseAssert "Cannot read test vector: " & error
for i, testData in testCase:
if i != 1:
continue
var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte())
for kv in testData.recursive_gossip:
let contentKey = ContentKey.decode(kv.content_key.hexToSeqByte().ByteList).get()
let contentValueOffer =
ContractTrieNodeOffer.decode(kv.content_value.hexToSeqByte()).get()
check:
validateOffer(stateRoot, contentKey.contractTrieNodeKey, contentValueOffer)
.isOk()