438 lines
17 KiB
Nim
438 lines
17 KiB
Nim
# 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.
|
|
|
|
import std/[options, sequtils]
|
|
import chronos, metrics
|
|
import unittest2
|
|
import ../libp2p/[builders,
|
|
switch,
|
|
protocols/connectivity/autonat/client,
|
|
protocols/connectivity/autonat/service]
|
|
import ./helpers
|
|
import stubs/autonatclientstub
|
|
|
|
proc createSwitch(autonatSvc: Service = nil, withAutonat = true, maxConnsPerPeer = 1, maxConns = 100): Switch =
|
|
var builder = SwitchBuilder.new()
|
|
.withRng(newRng())
|
|
.withAddresses(@[ MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet() ])
|
|
.withTcpTransport()
|
|
.withMaxConnsPerPeer(maxConnsPerPeer)
|
|
.withMaxConnections(maxConns)
|
|
.withMplex()
|
|
.withNoise()
|
|
|
|
if withAutonat:
|
|
builder = builder.withAutonat()
|
|
|
|
if autonatSvc != nil:
|
|
builder = builder.withServices(@[autonatSvc])
|
|
|
|
return builder.build()
|
|
|
|
suite "Autonat Service":
|
|
teardown:
|
|
checkTrackers()
|
|
|
|
asyncTest "Peer must be not reachable":
|
|
|
|
let autonatClientStub = AutonatClientStub.new(expectedDials = 3)
|
|
autonatClientStub.answer = NotReachable
|
|
|
|
let autonatService = AutonatService.new(autonatClientStub, newRng())
|
|
|
|
let switch1 = createSwitch(autonatService)
|
|
let switch2 = createSwitch()
|
|
let switch3 = createSwitch()
|
|
let switch4 = createSwitch()
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Unknown
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
await switch3.start()
|
|
await switch4.start()
|
|
|
|
await switch1.connect(switch2.peerInfo.peerId, switch2.peerInfo.addrs)
|
|
await switch1.connect(switch3.peerInfo.peerId, switch3.peerInfo.addrs)
|
|
await switch1.connect(switch4.peerInfo.peerId, switch4.peerInfo.addrs)
|
|
|
|
await autonatClientStub.finished
|
|
|
|
check autonatService.networkReachability == NetworkReachability.NotReachable
|
|
check libp2p_autonat_reachability_confidence.value(["NotReachable"]) == 0.3
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop())
|
|
|
|
asyncTest "Peer must be reachable":
|
|
|
|
let autonatService = AutonatService.new(AutonatClient.new(), newRng(), some(1.seconds))
|
|
|
|
let switch1 = createSwitch(autonatService)
|
|
let switch2 = createSwitch()
|
|
let switch3 = createSwitch()
|
|
let switch4 = createSwitch()
|
|
|
|
let awaiter = newFuture[void]()
|
|
|
|
proc statusAndConfidenceHandler(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} =
|
|
if networkReachability == NetworkReachability.Reachable and confidence.isSome() and confidence.get() >= 0.3:
|
|
if not awaiter.finished:
|
|
awaiter.complete()
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Unknown
|
|
|
|
autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
await switch3.start()
|
|
await switch4.start()
|
|
|
|
await switch1.connect(switch2.peerInfo.peerId, switch2.peerInfo.addrs)
|
|
await switch1.connect(switch3.peerInfo.peerId, switch3.peerInfo.addrs)
|
|
await switch1.connect(switch4.peerInfo.peerId, switch4.peerInfo.addrs)
|
|
|
|
await awaiter
|
|
|
|
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))
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop())
|
|
|
|
check switch1.peerInfo.addrs == switch1.peerInfo.listenAddrs
|
|
|
|
asyncTest "Peer must be not reachable and then reachable":
|
|
|
|
let autonatClientStub = AutonatClientStub.new(expectedDials = 6)
|
|
autonatClientStub.answer = NotReachable
|
|
|
|
let autonatService = AutonatService.new(autonatClientStub, newRng(), some(1.seconds))
|
|
|
|
let switch1 = createSwitch(autonatService)
|
|
let switch2 = createSwitch()
|
|
let switch3 = createSwitch()
|
|
let switch4 = createSwitch()
|
|
|
|
let awaiter = newFuture[void]()
|
|
|
|
proc statusAndConfidenceHandler(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} =
|
|
if networkReachability == NetworkReachability.NotReachable and confidence.isSome() and confidence.get() >= 0.3:
|
|
if not awaiter.finished:
|
|
autonatClientStub.answer = Reachable
|
|
awaiter.complete()
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Unknown
|
|
|
|
autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
await switch3.start()
|
|
await switch4.start()
|
|
|
|
await switch1.connect(switch2.peerInfo.peerId, switch2.peerInfo.addrs)
|
|
await switch1.connect(switch3.peerInfo.peerId, switch3.peerInfo.addrs)
|
|
await switch1.connect(switch4.peerInfo.peerId, switch4.peerInfo.addrs)
|
|
|
|
await awaiter
|
|
|
|
check autonatService.networkReachability == NetworkReachability.NotReachable
|
|
check libp2p_autonat_reachability_confidence.value(["NotReachable"]) == 0.3
|
|
|
|
await autonatClientStub.finished
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Reachable
|
|
check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 0.3
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop())
|
|
|
|
asyncTest "Peer must be reachable when one connected peer has autonat disabled":
|
|
|
|
let autonatService = AutonatService.new(AutonatClient.new(), newRng(), some(1.seconds), maxQueueSize = 2)
|
|
|
|
let switch1 = createSwitch(autonatService)
|
|
let switch2 = createSwitch(withAutonat = false)
|
|
let switch3 = createSwitch()
|
|
let switch4 = createSwitch()
|
|
|
|
let awaiter = newFuture[void]()
|
|
|
|
proc statusAndConfidenceHandler(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} =
|
|
if networkReachability == NetworkReachability.Reachable and confidence.isSome() and confidence.get() == 1:
|
|
if not awaiter.finished:
|
|
awaiter.complete()
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Unknown
|
|
|
|
autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
await switch3.start()
|
|
await switch4.start()
|
|
|
|
await switch1.connect(switch2.peerInfo.peerId, switch2.peerInfo.addrs)
|
|
await switch1.connect(switch3.peerInfo.peerId, switch3.peerInfo.addrs)
|
|
await switch1.connect(switch4.peerInfo.peerId, switch4.peerInfo.addrs)
|
|
|
|
await awaiter
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Reachable
|
|
check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 1
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop())
|
|
|
|
asyncTest "Unknown answers must be ignored":
|
|
|
|
let autonatClientStub = AutonatClientStub.new(expectedDials = 6)
|
|
autonatClientStub.answer = NotReachable
|
|
|
|
let autonatService = AutonatService.new(autonatClientStub, newRng(), some(1.seconds), maxQueueSize = 3)
|
|
|
|
let switch1 = createSwitch(autonatService)
|
|
let switch2 = createSwitch()
|
|
let switch3 = createSwitch()
|
|
let switch4 = createSwitch()
|
|
|
|
let awaiter = newFuture[void]()
|
|
|
|
proc statusAndConfidenceHandler(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} =
|
|
if networkReachability == NetworkReachability.NotReachable and confidence.isSome() and confidence.get() >= 0.3:
|
|
if not awaiter.finished:
|
|
autonatClientStub.answer = Unknown
|
|
awaiter.complete()
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Unknown
|
|
|
|
autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
await switch3.start()
|
|
await switch4.start()
|
|
|
|
await switch1.connect(switch2.peerInfo.peerId, switch2.peerInfo.addrs)
|
|
await switch1.connect(switch3.peerInfo.peerId, switch3.peerInfo.addrs)
|
|
await switch1.connect(switch4.peerInfo.peerId, switch4.peerInfo.addrs)
|
|
|
|
await awaiter
|
|
|
|
check autonatService.networkReachability == NetworkReachability.NotReachable
|
|
check libp2p_autonat_reachability_confidence.value(["NotReachable"]) == 1/3
|
|
|
|
await autonatClientStub.finished
|
|
|
|
check autonatService.networkReachability == NetworkReachability.NotReachable
|
|
check libp2p_autonat_reachability_confidence.value(["NotReachable"]) == 1/3
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop())
|
|
|
|
asyncTest "Calling setup and stop twice must work":
|
|
|
|
let switch = createSwitch()
|
|
let autonatService = AutonatService.new(AutonatClientStub.new(expectedDials = 0), newRng(), some(1.seconds))
|
|
|
|
check (await autonatService.setup(switch)) == true
|
|
check (await autonatService.setup(switch)) == false
|
|
|
|
check (await autonatService.stop(switch)) == true
|
|
check (await autonatService.stop(switch)) == false
|
|
|
|
await allFuturesThrowing(switch.stop())
|
|
|
|
asyncTest "Must bypass maxConnectionsPerPeer limit":
|
|
let autonatService = AutonatService.new(AutonatClient.new(), newRng(), some(1.seconds), maxQueueSize = 1)
|
|
|
|
let switch1 = createSwitch(autonatService, maxConnsPerPeer = 0)
|
|
let switch2 = createSwitch(maxConnsPerPeer = 0)
|
|
|
|
let awaiter = newFuture[void]()
|
|
|
|
proc statusAndConfidenceHandler(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} =
|
|
if networkReachability == NetworkReachability.Reachable and confidence.isSome() and confidence.get() == 1:
|
|
if not awaiter.finished:
|
|
awaiter.complete()
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Unknown
|
|
|
|
autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
await switch1.connect(switch2.peerInfo.peerId, switch2.peerInfo.addrs)
|
|
|
|
await awaiter
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Reachable
|
|
check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 1
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(), switch2.stop())
|
|
|
|
asyncTest "Must work when peers ask each other at the same time with max 1 conn per peer":
|
|
let autonatService1 = AutonatService.new(AutonatClient.new(), newRng(), some(500.millis), maxQueueSize = 3)
|
|
let autonatService2 = AutonatService.new(AutonatClient.new(), newRng(), some(500.millis), maxQueueSize = 3)
|
|
let autonatService3 = AutonatService.new(AutonatClient.new(), newRng(), some(500.millis), maxQueueSize = 3)
|
|
|
|
let switch1 = createSwitch(autonatService1, maxConnsPerPeer = 0)
|
|
let switch2 = createSwitch(autonatService2, maxConnsPerPeer = 0)
|
|
let switch3 = createSwitch(autonatService2, maxConnsPerPeer = 0)
|
|
|
|
let awaiter1 = newFuture[void]()
|
|
let awaiter2 = newFuture[void]()
|
|
let awaiter3 = newFuture[void]()
|
|
|
|
proc statusAndConfidenceHandler1(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} =
|
|
if networkReachability == NetworkReachability.Reachable and confidence.isSome() and confidence.get() == 1:
|
|
if not awaiter1.finished:
|
|
awaiter1.complete()
|
|
|
|
proc statusAndConfidenceHandler2(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} =
|
|
if networkReachability == NetworkReachability.Reachable and confidence.isSome() and confidence.get() == 1:
|
|
if not awaiter2.finished:
|
|
awaiter2.complete()
|
|
|
|
check autonatService1.networkReachability == NetworkReachability.Unknown
|
|
check autonatService2.networkReachability == NetworkReachability.Unknown
|
|
|
|
autonatService1.statusAndConfidenceHandler(statusAndConfidenceHandler1)
|
|
autonatService2.statusAndConfidenceHandler(statusAndConfidenceHandler2)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
await switch3.start()
|
|
|
|
await switch1.connect(switch2.peerInfo.peerId, switch2.peerInfo.addrs)
|
|
await switch2.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
await switch2.connect(switch3.peerInfo.peerId, switch3.peerInfo.addrs)
|
|
|
|
await awaiter1
|
|
await awaiter2
|
|
|
|
check autonatService1.networkReachability == NetworkReachability.Reachable
|
|
check autonatService2.networkReachability == NetworkReachability.Reachable
|
|
check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 1
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(), switch2.stop(), switch3.stop())
|
|
|
|
asyncTest "Must work for one peer when two peers ask each other at the same time with max 1 conn per peer":
|
|
let autonatService1 = AutonatService.new(AutonatClient.new(), newRng(), some(500.millis), maxQueueSize = 3)
|
|
let autonatService2 = AutonatService.new(AutonatClient.new(), newRng(), some(500.millis), maxQueueSize = 3)
|
|
|
|
let switch1 = createSwitch(autonatService1, maxConnsPerPeer = 0)
|
|
let switch2 = createSwitch(autonatService2, maxConnsPerPeer = 0)
|
|
|
|
let awaiter1 = newFuture[void]()
|
|
|
|
proc statusAndConfidenceHandler1(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} =
|
|
if networkReachability == NetworkReachability.Reachable and confidence.isSome() and confidence.get() == 1:
|
|
if not awaiter1.finished:
|
|
awaiter1.complete()
|
|
|
|
check autonatService1.networkReachability == NetworkReachability.Unknown
|
|
|
|
autonatService1.statusAndConfidenceHandler(statusAndConfidenceHandler1)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
await switch1.connect(switch2.peerInfo.peerId, switch2.peerInfo.addrs)
|
|
try:
|
|
# We allow a temp conn for the peer to dial us. It could use this conn to just connect to us and not dial.
|
|
# We don't care if it fails at this point or not. But this conn must be closed eventually.
|
|
# Bellow we check that there's only one connection between the peers
|
|
await switch2.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs, reuseConnection = false)
|
|
except CatchableError:
|
|
discard
|
|
|
|
await awaiter1
|
|
|
|
check autonatService1.networkReachability == NetworkReachability.Reachable
|
|
check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 1
|
|
|
|
# Make sure remote peer can't create a connection to us
|
|
check switch1.connManager.connCount(switch2.peerInfo.peerId) == 1
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(), switch2.stop())
|
|
|
|
asyncTest "Must work with low maxConnections":
|
|
let autonatService = AutonatService.new(AutonatClient.new(), newRng(), some(1.seconds), maxQueueSize = 1)
|
|
|
|
let switch1 = createSwitch(autonatService, maxConns = 4)
|
|
let switch2 = createSwitch()
|
|
let switch3 = createSwitch()
|
|
let switch4 = createSwitch()
|
|
let switch5 = createSwitch()
|
|
|
|
var awaiter = newFuture[void]()
|
|
|
|
proc statusAndConfidenceHandler(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} =
|
|
if networkReachability == NetworkReachability.Reachable and confidence.isSome() and confidence.get() == 1:
|
|
if not awaiter.finished:
|
|
awaiter.complete()
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Unknown
|
|
|
|
autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
await switch3.start()
|
|
await switch4.start()
|
|
await switch5.start()
|
|
|
|
await switch1.connect(switch2.peerInfo.peerId, switch2.peerInfo.addrs)
|
|
|
|
await awaiter
|
|
|
|
await switch1.connect(switch3.peerInfo.peerId, switch3.peerInfo.addrs)
|
|
await switch1.connect(switch4.peerInfo.peerId, switch4.peerInfo.addrs)
|
|
await switch5.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
# switch1 is now full, should stick to last observation
|
|
awaiter = newFuture[void]()
|
|
await autonatService.run(switch1)
|
|
await awaiter
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Reachable
|
|
check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 1
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop(), switch5.stop())
|
|
|
|
asyncTest "Peer must not ask an incoming peer":
|
|
let autonatService = AutonatService.new(AutonatClient.new(), newRng())
|
|
|
|
let switch1 = createSwitch(autonatService)
|
|
let switch2 = createSwitch()
|
|
|
|
proc statusAndConfidenceHandler(networkReachability: NetworkReachability, confidence: Option[float]) {.gcsafe, async.} =
|
|
fail()
|
|
|
|
check autonatService.networkReachability == NetworkReachability.Unknown
|
|
|
|
autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
await switch2.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
|
|
await sleepAsync(500.milliseconds)
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(), switch2.stop())
|