diff --git a/packages/connection-encrypter-plaintext/src/index.ts b/packages/connection-encrypter-plaintext/src/index.ts index 8b286535a0..af482b25c7 100644 --- a/packages/connection-encrypter-plaintext/src/index.ts +++ b/packages/connection-encrypter-plaintext/src/index.ts @@ -20,7 +20,7 @@ * ``` */ -import { UnexpectedPeerError, InvalidCryptoExchangeError } from '@libp2p/interface' +import { UnexpectedPeerError, InvalidCryptoExchangeError, serviceCapabilities } from '@libp2p/interface' import { peerIdFromBytes, peerIdFromKeys } from '@libp2p/peer-id' import { pbStream } from 'it-protobuf-stream' import { Exchange, KeyType } from './pb/proto.js' @@ -52,6 +52,12 @@ class Plaintext implements ConnectionEncrypter { this.timeout = init.timeout ?? 1000 } + readonly [Symbol.toStringTag] = '@libp2p/plaintext' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/connection-encryption' + ] + async secureInbound > = MultiaddrConnection> (localId: PeerId, conn: Stream, remoteId?: PeerId): Promise> { return this._encrypt(localId, conn, remoteId) } diff --git a/packages/connection-encrypter-tls/src/tls.ts b/packages/connection-encrypter-tls/src/tls.ts index ffe3750c93..d0faacb74b 100644 --- a/packages/connection-encrypter-tls/src/tls.ts +++ b/packages/connection-encrypter-tls/src/tls.ts @@ -19,7 +19,7 @@ */ import { TLSSocket, type TLSSocketOptions, connect } from 'node:tls' -import { CodeError } from '@libp2p/interface' +import { CodeError, serviceCapabilities } from '@libp2p/interface' import { generateCertificate, verifyPeerCertificate, itToStream, streamToIt } from './utils.js' import { PROTOCOL } from './index.js' import type { TLSComponents, TLSInit } from './index.js' @@ -37,6 +37,12 @@ export class TLS implements ConnectionEncrypter { this.timeout = init.timeout ?? 1000 } + readonly [Symbol.toStringTag] = '@libp2p/tls' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/connection-encryption' + ] + async secureInbound > = MultiaddrConnection> (localId: PeerId, conn: Stream, remoteId?: PeerId): Promise> { return this._encrypt(localId, conn, true, remoteId) } diff --git a/packages/integration-tests/test/circuit-relay.node.ts b/packages/integration-tests/test/circuit-relay.node.ts index fe5600b198..bdfcf6fba1 100644 --- a/packages/integration-tests/test/circuit-relay.node.ts +++ b/packages/integration-tests/test/circuit-relay.node.ts @@ -758,7 +758,10 @@ describe('circuit-relay', () => { circuitRelayTransport({ discoverRelays: 1 }) - ] + ], + services: { + identify: identify() + } }), createClient({ transports: [ @@ -766,7 +769,10 @@ describe('circuit-relay', () => { circuitRelayTransport({ discoverRelays: 1 }) - ] + ], + services: { + identify: identify() + } }), createRelay({ services: { @@ -846,7 +852,10 @@ describe('circuit-relay', () => { circuitRelayTransport({ discoverRelays: 1 }) - ] + ], + services: { + identify: identify() + } }), createClient({ transports: [ @@ -854,7 +863,10 @@ describe('circuit-relay', () => { circuitRelayTransport({ discoverRelays: 1 }) - ] + ], + services: { + identify: identify() + } }), createRelay({ services: { @@ -1010,7 +1022,8 @@ describe('circuit-relay', () => { defaultDurationLimit, applyDefaultLimit: false } - }) + }), + identify: identify() } }) @@ -1023,7 +1036,8 @@ describe('circuit-relay', () => { ] }, services: { - echoService + echoService, + identify: identify() } }) ]) diff --git a/packages/integration-tests/test/fixtures/base-options.browser.ts b/packages/integration-tests/test/fixtures/base-options.browser.ts index 68bf71f40b..2d826397c0 100644 --- a/packages/integration-tests/test/fixtures/base-options.browser.ts +++ b/packages/integration-tests/test/fixtures/base-options.browser.ts @@ -1,4 +1,6 @@ +import { yamux } from '@chainsafe/libp2p-yamux' import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' +import { identify } from '@libp2p/identify' import { mockConnectionGater } from '@libp2p/interface-compliance-tests/mocks' import { mplex } from '@libp2p/mplex' import { plaintext } from '@libp2p/plaintext' @@ -24,12 +26,16 @@ export function createBaseOptions implements Ka } } + readonly [Symbol.toStringTag] = '@libp2p/kad-dht' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/content-routing', + '@libp2p/peer-routing', + '@libp2p/peer-discovery' + ] + + readonly [serviceDependencies]: string[] = [ + '@libp2p/identify' + ] + get [contentRoutingSymbol] (): ContentRouting { return this.dhtContentRouting } diff --git a/packages/keychain/src/keychain.ts b/packages/keychain/src/keychain.ts index aafdf534c5..898bf200ee 100644 --- a/packages/keychain/src/keychain.ts +++ b/packages/keychain/src/keychain.ts @@ -2,7 +2,7 @@ import { pbkdf2, randomBytes } from '@libp2p/crypto' import { generateKeyPair, importKey, unmarshalPrivateKey } from '@libp2p/crypto/keys' -import { CodeError } from '@libp2p/interface' +import { CodeError, serviceCapabilities } from '@libp2p/interface' import { peerIdFromKeys } from '@libp2p/peer-id' import { Key } from 'interface-datastore/key' import mergeOptions from 'merge-options' @@ -119,6 +119,12 @@ export class DefaultKeychain implements Keychain { privates.set(this, { dek }) } + readonly [Symbol.toStringTag] = '@libp2p/keychain' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/keychain' + ] + /** * Generates the options for a keychain. A random salt is produced. * diff --git a/packages/libp2p/src/address-manager/index.ts b/packages/libp2p/src/address-manager/index.ts index 7dd7c4ca5c..f7e076bf0e 100644 --- a/packages/libp2p/src/address-manager/index.ts +++ b/packages/libp2p/src/address-manager/index.ts @@ -108,6 +108,8 @@ export class DefaultAddressManager { }) } + readonly [Symbol.toStringTag] = '@libp2p/address-manager' + _updatePeerStoreAddresses (): void { // if announce addresses have been configured, ensure they make it into our peer // record for things like identify diff --git a/packages/libp2p/src/components.ts b/packages/libp2p/src/components.ts index 724f8f28a5..508ab2fc33 100644 --- a/packages/libp2p/src/components.ts +++ b/packages/libp2p/src/components.ts @@ -1,4 +1,4 @@ -import { CodeError } from '@libp2p/interface' +import { CodeError, serviceCapabilities, serviceDependencies } from '@libp2p/interface' import { isStartable, type Startable, type Libp2pEvents, type ComponentLogger, type NodeInfo, type ConnectionProtector, type ConnectionGater, type ContentRouting, type TypedEventTarget, type Metrics, type PeerId, type PeerRouting, type PeerStore, type PrivateKey, type Upgrader } from '@libp2p/interface' import { defaultLogger } from '@libp2p/logger' import type { AddressManager, ConnectionManager, RandomWalk, Registrar, TransportManager } from '@libp2p/interface-internal' @@ -157,3 +157,41 @@ export function defaultComponents (init: ComponentsInit = {}): Components { // @ts-expect-error component keys are proxied return proxy } + +export function checkServiceDependencies (components: Components): void { + const serviceCapabilities: Record = {} + + for (const service of Object.values(components.components)) { + for (const capability of getServiceCapabilities(service)) { + serviceCapabilities[capability] = true + } + } + + for (const service of Object.values(components.components)) { + for (const capability of getServiceDependencies(service)) { + if (serviceCapabilities[capability] !== true) { + throw new CodeError(`Service "${getServiceName(service)}" required capability "${capability}" but it was not provided by any component, you may need to add additional configuration when creating your node.`, 'ERR_UNMET_SERVICE_DEPENDENCIES') + } + } + } +} + +function getServiceCapabilities (service: any): string[] { + if (Array.isArray(service?.[serviceCapabilities])) { + return service[serviceCapabilities] + } + + return [] +} + +function getServiceDependencies (service: any): string[] { + if (Array.isArray(service?.[serviceDependencies])) { + return service[serviceDependencies] + } + + return [] +} + +function getServiceName (service: any): string { + return service?.[Symbol.toStringTag] ?? service?.toString() ?? 'unknown' +} diff --git a/packages/libp2p/src/connection-manager/index.ts b/packages/libp2p/src/connection-manager/index.ts index caed9b358f..7b6233b35e 100644 --- a/packages/libp2p/src/connection-manager/index.ts +++ b/packages/libp2p/src/connection-manager/index.ts @@ -260,6 +260,8 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { }) } + readonly [Symbol.toStringTag] = '@libp2p/connection-manager' + isStarted (): boolean { return this.started } diff --git a/packages/libp2p/src/content-routing.ts b/packages/libp2p/src/content-routing.ts index ead55495f2..ee878cc5b7 100644 --- a/packages/libp2p/src/content-routing.ts +++ b/packages/libp2p/src/content-routing.ts @@ -26,6 +26,8 @@ export class CompoundContentRouting implements ContentRouting, Startable { this.components = components } + readonly [Symbol.toStringTag] = '@libp2p/content-routing' + isStarted (): boolean { return this.started } diff --git a/packages/libp2p/src/index.ts b/packages/libp2p/src/index.ts index a123bd9e2b..014ec77535 100644 --- a/packages/libp2p/src/index.ts +++ b/packages/libp2p/src/index.ts @@ -24,14 +24,14 @@ import type { PersistentPeerStoreInit } from '@libp2p/peer-store' import type { DNS } from '@multiformats/dns' import type { Datastore } from 'interface-datastore' -export type ServiceFactoryMap = Record> = { +export type ServiceFactoryMap = { [Property in keyof T]: (components: Components & T) => T[Property] } /** * For Libp2p configurations and modules details read the [Configuration Document](https://github.com/libp2p/js-libp2p/tree/main/doc/CONFIGURATION.md). */ -export interface Libp2pInit }> { +export interface Libp2pInit { /** * peerId instance (it will be created if not provided) */ @@ -135,7 +135,7 @@ export interface Libp2pInit export type { Libp2p } -export type Libp2pOptions> = Libp2pInit & { start?: boolean } +export type Libp2pOptions = Libp2pInit & { start?: boolean } /** * Returns a new instance of the Libp2p interface, generating a new PeerId @@ -163,7 +163,7 @@ export type Libp2pOptions> = Libp * const libp2p = await createLibp2p(options) * ``` */ -export async function createLibp2p }> (options: Libp2pOptions = {}): Promise> { +export async function createLibp2p (options: Libp2pOptions = {}): Promise> { const node = await createLibp2pNode(options) if (options.start !== false) { diff --git a/packages/libp2p/src/libp2p.ts b/packages/libp2p/src/libp2p.ts index 424472a92e..880121ac76 100644 --- a/packages/libp2p/src/libp2p.ts +++ b/packages/libp2p/src/libp2p.ts @@ -10,7 +10,7 @@ import { MemoryDatastore } from 'datastore-core/memory' import { concat as uint8ArrayConcat } from 'uint8arrays/concat' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { DefaultAddressManager } from './address-manager/index.js' -import { defaultComponents } from './components.js' +import { checkServiceDependencies, defaultComponents } from './components.js' import { connectionGater } from './config/connection-gater.js' import { validateConfig } from './config.js' import { DefaultConnectionManager } from './connection-manager/index.js' @@ -27,7 +27,7 @@ import type { Libp2p, Libp2pInit, Libp2pOptions } from './index.js' import type { PeerRouting, ContentRouting, Libp2pEvents, PendingDial, ServiceMap, AbortOptions, ComponentLogger, Logger, Connection, NewStreamOptions, Stream, Metrics, PeerId, PeerInfo, PeerStore, Topology, Libp2pStatus, IsDialableOptions } from '@libp2p/interface' import type { StreamHandler, StreamHandlerOptions } from '@libp2p/interface-internal' -export class Libp2pNode> extends TypedEventEmitter implements Libp2p { +export class Libp2pNode extends TypedEventEmitter implements Libp2p { public peerId: PeerId public peerStore: PeerStore public contentRouting: ContentRouting @@ -37,7 +37,7 @@ export class Libp2pNode> extends public logger: ComponentLogger public status: Libp2pStatus - public components: Components & T[keyof T] + public components: Components & T private readonly log: Logger constructor (init: Libp2pInit & Required, 'peerId'>>) { @@ -160,7 +160,6 @@ export class Libp2pNode> extends if (init.services != null) { for (const name of Object.keys(init.services)) { const createService = init.services[name] - // @ts-expect-error components type is not fully formed yet const service: any = createService(this.components) if (service == null) { @@ -189,6 +188,9 @@ export class Libp2pNode> extends } } } + + // Ensure all services have their required dependencies + checkServiceDependencies(components) } private configureComponent (name: string, component: T): T { @@ -409,7 +411,7 @@ export class Libp2pNode> extends * Returns a new Libp2pNode instance - this exposes more of the internals than the * libp2p interface and is useful for testing and debugging. */ -export async function createLibp2pNode > (options: Libp2pOptions = {}): Promise> { +export async function createLibp2pNode (options: Libp2pOptions = {}): Promise> { const peerId = options.peerId ??= await createEd25519PeerId() if (peerId.privateKey == null) { diff --git a/packages/libp2p/src/peer-routing.ts b/packages/libp2p/src/peer-routing.ts index 5e4315ad41..9b311f95a8 100644 --- a/packages/libp2p/src/peer-routing.ts +++ b/packages/libp2p/src/peer-routing.ts @@ -29,6 +29,8 @@ export class DefaultPeerRouting implements PeerRouting { this.routers = init.routers ?? [] } + readonly [Symbol.toStringTag] = '@libp2p/peer-routing' + /** * Iterates over all peer routers in parallel to find the given peer */ diff --git a/packages/libp2p/src/random-walk.ts b/packages/libp2p/src/random-walk.ts index 2146b541f4..646d87f806 100644 --- a/packages/libp2p/src/random-walk.ts +++ b/packages/libp2p/src/random-walk.ts @@ -39,6 +39,8 @@ export class RandomWalk extends TypedEventEmitter implements R setMaxListeners(Infinity, this.shutdownController.signal) } + readonly [Symbol.toStringTag] = '@libp2p/random-walk' + start (): void { this.shutdownController = new AbortController() setMaxListeners(Infinity, this.shutdownController.signal) diff --git a/packages/libp2p/src/registrar.ts b/packages/libp2p/src/registrar.ts index 0936096c41..fe9317fffb 100644 --- a/packages/libp2p/src/registrar.ts +++ b/packages/libp2p/src/registrar.ts @@ -40,6 +40,8 @@ export class DefaultRegistrar implements Registrar { this.components.events.addEventListener('peer:identify', this._onPeerIdentify) } + readonly [Symbol.toStringTag] = '@libp2p/registrar' + getProtocols (): string[] { return Array.from(new Set([ ...this.handlers.keys() diff --git a/packages/libp2p/src/transport-manager.ts b/packages/libp2p/src/transport-manager.ts index e7c49d44c8..55e2b1ea6e 100644 --- a/packages/libp2p/src/transport-manager.ts +++ b/packages/libp2p/src/transport-manager.ts @@ -37,6 +37,8 @@ export class DefaultTransportManager implements TransportManager, Startable { this.faultTolerance = init.faultTolerance ?? FaultTolerance.FATAL_ALL } + readonly [Symbol.toStringTag] = '@libp2p/transport-manager' + /** * Adds a `Transport` to the manager */ diff --git a/packages/libp2p/src/upgrader.ts b/packages/libp2p/src/upgrader.ts index ac4efe74ba..1a6088676e 100644 --- a/packages/libp2p/src/upgrader.ts +++ b/packages/libp2p/src/upgrader.ts @@ -122,6 +122,8 @@ export class DefaultUpgrader implements Upgrader { this.events = components.events } + readonly [Symbol.toStringTag] = '@libp2p/upgrader' + async shouldBlockConnection (remotePeer: PeerId, maConn: MultiaddrConnection, connectionType: ConnectionDeniedType): Promise { const connectionGater = this.components.connectionGater[connectionType] diff --git a/packages/libp2p/test/addresses/addresses.node.ts b/packages/libp2p/test/addresses/addresses.node.ts index 639503ce3c..e49bab1654 100644 --- a/packages/libp2p/test/addresses/addresses.node.ts +++ b/packages/libp2p/test/addresses/addresses.node.ts @@ -13,7 +13,7 @@ import type { Libp2pNode } from '../../src/libp2p.js' import type { PeerUpdate } from '@libp2p/interface' const listenAddresses = ['/ip4/127.0.0.1/tcp/0', '/ip4/127.0.0.1/tcp/8000/ws'] -const announceAddreses = ['/dns4/peer.io/tcp/433/p2p/12D3KooWNvSZnPi3RrhrTwEY4LuuBeB6K6facKUCJcyWG1aoDd2p'] +const announceAddresses = ['/dns4/peer.io/tcp/433/p2p/12D3KooWNvSZnPi3RrhrTwEY4LuuBeB6K6facKUCJcyWG1aoDd2p'] describe('libp2p.addressManager', () => { let libp2p: Libp2pNode @@ -31,7 +31,7 @@ describe('libp2p.addressManager', () => { ...AddressesOptions, addresses: { listen: listenAddresses, - announce: announceAddreses + announce: announceAddresses } } }) @@ -83,7 +83,7 @@ describe('libp2p.addressManager', () => { ...AddressesOptions, addresses: { listen: listenAddresses, - announce: announceAddreses + announce: announceAddresses } } }) @@ -94,10 +94,10 @@ describe('libp2p.addressManager', () => { // Announce 1 announce addr const advertiseMultiaddrs = libp2p.components.addressManager.getAddresses().map((ma) => ma.decapsulateCode(protocols('p2p').code).toString()) - expect(advertiseMultiaddrs.length).to.equal(announceAddreses.length) + expect(advertiseMultiaddrs.length).to.equal(announceAddresses.length) advertiseMultiaddrs.forEach((m) => { expect(tmListen).to.not.include(m) - expect(announceAddreses).to.include(m) + expect(announceAddresses).to.include(m) }) }) @@ -136,7 +136,7 @@ describe('libp2p.addressManager', () => { ...AddressesOptions, addresses: { listen: listenAddresses, - announce: announceAddreses, + announce: announceAddresses, announceFilter: (multiaddrs: Multiaddr[]) => multiaddrs.filter(m => !isLoopback(m)) } } @@ -181,7 +181,7 @@ describe('libp2p.addressManager', () => { config: { addresses: { listen: listenAddresses, - announce: announceAddreses + announce: announceAddresses }, transports: [ webSockets() @@ -192,7 +192,7 @@ describe('libp2p.addressManager', () => { } }) - expect(libp2p.getMultiaddrs().map(ma => ma.decapsulateCode(protocols('p2p').code).toString())).to.have.members(announceAddreses) + expect(libp2p.getMultiaddrs().map(ma => ma.decapsulateCode(protocols('p2p').code).toString())).to.have.members(announceAddresses) expect(libp2p.getMultiaddrs().map(ma => ma.decapsulateCode(protocols('p2p').code).toString())).to.not.have.members(listenAddresses) }) @@ -202,7 +202,7 @@ describe('libp2p.addressManager', () => { config: { addresses: { listen: listenAddresses, - announce: announceAddreses + announce: announceAddresses }, transports: [ webSockets() @@ -213,14 +213,19 @@ describe('libp2p.addressManager', () => { } }) - const eventPromise = pEvent<'self:peer:update', CustomEvent>(libp2p, 'self:peer:update') + const eventPromise = pEvent<'self:peer:update', CustomEvent>(libp2p, 'self:peer:update', { + filter: (event) => { + return event.detail.peer.addresses.map(({ multiaddr }) => multiaddr.toString()) + .includes(announceAddresses[0]) + } + }) await libp2p.start() const event = await eventPromise expect(event.detail.peer.addresses.map(({ multiaddr }) => multiaddr.toString())) - .to.include.members(announceAddreses, 'peer info did not include announce addresses') + .to.include.members(announceAddresses, 'peer info did not include announce addresses') }) it('should only include confirmed observed addresses in peer record', async () => { @@ -229,7 +234,7 @@ describe('libp2p.addressManager', () => { config: { addresses: { listen: listenAddresses, - announce: announceAddreses + announce: announceAddresses }, transports: [ webSockets() diff --git a/packages/libp2p/test/connection-manager/resolver.spec.ts b/packages/libp2p/test/connection-manager/resolver.spec.ts index acafa0a549..13f514c73f 100644 --- a/packages/libp2p/test/connection-manager/resolver.spec.ts +++ b/packages/libp2p/test/connection-manager/resolver.spec.ts @@ -3,6 +3,7 @@ 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 { identify } from '@libp2p/identify' import { mockConnection, mockConnectionGater, mockDuplex, mockMultiaddrConnection } from '@libp2p/interface-compliance-tests/mocks' import { mplex } from '@libp2p/mplex' import { peerIdFromString } from '@libp2p/peer-id' @@ -57,7 +58,10 @@ describe('dialing (resolvable addresses)', () => { connectionEncryption: [ plaintext() ], - connectionGater: mockConnectionGater() + connectionGater: mockConnectionGater(), + services: { + identify: identify() + } }), createLibp2pNode({ addresses: { @@ -82,7 +86,8 @@ describe('dialing (resolvable addresses)', () => { plaintext() ], services: { - relay: circuitRelayServer() + relay: circuitRelayServer(), + identify: identify() }, connectionGater: mockConnectionGater() }) @@ -115,7 +120,7 @@ describe('dialing (resolvable addresses)', () => { const relayedAddrFetched = multiaddr(relayedAddr(remoteId)) // Transport spy - const transport = getTransport(libp2p, 'libp2p/circuit-relay-v2') + const transport = getTransport(libp2p, '@libp2p/circuit-relay-v2-transport') const transportDialSpy = sinon.spy(transport, 'dial') // Resolver stub diff --git a/packages/libp2p/test/core/core.spec.ts b/packages/libp2p/test/core/core.spec.ts index 0794115afb..3c45a33431 100644 --- a/packages/libp2p/test/core/core.spec.ts +++ b/packages/libp2p/test/core/core.spec.ts @@ -1,6 +1,7 @@ /* eslint-env mocha */ import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' +import { identify } from '@libp2p/identify' import { webSockets } from '@libp2p/websockets' import { multiaddr } from '@multiformats/multiaddr' import { expect } from 'aegir/chai' @@ -49,7 +50,10 @@ describe('core', () => { transports: [ webSockets(), circuitRelayTransport() - ] + ], + services: { + identify: identify() + } }) await expect(libp2p.isDialable(multiaddr('/dns4/example.com/tls/ws'), { diff --git a/packages/libp2p/test/core/service-dependencies.spec.ts b/packages/libp2p/test/core/service-dependencies.spec.ts new file mode 100644 index 0000000000..bf11769599 --- /dev/null +++ b/packages/libp2p/test/core/service-dependencies.spec.ts @@ -0,0 +1,69 @@ +import { serviceCapabilities, serviceDependencies, stop } from '@libp2p/interface' +import { expect } from 'aegir/chai' +import { createLibp2p } from '../../src/index.js' +import type { Libp2p } from '@libp2p/interface' + +/** + * A service with no dependencies + */ +function serviceA () { + return () => { + return { + [serviceCapabilities]: [ + '@libp2p/service-a' + ] + } + } +} + +/** + * A service with a dependency on service A + */ +function serviceB () { + return () => { + return { + [Symbol.toStringTag]: 'service-b', + [serviceDependencies]: [ + '@libp2p/service-a' + ] + } + } +} + +describe('service dependencies', () => { + let node: Libp2p + + afterEach(async () => { + await stop(node) + }) + + it('should start when services have no dependencies', async () => { + node = await createLibp2p({ + services: { + a: serviceA() + } + }) + + expect(node).to.be.ok() + }) + + it('should error when service dependencies are unmet', async () => { + await expect(createLibp2p({ + services: { + b: serviceB() + } + })).to.eventually.be.rejected + .with.property('code', 'ERR_UNMET_SERVICE_DEPENDENCIES') + }) + + it('should not error when service dependencies are met', async () => { + node = await createLibp2p({ + services: { + a: serviceA(), + b: serviceB() + } + }) + + expect(node).to.be.ok() + }) +}) diff --git a/packages/libp2p/test/fixtures/base-options.browser.ts b/packages/libp2p/test/fixtures/base-options.browser.ts index ac7b44a8ae..b7da8cf477 100644 --- a/packages/libp2p/test/fixtures/base-options.browser.ts +++ b/packages/libp2p/test/fixtures/base-options.browser.ts @@ -1,4 +1,5 @@ import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' +import { identify } from '@libp2p/identify' import { mockConnectionGater } from '@libp2p/interface-compliance-tests/mocks' import { mplex } from '@libp2p/mplex' import { plaintext } from '@libp2p/plaintext' @@ -23,7 +24,10 @@ export function createBaseOptions { connectionEncryption: [ plaintext() ], - connectionGater: mockConnectionGater() + connectionGater: mockConnectionGater(), + services: { + identify: identify() + } }) await libp2p.start() @@ -757,7 +761,10 @@ describe('libp2p.upgrader', () => { connectionEncryption: [ plaintext() ], - connectionGater: mockConnectionGater() + connectionGater: mockConnectionGater(), + services: { + identify: identify() + } }) await remoteLibp2p.start() diff --git a/packages/metrics-devtools/src/index.ts b/packages/metrics-devtools/src/index.ts index 2148599596..28c0a120a7 100644 --- a/packages/metrics-devtools/src/index.ts +++ b/packages/metrics-devtools/src/index.ts @@ -16,7 +16,7 @@ * for Chrome or Firefox to inspect the state of your running node. */ -import { start, stop } from '@libp2p/interface' +import { serviceCapabilities, start, stop } from '@libp2p/interface' import { enable, disable } from '@libp2p/logger' import { simpleMetrics } from '@libp2p/simple-metrics' import { base64 } from 'multiformats/bases/base64' @@ -254,6 +254,12 @@ class DevToolsMetrics implements Metrics { })({}) } + readonly [Symbol.toStringTag] = '@libp2p/devtools-metrics' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/metrics' + ] + trackMultiaddrConnection (maConn: MultiaddrConnection): void { this.simpleMetrics.trackMultiaddrConnection(maConn) } diff --git a/packages/metrics-prometheus/src/index.ts b/packages/metrics-prometheus/src/index.ts index 5b67ce5918..1fb31a2686 100644 --- a/packages/metrics-prometheus/src/index.ts +++ b/packages/metrics-prometheus/src/index.ts @@ -66,6 +66,7 @@ * ``` */ +import { serviceCapabilities } from '@libp2p/interface' import each from 'it-foreach' import { collectDefaultMetrics, type DefaultMetricsCollectorConfiguration, register, type Registry, type RegistryContentType } from 'prom-client' import { PrometheusCounterGroup } from './counter-group.js' @@ -164,6 +165,12 @@ class PrometheusMetrics implements Metrics { }) } + readonly [Symbol.toStringTag] = '@libp2p/metrics-prometheus' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/metrics' + ] + /** * Increment the transfer stat for the passed key, making sure * it exists first diff --git a/packages/metrics-simple/src/index.ts b/packages/metrics-simple/src/index.ts index 8e312c5fc9..37f083dba9 100644 --- a/packages/metrics-simple/src/index.ts +++ b/packages/metrics-simple/src/index.ts @@ -23,6 +23,7 @@ * ``` */ +import { serviceCapabilities } from '@libp2p/interface' import { logger } from '@libp2p/logger' import each from 'it-foreach' import type { Startable, MultiaddrConnection, Stream, Connection, Metric, MetricGroup, StopTimer, Metrics, CalculatedMetricOptions, MetricOptions, Counter, CounterGroup, CalculateMetric } from '@libp2p/interface' @@ -132,6 +133,12 @@ class SimpleMetrics implements Metrics, Startable { this.transferStats = new Map() } + readonly [Symbol.toStringTag] = '@libp2p/metrics-simple' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/metrics' + ] + isStarted (): boolean { return this.started } diff --git a/packages/peer-discovery-bootstrap/src/index.ts b/packages/peer-discovery-bootstrap/src/index.ts index e2a5d3468f..eb09310505 100644 --- a/packages/peer-discovery-bootstrap/src/index.ts +++ b/packages/peer-discovery-bootstrap/src/index.ts @@ -32,7 +32,7 @@ * ``` */ -import { TypedEventEmitter, peerDiscoverySymbol } from '@libp2p/interface' +import { TypedEventEmitter, peerDiscoverySymbol, serviceCapabilities } from '@libp2p/interface' import { peerIdFromString } from '@libp2p/peer-id' import { P2P } from '@multiformats/mafmt' import { multiaddr } from '@multiformats/multiaddr' @@ -128,6 +128,10 @@ class Bootstrap extends TypedEventEmitter implements PeerDi readonly [Symbol.toStringTag] = '@libp2p/bootstrap' + readonly [serviceCapabilities]: string[] = [ + '@libp2p/peer-discovery' + ] + isStarted (): boolean { return Boolean(this.timer) } diff --git a/packages/peer-discovery-mdns/src/mdns.ts b/packages/peer-discovery-mdns/src/mdns.ts index 82a957fe2f..a7a56e4f80 100644 --- a/packages/peer-discovery-mdns/src/mdns.ts +++ b/packages/peer-discovery-mdns/src/mdns.ts @@ -1,4 +1,4 @@ -import { CustomEvent, TypedEventEmitter, peerDiscoverySymbol } from '@libp2p/interface' +import { CustomEvent, TypedEventEmitter, peerDiscoverySymbol, serviceCapabilities } from '@libp2p/interface' import multicastDNS from 'multicast-dns' import * as query from './query.js' import { stringGen } from './utils.js' @@ -58,6 +58,10 @@ export class MulticastDNS extends TypedEventEmitter impleme readonly [Symbol.toStringTag] = '@libp2p/mdns' + readonly [serviceCapabilities]: string[] = [ + '@libp2p/peer-discovery' + ] + isStarted (): boolean { return Boolean(this.mdns) } diff --git a/packages/peer-store/src/index.ts b/packages/peer-store/src/index.ts index 9c5e91d3dc..9e06a53548 100644 --- a/packages/peer-store/src/index.ts +++ b/packages/peer-store/src/index.ts @@ -45,6 +45,8 @@ export class PersistentPeerStore implements PeerStore { this.store = new PersistentStore(components, init) } + readonly [Symbol.toStringTag] = '@libp2p/peer-store' + async forEach (fn: (peer: Peer,) => void, query?: PeerQuery): Promise { this.log.trace('forEach await read lock') const release = await this.store.lock.readLock() diff --git a/packages/pnet/src/index.ts b/packages/pnet/src/index.ts index 19f5f010f7..c14cf54745 100644 --- a/packages/pnet/src/index.ts +++ b/packages/pnet/src/index.ts @@ -110,6 +110,8 @@ class PreSharedKeyConnectionProtector implements ConnectionProtector { this.tag = decodedPSK.tag ?? '' } + readonly [Symbol.toStringTag] = '@libp2p/pnet' + /** * Takes a given Connection and creates a private encryption stream * between its two peers from the PSK the Protector instance was diff --git a/packages/protocol-autonat/src/autonat.ts b/packages/protocol-autonat/src/autonat.ts index 230c6e2de8..4b40f19e0e 100644 --- a/packages/protocol-autonat/src/autonat.ts +++ b/packages/protocol-autonat/src/autonat.ts @@ -47,6 +47,8 @@ export class AutoNATService implements Startable { this._verifyExternalAddresses = this._verifyExternalAddresses.bind(this) } + readonly [Symbol.toStringTag] = '@libp2p/autonat' + isStarted (): boolean { return this.started } diff --git a/packages/protocol-dcutr/src/dcutr.ts b/packages/protocol-dcutr/src/dcutr.ts index 414a7e2159..2ebfffbc0c 100644 --- a/packages/protocol-dcutr/src/dcutr.ts +++ b/packages/protocol-dcutr/src/dcutr.ts @@ -1,4 +1,4 @@ -import { CodeError, ERR_INVALID_MESSAGE } from '@libp2p/interface' +import { CodeError, ERR_INVALID_MESSAGE, serviceDependencies } from '@libp2p/interface' import { type Multiaddr, multiaddr } from '@multiformats/multiaddr' import delay from 'delay' import { pbStream } from 'it-protobuf-stream' @@ -52,6 +52,12 @@ export class DefaultDCUtRService implements Startable { this.maxOutboundStreams = init.maxOutboundStreams ?? defaultValues.maxOutboundStreams } + readonly [Symbol.toStringTag] = '@libp2p/dcutr' + + readonly [serviceDependencies]: string[] = [ + '@libp2p/identify' + ] + isStarted (): boolean { return this.started } diff --git a/packages/protocol-echo/src/echo.ts b/packages/protocol-echo/src/echo.ts index 005c0e6192..88461f966e 100644 --- a/packages/protocol-echo/src/echo.ts +++ b/packages/protocol-echo/src/echo.ts @@ -21,6 +21,8 @@ export class Echo implements Startable, EchoInterface { this.init = init } + readonly [Symbol.toStringTag] = '@libp2p/echo' + async start (): Promise { await this.components.registrar.handle(this.protocol, ({ stream }) => { void pipe(stream, stream) diff --git a/packages/protocol-fetch/src/fetch.ts b/packages/protocol-fetch/src/fetch.ts index af01d0cd75..6f9a808f19 100644 --- a/packages/protocol-fetch/src/fetch.ts +++ b/packages/protocol-fetch/src/fetch.ts @@ -34,6 +34,8 @@ export class Fetch implements Startable, FetchInterface { this.init = init } + readonly [Symbol.toStringTag] = '@libp2p/fetch' + async start (): Promise { await this.components.registrar.handle(this.protocol, (data) => { void this.handleMessage(data) diff --git a/packages/protocol-identify/src/identify-push.ts b/packages/protocol-identify/src/identify-push.ts index ede8e88642..b220170576 100644 --- a/packages/protocol-identify/src/identify-push.ts +++ b/packages/protocol-identify/src/identify-push.ts @@ -1,6 +1,6 @@ /* eslint-disable complexity */ -import { setMaxListeners } from '@libp2p/interface' +import { serviceCapabilities, setMaxListeners } from '@libp2p/interface' import { RecordEnvelope, PeerRecord } from '@libp2p/peer-record' import { protocols } from '@multiformats/multiaddr' import drain from 'it-drain' @@ -40,6 +40,10 @@ export class IdentifyPush extends AbstractIdentify implements Startable, Identif } } + [serviceCapabilities]: string[] = [ + '@libp2p/identify-push' + ] + /** * Calls `push` on all peer connections */ diff --git a/packages/protocol-identify/src/identify.ts b/packages/protocol-identify/src/identify.ts index 7796de305f..1f72534410 100644 --- a/packages/protocol-identify/src/identify.ts +++ b/packages/protocol-identify/src/identify.ts @@ -1,6 +1,6 @@ /* eslint-disable complexity */ -import { CodeError, setMaxListeners } from '@libp2p/interface' +import { CodeError, serviceCapabilities, setMaxListeners } from '@libp2p/interface' import { peerIdFromKeys } from '@libp2p/peer-id' import { RecordEnvelope, PeerRecord } from '@libp2p/peer-record' import { protocols } from '@multiformats/multiaddr' @@ -33,6 +33,10 @@ export class Identify extends AbstractIdentify implements Startable, IdentifyInt } } + [serviceCapabilities]: string[] = [ + '@libp2p/identify' + ] + async _identify (connection: Connection, options: AbortOptions = {}): Promise { let stream: Stream | undefined diff --git a/packages/protocol-perf/src/perf-service.ts b/packages/protocol-perf/src/perf-service.ts index f3475eb506..9dd64309a9 100644 --- a/packages/protocol-perf/src/perf-service.ts +++ b/packages/protocol-perf/src/perf-service.ts @@ -28,6 +28,8 @@ export class Perf implements Startable, PerfInterface { this.runOnTransientConnection = init.runOnTransientConnection ?? RUN_ON_TRANSIENT_CONNECTION } + readonly [Symbol.toStringTag] = '@libp2p/perf' + async start (): Promise { await this.components.registrar.handle(this.protocol, (data: IncomingStreamData) => { void this.handleMessage(data).catch((err) => { diff --git a/packages/protocol-ping/src/ping.ts b/packages/protocol-ping/src/ping.ts index a7af5d16ff..a05ae490fa 100644 --- a/packages/protocol-ping/src/ping.ts +++ b/packages/protocol-ping/src/ping.ts @@ -32,6 +32,8 @@ export class PingService implements Startable, PingServiceInterface { this.handleMessage = this.handleMessage.bind(this) } + readonly [Symbol.toStringTag] = '@libp2p/ping' + async start (): Promise { await this.components.registrar.handle(this.protocol, this.handleMessage, { maxInboundStreams: this.maxInboundStreams, diff --git a/packages/pubsub-floodsub/src/index.ts b/packages/pubsub-floodsub/src/index.ts index 584cbea647..fd92f0b379 100644 --- a/packages/pubsub-floodsub/src/index.ts +++ b/packages/pubsub-floodsub/src/index.ts @@ -32,6 +32,7 @@ * ``` */ +import { serviceDependencies } from '@libp2p/interface' import { PubSubBaseProtocol, type PubSubComponents } from '@libp2p/pubsub' import { toString } from 'uint8arrays/to-string' import { SimpleTimeCache } from './cache.js' @@ -77,6 +78,12 @@ export class FloodSub extends PubSubBaseProtocol { }) } + readonly [Symbol.toStringTag] = '@libp2p/floodsub' + + readonly [serviceDependencies]: string[] = [ + '@libp2p/identify' + ] + /** * Decode a Uint8Array into an RPC object */ diff --git a/packages/stream-multiplexer-mplex/src/mplex.ts b/packages/stream-multiplexer-mplex/src/mplex.ts index 23746b9660..20b9799bcb 100644 --- a/packages/stream-multiplexer-mplex/src/mplex.ts +++ b/packages/stream-multiplexer-mplex/src/mplex.ts @@ -1,4 +1,4 @@ -import { CodeError } from '@libp2p/interface' +import { CodeError, serviceCapabilities } from '@libp2p/interface' import { closeSource } from '@libp2p/utils/close-source' import { RateLimiter } from '@libp2p/utils/rate-limiter' import { pipe } from 'it-pipe' @@ -120,6 +120,12 @@ export class MplexStreamMuxer implements StreamMuxer { }) } + readonly [Symbol.toStringTag] = '@libp2p/mplex' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/stream-multiplexing' + ] + /** * Returns a Map of streams and their ids */ diff --git a/packages/transport-circuit-relay-v2/src/server/index.ts b/packages/transport-circuit-relay-v2/src/server/index.ts index 16996249fd..eac1cfb2ab 100644 --- a/packages/transport-circuit-relay-v2/src/server/index.ts +++ b/packages/transport-circuit-relay-v2/src/server/index.ts @@ -123,6 +123,8 @@ class CircuitRelayServer extends TypedEventEmitter implements setMaxListeners(Infinity, this.shutdownController.signal) } + readonly [Symbol.toStringTag] = '@libp2p/circuit-relay-v2-server' + isStarted (): boolean { return this.started } diff --git a/packages/transport-circuit-relay-v2/src/transport/transport.ts b/packages/transport-circuit-relay-v2/src/transport/transport.ts index 31e41b89f8..455f3e25f8 100644 --- a/packages/transport-circuit-relay-v2/src/transport/transport.ts +++ b/packages/transport-circuit-relay-v2/src/transport/transport.ts @@ -1,5 +1,4 @@ -import { CodeError, start, stop } from '@libp2p/interface' -import { transportSymbol, type Transport, type CreateListenerOptions, type Listener, type Upgrader, type AbortOptions, type ComponentLogger, type Logger, type Connection, type Stream, type ConnectionGater, type PeerId, type PeerStore } from '@libp2p/interface' +import { CodeError, serviceCapabilities, serviceDependencies, start, stop, transportSymbol } from '@libp2p/interface' import { peerFilter } from '@libp2p/peer-collections' import { peerIdFromBytes, peerIdFromString } from '@libp2p/peer-id' import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn' @@ -12,6 +11,7 @@ import { RelayDiscovery } from './discovery.js' import { createListener } from './listener.js' import { ReservationStore } from './reservation-store.js' import type { CircuitRelayTransportComponents, CircuitRelayTransportInit } from './index.js' +import type { Transport, CreateListenerOptions, Listener, Upgrader, AbortOptions, ComponentLogger, Logger, Connection, Stream, ConnectionGater, PeerId, PeerStore } from '@libp2p/interface' import type { AddressManager, ConnectionManager, IncomingStreamData, Registrar, TransportManager } from '@libp2p/interface-internal' import type { Multiaddr } from '@multiformats/multiaddr' @@ -105,6 +105,26 @@ export class CircuitRelayTransport implements Transport { this.started = false } + readonly [Symbol.toStringTag] = '@libp2p/circuit-relay-v2-transport' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/transport', + '@libp2p/circuit-relay-v2-transport' + ] + + get [serviceDependencies] (): string[] { + // we only need identify if discovery is enabled + if (this.discovery != null) { + return [ + '@libp2p/identify' + ] + } + + return [] + } + + readonly [transportSymbol] = true + isStarted (): boolean { return this.started } @@ -133,10 +153,6 @@ export class CircuitRelayTransport implements Transport { this.started = false } - readonly [transportSymbol] = true - - readonly [Symbol.toStringTag] = 'libp2p/circuit-relay-v2' - /** * Dial a peer over a relay */ diff --git a/packages/transport-tcp/src/index.ts b/packages/transport-tcp/src/index.ts index 0b24d5a015..b175e0453b 100644 --- a/packages/transport-tcp/src/index.ts +++ b/packages/transport-tcp/src/index.ts @@ -28,7 +28,7 @@ */ import net from 'net' -import { AbortError, CodeError, transportSymbol } from '@libp2p/interface' +import { AbortError, CodeError, serviceCapabilities, transportSymbol } from '@libp2p/interface' import * as mafmt from '@multiformats/mafmt' import { CODE_CIRCUIT, CODE_P2P, CODE_UNIX } from './constants.js' import { type CloseServerOnMaxConnectionsOpts, TCPListener } from './listener.js' @@ -150,6 +150,10 @@ class TCP implements Transport { readonly [Symbol.toStringTag] = '@libp2p/tcp' + readonly [serviceCapabilities]: string[] = [ + '@libp2p/transport' + ] + async dial (ma: Multiaddr, options: TCPDialOptions): Promise { options.keepAlive = options.keepAlive ?? true options.noDelay = options.noDelay ?? true diff --git a/packages/transport-webrtc/package.json b/packages/transport-webrtc/package.json index 011b0956d4..d7469d0452 100644 --- a/packages/transport-webrtc/package.json +++ b/packages/transport-webrtc/package.json @@ -78,6 +78,7 @@ "devDependencies": { "@chainsafe/libp2p-yamux": "^6.0.2", "@libp2p/circuit-relay-v2": "^1.0.25", + "@libp2p/identify": "^2.0.3", "@libp2p/interface-compliance-tests": "^5.4.6", "@libp2p/logger": "^4.0.14", "@libp2p/peer-id-factory": "^4.1.3", diff --git a/packages/transport-webrtc/src/private-to-private/transport.ts b/packages/transport-webrtc/src/private-to-private/transport.ts index f9aa839bf2..89ad2ee48b 100644 --- a/packages/transport-webrtc/src/private-to-private/transport.ts +++ b/packages/transport-webrtc/src/private-to-private/transport.ts @@ -1,4 +1,4 @@ -import { CodeError, setMaxListeners } from '@libp2p/interface' +import { CodeError, serviceCapabilities, serviceDependencies, setMaxListeners } from '@libp2p/interface' import { type CreateListenerOptions, type DialOptions, transportSymbol, type Transport, type Listener, type Upgrader, type ComponentLogger, type Logger, type Connection, type PeerId, type CounterGroup, type Metrics, type Startable } from '@libp2p/interface' import { peerIdFromString } from '@libp2p/peer-id' import { multiaddr, type Multiaddr } from '@multiformats/multiaddr' @@ -72,6 +72,19 @@ export class WebRTCTransport implements Transport, Startable { } } + readonly [transportSymbol] = true + + readonly [Symbol.toStringTag] = '@libp2p/webrtc' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/transport' + ] + + readonly [serviceDependencies]: string[] = [ + '@libp2p/identify', + '@libp2p/circuit-relay-v2-transport' + ] + isStarted (): boolean { return this._started } @@ -96,10 +109,6 @@ export class WebRTCTransport implements Transport, Startable { }) } - readonly [Symbol.toStringTag] = '@libp2p/webrtc' - - readonly [transportSymbol] = true - /** * Filter check for all Multiaddrs that this transport can listen on */ diff --git a/packages/transport-webrtc/src/private-to-public/transport.ts b/packages/transport-webrtc/src/private-to-public/transport.ts index 3050bea758..1ce54776ad 100644 --- a/packages/transport-webrtc/src/private-to-public/transport.ts +++ b/packages/transport-webrtc/src/private-to-public/transport.ts @@ -1,5 +1,5 @@ import { noise } from '@chainsafe/libp2p-noise' -import { type CreateListenerOptions, transportSymbol, type Transport, type Listener, type ComponentLogger, type Logger, type Connection, type CounterGroup, type Metrics, type PeerId } from '@libp2p/interface' +import { transportSymbol, serviceCapabilities } from '@libp2p/interface' import * as p from '@libp2p/peer-id' import { protocols } from '@multiformats/multiaddr' import { WebRTCDirect } from '@multiformats/multiaddr-matcher' @@ -16,6 +16,7 @@ import * as sdp from './sdp.js' import { genUfrag } from './util.js' import type { WebRTCDialOptions } from './options.js' import type { DataChannelOptions } from '../index.js' +import type { CreateListenerOptions, Transport, Listener, ComponentLogger, Logger, Connection, CounterGroup, Metrics, PeerId } from '@libp2p/interface' import type { Multiaddr } from '@multiformats/multiaddr' /** @@ -73,6 +74,14 @@ export class WebRTCDirectTransport implements Transport { } } + readonly [transportSymbol] = true + + readonly [Symbol.toStringTag] = '@libp2p/webrtc-direct' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/transport' + ] + /** * Dial a given multiaddr */ @@ -103,16 +112,6 @@ export class WebRTCDirectTransport implements Transport { return this.listenFilter(multiaddrs) } - /** - * Implement toString() for WebRTCTransport - */ - readonly [Symbol.toStringTag] = '@libp2p/webrtc-direct' - - /** - * Symbol.for('@libp2p/transport') - */ - readonly [transportSymbol] = true - /** * Connect to a peer using a multiaddr */ diff --git a/packages/transport-webrtc/test/basics.spec.ts b/packages/transport-webrtc/test/basics.spec.ts index c54488a7af..c4d01470f5 100644 --- a/packages/transport-webrtc/test/basics.spec.ts +++ b/packages/transport-webrtc/test/basics.spec.ts @@ -3,6 +3,7 @@ import { noise } from '@chainsafe/libp2p-noise' import { yamux } from '@chainsafe/libp2p-yamux' import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' +import { identify } from '@libp2p/identify' import { webSockets } from '@libp2p/websockets' import * as filter from '@libp2p/websockets/filters' import { multiaddr } from '@multiformats/multiaddr' @@ -45,6 +46,9 @@ async function createNode (): Promise { }, connectionManager: { minConnections: 0 + }, + services: { + identify: identify() } }) } diff --git a/packages/transport-websockets/src/index.ts b/packages/transport-websockets/src/index.ts index c5d8a2b7b7..619bc85027 100644 --- a/packages/transport-websockets/src/index.ts +++ b/packages/transport-websockets/src/index.ts @@ -57,8 +57,7 @@ * ``` */ -import { AbortError, CodeError } from '@libp2p/interface' -import { type Transport, type MultiaddrFilter, transportSymbol, type CreateListenerOptions, type DialOptions, type Listener, type AbortOptions, type ComponentLogger, type Logger, type Connection } from '@libp2p/interface' +import { AbortError, CodeError, transportSymbol, serviceCapabilities } from '@libp2p/interface' import { multiaddrToUri as toUri } from '@multiformats/multiaddr-to-uri' import { connect, type WebSocketOptions } from 'it-ws/client' import pDefer from 'p-defer' @@ -66,6 +65,7 @@ import { isBrowser, isWebWorker } from 'wherearewe' import * as filters from './filters.js' import { createListener } from './listener.js' import { socketToMaConn } from './socket-to-conn.js' +import type { Transport, MultiaddrFilter, CreateListenerOptions, DialOptions, Listener, AbortOptions, ComponentLogger, Logger, Connection } from '@libp2p/interface' import type { Multiaddr } from '@multiformats/multiaddr' import type { Server } from 'http' import type { DuplexWebSocket } from 'it-ws/duplex' @@ -92,9 +92,13 @@ class WebSockets implements Transport { this.init = init } + readonly [transportSymbol] = true + readonly [Symbol.toStringTag] = '@libp2p/websockets' - readonly [transportSymbol] = true + readonly [serviceCapabilities]: string[] = [ + '@libp2p/transport' + ] async dial (ma: Multiaddr, options: DialOptions): Promise { this.log('dialing %s', ma) diff --git a/packages/transport-webtransport/src/index.ts b/packages/transport-webtransport/src/index.ts index 1ebe1cd508..fff191f25a 100644 --- a/packages/transport-webtransport/src/index.ts +++ b/packages/transport-webtransport/src/index.ts @@ -30,7 +30,7 @@ */ import { noise } from '@chainsafe/libp2p-noise' -import { AbortError, CodeError, transportSymbol } from '@libp2p/interface' +import { AbortError, CodeError, serviceCapabilities, transportSymbol } from '@libp2p/interface' import { WebTransport as WebTransportMatcher } from '@multiformats/multiaddr-matcher' import { raceSignal } from 'race-signal' import createListener from './listener.js' @@ -103,6 +103,10 @@ class WebTransportTransport implements Transport { readonly [transportSymbol] = true + readonly [serviceCapabilities]: string[] = [ + '@libp2p/transport' + ] + async dial (ma: Multiaddr, options: DialOptions): Promise { if (options?.signal?.aborted === true) { throw new AbortError() diff --git a/packages/upnp-nat/src/upnp-nat.ts b/packages/upnp-nat/src/upnp-nat.ts index 1fd56a3896..d56d0c99d9 100644 --- a/packages/upnp-nat/src/upnp-nat.ts +++ b/packages/upnp-nat/src/upnp-nat.ts @@ -1,5 +1,5 @@ import { upnpNat, type NatAPI, type MapPortOptions } from '@achingbrain/nat-port-mapper' -import { CodeError, ERR_INVALID_PARAMETERS } from '@libp2p/interface' +import { CodeError, ERR_INVALID_PARAMETERS, serviceCapabilities } from '@libp2p/interface' import { isLoopback } from '@libp2p/utils/multiaddr/is-loopback' import { isPrivateIp } from '@libp2p/utils/private-ip' import { fromNodeAddress } from '@multiformats/multiaddr' @@ -51,6 +51,12 @@ export class UPnPNAT implements Startable, UPnPNATInterface { }) } + readonly [Symbol.toStringTag] = '@libp2p/upnp-nat' + + readonly [serviceCapabilities]: string[] = [ + '@libp2p/nat-traversal' + ] + isStarted (): boolean { return this.started }