Create an ObservedAddrManager and add an AddressMapper in AutonatService and AutoRelayService (#871)
Co-authored-by: Tanguy <tanguy@status.im>
This commit is contained in:
parent
bac754e2ad
commit
af5299f26c
2
.pinned
2
.pinned
|
@ -1,6 +1,6 @@
|
||||||
bearssl;https://github.com/status-im/nim-bearssl@#acf9645e328bdcab481cfda1c158e07ecd46bd7b
|
bearssl;https://github.com/status-im/nim-bearssl@#acf9645e328bdcab481cfda1c158e07ecd46bd7b
|
||||||
chronicles;https://github.com/status-im/nim-chronicles@#32ac8679680ea699f7dbc046e8e0131cac97d41a
|
chronicles;https://github.com/status-im/nim-chronicles@#32ac8679680ea699f7dbc046e8e0131cac97d41a
|
||||||
chronos;https://github.com/status-im/nim-chronos@#5d3da66e563d21277b57a9b601744273c083a01b
|
chronos;https://github.com/status-im/nim-chronos@#f7835a192b45c37e97614d865141f21eea8c156e
|
||||||
dnsclient;https://github.com/ba0f3/dnsclient.nim@#fcd7443634b950eaea574e5eaa00a628ae029823
|
dnsclient;https://github.com/ba0f3/dnsclient.nim@#fcd7443634b950eaea574e5eaa00a628ae029823
|
||||||
faststreams;https://github.com/status-im/nim-faststreams@#814f8927e1f356f39219f37f069b83066bcc893a
|
faststreams;https://github.com/status-im/nim-faststreams@#814f8927e1f356f39219f37f069b83066bcc893a
|
||||||
httputils;https://github.com/status-im/nim-http-utils@#a85bd52ae0a956983ca6b3267c72961d2ec0245f
|
httputils;https://github.com/status-im/nim-http-utils@#a85bd52ae0a956983ca6b3267c72961d2ec0245f
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
# Nim-LibP2P
|
||||||
|
# Copyright (c) 2023 Status Research & Development GmbH
|
||||||
|
# Licensed under either of
|
||||||
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
# at your option.
|
||||||
|
# This file may not be copied, modified, or distributed except according to
|
||||||
|
# those terms.
|
||||||
|
|
||||||
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
else:
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
|
import
|
||||||
|
std/[sequtils, tables],
|
||||||
|
chronos, chronicles,
|
||||||
|
multiaddress, multicodec
|
||||||
|
|
||||||
|
type
|
||||||
|
## Manages observed MultiAddresses by reomte peers. It keeps track of the most observed IP and IP/Port.
|
||||||
|
ObservedAddrManager* = ref object of RootObj
|
||||||
|
observedIPsAndPorts: seq[MultiAddress]
|
||||||
|
maxSize: int
|
||||||
|
minCount: int
|
||||||
|
|
||||||
|
proc addObservation*(self:ObservedAddrManager, observedAddr: MultiAddress): bool =
|
||||||
|
## Adds a new observed MultiAddress. If the number of observations exceeds maxSize, the oldest one is removed.
|
||||||
|
if self.observedIPsAndPorts.len >= self.maxSize:
|
||||||
|
self.observedIPsAndPorts.del(0)
|
||||||
|
self.observedIPsAndPorts.add(observedAddr)
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc getProtocol(self: ObservedAddrManager, observations: seq[MultiAddress], multiCodec: MultiCodec): Opt[MultiAddress] =
|
||||||
|
var countTable = toCountTable(observations)
|
||||||
|
countTable.sort()
|
||||||
|
var orderedPairs = toSeq(countTable.pairs)
|
||||||
|
for (ma, count) in orderedPairs:
|
||||||
|
let maFirst = ma[0].get()
|
||||||
|
if maFirst.protoCode.get() == multiCodec and count >= self.minCount:
|
||||||
|
return Opt.some(ma)
|
||||||
|
return Opt.none(MultiAddress)
|
||||||
|
|
||||||
|
proc getMostObservedProtocol(self: ObservedAddrManager, multiCodec: MultiCodec): Opt[MultiAddress] =
|
||||||
|
## Returns the most observed IP address or none if the number of observations are less than minCount.
|
||||||
|
let observedIPs = self.observedIPsAndPorts.mapIt(it[0].get())
|
||||||
|
return self.getProtocol(observedIPs, multiCodec)
|
||||||
|
|
||||||
|
proc getMostObservedProtoAndPort(self: ObservedAddrManager, multiCodec: MultiCodec): Opt[MultiAddress] =
|
||||||
|
## Returns the most observed IP/Port address or none if the number of observations are less than minCount.
|
||||||
|
return self.getProtocol(self.observedIPsAndPorts, multiCodec)
|
||||||
|
|
||||||
|
proc getMostObservedProtosAndPorts*(self: ObservedAddrManager): seq[MultiAddress] =
|
||||||
|
## Returns the most observed IP4/Port and IP6/Port address or an empty seq if the number of observations
|
||||||
|
## are less than minCount.
|
||||||
|
var res: seq[MultiAddress]
|
||||||
|
let ip4 = self.getMostObservedProtoAndPort(multiCodec("ip4"))
|
||||||
|
if ip4.isSome():
|
||||||
|
res.add(ip4.get())
|
||||||
|
let ip6 = self.getMostObservedProtoAndPort(multiCodec("ip6"))
|
||||||
|
if ip6.isSome():
|
||||||
|
res.add(ip6.get())
|
||||||
|
return res
|
||||||
|
|
||||||
|
proc guessDialableAddr*(
|
||||||
|
self: ObservedAddrManager,
|
||||||
|
ma: MultiAddress): MultiAddress =
|
||||||
|
## Replaces the first proto valeu of each listen address by the corresponding (matching the proto code) most observed value.
|
||||||
|
## If the most observed value is not available, the original MultiAddress is returned.
|
||||||
|
try:
|
||||||
|
let maFirst = ma[0]
|
||||||
|
let maRest = ma[1..^1]
|
||||||
|
if maRest.isErr():
|
||||||
|
return ma
|
||||||
|
|
||||||
|
let observedIP = self.getMostObservedProtocol(maFirst.get().protoCode().get())
|
||||||
|
return
|
||||||
|
if observedIP.isNone() or maFirst.get() == observedIP.get():
|
||||||
|
ma
|
||||||
|
else:
|
||||||
|
observedIP.get() & maRest.get()
|
||||||
|
except CatchableError as error:
|
||||||
|
debug "Error while handling manual port forwarding", msg = error.msg
|
||||||
|
return ma
|
||||||
|
|
||||||
|
proc `$`*(self: ObservedAddrManager): string =
|
||||||
|
## Returns a string representation of the ObservedAddrManager.
|
||||||
|
return "IPs and Ports: " & $self.observedIPsAndPorts
|
||||||
|
|
||||||
|
proc new*(
|
||||||
|
T: typedesc[ObservedAddrManager],
|
||||||
|
maxSize = 10,
|
||||||
|
minCount = 3): T =
|
||||||
|
## Creates a new ObservedAddrManager.
|
||||||
|
return T(
|
||||||
|
observedIPsAndPorts: newSeq[MultiAddress](),
|
||||||
|
maxSize: maxSize,
|
||||||
|
minCount: minCount)
|
|
@ -28,7 +28,7 @@ else:
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[tables, sets, options, macros],
|
std/[tables, sets, options, macros],
|
||||||
chronos,
|
chronos, chronicles,
|
||||||
./crypto/crypto,
|
./crypto/crypto,
|
||||||
./protocols/identify,
|
./protocols/identify,
|
||||||
./protocols/protocol,
|
./protocols/protocol,
|
||||||
|
@ -220,3 +220,6 @@ proc identify*(
|
||||||
peerStore.updatePeerInfo(info)
|
peerStore.updatePeerInfo(info)
|
||||||
finally:
|
finally:
|
||||||
await stream.closeWithEOF()
|
await stream.closeWithEOF()
|
||||||
|
|
||||||
|
proc guessDialableAddr*(self: PeerStore, ma: MultiAddress): MultiAddress =
|
||||||
|
return self.identify.observedAddrManager.guessDialableAddr(ma)
|
||||||
|
|
|
@ -15,6 +15,7 @@ else:
|
||||||
import std/[options, deques, sequtils]
|
import std/[options, deques, sequtils]
|
||||||
import chronos, metrics
|
import chronos, metrics
|
||||||
import ../../../switch
|
import ../../../switch
|
||||||
|
import ../../../wire
|
||||||
import client
|
import client
|
||||||
import ../../../utils/heartbeat
|
import ../../../utils/heartbeat
|
||||||
import ../../../crypto/crypto
|
import ../../../crypto/crypto
|
||||||
|
@ -27,6 +28,7 @@ declarePublicGauge(libp2p_autonat_reachability_confidence, "autonat reachability
|
||||||
type
|
type
|
||||||
AutonatService* = ref object of Service
|
AutonatService* = ref object of Service
|
||||||
newConnectedPeerHandler: PeerEventHandler
|
newConnectedPeerHandler: PeerEventHandler
|
||||||
|
addressMapper: AddressMapper
|
||||||
scheduleHandle: Future[void]
|
scheduleHandle: Future[void]
|
||||||
networkReachability: NetworkReachability
|
networkReachability: NetworkReachability
|
||||||
confidence: Option[float]
|
confidence: Option[float]
|
||||||
|
@ -40,6 +42,7 @@ type
|
||||||
maxQueueSize: int
|
maxQueueSize: int
|
||||||
minConfidence: float
|
minConfidence: float
|
||||||
dialTimeout: Duration
|
dialTimeout: Duration
|
||||||
|
enableAddressMapper: bool
|
||||||
|
|
||||||
NetworkReachability* {.pure.} = enum
|
NetworkReachability* {.pure.} = enum
|
||||||
NotReachable, Reachable, Unknown
|
NotReachable, Reachable, Unknown
|
||||||
|
@ -55,7 +58,8 @@ proc new*(
|
||||||
numPeersToAsk: int = 5,
|
numPeersToAsk: int = 5,
|
||||||
maxQueueSize: int = 10,
|
maxQueueSize: int = 10,
|
||||||
minConfidence: float = 0.3,
|
minConfidence: float = 0.3,
|
||||||
dialTimeout = 30.seconds): T =
|
dialTimeout = 30.seconds,
|
||||||
|
enableAddressMapper = true): T =
|
||||||
return T(
|
return T(
|
||||||
scheduleInterval: scheduleInterval,
|
scheduleInterval: scheduleInterval,
|
||||||
networkReachability: Unknown,
|
networkReachability: Unknown,
|
||||||
|
@ -67,7 +71,8 @@ proc new*(
|
||||||
numPeersToAsk: numPeersToAsk,
|
numPeersToAsk: numPeersToAsk,
|
||||||
maxQueueSize: maxQueueSize,
|
maxQueueSize: maxQueueSize,
|
||||||
minConfidence: minConfidence,
|
minConfidence: minConfidence,
|
||||||
dialTimeout: dialTimeout)
|
dialTimeout: dialTimeout,
|
||||||
|
enableAddressMapper: enableAddressMapper)
|
||||||
|
|
||||||
proc networkReachability*(self: AutonatService): NetworkReachability {.inline.} =
|
proc networkReachability*(self: AutonatService): NetworkReachability {.inline.} =
|
||||||
return self.networkReachability
|
return self.networkReachability
|
||||||
|
@ -133,6 +138,7 @@ proc askPeer(self: AutonatService, switch: Switch, peerId: PeerId): Future[Netwo
|
||||||
await self.handleAnswer(ans)
|
await self.handleAnswer(ans)
|
||||||
if not isNil(self.statusAndConfidenceHandler):
|
if not isNil(self.statusAndConfidenceHandler):
|
||||||
await self.statusAndConfidenceHandler(self.networkReachability, self.confidence)
|
await self.statusAndConfidenceHandler(self.networkReachability, self.confidence)
|
||||||
|
await switch.peerInfo.update()
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
proc askConnectedPeers(self: AutonatService, switch: Switch) {.async.} =
|
proc askConnectedPeers(self: AutonatService, switch: Switch) {.async.} =
|
||||||
|
@ -153,7 +159,30 @@ proc schedule(service: AutonatService, switch: Switch, interval: Duration) {.asy
|
||||||
heartbeat "Scheduling AutonatService run", interval:
|
heartbeat "Scheduling AutonatService run", interval:
|
||||||
await service.run(switch)
|
await service.run(switch)
|
||||||
|
|
||||||
|
proc addressMapper(
|
||||||
|
self: AutonatService,
|
||||||
|
peerStore: PeerStore,
|
||||||
|
listenAddrs: seq[MultiAddress]): Future[seq[MultiAddress]] {.gcsafe, async.} =
|
||||||
|
|
||||||
|
if self.networkReachability != NetworkReachability.Reachable:
|
||||||
|
return listenAddrs
|
||||||
|
|
||||||
|
var addrs = newSeq[MultiAddress]()
|
||||||
|
for listenAddr in listenAddrs:
|
||||||
|
var processedMA = listenAddr
|
||||||
|
try:
|
||||||
|
let hostIP = initTAddress(listenAddr).get()
|
||||||
|
if not hostIP.isGlobal() and self.networkReachability == NetworkReachability.Reachable:
|
||||||
|
processedMA = peerStore.guessDialableAddr(listenAddr) # handle manual port forwarding
|
||||||
|
except CatchableError as exc:
|
||||||
|
debug "Error while handling address mapper", msg = exc.msg
|
||||||
|
addrs.add(processedMA)
|
||||||
|
return addrs
|
||||||
|
|
||||||
method setup*(self: AutonatService, switch: Switch): Future[bool] {.async.} =
|
method setup*(self: AutonatService, switch: Switch): Future[bool] {.async.} =
|
||||||
|
self.addressMapper = proc (listenAddrs: seq[MultiAddress]): Future[seq[MultiAddress]] {.gcsafe, async.} =
|
||||||
|
return await addressMapper(self, switch.peerStore, listenAddrs)
|
||||||
|
|
||||||
info "Setting up AutonatService"
|
info "Setting up AutonatService"
|
||||||
let hasBeenSetup = await procCall Service(self).setup(switch)
|
let hasBeenSetup = await procCall Service(self).setup(switch)
|
||||||
if hasBeenSetup:
|
if hasBeenSetup:
|
||||||
|
@ -163,6 +192,8 @@ method setup*(self: AutonatService, switch: Switch): Future[bool] {.async.} =
|
||||||
switch.connManager.addPeerEventHandler(self.newConnectedPeerHandler, PeerEventKind.Joined)
|
switch.connManager.addPeerEventHandler(self.newConnectedPeerHandler, PeerEventKind.Joined)
|
||||||
if self.scheduleInterval.isSome():
|
if self.scheduleInterval.isSome():
|
||||||
self.scheduleHandle = schedule(self, switch, self.scheduleInterval.get())
|
self.scheduleHandle = schedule(self, switch, self.scheduleInterval.get())
|
||||||
|
if self.enableAddressMapper:
|
||||||
|
switch.peerInfo.addressMappers.add(self.addressMapper)
|
||||||
return hasBeenSetup
|
return hasBeenSetup
|
||||||
|
|
||||||
method run*(self: AutonatService, switch: Switch) {.async, public.} =
|
method run*(self: AutonatService, switch: Switch) {.async, public.} =
|
||||||
|
@ -170,7 +201,6 @@ method run*(self: AutonatService, switch: Switch) {.async, public.} =
|
||||||
await askConnectedPeers(self, switch)
|
await askConnectedPeers(self, switch)
|
||||||
await self.callHandler()
|
await self.callHandler()
|
||||||
|
|
||||||
|
|
||||||
method stop*(self: AutonatService, switch: Switch): Future[bool] {.async, public.} =
|
method stop*(self: AutonatService, switch: Switch): Future[bool] {.async, public.} =
|
||||||
info "Stopping AutonatService"
|
info "Stopping AutonatService"
|
||||||
let hasBeenStopped = await procCall Service(self).stop(switch)
|
let hasBeenStopped = await procCall Service(self).stop(switch)
|
||||||
|
@ -180,6 +210,9 @@ method stop*(self: AutonatService, switch: Switch): Future[bool] {.async, public
|
||||||
self.scheduleHandle = nil
|
self.scheduleHandle = nil
|
||||||
if not isNil(self.newConnectedPeerHandler):
|
if not isNil(self.newConnectedPeerHandler):
|
||||||
switch.connManager.removePeerEventHandler(self.newConnectedPeerHandler, PeerEventKind.Joined)
|
switch.connManager.removePeerEventHandler(self.newConnectedPeerHandler, PeerEventKind.Joined)
|
||||||
|
if self.enableAddressMapper:
|
||||||
|
switch.peerInfo.addressMappers.keepItIf(it != self.addressMapper)
|
||||||
|
await switch.peerInfo.update()
|
||||||
return hasBeenStopped
|
return hasBeenStopped
|
||||||
|
|
||||||
proc statusAndConfidenceHandler*(self: AutonatService, statusAndConfidenceHandler: StatusAndConfidenceHandler) =
|
proc statusAndConfidenceHandler*(self: AutonatService, statusAndConfidenceHandler: StatusAndConfidenceHandler) =
|
||||||
|
|
|
@ -26,7 +26,10 @@ import ../protobuf/minprotobuf,
|
||||||
../multiaddress,
|
../multiaddress,
|
||||||
../protocols/protocol,
|
../protocols/protocol,
|
||||||
../utility,
|
../utility,
|
||||||
../errors
|
../errors,
|
||||||
|
../observedaddrmanager
|
||||||
|
|
||||||
|
export observedaddrmanager
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "libp2p identify"
|
topics = "libp2p identify"
|
||||||
|
@ -56,6 +59,7 @@ type
|
||||||
Identify* = ref object of LPProtocol
|
Identify* = ref object of LPProtocol
|
||||||
peerInfo*: PeerInfo
|
peerInfo*: PeerInfo
|
||||||
sendSignedPeerRecord*: bool
|
sendSignedPeerRecord*: bool
|
||||||
|
observedAddrManager*: ObservedAddrManager
|
||||||
|
|
||||||
IdentifyPushHandler* = proc (
|
IdentifyPushHandler* = proc (
|
||||||
peer: PeerId,
|
peer: PeerId,
|
||||||
|
@ -160,7 +164,8 @@ proc new*(
|
||||||
): T =
|
): T =
|
||||||
let identify = T(
|
let identify = T(
|
||||||
peerInfo: peerInfo,
|
peerInfo: peerInfo,
|
||||||
sendSignedPeerRecord: sendSignedPeerRecord
|
sendSignedPeerRecord: sendSignedPeerRecord,
|
||||||
|
observedAddrManager: ObservedAddrManager.new(),
|
||||||
)
|
)
|
||||||
identify.init()
|
identify.init()
|
||||||
identify
|
identify
|
||||||
|
@ -182,7 +187,7 @@ method init*(p: Identify) =
|
||||||
p.handler = handle
|
p.handler = handle
|
||||||
p.codec = IdentifyCodec
|
p.codec = IdentifyCodec
|
||||||
|
|
||||||
proc identify*(p: Identify,
|
proc identify*(self: Identify,
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
remotePeerId: PeerId): Future[IdentifyInfo] {.async, gcsafe.} =
|
remotePeerId: PeerId): Future[IdentifyInfo] {.async, gcsafe.} =
|
||||||
trace "initiating identify", conn
|
trace "initiating identify", conn
|
||||||
|
@ -194,23 +199,25 @@ proc identify*(p: Identify,
|
||||||
let infoOpt = decodeMsg(message)
|
let infoOpt = decodeMsg(message)
|
||||||
if infoOpt.isNone():
|
if infoOpt.isNone():
|
||||||
raise newException(IdentityInvalidMsgError, "Incorrect message received!")
|
raise newException(IdentityInvalidMsgError, "Incorrect message received!")
|
||||||
result = infoOpt.get()
|
|
||||||
|
|
||||||
if result.pubkey.isSome:
|
var info = infoOpt.get()
|
||||||
let peer = PeerId.init(result.pubkey.get())
|
if info.pubkey.isNone():
|
||||||
if peer.isErr:
|
|
||||||
raise newException(IdentityInvalidMsgError, $peer.error)
|
|
||||||
else:
|
|
||||||
result.peerId = peer.get()
|
|
||||||
if peer.get() != remotePeerId:
|
|
||||||
trace "Peer ids don't match",
|
|
||||||
remote = peer,
|
|
||||||
local = remotePeerId
|
|
||||||
|
|
||||||
raise newException(IdentityNoMatchError, "Peer ids don't match")
|
|
||||||
else:
|
|
||||||
raise newException(IdentityInvalidMsgError, "No pubkey in identify")
|
raise newException(IdentityInvalidMsgError, "No pubkey in identify")
|
||||||
|
|
||||||
|
let peer = PeerId.init(info.pubkey.get())
|
||||||
|
if peer.isErr:
|
||||||
|
raise newException(IdentityInvalidMsgError, $peer.error)
|
||||||
|
|
||||||
|
if peer.get() != remotePeerId:
|
||||||
|
trace "Peer ids don't match", remote = peer, local = remotePeerId
|
||||||
|
raise newException(IdentityNoMatchError, "Peer ids don't match")
|
||||||
|
info.peerId = peer.get()
|
||||||
|
|
||||||
|
if info.observedAddr.isSome:
|
||||||
|
if not self.observedAddrManager.addObservation(info.observedAddr.get()):
|
||||||
|
debug "Observed address is not valid", observedAddr = info.observedAddr.get()
|
||||||
|
return info
|
||||||
|
|
||||||
proc new*(T: typedesc[IdentifyPush], handler: IdentifyPushHandler = nil): T {.public.} =
|
proc new*(T: typedesc[IdentifyPush], handler: IdentifyPushHandler = nil): T {.public.} =
|
||||||
## Create a IdentifyPush protocol. `handler` will be called every time
|
## Create a IdentifyPush protocol. `handler` will be called every time
|
||||||
## a peer sends us new `PeerInfo`
|
## a peer sends us new `PeerInfo`
|
||||||
|
|
|
@ -32,9 +32,15 @@ type
|
||||||
backingOff: seq[PeerId]
|
backingOff: seq[PeerId]
|
||||||
peerAvailable: AsyncEvent
|
peerAvailable: AsyncEvent
|
||||||
onReservation: OnReservationHandler
|
onReservation: OnReservationHandler
|
||||||
|
addressMapper: AddressMapper
|
||||||
rng: ref HmacDrbgContext
|
rng: ref HmacDrbgContext
|
||||||
|
|
||||||
proc reserveAndUpdate(self: AutoRelayService, relayPid: PeerId, selfPid: PeerId) {.async.} =
|
proc addressMapper(
|
||||||
|
self: AutoRelayService,
|
||||||
|
listenAddrs: seq[MultiAddress]): Future[seq[MultiAddress]] {.gcsafe, async.} =
|
||||||
|
return concat(toSeq(self.relayAddresses.values))
|
||||||
|
|
||||||
|
proc reserveAndUpdate(self: AutoRelayService, relayPid: PeerId, switch: Switch) {.async.} =
|
||||||
while self.running:
|
while self.running:
|
||||||
let
|
let
|
||||||
rsvp = await self.client.reserve(relayPid).wait(chronos.seconds(5))
|
rsvp = await self.client.reserve(relayPid).wait(chronos.seconds(5))
|
||||||
|
@ -46,11 +52,15 @@ proc reserveAndUpdate(self: AutoRelayService, relayPid: PeerId, selfPid: PeerId)
|
||||||
break
|
break
|
||||||
if relayPid notin self.relayAddresses or self.relayAddresses[relayPid] != relayedAddr:
|
if relayPid notin self.relayAddresses or self.relayAddresses[relayPid] != relayedAddr:
|
||||||
self.relayAddresses[relayPid] = relayedAddr
|
self.relayAddresses[relayPid] = relayedAddr
|
||||||
|
await switch.peerInfo.update()
|
||||||
if not self.onReservation.isNil():
|
if not self.onReservation.isNil():
|
||||||
self.onReservation(concat(toSeq(self.relayAddresses.values)))
|
self.onReservation(concat(toSeq(self.relayAddresses.values)))
|
||||||
await sleepAsync chronos.seconds(ttl - 30)
|
await sleepAsync chronos.seconds(ttl - 30)
|
||||||
|
|
||||||
method setup*(self: AutoRelayService, switch: Switch): Future[bool] {.async, gcsafe.} =
|
method setup*(self: AutoRelayService, switch: Switch): Future[bool] {.async, gcsafe.} =
|
||||||
|
self.addressMapper = proc (listenAddrs: seq[MultiAddress]): Future[seq[MultiAddress]] {.gcsafe, async.} =
|
||||||
|
return await addressMapper(self, listenAddrs)
|
||||||
|
|
||||||
let hasBeenSetUp = await procCall Service(self).setup(switch)
|
let hasBeenSetUp = await procCall Service(self).setup(switch)
|
||||||
if hasBeenSetUp:
|
if hasBeenSetUp:
|
||||||
proc handlePeerJoined(peerId: PeerId, event: PeerEvent) {.async.} =
|
proc handlePeerJoined(peerId: PeerId, event: PeerEvent) {.async.} =
|
||||||
|
@ -63,6 +73,7 @@ method setup*(self: AutoRelayService, switch: Switch): Future[bool] {.async, gcs
|
||||||
future[].cancel()
|
future[].cancel()
|
||||||
switch.addPeerEventHandler(handlePeerJoined, Joined)
|
switch.addPeerEventHandler(handlePeerJoined, Joined)
|
||||||
switch.addPeerEventHandler(handlePeerLeft, Left)
|
switch.addPeerEventHandler(handlePeerLeft, Left)
|
||||||
|
switch.peerInfo.addressMappers.add(self.addressMapper)
|
||||||
await self.run(switch)
|
await self.run(switch)
|
||||||
return hasBeenSetUp
|
return hasBeenSetUp
|
||||||
|
|
||||||
|
@ -96,7 +107,7 @@ proc innerRun(self: AutoRelayService, switch: Switch) {.async, gcsafe.} =
|
||||||
for relayPid in connectedPeers:
|
for relayPid in connectedPeers:
|
||||||
if self.relayPeers.len() >= self.numRelays:
|
if self.relayPeers.len() >= self.numRelays:
|
||||||
break
|
break
|
||||||
self.relayPeers[relayPid] = self.reserveAndUpdate(relayPid, switch.peerInfo.peerId)
|
self.relayPeers[relayPid] = self.reserveAndUpdate(relayPid, switch)
|
||||||
|
|
||||||
if self.relayPeers.len() > 0:
|
if self.relayPeers.len() > 0:
|
||||||
await one(toSeq(self.relayPeers.values())) or self.peerAvailable.wait()
|
await one(toSeq(self.relayPeers.values())) or self.peerAvailable.wait()
|
||||||
|
@ -116,6 +127,8 @@ method stop*(self: AutoRelayService, switch: Switch): Future[bool] {.async, gcsa
|
||||||
if hasBeenStopped:
|
if hasBeenStopped:
|
||||||
self.running = false
|
self.running = false
|
||||||
self.runner.cancel()
|
self.runner.cancel()
|
||||||
|
switch.peerInfo.addressMappers.keepItIf(it != self.addressMapper)
|
||||||
|
await switch.peerInfo.update()
|
||||||
return hasBeenStopped
|
return hasBeenStopped
|
||||||
|
|
||||||
proc getAddresses*(self: AutoRelayService): seq[MultiAddress] =
|
proc getAddresses*(self: AutoRelayService): seq[MultiAddress] =
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import unittest2
|
import unittest2, chronos
|
||||||
|
|
||||||
export unittest2
|
export unittest2, chronos
|
||||||
|
|
||||||
template asyncTeardown*(body: untyped): untyped =
|
template asyncTeardown*(body: untyped): untyped =
|
||||||
teardown:
|
teardown:
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
# This file may not be copied, modified, or distributed except according to
|
# This file may not be copied, modified, or distributed except according to
|
||||||
# those terms.
|
# those terms.
|
||||||
|
|
||||||
import std/options
|
import std/[options, sequtils]
|
||||||
import chronos, metrics
|
import chronos, metrics
|
||||||
import unittest2
|
import unittest2
|
||||||
import ../libp2p/[builders,
|
import ../libp2p/[builders,
|
||||||
|
@ -104,9 +104,13 @@ suite "Autonat Service":
|
||||||
check autonatService.networkReachability() == NetworkReachability.Reachable
|
check autonatService.networkReachability() == NetworkReachability.Reachable
|
||||||
check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 0.3
|
check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 0.3
|
||||||
|
|
||||||
|
check switch1.peerInfo.addrs == switch1.peerInfo.listenAddrs.mapIt(switch1.peerStore.guessDialableAddr(it))
|
||||||
|
|
||||||
await allFuturesThrowing(
|
await allFuturesThrowing(
|
||||||
switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop())
|
switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop())
|
||||||
|
|
||||||
|
check switch1.peerInfo.addrs == switch1.peerInfo.listenAddrs
|
||||||
|
|
||||||
asyncTest "Peer must be not reachable and then reachable":
|
asyncTest "Peer must be not reachable and then reachable":
|
||||||
|
|
||||||
let autonatClientStub = AutonatClientStub.new(expectedDials = 6)
|
let autonatClientStub = AutonatClientStub.new(expectedDials = 6)
|
||||||
|
|
|
@ -72,10 +72,13 @@ suite "Autorelay":
|
||||||
await fut.wait(1.seconds)
|
await fut.wait(1.seconds)
|
||||||
let addresses = autorelay.getAddresses()
|
let addresses = autorelay.getAddresses()
|
||||||
check:
|
check:
|
||||||
addresses[0] == buildRelayMA(switchRelay, switchClient)
|
addresses == @[buildRelayMA(switchRelay, switchClient)]
|
||||||
addresses.len() == 1
|
addresses.len() == 1
|
||||||
|
addresses == switchClient.peerInfo.addrs
|
||||||
await allFutures(switchClient.stop(), switchRelay.stop())
|
await allFutures(switchClient.stop(), switchRelay.stop())
|
||||||
|
|
||||||
|
check addresses != switchClient.peerInfo.addrs
|
||||||
|
|
||||||
asyncTest "Three relays connections":
|
asyncTest "Three relays connections":
|
||||||
var state = 0
|
var state = 0
|
||||||
let
|
let
|
||||||
|
|
|
@ -405,5 +405,12 @@ suite "MultiAddress test suite":
|
||||||
let maWithTcp = MultiAddress.init(onionMAWithTcpStr).get()
|
let maWithTcp = MultiAddress.init(onionMAWithTcpStr).get()
|
||||||
check $(maWithTcp[multiCodec("onion3")].tryGet()) == onionMAStr
|
check $(maWithTcp[multiCodec("onion3")].tryGet()) == onionMAStr
|
||||||
|
|
||||||
|
test "matchPartial":
|
||||||
|
const
|
||||||
|
tcp = mapEq("tcp")
|
||||||
|
let ma = MultiAddress.init("/ip4/0.0.0.0/tcp/0").get()
|
||||||
|
|
||||||
|
check not tcp.matchPartial(ma)
|
||||||
|
check IP4.matchPartial(ma)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import testtcptransport,
|
||||||
testmultistream,
|
testmultistream,
|
||||||
testbufferstream,
|
testbufferstream,
|
||||||
testidentify,
|
testidentify,
|
||||||
|
testobservedaddrmanager,
|
||||||
testconnmngr,
|
testconnmngr,
|
||||||
testswitch,
|
testswitch,
|
||||||
testnoise,
|
testnoise,
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import unittest2,
|
||||||
|
../libp2p/multiaddress,
|
||||||
|
../libp2p/observedaddrmanager,
|
||||||
|
./helpers
|
||||||
|
|
||||||
|
suite "ObservedAddrManager":
|
||||||
|
teardown:
|
||||||
|
checkTrackers()
|
||||||
|
|
||||||
|
asyncTest "Calculate the most oberserved IP correctly":
|
||||||
|
|
||||||
|
let observedAddrManager = ObservedAddrManager.new(minCount = 3)
|
||||||
|
|
||||||
|
# Calculate the most oberserved IP4 correctly
|
||||||
|
let mostObservedIP4AndPort = MultiAddress.init("/ip4/1.2.3.0/tcp/1").get()
|
||||||
|
let maIP4 = MultiAddress.init("/ip4/0.0.0.0/tcp/80").get()
|
||||||
|
|
||||||
|
check:
|
||||||
|
observedAddrManager.addObservation(mostObservedIP4AndPort)
|
||||||
|
observedAddrManager.addObservation(mostObservedIP4AndPort)
|
||||||
|
|
||||||
|
observedAddrManager.guessDialableAddr(maIP4) == maIP4
|
||||||
|
|
||||||
|
observedAddrManager.addObservation(MultiAddress.init("/ip4/1.2.3.0/tcp/2").get())
|
||||||
|
observedAddrManager.addObservation(MultiAddress.init("/ip4/1.2.3.1/tcp/1").get())
|
||||||
|
|
||||||
|
observedAddrManager.guessDialableAddr(maIP4) == MultiAddress.init("/ip4/1.2.3.0/tcp/80").get()
|
||||||
|
observedAddrManager.getMostObservedProtosAndPorts().len == 0
|
||||||
|
|
||||||
|
observedAddrManager.addObservation(mostObservedIP4AndPort)
|
||||||
|
|
||||||
|
observedAddrManager.getMostObservedProtosAndPorts() == @[mostObservedIP4AndPort]
|
||||||
|
|
||||||
|
# Calculate the most oberserved IP6 correctly
|
||||||
|
let mostObservedIP6AndPort = MultiAddress.init("/ip6/::2/tcp/1").get()
|
||||||
|
let maIP6 = MultiAddress.init("/ip6/::1/tcp/80").get()
|
||||||
|
|
||||||
|
check:
|
||||||
|
observedAddrManager.addObservation(mostObservedIP6AndPort)
|
||||||
|
observedAddrManager.addObservation(mostObservedIP6AndPort)
|
||||||
|
|
||||||
|
observedAddrManager.guessDialableAddr(maIP6) == maIP6
|
||||||
|
|
||||||
|
observedAddrManager.addObservation(MultiAddress.init("/ip6/::2/tcp/2").get())
|
||||||
|
observedAddrManager.addObservation(MultiAddress.init("/ip6/::3/tcp/1").get())
|
||||||
|
|
||||||
|
observedAddrManager.guessDialableAddr(maIP6) == MultiAddress.init("/ip6/::2/tcp/80").get()
|
||||||
|
observedAddrManager.getMostObservedProtosAndPorts().len == 1
|
||||||
|
|
||||||
|
observedAddrManager.addObservation(mostObservedIP6AndPort)
|
||||||
|
|
||||||
|
observedAddrManager.getMostObservedProtosAndPorts() == @[mostObservedIP4AndPort, mostObservedIP6AndPort]
|
||||||
|
|
||||||
|
asyncTest "replace first proto value by most observed when there is only one protocol":
|
||||||
|
let observedAddrManager = ObservedAddrManager.new(minCount = 3)
|
||||||
|
let mostObservedIP4AndPort = MultiAddress.init("/ip4/1.2.3.4/tcp/1").get()
|
||||||
|
|
||||||
|
check:
|
||||||
|
observedAddrManager.addObservation(mostObservedIP4AndPort)
|
||||||
|
observedAddrManager.addObservation(mostObservedIP4AndPort)
|
||||||
|
observedAddrManager.addObservation(mostObservedIP4AndPort)
|
||||||
|
|
||||||
|
observedAddrManager.guessDialableAddr(
|
||||||
|
MultiAddress.init("/ip4/0.0.0.0").get()) == MultiAddress.init("/ip4/1.2.3.4").get()
|
Loading…
Reference in New Issue