mirror of https://github.com/status-im/nim-eth.git
145 lines
3.7 KiB
Nim
145 lines
3.7 KiB
Nim
# nim-eth - Node Discovery Protocol v5
|
|
# Copyright (c) 2020-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.
|
|
#
|
|
## Discovery v5 Protocol Messages as specified at
|
|
## https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire.md#protocol-messages
|
|
## These messages get RLP encoded.
|
|
##
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
import
|
|
std/[hashes, net],
|
|
stew/arrayops,
|
|
../../rlp, ./enr
|
|
|
|
type
|
|
MessageKind* = enum
|
|
# TODO This is needed only to make Nim 1.2.6 happy
|
|
# Without it, the `MessageKind` type cannot be used as
|
|
# a discriminator in case objects.
|
|
unused = 0x00
|
|
|
|
ping = 0x01
|
|
pong = 0x02
|
|
findnode = 0x03
|
|
nodes = 0x04
|
|
talkreq = 0x05
|
|
talkresp = 0x06
|
|
regtopic = 0x07
|
|
ticket = 0x08
|
|
regconfirmation = 0x09
|
|
topicquery = 0x0A
|
|
|
|
RequestId* = object
|
|
id*: seq[byte]
|
|
|
|
PingMessage* = object
|
|
enrSeq*: uint64
|
|
|
|
PongMessage* = object
|
|
enrSeq*: uint64
|
|
ip*: IpAddress
|
|
port*: uint16
|
|
|
|
FindNodeMessage* = object
|
|
distances*: seq[uint32]
|
|
|
|
NodesMessage* = object
|
|
total*: uint32
|
|
enrs*: seq[Record]
|
|
|
|
TalkReqMessage* = object
|
|
protocol*: seq[byte]
|
|
request*: seq[byte]
|
|
|
|
TalkRespMessage* = object
|
|
response*: seq[byte]
|
|
|
|
# Not implemented, specification is not final here.
|
|
RegTopicMessage* = object
|
|
TicketMessage* = object
|
|
RegConfirmationMessage* = object
|
|
TopicQueryMessage* = object
|
|
|
|
SomeMessage* = PingMessage or PongMessage or FindNodeMessage or NodesMessage or
|
|
TalkReqMessage or TalkRespMessage
|
|
|
|
Message* = object
|
|
reqId*: RequestId
|
|
case kind*: MessageKind
|
|
of ping:
|
|
ping*: PingMessage
|
|
of pong:
|
|
pong*: PongMessage
|
|
of findnode:
|
|
findNode*: FindNodeMessage
|
|
of nodes:
|
|
nodes*: NodesMessage
|
|
of talkreq:
|
|
talkreq*: TalkReqMessage
|
|
of talkresp:
|
|
talkresp*: TalkRespMessage
|
|
of regtopic:
|
|
regtopic*: RegTopicMessage
|
|
of ticket:
|
|
ticket*: TicketMessage
|
|
of regconfirmation:
|
|
regconfirmation*: RegConfirmationMessage
|
|
of topicquery:
|
|
topicquery*: TopicQueryMessage
|
|
else:
|
|
discard
|
|
|
|
template messageKind*(T: typedesc[SomeMessage]): MessageKind =
|
|
when T is PingMessage: ping
|
|
elif T is PongMessage: pong
|
|
elif T is FindNodeMessage: findNode
|
|
elif T is NodesMessage: nodes
|
|
elif T is TalkReqMessage: talkreq
|
|
elif T is TalkRespMessage: talkresp
|
|
|
|
proc read*(rlp: var Rlp, T: type RequestId): T
|
|
{.raises: [ValueError, RlpError, Defect].} =
|
|
mixin read
|
|
var reqId: RequestId
|
|
reqId.id = rlp.toBytes()
|
|
if reqId.id.len > 8:
|
|
raise newException(ValueError, "RequestId is > 8 bytes")
|
|
rlp.skipElem()
|
|
|
|
reqId
|
|
|
|
proc append*(writer: var RlpWriter, value: RequestId) =
|
|
writer.append(value.id)
|
|
|
|
proc read*(rlp: var Rlp, T: type IpAddress): T
|
|
{.raises: [RlpError, Defect].} =
|
|
let ipBytes = rlp.toBytes()
|
|
rlp.skipElem()
|
|
|
|
if ipBytes.len == 4:
|
|
var ip: array[4, byte]
|
|
discard copyFrom(ip, ipBytes)
|
|
IpAddress(family: IPv4, address_v4: ip)
|
|
elif ipBytes.len == 16:
|
|
var ip: array[16, byte]
|
|
discard copyFrom(ip, ipBytes)
|
|
IpAddress(family: IPv6, address_v6: ip)
|
|
else:
|
|
raise newException(RlpTypeMismatch,
|
|
"Amount of bytes for IP address is different from 4 or 16")
|
|
|
|
proc append*(writer: var RlpWriter, ip: IpAddress) =
|
|
case ip.family:
|
|
of IpAddressFamily.IPv4:
|
|
writer.append(ip.address_v4)
|
|
of IpAddressFamily.IPv6: writer.append(ip.address_v6)
|
|
|
|
proc hash*(reqId: RequestId): Hash =
|
|
hash(reqId.id)
|