mirror of https://github.com/waku-org/nwaku.git
169 lines
5.3 KiB
Nim
169 lines
5.3 KiB
Nim
{.push raises: [Defect].}
|
|
|
|
# Collection of utilities related to Waku peers
|
|
import
|
|
std/[options, sequtils, strutils],
|
|
stew/results,
|
|
stew/shims/net,
|
|
eth/keys,
|
|
eth/p2p/discoveryv5/enr,
|
|
libp2p/crypto/[crypto, secp],
|
|
libp2p/[errors,
|
|
multiaddress,
|
|
peerid,
|
|
peerinfo,
|
|
routing_record]
|
|
|
|
type
|
|
RemotePeerInfo* = ref object of RootObj
|
|
peerId*: PeerID
|
|
addrs*: seq[MultiAddress]
|
|
enr*: Option[enr.Record]
|
|
protocols*: seq[string]
|
|
|
|
func `$`*(remotePeerInfo: RemotePeerInfo): string =
|
|
$remotePeerInfo.peerId
|
|
|
|
proc init*(
|
|
p: typedesc[RemotePeerInfo],
|
|
peerId: PeerID,
|
|
addrs: seq[MultiAddress] = @[],
|
|
enr: Option[enr.Record] = none(enr.Record),
|
|
protocols: seq[string] = @[]): RemotePeerInfo =
|
|
|
|
let remotePeerInfo = RemotePeerInfo(
|
|
peerId: peerId,
|
|
addrs: addrs,
|
|
enr: enr,
|
|
protocols: protocols)
|
|
|
|
return remotePeerInfo
|
|
|
|
proc init*(p: typedesc[RemotePeerInfo],
|
|
peerId: string,
|
|
addrs: seq[MultiAddress] = @[],
|
|
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,
|
|
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)
|
|
|
|
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()
|
|
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])
|
|
|
|
## 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")
|
|
|
|
let
|
|
pubKey = ? keys.PublicKey.fromRaw(typedR.secp256k1.get)
|
|
peerId = ? PeerID.init(crypto.PublicKey(scheme: Secp256k1,
|
|
skkey: secp.SkPublicKey(pubKey)))
|
|
|
|
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")
|
|
|
|
return ok(RemotePeerInfo.init(peerId, addrs, some(enr)))
|
|
|
|
## 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.addrs,
|
|
none(enr.Record), # we could generate an ENR from PeerInfo
|
|
peerInfo.protocols)
|