nwaku/waku/v2/utils/peers.nim

237 lines
6.9 KiB
Nim
Raw Normal View History

when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}
# Collection of utilities related to Waku peers
import
std/[options, sequtils, strutils, times],
chronos,
stew/results,
2021-11-01 18:02:39 +00:00
stew/shims/net,
eth/keys,
eth/p2p/discoveryv5/enr,
libp2p/crypto/[crypto, secp],
libp2p/[errors,
multiaddress,
multicodec,
peerid,
peerinfo,
routing_record]
#import
# ../node/peer_manager/waku_peer_store
# todo organize this
type
Connectedness* = enum
# NotConnected: default state for a new peer. No connection and no further information on connectedness.
NotConnected,
# CannotConnect: attempted to connect to peer, but failed.
CannotConnect,
# CanConnect: was recently connected to peer and disconnected gracefully.
CanConnect,
# Connected: actively connected to peer.
Connected
PeerOrigin* = enum
UnknownOrigin,
Discv5,
Static,
Dns
PeerDirection* = enum
UnknownDirection,
Inbound,
Outbound
type
RemotePeerInfo* = ref object of RootObj
peerId*: PeerID
addrs*: seq[MultiAddress]
2022-03-17 16:33:17 +00:00
enr*: Option[enr.Record]
protocols*: seq[string]
agent*: string
protoVersion*: string
publicKey*: crypto.PublicKey
connectedness*: Connectedness
disconnectTime*: int64
origin*: PeerOrigin
direction*: PeerDirection
lastFailedConn*: Moment
numberFailedConn*: int
func `$`*(remotePeerInfo: RemotePeerInfo): string =
$remotePeerInfo.peerId
proc init*(
p: typedesc[RemotePeerInfo],
peerId: PeerID,
addrs: seq[MultiAddress] = @[],
2022-03-17 16:33:17 +00:00
enr: Option[enr.Record] = none(enr.Record),
protocols: seq[string] = @[]): RemotePeerInfo =
let remotePeerInfo = RemotePeerInfo(
peerId: peerId,
addrs: addrs,
2022-03-17 16:33:17 +00:00
enr: enr,
protocols: protocols)
return remotePeerInfo
proc init*(p: typedesc[RemotePeerInfo],
peerId: string,
addrs: seq[MultiAddress] = @[],
2022-03-17 16:33:17 +00:00
enr: Option[enr.Record] = none(enr.Record),
protocols: seq[string] = @[]): RemotePeerInfo
{.raises: [Defect, ResultError[cstring], LPError].} =
let remotePeerInfo = RemotePeerInfo(
peerId: PeerID.init(peerId).tryGet(),
addrs: addrs,
2022-03-17 16:33:17 +00:00
enr: enr,
protocols: protocols)
return remotePeerInfo
## Check if wire Address is supported
proc validWireAddr*(ma: MultiAddress): bool =
const
ValidTransports = mapOr(TCP, WebSockets)
return ValidTransports.match(ma)
2021-11-01 18:02:39 +00:00
func getTransportProtocol(typedR: TypedRecord): Option[IpTransportProtocol] =
if typedR.tcp6.isSome or typedR.tcp.isSome:
return some(IpTransportProtocol.tcpProtocol)
if typedR.udp6.isSome or typedR.udp.isSome:
return some(IpTransportProtocol.udpProtocol)
return none(IpTransportProtocol)
## Parses a fully qualified peer multiaddr, in the
## format `(ip4|ip6)/tcp/p2p`, into dialable PeerInfo
proc parseRemotePeerInfo*(address: string): RemotePeerInfo {.raises: [Defect, ValueError, LPError].}=
let multiAddr = MultiAddress.init(address).tryGet()
var
nwPart, tcpPart, p2pPart, wsPart, wssPart: MultiAddress
for addrPart in multiAddr.items():
case addrPart[].protoName()[]
# All protocols listed here: https://github.com/multiformats/multiaddr/blob/b746a7d014e825221cc3aea6e57a92d78419990f/protocols.csv
of "ip4", "ip6", "dns", "dnsaddr", "dns4", "dns6":
nwPart = addrPart.tryGet()
of "tcp":
tcpPart = addrPart.tryGet()
of "p2p":
p2pPart = addrPart.tryGet()
of "ws":
wsPart = addrPart.tryGet()
Secure websocket (#759) * add config Signed-off-by: rshiv <reeshav96@gmail.com> * secure websocket integration Signed-off-by: rshiv <reeshav96@gmail.com> * secure websocket debug Signed-off-by: rshiv <reeshav96@gmail.com> * config change Signed-off-by: rshiv <reeshav96@gmail.com> * working secure websocket changes Signed-off-by: rshiv <reeshav96@gmail.com> * Update submodules (#761) * Release v0.6 (#760) * Read from path Signed-off-by: rshiv <reeshav96@gmail.com> * Tests for secure websockets Signed-off-by: rshiv <reeshav96@gmail.com> * CI failure fix Signed-off-by: rshiv <reeshav96@gmail.com> * path resolver CI Signed-off-by: rshiv <reeshav96@gmail.com> * self review fix Signed-off-by: rshiv <reeshav96@gmail.com> * Update examples/v2/config_chat2.nim Co-authored-by: Hanno Cornelius <68783915+jm-clius@users.noreply.github.com> * review comment fix Signed-off-by: rshiv <reeshav96@gmail.com> * review comment fix Signed-off-by: rshiv <reeshav96@gmail.com> * Update waku/v2/utils/peers.nim Co-authored-by: Hanno Cornelius <68783915+jm-clius@users.noreply.github.com> * Update waku/v2/utils/wakuswitch.nim Co-authored-by: Hanno Cornelius <68783915+jm-clius@users.noreply.github.com> * Update waku/v2/utils/wakuswitch.nim Co-authored-by: Hanno Cornelius <68783915+jm-clius@users.noreply.github.com> * Update waku/v2/node/wakunode2.nim Co-authored-by: Hanno Cornelius <68783915+jm-clius@users.noreply.github.com> * review comment fix Signed-off-by: rshiv <reeshav96@gmail.com> * Update tests/v2/test_wakunode.nim Co-authored-by: Sanaz Taheri Boshrooyeh <35961250+staheri14@users.noreply.github.com> * Update waku/v2/utils/wakuswitch.nim Co-authored-by: Sanaz Taheri Boshrooyeh <35961250+staheri14@users.noreply.github.com> * Update waku/v2/utils/wakuswitch.nim Co-authored-by: Sanaz Taheri Boshrooyeh <35961250+staheri14@users.noreply.github.com> * Update waku/v2/utils/wakuswitch.nim Co-authored-by: Sanaz Taheri Boshrooyeh <35961250+staheri14@users.noreply.github.com> * handle review comments Signed-off-by: rshiv <reeshav96@gmail.com> Co-authored-by: Hanno Cornelius <68783915+jm-clius@users.noreply.github.com> Co-authored-by: Sanaz Taheri Boshrooyeh <35961250+staheri14@users.noreply.github.com>
2021-11-10 12:05:36 +00:00
of "wss":
wssPart = addrPart.tryGet()
# nim-libp2p dialing requires remote peers to be initialised with a peerId and a wire address
let
peerIdStr = p2pPart.toString()[].split("/")[^1]
wireAddr = nwPart & tcpPart & wsPart & wssPart
if (not wireAddr.validWireAddr()):
raise newException(ValueError, "Invalid node multi-address")
return RemotePeerInfo.init(peerIdStr, @[wireAddr])
2021-11-01 18:02:39 +00:00
## Converts an ENR to dialable RemotePeerInfo
proc toRemotePeerInfo*(enr: enr.Record): Result[RemotePeerInfo, cstring] =
let typedR = ? enr.toTypedRecord
if not typedR.secp256k1.isSome:
return err("enr: no secp256k1 key in record")
2021-11-01 18:02:39 +00:00
let
pubKey = ? keys.PublicKey.fromRaw(typedR.secp256k1.get)
peerId = ? PeerID.init(crypto.PublicKey(scheme: Secp256k1,
skkey: secp.SkPublicKey(pubKey)))
2021-11-01 18:02:39 +00:00
var addrs = newSeq[MultiAddress]()
let transportProto = getTransportProtocol(typedR)
if transportProto.isNone:
return err("enr: could not determine transport protocol")
case transportProto.get()
of tcpProtocol:
if typedR.ip.isSome and typedR.tcp.isSome:
let ip = ipv4(typedR.ip.get)
addrs.add MultiAddress.init(ip, tcpProtocol, Port typedR.tcp.get)
if typedR.ip6.isSome:
let ip = ipv6(typedR.ip6.get)
if typedR.tcp6.isSome:
addrs.add MultiAddress.init(ip, tcpProtocol, Port typedR.tcp6.get)
elif typedR.tcp.isSome:
addrs.add MultiAddress.init(ip, tcpProtocol, Port typedR.tcp.get)
else:
discard
of udpProtocol:
if typedR.ip.isSome and typedR.udp.isSome:
let ip = ipv4(typedR.ip.get)
addrs.add MultiAddress.init(ip, udpProtocol, Port typedR.udp.get)
if typedR.ip6.isSome:
let ip = ipv6(typedR.ip6.get)
if typedR.udp6.isSome:
addrs.add MultiAddress.init(ip, udpProtocol, Port typedR.udp6.get)
elif typedR.udp.isSome:
addrs.add MultiAddress.init(ip, udpProtocol, Port typedR.udp.get)
else:
discard
if addrs.len == 0:
return err("enr: no addresses in record")
2022-03-17 16:33:17 +00:00
return ok(RemotePeerInfo.init(peerId, addrs, some(enr)))
2021-11-01 18:02:39 +00:00
## Converts peer records to dialable RemotePeerInfo
## Useful if signed peer records have been received in an exchange
proc toRemotePeerInfo*(peerRecord: PeerRecord): RemotePeerInfo =
RemotePeerInfo.init(peerRecord.peerId,
peerRecord.addresses.mapIt(it.address))
## Converts the local peerInfo to dialable RemotePeerInfo
## Useful for testing or internal connections
proc toRemotePeerInfo*(peerInfo: PeerInfo): RemotePeerInfo =
RemotePeerInfo.init(peerInfo.peerId,
peerInfo.listenAddrs,
2022-03-17 16:33:17 +00:00
none(enr.Record), # we could generate an ENR from PeerInfo
peerInfo.protocols)
## Checks if a multiaddress contains a given protocol
## Useful for filtering multiaddresses based on their protocols
proc hasProtocol*(ma: MultiAddress, proto: string): bool =
## Returns ``true`` if ``ma`` contains protocol ``proto``.
let protos = ma.protocols()
if protos.isErr():
return false
for p in protos.get():
if p == MultiCodec.codec(proto):
return true
return false
func hasUdpPort*(peer: RemotePeerInfo): bool =
if peer.enr.isNone():
return false
let
enr = peer.enr.get()
typedEnrRes = enr.toTypedRecord()
if typedEnrRes.isErr():
return false
let typedEnr = typedEnrRes.get()
typedEnr.udp.isSome() or typedEnr.udp6.isSome()