diff --git a/README.md b/README.md
index 41ac925f4c..06b53a0e6b 100644
--- a/README.md
+++ b/README.md
@@ -1,76 +1,3 @@
-# js-libp2p-monorepo
-
-[![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)
-
-> JavaScript implementation of libp2p, a modular peer to peer network stack
-
-## Table of contents
-
-- [Structure](#structure)
- - [Project status](#project-status)
-- [Background](#background)
-- [Roadmap](#roadmap)
-- [Install](#install)
-- [Usage](#usage)
- - [Configuration](#configuration)
- - [Limits](#limits)
- - [Getting started](#getting-started)
- - [Tutorials and Examples](#tutorials-and-examples)
-- [Development](#development)
- - [Tests](#tests)
- - [Run unit tests](#run-unit-tests)
- - [Packages](#packages)
-- [Used by](#used-by)
-- [Contribute](#contribute)
-- [API Docs](#api-docs)
-- [License](#license)
-- [Contribution](#contribution)
-
-## Structure
-
-- [`/doc`](./doc) Docs for libp2p
-- [`/examples/auto-relay`](./examples/auto-relay) Shows how to configure relayed connections
-- [`/examples/chat`](./examples/chat) An example chat app using libp2p
-- [`/examples/connection-encryption`](./examples/connection-encryption) An example of how to configure connection encrypters
-- [`/examples/delegated-routing`](./examples/delegated-routing) How to configure libp2p delegated routers
-- [`/examples/discovery-mechanisms`](./examples/discovery-mechanisms) How to configure peer discovery mechanisms
-- [`/examples/echo`](./examples/echo) An example echo app
-- [`/examples/peer-and-content-routing`](./examples/peer-and-content-routing) How to use peer and content routing
-- [`/examples/pnet`](./examples/pnet) How to configure a libp2p private network
-- [`/examples/protocol-and-stream-muxing`](./examples/protocol-and-stream-muxing) How to use multiplex protocols streams
-- [`/examples/pubsub`](./examples/pubsub) An example using libp2p pubsub
-- [`/examples/transports`](./examples/transports) An example using different types of libp2p transport
-- [`/interop`](./interop) Multidimension Interop Test
-- [`/packages/crypto`](./packages/crypto) Crypto primitives for libp2p
-- [`/packages/interface`](./packages/interface) The interface implemented by a libp2p node
-- [`/packages/interface-compliance-tests`](./packages/interface-compliance-tests) Compliance tests for JS libp2p interfaces
-- [`/packages/interface-internal`](./packages/interface-internal) Interfaces implemented by internal libp2p components
-- [`/packages/kad-dht`](./packages/kad-dht) JavaScript implementation of the Kad-DHT for libp2p
-- [`/packages/keychain`](./packages/keychain) Key management and cryptographically protected messages
-- [`/packages/libp2p`](./packages/libp2p) JavaScript implementation of libp2p, a modular peer to peer network stack
-- [`/packages/logger`](./packages/logger) A logging component for use in js-libp2p modules
-- [`/packages/metrics-prometheus`](./packages/metrics-prometheus) Collect libp2p metrics for scraping by Prometheus or Graphana
-- [`/packages/multistream-select`](./packages/multistream-select) JavaScript implementation of multistream-select
-- [`/packages/peer-collections`](./packages/peer-collections) Stores values against a peer id
-- [`/packages/peer-discovery-bootstrap`](./packages/peer-discovery-bootstrap) Peer discovery via a list of bootstrap peers
-- [`/packages/peer-discovery-mdns`](./packages/peer-discovery-mdns) Node.js libp2p mDNS discovery implementation for peer discovery
-- [`/packages/peer-id`](./packages/peer-id) Implementation of @libp2p/interface-peer-id
-- [`/packages/peer-id-factory`](./packages/peer-id-factory) Create PeerId instances
-- [`/packages/peer-record`](./packages/peer-record) Used to transfer signed peer data across the network
-- [`/packages/peer-store`](./packages/peer-store) Stores information about peers libp2p knows on the network
-- [`/packages/protocol-perf`](./packages/protocol-perf) Implementation of Perf Protocol
-- [`/packages/pubsub`](./packages/pubsub) libp2p pubsub base class
-- [`/packages/pubsub-floodsub`](./packages/pubsub-floodsub) libp2p-floodsub, also known as pubsub-flood or just dumbsub, this implementation of pubsub focused on delivering an API for Publish/Subscribe, but with no CastTree Forming (it just floods the network).
-- [`/packages/stream-multiplexer-mplex`](./packages/stream-multiplexer-mplex) JavaScript implementation of
-- [`/packages/transport-tcp`](./packages/transport-tcp) A TCP transport for libp2p
-- [`/packages/transport-webrtc`](./packages/transport-webrtc) A libp2p transport using WebRTC connections
-- [`/packages/transport-websockets`](./packages/transport-websockets) JavaScript implementation of the WebSockets module that libp2p uses and that implements the interface-transport spec
-- [`/packages/transport-webtransport`](./packages/transport-webtransport) JavaScript implementation of the WebTransport module that libp2p uses and that implements the interface-transport spec
-- [`/packages/utils`](./packages/utils) Package to aggregate shared logic and dependencies for the libp2p ecosystem
-
@@ -99,6 +26,16 @@
+# js-libp2p-monorepo
+
+[![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)
+
+> JavaScript implementation of libp2p, a modular peer to peer network stack
+
+
### Project status
This project has been used in production for years in Ethereum, IPFS, and more. It is actively maintained by multiple organizations and continues to be improved! The API might change, but we strictly follow semver.
@@ -110,6 +47,26 @@ If you are looking for the documentation of the latest release, you can view the
**Want to update libp2p in your project?** Check our [migrations folder](./doc/migrations).
+## Table of contents
+
+- [Background](#background)
+- [Roadmap](#roadmap)
+- [Install](#install)
+- [Usage](#usage)
+ - [Configuration](#configuration)
+ - [Limits](#limits)
+ - [Getting started](#getting-started)
+ - [Tutorials and Examples](#tutorials-and-examples)
+- [Structure](#structure)
+- [Development](#development)
+ - [Tests](#tests)
+ - [Run unit tests](#run-unit-tests)
+ - [Packages](#packages)
+- [Used by](#used-by)
+- [Contribute](#contribute)
+- [API Docs](#api-docs)
+- [License](#license)
+
## Background
libp2p is the product of a long and arduous quest to understand the evolution of the Internet networking stack. In order to build P2P applications, devs have long had to make custom ad-hoc solutions to fit their needs, sometimes making some hard assumptions about their runtimes and the state of the network at the time of their development. Today, looking back more than 20 years, we see a clear pattern in the types of mechanisms built around the Internet Protocol, IP, which can be found throughout many layers of the OSI layer system, libp2p distils these mechanisms into flat categories and defines clear interfaces that once exposed, enable other protocols and applications to use and swap them, enabling upgradability and adaptability for the runtime, without breaking the API.
@@ -159,6 +116,48 @@ If you are starting your journey with `js-libp2p`, read the [GETTING\_STARTED.md
You can find multiple examples on the [examples folder](./examples) that will guide you through using libp2p for several scenarios.
+## Structure
+
+- [`/doc`](./doc) Docs for libp2p
+- [`/examples/auto-relay`](./examples/auto-relay) Shows how to configure relayed connections
+- [`/examples/chat`](./examples/chat) An example chat app using libp2p
+- [`/examples/connection-encryption`](./examples/connection-encryption) An example of how to configure connection encrypters
+- [`/examples/delegated-routing`](./examples/delegated-routing) How to configure libp2p delegated routers
+- [`/examples/discovery-mechanisms`](./examples/discovery-mechanisms) How to configure peer discovery mechanisms
+- [`/examples/echo`](./examples/echo) An example echo app
+- [`/examples/peer-and-content-routing`](./examples/peer-and-content-routing) How to use peer and content routing
+- [`/examples/pnet`](./examples/pnet) How to configure a libp2p private network
+- [`/examples/protocol-and-stream-muxing`](./examples/protocol-and-stream-muxing) How to use multiplex protocols streams
+- [`/examples/pubsub`](./examples/pubsub) An example using libp2p pubsub
+- [`/examples/transports`](./examples/transports) An example using different types of libp2p transport
+- [`/interop`](./interop) Multidimension Interop Test
+- [`/packages/crypto`](./packages/crypto) Crypto primitives for libp2p
+- [`/packages/interface`](./packages/interface) The interface implemented by a libp2p node
+- [`/packages/interface-compliance-tests`](./packages/interface-compliance-tests) Compliance tests for JS libp2p interfaces
+- [`/packages/interface-internal`](./packages/interface-internal) Interfaces implemented by internal libp2p components
+- [`/packages/kad-dht`](./packages/kad-dht) JavaScript implementation of the Kad-DHT for libp2p
+- [`/packages/keychain`](./packages/keychain) Key management and cryptographically protected messages
+- [`/packages/libp2p`](./packages/libp2p) JavaScript implementation of libp2p, a modular peer to peer network stack
+- [`/packages/logger`](./packages/logger) A logging component for use in js-libp2p modules
+- [`/packages/metrics-prometheus`](./packages/metrics-prometheus) Collect libp2p metrics for scraping by Prometheus or Graphana
+- [`/packages/multistream-select`](./packages/multistream-select) JavaScript implementation of multistream-select
+- [`/packages/peer-collections`](./packages/peer-collections) Stores values against a peer id
+- [`/packages/peer-discovery-bootstrap`](./packages/peer-discovery-bootstrap) Peer discovery via a list of bootstrap peers
+- [`/packages/peer-discovery-mdns`](./packages/peer-discovery-mdns) Node.js libp2p mDNS discovery implementation for peer discovery
+- [`/packages/peer-id`](./packages/peer-id) Implementation of @libp2p/interface-peer-id
+- [`/packages/peer-id-factory`](./packages/peer-id-factory) Create PeerId instances
+- [`/packages/peer-record`](./packages/peer-record) Used to transfer signed peer data across the network
+- [`/packages/peer-store`](./packages/peer-store) Stores information about peers libp2p knows on the network
+- [`/packages/protocol-perf`](./packages/protocol-perf) Implementation of Perf Protocol
+- [`/packages/pubsub`](./packages/pubsub) libp2p pubsub base class
+- [`/packages/pubsub-floodsub`](./packages/pubsub-floodsub) libp2p-floodsub, also known as pubsub-flood or just dumbsub, this implementation of pubsub focused on delivering an API for Publish/Subscribe, but with no CastTree Forming (it just floods the network).
+- [`/packages/stream-multiplexer-mplex`](./packages/stream-multiplexer-mplex) JavaScript implementation of
+- [`/packages/transport-tcp`](./packages/transport-tcp) A TCP transport for libp2p
+- [`/packages/transport-webrtc`](./packages/transport-webrtc) A libp2p transport using WebRTC connections
+- [`/packages/transport-websockets`](./packages/transport-websockets) JavaScript implementation of the WebSockets module that libp2p uses and that implements the interface-transport spec
+- [`/packages/transport-webtransport`](./packages/transport-webtransport) JavaScript implementation of the WebTransport module that libp2p uses and that implements the interface-transport spec
+- [`/packages/utils`](./packages/utils) Package to aggregate shared logic and dependencies for the libp2p ecosystem
+
## Development
**Clone and install dependencies:**
diff --git a/doc/GETTING_STARTED.md b/doc/GETTING_STARTED.md
index 50b37076c6..49db548f8f 100644
--- a/doc/GETTING_STARTED.md
+++ b/doc/GETTING_STARTED.md
@@ -134,7 +134,7 @@ import { createLibp2p } from 'libp2p'
import { webSockets } from '@libp2p/websockets'
import { noise } from '@chainsafe/libp2p-noise'
import { mplex } from '@libp2p/mplex'
-import { yamux } from '@chainsafe/libp2p-yamux',
+import { yamux } from '@chainsafe/libp2p-yamux'
const node = await createLibp2p({
transports: [webSockets()],
@@ -214,7 +214,7 @@ import { createLibp2p } from 'libp2p'
import { webSockets } from '@libp2p/websockets'
import { noise } from '@chainsafe/libp2p-noise'
import { mplex } from '@libp2p/mplex'
-import { yamux } from '@chainsafe/libp2p-yamux',
+import { yamux } from '@chainsafe/libp2p-yamux'
import { bootstrap } from '@libp2p/bootstrap'
diff --git a/packages/libp2p/.aegir.js b/packages/libp2p/.aegir.js
index a0d8cbc706..36fea1dd21 100644
--- a/packages/libp2p/.aegir.js
+++ b/packages/libp2p/.aegir.js
@@ -24,7 +24,9 @@ export default {
const peerId = await createEd25519PeerId()
const libp2p = await createLibp2p({
connectionManager: {
- inboundConnectionThreshold: Infinity,
+ inboundConnectionThreshold: 1000,
+ maxIncomingPendingConnections: 1000,
+ maxConnections: 1000,
minConnections: 0
},
addresses: {
@@ -51,7 +53,7 @@ export default {
fetch: fetchService(),
relay: circuitRelayServer({
reservations: {
- maxReservations: Infinity
+ maxReservations: 100000
}
})
}
diff --git a/packages/libp2p/package.json b/packages/libp2p/package.json
index 6528095829..25146e5426 100644
--- a/packages/libp2p/package.json
+++ b/packages/libp2p/package.json
@@ -164,7 +164,8 @@
"uint8arraylist": "^2.4.3",
"uint8arrays": "^4.0.6",
"wherearewe": "^2.0.1",
- "xsalsa20": "^1.1.0"
+ "xsalsa20": "^1.1.0",
+ "yup": "^1.2.0"
},
"devDependencies": {
"@chainsafe/libp2p-gossipsub": "^10.0.0",
diff --git a/packages/libp2p/src/address-manager/utils.ts b/packages/libp2p/src/address-manager/utils.ts
index 7062446a86..c0b53ec010 100644
--- a/packages/libp2p/src/address-manager/utils.ts
+++ b/packages/libp2p/src/address-manager/utils.ts
@@ -1,3 +1,8 @@
+import { type ObjectSchema, object, array, string, mixed } from 'yup'
+import { validateMultiaddr } from '../config/helpers.js'
+import type { AddressManagerInit } from '.'
+import type { Multiaddr } from '@multiformats/multiaddr'
+
export function debounce (func: () => void, wait: number): () => void {
let timeout: ReturnType | undefined
@@ -11,3 +16,12 @@ export function debounce (func: () => void, wait: number): () => void {
timeout = setTimeout(later, wait)
}
}
+
+export function validateAddressManagerConfig (opts: AddressManagerInit): ObjectSchema> {
+ return object({
+ listen: array().of(string()).test('is multiaddr', validateMultiaddr).default([]),
+ announce: array().of(string()).test('is multiaddr', validateMultiaddr).default([]),
+ noAnnounce: array().of(string()).test('is multiaddr', validateMultiaddr).default([]),
+ announceFilter: mixed().default(() => (addrs: Multiaddr[]): Multiaddr[] => addrs)
+ })
+}
diff --git a/packages/libp2p/src/autonat/index.ts b/packages/libp2p/src/autonat/index.ts
index c23ba6a9b7..cf6e191867 100644
--- a/packages/libp2p/src/autonat/index.ts
+++ b/packages/libp2p/src/autonat/index.ts
@@ -31,6 +31,7 @@ import map from 'it-map'
import parallel from 'it-parallel'
import { pipe } from 'it-pipe'
import isPrivateIp from 'private-ip'
+import { number, object, string } from 'yup'
import { codes } from '../errors.js'
import {
MAX_INBOUND_STREAMS,
@@ -108,14 +109,23 @@ class DefaultAutoNATService implements Startable {
private started: boolean
constructor (components: AutoNATComponents, init: AutoNATServiceInit) {
+ const validatedConfig = object({
+ protocolPrefix: string().default(PROTOCOL_PREFIX),
+ timeout: number().integer().default(TIMEOUT),
+ startupDelay: number().integer().default(STARTUP_DELAY),
+ refreshInterval: number().integer().default(REFRESH_INTERVAL),
+ maxInboundStreams: number().integer().default(MAX_INBOUND_STREAMS),
+ maxOutboundStreams: number().integer().default(MAX_OUTBOUND_STREAMS)
+ }).validateSync(init)
+
this.components = components
this.started = false
- 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.protocol = `/${validatedConfig.protocolPrefix}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`
+ this.timeout = validatedConfig.timeout
+ this.maxInboundStreams = validatedConfig.maxInboundStreams
+ this.maxOutboundStreams = validatedConfig.maxOutboundStreams
+ this.startupDelay = validatedConfig.startupDelay
+ this.refreshInterval = validatedConfig.refreshInterval
this._verifyExternalAddresses = this._verifyExternalAddresses.bind(this)
}
diff --git a/packages/libp2p/src/circuit-relay/constants.ts b/packages/libp2p/src/circuit-relay/constants.ts
index d35f45881a..7907ed5817 100644
--- a/packages/libp2p/src/circuit-relay/constants.ts
+++ b/packages/libp2p/src/circuit-relay/constants.ts
@@ -70,3 +70,8 @@ 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
+
+/**
+ * The default timeout for Incoming STOP requests from the relay
+ */
+export const DEFAULT_STOP_TIMEOUT = 30 * second
diff --git a/packages/libp2p/src/circuit-relay/server/index.ts b/packages/libp2p/src/circuit-relay/server/index.ts
index 85090b0be4..02fd291c88 100644
--- a/packages/libp2p/src/circuit-relay/server/index.ts
+++ b/packages/libp2p/src/circuit-relay/server/index.ts
@@ -6,10 +6,16 @@ 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 { object, number, boolean } from 'yup'
import { MAX_CONNECTIONS } from '../../connection-manager/constants.js'
+import { DEFAULT_MAX_INBOUND_STREAMS, DEFAULT_MAX_OUTBOUND_STREAMS } from '../../registrar.js'
import {
CIRCUIT_PROTO_CODE,
+ DEFAULT_DURATION_LIMIT,
DEFAULT_HOP_TIMEOUT,
+ DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL,
+ DEFAULT_MAX_RESERVATION_STORE_SIZE,
+ DEFAULT_MAX_RESERVATION_TTL,
RELAY_SOURCE_TAG,
RELAY_V2_HOP_CODEC,
RELAY_V2_STOP_CODEC
@@ -95,10 +101,6 @@ export interface RelayServerEvents {
'relay:advert:error': CustomEvent
}
-const defaults = {
- maxOutboundStopStreams: MAX_CONNECTIONS
-}
-
class CircuitRelayServer extends EventEmitter implements Startable, CircuitRelayService {
private readonly registrar: Registrar
private readonly peerStore: PeerStore
@@ -121,6 +123,20 @@ class CircuitRelayServer extends EventEmitter implements Star
constructor (components: CircuitRelayServerComponents, init: CircuitRelayServerInit = {}) {
super()
+ const validatedConfig = object({
+ hopTimeout: number().min(0).integer().default(DEFAULT_HOP_TIMEOUT),
+ reservations: object({
+ maxReservations: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_STORE_SIZE),
+ reservationClearInterval: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL),
+ applyDefaultLimit: boolean().default(true),
+ reservationTtl: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_TTL),
+ defaultDurationLimit: number().integer().min(0).default(DEFAULT_DURATION_LIMIT).max(init?.reservations?.reservationTtl ?? DEFAULT_MAX_RESERVATION_TTL, `default duration limit must be less than reservation TTL: ${init?.reservations?.reservationTtl}`)
+ }),
+ maxInboundHopStreams: number().integer().min(0).default(DEFAULT_MAX_INBOUND_STREAMS),
+ maxOutboundHopStreams: number().integer().min(0).default(DEFAULT_MAX_OUTBOUND_STREAMS),
+ maxOutboundStopStreams: number().integer().min(0).default(MAX_CONNECTIONS)
+ }).validateSync(init)
+
this.registrar = components.registrar
this.peerStore = components.peerStore
this.addressManager = components.addressManager
@@ -128,11 +144,11 @@ class CircuitRelayServer extends EventEmitter implements Star
this.connectionManager = components.connectionManager
this.connectionGater = components.connectionGater
this.started = false
- this.hopTimeout = init?.hopTimeout ?? DEFAULT_HOP_TIMEOUT
+ this.hopTimeout = validatedConfig.hopTimeout
this.shutdownController = new AbortController()
- this.maxInboundHopStreams = init.maxInboundHopStreams
- this.maxOutboundHopStreams = init.maxOutboundHopStreams
- this.maxOutboundStopStreams = init.maxOutboundStopStreams ?? defaults.maxOutboundStopStreams
+ this.maxInboundHopStreams = validatedConfig.maxInboundHopStreams
+ this.maxOutboundHopStreams = validatedConfig.maxOutboundHopStreams
+ this.maxOutboundStopStreams = validatedConfig.maxOutboundStopStreams
try {
// fails on node < 15.4
diff --git a/packages/libp2p/src/circuit-relay/server/reservation-store.ts b/packages/libp2p/src/circuit-relay/server/reservation-store.ts
index 85b9dbc1cb..e32db059ad 100644
--- a/packages/libp2p/src/circuit-relay/server/reservation-store.ts
+++ b/packages/libp2p/src/circuit-relay/server/reservation-store.ts
@@ -1,4 +1,5 @@
import { PeerMap } from '@libp2p/peer-collections'
+import { object, mixed, number, boolean } from 'yup'
import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT, DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL, DEFAULT_MAX_RESERVATION_STORE_SIZE, DEFAULT_MAX_RESERVATION_TTL } from '../constants.js'
import { type Limit, Status } from '../pb/index.js'
import type { RelayReservation } from '../index.js'
@@ -50,12 +51,21 @@ export class ReservationStore implements Startable {
private readonly defaultDataLimit: bigint
constructor (options: ReservationStoreOptions = {}) {
- this.maxReservations = options.maxReservations ?? DEFAULT_MAX_RESERVATION_STORE_SIZE
- this.reservationClearInterval = options.reservationClearInterval ?? DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL
- this.applyDefaultLimit = options.applyDefaultLimit !== false
- this.reservationTtl = options.reservationTtl ?? DEFAULT_MAX_RESERVATION_TTL
- this.defaultDurationLimit = options.defaultDurationLimit ?? DEFAULT_DURATION_LIMIT
- this.defaultDataLimit = options.defaultDataLimit ?? DEFAULT_DATA_LIMIT
+ const validatedConfig = object({
+ maxReservations: number().min(0).integer().default(DEFAULT_MAX_RESERVATION_STORE_SIZE),
+ reservationClearInterval: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL),
+ applyDefaultLimit: boolean().default(true),
+ reservationTtl: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_TTL),
+ defaultDurationLimit: number().integer().min(0).default(DEFAULT_DURATION_LIMIT),
+ defaultDataLimit: mixed().test('is-bigint', 'Invalid bigint', value => typeof value === 'bigint').default(DEFAULT_DATA_LIMIT)
+ }).validateSync(options)
+
+ this.maxReservations = validatedConfig.maxReservations
+ this.reservationClearInterval = validatedConfig.reservationClearInterval
+ this.applyDefaultLimit = validatedConfig.applyDefaultLimit
+ this.reservationTtl = validatedConfig.reservationTtl
+ this.defaultDurationLimit = validatedConfig.defaultDurationLimit
+ this.defaultDataLimit = validatedConfig.defaultDataLimit as bigint
}
isStarted (): boolean {
diff --git a/packages/libp2p/src/circuit-relay/transport/index.ts b/packages/libp2p/src/circuit-relay/transport/index.ts
index 5f11b697ac..1aa7e02ef7 100644
--- a/packages/libp2p/src/circuit-relay/transport/index.ts
+++ b/packages/libp2p/src/circuit-relay/transport/index.ts
@@ -6,9 +6,10 @@ 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 { number, object } from 'yup'
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, DEFAULT_STOP_TIMEOUT, 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'
@@ -100,12 +101,6 @@ export interface CircuitRelayTransportInit extends RelayStoreInit {
reservationCompletionTimeout?: number
}
-const defaults = {
- maxInboundStopStreams: MAX_CONNECTIONS,
- maxOutboundStopStreams: MAX_CONNECTIONS,
- stopTimeout: 30000
-}
-
class CircuitRelayTransport implements Transport {
private readonly discovery?: RelayDiscovery
private readonly registrar: Registrar
@@ -116,12 +111,19 @@ class CircuitRelayTransport implements Transport {
private readonly addressManager: AddressManager
private readonly connectionGater: ConnectionGater
private readonly reservationStore: ReservationStore
- private readonly maxInboundStopStreams: number
+ private readonly maxInboundStopStreams?: number
private readonly maxOutboundStopStreams?: number
- private readonly stopTimeout: number
+ private readonly stopTimeout?: number
private started: boolean
constructor (components: CircuitRelayTransportComponents, init: CircuitRelayTransportInit) {
+ const validatedConfig = object({
+ discoverRelays: number().min(0).integer().default(0),
+ maxInboundStopStreams: number().min(0).integer().default(MAX_CONNECTIONS),
+ maxOutboundStopStreams: number().min(0).integer().default(MAX_CONNECTIONS),
+ stopTimeout: number().min(0).integer().default(DEFAULT_STOP_TIMEOUT)
+ }).validateSync(init)
+
this.registrar = components.registrar
this.peerStore = components.peerStore
this.connectionManager = components.connectionManager
@@ -129,11 +131,11 @@ class CircuitRelayTransport implements Transport {
this.upgrader = components.upgrader
this.addressManager = components.addressManager
this.connectionGater = components.connectionGater
- this.maxInboundStopStreams = init.maxInboundStopStreams ?? defaults.maxInboundStopStreams
- this.maxOutboundStopStreams = init.maxOutboundStopStreams ?? defaults.maxOutboundStopStreams
- this.stopTimeout = init.stopTimeout ?? defaults.stopTimeout
+ this.maxInboundStopStreams = validatedConfig.maxInboundStopStreams
+ this.maxOutboundStopStreams = validatedConfig.maxOutboundStopStreams
+ this.stopTimeout = validatedConfig.stopTimeout
- if (init.discoverRelays != null && init.discoverRelays > 0) {
+ if (validatedConfig.discoverRelays > 0) {
this.discovery = new RelayDiscovery(components)
this.discovery.addEventListener('relay:discover', (evt) => {
this.reservationStore.addRelay(evt.detail, 'discovered')
@@ -321,7 +323,7 @@ class CircuitRelayTransport implements Transport {
* An incoming STOP request means a remote peer wants to dial us via a relay
*/
async onStop ({ connection, stream }: IncomingStreamData): Promise {
- const signal = AbortSignal.timeout(this.stopTimeout)
+ const signal = AbortSignal.timeout(this.stopTimeout ?? DEFAULT_STOP_TIMEOUT)
const pbstr = pbStream(stream).pb(StopMessage)
const request = await pbstr.read({
signal
diff --git a/packages/libp2p/src/config.ts b/packages/libp2p/src/config.ts
deleted file mode 100644
index a9b83fb532..0000000000
--- a/packages/libp2p/src/config.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { CodeError } from '@libp2p/interface/errors'
-import { FaultTolerance } from '@libp2p/interface/transport'
-import { defaultAddressSort } from '@libp2p/utils/address-sort'
-import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers'
-import mergeOptions from 'merge-options'
-import { codes, messages } from './errors.js'
-import type { Libp2pInit } from './index.js'
-import type { ServiceMap, RecursivePartial } from '@libp2p/interface'
-import type { Multiaddr } from '@multiformats/multiaddr'
-
-const DefaultConfig: Partial = {
- addresses: {
- listen: [],
- announce: [],
- noAnnounce: [],
- announceFilter: (multiaddrs: Multiaddr[]) => multiaddrs
- },
- connectionManager: {
- resolvers: {
- dnsaddr: dnsaddrResolver
- },
- addressSorter: defaultAddressSort
- },
- transportManager: {
- faultTolerance: FaultTolerance.FATAL_ALL
- }
-}
-
-export function validateConfig > (opts: RecursivePartial>): Libp2pInit {
- const resultingOptions: Libp2pInit = mergeOptions(DefaultConfig, opts)
-
- if (resultingOptions.transports == null || resultingOptions.transports.length < 1) {
- throw new CodeError(messages.ERR_TRANSPORTS_REQUIRED, codes.ERR_TRANSPORTS_REQUIRED)
- }
-
- if (resultingOptions.connectionProtector === null && globalThis.process?.env?.LIBP2P_FORCE_PNET != null) { // eslint-disable-line no-undef
- throw new CodeError(messages.ERR_PROTECTOR_REQUIRED, codes.ERR_PROTECTOR_REQUIRED)
- }
-
- return resultingOptions
-}
diff --git a/packages/libp2p/src/config/config.ts b/packages/libp2p/src/config/config.ts
new file mode 100644
index 0000000000..fb928825eb
--- /dev/null
+++ b/packages/libp2p/src/config/config.ts
@@ -0,0 +1,44 @@
+import { FaultTolerance } from '@libp2p/interface/transport'
+import { publicAddressesFirst } from '@libp2p/utils/address-sort'
+import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers'
+import mergeOptions from 'merge-options'
+import { object } from 'yup'
+import { validateAddressManagerConfig } from '../address-manager/utils.js'
+import { validateConnectionManagerConfig } from '../connection-manager/utils.js'
+import type { AddressManagerInit } from '../address-manager'
+import type { ConnectionManagerInit } from '../connection-manager/index.js'
+import type { Libp2pInit } from '../index.js'
+import type { ServiceMap, RecursivePartial } from '@libp2p/interface'
+
+const DefaultConfig: Partial = {
+ connectionManager: {
+ resolvers: {
+ dnsaddr: dnsaddrResolver
+ },
+ addressSorter: publicAddressesFirst
+ },
+ transportManager: {
+ faultTolerance: FaultTolerance.FATAL_ALL
+ }
+}
+
+export function validateConfig > (opts: RecursivePartial>): Libp2pInit {
+ const libp2pConfig = object({
+ addresses: validateAddressManagerConfig(opts?.addresses as AddressManagerInit),
+ connectionManager: validateConnectionManagerConfig(opts?.connectionManager as ConnectionManagerInit)
+ })
+
+ if ((opts?.services) != null) {
+ // @ts-expect-error until we resolve https://github.com/libp2p/js-libp2p/pull/1762 and have a better way of discovering type dependencies
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
+ if ((opts.services?.kadDHT || opts.services?.relay || opts.services?.ping) && !opts.services.identify) {
+ throw new Error('identify service is required when using kadDHT, relay, or ping')
+ }
+ }
+
+ const parsedOpts = libp2pConfig.validateSync(opts)
+
+ const resultingOptions: Libp2pInit = mergeOptions(DefaultConfig, parsedOpts)
+
+ return resultingOptions
+}
diff --git a/packages/libp2p/src/config/helpers.ts b/packages/libp2p/src/config/helpers.ts
new file mode 100644
index 0000000000..9cb4106ae8
--- /dev/null
+++ b/packages/libp2p/src/config/helpers.ts
@@ -0,0 +1,12 @@
+import { multiaddr } from '@multiformats/multiaddr'
+
+export const validateMultiaddr = (value: Array | undefined): boolean => {
+ value?.forEach((addr) => {
+ try {
+ multiaddr(addr)
+ } catch (err) {
+ throw new Error(`invalid multiaddr: ${addr}`)
+ }
+ })
+ return true
+}
diff --git a/packages/libp2p/src/connection-manager/utils.ts b/packages/libp2p/src/connection-manager/utils.ts
index e195e48bf6..760461beab 100644
--- a/packages/libp2p/src/connection-manager/utils.ts
+++ b/packages/libp2p/src/connection-manager/utils.ts
@@ -2,6 +2,10 @@ import { setMaxListeners } from 'events'
import { logger } from '@libp2p/logger'
import { type AbortOptions, multiaddr, type Multiaddr } from '@multiformats/multiaddr'
import { type ClearableSignal, anySignal } from 'any-signal'
+import { type ObjectSchema, array, number, object, string } from 'yup'
+import { validateMultiaddr } from '../config/helpers.js'
+import { AUTO_DIAL_CONCURRENCY, AUTO_DIAL_INTERVAL, AUTO_DIAL_PRIORITY, DIAL_TIMEOUT, INBOUND_CONNECTION_THRESHOLD, INBOUND_UPGRADE_TIMEOUT, MAX_CONNECTIONS, MAX_INCOMING_PENDING_CONNECTIONS, MAX_PARALLEL_DIALS, MAX_PARALLEL_DIALS_PER_PEER, MAX_PEER_ADDRS_TO_DIAL, MIN_CONNECTIONS } from './constants.js'
+import type { ConnectionManagerInit } from '.'
const log = logger('libp2p:connection-manager:utils')
@@ -73,3 +77,22 @@ export function combineSignals (...signals: Array): Cle
return signal
}
+
+export const validateConnectionManagerConfig = (opts: ConnectionManagerInit): ObjectSchema> => {
+ return object({
+ maxConnections: number().min(opts?.minConnections ?? MIN_CONNECTIONS, `maxConnections must be greater than the min connections limit: ${opts?.minConnections}`).integer().default(MAX_CONNECTIONS),
+ minConnections: number().min(0).integer().max(opts?.maxConnections ?? MAX_CONNECTIONS, `minConnections must be less than the max connections limit: ${opts?.maxConnections}`).default(MIN_CONNECTIONS),
+ autoDialInterval: number().min(0).integer().default(AUTO_DIAL_INTERVAL),
+ autoDialConcurrency: number().min(0).integer().default(AUTO_DIAL_CONCURRENCY),
+ autoDialPriority: number().min(0).integer().default(AUTO_DIAL_PRIORITY),
+ maxParallelDials: number().min(0).integer().default(MAX_PARALLEL_DIALS),
+ maxParallelDialsPerPeer: number().max(opts?.autoDialConcurrency ?? AUTO_DIAL_CONCURRENCY, `maxParallelDialsPerPeer must be less than the min auto dial conccurency limit: ${opts?.autoDialConcurrency}`).default(MAX_PARALLEL_DIALS_PER_PEER),
+ maxPeerAddrsToDialed: number().min(0).integer().default(MAX_PEER_ADDRS_TO_DIAL),
+ dialTimeout: number().min(0).integer().default(DIAL_TIMEOUT),
+ inboundUpgradeTimeout: number().integer().default(INBOUND_UPGRADE_TIMEOUT),
+ allow: array().of(string()).test('is multiaddr', validateMultiaddr).optional(),
+ deny: array().of(string()).test('is multiaddr', validateMultiaddr).optional(),
+ inboundConnectionThreshold: number().max(opts?.maxConnections ?? MAX_CONNECTIONS, `inboundConnectionThreshold must be less than the max connections limit: ${opts?.inboundConnectionThreshold}`).integer().default(INBOUND_CONNECTION_THRESHOLD),
+ maxIncomingPendingConnections: number().integer().max(opts?.maxConnections ?? MAX_CONNECTIONS, `maxIncomingPendingConnections must be less than the max connections limit: ${opts?.maxIncomingPendingConnections}`).default(MAX_INCOMING_PENDING_CONNECTIONS)
+ })
+}
diff --git a/packages/libp2p/src/dcutr/dcutr.ts b/packages/libp2p/src/dcutr/dcutr.ts
index f5954267ff..8c45208a43 100644
--- a/packages/libp2p/src/dcutr/dcutr.ts
+++ b/packages/libp2p/src/dcutr/dcutr.ts
@@ -5,6 +5,7 @@ import { Circuit, IP, DNS } from '@multiformats/multiaddr-matcher'
import delay from 'delay'
import { pbStream } from 'it-protobuf-stream'
import isPrivate from 'private-ip'
+import { number, object } from 'yup'
import { codes } from '../errors.js'
import { HolePunch } from './pb/message.js'
import { multicodec } from './index.js'
@@ -24,15 +25,6 @@ const MAX_DCUTR_MESSAGE_SIZE = 1024 * 4
// ensure the dial has a high priority to jump to the head of the dial queue
const DCUTR_DIAL_PRIORITY = 100
-const defaultValues = {
- // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/holepunch/holepuncher.go#L27
- timeout: 5000,
- // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/holepunch/holepuncher.go#L28
- retries: 3,
- maxInboundStreams: 1,
- maxOutboundStreams: 1
-}
-
export class DefaultDCUtRService implements Startable {
private started: boolean
private readonly timeout: number
@@ -54,10 +46,19 @@ export class DefaultDCUtRService implements Startable {
this.connectionManager = components.connectionManager
this.transportManager = components.transportManager
- this.timeout = init.timeout ?? defaultValues.timeout
- this.retries = init.retries ?? defaultValues.retries
- this.maxInboundStreams = init.maxInboundStreams ?? defaultValues.maxInboundStreams
- this.maxOutboundStreams = init.maxOutboundStreams ?? defaultValues.maxOutboundStreams
+ const validatedConfig = object({
+ // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/holepunch/holepuncher.go#L27
+ timeout: number().integer().default(5000).min(1),
+ // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/holepunch/holepuncher.go#L28
+ retries: number().integer().default(3).min(1),
+ maxInboundStreams: number().integer().default(1).min(1),
+ maxOutboundStreams: number().integer().default(1).min(1)
+ }).validateSync(init)
+
+ this.timeout = validatedConfig.timeout
+ this.retries = validatedConfig.retries
+ this.maxInboundStreams = validatedConfig.maxInboundStreams
+ this.maxOutboundStreams = validatedConfig.maxOutboundStreams
}
isStarted (): boolean {
diff --git a/packages/libp2p/src/fetch/constants.ts b/packages/libp2p/src/fetch/constants.ts
index cbab081bcd..d0be9327b2 100644
--- a/packages/libp2p/src/fetch/constants.ts
+++ b/packages/libp2p/src/fetch/constants.ts
@@ -1,3 +1,7 @@
// https://github.com/libp2p/specs/tree/master/fetch#wire-protocol
export const PROTOCOL_VERSION = '0.0.1'
export const PROTOCOL_NAME = 'fetch'
+
+export const MAX_INBOUND_STREAMS = 1
+export const MAX_OUTBOUND_STREAMS = 1
+export const TIMEOUT = 60000
diff --git a/packages/libp2p/src/fetch/index.ts b/packages/libp2p/src/fetch/index.ts
index ea0f15dca8..7a5e577ea8 100644
--- a/packages/libp2p/src/fetch/index.ts
+++ b/packages/libp2p/src/fetch/index.ts
@@ -6,8 +6,9 @@ import * as lp from 'it-length-prefixed'
import { pipe } from 'it-pipe'
import { fromString as uint8arrayFromString } from 'uint8arrays/from-string'
import { toString as uint8arrayToString } from 'uint8arrays/to-string'
+import { number, object, string } from 'yup'
import { codes } from '../errors.js'
-import { PROTOCOL_NAME, PROTOCOL_VERSION } from './constants.js'
+import { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, PROTOCOL_NAME, PROTOCOL_VERSION, TIMEOUT } from './constants.js'
import { FetchRequest, FetchResponse } from './pb/proto.js'
import type { AbortOptions } from '@libp2p/interface'
import type { Stream } from '@libp2p/interface/connection'
@@ -18,8 +19,6 @@ import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/r
const log = logger('libp2p:fetch')
-const DEFAULT_TIMEOUT = 10000
-
export interface FetchServiceInit {
protocolPrefix?: string
maxInboundStreams?: number
@@ -94,15 +93,27 @@ class DefaultFetchService implements Startable, FetchService {
private readonly components: FetchServiceComponents
private readonly lookupFunctions: Map
private started: boolean
- private readonly init: FetchServiceInit
+ private readonly timeout: number
+ private readonly maxInboundStreams: number
+ private readonly maxOutboundStreams: number
constructor (components: FetchServiceComponents, init: FetchServiceInit) {
+ const validatedConfig = object({
+ protocolPrefix: string().default('libp2p'),
+ timeout: number().integer().default(TIMEOUT),
+ maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS),
+ maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS)
+ }).validateSync(init)
+
this.started = false
this.components = components
- this.protocol = `/${init.protocolPrefix ?? 'libp2p'}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`
+ this.protocol = `/${validatedConfig.protocolPrefix}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`
+ this.timeout = validatedConfig.timeout
+ this.maxInboundStreams = validatedConfig.maxInboundStreams
+ this.maxOutboundStreams = validatedConfig.maxOutboundStreams
+
this.lookupFunctions = new Map() // Maps key prefix to value lookup function
this.handleMessage = this.handleMessage.bind(this)
- this.init = init
}
async start (): Promise {
@@ -115,8 +126,8 @@ class DefaultFetchService implements Startable, FetchService {
log.error(err)
})
}, {
- maxInboundStreams: this.init.maxInboundStreams,
- maxOutboundStreams: this.init.maxOutboundStreams
+ maxInboundStreams: this.maxInboundStreams,
+ maxOutboundStreams: this.maxOutboundStreams
})
this.started = true
}
@@ -143,8 +154,8 @@ class DefaultFetchService implements Startable, FetchService {
// create a timeout if no abort signal passed
if (signal == null) {
- log('using default timeout of %d ms', this.init.timeout)
- signal = AbortSignal.timeout(this.init.timeout ?? DEFAULT_TIMEOUT)
+ log('using default timeout of %d ms', this.timeout)
+ signal = AbortSignal.timeout(this.timeout)
try {
// fails on node < 15.4
diff --git a/packages/libp2p/src/identify/consts.ts b/packages/libp2p/src/identify/consts.ts
index e65f222951..ae3356018e 100644
--- a/packages/libp2p/src/identify/consts.ts
+++ b/packages/libp2p/src/identify/consts.ts
@@ -5,8 +5,21 @@ export const AGENT_VERSION = `js-libp2p/${version}`
export const MULTICODEC_IDENTIFY = '/ipfs/id/1.0.0' // deprecated
export const MULTICODEC_IDENTIFY_PUSH = '/ipfs/id/push/1.0.0' // deprecated
+export const PROTOCOL_PREFIX = 'ipfs'
+export const MAX_IDENTIFY_MESSAGE_SIZE = 1024 * 8 // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L52
+export const MAX_INBOUND_STREAMS = 1
+export const MAX_OUTBOUND_STREAMS = 1
+export const MAX_PUSH_INCOMING_STREAMS = 1
+export const MAX_PUSH_OUTGOING_STREAMS = 1
+export const MAX_OBSERVED_ADDRESSES = 10
+
+export const RUN_ON_TRANSIENT_CONNECTION = true
+export const RUN_ON_CONNECTION_OPEN = true
+
export const IDENTIFY_PROTOCOL_VERSION = '0.1.0'
export const MULTICODEC_IDENTIFY_PROTOCOL_NAME = 'id'
export const MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME = 'id/push'
export const MULTICODEC_IDENTIFY_PROTOCOL_VERSION = '1.0.0'
export const MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION = '1.0.0'
+
+export const TIMEOUT = 60000
diff --git a/packages/libp2p/src/identify/identify.ts b/packages/libp2p/src/identify/identify.ts
index e9905d7184..b4df947a7c 100644
--- a/packages/libp2p/src/identify/identify.ts
+++ b/packages/libp2p/src/identify/identify.ts
@@ -8,6 +8,7 @@ import { pbStream } from 'it-protobuf-stream'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import { isNode, isBrowser, isWebWorker, isElectronMain, isElectronRenderer, isReactNative } from 'wherearewe'
+import { boolean, number, object, string } from 'yup'
import { codes } from '../errors.js'
import {
AGENT_VERSION,
@@ -15,7 +16,17 @@ import {
MULTICODEC_IDENTIFY_PROTOCOL_NAME,
MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME,
MULTICODEC_IDENTIFY_PROTOCOL_VERSION,
- MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION
+ MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION,
+ MAX_INBOUND_STREAMS,
+ MAX_OUTBOUND_STREAMS,
+ MAX_IDENTIFY_MESSAGE_SIZE,
+ TIMEOUT,
+ RUN_ON_CONNECTION_OPEN,
+ PROTOCOL_PREFIX,
+ RUN_ON_TRANSIENT_CONNECTION,
+ MAX_PUSH_INCOMING_STREAMS,
+ MAX_PUSH_OUTGOING_STREAMS,
+ MAX_OBSERVED_ADDRESSES
} from './consts.js'
import { Identify } from './pb/message.js'
import type { IdentifyService, IdentifyServiceComponents, IdentifyServiceInit } from './index.js'
@@ -31,24 +42,6 @@ import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/r
const log = logger('libp2p:identify')
-// https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L52
-const MAX_IDENTIFY_MESSAGE_SIZE = 1024 * 8
-
-const defaultValues = {
- protocolPrefix: 'ipfs',
- agentVersion: AGENT_VERSION,
- // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L48
- timeout: 60000,
- maxInboundStreams: 1,
- maxOutboundStreams: 1,
- maxPushIncomingStreams: 1,
- maxPushOutgoingStreams: 1,
- maxObservedAddresses: 10,
- maxIdentifyMessageSize: 8192,
- runOnConnectionOpen: true,
- runOnTransientConnection: true
-}
-
export class DefaultIdentifyService implements Startable, IdentifyService {
private readonly identifyProtocolStr: string
private readonly identifyPushProtocolStr: string
@@ -72,8 +65,23 @@ export class DefaultIdentifyService implements Startable, IdentifyService {
private readonly maxObservedAddresses: number
private readonly events: EventEmitter
private readonly runOnTransientConnection: boolean
+ private readonly runOnConnectionOpen: boolean
constructor (components: IdentifyServiceComponents, init: IdentifyServiceInit) {
+ const validatedConfig = object({
+ protocolPrefix: string().default(PROTOCOL_PREFIX),
+ agentVersion: string().default(AGENT_VERSION),
+ timeout: number().integer().default(TIMEOUT),
+ maxIdentifyMessageSize: number().integer().min(0).default(MAX_IDENTIFY_MESSAGE_SIZE),
+ maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS),
+ maxPushIncomingStreams: number().integer().min(0).default(MAX_PUSH_INCOMING_STREAMS),
+ maxPushOutgoingStreams: number().integer().min(0).default(MAX_PUSH_OUTGOING_STREAMS),
+ maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS),
+ maxObservedAddresses: number().integer().min(0).default(MAX_OBSERVED_ADDRESSES),
+ runOnConnectionOpen: boolean().default(RUN_ON_CONNECTION_OPEN),
+ runOnTransientConnection: boolean().default(RUN_ON_TRANSIENT_CONNECTION)
+ }).validateSync(init)
+
this.started = false
this.peerId = components.peerId
this.peerStore = components.peerStore
@@ -82,24 +90,25 @@ export class DefaultIdentifyService implements Startable, IdentifyService {
this.connectionManager = components.connectionManager
this.events = components.events
- this.identifyProtocolStr = `/${init.protocolPrefix ?? defaultValues.protocolPrefix}/${MULTICODEC_IDENTIFY_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PROTOCOL_VERSION}`
- this.identifyPushProtocolStr = `/${init.protocolPrefix ?? defaultValues.protocolPrefix}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION}`
- this.timeout = init.timeout ?? defaultValues.timeout
- this.maxInboundStreams = init.maxInboundStreams ?? defaultValues.maxInboundStreams
- this.maxOutboundStreams = init.maxOutboundStreams ?? defaultValues.maxOutboundStreams
- this.maxPushIncomingStreams = init.maxPushIncomingStreams ?? defaultValues.maxPushIncomingStreams
- this.maxPushOutgoingStreams = init.maxPushOutgoingStreams ?? defaultValues.maxPushOutgoingStreams
- this.maxIdentifyMessageSize = init.maxIdentifyMessageSize ?? defaultValues.maxIdentifyMessageSize
- this.maxObservedAddresses = init.maxObservedAddresses ?? defaultValues.maxObservedAddresses
- this.runOnTransientConnection = init.runOnTransientConnection ?? defaultValues.runOnTransientConnection
+ this.identifyProtocolStr = `/${validatedConfig.protocolPrefix}/${MULTICODEC_IDENTIFY_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PROTOCOL_VERSION}`
+ this.identifyPushProtocolStr = `/${validatedConfig.protocolPrefix}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION}`
+ this.timeout = validatedConfig.timeout
+ this.maxInboundStreams = validatedConfig.maxInboundStreams
+ this.maxOutboundStreams = validatedConfig.maxOutboundStreams
+ this.maxPushIncomingStreams = validatedConfig.maxPushIncomingStreams
+ this.maxPushOutgoingStreams = validatedConfig.maxPushOutgoingStreams
+ this.maxIdentifyMessageSize = validatedConfig.maxIdentifyMessageSize
+ this.maxObservedAddresses = validatedConfig.maxObservedAddresses
+ this.runOnTransientConnection = validatedConfig.runOnTransientConnection
+ this.runOnConnectionOpen = validatedConfig.runOnConnectionOpen
// Store self host metadata
this.host = {
- protocolVersion: `${init.protocolPrefix ?? defaultValues.protocolPrefix}/${IDENTIFY_PROTOCOL_VERSION}`,
- agentVersion: init.agentVersion ?? defaultValues.agentVersion
+ protocolVersion: `${validatedConfig.protocolPrefix}/${IDENTIFY_PROTOCOL_VERSION}`,
+ agentVersion: validatedConfig.agentVersion
}
- if (init.runOnConnectionOpen ?? defaultValues.runOnConnectionOpen) {
+ if (this.runOnConnectionOpen) {
// When a new connection happens, trigger identify
components.events.addEventListener('connection:open', (evt) => {
const connection = evt.detail
@@ -313,7 +322,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService {
log('our observed address is %a', cleanObservedAddr)
if (cleanObservedAddr != null &&
- this.addressManager.getObservedAddrs().length < (this.maxObservedAddresses ?? Infinity)) {
+ this.addressManager.getObservedAddrs().length < (this.maxObservedAddresses)) {
log('storing our observed address %a', cleanObservedAddr)
this.addressManager.addObservedAddr(cleanObservedAddr)
}
diff --git a/packages/libp2p/src/identify/index.ts b/packages/libp2p/src/identify/index.ts
index a61309b3d3..cd8e6a7ab4 100644
--- a/packages/libp2p/src/identify/index.ts
+++ b/packages/libp2p/src/identify/index.ts
@@ -1,7 +1,4 @@
-import {
- MULTICODEC_IDENTIFY,
- MULTICODEC_IDENTIFY_PUSH
-} from './consts.js'
+import { MULTICODEC_IDENTIFY, MULTICODEC_IDENTIFY_PUSH } from './consts.js'
import { DefaultIdentifyService } from './identify.js'
import { Identify } from './pb/message.js'
import type { AbortOptions, IdentifyResult, Libp2pEvents } from '@libp2p/interface'
diff --git a/packages/libp2p/src/libp2p.ts b/packages/libp2p/src/libp2p.ts
index 2a6912d8eb..e382098e18 100644
--- a/packages/libp2p/src/libp2p.ts
+++ b/packages/libp2p/src/libp2p.ts
@@ -18,8 +18,8 @@ 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 { validateConfig } from './config/config.js'
import { connectionGater } from './config/connection-gater.js'
-import { validateConfig } from './config.js'
import { DefaultConnectionManager } from './connection-manager/index.js'
import { CompoundContentRouting } from './content-routing/index.js'
import { codes } from './errors.js'
diff --git a/packages/libp2p/src/ping/index.ts b/packages/libp2p/src/ping/index.ts
index 49c813b72e..74491dc267 100644
--- a/packages/libp2p/src/ping/index.ts
+++ b/packages/libp2p/src/ping/index.ts
@@ -4,6 +4,7 @@ import { logger } from '@libp2p/logger'
import first from 'it-first'
import { pipe } from 'it-pipe'
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
+import { boolean, number, object, string } from 'yup'
import { codes } from '../errors.js'
import { PROTOCOL_PREFIX, PROTOCOL_NAME, PING_LENGTH, PROTOCOL_VERSION, TIMEOUT, MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS } from './constants.js'
import type { AbortOptions } from '@libp2p/interface'
@@ -49,11 +50,20 @@ class DefaultPingService implements Startable, PingService {
constructor (components: PingServiceComponents, init: PingServiceInit) {
this.components = components
this.started = false
- 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.runOnTransientConnection = init.runOnTransientConnection ?? true
+
+ const validatedConfig = object({
+ protocolPrefix: string().default(PROTOCOL_PREFIX),
+ timeout: number().integer().default(TIMEOUT),
+ maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS),
+ maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS),
+ runOnTransientConnection: boolean().default(true)
+ }).validateSync(init)
+
+ this.protocol = `/${validatedConfig.protocolPrefix}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`
+ this.timeout = validatedConfig.timeout
+ this.maxInboundStreams = validatedConfig.maxInboundStreams
+ this.maxOutboundStreams = validatedConfig.maxOutboundStreams
+ this.runOnTransientConnection = validatedConfig.runOnTransientConnection
}
async start (): Promise {
diff --git a/packages/libp2p/src/upnp-nat/index.ts b/packages/libp2p/src/upnp-nat/index.ts
index 5dc183700e..1bd2a74c67 100644
--- a/packages/libp2p/src/upnp-nat/index.ts
+++ b/packages/libp2p/src/upnp-nat/index.ts
@@ -5,6 +5,7 @@ import { isLoopback } from '@libp2p/utils/multiaddr/is-loopback'
import { fromNodeAddress } from '@multiformats/multiaddr'
import isPrivateIp from 'private-ip'
import { isBrowser } from 'wherearewe'
+import { boolean, number, object, string } from 'yup'
import { codes } from '../errors.js'
import * as pkg from '../version.js'
import type { PeerId } from '@libp2p/interface/peer-id'
@@ -70,21 +71,32 @@ class UPnPNAT implements Startable {
private readonly localAddress?: string
private readonly description: string
private readonly ttl: number
- private readonly keepAlive: boolean
+ private readonly keepAlive?: boolean
private readonly gateway?: string
private started: boolean
private client?: NatAPI
constructor (components: UPnPNATComponents, init: UPnPNATInit) {
this.components = components
-
this.started = false
- this.externalAddress = init.externalAddress
- this.localAddress = init.localAddress
- this.description = init.description ?? `${pkg.name}@${pkg.version} ${this.components.peerId.toString()}`
- this.ttl = init.ttl ?? DEFAULT_TTL
- this.keepAlive = init.keepAlive ?? true
- this.gateway = init.gateway
+
+ const validIPRegex = /^(?:(?:^|\.)(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])){4}$/
+
+ const validatedConfig = object({
+ externalAddress: string().matches(validIPRegex, 'Invalid IP address'),
+ localAddress: string().matches(validIPRegex, 'Invalid IP address'),
+ description: string().default(`${pkg.name}@${pkg.version} ${this.components.peerId.toString()}`),
+ ttl: number().integer().default(DEFAULT_TTL),
+ keepAlive: boolean().default(true),
+ gateway: string().optional()
+ }).validateSync(init)
+
+ this.externalAddress = validatedConfig.externalAddress
+ this.localAddress = validatedConfig.localAddress
+ this.description = validatedConfig.description
+ this.ttl = validatedConfig.ttl
+ this.keepAlive = validatedConfig.keepAlive
+ this.gateway = validatedConfig.gateway
if (this.ttl < DEFAULT_TTL) {
throw new CodeError(`NatManager ttl should be at least ${DEFAULT_TTL} seconds`, codes.ERR_INVALID_PARAMETERS)
diff --git a/packages/libp2p/test/circuit-relay/discovery.node.ts b/packages/libp2p/test/circuit-relay/discovery.node.ts
index 19517e67be..b6c434d6d7 100644
--- a/packages/libp2p/test/circuit-relay/discovery.node.ts
+++ b/packages/libp2p/test/circuit-relay/discovery.node.ts
@@ -5,6 +5,7 @@ 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 { identifyService } from '../../src/identify/index.js'
import { createLibp2p } from '../../src/index.js'
import { plaintext } from '../../src/insecure/index.js'
import { getRelayAddress, hasRelay, MockContentRouting, mockContentRouting } from './utils.js'
@@ -34,6 +35,7 @@ describe('circuit-relay discovery', () => {
mockContentRouting()
],
services: {
+ identify: identifyService(),
relay: circuitRelayServer({
advertise: {
bootDelay: 10
diff --git a/packages/libp2p/test/configuration/protocol-prefix.node.ts b/packages/libp2p/test/configuration/protocol-prefix.node.ts
index db4ecea61c..93eb2af4f5 100644
--- a/packages/libp2p/test/configuration/protocol-prefix.node.ts
+++ b/packages/libp2p/test/configuration/protocol-prefix.node.ts
@@ -67,7 +67,8 @@ describe('Protocol prefix is configurable', () => {
'/ipfs/id/1.0.0',
'/ipfs/id/push/1.0.0',
'/ipfs/ping/1.0.0',
- '/libp2p/fetch/0.0.1'
+ '/libp2p/fetch/0.0.1',
+ '/libp2p/circuit/relay/0.2.0/stop'
])
})
})
diff --git a/packages/libp2p/test/connection-manager/index.node.ts b/packages/libp2p/test/connection-manager/index.node.ts
index add7158a47..9a45635f4d 100644
--- a/packages/libp2p/test/connection-manager/index.node.ts
+++ b/packages/libp2p/test/connection-manager/index.node.ts
@@ -235,7 +235,9 @@ describe('libp2p.connections', () => {
},
connectionManager: {
minConnections,
- maxConnections: 1
+ maxConnections: 1,
+ inboundConnectionThreshold: 1,
+ maxIncomingPendingConnections: 1
}
}
})
diff --git a/packages/libp2p/test/connection-manager/index.spec.ts b/packages/libp2p/test/connection-manager/index.spec.ts
index 699339da03..4189969895 100644
--- a/packages/libp2p/test/connection-manager/index.spec.ts
+++ b/packages/libp2p/test/connection-manager/index.spec.ts
@@ -77,7 +77,9 @@ describe('Connection Manager', () => {
config: createBaseOptions({
connectionManager: {
maxConnections: max,
- minConnections: 2
+ minConnections: 2,
+ inboundConnectionThreshold: max,
+ maxIncomingPendingConnections: max
}
}),
started: false
@@ -136,7 +138,9 @@ describe('Connection Manager', () => {
config: createBaseOptions({
connectionManager: {
maxConnections: max,
- minConnections: 2
+ minConnections: 2,
+ maxIncomingPendingConnections: max,
+ inboundConnectionThreshold: max
}
}),
started: false
@@ -202,6 +206,8 @@ describe('Connection Manager', () => {
connectionManager: {
maxConnections: max,
minConnections: 0,
+ maxIncomingPendingConnections: max,
+ inboundConnectionThreshold: max,
allow: [
'/ip4/83.13.55.32'
]
@@ -286,7 +292,9 @@ describe('Connection Manager', () => {
config: createBaseOptions({
connectionManager: {
maxConnections: max,
- minConnections: 0
+ minConnections: 0,
+ maxIncomingPendingConnections: max,
+ inboundConnectionThreshold: max
}
}),
started: false
@@ -319,11 +327,13 @@ describe('Connection Manager', () => {
config: createBaseOptions({
connectionManager: {
maxConnections: 5,
- minConnections: 6
+ minConnections: 6,
+ maxIncomingPendingConnections: 5,
+ inboundConnectionThreshold: 5
}
}),
started: false
- })).to.eventually.rejected('maxConnections must be greater')
+ })).to.eventually.rejectedWith('minConnections must be less than the max connections limit: 5')
})
it('should reconnect to important peers on startup', async () => {
diff --git a/packages/libp2p/test/connection-manager/resolver.spec.ts b/packages/libp2p/test/connection-manager/resolver.spec.ts
index adccf3976d..ef8ed7f905 100644
--- a/packages/libp2p/test/connection-manager/resolver.spec.ts
+++ b/packages/libp2p/test/connection-manager/resolver.spec.ts
@@ -14,6 +14,7 @@ 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 { identifyService } from '../../src/identify/index.js'
import { plaintext } from '../../src/insecure/index.js'
import { createLibp2pNode, type Libp2pNode } from '../../src/libp2p.js'
import type { PeerId } from '@libp2p/interface/peer-id'
@@ -93,7 +94,8 @@ describe('dialing (resolvable addresses)', () => {
plaintext()
],
services: {
- relay: circuitRelayServer()
+ relay: circuitRelayServer(),
+ identify: identifyService()
},
connectionGater: mockConnectionGater()
})
diff --git a/packages/libp2p/test/ping/ping.node.ts b/packages/libp2p/test/ping/ping.node.ts
index aba6537aa7..6c5e0f299e 100644
--- a/packages/libp2p/test/ping/ping.node.ts
+++ b/packages/libp2p/test/ping/ping.node.ts
@@ -4,6 +4,7 @@ import { multiaddr } from '@multiformats/multiaddr'
import { expect } from 'aegir/chai'
import { pipe } from 'it-pipe'
import pDefer from 'p-defer'
+import { identifyService } from '../../src/identify/index.js'
import { PROTOCOL } from '../../src/ping/constants.js'
import { pingService, type PingService } from '../../src/ping/index.js'
import { createBaseOptions } from '../fixtures/base-options.js'
@@ -18,6 +19,7 @@ describe('ping', () => {
createNode({
config: createBaseOptions({
services: {
+ identify: identifyService(),
ping: pingService()
}
})
@@ -25,6 +27,7 @@ describe('ping', () => {
createNode({
config: createBaseOptions({
services: {
+ identify: identifyService(),
ping: pingService()
}
})
@@ -32,6 +35,7 @@ describe('ping', () => {
createNode({
config: createBaseOptions({
services: {
+ identify: identifyService(),
ping: pingService()
}
})
@@ -106,6 +110,7 @@ describe('ping', () => {
const client = await createNode({
config: createBaseOptions({
services: {
+ identify: identifyService(),
ping: pingService({
// Allow two outbound ping streams.
// It is not allowed by the spec, but this test needs to open two concurrent streams.
diff --git a/packages/transport-webrtc/.aegir.js b/packages/transport-webrtc/.aegir.js
index 491576df87..dfbb5439dc 100644
--- a/packages/transport-webrtc/.aegir.js
+++ b/packages/transport-webrtc/.aegir.js
@@ -1,4 +1,3 @@
-
/** @type {import('aegir').PartialOptions} */
export default {
build: {
@@ -32,7 +31,7 @@ export default {
services: {
relay: circuitRelayServer({
reservations: {
- maxReservations: Infinity
+ maxReservations: 10000
}
}),
identify: identifyService()