Skip to content

Commit

Permalink
Add retrieval content validation to state network. (#2166)
Browse files Browse the repository at this point in the history
* Add retrieval content validation to state network.

* Complete state validation tests for retrieval content.

* Improve naming functions and parameters.
  • Loading branch information
web3-developer committed May 6, 2024
1 parent 143f2e9 commit 5c4358a
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 20 deletions.
2 changes: 1 addition & 1 deletion fluffy/network/state/state_content.nim
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func packNibbles*(nibbles: seq[byte]): Nibbles =
func unpackNibbles*(nibbles: Nibbles): seq[byte] =
doAssert(nibbles.len() <= MAX_PACKED_NIBBLES_LEN, "Packed nibbles length is too long")

var output = newSeqOfCap[byte](nibbles.len * 2)
var output = newSeqOfCap[byte](nibbles.len() * 2)

for i, pair in nibbles:
if i == 0 and pair == 0x00:
Expand Down
30 changes: 11 additions & 19 deletions fluffy/network/state/state_network.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import
eth/p2p/discoveryv5/[protocol, enr],
../../database/content_db,
../wire/[portal_protocol, portal_stream, portal_protocol_config],
./state_content
./state_content,
./state_validation

logScope:
topics = "portal_state"
Expand Down Expand Up @@ -79,34 +80,25 @@ func decodeValue*(

Opt.some(value)

proc validateAccountTrieNode(
n: StateNetwork, key: ContentKey, contentValue: RetrievalContentValue
): bool =
true

proc validateContractTrieNode(
n: StateNetwork, key: ContentKey, contentValue: RetrievalContentValue
): bool =
true

proc validateContractCode(
n: StateNetwork, key: ContentKey, contentValue: RetrievalContentValue
): bool =
true

proc validateContent*(
n: StateNetwork, contentKey: ContentKey, contentValue: RetrievalContentValue
): bool =
doAssert(contentKey.contentType == contentValue.contentType)

case contentKey.contentType
of unused:
warn "Received content with unused content type"
false
of accountTrieNode:
validateAccountTrieNode(n, contentKey, contentValue)
validateFetchedAccountTrieNode(
contentKey.accountTrieNodeKey, contentValue.accountTrieNode
)
of contractTrieNode:
validateContractTrieNode(n, contentKey, contentValue)
validateFetchedContractTrieNode(
contentKey.contractTrieNodeKey, contentValue.contractTrieNode
)
of contractCode:
validateContractCode(n, contentKey, contentValue)
validateFetchedContractCode(contentKey.contractCodeKey, contentValue.contractCode)

proc getContent*(n: StateNetwork, key: ContentKey): Future[Opt[seq[byte]]] {.async.} =
let
Expand Down
34 changes: 34 additions & 0 deletions fluffy/network/state/state_validation.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Fluffy
# Copyright (c) 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 eth/common, ./state_content

proc validateFetchedAccountTrieNode*(
trustedAccountTrieNodeKey: AccountTrieNodeKey,
accountTrieNode: AccountTrieNodeRetrieval,
): bool =
let expectedHash = trustedAccountTrieNodeKey.nodeHash
let actualHash = keccakHash(accountTrieNode.node.asSeq())

expectedHash == actualHash

proc validateFetchedContractTrieNode*(
trustedContractTrieNodeKey: ContractTrieNodeKey,
contractTrieNode: ContractTrieNodeRetrieval,
): bool =
let expectedHash = trustedContractTrieNodeKey.nodeHash
let actualHash = keccakHash(contractTrieNode.node.asSeq())

expectedHash == actualHash

proc validateFetchedContractCode*(
trustedContractCodeKey: ContractCodeKey, contractCode: ContractCodeRetrieval
): bool =
let expectedHash = trustedContractCodeKey.codeHash
let actualHash = keccakHash(contractCode.code.asSeq())

expectedHash == actualHash
143 changes: 143 additions & 0 deletions fluffy/tests/state_network_tests/test_state_validation.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# 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,
unittest2,
stew/byteutils,
../../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
content_key: string
content_value_offer: string
content_value_retrieval: string
recursive_gossip: YamlTrieNodeRecursiveGossipKV

type YamlTrieNodeKVs = seq[YamlTrieNodeKV]

type YamlContractBytecodeKV = object
content_key: string
content_value_offer: string
content_value_retrieval: string

type YamlContractBytecodeKVs = seq[YamlContractBytecodeKV]

suite "State Validation":
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 = decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueRetrieval = SSZ.decode(
testData.content_value_retrieval.hexToSeqByte(), AccountTrieNodeRetrieval
)

check:
validateFetchedAccountTrieNode(
contentKey.accountTrieNodeKey, contentValueRetrieval
)

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 = decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueRetrieval = SSZ.decode(
testData.content_value_retrieval.hexToSeqByte(), AccountTrieNodeRetrieval
)

contentValueRetrieval.node[^1] += 1 # Modify node hash

check:
not validateFetchedAccountTrieNode(
contentKey.accountTrieNodeKey, contentValueRetrieval
)

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 = decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueRetrieval = SSZ.decode(
testData.content_value_retrieval.hexToSeqByte(), ContractTrieNodeRetrieval
)

check:
validateFetchedContractTrieNode(
contentKey.contractTrieNodeKey, contentValueRetrieval
)

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 = decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueRetrieval = SSZ.decode(
testData.content_value_retrieval.hexToSeqByte(), ContractTrieNodeRetrieval
)

contentValueRetrieval.node[^1] += 1 # Modify node hash

check:
not validateFetchedContractTrieNode(
contentKey.contractTrieNodeKey, contentValueRetrieval
)

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 = decode(testData.content_key.hexToSeqByte().ByteList).get()
let contentValueRetrieval = SSZ.decode(
testData.content_value_retrieval.hexToSeqByte(), ContractCodeRetrieval
)

check:
validateFetchedContractCode(contentKey.contractCodeKey, contentValueRetrieval)

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 = decode(testData.content_key.hexToSeqByte().ByteList).get()
var contentValueRetrieval = SSZ.decode(
testData.content_value_retrieval.hexToSeqByte(), ContractCodeRetrieval
)

contentValueRetrieval.code[^1] += 1 # Modify node hash

check:
not validateFetchedContractCode(
contentKey.contractCodeKey, contentValueRetrieval
)

0 comments on commit 5c4358a

Please sign in to comment.