From 0b8363764d0705368f2fd94acec124d80fe6bff6 Mon Sep 17 00:00:00 2001 From: web3-developer <51288821+web3-developer@users.noreply.github.com> Date: Thu, 20 Jun 2024 00:21:23 +0800 Subject: [PATCH] More state endpoint tests (#2399) --- fluffy/network/state/state_endpoints.nim | 23 +-- .../all_state_network_tests.nim | 1 + .../test_state_endpoints_genesis.nim | 162 ++++++++++++++++++ 3 files changed, 172 insertions(+), 14 deletions(-) create mode 100644 fluffy/tests/state_network_tests/test_state_endpoints_genesis.nim diff --git a/fluffy/network/state/state_endpoints.nim b/fluffy/network/state/state_endpoints.nim index 5d699c920..b300534ea 100644 --- a/fluffy/network/state/state_endpoints.nim +++ b/fluffy/network/state/state_endpoints.nim @@ -40,15 +40,13 @@ proc getNextNodeHash( let nextNibble = nibbles[nibbleIdx] doAssert(nextNibble < 16) - let nextHashBytes = trieNodeRlp.listElem(nextNibble.int) - doAssert(not nextHashBytes.isEmpty()) + let nextHashBytes = trieNodeRlp.listElem(nextNibble.int).toBytes() + if nextHashBytes.len() == 0: + return Opt.none((Nibbles, NodeHash)) nibbleIdx += 1 return Opt.some( - ( - nibbles[0 ..< nibbleIdx].packNibbles(), - KeccakHash.fromBytes(nextHashBytes.toBytes()), - ) + (nibbles[0 ..< nibbleIdx].packNibbles(), KeccakHash.fromBytes(nextHashBytes)) ) # leaf or extension node @@ -57,16 +55,13 @@ proc getNextNodeHash( return Opt.none((Nibbles, NodeHash)) # extension node + let nextHashBytes = trieNodeRlp.listElem(1).toBytes() + if nextHashBytes.len() == 0: + return Opt.none((Nibbles, NodeHash)) + nibbleIdx += prefix.unpackNibbles().len() - - let nextHashBytes = trieNodeRlp.listElem(1) - doAssert(not nextHashBytes.isEmpty()) - Opt.some( - ( - nibbles[0 ..< nibbleIdx].packNibbles(), - KeccakHash.fromBytes(nextHashBytes.toBytes()), - ) + (nibbles[0 ..< nibbleIdx].packNibbles(), KeccakHash.fromBytes(nextHashBytes)) ) except RlpError as e: raiseAssert(e.msg) diff --git a/fluffy/tests/state_network_tests/all_state_network_tests.nim b/fluffy/tests/state_network_tests/all_state_network_tests.nim index fd4487d99..1f92b3ebc 100644 --- a/fluffy/tests/state_network_tests/all_state_network_tests.nim +++ b/fluffy/tests/state_network_tests/all_state_network_tests.nim @@ -11,6 +11,7 @@ import ./test_state_content_keys_vectors, ./test_state_content_nibbles, ./test_state_content_values_vectors, + ./test_state_endpoints_genesis, ./test_state_endpoints_vectors, ./test_state_gossip_getparent_genesis, ./test_state_gossip_getparent_vectors, diff --git a/fluffy/tests/state_network_tests/test_state_endpoints_genesis.nim b/fluffy/tests/state_network_tests/test_state_endpoints_genesis.nim new file mode 100644 index 000000000..9292d4d86 --- /dev/null +++ b/fluffy/tests/state_network_tests/test_state_endpoints_genesis.nim @@ -0,0 +1,162 @@ +# Nimbus +# 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. + +{.push raises: [].} + +import + std/os, + testutils/unittests, + chronos, + results, + eth/[common, trie], + ../../../nimbus/common/chain_config, + ../../network/wire/[portal_protocol, portal_stream], + ../../network/state/ + [state_content, state_network, state_gossip, state_endpoints, state_utils], + ../../database/content_db, + ./state_test_helpers + +suite "State Endpoints - Genesis JSON Files": + const STATE_NODE1_PORT = 20702 + + const genesisFiles = [ + "berlin2000.json", "calaveras.json", "chainid1.json", "chainid7.json", + "devnet4.json", "devnet5.json", "holesky.json", "mainshadow1.json", "merge.json", + ] + + asyncTest "Test getBalance, getTransactionCount, getStorageAt and getCode using JSON files": + let + rng = newRng() + stateNode1 = newStateNode(rng, STATE_NODE1_PORT) + + for file in genesisFiles: + let + accounts = getGenesisAlloc("fluffy" / "tests" / "custom_genesis" / file) + (accountState, storageStates) = accounts.toState() + stateRoot = accountState.rootHash() + + for address, account in accounts: + let + proof = accountState.generateAccountProof(address) + leafNode = proof[^1] + addressHash = keccakHash(address).data + path = removeLeafKeyEndNibbles(Nibbles.init(addressHash, true), leafNode) + key = AccountTrieNodeKey.init(path, keccakHash(leafNode.asSeq())) + offer = AccountTrieNodeOffer(proof: proof) + + # store the account leaf node + let contentKey = key.toContentKey().encode() + stateNode1.portalProtocol.storeContent( + contentKey, contentKey.toContentId(), offer.toRetrievalValue().encode() + ) + + # store the account parent nodes / all remaining nodes + var + parent = offer.withKey(key).getParent() + parentContentKey = parent.key.toContentKey().encode() + + stateNode1.portalProtocol.storeContent( + parentContentKey, + parentContentKey.toContentId(), + parent.offer.toRetrievalValue().encode(), + ) + + for i in proof.low ..< proof.high - 1: + parent = parent.getParent() + parentContentKey = parent.key.toContentKey().encode() + + stateNode1.portalProtocol.storeContent( + parentContentKey, + parentContentKey.toContentId(), + parent.offer.toRetrievalValue().encode(), + ) + + # mock the block hash because we don't have history network running + stateNode1.mockBlockHashToStateRoot(offer.blockHash, stateRoot) + + # verify can lookup account values by walking the trie via the state network endpoints + let + balanceRes = + await stateNode1.stateNetwork.getBalance(offer.blockHash, address) + nonceRes = + await stateNode1.stateNetwork.getTransactionCount(offer.blockHash, address) + check: + balanceRes.isOk() + balanceRes.get() == account.balance + nonceRes.isOk() + nonceRes.get() == account.nonce + + if account.code.len() > 0: + block: + # store the code + let + key = + ContractCodeKey(address: address, codeHash: keccakHash(account.code)) + value = ContractCodeRetrieval(code: Bytecode.init(account.code)) + + let contentKey = key.toContentKey().encode() + stateNode1.portalProtocol.storeContent( + contentKey, contentKey.toContentId(), value.encode() + ) + + # verify can lookup code by walking the trie via the state network endpoints + let codeRes = + await stateNode1.stateNetwork.getCode(offer.blockHash, address) + check: + codeRes.isOk() + codeRes.get().asSeq() == account.code + + # next test the storage for accounts that have code + let storageState = storageStates[address] + for slotKey, slotValue in account.storage: + let + storageProof = storageState.generateStorageProof(slotKey) + leafNode = storageProof[^1] + slotKeyHash = keccakHash(toBytesBE(slotKey)).data + path = removeLeafKeyEndNibbles( + Nibbles.init(keccakHash(toBytesBE(slotKey)).data, true), leafNode + ) + key = ContractTrieNodeKey( + address: address, path: path, nodeHash: keccakHash(leafNode.asSeq()) + ) + offer = + ContractTrieNodeOffer(storageProof: storageProof, accountProof: proof) + + # store the contract storage leaf node + let contentKey = key.toContentKey().encode() + stateNode1.portalProtocol.storeContent( + contentKey, contentKey.toContentId(), offer.toRetrievalValue().encode() + ) + + # store the remaining contract storage nodes + var + parent = offer.withKey(key).getParent() + parentContentKey = parent.key.toContentKey().encode() + + stateNode1.portalProtocol.storeContent( + parentContentKey, + parentContentKey.toContentId(), + parent.offer.toRetrievalValue().encode(), + ) + + for i in storageProof.low ..< storageProof.high - 1: + parent = parent.getParent() + parentContentKey = parent.key.toContentKey().encode() + + stateNode1.portalProtocol.storeContent( + parentContentKey, + parentContentKey.toContentId(), + parent.offer.toRetrievalValue().encode(), + ) + + # verify can lookup contract values by walking the trie via the state network endpoints + let storageAtRes = await stateNode1.stateNetwork.getStorageAt( + offer.blockHash, address, slotKey + ) + check: + storageAtRes.isOk() + storageAtRes.get() == slotValue