From 6ba3dd8f2d94dda30adcff4d0230604a303f962d Mon Sep 17 00:00:00 2001 From: chad Date: Tue, 23 May 2023 19:05:22 -0500 Subject: [PATCH 01/20] wip --- package.json | 1 + src/config.ts | 18 ++++++------- src/connection-manager/utils.ts | 47 +++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index d6da32a37a..0781e964db 100644 --- a/package.json +++ b/package.json @@ -179,6 +179,7 @@ "private-ip": "^3.0.0", "protons-runtime": "^5.0.0", "rate-limiter-flexible": "^2.3.11", + "superstruct": "^1.0.3", "uint8arraylist": "^2.3.2", "uint8arrays": "^4.0.2", "wherearewe": "^2.0.0", diff --git a/src/config.ts b/src/config.ts index 20e8176b5f..a2337c1a2d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,9 +1,9 @@ import { FaultTolerance } from '@libp2p/interface-transport' -import { CodeError } from '@libp2p/interfaces/errors' import { publicAddressesFirst } from '@libp2p/utils/address-sort' import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers' import mergeOptions from 'merge-options' -import { codes, messages } from './errors.js' +import { object, optional } from 'superstruct' +import { validateConnectionManagerConfig } from './connection-manager/utils.js' import type { Libp2pInit } from './index.js' import type { ServiceMap } from '@libp2p/interface-libp2p' import type { RecursivePartial } from '@libp2p/interfaces' @@ -28,15 +28,15 @@ const DefaultConfig: Partial = { } export function validateConfig > (opts: RecursivePartial>): Libp2pInit { - const resultingOptions: Libp2pInit = mergeOptions(DefaultConfig, opts) + const libp2pConfig = object({ + connectionManager: optional(validateConnectionManagerConfig(opts.connectionManager)) + }) - if (resultingOptions.transports == null || resultingOptions.transports.length < 1) { - throw new CodeError(messages.ERR_TRANSPORTS_REQUIRED, codes.ERR_TRANSPORTS_REQUIRED) - } + const [error] = libp2pConfig.validate(opts) - 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) - } + if (error != null) throw error + + const resultingOptions: Libp2pInit = mergeOptions(DefaultConfig, opts) return resultingOptions } diff --git a/src/connection-manager/utils.ts b/src/connection-manager/utils.ts index e195e48bf6..216230d6aa 100644 --- a/src/connection-manager/utils.ts +++ b/src/connection-manager/utils.ts @@ -2,6 +2,8 @@ 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 { define, integer, max, min, number, object, type Struct } from 'superstruct' +import { type ConnectionManagerInit } from '.' const log = logger('libp2p:connection-manager:utils') @@ -73,3 +75,48 @@ export function combineSignals (...signals: Array): Cle return signal } + +export const validateConnectionManagerConfig = (opts: ConnectionManagerInit): Struct => { + const minConnections = define('minConnections', (value: number) => { + if (value < 0) { + return false + } + + if (value > opts.maxConnections) { + return false + } + if (!Number.isInteger(value)) { + return false + } + + return true + }) + + const validateMultiaddr = define('allow', (value) => { + value.forEach((addr) => { + try { + multiaddr(addr) + } catch (err) { + throw new Error(`invalid multiaddr: ${addr}`) + } + }) + return true + }) + + return object({ + maxConnections: min(integer(), opts.minConnections), + minConnections, + autoDialInterval: min(number(), 0), + autoDialConcurrency: min(integer(), 0), + autoDialPriority: min(integer(), 0), + maxParallelDials: min(integer(), opts.autoDialConcurrency), + maxParallelDialsPerPeer: min(integer(), opts.autoDialConcurrency), + maxPeerAddrsToDialed: min(integer(), 0), + dialTimeout: min(integer(), 0), + inboundUpgradeTimeout: min(integer(), 0), + allow: validateMultiaddr, + deny: validateMultiaddr, + inboundConnectionThreshold: max(integer(), opts.maxConnections), + maxIncomingPendingConnections: max(integer(), opts.maxConnections) + }) +} From 68f6e83bb86c5784eeb843eeb7a1bb8c7cfc9929 Mon Sep 17 00:00:00 2001 From: chad Date: Mon, 29 May 2023 12:04:40 -0500 Subject: [PATCH 02/20] wip --- package.json | 4 +-- src/config.ts | 13 ++++---- src/connection-manager/utils.ts | 54 ++++++++++++--------------------- src/libp2p.ts | 2 +- 4 files changed, 28 insertions(+), 45 deletions(-) diff --git a/package.json b/package.json index 0781e964db..bc2945fbcf 100644 --- a/package.json +++ b/package.json @@ -179,11 +179,11 @@ "private-ip": "^3.0.0", "protons-runtime": "^5.0.0", "rate-limiter-flexible": "^2.3.11", - "superstruct": "^1.0.3", "uint8arraylist": "^2.3.2", "uint8arrays": "^4.0.2", "wherearewe": "^2.0.0", - "xsalsa20": "^1.1.0" + "xsalsa20": "^1.1.0", + "yup": "^1.1.1" }, "devDependencies": { "@chainsafe/libp2p-gossipsub": "^8.0.0", diff --git a/src/config.ts b/src/config.ts index a2337c1a2d..b907655d93 100644 --- a/src/config.ts +++ b/src/config.ts @@ -2,8 +2,9 @@ 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, optional } from 'superstruct' +import { object } from 'yup' import { validateConnectionManagerConfig } from './connection-manager/utils.js' +import type { ConnectionManagerInit } from './connection-manager/index.js' import type { Libp2pInit } from './index.js' import type { ServiceMap } from '@libp2p/interface-libp2p' import type { RecursivePartial } from '@libp2p/interfaces' @@ -27,16 +28,14 @@ const DefaultConfig: Partial = { } } -export function validateConfig > (opts: RecursivePartial>): Libp2pInit { +export async function validateConfig > (opts: RecursivePartial>): Promise> { const libp2pConfig = object({ - connectionManager: optional(validateConnectionManagerConfig(opts.connectionManager)) + connectionManager: validateConnectionManagerConfig(opts.connectionManager as ConnectionManagerInit) }) - const [error] = libp2pConfig.validate(opts) + const parsedOpts = await libp2pConfig.validate(opts) - if (error != null) throw error - - const resultingOptions: Libp2pInit = mergeOptions(DefaultConfig, opts) + const resultingOptions: Libp2pInit = mergeOptions(DefaultConfig, parsedOpts) return resultingOptions } diff --git a/src/connection-manager/utils.ts b/src/connection-manager/utils.ts index 216230d6aa..3e7a466512 100644 --- a/src/connection-manager/utils.ts +++ b/src/connection-manager/utils.ts @@ -2,8 +2,7 @@ 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 { define, integer, max, min, number, object, type Struct } from 'superstruct' -import { type ConnectionManagerInit } from '.' +import { array, number, object, string } from 'yup' const log = logger('libp2p:connection-manager:utils') @@ -76,24 +75,9 @@ export function combineSignals (...signals: Array): Cle return signal } -export const validateConnectionManagerConfig = (opts: ConnectionManagerInit): Struct => { - const minConnections = define('minConnections', (value: number) => { - if (value < 0) { - return false - } - - if (value > opts.maxConnections) { - return false - } - if (!Number.isInteger(value)) { - return false - } - - return true - }) - - const validateMultiaddr = define('allow', (value) => { - value.forEach((addr) => { +export const validateConnectionManagerConfig = (opts: any): any => { + const validateMultiaddr = (value: Array | undefined): boolean => { + value?.forEach((addr) => { try { multiaddr(addr) } catch (err) { @@ -101,22 +85,22 @@ export const validateConnectionManagerConfig = (opts: ConnectionManagerInit): St } }) return true - }) + } return object({ - maxConnections: min(integer(), opts.minConnections), - minConnections, - autoDialInterval: min(number(), 0), - autoDialConcurrency: min(integer(), 0), - autoDialPriority: min(integer(), 0), - maxParallelDials: min(integer(), opts.autoDialConcurrency), - maxParallelDialsPerPeer: min(integer(), opts.autoDialConcurrency), - maxPeerAddrsToDialed: min(integer(), 0), - dialTimeout: min(integer(), 0), - inboundUpgradeTimeout: min(integer(), 0), - allow: validateMultiaddr, - deny: validateMultiaddr, - inboundConnectionThreshold: max(integer(), opts.maxConnections), - maxIncomingPendingConnections: max(integer(), opts.maxConnections) + maxConnections: number().min(opts.minConnections).integer().default(300).optional(), + minConnections: number().min(0).integer().default(50).optional().max(opts.maxConnections), + autoDialInterval: number().min(0).integer().optional(), + autoDialConcurrency: number().min(0).integer().optional(), + autoDialPriority: number().min(0).integer().optional(), + maxParallelDials: number().min(0).integer().optional(), + maxParallelDialsPerPeer: number().min(opts.autoDialConcurrency).optional(), + maxPeerAddrsToDialed: number().min(0).integer().optional(), + dialTimeout: number().min(0).integer().optional(), + inboundUpgradeTimeout: number().integer().optional(), + allow: array().of(string()).test('is multiaddr', validateMultiaddr).optional(), + deny: array().of(string()).test('is multiaddr', validateMultiaddr).optional(), + inboundConnectionThreshold: number().default(5).max(opts.maxConnections).integer().optional(), + maxIncomingPendingConnections: number().integer().max(opts.maxConnections).optional() }) } diff --git a/src/libp2p.ts b/src/libp2p.ts index 130c3a2f82..5bec84f222 100644 --- a/src/libp2p.ts +++ b/src/libp2p.ts @@ -429,5 +429,5 @@ export async function createLibp2pNode Date: Mon, 29 May 2023 16:16:27 -0500 Subject: [PATCH 03/20] refactor: added connection manager config validation --- .aegir.js | 12 ++++++----- src/config.ts | 6 +++--- src/connection-manager/utils.ts | 30 ++++++++++++++------------- src/libp2p.ts | 2 +- test/connection-manager/index.spec.ts | 20 +++++++++++++----- 5 files changed, 42 insertions(+), 28 deletions(-) diff --git a/.aegir.js b/.aegir.js index e006da5444..12df259538 100644 --- a/.aegir.js +++ b/.aegir.js @@ -1,9 +1,9 @@ -import { webSockets } from '@libp2p/websockets' -import { mplex } from '@libp2p/mplex' import { noise } from '@chainsafe/libp2p-noise' -import { pipe } from 'it-pipe' -import { createFromJSON } from '@libp2p/peer-id-factory' import { yamux } from '@chainsafe/libp2p-yamux' +import { mplex } from '@libp2p/mplex' +import { createFromJSON } from '@libp2p/peer-id-factory' +import { webSockets } from '@libp2p/websockets' +import { pipe } from 'it-pipe' /** @type {import('aegir').PartialOptions} */ export default { @@ -26,7 +26,9 @@ export default { const peerId = await createFromJSON(Peers[Peers.length - 1]) const libp2p = await createLibp2p({ connectionManager: { - inboundConnectionThreshold: Infinity + inboundConnectionThreshold: 1000, + maxIncomingPendingConnections: 1000, + maxConnections: 1000 }, addresses: { listen: [MULTIADDRS_WEBSOCKETS[0]] diff --git a/src/config.ts b/src/config.ts index b907655d93..96ef04890a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -28,12 +28,12 @@ const DefaultConfig: Partial = { } } -export async function validateConfig > (opts: RecursivePartial>): Promise> { +export function validateConfig > (opts: RecursivePartial>): Libp2pInit { const libp2pConfig = object({ - connectionManager: validateConnectionManagerConfig(opts.connectionManager as ConnectionManagerInit) + connectionManager: validateConnectionManagerConfig(opts?.connectionManager as ConnectionManagerInit) }) - const parsedOpts = await libp2pConfig.validate(opts) + const parsedOpts = libp2pConfig.validateSync(opts) const resultingOptions: Libp2pInit = mergeOptions(DefaultConfig, parsedOpts) diff --git a/src/connection-manager/utils.ts b/src/connection-manager/utils.ts index 3e7a466512..f01ec904c2 100644 --- a/src/connection-manager/utils.ts +++ b/src/connection-manager/utils.ts @@ -2,7 +2,9 @@ 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 { array, number, object, string } from 'yup' +import { type ObjectSchema, array, number, object, string } from 'yup' +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') @@ -75,7 +77,7 @@ export function combineSignals (...signals: Array): Cle return signal } -export const validateConnectionManagerConfig = (opts: any): any => { +export const validateConnectionManagerConfig = (opts: ConnectionManagerInit): ObjectSchema> => { const validateMultiaddr = (value: Array | undefined): boolean => { value?.forEach((addr) => { try { @@ -88,19 +90,19 @@ export const validateConnectionManagerConfig = (opts: any): any => { } return object({ - maxConnections: number().min(opts.minConnections).integer().default(300).optional(), - minConnections: number().min(0).integer().default(50).optional().max(opts.maxConnections), - autoDialInterval: number().min(0).integer().optional(), - autoDialConcurrency: number().min(0).integer().optional(), - autoDialPriority: number().min(0).integer().optional(), - maxParallelDials: number().min(0).integer().optional(), - maxParallelDialsPerPeer: number().min(opts.autoDialConcurrency).optional(), - maxPeerAddrsToDialed: number().min(0).integer().optional(), - dialTimeout: number().min(0).integer().optional(), - inboundUpgradeTimeout: number().integer().optional(), + 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().default(5).max(opts.maxConnections).integer().optional(), - maxIncomingPendingConnections: number().integer().max(opts.maxConnections).optional() + inboundConnectionThreshold: number().max(opts?.maxConnections ?? MAX_CONNECTIONS, `inboundConnectionThreshold must be less than the max connections limit: ${opts?.maxConnections}`).integer().default(INBOUND_CONNECTION_THRESHOLD), + maxIncomingPendingConnections: number().integer().max(opts?.maxConnections ?? MAX_CONNECTIONS, `maxIncomingPendingConnections must be less than the max connections limit: ${opts?.maxConnections}`).default(MAX_INCOMING_PENDING_CONNECTIONS) }) } diff --git a/src/libp2p.ts b/src/libp2p.ts index 5bec84f222..130c3a2f82 100644 --- a/src/libp2p.ts +++ b/src/libp2p.ts @@ -429,5 +429,5 @@ export async function createLibp2pNode { config: createBaseOptions({ connectionManager: { maxConnections: max, - minConnections: 2 + minConnections: 2, + inboundConnectionThreshold: max, + maxIncomingPendingConnections: max } }), started: false @@ -135,7 +137,9 @@ describe('Connection Manager', () => { config: createBaseOptions({ connectionManager: { maxConnections: max, - minConnections: 2 + minConnections: 2, + maxIncomingPendingConnections: max, + inboundConnectionThreshold: max } }), started: false @@ -201,6 +205,8 @@ describe('Connection Manager', () => { connectionManager: { maxConnections: max, minConnections: 0, + maxIncomingPendingConnections: max, + inboundConnectionThreshold: max, allow: [ '/ip4/83.13.55.32' ] @@ -285,7 +291,9 @@ describe('Connection Manager', () => { config: createBaseOptions({ connectionManager: { maxConnections: max, - minConnections: 0 + minConnections: 0, + maxIncomingPendingConnections: max, + inboundConnectionThreshold: max } }), started: false @@ -318,11 +326,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 () => { From 71490dbd0081f12d6f8fefcfefbb52b25920ee54 Mon Sep 17 00:00:00 2001 From: chad Date: Mon, 29 May 2023 16:35:36 -0500 Subject: [PATCH 04/20] refactor: added addresses config validation (#1573) --- src/address-manager/utils.ts | 11 +++++++++++ src/config.ts | 3 +++ src/connection-manager/utils.ts | 12 +----------- src/utils.ts | 12 ++++++++++++ 4 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 src/utils.ts diff --git a/src/address-manager/utils.ts b/src/address-manager/utils.ts index 54c691a6fb..30e8caf5d7 100644 --- a/src/address-manager/utils.ts +++ b/src/address-manager/utils.ts @@ -1,3 +1,6 @@ +import { type ObjectSchema, object, array, string } from 'yup' +import { validateMultiaddr } from '../utils.js' +import type { AddressManagerInit } from '.' export function debounce (func: () => void, wait: number): () => void { let timeout: ReturnType | undefined @@ -12,3 +15,11 @@ 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).optional(), + announce: array().of(string()).test('is multiaddr', validateMultiaddr).optional(), + noAnnounce: array().of(string()).test('is multiaddr', validateMultiaddr).optional() + }) +} diff --git a/src/config.ts b/src/config.ts index 96ef04890a..4980fe4ecc 100644 --- a/src/config.ts +++ b/src/config.ts @@ -3,7 +3,9 @@ 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/index.js' import type { ConnectionManagerInit } from './connection-manager/index.js' import type { Libp2pInit } from './index.js' import type { ServiceMap } from '@libp2p/interface-libp2p' @@ -30,6 +32,7 @@ const DefaultConfig: Partial = { export function validateConfig > (opts: RecursivePartial>): Libp2pInit { const libp2pConfig = object({ + addresses: validateAddressManagerConfig(opts?.addresses as AddressManagerInit), connectionManager: validateConnectionManagerConfig(opts?.connectionManager as ConnectionManagerInit) }) diff --git a/src/connection-manager/utils.ts b/src/connection-manager/utils.ts index f01ec904c2..91e83afdf8 100644 --- a/src/connection-manager/utils.ts +++ b/src/connection-manager/utils.ts @@ -3,6 +3,7 @@ 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 '../utils.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 '.' @@ -78,17 +79,6 @@ export function combineSignals (...signals: Array): Cle } export const validateConnectionManagerConfig = (opts: ConnectionManagerInit): ObjectSchema> => { - const validateMultiaddr = (value: Array | undefined): boolean => { - value?.forEach((addr) => { - try { - multiaddr(addr) - } catch (err) { - throw new Error(`invalid multiaddr: ${addr}`) - } - }) - return true - } - 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), diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000000..9cb4106ae8 --- /dev/null +++ b/src/utils.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 +} From 3aeb75452e9c38dca07a6ab0c203b88594b15c8c Mon Sep 17 00:00:00 2001 From: chad Date: Tue, 30 May 2023 22:12:11 -0500 Subject: [PATCH 05/20] wip --- src/address-manager/utils.ts | 2 +- src/circuit-relay/utils.ts | 21 ++++++++++++++++++++- src/{ => config}/config.ts | 14 +++++++++----- src/config/helpers.ts | 24 ++++++++++++++++++++++++ src/connection-manager/utils.ts | 2 +- src/identify/config.ts | 15 +++++++++++++++ src/identify/consts.ts | 5 +++++ src/identify/identify.ts | 12 +++++++----- src/libp2p.ts | 3 ++- src/utils.ts | 12 ------------ 10 files changed, 84 insertions(+), 26 deletions(-) rename src/{ => config}/config.ts (71%) create mode 100644 src/config/helpers.ts create mode 100644 src/identify/config.ts delete mode 100644 src/utils.ts diff --git a/src/address-manager/utils.ts b/src/address-manager/utils.ts index 30e8caf5d7..bfcdb6211b 100644 --- a/src/address-manager/utils.ts +++ b/src/address-manager/utils.ts @@ -1,5 +1,5 @@ import { type ObjectSchema, object, array, string } from 'yup' -import { validateMultiaddr } from '../utils.js' +import { validateMultiaddr } from '../config/helpers.js' import type { AddressManagerInit } from '.' export function debounce (func: () => void, wait: number): () => void { diff --git a/src/circuit-relay/utils.ts b/src/circuit-relay/utils.ts index 6545680479..7b111e2fd0 100644 --- a/src/circuit-relay/utils.ts +++ b/src/circuit-relay/utils.ts @@ -3,7 +3,11 @@ import { abortableSource } from 'abortable-iterator' import { anySignal } from 'any-signal' import { CID } from 'multiformats/cid' import { sha256 } from 'multiformats/hashes/sha2' -import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT } from './constants.js' +import { object, number, boolean } from 'yup' +import { DEFAULT_MAX_INBOUND_STREAMS, DEFAULT_MAX_OUTBOUND_STREAMS } from '../registrar.js' +import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT, DEFAULT_HOP_TIMEOUT, DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL, DEFAULT_MAX_RESERVATION_STORE_SIZE, DEFAULT_MAX_RESERVATION_TTL } from './constants.js' +import { type CircuitRelayServerInit, circuitRelayServer, type CircuitRelayServerComponents } from './server/index.js' +import type { CircuitRelayService } from './index.js' import type { Limit } from './pb/index.js' import type { Stream } from '@libp2p/interface-connection' import type { Source } from 'it-stream-types' @@ -124,3 +128,18 @@ export function getExpirationMilliseconds (expireTimeSeconds: bigint): number { // downcast to number to use with setTimeout return Number(expireTimeMillis - BigInt(currentTime)) } + +export const validateCircuitRelayServicesConfig = (opts: CircuitRelayServerInit): (components: CircuitRelayServerComponents) => CircuitRelayService => { + return circuitRelayServer(object({ + hopTimeout: number().min(0).integer().default(DEFAULT_HOP_TIMEOUT).optional(), + reservations: object({ + maxReservations: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_STORE_SIZE).optional(), + reservationClearInterval: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL).optional(), + applyDefaultLimit: boolean().default(true).optional(), + reservationTtl: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_TTL).optional(), + defaultDurationLimit: number().integer().min(0).default(DEFAULT_DURATION_LIMIT).max(opts?.reservations?.reservationTtl ?? DEFAULT_MAX_RESERVATION_TTL, `default duration limit must be less than reservation TTL: ${opts?.reservations?.reservationTtl}`).optional() + }), + maxInboundHopStreams: number().integer().min(0).default(DEFAULT_MAX_INBOUND_STREAMS).optional(), + maxOutboundHopStreams: number().integer().min(0).default(DEFAULT_MAX_OUTBOUND_STREAMS).optional() + }).validateSync(opts)) +} diff --git a/src/config.ts b/src/config/config.ts similarity index 71% rename from src/config.ts rename to src/config/config.ts index 4980fe4ecc..85ffe4ac7e 100644 --- a/src/config.ts +++ b/src/config/config.ts @@ -3,11 +3,12 @@ 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/index.js' -import type { ConnectionManagerInit } from './connection-manager/index.js' -import type { Libp2pInit } from './index.js' +import { validateAddressManagerConfig } from '../address-manager/utils.js' +import { validateConnectionManagerConfig } from '../connection-manager/utils.js' +import { validateServicesConfig } from './helpers.js' +import type { AddressManagerInit } from '../address-manager/index.js' +import type { ConnectionManagerInit } from '../connection-manager/index.js' +import type { Libp2pInit, ServiceFactoryMap } from '../index.js' import type { ServiceMap } from '@libp2p/interface-libp2p' import type { RecursivePartial } from '@libp2p/interfaces' import type { Multiaddr } from '@multiformats/multiaddr' @@ -36,6 +37,9 @@ export function validateConfig > connectionManager: validateConnectionManagerConfig(opts?.connectionManager as ConnectionManagerInit) }) + // @ts-expect-error + opts.services = validateServicesConfig(opts?.services as ServiceFactoryMap) as ServiceFactoryMap + const parsedOpts = libp2pConfig.validateSync(opts) const resultingOptions: Libp2pInit = mergeOptions(DefaultConfig, parsedOpts) diff --git a/src/config/helpers.ts b/src/config/helpers.ts new file mode 100644 index 0000000000..3f0b2912e3 --- /dev/null +++ b/src/config/helpers.ts @@ -0,0 +1,24 @@ +import { multiaddr } from '@multiformats/multiaddr' +import { validateCircuitRelayServicesConfig } from '../circuit-relay/utils.js' +import { validateIdentifyConfig } from '../identify/config.js' +import type { CircuitRelayServerInit } from '../circuit-relay/server/index.js' +import type { IdentifyServiceInit } from '../identify/index.js' +import type { ServiceMap } from '@libp2p/interface-libp2p' + +export const validateMultiaddr = (value: Array | undefined): boolean => { + value?.forEach((addr) => { + try { + multiaddr(addr) + } catch (err) { + throw new Error(`invalid multiaddr: ${addr}`) + } + }) + return true +} + +export const validateServicesConfig = (opts: ServiceMap): ServiceMap => { + return { + identify: validateIdentifyConfig(opts?.identify as IdentifyServiceInit), + relay: validateCircuitRelayServicesConfig(opts?.relay as CircuitRelayServerInit) + } +} diff --git a/src/connection-manager/utils.ts b/src/connection-manager/utils.ts index 91e83afdf8..d6604ad897 100644 --- a/src/connection-manager/utils.ts +++ b/src/connection-manager/utils.ts @@ -3,7 +3,7 @@ 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 '../utils.js' +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 '.' diff --git a/src/identify/config.ts b/src/identify/config.ts new file mode 100644 index 0000000000..f31838f7af --- /dev/null +++ b/src/identify/config.ts @@ -0,0 +1,15 @@ +import { number, object, string } from 'yup' +import { AGENT_VERSION, MAX_IDENTIFY_MESSAGE_SIZE, MAX_INBOUND_STREAMS } from './consts' +import { identifyService, type IdentifyServiceComponents, type IdentifyServiceInit } from '.' +import type { DefaultIdentifyService } from './identify' + +export const validateIdentifyConfig = (opts: IdentifyServiceInit): (components: IdentifyServiceComponents) => DefaultIdentifyService => { + return identifyService(object({ + protocolPrefix: string().default('ipfs'), + agentVersion: string().default(AGENT_VERSION), + timeout: number().integer().default(60000), + maxIdentifyMessageSize: number().integer().min(0).default(MAX_IDENTIFY_MESSAGE_SIZE), + maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS), + maxOutboundStreams: number().integer().min(0).default(0) + }).validateSync(opts)) +} diff --git a/src/identify/consts.ts b/src/identify/consts.ts index adb38720af..d99ea12bb8 100644 --- a/src/identify/consts.ts +++ b/src/identify/consts.ts @@ -6,6 +6,11 @@ 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 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 IDENTIFY_PROTOCOL_VERSION = '0.1.0' export const MULTICODEC_IDENTIFY_PROTOCOL_NAME = 'id' export const MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME = 'id/push' diff --git a/src/identify/identify.ts b/src/identify/identify.ts index ad53d5f275..26edf4e070 100644 --- a/src/identify/identify.ts +++ b/src/identify/identify.ts @@ -20,7 +20,10 @@ 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_PUSH_INCOMING_STREAMS } from './consts.js' import { Identify } from './pb/message.js' import type { IdentifyServiceComponents, IdentifyServiceInit } from './index.js' @@ -38,16 +41,15 @@ import type { Startable } from '@libp2p/interfaces/startable' 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, + maxInboundStreams: MAX_INBOUND_STREAMS, + maxOutboundStreams: MAX_OUTBOUND_STREAMS, + maxPushIncomingStreams: MAX_PUSH_INCOMING_STREAMS, maxPushOutgoingStreams: 1, maxObservedAddresses: 10, maxIdentifyMessageSize: 8192 diff --git a/src/libp2p.ts b/src/libp2p.ts index 130c3a2f82..6bd28a3b2b 100644 --- a/src/libp2p.ts +++ b/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' @@ -161,6 +161,7 @@ export class Libp2pNode> extends }) // User defined modules + console.log('services', init.services) if (init.services != null) { for (const name of Object.keys(init.services)) { const createService = init.services[name] diff --git a/src/utils.ts b/src/utils.ts deleted file mode 100644 index 9cb4106ae8..0000000000 --- a/src/utils.ts +++ /dev/null @@ -1,12 +0,0 @@ -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 -} From 722dc7a5c3d0cdb182c3041fe6764380f2ee32f1 Mon Sep 17 00:00:00 2001 From: chad Date: Mon, 3 Jul 2023 08:46:40 -0500 Subject: [PATCH 06/20] wip --- packages/libp2p/package.json | 3 +- packages/libp2p/src/config.ts | 56 ------------------------ packages/libp2p/src/config/config.ts | 12 +++-- packages/libp2p/src/identify/config.ts | 6 +-- packages/libp2p/src/identify/identify.ts | 3 +- 5 files changed, 15 insertions(+), 65 deletions(-) delete mode 100644 packages/libp2p/src/config.ts diff --git a/packages/libp2p/package.json b/packages/libp2p/package.json index ce64a297a6..0ee519dc83 100644 --- a/packages/libp2p/package.json +++ b/packages/libp2p/package.json @@ -157,7 +157,8 @@ "uint8arraylist": "^2.4.3", "uint8arrays": "^4.0.4", "wherearewe": "^2.0.1", - "xsalsa20": "^1.1.0" + "xsalsa20": "^1.1.0", + "yup": "^1.2.0" }, "devDependencies": { "@chainsafe/libp2p-gossipsub": "^8.0.0", diff --git a/packages/libp2p/src/config.ts b/packages/libp2p/src/config.ts deleted file mode 100644 index c5bb7ce042..0000000000 --- a/packages/libp2p/src/config.ts +++ /dev/null @@ -1,56 +0,0 @@ -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 { validateServicesConfig } from './helpers.js' -import type { AddressManagerInit } from '../address-manager/index.js' -import type { ConnectionManagerInit } from '../connection-manager/index.js' -import type { Libp2pInit, ServiceFactoryMap } from '../index.js' -import type { ServiceMap } from '@libp2p/interface-libp2p' -import type { RecursivePartial } from '@libp2p/interfaces' -import { CodeError } from '@libp2p/interface/errors' -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 { 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: 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) - }) - - // @ts-expect-error - opts.services = validateServicesConfig(opts?.services as ServiceFactoryMap) as ServiceFactoryMap - - const parsedOpts = libp2pConfig.validateSync(opts) - - const resultingOptions: Libp2pInit = mergeOptions(DefaultConfig, parsedOpts) - - return resultingOptions -} diff --git a/packages/libp2p/src/config/config.ts b/packages/libp2p/src/config/config.ts index 9b3d4a0835..9b73606680 100644 --- a/packages/libp2p/src/config/config.ts +++ b/packages/libp2p/src/config/config.ts @@ -1,12 +1,16 @@ -import { CodeError } from '@libp2p/interface/errors' 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 { codes, messages } from './errors.js' -import type { Libp2pInit } from './index.js' import type { ServiceMap, RecursivePartial } from '@libp2p/interface' import type { Multiaddr } from '@multiformats/multiaddr' +import type { Libp2pInit, ServiceFactoryMap } from '../index.js' +import type { AddressManagerInit } from '../address-manager' +import { validateAddressManagerConfig } from '../address-manager/utils.js' +import { object } from 'yup' +import { validateConnectionManagerConfig } from '../connection-manager/utils.js' +import type { ConnectionManagerInit } from '../connection-manager/index.js' +import { validateServicesConfig } from './helpers.js' const DefaultConfig: Partial = { addresses: { @@ -32,7 +36,7 @@ export function validateConfig > connectionManager: validateConnectionManagerConfig(opts?.connectionManager as ConnectionManagerInit) }) - // @ts-expect-error + //@ts-expect-error opts.services = validateServicesConfig(opts?.services as ServiceFactoryMap) as ServiceFactoryMap const parsedOpts = libp2pConfig.validateSync(opts) diff --git a/packages/libp2p/src/identify/config.ts b/packages/libp2p/src/identify/config.ts index f31838f7af..2337d0baa8 100644 --- a/packages/libp2p/src/identify/config.ts +++ b/packages/libp2p/src/identify/config.ts @@ -1,7 +1,7 @@ import { number, object, string } from 'yup' -import { AGENT_VERSION, MAX_IDENTIFY_MESSAGE_SIZE, MAX_INBOUND_STREAMS } from './consts' -import { identifyService, type IdentifyServiceComponents, type IdentifyServiceInit } from '.' -import type { DefaultIdentifyService } from './identify' +import { AGENT_VERSION, MAX_IDENTIFY_MESSAGE_SIZE, MAX_INBOUND_STREAMS } from './consts.js' +import { identifyService, type IdentifyServiceComponents, type IdentifyServiceInit } from './index.js' +import type { DefaultIdentifyService } from './identify.js' export const validateIdentifyConfig = (opts: IdentifyServiceInit): (components: IdentifyServiceComponents) => DefaultIdentifyService => { return identifyService(object({ diff --git a/packages/libp2p/src/identify/identify.ts b/packages/libp2p/src/identify/identify.ts index 38d87ed965..60ca14307f 100644 --- a/packages/libp2p/src/identify/identify.ts +++ b/packages/libp2p/src/identify/identify.ts @@ -23,7 +23,8 @@ import { MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION, MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, - MAX_PUSH_INCOMING_STREAMS + MAX_PUSH_INCOMING_STREAMS, + MAX_IDENTIFY_MESSAGE_SIZE } from './consts.js' import { Identify } from './pb/message.js' import type { IdentifyServiceComponents, IdentifyServiceInit } from './index.js' From 27a9789158618141beb724c0842853b69e6d083c Mon Sep 17 00:00:00 2001 From: chad Date: Mon, 3 Jul 2023 13:35:07 -0500 Subject: [PATCH 07/20] feat: added fetch validation --- packages/libp2p/src/config/helpers.ts | 9 ++++- packages/libp2p/src/fetch/config.ts | 12 +++++++ packages/libp2p/src/fetch/constants.ts | 4 +++ packages/libp2p/src/identify/config.ts | 8 ++--- packages/libp2p/src/identify/consts.ts | 5 +++ packages/libp2p/src/identify/identify.ts | 46 ++++++++++-------------- packages/libp2p/src/libp2p.ts | 1 - packages/libp2p/src/ping/config.ts | 12 +++++++ 8 files changed, 63 insertions(+), 34 deletions(-) create mode 100644 packages/libp2p/src/fetch/config.ts create mode 100644 packages/libp2p/src/ping/config.ts diff --git a/packages/libp2p/src/config/helpers.ts b/packages/libp2p/src/config/helpers.ts index 3f0b2912e3..3f3d4f78ec 100644 --- a/packages/libp2p/src/config/helpers.ts +++ b/packages/libp2p/src/config/helpers.ts @@ -4,6 +4,10 @@ import { validateIdentifyConfig } from '../identify/config.js' import type { CircuitRelayServerInit } from '../circuit-relay/server/index.js' import type { IdentifyServiceInit } from '../identify/index.js' import type { ServiceMap } from '@libp2p/interface-libp2p' +import type { PingServiceInit } from '../ping/index.js' +import { validatePingConfig } from '../ping/config.js' +import type { FetchServiceInit } from '../fetch/index.js' +import { validateFetchConfig } from '../fetch/config.js' export const validateMultiaddr = (value: Array | undefined): boolean => { value?.forEach((addr) => { @@ -18,7 +22,10 @@ export const validateMultiaddr = (value: Array | undefined): export const validateServicesConfig = (opts: ServiceMap): ServiceMap => { return { + ping: validatePingConfig(opts?.ping as PingServiceInit), + fetch: validateFetchConfig(opts?.fetch as FetchServiceInit), identify: validateIdentifyConfig(opts?.identify as IdentifyServiceInit), - relay: validateCircuitRelayServicesConfig(opts?.relay as CircuitRelayServerInit) + relay: validateCircuitRelayServicesConfig(opts?.relay as CircuitRelayServerInit), + ...opts, } } diff --git a/packages/libp2p/src/fetch/config.ts b/packages/libp2p/src/fetch/config.ts new file mode 100644 index 0000000000..c1b182beb9 --- /dev/null +++ b/packages/libp2p/src/fetch/config.ts @@ -0,0 +1,12 @@ +import { number, object, string } from "yup" +import { fetchService, type FetchService, type FetchServiceComponents, type FetchServiceInit } from "./index.js" +import { TIMEOUT, MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS } from "./constants.js" + +export const validateFetchConfig = (opts: FetchServiceInit): (components: FetchServiceComponents) => FetchService => { + return fetchService(object({ + protocolPrefix: string().default('ipfs'), + timeout: number().integer().default(TIMEOUT), + maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS), + maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS), + }).validateSync(opts)) +} diff --git a/packages/libp2p/src/fetch/constants.ts b/packages/libp2p/src/fetch/constants.ts index 1c2551bb05..5d9cee0821 100644 --- a/packages/libp2p/src/fetch/constants.ts +++ b/packages/libp2p/src/fetch/constants.ts @@ -2,3 +2,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/identify/config.ts b/packages/libp2p/src/identify/config.ts index 2337d0baa8..45720241a8 100644 --- a/packages/libp2p/src/identify/config.ts +++ b/packages/libp2p/src/identify/config.ts @@ -1,15 +1,15 @@ import { number, object, string } from 'yup' -import { AGENT_VERSION, MAX_IDENTIFY_MESSAGE_SIZE, MAX_INBOUND_STREAMS } from './consts.js' +import { AGENT_VERSION, MAX_IDENTIFY_MESSAGE_SIZE, MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, PROTOCOL_PREFIX, TIMEOUT } from './consts.js' import { identifyService, type IdentifyServiceComponents, type IdentifyServiceInit } from './index.js' import type { DefaultIdentifyService } from './identify.js' export const validateIdentifyConfig = (opts: IdentifyServiceInit): (components: IdentifyServiceComponents) => DefaultIdentifyService => { return identifyService(object({ - protocolPrefix: string().default('ipfs'), + protocolPrefix: string().default(PROTOCOL_PREFIX), agentVersion: string().default(AGENT_VERSION), - timeout: number().integer().default(60000), + timeout: number().integer().default(TIMEOUT), maxIdentifyMessageSize: number().integer().min(0).default(MAX_IDENTIFY_MESSAGE_SIZE), maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS), - maxOutboundStreams: number().integer().min(0).default(0) + maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS), }).validateSync(opts)) } diff --git a/packages/libp2p/src/identify/consts.ts b/packages/libp2p/src/identify/consts.ts index d99ea12bb8..bd68698dc7 100644 --- a/packages/libp2p/src/identify/consts.ts +++ b/packages/libp2p/src/identify/consts.ts @@ -6,13 +6,18 @@ 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 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 \ No newline at end of file diff --git a/packages/libp2p/src/identify/identify.ts b/packages/libp2p/src/identify/identify.ts index 60ca14307f..fdb1eaea9f 100644 --- a/packages/libp2p/src/identify/identify.ts +++ b/packages/libp2p/src/identify/identify.ts @@ -24,7 +24,10 @@ import { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, MAX_PUSH_INCOMING_STREAMS, - MAX_IDENTIFY_MESSAGE_SIZE + MAX_IDENTIFY_MESSAGE_SIZE, + TIMEOUT, + MAX_PUSH_OUTGOING_STREAMS, + MAX_OBSERVED_ADDRESSES } from './consts.js' import { Identify } from './pb/message.js' import type { IdentifyServiceComponents, IdentifyServiceInit } from './index.js' @@ -42,19 +45,6 @@ const log = logger('libp2p:identify') // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L52 -const defaultValues = { - protocolPrefix: 'ipfs', - agentVersion: AGENT_VERSION, - // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L48 - timeout: 60000, - maxInboundStreams: MAX_INBOUND_STREAMS, - maxOutboundStreams: MAX_OUTBOUND_STREAMS, - maxPushIncomingStreams: MAX_PUSH_INCOMING_STREAMS, - maxPushOutgoingStreams: 1, - maxObservedAddresses: 10, - maxIdentifyMessageSize: 8192 -} - export class DefaultIdentifyService implements Startable { private readonly identifyProtocolStr: string private readonly identifyPushProtocolStr: string @@ -87,20 +77,20 @@ export class DefaultIdentifyService implements Startable { 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.identifyProtocolStr = `/${init.protocolPrefix}/${MULTICODEC_IDENTIFY_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PROTOCOL_VERSION}` + this.identifyPushProtocolStr = `/${init.protocolPrefix}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION}` + this.timeout = init.timeout ?? TIMEOUT + this.maxInboundStreams = init.maxInboundStreams ?? MAX_INBOUND_STREAMS + this.maxOutboundStreams = init.maxOutboundStreams ?? MAX_OUTBOUND_STREAMS + this.maxPushIncomingStreams = init.maxPushIncomingStreams ?? MAX_PUSH_INCOMING_STREAMS + this.maxPushOutgoingStreams = init.maxPushOutgoingStreams ?? MAX_PUSH_OUTGOING_STREAMS + this.maxIdentifyMessageSize = init.maxIdentifyMessageSize ?? MAX_IDENTIFY_MESSAGE_SIZE + this.maxObservedAddresses = init.maxObservedAddresses ?? MAX_OBSERVED_ADDRESSES // Store self host metadata this.host = { - protocolVersion: `${init.protocolPrefix ?? defaultValues.protocolPrefix}/${IDENTIFY_PROTOCOL_VERSION}`, - agentVersion: init.agentVersion ?? defaultValues.agentVersion + protocolVersion: `${init.protocolPrefix}/${IDENTIFY_PROTOCOL_VERSION}`, + agentVersion: init.agentVersion ?? AGENT_VERSION } // When a new connection happens, trigger identify @@ -277,7 +267,7 @@ export class DefaultIdentifyService implements Startable { [], source, (source) => lp.decode(source, { - maxDataLength: this.maxIdentifyMessageSize ?? MAX_IDENTIFY_MESSAGE_SIZE + maxDataLength: this.maxIdentifyMessageSize }), async (source) => first(source) ) @@ -328,7 +318,7 @@ export class DefaultIdentifyService implements Startable { 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) } @@ -415,7 +405,7 @@ export class DefaultIdentifyService implements Startable { // make stream abortable const source = abortableDuplex(stream, AbortSignal.timeout(this.timeout)) const pb = pbStream(source, { - maxDataLength: this.maxIdentifyMessageSize ?? MAX_IDENTIFY_MESSAGE_SIZE + maxDataLength: this.maxIdentifyMessageSize }) const message = await pb.readPB(Identify) diff --git a/packages/libp2p/src/libp2p.ts b/packages/libp2p/src/libp2p.ts index 8d2ddb5077..ca88c5f9db 100644 --- a/packages/libp2p/src/libp2p.ts +++ b/packages/libp2p/src/libp2p.ts @@ -161,7 +161,6 @@ export class Libp2pNode> extends }) // User defined modules - console.log('services', init.services) if (init.services != null) { for (const name of Object.keys(init.services)) { const createService = init.services[name] diff --git a/packages/libp2p/src/ping/config.ts b/packages/libp2p/src/ping/config.ts new file mode 100644 index 0000000000..eae1fd0b66 --- /dev/null +++ b/packages/libp2p/src/ping/config.ts @@ -0,0 +1,12 @@ +import { number, object, string } from "yup" +import { PingService, PingServiceComponents, PingServiceInit, pingService } from "./index.js" +import { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, PROTOCOL_PREFIX, TIMEOUT } from "./constants.js" + +export const validatePingConfig = (opts: PingServiceInit): (components: PingServiceComponents) => PingService => { + return pingService(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) + }).validateSync(opts)) +} From 73f9fc7877aafbdd7061e77514aef1e2f8753328 Mon Sep 17 00:00:00 2001 From: chad Date: Mon, 3 Jul 2023 16:57:15 -0500 Subject: [PATCH 08/20] feat: added default announce filter (#1573) --- packages/libp2p/src/address-manager/utils.ts | 10 ++++++---- packages/libp2p/src/config/config.ts | 9 +-------- packages/libp2p/src/connection-manager/utils.ts | 2 +- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/libp2p/src/address-manager/utils.ts b/packages/libp2p/src/address-manager/utils.ts index bfcdb6211b..8790e8f1bf 100644 --- a/packages/libp2p/src/address-manager/utils.ts +++ b/packages/libp2p/src/address-manager/utils.ts @@ -1,6 +1,7 @@ -import { type ObjectSchema, object, array, string } from 'yup' +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 @@ -18,8 +19,9 @@ export function debounce (func: () => void, wait: number): () => void { export function validateAddressManagerConfig (opts: AddressManagerInit): ObjectSchema> { return object({ - listen: array().of(string()).test('is multiaddr', validateMultiaddr).optional(), - announce: array().of(string()).test('is multiaddr', validateMultiaddr).optional(), - noAnnounce: array().of(string()).test('is multiaddr', validateMultiaddr).optional() + 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/config/config.ts b/packages/libp2p/src/config/config.ts index 9b73606680..c570bb83ed 100644 --- a/packages/libp2p/src/config/config.ts +++ b/packages/libp2p/src/config/config.ts @@ -3,7 +3,6 @@ import { publicAddressesFirst } from '@libp2p/utils/address-sort' import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers' import mergeOptions from 'merge-options' import type { ServiceMap, RecursivePartial } from '@libp2p/interface' -import type { Multiaddr } from '@multiformats/multiaddr' import type { Libp2pInit, ServiceFactoryMap } from '../index.js' import type { AddressManagerInit } from '../address-manager' import { validateAddressManagerConfig } from '../address-manager/utils.js' @@ -13,12 +12,6 @@ import type { ConnectionManagerInit } from '../connection-manager/index.js' import { validateServicesConfig } from './helpers.js' const DefaultConfig: Partial = { - addresses: { - listen: [], - announce: [], - noAnnounce: [], - announceFilter: (multiaddrs: Multiaddr[]) => multiaddrs - }, connectionManager: { resolvers: { dnsaddr: dnsaddrResolver @@ -33,7 +26,7 @@ const DefaultConfig: Partial = { export function validateConfig > (opts: RecursivePartial>): Libp2pInit { const libp2pConfig = object({ addresses: validateAddressManagerConfig(opts?.addresses as AddressManagerInit), - connectionManager: validateConnectionManagerConfig(opts?.connectionManager as ConnectionManagerInit) + connectionManager: validateConnectionManagerConfig(opts?.connectionManager as ConnectionManagerInit), }) //@ts-expect-error diff --git a/packages/libp2p/src/connection-manager/utils.ts b/packages/libp2p/src/connection-manager/utils.ts index d6604ad897..d63ca22c99 100644 --- a/packages/libp2p/src/connection-manager/utils.ts +++ b/packages/libp2p/src/connection-manager/utils.ts @@ -93,6 +93,6 @@ export const validateConnectionManagerConfig = (opts: ConnectionManagerInit): Ob 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?.maxConnections}`).integer().default(INBOUND_CONNECTION_THRESHOLD), - maxIncomingPendingConnections: number().integer().max(opts?.maxConnections ?? MAX_CONNECTIONS, `maxIncomingPendingConnections must be less than the max connections limit: ${opts?.maxConnections}`).default(MAX_INCOMING_PENDING_CONNECTIONS) + maxIncomingPendingConnections: number().integer().max(opts?.maxConnections ?? MAX_CONNECTIONS, `maxIncomingPendingConnections must be less than the max connections limit: ${opts?.maxConnections}`).default(MAX_INCOMING_PENDING_CONNECTIONS), }) } From 893e068d097ecfb5457b64c9de1862990ad4d9cc Mon Sep 17 00:00:00 2001 From: chad Date: Mon, 3 Jul 2023 19:02:58 -0500 Subject: [PATCH 09/20] feat: refactored config to validate before returning service (#1573) --- packages/libp2p/.aegir.js | 2 +- .../libp2p/src/circuit-relay/server/index.ts | 19 +++++++++++++++++ packages/libp2p/src/circuit-relay/utils.ts | 21 +------------------ packages/libp2p/src/config/config.ts | 11 ++++++---- packages/libp2p/src/config/helpers.ts | 19 ----------------- packages/libp2p/src/fetch/config.ts | 12 ----------- packages/libp2p/src/fetch/index.ts | 11 +++++++++- packages/libp2p/src/identify/config.ts | 15 ------------- packages/libp2p/src/identify/index.ts | 18 +++++++++++++++- packages/libp2p/src/ping/config.ts | 12 ----------- packages/libp2p/src/ping/index.ts | 9 ++++++++ .../test/connection-manager/resolver.spec.ts | 4 +++- 12 files changed, 67 insertions(+), 86 deletions(-) delete mode 100644 packages/libp2p/src/fetch/config.ts delete mode 100644 packages/libp2p/src/identify/config.ts delete mode 100644 packages/libp2p/src/ping/config.ts diff --git a/packages/libp2p/.aegir.js b/packages/libp2p/.aegir.js index 9ce14b21b2..064a631041 100644 --- a/packages/libp2p/.aegir.js +++ b/packages/libp2p/.aegir.js @@ -52,7 +52,7 @@ export default { fetch: fetchService(), relay: circuitRelayServer({ reservations: { - maxReservations: Infinity + maxReservations: 100000 } }) } diff --git a/packages/libp2p/src/circuit-relay/server/index.ts b/packages/libp2p/src/circuit-relay/server/index.ts index 8c383b074a..a381a38d6e 100644 --- a/packages/libp2p/src/circuit-relay/server/index.ts +++ b/packages/libp2p/src/circuit-relay/server/index.ts @@ -9,7 +9,11 @@ import pDefer from 'p-defer' import { MAX_CONNECTIONS } from '../../connection-manager/constants.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 } from '../constants.js' @@ -28,6 +32,8 @@ import type { AddressManager } from '@libp2p/interface-internal/address-manager' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar' import type { PeerMap } from '@libp2p/peer-collections' +import { object, number, boolean } from 'yup' +import { DEFAULT_MAX_INBOUND_STREAMS, DEFAULT_MAX_OUTBOUND_STREAMS } from '../../registrar.js' const log = logger('libp2p:circuit-relay:server') @@ -438,6 +444,19 @@ class CircuitRelayServer extends EventEmitter implements Star } export function circuitRelayServer (init: CircuitRelayServerInit = {}): (components: CircuitRelayServerComponents) => CircuitRelayService { + 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) + }).validateSync(init) + return (components) => { return new CircuitRelayServer(components, init) } diff --git a/packages/libp2p/src/circuit-relay/utils.ts b/packages/libp2p/src/circuit-relay/utils.ts index 6d3c89e546..e37b020d71 100644 --- a/packages/libp2p/src/circuit-relay/utils.ts +++ b/packages/libp2p/src/circuit-relay/utils.ts @@ -3,11 +3,7 @@ import { abortableSource } from 'abortable-iterator' import { anySignal } from 'any-signal' import { CID } from 'multiformats/cid' import { sha256 } from 'multiformats/hashes/sha2' -import { object, number, boolean } from 'yup' -import { DEFAULT_MAX_INBOUND_STREAMS, DEFAULT_MAX_OUTBOUND_STREAMS } from '../registrar.js' -import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT, DEFAULT_HOP_TIMEOUT, DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL, DEFAULT_MAX_RESERVATION_STORE_SIZE, DEFAULT_MAX_RESERVATION_TTL } from './constants.js' -import { type CircuitRelayServerInit, circuitRelayServer, type CircuitRelayServerComponents } from './server/index.js' -import type { CircuitRelayService } from './index.js' +import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT } from './constants.js' import type { Limit } from './pb/index.js' import type { Stream } from '@libp2p/interface/connection' import type { Source } from 'it-stream-types' @@ -128,18 +124,3 @@ export function getExpirationMilliseconds (expireTimeSeconds: bigint): number { // downcast to number to use with setTimeout return Number(expireTimeMillis - BigInt(currentTime)) } - -export const validateCircuitRelayServicesConfig = (opts: CircuitRelayServerInit): (components: CircuitRelayServerComponents) => CircuitRelayService => { - return circuitRelayServer(object({ - hopTimeout: number().min(0).integer().default(DEFAULT_HOP_TIMEOUT).optional(), - reservations: object({ - maxReservations: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_STORE_SIZE).optional(), - reservationClearInterval: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL).optional(), - applyDefaultLimit: boolean().default(true).optional(), - reservationTtl: number().integer().min(0).default(DEFAULT_MAX_RESERVATION_TTL).optional(), - defaultDurationLimit: number().integer().min(0).default(DEFAULT_DURATION_LIMIT).max(opts?.reservations?.reservationTtl ?? DEFAULT_MAX_RESERVATION_TTL, `default duration limit must be less than reservation TTL: ${opts?.reservations?.reservationTtl}`).optional() - }), - maxInboundHopStreams: number().integer().min(0).default(DEFAULT_MAX_INBOUND_STREAMS).optional(), - maxOutboundHopStreams: number().integer().min(0).default(DEFAULT_MAX_OUTBOUND_STREAMS).optional() - }).validateSync(opts)) -} diff --git a/packages/libp2p/src/config/config.ts b/packages/libp2p/src/config/config.ts index c570bb83ed..b26027895d 100644 --- a/packages/libp2p/src/config/config.ts +++ b/packages/libp2p/src/config/config.ts @@ -3,13 +3,12 @@ import { publicAddressesFirst } from '@libp2p/utils/address-sort' import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers' import mergeOptions from 'merge-options' import type { ServiceMap, RecursivePartial } from '@libp2p/interface' -import type { Libp2pInit, ServiceFactoryMap } from '../index.js' +import type { Libp2pInit } from '../index.js' import type { AddressManagerInit } from '../address-manager' import { validateAddressManagerConfig } from '../address-manager/utils.js' import { object } from 'yup' import { validateConnectionManagerConfig } from '../connection-manager/utils.js' import type { ConnectionManagerInit } from '../connection-manager/index.js' -import { validateServicesConfig } from './helpers.js' const DefaultConfig: Partial = { connectionManager: { @@ -29,8 +28,12 @@ export function validateConfig > connectionManager: validateConnectionManagerConfig(opts?.connectionManager as ConnectionManagerInit), }) - //@ts-expect-error - opts.services = validateServicesConfig(opts?.services as ServiceFactoryMap) as ServiceFactoryMap + if (opts?.services) { + // @ts-expect-error + 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) diff --git a/packages/libp2p/src/config/helpers.ts b/packages/libp2p/src/config/helpers.ts index 3f3d4f78ec..9cb4106ae8 100644 --- a/packages/libp2p/src/config/helpers.ts +++ b/packages/libp2p/src/config/helpers.ts @@ -1,13 +1,4 @@ import { multiaddr } from '@multiformats/multiaddr' -import { validateCircuitRelayServicesConfig } from '../circuit-relay/utils.js' -import { validateIdentifyConfig } from '../identify/config.js' -import type { CircuitRelayServerInit } from '../circuit-relay/server/index.js' -import type { IdentifyServiceInit } from '../identify/index.js' -import type { ServiceMap } from '@libp2p/interface-libp2p' -import type { PingServiceInit } from '../ping/index.js' -import { validatePingConfig } from '../ping/config.js' -import type { FetchServiceInit } from '../fetch/index.js' -import { validateFetchConfig } from '../fetch/config.js' export const validateMultiaddr = (value: Array | undefined): boolean => { value?.forEach((addr) => { @@ -19,13 +10,3 @@ export const validateMultiaddr = (value: Array | undefined): }) return true } - -export const validateServicesConfig = (opts: ServiceMap): ServiceMap => { - return { - ping: validatePingConfig(opts?.ping as PingServiceInit), - fetch: validateFetchConfig(opts?.fetch as FetchServiceInit), - identify: validateIdentifyConfig(opts?.identify as IdentifyServiceInit), - relay: validateCircuitRelayServicesConfig(opts?.relay as CircuitRelayServerInit), - ...opts, - } -} diff --git a/packages/libp2p/src/fetch/config.ts b/packages/libp2p/src/fetch/config.ts deleted file mode 100644 index c1b182beb9..0000000000 --- a/packages/libp2p/src/fetch/config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { number, object, string } from "yup" -import { fetchService, type FetchService, type FetchServiceComponents, type FetchServiceInit } from "./index.js" -import { TIMEOUT, MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS } from "./constants.js" - -export const validateFetchConfig = (opts: FetchServiceInit): (components: FetchServiceComponents) => FetchService => { - return fetchService(object({ - protocolPrefix: string().default('ipfs'), - timeout: number().integer().default(TIMEOUT), - maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS), - maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS), - }).validateSync(opts)) -} diff --git a/packages/libp2p/src/fetch/index.ts b/packages/libp2p/src/fetch/index.ts index 8084fadc82..4af8465a4b 100644 --- a/packages/libp2p/src/fetch/index.ts +++ b/packages/libp2p/src/fetch/index.ts @@ -8,7 +8,7 @@ import { pipe } from 'it-pipe' import { fromString as uint8arrayFromString } from 'uint8arrays/from-string' import { toString as uint8arrayToString } from 'uint8arrays/to-string' 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' @@ -16,6 +16,7 @@ import type { PeerId } from '@libp2p/interface/peer-id' import type { Startable } from '@libp2p/interface/startable' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar' +import { number, object, string } from 'yup' const log = logger('libp2p:fetch') @@ -309,5 +310,13 @@ class DefaultFetchService implements Startable, FetchService { } export function fetchService (init: FetchServiceInit = {}): (components: FetchServiceComponents) => FetchService { + + object({ + protocolPrefix: string().default('ipfs'), + 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) + return (components) => new DefaultFetchService(components, init) } diff --git a/packages/libp2p/src/identify/config.ts b/packages/libp2p/src/identify/config.ts deleted file mode 100644 index 45720241a8..0000000000 --- a/packages/libp2p/src/identify/config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { number, object, string } from 'yup' -import { AGENT_VERSION, MAX_IDENTIFY_MESSAGE_SIZE, MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, PROTOCOL_PREFIX, TIMEOUT } from './consts.js' -import { identifyService, type IdentifyServiceComponents, type IdentifyServiceInit } from './index.js' -import type { DefaultIdentifyService } from './identify.js' - -export const validateIdentifyConfig = (opts: IdentifyServiceInit): (components: IdentifyServiceComponents) => DefaultIdentifyService => { - return identifyService(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), - maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS), - }).validateSync(opts)) -} diff --git a/packages/libp2p/src/identify/index.ts b/packages/libp2p/src/identify/index.ts index dad6f8804e..dab2efbeec 100644 --- a/packages/libp2p/src/identify/index.ts +++ b/packages/libp2p/src/identify/index.ts @@ -1,6 +1,12 @@ import { + AGENT_VERSION, + MAX_IDENTIFY_MESSAGE_SIZE, + MAX_INBOUND_STREAMS, + MAX_OUTBOUND_STREAMS, MULTICODEC_IDENTIFY, - MULTICODEC_IDENTIFY_PUSH + MULTICODEC_IDENTIFY_PUSH, + PROTOCOL_PREFIX, + TIMEOUT } from './consts.js' import { DefaultIdentifyService } from './identify.js' import { Identify } from './pb/message.js' @@ -11,6 +17,7 @@ import type { PeerStore } from '@libp2p/interface/peer-store' import type { AddressManager } from '@libp2p/interface-internal/address-manager' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { Registrar } from '@libp2p/interface-internal/registrar' +import { number, object, string } from 'yup' export interface IdentifyServiceInit { /** @@ -61,5 +68,14 @@ export const multicodecs = { export const Message = { Identify } export function identifyService (init: IdentifyServiceInit = {}): (components: IdentifyServiceComponents) => DefaultIdentifyService { + 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), + maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS), + }).validateSync(init) + return (components) => new DefaultIdentifyService(components, init) } diff --git a/packages/libp2p/src/ping/config.ts b/packages/libp2p/src/ping/config.ts deleted file mode 100644 index eae1fd0b66..0000000000 --- a/packages/libp2p/src/ping/config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { number, object, string } from "yup" -import { PingService, PingServiceComponents, PingServiceInit, pingService } from "./index.js" -import { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, PROTOCOL_PREFIX, TIMEOUT } from "./constants.js" - -export const validatePingConfig = (opts: PingServiceInit): (components: PingServiceComponents) => PingService => { - return pingService(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) - }).validateSync(opts)) -} diff --git a/packages/libp2p/src/ping/index.ts b/packages/libp2p/src/ping/index.ts index 3903f9f0ae..96bcc5d855 100644 --- a/packages/libp2p/src/ping/index.ts +++ b/packages/libp2p/src/ping/index.ts @@ -16,6 +16,7 @@ import type { Startable } from '@libp2p/interface/startable' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar' import type { Multiaddr } from '@multiformats/multiaddr' +import { number, object, string } from 'yup' const log = logger('libp2p:ping') @@ -136,5 +137,13 @@ class DefaultPingService implements Startable, PingService { } export function pingService (init: PingServiceInit = {}): (components: PingServiceComponents) => PingService { + + 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) + }).validateSync(init) + return (components) => new DefaultPingService(components, init) } diff --git a/packages/libp2p/test/connection-manager/resolver.spec.ts b/packages/libp2p/test/connection-manager/resolver.spec.ts index adccf3976d..5cfa614cee 100644 --- a/packages/libp2p/test/connection-manager/resolver.spec.ts +++ b/packages/libp2p/test/connection-manager/resolver.spec.ts @@ -19,6 +19,7 @@ import { createLibp2pNode, type Libp2pNode } from '../../src/libp2p.js' import type { PeerId } from '@libp2p/interface/peer-id' import type { Transport } from '@libp2p/interface/transport' import type { Multiaddr } from '@multiformats/multiaddr' +import { identifyService } from '../../src/identify/index.js' const relayAddr = multiaddr(process.env.RELAY_MULTIADDR) @@ -93,7 +94,8 @@ describe('dialing (resolvable addresses)', () => { plaintext() ], services: { - relay: circuitRelayServer() + relay: circuitRelayServer(), + identify: identifyService() }, connectionGater: mockConnectionGater() }) From 1e9b0e3e08f23fcaccf7104167e3be2935648b16 Mon Sep 17 00:00:00 2001 From: chad Date: Tue, 4 Jul 2023 12:00:42 -0500 Subject: [PATCH 10/20] chore: linting fixes --- packages/libp2p/src/address-manager/utils.ts | 2 +- .../libp2p/src/circuit-relay/server/index.ts | 4 ++-- packages/libp2p/src/config/config.ts | 17 +++++++++-------- packages/libp2p/src/connection-manager/utils.ts | 2 +- packages/libp2p/src/fetch/index.ts | 5 ++--- packages/libp2p/src/identify/consts.ts | 2 +- packages/libp2p/src/identify/index.ts | 4 ++-- packages/libp2p/src/ping/index.ts | 3 +-- .../test/connection-manager/resolver.spec.ts | 2 +- 9 files changed, 20 insertions(+), 21 deletions(-) diff --git a/packages/libp2p/src/address-manager/utils.ts b/packages/libp2p/src/address-manager/utils.ts index 8790e8f1bf..c0b53ec010 100644 --- a/packages/libp2p/src/address-manager/utils.ts +++ b/packages/libp2p/src/address-manager/utils.ts @@ -22,6 +22,6 @@ export function validateAddressManagerConfig (opts: AddressManagerInit): ObjectS 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), + announceFilter: mixed().default(() => (addrs: Multiaddr[]): Multiaddr[] => addrs) }) } diff --git a/packages/libp2p/src/circuit-relay/server/index.ts b/packages/libp2p/src/circuit-relay/server/index.ts index a381a38d6e..7cb1e4cb4a 100644 --- a/packages/libp2p/src/circuit-relay/server/index.ts +++ b/packages/libp2p/src/circuit-relay/server/index.ts @@ -6,7 +6,9 @@ import { RecordEnvelope } from '@libp2p/peer-record' import { type Multiaddr, multiaddr } from '@multiformats/multiaddr' import { pbStream, type ProtobufStream } from 'it-pb-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, @@ -32,8 +34,6 @@ import type { AddressManager } from '@libp2p/interface-internal/address-manager' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar' import type { PeerMap } from '@libp2p/peer-collections' -import { object, number, boolean } from 'yup' -import { DEFAULT_MAX_INBOUND_STREAMS, DEFAULT_MAX_OUTBOUND_STREAMS } from '../../registrar.js' const log = logger('libp2p:circuit-relay:server') diff --git a/packages/libp2p/src/config/config.ts b/packages/libp2p/src/config/config.ts index b26027895d..fb928825eb 100644 --- a/packages/libp2p/src/config/config.ts +++ b/packages/libp2p/src/config/config.ts @@ -2,13 +2,13 @@ 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 type { ServiceMap, RecursivePartial } from '@libp2p/interface' -import type { Libp2pInit } from '../index.js' -import type { AddressManagerInit } from '../address-manager' -import { validateAddressManagerConfig } from '../address-manager/utils.js' 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: { @@ -25,12 +25,13 @@ const DefaultConfig: Partial = { export function validateConfig > (opts: RecursivePartial>): Libp2pInit { const libp2pConfig = object({ addresses: validateAddressManagerConfig(opts?.addresses as AddressManagerInit), - connectionManager: validateConnectionManagerConfig(opts?.connectionManager as ConnectionManagerInit), + connectionManager: validateConnectionManagerConfig(opts?.connectionManager as ConnectionManagerInit) }) - if (opts?.services) { - // @ts-expect-error - if ((opts.services?.kadDHT || opts.services?.relay || opts.services?.ping) && !opts.services.identify) { + 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') } } diff --git a/packages/libp2p/src/connection-manager/utils.ts b/packages/libp2p/src/connection-manager/utils.ts index d63ca22c99..d6604ad897 100644 --- a/packages/libp2p/src/connection-manager/utils.ts +++ b/packages/libp2p/src/connection-manager/utils.ts @@ -93,6 +93,6 @@ export const validateConnectionManagerConfig = (opts: ConnectionManagerInit): Ob 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?.maxConnections}`).integer().default(INBOUND_CONNECTION_THRESHOLD), - maxIncomingPendingConnections: number().integer().max(opts?.maxConnections ?? MAX_CONNECTIONS, `maxIncomingPendingConnections must be less than the max connections limit: ${opts?.maxConnections}`).default(MAX_INCOMING_PENDING_CONNECTIONS), + maxIncomingPendingConnections: number().integer().max(opts?.maxConnections ?? MAX_CONNECTIONS, `maxIncomingPendingConnections must be less than the max connections limit: ${opts?.maxConnections}`).default(MAX_INCOMING_PENDING_CONNECTIONS) }) } diff --git a/packages/libp2p/src/fetch/index.ts b/packages/libp2p/src/fetch/index.ts index 4af8465a4b..28fb0d42a9 100644 --- a/packages/libp2p/src/fetch/index.ts +++ b/packages/libp2p/src/fetch/index.ts @@ -7,6 +7,7 @@ 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 { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, PROTOCOL_NAME, PROTOCOL_VERSION, TIMEOUT } from './constants.js' import { FetchRequest, FetchResponse } from './pb/proto.js' @@ -16,7 +17,6 @@ import type { PeerId } from '@libp2p/interface/peer-id' import type { Startable } from '@libp2p/interface/startable' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar' -import { number, object, string } from 'yup' const log = logger('libp2p:fetch') @@ -310,12 +310,11 @@ class DefaultFetchService implements Startable, FetchService { } export function fetchService (init: FetchServiceInit = {}): (components: FetchServiceComponents) => FetchService { - object({ protocolPrefix: string().default('ipfs'), timeout: number().integer().default(TIMEOUT), maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS), - maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS), + maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS) }).validateSync(init) return (components) => new DefaultFetchService(components, init) diff --git a/packages/libp2p/src/identify/consts.ts b/packages/libp2p/src/identify/consts.ts index bd68698dc7..0218a2db04 100644 --- a/packages/libp2p/src/identify/consts.ts +++ b/packages/libp2p/src/identify/consts.ts @@ -20,4 +20,4 @@ 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 \ No newline at end of file +export const TIMEOUT = 60000 diff --git a/packages/libp2p/src/identify/index.ts b/packages/libp2p/src/identify/index.ts index dab2efbeec..a292b7a56a 100644 --- a/packages/libp2p/src/identify/index.ts +++ b/packages/libp2p/src/identify/index.ts @@ -1,3 +1,4 @@ +import { number, object, string } from 'yup' import { AGENT_VERSION, MAX_IDENTIFY_MESSAGE_SIZE, @@ -17,7 +18,6 @@ import type { PeerStore } from '@libp2p/interface/peer-store' import type { AddressManager } from '@libp2p/interface-internal/address-manager' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { Registrar } from '@libp2p/interface-internal/registrar' -import { number, object, string } from 'yup' export interface IdentifyServiceInit { /** @@ -74,7 +74,7 @@ export function identifyService (init: IdentifyServiceInit = {}): (components: I timeout: number().integer().default(TIMEOUT), maxIdentifyMessageSize: number().integer().min(0).default(MAX_IDENTIFY_MESSAGE_SIZE), maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS), - maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS), + maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS) }).validateSync(init) return (components) => new DefaultIdentifyService(components, init) diff --git a/packages/libp2p/src/ping/index.ts b/packages/libp2p/src/ping/index.ts index 96bcc5d855..3523a2e902 100644 --- a/packages/libp2p/src/ping/index.ts +++ b/packages/libp2p/src/ping/index.ts @@ -7,6 +7,7 @@ import { anySignal } from 'any-signal' import first from 'it-first' import { pipe } from 'it-pipe' import { equals as uint8ArrayEquals } from 'uint8arrays/equals' +import { 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' @@ -16,7 +17,6 @@ import type { Startable } from '@libp2p/interface/startable' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar' import type { Multiaddr } from '@multiformats/multiaddr' -import { number, object, string } from 'yup' const log = logger('libp2p:ping') @@ -137,7 +137,6 @@ class DefaultPingService implements Startable, PingService { } export function pingService (init: PingServiceInit = {}): (components: PingServiceComponents) => PingService { - object({ protocolPrefix: string().default(PROTOCOL_PREFIX), timeout: number().integer().default(TIMEOUT), diff --git a/packages/libp2p/test/connection-manager/resolver.spec.ts b/packages/libp2p/test/connection-manager/resolver.spec.ts index 5cfa614cee..ef8ed7f905 100644 --- a/packages/libp2p/test/connection-manager/resolver.spec.ts +++ b/packages/libp2p/test/connection-manager/resolver.spec.ts @@ -14,12 +14,12 @@ 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' import type { Transport } from '@libp2p/interface/transport' import type { Multiaddr } from '@multiformats/multiaddr' -import { identifyService } from '../../src/identify/index.js' const relayAddr = multiaddr(process.env.RELAY_MULTIADDR) From c70f7c14a655797edad2167eb5ae0bba67c6c6e1 Mon Sep 17 00:00:00 2001 From: chad Date: Tue, 4 Jul 2023 17:59:36 -0500 Subject: [PATCH 11/20] wip --- packages/libp2p/src/autonat/index.ts | 14 ++++++++++++-- packages/libp2p/src/circuit-relay/server/index.ts | 4 ++-- .../libp2p/src/circuit-relay/transport/index.ts | 7 ++++++- packages/libp2p/src/fetch/index.ts | 4 ++-- packages/libp2p/src/identify/index.ts | 4 ++-- packages/libp2p/src/ping/index.ts | 4 ++-- packages/libp2p/src/upnp-nat/index.ts | 5 ++++- .../libp2p/test/circuit-relay/discovery.node.ts | 2 ++ 8 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/libp2p/src/autonat/index.ts b/packages/libp2p/src/autonat/index.ts index 8d5787156d..afc07c55b8 100644 --- a/packages/libp2p/src/autonat/index.ts +++ b/packages/libp2p/src/autonat/index.ts @@ -11,6 +11,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 { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, @@ -89,7 +90,7 @@ class DefaultAutoNATService implements Startable { constructor (components: AutoNATComponents, init: AutoNATServiceInit) { this.components = components this.started = false - this.protocol = `/${init.protocolPrefix ?? PROTOCOL_PREFIX}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}` + this.protocol = `/${init.protocolPrefix}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}` this.timeout = init.timeout ?? TIMEOUT this.maxInboundStreams = init.maxInboundStreams ?? MAX_INBOUND_STREAMS this.maxOutboundStreams = init.maxOutboundStreams ?? MAX_OUTBOUND_STREAMS @@ -566,7 +567,16 @@ class DefaultAutoNATService implements Startable { } export function autoNATService (init: AutoNATServiceInit = {}): (components: AutoNATComponents) => unknown { + 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) + return (components) => { - return new DefaultAutoNATService(components, init) + return new DefaultAutoNATService(components, validatedConfig) } } diff --git a/packages/libp2p/src/circuit-relay/server/index.ts b/packages/libp2p/src/circuit-relay/server/index.ts index 7cb1e4cb4a..ac94b08c70 100644 --- a/packages/libp2p/src/circuit-relay/server/index.ts +++ b/packages/libp2p/src/circuit-relay/server/index.ts @@ -444,7 +444,7 @@ class CircuitRelayServer extends EventEmitter implements Star } export function circuitRelayServer (init: CircuitRelayServerInit = {}): (components: CircuitRelayServerComponents) => CircuitRelayService { - object({ + 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), @@ -458,6 +458,6 @@ export function circuitRelayServer (init: CircuitRelayServerInit = {}): (compone }).validateSync(init) return (components) => { - return new CircuitRelayServer(components, init) + return new CircuitRelayServer(components, validatedConfig) } } diff --git a/packages/libp2p/src/circuit-relay/transport/index.ts b/packages/libp2p/src/circuit-relay/transport/index.ts index 1df1b53cc4..655d80f98c 100644 --- a/packages/libp2p/src/circuit-relay/transport/index.ts +++ b/packages/libp2p/src/circuit-relay/transport/index.ts @@ -6,6 +6,7 @@ import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn' import * as mafmt from '@multiformats/mafmt' import { multiaddr } from '@multiformats/multiaddr' import { pbStream } from 'it-pb-stream' +import { 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' @@ -351,7 +352,11 @@ class CircuitRelayTransport implements Transport { } export function circuitRelayTransport (init: CircuitRelayTransportInit = {}): (components: CircuitRelayTransportComponents) => Transport { + const validatedConfig = object({ + + }).validate(init) + return (components) => { - return new CircuitRelayTransport(components, init) + return new CircuitRelayTransport(components, validatedConfig) } } diff --git a/packages/libp2p/src/fetch/index.ts b/packages/libp2p/src/fetch/index.ts index 28fb0d42a9..406005b524 100644 --- a/packages/libp2p/src/fetch/index.ts +++ b/packages/libp2p/src/fetch/index.ts @@ -310,12 +310,12 @@ class DefaultFetchService implements Startable, FetchService { } export function fetchService (init: FetchServiceInit = {}): (components: FetchServiceComponents) => FetchService { - object({ + const validatedConfig = object({ protocolPrefix: string().default('ipfs'), 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) - return (components) => new DefaultFetchService(components, init) + return (components) => new DefaultFetchService(components, validatedConfig) } diff --git a/packages/libp2p/src/identify/index.ts b/packages/libp2p/src/identify/index.ts index a292b7a56a..73c951c7e6 100644 --- a/packages/libp2p/src/identify/index.ts +++ b/packages/libp2p/src/identify/index.ts @@ -68,7 +68,7 @@ export const multicodecs = { export const Message = { Identify } export function identifyService (init: IdentifyServiceInit = {}): (components: IdentifyServiceComponents) => DefaultIdentifyService { - object({ + const validatedConfig = object({ protocolPrefix: string().default(PROTOCOL_PREFIX), agentVersion: string().default(AGENT_VERSION), timeout: number().integer().default(TIMEOUT), @@ -77,5 +77,5 @@ export function identifyService (init: IdentifyServiceInit = {}): (components: I maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS) }).validateSync(init) - return (components) => new DefaultIdentifyService(components, init) + return (components) => new DefaultIdentifyService(components, validatedConfig) } diff --git a/packages/libp2p/src/ping/index.ts b/packages/libp2p/src/ping/index.ts index 3523a2e902..b831b0e152 100644 --- a/packages/libp2p/src/ping/index.ts +++ b/packages/libp2p/src/ping/index.ts @@ -137,12 +137,12 @@ class DefaultPingService implements Startable, PingService { } export function pingService (init: PingServiceInit = {}): (components: PingServiceComponents) => PingService { - object({ + 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) }).validateSync(init) - return (components) => new DefaultPingService(components, init) + return (components) => new DefaultPingService(components, validatedConfig) } diff --git a/packages/libp2p/src/upnp-nat/index.ts b/packages/libp2p/src/upnp-nat/index.ts index 8e422f6b1a..168043b4a2 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 { object } from 'yup' import { codes } from '../errors.js' import * as pkg from '../version.js' import type { PeerId } from '@libp2p/interface/peer-id' @@ -206,7 +207,9 @@ class UPnPNAT implements Startable { } export function uPnPNATService (init: UPnPNATInit = {}): (components: UPnPNATComponents) => UPnPNAT { + const validatedConfig = object({}).validate(init) + return (components: UPnPNATComponents) => { - return new UPnPNAT(components, init) + return new UPnPNAT(components, validatedConfig) } } 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 From dd17fcda0558881fdb9439ae27a1d2f642d3d1d1 Mon Sep 17 00:00:00 2001 From: chad Date: Tue, 4 Jul 2023 19:04:54 -0500 Subject: [PATCH 12/20] feat: added validation to unpnp and circuit relay transport --- .../src/circuit-relay/transport/index.ts | 21 ++++++++----------- packages/libp2p/src/upnp-nat/index.ts | 19 ++++++++++++----- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/packages/libp2p/src/circuit-relay/transport/index.ts b/packages/libp2p/src/circuit-relay/transport/index.ts index 655d80f98c..5d874bbe13 100644 --- a/packages/libp2p/src/circuit-relay/transport/index.ts +++ b/packages/libp2p/src/circuit-relay/transport/index.ts @@ -6,7 +6,7 @@ import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn' import * as mafmt from '@multiformats/mafmt' import { multiaddr } from '@multiformats/multiaddr' import { pbStream } from 'it-pb-stream' -import { object } from 'yup' +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' @@ -88,11 +88,6 @@ export interface CircuitRelayTransportInit extends RelayStoreInit { maxOutboundStopStreams?: number } -const defaults = { - maxInboundStopStreams: MAX_CONNECTIONS, - maxOutboundStopStreams: MAX_CONNECTIONS -} - class CircuitRelayTransport implements Transport { private readonly discovery?: RelayDiscovery private readonly registrar: Registrar @@ -103,7 +98,7 @@ 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 started: boolean @@ -115,8 +110,8 @@ 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.maxInboundStopStreams = init.maxInboundStopStreams + this.maxOutboundStopStreams = init.maxOutboundStopStreams if (init.discoverRelays != null && init.discoverRelays > 0) { this.discovery = new RelayDiscovery(components) @@ -351,10 +346,12 @@ class CircuitRelayTransport implements Transport { } } -export function circuitRelayTransport (init: CircuitRelayTransportInit = {}): (components: CircuitRelayTransportComponents) => Transport { +export function circuitRelayTransport (init?: CircuitRelayTransportInit): (components: CircuitRelayTransportComponents) => Transport { const validatedConfig = object({ - - }).validate(init) + discoverRelays: number().min(0).integer().default(0), + maxInboundStopStreams: number().min(0).integer().default(MAX_CONNECTIONS), + maxOutboundStopStreams: number().min(0).integer().default(MAX_CONNECTIONS) + }).validateSync(init) return (components) => { return new CircuitRelayTransport(components, validatedConfig) diff --git a/packages/libp2p/src/upnp-nat/index.ts b/packages/libp2p/src/upnp-nat/index.ts index 168043b4a2..b2f42787dc 100644 --- a/packages/libp2p/src/upnp-nat/index.ts +++ b/packages/libp2p/src/upnp-nat/index.ts @@ -5,7 +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 { object } from 'yup' +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' @@ -71,7 +71,7 @@ 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 @@ -84,7 +84,7 @@ class UPnPNAT implements Startable { 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.keepAlive = init.keepAlive this.gateway = init.gateway if (this.ttl < DEFAULT_TTL) { @@ -206,8 +206,17 @@ class UPnPNAT implements Startable { } } -export function uPnPNATService (init: UPnPNATInit = {}): (components: UPnPNATComponents) => UPnPNAT { - const validatedConfig = object({}).validate(init) +export function uPnPNATService (init?: UPnPNATInit): (components: UPnPNATComponents) => UPnPNAT { + 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().optional(), + ttl: number().integer().default(DEFAULT_TTL), + keepAlive: boolean().default(true), + gateway: string().optional() + }).validateSync(init) return (components: UPnPNATComponents) => { return new UPnPNAT(components, validatedConfig) From f86432a7f040b459a38eff93562b6a57cfe9d9bb Mon Sep 17 00:00:00 2001 From: chad Date: Tue, 4 Jul 2023 20:10:26 -0500 Subject: [PATCH 13/20] test: refactor tests + updated config (#1573) --- packages/libp2p/src/connection-manager/utils.ts | 4 ++-- packages/libp2p/src/fetch/index.ts | 4 ++-- packages/libp2p/test/configuration/protocol-prefix.node.ts | 3 ++- packages/libp2p/test/connection-manager/index.node.ts | 4 +++- packages/libp2p/test/ping/ping.node.ts | 5 +++++ packages/transport-webrtc/.aegir.js | 3 +-- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/libp2p/src/connection-manager/utils.ts b/packages/libp2p/src/connection-manager/utils.ts index d6604ad897..760461beab 100644 --- a/packages/libp2p/src/connection-manager/utils.ts +++ b/packages/libp2p/src/connection-manager/utils.ts @@ -92,7 +92,7 @@ export const validateConnectionManagerConfig = (opts: ConnectionManagerInit): Ob 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?.maxConnections}`).integer().default(INBOUND_CONNECTION_THRESHOLD), - maxIncomingPendingConnections: number().integer().max(opts?.maxConnections ?? MAX_CONNECTIONS, `maxIncomingPendingConnections must be less than the max connections limit: ${opts?.maxConnections}`).default(MAX_INCOMING_PENDING_CONNECTIONS) + 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/fetch/index.ts b/packages/libp2p/src/fetch/index.ts index 406005b524..40c8a542df 100644 --- a/packages/libp2p/src/fetch/index.ts +++ b/packages/libp2p/src/fetch/index.ts @@ -101,7 +101,7 @@ class DefaultFetchService implements Startable, FetchService { constructor (components: FetchServiceComponents, init: FetchServiceInit) { this.started = false this.components = components - this.protocol = `/${init.protocolPrefix ?? 'libp2p'}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}` + this.protocol = `/${init.protocolPrefix}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}` this.lookupFunctions = new Map() // Maps key prefix to value lookup function this.handleMessage = this.handleMessage.bind(this) this.init = init @@ -311,7 +311,7 @@ class DefaultFetchService implements Startable, FetchService { export function fetchService (init: FetchServiceInit = {}): (components: FetchServiceComponents) => FetchService { const validatedConfig = object({ - protocolPrefix: string().default('ipfs'), + 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) 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 2b5e1b30ca..f4d95167a6 100644 --- a/packages/libp2p/test/connection-manager/index.node.ts +++ b/packages/libp2p/test/connection-manager/index.node.ts @@ -236,7 +236,9 @@ describe('libp2p.connections', () => { }, connectionManager: { minConnections, - maxConnections: 1 + maxConnections: 1, + inboundConnectionThreshold: 1, + maxIncomingPendingConnections: 1 } } }) diff --git a/packages/libp2p/test/ping/ping.node.ts b/packages/libp2p/test/ping/ping.node.ts index d5c1d18840..ce34ef35ab 100644 --- a/packages/libp2p/test/ping/ping.node.ts +++ b/packages/libp2p/test/ping/ping.node.ts @@ -9,6 +9,7 @@ import { pingService, type PingService } from '../../src/ping/index.js' import { createBaseOptions } from '../fixtures/base-options.js' import { createNode, populateAddressBooks } from '../fixtures/creators/peer.js' import type { Libp2p } from '@libp2p/interface' +import { identifyService } from '../../src/identify/index.js' describe('ping', () => { let nodes: Array> @@ -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() } }) @@ -104,6 +108,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 f19d27456f..e8c056c9ca 100644 --- a/packages/transport-webrtc/.aegir.js +++ b/packages/transport-webrtc/.aegir.js @@ -1,4 +1,3 @@ - /** @type {import('aegir').PartialOptions} */ export default { build: { @@ -35,7 +34,7 @@ export default { services: { relay: circuitRelayServer({ reservations: { - maxReservations: Infinity + maxReservations: 10000 } }), identify: identifyService() From c3ed8787ad4b3a4201168dd84e351723c94b7474 Mon Sep 17 00:00:00 2001 From: chad Date: Tue, 4 Jul 2023 20:28:45 -0500 Subject: [PATCH 14/20] chore: linting (#1573) --- packages/libp2p/test/ping/ping.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/libp2p/test/ping/ping.node.ts b/packages/libp2p/test/ping/ping.node.ts index ce34ef35ab..7c53da02a2 100644 --- a/packages/libp2p/test/ping/ping.node.ts +++ b/packages/libp2p/test/ping/ping.node.ts @@ -4,12 +4,12 @@ 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' import { createNode, populateAddressBooks } from '../fixtures/creators/peer.js' import type { Libp2p } from '@libp2p/interface' -import { identifyService } from '../../src/identify/index.js' describe('ping', () => { let nodes: Array> From 6bd7bec57a59d42ed3549dcc0efe0434bee8bc1b Mon Sep 17 00:00:00 2001 From: chad Date: Thu, 20 Jul 2023 19:49:21 -0500 Subject: [PATCH 15/20] feat: updated config validation for identify and circuit relay (#1573) --- .../libp2p/src/circuit-relay/constants.ts | 5 +++++ .../src/circuit-relay/transport/index.ts | 22 +++++++++---------- packages/libp2p/src/identify/identify.ts | 2 +- packages/libp2p/src/identify/index.ts | 7 +++--- 4 files changed, 21 insertions(+), 15 deletions(-) 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/transport/index.ts b/packages/libp2p/src/circuit-relay/transport/index.ts index 67b00ea238..403876b1d2 100644 --- a/packages/libp2p/src/circuit-relay/transport/index.ts +++ b/packages/libp2p/src/circuit-relay/transport/index.ts @@ -9,7 +9,7 @@ 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' @@ -92,7 +92,7 @@ export interface CircuitRelayTransportInit extends RelayStoreInit { * must finish the initial protocol negotiation within this timeout in ms * (default: 30000) */ - stopTimeout: number + stopTimeout?: number /** * When creating a reservation it must complete within this number of ms @@ -113,7 +113,7 @@ class CircuitRelayTransport implements Transport { private readonly reservationStore: ReservationStore private readonly maxInboundStopStreams?: number private readonly maxOutboundStopStreams?: number - private readonly stopTimeout: number + private readonly stopTimeout?: number private started: boolean constructor (components: CircuitRelayTransportComponents, init: CircuitRelayTransportInit) { @@ -256,7 +256,7 @@ class CircuitRelayTransport implements Transport { try { const pbstr = pbStream(stream) const hopstr = pbstr.pb(HopMessage) - hopstr.write({ + await hopstr.write({ type: HopMessage.Type.CONNECT, peer: { id: destinationPeer.toBytes(), @@ -315,7 +315,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 @@ -325,7 +325,7 @@ class CircuitRelayTransport implements Transport { if (request?.type === undefined) { log.error('type was missing from circuit v2 stop protocol request from %s', connection.remotePeer) - pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, { + await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, { signal }) await stream.close() @@ -335,7 +335,7 @@ class CircuitRelayTransport implements Transport { // Validate the STOP request has the required input if (request.type !== StopMessage.Type.CONNECT) { log.error('invalid stop connect request via peer %p', connection.remotePeer) - pbstr.write({ type: StopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE }, { + await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE }, { signal }) await stream.close() @@ -344,7 +344,7 @@ class CircuitRelayTransport implements Transport { if (!isValidStop(request)) { log.error('invalid stop connect request via peer %p', connection.remotePeer) - pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, { + await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, { signal }) await stream.close() @@ -355,7 +355,7 @@ class CircuitRelayTransport implements Transport { if ((await this.connectionGater.denyInboundRelayedConnection?.(connection.remotePeer, remotePeerId)) === true) { log.error('connection gater denied inbound relayed connection from %p', connection.remotePeer) - pbstr.write({ type: StopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }, { + await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }, { signal }) await stream.close() @@ -363,7 +363,7 @@ class CircuitRelayTransport implements Transport { } log.trace('sending success response to %p', connection.remotePeer) - pbstr.write({ type: StopMessage.Type.STATUS, status: Status.OK }, { + await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.OK }, { signal }) @@ -386,7 +386,7 @@ export function circuitRelayTransport (init?: CircuitRelayTransportInit): (compo 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(30000) + stopTimeout: number().min(0).integer().default(DEFAULT_STOP_TIMEOUT) }).validateSync(init) return (components) => { diff --git a/packages/libp2p/src/identify/identify.ts b/packages/libp2p/src/identify/identify.ts index 1ee154b65e..adfcb4015c 100644 --- a/packages/libp2p/src/identify/identify.ts +++ b/packages/libp2p/src/identify/identify.ts @@ -86,7 +86,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { agentVersion: init.agentVersion ?? AGENT_VERSION } - if (init.runOnConnectionOpen) { + if (init.runOnConnectionOpen === true) { // When a new connection happens, trigger identify components.events.addEventListener('connection:open', (evt) => { const connection = evt.detail diff --git a/packages/libp2p/src/identify/index.ts b/packages/libp2p/src/identify/index.ts index f62a44b378..fc9f75873f 100644 --- a/packages/libp2p/src/identify/index.ts +++ b/packages/libp2p/src/identify/index.ts @@ -1,4 +1,4 @@ -import { number, object, string } from 'yup' +import { boolean, number, object, string } from 'yup' import { AGENT_VERSION, MAX_IDENTIFY_MESSAGE_SIZE, @@ -93,8 +93,9 @@ export function identifyService (init: IdentifyServiceInit = {}): (components: I timeout: number().integer().default(TIMEOUT), maxIdentifyMessageSize: number().integer().min(0).default(MAX_IDENTIFY_MESSAGE_SIZE), maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS), - maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS) + maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS), + runOnConnectionOpen: boolean().default(true) }).validateSync(init) return (components) => new DefaultIdentifyService(components, validatedConfig) -} \ No newline at end of file +} From 8e23b5910a004e31e96649a879f4269c24a8cc77 Mon Sep 17 00:00:00 2001 From: chad Date: Mon, 31 Jul 2023 12:18:09 -0500 Subject: [PATCH 16/20] chore: linting --- packages/libp2p/src/identify/identify.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/libp2p/src/identify/identify.ts b/packages/libp2p/src/identify/identify.ts index cda614b4c4..75a597d89f 100644 --- a/packages/libp2p/src/identify/identify.ts +++ b/packages/libp2p/src/identify/identify.ts @@ -83,7 +83,6 @@ export class DefaultIdentifyService implements Startable, IdentifyService { this.maxObservedAddresses = init.maxObservedAddresses ?? MAX_OBSERVED_ADDRESSES this.runOnTransientConnection = init.runOnTransientConnection ?? RUN_ON_TRANSIENT_CONNECTION - // Store self host metadata this.host = { protocolVersion: `${init.protocolPrefix}/${IDENTIFY_PROTOCOL_VERSION}`, From 0d4c8e4f7364760b0e7779f65537c0e8cce1d849 Mon Sep 17 00:00:00 2001 From: chad Date: Sun, 20 Aug 2023 17:03:06 -0500 Subject: [PATCH 17/20] fix: added transient connection default to identify service (#1573) --- packages/libp2p/src/identify/consts.ts | 3 ++- packages/libp2p/src/identify/identify.ts | 7 +++---- packages/libp2p/src/identify/index.ts | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/libp2p/src/identify/consts.ts b/packages/libp2p/src/identify/consts.ts index 333a68526a..ae3356018e 100644 --- a/packages/libp2p/src/identify/consts.ts +++ b/packages/libp2p/src/identify/consts.ts @@ -13,7 +13,8 @@ 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 = false +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' diff --git a/packages/libp2p/src/identify/identify.ts b/packages/libp2p/src/identify/identify.ts index 75a597d89f..898f2edfa5 100644 --- a/packages/libp2p/src/identify/identify.ts +++ b/packages/libp2p/src/identify/identify.ts @@ -22,8 +22,7 @@ import { MAX_IDENTIFY_MESSAGE_SIZE, TIMEOUT, MAX_PUSH_OUTGOING_STREAMS, - MAX_OBSERVED_ADDRESSES, - RUN_ON_TRANSIENT_CONNECTION + MAX_OBSERVED_ADDRESSES } from './consts.js' import { Identify } from './pb/message.js' import type { IdentifyService, IdentifyServiceComponents, IdentifyServiceInit } from './index.js' @@ -61,7 +60,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { private readonly maxIdentifyMessageSize: number private readonly maxObservedAddresses: number private readonly events: EventEmitter - private readonly runOnTransientConnection: boolean + private readonly runOnTransientConnection?: boolean constructor (components: IdentifyServiceComponents, init: IdentifyServiceInit) { this.started = false @@ -81,7 +80,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { this.maxPushOutgoingStreams = init.maxPushOutgoingStreams ?? MAX_PUSH_OUTGOING_STREAMS this.maxIdentifyMessageSize = init.maxIdentifyMessageSize ?? MAX_IDENTIFY_MESSAGE_SIZE this.maxObservedAddresses = init.maxObservedAddresses ?? MAX_OBSERVED_ADDRESSES - this.runOnTransientConnection = init.runOnTransientConnection ?? RUN_ON_TRANSIENT_CONNECTION + this.runOnTransientConnection = init.runOnTransientConnection // Store self host metadata this.host = { diff --git a/packages/libp2p/src/identify/index.ts b/packages/libp2p/src/identify/index.ts index 25fba418d0..4bfd0fbc17 100644 --- a/packages/libp2p/src/identify/index.ts +++ b/packages/libp2p/src/identify/index.ts @@ -7,6 +7,8 @@ import { MULTICODEC_IDENTIFY, MULTICODEC_IDENTIFY_PUSH, PROTOCOL_PREFIX, + RUN_ON_CONNECTION_OPEN, + RUN_ON_TRANSIENT_CONNECTION, TIMEOUT } from './consts.js' import { DefaultIdentifyService } from './identify.js' @@ -99,7 +101,8 @@ export function identifyService (init: IdentifyServiceInit = {}): (components: I maxIdentifyMessageSize: number().integer().min(0).default(MAX_IDENTIFY_MESSAGE_SIZE), maxInboundStreams: number().integer().min(0).default(MAX_INBOUND_STREAMS), maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS), - runOnConnectionOpen: boolean().default(true) + runOnConnectionOpen: boolean().default(RUN_ON_CONNECTION_OPEN), + runOnTransientConnection: boolean().default(RUN_ON_TRANSIENT_CONNECTION) }).validateSync(init) return (components) => new DefaultIdentifyService(components, validatedConfig) From a89818ab9dd88d9c9aeca171016698036261b41d Mon Sep 17 00:00:00 2001 From: chad Date: Sun, 20 Aug 2023 21:01:38 -0500 Subject: [PATCH 18/20] refactor: Refactored config to be done inside services (#1573) --- packages/libp2p/src/autonat/index.ts | 32 ++++++------ .../libp2p/src/circuit-relay/server/index.ts | 41 +++++++-------- .../circuit-relay/server/reservation-store.ts | 22 +++++--- .../src/circuit-relay/transport/index.ts | 26 +++++----- packages/libp2p/src/fetch/index.ts | 37 +++++++------- packages/libp2p/src/identify/identify.ts | 50 +++++++++++++------ packages/libp2p/src/identify/index.ts | 27 +--------- packages/libp2p/src/ping/index.ts | 30 +++++------ packages/libp2p/src/upnp-nat/index.ts | 40 +++++++-------- 9 files changed, 157 insertions(+), 148 deletions(-) diff --git a/packages/libp2p/src/autonat/index.ts b/packages/libp2p/src/autonat/index.ts index d63e586a18..edd0ab727f 100644 --- a/packages/libp2p/src/autonat/index.ts +++ b/packages/libp2p/src/autonat/index.ts @@ -109,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_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) } @@ -588,16 +597,7 @@ class DefaultAutoNATService implements Startable { } export function autoNATService (init: AutoNATServiceInit = {}): (components: AutoNATComponents) => unknown { - 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) - return (components) => { - return new DefaultAutoNATService(components, validatedConfig) + return new DefaultAutoNATService(components, init) } } diff --git a/packages/libp2p/src/circuit-relay/server/index.ts b/packages/libp2p/src/circuit-relay/server/index.ts index cf878ae040..5514647207 100644 --- a/packages/libp2p/src/circuit-relay/server/index.ts +++ b/packages/libp2p/src/circuit-relay/server/index.ts @@ -100,10 +100,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 @@ -126,6 +122,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 @@ -133,11 +143,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 @@ -446,20 +456,7 @@ class CircuitRelayServer extends EventEmitter implements Star } export function circuitRelayServer (init: CircuitRelayServerInit = {}): (components: CircuitRelayServerComponents) => CircuitRelayService { - 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) - }).validateSync(init) - return (components) => { - return new CircuitRelayServer(components, validatedConfig) + return new CircuitRelayServer(components, init) } } 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 60a6137181..6a21027489 100644 --- a/packages/libp2p/src/circuit-relay/transport/index.ts +++ b/packages/libp2p/src/circuit-relay/transport/index.ts @@ -117,6 +117,13 @@ class CircuitRelayTransport implements Transport { 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 @@ -124,11 +131,11 @@ class CircuitRelayTransport implements Transport { this.upgrader = components.upgrader this.addressManager = components.addressManager this.connectionGater = components.connectionGater - this.maxInboundStopStreams = init.maxInboundStopStreams - this.maxOutboundStopStreams = init.maxOutboundStopStreams - this.stopTimeout = init.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') @@ -384,15 +391,8 @@ class CircuitRelayTransport implements Transport { } } -export function circuitRelayTransport (init?: CircuitRelayTransportInit): (components: CircuitRelayTransportComponents) => Transport { - 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) - +export function circuitRelayTransport (init: CircuitRelayTransportInit = {}): (components: CircuitRelayTransportComponents) => Transport { return (components) => { - return new CircuitRelayTransport(components, validatedConfig) + return new CircuitRelayTransport(components, init) } } diff --git a/packages/libp2p/src/fetch/index.ts b/packages/libp2p/src/fetch/index.ts index c1455361e0..38776a99fd 100644 --- a/packages/libp2p/src/fetch/index.ts +++ b/packages/libp2p/src/fetch/index.ts @@ -20,8 +20,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 @@ -96,15 +94,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}/${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 { @@ -117,8 +127,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 } @@ -144,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 @@ -310,12 +320,5 @@ class DefaultFetchService implements Startable, FetchService { } export function fetchService (init: FetchServiceInit = {}): (components: FetchServiceComponents) => FetchService { - 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) - - return (components) => new DefaultFetchService(components, validatedConfig) + return (components) => new DefaultFetchService(components, init) } diff --git a/packages/libp2p/src/identify/identify.ts b/packages/libp2p/src/identify/identify.ts index 898f2edfa5..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, @@ -18,9 +19,12 @@ import { MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION, MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, - MAX_PUSH_INCOMING_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' @@ -60,9 +64,24 @@ export class DefaultIdentifyService implements Startable, IdentifyService { private readonly maxIdentifyMessageSize: number private readonly maxObservedAddresses: number private readonly events: EventEmitter - private readonly runOnTransientConnection?: boolean + 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 @@ -71,24 +90,25 @@ export class DefaultIdentifyService implements Startable, IdentifyService { this.connectionManager = components.connectionManager this.events = components.events - this.identifyProtocolStr = `/${init.protocolPrefix}/${MULTICODEC_IDENTIFY_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PROTOCOL_VERSION}` - this.identifyPushProtocolStr = `/${init.protocolPrefix}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION}` - this.timeout = init.timeout ?? TIMEOUT - this.maxInboundStreams = init.maxInboundStreams ?? MAX_INBOUND_STREAMS - this.maxOutboundStreams = init.maxOutboundStreams ?? MAX_OUTBOUND_STREAMS - this.maxPushIncomingStreams = init.maxPushIncomingStreams ?? MAX_PUSH_INCOMING_STREAMS - this.maxPushOutgoingStreams = init.maxPushOutgoingStreams ?? MAX_PUSH_OUTGOING_STREAMS - this.maxIdentifyMessageSize = init.maxIdentifyMessageSize ?? MAX_IDENTIFY_MESSAGE_SIZE - this.maxObservedAddresses = init.maxObservedAddresses ?? MAX_OBSERVED_ADDRESSES - this.runOnTransientConnection = init.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}/${IDENTIFY_PROTOCOL_VERSION}`, - agentVersion: init.agentVersion ?? AGENT_VERSION + protocolVersion: `${validatedConfig.protocolPrefix}/${IDENTIFY_PROTOCOL_VERSION}`, + agentVersion: validatedConfig.agentVersion } - if (init.runOnConnectionOpen === true) { + if (this.runOnConnectionOpen) { // When a new connection happens, trigger identify components.events.addEventListener('connection:open', (evt) => { const connection = evt.detail diff --git a/packages/libp2p/src/identify/index.ts b/packages/libp2p/src/identify/index.ts index 4bfd0fbc17..cd8e6a7ab4 100644 --- a/packages/libp2p/src/identify/index.ts +++ b/packages/libp2p/src/identify/index.ts @@ -1,16 +1,4 @@ -import { boolean, number, object, string } from 'yup' -import { - AGENT_VERSION, - MAX_IDENTIFY_MESSAGE_SIZE, - MAX_INBOUND_STREAMS, - MAX_OUTBOUND_STREAMS, - MULTICODEC_IDENTIFY, - MULTICODEC_IDENTIFY_PUSH, - PROTOCOL_PREFIX, - RUN_ON_CONNECTION_OPEN, - RUN_ON_TRANSIENT_CONNECTION, - TIMEOUT -} 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' @@ -94,16 +82,5 @@ export interface IdentifyService { } export function identifyService (init: IdentifyServiceInit = {}): (components: IdentifyServiceComponents) => IdentifyService { - 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), - maxOutboundStreams: number().integer().min(0).default(MAX_OUTBOUND_STREAMS), - runOnConnectionOpen: boolean().default(RUN_ON_CONNECTION_OPEN), - runOnTransientConnection: boolean().default(RUN_ON_TRANSIENT_CONNECTION) - }).validateSync(init) - - return (components) => new DefaultIdentifyService(components, validatedConfig) + return (components) => new DefaultIdentifyService(components, init) } diff --git a/packages/libp2p/src/ping/index.ts b/packages/libp2p/src/ping/index.ts index 83a7761951..e7ece74d26 100644 --- a/packages/libp2p/src/ping/index.ts +++ b/packages/libp2p/src/ping/index.ts @@ -5,7 +5,7 @@ import { abortableDuplex } from 'abortable-iterator' import first from 'it-first' import { pipe } from 'it-pipe' import { equals as uint8ArrayEquals } from 'uint8arrays/equals' -import { number, object, string } from 'yup' +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' @@ -51,11 +51,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 { @@ -155,12 +164,5 @@ class DefaultPingService implements Startable, PingService { } export function pingService (init: PingServiceInit = {}): (components: PingServiceComponents) => PingService { - 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) - }).validateSync(init) - - return (components) => new DefaultPingService(components, validatedConfig) + return (components) => new DefaultPingService(components, init) } diff --git a/packages/libp2p/src/upnp-nat/index.ts b/packages/libp2p/src/upnp-nat/index.ts index b2f42787dc..6969263b66 100644 --- a/packages/libp2p/src/upnp-nat/index.ts +++ b/packages/libp2p/src/upnp-nat/index.ts @@ -78,14 +78,25 @@ class UPnPNAT implements Startable { 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 - 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) @@ -206,19 +217,8 @@ class UPnPNAT implements Startable { } } -export function uPnPNATService (init?: UPnPNATInit): (components: UPnPNATComponents) => UPnPNAT { - 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().optional(), - ttl: number().integer().default(DEFAULT_TTL), - keepAlive: boolean().default(true), - gateway: string().optional() - }).validateSync(init) - +export function uPnPNATService (init: UPnPNATInit = {}): (components: UPnPNATComponents) => UPnPNAT { return (components: UPnPNATComponents) => { - return new UPnPNAT(components, validatedConfig) + return new UPnPNAT(components, init) } } From 6638573c30df3f14d455c206f12b3c1456fed0a8 Mon Sep 17 00:00:00 2001 From: Chad Nehemiah Date: Thu, 5 Oct 2023 14:32:35 -0500 Subject: [PATCH 19/20] Update packages/libp2p/src/circuit-relay/server/index.ts Co-authored-by: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> --- packages/libp2p/src/circuit-relay/server/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/libp2p/src/circuit-relay/server/index.ts b/packages/libp2p/src/circuit-relay/server/index.ts index 5514647207..f64239bdae 100644 --- a/packages/libp2p/src/circuit-relay/server/index.ts +++ b/packages/libp2p/src/circuit-relay/server/index.ts @@ -16,8 +16,9 @@ import { 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 + RELAY_SOURCE_TAG, + RELAY_V2_HOP_CODEC, + RELAY_V2_STOP_CODEC } from '../constants.js' import { HopMessage, type Reservation, Status, StopMessage } from '../pb/index.js' import { createLimitedRelay } from '../utils.js' From d45851d01f85b80dea647e0aaefa8371b753ec43 Mon Sep 17 00:00:00 2001 From: chad Date: Thu, 5 Oct 2023 15:48:14 -0500 Subject: [PATCH 20/20] feat: added validation to dcutr --- .../libp2p/src/circuit-relay/server/index.ts | 4 +-- packages/libp2p/src/dcutr/dcutr.ts | 27 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/libp2p/src/circuit-relay/server/index.ts b/packages/libp2p/src/circuit-relay/server/index.ts index f64239bdae..758db09e4d 100644 --- a/packages/libp2p/src/circuit-relay/server/index.ts +++ b/packages/libp2p/src/circuit-relay/server/index.ts @@ -16,8 +16,8 @@ import { DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL, DEFAULT_MAX_RESERVATION_STORE_SIZE, DEFAULT_MAX_RESERVATION_TTL, - RELAY_SOURCE_TAG, - RELAY_V2_HOP_CODEC, + RELAY_SOURCE_TAG, + RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js' import { HopMessage, type Reservation, Status, StopMessage } from '../pb/index.js' 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 {