Fluffy: Implement poke in state network (#2750)

* Implement poke in state network.
This commit is contained in:
bhartnett 2024-11-28 20:33:57 +08:00 committed by GitHub
parent 5e90522e70
commit 23a43d1d15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 298 additions and 146 deletions

View File

@ -60,12 +60,12 @@ type
ContentKeyType* = AccountTrieNodeKey | ContractTrieNodeKey | ContractCodeKey ContentKeyType* = AccountTrieNodeKey | ContractTrieNodeKey | ContractCodeKey
func init*(T: type AccountTrieNodeKey, path: Nibbles, nodeHash: Hash32): T {.inline.} = func init*(T: type AccountTrieNodeKey, path: Nibbles, nodeHash: Hash32): T =
T(path: path, nodeHash: nodeHash) T(path: path, nodeHash: nodeHash)
func init*( func init*(
T: type ContractTrieNodeKey, addressHash: Hash32, path: Nibbles, nodeHash: Hash32 T: type ContractTrieNodeKey, addressHash: Hash32, path: Nibbles, nodeHash: Hash32
): T {.inline.} = ): T =
T(addressHash: addressHash, path: path, nodeHash: nodeHash) T(addressHash: addressHash, path: path, nodeHash: nodeHash)
func init*( func init*(
@ -73,13 +73,13 @@ func init*(
): T {.inline.} = ): T {.inline.} =
T(addressHash: addressHash, codeHash: codeHash) T(addressHash: addressHash, codeHash: codeHash)
func toContentKey*(key: AccountTrieNodeKey): ContentKey {.inline.} = template toContentKey*(key: AccountTrieNodeKey): ContentKey =
ContentKey(contentType: accountTrieNode, accountTrieNodeKey: key) ContentKey(contentType: accountTrieNode, accountTrieNodeKey: key)
func toContentKey*(key: ContractTrieNodeKey): ContentKey {.inline.} = template toContentKey*(key: ContractTrieNodeKey): ContentKey =
ContentKey(contentType: contractTrieNode, contractTrieNodeKey: key) ContentKey(contentType: contractTrieNode, contractTrieNodeKey: key)
func toContentKey*(key: ContractCodeKey): ContentKey {.inline.} = template toContentKey*(key: ContractCodeKey): ContentKey =
ContentKey(contentType: contractCode, contractCodeKey: key) ContentKey(contentType: contractCode, contractCodeKey: key)
proc readSszBytes*(data: openArray[byte], val: var ContentKey) {.raises: [SszError].} = proc readSszBytes*(data: openArray[byte], val: var ContentKey) {.raises: [SszError].} =
@ -89,7 +89,7 @@ proc readSszBytes*(data: openArray[byte], val: var ContentKey) {.raises: [SszErr
readSszValue(data, val) readSszValue(data, val)
func encode*(contentKey: ContentKey): ContentKeyByteList {.inline.} = func encode*(contentKey: ContentKey): ContentKeyByteList =
doAssert(contentKey.contentType != unused) doAssert(contentKey.contentType != unused)
ContentKeyByteList.init(SSZ.encode(contentKey)) ContentKeyByteList.init(SSZ.encode(contentKey))
@ -101,6 +101,6 @@ func decode*(
return err("ContentKey contentType: unused") return err("ContentKey contentType: unused")
ok(key) ok(key)
func toContentId*(contentKey: ContentKeyByteList): ContentId {.inline.} = func toContentId*(contentKey: ContentKeyByteList): ContentId =
let idHash = sha256.digest(contentKey.asSeq()) let idHash = sha256.digest(contentKey.asSeq())
readUintBE[256](idHash.data) readUintBE[256](idHash.data)

View File

@ -52,12 +52,10 @@ type
AccountTrieNodeRetrieval | ContractTrieNodeRetrieval | ContractCodeRetrieval AccountTrieNodeRetrieval | ContractTrieNodeRetrieval | ContractCodeRetrieval
ContentValueType* = ContentOfferType | ContentRetrievalType ContentValueType* = ContentOfferType | ContentRetrievalType
func init*( func init*(T: type AccountTrieNodeOffer, proof: TrieProof, blockHash: Hash32): T =
T: type AccountTrieNodeOffer, proof: TrieProof, blockHash: Hash32
): T {.inline.} =
T(proof: proof, blockHash: blockHash) T(proof: proof, blockHash: blockHash)
func init*(T: type AccountTrieNodeRetrieval, node: TrieNode): T {.inline.} = func init*(T: type AccountTrieNodeRetrieval, node: TrieNode): T =
T(node: node) T(node: node)
func init*( func init*(
@ -65,10 +63,10 @@ func init*(
storageProof: TrieProof, storageProof: TrieProof,
accountProof: TrieProof, accountProof: TrieProof,
blockHash: Hash32, blockHash: Hash32,
): T {.inline.} = ): T =
T(storageProof: storageProof, accountProof: accountProof, blockHash: blockHash) T(storageProof: storageProof, accountProof: accountProof, blockHash: blockHash)
func init*(T: type ContractTrieNodeRetrieval, node: TrieNode): T {.inline.} = func init*(T: type ContractTrieNodeRetrieval, node: TrieNode): T =
T(node: node) T(node: node)
func init*( func init*(
@ -76,35 +74,50 @@ func init*(
code: Bytecode, code: Bytecode,
accountProof: TrieProof, accountProof: TrieProof,
blockHash: Hash32, blockHash: Hash32,
): T {.inline.} = ): T =
T(code: code, accountProof: accountProof, blockHash: blockHash) T(code: code, accountProof: accountProof, blockHash: blockHash)
func init*(T: type ContractCodeRetrieval, code: Bytecode): T {.inline.} = func init*(T: type ContractCodeRetrieval, code: Bytecode): T =
T(code: code) T(code: code)
func toRetrievalValue*( template toRetrieval*(offer: AccountTrieNodeOffer): AccountTrieNodeRetrieval =
offer: AccountTrieNodeOffer
): AccountTrieNodeRetrieval {.inline.} =
AccountTrieNodeRetrieval.init(offer.proof[^1]) AccountTrieNodeRetrieval.init(offer.proof[^1])
func toRetrievalValue*( template toRetrieval*(offer: ContractTrieNodeOffer): ContractTrieNodeRetrieval =
offer: ContractTrieNodeOffer
): ContractTrieNodeRetrieval {.inline.} =
ContractTrieNodeRetrieval.init(offer.storageProof[^1]) ContractTrieNodeRetrieval.init(offer.storageProof[^1])
func toRetrievalValue*(offer: ContractCodeOffer): ContractCodeRetrieval {.inline.} = template toRetrieval*(offer: ContractCodeOffer): ContractCodeRetrieval =
ContractCodeRetrieval.init(offer.code) ContractCodeRetrieval.init(offer.code)
func empty*(T: type TrieProof): T {.inline.} = func toOffer*(
retrieval: AccountTrieNodeRetrieval, parent: AccountTrieNodeOffer
): AccountTrieNodeOffer =
var proof = parent.proof
let added = proof.add(retrieval.node)
doAssert(added)
AccountTrieNodeOffer.init(proof, parent.blockHash)
func toOffer*(
retrieval: ContractTrieNodeRetrieval, parent: ContractTrieNodeOffer
): ContractTrieNodeOffer =
var proof = parent.storageProof
let added = proof.add(retrieval.node)
doAssert(added)
ContractTrieNodeOffer.init(proof, parent.accountProof, parent.blockHash)
func toOffer*(
retrieval: ContractCodeRetrieval, parent: ContractCodeOffer
): ContractCodeOffer =
ContractCodeOffer.init(retrieval.code, parent.accountProof, parent.blockHash)
template empty*(T: type TrieProof): T =
T.init(@[]) T.init(@[])
func empty*(T: type Bytecode): T {.inline.} = template empty*(T: type Bytecode): T =
T(@[]) T(@[])
func encode*(value: ContentValueType): seq[byte] {.inline.} = func encode*(value: ContentValueType): seq[byte] =
SSZ.encode(value) SSZ.encode(value)
func decode*( func decode*(T: type ContentValueType, bytes: openArray[byte]): Result[T, string] =
T: type ContentValueType, bytes: openArray[byte]
): Result[T, string] {.inline.} =
decodeSsz(bytes, T) decodeSsz(bytes, T)

View File

@ -16,7 +16,9 @@ import
./state_network, ./state_network,
./state_utils ./state_utils
export results, state_network from eth/common/eth_types_rlp import rlpHash
export results, state_network, hashes, addresses
logScope: logScope:
topics = "portal_state" topics = "portal_state"
@ -72,7 +74,10 @@ proc getNextNodeHash(
raiseAssert(e.msg) raiseAssert(e.msg)
proc getAccountProof( proc getAccountProof(
n: StateNetwork, stateRoot: Hash32, address: Address n: StateNetwork,
stateRoot: Hash32,
address: Address,
maybeBlockHash: Opt[Hash32], # required for poke
): Future[Result[(TrieProof, bool), string]] {.async: (raises: [CancelledError]).} = ): Future[Result[(TrieProof, bool), string]] {.async: (raises: [CancelledError]).} =
let nibbles = address.toPath().unpackNibbles() let nibbles = address.toPath().unpackNibbles()
@ -81,15 +86,26 @@ proc getAccountProof(
key = AccountTrieNodeKey.init(Nibbles.empty(), stateRoot) key = AccountTrieNodeKey.init(Nibbles.empty(), stateRoot)
proof = TrieProof.empty() proof = TrieProof.empty()
while nibblesIdx < nibbles.len(): # Construct the parent offer which is used to provide the data needed to
let accountTrieNode = (await n.getAccountTrieNode(key)).valueOr: # implement poke after the account trie node has been retrieved
return err("Failed to get account trie node when building account proof") maybeParentOffer =
if maybeBlockHash.isSome():
Opt.some(AccountTrieNodeOffer.init(proof, maybeBlockHash.get()))
else:
Opt.none(AccountTrieNodeOffer)
while nibblesIdx < nibbles.len():
let let
accountTrieNode = (await n.getAccountTrieNode(key, maybeParentOffer)).valueOr:
return err("Failed to get account trie node when building account proof")
trieNode = accountTrieNode.node trieNode = accountTrieNode.node
added = proof.add(trieNode) added = proof.add(trieNode)
doAssert(added) doAssert(added)
if maybeParentOffer.isSome():
let added = maybeParentOffer.get().proof.add(trieNode)
doAssert(added)
let (nextPath, nextNodeHash) = trieNode.getNextNodeHash(nibbles, nibblesIdx).valueOr: let (nextPath, nextNodeHash) = trieNode.getNextNodeHash(nibbles, nibblesIdx).valueOr:
break break
@ -99,7 +115,12 @@ proc getAccountProof(
ok((proof, nibblesIdx == nibbles.len())) ok((proof, nibblesIdx == nibbles.len()))
proc getStorageProof( proc getStorageProof(
n: StateNetwork, storageRoot: Hash32, address: Address, slotKey: UInt256 n: StateNetwork,
storageRoot: Hash32,
address: Address,
slotKey: UInt256,
maybeBlockHash: Opt[Hash32],
maybeAccProof: Opt[TrieProof],
): Future[Result[(TrieProof, bool), string]] {.async: (raises: [CancelledError]).} = ): Future[Result[(TrieProof, bool), string]] {.async: (raises: [CancelledError]).} =
let nibbles = slotKey.toPath().unpackNibbles() let nibbles = slotKey.toPath().unpackNibbles()
@ -109,15 +130,29 @@ proc getStorageProof(
key = ContractTrieNodeKey.init(addressHash, Nibbles.empty(), storageRoot) key = ContractTrieNodeKey.init(addressHash, Nibbles.empty(), storageRoot)
proof = TrieProof.empty() proof = TrieProof.empty()
while nibblesIdx < nibbles.len(): # Construct the parent offer which is used to provide the data needed to
let contractTrieNode = (await n.getContractTrieNode(key)).valueOr: # implement poke after the contract trie node has been retrieved
return err("Failed to get contract trie node when building account proof") maybeParentOffer =
if maybeBlockHash.isSome():
doAssert maybeAccProof.isSome()
Opt.some(
ContractTrieNodeOffer.init(proof, maybeAccProof.get(), maybeBlockHash.get())
)
else:
Opt.none(ContractTrieNodeOffer)
while nibblesIdx < nibbles.len():
let let
contractTrieNode = (await n.getContractTrieNode(key, maybeParentOffer)).valueOr:
return err("Failed to get contract trie node when building storage proof")
trieNode = contractTrieNode.node trieNode = contractTrieNode.node
added = proof.add(trieNode) added = proof.add(trieNode)
doAssert(added) doAssert(added)
if maybeParentOffer.isSome():
let added = maybeParentOffer.get().storageProof.add(trieNode)
doAssert(added)
let (nextPath, nextNodeHash) = trieNode.getNextNodeHash(nibbles, nibblesIdx).valueOr: let (nextPath, nextNodeHash) = trieNode.getNextNodeHash(nibbles, nibblesIdx).valueOr:
break break
@ -127,9 +162,11 @@ proc getStorageProof(
ok((proof, nibblesIdx == nibbles.len())) ok((proof, nibblesIdx == nibbles.len()))
proc getAccount( proc getAccount(
n: StateNetwork, stateRoot: Hash32, address: Address n: StateNetwork, stateRoot: Hash32, address: Address, maybeBlockHash: Opt[Hash32]
): Future[Opt[Account]] {.async: (raises: [CancelledError]).} = ): Future[Opt[Account]] {.async: (raises: [CancelledError]).} =
let (accountProof, exists) = (await n.getAccountProof(stateRoot, address)).valueOr: let (accountProof, exists) = (
await n.getAccountProof(stateRoot, address, maybeBlockHash)
).valueOr:
warn "Failed to get account proof", error = error warn "Failed to get account proof", error = error
return Opt.none(Account) return Opt.none(Account)
@ -145,39 +182,64 @@ proc getAccount(
Opt.some(account) Opt.some(account)
proc getBalanceByStateRoot*( proc getBalanceByStateRoot*(
n: StateNetwork, stateRoot: Hash32, address: Address n: StateNetwork,
stateRoot: Hash32,
address: Address,
maybeBlockHash = Opt.none(Hash32),
): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} = ): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} =
let account = (await n.getAccount(stateRoot, address)).valueOr: let account = (await n.getAccount(stateRoot, address, maybeBlockHash)).valueOr:
return Opt.none(UInt256) return Opt.none(UInt256)
Opt.some(account.balance) Opt.some(account.balance)
proc getTransactionCountByStateRoot*( proc getTransactionCountByStateRoot*(
n: StateNetwork, stateRoot: Hash32, address: Address n: StateNetwork,
stateRoot: Hash32,
address: Address,
maybeBlockHash = Opt.none(Hash32),
): Future[Opt[AccountNonce]] {.async: (raises: [CancelledError]).} = ): Future[Opt[AccountNonce]] {.async: (raises: [CancelledError]).} =
let account = (await n.getAccount(stateRoot, address)).valueOr: let account = (await n.getAccount(stateRoot, address, maybeBlockHash)).valueOr:
return Opt.none(AccountNonce) return Opt.none(AccountNonce)
Opt.some(account.nonce) Opt.some(account.nonce)
proc getStorageAtByStateRoot*( proc getStorageAtByStateRoot*(
n: StateNetwork, stateRoot: Hash32, address: Address, slotKey: UInt256 n: StateNetwork,
stateRoot: Hash32,
address: Address,
slotKey: UInt256,
maybeBlockHash = Opt.none(Hash32),
): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} = ): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} =
let account = (await n.getAccount(stateRoot, address)).valueOr: let (accountProof, exists) = (
await n.getAccountProof(stateRoot, address, maybeBlockHash)
).valueOr:
warn "Failed to get account proof", error = error
return Opt.none(UInt256) return Opt.none(UInt256)
let account =
if exists:
accountProof.toAccount().valueOr:
error "Failed to get account from accountProof"
return Opt.none(UInt256)
else:
info "Account doesn't exist, returning default account"
# return an empty account if the account doesn't exist
EMPTY_ACCOUNT
if account.storageRoot == EMPTY_ROOT_HASH: if account.storageRoot == EMPTY_ROOT_HASH:
info "Storage doesn't exist, returning default storage value" info "Storage doesn't exist, returning default storage value"
# return zero if the storage doesn't exist # return zero if the storage doesn't exist
return Opt.some(0.u256) return Opt.some(0.u256)
let (storageProof, exists) = ( let (storageProof, slotExists) = (
await n.getStorageProof(account.storageRoot, address, slotKey) await n.getStorageProof(
account.storageRoot, address, slotKey, maybeBlockHash, Opt.some(accountProof)
)
).valueOr: ).valueOr:
warn "Failed to get storage proof", error = error warn "Failed to get storage proof", error = error
return Opt.none(UInt256) return Opt.none(UInt256)
if not exists: if not slotExists:
info "Slot doesn't exist, returning default storage value" info "Slot doesn't exist, returning default storage value"
# return zero if the slot doesn't exist # return zero if the slot doesn't exist
return Opt.some(0.u256) return Opt.some(0.u256)
@ -189,11 +251,27 @@ proc getStorageAtByStateRoot*(
Opt.some(slotValue) Opt.some(slotValue)
proc getCodeByStateRoot*( proc getCodeByStateRoot*(
n: StateNetwork, stateRoot: Hash32, address: Address n: StateNetwork,
stateRoot: Hash32,
address: Address,
maybeBlockHash = Opt.none(Hash32),
): Future[Opt[Bytecode]] {.async: (raises: [CancelledError]).} = ): Future[Opt[Bytecode]] {.async: (raises: [CancelledError]).} =
let account = (await n.getAccount(stateRoot, address)).valueOr: let (accountProof, exists) = (
await n.getAccountProof(stateRoot, address, maybeBlockHash)
).valueOr:
warn "Failed to get account proof", error = error
return Opt.none(Bytecode) return Opt.none(Bytecode)
let account =
if exists:
accountProof.toAccount().valueOr:
error "Failed to get account from accountProof"
return Opt.none(Bytecode)
else:
info "Account doesn't exist, returning default account"
# return an empty account if the account doesn't exist
EMPTY_ACCOUNT
if account.codeHash == EMPTY_CODE_HASH: if account.codeHash == EMPTY_CODE_HASH:
info "Code doesn't exist, returning default code value" info "Code doesn't exist, returning default code value"
# return empty bytecode if the code doesn't exist # return empty bytecode if the code doesn't exist
@ -201,7 +279,14 @@ proc getCodeByStateRoot*(
let let
contractCodeKey = ContractCodeKey.init(keccak256(address.data), account.codeHash) contractCodeKey = ContractCodeKey.init(keccak256(address.data), account.codeHash)
contractCodeRetrieval = (await n.getContractCode(contractCodeKey)).valueOr: maybeParentOffer =
if maybeBlockHash.isSome():
Opt.some(
ContractCodeOffer.init(Bytecode.empty(), accountProof, maybeBlockHash.get())
)
else:
Opt.none(ContractCodeOffer)
contractCodeRetrieval = (await n.getContractCode(contractCodeKey, maybeParentOffer)).valueOr:
warn "Failed to get contract code" warn "Failed to get contract code"
return Opt.none(Bytecode) return Opt.none(Bytecode)
@ -214,10 +299,16 @@ type Proofs* = ref object
slotProofs*: seq[TrieProof] slotProofs*: seq[TrieProof]
proc getProofsByStateRoot*( proc getProofsByStateRoot*(
n: StateNetwork, stateRoot: Hash32, address: Address, slotKeys: seq[UInt256] n: StateNetwork,
stateRoot: Hash32,
address: Address,
slotKeys: seq[UInt256],
maybeBlockHash = Opt.none(Hash32),
): Future[Opt[Proofs]] {.async: (raises: [CancelledError]).} = ): Future[Opt[Proofs]] {.async: (raises: [CancelledError]).} =
let let
(accountProof, accountExists) = (await n.getAccountProof(stateRoot, address)).valueOr: (accountProof, accountExists) = (
await n.getAccountProof(stateRoot, address, maybeBlockHash)
).valueOr:
warn "Failed to get account proof", error = error warn "Failed to get account proof", error = error
return Opt.none(Proofs) return Opt.none(Proofs)
account = account =
@ -241,7 +332,9 @@ proc getProofsByStateRoot*(
let let
(storageProof, slotExists) = ( (storageProof, slotExists) = (
await n.getStorageProof(account.storageRoot, address, slotKey) await n.getStorageProof(
account.storageRoot, address, slotKey, maybeBlockHash, Opt.some(accountProof)
)
).valueOr: ).valueOr:
warn "Failed to get storage proof", error = error warn "Failed to get storage proof", error = error
return Opt.none(Proofs) return Opt.none(Proofs)
@ -265,41 +358,45 @@ proc getProofsByStateRoot*(
proc getBalance*( proc getBalance*(
n: StateNetwork, blockNumOrHash: uint64 | Hash32, address: Address n: StateNetwork, blockNumOrHash: uint64 | Hash32, address: Address
): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} = ): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} =
let stateRoot = (await n.getStateRootByBlockNumOrHash(blockNumOrHash)).valueOr: let header = (await n.getBlockHeaderByBlockNumOrHash(blockNumOrHash)).valueOr:
warn "Failed to get state root by block number or hash", blockNumOrHash warn "Failed to get block header by block number or hash", blockNumOrHash
return Opt.none(UInt256) return Opt.none(UInt256)
await n.getBalanceByStateRoot(stateRoot, address) await n.getBalanceByStateRoot(header.stateRoot, address, Opt.some(header.rlpHash()))
# Used by: eth_getTransactionCount # Used by: eth_getTransactionCount
proc getTransactionCount*( proc getTransactionCount*(
n: StateNetwork, blockNumOrHash: uint64 | Hash32, address: Address n: StateNetwork, blockNumOrHash: uint64 | Hash32, address: Address
): Future[Opt[AccountNonce]] {.async: (raises: [CancelledError]).} = ): Future[Opt[AccountNonce]] {.async: (raises: [CancelledError]).} =
let stateRoot = (await n.getStateRootByBlockNumOrHash(blockNumOrHash)).valueOr: let header = (await n.getBlockHeaderByBlockNumOrHash(blockNumOrHash)).valueOr:
warn "Failed to get state root by block number or hash", blockNumOrHash warn "Failed to get block header by block number or hash", blockNumOrHash
return Opt.none(AccountNonce) return Opt.none(AccountNonce)
await n.getTransactionCountByStateRoot(stateRoot, address) await n.getTransactionCountByStateRoot(
header.stateRoot, address, Opt.some(header.rlpHash())
)
# Used by: eth_getStorageAt # Used by: eth_getStorageAt
proc getStorageAt*( proc getStorageAt*(
n: StateNetwork, blockNumOrHash: uint64 | Hash32, address: Address, slotKey: UInt256 n: StateNetwork, blockNumOrHash: uint64 | Hash32, address: Address, slotKey: UInt256
): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} = ): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} =
let stateRoot = (await n.getStateRootByBlockNumOrHash(blockNumOrHash)).valueOr: let header = (await n.getBlockHeaderByBlockNumOrHash(blockNumOrHash)).valueOr:
warn "Failed to get state root by block number or hash", blockNumOrHash warn "Failed to get block header by block number or hash", blockNumOrHash
return Opt.none(UInt256) return Opt.none(UInt256)
await n.getStorageAtByStateRoot(stateRoot, address, slotKey) await n.getStorageAtByStateRoot(
header.stateRoot, address, slotKey, Opt.some(header.rlpHash())
)
# Used by: eth_getCode # Used by: eth_getCode
proc getCode*( proc getCode*(
n: StateNetwork, blockNumOrHash: uint64 | Hash32, address: Address n: StateNetwork, blockNumOrHash: uint64 | Hash32, address: Address
): Future[Opt[Bytecode]] {.async: (raises: [CancelledError]).} = ): Future[Opt[Bytecode]] {.async: (raises: [CancelledError]).} =
let stateRoot = (await n.getStateRootByBlockNumOrHash(blockNumOrHash)).valueOr: let header = (await n.getBlockHeaderByBlockNumOrHash(blockNumOrHash)).valueOr:
warn "Failed to get state root by block number or hash", blockNumOrHash warn "Failed to get block header by block number or hash", blockNumOrHash
return Opt.none(Bytecode) return Opt.none(Bytecode)
await n.getCodeByStateRoot(stateRoot, address) await n.getCodeByStateRoot(header.stateRoot, address, Opt.some(header.rlpHash()))
# Used by: eth_getProof # Used by: eth_getProof
proc getProofs*( proc getProofs*(
@ -308,8 +405,10 @@ proc getProofs*(
address: Address, address: Address,
slotKeys: seq[UInt256], slotKeys: seq[UInt256],
): Future[Opt[Proofs]] {.async: (raises: [CancelledError]).} = ): Future[Opt[Proofs]] {.async: (raises: [CancelledError]).} =
let stateRoot = (await n.getStateRootByBlockNumOrHash(blockNumOrHash)).valueOr: let header = (await n.getBlockHeaderByBlockNumOrHash(blockNumOrHash)).valueOr:
warn "Failed to get state root by block number or hash", blockNumOrHash warn "Failed to get block header by block number or hash", blockNumOrHash
return Opt.none(Proofs) return Opt.none(Proofs)
await n.getProofsByStateRoot(stateRoot, address, slotKeys) await n.getProofsByStateRoot(
header.stateRoot, address, slotKeys, Opt.some(header.rlpHash())
)

View File

@ -81,8 +81,9 @@ proc new*(
proc getContent( proc getContent(
n: StateNetwork, n: StateNetwork,
key: AccountTrieNodeKey | ContractTrieNodeKey | ContractCodeKey, key: ContentKeyType,
V: type ContentRetrievalType, V: type ContentRetrievalType,
maybeParentOffer: Opt[ContentOfferType],
): Future[Opt[V]] {.async: (raises: [CancelledError]).} = ): Future[Opt[V]] {.async: (raises: [CancelledError]).} =
let let
contentKeyBytes = key.toContentKey().encode() contentKeyBytes = key.toContentKey().encode()
@ -118,42 +119,54 @@ proc getContent(
contentKeyBytes, contentId, contentValueBytes, cacheContent = true contentKeyBytes, contentId, contentValueBytes, cacheContent = true
) )
if maybeParentOffer.isSome():
let offer = contentValue.toOffer(maybeParentOffer.get())
n.portalProtocol.triggerPoke(
contentLookupResult.nodesInterestedInContent, contentKeyBytes, offer.encode()
)
return Opt.some(contentValue) return Opt.some(contentValue)
# Content was requested `1 + requestRetries` times and all failed on validation # Content was requested `1 + requestRetries` times and all failed on validation
Opt.none(V) Opt.none(V)
proc getAccountTrieNode*( proc getAccountTrieNode*(
n: StateNetwork, key: AccountTrieNodeKey n: StateNetwork,
key: AccountTrieNodeKey,
maybeParentOffer = Opt.none(AccountTrieNodeOffer),
): Future[Opt[AccountTrieNodeRetrieval]] {. ): Future[Opt[AccountTrieNodeRetrieval]] {.
async: (raw: true, raises: [CancelledError]) async: (raw: true, raises: [CancelledError])
.} = .} =
n.getContent(key, AccountTrieNodeRetrieval) n.getContent(key, AccountTrieNodeRetrieval, maybeParentOffer)
proc getContractTrieNode*( proc getContractTrieNode*(
n: StateNetwork, key: ContractTrieNodeKey n: StateNetwork,
key: ContractTrieNodeKey,
maybeParentOffer = Opt.none(ContractTrieNodeOffer),
): Future[Opt[ContractTrieNodeRetrieval]] {. ): Future[Opt[ContractTrieNodeRetrieval]] {.
async: (raw: true, raises: [CancelledError]) async: (raw: true, raises: [CancelledError])
.} = .} =
n.getContent(key, ContractTrieNodeRetrieval) n.getContent(key, ContractTrieNodeRetrieval, maybeParentOffer)
proc getContractCode*( proc getContractCode*(
n: StateNetwork, key: ContractCodeKey n: StateNetwork,
key: ContractCodeKey,
maybeParentOffer = Opt.none(ContractCodeOffer),
): Future[Opt[ContractCodeRetrieval]] {.async: (raw: true, raises: [CancelledError]).} = ): Future[Opt[ContractCodeRetrieval]] {.async: (raw: true, raises: [CancelledError]).} =
n.getContent(key, ContractCodeRetrieval) n.getContent(key, ContractCodeRetrieval, maybeParentOffer)
proc getStateRootByBlockNumOrHash*( proc getBlockHeaderByBlockNumOrHash*(
n: StateNetwork, blockNumOrHash: uint64 | Hash32 n: StateNetwork, blockNumOrHash: uint64 | Hash32
): Future[Opt[Hash32]] {.async: (raises: [CancelledError]).} = ): Future[Opt[Header]] {.async: (raises: [CancelledError]).} =
let hn = n.historyNetwork.valueOr: let hn = n.historyNetwork.valueOr:
warn "History network is not available" warn "History network is not available"
return Opt.none(Hash32) return Opt.none(Header)
let header = (await hn.getVerifiedBlockHeader(blockNumOrHash)).valueOr: let header = (await hn.getVerifiedBlockHeader(blockNumOrHash)).valueOr:
warn "Failed to get block header from history", blockNumOrHash warn "Failed to get block header from history", blockNumOrHash
return Opt.none(Hash32) return Opt.none(Header)
Opt.some(header.stateRoot) Opt.some(header)
proc processOffer*( proc processOffer*(
n: StateNetwork, n: StateNetwork,
@ -168,9 +181,9 @@ proc processOffer*(
return err("Unable to decode offered content value") return err("Unable to decode offered content value")
validationRes = validationRes =
if n.validateStateIsCanonical: if n.validateStateIsCanonical:
let stateRoot = (await n.getStateRootByBlockNumOrHash(contentValue.blockHash)).valueOr: let header = (await n.getBlockHeaderByBlockNumOrHash(contentValue.blockHash)).valueOr:
return err("Failed to get state root by block hash") return err("Failed to get block header by hash")
validateOffer(Opt.some(stateRoot), contentKey, contentValue) validateOffer(Opt.some(header.stateRoot), contentKey, contentValue)
else: else:
# Skip state root validation # Skip state root validation
validateOffer(Opt.none(Hash32), contentKey, contentValue) validateOffer(Opt.none(Hash32), contentKey, contentValue)
@ -182,7 +195,7 @@ proc processOffer*(
return err("Received offered content with invalid content key") return err("Received offered content with invalid content key")
n.portalProtocol.storeContent( n.portalProtocol.storeContent(
contentKeyBytes, contentId, contentValue.toRetrievalValue().encode() contentKeyBytes, contentId, contentValue.toRetrieval().encode()
) )
await gossipOffer( await gossipOffer(

View File

@ -146,7 +146,7 @@ func validateOffer*(
): Result[void, string] = ): Result[void, string] =
?validateTrieProof(trustedStateRoot, key.path, offer.proof) ?validateTrieProof(trustedStateRoot, key.path, offer.proof)
validateRetrieval(key, offer.toRetrievalValue()) validateRetrieval(key, offer.toRetrieval())
func validateOffer*( func validateOffer*(
trustedStateRoot: Opt[Hash32], trustedStateRoot: Opt[Hash32],
@ -164,7 +164,7 @@ func validateOffer*(
?validateTrieProof(Opt.some(account.storageRoot), key.path, offer.storageProof) ?validateTrieProof(Opt.some(account.storageRoot), key.path, offer.storageProof)
validateRetrieval(key, offer.toRetrievalValue()) validateRetrieval(key, offer.toRetrieval())
func validateOffer*( func validateOffer*(
trustedStateRoot: Opt[Hash32], key: ContractCodeKey, offer: ContractCodeOffer trustedStateRoot: Opt[Hash32], key: ContractCodeKey, offer: ContractCodeOffer
@ -180,7 +180,10 @@ func validateOffer*(
if not offer.code.hashEquals(account.codeHash): if not offer.code.hashEquals(account.codeHash):
return err("hash of bytecode doesn't match the code hash in the account proof") return err("hash of bytecode doesn't match the code hash in the account proof")
validateRetrieval(key, offer.toRetrievalValue()) validateRetrieval(key, offer.toRetrieval())
# Local validations that check the structure of the content keys and values.
# None of the validations below check if the data is canonical or not
func validateGetContentKey*( func validateGetContentKey*(
keyBytes: ContentKeyByteList keyBytes: ContentKeyByteList
@ -204,24 +207,52 @@ func validateRetrieval*(
let retrieval = ?ContractCodeRetrieval.decode(contentBytes) let retrieval = ?ContractCodeRetrieval.decode(contentBytes)
validateRetrieval(key.contractCodeKey, retrieval) validateRetrieval(key.contractCodeKey, retrieval)
func validateOfferGetValue*( func validateRetrievalGetOffer*(
trustedStateRoot: Opt[Hash32], key: ContentKey, contentBytes: seq[byte] key: ContentKey, contentBytes: seq[byte], parentContentBytes: seq[byte]
): Result[seq[byte], string] = ): Result[seq[byte], string] =
let value = case key.contentType
case key.contentType of unused:
of unused: raiseAssert("ContentKey contentType: unused")
raiseAssert("ContentKey contentType: unused") of accountTrieNode:
of accountTrieNode: let
let offer = ?AccountTrieNodeOffer.decode(contentBytes) retrieval = ?AccountTrieNodeRetrieval.decode(contentBytes)
?validateOffer(trustedStateRoot, key.accountTrieNodeKey, offer) parentOffer = ?AccountTrieNodeOffer.decode(parentContentBytes)
offer.toRetrievalValue.encode() offer = retrieval.toOffer(parentOffer)
of contractTrieNode: ?validateRetrieval(key.accountTrieNodeKey, retrieval)
let offer = ?ContractTrieNodeOffer.decode(contentBytes) ?validateOffer(Opt.none(Hash32), key.accountTrieNodeKey, offer)
?validateOffer(trustedStateRoot, key.contractTrieNodeKey, offer) ok(offer.encode())
offer.toRetrievalValue.encode() of contractTrieNode:
of contractCode: let
let offer = ?ContractCodeOffer.decode(contentBytes) retrieval = ?ContractTrieNodeRetrieval.decode(contentBytes)
?validateOffer(trustedStateRoot, key.contractCodeKey, offer) parentOffer = ?ContractTrieNodeOffer.decode(parentContentBytes)
offer.toRetrievalValue.encode() offer = retrieval.toOffer(parentOffer)
?validateRetrieval(key.contractTrieNodeKey, retrieval)
?validateOffer(Opt.none(Hash32), key.contractTrieNodeKey, offer)
ok(offer.encode())
of contractCode:
let
retrieval = ?ContractCodeRetrieval.decode(contentBytes)
parentOffer = ?ContractCodeOffer.decode(parentContentBytes)
offer = retrieval.toOffer(parentOffer)
?validateRetrieval(key.contractCodeKey, retrieval)
?validateOffer(Opt.none(Hash32), key.contractCodeKey, offer)
ok(offer.encode())
ok(value) func validateOfferGetRetrieval*(
key: ContentKey, contentBytes: seq[byte]
): Result[seq[byte], string] =
case key.contentType
of unused:
raiseAssert("ContentKey contentType: unused")
of accountTrieNode:
let offer = ?AccountTrieNodeOffer.decode(contentBytes)
?validateOffer(Opt.none(Hash32), key.accountTrieNodeKey, offer)
ok(offer.toRetrieval.encode())
of contractTrieNode:
let offer = ?ContractTrieNodeOffer.decode(contentBytes)
?validateOffer(Opt.none(Hash32), key.contractTrieNodeKey, offer)
ok(offer.toRetrieval.encode())
of contractCode:
let offer = ?ContractCodeOffer.decode(contentBytes)
?validateOffer(Opt.none(Hash32), key.contractCodeKey, offer)
ok(offer.toRetrieval.encode())

View File

@ -74,7 +74,7 @@ proc installPortalStateApiHandlers*(rpcServer: RpcServer, p: PortalProtocol) =
contentBytes = hexToSeqByte(contentItem[1]) contentBytes = hexToSeqByte(contentItem[1])
contentKV = ContentKV(contentKey: keyBytes, content: contentBytes) contentKV = ContentKV(contentKey: keyBytes, content: contentBytes)
discard validateOfferGetValue(Opt.none(Hash32), key, contentBytes).valueOr: discard validateOfferGetRetrieval(key, contentBytes).valueOr:
raise invalidValueErr() raise invalidValueErr()
contentItemsToOffer.add(contentKV) contentItemsToOffer.add(contentKV)
@ -134,7 +134,7 @@ proc installPortalStateApiHandlers*(rpcServer: RpcServer, p: PortalProtocol) =
(key, contentId) = validateGetContentKey(keyBytes).valueOr: (key, contentId) = validateGetContentKey(keyBytes).valueOr:
raise invalidKeyErr() raise invalidKeyErr()
contentBytes = hexToSeqByte(content) contentBytes = hexToSeqByte(content)
contentValue = validateOfferGetValue(Opt.none(Hash32), key, contentBytes).valueOr: contentValue = validateOfferGetRetrieval(key, contentBytes).valueOr:
raise invalidValueErr() raise invalidValueErr()
p.storeContent(keyBytes, contentId, contentValue) p.storeContent(keyBytes, contentId, contentValue)
@ -156,7 +156,7 @@ proc installPortalStateApiHandlers*(rpcServer: RpcServer, p: PortalProtocol) =
(key, contentId) = validateGetContentKey(keyBytes).valueOr: (key, contentId) = validateGetContentKey(keyBytes).valueOr:
raise invalidKeyErr() raise invalidKeyErr()
contentBytes = hexToSeqByte(content) contentBytes = hexToSeqByte(content)
contentValue = validateOfferGetValue(Opt.none(Hash32), key, contentBytes).valueOr: contentValue = validateOfferGetRetrieval(key, contentBytes).valueOr:
raise invalidValueErr() raise invalidValueErr()
p.storeContent(keyBytes, contentId, contentValue) p.storeContent(keyBytes, contentId, contentValue)

View File

@ -41,7 +41,7 @@ suite "State Endpoints - Genesis JSON Files":
# store the account leaf node # store the account leaf node
let contentKey = key.toContentKey().encode() let contentKey = key.toContentKey().encode()
stateNode.portalProtocol.storeContent( stateNode.portalProtocol.storeContent(
contentKey, contentKey.toContentId(), offer.toRetrievalValue().encode() contentKey, contentKey.toContentId(), offer.toRetrieval().encode()
) )
# store the account parent nodes / all remaining nodes # store the account parent nodes / all remaining nodes
@ -52,7 +52,7 @@ suite "State Endpoints - Genesis JSON Files":
stateNode.portalProtocol.storeContent( stateNode.portalProtocol.storeContent(
parentContentKey, parentContentKey,
parentContentKey.toContentId(), parentContentKey.toContentId(),
parent.offer.toRetrievalValue().encode(), parent.offer.toRetrieval().encode(),
) )
for i in proof.low ..< proof.high - 1: for i in proof.low ..< proof.high - 1:
@ -62,7 +62,7 @@ suite "State Endpoints - Genesis JSON Files":
stateNode.portalProtocol.storeContent( stateNode.portalProtocol.storeContent(
parentContentKey, parentContentKey,
parentContentKey.toContentId(), parentContentKey.toContentId(),
parent.offer.toRetrievalValue().encode(), parent.offer.toRetrieval().encode(),
) )
proc setupCodeInDb( proc setupCodeInDb(
@ -101,7 +101,7 @@ suite "State Endpoints - Genesis JSON Files":
# store the contract storage leaf node # store the contract storage leaf node
let contentKey = key.toContentKey().encode() let contentKey = key.toContentKey().encode()
stateNode.portalProtocol.storeContent( stateNode.portalProtocol.storeContent(
contentKey, contentKey.toContentId(), offer.toRetrievalValue().encode() contentKey, contentKey.toContentId(), offer.toRetrieval().encode()
) )
# store the remaining contract storage nodes # store the remaining contract storage nodes
@ -112,7 +112,7 @@ suite "State Endpoints - Genesis JSON Files":
stateNode.portalProtocol.storeContent( stateNode.portalProtocol.storeContent(
parentContentKey, parentContentKey,
parentContentKey.toContentId(), parentContentKey.toContentId(),
parent.offer.toRetrievalValue().encode(), parent.offer.toRetrieval().encode(),
) )
for i in storageProof.low ..< storageProof.high - 1: for i in storageProof.low ..< storageProof.high - 1:
@ -122,7 +122,7 @@ suite "State Endpoints - Genesis JSON Files":
stateNode.portalProtocol.storeContent( stateNode.portalProtocol.storeContent(
parentContentKey, parentContentKey,
parentContentKey.toContentId(), parentContentKey.toContentId(),
parent.offer.toRetrievalValue().encode(), parent.offer.toRetrieval().encode(),
) )
asyncTest "Test getBalance, getTransactionCount, getStorageAt and getCode using JSON files": asyncTest "Test getBalance, getTransactionCount, getStorageAt and getCode using JSON files":

View File

@ -39,13 +39,13 @@ suite "State Gossip getParent - Genesis JSON Files":
offer = AccountTrieNodeOffer(proof: proof) offer = AccountTrieNodeOffer(proof: proof)
var db = newMemoryDB() var db = newMemoryDB()
db.put(key.nodeHash.data, offer.toRetrievalValue().node.asSeq()) db.put(key.nodeHash.data, offer.toRetrieval().node.asSeq())
# validate each parent offer until getting to the root node # validate each parent offer until getting to the root node
var parent = offer.withKey(key).getParent() var parent = offer.withKey(key).getParent()
check validateOffer(Opt.some(accountState.rootHash()), parent.key, parent.offer) check validateOffer(Opt.some(accountState.rootHash()), parent.key, parent.offer)
.isOk() .isOk()
db.put(parent.key.nodeHash.data, parent.offer.toRetrievalValue().node.asSeq()) db.put(parent.key.nodeHash.data, parent.offer.toRetrieval().node.asSeq())
for i in proof.low ..< proof.high - 1: for i in proof.low ..< proof.high - 1:
parent = parent.getParent() parent = parent.getParent()
@ -53,7 +53,7 @@ suite "State Gossip getParent - Genesis JSON Files":
Opt.some(accountState.rootHash()), parent.key, parent.offer Opt.some(accountState.rootHash()), parent.key, parent.offer
) )
.isOk() .isOk()
db.put(parent.key.nodeHash.data, parent.offer.toRetrievalValue().node.asSeq()) db.put(parent.key.nodeHash.data, parent.offer.toRetrieval().node.asSeq())
# after putting all parent nodes into the trie, verify can lookup the leaf # after putting all parent nodes into the trie, verify can lookup the leaf
let let
@ -94,7 +94,7 @@ suite "State Gossip getParent - Genesis JSON Files":
) )
var db = newMemoryDB() var db = newMemoryDB()
db.put(key.nodeHash.data, offer.toRetrievalValue().node.asSeq()) db.put(key.nodeHash.data, offer.toRetrieval().node.asSeq())
# validate each parent offer until getting to the root node # validate each parent offer until getting to the root node
var parent = offer.withKey(key).getParent() var parent = offer.withKey(key).getParent()
@ -102,9 +102,7 @@ suite "State Gossip getParent - Genesis JSON Files":
Opt.some(accountState.rootHash()), parent.key, parent.offer Opt.some(accountState.rootHash()), parent.key, parent.offer
) )
.isOk() .isOk()
db.put( db.put(parent.key.nodeHash.data, parent.offer.toRetrieval().node.asSeq())
parent.key.nodeHash.data, parent.offer.toRetrievalValue().node.asSeq()
)
for i in storageProof.low ..< storageProof.high - 1: for i in storageProof.low ..< storageProof.high - 1:
parent = parent.getParent() parent = parent.getParent()
@ -112,9 +110,7 @@ suite "State Gossip getParent - Genesis JSON Files":
Opt.some(accountState.rootHash()), parent.key, parent.offer Opt.some(accountState.rootHash()), parent.key, parent.offer
) )
.isOk() .isOk()
db.put( db.put(parent.key.nodeHash.data, parent.offer.toRetrieval().node.asSeq())
parent.key.nodeHash.data, parent.offer.toRetrievalValue().node.asSeq()
)
# after putting all parent nodes into the trie, verify can lookup the leaf # after putting all parent nodes into the trie, verify can lookup the leaf
let let

View File

@ -39,7 +39,7 @@ suite "State Gossip getParent - Test Vectors":
parentKey.toContentKey().encode() == parentKey.toContentKey().encode() ==
parentTestData.content_key.hexToSeqByte().ContentKeyByteList parentTestData.content_key.hexToSeqByte().ContentKeyByteList
parentOffer.encode() == parentTestData.content_value_offer.hexToSeqByte() parentOffer.encode() == parentTestData.content_value_offer.hexToSeqByte()
parentOffer.toRetrievalValue().encode() == parentOffer.toRetrieval().encode() ==
parentTestData.content_value_retrieval.hexToSeqByte() parentTestData.content_value_retrieval.hexToSeqByte()
test "Check contract storage trie node parent matches expected recursive gossip": test "Check contract storage trie node parent matches expected recursive gossip":
@ -68,5 +68,5 @@ suite "State Gossip getParent - Test Vectors":
parentKey.toContentKey().encode() == parentKey.toContentKey().encode() ==
parentTestData.content_key.hexToSeqByte().ContentKeyByteList parentTestData.content_key.hexToSeqByte().ContentKeyByteList
parentOffer.encode() == parentTestData.content_value_offer.hexToSeqByte() parentOffer.encode() == parentTestData.content_value_offer.hexToSeqByte()
parentOffer.toRetrievalValue().encode() == parentOffer.toRetrieval().encode() ==
parentTestData.content_value_retrieval.hexToSeqByte() parentTestData.content_value_retrieval.hexToSeqByte()

View File

@ -78,8 +78,8 @@ procSuite "State Gossip - Gossip Offer":
check: check:
stateNode2.containsId(contentId) stateNode2.containsId(contentId)
res1.isOk() res1.isOk()
res1.get() == contentValue.toRetrievalValue() res1.get() == contentValue.toRetrieval()
res1.get().node == contentValue.toRetrievalValue().node res1.get().node == contentValue.toRetrieval().node
# check that the parent offer was not received by the second state instance # check that the parent offer was not received by the second state instance
let res2 = await stateNode2.stateNetwork.getAccountTrieNode( let res2 = await stateNode2.stateNetwork.getAccountTrieNode(
@ -147,8 +147,8 @@ procSuite "State Gossip - Gossip Offer":
check: check:
stateNode2.containsId(contentId) stateNode2.containsId(contentId)
res1.isOk() res1.isOk()
res1.get() == contentValue.toRetrievalValue() res1.get() == contentValue.toRetrieval()
res1.get().node == contentValue.toRetrievalValue().node res1.get().node == contentValue.toRetrieval().node
# check that the offer parent was not received by the second state instance # check that the offer parent was not received by the second state instance
let res2 = await stateNode2.stateNetwork.getContractTrieNode( let res2 = await stateNode2.stateNetwork.getContractTrieNode(
@ -205,8 +205,8 @@ procSuite "State Gossip - Gossip Offer":
check: check:
stateNode2.containsId(contentId) stateNode2.containsId(contentId)
res1.isOk() res1.isOk()
res1.get() == contentValue.toRetrievalValue() res1.get() == contentValue.toRetrieval()
res1.get().code == contentValue.toRetrievalValue().code res1.get().code == contentValue.toRetrieval().code
await stateNode1.stop() await stateNode1.stop()
await stateNode2.stop() await stateNode2.stop()

View File

@ -90,8 +90,8 @@ procSuite "State Network - Offer Content":
check: check:
stateNode1.containsId(contentId) stateNode1.containsId(contentId)
getRes.isOk() getRes.isOk()
getRes.get() == contentValue.toRetrievalValue() getRes.get() == contentValue.toRetrieval()
getRes.get().node == contentValue.toRetrievalValue().node getRes.get().node == contentValue.toRetrieval().node
await stateNode1.stop() await stateNode1.stop()
@ -159,8 +159,8 @@ procSuite "State Network - Offer Content":
check: check:
stateNode1.containsId(contentId) stateNode1.containsId(contentId)
getRes.isOk() getRes.isOk()
getRes.get() == contentValue.toRetrievalValue() getRes.get() == contentValue.toRetrieval()
getRes.get().node == contentValue.toRetrievalValue().node getRes.get().node == contentValue.toRetrieval().node
await stateNode1.stop() await stateNode1.stop()
@ -227,8 +227,8 @@ procSuite "State Network - Offer Content":
check: check:
stateNode1.containsId(contentId) stateNode1.containsId(contentId)
getRes.isOk() getRes.isOk()
getRes.get() == contentValue.toRetrievalValue() getRes.get() == contentValue.toRetrieval()
getRes.get().code == contentValue.toRetrievalValue().code getRes.get().code == contentValue.toRetrieval().code
await stateNode1.stop() await stateNode1.stop()
@ -277,8 +277,8 @@ procSuite "State Network - Offer Content":
check: check:
stateNode2.containsId(contentId) stateNode2.containsId(contentId)
getRes.isOk() getRes.isOk()
getRes.get() == contentValue.toRetrievalValue() getRes.get() == contentValue.toRetrieval()
getRes.get().node == contentValue.toRetrievalValue().node getRes.get().node == contentValue.toRetrieval().node
await stateNode1.stop() await stateNode1.stop()
await stateNode2.stop() await stateNode2.stop()
@ -327,8 +327,8 @@ procSuite "State Network - Offer Content":
check: check:
stateNode2.containsId(contentId) stateNode2.containsId(contentId)
getRes.isOk() getRes.isOk()
getRes.get() == contentValue.toRetrievalValue() getRes.get() == contentValue.toRetrieval()
getRes.get().node == contentValue.toRetrievalValue().node getRes.get().node == contentValue.toRetrieval().node
await stateNode1.stop() await stateNode1.stop()
await stateNode2.stop() await stateNode2.stop()
@ -376,8 +376,8 @@ procSuite "State Network - Offer Content":
check: check:
stateNode2.containsId(contentId) stateNode2.containsId(contentId)
getRes.isOk() getRes.isOk()
getRes.get() == contentValue.toRetrievalValue() getRes.get() == contentValue.toRetrieval()
getRes.get().code == contentValue.toRetrievalValue().code getRes.get().code == contentValue.toRetrieval().code
await stateNode1.stop() await stateNode1.stop()
await stateNode2.stop() await stateNode2.stop()