From 28794fe41f8a53d17b8836f3cd97e3b5b6c220ab Mon Sep 17 00:00:00 2001
From: David
Date: Wed, 4 Oct 2023 20:18:10 +0200
Subject: [PATCH 1/3] docs: fix syntax error in JS example in README (#2123)
Co-authored-by: Chad Nehemiah
---
doc/GETTING_STARTED.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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'
From 980857c34783d15af98036fa47f10da3d52a9cad Mon Sep 17 00:00:00 2001
From: Chad Nehemiah
Date: Wed, 4 Oct 2023 21:16:19 -0500
Subject: [PATCH 2/3] docs: update README structure (#2115)
---
README.md | 145 +++++++++++++++++++++++++++---------------------------
1 file changed, 72 insertions(+), 73 deletions(-)
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:**
From e9099d405b205b741fcd0958569bfb06d6211e65 Mon Sep 17 00:00:00 2001
From: Chad Nehemiah
Date: Thu, 5 Oct 2023 18:34:21 -0500
Subject: [PATCH 3/3] feat: configuration validation (#1778)
Co-authored-by: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com>
---
packages/libp2p/.aegir.js | 6 +-
packages/libp2p/package.json | 3 +-
packages/libp2p/src/address-manager/utils.ts | 14 ++++
packages/libp2p/src/autonat/index.ts | 22 ++++--
.../libp2p/src/circuit-relay/constants.ts | 5 ++
.../libp2p/src/circuit-relay/server/index.ts | 32 ++++++--
.../circuit-relay/server/reservation-store.ts | 22 ++++--
.../src/circuit-relay/transport/index.ts | 30 ++++----
packages/libp2p/src/config.ts | 41 ----------
packages/libp2p/src/config/config.ts | 44 +++++++++++
packages/libp2p/src/config/helpers.ts | 12 +++
.../libp2p/src/connection-manager/utils.ts | 23 ++++++
packages/libp2p/src/dcutr/dcutr.ts | 27 +++----
packages/libp2p/src/fetch/constants.ts | 4 +
packages/libp2p/src/fetch/index.ts | 31 +++++---
packages/libp2p/src/identify/consts.ts | 13 ++++
packages/libp2p/src/identify/identify.ts | 75 +++++++++++--------
packages/libp2p/src/identify/index.ts | 5 +-
packages/libp2p/src/libp2p.ts | 2 +-
packages/libp2p/src/ping/index.ts | 20 +++--
packages/libp2p/src/upnp-nat/index.ts | 28 +++++--
.../test/circuit-relay/discovery.node.ts | 2 +
.../configuration/protocol-prefix.node.ts | 3 +-
.../test/connection-manager/index.node.ts | 4 +-
.../test/connection-manager/index.spec.ts | 20 +++--
.../test/connection-manager/resolver.spec.ts | 4 +-
packages/libp2p/test/ping/ping.node.ts | 5 ++
packages/transport-webrtc/.aegir.js | 3 +-
28 files changed, 338 insertions(+), 162 deletions(-)
delete mode 100644 packages/libp2p/src/config.ts
create mode 100644 packages/libp2p/src/config/config.ts
create mode 100644 packages/libp2p/src/config/helpers.ts
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 10b4827403..3b5b1879da 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 3f70e6a850..319493d39b 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 aed3d5be8b..758db09e4d 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 0f5d3bf62e..36a747c99c 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 483f9a0366..6125c55b20 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 190766bfa2..6639a356f1 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 604fb15f3b..378919526b 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 caa5248b45..3e992da045 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 29c80f0cc7..d5c829b142 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 8e422f6b1a..6969263b66 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()