# 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. {.push raises: [].} import std/sequtils import stew/results import chronos, chronicles import core import ../../protocol, ../../../stream/connection, ../../../switch, ../../../utils/future export DcutrError type DcutrClient* = ref object connectTimeout: Duration maxDialableAddrs: int logScope: topics = "libp2p dcutrclient" proc new*(T: typedesc[DcutrClient], connectTimeout = 15.seconds, maxDialableAddrs = 8): T = return T(connectTimeout: connectTimeout, maxDialableAddrs: maxDialableAddrs) proc startSync*(self: DcutrClient, switch: Switch, remotePeerId: PeerId, addrs: seq[MultiAddress]) {.async.} = logScope: peerId = switch.peerInfo.peerId var peerDialableAddrs: seq[MultiAddress] stream: Connection try: var ourDialableAddrs = getHolePunchableAddrs(addrs) if ourDialableAddrs.len == 0: debug "Dcutr initiator has no supported dialable addresses. Aborting Dcutr.", addrs return stream = await switch.dial(remotePeerId, DcutrCodec) await stream.send(MsgType.Connect, addrs) debug "Dcutr initiator has sent a Connect message." let rttStart = Moment.now() let connectAnswer = DcutrMsg.decode(await stream.readLp(1024)) peerDialableAddrs = getHolePunchableAddrs(connectAnswer.addrs) if peerDialableAddrs.len == 0: debug "Dcutr receiver has no supported dialable addresses to connect to. Aborting Dcutr.", addrs=connectAnswer.addrs return let rttEnd = Moment.now() debug "Dcutr initiator has received a Connect message back.", connectAnswer let halfRtt = (rttEnd - rttStart) div 2'i64 await stream.send(MsgType.Sync, @[]) debug "Dcutr initiator has sent a Sync message." await sleepAsync(halfRtt) if peerDialableAddrs.len > self.maxDialableAddrs: peerDialableAddrs = peerDialableAddrs[0..