From bdbd8e4c870c8bba800b6cb6e7cafab5d31187b0 Mon Sep 17 00:00:00 2001 From: Kim De Mey Date: Wed, 17 Nov 2021 17:11:17 +0100 Subject: [PATCH] Add SSZ Unions through case objects (#882) * Add SSZ Unions through case objects * Add connection id content response test and improve other test vectors * Implement content keys and ids for state network as per spec Content keys case object is used so that it can be serialized and deserialized as an SSZ Union. * Let message Union in Portal wire protocol start at 0 as per new spec --- fluffy/common/common_types.nim | 1 + fluffy/network/state/state_content.nim | 136 ++++++++++++++------- fluffy/network/state/state_network.nim | 13 +- fluffy/network/wire/messages.nim | 85 ++++++------- fluffy/network/wire/portal_protocol.nim | 38 +++--- fluffy/tests/test_portal_wire_encoding.nim | 88 +++++++------ fluffy/tests/test_portal_wire_protocol.nim | 1 - fluffy/tests/test_state_network.nim | 27 ++-- fluffy/tools/portalcli.nim | 7 +- vendor/nim-ssz-serialization | 2 +- 10 files changed, 225 insertions(+), 173 deletions(-) diff --git a/fluffy/common/common_types.nim b/fluffy/common/common_types.nim index 82b64fb81..d0b1ccfb7 100644 --- a/fluffy/common/common_types.nim +++ b/fluffy/common/common_types.nim @@ -13,3 +13,4 @@ import type ByteList* = List[byte, 2048] Bytes2* = array[2, byte] + Bytes32* = array[32, byte] diff --git a/fluffy/network/state/state_content.nim b/fluffy/network/state/state_content.nim index 3f130e5e6..835ee72a3 100644 --- a/fluffy/network/state/state_content.nim +++ b/fluffy/network/state/state_content.nim @@ -5,78 +5,124 @@ # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. -# https://github.com/ethereum/stateless-ethereum-specs/blob/master/state-network.md#content +# As per spec: +# https://github.com/ethereum/portal-network-specs/blob/master/state-network.md#content-keys-and-content-ids {.push raises: [Defect].} import std/options, - nimcrypto/[sha2, hash], stew/objects, stint, + nimcrypto/[hash, sha2, keccak], stew/objects, stint, ssz_serialization, ../../common/common_types export ssz_serialization, common_types type - ContentType* = enum - Account = 0x01 - ContractStorage = 0x02 - ContractBytecode = 0x03 - - NetworkId* = uint16 - NodeHash* = MDigest[32 * 8] # keccak256 - CodeHash* = MDigest[32 * 8] # keccak256 - Address* = array[20, byte] - ContentKey* = object - networkId*: NetworkId - contentType*: ContentType - # TODO: How shall we deal with the different ContentKey structures? - # Lets start with just node hashes for now. - # address: Address - # triePath: ByteList + ContentType* = enum + accountTrieNode = 0x00 + contractStorageTrieNode = 0x01 + accountTrieProof = 0x02 + contractStorageTrieProof = 0x03 + contractBytecode = 0x04 + + AccountTrieNodeKey* = object + path*: ByteList nodeHash*: NodeHash + stateRoot*: Bytes32 + + ContractStorageTrieNodeKey* = object + address*: Address + path*: ByteList + nodeHash*: NodeHash + stateRoot*: Bytes32 + + AccountTrieProofKey* = object + address*: Address + stateRoot*: Bytes32 + + ContractStorageTrieProofKey* = object + address*: Address + slot*: UInt256 + stateRoot*: Bytes32 + + ContractBytecodeKey* = object + address*: Address + codeHash*: CodeHash + + ContentKey* = object + case contentType*: ContentType + of accountTrieNode: + accountTrieNodeKey*: AccountTrieNodeKey + of contractStorageTrieNode: + contractStorageTrieNodeKey*: ContractStorageTrieNodeKey + of accountTrieProof: + accountTrieProofKey*: AccountTrieProofKey + of contractStorageTrieProof: + contractStorageTrieProofKey*: ContractStorageTrieProofKey + of contractBytecode: + contractBytecodeKey*: ContractBytecodeKey ContentId* = Uint256 - KeccakHash* = MDigest[32 * 8] # could also import from either eth common types or trie defs - -template toSszType*(x: ContentType): uint8 = - uint8(x) - -template toSszType*(x: auto): auto = - x - -func fromSszBytes*(T: type ContentType, data: openArray[byte]): - T {.raises: [MalformedSszError, Defect].} = - if data.len != sizeof(uint8): - raiseIncorrectSize T - - var contentType: T - if not checkedEnumAssign(contentType, data[0]): - raiseIncorrectSize T - - contentType - func encode*(contentKey: ContentKey): ByteList = ByteList.init(SSZ.encode(contentKey)) -# TODO consider if more powerfull error handling is necessary here. func decode*(contentKey: ByteList): Option[ContentKey] = try: some(SSZ.decode(contentKey.asSeq(), ContentKey)) except SszError: return none[ContentKey]() -func toContentId*(contentKey: ByteList): ContentId = - # TODO: Hash function to be defined, sha256 used now, might be confusing - # with keccak256 that is used for the actual nodes: - # https://github.com/ethereum/stateless-ethereum-specs/blob/master/state-network.md#content - let idHash = sha2.sha_256.digest(contentKey.asSeq()) +template computeContentId*(digestCtxType: type, body: untyped): ContentId = + var h {.inject.}: digestCtxType + init(h) + body + let idHash = finish(h) readUintBE[256](idHash.data) -func toContentId*(contentKey: ContentKey): ContentId = - toContentId(encode(contentKey)) +proc toContentId*(contentKey: ContentKey): ContentId = + case contentKey.contentType: + of accountTrieNode: # sha256(path | node_hash) + let key = contentKey.accountTrieNodeKey + computeContentId sha256: + h.update(key.path.asSeq()) + h.update(key.nodeHash.data) + of contractStorageTrieNode: # sha256(address | path | node_hash) + let key = contentKey.contractStorageTrieNodeKey + computeContentId sha256: + h.update(key.address) + h.update(key.path.asSeq()) + h.update(key.nodeHash.data) + of accountTrieProof: # keccak(address) + let key = contentKey.accountTrieProofKey + computeContentId keccak256: + h.update(key.address) + of contractStorageTrieProof: # (keccak(address) + keccak(slot)) % 2**256 + # TODO: Why is keccak run on slot, when it can be used directly? + # Also, value to LE or BE? Not mentioned in specification. + let key = contentKey.contractStorageTrieProofKey + let n1 = + block: computeContentId keccak256: + h.update(key.address) + let n2 = + block: computeContentId keccak256: + h.update(toBytesLE(key.slot)) + + n1 + n2 # uint256 will wrap arround, practically applying the modulo 256 + of contractBytecode: # sha256(address | code_hash) + let key = contentKey.contractBytecodeKey + computeContentId sha256: + h.update(key.address) + h.update(key.codeHash.data) + +proc toContentId*(contentKey: ByteList): Option[ContentId] = + let key = decode(contentKey) + if key.isSome(): + some(key.get().toContentId()) + else: + none(ContentId) diff --git a/fluffy/network/state/state_network.nim b/fluffy/network/state/state_network.nim index 39a4f735a..049b51ba5 100644 --- a/fluffy/network/state/state_network.nim +++ b/fluffy/network/state/state_network.nim @@ -24,17 +24,20 @@ type StateNetwork* = ref object proc getHandler(contentDB: ContentDB): ContentHandler = return (proc (contentKey: state_content.ByteList): ContentResult = let contentId = toContentId(contentKey) - let maybeContent = contentDB.get(contentId) - if (maybeContent.isSome()): - ContentResult(kind: ContentFound, content: maybeContent.unsafeGet()) + if contentId.isSome(): + let maybeContent = contentDB.get(contentId.get()) + if (maybeContent.isSome()): + ContentResult(kind: ContentFound, content: maybeContent.unsafeGet()) + else: + ContentResult(kind: ContentMissing, contentId: contentId.get()) else: - ContentResult(kind: ContentMissing, contentId: contentId)) + ContentResult(kind: ContentKeyValidationFailure, error: "")) proc getContent*(n: StateNetwork, key: ContentKey): Future[Option[seq[byte]]] {.async.} = let keyEncoded = encode(key) - contentId = toContentId(keyEncoded) + contentId = toContentId(key) let nodeId = n.portalProtocol.localNode.id diff --git a/fluffy/network/wire/messages.nim b/fluffy/network/wire/messages.nim index 8a165a0bb..30971d71a 100644 --- a/fluffy/network/wire/messages.nim +++ b/fluffy/network/wire/messages.nim @@ -30,16 +30,19 @@ type dataRadius*: UInt256 MessageKind* = enum - unused = 0x00 + ping = 0x00 + pong = 0x01 + findnode = 0x02 + nodes = 0x03 + findcontent = 0x04 + content = 0x05 + offer = 0x06 + accept = 0x07 - ping = 0x01 - pong = 0x02 - findnode = 0x03 - nodes = 0x04 - findcontent = 0x05 - content = 0x06 - offer = 0x07 - accept = 0x08 + ContentMessageType* = enum + connectionIdType = 0x00 + contentType = 0x01 + enrsType = 0x02 PingMessage* = object enrSeq*: uint64 @@ -60,11 +63,14 @@ type FindContentMessage* = object contentKey*: ByteList - # TODO: Must become an SSZ Union ContentMessage* = object - connectionId*: Bytes2 - content*: ByteList - enrs*: List[ByteList, 32] + case contentMessageType*: ContentMessageType + of connectionIdType: + connectionId*: Bytes2 + of contentType: + content*: ByteList + of enrsType: + enrs*: List[ByteList, 32] OfferMessage* = object contentKeys*: ContentKeysList @@ -73,7 +79,6 @@ type connectionId*: Bytes2 contentKeys*: ContentKeysBitList - # TODO: Needs to become an SSZ Union Message* = object case kind*: MessageKind of ping: @@ -81,7 +86,7 @@ type of pong: pong*: PongMessage of findnode: - findNode*: FindNodeMessage + findnode*: FindNodeMessage of nodes: nodes*: NodesMessage of findcontent: @@ -92,8 +97,6 @@ type offer*: OfferMessage of accept: accept*: AcceptMessage - else: - discard SomeMessage* = PingMessage or PongMessage or @@ -104,7 +107,7 @@ type template messageKind*(T: typedesc[SomeMessage]): MessageKind = when T is PingMessage: ping elif T is PongMessage: pong - elif T is FindNodeMessage: findNode + elif T is FindNodeMessage: findnode elif T is NodesMessage: nodes elif T is FindContentMessage: findcontent elif T is ContentMessage: content @@ -122,42 +125,24 @@ func fromSszBytes*(T: type UInt256, data: openArray[byte]): T.fromBytesLE(data) proc encodeMessage*[T: SomeMessage](m: T): seq[byte] = - ord(messageKind(T)).byte & SSZ.encode(m) + # TODO: Could/should be macro'd away, + # or we just use SSZ.encode(Message) directly + when T is PingMessage: SSZ.encode(Message(kind: ping, ping: m)) + elif T is PongMessage: SSZ.encode(Message(kind: pong, pong: m)) + elif T is FindNodeMessage: SSZ.encode(Message(kind: findnode, findnode: m)) + elif T is NodesMessage: SSZ.encode(Message(kind: nodes, nodes: m)) + elif T is FindContentMessage: SSZ.encode(Message(kind: findcontent, findcontent: m)) + elif T is ContentMessage: SSZ.encode(Message(kind: content, content: m)) + elif T is OfferMessage: SSZ.encode(Message(kind: offer, offer: m)) + elif T is AcceptMessage: SSZ.encode(Message(kind: accept, accept: m)) proc decodeMessage*(body: openarray[byte]): Result[Message, cstring] = - # Decodes to the specific `Message` type. - if body.len < 1: - return err("No message data, peer might not support this talk protocol") - - var kind: MessageKind - if not checkedEnumAssign(kind, body[0]): - return err("Invalid message type") - - var message = Message(kind: kind) - try: - case kind - of unused: return err("Invalid message type") - of ping: - message.ping = SSZ.decode(body.toOpenArray(1, body.high), PingMessage) - of pong: - message.pong = SSZ.decode(body.toOpenArray(1, body.high), PongMessage) - of findNode: - message.findNode = SSZ.decode(body.toOpenArray(1, body.high), FindNodeMessage) - of nodes: - message.nodes = SSZ.decode(body.toOpenArray(1, body.high), NodesMessage) - of findcontent: - message.findcontent = SSZ.decode(body.toOpenArray(1, body.high), FindContentMessage) - of content: - message.content = SSZ.decode(body.toOpenArray(1, body.high), ContentMessage) - of offer: - message.offer = SSZ.decode(body.toOpenArray(1, body.high), OfferMessage) - of accept: - message.accept = SSZ.decode(body.toOpenArray(1, body.high), AcceptMessage) + if body.len < 1: # TODO: This check should probably move a layer down + return err("No message data, peer might not support this talk protocol") + ok(SSZ.decode(body, Message)) except SszError: - return err("Invalid message encoding") - - ok(message) + err("Invalid message encoding") template innerMessage[T: SomeMessage](message: Message, expected: MessageKind): Option[T] = if (message.kind == expected): diff --git a/fluffy/network/wire/portal_protocol.nim b/fluffy/network/wire/portal_protocol.nim index cef6de604..4d304d62b 100644 --- a/fluffy/network/wire/portal_protocol.nim +++ b/fluffy/network/wire/portal_protocol.nim @@ -137,26 +137,25 @@ proc handleFindContent(p: PortalProtocol, fc: FindContentMessage): seq[byte] = # TODO: Need to provide uTP connectionId when content is too large for a # single response. let content = contentHandlingResult.content - let enrs = List[ByteList, 32](@[]) # Empty enrs when payload is send encodeMessage(ContentMessage( - enrs: enrs, content: ByteList(content))) + contentMessageType: contentType, content: ByteList(content))) of ContentMissing: let contentId = contentHandlingResult.contentId closestNodes = p.routingTable.neighbours( NodeId(contentId), seenOnly = true) - content = ByteList(@[]) # Empty payload when enrs are send enrs = closestNodes.map(proc(x: Node): ByteList = ByteList(x.record.raw)) encodeMessage(ContentMessage( - enrs: List[ByteList, 32](List(enrs)), content: content)) + contentMessageType: enrsType, enrs: List[ByteList, 32](List(enrs)))) of ContentKeyValidationFailure: - # Return empty response when content key validation fails + # Return empty content response when content key validation fails + # TODO: Better would be to return no message at all, or we need to add a + # None type or so. let content = ByteList(@[]) - let enrs = List[ByteList, 32](@[]) # Empty enrs when payload is send encodeMessage(ContentMessage( - enrs: enrs, content: content)) + contentMessageType: contentType, content: content)) proc handleOffer(p: PortalProtocol, a: OfferMessage): seq[byte] = let @@ -203,8 +202,9 @@ proc messageHandler*(protocol: TalkProtocol, request: seq[byte], of MessageKind.offer: p.handleOffer(message.offer) else: - # This shouldn't occur as the 0 case is already covered in `decodedMessage` - debug "Packet decoding error: Invalid message type" + # This would mean a that Portal wire response message is being send over a + # discv5 talkreq message. + debug "Invalid Portal wire message type over talkreq", kind = message.kind @[] else: debug "Packet decoding error", error = decoded.error, srcId, srcUdpAddress @@ -388,7 +388,14 @@ proc lookup*(p: PortalProtocol, target: NodeId): Future[seq[Node]] {.async.} = proc handleFoundContentMessage(p: PortalProtocol, m: ContentMessage, dst: Node, nodes: var seq[Node]): LookupResult = - if (m.enrs.len() != 0 and m.content.len() == 0): + case m.contentMessageType: + of connectionIdType: + # TODO: We'd have to get the data through uTP, or wrap some proc around + # this call that does that. + LookupResult(kind: Content) + of contentType: + LookupResult(kind: Content, content: m.content) + of enrsType: let records = recordsFromBytes(m.enrs) let verifiedNodes = verifyNodesRecords(records, dst, EnrsResultLimit) nodes.add(verifiedNodes) @@ -397,16 +404,7 @@ proc handleFoundContentMessage(p: PortalProtocol, m: ContentMessage, # Attempt to add all nodes discovered discard p.routingTable.addNode(n) - return LookupResult(kind: Nodes, nodes: nodes) - elif (m.content.len() != 0 and m.enrs.len() == 0): - return LookupResult(kind: Content, content: m.content) - elif ((m.content.len() != 0 and m.enrs.len() != 0)): - # Both content and enrs are filled, which means protocol breach. For now - # just logging offending node to quickly identify it - warn "Invalid foundcontent response form node ", uri = toURI(dst.record) - return LookupResult(kind: Nodes, nodes: nodes) - else: - return LookupResult(kind: Nodes, nodes: nodes) + LookupResult(kind: Nodes, nodes: nodes) proc contentLookupWorker(p: PortalProtocol, destNode: Node, target: ByteList): Future[LookupResult] {.async.} = diff --git a/fluffy/tests/test_portal_wire_encoding.nim b/fluffy/tests/test_portal_wire_encoding.nim index 21556cf41..f3b26adf0 100644 --- a/fluffy/tests/test_portal_wire_encoding.nim +++ b/fluffy/tests/test_portal_wire_encoding.nim @@ -13,8 +13,8 @@ import suite "Portal Wire Protocol Message Encodings": test "Ping Request": - var dataRadius: UInt256 let + dataRadius = UInt256.high() - 1 # Full radius - 1 enrSeq = 1'u64 # Can be any custom payload, testing with just dataRadius here. customPayload = ByteList(SSZ.encode(CustomPayload(dataRadius: dataRadius))) @@ -22,7 +22,7 @@ suite "Portal Wire Protocol Message Encodings": let encoded = encodeMessage(p) check encoded.toHex == - "0101000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000" + "0001000000000000000c000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" let decoded = decodeMessage(encoded) check decoded.isOk() @@ -33,8 +33,8 @@ suite "Portal Wire Protocol Message Encodings": message.ping.customPayload == customPayload test "Pong Response": - var dataRadius: UInt256 let + dataRadius = UInt256.high() div 2.stuint(256) # Radius of half the UInt256 enrSeq = 1'u64 # Can be any custom payload, testing with just dataRadius here. customPayload = ByteList(SSZ.encode(CustomPayload(dataRadius: dataRadius))) @@ -42,7 +42,7 @@ suite "Portal Wire Protocol Message Encodings": let encoded = encodeMessage(p) check encoded.toHex == - "0201000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000" + "0101000000000000000c000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f" let decoded = decodeMessage(encoded) check decoded.isOk() @@ -54,11 +54,11 @@ suite "Portal Wire Protocol Message Encodings": test "FindNode Request": let - distances = List[uint16, 256](@[0x0100'u16]) + distances = List[uint16, 256](@[0x0100'u16, 0x00ff'u16]) fn = FindNodeMessage(distances: distances) let encoded = encodeMessage(fn) - check encoded.toHex == "03040000000001" + check encoded.toHex == "02040000000001ff00" let decoded = decodeMessage(encoded) check decoded.isOk() @@ -68,13 +68,13 @@ suite "Portal Wire Protocol Message Encodings": message.kind == findnode message.findnode.distances == distances - test "Nodes Response - empty": + test "Nodes Response - empty enr list": let total = 0x1'u8 n = NodesMessage(total: total) let encoded = encodeMessage(n) - check encoded.toHex == "040105000000" + check encoded.toHex == "030105000000" let decoded = decodeMessage(encoded) check decoded.isOk() @@ -85,7 +85,7 @@ suite "Portal Wire Protocol Message Encodings": message.nodes.total == total message.nodes.enrs.len() == 0 - test "Nodes Response - enr": + test "Nodes Response - enrs": var e1, e2: Record check: e1.fromURI("enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg") @@ -96,7 +96,7 @@ suite "Portal Wire Protocol Message Encodings": n = NodesMessage(total: total, enrs: List[ByteList, 32](@[ByteList(e1.raw), ByteList(e2.raw)])) let encoded = encodeMessage(n) - check encoded.toHex == "040105000000080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235" + check encoded.toHex == "030105000000080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235" let decoded = decodeMessage(encoded) check decoded.isOk() @@ -110,12 +110,13 @@ suite "Portal Wire Protocol Message Encodings": message.nodes.enrs[1] == ByteList(e2.raw) test "FindContent Request": + const contentKeyString = "0x706f7274616c" let - contentEncoded = ByteList.init(@[1'u8]) - fn = FindContentMessage(contentKey: contentEncoded) + contentKey = ByteList.init(hexToSeqByte(contentKeyString)) + fc = FindContentMessage(contentKey: contentKey) - let encoded = encodeMessage(fn) - check encoded.toHex == "050400000001" + let encoded = encodeMessage(fc) + check encoded.toHex == "0404000000706f7274616c" let decoded = decodeMessage(encoded) check decoded.isOk() @@ -123,16 +124,16 @@ suite "Portal Wire Protocol Message Encodings": let message = decoded.get() check: message.kind == findcontent - message.findcontent.contentKey == contentEncoded + message.findcontent.contentKey == contentKey - test "Content Response - payload": + test "Content Response - connection id": let - enrs = List[ByteList, 32](@[]) - content = ByteList(@[byte 0x01, 0x02, 0x03]) - n = ContentMessage(enrs: enrs, content: content) + connectionId = Bytes2([byte 0x01, 0x02]) + c = ContentMessage( + contentMessageType: connectionIdType, connectionId: connectionId) - let encoded = encodeMessage(n) - check encoded.toHex == "0600000a0000000d000000010203" + let encoded = encodeMessage(c) + check encoded.toHex == "05000102" let decoded = decodeMessage(encoded) check decoded.isOk() @@ -140,7 +141,25 @@ suite "Portal Wire Protocol Message Encodings": let message = decoded.get() check: message.kind == MessageKind.content - message.content.enrs.len() == 0 + message.content.contentMessageType == connectionIdType + message.content.connectionId == connectionId + + test "Content Response - content payload": + const contentString = "0x7468652063616b652069732061206c6965" + let + content = ByteList(hexToSeqByte(contentString)) + c = ContentMessage(contentMessageType: contentType, content: content) + + let encoded = encodeMessage(c) + check encoded.toHex == "05017468652063616b652069732061206c6965" + + let decoded = decodeMessage(encoded) + check decoded.isOk() + + let message = decoded.get() + check: + message.kind == MessageKind.content + message.content.contentMessageType == contentType message.content.content == content test "Content Response - enrs": @@ -151,11 +170,10 @@ suite "Portal Wire Protocol Message Encodings": let enrs = List[ByteList, 32](@[ByteList(e1.raw), ByteList(e2.raw)]) - content = ByteList(@[]) - n = ContentMessage(enrs: enrs, content: content) + c = ContentMessage(contentMessageType: enrsType, enrs: enrs) - let encoded = encodeMessage(n) - check encoded.toHex == "0600000a0000000a000000080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235" + let encoded = encodeMessage(c) + check encoded.toHex == "0502080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235" let decoded = decodeMessage(encoded) check decoded.isOk() @@ -163,18 +181,18 @@ suite "Portal Wire Protocol Message Encodings": let message = decoded.get() check: message.kind == MessageKind.content + message.content.contentMessageType == enrsType message.content.enrs.len() == 2 message.content.enrs[0] == ByteList(e1.raw) message.content.enrs[1] == ByteList(e2.raw) - message.content.content == content test "Offer Request": let contentKeys = ContentKeysList(List(@[ByteList(@[byte 0x01, 0x02, 0x03])])) - am = OfferMessage(contentKeys: contentKeys) + o = OfferMessage(contentKeys: contentKeys) - let encoded = encodeMessage(am) - check encoded.toHex == "070400000004000000010203" + let encoded = encodeMessage(o) + check encoded.toHex == "060400000004000000010203" let decoded = decodeMessage(encoded) check decoded.isOk() @@ -185,14 +203,14 @@ suite "Portal Wire Protocol Message Encodings": message.offer.contentKeys == contentKeys test "Accept Response": + var contentKeys = ContentKeysBitList.init(8) + contentKeys.setBit(0) let connectionId = Bytes2([byte 0x01, 0x02]) - contentKeys = ContentKeysBitList.init(8) - n = AcceptMessage(connectionId: connectionId, - contentKeys: contentKeys) + a = AcceptMessage(connectionId: connectionId, contentKeys: contentKeys) - let encoded = encodeMessage(n) - check encoded.toHex == "080102060000000001" + let encoded = encodeMessage(a) + check encoded.toHex == "070102060000000101" let decoded = decodeMessage(encoded) check decoded.isOk() diff --git a/fluffy/tests/test_portal_wire_protocol.nim b/fluffy/tests/test_portal_wire_protocol.nim index 9eca27c6c..db9cdf29f 100644 --- a/fluffy/tests/test_portal_wire_protocol.nim +++ b/fluffy/tests/test_portal_wire_protocol.nim @@ -133,7 +133,6 @@ procSuite "Portal Wire Protocol Tests": check: content.isOk() content.get().enrs.len() == 1 - content.get().content.len() == 0 await test.stopTest() diff --git a/fluffy/tests/test_state_network.nim b/fluffy/tests/test_state_network.nim index d7a3e2f02..ffb2b0b9e 100644 --- a/fluffy/tests/test_state_network.nim +++ b/fluffy/tests/test_state_network.nim @@ -59,10 +59,10 @@ procSuite "State Content Network": copyMem(nodeHash.data.addr, unsafeAddr k[0], sizeof(nodeHash.data)) let + # TODO: add stateRoot, and path eventually + accountTrieNodeKey = AccountTrieNodeKey(nodeHash: nodeHash) contentKey = ContentKey( - networkId: 0'u16, - contentType: state_content.ContentType.Account, - nodeHash: nodeHash) + contentType: accountTrieNode, accountTrieNodeKey: accountTrieNodeKey) contentId = toContentId(contentKey) proto1.contentDB.put(contentId, v) @@ -72,10 +72,10 @@ procSuite "State Content Network": copyMem(nodeHash.data.addr, unsafeAddr key[0], sizeof(nodeHash.data)) let + accountTrieNodeKey = AccountTrieNodeKey(nodeHash: nodeHash) contentKey = ContentKey( - networkId: 0'u16, - contentType: state_content.ContentType.Account, - nodeHash: nodeHash) + contentType: accountTrieNode, accountTrieNodeKey: accountTrieNodeKey) + contentId = toContentId(contentKey) # Note: GetContent and thus the lookup here is not really needed, as we # only have to request data to one node. @@ -102,12 +102,10 @@ procSuite "State Content Network": node3 = initDiscoveryNode( rng, PrivateKey.random(rng[]), localAddress(20304)) - proto1 = StateNetwork.new(node1, ContentDB.new("", inMemory = true)) proto2 = StateNetwork.new(node2, ContentDB.new("", inMemory = true)) proto3 = StateNetwork.new(node3, ContentDB.new("", inMemory = true)) - # Node1 knows about Node2, and Node2 knows about Node3 which hold all content check proto1.portalProtocol.addNode(node2.localNode) == Added check proto2.portalProtocol.addNode(node3.localNode) == Added @@ -122,10 +120,9 @@ procSuite "State Content Network": copyMem(nodeHash.data.addr, unsafeAddr k[0], sizeof(nodeHash.data)) let + accountTrieNodeKey = AccountTrieNodeKey(nodeHash: nodeHash) contentKey = ContentKey( - networkId: 0'u16, - contentType: state_content.ContentType.Account, - nodeHash: nodeHash) + contentType: accountTrieNode, accountTrieNodeKey: accountTrieNodeKey) contentId = toContentId(contentKey) proto2.contentDB.put(contentId, v) @@ -138,10 +135,10 @@ procSuite "State Content Network": let firstKey = keys[0] copyMem(nodeHash.data.addr, unsafeAddr firstKey[0], sizeof(nodeHash.data)) - let contentKey = ContentKey( - networkId: 0'u16, - contentType: state_content.ContentType.Account, - nodeHash: nodeHash) + let + accountTrieNodeKey = AccountTrieNodeKey(nodeHash: nodeHash) + contentKey = ContentKey( + contentType: accountTrieNode, accountTrieNodeKey: accountTrieNodeKey) let foundContent = await proto1.getContent(contentKey) diff --git a/fluffy/tools/portalcli.nim b/fluffy/tools/portalcli.nim index d1fb13c85..345e967c3 100644 --- a/fluffy/tools/portalcli.nim +++ b/fluffy/tools/portalcli.nim @@ -162,7 +162,12 @@ proc testHandler(contentKey: state_content.ByteList): ContentResult = # Note: We don't incorperate storage in this tool so we always return # missing content. For now we are using the state network derivation but it # could be selective based on the network the tool is used for. - ContentResult(kind: ContentMissing, contentId: toContentId(contentKey)) + let contentId = toContentId(contentKey) + if contentId.isSome(): + ContentResult(kind: ContentMissing, contentId: contentId.get()) + else: + ContentResult(kind: ContentKeyValidationFailure, + error: "Failed decoding content key") proc run(config: DiscoveryConf) = let diff --git a/vendor/nim-ssz-serialization b/vendor/nim-ssz-serialization index 5d65b20d6..1f07d6500 160000 --- a/vendor/nim-ssz-serialization +++ b/vendor/nim-ssz-serialization @@ -1 +1 @@ -Subproject commit 5d65b20d6b56265902e5c9b6a2be4d1c2fd6427e +Subproject commit 1f07d6500aa708321aefd1a209aaba05b9ea80a8