diff --git a/libp2p/protocols/connectivity/autonat/client.nim b/libp2p/protocols/connectivity/autonat/client.nim index 8a74ef009..0322a1edf 100644 --- a/libp2p/protocols/connectivity/autonat/client.nim +++ b/libp2p/protocols/connectivity/autonat/client.nim @@ -20,8 +20,6 @@ import ../../../switch, ../../../peerid import core -export core - logScope: topics = "libp2p autonat" diff --git a/libp2p/protocols/connectivity/autonat/service.nim b/libp2p/protocols/connectivity/autonat/service.nim index d10f96c26..50edae185 100644 --- a/libp2p/protocols/connectivity/autonat/service.nim +++ b/libp2p/protocols/connectivity/autonat/service.nim @@ -17,9 +17,12 @@ import chronos, metrics import ../../../switch import ../../../wire import client +from core import NetworkReachability, AutonatUnreachableError import ../../../utils/heartbeat import ../../../crypto/crypto +export options, core.NetworkReachability + logScope: topics = "libp2p autonatservice" @@ -30,7 +33,7 @@ type newConnectedPeerHandler: PeerEventHandler addressMapper: AddressMapper scheduleHandle: Future[void] - networkReachability: NetworkReachability + networkReachability*: NetworkReachability confidence: Option[float] answers: Deque[NetworkReachability] autonatClient: AutonatClient @@ -71,9 +74,6 @@ proc new*( dialTimeout: dialTimeout, enableAddressMapper: enableAddressMapper) -proc networkReachability*(self: AutonatService): NetworkReachability {.inline.} = - return self.networkReachability - proc callHandler(self: AutonatService) {.async.} = if not isNil(self.statusAndConfidenceHandler): await self.statusAndConfidenceHandler(self.networkReachability, self.confidence) diff --git a/libp2p/protocols/connectivity/dcutr/client.nim b/libp2p/protocols/connectivity/dcutr/client.nim index 560741e6c..220b6cdb5 100644 --- a/libp2p/protocols/connectivity/dcutr/client.nim +++ b/libp2p/protocols/connectivity/dcutr/client.nim @@ -23,6 +23,8 @@ import ../../protocol, ../../../switch, ../../../utils/future +export DcutrError + type DcutrClient* = ref object connectTimeout: Duration diff --git a/libp2p/protocols/connectivity/dcutr/server.nim b/libp2p/protocols/connectivity/dcutr/server.nim index 0dada156f..b0f14d3b0 100644 --- a/libp2p/protocols/connectivity/dcutr/server.nim +++ b/libp2p/protocols/connectivity/dcutr/server.nim @@ -23,6 +23,7 @@ import ../../protocol, ../../../switch, ../../../utils/future +export DcutrError export chronicles type Dcutr* = ref object of LPProtocol diff --git a/libp2p/protocols/connectivity/relay/relay.nim b/libp2p/protocols/connectivity/relay/relay.nim index 5e749702b..d099ff87b 100644 --- a/libp2p/protocols/connectivity/relay/relay.nim +++ b/libp2p/protocols/connectivity/relay/relay.nim @@ -101,7 +101,7 @@ proc createReserveResponse( status: some(Ok)) return ok(msg) -proc isRelayed(conn: Connection): bool = +proc isRelayed*(conn: Connection): bool = var wrappedConn = conn while not isNil(wrappedConn): if wrappedConn of RelayConnection: diff --git a/libp2p/services/hpservice.nim b/libp2p/services/hpservice.nim new file mode 100644 index 000000000..a2ee4974a --- /dev/null +++ b/libp2p/services/hpservice.nim @@ -0,0 +1,105 @@ +# Nim-LibP2P +# Copyright (c) 2022 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/[tables, sequtils] + +import chronos, chronicles + +import ../switch, ../wire +import ../protocols/rendezvous +import ../services/autorelayservice +import ../discovery/[rendezvousinterface, discoverymngr] +import ../protocols/connectivity/relay/relay +import ../protocols/connectivity/autonat/service +import ../protocols/connectivity/dcutr/[client, server] + + +logScope: + topics = "libp2p hpservice" + +type + HPService* = ref object of Service + newConnectedPeerHandler: PeerEventHandler + onNewStatusHandler: StatusAndConfidenceHandler + autoRelayService: AutoRelayService + autonatService: AutonatService + isPublicIPAddrProc: IsPublicIPAddrProc + + IsPublicIPAddrProc* = proc(ta: TransportAddress): bool {.gcsafe, raises: [Defect].} + +proc new*(T: typedesc[HPService], autonatService: AutonatService, autoRelayService: AutoRelayService, + isPublicIPAddrProc: IsPublicIPAddrProc = isGlobal): T = + return T(autonatService: autonatService, autoRelayService: autoRelayService, isPublicIPAddrProc: isPublicIPAddrProc) + +proc tryStartingDirectConn(self: HPService, switch: Switch, peerId: PeerId): Future[bool] {.async.} = + await sleepAsync(500.milliseconds) # wait for AddressBook to be populated + for address in switch.peerStore[AddressBook][peerId]: + try: + let ta = initTAddress(address) + if ta.isOk() and self.isPublicIPAddrProc(ta.get()): + await switch.connect(peerId, @[address], true, false) + debug "Direct connection created." + return true + except CatchableError as err: + debug "Failed to create direct connection.", err = err.msg + continue + return false + +method setup*(self: HPService, switch: Switch): Future[bool] {.async.} = + var hasBeenSetup = await procCall Service(self).setup(switch) + hasBeenSetup = hasBeenSetup and await self.autonatService.setup(switch) + + if hasBeenSetup: + let dcutrProto = Dcutr.new(switch) + switch.mount(dcutrProto) + + self.newConnectedPeerHandler = proc (peerId: PeerId, event: PeerEvent): Future[void] {.async.} = + try: + let conn = switch.connManager.selectMuxer(peerId).connection + if isRelayed(conn) and conn.transportDir == Direction.In: + if await self.tryStartingDirectConn(switch, peerId): + await conn.close() + return + let dcutrClient = DcutrClient.new() + var natAddrs = switch.peerStore.getMostObservedProtosAndPorts() + if natAddrs.len == 0: + natAddrs = switch.peerInfo.listenAddrs.mapIt(switch.peerStore.guessDialableAddr(it)) + await dcutrClient.startSync(switch, peerId, natAddrs) + await sleepAsync(2000.milliseconds) # grace period before closing relayed connection + await conn.close() + except CatchableError as err: + debug "Hole punching failed during dcutr", err = err.msg + + switch.connManager.addPeerEventHandler(self.newConnectedPeerHandler, PeerEventKind.Joined) + + self.onNewStatusHandler = proc (networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} = + if networkReachability == NetworkReachability.NotReachable: + discard await self.autoRelayService.setup(switch) + elif networkReachability == NetworkReachability.Reachable: + discard await self.autoRelayService.stop(switch) + + # We do it here instead of in the AutonatService because this is useful only when hole punching. + for t in switch.transports: + t.networkReachability = networkReachability + + self.autonatService.statusAndConfidenceHandler(self.onNewStatusHandler) + return hasBeenSetup + +method run*(self: HPService, switch: Switch) {.async, public.} = + await self.autonatService.run(switch) + +method stop*(self: HPService, switch: Switch): Future[bool] {.async, public.} = + discard await self.autonatService.stop(switch) + if not isNil(self.newConnectedPeerHandler): + switch.connManager.removePeerEventHandler(self.newConnectedPeerHandler, PeerEventKind.Joined) diff --git a/libp2p/wire.nim b/libp2p/wire.nim index 088814945..f375e7b77 100644 --- a/libp2p/wire.nim +++ b/libp2p/wire.nim @@ -71,7 +71,7 @@ proc initTAddress*(ma: MultiAddress): MaResult[TransportAddress] = res.port = Port(fromBytesBE(uint16, pbuf)) ok(res) else: - err("MultiAddress must be wire address (tcp, udp or unix)") + err("MultiAddress must be wire address (tcp, udp or unix): " & $ma) proc connect*( ma: MultiAddress, diff --git a/tests/stubs/autonatclientstub.nim b/tests/stubs/autonatclientstub.nim index f801660b7..935024425 100644 --- a/tests/stubs/autonatclientstub.nim +++ b/tests/stubs/autonatclientstub.nim @@ -19,6 +19,7 @@ import ../../libp2p/[protocols/connectivity/autonat/client, peerid, multiaddress, switch] +from ../../libp2p/protocols/connectivity/autonat/core import NetworkReachability, AutonatUnreachableError, AutonatError type AutonatClientStub* = ref object of AutonatClient diff --git a/tests/stubs/switchstub.nim b/tests/stubs/switchstub.nim new file mode 100644 index 000000000..38c99739f --- /dev/null +++ b/tests/stubs/switchstub.nim @@ -0,0 +1,48 @@ +# 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. + +{.used.} + +when (NimMajor, NimMinor) < (1, 4): + {.push raises: [Defect].} +else: + {.push raises: [].} + +import chronos +import ../../libp2p/[peerid, multiaddress, switch] + +type + SwitchStub* = ref object of Switch + switch*: Switch + connectStub*: proc(): Future[void] {.async.} + +method connect*( + self: SwitchStub, + peerId: PeerId, + addrs: seq[MultiAddress], + forceDial = false, + reuseConnection = true, + upgradeDir = Direction.Out) {.async.} = + if (self.connectStub != nil): + await self.connectStub() + else: + await self.switch.connect(peerId, addrs, forceDial, reuseConnection, upgradeDir) + +proc new*(T: typedesc[SwitchStub], switch: Switch, connectStub: proc (): Future[void] {.async.} = nil): T = + return SwitchStub( + switch: switch, + peerInfo: switch.peerInfo, + ms: switch.ms, + transports: switch.transports, + connManager: switch.connManager, + peerStore: switch.peerStore, + dialer: switch.dialer, + nameResolver: switch.nameResolver, + services: switch.services, + connectStub: connectStub) diff --git a/tests/stubs/torstub.nim b/tests/stubs/torstub.nim index ca5fe9776..2b96bbfb3 100644 --- a/tests/stubs/torstub.nim +++ b/tests/stubs/torstub.nim @@ -1,3 +1,12 @@ +# 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. + {.used.} when (NimMajor, NimMinor) < (1, 4): diff --git a/tests/testautonatservice.nim b/tests/testautonatservice.nim index 21636c74b..7479a1c13 100644 --- a/tests/testautonatservice.nim +++ b/tests/testautonatservice.nim @@ -51,7 +51,7 @@ suite "Autonat Service": let switch3 = createSwitch() let switch4 = createSwitch() - check autonatService.networkReachability() == NetworkReachability.Unknown + check autonatService.networkReachability == NetworkReachability.Unknown await switch1.start() await switch2.start() @@ -64,7 +64,7 @@ suite "Autonat Service": await autonatClientStub.finished - check autonatService.networkReachability() == NetworkReachability.NotReachable + check autonatService.networkReachability == NetworkReachability.NotReachable check libp2p_autonat_reachability_confidence.value(["NotReachable"]) == 0.3 await allFuturesThrowing( @@ -86,7 +86,7 @@ suite "Autonat Service": if not awaiter.finished: awaiter.complete() - check autonatService.networkReachability() == NetworkReachability.Unknown + check autonatService.networkReachability == NetworkReachability.Unknown autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler) @@ -101,7 +101,7 @@ suite "Autonat Service": await awaiter - check autonatService.networkReachability() == NetworkReachability.Reachable + check autonatService.networkReachability == NetworkReachability.Reachable check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 0.3 check switch1.peerInfo.addrs == switch1.peerInfo.listenAddrs.mapIt(switch1.peerStore.guessDialableAddr(it)) @@ -131,7 +131,7 @@ suite "Autonat Service": autonatClientStub.answer = Reachable awaiter.complete() - check autonatService.networkReachability() == NetworkReachability.Unknown + check autonatService.networkReachability == NetworkReachability.Unknown autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler) @@ -146,12 +146,12 @@ suite "Autonat Service": await awaiter - check autonatService.networkReachability() == NetworkReachability.NotReachable + check autonatService.networkReachability == NetworkReachability.NotReachable check libp2p_autonat_reachability_confidence.value(["NotReachable"]) == 0.3 await autonatClientStub.finished - check autonatService.networkReachability() == NetworkReachability.Reachable + check autonatService.networkReachability == NetworkReachability.Reachable check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 0.3 await allFuturesThrowing(switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop()) @@ -172,7 +172,7 @@ suite "Autonat Service": if not awaiter.finished: awaiter.complete() - check autonatService.networkReachability() == NetworkReachability.Unknown + check autonatService.networkReachability == NetworkReachability.Unknown autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler) @@ -187,7 +187,7 @@ suite "Autonat Service": await awaiter - check autonatService.networkReachability() == NetworkReachability.Reachable + check autonatService.networkReachability == NetworkReachability.Reachable check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 1 await allFuturesThrowing( @@ -213,7 +213,7 @@ suite "Autonat Service": autonatClientStub.answer = Unknown awaiter.complete() - check autonatService.networkReachability() == NetworkReachability.Unknown + check autonatService.networkReachability == NetworkReachability.Unknown autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler) @@ -228,12 +228,12 @@ suite "Autonat Service": await awaiter - check autonatService.networkReachability() == NetworkReachability.NotReachable + check autonatService.networkReachability == NetworkReachability.NotReachable check libp2p_autonat_reachability_confidence.value(["NotReachable"]) == 1/3 await autonatClientStub.finished - check autonatService.networkReachability() == NetworkReachability.NotReachable + check autonatService.networkReachability == NetworkReachability.NotReachable check libp2p_autonat_reachability_confidence.value(["NotReachable"]) == 1/3 await allFuturesThrowing(switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop()) @@ -264,7 +264,7 @@ suite "Autonat Service": if not awaiter.finished: awaiter.complete() - check autonatService.networkReachability() == NetworkReachability.Unknown + check autonatService.networkReachability == NetworkReachability.Unknown autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler) @@ -275,7 +275,7 @@ suite "Autonat Service": await awaiter - check autonatService.networkReachability() == NetworkReachability.Reachable + check autonatService.networkReachability == NetworkReachability.Reachable check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 1 await allFuturesThrowing( @@ -304,8 +304,8 @@ suite "Autonat Service": if not awaiter2.finished: awaiter2.complete() - check autonatService1.networkReachability() == NetworkReachability.Unknown - check autonatService2.networkReachability() == NetworkReachability.Unknown + check autonatService1.networkReachability == NetworkReachability.Unknown + check autonatService2.networkReachability == NetworkReachability.Unknown autonatService1.statusAndConfidenceHandler(statusAndConfidenceHandler1) autonatService2.statusAndConfidenceHandler(statusAndConfidenceHandler2) @@ -321,8 +321,8 @@ suite "Autonat Service": await awaiter1 await awaiter2 - check autonatService1.networkReachability() == NetworkReachability.Reachable - check autonatService2.networkReachability() == NetworkReachability.Reachable + check autonatService1.networkReachability == NetworkReachability.Reachable + check autonatService2.networkReachability == NetworkReachability.Reachable check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 1 await allFuturesThrowing( @@ -342,7 +342,7 @@ suite "Autonat Service": if not awaiter1.finished: awaiter1.complete() - check autonatService1.networkReachability() == NetworkReachability.Unknown + check autonatService1.networkReachability == NetworkReachability.Unknown autonatService1.statusAndConfidenceHandler(statusAndConfidenceHandler1) @@ -360,7 +360,7 @@ suite "Autonat Service": await awaiter1 - check autonatService1.networkReachability() == NetworkReachability.Reachable + check autonatService1.networkReachability == NetworkReachability.Reachable check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 1 # Make sure remote peer can't create a connection to us @@ -385,7 +385,7 @@ suite "Autonat Service": if not awaiter.finished: awaiter.complete() - check autonatService.networkReachability() == NetworkReachability.Unknown + check autonatService.networkReachability == NetworkReachability.Unknown autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler) @@ -407,7 +407,7 @@ suite "Autonat Service": await autonatService.run(switch1) await awaiter - check autonatService.networkReachability() == NetworkReachability.Reachable + check autonatService.networkReachability == NetworkReachability.Reachable check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 1 await allFuturesThrowing( @@ -422,7 +422,7 @@ suite "Autonat Service": proc statusAndConfidenceHandler(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} = fail() - check autonatService.networkReachability() == NetworkReachability.Unknown + check autonatService.networkReachability == NetworkReachability.Unknown autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler) diff --git a/tests/testdcutr.nim b/tests/testdcutr.nim index cc0bd2bf6..a5a196402 100644 --- a/tests/testdcutr.nim +++ b/tests/testdcutr.nim @@ -16,36 +16,7 @@ from ../libp2p/protocols/connectivity/autonat/core import NetworkReachability import ../libp2p/builders import ../libp2p/utils/future import ./helpers - -type - SwitchStub* = ref object of Switch - switch: Switch - connectStub*: proc(): Future[void] {.async.} - -proc new*(T: typedesc[SwitchStub], switch: Switch, connectStub: proc (): Future[void] {.async.} = nil): T = - return SwitchStub( - switch: switch, - peerInfo: switch.peerInfo, - ms: switch.ms, - transports: switch.transports, - connManager: switch.connManager, - peerStore: switch.peerStore, - dialer: switch.dialer, - nameResolver: switch.nameResolver, - services: switch.services, - connectStub: connectStub) - -method connect*( - self: SwitchStub, - peerId: PeerId, - addrs: seq[MultiAddress], - forceDial = false, - reuseConnection = true, - upgradeDir = Direction.Out) {.async.} = - if (self.connectStub != nil): - await self.connectStub() - else: - await self.switch.connect(peerId, addrs, forceDial, reuseConnection, upgradeDir) +import ./stubs/switchstub suite "Dcutr": teardown: diff --git a/tests/testhpservice.nim b/tests/testhpservice.nim new file mode 100644 index 000000000..572727899 --- /dev/null +++ b/tests/testhpservice.nim @@ -0,0 +1,167 @@ +# Nim-LibP2P +# Copyright (c) 2022 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. + +{.used.} + +when (NimMajor, NimMinor) < (1, 4): + {.push raises: [Defect].} +else: + {.push raises: [].} + +import chronos + +import unittest2 +import ./helpers +import ./stubs/switchstub +import ../libp2p/[builders, + switch, + services/hpservice, + services/autorelayservice] +import ../libp2p/protocols/connectivity/relay/[relay, client] +import ../libp2p/protocols/connectivity/autonat/[service] +import ../libp2p/wire +import stubs/autonatclientstub + +proc isPublicAddrIPAddrMock(ta: TransportAddress): bool = + return true + +proc createSwitch(r: Relay = nil, hpService: Service = nil): Switch {.raises: [LPError, Defect].} = + var builder = SwitchBuilder.new() + .withRng(newRng()) + .withAddresses(@[ MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet() ]) + .withTcpTransport() + .withMplex() + .withAutonat() + .withNoise() + + if hpService != nil: + builder = builder.withServices(@[hpService]) + + if r != nil: + builder = builder.withCircuitRelay(r) + + return builder.build() + +proc buildRelayMA(switchRelay: Switch, switchClient: Switch): MultiAddress = + MultiAddress.init($switchRelay.peerInfo.addrs[0] & "/p2p/" & + $switchRelay.peerInfo.peerId & "/p2p-circuit/p2p/" & + $switchClient.peerInfo.peerId).get() + +suite "Hole Punching": + teardown: + checkTrackers() + + asyncTest "Direct connection must work when peer address is public": + let autonatClientStub = AutonatClientStub.new(expectedDials = 1) + autonatClientStub.answer = NotReachable + let autonatService = AutonatService.new(autonatClientStub, newRng(), maxQueueSize = 1) + + let relayClient = RelayClient.new() + let privatePeerRelayAddr = newFuture[seq[MultiAddress]]() + + let publicPeerSwitch = createSwitch(RelayClient.new()) + proc checkMA(address: seq[MultiAddress]) = + if not privatePeerRelayAddr.completed(): + privatePeerRelayAddr.complete(address) + + let autoRelayService = AutoRelayService.new(1, relayClient, checkMA, newRng()) + + let hpservice = HPService.new(autonatService, autoRelayService, isPublicAddrIPAddrMock) + + let privatePeerSwitch = createSwitch(relayClient, hpservice) + let switchRelay = createSwitch(Relay.new()) + + await allFutures(switchRelay.start(), privatePeerSwitch.start(), publicPeerSwitch.start()) + + await privatePeerSwitch.connect(switchRelay.peerInfo.peerId, switchRelay.peerInfo.addrs) + + await publicPeerSwitch.connect(privatePeerSwitch.peerInfo.peerId, (await privatePeerRelayAddr)) + + checkExpiring: + privatePeerSwitch.connManager.connCount(publicPeerSwitch.peerInfo.peerId) == 1 and + not isRelayed(privatePeerSwitch.connManager.selectMuxer(publicPeerSwitch.peerInfo.peerId).connection) + + await allFuturesThrowing( + privatePeerSwitch.stop(), publicPeerSwitch.stop(), switchRelay.stop()) + + proc holePunchingTest(connectStub: proc (): Future[void] {.async.}, + isPublicIPAddrProc: IsPublicIPAddrProc, + answer: Answer) {.async.} = + # There's no check in this test cause it can't test hole punching locally. It exists just to be sure the rest of + # the code works properly. + + let autonatClientStub1 = AutonatClientStub.new(expectedDials = 1) + autonatClientStub1.answer = NotReachable + let autonatService1 = AutonatService.new(autonatClientStub1, newRng(), maxQueueSize = 1) + + let autonatClientStub2 = AutonatClientStub.new(expectedDials = 1) + autonatClientStub2.answer = answer + let autonatService2 = AutonatService.new(autonatClientStub2, newRng(), maxQueueSize = 1) + + let relayClient1 = RelayClient.new() + let relayClient2 = RelayClient.new() + let privatePeerRelayAddr1 = newFuture[seq[MultiAddress]]() + + proc checkMA(address: seq[MultiAddress]) = + if not privatePeerRelayAddr1.completed(): + privatePeerRelayAddr1.complete(address) + + let autoRelayService1 = AutoRelayService.new(1, relayClient1, checkMA, newRng()) + let autoRelayService2 = AutoRelayService.new(1, relayClient2, nil, newRng()) + + let hpservice1 = HPService.new(autonatService1, autoRelayService1, isPublicIPAddrProc) + let hpservice2 = HPService.new(autonatService2, autoRelayService2) + + let privatePeerSwitch1 = SwitchStub.new(createSwitch(relayClient1, hpservice1)) + let privatePeerSwitch2 = createSwitch(relayClient2, hpservice2) + let switchRelay = createSwitch(Relay.new()) + let switchAux = createSwitch() + let switchAux2 = createSwitch() + let switchAux3 = createSwitch() + let switchAux4 = createSwitch() + + var awaiter = newFuture[void]() + + await allFutures( + switchRelay.start(), privatePeerSwitch1.start(), privatePeerSwitch2.start(), + switchAux.start(), switchAux2.start(), switchAux3.start(), switchAux4.start() + ) + + await privatePeerSwitch1.connect(switchRelay.peerInfo.peerId, switchRelay.peerInfo.addrs) + await privatePeerSwitch2.connect(switchAux.peerInfo.peerId, switchAux.peerInfo.addrs) + + await sleepAsync(200.millis) + + await privatePeerSwitch1.connect(switchAux2.peerInfo.peerId, switchAux2.peerInfo.addrs) + await privatePeerSwitch1.connect(switchAux3.peerInfo.peerId, switchAux3.peerInfo.addrs) + await privatePeerSwitch1.connect(switchAux4.peerInfo.peerId, switchAux4.peerInfo.addrs) + + await privatePeerSwitch2.connect(switchAux2.peerInfo.peerId, switchAux2.peerInfo.addrs) + await privatePeerSwitch2.connect(switchAux3.peerInfo.peerId, switchAux3.peerInfo.addrs) + await privatePeerSwitch2.connect(switchAux4.peerInfo.peerId, switchAux4.peerInfo.addrs) + + privatePeerSwitch1.connectStub = connectStub + await privatePeerSwitch2.connect(privatePeerSwitch1.peerInfo.peerId, (await privatePeerRelayAddr1)) + + await sleepAsync(200.millis) + + await allFuturesThrowing( + privatePeerSwitch1.stop(), privatePeerSwitch2.stop(), switchRelay.stop(), + switchAux.stop(), switchAux2.stop(), switchAux3.stop(), switchAux4.stop()) + + asyncTest "Hole punching when peers addresses are private": + await holePunchingTest(nil, isGlobal, NotReachable) + + asyncTest "Hole punching when there is an error during unilateral direct connection": + + proc connectStub(): Future[void] {.async.} = + raise newException(CatchableError, "error") + + await holePunchingTest(connectStub, isPublicAddrIPAddrMock, Reachable) + diff --git a/tests/testnative.nim b/tests/testnative.nim index 317e976b0..48e971c1d 100644 --- a/tests/testnative.nim +++ b/tests/testnative.nim @@ -44,4 +44,5 @@ import testtcptransport, testautonat, testautonatservice, testautorelay, - testdcutr + testdcutr, + testhpservice