From 893e068d097ecfb5457b64c9de1862990ad4d9cc Mon Sep 17 00:00:00 2001 From: chad Date: Mon, 3 Jul 2023 19:02:58 -0500 Subject: [PATCH] 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() })