mirror of https://github.com/vacp2p/nim-libp2p.git
1039 lines
31 KiB
Nim
1039 lines
31 KiB
Nim
{.used.}
|
|
|
|
# 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 options, sequtils
|
|
import chronos
|
|
import stew/byteutils
|
|
import
|
|
../libp2p/[
|
|
errors,
|
|
switch,
|
|
multistream,
|
|
builders,
|
|
stream/bufferstream,
|
|
stream/connection,
|
|
multicodec,
|
|
multiaddress,
|
|
peerinfo,
|
|
crypto/crypto,
|
|
protocols/protocol,
|
|
protocols/secure/secure,
|
|
muxers/muxer,
|
|
muxers/mplex/lpchannel,
|
|
stream/lpstream,
|
|
nameresolving/mockresolver,
|
|
stream/chronosstream,
|
|
utils/semaphore,
|
|
transports/tcptransport,
|
|
transports/wstransport,
|
|
]
|
|
import ./helpers
|
|
|
|
const TestCodec = "/test/proto/1.0.0"
|
|
|
|
type TestProto = ref object of LPProtocol
|
|
|
|
suite "Switch":
|
|
teardown:
|
|
checkTrackers()
|
|
|
|
asyncTest "e2e use switch dial proto string":
|
|
let done = newFuture[void]()
|
|
proc handle(conn: Connection, proto: string) {.async.} =
|
|
try:
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.writeLp("Hello!")
|
|
finally:
|
|
await conn.close()
|
|
done.complete()
|
|
|
|
let testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
let switch1 = newStandardSwitch()
|
|
switch1.mount(testProto)
|
|
|
|
let switch2 = newStandardSwitch()
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
let conn =
|
|
await switch2.dial(switch1.peerInfo.peerId, switch1.peerInfo.addrs, TestCodec)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await conn.writeLp("Hello!")
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.close()
|
|
|
|
await allFuturesThrowing(done.wait(5.seconds), switch1.stop(), switch2.stop())
|
|
|
|
check not switch1.isConnected(switch2.peerInfo.peerId)
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
asyncTest "e2e use switch dial proto string with custom matcher":
|
|
let done = newFuture[void]()
|
|
proc handle(conn: Connection, proto: string) {.async.} =
|
|
try:
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.writeLp("Hello!")
|
|
finally:
|
|
await conn.close()
|
|
done.complete()
|
|
|
|
let testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
let callProto = TestCodec & "/pew"
|
|
|
|
proc match(proto: string): bool {.gcsafe.} =
|
|
return proto == callProto
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
switch1.mount(testProto, match)
|
|
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
let conn =
|
|
await switch2.dial(switch1.peerInfo.peerId, switch1.peerInfo.addrs, callProto)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await conn.writeLp("Hello!")
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.close()
|
|
|
|
await allFuturesThrowing(done.wait(5.seconds), switch1.stop(), switch2.stop())
|
|
|
|
check not switch1.isConnected(switch2.peerInfo.peerId)
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
asyncTest "e2e should not leak bufferstreams and connections on channel close":
|
|
let done = newFuture[void]()
|
|
proc handle(conn: Connection, proto: string) {.async.} =
|
|
try:
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.writeLp("Hello!")
|
|
finally:
|
|
await conn.close()
|
|
done.complete()
|
|
|
|
let testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
let switch1 = newStandardSwitch()
|
|
switch1.mount(testProto)
|
|
|
|
let switch2 = newStandardSwitch()
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
let conn =
|
|
await switch2.dial(switch1.peerInfo.peerId, switch1.peerInfo.addrs, TestCodec)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await conn.writeLp("Hello!")
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.close()
|
|
|
|
await allFuturesThrowing(done.wait(5.seconds), switch1.stop(), switch2.stop())
|
|
|
|
check not switch1.isConnected(switch2.peerInfo.peerId)
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
asyncTest "e2e use connect then dial":
|
|
proc handle(conn: Connection, proto: string) {.async.} =
|
|
try:
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
finally:
|
|
await conn.writeLp("Hello!")
|
|
await conn.close()
|
|
|
|
let testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
switch1.mount(testProto)
|
|
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
await switch2.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
let conn =
|
|
await switch2.dial(switch1.peerInfo.peerId, switch1.peerInfo.addrs, TestCodec)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await conn.writeLp("Hello!")
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
|
|
await allFuturesThrowing(conn.close(), switch1.stop(), switch2.stop())
|
|
|
|
check not switch1.isConnected(switch2.peerInfo.peerId)
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
asyncTest "e2e connect to peer with unknown PeerId":
|
|
let resolver = MockResolver.new()
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
let switch2 = newStandardSwitch(
|
|
secureManagers = [SecureProtocol.Noise], nameResolver = resolver
|
|
)
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
# via dnsaddr
|
|
resolver.txtResponses["_dnsaddr.test.io"] =
|
|
@["dnsaddr=" & $switch1.peerInfo.addrs[0] & "/p2p/" & $switch1.peerInfo.peerId]
|
|
|
|
check:
|
|
(await switch2.connect(MultiAddress.init("/dnsaddr/test.io/").tryGet(), true)) ==
|
|
switch1.peerInfo.peerId
|
|
await switch2.disconnect(switch1.peerInfo.peerId)
|
|
|
|
# via direct ip
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
check:
|
|
(await switch2.connect(switch1.peerInfo.addrs[0], true)) == switch1.peerInfo.peerId
|
|
|
|
await switch2.disconnect(switch1.peerInfo.peerId)
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop())
|
|
|
|
asyncTest "e2e connect to peer with known PeerId":
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
# via direct ip
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
# without specifying allow unknown, will fail
|
|
expect(DialFailedError):
|
|
discard await switch2.connect(switch1.peerInfo.addrs[0])
|
|
|
|
# with invalid PeerId, will fail
|
|
let fakeMa = concat(
|
|
switch1.peerInfo.addrs[0],
|
|
MultiAddress.init(multiCodec("p2p"), PeerId.random.tryGet().data).tryGet(),
|
|
)
|
|
.tryGet()
|
|
expect(CatchableError):
|
|
discard (await switch2.connect(fakeMa))
|
|
|
|
# real thing works
|
|
check (await switch2.connect(switch1.peerInfo.fullAddrs.tryGet()[0])) ==
|
|
switch1.peerInfo.peerId
|
|
|
|
await switch2.disconnect(switch1.peerInfo.peerId)
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop())
|
|
|
|
asyncTest "e2e should not leak on peer disconnect":
|
|
let switch1 = newStandardSwitch()
|
|
let switch2 = newStandardSwitch()
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
let startCounts =
|
|
@[
|
|
switch1.connManager.inSema.count, switch1.connManager.outSema.count,
|
|
switch2.connManager.inSema.count, switch2.connManager.outSema.count,
|
|
]
|
|
|
|
await switch2.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await switch2.disconnect(switch1.peerInfo.peerId)
|
|
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
checkUntilTimeout:
|
|
not switch1.isConnected(switch2.peerInfo.peerId)
|
|
|
|
checkTracker(LPChannelTrackerName)
|
|
checkTracker(SecureConnTrackerName)
|
|
|
|
checkUntilTimeout:
|
|
startCounts ==
|
|
@[
|
|
switch1.connManager.inSema.count, switch1.connManager.outSema.count,
|
|
switch2.connManager.inSema.count, switch2.connManager.outSema.count,
|
|
]
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop())
|
|
|
|
asyncTest "e2e should trigger connection events (remote)":
|
|
let switch1 = newStandardSwitch()
|
|
let switch2 = newStandardSwitch()
|
|
|
|
var step = 0
|
|
var kinds: set[ConnEventKind]
|
|
proc hook(peerId: PeerId, event: ConnEvent) {.async.} =
|
|
kinds = kinds + {event.kind}
|
|
case step
|
|
of 0:
|
|
check:
|
|
event.kind == ConnEventKind.Connected
|
|
peerId == switch1.peerInfo.peerId
|
|
of 1:
|
|
check:
|
|
event.kind == ConnEventKind.Disconnected
|
|
|
|
check peerId == switch1.peerInfo.peerId
|
|
else:
|
|
check false
|
|
|
|
step.inc()
|
|
|
|
switch2.addConnEventHandler(hook, ConnEventKind.Connected)
|
|
switch2.addConnEventHandler(hook, ConnEventKind.Disconnected)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
await switch2.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await switch2.disconnect(switch1.peerInfo.peerId)
|
|
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
checkUntilTimeout:
|
|
not switch1.isConnected(switch2.peerInfo.peerId)
|
|
|
|
checkTracker(LPChannelTrackerName)
|
|
checkTracker(SecureConnTrackerName)
|
|
|
|
check:
|
|
kinds == {ConnEventKind.Connected, ConnEventKind.Disconnected}
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop())
|
|
|
|
asyncTest "e2e should trigger connection events (local)":
|
|
let switch1 = newStandardSwitch()
|
|
let switch2 = newStandardSwitch()
|
|
|
|
var step = 0
|
|
var kinds: set[ConnEventKind]
|
|
proc hook(peerId: PeerId, event: ConnEvent) {.async.} =
|
|
kinds = kinds + {event.kind}
|
|
case step
|
|
of 0:
|
|
check:
|
|
event.kind == ConnEventKind.Connected
|
|
peerId == switch2.peerInfo.peerId
|
|
of 1:
|
|
check:
|
|
event.kind == ConnEventKind.Disconnected
|
|
|
|
check peerId == switch2.peerInfo.peerId
|
|
else:
|
|
check false
|
|
|
|
step.inc()
|
|
|
|
switch1.addConnEventHandler(hook, ConnEventKind.Connected)
|
|
switch1.addConnEventHandler(hook, ConnEventKind.Disconnected)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
await switch2.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await switch2.disconnect(switch1.peerInfo.peerId)
|
|
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
checkUntilTimeout:
|
|
not switch1.isConnected(switch2.peerInfo.peerId)
|
|
|
|
checkTracker(LPChannelTrackerName)
|
|
checkTracker(SecureConnTrackerName)
|
|
|
|
check:
|
|
kinds == {ConnEventKind.Connected, ConnEventKind.Disconnected}
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop())
|
|
|
|
asyncTest "e2e should trigger peer events (remote)":
|
|
let switch1 = newStandardSwitch()
|
|
let switch2 = newStandardSwitch()
|
|
|
|
var step = 0
|
|
var kinds: set[PeerEventKind]
|
|
proc handler(peerId: PeerId, event: PeerEvent) {.async.} =
|
|
kinds = kinds + {event.kind}
|
|
case step
|
|
of 0:
|
|
check:
|
|
event.kind == PeerEventKind.Joined
|
|
peerId == switch2.peerInfo.peerId
|
|
of 1:
|
|
check:
|
|
event.kind == PeerEventKind.Left
|
|
peerId == switch2.peerInfo.peerId
|
|
else:
|
|
check false
|
|
|
|
step.inc()
|
|
|
|
switch1.addPeerEventHandler(handler, PeerEventKind.Joined)
|
|
switch1.addPeerEventHandler(handler, PeerEventKind.Left)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
await switch2.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await switch2.disconnect(switch1.peerInfo.peerId)
|
|
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
checkUntilTimeout:
|
|
not switch1.isConnected(switch2.peerInfo.peerId)
|
|
|
|
checkTracker(LPChannelTrackerName)
|
|
checkTracker(SecureConnTrackerName)
|
|
|
|
check:
|
|
kinds == {PeerEventKind.Joined, PeerEventKind.Left}
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop())
|
|
|
|
asyncTest "e2e should trigger peer events (local)":
|
|
let switch1 = newStandardSwitch()
|
|
let switch2 = newStandardSwitch()
|
|
|
|
var step = 0
|
|
var kinds: set[PeerEventKind]
|
|
proc handler(peerId: PeerId, event: PeerEvent) {.async.} =
|
|
kinds = kinds + {event.kind}
|
|
case step
|
|
of 0:
|
|
check:
|
|
event.kind == PeerEventKind.Joined
|
|
peerId == switch1.peerInfo.peerId
|
|
of 1:
|
|
check:
|
|
event.kind == PeerEventKind.Left
|
|
peerId == switch1.peerInfo.peerId
|
|
else:
|
|
check false
|
|
|
|
step.inc()
|
|
|
|
switch2.addPeerEventHandler(handler, PeerEventKind.Joined)
|
|
switch2.addPeerEventHandler(handler, PeerEventKind.Left)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
await switch2.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await switch2.disconnect(switch1.peerInfo.peerId)
|
|
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
checkUntilTimeout:
|
|
not switch1.isConnected(switch2.peerInfo.peerId)
|
|
|
|
checkTracker(LPChannelTrackerName)
|
|
checkTracker(SecureConnTrackerName)
|
|
|
|
check:
|
|
kinds == {PeerEventKind.Joined, PeerEventKind.Left}
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop())
|
|
|
|
asyncTest "e2e should trigger peer events only once per peer":
|
|
let switch1 = newStandardSwitch()
|
|
|
|
let rng = crypto.newRng()
|
|
# use same private keys to emulate two connection from same peer
|
|
let privKey = PrivateKey.random(rng[]).tryGet()
|
|
let switch2 = newStandardSwitch(privKey = some(privKey), rng = rng)
|
|
|
|
let switch3 = newStandardSwitch(privKey = some(privKey), rng = rng)
|
|
|
|
var step = 0
|
|
var kinds: set[PeerEventKind]
|
|
proc handler(peerId: PeerId, event: PeerEvent) {.async.} =
|
|
kinds = kinds + {event.kind}
|
|
case step
|
|
of 0:
|
|
check:
|
|
event.kind == PeerEventKind.Joined
|
|
of 1:
|
|
check:
|
|
event.kind == PeerEventKind.Left
|
|
else:
|
|
check false # should not trigger this
|
|
|
|
step.inc()
|
|
|
|
switch1.addPeerEventHandler(handler, PeerEventKind.Joined)
|
|
switch1.addPeerEventHandler(handler, PeerEventKind.Left)
|
|
|
|
await switch1.start()
|
|
await switch2.start()
|
|
await switch3.start()
|
|
|
|
await switch2.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
# should trigger 1st Join event
|
|
await switch3.connect(switch1.peerInfo.peerId, switch1.peerInfo.addrs)
|
|
# should trigger 2nd Join event
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
check switch3.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await switch2.disconnect(switch1.peerInfo.peerId) # should trigger 1st Left event
|
|
await switch3.disconnect(switch1.peerInfo.peerId) # should trigger 2nd Left event
|
|
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
check not switch3.isConnected(switch1.peerInfo.peerId)
|
|
checkUntilTimeout:
|
|
not switch1.isConnected(switch2.peerInfo.peerId)
|
|
checkUntilTimeout:
|
|
not switch1.isConnected(switch3.peerInfo.peerId)
|
|
|
|
checkTracker(LPChannelTrackerName)
|
|
checkTracker(SecureConnTrackerName)
|
|
|
|
check:
|
|
kinds == {PeerEventKind.Joined, PeerEventKind.Left}
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop(), switch3.stop())
|
|
|
|
asyncTest "e2e should allow dropping peer from connection events":
|
|
let rng = crypto.newRng()
|
|
# use same private keys to emulate two connection from same peer
|
|
let
|
|
privateKey = PrivateKey.random(rng[]).tryGet()
|
|
peerInfo = PeerInfo.new(privateKey)
|
|
|
|
var switches: seq[Switch]
|
|
var done = newFuture[void]()
|
|
var onConnect: Future[void]
|
|
proc hook(peerId: PeerId, event: ConnEvent) {.async.} =
|
|
case event.kind
|
|
of ConnEventKind.Connected:
|
|
await onConnect
|
|
await switches[0].disconnect(peerInfo.peerId) # trigger disconnect
|
|
of ConnEventKind.Disconnected:
|
|
check not switches[0].isConnected(peerInfo.peerId)
|
|
done.complete()
|
|
|
|
switches.add(newStandardSwitch(rng = rng))
|
|
|
|
switches[0].addConnEventHandler(hook, ConnEventKind.Connected)
|
|
switches[0].addConnEventHandler(hook, ConnEventKind.Disconnected)
|
|
await switches[0].start()
|
|
|
|
switches.add(newStandardSwitch(privKey = some(privateKey), rng = rng))
|
|
onConnect =
|
|
switches[1].connect(switches[0].peerInfo.peerId, switches[0].peerInfo.addrs)
|
|
await onConnect
|
|
|
|
await done
|
|
|
|
await allFuturesThrowing(switches.mapIt(it.stop()))
|
|
|
|
asyncTest "e2e should allow dropping multiple connections for peer from connection events":
|
|
let rng = crypto.newRng()
|
|
# use same private keys to emulate two connection from same peer
|
|
let
|
|
privateKey = PrivateKey.random(rng[]).tryGet()
|
|
peerInfo = PeerInfo.new(privateKey)
|
|
|
|
var conns = 1
|
|
var switches: seq[Switch]
|
|
var done = newFuture[void]()
|
|
var onConnect: Future[void]
|
|
proc hook(peerId2: PeerId, event: ConnEvent) {.async.} =
|
|
case event.kind
|
|
of ConnEventKind.Connected:
|
|
if conns == 5:
|
|
await onConnect
|
|
await switches[0].disconnect(peerInfo.peerId) # trigger disconnect
|
|
return
|
|
|
|
conns.inc
|
|
of ConnEventKind.Disconnected:
|
|
if conns == 1:
|
|
check not switches[0].isConnected(peerInfo.peerId)
|
|
done.complete()
|
|
conns.dec
|
|
|
|
switches.add(newStandardSwitch(maxConnsPerPeer = 10, rng = rng))
|
|
|
|
switches[0].addConnEventHandler(hook, ConnEventKind.Connected)
|
|
await switches[0].start()
|
|
|
|
for i in 1 .. 5:
|
|
switches.add(newStandardSwitch(privKey = some(privateKey), rng = rng))
|
|
switches[i].addConnEventHandler(hook, ConnEventKind.Disconnected)
|
|
onConnect =
|
|
switches[i].connect(switches[0].peerInfo.peerId, switches[0].peerInfo.addrs)
|
|
await onConnect
|
|
|
|
await done
|
|
checkTracker(LPChannelTrackerName)
|
|
checkTracker(SecureConnTrackerName)
|
|
|
|
await allFuturesThrowing(switches.mapIt(it.stop()))
|
|
|
|
asyncTest "e2e closing remote conn should not leak":
|
|
let ma = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()]
|
|
|
|
let transport = TcpTransport.new(upgrade = Upgrade())
|
|
await transport.start(ma)
|
|
|
|
proc acceptHandler() {.async.} =
|
|
let conn = await transport.accept()
|
|
await conn.closeWithEOF()
|
|
|
|
let handlerWait = acceptHandler()
|
|
let switch = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
|
|
await switch.start()
|
|
|
|
var peerId = PeerId.init(PrivateKey.random(ECDSA, rng[]).get()).get()
|
|
expect DialFailedError:
|
|
await switch.connect(peerId, transport.addrs)
|
|
|
|
await handlerWait
|
|
|
|
checkTracker(LPChannelTrackerName)
|
|
checkTracker(SecureConnTrackerName)
|
|
checkTracker(ChronosStreamTrackerName)
|
|
|
|
await allFuturesThrowing(transport.stop(), switch.stop())
|
|
|
|
asyncTest "e2e calling closeWithEOF on the same stream should not assert":
|
|
proc handle(conn: Connection, proto: string) {.async.} =
|
|
discard await conn.readLp(100)
|
|
|
|
let testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
switch1.mount(testProto)
|
|
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
|
|
await switch1.start()
|
|
|
|
let conn =
|
|
await switch2.dial(switch1.peerInfo.peerId, switch1.peerInfo.addrs, TestCodec)
|
|
|
|
proc closeReader() {.async.} =
|
|
await conn.closeWithEOF()
|
|
|
|
var readers: seq[Future[void]]
|
|
for i in 0 .. 10:
|
|
readers.add(closeReader())
|
|
|
|
await allFuturesThrowing(readers)
|
|
await switch2.stop() #Otherwise this leaks
|
|
checkUntilTimeout:
|
|
not switch1.isConnected(switch2.peerInfo.peerId)
|
|
|
|
checkTracker(LPChannelTrackerName)
|
|
checkTracker(SecureConnTrackerName)
|
|
checkTracker(ChronosStreamTrackerName)
|
|
|
|
await switch1.stop()
|
|
|
|
asyncTest "connect to inexistent peer":
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
await switch2.start()
|
|
let someAddr = MultiAddress.init("/ip4/127.128.0.99").get()
|
|
let seckey = PrivateKey.random(ECDSA, rng[]).get()
|
|
let somePeer = PeerInfo.new(seckey, [someAddr])
|
|
expect(DialFailedError):
|
|
discard await switch2.dial(somePeer.peerId, somePeer.addrs, TestCodec)
|
|
await switch2.stop()
|
|
|
|
asyncTest "e2e total connection limits on incoming connections":
|
|
var switches: seq[Switch]
|
|
let destSwitch = newStandardSwitch(maxConnections = 3)
|
|
switches.add(destSwitch)
|
|
await destSwitch.start()
|
|
|
|
let destPeerInfo = destSwitch.peerInfo
|
|
for i in 0 ..< 3:
|
|
let switch = newStandardSwitch()
|
|
switches.add(switch)
|
|
await switch.start()
|
|
|
|
check await switch.connect(destPeerInfo.peerId, destPeerInfo.addrs).withTimeout(
|
|
1000.millis
|
|
)
|
|
|
|
let switchFail = newStandardSwitch()
|
|
switches.add(switchFail)
|
|
await switchFail.start()
|
|
|
|
check not (
|
|
await switchFail.connect(destPeerInfo.peerId, destPeerInfo.addrs).withTimeout(
|
|
1000.millis
|
|
)
|
|
)
|
|
|
|
await allFuturesThrowing(allFutures(switches.mapIt(it.stop())))
|
|
|
|
asyncTest "e2e total connection limits on incoming connections":
|
|
var switches: seq[Switch]
|
|
for i in 0 ..< 3:
|
|
switches.add(newStandardSwitch())
|
|
await switches[i].start()
|
|
|
|
let srcSwitch = newStandardSwitch(maxConnections = 3)
|
|
await srcSwitch.start()
|
|
|
|
let dstSwitch = newStandardSwitch()
|
|
await dstSwitch.start()
|
|
|
|
for s in switches:
|
|
check await srcSwitch.connect(s.peerInfo.peerId, s.peerInfo.addrs).withTimeout(
|
|
1000.millis
|
|
)
|
|
|
|
expect TooManyConnectionsError:
|
|
await srcSwitch.connect(dstSwitch.peerInfo.peerId, dstSwitch.peerInfo.addrs)
|
|
|
|
switches.add(srcSwitch)
|
|
switches.add(dstSwitch)
|
|
|
|
await allFuturesThrowing(allFutures(switches.mapIt(it.stop())))
|
|
|
|
asyncTest "e2e max incoming connection limits":
|
|
var switches: seq[Switch]
|
|
let destSwitch = newStandardSwitch(maxIn = 3)
|
|
switches.add(destSwitch)
|
|
await destSwitch.start()
|
|
|
|
let destPeerInfo = destSwitch.peerInfo
|
|
for i in 0 ..< 3:
|
|
let switch = newStandardSwitch()
|
|
switches.add(switch)
|
|
await switch.start()
|
|
|
|
check await switch.connect(destPeerInfo.peerId, destPeerInfo.addrs).withTimeout(
|
|
1000.millis
|
|
)
|
|
|
|
let switchFail = newStandardSwitch()
|
|
switches.add(switchFail)
|
|
await switchFail.start()
|
|
|
|
check not (
|
|
await switchFail.connect(destPeerInfo.peerId, destPeerInfo.addrs).withTimeout(
|
|
1000.millis
|
|
)
|
|
)
|
|
|
|
await allFuturesThrowing(allFutures(switches.mapIt(it.stop())))
|
|
|
|
asyncTest "e2e max outgoing connection limits":
|
|
var switches: seq[Switch]
|
|
for i in 0 ..< 3:
|
|
switches.add(newStandardSwitch())
|
|
await switches[i].start()
|
|
|
|
let srcSwitch = newStandardSwitch(maxOut = 3)
|
|
await srcSwitch.start()
|
|
|
|
let dstSwitch = newStandardSwitch()
|
|
await dstSwitch.start()
|
|
|
|
for s in switches:
|
|
check await srcSwitch.connect(s.peerInfo.peerId, s.peerInfo.addrs).withTimeout(
|
|
1000.millis
|
|
)
|
|
|
|
expect TooManyConnectionsError:
|
|
await srcSwitch.connect(dstSwitch.peerInfo.peerId, dstSwitch.peerInfo.addrs)
|
|
|
|
switches.add(srcSwitch)
|
|
switches.add(dstSwitch)
|
|
|
|
await allFuturesThrowing(allFutures(switches.mapIt(it.stop())))
|
|
|
|
asyncTest "e2e peer store":
|
|
let done = newFuture[void]()
|
|
proc handle(conn: Connection, proto: string) {.async.} =
|
|
try:
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.writeLp("Hello!")
|
|
finally:
|
|
await conn.close()
|
|
done.complete()
|
|
|
|
let testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
let switch1 = newStandardSwitch()
|
|
switch1.mount(testProto)
|
|
|
|
let switch2 = newStandardSwitch(peerStoreCapacity = 0)
|
|
await switch1.start()
|
|
await switch2.start()
|
|
|
|
let conn =
|
|
await switch2.dial(switch1.peerInfo.peerId, switch1.peerInfo.addrs, TestCodec)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await conn.writeLp("Hello!")
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.close()
|
|
|
|
await allFuturesThrowing(done.wait(5.seconds), switch1.stop(), switch2.stop())
|
|
|
|
check not switch1.isConnected(switch2.peerInfo.peerId)
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
check:
|
|
switch1.peerStore[AddressBook][switch2.peerInfo.peerId] == switch2.peerInfo.addrs
|
|
switch1.peerStore[ProtoBook][switch2.peerInfo.peerId] == switch2.peerInfo.protocols
|
|
|
|
switch1.peerInfo.peerId notin switch2.peerStore[AddressBook]
|
|
switch1.peerInfo.peerId notin switch2.peerStore[ProtoBook]
|
|
|
|
asyncTest "e2e should allow multiple local addresses":
|
|
when defined(windows):
|
|
# this randomly locks the Windows CI job
|
|
skip()
|
|
return
|
|
proc handle(conn: Connection, proto: string) {.async.} =
|
|
try:
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.writeLp("Hello!")
|
|
finally:
|
|
await conn.close()
|
|
|
|
let testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
let addrs =
|
|
@[
|
|
MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet(),
|
|
MultiAddress.init("/ip6/::1/tcp/0").tryGet(),
|
|
]
|
|
|
|
let switch1 = newStandardSwitch(
|
|
addrs = addrs, transportFlags = {ServerFlags.ReuseAddr, ServerFlags.ReusePort}
|
|
)
|
|
|
|
switch1.mount(testProto)
|
|
|
|
let switch2 = newStandardSwitch()
|
|
let switch3 =
|
|
newStandardSwitch(addrs = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet())
|
|
|
|
await allFuturesThrowing(switch1.start(), switch2.start(), switch3.start())
|
|
|
|
check IP4.matchPartial(switch1.peerInfo.addrs[0])
|
|
check IP6.matchPartial(switch1.peerInfo.addrs[1])
|
|
|
|
let conn = await switch2.dial(
|
|
switch1.peerInfo.peerId, @[switch1.peerInfo.addrs[0]], TestCodec
|
|
)
|
|
|
|
check switch1.isConnected(switch2.peerInfo.peerId)
|
|
check switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await conn.writeLp("Hello!")
|
|
check "Hello!" == string.fromBytes(await conn.readLp(1024))
|
|
await conn.close()
|
|
|
|
let connv6 = await switch3.dial(
|
|
switch1.peerInfo.peerId, @[switch1.peerInfo.addrs[1]], TestCodec
|
|
)
|
|
|
|
check switch1.isConnected(switch3.peerInfo.peerId)
|
|
check switch3.isConnected(switch1.peerInfo.peerId)
|
|
|
|
await connv6.writeLp("Hello!")
|
|
check "Hello!" == string.fromBytes(await connv6.readLp(1024))
|
|
await connv6.close()
|
|
|
|
await allFuturesThrowing(switch1.stop(), switch2.stop(), switch3.stop())
|
|
|
|
check not switch1.isConnected(switch2.peerInfo.peerId)
|
|
check not switch2.isConnected(switch1.peerInfo.peerId)
|
|
|
|
asyncTest "e2e dial dns4 address":
|
|
let resolver = MockResolver.new()
|
|
resolver.ipResponses[("localhost", false)] = @["127.0.0.1"]
|
|
resolver.ipResponses[("localhost", true)] = @["::1"]
|
|
|
|
let
|
|
srcSwitch = newStandardSwitch(nameResolver = resolver)
|
|
destSwitch = newStandardSwitch()
|
|
|
|
await destSwitch.start()
|
|
await srcSwitch.start()
|
|
|
|
let testAddr =
|
|
MultiAddress.init("/dns4/localhost/").tryGet() &
|
|
destSwitch.peerInfo.addrs[0][1].tryGet()
|
|
|
|
await srcSwitch.connect(destSwitch.peerInfo.peerId, @[testAddr])
|
|
check srcSwitch.isConnected(destSwitch.peerInfo.peerId)
|
|
|
|
await destSwitch.stop()
|
|
await srcSwitch.stop()
|
|
|
|
asyncTest "e2e dial dnsaddr with multiple transports":
|
|
let resolver = MockResolver.new()
|
|
|
|
let
|
|
wsAddress = MultiAddress.init("/ip4/127.0.0.1/tcp/0/ws").tryGet()
|
|
tcpAddress = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet()
|
|
|
|
srcTcpSwitch = newStandardSwitch(nameResolver = resolver)
|
|
srcWsSwitch = SwitchBuilder
|
|
.new()
|
|
.withAddress(wsAddress)
|
|
.withRng(crypto.newRng())
|
|
.withMplex()
|
|
.withTransport(
|
|
proc(upgr: Upgrade): Transport =
|
|
WsTransport.new(upgr)
|
|
)
|
|
.withNameResolver(resolver)
|
|
.withNoise()
|
|
.build()
|
|
|
|
destSwitch = SwitchBuilder
|
|
.new()
|
|
.withAddresses(@[tcpAddress, wsAddress])
|
|
.withRng(crypto.newRng())
|
|
.withMplex()
|
|
.withTransport(
|
|
proc(upgr: Upgrade): Transport =
|
|
WsTransport.new(upgr)
|
|
)
|
|
.withTcpTransport()
|
|
.withNoise()
|
|
.build()
|
|
|
|
await destSwitch.start()
|
|
await srcWsSwitch.start()
|
|
|
|
resolver.txtResponses["_dnsaddr.test.io"] =
|
|
@[
|
|
"dnsaddr=/dns4/localhost" & $destSwitch.peerInfo.addrs[0][1 ..^ 1].tryGet() &
|
|
"/p2p/" & $destSwitch.peerInfo.peerId,
|
|
"dnsaddr=/dns4/localhost" & $destSwitch.peerInfo.addrs[1][1 ..^ 1].tryGet(),
|
|
]
|
|
resolver.ipResponses[("localhost", false)] = @["127.0.0.1"]
|
|
|
|
let testAddr = MultiAddress.init("/dnsaddr/test.io/").tryGet()
|
|
|
|
await srcTcpSwitch.connect(destSwitch.peerInfo.peerId, @[testAddr])
|
|
check srcTcpSwitch.isConnected(destSwitch.peerInfo.peerId)
|
|
|
|
await srcWsSwitch.connect(destSwitch.peerInfo.peerId, @[testAddr])
|
|
check srcWsSwitch.isConnected(destSwitch.peerInfo.peerId)
|
|
|
|
await destSwitch.stop()
|
|
await srcWsSwitch.stop()
|
|
await srcTcpSwitch.stop()
|
|
|
|
asyncTest "mount unstarted protocol":
|
|
proc handle(conn: Connection, proto: string) {.async.} =
|
|
check "test123" == string.fromBytes(await conn.readLp(1024))
|
|
await conn.writeLp("test456")
|
|
await conn.close()
|
|
|
|
let
|
|
src = newStandardSwitch()
|
|
dst = newStandardSwitch()
|
|
testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
await src.start()
|
|
await dst.start()
|
|
expect LPError:
|
|
dst.mount(testProto)
|
|
await testProto.start()
|
|
dst.mount(testProto)
|
|
|
|
let conn = await src.dial(dst.peerInfo.peerId, dst.peerInfo.addrs, TestCodec)
|
|
await conn.writeLp("test123")
|
|
check "test456" == string.fromBytes(await conn.readLp(1024))
|
|
await conn.close()
|
|
await src.stop()
|
|
await dst.stop()
|
|
|
|
asyncTest "switch failing to start stops properly":
|
|
let switch = newStandardSwitch(
|
|
addrs =
|
|
@[
|
|
MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet(),
|
|
MultiAddress.init("/ip4/1.1.1.1/tcp/0").tryGet(),
|
|
]
|
|
)
|
|
|
|
expect LPError:
|
|
await switch.start()
|
|
# test is that this doesn't leak
|
|
|
|
asyncTest "starting two times does not crash":
|
|
let switch =
|
|
newStandardSwitch(addrs = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()])
|
|
|
|
await switch.start()
|
|
await switch.start()
|
|
|
|
await allFuturesThrowing(switch.stop())
|