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.
This commit is contained in:
web3-developer 2024-08-08 00:01:30 +08:00 committed by GitHub
parent d5786758b5
commit 93a160b569
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 188 additions and 489 deletions

View File

@ -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

View File

@ -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)

View File

@ -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"

View File

@ -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

View File

@ -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.} =

View File

@ -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,
)

View File

@ -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)

View File

@ -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":

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()),
)

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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:

@ -1 +1 @@
Subproject commit 4254dac8ce1cbe28fc4704d92aa6809c73451c20
Subproject commit 92b5a99e748a964fcc5c59e0c6fef248ccdd88f4