2022-11-04 09:52:27 +00:00
|
|
|
when (NimMajor, NimMinor) < (1, 4):
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
else:
|
|
|
|
{.push raises: [].}
|
2021-07-14 17:58:46 +00:00
|
|
|
|
2021-01-25 11:03:52 +00:00
|
|
|
# Collection of utilities related to Waku peers
|
|
|
|
import
|
2022-03-29 08:09:48 +00:00
|
|
|
std/[options, sequtils, strutils],
|
2021-10-06 12:29:08 +00:00
|
|
|
stew/results,
|
2021-11-01 18:02:39 +00:00
|
|
|
stew/shims/net,
|
|
|
|
eth/keys,
|
|
|
|
eth/p2p/discoveryv5/enr,
|
|
|
|
libp2p/crypto/[crypto, secp],
|
2021-10-06 12:29:08 +00:00
|
|
|
libp2p/[errors,
|
|
|
|
multiaddress,
|
2023-01-26 10:18:30 +00:00
|
|
|
multicodec,
|
2021-10-06 12:29:08 +00:00
|
|
|
peerid,
|
2022-03-29 08:09:48 +00:00
|
|
|
peerinfo,
|
|
|
|
routing_record]
|
2021-10-06 12:29:08 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
RemotePeerInfo* = ref object of RootObj
|
|
|
|
peerId*: PeerID
|
|
|
|
addrs*: seq[MultiAddress]
|
2022-03-17 16:33:17 +00:00
|
|
|
enr*: Option[enr.Record]
|
2021-10-06 12:29:08 +00:00
|
|
|
protocols*: seq[string]
|
|
|
|
|
|
|
|
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),
|
2021-10-06 12:29:08 +00:00
|
|
|
protocols: seq[string] = @[]): RemotePeerInfo =
|
|
|
|
|
|
|
|
let remotePeerInfo = RemotePeerInfo(
|
|
|
|
peerId: peerId,
|
|
|
|
addrs: addrs,
|
2022-03-17 16:33:17 +00:00
|
|
|
enr: enr,
|
2021-10-06 12:29:08 +00:00
|
|
|
protocols: protocols)
|
2023-02-07 13:06:50 +00:00
|
|
|
|
2021-10-06 12:29:08 +00:00
|
|
|
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),
|
2021-10-06 12:29:08 +00:00
|
|
|
protocols: seq[string] = @[]): RemotePeerInfo
|
|
|
|
{.raises: [Defect, ResultError[cstring], LPError].} =
|
2023-02-07 13:06:50 +00:00
|
|
|
|
2021-10-06 12:29:08 +00:00
|
|
|
let remotePeerInfo = RemotePeerInfo(
|
|
|
|
peerId: PeerID.init(peerId).tryGet(),
|
|
|
|
addrs: addrs,
|
2022-03-17 16:33:17 +00:00
|
|
|
enr: enr,
|
2021-10-06 12:29:08 +00:00
|
|
|
protocols: protocols)
|
|
|
|
|
|
|
|
return remotePeerInfo
|
2021-01-25 11:03:52 +00:00
|
|
|
|
2021-11-02 10:29:11 +00:00
|
|
|
## Check if wire Address is supported
|
|
|
|
proc validWireAddr*(ma: MultiAddress): bool =
|
|
|
|
const
|
|
|
|
ValidTransports = mapOr(TCP, WebSockets)
|
|
|
|
return ValidTransports.match(ma)
|
2021-01-25 11:03:52 +00:00
|
|
|
|
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)
|
|
|
|
|
2021-01-25 11:03:52 +00:00
|
|
|
## Parses a fully qualified peer multiaddr, in the
|
|
|
|
## format `(ip4|ip6)/tcp/p2p`, into dialable PeerInfo
|
2021-10-06 12:29:08 +00:00
|
|
|
proc parseRemotePeerInfo*(address: string): RemotePeerInfo {.raises: [Defect, ValueError, LPError].}=
|
2021-11-02 10:29:11 +00:00
|
|
|
let multiAddr = MultiAddress.init(address).tryGet()
|
2021-01-25 11:03:52 +00:00
|
|
|
|
|
|
|
var
|
2022-02-16 16:12:09 +00:00
|
|
|
nwPart, tcpPart, p2pPart, wsPart, wssPart: MultiAddress
|
2021-01-25 11:03:52 +00:00
|
|
|
|
|
|
|
for addrPart in multiAddr.items():
|
|
|
|
case addrPart[].protoName()[]
|
2022-02-16 16:12:09 +00:00
|
|
|
# All protocols listed here: https://github.com/multiformats/multiaddr/blob/b746a7d014e825221cc3aea6e57a92d78419990f/protocols.csv
|
|
|
|
of "ip4", "ip6", "dns", "dnsaddr", "dns4", "dns6":
|
|
|
|
nwPart = addrPart.tryGet()
|
2021-01-25 11:03:52 +00:00
|
|
|
of "tcp":
|
|
|
|
tcpPart = addrPart.tryGet()
|
|
|
|
of "p2p":
|
|
|
|
p2pPart = addrPart.tryGet()
|
2021-11-02 10:29:11 +00:00
|
|
|
of "ws":
|
|
|
|
wsPart = addrPart.tryGet()
|
2021-11-10 12:05:36 +00:00
|
|
|
of "wss":
|
|
|
|
wssPart = addrPart.tryGet()
|
2021-11-02 10:29:11 +00:00
|
|
|
|
2021-01-25 11:03:52 +00:00
|
|
|
# nim-libp2p dialing requires remote peers to be initialised with a peerId and a wire address
|
|
|
|
let
|
2023-02-07 13:06:50 +00:00
|
|
|
peerIdStr = p2pPart.toString()[].split("/")[^1]
|
2021-11-02 10:29:11 +00:00
|
|
|
|
2022-02-16 16:12:09 +00:00
|
|
|
wireAddr = nwPart & tcpPart & wsPart & wssPart
|
2021-11-02 10:29:11 +00:00
|
|
|
if (not wireAddr.validWireAddr()):
|
2021-01-25 11:03:52 +00:00
|
|
|
raise newException(ValueError, "Invalid node multi-address")
|
2021-11-02 10:29:11 +00:00
|
|
|
|
2021-10-06 12:29:08 +00:00
|
|
|
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")
|
2023-02-07 13:06:50 +00:00
|
|
|
|
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)))
|
2023-02-07 13:06:50 +00:00
|
|
|
|
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
|
|
|
|
2022-03-29 08:09:48 +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))
|
|
|
|
|
2021-10-06 12:29:08 +00:00
|
|
|
## Converts the local peerInfo to dialable RemotePeerInfo
|
|
|
|
## Useful for testing or internal connections
|
|
|
|
proc toRemotePeerInfo*(peerInfo: PeerInfo): RemotePeerInfo =
|
|
|
|
RemotePeerInfo.init(peerInfo.peerId,
|
2022-10-28 09:51:46 +00:00
|
|
|
peerInfo.listenAddrs,
|
2022-03-17 16:33:17 +00:00
|
|
|
none(enr.Record), # we could generate an ENR from PeerInfo
|
2021-10-06 12:29:08 +00:00
|
|
|
peerInfo.protocols)
|
2023-01-26 10:18:30 +00:00
|
|
|
|
|
|
|
## 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
|
2023-02-07 13:06:50 +00:00
|
|
|
|
|
|
|
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()
|
|
|
|
|