2021-07-09 11:34:16 +00:00
|
|
|
# Nimbus - Portal Network- Message types
|
|
|
|
# Copyright (c) 2021 Status Research & Development GmbH
|
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2021-10-20 20:31:05 +00:00
|
|
|
## Definitions and encoding of the messages of the Portal wire protocol:
|
|
|
|
## https://github.com/ethereum/portal-network-specs/blob/master/portal-wire-protocol.md#request---response-messages
|
2021-07-09 11:34:16 +00:00
|
|
|
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
|
|
|
import
|
2021-09-13 13:56:44 +00:00
|
|
|
std/options,
|
2021-07-09 11:34:16 +00:00
|
|
|
stint, stew/[results, objects],
|
2021-10-23 12:28:12 +00:00
|
|
|
ssz_serialization,
|
2021-09-24 15:18:54 +00:00
|
|
|
../../common/common_types
|
2021-07-09 11:34:16 +00:00
|
|
|
|
2021-09-24 15:18:54 +00:00
|
|
|
export ssz_serialization, stint, common_types
|
2021-07-09 11:34:16 +00:00
|
|
|
|
2021-09-22 09:28:04 +00:00
|
|
|
const
|
2022-04-06 11:47:23 +00:00
|
|
|
contentKeysLimit* = 64
|
2022-08-09 12:32:41 +00:00
|
|
|
# overhead of content message is a result of 1byte for kind enum, and
|
|
|
|
# 4 bytes for offset in ssz serialization
|
|
|
|
offerMessageOverhead* = 5
|
|
|
|
|
|
|
|
# each key in ContentKeysList has uint32 offset which results in 4 bytes per
|
|
|
|
# key overhead when serialized
|
|
|
|
perContentKeyOverhead* = 4
|
2021-09-22 09:28:04 +00:00
|
|
|
|
2021-07-09 11:34:16 +00:00
|
|
|
type
|
2021-09-22 09:28:04 +00:00
|
|
|
ContentKeysList* = List[ByteList, contentKeysLimit]
|
|
|
|
ContentKeysBitList* = BitList[contentKeysLimit]
|
2021-09-03 08:57:19 +00:00
|
|
|
|
2021-10-13 19:35:54 +00:00
|
|
|
# TODO: should become part of the specific networks, considering it is custom.
|
|
|
|
CustomPayload* = object
|
|
|
|
dataRadius*: UInt256
|
|
|
|
|
2021-07-09 11:34:16 +00:00
|
|
|
MessageKind* = enum
|
2021-11-17 16:11:17 +00:00
|
|
|
ping = 0x00
|
|
|
|
pong = 0x01
|
2022-09-10 19:00:27 +00:00
|
|
|
findNodes = 0x02
|
2021-11-17 16:11:17 +00:00
|
|
|
nodes = 0x03
|
2022-09-10 19:00:27 +00:00
|
|
|
findContent = 0x04
|
2021-11-17 16:11:17 +00:00
|
|
|
content = 0x05
|
|
|
|
offer = 0x06
|
|
|
|
accept = 0x07
|
|
|
|
|
|
|
|
ContentMessageType* = enum
|
|
|
|
connectionIdType = 0x00
|
|
|
|
contentType = 0x01
|
|
|
|
enrsType = 0x02
|
2021-07-09 11:34:16 +00:00
|
|
|
|
|
|
|
PingMessage* = object
|
|
|
|
enrSeq*: uint64
|
2021-10-13 19:35:54 +00:00
|
|
|
customPayload*: ByteList
|
2021-07-09 11:34:16 +00:00
|
|
|
|
|
|
|
PongMessage* = object
|
|
|
|
enrSeq*: uint64
|
2021-10-13 19:35:54 +00:00
|
|
|
customPayload*: ByteList
|
2021-07-09 11:34:16 +00:00
|
|
|
|
2021-12-08 10:54:22 +00:00
|
|
|
FindNodesMessage* = object
|
2021-07-09 11:34:16 +00:00
|
|
|
distances*: List[uint16, 256]
|
|
|
|
|
|
|
|
NodesMessage* = object
|
|
|
|
total*: uint8
|
|
|
|
enrs*: List[ByteList, 32] # ByteList here is the rlp encoded ENR. This could
|
2021-10-13 19:35:54 +00:00
|
|
|
# also be limited to ~300 bytes instead of 2048
|
2021-07-09 11:34:16 +00:00
|
|
|
|
|
|
|
FindContentMessage* = object
|
2021-08-18 07:23:57 +00:00
|
|
|
contentKey*: ByteList
|
2021-07-09 11:34:16 +00:00
|
|
|
|
2021-10-13 19:35:54 +00:00
|
|
|
ContentMessage* = object
|
2021-11-17 16:11:17 +00:00
|
|
|
case contentMessageType*: ContentMessageType
|
|
|
|
of connectionIdType:
|
|
|
|
connectionId*: Bytes2
|
|
|
|
of contentType:
|
|
|
|
content*: ByteList
|
|
|
|
of enrsType:
|
|
|
|
enrs*: List[ByteList, 32]
|
2021-07-09 11:34:16 +00:00
|
|
|
|
2021-09-22 09:28:04 +00:00
|
|
|
OfferMessage* = object
|
|
|
|
contentKeys*: ContentKeysList
|
2021-07-09 11:34:16 +00:00
|
|
|
|
2021-09-22 09:28:04 +00:00
|
|
|
AcceptMessage* = object
|
|
|
|
connectionId*: Bytes2
|
|
|
|
contentKeys*: ContentKeysBitList
|
2021-07-09 11:34:16 +00:00
|
|
|
|
|
|
|
Message* = object
|
|
|
|
case kind*: MessageKind
|
|
|
|
of ping:
|
|
|
|
ping*: PingMessage
|
|
|
|
of pong:
|
|
|
|
pong*: PongMessage
|
2022-09-10 19:00:27 +00:00
|
|
|
of findNodes:
|
|
|
|
findNodes*: FindNodesMessage
|
2021-07-09 11:34:16 +00:00
|
|
|
of nodes:
|
|
|
|
nodes*: NodesMessage
|
2022-09-10 19:00:27 +00:00
|
|
|
of findContent:
|
|
|
|
findContent*: FindContentMessage
|
2021-10-13 19:35:54 +00:00
|
|
|
of content:
|
|
|
|
content*: ContentMessage
|
2021-09-22 09:28:04 +00:00
|
|
|
of offer:
|
|
|
|
offer*: OfferMessage
|
|
|
|
of accept:
|
|
|
|
accept*: AcceptMessage
|
2021-07-09 11:34:16 +00:00
|
|
|
|
|
|
|
SomeMessage* =
|
|
|
|
PingMessage or PongMessage or
|
2021-12-08 10:54:22 +00:00
|
|
|
FindNodesMessage or NodesMessage or
|
2021-10-13 19:35:54 +00:00
|
|
|
FindContentMessage or ContentMessage or
|
2021-09-22 09:28:04 +00:00
|
|
|
OfferMessage or AcceptMessage
|
2021-07-09 11:34:16 +00:00
|
|
|
|
|
|
|
template messageKind*(T: typedesc[SomeMessage]): MessageKind =
|
|
|
|
when T is PingMessage: ping
|
|
|
|
elif T is PongMessage: pong
|
2022-09-10 19:00:27 +00:00
|
|
|
elif T is FindNodesMessage: findNodes
|
2021-07-09 11:34:16 +00:00
|
|
|
elif T is NodesMessage: nodes
|
2022-09-10 19:00:27 +00:00
|
|
|
elif T is FindContentMessage: findContent
|
2021-10-13 19:35:54 +00:00
|
|
|
elif T is ContentMessage: content
|
2021-09-22 09:28:04 +00:00
|
|
|
elif T is OfferMessage: offer
|
|
|
|
elif T is AcceptMessage: accept
|
2021-07-09 11:34:16 +00:00
|
|
|
|
2021-07-09 14:15:10 +00:00
|
|
|
template toSszType*(x: UInt256): array[32, byte] =
|
|
|
|
toBytesLE(x)
|
2021-07-09 11:34:16 +00:00
|
|
|
|
|
|
|
func fromSszBytes*(T: type UInt256, data: openArray[byte]):
|
2022-01-31 20:57:34 +00:00
|
|
|
T {.raises: [Defect, MalformedSszError].} =
|
2021-07-09 11:34:16 +00:00
|
|
|
if data.len != sizeof(result):
|
|
|
|
raiseIncorrectSize T
|
|
|
|
|
|
|
|
T.fromBytesLE(data)
|
|
|
|
|
2021-12-08 10:54:22 +00:00
|
|
|
func encodeMessage*[T: SomeMessage](m: T): seq[byte] =
|
2021-11-17 16:11:17 +00:00
|
|
|
# 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))
|
2022-09-10 19:00:27 +00:00
|
|
|
elif T is FindNodesMessage: SSZ.encode(Message(kind: findNodes, findNodes: m))
|
2021-11-17 16:11:17 +00:00
|
|
|
elif T is NodesMessage: SSZ.encode(Message(kind: nodes, nodes: m))
|
2022-09-10 19:00:27 +00:00
|
|
|
elif T is FindContentMessage: SSZ.encode(Message(kind: findContent, findContent: m))
|
2021-11-17 16:11:17 +00:00
|
|
|
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))
|
2021-07-09 11:34:16 +00:00
|
|
|
|
2021-12-13 08:06:29 +00:00
|
|
|
func decodeMessage*(body: openArray[byte]): Result[Message, cstring] =
|
2021-07-09 11:34:16 +00:00
|
|
|
try:
|
2021-11-17 16:11:17 +00:00
|
|
|
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))
|
2021-07-09 11:34:16 +00:00
|
|
|
except SszError:
|
2021-11-17 16:11:17 +00:00
|
|
|
err("Invalid message encoding")
|
2021-08-05 14:04:29 +00:00
|
|
|
|
|
|
|
template innerMessage[T: SomeMessage](message: Message, expected: MessageKind): Option[T] =
|
|
|
|
if (message.kind == expected):
|
|
|
|
some[T](message.expected)
|
|
|
|
else:
|
|
|
|
none[T]()
|
|
|
|
|
2021-09-13 13:56:44 +00:00
|
|
|
# All our Message variants correspond to enum MessageKind, therefore we are able to
|
2021-08-05 14:04:29 +00:00
|
|
|
# zoom in on inner structure of message by defining expected type T.
|
2021-09-13 13:56:44 +00:00
|
|
|
# If expected variant is not active, return None
|
2021-12-08 10:54:22 +00:00
|
|
|
func getInnnerMessage*[T: SomeMessage](m: Message): Option[T] =
|
2021-08-05 14:04:29 +00:00
|
|
|
innerMessage[T](m, messageKind(T))
|
|
|
|
|
2021-09-13 13:56:44 +00:00
|
|
|
# Simple conversion from Option to Result, looks like something which could live in
|
2021-08-05 14:04:29 +00:00
|
|
|
# Result library.
|
2021-12-08 10:54:22 +00:00
|
|
|
func optToResult*[T, E](opt: Option[T], e: E): Result[T, E] =
|
2021-08-05 14:04:29 +00:00
|
|
|
if (opt.isSome()):
|
|
|
|
ok(opt.unsafeGet())
|
|
|
|
else:
|
|
|
|
err(e)
|
|
|
|
|
2021-12-08 10:54:22 +00:00
|
|
|
func getInnerMessageResult*[T: SomeMessage](m: Message, errMessage: cstring): Result[T, cstring] =
|
2021-08-05 14:04:29 +00:00
|
|
|
optToResult(getInnnerMessage[T](m), errMessage)
|
2022-08-09 12:32:41 +00:00
|
|
|
|
|
|
|
func getTalkReqOverhead*(protocolIdLen: int): int =
|
|
|
|
return (
|
|
|
|
16 + # IV size
|
|
|
|
55 + # header size
|
|
|
|
1 + # talkReq msg id
|
|
|
|
3 + # rlp encoding outer list, max length will be encoded in 2 bytes
|
|
|
|
9 + # request id (max = 8) + 1 byte from rlp encoding byte string
|
|
|
|
protocolIdLen + 1 + # + 1 is necessary due to rlp encoding of byte string
|
|
|
|
3 + # rlp encoding response byte string, max length in 2 bytes
|
|
|
|
16 # HMAC
|
|
|
|
)
|
|
|
|
|
|
|
|
func getTalkReqOverhead*(protocolId: openArray[byte]): int =
|
|
|
|
return getTalkReqOverhead(len(protocolId))
|