Skip to content

Circuit Relay example

Circuit Relay can be used when a node cannot reach another node directly, but can reach it through another node (the Relay).

That may happen because of NAT, Firewalls, or incompatible transports.

More informations here.

import chronos, stew/byteutils
import libp2p, libp2p/protocols/connectivity/relay/[relay, client]

# Helper to create a circuit relay node
proc createCircuitRelaySwitch(r: Relay): Switch =
  SwitchBuilder
  .new()
  .withRng(newRng())
  .withAddresses(@[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()])
  .withTcpTransport()
  .withMplex()
  .withNoise()
  .withCircuitRelay(r)
  .build()

proc main() {.async.} =
  # Create a custom protocol
  let customProtoCodec = "/test"
  var proto = new LPProtocol
  proto.codec = customProtoCodec
  proto.handler = proc(conn: Connection, proto: string) {.async.} =
    var msg = string.fromBytes(await conn.readLp(1024))
    echo "1 - Dst Received: ", msg
    assert "test1" == msg
    await conn.writeLp("test2")
    msg = string.fromBytes(await conn.readLp(1024))
    echo "2 - Dst Received: ", msg
    assert "test3" == msg
    await conn.writeLp("test4")

  let
    relay = Relay.new()
    clSrc = RelayClient.new()
    clDst = RelayClient.new()

    # Create three hosts, enable relay client on two of them.
    # The third one can relay connections for other peers.
    # RelayClient can use a relay, Relay is a relay.
    swRel = createCircuitRelaySwitch(relay)
    swSrc = createCircuitRelaySwitch(clSrc)
    swDst = createCircuitRelaySwitch(clDst)

  swDst.mount(proto)

  await swRel.start()
  await swSrc.start()
  await swDst.start()

  let
    # Create a relay address to swDst using swRel as the relay
    addrs = MultiAddress
      .init(
        $swRel.peerInfo.addrs[0] & "/p2p/" & $swRel.peerInfo.peerId & "/p2p-circuit"
      )
      .get()

  # Connect Dst to the relay
  await swDst.connect(swRel.peerInfo.peerId, swRel.peerInfo.addrs)

  # Dst reserve a slot on the relay.
  let rsvp = await clDst.reserve(swRel.peerInfo.peerId, swRel.peerInfo.addrs)

  # Src dial Dst using the relay
  let conn = await swSrc.dial(swDst.peerInfo.peerId, @[addrs], customProtoCodec)

  await conn.writeLp("test1")
  var msg = string.fromBytes(await conn.readLp(1024))
  echo "1 - Src Received: ", msg
  assert "test2" == msg
  await conn.writeLp("test3")
  msg = string.fromBytes(await conn.readLp(1024))
  echo "2 - Src Received: ", msg
  assert "test4" == msg

  await relay.stop()
  await allFutures(swSrc.stop(), swDst.stop(), swRel.stop())

waitFor(main())