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
This commit is contained in:
Kim De Mey 2021-11-17 17:11:17 +01:00 committed by GitHub
parent 414fdafab9
commit bdbd8e4c87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 225 additions and 173 deletions

View File

@ -13,3 +13,4 @@ import
type type
ByteList* = List[byte, 2048] ByteList* = List[byte, 2048]
Bytes2* = array[2, byte] Bytes2* = array[2, byte]
Bytes32* = array[32, byte]

View File

@ -5,78 +5,124 @@
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # * 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. # 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].} {.push raises: [Defect].}
import import
std/options, std/options,
nimcrypto/[sha2, hash], stew/objects, stint, nimcrypto/[hash, sha2, keccak], stew/objects, stint,
ssz_serialization, ssz_serialization,
../../common/common_types ../../common/common_types
export ssz_serialization, common_types export ssz_serialization, common_types
type type
ContentType* = enum
Account = 0x01
ContractStorage = 0x02
ContractBytecode = 0x03
NetworkId* = uint16
NodeHash* = MDigest[32 * 8] # keccak256 NodeHash* = MDigest[32 * 8] # keccak256
CodeHash* = MDigest[32 * 8] # keccak256 CodeHash* = MDigest[32 * 8] # keccak256
Address* = array[20, byte] Address* = array[20, byte]
ContentKey* = object ContentType* = enum
networkId*: NetworkId accountTrieNode = 0x00
contentType*: ContentType contractStorageTrieNode = 0x01
# TODO: How shall we deal with the different ContentKey structures? accountTrieProof = 0x02
# Lets start with just node hashes for now. contractStorageTrieProof = 0x03
# address: Address contractBytecode = 0x04
# triePath: ByteList
AccountTrieNodeKey* = object
path*: ByteList
nodeHash*: NodeHash 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 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 = func encode*(contentKey: ContentKey): ByteList =
ByteList.init(SSZ.encode(contentKey)) ByteList.init(SSZ.encode(contentKey))
# TODO consider if more powerfull error handling is necessary here.
func decode*(contentKey: ByteList): Option[ContentKey] = func decode*(contentKey: ByteList): Option[ContentKey] =
try: try:
some(SSZ.decode(contentKey.asSeq(), ContentKey)) some(SSZ.decode(contentKey.asSeq(), ContentKey))
except SszError: except SszError:
return none[ContentKey]() return none[ContentKey]()
func toContentId*(contentKey: ByteList): ContentId = template computeContentId*(digestCtxType: type, body: untyped): ContentId =
# TODO: Hash function to be defined, sha256 used now, might be confusing var h {.inject.}: digestCtxType
# with keccak256 that is used for the actual nodes: init(h)
# https://github.com/ethereum/stateless-ethereum-specs/blob/master/state-network.md#content body
let idHash = sha2.sha_256.digest(contentKey.asSeq()) let idHash = finish(h)
readUintBE[256](idHash.data) readUintBE[256](idHash.data)
func toContentId*(contentKey: ContentKey): ContentId = proc toContentId*(contentKey: ContentKey): ContentId =
toContentId(encode(contentKey)) 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)

View File

@ -24,17 +24,20 @@ type StateNetwork* = ref object
proc getHandler(contentDB: ContentDB): ContentHandler = proc getHandler(contentDB: ContentDB): ContentHandler =
return (proc (contentKey: state_content.ByteList): ContentResult = return (proc (contentKey: state_content.ByteList): ContentResult =
let contentId = toContentId(contentKey) let contentId = toContentId(contentKey)
let maybeContent = contentDB.get(contentId) if contentId.isSome():
if (maybeContent.isSome()): let maybeContent = contentDB.get(contentId.get())
ContentResult(kind: ContentFound, content: maybeContent.unsafeGet()) if (maybeContent.isSome()):
ContentResult(kind: ContentFound, content: maybeContent.unsafeGet())
else:
ContentResult(kind: ContentMissing, contentId: contentId.get())
else: else:
ContentResult(kind: ContentMissing, contentId: contentId)) ContentResult(kind: ContentKeyValidationFailure, error: ""))
proc getContent*(n: StateNetwork, key: ContentKey): proc getContent*(n: StateNetwork, key: ContentKey):
Future[Option[seq[byte]]] {.async.} = Future[Option[seq[byte]]] {.async.} =
let let
keyEncoded = encode(key) keyEncoded = encode(key)
contentId = toContentId(keyEncoded) contentId = toContentId(key)
let nodeId = n.portalProtocol.localNode.id let nodeId = n.portalProtocol.localNode.id

View File

@ -30,16 +30,19 @@ type
dataRadius*: UInt256 dataRadius*: UInt256
MessageKind* = enum MessageKind* = enum
unused = 0x00 ping = 0x00
pong = 0x01
findnode = 0x02
nodes = 0x03
findcontent = 0x04
content = 0x05
offer = 0x06
accept = 0x07
ping = 0x01 ContentMessageType* = enum
pong = 0x02 connectionIdType = 0x00
findnode = 0x03 contentType = 0x01
nodes = 0x04 enrsType = 0x02
findcontent = 0x05
content = 0x06
offer = 0x07
accept = 0x08
PingMessage* = object PingMessage* = object
enrSeq*: uint64 enrSeq*: uint64
@ -60,11 +63,14 @@ type
FindContentMessage* = object FindContentMessage* = object
contentKey*: ByteList contentKey*: ByteList
# TODO: Must become an SSZ Union
ContentMessage* = object ContentMessage* = object
connectionId*: Bytes2 case contentMessageType*: ContentMessageType
content*: ByteList of connectionIdType:
enrs*: List[ByteList, 32] connectionId*: Bytes2
of contentType:
content*: ByteList
of enrsType:
enrs*: List[ByteList, 32]
OfferMessage* = object OfferMessage* = object
contentKeys*: ContentKeysList contentKeys*: ContentKeysList
@ -73,7 +79,6 @@ type
connectionId*: Bytes2 connectionId*: Bytes2
contentKeys*: ContentKeysBitList contentKeys*: ContentKeysBitList
# TODO: Needs to become an SSZ Union
Message* = object Message* = object
case kind*: MessageKind case kind*: MessageKind
of ping: of ping:
@ -81,7 +86,7 @@ type
of pong: of pong:
pong*: PongMessage pong*: PongMessage
of findnode: of findnode:
findNode*: FindNodeMessage findnode*: FindNodeMessage
of nodes: of nodes:
nodes*: NodesMessage nodes*: NodesMessage
of findcontent: of findcontent:
@ -92,8 +97,6 @@ type
offer*: OfferMessage offer*: OfferMessage
of accept: of accept:
accept*: AcceptMessage accept*: AcceptMessage
else:
discard
SomeMessage* = SomeMessage* =
PingMessage or PongMessage or PingMessage or PongMessage or
@ -104,7 +107,7 @@ type
template messageKind*(T: typedesc[SomeMessage]): MessageKind = template messageKind*(T: typedesc[SomeMessage]): MessageKind =
when T is PingMessage: ping when T is PingMessage: ping
elif T is PongMessage: pong elif T is PongMessage: pong
elif T is FindNodeMessage: findNode elif T is FindNodeMessage: findnode
elif T is NodesMessage: nodes elif T is NodesMessage: nodes
elif T is FindContentMessage: findcontent elif T is FindContentMessage: findcontent
elif T is ContentMessage: content elif T is ContentMessage: content
@ -122,42 +125,24 @@ func fromSszBytes*(T: type UInt256, data: openArray[byte]):
T.fromBytesLE(data) T.fromBytesLE(data)
proc encodeMessage*[T: SomeMessage](m: T): seq[byte] = 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] = 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: try:
case kind if body.len < 1: # TODO: This check should probably move a layer down
of unused: return err("Invalid message type") return err("No message data, peer might not support this talk protocol")
of ping: ok(SSZ.decode(body, Message))
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)
except SszError: except SszError:
return err("Invalid message encoding") err("Invalid message encoding")
ok(message)
template innerMessage[T: SomeMessage](message: Message, expected: MessageKind): Option[T] = template innerMessage[T: SomeMessage](message: Message, expected: MessageKind): Option[T] =
if (message.kind == expected): if (message.kind == expected):

View File

@ -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 # TODO: Need to provide uTP connectionId when content is too large for a
# single response. # single response.
let content = contentHandlingResult.content let content = contentHandlingResult.content
let enrs = List[ByteList, 32](@[]) # Empty enrs when payload is send
encodeMessage(ContentMessage( encodeMessage(ContentMessage(
enrs: enrs, content: ByteList(content))) contentMessageType: contentType, content: ByteList(content)))
of ContentMissing: of ContentMissing:
let let
contentId = contentHandlingResult.contentId contentId = contentHandlingResult.contentId
closestNodes = p.routingTable.neighbours( closestNodes = p.routingTable.neighbours(
NodeId(contentId), seenOnly = true) NodeId(contentId), seenOnly = true)
content = ByteList(@[]) # Empty payload when enrs are send
enrs = enrs =
closestNodes.map(proc(x: Node): ByteList = ByteList(x.record.raw)) closestNodes.map(proc(x: Node): ByteList = ByteList(x.record.raw))
encodeMessage(ContentMessage( encodeMessage(ContentMessage(
enrs: List[ByteList, 32](List(enrs)), content: content)) contentMessageType: enrsType, enrs: List[ByteList, 32](List(enrs))))
of ContentKeyValidationFailure: 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 content = ByteList(@[])
let enrs = List[ByteList, 32](@[]) # Empty enrs when payload is send
encodeMessage(ContentMessage( encodeMessage(ContentMessage(
enrs: enrs, content: content)) contentMessageType: contentType, content: content))
proc handleOffer(p: PortalProtocol, a: OfferMessage): seq[byte] = proc handleOffer(p: PortalProtocol, a: OfferMessage): seq[byte] =
let let
@ -203,8 +202,9 @@ proc messageHandler*(protocol: TalkProtocol, request: seq[byte],
of MessageKind.offer: of MessageKind.offer:
p.handleOffer(message.offer) p.handleOffer(message.offer)
else: else:
# This shouldn't occur as the 0 case is already covered in `decodedMessage` # This would mean a that Portal wire response message is being send over a
debug "Packet decoding error: Invalid message type" # discv5 talkreq message.
debug "Invalid Portal wire message type over talkreq", kind = message.kind
@[] @[]
else: else:
debug "Packet decoding error", error = decoded.error, srcId, srcUdpAddress 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, proc handleFoundContentMessage(p: PortalProtocol, m: ContentMessage,
dst: Node, nodes: var seq[Node]): LookupResult = 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 records = recordsFromBytes(m.enrs)
let verifiedNodes = verifyNodesRecords(records, dst, EnrsResultLimit) let verifiedNodes = verifyNodesRecords(records, dst, EnrsResultLimit)
nodes.add(verifiedNodes) nodes.add(verifiedNodes)
@ -397,16 +404,7 @@ proc handleFoundContentMessage(p: PortalProtocol, m: ContentMessage,
# Attempt to add all nodes discovered # Attempt to add all nodes discovered
discard p.routingTable.addNode(n) discard p.routingTable.addNode(n)
return LookupResult(kind: Nodes, nodes: nodes) 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)
proc contentLookupWorker(p: PortalProtocol, destNode: Node, target: ByteList): proc contentLookupWorker(p: PortalProtocol, destNode: Node, target: ByteList):
Future[LookupResult] {.async.} = Future[LookupResult] {.async.} =

View File

@ -13,8 +13,8 @@ import
suite "Portal Wire Protocol Message Encodings": suite "Portal Wire Protocol Message Encodings":
test "Ping Request": test "Ping Request":
var dataRadius: UInt256
let let
dataRadius = UInt256.high() - 1 # Full radius - 1
enrSeq = 1'u64 enrSeq = 1'u64
# Can be any custom payload, testing with just dataRadius here. # Can be any custom payload, testing with just dataRadius here.
customPayload = ByteList(SSZ.encode(CustomPayload(dataRadius: dataRadius))) customPayload = ByteList(SSZ.encode(CustomPayload(dataRadius: dataRadius)))
@ -22,7 +22,7 @@ suite "Portal Wire Protocol Message Encodings":
let encoded = encodeMessage(p) let encoded = encodeMessage(p)
check encoded.toHex == check encoded.toHex ==
"0101000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000" "0001000000000000000c000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
let decoded = decodeMessage(encoded) let decoded = decodeMessage(encoded)
check decoded.isOk() check decoded.isOk()
@ -33,8 +33,8 @@ suite "Portal Wire Protocol Message Encodings":
message.ping.customPayload == customPayload message.ping.customPayload == customPayload
test "Pong Response": test "Pong Response":
var dataRadius: UInt256
let let
dataRadius = UInt256.high() div 2.stuint(256) # Radius of half the UInt256
enrSeq = 1'u64 enrSeq = 1'u64
# Can be any custom payload, testing with just dataRadius here. # Can be any custom payload, testing with just dataRadius here.
customPayload = ByteList(SSZ.encode(CustomPayload(dataRadius: dataRadius))) customPayload = ByteList(SSZ.encode(CustomPayload(dataRadius: dataRadius)))
@ -42,7 +42,7 @@ suite "Portal Wire Protocol Message Encodings":
let encoded = encodeMessage(p) let encoded = encodeMessage(p)
check encoded.toHex == check encoded.toHex ==
"0201000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000" "0101000000000000000c000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"
let decoded = decodeMessage(encoded) let decoded = decodeMessage(encoded)
check decoded.isOk() check decoded.isOk()
@ -54,11 +54,11 @@ suite "Portal Wire Protocol Message Encodings":
test "FindNode Request": test "FindNode Request":
let let
distances = List[uint16, 256](@[0x0100'u16]) distances = List[uint16, 256](@[0x0100'u16, 0x00ff'u16])
fn = FindNodeMessage(distances: distances) fn = FindNodeMessage(distances: distances)
let encoded = encodeMessage(fn) let encoded = encodeMessage(fn)
check encoded.toHex == "03040000000001" check encoded.toHex == "02040000000001ff00"
let decoded = decodeMessage(encoded) let decoded = decodeMessage(encoded)
check decoded.isOk() check decoded.isOk()
@ -68,13 +68,13 @@ suite "Portal Wire Protocol Message Encodings":
message.kind == findnode message.kind == findnode
message.findnode.distances == distances message.findnode.distances == distances
test "Nodes Response - empty": test "Nodes Response - empty enr list":
let let
total = 0x1'u8 total = 0x1'u8
n = NodesMessage(total: total) n = NodesMessage(total: total)
let encoded = encodeMessage(n) let encoded = encodeMessage(n)
check encoded.toHex == "040105000000" check encoded.toHex == "030105000000"
let decoded = decodeMessage(encoded) let decoded = decodeMessage(encoded)
check decoded.isOk() check decoded.isOk()
@ -85,7 +85,7 @@ suite "Portal Wire Protocol Message Encodings":
message.nodes.total == total message.nodes.total == total
message.nodes.enrs.len() == 0 message.nodes.enrs.len() == 0
test "Nodes Response - enr": test "Nodes Response - enrs":
var e1, e2: Record var e1, e2: Record
check: check:
e1.fromURI("enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg") 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)])) n = NodesMessage(total: total, enrs: List[ByteList, 32](@[ByteList(e1.raw), ByteList(e2.raw)]))
let encoded = encodeMessage(n) let encoded = encodeMessage(n)
check encoded.toHex == "040105000000080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235" check encoded.toHex == "030105000000080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235"
let decoded = decodeMessage(encoded) let decoded = decodeMessage(encoded)
check decoded.isOk() check decoded.isOk()
@ -110,12 +110,13 @@ suite "Portal Wire Protocol Message Encodings":
message.nodes.enrs[1] == ByteList(e2.raw) message.nodes.enrs[1] == ByteList(e2.raw)
test "FindContent Request": test "FindContent Request":
const contentKeyString = "0x706f7274616c"
let let
contentEncoded = ByteList.init(@[1'u8]) contentKey = ByteList.init(hexToSeqByte(contentKeyString))
fn = FindContentMessage(contentKey: contentEncoded) fc = FindContentMessage(contentKey: contentKey)
let encoded = encodeMessage(fn) let encoded = encodeMessage(fc)
check encoded.toHex == "050400000001" check encoded.toHex == "0404000000706f7274616c"
let decoded = decodeMessage(encoded) let decoded = decodeMessage(encoded)
check decoded.isOk() check decoded.isOk()
@ -123,16 +124,16 @@ suite "Portal Wire Protocol Message Encodings":
let message = decoded.get() let message = decoded.get()
check: check:
message.kind == findcontent message.kind == findcontent
message.findcontent.contentKey == contentEncoded message.findcontent.contentKey == contentKey
test "Content Response - payload": test "Content Response - connection id":
let let
enrs = List[ByteList, 32](@[]) connectionId = Bytes2([byte 0x01, 0x02])
content = ByteList(@[byte 0x01, 0x02, 0x03]) c = ContentMessage(
n = ContentMessage(enrs: enrs, content: content) contentMessageType: connectionIdType, connectionId: connectionId)
let encoded = encodeMessage(n) let encoded = encodeMessage(c)
check encoded.toHex == "0600000a0000000d000000010203" check encoded.toHex == "05000102"
let decoded = decodeMessage(encoded) let decoded = decodeMessage(encoded)
check decoded.isOk() check decoded.isOk()
@ -140,7 +141,25 @@ suite "Portal Wire Protocol Message Encodings":
let message = decoded.get() let message = decoded.get()
check: check:
message.kind == MessageKind.content 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 message.content.content == content
test "Content Response - enrs": test "Content Response - enrs":
@ -151,11 +170,10 @@ suite "Portal Wire Protocol Message Encodings":
let let
enrs = List[ByteList, 32](@[ByteList(e1.raw), ByteList(e2.raw)]) enrs = List[ByteList, 32](@[ByteList(e1.raw), ByteList(e2.raw)])
content = ByteList(@[]) c = ContentMessage(contentMessageType: enrsType, enrs: enrs)
n = ContentMessage(enrs: enrs, content: content)
let encoded = encodeMessage(n) let encoded = encodeMessage(c)
check encoded.toHex == "0600000a0000000a000000080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235" check encoded.toHex == "0502080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235"
let decoded = decodeMessage(encoded) let decoded = decodeMessage(encoded)
check decoded.isOk() check decoded.isOk()
@ -163,18 +181,18 @@ suite "Portal Wire Protocol Message Encodings":
let message = decoded.get() let message = decoded.get()
check: check:
message.kind == MessageKind.content message.kind == MessageKind.content
message.content.contentMessageType == enrsType
message.content.enrs.len() == 2 message.content.enrs.len() == 2
message.content.enrs[0] == ByteList(e1.raw) message.content.enrs[0] == ByteList(e1.raw)
message.content.enrs[1] == ByteList(e2.raw) message.content.enrs[1] == ByteList(e2.raw)
message.content.content == content
test "Offer Request": test "Offer Request":
let let
contentKeys = ContentKeysList(List(@[ByteList(@[byte 0x01, 0x02, 0x03])])) contentKeys = ContentKeysList(List(@[ByteList(@[byte 0x01, 0x02, 0x03])]))
am = OfferMessage(contentKeys: contentKeys) o = OfferMessage(contentKeys: contentKeys)
let encoded = encodeMessage(am) let encoded = encodeMessage(o)
check encoded.toHex == "070400000004000000010203" check encoded.toHex == "060400000004000000010203"
let decoded = decodeMessage(encoded) let decoded = decodeMessage(encoded)
check decoded.isOk() check decoded.isOk()
@ -185,14 +203,14 @@ suite "Portal Wire Protocol Message Encodings":
message.offer.contentKeys == contentKeys message.offer.contentKeys == contentKeys
test "Accept Response": test "Accept Response":
var contentKeys = ContentKeysBitList.init(8)
contentKeys.setBit(0)
let let
connectionId = Bytes2([byte 0x01, 0x02]) connectionId = Bytes2([byte 0x01, 0x02])
contentKeys = ContentKeysBitList.init(8) a = AcceptMessage(connectionId: connectionId, contentKeys: contentKeys)
n = AcceptMessage(connectionId: connectionId,
contentKeys: contentKeys)
let encoded = encodeMessage(n) let encoded = encodeMessage(a)
check encoded.toHex == "080102060000000001" check encoded.toHex == "070102060000000101"
let decoded = decodeMessage(encoded) let decoded = decodeMessage(encoded)
check decoded.isOk() check decoded.isOk()

View File

@ -133,7 +133,6 @@ procSuite "Portal Wire Protocol Tests":
check: check:
content.isOk() content.isOk()
content.get().enrs.len() == 1 content.get().enrs.len() == 1
content.get().content.len() == 0
await test.stopTest() await test.stopTest()

View File

@ -59,10 +59,10 @@ procSuite "State Content Network":
copyMem(nodeHash.data.addr, unsafeAddr k[0], sizeof(nodeHash.data)) copyMem(nodeHash.data.addr, unsafeAddr k[0], sizeof(nodeHash.data))
let let
# TODO: add stateRoot, and path eventually
accountTrieNodeKey = AccountTrieNodeKey(nodeHash: nodeHash)
contentKey = ContentKey( contentKey = ContentKey(
networkId: 0'u16, contentType: accountTrieNode, accountTrieNodeKey: accountTrieNodeKey)
contentType: state_content.ContentType.Account,
nodeHash: nodeHash)
contentId = toContentId(contentKey) contentId = toContentId(contentKey)
proto1.contentDB.put(contentId, v) proto1.contentDB.put(contentId, v)
@ -72,10 +72,10 @@ procSuite "State Content Network":
copyMem(nodeHash.data.addr, unsafeAddr key[0], sizeof(nodeHash.data)) copyMem(nodeHash.data.addr, unsafeAddr key[0], sizeof(nodeHash.data))
let let
accountTrieNodeKey = AccountTrieNodeKey(nodeHash: nodeHash)
contentKey = ContentKey( contentKey = ContentKey(
networkId: 0'u16, contentType: accountTrieNode, accountTrieNodeKey: accountTrieNodeKey)
contentType: state_content.ContentType.Account, contentId = toContentId(contentKey)
nodeHash: nodeHash)
# Note: GetContent and thus the lookup here is not really needed, as we # Note: GetContent and thus the lookup here is not really needed, as we
# only have to request data to one node. # only have to request data to one node.
@ -102,12 +102,10 @@ procSuite "State Content Network":
node3 = initDiscoveryNode( node3 = initDiscoveryNode(
rng, PrivateKey.random(rng[]), localAddress(20304)) rng, PrivateKey.random(rng[]), localAddress(20304))
proto1 = StateNetwork.new(node1, ContentDB.new("", inMemory = true)) proto1 = StateNetwork.new(node1, ContentDB.new("", inMemory = true))
proto2 = StateNetwork.new(node2, ContentDB.new("", inMemory = true)) proto2 = StateNetwork.new(node2, ContentDB.new("", inMemory = true))
proto3 = StateNetwork.new(node3, 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 # Node1 knows about Node2, and Node2 knows about Node3 which hold all content
check proto1.portalProtocol.addNode(node2.localNode) == Added check proto1.portalProtocol.addNode(node2.localNode) == Added
check proto2.portalProtocol.addNode(node3.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)) copyMem(nodeHash.data.addr, unsafeAddr k[0], sizeof(nodeHash.data))
let let
accountTrieNodeKey = AccountTrieNodeKey(nodeHash: nodeHash)
contentKey = ContentKey( contentKey = ContentKey(
networkId: 0'u16, contentType: accountTrieNode, accountTrieNodeKey: accountTrieNodeKey)
contentType: state_content.ContentType.Account,
nodeHash: nodeHash)
contentId = toContentId(contentKey) contentId = toContentId(contentKey)
proto2.contentDB.put(contentId, v) proto2.contentDB.put(contentId, v)
@ -138,10 +135,10 @@ procSuite "State Content Network":
let firstKey = keys[0] let firstKey = keys[0]
copyMem(nodeHash.data.addr, unsafeAddr firstKey[0], sizeof(nodeHash.data)) copyMem(nodeHash.data.addr, unsafeAddr firstKey[0], sizeof(nodeHash.data))
let contentKey = ContentKey( let
networkId: 0'u16, accountTrieNodeKey = AccountTrieNodeKey(nodeHash: nodeHash)
contentType: state_content.ContentType.Account, contentKey = ContentKey(
nodeHash: nodeHash) contentType: accountTrieNode, accountTrieNodeKey: accountTrieNodeKey)
let foundContent = await proto1.getContent(contentKey) let foundContent = await proto1.getContent(contentKey)

View File

@ -162,7 +162,12 @@ proc testHandler(contentKey: state_content.ByteList): ContentResult =
# Note: We don't incorperate storage in this tool so we always return # 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 # 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. # 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) = proc run(config: DiscoveryConf) =
let let

@ -1 +1 @@
Subproject commit 5d65b20d6b56265902e5c9b6a2be4d1c2fd6427e Subproject commit 1f07d6500aa708321aefd1a209aaba05b9ea80a8