diff --git a/doc/migrations/v0.44-v0.45.md b/doc/migrations/v0.44-v0.45.md index d666e7bf3f..275882b8ab 100644 --- a/doc/migrations/v0.44-v0.45.md +++ b/doc/migrations/v0.44-v0.45.md @@ -42,6 +42,12 @@ const node = createLibp2p({ fetch: { /** fetch options **/ }, + nat: { + /** UPnP NAT options **/ + }, + autonat: { + /** AutoNAT options **/ + }, pubsub: gossipSub(), dht: kadDHT(), relay: circuitRelayServer() @@ -52,10 +58,12 @@ const node = createLibp2p({ ```js import { createLibp2p } from 'libp2p' +import { autonatService } from 'libp2p/autonat' import { circuitRelayServer } from 'libp2p/circuit-relay' import { identifyService } from 'libp2p/identify' import { pingService } from 'libp2p/ping' import { fetchService } from 'libp2p/fetch' +import { uPnPNATService } from 'libp2p/upnp-nat' import { kadDHT } from '@libp2p/kad-dht' import { gossipSub } from '@ChainSafe/libp2p-gossipsub' @@ -71,6 +79,12 @@ const node = createLibp2p({ fetch: fetchService({ /** fetch options **/ }), + uPnPNAT: uPnPNATService({ + /** UPnP NAT options **/ + }), + autonat: autonatService({ + /** AutoNAT options **/ + }), pubsub: gossipSub(), dht: kadDHT(), relay: circuitRelayServer() diff --git a/src/autonat/constants.ts b/src/autonat/constants.ts index d76113de8f..2b15ab68d1 100644 --- a/src/autonat/constants.ts +++ b/src/autonat/constants.ts @@ -1,4 +1,19 @@ +/** + * The prefix to use in the protocol + */ +export const PROTOCOL_PREFIX = 'libp2p' -export const PROTOCOL = '/libp2p/autonat/1.0.0' -export const PROTOCOL_VERSION = '1.0.0' +/** + * The name to use in the protocol + */ export const PROTOCOL_NAME = 'autonat' + +/** + * The version to use in the protocol + */ +export const PROTOCOL_VERSION = '1.0.0' +export const TIMEOUT = 30000 +export const STARTUP_DELAY = 5000 +export const REFRESH_INTERVAL = 60000 +export const MAX_INBOUND_STREAMS = 1 +export const MAX_OUTBOUND_STREAMS = 1 diff --git a/src/autonat/index.ts b/src/autonat/index.ts index bc76cfe7c2..1cf28dabe0 100644 --- a/src/autonat/index.ts +++ b/src/autonat/index.ts @@ -20,7 +20,9 @@ import parallel from 'it-parallel' import { pipe } from 'it-pipe' import isPrivateIp from 'private-ip' import { - PROTOCOL + MAX_INBOUND_STREAMS, + MAX_OUTBOUND_STREAMS, + PROTOCOL_NAME, PROTOCOL_PREFIX, PROTOCOL_VERSION, REFRESH_INTERVAL, STARTUP_DELAY, TIMEOUT } from './constants.js' import { Message } from './pb/index.js' import { anySignal } from 'any-signal' @@ -32,45 +34,39 @@ const log = logger('libp2p:autonat') // https://github.com/libp2p/specs/blob/master/autonat/README.md#autonat-protocol const REQUIRED_SUCCESSFUL_DIALS = 4 -// Wait this long before we start to query autonat nodes -const AUTONAT_STARTUP_DELAY = 5000 - -// Only try to verify our external address this often -const AUTONAT_REFRESH_INTERVAL = 60000 - export interface AutonatServiceInit { /** * Allows overriding the protocol prefix used */ - protocolPrefix: string + protocolPrefix?: string /** * How long we should wait for a remote peer to verify our external address */ - timeout: number + timeout?: number /** * How long to wait after startup before trying to verify our external address */ - startupDelay: number + startupDelay?: number /** * Verify our external addresses this often */ - refreshInterval: number + refreshInterval?: number /** * How many parallel inbound autonat streams we allow per-connection */ - maxInboundStreams: number + maxInboundStreams?: number /** * How many parallel outbound autonat streams we allow per-connection */ - maxOutboundStreams: number + maxOutboundStreams?: number } -export interface DefaultAutonatComponents { +export interface AutonatComponents { registrar: Registrar addressManager: AddressManager transportManager: TransportManager @@ -79,21 +75,26 @@ export interface DefaultAutonatComponents { peerRouting: PeerRouting } -export class AutonatService implements Startable { - private readonly components: DefaultAutonatComponents - private readonly _init: AutonatServiceInit +class DefaultAutonatService implements Startable { + private readonly components: AutonatComponents private readonly startupDelay: number private readonly refreshInterval: number + private readonly protocol: string + private readonly timeout: number + private readonly maxInboundStreams: number + private readonly maxOutboundStreams: number private verifyAddressTimeout?: ReturnType private started: boolean - constructor (components: DefaultAutonatComponents, init: AutonatServiceInit) { + constructor (components: AutonatComponents, init: AutonatServiceInit) { this.components = components this.started = false - this._init = init - this.startupDelay = init.startupDelay ?? AUTONAT_STARTUP_DELAY - this.refreshInterval = init.refreshInterval ?? AUTONAT_REFRESH_INTERVAL - + this.protocol = `/${init.protocolPrefix ?? PROTOCOL_PREFIX}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}` + this.timeout = init.timeout ?? TIMEOUT + this.maxInboundStreams = init.maxInboundStreams ?? MAX_INBOUND_STREAMS + this.maxOutboundStreams = init.maxOutboundStreams ?? MAX_OUTBOUND_STREAMS + this.startupDelay = init.startupDelay ?? STARTUP_DELAY + this.refreshInterval = init.refreshInterval ?? REFRESH_INTERVAL this._verifyExternalAddresses = this._verifyExternalAddresses.bind(this) } @@ -106,14 +107,14 @@ export class AutonatService implements Startable { return } - await this.components.registrar.handle(PROTOCOL, (data) => { + await this.components.registrar.handle(this.protocol, (data) => { void this.handleIncomingAutonatStream(data) .catch(err => { log.error(err) }) }, { - maxInboundStreams: this._init.maxInboundStreams, - maxOutboundStreams: this._init.maxOutboundStreams + maxInboundStreams: this.maxInboundStreams, + maxOutboundStreams: this.maxOutboundStreams }) this.verifyAddressTimeout = setTimeout(this._verifyExternalAddresses, this.startupDelay) @@ -122,7 +123,7 @@ export class AutonatService implements Startable { } async stop (): Promise { - await this.components.registrar.unhandle(PROTOCOL) + await this.components.registrar.unhandle(this.protocol) clearTimeout(this.verifyAddressTimeout) this.started = false @@ -132,7 +133,7 @@ export class AutonatService implements Startable { * Handle an incoming autonat request */ async handleIncomingAutonatStream (data: IncomingStreamData): Promise { - const signal = anySignal([AbortSignal.timeout(this._init.timeout)]) + const signal = anySignal([AbortSignal.timeout(this.timeout)]) // this controller may be used while dialing lots of peers so prevent MaxListenersExceededWarning // appearing in the console @@ -403,7 +404,7 @@ export class AutonatService implements Startable { return } - const signal = AbortSignal.timeout(this._init.timeout) + const signal = AbortSignal.timeout(this.timeout) // this controller may be used while dialing lots of peers so prevent MaxListenersExceededWarning // appearing in the console @@ -433,7 +434,7 @@ export class AutonatService implements Startable { const results: Record = {} const networkSegments: string[] = [] - async function verifyAddress (peer: PeerInfo): Promise { + const verifyAddress = async (peer: PeerInfo): Promise => { try { log('Asking %p to verify multiaddr', peer.id) @@ -441,7 +442,7 @@ export class AutonatService implements Startable { signal }) - const stream = await connection.newStream(PROTOCOL, { + const stream = await connection.newStream(this.protocol, { signal }) const source = abortableDuplex(stream, signal) @@ -563,3 +564,9 @@ export class AutonatService implements Startable { } } } + +export function autonatService (init: AutonatServiceInit = {}): (components: AutonatComponents) => {} { + return (components) => { + return new DefaultAutonatService(components, init) + } +} diff --git a/src/upnp-nat/index.ts b/src/upnp-nat/index.ts index 21cc184775..2a8a079249 100644 --- a/src/upnp-nat/index.ts +++ b/src/upnp-nat/index.ts @@ -50,7 +50,7 @@ export interface UPnPNATInit { /** * Whether to automatically refresh UPnP port mappings when their TTL is reached */ - keepAlive: boolean + keepAlive?: boolean /** * Pass a value to use instead of auto-detection @@ -205,7 +205,7 @@ class UPnPNAT implements Startable { } } -export function uPnPNAT (init: UPnPNATInit): (components: UPnPNATComponents) => {} { +export function uPnPNATService (init: UPnPNATInit = {}): (components: UPnPNATComponents) => {} { return (components: UPnPNATComponents) => { return new UPnPNAT(components, init) } diff --git a/test/autonat/index.spec.ts b/test/autonat/index.spec.ts index 3a3cd7b242..c3e0dac643 100644 --- a/test/autonat/index.spec.ts +++ b/test/autonat/index.spec.ts @@ -5,14 +5,14 @@ import { expect } from 'aegir/chai' import sinon from 'sinon' import { createEd25519PeerId } from '@libp2p/peer-id-factory' import { start, stop } from '@libp2p/interfaces/startable' -import { AutonatService, AutonatServiceInit } from '../../src/autonat/index.js' +import { autonatService, AutonatServiceInit } from '../../src/autonat/index.js' import { StubbedInstance, stubInterface } from 'sinon-ts' import type { PeerRouting } from '@libp2p/interface-peer-routing' import { Multiaddr, multiaddr } from '@multiformats/multiaddr' import type { Registrar } from '@libp2p/interface-registrar' import type { AddressManager } from '@libp2p/interface-address-manager' import type { Connection, Stream } from '@libp2p/interface-connection' -import { PROTOCOL } from '../../src/autonat/constants.js' +import { PROTOCOL_NAME, PROTOCOL_PREFIX, PROTOCOL_VERSION } from '../../src/autonat/constants.js' import { Message } from '../../src/autonat/pb/index.js' import type { PeerId } from '@libp2p/interface-peer-id' import { pushable } from 'it-pushable' @@ -36,7 +36,7 @@ const defaultInit: AutonatServiceInit = { } describe('autonat', () => { - let service: AutonatService + let service: any let components: Components let peerRouting: StubbedInstance let registrar: StubbedInstance @@ -65,7 +65,7 @@ describe('autonat', () => { peerStore }) - service = new AutonatService(components, defaultInit) + service = autonatService(defaultInit)(components) await start(components) await start(service) @@ -94,7 +94,7 @@ describe('autonat', () => { // stub autonat protocol stream const stream = stubInterface() - connection.newStream.withArgs(PROTOCOL).resolves(stream) + connection.newStream.withArgs(`/${PROTOCOL_PREFIX}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`).resolves(stream) // stub autonat response const response = Message.encode({ diff --git a/test/upnp-nat/upnp-nat.node.ts b/test/upnp-nat/upnp-nat.node.ts index f26b746d7a..c2e95d73c8 100644 --- a/test/upnp-nat/upnp-nat.node.ts +++ b/test/upnp-nat/upnp-nat.node.ts @@ -6,7 +6,7 @@ import { DefaultTransportManager } from '../../src/transport-manager.js' import { FaultTolerance } from '@libp2p/interface-transport' import { tcp } from '@libp2p/tcp' import { mockUpgrader } from '@libp2p/interface-mocks' -import { uPnPNAT } from '../../src/upnp-nat/index.js' +import { uPnPNATService } from '../../src/upnp-nat/index.js' import Peers from '../fixtures/peers.js' import { codes } from '../../src/errors.js' import { createFromJSON } from '@libp2p/peer-id-factory' @@ -51,7 +51,7 @@ describe('UPnP NAT (TCP)', () => { faultTolerance: FaultTolerance.NO_FATAL }) - const natManager: any = uPnPNAT({ + const natManager: any = uPnPNATService({ keepAlive: true, ...natManagerOptions })(components) @@ -229,7 +229,7 @@ describe('UPnP NAT (TCP)', () => { const peerId = await createFromJSON(Peers[0]) expect(() => { - uPnPNAT({ ttl: 5, keepAlive: true })(defaultComponents({ peerId })) + uPnPNATService({ ttl: 5, keepAlive: true })(defaultComponents({ peerId })) }).to.throw().with.property('code', codes.ERR_INVALID_PARAMETERS) }) })