diff --git a/.release-please.json b/.release-please.json index 205a5caa01..2c1e29f7b2 100644 --- a/.release-please.json +++ b/.release-please.json @@ -29,6 +29,7 @@ "packages/pubsub": {}, "packages/pubsub-floodsub": {}, "packages/stream-multiplexer-mplex": {}, + "packages/transport-circuit-relay-v2": {}, "packages/transport-tcp": {}, "packages/transport-webrtc": {}, "packages/transport-websockets": {}, diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index 2f47c549c5..a67c5514ff 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -443,7 +443,7 @@ import { tcp } from '@libp2p/tcp' import { mplex } from '@libp2p/mplex' import { yamux } from '@chainsafe/libp2p-yamux' import { noise } from '@chainsafe/libp2p-noise' -import { circuitRelayTransport, circuitRelayServer } from 'libp2p/circuit-relay' +import { circuitRelayTransport, circuitRelayServer } from '@libp2p/circuit-relay-v2' import { identify } from '@libp2p/identify' @@ -501,7 +501,7 @@ import { tcp } from '@libp2p/tcp' import { mplex } from '@libp2p/mplex' import { yamux } from '@chainsafe/libp2p-yamux' import { noise } from '@chainsafe/libp2p-noise' -import { circuitRelayTransport } from 'libp2p/circuit-relay' +import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' const node = await createLibp2p({ transports: [ @@ -529,7 +529,7 @@ import { createLibp2p } from 'libp2p' import { tcp } from '@libp2p/tcp' import { mplex } from '@libp2p/mplex' import { noise } from '@chainsafe/libp2p-noise' -import { circuitRelayTransport } from 'libp2p/circuit-relay' +import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' const node = await createLibp2p({ transports: [ diff --git a/interop/package.json b/interop/package.json index 0fac675167..c744a57f01 100644 --- a/interop/package.json +++ b/interop/package.json @@ -53,6 +53,7 @@ "dependencies": { "@chainsafe/libp2p-noise": "^13.0.0", "@chainsafe/libp2p-yamux": "^5.0.0", + "@libp2p/circuit-relay-v2": "^0.0.0", "@libp2p/identify": "^0.0.0", "@libp2p/mplex": "^9.0.12", "@libp2p/ping": "^0.0.0", diff --git a/interop/relay.js b/interop/relay.js index 18c5dadc15..d6f0c2f92f 100644 --- a/interop/relay.js +++ b/interop/relay.js @@ -1,10 +1,10 @@ import { noise } from '@chainsafe/libp2p-noise' import { yamux } from '@chainsafe/libp2p-yamux' +import { circuitRelayServer } from '@libp2p/circuit-relay-v2' import { identify } from '@libp2p/identify' import { webSockets } from '@libp2p/websockets' import * as filters from '@libp2p/websockets/filters' import { createLibp2p } from 'libp2p' -import { circuitRelayServer } from 'libp2p/circuit-relay' export async function createRelay () { const server = await createLibp2p({ diff --git a/interop/test/ping.spec.ts b/interop/test/ping.spec.ts index b502e00482..daa871ddc7 100644 --- a/interop/test/ping.spec.ts +++ b/interop/test/ping.spec.ts @@ -4,6 +4,7 @@ import { } from 'aegir/chai' import { noise } from '@chainsafe/libp2p-noise' import { yamux } from '@chainsafe/libp2p-yamux' +import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' import { type Identify, identify } from '@libp2p/identify' import { mplex } from '@libp2p/mplex' import { ping, type PingService } from '@libp2p/ping' @@ -14,7 +15,6 @@ import * as filters from '@libp2p/websockets/filters' import { webTransport } from '@libp2p/webtransport' import { type Multiaddr, multiaddr } from '@multiformats/multiaddr' import { createLibp2p, type Libp2p, type Libp2pOptions } from 'libp2p' -import { circuitRelayTransport } from 'libp2p/circuit-relay' async function redisProxy (commands: any[]): Promise { const res = await fetch(`http://localhost:${process.env.proxyPort ?? ''}/`, { body: JSON.stringify(commands), method: 'POST' }) diff --git a/interop/tsconfig.json b/interop/tsconfig.json index 66201d8156..319eeaf8b2 100644 --- a/interop/tsconfig.json +++ b/interop/tsconfig.json @@ -20,6 +20,9 @@ { "path": "../packages/stream-multiplexer-mplex" }, + { + "path": "../packages/transport-circuit-relay-v2" + }, { "path": "../packages/transport-tcp" }, diff --git a/packages/connection-encrypter-plaintext/README.md b/packages/connection-encrypter-plaintext/README.md index 8da01698a1..96948e9301 100644 --- a/packages/connection-encrypter-plaintext/README.md +++ b/packages/connection-encrypter-plaintext/README.md @@ -39,8 +39,6 @@ Loading this module through a script tag will make it's exports available as `Li ``` -> Implementation of the DCUtR Protocol - # API Docs - diff --git a/packages/libp2p/.aegir.js b/packages/libp2p/.aegir.js index 9ccbcc9f9e..62d9b2c045 100644 --- a/packages/libp2p/.aegir.js +++ b/packages/libp2p/.aegir.js @@ -16,7 +16,7 @@ export default { const { WebSockets } = await import('@multiformats/mafmt') const { createLibp2p } = await import('./dist/src/index.js') const { plaintext } = await import('@libp2p/plaintext') - const { circuitRelayServer, circuitRelayTransport } = await import('./dist/src/circuit-relay/index.js') + const { circuitRelayServer, circuitRelayTransport } = await import('@libp2p/circuit-relay-v2') const { identify } = await import('@libp2p/identify') const { fetchService } = await import('./dist/src/fetch/index.js') diff --git a/packages/libp2p/package.json b/packages/libp2p/package.json index f4352efe5a..f533fd2055 100644 --- a/packages/libp2p/package.json +++ b/packages/libp2p/package.json @@ -48,10 +48,6 @@ "types": "./dist/src/index.d.ts", "import": "./dist/src/index.js" }, - "./circuit-relay": { - "types": "./dist/src/circuit-relay/index.d.ts", - "import": "./dist/src/circuit-relay/index.js" - }, "./fetch": { "types": "./dist/src/fetch/index.d.ts", "import": "./dist/src/fetch/index.js" @@ -99,7 +95,6 @@ "@libp2p/peer-collections": "^4.0.8", "@libp2p/peer-id": "^3.0.6", "@libp2p/peer-id-factory": "^3.0.8", - "@libp2p/peer-record": "^6.0.9", "@libp2p/peer-store": "^9.0.9", "@libp2p/utils": "^4.0.7", "@multiformats/mafmt": "^12.1.2", @@ -116,15 +111,12 @@ "it-length-prefixed": "^9.0.1", "it-map": "^3.0.3", "it-merge": "^3.0.0", - "it-pair": "^2.0.6", "it-pipe": "^3.0.1", - "it-protobuf-stream": "^1.0.0", "it-stream-types": "^2.0.1", "merge-options": "^3.0.4", "multiformats": "^12.0.1", "p-defer": "^4.0.0", "p-queue": "^7.3.4", - "p-retry": "^6.0.0", "private-ip": "^3.0.0", "protons-runtime": "^5.0.0", "rate-limiter-flexible": "^3.0.0", @@ -136,6 +128,7 @@ "@chainsafe/libp2p-noise": "^13.0.0", "@chainsafe/libp2p-yamux": "^5.0.0", "@libp2p/bootstrap": "^9.0.12", + "@libp2p/circuit-relay-v2": "^0.0.0", "@libp2p/daemon-client": "^7.0.0", "@libp2p/daemon-server": "^6.0.0", "@libp2p/floodsub": "^8.0.13", @@ -152,7 +145,6 @@ "execa": "^8.0.1", "go-libp2p": "^1.1.1", "it-pushable": "^3.2.0", - "it-to-buffer": "^4.0.1", "npm-run-all": "^4.1.5", "p-event": "^6.0.0", "p-times": "^4.0.0", diff --git a/packages/libp2p/src/connection-manager/auto-dial.ts b/packages/libp2p/src/connection-manager/auto-dial.ts index 515c23d90f..62cc7a414a 100644 --- a/packages/libp2p/src/connection-manager/auto-dial.ts +++ b/packages/libp2p/src/connection-manager/auto-dial.ts @@ -1,6 +1,6 @@ import { PeerMap, PeerSet } from '@libp2p/peer-collections' +import { PeerJobQueue } from '@libp2p/utils/peer-job-queue' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' -import { PeerJobQueue } from '../utils/peer-job-queue.js' import { AUTO_DIAL_CONCURRENCY, AUTO_DIAL_DISCOVERED_PEERS_DEBOUNCE, AUTO_DIAL_INTERVAL, AUTO_DIAL_MAX_QUEUE_LENGTH, AUTO_DIAL_PEER_RETRY_THRESHOLD, AUTO_DIAL_PRIORITY, LAST_DIAL_FAILURE_KEY, MIN_CONNECTIONS } from './constants.js' import type { Libp2pEvents, Logger, ComponentLogger } from '@libp2p/interface' import type { TypedEventTarget } from '@libp2p/interface/events' diff --git a/packages/libp2p/test/circuit-relay/discovery.node.ts b/packages/libp2p/test/circuit-relay/discovery.node.ts deleted file mode 100644 index c62175fd71..0000000000 --- a/packages/libp2p/test/circuit-relay/discovery.node.ts +++ /dev/null @@ -1,119 +0,0 @@ -/* eslint-env mocha */ - -import { yamux } from '@chainsafe/libp2p-yamux' -import { plaintext } from '@libp2p/plaintext' -import { tcp } from '@libp2p/tcp' -import { expect } from 'aegir/chai' -import { pEvent } from 'p-event' -import { circuitRelayServer, type CircuitRelayService, circuitRelayTransport } from '../../src/circuit-relay/index.js' -import { createLibp2p } from '../../src/index.js' -import { getRelayAddress, hasRelay, MockContentRouting, mockContentRouting } from './utils.js' -import type { Libp2p } from '@libp2p/interface' - -describe('circuit-relay discovery', () => { - let local: Libp2p - let remote: Libp2p - let relay: Libp2p<{ relay: CircuitRelayService }> - - beforeEach(async () => { - // create relay first so it has time to advertise itself via content routing - relay = await createLibp2p({ - addresses: { - listen: ['/ip4/127.0.0.1/tcp/0'] - }, - transports: [ - tcp() - ], - streamMuxers: [ - yamux() - ], - connectionEncryption: [ - plaintext() - ], - contentRouters: [ - mockContentRouting() - ], - services: { - relay: circuitRelayServer({ - advertise: { - bootDelay: 10 - } - }) - } - }) - - // wait for relay to advertise service successfully - await pEvent(relay.services.relay, 'relay:advert:success') - - // now create client nodes - ;[local, remote] = await Promise.all([ - createLibp2p({ - addresses: { - listen: ['/ip4/127.0.0.1/tcp/0'] - }, - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ], - streamMuxers: [ - yamux() - ], - connectionEncryption: [ - plaintext() - ], - contentRouters: [ - mockContentRouting() - ] - }), - createLibp2p({ - addresses: { - listen: ['/ip4/127.0.0.1/tcp/0'] - }, - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ], - streamMuxers: [ - yamux() - ], - connectionEncryption: [ - plaintext() - ], - contentRouters: [ - mockContentRouting() - ] - }) - ]) - }) - - afterEach(async () => { - MockContentRouting.reset() - - // Stop each node - return Promise.all([local, remote, relay].map(async libp2p => { - if (libp2p != null) { - await libp2p.stop() - } - })) - }) - - it('should find provider for relay and add it as listen relay', async () => { - // both nodes should discover the relay - they have no direct connection - // so it will be via content routing - const localRelayPeerId = await hasRelay(local) - expect(localRelayPeerId.toString()).to.equal(relay.peerId.toString()) - - const remoteRelayPeerId = await hasRelay(remote) - expect(remoteRelayPeerId.toString()).to.equal(relay.peerId.toString()) - - const relayedAddr = getRelayAddress(remote) - // Dial from remote through the relayed address - const conn = await local.dial(relayedAddr) - - expect(conn).to.exist() - }) -}) diff --git a/packages/libp2p/test/circuit-relay/relay.node.ts b/packages/libp2p/test/circuit-relay/relay.node.ts deleted file mode 100644 index d98916e08d..0000000000 --- a/packages/libp2p/test/circuit-relay/relay.node.ts +++ /dev/null @@ -1,1071 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ['error', 6] */ - -import { yamux } from '@chainsafe/libp2p-yamux' -import { identify } from '@libp2p/identify' -import { mplex } from '@libp2p/mplex' -import { plaintext } from '@libp2p/plaintext' -import { tcp } from '@libp2p/tcp' -import { Circuit } from '@multiformats/mafmt' -import { multiaddr } from '@multiformats/multiaddr' -import { expect } from 'aegir/chai' -import delay from 'delay' -import all from 'it-all' -import { pipe } from 'it-pipe' -import { pbStream } from 'it-protobuf-stream' -import defer from 'p-defer' -import pWaitFor from 'p-wait-for' -import sinon from 'sinon' -import { Uint8ArrayList } from 'uint8arraylist' -import { DEFAULT_DATA_LIMIT, RELAY_V2_HOP_CODEC } from '../../src/circuit-relay/constants.js' -import { circuitRelayServer, type CircuitRelayService, circuitRelayTransport } from '../../src/circuit-relay/index.js' -import { HopMessage, Status } from '../../src/circuit-relay/pb/index.js' -import { createLibp2p, type Libp2pOptions } from '../../src/index.js' -import { discoveredRelayConfig, doesNotHaveRelay, getRelayAddress, hasRelay, notUsingAsRelay, usingAsRelay, usingAsRelayCount } from './utils.js' -import type { Components } from '../../src/components.js' -import type { Libp2p } from '@libp2p/interface' -import type { Connection } from '@libp2p/interface/connection' - -async function createClient (options: Libp2pOptions = {}): Promise { - return createLibp2p({ - addresses: { - listen: ['/ip4/127.0.0.1/tcp/0'] - }, - transports: [ - tcp(), - circuitRelayTransport() - ], - streamMuxers: [ - yamux(), - mplex() - ], - connectionEncryption: [ - plaintext() - ], - connectionManager: { - minConnections: 0 - }, - services: { - identify: identify() - }, - ...options - }) -} - -async function createRelay (options: Libp2pOptions = {}): Promise> { - return createLibp2p({ - addresses: { - listen: ['/ip4/127.0.0.1/tcp/0'] - }, - transports: [ - tcp(), - circuitRelayTransport() - ], - streamMuxers: [ - yamux(), - mplex() - ], - connectionEncryption: [ - plaintext() - ], - ...options, - services: { - relay: circuitRelayServer(), - identify: identify(), - ...(options.services ?? {}) - } - }) -} - -const ECHO_PROTOCOL = '/test/echo/1.0.0' -const echoService = (components: Components): unknown => { - return { - async start () { - await components.registrar.handle(ECHO_PROTOCOL, ({ stream }) => { - void pipe( - stream, stream - ) - }, { - runOnTransientConnection: true - }) - }, - stop () {} - } -} - -describe('circuit-relay', () => { - describe('flows with 1 listener', () => { - let local: Libp2p - let relay1: Libp2p<{ relay: CircuitRelayService }> - let relay2: Libp2p<{ relay: CircuitRelayService }> - let relay3: Libp2p<{ relay: CircuitRelayService }> - - beforeEach(async () => { - // create 1 node and 3 relays - [local, relay1, relay2, relay3] = await Promise.all([ - createClient({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }), - createRelay({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }), - createRelay({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }), - createRelay({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }) - ]) - }) - - afterEach(async () => { - // Stop each node - await Promise.all([local, relay1, relay2, relay3].map(async libp2p => { - if (libp2p != null) { - await libp2p.stop() - } - })) - }) - - it('should ask if node supports hop on protocol change (relay protocol) and add to listen multiaddrs', async () => { - // discover relay - await local.dial(relay1.getMultiaddrs()[0]) - await discoveredRelayConfig(local, relay1) - - // wait for peer added as listen relay - await usingAsRelay(local, relay1) - - // peer has relay multicodec - const peer = await local.peerStore.get(relay1.peerId) - expect(peer.protocols).to.include(RELAY_V2_HOP_CODEC) - }) - - it('should only add discovered relays relayed addresses', async () => { - // discover all relays and connect - await Promise.all([relay1, relay2, relay3].map(async relay => { - await local.dial(relay.getMultiaddrs()[0]) - await discoveredRelayConfig(local, relay) - })) - - const relayPeerId = await hasRelay(local) - - // find the relay we aren't using - const nonRelays = [relay1, relay2, relay3].filter(node => !node.peerId.equals(relayPeerId)) - - // should not be listening on two of them - expect(nonRelays).to.have.lengthOf(2) - - await Promise.all( - nonRelays.map(async nonRelay => { - // wait to guarantee the dialed peer is not added as a listen relay - await expect(usingAsRelay(local, nonRelay, { - timeout: 1000 - })).to.eventually.be.rejected() - }) - ) - }) - - it('should not listen on a relayed address after we disconnect from peer', async () => { - // discover one relay and connect - await local.dial(relay1.getMultiaddrs()[0]) - await discoveredRelayConfig(local, relay1) - - // wait for listening on the relay - await usingAsRelay(local, relay1) - - // disconnect from peer used for relay - await local.hangUp(relay1.peerId) - - // stop the relay so we don't reconnect to it - await relay1.stop() - - // wait for removed listening on the relay - await expect(usingAsRelay(local, relay1, { - timeout: 1000 - })).to.eventually.be.rejected() - }) - - it('should try to listen on other connected peers relayed address if one used relay disconnects', async () => { - // connect to all relays - await Promise.all([relay1, relay2, relay3].map(async relay => { - await local.dial(relay.getMultiaddrs()[0]) - })) - - // discover one relay and connect - const relayPeerId = await hasRelay(local) - - // shut down the connected relay - const relay = [local, relay1, relay2, relay3].find(node => node.peerId.equals(relayPeerId)) - - if (relay == null) { - throw new Error('could not find relay') - } - - await relay.stop() - await pWaitFor(() => local.getConnections(relay.peerId).length === 0) - - // should not be using the relay any more - await expect(usingAsRelay(local, relay, { - timeout: 1000 - })).to.eventually.be.rejected() - - // should connect to another available relay - const newRelayPeerId = await hasRelay(local) - expect(newRelayPeerId.toString()).to.not.equal(relayPeerId.toString()) - }) - - it('should try to listen on stored peers relayed address if one used relay disconnects and there are not enough connected', async () => { - // discover one relay and connect - await local.dial(relay1.getMultiaddrs()[0]) - - // wait for peer to be used as a relay - await usingAsRelay(local, relay1) - - // discover an extra relay and connect to gather its Hop support - await local.dial(relay2.getMultiaddrs()[0]) - - // wait for identify for newly dialed peer - await discoveredRelayConfig(local, relay2) - - // disconnect not used listen relay - await local.hangUp(relay2.peerId) - - // shut down connected relay - await relay1.stop() - await pWaitFor(() => local.getConnections(relay1.peerId).length === 0) - - // should have retrieved other relay details from peer store and connected to it - await usingAsRelay(local, relay2) - }) - - it('should not fail when trying to dial unreachable peers to add as hop relay and replaced removed ones', async () => { - const deferred = defer() - - // discover one relay and connect - await relay1.dial(relay2.getMultiaddrs()[0]) - - // wait for peer to be used as a relay - await usingAsRelay(relay1, relay2) - - // discover an extra relay and connect to gather its Hop support - await relay1.dial(relay3.getMultiaddrs()[0]) - - // wait for identify for newly dialled peer - await discoveredRelayConfig(relay1, relay3) - - // stub dial, make sure we can't reconnect - // @ts-expect-error private field - sinon.stub(relay1.components.connectionManager, 'openConnection').callsFake(async () => { - deferred.resolve() - return Promise.reject(new Error('failed to dial')) - }) - - await Promise.all([ - // disconnect not used listen relay - relay1.hangUp(relay3.peerId), - - // disconnect from relay - relay1.hangUp(relay2.peerId) - ]) - - expect(relay1.getConnections()).to.be.empty() - - // wait for failed dial - await deferred.promise - }) - - it('should announce new addresses when using a peer as a relay', async () => { - // should not have have a circuit address to start with - expect(local.getMultiaddrs().find(ma => Circuit.matches(ma))).to.be.undefined() - - // set up listener for address change - const deferred = defer() - - local.addEventListener('self:peer:update', ({ detail }) => { - const hasCircuitRelayAddress = detail.peer.addresses - .map(({ multiaddr }) => multiaddr) - .find(ma => Circuit.matches(ma)) != null - - if (hasCircuitRelayAddress) { - deferred.resolve() - } - }) - - // discover relay - await local.dial(relay1.getMultiaddrs()[0]) - await discoveredRelayConfig(local, relay1) - - // wait for peer added as listen relay - await usingAsRelay(local, relay1) - - // should have emitted a change:multiaddrs event with a circuit address - await deferred.promise - }) - - it('should announce new addresses when using no longer using peer as a relay', async () => { - // should not have have a circuit address to start with - expect(local.getMultiaddrs().find(ma => Circuit.matches(ma))).to.be.undefined() - - // discover relay - await local.dial(relay1.getMultiaddrs()[0]) - await discoveredRelayConfig(local, relay1) - - // wait for peer added as listen relay - await usingAsRelay(local, relay1) - - // shut down the relay - await relay1.stop() - - // should no longer have a circuit address - await doesNotHaveRelay(local) - }) - }) - - describe('flows with 2 listeners', () => { - let local: Libp2p - let remote: Libp2p - let relay1: Libp2p<{ relay: CircuitRelayService }> - let relay2: Libp2p<{ relay: CircuitRelayService }> - let relay3: Libp2p<{ relay: CircuitRelayService }> - - beforeEach(async () => { - [local, remote, relay1, relay2, relay3] = await Promise.all([ - createClient({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 3 - }) - ] - }), - createClient({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }), - createRelay({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }), - createRelay({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }), - createRelay({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }) - ]) - }) - - afterEach(async () => { - // Stop each node - return Promise.all([local, remote, relay1, relay2, relay3].map(async libp2p => { - if (libp2p != null) { - await libp2p.stop() - } - })) - }) - - it('should not add listener to a already relayed connection', async () => { - // Relay 1 discovers Relay 3 and connect - await relay1.dial(relay3.getMultiaddrs()) - await usingAsRelay(relay1, relay3) - - // Relay 2 discovers Relay 3 and connect - await relay2.dial(relay3.getMultiaddrs()) - await usingAsRelay(relay2, relay3) - - // Relay 1 discovers Relay 2 relayed multiaddr via Relay 3 - const ma2RelayedBy3 = relay2.getMultiaddrs()[relay2.getMultiaddrs().length - 1] - await relay1.peerStore.merge(relay2.peerId, { - multiaddrs: [ma2RelayedBy3] - }) - await relay1.dial(relay2.peerId) - - // Peer not added as listen relay - await expect(usingAsRelay(relay1, relay2, { - timeout: 1000 - })).to.eventually.be.rejected() - }) - - it('should be able to dial a peer from its relayed address previously added', async () => { - // discover relay and make reservation - await remote.dial(relay1.getMultiaddrs()[0]) - await usingAsRelay(remote, relay1) - - // dial the remote through the relay - const ma = getRelayAddress(remote) - await local.dial(ma) - }) - - it('should be able to dial a peer from its relayed address without peer id', async () => { - // discover relay and make reservation - await remote.dial(relay1.getMultiaddrs()[0]) - await usingAsRelay(remote, relay1) - - // get the relayed multiaddr without the remote's peer id - const ma = getRelayAddress(remote) - const maWithoutPeerId = multiaddr(`${ma.toString().split('/p2p-circuit')[0]}/p2p-circuit`) - expect(maWithoutPeerId.getPeerId()).to.not.equal(remote.peerId.toString()) - - // ensure this is the only address we have for the peer - await local.peerStore.patch(remote.peerId, { - multiaddrs: [ - maWithoutPeerId - ] - }) - - // dial via peer id so we load the address from the address book - await local.dial(remote.peerId) - }) - - it('should not stay connected to a relay when not already connected and HOP fails', async () => { - // dial the remote through the relay - const relayedMultiaddr = relay1.getMultiaddrs()[0].encapsulate(`/p2p-circuit/p2p/${remote.peerId.toString()}`) - - await expect(local.dial(relayedMultiaddr)) - .to.eventually.be.rejected() - .and.to.have.property('message').that.matches(/NO_RESERVATION/) - - // we should not be connected to the relay, because we weren't before the dial - expect(local.getConnections(relay1.peerId)).to.be.empty() - }) - - it('dialer should stay connected to an already connected relay on hop failure', async () => { - await local.dial(relay1.getMultiaddrs()[0]) - - // dial the remote through the relay - const relayedMultiaddr = relay1.getMultiaddrs()[0].encapsulate(`/p2p-circuit/p2p/${remote.peerId.toString()}`) - - await expect(local.dial(relayedMultiaddr)) - .to.eventually.be.rejected() - .and.to.have.property('message').that.matches(/NO_RESERVATION/) - - // we should still be connected to the relay - const conns = local.getConnections(relay1.peerId) - expect(conns).to.have.lengthOf(1) - expect(conns).to.have.nested.property('[0].status', 'open') - }) - - it('dialer should close hop stream on hop failure', async () => { - await local.dial(relay1.getMultiaddrs()[0]) - - // dial the remote through the relay - const relayedMultiaddr = relay1.getMultiaddrs()[0].encapsulate(`/p2p-circuit/p2p/${remote.peerId.toString()}`) - - await expect(local.dial(relayedMultiaddr)) - .to.eventually.be.rejected() - .and.to.have.property('message').that.matches(/NO_RESERVATION/) - - // we should still be connected to the relay - const conns = local.getConnections(relay1.peerId) - expect(conns).to.have.lengthOf(1) - expect(conns).to.have.nested.property('[0].status', 'open') - - // we should not have any streams with the hop codec - const streams = local.getConnections(relay1.peerId) - .map(conn => conn.streams) - .flat() - .filter(stream => stream.protocol === RELAY_V2_HOP_CODEC) - - expect(streams).to.be.empty() - }) - - it('destination peer should stay connected to an already connected relay on hop failure', async () => { - await local.dial(relay1.getMultiaddrs()[0]) - await usingAsRelay(local, relay1) - - const conns = relay1.getConnections(local.peerId) - expect(conns).to.have.lengthOf(1) - - // this should fail as the local peer has HOP disabled - await expect(conns[0].newStream(RELAY_V2_HOP_CODEC)) - .to.be.rejected() - - // we should still be connected to the relay - const remoteConns = local.getConnections(relay1.peerId) - expect(remoteConns).to.have.lengthOf(1) - expect(remoteConns).to.have.nested.property('[0].status', 'open') - }) - - it('should fail to dial remote over relay over relay', async () => { - // relay1 dials relay2 - await relay1.dial(relay2.getMultiaddrs()[0]) - await usingAsRelay(relay1, relay2) - - // remote dials relay2 - await remote.dial(relay2.getMultiaddrs()[0]) - await usingAsRelay(remote, relay2) - - // local dials remote via relay1 via relay2 - const ma = getRelayAddress(relay1).encapsulate(`/p2p-circuit/p2p/${remote.peerId.toString()}`) - - await expect(local.dial(ma)).to.eventually.be.rejected - .with.property('code', 'ERR_RELAYED_DIAL') - }) - - it('should fail to open connection over relayed connection', async () => { - // relay1 dials relay2 - await relay1.dial(relay2.getMultiaddrs()[0]) - await usingAsRelay(relay1, relay2) - - // remote dials relay2 - await remote.dial(relay2.getMultiaddrs()[0]) - await usingAsRelay(remote, relay2) - - // local dials relay1 via relay2 - const ma = getRelayAddress(relay1) - - // open hop stream and try to connect to remote - const stream = await local.dialProtocol(ma, RELAY_V2_HOP_CODEC, { - runOnTransientConnection: true - }) - - const hopStream = pbStream(stream).pb(HopMessage) - - await hopStream.write({ - type: HopMessage.Type.CONNECT, - peer: { - id: remote.peerId.toBytes(), - addrs: [] - } - }) - - const response = await hopStream.read() - expect(response).to.have.property('type', HopMessage.Type.STATUS) - expect(response).to.have.property('status', Status.PERMISSION_DENIED) - }) - - it('should emit connection:close when relay stops', async () => { - // discover relay and make reservation - await remote.dial(relay1.getMultiaddrs()[0]) - await usingAsRelay(remote, relay1) - - // dial the remote through the relay - const ma = getRelayAddress(remote) - await local.dial(ma) - - const deferred = defer() - const events: Array> = [] - - local.addEventListener('connection:close', (evt) => { - events.push(evt) - - if (events.length === 2) { - deferred.resolve() - } - }) - - // shut down relay - await relay1.stop() - - // wait for events - await deferred.promise - - // should have closed connections to remote and to relay - expect(events[0].detail.remotePeer.toString()).to.equal(remote.peerId.toString()) - expect(events[1].detail.remotePeer.toString()).to.equal(relay1.peerId.toString()) - }) - - it('should remove the relay event listener when the relay stops', async () => { - // discover relay and make reservation - await local.dial(relay1.getMultiaddrs()[0]) - await local.dial(relay2.getMultiaddrs()[0]) - - await usingAsRelayCount(local, [relay1, relay2], 2) - - // expect 2 listeners - // @ts-expect-error these are private fields - const listeners = local.components.transportManager.getListeners() - - // @ts-expect-error as a result these will have any types - const circuitListener = listeners.filter(listener => { - // @ts-expect-error as a result these will have any types - const circuitMultiaddrs = listener.getAddrs().filter(ma => Circuit.matches(ma)) - return circuitMultiaddrs.length > 0 - }) - - expect(circuitListener[0].relayStore.listenerCount('relay:removed')).to.equal(2) - - // remove one listener - await local.hangUp(relay1.peerId) - - await notUsingAsRelay(local, relay1) - - // expect 1 listener - expect(circuitListener[0].relayStore.listenerCount('relay:removed')).to.equal(1) - }) - - it('should mark a relayed connection as transient', async () => { - // discover relay and make reservation - const connectionToRelay = await remote.dial(relay1.getMultiaddrs()[0]) - - // connection to relay should not be marked transient - expect(connectionToRelay).to.have.property('transient', false) - - await usingAsRelay(remote, relay1) - - // dial the remote through the relay - const ma = getRelayAddress(remote) - const connection = await local.dial(ma) - - // connection to remote through relay should be marked transient - expect(connection).to.have.property('transient', true) - }) - - it('should not open streams on a transient connection', async () => { - // discover relay and make reservation - await remote.dial(relay1.getMultiaddrs()[0]) - await usingAsRelay(remote, relay1) - - // dial the remote through the relay - const ma = getRelayAddress(remote) - const connection = await local.dial(ma) - - // connection should be marked transient - expect(connection).to.have.property('transient', true) - - await expect(connection.newStream('/my-protocol/1.0.0')) - .to.eventually.be.rejected.with.property('code', 'ERR_TRANSIENT_CONNECTION') - }) - - it('should not allow incoming streams on a transient connection', async () => { - const protocol = '/my-protocol/1.0.0' - - // remote registers handler, disallow running over transient streams - await remote.handle(protocol, ({ stream }) => { - void pipe(stream, stream) - }, { - runOnTransientConnection: false - }) - - // discover relay and make reservation - await remote.dial(relay1.getMultiaddrs()[0]) - await usingAsRelay(remote, relay1) - - // dial the remote through the relay - const ma = getRelayAddress(remote) - const connection = await local.dial(ma) - - // connection should be marked transient - expect(connection).to.have.property('transient', true) - - await expect(connection.newStream('/my-protocol/1.0.0', { - runOnTransientConnection: false - })) - .to.eventually.be.rejected.with.property('code', 'ERR_TRANSIENT_CONNECTION') - }) - - it('should open streams on a transient connection when told to do so', async () => { - const protocol = '/my-protocol/1.0.0' - - // remote registers handler, allow running over transient streams - await remote.handle(protocol, ({ stream }) => { - void pipe(stream, stream) - }, { - runOnTransientConnection: true - }) - - // discover relay and make reservation - await remote.dial(relay1.getMultiaddrs()[0]) - await usingAsRelay(remote, relay1) - - // dial the remote through the relay - const ma = getRelayAddress(remote) - const connection = await local.dial(ma) - - // connection should be marked transient - expect(connection).to.have.property('transient', true) - - await expect(connection.newStream('/my-protocol/1.0.0', { - runOnTransientConnection: true - })) - .to.eventually.be.ok() - }) - }) - - describe('flows with data limit', () => { - let local: Libp2p - let remote: Libp2p - let relay: Libp2p<{ relay: CircuitRelayService }> - - beforeEach(async () => { - [local, remote, relay] = await Promise.all([ - createClient({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }), - createClient({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }), - createRelay({ - services: { - relay: circuitRelayServer({ - reservations: { - defaultDataLimit: 1024n - } - }) - } - }) - ]) - }) - - afterEach(async () => { - // Stop each node - return Promise.all([local, remote, relay].map(async libp2p => { - if (libp2p != null) { - await libp2p.stop() - } - })) - }) - - it('should close the connection when too much data is sent', async () => { - // local discover relay - await local.dial(relay.getMultiaddrs()[0]) - await usingAsRelay(local, relay) - - // remote discover relay - await remote.dial(relay.getMultiaddrs()[0]) - await usingAsRelay(remote, relay) - - // collect transferred data - const transferred = new Uint8ArrayList() - - // set up an echo server on the remote - const protocol = '/test/protocol/1.0.0' - await remote.handle(protocol, ({ stream }) => { - void Promise.resolve().then(async () => { - try { - for await (const buf of stream.source) { - transferred.append(buf) - } - } catch {} - }) - }) - - // dial the remote from the local through the relay - const ma = getRelayAddress(remote) - - try { - const stream = await local.dialProtocol(ma, protocol) - - await stream.sink(async function * () { - while (true) { - await delay(100) - yield new Uint8Array(2048) - } - }()) - } catch {} - - // we cannot be exact about this figure because mss, encryption and other - // protocols all send data over connections when they are opened - expect(transferred.byteLength).to.be.lessThan(1024) - }) - }) - - describe('flows with duration limit', () => { - let local: Libp2p - let remote: Libp2p - let relay: Libp2p<{ relay: CircuitRelayService }> - - beforeEach(async () => { - [local, remote, relay] = await Promise.all([ - createClient({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }), - createClient({ - transports: [ - tcp(), - circuitRelayTransport({ - discoverRelays: 1 - }) - ] - }), - createRelay({ - services: { - relay: circuitRelayServer({ - reservations: { - defaultDurationLimit: 1000 - } - }) - } - }) - ]) - }) - - afterEach(async () => { - // Stop each node - return Promise.all([local, remote, relay].map(async libp2p => { - if (libp2p != null) { - await libp2p.stop() - } - })) - }) - - it('should close the connection when connection is open for too long', async () => { - // local discover relay - await local.dial(relay.getMultiaddrs()[0]) - await usingAsRelay(local, relay) - - // remote discover relay - await remote.dial(relay.getMultiaddrs()[0]) - await usingAsRelay(remote, relay) - - // collect transferred data - const transferred = new Uint8ArrayList() - - // set up an echo server on the remote - const protocol = '/test/protocol/1.0.0' - await remote.handle(protocol, ({ stream }) => { - void Promise.resolve().then(async () => { - try { - for await (const buf of stream.source) { - transferred.append(buf) - } - } catch {} - }) - }, { - runOnTransientConnection: true - }) - - // dial the remote from the local through the relay - const ma = getRelayAddress(remote) - - try { - const stream = await local.dialProtocol(ma, protocol, { - runOnTransientConnection: true - }) - - await stream.sink(async function * () { - while (true) { - await delay(100) - yield new Uint8Array(10) - await delay(5000) - } - }()) - } catch {} - - expect(transferred.byteLength).to.equal(10) - }) - }) - - describe('preconfigured relay address', () => { - let local: Libp2p - let remote: Libp2p - let relay: Libp2p<{ relay: CircuitRelayService }> - - beforeEach(async () => { - relay = await createRelay() - - ;[local, remote] = await Promise.all([ - createClient(), - createClient({ - addresses: { - listen: [ - `${relay.getMultiaddrs()[0].toString()}/p2p-circuit` - ] - } - }) - ]) - }) - - afterEach(async () => { - // Stop each node - await Promise.all([local, remote, relay].map(async libp2p => { - if (libp2p != null) { - await libp2p.stop() - } - })) - }) - - it('should be able to dial remote on preconfigured relay address', async () => { - const ma = getRelayAddress(remote) - - await expect(local.dial(ma)).to.eventually.be.ok() - }) - }) - - describe('preconfigured relay without a peer id', () => { - let local: Libp2p - let remote: Libp2p - let relay: Libp2p<{ relay: CircuitRelayService }> - - beforeEach(async () => { - relay = await createRelay() - - ;[local, remote] = await Promise.all([ - createClient(), - createClient({ - addresses: { - listen: [ - `${relay.getMultiaddrs()[0].toString().split('/p2p')[0]}/p2p-circuit` - ] - } - }) - ]) - }) - - afterEach(async () => { - // Stop each node - await Promise.all([local, remote, relay].map(async libp2p => { - if (libp2p != null) { - await libp2p.stop() - } - })) - }) - - it('should be able to dial remote on preconfigured relay address', async () => { - const ma = getRelayAddress(remote) - - await expect(local.dial(ma)).to.eventually.be.ok() - }) - }) - - describe('unlimited relay', () => { - let local: Libp2p - let remote: Libp2p - let relay: Libp2p<{ relay: CircuitRelayService }> - const defaultDurationLimit = 100 - - beforeEach(async () => { - relay = await createRelay({ - services: { - relay: circuitRelayServer({ - reservations: { - defaultDurationLimit, - applyDefaultLimit: false - } - }) - } - }) - - ;[local, remote] = await Promise.all([ - createClient(), - createClient({ - addresses: { - listen: [ - `${relay.getMultiaddrs()[0].toString().split('/p2p')[0]}/p2p-circuit` - ] - }, - services: { - echoService - } - }) - ]) - }) - - afterEach(async () => { - // Stop each node - await Promise.all([local, remote, relay].map(async libp2p => { - if (libp2p != null) { - await libp2p.stop() - } - })) - }) - - it('should not apply a data limit', async () => { - const ma = getRelayAddress(remote) - - const stream = await local.dialProtocol(ma, ECHO_PROTOCOL, { - runOnTransientConnection: true - }) - - // write more than the default data limit - const data = new Uint8Array(Number(DEFAULT_DATA_LIMIT * 2n)) - - const result = await pipe( - [data], - stream, - async (source) => new Uint8ArrayList(...(await all(source))) - ) - - expect(result.subarray()).to.equalBytes(data) - }) - - it('should not apply a time limit', async () => { - const ma = getRelayAddress(remote) - - const stream = await local.dialProtocol(ma, ECHO_PROTOCOL, { - runOnTransientConnection: true - }) - - let finished = false - - setTimeout(() => { - finished = true - }, defaultDurationLimit * 5) - - const start = Date.now() - let finish = 0 - - await pipe( - async function * () { - while (true) { - yield new Uint8Array() - await delay(10) - - if (finished) { - finish = Date.now() - break - } - } - }, - stream - ) - - // default time limit is set to 100ms so the stream should have been open - // for longer than that - expect(finish - start).to.be.greaterThan(defaultDurationLimit) - }) - }) -}) diff --git a/packages/libp2p/test/circuit-relay/relay.spec.ts b/packages/libp2p/test/circuit-relay/relay.spec.ts deleted file mode 100644 index e59393f3c2..0000000000 --- a/packages/libp2p/test/circuit-relay/relay.spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ['error', 6] */ - -import { yamux } from '@chainsafe/libp2p-yamux' -import { identify } from '@libp2p/identify' -import { mplex } from '@libp2p/mplex' -import { plaintext } from '@libp2p/plaintext' -import { webSockets } from '@libp2p/websockets' -import * as filters from '@libp2p/websockets/filters' -import { Circuit } from '@multiformats/mafmt' -import { expect } from 'aegir/chai' -import { pEvent } from 'p-event' -import { circuitRelayTransport } from '../../src/circuit-relay/index.js' -import { createLibp2p } from '../../src/index.js' -import { hasRelay } from './utils.js' -import type { Libp2p } from '@libp2p/interface' -import type { Connection } from '@libp2p/interface/connection' -import type { PeerId } from '@libp2p/interface/peer-id' - -describe('circuit-relay', () => { - let local: Libp2p - let remote: Libp2p - - beforeEach(async () => { - [local, remote] = await Promise.all([ - createLibp2p({ - transports: [ - webSockets({ - filter: filters.all - }), - circuitRelayTransport() - ], - streamMuxers: [ - yamux(), - mplex() - ], - connectionEncryption: [ - plaintext() - ], - connectionGater: { - denyDialMultiaddr: () => false - }, - services: { - identify: identify() - } - }), - createLibp2p({ - addresses: { - listen: [ - `${process.env.RELAY_MULTIADDR}/p2p-circuit` - ] - }, - transports: [ - webSockets({ - filter: filters.all - }), - circuitRelayTransport() - ], - streamMuxers: [ - yamux(), - mplex() - ], - connectionEncryption: [ - plaintext() - ], - connectionGater: { - denyDialMultiaddr: () => false - }, - services: { - identify: identify() - } - }) - ]) - }) - - afterEach(async () => { - // Stop each node - return Promise.all([local, remote].map(async libp2p => { - if (libp2p != null) { - await libp2p.stop() - } - })) - }) - - it('should emit a peer:disconnect event when the remote peer disconnects', async () => { - await hasRelay(remote) - - // dial remote through relay - await local.dial(remote.getMultiaddrs().filter(ma => Circuit.matches(ma))) - - const eventPromise = pEvent<'peer:disconnect', CustomEvent>(local, 'peer:disconnect') - - // shut down remote - await remote.stop() - - // wait for event - const event = await eventPromise - - // should have received peer:disconnect from remote peer - expect(event.detail.toString()).to.equal(remote.peerId.toString()) - }) - - it('should emit a connection:close event when the remote peer disconnects', async () => { - await hasRelay(remote) - - // dial remote through relay - await local.dial(remote.getMultiaddrs().filter(ma => Circuit.matches(ma))) - - const eventPromise = pEvent<'connection:close', CustomEvent>(local, 'connection:close') - - // shut down remote - await remote.stop() - - // wait for event - const event = await eventPromise - - // connection should have been to remote - expect(event.detail.remotePeer.toString()).to.equal(remote.peerId.toString()) - }) -}) diff --git a/packages/libp2p/test/connection-manager/resolver.spec.ts b/packages/libp2p/test/connection-manager/resolver.spec.ts index 95e63b7b55..b19dd34665 100644 --- a/packages/libp2p/test/connection-manager/resolver.spec.ts +++ b/packages/libp2p/test/connection-manager/resolver.spec.ts @@ -1,6 +1,8 @@ /* eslint-env mocha */ import { yamux } from '@chainsafe/libp2p-yamux' +import { RELAY_V2_HOP_CODEC } from '@libp2p/circuit-relay-v2' +import { circuitRelayServer, type CircuitRelayService, circuitRelayTransport } from '@libp2p/circuit-relay-v2' import { mockConnection, mockConnectionGater, mockDuplex, mockMultiaddrConnection } from '@libp2p/interface-compliance-tests/mocks' import { mplex } from '@libp2p/mplex' import { peerIdFromString } from '@libp2p/peer-id' @@ -12,8 +14,6 @@ import { multiaddr } from '@multiformats/multiaddr' import { expect } from 'aegir/chai' import pDefer from 'p-defer' import sinon from 'sinon' -import { RELAY_V2_HOP_CODEC } from '../../src/circuit-relay/constants.js' -import { circuitRelayServer, type CircuitRelayService, circuitRelayTransport } from '../../src/circuit-relay/index.js' import { codes as ErrorCodes } from '../../src/errors.js' import { createLibp2pNode, type Libp2pNode } from '../../src/libp2p.js' import type { PeerId } from '@libp2p/interface/peer-id' diff --git a/packages/libp2p/test/fixtures/base-options.browser.ts b/packages/libp2p/test/fixtures/base-options.browser.ts index be8b3286e5..ac7b44a8ae 100644 --- a/packages/libp2p/test/fixtures/base-options.browser.ts +++ b/packages/libp2p/test/fixtures/base-options.browser.ts @@ -1,10 +1,10 @@ +import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' import { mockConnectionGater } from '@libp2p/interface-compliance-tests/mocks' import { mplex } from '@libp2p/mplex' import { plaintext } from '@libp2p/plaintext' import { webSockets } from '@libp2p/websockets' import * as filters from '@libp2p/websockets/filters' import mergeOptions from 'merge-options' -import { circuitRelayTransport } from '../../src/circuit-relay/index.js' import type { Libp2pOptions } from '../../src/index.js' import type { ServiceMap } from '@libp2p/interface' diff --git a/packages/libp2p/test/fixtures/base-options.ts b/packages/libp2p/test/fixtures/base-options.ts index 97812666b6..813a41aaa3 100644 --- a/packages/libp2p/test/fixtures/base-options.ts +++ b/packages/libp2p/test/fixtures/base-options.ts @@ -1,11 +1,11 @@ import { yamux } from '@chainsafe/libp2p-yamux' +import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' import { mplex } from '@libp2p/mplex' import { plaintext } from '@libp2p/plaintext' import { tcp } from '@libp2p/tcp' import { webSockets } from '@libp2p/websockets' import * as filters from '@libp2p/websockets/filters' import mergeOptions from 'merge-options' -import { circuitRelayTransport } from '../../src/circuit-relay/index.js' import type { Libp2pOptions } from '../../src' import type { ServiceMap } from '@libp2p/interface' diff --git a/packages/libp2p/test/interop.ts b/packages/libp2p/test/interop.ts index a3418f08a1..af9991a661 100644 --- a/packages/libp2p/test/interop.ts +++ b/packages/libp2p/test/interop.ts @@ -2,6 +2,7 @@ import fs from 'fs' import { gossipsub } from '@chainsafe/libp2p-gossipsub' import { noise } from '@chainsafe/libp2p-noise' import { yamux } from '@chainsafe/libp2p-yamux' +import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2' import { unmarshalPrivateKey } from '@libp2p/crypto/keys' import { createClient } from '@libp2p/daemon-client' import { createServer } from '@libp2p/daemon-server' @@ -20,7 +21,6 @@ import { multiaddr } from '@multiformats/multiaddr' import { execa } from 'execa' import { path as p2pd } from 'go-libp2p' import pDefer from 'p-defer' -import { circuitRelayServer, circuitRelayTransport } from '../src/circuit-relay/index.js' import { createLibp2p, type Libp2pOptions, type ServiceFactoryMap } from '../src/index.js' import type { ServiceMap } from '@libp2p/interface' import type { PeerId } from '@libp2p/interface/peer-id' diff --git a/packages/libp2p/test/upgrading/upgrader.spec.ts b/packages/libp2p/test/upgrading/upgrader.spec.ts index 7406c2c779..643f881fb8 100644 --- a/packages/libp2p/test/upgrading/upgrader.spec.ts +++ b/packages/libp2p/test/upgrading/upgrader.spec.ts @@ -1,6 +1,7 @@ /* eslint-env mocha */ import { yamux } from '@chainsafe/libp2p-yamux' +import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' import { TypedEventEmitter } from '@libp2p/interface/events' import { mockConnectionGater, mockConnectionManager, mockMultiaddrConnPair, mockRegistrar, mockStream, mockMuxer } from '@libp2p/interface-compliance-tests/mocks' import { mplex } from '@libp2p/mplex' @@ -22,7 +23,6 @@ import sinon from 'sinon' import { type StubbedInstance, stubInterface } from 'sinon-ts' import { Uint8ArrayList } from 'uint8arraylist' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import { circuitRelayTransport } from '../../src/circuit-relay/index.js' import { type Components, defaultComponents } from '../../src/components.js' import { codes } from '../../src/errors.js' import { createLibp2p } from '../../src/index.js' diff --git a/packages/libp2p/tsconfig.json b/packages/libp2p/tsconfig.json index c7e77d63d5..dc98117467 100644 --- a/packages/libp2p/tsconfig.json +++ b/packages/libp2p/tsconfig.json @@ -5,10 +5,12 @@ }, "include": [ "src", - "test", - "../upnp-nat/test/upnp-nat" + "test" ], "references": [ + { + "path": "../connection-encrypter-plaintext" + }, { "path": "../crypto" }, @@ -45,9 +47,6 @@ { "path": "../peer-id-factory" }, - { - "path": "../peer-record" - }, { "path": "../peer-store" }, @@ -60,6 +59,9 @@ { "path": "../stream-multiplexer-mplex" }, + { + "path": "../transport-circuit-relay-v2" + }, { "path": "../transport-tcp" }, diff --git a/packages/libp2p/typedoc.json b/packages/libp2p/typedoc.json index 66cdc20483..9797b17f28 100644 --- a/packages/libp2p/typedoc.json +++ b/packages/libp2p/typedoc.json @@ -1,7 +1,6 @@ { "entryPoints": [ "./src/index.ts", - "./src/circuit-relay/index.ts", "./src/fetch/index.ts" ] } diff --git a/packages/protocol-dcutr/tsconfig.json b/packages/protocol-dcutr/tsconfig.json index ccf0ecdcd7..b4d9448ec6 100644 --- a/packages/protocol-dcutr/tsconfig.json +++ b/packages/protocol-dcutr/tsconfig.json @@ -13,9 +13,6 @@ }, { "path": "../interface-internal" - }, - { - "path": "../logger" } ] } diff --git a/packages/transport-circuit-relay-v2/LICENSE b/packages/transport-circuit-relay-v2/LICENSE new file mode 100644 index 0000000000..20ce483c86 --- /dev/null +++ b/packages/transport-circuit-relay-v2/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/transport-circuit-relay-v2/LICENSE-APACHE b/packages/transport-circuit-relay-v2/LICENSE-APACHE new file mode 100644 index 0000000000..14478a3b60 --- /dev/null +++ b/packages/transport-circuit-relay-v2/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/transport-circuit-relay-v2/LICENSE-MIT b/packages/transport-circuit-relay-v2/LICENSE-MIT new file mode 100644 index 0000000000..72dc60d84b --- /dev/null +++ b/packages/transport-circuit-relay-v2/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/transport-circuit-relay-v2/README.md b/packages/transport-circuit-relay-v2/README.md new file mode 100644 index 0000000000..fb0095699f --- /dev/null +++ b/packages/transport-circuit-relay-v2/README.md @@ -0,0 +1,69 @@ +[![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 Circuit Relay v2 + +# About + +The `circuitRelayTransport` allows libp2p to dial and listen on [Circuit Relay](https://docs.libp2p.io/concepts/nat/circuit-relay/) +addresses. + +## Example + +```typescript +import { createLibp2p } from 'libp2p' +import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' + +const node = await createLibp2p({ + transports: [ + circuitRelayTransport() + ] +}) +``` + +The `circuitRelayServer` function allows libp2p to function as a [Circuit Relay](https://docs.libp2p.io/concepts/nat/circuit-relay/) +server. This will not work in browsers. + +## Example + +```typescript +import { createLibp2p } from 'libp2p' +import { circuitRelayServer } from '@libp2p/circuit-relay-v2' + +const node = await createLibp2p({ + services: [ + circuitRelay: circuitRelayServer() + ] +}) +``` + +# Install + +```console +$ npm i @libp2p/circuit-relay-v2 +``` + +## 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/transport-circuit-relay-v2/package.json b/packages/transport-circuit-relay-v2/package.json new file mode 100644 index 0000000000..026fe8d810 --- /dev/null +++ b/packages/transport-circuit-relay-v2/package.json @@ -0,0 +1,83 @@ +{ + "name": "@libp2p/circuit-relay-v2", + "version": "0.0.0", + "description": "Implementation of Circuit Relay v2", + "license": "Apache-2.0 OR MIT", + "homepage": "https://github.com/libp2p/js-libp2p/tree/master/packages/transport-circuit-relay-v2#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", + "@libp2p/peer-collections": "^4.0.8", + "@libp2p/peer-id": "^3.0.6", + "@libp2p/peer-record": "^6.0.9", + "@libp2p/utils": "^4.0.7", + "@multiformats/mafmt": "^12.1.6", + "@multiformats/multiaddr": "^12.1.5", + "any-signal": "^4.1.1", + "delay": "^6.0.0", + "it-protobuf-stream": "^1.0.2", + "it-stream-types": "^2.0.1", + "multiformats": "^12.1.3", + "p-defer": "^4.0.0", + "p-retry": "^6.1.0", + "protons-runtime": "^5.0.0", + "uint8arraylist": "^2.4.3", + "uint8arrays": "^4.0.6" + }, + "devDependencies": { + "@libp2p/interface-compliance-tests": "^4.1.5", + "@libp2p/logger": "^3.0.2", + "@libp2p/peer-id-factory": "^3.0.8", + "aegir": "^41.0.2", + "it-drain": "^3.0.3", + "it-pair": "^2.0.6", + "it-pushable": "^3.2.1", + "it-to-buffer": "^4.0.3", + "p-wait-for": "^5.0.2", + "protons": "^7.3.0", + "sinon": "^17.0.0", + "sinon-ts": "^2.0.0" + } +} diff --git a/packages/libp2p/src/circuit-relay/constants.ts b/packages/transport-circuit-relay-v2/src/constants.ts similarity index 85% rename from packages/libp2p/src/circuit-relay/constants.ts rename to packages/transport-circuit-relay-v2/src/constants.ts index d35f45881a..50ed7a1134 100644 --- a/packages/libp2p/src/circuit-relay/constants.ts +++ b/packages/transport-circuit-relay-v2/src/constants.ts @@ -70,3 +70,10 @@ export const DEFAULT_HOP_TIMEOUT = 30 * second * How long to wait before starting to advertise the relay service */ export const DEFAULT_ADVERT_BOOT_DELAY = 30 * second + +export const MAX_CONNECTIONS = 300 + +export const ERR_NO_ROUTERS_AVAILABLE = 'ERR_NO_ROUTERS_AVAILABLE' +export const ERR_RELAYED_DIAL = 'ERR_RELAYED_DIAL' +export const ERR_HOP_REQUEST_FAILED = 'ERR_HOP_REQUEST_FAILED' +export const ERR_TRANSFER_LIMIT_EXCEEDED = 'ERR_TRANSFER_LIMIT_EXCEEDED' diff --git a/packages/libp2p/src/circuit-relay/index.ts b/packages/transport-circuit-relay-v2/src/index.ts similarity index 87% rename from packages/libp2p/src/circuit-relay/index.ts rename to packages/transport-circuit-relay-v2/src/index.ts index 5e8757fb10..d68b389903 100644 --- a/packages/libp2p/src/circuit-relay/index.ts +++ b/packages/transport-circuit-relay-v2/src/index.ts @@ -8,7 +8,7 @@ * * ```typescript * import { createLibp2p } from 'libp2p' - * import { circuitRelayTransport } from 'libp2p/circuit-relay' + * import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' * * const node = await createLibp2p({ * transports: [ @@ -24,7 +24,7 @@ * * ```typescript * import { createLibp2p } from 'libp2p' - * import { circuitRelayServer } from 'libp2p/circuit-relay' + * import { circuitRelayServer } from '@libp2p/circuit-relay-v2' * * const node = await createLibp2p({ * services: [ @@ -57,3 +57,8 @@ export interface CircuitRelayService extends TypedEventEmitter implem private timeout?: any private started: boolean private readonly bootDelay: number + readonly #log: Logger /** * Creates an instance of Relay @@ -43,6 +43,7 @@ export class AdvertService extends TypedEventEmitter implem constructor (components: AdvertServiceComponents, init?: AdvertServiceInit) { super() + this.#log = components.logger.forComponent('libp2p:circuit-relay:advert-service') this.contentRouting = components.contentRouting this.bootDelay = init?.bootDelay ?? DEFAULT_ADVERT_BOOT_DELAY this.started = false @@ -63,7 +64,7 @@ export class AdvertService extends TypedEventEmitter implem // Advertise service if HOP enabled and advertising enabled this.timeout = setTimeout(() => { this._advertiseService().catch(err => { - log.error('could not advertise service', err) + this.#log.error('could not advertise service', err) }) }, this.bootDelay) @@ -94,13 +95,13 @@ export class AdvertService extends TypedEventEmitter implem } catch (err: any) { this.safeDispatchEvent('advert:error', { detail: err }) - if (err.code === codes.ERR_NO_ROUTERS_AVAILABLE) { - log.error('a content router, such as a DHT, must be provided in order to advertise the relay service', err) + if (err.code === ERR_NO_ROUTERS_AVAILABLE) { + this.#log.error('a content router, such as a DHT, must be provided in order to advertise the relay service', err) this.stop() return } - log.error('could not advertise service', err) + this.#log.error('could not advertise service', err) throw err } }) diff --git a/packages/libp2p/src/circuit-relay/server/index.ts b/packages/transport-circuit-relay-v2/src/server/index.ts similarity index 84% rename from packages/libp2p/src/circuit-relay/server/index.ts rename to packages/transport-circuit-relay-v2/src/server/index.ts index 8da847d95b..4bdc923dac 100644 --- a/packages/libp2p/src/circuit-relay/server/index.ts +++ b/packages/transport-circuit-relay-v2/src/server/index.ts @@ -1,14 +1,13 @@ import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface/events' -import { logger } from '@libp2p/logger' import { peerIdFromBytes } from '@libp2p/peer-id' import { RecordEnvelope } from '@libp2p/peer-record' import { type Multiaddr, multiaddr } from '@multiformats/multiaddr' import { pbStream, type ProtobufStream } from 'it-protobuf-stream' import pDefer from 'p-defer' -import { MAX_CONNECTIONS } from '../../connection-manager/constants.js' import { CIRCUIT_PROTO_CODE, DEFAULT_HOP_TIMEOUT, + MAX_CONNECTIONS, RELAY_SOURCE_TAG, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC @@ -19,6 +18,7 @@ import { AdvertService, type AdvertServiceComponents, type AdvertServiceInit } f import { ReservationStore, type ReservationStoreInit } from './reservation-store.js' import { ReservationVoucherRecord } from './reservation-voucher.js' import type { CircuitRelayService, RelayReservation } from '../index.js' +import type { ComponentLogger, Logger } from '@libp2p/interface' import type { Connection, Stream } from '@libp2p/interface/connection' import type { ConnectionGater } from '@libp2p/interface/connection-gater' import type { PeerId } from '@libp2p/interface/peer-id' @@ -29,8 +29,6 @@ import type { ConnectionManager } from '@libp2p/interface-internal/connection-ma import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar' import type { PeerMap } from '@libp2p/peer-collections' -const log = logger('libp2p:circuit-relay:server') - const isRelayAddr = (ma: Multiaddr): boolean => ma.protoCodes().includes(CIRCUIT_PROTO_CODE) export interface CircuitRelayServerInit { @@ -86,6 +84,7 @@ export interface CircuitRelayServerComponents extends AdvertServiceComponents { peerId: PeerId connectionManager: ConnectionManager connectionGater: ConnectionGater + logger: ComponentLogger } export interface RelayServerEvents { @@ -113,6 +112,7 @@ class CircuitRelayServer extends TypedEventEmitter implements private readonly maxInboundHopStreams?: number private readonly maxOutboundHopStreams?: number private readonly maxOutboundStopStreams: number + readonly #log: Logger /** * Creates an instance of Relay @@ -120,6 +120,7 @@ class CircuitRelayServer extends TypedEventEmitter implements constructor (components: CircuitRelayServerComponents, init: CircuitRelayServerInit = {}) { super() + this.#log = components.logger.forComponent('libp2p:circuit-relay:server') this.registrar = components.registrar this.peerStore = components.peerStore this.addressManager = components.addressManager @@ -165,7 +166,7 @@ class CircuitRelayServer extends TypedEventEmitter implements await this.registrar.handle(RELAY_V2_HOP_CODEC, (data) => { void this.onHop(data).catch(err => { - log.error(err) + this.#log.error(err) }) }, { maxInboundStreams: this.maxInboundHopStreams, @@ -191,7 +192,7 @@ class CircuitRelayServer extends TypedEventEmitter implements } async onHop ({ connection, stream }: IncomingStreamData): Promise { - log('received circuit v2 hop protocol stream from %p', connection.remotePeer) + this.#log('received circuit v2 hop protocol stream from %p', connection.remotePeer) const hopTimeoutPromise = pDefer() const timeout = setTimeout(() => { @@ -209,7 +210,7 @@ class CircuitRelayServer extends TypedEventEmitter implements throw new Error('request was invalid, could not read from stream') } - log('received', request.type) + this.#log('received', request.type) await Promise.race([ this.handleHopProtocol({ @@ -220,7 +221,7 @@ class CircuitRelayServer extends TypedEventEmitter implements hopTimeoutPromise.promise ]) } catch (err: any) { - log.error('error while handling hop', err) + this.#log.error('error while handling hop', err) await pbstr.pb(HopMessage).write({ type: HopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE @@ -232,12 +233,12 @@ class CircuitRelayServer extends TypedEventEmitter implements } async handleHopProtocol ({ stream, request, connection }: HopProtocolOptions): Promise { - log('received hop message') + this.#log('received hop message') switch (request.type) { case HopMessage.Type.RESERVE: await this.handleReserve({ stream, request, connection }); break case HopMessage.Type.CONNECT: await this.handleConnect({ stream, request, connection }); break default: { - log.error('invalid hop request type %s via peer %p', request.type, connection.remotePeer) + this.#log.error('invalid hop request type %s via peer %p', request.type, connection.remotePeer) await stream.pb(HopMessage).write({ type: HopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE }) } } @@ -245,16 +246,16 @@ class CircuitRelayServer extends TypedEventEmitter implements async handleReserve ({ stream, request, connection }: HopProtocolOptions): Promise { const hopstr = stream.pb(HopMessage) - log('hop reserve request from %p', connection.remotePeer) + this.#log('hop reserve request from %p', connection.remotePeer) if (isRelayAddr(connection.remoteAddr)) { - log.error('relay reservation over circuit connection denied for peer: %p', connection.remotePeer) + this.#log.error('relay reservation over circuit connection denied for peer: %p', connection.remotePeer) await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }) return } if ((await this.connectionGater.denyInboundRelayReservation?.(connection.remotePeer)) === true) { - log.error('reservation for %p denied by connection gater', connection.remotePeer) + this.#log.error('reservation for %p denied by connection gater', connection.remotePeer) await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }) return } @@ -284,9 +285,9 @@ class CircuitRelayServer extends TypedEventEmitter implements reservation: await this.makeReservation(connection.remotePeer, BigInt(result.expire ?? 0)), limit: this.reservationStore.get(connection.remotePeer)?.limit }) - log('sent confirmation response to %s', connection.remotePeer) + this.#log('sent confirmation response to %s', connection.remotePeer) } catch (err) { - log.error('failed to send confirmation response to %p', connection.remotePeer, err) + this.#log.error('failed to send confirmation response to %p', connection.remotePeer, err) this.reservationStore.removeReservation(connection.remotePeer) } } @@ -322,37 +323,37 @@ class CircuitRelayServer extends TypedEventEmitter implements const hopstr = stream.pb(HopMessage) if (isRelayAddr(connection.remoteAddr)) { - log.error('relay reservation over circuit connection denied for peer: %p', connection.remotePeer) + this.#log.error('relay reservation over circuit connection denied for peer: %p', connection.remotePeer) await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }) return } - log('hop connect request from %p', connection.remotePeer) + this.#log('hop connect request from %p', connection.remotePeer) let dstPeer: PeerId try { if (request.peer == null) { - log.error('no peer info in hop connect request') + this.#log.error('no peer info in hop connect request') throw new Error('no peer info in request') } request.peer.addrs.forEach(multiaddr) dstPeer = peerIdFromBytes(request.peer.id) } catch (err) { - log.error('invalid hop connect request via peer %p %s', connection.remotePeer, err) + this.#log.error('invalid hop connect request via peer %p %s', connection.remotePeer, err) await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }) return } if (!this.reservationStore.hasReservation(dstPeer)) { - log.error('hop connect denied for destination peer %p not having a reservation for %p with status %s', dstPeer, connection.remotePeer, Status.NO_RESERVATION) + this.#log.error('hop connect denied for destination peer %p not having a reservation for %p with status %s', dstPeer, connection.remotePeer, Status.NO_RESERVATION) await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.NO_RESERVATION }) return } if ((await this.connectionGater.denyOutboundRelayedConnection?.(connection.remotePeer, dstPeer)) === true) { - log.error('hop connect for %p to %p denied by connection gater', connection.remotePeer, dstPeer) + this.#log.error('hop connect for %p to %p denied by connection gater', connection.remotePeer, dstPeer) await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }) return } @@ -360,7 +361,7 @@ class CircuitRelayServer extends TypedEventEmitter implements const connections = this.connectionManager.getConnections(dstPeer) if (connections.length === 0) { - log('hop connect denied for destination peer %p not having a connection for %p as there is no destination connection', dstPeer, connection.remotePeer) + this.#log('hop connect denied for destination peer %p not having a connection for %p as there is no destination connection', dstPeer, connection.remotePeer) await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.NO_RESERVATION }) return } @@ -379,7 +380,7 @@ class CircuitRelayServer extends TypedEventEmitter implements }) if (destinationStream == null) { - log.error('failed to open stream to destination peer %p', destinationConnection?.remotePeer) + this.#log.error('failed to open stream to destination peer %p', destinationConnection?.remotePeer) await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.CONNECTION_FAILED }) return } @@ -387,10 +388,12 @@ class CircuitRelayServer extends TypedEventEmitter implements await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.OK }) const sourceStream = stream.unwrap() - log('connection from %p to %p established - merging streams', connection.remotePeer, dstPeer) + this.#log('connection from %p to %p established - merging streams', connection.remotePeer, dstPeer) const limit = this.reservationStore.get(dstPeer)?.limit // Short circuit the two streams to create the relayed connection - createLimitedRelay(sourceStream, destinationStream, this.shutdownController.signal, limit) + createLimitedRelay(sourceStream, destinationStream, this.shutdownController.signal, limit, { + log: this.#log + }) } /** @@ -400,7 +403,7 @@ class CircuitRelayServer extends TypedEventEmitter implements connection, request }: StopOptions): Promise { - log('starting circuit relay v2 stop request to %s', connection.remotePeer) + this.#log('starting circuit relay v2 stop request to %s', connection.remotePeer) const stream = await connection.newStream([RELAY_V2_STOP_CODEC], { maxOutboundStreams: this.maxOutboundStopStreams, runOnTransientConnection: true @@ -413,21 +416,21 @@ class CircuitRelayServer extends TypedEventEmitter implements try { response = await stopstr.read() } catch (err) { - log.error('error parsing stop message response from %p', connection.remotePeer) + this.#log.error('error parsing stop message response from %p', connection.remotePeer) } if (response == null) { - log.error('could not read response from %p', connection.remotePeer) + this.#log.error('could not read response from %p', connection.remotePeer) await stream.close() return } if (response.status === Status.OK) { - log('stop request to %p was successful', connection.remotePeer) + this.#log('stop request to %p was successful', connection.remotePeer) return pbstr.unwrap() } - log('stop request failed with code %d', response.status) + this.#log('stop request failed with code %d', response.status) await stream.close() } diff --git a/packages/libp2p/src/circuit-relay/server/reservation-store.ts b/packages/transport-circuit-relay-v2/src/server/reservation-store.ts similarity index 100% rename from packages/libp2p/src/circuit-relay/server/reservation-store.ts rename to packages/transport-circuit-relay-v2/src/server/reservation-store.ts diff --git a/packages/libp2p/src/circuit-relay/server/reservation-voucher.ts b/packages/transport-circuit-relay-v2/src/server/reservation-voucher.ts similarity index 100% rename from packages/libp2p/src/circuit-relay/server/reservation-voucher.ts rename to packages/transport-circuit-relay-v2/src/server/reservation-voucher.ts diff --git a/packages/libp2p/src/circuit-relay/transport/discovery.ts b/packages/transport-circuit-relay-v2/src/transport/discovery.ts similarity index 84% rename from packages/libp2p/src/circuit-relay/transport/discovery.ts rename to packages/transport-circuit-relay-v2/src/transport/discovery.ts index 80bfa1f4d2..b3e9683702 100644 --- a/packages/libp2p/src/circuit-relay/transport/discovery.ts +++ b/packages/transport-circuit-relay-v2/src/transport/discovery.ts @@ -1,10 +1,10 @@ import { TypedEventEmitter } from '@libp2p/interface/events' -import { logger } from '@libp2p/logger' import { RELAY_RENDEZVOUS_NS, RELAY_V2_HOP_CODEC } from '../constants.js' import { namespaceToCid } from '../utils.js' +import type { ComponentLogger, Logger } from '@libp2p/interface' import type { ContentRouting } from '@libp2p/interface/content-routing' import type { PeerId } from '@libp2p/interface/peer-id' import type { PeerStore } from '@libp2p/interface/peer-store' @@ -13,8 +13,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/transport-manager' -const log = logger('libp2p:circuit-relay:discover-relays') - export interface RelayDiscoveryEvents { 'relay:discover': CustomEvent } @@ -26,6 +24,7 @@ export interface RelayDiscoveryComponents { transportManager: TransportManager contentRouting: ContentRouting registrar: Registrar + logger: ComponentLogger } /** @@ -39,9 +38,12 @@ export class RelayDiscovery extends TypedEventEmitter impl private readonly registrar: Registrar private started: boolean private topologyId?: string + readonly #log: Logger constructor (components: RelayDiscoveryComponents) { super() + + this.#log = components.logger.forComponent('libp2p:circuit-relay:discover-relays') this.started = false this.peerId = components.peerId this.peerStore = components.peerStore @@ -65,7 +67,7 @@ export class RelayDiscovery extends TypedEventEmitter impl void this.discover() .catch(err => { - log.error('error listening on relays', err) + this.#log.error('error listening on relays', err) }) this.started = true @@ -88,7 +90,7 @@ export class RelayDiscovery extends TypedEventEmitter impl * 3. Search the network */ async discover (): Promise { - log('searching peer store for relays') + this.#log('searching peer store for relays') const peers = (await this.peerStore.all({ filters: [ // filter by a list of peers supporting RELAY_V2_HOP and ones we are not listening on @@ -102,14 +104,14 @@ export class RelayDiscovery extends TypedEventEmitter impl })) for (const peer of peers) { - log('found relay peer %p in content peer store', peer.id) + this.#log('found relay peer %p in content peer store', peer.id) this.safeDispatchEvent('relay:discover', { detail: peer.id }) } - log('found %d relay peers in peer store', peers.length) + this.#log('found %d relay peers in peer store', peers.length) try { - log('searching content routing for relays') + this.#log('searching content routing for relays') const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS) let found = 0 @@ -123,14 +125,14 @@ export class RelayDiscovery extends TypedEventEmitter impl multiaddrs: provider.multiaddrs }) - log('found relay peer %p in content routing', peerId) + this.#log('found relay peer %p in content routing', peerId) this.safeDispatchEvent('relay:discover', { detail: peerId }) } } - log('found %d relay peers in content routing', found) + this.#log('found %d relay peers in content routing', found) } catch (err: any) { - log.error('failed when finding relays on the network', err) + this.#log.error('failed when finding relays on the network', err) } } } diff --git a/packages/libp2p/src/circuit-relay/transport/index.ts b/packages/transport-circuit-relay-v2/src/transport/index.ts similarity index 84% rename from packages/libp2p/src/circuit-relay/transport/index.ts rename to packages/transport-circuit-relay-v2/src/transport/index.ts index 022b44c57e..dec8e67b28 100644 --- a/packages/libp2p/src/circuit-relay/transport/index.ts +++ b/packages/transport-circuit-relay-v2/src/transport/index.ts @@ -1,19 +1,16 @@ import { CodeError } from '@libp2p/interface/errors' import { symbol, type Transport, type CreateListenerOptions, type Listener, type Upgrader } from '@libp2p/interface/transport' -import { logger } from '@libp2p/logger' import { peerIdFromBytes, peerIdFromString } from '@libp2p/peer-id' import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn' import * as mafmt from '@multiformats/mafmt' import { multiaddr } from '@multiformats/multiaddr' import { pbStream } from 'it-protobuf-stream' -import { MAX_CONNECTIONS } from '../../connection-manager/constants.js' -import { codes } from '../../errors.js' -import { CIRCUIT_PROTO_CODE, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js' +import { CIRCUIT_PROTO_CODE, ERR_HOP_REQUEST_FAILED, ERR_RELAYED_DIAL, MAX_CONNECTIONS, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js' import { StopMessage, HopMessage, Status } from '../pb/index.js' import { RelayDiscovery, type RelayDiscoveryComponents } from './discovery.js' import { createListener } from './listener.js' import { type RelayStoreInit, ReservationStore } from './reservation-store.js' -import type { Libp2pEvents, AbortOptions } from '@libp2p/interface' +import type { Libp2pEvents, AbortOptions, ComponentLogger, Logger } from '@libp2p/interface' import type { Connection, Stream } from '@libp2p/interface/connection' import type { ConnectionGater } from '@libp2p/interface/connection-gater' import type { ContentRouting } from '@libp2p/interface/content-routing' @@ -25,8 +22,6 @@ import type { ConnectionManager } from '@libp2p/interface-internal/connection-ma import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar' import type { Multiaddr } from '@multiformats/multiaddr' -const log = logger('libp2p:circuit-relay:transport') - const isValidStop = (request: StopMessage): request is Required => { if (request.peer == null) { return false @@ -51,6 +46,7 @@ export interface CircuitRelayTransportComponents extends RelayDiscoveryComponent contentRouting: ContentRouting connectionGater: ConnectionGater events: TypedEventTarget + logger: ComponentLogger } interface ConnectOptions { @@ -116,15 +112,19 @@ class CircuitRelayTransport implements Transport { private readonly addressManager: AddressManager private readonly connectionGater: ConnectionGater private readonly reservationStore: ReservationStore + private readonly logger: ComponentLogger private readonly maxInboundStopStreams: number private readonly maxOutboundStopStreams?: number private readonly stopTimeout: number private started: boolean + readonly #log: Logger constructor (components: CircuitRelayTransportComponents, init: CircuitRelayTransportInit) { + this.#log = components.logger.forComponent('libp2p:circuit-relay:transport') this.registrar = components.registrar this.peerStore = components.peerStore this.connectionManager = components.connectionManager + this.logger = components.logger this.peerId = components.peerId this.upgrader = components.upgrader this.addressManager = components.addressManager @@ -138,7 +138,7 @@ class CircuitRelayTransport implements Transport { this.discovery.addEventListener('relay:discover', (evt) => { this.reservationStore.addRelay(evt.detail, 'discovered') .catch(err => { - log.error('could not add discovered relay %p', evt.detail, err) + this.#log.error('could not add discovered relay %p', evt.detail, err) }) }) } @@ -147,7 +147,7 @@ class CircuitRelayTransport implements Transport { this.reservationStore.addEventListener('relay:not-enough-relays', () => { this.discovery?.discover() .catch(err => { - log.error('could not discover relays', err) + this.#log.error('could not discover relays', err) }) }) @@ -164,7 +164,7 @@ class CircuitRelayTransport implements Transport { await this.registrar.handle(RELAY_V2_STOP_CODEC, (data) => { void this.onStop(data).catch(err => { - log.error('error while handling STOP protocol', err) + this.#log.error('error while handling STOP protocol', err) data.stream.abort(err) }) }, { @@ -194,8 +194,8 @@ class CircuitRelayTransport implements Transport { async dial (ma: Multiaddr, options: AbortOptions = {}): Promise { if (ma.protoCodes().filter(code => code === CIRCUIT_PROTO_CODE).length !== 1) { const errMsg = 'Invalid circuit relay address' - log.error(errMsg, ma) - throw new CodeError(errMsg, codes.ERR_RELAYED_DIAL) + this.#log.error(errMsg, ma) + throw new CodeError(errMsg, ERR_RELAYED_DIAL) } // Check the multiaddr to see if it contains a relay and a destination peer @@ -207,8 +207,8 @@ class CircuitRelayTransport implements Transport { if (relayId == null || destinationId == null) { const errMsg = `Circuit relay dial to ${ma.toString()} failed as address did not have peer ids` - log.error(errMsg) - throw new CodeError(errMsg, codes.ERR_RELAYED_DIAL) + this.#log.error(errMsg) + throw new CodeError(errMsg, ERR_RELAYED_DIAL) } const relayPeer = peerIdFromString(relayId) @@ -241,7 +241,7 @@ class CircuitRelayTransport implements Transport { disconnectOnFailure }) } catch (err: any) { - log.error('circuit relay dial to destination %p via relay %p failed', destinationPeer, relayPeer, err) + this.#log.error('circuit relay dial to destination %p via relay %p failed', destinationPeer, relayPeer, err) if (stream != null) { stream.abort(err) @@ -273,7 +273,7 @@ class CircuitRelayTransport implements Transport { const status = await hopstr.read() if (status.status !== Status.OK) { - throw new CodeError(`failed to connect via relay with status ${status?.status?.toString() ?? 'undefined'}`, codes.ERR_HOP_REQUEST_FAILED) + throw new CodeError(`failed to connect via relay with status ${status?.status?.toString() ?? 'undefined'}`, ERR_HOP_REQUEST_FAILED) } const maConn = streamToMaConnection({ @@ -282,12 +282,12 @@ class CircuitRelayTransport implements Transport { localAddr: relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toString()}`) }) - log('new outbound transient connection %a', maConn.remoteAddr) + this.#log('new outbound transient connection %a', maConn.remoteAddr) return await this.upgrader.upgradeOutbound(maConn, { transient: true }) } catch (err) { - log.error(`Circuit relay dial to destination ${destinationPeer.toString()} via relay ${connection.remotePeer.toString()} failed`, err) + this.#log.error(`Circuit relay dial to destination ${destinationPeer.toString()} via relay ${connection.remotePeer.toString()} failed`, err) disconnectOnFailure && await connection.close() throw err } @@ -299,7 +299,8 @@ class CircuitRelayTransport implements Transport { createListener (options: CreateListenerOptions): Listener { return createListener({ connectionManager: this.connectionManager, - relayStore: this.reservationStore + relayStore: this.reservationStore, + logger: this.logger }) } @@ -327,10 +328,10 @@ class CircuitRelayTransport implements Transport { signal }) - log('new circuit relay v2 stop stream from %p with type %s', connection.remotePeer, request.type) + this.#log('new circuit relay v2 stop stream from %p with type %s', connection.remotePeer, request.type) if (request?.type === undefined) { - log.error('type was missing from circuit v2 stop protocol request from %s', connection.remotePeer) + this.#log.error('type was missing from circuit v2 stop protocol request from %s', connection.remotePeer) await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, { signal }) @@ -340,7 +341,7 @@ class CircuitRelayTransport implements Transport { // Validate the STOP request has the required input if (request.type !== StopMessage.Type.CONNECT) { - log.error('invalid stop connect request via peer %p', connection.remotePeer) + this.#log.error('invalid stop connect request via peer %p', connection.remotePeer) await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE }, { signal }) @@ -349,7 +350,7 @@ class CircuitRelayTransport implements Transport { } if (!isValidStop(request)) { - log.error('invalid stop connect request via peer %p', connection.remotePeer) + this.#log.error('invalid stop connect request via peer %p', connection.remotePeer) await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, { signal }) @@ -360,7 +361,7 @@ class CircuitRelayTransport implements Transport { const remotePeerId = peerIdFromBytes(request.peer.id) if ((await this.connectionGater.denyInboundRelayedConnection?.(connection.remotePeer, remotePeerId)) === true) { - log.error('connection gater denied inbound relayed connection from %p', connection.remotePeer) + this.#log.error('connection gater denied inbound relayed connection from %p', connection.remotePeer) await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }, { signal }) @@ -368,7 +369,7 @@ class CircuitRelayTransport implements Transport { return } - log.trace('sending success response to %p', connection.remotePeer) + this.#log.trace('sending success response to %p', connection.remotePeer) await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.OK }, { signal }) @@ -381,11 +382,11 @@ class CircuitRelayTransport implements Transport { localAddr }) - log('new inbound transient connection %a', maConn.remoteAddr) + this.#log('new inbound transient connection %a', maConn.remoteAddr) await this.upgrader.upgradeInbound(maConn, { transient: true }) - log('%s connection %a upgraded', 'inbound', maConn.remoteAddr) + this.#log('%s connection %a upgraded', 'inbound', maConn.remoteAddr) } } diff --git a/packages/libp2p/src/circuit-relay/transport/listener.ts b/packages/transport-circuit-relay-v2/src/transport/listener.ts similarity index 86% rename from packages/libp2p/src/circuit-relay/transport/listener.ts rename to packages/transport-circuit-relay-v2/src/transport/listener.ts index d7718c7698..8767c4662c 100644 --- a/packages/libp2p/src/circuit-relay/transport/listener.ts +++ b/packages/transport-circuit-relay-v2/src/transport/listener.ts @@ -1,29 +1,30 @@ import { CodeError } from '@libp2p/interface/errors' import { TypedEventEmitter } from '@libp2p/interface/events' -import { logger } from '@libp2p/logger' import { PeerMap } from '@libp2p/peer-collections' import { multiaddr } from '@multiformats/multiaddr' import type { ReservationStore } from './reservation-store.js' +import type { ComponentLogger, Logger } from '@libp2p/interface' import type { PeerId } from '@libp2p/interface/peer-id' import type { Listener, ListenerEvents } from '@libp2p/interface/transport' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { Multiaddr } from '@multiformats/multiaddr' -const log = logger('libp2p:circuit-relay:transport:listener') - export interface CircuitRelayTransportListenerComponents { connectionManager: ConnectionManager relayStore: ReservationStore + logger: ComponentLogger } class CircuitRelayTransportListener extends TypedEventEmitter implements Listener { private readonly connectionManager: ConnectionManager private readonly relayStore: ReservationStore private readonly listeningAddrs: PeerMap + readonly #log: Logger constructor (components: CircuitRelayTransportListenerComponents) { super() + this.#log = components.logger.forComponent('libp2p:circuit-relay:transport:listener') this.connectionManager = components.connectionManager this.relayStore = components.relayStore this.listeningAddrs = new PeerMap() @@ -37,7 +38,7 @@ class CircuitRelayTransportListener extends TypedEventEmitter im } async listen (addr: Multiaddr): Promise { - log('listen on %a', addr) + this.#log('listen on %a', addr) // remove the circuit part to get the peer id of the relay const relayAddr = addr.decapsulate('/p2p-circuit') @@ -56,7 +57,7 @@ class CircuitRelayTransportListener extends TypedEventEmitter im } if (this.listeningAddrs.has(relayConn.remotePeer)) { - log('already listening on relay %p', relayConn.remotePeer) + this.#log('already listening on relay %p', relayConn.remotePeer) return } @@ -79,12 +80,12 @@ class CircuitRelayTransportListener extends TypedEventEmitter im #removeRelayPeer (peerId: PeerId): void { const had = this.listeningAddrs.has(peerId) - log('relay peer removed %p - had reservation', peerId, had) + this.#log('relay peer removed %p - had reservation', peerId, had) this.listeningAddrs.delete(peerId) if (had) { - log.trace('removing relay event listener for peer %p', peerId) + this.#log.trace('removing relay event listener for peer %p', peerId) this.relayStore.removeEventListener('relay:removed', this._onRemoveRelayPeer) // Announce listen addresses change this.safeDispatchEvent('close', {}) diff --git a/packages/libp2p/src/circuit-relay/transport/reservation-store.ts b/packages/transport-circuit-relay-v2/src/transport/reservation-store.ts similarity index 87% rename from packages/libp2p/src/circuit-relay/transport/reservation-store.ts rename to packages/transport-circuit-relay-v2/src/transport/reservation-store.ts index 9300357240..c7914a0a92 100644 --- a/packages/libp2p/src/circuit-relay/transport/reservation-store.ts +++ b/packages/transport-circuit-relay-v2/src/transport/reservation-store.ts @@ -1,14 +1,13 @@ import { TypedEventEmitter, type TypedEventTarget } from '@libp2p/interface/events' -import { logger } from '@libp2p/logger' import { PeerMap } from '@libp2p/peer-collections' +import { PeerJobQueue } from '@libp2p/utils/peer-job-queue' import { multiaddr } from '@multiformats/multiaddr' import { pbStream } from 'it-protobuf-stream' -import { PeerJobQueue } from '../../utils/peer-job-queue.js' import { DEFAULT_RESERVATION_CONCURRENCY, RELAY_TAG, RELAY_V2_HOP_CODEC } from '../constants.js' import { HopMessage, Status } from '../pb/index.js' import { getExpirationMilliseconds } from '../utils.js' import type { Reservation } from '../pb/index.js' -import type { Libp2pEvents, AbortOptions } from '@libp2p/interface' +import type { Libp2pEvents, AbortOptions, ComponentLogger, Logger } from '@libp2p/interface' import type { Connection } from '@libp2p/interface/connection' import type { PeerId } from '@libp2p/interface/peer-id' import type { PeerStore } from '@libp2p/interface/peer-store' @@ -16,8 +15,6 @@ import type { Startable } from '@libp2p/interface/startable' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { TransportManager } from '@libp2p/interface-internal/transport-manager' -const log = logger('libp2p:circuit-relay:transport:reservation-store') - // allow refreshing a relay reservation if it will expire in the next 10 minutes const REFRESH_WINDOW = (60 * 1000) * 10 @@ -33,6 +30,7 @@ export interface RelayStoreComponents { transportManager: TransportManager peerStore: PeerStore events: TypedEventTarget + logger: ComponentLogger } export interface RelayStoreInit { @@ -88,10 +86,12 @@ export class ReservationStore extends TypedEventEmitter private readonly maxReservationQueueLength: number private readonly reservationCompletionTimeout: number private started: boolean + readonly #log: Logger constructor (components: RelayStoreComponents, init?: RelayStoreInit) { super() + this.#log = components.logger.forComponent('libp2p:circuit-relay:transport:reservation-store') this.peerId = components.peerId this.connectionManager = components.connectionManager this.transportManager = components.transportManager @@ -141,21 +141,21 @@ export class ReservationStore extends TypedEventEmitter */ async addRelay (peerId: PeerId, type: RelayType): Promise { if (this.peerId.equals(peerId)) { - log('not trying to use self as relay') + this.#log('not trying to use self as relay') return } if (this.reserveQueue.size > this.maxReservationQueueLength) { - log('not adding relay as the queue is full') + this.#log('not adding relay as the queue is full') return } if (this.reserveQueue.hasJob(peerId)) { - log('relay peer is already in the reservation queue') + this.#log('relay peer is already in the reservation queue') return } - log('add relay %p', peerId) + this.#log('add relay %p', peerId) await this.reserveQueue.add(async () => { try { @@ -164,7 +164,7 @@ export class ReservationStore extends TypedEventEmitter if (existingReservation != null) { if (getExpirationMilliseconds(existingReservation.reservation.expire) > REFRESH_WINDOW) { - log('already have reservation on relay peer %p and it expires in more than 10 minutes', peerId) + this.#log('already have reservation on relay peer %p and it expires in more than 10 minutes', peerId) return } @@ -179,7 +179,7 @@ export class ReservationStore extends TypedEventEmitter return acc }, 0) >= this.maxDiscoveredRelays) { - log('already have enough discovered relays') + this.#log('already have enough discovered relays') return } @@ -190,7 +190,7 @@ export class ReservationStore extends TypedEventEmitter }) if (connection.remoteAddr.protoNames().includes('p2p-circuit')) { - log('not creating reservation over relayed connection') + this.#log('not creating reservation over relayed connection') return } @@ -198,7 +198,7 @@ export class ReservationStore extends TypedEventEmitter signal }) - log('created reservation on relay peer %p', peerId) + this.#log('created reservation on relay peer %p', peerId) const expiration = getExpirationMilliseconds(reservation.expire) @@ -208,7 +208,7 @@ export class ReservationStore extends TypedEventEmitter const timeout = setTimeout(() => { this.addRelay(peerId, type).catch(err => { - log.error('could not refresh reservation to relay %p', peerId, err) + this.#log.error('could not refresh reservation to relay %p', peerId, err) }) }, timeoutDuration) @@ -232,7 +232,7 @@ export class ReservationStore extends TypedEventEmitter // listen on multiaddr that only the circuit transport is listening for await this.transportManager.listen([multiaddr(`/p2p/${peerId.toString()}/p2p-circuit`)]) } catch (err) { - log.error('could not reserve slot on %p', peerId, err) + this.#log.error('could not reserve slot on %p', peerId, err) // cancel the renewal timeout if it's been set const reservation = this.reservations.get(peerId) @@ -260,7 +260,7 @@ export class ReservationStore extends TypedEventEmitter async #createReservation (connection: Connection, options: AbortOptions): Promise { options.signal?.throwIfAborted() - log('requesting reservation from %p', connection.remotePeer) + this.#log('requesting reservation from %p', connection.remotePeer) const stream = await connection.newStream(RELAY_V2_HOP_CODEC, options) const pbstr = pbStream(stream) const hopstr = pbstr.pb(HopMessage) @@ -271,7 +271,7 @@ export class ReservationStore extends TypedEventEmitter try { response = await hopstr.read(options) } catch (err: any) { - log.error('error parsing reserve message response from %p because', connection.remotePeer, err) + this.#log.error('error parsing reserve message response from %p because', connection.remotePeer, err) throw err } finally { await stream.close() @@ -282,7 +282,7 @@ export class ReservationStore extends TypedEventEmitter } const errMsg = `reservation failed with status ${response.status ?? 'undefined'}` - log.error(errMsg) + this.#log.error(errMsg) throw new Error(errMsg) } @@ -297,7 +297,7 @@ export class ReservationStore extends TypedEventEmitter return } - log('connection to relay %p closed, removing reservation from local store', peerId) + this.#log('connection to relay %p closed, removing reservation from local store', peerId) clearTimeout(existingReservation.timeout) this.reservations.delete(peerId) @@ -305,7 +305,7 @@ export class ReservationStore extends TypedEventEmitter this.safeDispatchEvent('relay:removed', { detail: peerId }) if (this.reservations.size < this.maxDiscoveredRelays) { - log('not enough relays %d/%d', this.reservations.size, this.maxDiscoveredRelays) + this.#log('not enough relays %d/%d', this.reservations.size, this.maxDiscoveredRelays) this.safeDispatchEvent('relay:not-enough-relays', {}) } } diff --git a/packages/libp2p/src/circuit-relay/utils.ts b/packages/transport-circuit-relay-v2/src/utils.ts similarity index 86% rename from packages/libp2p/src/circuit-relay/utils.ts rename to packages/transport-circuit-relay-v2/src/utils.ts index 550c80a177..4349bd2ae3 100644 --- a/packages/libp2p/src/circuit-relay/utils.ts +++ b/packages/transport-circuit-relay-v2/src/utils.ts @@ -3,15 +3,16 @@ import { logger } from '@libp2p/logger' import { anySignal } from 'any-signal' import { CID } from 'multiformats/cid' import { sha256 } from 'multiformats/hashes/sha2' -import { codes } from '../errors.js' +import { ERR_TRANSFER_LIMIT_EXCEEDED } from './constants.js' import type { Limit } from './pb/index.js' +import type { LoggerOptions } from '@libp2p/interface' import type { Stream } from '@libp2p/interface/connection' import type { Source } from 'it-stream-types' import type { Uint8ArrayList } from 'uint8arraylist' const log = logger('libp2p:circuit-relay:utils') -async function * countStreamBytes (source: Source, limit: { remaining: bigint }): AsyncGenerator { +async function * countStreamBytes (source: Source, limit: { remaining: bigint }, options: LoggerOptions): AsyncGenerator { const limitBytes = limit.remaining for await (const buf of source) { @@ -27,10 +28,10 @@ async function * countStreamBytes (source: Source, yield buf.subarray(0, remaining) } } catch (err: any) { - log.error(err) + options.log.error(err) } - throw new CodeError(`data limit of ${limitBytes} bytes exceeded`, codes.ERR_TRANSFER_LIMIT_EXCEEDED) + throw new CodeError(`data limit of ${limitBytes} bytes exceeded`, ERR_TRANSFER_LIMIT_EXCEEDED) } limit.remaining -= len @@ -38,7 +39,7 @@ async function * countStreamBytes (source: Source, } } -export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: AbortSignal, limit?: Limit): void { +export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: AbortSignal, limit: Limit | undefined, options: LoggerOptions): void { function abortStreams (err: Error): void { src.abort(err) dst.abort(err) @@ -69,12 +70,12 @@ export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: Abort queueMicrotask(() => { const onAbort = (): void => { - dst.abort(new CodeError(`duration limit of ${limit?.duration} ms exceeded`, codes.ERR_TRANSFER_LIMIT_EXCEEDED)) + dst.abort(new CodeError(`duration limit of ${limit?.duration} ms exceeded`, ERR_TRANSFER_LIMIT_EXCEEDED)) } signal.addEventListener('abort', onAbort, { once: true }) - void dst.sink(dataLimit == null ? src.source : countStreamBytes(src.source, dataLimit)) + void dst.sink(dataLimit == null ? src.source : countStreamBytes(src.source, dataLimit, options)) .catch(err => { log.error('error while relaying streams src -> dst', err) abortStreams(err) @@ -92,12 +93,12 @@ export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: Abort queueMicrotask(() => { const onAbort = (): void => { - src.abort(new CodeError(`duration limit of ${limit?.duration} ms exceeded`, codes.ERR_TRANSFER_LIMIT_EXCEEDED)) + src.abort(new CodeError(`duration limit of ${limit?.duration} ms exceeded`, ERR_TRANSFER_LIMIT_EXCEEDED)) } signal.addEventListener('abort', onAbort, { once: true }) - void src.sink(dataLimit == null ? dst.source : countStreamBytes(dst.source, dataLimit)) + void src.sink(dataLimit == null ? dst.source : countStreamBytes(dst.source, dataLimit, options)) .catch(err => { log.error('error while relaying streams dst -> src', err) abortStreams(err) diff --git a/packages/libp2p/test/circuit-relay/hop.spec.ts b/packages/transport-circuit-relay-v2/test/hop.spec.ts similarity index 96% rename from packages/libp2p/test/circuit-relay/hop.spec.ts rename to packages/transport-circuit-relay-v2/test/hop.spec.ts index b1beb7abb9..a6ff4358be 100644 --- a/packages/libp2p/test/circuit-relay/hop.spec.ts +++ b/packages/transport-circuit-relay-v2/test/hop.spec.ts @@ -1,9 +1,9 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ['error', 5] */ +/* eslint-disable max-nested-callbacks */ import { TypedEventEmitter, type TypedEventTarget } from '@libp2p/interface/events' import { isStartable } from '@libp2p/interface/startable' import { mockRegistrar, mockUpgrader, mockNetwork, mockConnectionManager, mockConnectionGater } from '@libp2p/interface-compliance-tests/mocks' +import { defaultLogger } from '@libp2p/logger' import { PeerMap } from '@libp2p/peer-collections' import { createEd25519PeerId } from '@libp2p/peer-id-factory' import { type Multiaddr, multiaddr } from '@multiformats/multiaddr' @@ -11,11 +11,10 @@ import { expect } from 'aegir/chai' import { type MessageStream, pbStream } from 'it-protobuf-stream' import Sinon from 'sinon' import { type StubbedInstance, stubInterface } from 'sinon-ts' -import { DEFAULT_MAX_RESERVATION_STORE_SIZE, RELAY_SOURCE_TAG, RELAY_V2_HOP_CODEC } from '../../src/circuit-relay/constants.js' -import { circuitRelayServer, type CircuitRelayService, circuitRelayTransport } from '../../src/circuit-relay/index.js' -import { HopMessage, Status } from '../../src/circuit-relay/pb/index.js' -import { matchPeerId } from '../fixtures/match-peer-id.js' -import type { CircuitRelayServerInit } from '../../src/circuit-relay/server/index.js' +import { DEFAULT_MAX_RESERVATION_STORE_SIZE, RELAY_SOURCE_TAG, RELAY_V2_HOP_CODEC } from '../src/constants.js' +import { circuitRelayServer, type CircuitRelayService, circuitRelayTransport } from '../src/index.js' +import { HopMessage, Status } from '../src/pb/index.js' +import type { CircuitRelayServerInit } from '../src/server/index.js' import type { Libp2pEvents } from '@libp2p/interface' import type { Connection, Stream } from '@libp2p/interface/connection' import type { ConnectionGater } from '@libp2p/interface/connection-gater' @@ -28,6 +27,10 @@ import type { ConnectionManager } from '@libp2p/interface-internal/connection-ma import type { Registrar } from '@libp2p/interface-internal/registrar' import type { TransportManager } from '@libp2p/interface-internal/transport-manager' +export function matchPeerId (peerId: PeerId): Sinon.SinonMatcher { + return Sinon.match(p => p.toString() === peerId.toString()) +} + interface Node { peerId: PeerId multiaddr: Multiaddr @@ -96,7 +99,8 @@ describe('circuit-relay hop protocol', function () { peerId, peerStore, registrar, - connectionGater + connectionGater, + logger: defaultLogger() }) if (isStartable(service)) { @@ -113,7 +117,8 @@ describe('circuit-relay hop protocol', function () { transportManager: stubInterface(), upgrader, connectionGater, - events + events, + logger: defaultLogger() }) if (isStartable(transport)) { diff --git a/packages/libp2p/test/circuit-relay/reservation-store.spec.ts b/packages/transport-circuit-relay-v2/test/reservation-store.spec.ts similarity index 83% rename from packages/libp2p/test/circuit-relay/reservation-store.spec.ts rename to packages/transport-circuit-relay-v2/test/reservation-store.spec.ts index 4bc026043a..c42d720f52 100644 --- a/packages/libp2p/test/circuit-relay/reservation-store.spec.ts +++ b/packages/transport-circuit-relay-v2/test/reservation-store.spec.ts @@ -1,16 +1,16 @@ /* eslint-env mocha */ +import { createEd25519PeerId } from '@libp2p/peer-id-factory' import { multiaddr } from '@multiformats/multiaddr' import { expect } from 'aegir/chai' -import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT } from '../../src/circuit-relay/constants.js' -import { Status } from '../../src/circuit-relay/pb/index.js' -import { ReservationStore } from '../../src/circuit-relay/server/reservation-store.js' -import { createPeerId } from '../fixtures/creators/peer.js' +import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT } from '../src/constants.js' +import { Status } from '../src/pb/index.js' +import { ReservationStore } from '../src/server/reservation-store.js' describe('circuit-relay server reservation store', function () { it('should add reservation', async function () { const store = new ReservationStore({ maxReservations: 2 }) - const peer = await createPeerId() + const peer = await createEd25519PeerId() const result = store.reserve(peer, multiaddr()) expect(result.status).to.equal(Status.OK) expect(result.expire).to.not.be.undefined() @@ -19,7 +19,7 @@ describe('circuit-relay server reservation store', function () { it('should add reservation if peer already has reservation', async function () { const store = new ReservationStore({ maxReservations: 1 }) - const peer = await createPeerId() + const peer = await createEd25519PeerId() store.reserve(peer, multiaddr()) const result = store.reserve(peer, multiaddr()) expect(result.status).to.equal(Status.OK) @@ -29,14 +29,14 @@ describe('circuit-relay server reservation store', function () { it('should fail to add reservation on exceeding limit', async function () { const store = new ReservationStore({ maxReservations: 0 }) - const peer = await createPeerId() + const peer = await createEd25519PeerId() const result = store.reserve(peer, multiaddr()) expect(result.status).to.equal(Status.RESERVATION_REFUSED) }) it('should remove reservation', async function () { const store = new ReservationStore({ maxReservations: 10 }) - const peer = await createPeerId() + const peer = await createEd25519PeerId() const result = store.reserve(peer, multiaddr()) expect(result.status).to.equal(Status.OK) expect(store.hasReservation(peer)).to.be.true() @@ -53,7 +53,7 @@ describe('circuit-relay server reservation store', function () { defaultDataLimit, defaultDurationLimit }) - const peer = await createPeerId() + const peer = await createEd25519PeerId() store.reserve(peer, multiaddr()) const reservation = store.get(peer) @@ -64,7 +64,7 @@ describe('circuit-relay server reservation store', function () { it('should apply default connection limits', async function () { const store = new ReservationStore() - const peer = await createPeerId() + const peer = await createEd25519PeerId() store.reserve(peer, multiaddr()) const reservation = store.get(peer) @@ -77,7 +77,7 @@ describe('circuit-relay server reservation store', function () { const store = new ReservationStore({ applyDefaultLimit: false }) - const peer = await createPeerId() + const peer = await createEd25519PeerId() store.reserve(peer, multiaddr()) const reservation = store.get(peer) diff --git a/packages/libp2p/test/circuit-relay/stop.spec.ts b/packages/transport-circuit-relay-v2/test/stop.spec.ts similarity index 95% rename from packages/libp2p/test/circuit-relay/stop.spec.ts rename to packages/transport-circuit-relay-v2/test/stop.spec.ts index f17642b951..e6725e8f2d 100644 --- a/packages/libp2p/test/circuit-relay/stop.spec.ts +++ b/packages/transport-circuit-relay-v2/test/stop.spec.ts @@ -3,6 +3,7 @@ import { TypedEventEmitter } from '@libp2p/interface/events' import { isStartable } from '@libp2p/interface/startable' import { mockStream } from '@libp2p/interface-compliance-tests/mocks' +import { defaultLogger } from '@libp2p/logger' import { createEd25519PeerId } from '@libp2p/peer-id-factory' import { expect } from 'aegir/chai' import delay from 'delay' @@ -10,8 +11,8 @@ import { duplexPair } from 'it-pair/duplex' import { pbStream, type MessageStream } from 'it-protobuf-stream' import Sinon from 'sinon' import { stubInterface } from 'sinon-ts' -import { circuitRelayTransport } from '../../src/circuit-relay/index.js' -import { Status, StopMessage } from '../../src/circuit-relay/pb/index.js' +import { circuitRelayTransport } from '../src/index.js' +import { Status, StopMessage } from '../src/pb/index.js' import type { Connection, Stream } from '@libp2p/interface/connection' import type { ConnectionGater } from '@libp2p/interface/connection-gater' import type { ContentRouting } from '@libp2p/interface/content-routing' @@ -43,7 +44,8 @@ describe('circuit-relay stop protocol', function () { transportManager: stubInterface(), upgrader: stubInterface(), connectionGater: stubInterface(), - events: new TypedEventEmitter() + events: new TypedEventEmitter(), + logger: defaultLogger() } transport = circuitRelayTransport({ diff --git a/packages/libp2p/test/circuit-relay/utils.spec.ts b/packages/transport-circuit-relay-v2/test/utils.spec.ts similarity index 94% rename from packages/libp2p/test/circuit-relay/utils.spec.ts rename to packages/transport-circuit-relay-v2/test/utils.spec.ts index 71e0b8b2e4..c75852764e 100644 --- a/packages/libp2p/test/circuit-relay/utils.spec.ts +++ b/packages/transport-circuit-relay-v2/test/utils.spec.ts @@ -1,5 +1,6 @@ /* eslint-env mocha */ +import { type Logger } from '@libp2p/interface' import { mockStream } from '@libp2p/interface-compliance-tests/mocks' import { expect } from 'aegir/chai' import delay from 'delay' @@ -7,8 +8,9 @@ import drain from 'it-drain' import { pushable } from 'it-pushable' import toBuffer from 'it-to-buffer' import Sinon from 'sinon' +import { stubInterface } from 'sinon-ts' import { fromString as uint8arrayFromString } from 'uint8arrays/from-string' -import { createLimitedRelay, getExpirationMilliseconds, namespaceToCid } from '../../src/circuit-relay/utils.js' +import { createLimitedRelay, getExpirationMilliseconds, namespaceToCid } from '../src/utils.js' import type { Duplex, Source } from 'it-stream-types' describe('circuit-relay utils', () => { @@ -48,7 +50,9 @@ describe('circuit-relay utils', () => { const localStreamAbortSpy = Sinon.spy(localStream, 'abort') const remoteStreamAbortSpy = Sinon.spy(remoteStream, 'abort') - createLimitedRelay(localStream, remoteStream, controller.signal) + createLimitedRelay(localStream, remoteStream, controller.signal, undefined, { + log: stubInterface() + }) expect(await toBuffer(received)).to.have.property('byteLength', 12) expect(localStreamAbortSpy).to.have.property('called', false) @@ -94,7 +98,9 @@ describe('circuit-relay utils', () => { const localStreamAbortSpy = Sinon.spy(localStream, 'abort') const remoteStreamAbortSpy = Sinon.spy(remoteStream, 'abort') - createLimitedRelay(localStream, remoteStream, controller.signal, limit) + createLimitedRelay(localStream, remoteStream, controller.signal, limit, { + log: stubInterface() + }) expect(await toBuffer(received)).to.have.property('byteLength', 5) expect(localStreamAbortSpy).to.have.property('called', true) @@ -152,7 +158,9 @@ describe('circuit-relay utils', () => { const localStreamAbortSpy = Sinon.spy(localStream, 'abort') const remoteStreamAbortSpy = Sinon.spy(remoteStream, 'abort') - createLimitedRelay(localStream, remoteStream, controller.signal, limit) + createLimitedRelay(localStream, remoteStream, controller.signal, limit, { + log: stubInterface() + }) expect(await toBuffer(received)).to.have.property('byteLength', 5) expect(localStreamAbortSpy).to.have.property('called', true) @@ -199,7 +207,9 @@ describe('circuit-relay utils', () => { const localStreamAbortSpy = Sinon.spy(localStream, 'abort') const remoteStreamAbortSpy = Sinon.spy(remoteStream, 'abort') - createLimitedRelay(localStream, remoteStream, controller.signal, limit) + createLimitedRelay(localStream, remoteStream, controller.signal, limit, { + log: stubInterface() + }) expect(await toBuffer(received)).to.have.property('byteLength', 12) expect(localStreamAbortSpy).to.have.property('called', true) diff --git a/packages/libp2p/test/circuit-relay/utils.ts b/packages/transport-circuit-relay-v2/test/utils.ts similarity index 98% rename from packages/libp2p/test/circuit-relay/utils.ts rename to packages/transport-circuit-relay-v2/test/utils.ts index b530b8d241..e0db1f8091 100644 --- a/packages/libp2p/test/circuit-relay/utils.ts +++ b/packages/transport-circuit-relay-v2/test/utils.ts @@ -1,7 +1,7 @@ import { peerIdFromString } from '@libp2p/peer-id' import pWaitFor from 'p-wait-for' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' -import { RELAY_V2_HOP_CODEC } from '../../src/circuit-relay/constants.js' +import { RELAY_V2_HOP_CODEC } from '../../../packages/transport-circuit-relay-v2/src/constants.js' import type { Libp2p, AbortOptions } from '@libp2p/interface' import type { ContentRouting } from '@libp2p/interface/content-routing' import type { PeerId } from '@libp2p/interface/peer-id' diff --git a/packages/transport-circuit-relay-v2/tsconfig.json b/packages/transport-circuit-relay-v2/tsconfig.json new file mode 100644 index 0000000000..c550b0926f --- /dev/null +++ b/packages/transport-circuit-relay-v2/tsconfig.json @@ -0,0 +1,39 @@ +{ + "extends": "aegir/src/config/tsconfig.aegir.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src", + "test" + ], + "references": [ + { + "path": "../interface" + }, + { + "path": "../interface-compliance-tests" + }, + { + "path": "../interface-internal" + }, + { + "path": "../logger" + }, + { + "path": "../peer-collections" + }, + { + "path": "../peer-id" + }, + { + "path": "../peer-id-factory" + }, + { + "path": "../peer-record" + }, + { + "path": "../utils" + } + ] +} diff --git a/packages/transport-circuit-relay-v2/typedoc.json b/packages/transport-circuit-relay-v2/typedoc.json new file mode 100644 index 0000000000..f599dc728d --- /dev/null +++ b/packages/transport-circuit-relay-v2/typedoc.json @@ -0,0 +1,5 @@ +{ + "entryPoints": [ + "./src/index.ts" + ] +} diff --git a/packages/transport-webrtc/.aegir.js b/packages/transport-webrtc/.aegir.js index 3b38600b7a..02e55efc8a 100644 --- a/packages/transport-webrtc/.aegir.js +++ b/packages/transport-webrtc/.aegir.js @@ -7,7 +7,7 @@ export default { test: { before: async () => { const { createLibp2p } = await import('libp2p') - const { circuitRelayServer } = await import('libp2p/circuit-relay') + const { circuitRelayServer } = await import('@libp2p/circuit-relay-v2') const { webSockets } = await import('@libp2p/websockets') const { noise } = await import('@chainsafe/libp2p-noise') const { yamux } = await import('@chainsafe/libp2p-yamux') diff --git a/packages/transport-webrtc/package.json b/packages/transport-webrtc/package.json index d6a930239d..82e625f1e5 100644 --- a/packages/transport-webrtc/package.json +++ b/packages/transport-webrtc/package.json @@ -75,6 +75,7 @@ }, "devDependencies": { "@chainsafe/libp2p-yamux": "^5.0.0", + "@libp2p/circuit-relay-v2": "^0.0.0", "@libp2p/interface-compliance-tests": "^4.1.5", "@libp2p/peer-id-factory": "^3.0.8", "@libp2p/websockets": "^7.0.13", diff --git a/packages/transport-webrtc/test/basics.spec.ts b/packages/transport-webrtc/test/basics.spec.ts index 21725935e2..10b8d09dc2 100644 --- a/packages/transport-webrtc/test/basics.spec.ts +++ b/packages/transport-webrtc/test/basics.spec.ts @@ -2,6 +2,7 @@ import { noise } from '@chainsafe/libp2p-noise' import { yamux } from '@chainsafe/libp2p-yamux' +import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' import { webSockets } from '@libp2p/websockets' import * as filter from '@libp2p/websockets/filters' import { WebRTC } from '@multiformats/mafmt' @@ -13,7 +14,6 @@ import { pipe } from 'it-pipe' import { pushable } from 'it-pushable' import toBuffer from 'it-to-buffer' import { createLibp2p } from 'libp2p' -import { circuitRelayTransport } from 'libp2p/circuit-relay' import pDefer from 'p-defer' import pRetry from 'p-retry' import { webRTC } from '../src/index.js' diff --git a/packages/transport-webrtc/tsconfig.json b/packages/transport-webrtc/tsconfig.json index 4008808465..8fac17524d 100644 --- a/packages/transport-webrtc/tsconfig.json +++ b/packages/transport-webrtc/tsconfig.json @@ -30,6 +30,9 @@ { "path": "../peer-id-factory" }, + { + "path": "../transport-circuit-relay-v2" + }, { "path": "../transport-websockets" } diff --git a/packages/utils/package.json b/packages/utils/package.json index 2e4f067ce1..67f2237361 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -60,6 +60,10 @@ "types": "./dist/src/multiaddr/is-private.d.ts", "import": "./dist/src/multiaddr/is-private.js" }, + "./peer-job-queue": { + "types": "./dist/src/peer-job-queue.d.ts", + "import": "./dist/src/peer-job-queue.js" + }, "./stream-to-ma-conn": { "types": "./dist/src/stream-to-ma-conn.d.ts", "import": "./dist/src/stream-to-ma-conn.js" @@ -93,14 +97,17 @@ "@multiformats/multiaddr-matcher": "^1.0.1", "is-loopback-addr": "^2.0.1", "it-stream-types": "^2.0.1", + "p-queue": "^7.4.1", "private-ip": "^3.0.0", "uint8arraylist": "^2.4.3" }, "devDependencies": { + "@libp2p/peer-id-factory": "^3.0.8", "aegir": "^41.0.2", "it-all": "^3.0.1", "it-pair": "^2.0.6", "it-pipe": "^3.0.1", + "p-defer": "^4.0.0", "uint8arrays": "^4.0.4" } } diff --git a/packages/libp2p/src/utils/peer-job-queue.ts b/packages/utils/src/peer-job-queue.ts similarity index 94% rename from packages/libp2p/src/utils/peer-job-queue.ts rename to packages/utils/src/peer-job-queue.ts index 1b38e509b8..6711798007 100644 --- a/packages/libp2p/src/utils/peer-job-queue.ts +++ b/packages/utils/src/peer-job-queue.ts @@ -1,8 +1,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { CodeError } from '@libp2p/interface/errors' +import { CodeError, ERR_INVALID_PARAMETERS } from '@libp2p/interface/errors' import PQueue from 'p-queue' -import { codes } from '../errors.js' import type { PeerId } from '@libp2p/interface/peer-id' import type { QueueAddOptions, Options, Queue } from 'p-queue' @@ -51,7 +50,7 @@ class PeerPriorityQueue implements Queue const priority = options?.priority ?? 0 if (peerId == null) { - throw new CodeError('missing peer id', codes.ERR_INVALID_PARAMETERS) + throw new CodeError('missing peer id', ERR_INVALID_PARAMETERS) } const element: PeerJob = { diff --git a/packages/libp2p/test/utils/peer-job-queue.spec.ts b/packages/utils/test/peer-job-queue.spec.ts similarity index 92% rename from packages/libp2p/test/utils/peer-job-queue.spec.ts rename to packages/utils/test/peer-job-queue.spec.ts index a57018cf21..891e1b7f81 100644 --- a/packages/libp2p/test/utils/peer-job-queue.spec.ts +++ b/packages/utils/test/peer-job-queue.spec.ts @@ -3,7 +3,7 @@ import { createEd25519PeerId } from '@libp2p/peer-id-factory' import { expect } from 'aegir/chai' import pDefer from 'p-defer' -import { PeerJobQueue } from '../../src/utils/peer-job-queue.js' +import { PeerJobQueue } from '../src/peer-job-queue.js' describe('peer job queue', () => { it('should have jobs', async () => { diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json index b382e52ffe..3206b9ea4d 100644 --- a/packages/utils/tsconfig.json +++ b/packages/utils/tsconfig.json @@ -13,6 +13,9 @@ }, { "path": "../logger" + }, + { + "path": "../peer-id-factory" } ] } diff --git a/packages/utils/typedoc.json b/packages/utils/typedoc.json index 3af8fd78c0..e7473ae4a3 100644 --- a/packages/utils/typedoc.json +++ b/packages/utils/typedoc.json @@ -6,6 +6,7 @@ "./src/ip-port-to-multiaddr.ts", "./src/multiaddr/is-loopback.ts", "./src/multiaddr/is-private.ts", + "./src/peer-job-queue.ts", "./src/stream-to-ma-conn.ts" ] }