From 93a160b5698d481aaf0d14660679216e4421c251 Mon Sep 17 00:00:00 2001 From: web3-developer <51288821+web3-developer@users.noreply.github.com> Date: Thu, 8 Aug 2024 00:01:30 +0800 Subject: [PATCH] Update Fluffy State Network to match Portal spec addressHash change (#2548) * Update state network to use addressHash instead of address in contract trie and contract code content keys. * Fix path calculation bug in getParent when working with extension nodes. * Bump portal spec tests repo. * Finish updating tests due to portal test vector changes. * Update Fluffy state bridge to use addressHash. * Update Fluffy book with correct commands for running portal hive tests. --- .../docs/fluffy-with-portal-hive.md | 8 +- fluffy/network/state/content/content_keys.nim | 17 +- fluffy/network/state/state_endpoints.nim | 21 ++- fluffy/network/state/state_gossip.nim | 14 +- fluffy/network/state/state_utils.nim | 2 +- fluffy/network/state/state_validation.nim | 4 +- .../state_test_helpers.nim | 16 -- .../test_state_content_keys_vectors.nim | 15 +- .../test_state_endpoints_genesis.nim | 13 +- .../test_state_endpoints_vectors.nim | 53 +++--- .../test_state_gossip_getparent_genesis.nim | 8 +- .../test_state_gossip_getparent_vectors.nim | 128 +++---------- .../test_state_gossip_gossipoffer_vectors.nim | 178 +----------------- .../test_state_validation_genesis.nim | 22 ++- .../test_state_validation_vectors.nim | 112 ++--------- .../portal_bridge/portal_bridge_conf.nim | 3 +- .../state_bridge/offers_builder.nim | 29 +-- .../state_bridge/world_state.nim | 32 ++-- vendor/portal-spec-tests | 2 +- 19 files changed, 188 insertions(+), 489 deletions(-) diff --git a/fluffy/docs/the_fluffy_book/docs/fluffy-with-portal-hive.md b/fluffy/docs/the_fluffy_book/docs/fluffy-with-portal-hive.md index d8ceabc25..f709e030a 100644 --- a/fluffy/docs/the_fluffy_book/docs/fluffy-with-portal-hive.md +++ b/fluffy/docs/the_fluffy_book/docs/fluffy-with-portal-hive.md @@ -17,11 +17,11 @@ go build . Example commands for running test suites: ```sh -# Run the history tests with the 3 different clients -./hive --sim history --client fluffy,trin,ultralight +# Run the portal tests with only the fluffy client +./hive --sim portal --client fluffy -# Run the state tests with only the fluffy client -./hive --sim state --client fluffy +# Run the portal tests with the 3 different clients +./hive --sim portal --client fluffy,trin,ultralight # Access results through web-ui: ```sh diff --git a/fluffy/network/state/content/content_keys.nim b/fluffy/network/state/content/content_keys.nim index acaefbc80..9d1fe2732 100644 --- a/fluffy/network/state/content/content_keys.nim +++ b/fluffy/network/state/content/content_keys.nim @@ -23,7 +23,7 @@ export ssz_serialization, common_types, hash, results type NodeHash* = KeccakHash CodeHash* = KeccakHash - Address* = EthAddress + AddressHash* = KeccakHash ContentType* = enum # Note: Need to add this unused value as a case object with an enum without @@ -43,12 +43,12 @@ type nodeHash*: NodeHash ContractTrieNodeKey* = object - address*: Address + addressHash*: AddressHash path*: Nibbles nodeHash*: NodeHash ContractCodeKey* = object - address*: Address + addressHash*: AddressHash codeHash*: CodeHash ContentKey* = object @@ -68,12 +68,15 @@ func init*(T: type AccountTrieNodeKey, path: Nibbles, nodeHash: NodeHash): T = AccountTrieNodeKey(path: path, nodeHash: nodeHash) func init*( - T: type ContractTrieNodeKey, address: Address, path: Nibbles, nodeHash: NodeHash + T: type ContractTrieNodeKey, + addressHash: AddressHash, + path: Nibbles, + nodeHash: NodeHash, ): T = - ContractTrieNodeKey(address: address, path: path, nodeHash: nodeHash) + ContractTrieNodeKey(addressHash: addressHash, path: path, nodeHash: nodeHash) -func init*(T: type ContractCodeKey, address: Address, codeHash: CodeHash): T = - ContractCodeKey(address: address, codeHash: codeHash) +func init*(T: type ContractCodeKey, addressHash: AddressHash, codeHash: CodeHash): T = + ContractCodeKey(addressHash: addressHash, codeHash: codeHash) func toContentKey*(key: AccountTrieNodeKey): ContentKey = ContentKey(contentType: accountTrieNode, accountTrieNodeKey: key) diff --git a/fluffy/network/state/state_endpoints.nim b/fluffy/network/state/state_endpoints.nim index d4efb8544..78ff85f35 100644 --- a/fluffy/network/state/state_endpoints.nim +++ b/fluffy/network/state/state_endpoints.nim @@ -67,7 +67,7 @@ proc getNextNodeHash( raiseAssert(e.msg) proc getAccountProof( - n: StateNetwork, stateRoot: KeccakHash, address: Address + n: StateNetwork, stateRoot: KeccakHash, address: EthAddress ): Future[Opt[TrieProof]] {.async: (raises: [CancelledError]).} = let nibbles = address.toPath().unpackNibbles() @@ -94,13 +94,14 @@ proc getAccountProof( Opt.some(proof) proc getStorageProof( - n: StateNetwork, storageRoot: KeccakHash, address: Address, storageKey: UInt256 + n: StateNetwork, storageRoot: KeccakHash, address: EthAddress, storageKey: UInt256 ): Future[Opt[TrieProof]] {.async: (raises: [CancelledError]).} = let nibbles = storageKey.toPath().unpackNibbles() var + addressHash = keccakHash(address) nibblesIdx = 0 - key = ContractTrieNodeKey.init(address, Nibbles.empty(), storageRoot) + key = ContractTrieNodeKey.init(addressHash, Nibbles.empty(), storageRoot) proof = TrieProof.empty() while nibblesIdx < nibbles.len(): @@ -116,12 +117,12 @@ proc getStorageProof( let (nextPath, nextNodeHash) = trieNode.getNextNodeHash(nibbles, nibblesIdx).valueOr: break - key = ContractTrieNodeKey.init(address, nextPath, nextNodeHash) + key = ContractTrieNodeKey.init(addressHash, nextPath, nextNodeHash) Opt.some(proof) proc getAccount( - n: StateNetwork, blockHash: BlockHash, address: Address + n: StateNetwork, blockHash: BlockHash, address: EthAddress ): Future[Opt[Account]] {.async: (raises: [CancelledError]).} = let stateRoot = (await n.getStateRootByBlockHash(blockHash)).valueOr: @@ -138,7 +139,7 @@ proc getAccount( # Used by: eth_getBalance, proc getBalance*( - n: StateNetwork, blockHash: BlockHash, address: Address + n: StateNetwork, blockHash: BlockHash, address: EthAddress ): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} = let account = (await n.getAccount(blockHash, address)).valueOr: return Opt.none(UInt256) @@ -147,7 +148,7 @@ proc getBalance*( # Used by: eth_getTransactionCount proc getTransactionCount*( - n: StateNetwork, blockHash: BlockHash, address: Address + n: StateNetwork, blockHash: BlockHash, address: EthAddress ): Future[Opt[AccountNonce]] {.async: (raises: [CancelledError]).} = let account = (await n.getAccount(blockHash, address)).valueOr: return Opt.none(AccountNonce) @@ -156,7 +157,7 @@ proc getTransactionCount*( # Used by: eth_getStorageAt proc getStorageAt*( - n: StateNetwork, blockHash: BlockHash, address: Address, slotKey: UInt256 + n: StateNetwork, blockHash: BlockHash, address: EthAddress, slotKey: UInt256 ): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} = let account = (await n.getAccount(blockHash, address)).valueOr: @@ -172,12 +173,12 @@ proc getStorageAt*( # Used by: eth_getCode proc getCode*( - n: StateNetwork, blockHash: BlockHash, address: Address + n: StateNetwork, blockHash: BlockHash, address: EthAddress ): Future[Opt[Bytecode]] {.async: (raises: [CancelledError]).} = let account = (await n.getAccount(blockHash, address)).valueOr: return Opt.none(Bytecode) - contractCodeKey = ContractCodeKey.init(address, account.codeHash) + contractCodeKey = ContractCodeKey.init(keccakHash(address), account.codeHash) let contractCodeRetrieval = (await n.getContractCode(contractCodeKey)).valueOr: warn "Failed to get contract code" diff --git a/fluffy/network/state/state_gossip.nim b/fluffy/network/state/state_gossip.nim index 6d6c267b4..3ff2e647c 100644 --- a/fluffy/network/state/state_gossip.nim +++ b/fluffy/network/state/state_gossip.nim @@ -60,7 +60,9 @@ func getParent(p: ProofWithPath): ProofWithPath = # leaf or extension node so we need to remove one or more nibbles let (_, _, prefixNibbles) = decodePrefix(parentEndNode.listElem(0)) - parentProof.withPath(unpackedNibbles.dropN(prefixNibbles.len()).packNibbles()) + parentProof.withPath( + unpackedNibbles.dropN(prefixNibbles.unpackNibbles().len()).packNibbles() + ) except RlpError as e: raiseAssert(e.msg) @@ -79,7 +81,7 @@ func getParent*(offerWithKey: ContractTrieOfferWithKey): ContractTrieOfferWithKe (key, offer) = offerWithKey parent = offer.storageProof.withPath(key.path).getParent() parentKey = ContractTrieNodeKey.init( - key.address, parent.path, keccakHash(parent.proof[^1].asSeq()) + key.addressHash, parent.path, keccakHash(parent.proof[^1].asSeq()) ) parentOffer = ContractTrieNodeOffer.init(parent.proof, offer.accountProof, offer.blockHash) @@ -134,12 +136,12 @@ proc recursiveGossipOffer*( offerBytes: seq[byte], key: AccountTrieNodeKey, offer: AccountTrieNodeOffer, -) {.async: (raises: [CancelledError]).} = +): Future[ContentKeyByteList] {.async: (raises: [CancelledError]).} = await gossipOffer(p, srcNodeId, keyBytes, offerBytes, key, offer) # root node, recursive gossip is finished if key.path.unpackNibbles().len() == 0: - return + return keyBytes # continue the recursive gossip by sharing the parent offer with peers let @@ -159,12 +161,12 @@ proc recursiveGossipOffer*( offerBytes: seq[byte], key: ContractTrieNodeKey, offer: ContractTrieNodeOffer, -) {.async: (raises: [CancelledError]).} = +): Future[ContentKeyByteList] {.async: (raises: [CancelledError]).} = await gossipOffer(p, srcNodeId, keyBytes, offerBytes, key, offer) # root node, recursive gossip is finished if key.path.unpackNibbles().len() == 0: - return + return keyBytes # continue the recursive gossip by sharing the parent offer with peers let diff --git a/fluffy/network/state/state_utils.nim b/fluffy/network/state/state_utils.nim index 76c7f94b4..22872416c 100644 --- a/fluffy/network/state/state_utils.nim +++ b/fluffy/network/state/state_utils.nim @@ -87,7 +87,7 @@ func removeLeafKeyEndNibbles*( func toPath*(hash: KeccakHash): Nibbles {.inline.} = Nibbles.init(hash.data, isEven = true) -func toPath*(address: Address): Nibbles {.inline.} = +func toPath*(address: EthAddress): Nibbles {.inline.} = keccakHash(address).toPath() func toPath*(slotKey: UInt256): Nibbles {.inline.} = diff --git a/fluffy/network/state/state_validation.nim b/fluffy/network/state/state_validation.nim index af69fb06e..c28fa7e47 100644 --- a/fluffy/network/state/state_validation.nim +++ b/fluffy/network/state/state_validation.nim @@ -156,7 +156,7 @@ proc validateOffer*( ): Result[void, string] = ?validateTrieProof( trustedStateRoot, - key.address.toPath(), + key.addressHash.toPath(), offer.accountProof, allowKeyEndInPathForLeafs = true, ) @@ -172,7 +172,7 @@ proc validateOffer*( ): Result[void, string] = ?validateTrieProof( trustedStateRoot, - key.address.toPath(), + key.addressHash.toPath(), offer.accountProof, allowKeyEndInPathForLeafs = true, ) diff --git a/fluffy/tests/state_network_tests/state_test_helpers.nim b/fluffy/tests/state_network_tests/state_test_helpers.nim index 717f1fd2f..db5f4808a 100644 --- a/fluffy/tests/state_network_tests/state_test_helpers.nim +++ b/fluffy/tests/state_network_tests/state_test_helpers.nim @@ -26,17 +26,11 @@ export 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 - YamlTrieNodeKV* = object state_root*: string content_key*: string content_value_offer*: string content_value_retrieval*: string - recursive_gossip*: YamlTrieNodeRecursiveGossipKV YamlTrieNodeKVs* = seq[YamlTrieNodeKV] @@ -48,16 +42,6 @@ type YamlContractBytecodeKVs* = seq[YamlContractBytecodeKV] - YamlRecursiveGossipKV* = object - content_key*: string - content_value*: string - - YamlRecursiveGossipData* = object - state_root*: string - recursive_gossip*: seq[YamlRecursiveGossipKV] - - YamlRecursiveGossipKVs* = seq[YamlRecursiveGossipData] - func asNibbles*(key: openArray[byte], isEven = true): Nibbles = Nibbles.init(key, isEven) diff --git a/fluffy/tests/state_network_tests/test_state_content_keys_vectors.nim b/fluffy/tests/state_network_tests/test_state_content_keys_vectors.nim index fd5b656df..76a44a536 100644 --- a/fluffy/tests/state_network_tests/test_state_content_keys_vectors.nim +++ b/fluffy/tests/state_network_tests/test_state_content_keys_vectors.nim @@ -8,6 +8,7 @@ import unittest2, stew/byteutils, + eth/common, ../../network/state/state_content, ../../eth_data/yaml_utils @@ -58,10 +59,10 @@ suite "State Content Keys": raiseAssert "Cannot read test vector: " & error packedNibbles = packNibbles(testCase.path) - address = Address.fromHex(testCase.address) + addressHash = EthAddress.fromHex(testCase.address).keccakHash() nodeHash = NodeHash.fromHex(testCase.node_hash) contentKey = - ContractTrieNodeKey.init(address, packedNibbles, nodeHash).toContentKey() + ContractTrieNodeKey.init(addressHash, packedNibbles, nodeHash).toContentKey() encoded = contentKey.encode() check: @@ -73,7 +74,9 @@ suite "State Content Keys": decoded.isOk() decoded.value().contentType == contractTrieNode decoded.value().contractTrieNodeKey == - ContractTrieNodeKey(address: address, path: packedNibbles, nodeHash: nodeHash) + ContractTrieNodeKey( + addressHash: addressHash, path: packedNibbles, nodeHash: nodeHash + ) test "Encode/decode ContractCodeKey": const file = testVectorDir & "contract_bytecode_key.yaml" @@ -88,9 +91,9 @@ suite "State Content Keys": testCase = YamlContractBytecodeKey.loadFromYaml(file).valueOr: raiseAssert "Cannot read test vector: " & error - address = Address.fromHex(testCase.address) + addressHash = EthAddress.fromHex(testCase.address).keccakHash() codeHash = CodeHash.fromHex(testCase.code_hash) - contentKey = ContractCodeKey.init(address, codeHash).toContentKey() + contentKey = ContractCodeKey.init(addressHash, codeHash).toContentKey() encoded = contentKey.encode() check: @@ -101,7 +104,7 @@ suite "State Content Keys": check: decoded.isOk() decoded.value().contentType == contractCode - decoded.value().contractCodeKey.address == address + decoded.value().contractCodeKey.addressHash == addressHash decoded.value().contractCodeKey.codeHash == codeHash test "Invalid prefix - 0 value": diff --git a/fluffy/tests/state_network_tests/test_state_endpoints_genesis.nim b/fluffy/tests/state_network_tests/test_state_endpoints_genesis.nim index 9292d4d86..af5c15969 100644 --- a/fluffy/tests/state_network_tests/test_state_endpoints_genesis.nim +++ b/fluffy/tests/state_network_tests/test_state_endpoints_genesis.nim @@ -43,8 +43,8 @@ suite "State Endpoints - Genesis JSON Files": let proof = accountState.generateAccountProof(address) leafNode = proof[^1] - addressHash = keccakHash(address).data - path = removeLeafKeyEndNibbles(Nibbles.init(addressHash, true), leafNode) + addressHash = keccakHash(address) + path = removeLeafKeyEndNibbles(Nibbles.init(addressHash.data, true), leafNode) key = AccountTrieNodeKey.init(path, keccakHash(leafNode.asSeq())) offer = AccountTrieNodeOffer(proof: proof) @@ -94,8 +94,9 @@ suite "State Endpoints - Genesis JSON Files": block: # store the code let - key = - ContractCodeKey(address: address, codeHash: keccakHash(account.code)) + key = ContractCodeKey( + addressHash: addressHash, codeHash: keccakHash(account.code) + ) value = ContractCodeRetrieval(code: Bytecode.init(account.code)) let contentKey = key.toContentKey().encode() @@ -121,7 +122,9 @@ suite "State Endpoints - Genesis JSON Files": Nibbles.init(keccakHash(toBytesBE(slotKey)).data, true), leafNode ) key = ContractTrieNodeKey( - address: address, path: path, nodeHash: keccakHash(leafNode.asSeq()) + addressHash: addressHash, + path: path, + nodeHash: keccakHash(leafNode.asSeq()), ) offer = ContractTrieNodeOffer(storageProof: storageProof, accountProof: proof) diff --git a/fluffy/tests/state_network_tests/test_state_endpoints_vectors.nim b/fluffy/tests/state_network_tests/test_state_endpoints_vectors.nim index 6e2e39b19..5a99770bc 100644 --- a/fluffy/tests/state_network_tests/test_state_endpoints_vectors.nim +++ b/fluffy/tests/state_network_tests/test_state_endpoints_vectors.nim @@ -27,10 +27,10 @@ procSuite "State Endpoints": let rng = newRng() asyncTest "Gossip then query getBalance and getTransactionCount": - const file = testVectorDir / "recursive_gossip.yaml" + const file = testVectorDir / "account_trie_node.yaml" let - testCase = YamlRecursiveGossipKVs.loadFromYaml(file).valueOr: + testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr: raiseAssert "Cannot read test vector: " & error stateNode1 = newStateNode(rng, STATE_NODE1_PORT) stateNode2 = newStateNode(rng, STATE_NODE2_PORT) @@ -45,16 +45,17 @@ procSuite "State Endpoints": (await stateNode2.portalProtocol().ping(stateNode1.localNode())).isOk() for i, testData in testCase: - if i == 1: + if i != 0 and i != 3: + # only using the leaf nodes from the test data continue let stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte()) - leafData = testData.recursive_gossip[0] + leafData = testData contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList contentKey = ContentKey.decode(contentKeyBytes).get() contentId = toContentId(contentKeyBytes) - contentValueBytes = leafData.content_value.hexToSeqByte() + contentValueBytes = leafData.content_value_offer.hexToSeqByte() contentValue = AccountTrieNodeOffer.decode(contentValueBytes).get() # set valid state root @@ -62,7 +63,7 @@ procSuite "State Endpoints": stateNode2.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot) # offer the leaf node - await stateNode1.portalProtocol.recursiveGossipOffer( + let rootKeyBytes = await stateNode1.portalProtocol.recursiveGossipOffer( Opt.none(NodeId), contentKeyBytes, contentValueBytes, @@ -70,16 +71,14 @@ procSuite "State Endpoints": contentValue, ) - # wait for recursive gossip to complete - for node in testData.recursive_gossip: - let keyBytes = node.content_key.hexToSeqByte().ContentKeyByteList - await stateNode2.waitUntilContentAvailable(toContentId(keyBytes)) + await stateNode1.waitUntilContentAvailable(toContentId(rootKeyBytes)) + await stateNode2.waitUntilContentAvailable(toContentId(rootKeyBytes)) let address = if i == 0: EthAddress.fromHex("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2") - elif i == 2: + elif i == 3: EthAddress.fromHex("0x1584a2c066b7a455dbd6ae2807a7334e83c35fa5") else: raiseAssert("Invalid test case") @@ -134,11 +133,14 @@ procSuite "State Endpoints": asyncTest "Gossip then query getStorageAt and getCode": const - file = testVectorDir / "recursive_gossip.yaml" + accountTrieFile = testVectorDir / "account_trie_node.yaml" + contractTrieFile = testVectorDir / "contract_storage_trie_node.yaml" bytecodeFile = testVectorDir / "contract_bytecode.yaml" let - testCase = YamlRecursiveGossipKVs.loadFromYaml(file).valueOr: + accountTrieTestCase = YamlTrieNodeKVs.loadFromYaml(accountTrieFile).valueOr: + raiseAssert "Cannot read test vector: " & error + contractTrieTestCase = YamlTrieNodeKVs.loadFromYaml(contractTrieFile).valueOr: raiseAssert "Cannot read test vector: " & error stateNode1 = newStateNode(rng, STATE_NODE1_PORT) stateNode2 = newStateNode(rng, STATE_NODE2_PORT) @@ -155,13 +157,13 @@ procSuite "State Endpoints": block: # seed the account data let - testData = testCase[0] + testData = accountTrieTestCase[0] stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte()) - leafData = testData.recursive_gossip[0] + leafData = testData contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList contentKey = ContentKey.decode(contentKeyBytes).get() contentId = toContentId(contentKeyBytes) - contentValueBytes = leafData.content_value.hexToSeqByte() + contentValueBytes = leafData.content_value_offer.hexToSeqByte() contentValue = AccountTrieNodeOffer.decode(contentValueBytes).get() # set valid state root @@ -169,7 +171,7 @@ procSuite "State Endpoints": stateNode2.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot) # offer the leaf node - await stateNode1.portalProtocol.recursiveGossipOffer( + let rootKeyBytes = await stateNode1.portalProtocol.recursiveGossipOffer( Opt.none(NodeId), contentKeyBytes, contentValueBytes, @@ -177,16 +179,19 @@ procSuite "State Endpoints": contentValue, ) + # wait for gossip to complete + await stateNode2.waitUntilContentAvailable(toContentId(rootKeyBytes)) + block: # seed the storage data let - testData = testCase[1] + testData = contractTrieTestCase[0] stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte()) - leafData = testData.recursive_gossip[0] + leafData = testData contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList contentKey = ContentKey.decode(contentKeyBytes).get() contentId = toContentId(contentKeyBytes) - contentValueBytes = leafData.content_value.hexToSeqByte() + contentValueBytes = leafData.content_value_offer.hexToSeqByte() contentValue = ContractTrieNodeOffer.decode(contentValueBytes).get() # set valid state root @@ -194,7 +199,7 @@ procSuite "State Endpoints": stateNode2.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot) # offer the leaf node - await stateNode1.portalProtocol.recursiveGossipOffer( + let storageRootKeyBytes = await stateNode1.portalProtocol.recursiveGossipOffer( Opt.none(NodeId), contentKeyBytes, contentValueBytes, @@ -202,10 +207,8 @@ procSuite "State Endpoints": contentValue, ) - # wait for recursive gossip to complete - for node in testData.recursive_gossip: - let keyBytes = node.content_key.hexToSeqByte().ContentKeyByteList - await stateNode2.waitUntilContentAvailable(toContentId(keyBytes)) + # wait for gossip to complete + await stateNode2.waitUntilContentAvailable(toContentId(storageRootKeyBytes)) let address = EthAddress.fromHex("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2") diff --git a/fluffy/tests/state_network_tests/test_state_gossip_getparent_genesis.nim b/fluffy/tests/state_network_tests/test_state_gossip_getparent_genesis.nim index 8063a3c10..29860743d 100644 --- a/fluffy/tests/state_network_tests/test_state_gossip_getparent_genesis.nim +++ b/fluffy/tests/state_network_tests/test_state_gossip_getparent_genesis.nim @@ -68,7 +68,9 @@ suite "State Gossip getParent - Genesis JSON Files": (accountState, storageStates) = accounts.toState() for address, account in accounts: - let accountProof = accountState.generateAccountProof(address) + let + addressHash = address.keccakHash() + accountProof = accountState.generateAccountProof(address) if account.code.len() > 0: let storageState = storageStates[address] @@ -82,7 +84,9 @@ suite "State Gossip getParent - Genesis JSON Files": Nibbles.init(keccakHash(toBytesBE(slotKey)).data, true), leafNode ) key = ContractTrieNodeKey( - address: address, path: path, nodeHash: keccakHash(leafNode.asSeq()) + addressHash: addressHash, + path: path, + nodeHash: keccakHash(leafNode.asSeq()), ) offer = ContractTrieNodeOffer( storageProof: storageProof, accountProof: accountProof diff --git a/fluffy/tests/state_network_tests/test_state_gossip_getparent_vectors.nim b/fluffy/tests/state_network_tests/test_state_gossip_getparent_vectors.nim index 1e5ba94bf..a77a13d41 100644 --- a/fluffy/tests/state_network_tests/test_state_gossip_getparent_vectors.nim +++ b/fluffy/tests/state_network_tests/test_state_gossip_getparent_vectors.nim @@ -6,12 +6,11 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - std/[os, strutils], + std/os, results, unittest2, stew/byteutils, eth/common, - ../../common/common_utils, ../../network/state/[state_content, state_gossip], ./state_test_helpers @@ -23,29 +22,26 @@ suite "State Gossip getParent - Test Vectors": raiseAssert "Cannot read test vector: " & error for i, testData in testCase: - var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte()) + if i == 0 or i == 3: + let + parentTestData = testCase[i + 1] + key = ContentKey + .decode(testData.content_key.hexToSeqByte().ContentKeyByteList) + .get() + offer = AccountTrieNodeOffer + .decode(testData.content_value_offer.hexToSeqByte()) + .get() - let key = - ContentKey.decode(testData.content_key.hexToSeqByte().ContentKeyByteList).get() - let offer = - AccountTrieNodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get() - - if i == 1: # second test case only has root node and no recursive gossip - doAssertRaises(AssertionDefect): - discard offer.withKey(key.accountTrieNodeKey).getParent() - continue - - let (parentKey, parentOffer) = offer.withKey(key.accountTrieNodeKey).getParent() - check: - parentKey.path.unpackNibbles().len() < - key.accountTrieNodeKey.path.unpackNibbles().len() - parentOffer.proof.len() == offer.proof.len() - 1 - parentKey.toContentKey().encode() == - testData.recursive_gossip.content_key.hexToSeqByte().ContentKeyByteList - parentOffer.encode() == - testData.recursive_gossip.content_value_offer.hexToSeqByte() - parentOffer.toRetrievalValue().encode() == - testData.recursive_gossip.content_value_retrieval.hexToSeqByte() + let (parentKey, parentOffer) = offer.withKey(key.accountTrieNodeKey).getParent() + check: + parentKey.path.unpackNibbles().len() < + key.accountTrieNodeKey.path.unpackNibbles().len() + parentOffer.proof.len() == offer.proof.len() - 1 + parentKey.toContentKey().encode() == + parentTestData.content_key.hexToSeqByte().ContentKeyByteList + parentOffer.encode() == parentTestData.content_value_offer.hexToSeqByte() + parentOffer.toRetrievalValue().encode() == + parentTestData.content_value_retrieval.hexToSeqByte() test "Check contract storage trie node parent matches expected recursive gossip": const file = testVectorDir / "contract_storage_trie_node.yaml" @@ -54,88 +50,24 @@ suite "State Gossip getParent - Test Vectors": raiseAssert "Cannot read test vector: " & error for i, testData in testCase: - var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte()) - - let key = - ContentKey.decode(testData.content_key.hexToSeqByte().ContentKeyByteList).get() - let offer = - ContractTrieNodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get() - - if i == 1: # second test case only has root node and no recursive gossip - doAssertRaises(AssertionDefect): - discard offer.withKey(key.contractTrieNodeKey).getParent() - continue - - let (parentKey, parentOffer) = offer.withKey(key.contractTrieNodeKey).getParent() - check: - parentKey.path.unpackNibbles().len() < - key.contractTrieNodeKey.path.unpackNibbles().len() - parentOffer.storageProof.len() == offer.storageProof.len() - 1 - parentKey.toContentKey().encode() == - testData.recursive_gossip.content_key.hexToSeqByte().ContentKeyByteList - parentOffer.encode() == - testData.recursive_gossip.content_value_offer.hexToSeqByte() - parentOffer.toRetrievalValue().encode() == - testData.recursive_gossip.content_value_retrieval.hexToSeqByte() - - test "Check each account trie node parent matches expected recursive gossip": - 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 - - for j in 0 ..< testData.recursive_gossip.high: + if i == 0: let + parentTestData = testCase[i + 1] key = ContentKey - .decode( - testData.recursive_gossip[j].content_key.hexToSeqByte().ContentKeyByteList - ) - .get() - offer = AccountTrieNodeOffer - .decode(testData.recursive_gossip[j].content_value.hexToSeqByte()) - .get() - (parentKey, parentOffer) = offer.withKey(key.accountTrieNodeKey).getParent() - - check: - parentKey.path.unpackNibbles().len() < - key.accountTrieNodeKey.path.unpackNibbles().len() - parentOffer.proof.len() == offer.proof.len() - 1 - parentKey.toContentKey().encode() == - testData.recursive_gossip[j + 1].content_key.hexToSeqByte().ContentKeyByteList - parentOffer.encode() == - testData.recursive_gossip[j + 1].content_value.hexToSeqByte() - - test "Check each contract trie node parent matches expected recursive gossip": - 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 - - for j in 0 ..< testData.recursive_gossip.high: - let - key = ContentKey - .decode( - testData.recursive_gossip[j].content_key.hexToSeqByte().ContentKeyByteList - ) + .decode(testData.content_key.hexToSeqByte().ContentKeyByteList) .get() offer = ContractTrieNodeOffer - .decode(testData.recursive_gossip[j].content_value.hexToSeqByte()) + .decode(testData.content_value_offer.hexToSeqByte()) .get() - (parentKey, parentOffer) = offer.withKey(key.contractTrieNodeKey).getParent() + let (parentKey, parentOffer) = + offer.withKey(key.contractTrieNodeKey).getParent() check: parentKey.path.unpackNibbles().len() < key.contractTrieNodeKey.path.unpackNibbles().len() parentOffer.storageProof.len() == offer.storageProof.len() - 1 parentKey.toContentKey().encode() == - testData.recursive_gossip[j + 1].content_key.hexToSeqByte().ContentKeyByteList - parentOffer.encode() == - testData.recursive_gossip[j + 1].content_value.hexToSeqByte() + parentTestData.content_key.hexToSeqByte().ContentKeyByteList + parentOffer.encode() == parentTestData.content_value_offer.hexToSeqByte() + parentOffer.toRetrievalValue().encode() == + parentTestData.content_value_retrieval.hexToSeqByte() diff --git a/fluffy/tests/state_network_tests/test_state_gossip_gossipoffer_vectors.nim b/fluffy/tests/state_network_tests/test_state_gossip_gossipoffer_vectors.nim index 03bbdd457..b81ea5de9 100644 --- a/fluffy/tests/state_network_tests/test_state_gossip_gossipoffer_vectors.nim +++ b/fluffy/tests/state_network_tests/test_state_gossip_gossipoffer_vectors.nim @@ -42,10 +42,11 @@ procSuite "State Gossip - Gossip Offer": (await stateNode1.portalProtocol().ping(stateNode2.localNode())).isOk() for i, testData in testCase: - if i == 1: - continue # skip scenario with no parent + if i != 0 and i != 3: + continue # skip scenarios with no parent let + parentTestData = testCase[i + 1] stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte()) contentKeyBytes = testData.content_key.hexToSeqByte().ContentKeyByteList contentKey = ContentKey.decode(contentKeyBytes).get() @@ -54,11 +55,10 @@ procSuite "State Gossip - Gossip Offer": contentValue = AccountTrieNodeOffer.decode(contentValueBytes).get() parentContentKeyBytes = - testData.recursive_gossip.content_key.hexToSeqByte().ContentKeyByteList + parentTestData.content_key.hexToSeqByte().ContentKeyByteList parentContentKey = ContentKey.decode(parentContentKeyBytes).get() parentContentId = toContentId(parentContentKeyBytes) - parentContentValueBytes = - testData.recursive_gossip.content_value_offer.hexToSeqByte() + parentContentValueBytes = parentTestData.content_value_offer.hexToSeqByte() parentContentValue = AccountTrieNodeOffer.decode(parentContentValueBytes).get() # set valid state root @@ -115,10 +115,11 @@ procSuite "State Gossip - Gossip Offer": (await stateNode1.portalProtocol().ping(stateNode2.localNode())).isOk() for i, testData in testCase: - if i == 1: - continue # skip scenario with no parent + if i != 0: + continue # skip scenarios with no parent let + parentTestData = testCase[i + 1] stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte()) contentKeyBytes = testData.content_key.hexToSeqByte().ContentKeyByteList contentKey = ContentKey.decode(contentKeyBytes).get() @@ -127,11 +128,10 @@ procSuite "State Gossip - Gossip Offer": contentValue = ContractTrieNodeOffer.decode(contentValueBytes).get() parentContentKeyBytes = - testData.recursive_gossip.content_key.hexToSeqByte().ContentKeyByteList + parentTestData.content_key.hexToSeqByte().ContentKeyByteList parentContentKey = ContentKey.decode(parentContentKeyBytes).get() parentContentId = toContentId(parentContentKeyBytes) - parentContentValueBytes = - testData.recursive_gossip.content_value_offer.hexToSeqByte() + parentContentValueBytes = parentTestData.content_value_offer.hexToSeqByte() parentContentValue = ContractTrieNodeOffer.decode(parentContentValueBytes).get() # set valid state root @@ -225,161 +225,3 @@ procSuite "State Gossip - Gossip Offer": await stateNode1.stop() await stateNode2.stop() - - asyncTest "Recursive gossip account trie nodes": - const file = testVectorDir / "recursive_gossip.yaml" - - let - testCase = YamlRecursiveGossipKVs.loadFromYaml(file).valueOr: - raiseAssert "Cannot read test vector: " & error - stateNode1 = newStateNode(rng, STATE_NODE1_PORT) - stateNode2 = newStateNode(rng, STATE_NODE2_PORT) - - stateNode1.start() - stateNode2.start() - - check: - stateNode1.portalProtocol().addNode(stateNode2.localNode()) == Added - stateNode2.portalProtocol().addNode(stateNode1.localNode()) == Added - (await stateNode1.portalProtocol().ping(stateNode2.localNode())).isOk() - (await stateNode2.portalProtocol().ping(stateNode1.localNode())).isOk() - - for i, testData in testCase: - if i == 1: - continue - - let - stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte()) - leafData = testData.recursive_gossip[0] - contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList - contentKey = ContentKey.decode(contentKeyBytes).get() - contentId = toContentId(contentKeyBytes) - contentValueBytes = leafData.content_value.hexToSeqByte() - contentValue = AccountTrieNodeOffer.decode(contentValueBytes).get() - - # set valid state root - stateNode1.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot) - stateNode2.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot) - - check not stateNode1.containsId(contentId) - check not stateNode2.containsId(contentId) - - # offer the leaf node - await stateNode1.portalProtocol.recursiveGossipOffer( - Opt.none(NodeId), - contentKeyBytes, - contentValueBytes, - contentKey.accountTrieNodeKey, - contentValue, - ) - - # wait for recursive gossip to complete - for node in testData.recursive_gossip: - let keyBytes = node.content_key.hexToSeqByte().ContentKeyByteList - await stateNode2.waitUntilContentAvailable(toContentId(keyBytes)) - - # check that all nodes were received by both state instances - for kv in testData.recursive_gossip: - let - expectedKeyBytes = kv.content_key.hexToSeqByte().ContentKeyByteList - expectedKey = ContentKey.decode(expectedKeyBytes).get() - expectedId = toContentId(expectedKeyBytes) - expectedValue = - AccountTrieNodeOffer.decode(kv.content_value.hexToSeqByte()).get() - res1 = await stateNode1.stateNetwork.getAccountTrieNode( - expectedKey.accountTrieNodeKey - ) - res2 = await stateNode2.stateNetwork.getAccountTrieNode( - expectedKey.accountTrieNodeKey - ) - check: - stateNode1.containsId(expectedId) - stateNode2.containsId(expectedId) - res1.isOk() - res1.get() == expectedValue.toRetrievalValue() - res1.get().node == expectedValue.toRetrievalValue().node - res2.isOk() - res2.get() == expectedValue.toRetrievalValue() - res2.get().node == expectedValue.toRetrievalValue().node - - await stateNode1.stop() - await stateNode2.stop() - - asyncTest "Recursive gossip contract trie nodes": - const file = testVectorDir / "recursive_gossip.yaml" - - let - testCase = YamlRecursiveGossipKVs.loadFromYaml(file).valueOr: - raiseAssert "Cannot read test vector: " & error - stateNode1 = newStateNode(rng, STATE_NODE1_PORT) - stateNode2 = newStateNode(rng, STATE_NODE2_PORT) - - stateNode1.start() - stateNode2.start() - - check: - stateNode1.portalProtocol().addNode(stateNode2.localNode()) == Added - stateNode2.portalProtocol().addNode(stateNode1.localNode()) == Added - (await stateNode1.portalProtocol().ping(stateNode2.localNode())).isOk() - (await stateNode2.portalProtocol().ping(stateNode1.localNode())).isOk() - - for i, testData in testCase: - if i != 1: - continue - - let - stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte()) - leafData = testData.recursive_gossip[0] - contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList - contentKey = ContentKey.decode(contentKeyBytes).get() - contentId = toContentId(contentKeyBytes) - contentValueBytes = leafData.content_value.hexToSeqByte() - contentValue = ContractTrieNodeOffer.decode(contentValueBytes).get() - - # set valid state root - stateNode1.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot) - stateNode2.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot) - - check not stateNode1.containsId(contentId) - check not stateNode2.containsId(contentId) - - # offer the leaf node - await stateNode1.portalProtocol.recursiveGossipOffer( - Opt.none(NodeId), - contentKeyBytes, - contentValueBytes, - contentKey.contractTrieNodeKey, - contentValue, - ) - - # wait for recursive gossip to complete - for node in testData.recursive_gossip: - let keyBytes = node.content_key.hexToSeqByte().ContentKeyByteList - await stateNode2.waitUntilContentAvailable(toContentId(keyBytes)) - - # check that all nodes were received by both state instances - for kv in testData.recursive_gossip: - let - expectedKeyBytes = kv.content_key.hexToSeqByte().ContentKeyByteList - expectedKey = ContentKey.decode(expectedKeyBytes).get() - expectedId = toContentId(expectedKeyBytes) - expectedValue = - ContractTrieNodeOffer.decode(kv.content_value.hexToSeqByte()).get() - res1 = await stateNode1.stateNetwork.getContractTrieNode( - expectedKey.contractTrieNodeKey - ) - res2 = await stateNode2.stateNetwork.getContractTrieNode( - expectedKey.contractTrieNodeKey - ) - check: - stateNode1.containsId(expectedId) - stateNode2.containsId(expectedId) - res1.isOk() - res1.get() == expectedValue.toRetrievalValue() - res1.get().node == expectedValue.toRetrievalValue().node - res2.isOk() - res2.get() == expectedValue.toRetrievalValue() - res2.get().node == expectedValue.toRetrievalValue().node - - await stateNode1.stop() - await stateNode2.stop() diff --git a/fluffy/tests/state_network_tests/test_state_validation_genesis.nim b/fluffy/tests/state_network_tests/test_state_validation_genesis.nim index e6150a018..d6a321de8 100644 --- a/fluffy/tests/state_network_tests/test_state_validation_genesis.nim +++ b/fluffy/tests/state_network_tests/test_state_validation_genesis.nim @@ -26,10 +26,10 @@ template checkValidProofsForExistingLeafs( acc.codeHash = keccakHash(account.code) let + addressHash = address.keccakHash() accountProof = accountState.generateAccountProof(address) - accountPath = removeLeafKeyEndNibbles( - Nibbles.init(keccakHash(address).data, true), accountProof[^1] - ) + accountPath = + removeLeafKeyEndNibbles(Nibbles.init(addressHash.data, true), accountProof[^1]) accountTrieNodeKey = AccountTrieNodeKey( path: accountPath, nodeHash: keccakHash(accountProof[^1].asSeq()) ) @@ -40,7 +40,8 @@ template checkValidProofsForExistingLeafs( check proofResult.isOk() let - contractCodeKey = ContractCodeKey(address: address, codeHash: acc.codeHash) + contractCodeKey = + ContractCodeKey(addressHash: addressHash, codeHash: acc.codeHash) contractCode = ContractCodeOffer(code: Bytecode.init(account.code), accountProof: accountProof) codeResult = @@ -58,7 +59,7 @@ template checkValidProofsForExistingLeafs( Nibbles.init(keccakHash(toBytesBE(slotKey)).data, true), storageProof[^1] ) contractTrieNodeKey = ContractTrieNodeKey( - address: address, + addressHash: addressHash, path: slotPath, nodeHash: keccakHash(storageProof[^1].asSeq()), ) @@ -80,10 +81,10 @@ template checkInvalidProofsWithBadValue( acc.codeHash = keccakHash(account.code) var + addressHash = address.keccakHash() accountProof = accountState.generateAccountProof(address) - accountPath = removeLeafKeyEndNibbles( - Nibbles.init(keccakHash(address).data, true), accountProof[^1] - ) + accountPath = + removeLeafKeyEndNibbles(Nibbles.init(addressHash.data, true), accountProof[^1]) accountTrieNodeKey = AccountTrieNodeKey( path: accountPath, nodeHash: keccakHash(accountProof[^1].asSeq()) ) @@ -96,7 +97,8 @@ template checkInvalidProofsWithBadValue( check proofResult.isErr() let - contractCodeKey = ContractCodeKey(address: address, codeHash: acc.codeHash) + contractCodeKey = + ContractCodeKey(addressHash: addressHash, codeHash: acc.codeHash) contractCode = ContractCodeOffer( code: Bytecode.init(@[1u8, 2, 3]), # bad code value accountProof: accountProof, @@ -116,7 +118,7 @@ template checkInvalidProofsWithBadValue( Nibbles.init(keccakHash(toBytesBE(slotKey)).data, true), storageProof[^1] ) contractTrieNodeKey = ContractTrieNodeKey( - address: address, + addressHash: addressHash, path: slotPath, nodeHash: keccakHash(storageProof[^1].asSeq()), ) diff --git a/fluffy/tests/state_network_tests/test_state_validation_vectors.nim b/fluffy/tests/state_network_tests/test_state_validation_vectors.nim index ca1ce7aa4..82b45ea8f 100644 --- a/fluffy/tests/state_network_tests/test_state_validation_vectors.nim +++ b/fluffy/tests/state_network_tests/test_state_validation_vectors.nim @@ -150,27 +150,13 @@ suite "State Validation - Test Vectors": ) .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().ContentKeyByteList) - .get() - let contentValueOffer = AccountTrieNodeOffer - .decode(testData.recursive_gossip.content_value_offer.hexToSeqByte()) - .get() - - check: - validateOffer( - Opt.some(stateRoot), contentKey.accountTrieNodeKey, contentValueOffer - ) - .isOk() - test "Validate invalid AccountTrieNodeOffer nodes - bad state roots": const file = testVectorDir / "account_trie_node.yaml" const stateRoots = [ "0xBAD7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61", "0xBAD7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61", + "0xBAD7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61", + "0xBAD8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544", "0xBAD8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544", ] @@ -178,16 +164,16 @@ suite "State Validation - Test Vectors": 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().ContentKeyByteList).get() - let contentValueOffer = - AccountTrieNodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get() - - let res = validateOffer( - Opt.some(stateRoot), contentKey.accountTrieNodeKey, contentValueOffer - ) + let + stateRoot = KeccakHash.fromBytes(stateRoots[i].hexToSeqByte()) + contentKey = ContentKey + .decode(testData.content_key.hexToSeqByte().ContentKeyByteList) + .get() + contentValueOffer = + AccountTrieNodeOffer.decode(testData.content_value_offer.hexToSeqByte()).get() + res = validateOffer( + Opt.some(stateRoot), contentKey.accountTrieNodeKey, contentValueOffer + ) check: res.isErr() res.error() == "hash of proof root node doesn't match the expected root hash" @@ -216,7 +202,7 @@ suite "State Validation - Test Vectors": res.error() == "hash of proof root node doesn't match the expected root hash" for i, testData in testCase: - if i == 1: + if i == 2: continue # second test case only has root node var stateRoot = KeccakHash.fromBytes(testData.state_root.hexToSeqByte()) @@ -275,27 +261,12 @@ suite "State Validation - Test Vectors": ) .isOk() - if i == 1: - continue # second test case has no recursive gossip - - let contentKey = ContentKey - .decode(testData.recursive_gossip.content_key.hexToSeqByte().ContentKeyByteList) - .get() - let contentValueOffer = ContractTrieNodeOffer - .decode(testData.recursive_gossip.content_value_offer.hexToSeqByte()) - .get() - - check: - validateOffer( - Opt.some(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", + "0xBAD7b80af0c28bc1489513346d2706885be90abb07f23ca28e50482adb392d61", ] let testCase = YamlTrieNodeKVs.loadFromYaml(file).valueOr: @@ -525,58 +496,3 @@ suite "State Validation - Test Vectors": res.isErr() res.error() == "hash of bytecode doesn't match the code hash in the account proof" - - # 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().ContentKeyByteList).get() - let contentValueOffer = - AccountTrieNodeOffer.decode(kv.content_value.hexToSeqByte()).get() - - check: - validateOffer( - Opt.some(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().ContentKeyByteList).get() - let contentValueOffer = - ContractTrieNodeOffer.decode(kv.content_value.hexToSeqByte()).get() - - check: - validateOffer( - Opt.some(stateRoot), contentKey.contractTrieNodeKey, contentValueOffer - ) - .isOk() diff --git a/fluffy/tools/portal_bridge/portal_bridge_conf.nim b/fluffy/tools/portal_bridge/portal_bridge_conf.nim index 5fd00da9a..f9ea85d63 100644 --- a/fluffy/tools/portal_bridge/portal_bridge_conf.nim +++ b/fluffy/tools/portal_bridge/portal_bridge_conf.nim @@ -152,7 +152,8 @@ type .}: bool gossipGenesis* {. - desc: "Enable gossip of the genesis state into the portal network", + desc: + "Enable gossip of the genesis state into the portal network when starting from block 1", defaultValue: true, name: "gossip-genesis" .}: bool diff --git a/fluffy/tools/portal_bridge/state_bridge/offers_builder.nim b/fluffy/tools/portal_bridge/state_bridge/offers_builder.nim index 0e4f32daa..91579a697 100644 --- a/fluffy/tools/portal_bridge/state_bridge/offers_builder.nim +++ b/fluffy/tools/portal_bridge/state_bridge/offers_builder.nim @@ -27,12 +27,12 @@ proc toTrieProof(proof: seq[seq[byte]]): TrieProof = TrieProof.init(proof.map((node) => TrieNode.init(node))) proc buildAccountTrieNodeOffer( - builder: var OffersBuilder, address: EthAddress, proof: TrieProof + builder: var OffersBuilder, addressHash: content_keys.AddressHash, proof: TrieProof ) = try: let path = removeLeafKeyEndNibbles( - Nibbles.init(worldState.toAccountKey(address).data, isEven = true), proof[^1] + Nibbles.init(addressHash.data, isEven = true), proof[^1] ) offerKey = AccountTrieNodeKey.init(path, keccakHash(proof[^1].asSeq())) offerValue = AccountTrieNodeOffer.init(proof, builder.blockHash) @@ -43,7 +43,7 @@ proc buildAccountTrieNodeOffer( proc buildContractTrieNodeOffer( builder: var OffersBuilder, - address: EthAddress, + addressHash: content_keys.AddressHash, slotHash: SlotKeyHash, storageProof: TrieProof, accountProof: TrieProof, @@ -53,8 +53,9 @@ proc buildContractTrieNodeOffer( path = removeLeafKeyEndNibbles( Nibbles.init(slotHash.data, isEven = true), storageProof[^1] ) - offerKey = - ContractTrieNodeKey.init(address, path, keccakHash(storageProof[^1].asSeq())) + offerKey = ContractTrieNodeKey.init( + addressHash, path, keccakHash(storageProof[^1].asSeq()) + ) offerValue = ContractTrieNodeOffer.init(storageProof, accountProof, builder.blockHash) @@ -64,30 +65,32 @@ proc buildContractTrieNodeOffer( proc buildContractCodeOffer( builder: var OffersBuilder, - address: EthAddress, + addressHash: content_keys.AddressHash, code: seq[byte], accountProof: TrieProof, ) = let #bytecode = Bytelist.init(code) # This fails to compile for some reason bytecode = List[byte, MAX_BYTECODE_LEN](code) - offerKey = ContractCodeKey.init(address, keccakHash(code)) + offerKey = ContractCodeKey.init(addressHash, keccakHash(code)) offerValue = ContractCodeOffer.init(bytecode, accountProof, builder.blockHash) builder.contractCodeOffers.add(offerValue.withKey(offerKey)) proc buildBlockOffers*(builder: var OffersBuilder) = - for address, proof in builder.worldState.updatedAccountProofs(): + for addressHash, proof in builder.worldState.updatedAccountProofs(): let accountProof = toTrieProof(proof) - builder.buildAccountTrieNodeOffer(address, accountProof) + builder.buildAccountTrieNodeOffer(addressHash, accountProof) - for slotHash, sProof in builder.worldState.updatedStorageProofs(address): + for slotHash, sProof in builder.worldState.updatedStorageProofs(addressHash): let storageProof = toTrieProof(sProof) - builder.buildContractTrieNodeOffer(address, slotHash, storageProof, accountProof) + builder.buildContractTrieNodeOffer( + addressHash, slotHash, storageProof, accountProof + ) - let code = builder.worldState.getUpdatedBytecode(address) + let code = builder.worldState.getUpdatedBytecode(addressHash) if code.len() > 0: - builder.buildContractCodeOffer(address, code, accountProof) + builder.buildContractCodeOffer(addressHash, code, accountProof) proc getAccountTrieOffers*(builder: OffersBuilder): lent seq[AccountTrieOfferWithKey] = builder.accountTrieOffers diff --git a/fluffy/tools/portal_bridge/state_bridge/world_state.nim b/fluffy/tools/portal_bridge/state_bridge/world_state.nim index 64e5f7bf6..38aed481e 100644 --- a/fluffy/tools/portal_bridge/state_bridge/world_state.nim +++ b/fluffy/tools/portal_bridge/state_bridge/world_state.nim @@ -109,9 +109,7 @@ proc setAccountPreimage( ) = state.preimagesDb.put(rlp.encode(accountKey), rlp.encode(address)) -proc getAccount*(state: WorldStateRef, address: EthAddress): AccountState = - let accountKey = toAccountKey(address) - +proc getAccount(state: WorldStateRef, accountKey: AddressHash): AccountState = try: if state.accountsTrie.contains(accountKey.data): let accountBytes = state.accountsTrie.get(accountKey.data) @@ -121,6 +119,9 @@ proc getAccount*(state: WorldStateRef, address: EthAddress): AccountState = except RlpError as e: raiseAssert(e.msg) # should never happen unless the database is corrupted +proc getAccount*(state: WorldStateRef, address: EthAddress): AccountState {.inline.} = + state.getAccount(toAccountKey(address)) + proc setAccount*(state: WorldStateRef, address: EthAddress, accState: AccountState) = let accountKey = toAccountKey(address) state.setAccountPreimage(accountKey, address) @@ -166,7 +167,7 @@ proc deleteAccount*(state: WorldStateRef, address: EthAddress) = raiseAssert(e.msg) # should never happen unless the database is corrupted # Returns the account proofs for all the updated accounts from the last transaction -iterator updatedAccountProofs*(state: WorldStateRef): (EthAddress, seq[seq[byte]]) = +iterator updatedAccountProofs*(state: WorldStateRef): (AddressHash, seq[seq[byte]]) = let trie = initHexaryTrie( state.db.getAccountsUpdatedCache(), state.stateRoot(), isPruning = false ) @@ -175,16 +176,15 @@ iterator updatedAccountProofs*(state: WorldStateRef): (EthAddress, seq[seq[byte] for key in trie.keys(): if key.len() == 0: continue # skip the empty node created on initialization - let address = state.getAccountPreimage(KeccakHash.fromBytes(key)) - yield (address, trie.getBranch(key)) + yield (KeccakHash.fromBytes(key), trie.getBranch(key)) except RlpError as e: raiseAssert(e.msg) # should never happen unless the database is corrupted # Returns the storage proofs for the updated slots for the given account from the last transaction iterator updatedStorageProofs*( - state: WorldStateRef, address: EthAddress + state: WorldStateRef, accountKey: AddressHash ): (SlotKeyHash, seq[seq[byte]]) = - let accState = state.getAccount(address) + let accState = state.getAccount(accountKey) let trie = initHexaryTrie( state.db.getStorageUpdatedCache(), accState.account.storageRoot, isPruning = false @@ -199,9 +199,9 @@ iterator updatedStorageProofs*( raiseAssert(e.msg) # should never happen unless the database is corrupted proc getUpdatedBytecode*( - state: WorldStateRef, address: EthAddress + state: WorldStateRef, accountKey: AddressHash ): seq[byte] {.inline.} = - state.db.getBytecodeUpdatedCache().get(toAccountKey(address).data) + state.db.getBytecodeUpdatedCache().get(accountKey.data) # Slow: Used for testing only proc verifyProofs*( @@ -215,11 +215,11 @@ proc verifyProofs*( for k, v in trie.replicate(): memDb.put(k, v) - for address, proof in state.updatedAccountProofs(): + for accountKey, proof in state.updatedAccountProofs(): doAssert isValidBranch( proof, expectedStateRoot, - @(toAccountKey(address).data), + @(accountKey.data), rlpFromBytes(proof[^1]).listElem(1).toBytes(), # pull the value out of the proof ) for p in proof: @@ -228,15 +228,15 @@ proc verifyProofs*( let memTrie = initHexaryTrie(memDb, expectedStateRoot, isPruning = false) doAssert(memTrie.rootHash() == expectedStateRoot) - for address, proof in state.updatedAccountProofs(): + for accountKey, proof in state.updatedAccountProofs(): let - accountBytes = memTrie.get(toAccountKey(address).data) + accountBytes = memTrie.get(accountKey.data) account = rlp.decode(accountBytes, Account) doAssert(accountBytes.len() > 0) doAssert(accountBytes == rlpFromBytes(proof[^1]).listElem(1).toBytes()) # pull the value out of the proof - for slotHash, sProof in state.updatedStorageProofs(address): + for slotHash, sProof in state.updatedStorageProofs(accountKey): doAssert isValidBranch( sProof, account.storageRoot, @@ -245,7 +245,7 @@ proc verifyProofs*( # pull the value out of the proof ) - let updatedCode = state.getUpdatedBytecode(address) + let updatedCode = state.getUpdatedBytecode(accountKey) if updatedCode.len() > 0: doAssert(account.codeHash == keccakHash(updatedCode)) except RlpError as e: diff --git a/vendor/portal-spec-tests b/vendor/portal-spec-tests index 4254dac8c..92b5a99e7 160000 --- a/vendor/portal-spec-tests +++ b/vendor/portal-spec-tests @@ -1 +1 @@ -Subproject commit 4254dac8ce1cbe28fc4704d92aa6809c73451c20 +Subproject commit 92b5a99e748a964fcc5c59e0c6fef248ccdd88f4