Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update autonat and upnp-nat services to match fetch etc #1729

Merged
merged 6 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions doc/migrations/v0.44-v0.45.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ const node = createLibp2p({
fetch: {
/** fetch options **/
},
nat: {
/** UPnP NAT options **/
},
autonat: {
/** AutoNAT options **/
},
pubsub: gossipSub(),
dht: kadDHT(),
relay: circuitRelayServer()
Expand All @@ -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'

Expand All @@ -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()
Expand Down
19 changes: 17 additions & 2 deletions src/autonat/constants.ts
Original file line number Diff line number Diff line change
@@ -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
67 changes: 37 additions & 30 deletions src/autonat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
Expand All @@ -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<typeof setTimeout>
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)
}

Expand All @@ -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)
Expand All @@ -122,7 +123,7 @@ export class AutonatService implements Startable {
}

async stop (): Promise<void> {
await this.components.registrar.unhandle(PROTOCOL)
await this.components.registrar.unhandle(this.protocol)
clearTimeout(this.verifyAddressTimeout)

this.started = false
Expand All @@ -132,7 +133,7 @@ export class AutonatService implements Startable {
* Handle an incoming autonat request
*/
async handleIncomingAutonatStream (data: IncomingStreamData): Promise<void> {
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -433,15 +434,15 @@ export class AutonatService implements Startable {
const results: Record<string, { success: number, failure: number }> = {}
const networkSegments: string[] = []

async function verifyAddress (peer: PeerInfo): Promise<Message.DialResponse | undefined> {
const verifyAddress = async (peer: PeerInfo): Promise<Message.DialResponse | undefined> => {
try {
log('Asking %p to verify multiaddr', peer.id)

const connection = await self.components.connectionManager.openConnection(peer.id, {
signal
})

const stream = await connection.newStream(PROTOCOL, {
const stream = await connection.newStream(this.protocol, {
signal
})
const source = abortableDuplex(stream, signal)
Expand Down Expand Up @@ -563,3 +564,9 @@ export class AutonatService implements Startable {
}
}
}

export function autonatService (init: AutonatServiceInit = {}): (components: AutonatComponents) => {} {
return (components) => {
return new DefaultAutonatService(components, init)
}
}
4 changes: 2 additions & 2 deletions src/upnp-nat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
10 changes: 5 additions & 5 deletions test/autonat/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -36,7 +36,7 @@ const defaultInit: AutonatServiceInit = {
}

describe('autonat', () => {
let service: AutonatService
let service: any
maschad marked this conversation as resolved.
Show resolved Hide resolved
let components: Components
let peerRouting: StubbedInstance<PeerRouting>
let registrar: StubbedInstance<Registrar>
Expand Down Expand Up @@ -65,7 +65,7 @@ describe('autonat', () => {
peerStore
})

service = new AutonatService(components, defaultInit)
service = autonatService(defaultInit)(components)

await start(components)
await start(service)
Expand Down Expand Up @@ -94,7 +94,7 @@ describe('autonat', () => {

// stub autonat protocol stream
const stream = stubInterface<Stream>()
connection.newStream.withArgs(PROTOCOL).resolves(stream)
connection.newStream.withArgs(`/${PROTOCOL_PREFIX}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`).resolves(stream)

// stub autonat response
const response = Message.encode({
Expand Down
6 changes: 3 additions & 3 deletions test/upnp-nat/upnp-nat.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -51,7 +51,7 @@ describe('UPnP NAT (TCP)', () => {
faultTolerance: FaultTolerance.NO_FATAL
})

const natManager: any = uPnPNAT({
const natManager: any = uPnPNATService({
keepAlive: true,
...natManagerOptions
})(components)
Expand Down Expand Up @@ -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)
})
})