301 lines
9.4 KiB
Nim
301 lines
9.4 KiB
Nim
# 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.}
|
|
|
|
{.push raises: [].}
|
|
|
|
import chronos
|
|
|
|
import unittest2
|
|
import ./helpers
|
|
import ./stubs/switchstub
|
|
import ../libp2p/[builders, switch, wire, services/hpservice, services/autorelayservice]
|
|
import ../libp2p/protocols/connectivity/relay/[relay, client]
|
|
import ../libp2p/protocols/connectivity/autonat/[service]
|
|
import ../libp2p/nameresolving/[nameresolver, mockresolver]
|
|
import stubs/autonatclientstub
|
|
|
|
proc createSwitch(
|
|
r: Relay = nil, hpService: Service = nil, nameResolver: NameResolver = nil
|
|
): Switch {.raises: [LPError].} =
|
|
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)
|
|
|
|
if nameResolver != nil:
|
|
builder = builder.withNameResolver(nameResolver)
|
|
|
|
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)
|
|
|
|
let privatePeerSwitch =
|
|
createSwitch(relayClient, hpservice, nameresolver = MockResolver.default())
|
|
let peerSwitch = createSwitch()
|
|
let switchRelay = createSwitch(Relay.new())
|
|
|
|
await allFuturesThrowing(
|
|
switchRelay.start(),
|
|
privatePeerSwitch.start(),
|
|
publicPeerSwitch.start(),
|
|
peerSwitch.start(),
|
|
)
|
|
publicPeerSwitch.peerInfo.addrs.add(
|
|
[
|
|
MultiAddress.init("/dns4/localhost/").tryGet() &
|
|
publicPeerSwitch.peerInfo.addrs[0][1].tryGet()
|
|
]
|
|
)
|
|
|
|
await privatePeerSwitch.connect(
|
|
switchRelay.peerInfo.peerId, switchRelay.peerInfo.addrs
|
|
)
|
|
await privatePeerSwitch.connect(
|
|
peerSwitch.peerInfo.peerId, peerSwitch.peerInfo.addrs
|
|
) # for autonat
|
|
|
|
await publicPeerSwitch.connect(
|
|
privatePeerSwitch.peerInfo.peerId, (await privatePeerRelayAddr)
|
|
)
|
|
|
|
checkUntilTimeout:
|
|
privatePeerSwitch.connManager.connCount(publicPeerSwitch.peerInfo.peerId) == 1
|
|
not isRelayed(
|
|
privatePeerSwitch.connManager.selectMuxer(publicPeerSwitch.peerInfo.peerId).connection
|
|
)
|
|
|
|
await allFuturesThrowing(
|
|
privatePeerSwitch.stop(),
|
|
publicPeerSwitch.stop(),
|
|
switchRelay.stop(),
|
|
peerSwitch.stop(),
|
|
)
|
|
|
|
asyncTest "Direct connection must work when peer address is public and dns is used":
|
|
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)
|
|
|
|
let privatePeerSwitch =
|
|
createSwitch(relayClient, hpservice, nameResolver = MockResolver.default())
|
|
let peerSwitch = createSwitch()
|
|
let switchRelay = createSwitch(Relay.new())
|
|
|
|
await allFuturesThrowing(
|
|
switchRelay.start(),
|
|
privatePeerSwitch.start(),
|
|
publicPeerSwitch.start(),
|
|
peerSwitch.start(),
|
|
)
|
|
publicPeerSwitch.peerInfo.addrs.add(
|
|
[
|
|
MultiAddress.init("/dns4/localhost/").tryGet() &
|
|
publicPeerSwitch.peerInfo.addrs[0][1].tryGet()
|
|
]
|
|
)
|
|
|
|
await privatePeerSwitch.connect(
|
|
switchRelay.peerInfo.peerId, switchRelay.peerInfo.addrs
|
|
)
|
|
await privatePeerSwitch.connect(
|
|
peerSwitch.peerInfo.peerId, peerSwitch.peerInfo.addrs
|
|
) # for autonat
|
|
|
|
await publicPeerSwitch.connect(
|
|
privatePeerSwitch.peerInfo.peerId, (await privatePeerRelayAddr)
|
|
)
|
|
|
|
checkUntilTimeout:
|
|
privatePeerSwitch.connManager.connCount(publicPeerSwitch.peerInfo.peerId) == 1
|
|
not isRelayed(
|
|
privatePeerSwitch.connManager.selectMuxer(publicPeerSwitch.peerInfo.peerId).connection
|
|
)
|
|
|
|
await allFuturesThrowing(
|
|
privatePeerSwitch.stop(),
|
|
publicPeerSwitch.stop(),
|
|
switchRelay.stop(),
|
|
peerSwitch.stop(),
|
|
)
|
|
|
|
proc holePunchingTest(
|
|
initiatorConnectStub: connectStubType,
|
|
rcvConnectStub: connectStubType,
|
|
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)
|
|
let hpservice2 = HPService.new(autonatService2, autoRelayService2)
|
|
|
|
let privatePeerSwitch1 = SwitchStub.new(
|
|
createSwitch(relayClient1, hpservice1, nameresolver = MockResolver.default())
|
|
)
|
|
let privatePeerSwitch2 = SwitchStub.new(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 allFuturesThrowing(
|
|
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 = initiatorConnectStub
|
|
await privatePeerSwitch2.connect(
|
|
privatePeerSwitch1.peerInfo.peerId, (await privatePeerRelayAddr1)
|
|
)
|
|
privatePeerSwitch2.connectStub = rcvConnectStub
|
|
|
|
# wait for hole punching to finish in the background
|
|
await sleepAsync(600.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, nil, NotReachable)
|
|
|
|
asyncTest "Hole punching when peers addresses are private and there is an error in the initiator side":
|
|
proc connectStub(
|
|
self: SwitchStub,
|
|
peerId: PeerId,
|
|
addrs: seq[MultiAddress],
|
|
forceDial = false,
|
|
reuseConnection = true,
|
|
dir = Direction.Out,
|
|
): Future[void] {.async.} =
|
|
self.connectStub = nil # this stub should be called only once
|
|
raise newException(CatchableError, "error")
|
|
|
|
await holePunchingTest(connectStub, nil, Reachable)
|