feat: add hole-punching interop tests (#998)
This commit is contained in:
parent
2ede0fa40c
commit
3be681ec4d
|
@ -52,3 +52,17 @@ jobs:
|
||||||
with:
|
with:
|
||||||
test-filter: nim-libp2p-head
|
test-filter: nim-libp2p-head
|
||||||
extra-versions: ${{ github.workspace }}/test_head.json
|
extra-versions: ${{ github.workspace }}/test_head.json
|
||||||
|
|
||||||
|
run-hole-punching-interop:
|
||||||
|
name: Run hole-punching interoperability tests
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Build image
|
||||||
|
run: docker buildx build --load -t nim-libp2p-head -f tests/hole-punching-interop/Dockerfile .
|
||||||
|
- name: Run tests
|
||||||
|
uses: libp2p/test-plans/.github/actions/run-interop-hole-punch-test@master
|
||||||
|
with:
|
||||||
|
test-filter: nim-libp2p-head
|
||||||
|
extra-versions: ${{ github.workspace }}/tests/hole-punching-interop/version.json
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# syntax=docker/dockerfile:1.5-labs
|
||||||
|
FROM nimlang/nim:1.6.14 as builder
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
COPY .pinned libp2p.nimble nim-libp2p/
|
||||||
|
|
||||||
|
RUN cd nim-libp2p && nimble install_pinned && nimble install redis -y
|
||||||
|
|
||||||
|
COPY . nim-libp2p/
|
||||||
|
|
||||||
|
RUN cd nim-libp2p && nim c --skipParentCfg --NimblePath:./nimbledeps/pkgs -d:chronicles_log_level=DEBUG -d:chronicles_default_output_device=stderr -d:release --threads:off --skipProjCfg -o:hole-punching-tests ./tests/hole-punching-interop/hole_punching.nim
|
||||||
|
|
||||||
|
FROM --platform=linux/amd64 debian:bookworm-slim
|
||||||
|
RUN --mount=type=cache,target=/var/cache/apt apt-get update && apt-get install -y dnsutils jq curl tcpdump iproute2
|
||||||
|
COPY --from=builder /workspace/nim-libp2p/hole-punching-tests /usr/bin/hole-punch-client
|
||||||
|
ENV RUST_BACKTRACE=1
|
|
@ -0,0 +1,114 @@
|
||||||
|
import std/[os, options, strformat]
|
||||||
|
import redis
|
||||||
|
import chronos, chronicles
|
||||||
|
import ../../libp2p/[builders,
|
||||||
|
switch,
|
||||||
|
observedaddrmanager,
|
||||||
|
services/hpservice,
|
||||||
|
services/autorelayservice,
|
||||||
|
protocols/connectivity/autonat/client as aclient,
|
||||||
|
protocols/connectivity/relay/client as rclient,
|
||||||
|
protocols/connectivity/relay/relay,
|
||||||
|
protocols/connectivity/autonat/service,
|
||||||
|
protocols/ping]
|
||||||
|
import ../stubs/autonatclientstub
|
||||||
|
|
||||||
|
proc createSwitch(r: Relay = nil, hpService: Service = nil): Switch =
|
||||||
|
let rng = newRng()
|
||||||
|
var builder = SwitchBuilder.new()
|
||||||
|
.withRng(rng)
|
||||||
|
.withAddresses(@[ MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet() ])
|
||||||
|
.withObservedAddrManager(ObservedAddrManager.new(maxSize = 1, minCount = 1))
|
||||||
|
.withTcpTransport({ServerFlags.TcpNoDelay})
|
||||||
|
.withYamux()
|
||||||
|
.withAutonat()
|
||||||
|
.withNoise()
|
||||||
|
|
||||||
|
if hpService != nil:
|
||||||
|
builder = builder.withServices(@[hpService])
|
||||||
|
|
||||||
|
if r != nil:
|
||||||
|
builder = builder.withCircuitRelay(r)
|
||||||
|
|
||||||
|
let s = builder.build()
|
||||||
|
s.mount(Ping.new(rng=rng))
|
||||||
|
return s
|
||||||
|
|
||||||
|
proc main() {.async.} =
|
||||||
|
try:
|
||||||
|
let relayClient = RelayClient.new()
|
||||||
|
let autoRelayService = AutoRelayService.new(1, relayClient, nil, newRng())
|
||||||
|
let autonatClientStub = AutonatClientStub.new(expectedDials = 1)
|
||||||
|
autonatClientStub.answer = NotReachable
|
||||||
|
let autonatService = AutonatService.new(autonatClientStub, newRng(), maxQueueSize = 1)
|
||||||
|
let hpservice = HPService.new(autonatService, autoRelayService)
|
||||||
|
|
||||||
|
let
|
||||||
|
isListener = getEnv("MODE") == "listen"
|
||||||
|
switch = createSwitch(relayClient, hpservice)
|
||||||
|
auxSwitch = createSwitch()
|
||||||
|
redisClient = open("redis", 6379.Port)
|
||||||
|
|
||||||
|
debug "Connected to redis"
|
||||||
|
|
||||||
|
await switch.start()
|
||||||
|
await auxSwitch.start()
|
||||||
|
|
||||||
|
let relayAddr =
|
||||||
|
try:
|
||||||
|
redisClient.bLPop(@["RELAY_TCP_ADDRESS"], 0)
|
||||||
|
except Exception as e:
|
||||||
|
raise newException(CatchableError, e.msg)
|
||||||
|
|
||||||
|
# This is necessary to make the autonat service work. It will ask this peer for our reachability which the autonat
|
||||||
|
# client stub will answer NotReachable.
|
||||||
|
await switch.connect(auxSwitch.peerInfo.peerId, auxSwitch.peerInfo.addrs)
|
||||||
|
|
||||||
|
# Wait for autonat to be NotReachable
|
||||||
|
while autonatService.networkReachability != NetworkReachability.NotReachable:
|
||||||
|
await sleepAsync(100.milliseconds)
|
||||||
|
|
||||||
|
# This will trigger the autonat relay service to make a reservation.
|
||||||
|
let relayMA = MultiAddress.init(relayAddr[1]).tryGet()
|
||||||
|
debug "Got relay address", relayMA
|
||||||
|
let relayId = await switch.connect(relayMA)
|
||||||
|
debug "Connected to relay", relayId
|
||||||
|
|
||||||
|
# Wait for our relay address to be published
|
||||||
|
while switch.peerInfo.addrs.len == 0:
|
||||||
|
await sleepAsync(100.milliseconds)
|
||||||
|
|
||||||
|
if isListener:
|
||||||
|
let listenerPeerId = switch.peerInfo.peerId
|
||||||
|
discard redisClient.rPush("LISTEN_CLIENT_PEER_ID", $listenerPeerId)
|
||||||
|
debug "Pushed listener client peer id to redis", listenerPeerId
|
||||||
|
|
||||||
|
# Nothing to do anymore, wait to be killed
|
||||||
|
await sleepAsync(2.minutes)
|
||||||
|
else:
|
||||||
|
let listenerId =
|
||||||
|
try:
|
||||||
|
PeerId.init(redisClient.bLPop(@["LISTEN_CLIENT_PEER_ID"], 0)[1]).tryGet()
|
||||||
|
except Exception as e:
|
||||||
|
raise newException(CatchableError, e.msg)
|
||||||
|
|
||||||
|
debug "Got listener peer id", listenerId
|
||||||
|
let listenerRelayAddr = MultiAddress.init($relayMA & "/p2p-circuit").tryGet()
|
||||||
|
|
||||||
|
debug "Dialing listener relay address", listenerRelayAddr
|
||||||
|
await switch.connect(listenerId, @[listenerRelayAddr])
|
||||||
|
|
||||||
|
# wait for hole-punching to complete in the background
|
||||||
|
await sleepAsync(5000.milliseconds)
|
||||||
|
|
||||||
|
let conn = switch.connManager.selectMuxer(listenerId).connection
|
||||||
|
let channel = await switch.dial(listenerId, @[listenerRelayAddr], PingCodec)
|
||||||
|
let delay = await Ping.new().ping(channel)
|
||||||
|
await allFuturesThrowing(channel.close(), conn.close(), switch.stop(), auxSwitch.stop())
|
||||||
|
echo &"""{{"rtt_to_holepunched_peer_millis":{delay.millis}}}"""
|
||||||
|
quit(0)
|
||||||
|
except CatchableError as e:
|
||||||
|
error "Unexpected error", msg = e.msg
|
||||||
|
|
||||||
|
discard waitFor(main().withTimeout(4.minutes))
|
||||||
|
quit(1)
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"id": "nim-libp2p-head",
|
||||||
|
"containerImageID": "nim-libp2p-head",
|
||||||
|
"transports": [
|
||||||
|
"tcp"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue