From a4d7d07d68542b80b8a669af1386498480d5fb44 Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Thu, 9 Nov 2023 08:54:06 +0000 Subject: [PATCH] refactor!: extract DCUtR into separate module (#2220) BREAKING CHANGE: imports from `libp2p/dcutr` now need to be from `@libp2p/dcutr` --- .release-please.json | 1 + doc/migrations/v0.46-v1.0.0.md | 31 +++ packages/interface/package.json | 6 +- packages/interface/src/errors.ts | 1 + packages/libp2p/package.json | 4 - packages/libp2p/test/dcutr/dcutr.node.ts | 177 ------------------ packages/logger/src/index.ts | 3 +- packages/pnet/package.json | 2 +- packages/protocol-dcutr/LICENSE | 4 + packages/protocol-dcutr/LICENSE-APACHE | 5 + packages/protocol-dcutr/LICENSE-MIT | 19 ++ packages/protocol-dcutr/README.md | 88 +++++++++ packages/protocol-dcutr/package.json | 66 +++++++ .../src/dcutr => protocol-dcutr/src}/dcutr.ts | 69 ++++--- .../src/dcutr => protocol-dcutr/src}/index.ts | 8 +- .../src}/pb/message.proto | 0 .../src}/pb/message.ts | 0 .../src/dcutr => protocol-dcutr/src}/utils.ts | 0 .../test}/utils.spec.ts | 2 +- packages/protocol-dcutr/tsconfig.json | 21 +++ packages/protocol-dcutr/typedoc.json | 5 + packages/protocol-identify/README.md | 2 - packages/protocol-ping/README.md | 2 - 23 files changed, 286 insertions(+), 230 deletions(-) delete mode 100644 packages/libp2p/test/dcutr/dcutr.node.ts create mode 100644 packages/protocol-dcutr/LICENSE create mode 100644 packages/protocol-dcutr/LICENSE-APACHE create mode 100644 packages/protocol-dcutr/LICENSE-MIT create mode 100644 packages/protocol-dcutr/README.md create mode 100644 packages/protocol-dcutr/package.json rename packages/{libp2p/src/dcutr => protocol-dcutr/src}/dcutr.ts (83%) rename packages/{libp2p/src/dcutr => protocol-dcutr/src}/index.ts (92%) rename packages/{libp2p/src/dcutr => protocol-dcutr/src}/pb/message.proto (100%) rename packages/{libp2p/src/dcutr => protocol-dcutr/src}/pb/message.ts (100%) rename packages/{libp2p/src/dcutr => protocol-dcutr/src}/utils.ts (100%) rename packages/{libp2p/test/dcutr => protocol-dcutr/test}/utils.spec.ts (96%) create mode 100644 packages/protocol-dcutr/tsconfig.json create mode 100644 packages/protocol-dcutr/typedoc.json diff --git a/.release-please.json b/.release-please.json index 9acf79f74b..a29048d9c9 100644 --- a/.release-please.json +++ b/.release-please.json @@ -21,6 +21,7 @@ "packages/peer-record": {}, "packages/peer-store": {}, "packages/protocol-autonat": {}, + "packages/protocol-dcutr": {}, "packages/protocol-identify": {}, "packages/protocol-perf": {}, "packages/protocol-ping": {}, diff --git a/doc/migrations/v0.46-v1.0.0.md b/doc/migrations/v0.46-v1.0.0.md index a5dbe4aa04..787d993009 100644 --- a/doc/migrations/v0.46-v1.0.0.md +++ b/doc/migrations/v0.46-v1.0.0.md @@ -8,6 +8,7 @@ A migration guide for refactoring your application code from libp2p `v0.46` to ` - [AutoNAT](#autonat) - [Ping](#ping) - [Identify](#identify) +- [DCUtR](#dcutr) - [KeyChain](#keychain) - [UPnPNat](#upnpnat) - [Pnet](#pnet) @@ -103,6 +104,36 @@ const node = await createLibp2p({ }) ``` +## DCUtR + +The DCUtR service is now published in its own package. + +**Before** + +```ts +import { createLibp2p } from 'libp2p' +import { dcutrService } from 'libp2p/dcutr' + +const node = await createLibp2p({ + services: { + identify: dcutrService() + } +}) +``` + +**After** + +```ts +import { createLibp2p } from 'libp2p' +import { dcutr } from '@libp2p/dcutr' + +const node = await createLibp2p({ + services: { + dcutr: dcutr() + } +}) +``` + ## KeyChain The KeyChain object is no longer included on Libp2p and must be instantiated explicitly if desired. diff --git a/packages/interface/package.json b/packages/interface/package.json index 20f5c4cea5..d1304629bd 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -39,9 +39,6 @@ "!dist/test", "!**/*.tsbuildinfo" ], - "browser": { - "events": "./dist/src/events.browser.js" - }, "exports": { ".": { "types": "./dist/src/index.d.ts", @@ -178,5 +175,8 @@ "it-drain": "^3.0.3", "sinon": "^17.0.0", "sinon-ts": "^2.0.0" + }, + "browser": { + "events": "./dist/src/events.browser.js" } } diff --git a/packages/interface/src/errors.ts b/packages/interface/src/errors.ts index 9758522287..8a1a4dafeb 100644 --- a/packages/interface/src/errors.ts +++ b/packages/interface/src/errors.ts @@ -71,3 +71,4 @@ export class InvalidCryptoTransmissionError extends Error { export const ERR_TIMEOUT = 'ERR_TIMEOUT' export const ERR_INVALID_PARAMETERS = 'ERR_INVALID_PARAMETERS' export const ERR_NOT_FOUND = 'ERR_NOT_FOUND' +export const ERR_INVALID_MESSAGE = 'ERR_INVALID_MESSAGE' diff --git a/packages/libp2p/package.json b/packages/libp2p/package.json index 47b2965e1d..cb316d419a 100644 --- a/packages/libp2p/package.json +++ b/packages/libp2p/package.json @@ -52,10 +52,6 @@ "types": "./dist/src/circuit-relay/index.d.ts", "import": "./dist/src/circuit-relay/index.js" }, - "./dcutr": { - "types": "./dist/src/dcutr/index.d.ts", - "import": "./dist/src/dcutr/index.js" - }, "./fetch": { "types": "./dist/src/fetch/index.d.ts", "import": "./dist/src/fetch/index.js" diff --git a/packages/libp2p/test/dcutr/dcutr.node.ts b/packages/libp2p/test/dcutr/dcutr.node.ts deleted file mode 100644 index c5e3102bff..0000000000 --- a/packages/libp2p/test/dcutr/dcutr.node.ts +++ /dev/null @@ -1,177 +0,0 @@ -/* eslint-env mocha */ - -import { identify } from '@libp2p/identify' -import { multiaddr } from '@multiformats/multiaddr' -import { expect } from 'aegir/chai' -import delay from 'delay' -import pRetry from 'p-retry' -import { circuitRelayServer, type CircuitRelayService } from '../../src/circuit-relay/index.js' -import { dcutrService } from '../../src/dcutr/index.js' -import { createLibp2pNode } from '../../src/libp2p.js' -import { usingAsRelay } from '../circuit-relay/utils.js' -import { createBaseOptions } from '../fixtures/base-options.js' -import type { Libp2p } from '@libp2p/interface' - -const RELAY_PORT = 47330 -const LOCAL_PORT = 47331 -const REMOTE_PORT = 47332 - -describe('dcutr', () => { - let relay: Libp2p<{ identify: unknown, relay: CircuitRelayService }> - let libp2pA: Libp2p<{ identify: unknown, dcutr: unknown }> - let libp2pB: Libp2p<{ identify: unknown, dcutr: unknown }> - - async function waitForOnlyDirectConnections (): Promise { - await pRetry(async () => { - const connections = libp2pA.getConnections(libp2pB.peerId) - const onlyDirect = connections.filter(conn => !conn.transient) - - if (onlyDirect.length === connections.length) { - // all connections are direct - return true - } - - // wait a bit before trying again - await delay(1000) - - throw new Error('Did not upgrade connection') - }, { - retries: 10 - }) - } - - beforeEach(async () => { - relay = await createLibp2pNode(createBaseOptions({ - addresses: { - listen: [`/ip4/0.0.0.0/tcp/${RELAY_PORT}`] - }, - services: { - identify: identify(), - relay: circuitRelayServer() - } - })) - - await relay.start() - }) - - afterEach(async () => { - if (relay != null) { - await relay.stop() - } - }) - - describe('unilateral connection upgrade', () => { - beforeEach(async () => { - libp2pA = await createLibp2pNode(createBaseOptions({ - addresses: { - // A should have a publicly dialable address - announce: [`/dns4/localhost/tcp/${LOCAL_PORT}`], - listen: [`/ip4/0.0.0.0/tcp/${LOCAL_PORT}`] - }, - services: { - identify: identify(), - dcutr: dcutrService() - } - })) - libp2pB = await createLibp2pNode(createBaseOptions({ - addresses: { - listen: [ - `/ip4/0.0.0.0/tcp/${REMOTE_PORT}`, - `/ip4/127.0.0.1/tcp/${RELAY_PORT}/p2p/${relay.peerId}/p2p-circuit` - ] - }, - services: { - identify: identify(), - dcutr: dcutrService() - } - })) - - await libp2pA.start() - await libp2pB.start() - - // wait for B to have a relay address - await usingAsRelay(libp2pB, relay) - }) - - afterEach(async () => { - if (libp2pA != null) { - await libp2pA.stop() - } - - if (libp2pB != null) { - await libp2pB.stop() - } - }) - - it('should upgrade the connection when A has dialed B via a relay but also has a publicly dialable address', async () => { - const relayedAddress = multiaddr(`/ip4/127.0.0.1/tcp/${RELAY_PORT}/p2p/${relay.peerId}/p2p-circuit/p2p/${libp2pB.peerId}`) - const connection = await libp2pA.dial(relayedAddress) - - // connection should be transient - expect(connection).to.have.property('transient', true) - - // wait for DCUtR unilateral upgrade - await waitForOnlyDirectConnections() - - // should have closed the relayed connection - expect(libp2pA.getConnections(libp2pB.peerId)).to.have.lengthOf(1, 'had multiple connections to remote peer') - }) - }) - - // TODO: how to test this? - describe.skip('DCUtR connection upgrade', () => { - beforeEach(async () => { - libp2pA = await createLibp2pNode(createBaseOptions({ - addresses: { - listen: [`/ip4/0.0.0.0/tcp/${LOCAL_PORT}`] - }, - services: { - identify: identify(), - dcutr: dcutrService() - } - })) - libp2pB = await createLibp2pNode(createBaseOptions({ - addresses: { - listen: [ - `/ip4/0.0.0.0/tcp/${REMOTE_PORT}`, - `/ip4/127.0.0.1/tcp/${RELAY_PORT}/p2p/${relay.peerId}/p2p-circuit` - ] - }, - services: { - identify: identify(), - dcutr: dcutrService() - } - })) - - await libp2pA.start() - await libp2pB.start() - - // wait for B to have a relay address - await usingAsRelay(libp2pB, relay) - }) - - afterEach(async () => { - if (libp2pA != null) { - await libp2pA.stop() - } - - if (libp2pB != null) { - await libp2pB.stop() - } - }) - - it('should perform connection upgrade', async () => { - const relayedAddress = multiaddr(`/ip4/127.0.0.1/tcp/${RELAY_PORT}/p2p/${relay.peerId}/p2p-circuit/p2p/${libp2pB.peerId}`) - const connection = await libp2pA.dial(relayedAddress) - - // connection should be transient - expect(connection).to.have.property('transient', true) - - // wait for DCUtR unilateral upgrade - await waitForOnlyDirectConnections() - - // should have closed the relayed connection - expect(libp2pA.getConnections(libp2pB.peerId)).to.have.lengthOf(1, 'had multiple connections to remote peer') - }) - }) -}) diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index be3572c218..3dce448a66 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,8 +1,7 @@ /** * @packageDocumentation * - * A logger for libp2p based on the venerable [debug](https://www.npmjs.com/package/debug) - * module. + * A logger for libp2p based on the venerable [debug](https://www.npmjs.com/package/debug) module. * * @example * diff --git a/packages/pnet/package.json b/packages/pnet/package.json index a568b2e111..7ba94a5b31 100644 --- a/packages/pnet/package.json +++ b/packages/pnet/package.json @@ -1,6 +1,6 @@ { "name": "@libp2p/pnet", - "version": "1.0.0", + "version": "0.0.0", "description": "Implementation of Connection protection management via a shared secret", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/libp2p/js-libp2p/tree/master/packages/pnet#readme", diff --git a/packages/protocol-dcutr/LICENSE b/packages/protocol-dcutr/LICENSE new file mode 100644 index 0000000000..20ce483c86 --- /dev/null +++ b/packages/protocol-dcutr/LICENSE @@ -0,0 +1,4 @@ +This project is dual licensed under MIT and Apache-2.0. + +MIT: https://www.opensource.org/licenses/mit +Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/protocol-dcutr/LICENSE-APACHE b/packages/protocol-dcutr/LICENSE-APACHE new file mode 100644 index 0000000000..14478a3b60 --- /dev/null +++ b/packages/protocol-dcutr/LICENSE-APACHE @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/packages/protocol-dcutr/LICENSE-MIT b/packages/protocol-dcutr/LICENSE-MIT new file mode 100644 index 0000000000..72dc60d84b --- /dev/null +++ b/packages/protocol-dcutr/LICENSE-MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/protocol-dcutr/README.md b/packages/protocol-dcutr/README.md new file mode 100644 index 0000000000..204e28fb00 --- /dev/null +++ b/packages/protocol-dcutr/README.md @@ -0,0 +1,88 @@ +[![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) +[![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) +[![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) +[![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p/main.yml?branch=master\&style=flat-square)](https://github.com/libp2p/js-libp2p/actions/workflows/main.yml?query=branch%3Amaster) + +> Implementation of the DCUtR Protocol + +# About + +Direct Connection Upgrade through Relay (DCUtR) is a protocol that allows two +nodes to connect to each other who would otherwise be prevented doing so due +to being behind NATed connections or firewalls. + +The protocol involves making a relayed connection between the two peers and +using the relay to synchronise connection timings so that they dial each other +at precisely the same moment. + +## Example + +```ts +import { createLibp2p } from 'libp2p' +import { circuitRelayTransport } from 'libp2p/circuit-relay' +import { tcp } from '@libp2p/tcp' +import { identify } from '@libp2p/identify' +import { dcutr } from '@libp2p/dcutr' + +const node = await createLibp2p({ + transports: [ + circuitRelayTransport(), + tcp() + ], + services: { + identify: identify(), + dcutr: dcutr() + } +}) + +// QmTarget is a peer that is behind a NAT, supports TCP and has a relay +// reservation +await node.dial('/ip4/.../p2p/QmRelay/p2p-circuit/p2p/QmTarget') + +// after a while the connection should automatically get upgraded to a +// direct connection (e.g. non-transient) +while (true) { + const connections = node.getConnections() + + if (connections.find(conn => conn.transient === false)) { + console.info('have direct connection') + break + } else { + console.info('have relayed connection') + + // wait a few seconds to see if it's succeeded yet + await new Promise((resolve) => { + setTimeout(() => resolve(), 5000) + }) + } +} +``` + +# Install + +```console +$ npm i @libp2p/dcutr +``` + +## Browser ` +``` + +# API Docs + +- + +# License + +Licensed under either of + +- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](LICENSE-MIT) / ) + +# Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/protocol-dcutr/package.json b/packages/protocol-dcutr/package.json new file mode 100644 index 0000000000..d83b6dbfa7 --- /dev/null +++ b/packages/protocol-dcutr/package.json @@ -0,0 +1,66 @@ +{ + "name": "@libp2p/dcutr", + "version": "0.0.0", + "description": "Implementation of the DCUtR Protocol", + "license": "Apache-2.0 OR MIT", + "homepage": "https://github.com/libp2p/js-libp2p/tree/master/packages/protocol-dcutr#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/libp2p/js-libp2p.git" + }, + "bugs": { + "url": "https://github.com/libp2p/js-libp2p/issues" + }, + "type": "module", + "types": "./dist/src/index.d.ts", + "files": [ + "src", + "dist", + "!dist/test", + "!**/*.tsbuildinfo" + ], + "exports": { + ".": { + "types": "./dist/src/index.d.ts", + "import": "./dist/src/index.js" + } + }, + "eslintConfig": { + "extends": "ipfs", + "parserOptions": { + "project": true, + "sourceType": "module" + } + }, + "scripts": { + "start": "node dist/src/main.js", + "build": "aegir build", + "test": "aegir test", + "clean": "aegir clean", + "generate": "protons ./src/pb/index.proto", + "lint": "aegir lint", + "test:chrome": "aegir test -t browser --cov", + "test:chrome-webworker": "aegir test -t webworker", + "test:firefox": "aegir test -t browser -- --browser firefox", + "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", + "test:node": "aegir test -t node --cov", + "dep-check": "aegir dep-check" + }, + "dependencies": { + "@libp2p/interface": "^0.1.2", + "@libp2p/interface-internal": "^0.1.5", + "@multiformats/multiaddr": "^12.1.5", + "@multiformats/multiaddr-matcher": "^1.1.0", + "delay": "^6.0.0", + "it-protobuf-stream": "^1.0.2", + "private-ip": "^3.0.0", + "protons-runtime": "^5.0.0", + "uint8arraylist": "^2.4.3" + }, + "devDependencies": { + "aegir": "^41.0.2", + "protons": "^7.3.0", + "sinon": "^17.0.0", + "sinon-ts": "^2.0.0" + } +} diff --git a/packages/libp2p/src/dcutr/dcutr.ts b/packages/protocol-dcutr/src/dcutr.ts similarity index 83% rename from packages/libp2p/src/dcutr/dcutr.ts rename to packages/protocol-dcutr/src/dcutr.ts index ce9146d593..dde57760a1 100644 --- a/packages/libp2p/src/dcutr/dcutr.ts +++ b/packages/protocol-dcutr/src/dcutr.ts @@ -1,13 +1,12 @@ -import { CodeError } from '@libp2p/interface/errors' -import { logger } from '@libp2p/logger' +import { CodeError, ERR_INVALID_MESSAGE } from '@libp2p/interface/errors' import { type Multiaddr, multiaddr } from '@multiformats/multiaddr' import delay from 'delay' import { pbStream } from 'it-protobuf-stream' -import { codes } from '../errors.js' import { HolePunch } from './pb/message.js' import { isPublicAndDialable } from './utils.js' import { multicodec } from './index.js' import type { DCUtRServiceComponents, DCUtRServiceInit } from './index.js' +import type { Logger } from '@libp2p/interface' import type { Connection, Stream } from '@libp2p/interface/connection' import type { PeerStore } from '@libp2p/interface/peer-store' import type { Startable } from '@libp2p/interface/startable' @@ -16,8 +15,6 @@ import type { ConnectionManager } from '@libp2p/interface-internal/connection-ma import type { Registrar } from '@libp2p/interface-internal/registrar' import type { TransportManager } from '@libp2p/interface-internal/src/transport-manager/index.js' -const log = logger('libp2p:dcutr') - // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#rpc-messages const MAX_DCUTR_MESSAGE_SIZE = 1024 * 4 // ensure the dial has a high priority to jump to the head of the dial queue @@ -44,8 +41,10 @@ export class DefaultDCUtRService implements Startable { private readonly addressManager: AddressManager private readonly transportManager: TransportManager private topologyId?: string + readonly #log: Logger constructor (components: DCUtRServiceComponents, init: DCUtRServiceInit) { + this.#log = components.logger.forComponent('libp2p:dcutr') this.started = false this.peerStore = components.peerStore this.registrar = components.registrar @@ -85,14 +84,14 @@ export class DefaultDCUtRService implements Startable { this.upgradeInbound(connection) .catch(err => { - log.error('error during outgoing DCUtR attempt', err) + this.#log.error('error during outgoing DCUtR attempt', err) }) } }) await this.registrar.handle(multicodec, (data) => { void this.handleIncomingUpgrade(data.stream, data.connection).catch(err => { - log.error('error during incoming DCUtR attempt', err) + this.#log.error('error during incoming DCUtR attempt', err) data.stream.abort(err) }) }, { @@ -151,38 +150,38 @@ export class DefaultDCUtRService implements Startable { // 2. B sends to A a Connect message containing its observed (and // possibly predicted) addresses from identify and starts a timer // to measure RTT of the relay connection. - log('B sending connect to %p', relayedConnection.remotePeer) + this.#log('B sending connect to %p', relayedConnection.remotePeer) const connectTimer = Date.now() await pb.write({ type: HolePunch.Type.CONNECT, observedAddresses: this.addressManager.getAddresses().map(ma => ma.bytes) }, options) - log('B receiving connect from %p', relayedConnection.remotePeer) + this.#log('B receiving connect from %p', relayedConnection.remotePeer) // 4. Upon receiving the Connect, B sends a Sync message const connect = await pb.read(options) if (connect.type !== HolePunch.Type.CONNECT) { - log('A sent wrong message type') - throw new CodeError('DCUtR message type was incorrect', codes.ERR_INVALID_MESSAGE) + this.#log('A sent wrong message type') + throw new CodeError('DCUtR message type was incorrect', ERR_INVALID_MESSAGE) } const multiaddrs = this.getDialableMultiaddrs(connect.observedAddresses) if (multiaddrs.length === 0) { - log('A did not have any dialable multiaddrs') - throw new CodeError('DCUtR connect message had no multiaddrs', codes.ERR_INVALID_MESSAGE) + this.#log('A did not have any dialable multiaddrs') + throw new CodeError('DCUtR connect message had no multiaddrs', ERR_INVALID_MESSAGE) } const rtt = Date.now() - connectTimer - log('A sending sync, rtt %dms', rtt) + this.#log('A sending sync, rtt %dms', rtt) await pb.write({ type: HolePunch.Type.SYNC, observedAddresses: [] }, options) - log('A waiting for half RTT') + this.#log('A waiting for half RTT') // ..and starts a timer for half the RTT measured from the time between // sending the initial Connect and receiving the response await delay(rtt / 2) @@ -191,19 +190,19 @@ export class DefaultDCUtRService implements Startable { // now we only have tcp support // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol - log('B dialing', multiaddrs) + this.#log('B dialing', multiaddrs) // Upon expiry of the timer, B dials the address to A. const conn = await this.connectionManager.openConnection(multiaddrs, { signal: options.signal, priority: DCUTR_DIAL_PRIORITY }) - log('DCUtR to %p succeeded to address %a, closing relayed connection', relayedConnection.remotePeer, conn.remoteAddr) + this.#log('DCUtR to %p succeeded to address %a, closing relayed connection', relayedConnection.remotePeer, conn.remoteAddr) await relayedConnection.close(options) break } catch (err: any) { - log.error('error while attempting DCUtR on attempt %d of %d', i + 1, this.retries, err) + this.#log.error('error while attempting DCUtR on attempt %d of %d', i + 1, this.retries, err) stream?.abort(err) if (i === this.retries) { @@ -248,7 +247,7 @@ export class DefaultDCUtRService implements Startable { const signal = AbortSignal.timeout(this.timeout) try { - log('attempting unilateral connection upgrade to %a', publicAddresses) + this.#log('attempting unilateral connection upgrade to %a', publicAddresses) // force-dial the multiaddr(s), otherwise `connectionManager.openConnection` // will return the existing relayed connection @@ -261,7 +260,7 @@ export class DefaultDCUtRService implements Startable { throw new Error('Could not open a new, non-transient, connection') } - log('unilateral connection upgrade to %p succeeded via %a, closing relayed connection', relayedConnection.remotePeer, connection.remoteAddr) + this.#log('unilateral connection upgrade to %p succeeded via %a, closing relayed connection', relayedConnection.remotePeer, connection.remoteAddr) await relayedConnection.close({ signal @@ -269,10 +268,10 @@ export class DefaultDCUtRService implements Startable { return true } catch (err) { - log.error('unilateral connection upgrade to %p on addresses %a failed', relayedConnection.remotePeer, publicAddresses, err) + this.#log.error('unilateral connection upgrade to %p on addresses %a failed', relayedConnection.remotePeer, publicAddresses, err) } } else { - log('peer %p has no public addresses, not attempting unilateral connection upgrade', relayedConnection.remotePeer) + this.#log('peer %p has no public addresses, not attempting unilateral connection upgrade', relayedConnection.remotePeer) } // no public addresses or failed to dial public addresses @@ -294,39 +293,39 @@ export class DefaultDCUtRService implements Startable { maxDataLength: MAX_DCUTR_MESSAGE_SIZE }).pb(HolePunch) - log('A receiving connect') + this.#log('A receiving connect') // 3. Upon receiving the Connect, A responds back with a Connect message // containing its observed (and possibly predicted) addresses. const connect = await pb.read(options) if (connect.type !== HolePunch.Type.CONNECT) { - log('B sent wrong message type') - throw new CodeError('DCUtR message type was incorrect', codes.ERR_INVALID_MESSAGE) + this.#log('B sent wrong message type') + throw new CodeError('DCUtR message type was incorrect', ERR_INVALID_MESSAGE) } if (connect.observedAddresses.length === 0) { - log('B sent no multiaddrs') - throw new CodeError('DCUtR connect message had no multiaddrs', codes.ERR_INVALID_MESSAGE) + this.#log('B sent no multiaddrs') + throw new CodeError('DCUtR connect message had no multiaddrs', ERR_INVALID_MESSAGE) } const multiaddrs = this.getDialableMultiaddrs(connect.observedAddresses) if (multiaddrs.length === 0) { - log('B had no dialable multiaddrs') - throw new CodeError('DCUtR connect message had no dialable multiaddrs', codes.ERR_INVALID_MESSAGE) + this.#log('B had no dialable multiaddrs') + throw new CodeError('DCUtR connect message had no dialable multiaddrs', ERR_INVALID_MESSAGE) } - log('A sending connect') + this.#log('A sending connect') await pb.write({ type: HolePunch.Type.CONNECT, observedAddresses: this.addressManager.getAddresses().map(ma => ma.bytes) }) - log('A receiving sync') + this.#log('A receiving sync') const sync = await pb.read(options) if (sync.type !== HolePunch.Type.SYNC) { - throw new CodeError('DCUtR message type was incorrect', codes.ERR_INVALID_MESSAGE) + throw new CodeError('DCUtR message type was incorrect', ERR_INVALID_MESSAGE) } // TODO: when we have a QUIC transport, the dial step is different - for @@ -334,17 +333,17 @@ export class DefaultDCUtRService implements Startable { // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol // Upon receiving the Sync, A immediately dials the address to B - log('A dialing', multiaddrs) + this.#log('A dialing', multiaddrs) const connection = await this.connectionManager.openConnection(multiaddrs, { signal: options.signal, priority: DCUTR_DIAL_PRIORITY, force: true }) - log('DCUtR to %p succeeded via %a, closing relayed connection', relayedConnection.remotePeer, connection.remoteAddr) + this.#log('DCUtR to %p succeeded via %a, closing relayed connection', relayedConnection.remotePeer, connection.remoteAddr) await relayedConnection.close(options) } catch (err: any) { - log.error('incoming DCUtR from %p failed', relayedConnection.remotePeer, err) + this.#log.error('incoming DCUtR from %p failed', relayedConnection.remotePeer, err) stream.abort(err) } finally { await stream.close(options) diff --git a/packages/libp2p/src/dcutr/index.ts b/packages/protocol-dcutr/src/index.ts similarity index 92% rename from packages/libp2p/src/dcutr/index.ts rename to packages/protocol-dcutr/src/index.ts index a6357eae84..fefa57740f 100644 --- a/packages/libp2p/src/dcutr/index.ts +++ b/packages/protocol-dcutr/src/index.ts @@ -16,7 +16,7 @@ * import { circuitRelayTransport } from 'libp2p/circuit-relay' * import { tcp } from '@libp2p/tcp' * import { identify } from '@libp2p/identify' - * import { dCUtRService } from 'libp2p/dcutr' + * import { dcutr } from '@libp2p/dcutr' * * const node = await createLibp2p({ * transports: [ @@ -25,7 +25,7 @@ * ], * services: { * identify: identify(), - * dcutr: dcutrService() + * dcutr: dcutr() * } * }) * @@ -54,6 +54,7 @@ */ import { DefaultDCUtRService } from './dcutr.js' +import type { ComponentLogger } from '@libp2p/interface' import type { PeerStore } from '@libp2p/interface/peer-store' import type { AddressManager } from '@libp2p/interface-internal/address-manager' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' @@ -90,6 +91,7 @@ export interface DCUtRServiceComponents { registrar: Registrar addressManager: AddressManager transportManager: TransportManager + logger: ComponentLogger } /** @@ -97,6 +99,6 @@ export interface DCUtRServiceComponents { */ export const multicodec = '/libp2p/dcutr' -export function dcutrService (init: DCUtRServiceInit = {}): (components: DCUtRServiceComponents) => unknown { +export function dcutr (init: DCUtRServiceInit = {}): (components: DCUtRServiceComponents) => unknown { return (components) => new DefaultDCUtRService(components, init) } diff --git a/packages/libp2p/src/dcutr/pb/message.proto b/packages/protocol-dcutr/src/pb/message.proto similarity index 100% rename from packages/libp2p/src/dcutr/pb/message.proto rename to packages/protocol-dcutr/src/pb/message.proto diff --git a/packages/libp2p/src/dcutr/pb/message.ts b/packages/protocol-dcutr/src/pb/message.ts similarity index 100% rename from packages/libp2p/src/dcutr/pb/message.ts rename to packages/protocol-dcutr/src/pb/message.ts diff --git a/packages/libp2p/src/dcutr/utils.ts b/packages/protocol-dcutr/src/utils.ts similarity index 100% rename from packages/libp2p/src/dcutr/utils.ts rename to packages/protocol-dcutr/src/utils.ts diff --git a/packages/libp2p/test/dcutr/utils.spec.ts b/packages/protocol-dcutr/test/utils.spec.ts similarity index 96% rename from packages/libp2p/test/dcutr/utils.spec.ts rename to packages/protocol-dcutr/test/utils.spec.ts index f5ed2b5fcb..bba0f524d2 100644 --- a/packages/libp2p/test/dcutr/utils.spec.ts +++ b/packages/protocol-dcutr/test/utils.spec.ts @@ -4,7 +4,7 @@ import { multiaddr } from '@multiformats/multiaddr' import { expect } from 'aegir/chai' import Sinon from 'sinon' import { stubInterface } from 'sinon-ts' -import { isPublicAndDialable } from '../../src/dcutr/utils.js' +import { isPublicAndDialable } from '../src/utils.js' import type { Transport } from '@libp2p/interface/transport' import type { TransportManager } from '@libp2p/interface-internal/transport-manager' diff --git a/packages/protocol-dcutr/tsconfig.json b/packages/protocol-dcutr/tsconfig.json new file mode 100644 index 0000000000..ccf0ecdcd7 --- /dev/null +++ b/packages/protocol-dcutr/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "aegir/src/config/tsconfig.aegir.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src", + "test" + ], + "references": [ + { + "path": "../interface" + }, + { + "path": "../interface-internal" + }, + { + "path": "../logger" + } + ] +} diff --git a/packages/protocol-dcutr/typedoc.json b/packages/protocol-dcutr/typedoc.json new file mode 100644 index 0000000000..f599dc728d --- /dev/null +++ b/packages/protocol-dcutr/typedoc.json @@ -0,0 +1,5 @@ +{ + "entryPoints": [ + "./src/index.ts" + ] +} diff --git a/packages/protocol-identify/README.md b/packages/protocol-identify/README.md index 66e8c37d32..c51080f022 100644 --- a/packages/protocol-identify/README.md +++ b/packages/protocol-identify/README.md @@ -37,8 +37,6 @@ Loading this module through a script tag will make it's exports available as `Li ``` -> Implementation of Autonat Protocol - # API Docs - diff --git a/packages/protocol-ping/README.md b/packages/protocol-ping/README.md index a20b7e1382..b6f29491f3 100644 --- a/packages/protocol-ping/README.md +++ b/packages/protocol-ping/README.md @@ -41,8 +41,6 @@ Loading this module through a script tag will make it's exports available as `Li ``` -> Implementation of Autonat Protocol - # API Docs -