UNPKG

ipfs-core

Version:

JavaScript implementation of the IPFS specification

248 lines (219 loc) 7.73 kB
import get from 'dlv' import mergeOpts from 'merge-options' import errCode from 'err-code' import { routers } from 'ipfs-core-config/libp2p-pubsub-routers' import { delegatedPeerRouting } from '@libp2p/delegated-peer-routing' import { delegatedContentRouting } from '@libp2p/delegated-content-routing' import { create as ipfsHttpClient } from 'ipfs-http-client' import { multiaddr } from '@multiformats/multiaddr' import { ipfsCore as pkgversion } from '../version.js' import { libp2pConfig as getEnvLibp2pOptions } from 'ipfs-core-config/libp2p' import { createLibp2p as createNode } from 'libp2p' import { kadDHT } from '@libp2p/kad-dht' import { bootstrap } from '@libp2p/bootstrap' import { ipnsValidator } from 'ipns/validator' import { ipnsSelector } from 'ipns/selector' import { webSockets } from '@libp2p/websockets' import { mplex } from '@libp2p/mplex' import { noise } from '@chainsafe/libp2p-noise' const mergeOptions = mergeOpts.bind({ ignoreUndefined: true, concatArrays: true }) /** * @typedef {object} DekOptions * @property {string} hash * @property {string} salt * @property {number} iterationCount * @property {number} keyLength * * @typedef {object} KeychainConfig * @property {string} [pass] * @property {DekOptions} [dek] * * @typedef {import('ipfs-repo').IPFSRepo} Repo * @typedef {import('@libp2p/interface-peer-id').PeerId} PeerId * @typedef {import('../types').Options} IPFSOptions * @typedef {import('libp2p').Libp2p} LibP2P * @typedef {import('libp2p').Libp2pOptions} Libp2pOptions * @typedef {import('ipfs-core-types/src/config').Config} IPFSConfig * @typedef {import('@multiformats/multiaddr').Multiaddr} Multiaddr */ /** * @param {object} config * @param {Repo} config.repo * @param {IPFSOptions|undefined} config.options * @param {PeerId} config.peerId * @param {Multiaddr[]|undefined} config.multiaddrs * @param {KeychainConfig|undefined} config.keychainConfig * @param {Partial<IPFSConfig>|undefined} config.config */ export function createLibp2p ({ options = {}, peerId, multiaddrs = [], repo, keychainConfig = {}, config = {} }) { const { datastore } = repo const libp2pOptions = getLibp2pOptions({ options, config, datastore, keychainConfig, peerId, multiaddrs }) if (typeof options.libp2p === 'function') { return options.libp2p({ libp2pOptions, options, config, datastore, peerId }) } // do not start by default libp2pOptions.start = false return createNode(libp2pOptions) } /** * @param {object} input * @param {IPFSOptions} input.options * @param {Partial<IPFSConfig>} input.config * @param {Repo['datastore']} input.datastore * @param {KeychainConfig} input.keychainConfig * @param {PeerId} input.peerId * @param {Multiaddr[]} input.multiaddrs * @returns {Libp2pOptions} */ function getLibp2pOptions ({ options, config, datastore, keychainConfig, peerId, multiaddrs }) { const getPubsubRouter = () => { const router = get(config, 'Pubsub.Router') || 'gossipsub' const availableRouters = routers() if (!availableRouters[router]) { throw errCode(new Error(`Router unavailable. Configure libp2p.modules.pubsub to use the ${router} router.`), 'ERR_NOT_SUPPORTED') } return availableRouters[router] } /** @type {Libp2pOptions} */ const libp2pDefaults = { datastore, peerId: peerId } /** @type {Libp2pOptions} */ const libp2pOptions = { addresses: { listen: multiaddrs.map(ma => ma.toString()), announce: get(options, 'addresses.announce', get(config, 'Addresses.Announce', [])), noAnnounce: get(options, 'addresses.noAnnounce', get(config, 'Addresses.NoAnnounce', [])) }, connectionManager: get(options, 'connectionManager', { maxConnections: get(options, 'config.Swarm.ConnMgr.HighWater', get(config, 'Swarm.ConnMgr.HighWater')), minConnections: get(options, 'config.Swarm.ConnMgr.LowWater', get(config, 'Swarm.ConnMgr.LowWater')) }), keychain: keychainConfig, identify: { host: { agentVersion: `js-ipfs/${pkgversion}` } }, contentRouters: [], peerRouters: [], peerDiscovery: [], transports: [], streamMuxers: [ mplex({ maxInboundStreams: 256, maxOutboundStreams: 1024 }) ], connectionEncryption: [ noise() ], relay: { enabled: get(options, 'relay.enabled', get(config, 'relay.enabled', true)), hop: { enabled: get(options, 'relay.hop.enabled', get(config, 'relay.hop.enabled', false)), active: get(options, 'relay.hop.active', get(config, 'relay.hop.active', false)) } }, nat: { enabled: !get(config, 'Swarm.DisableNatPortMap', false) } } if (get(options, 'config.Pubsub.Enabled', get(config, 'Pubsub.Enabled', true))) { libp2pOptions.pubsub = getPubsubRouter() } if (get(config, 'Routing.Type', 'dhtclient') !== 'none') { libp2pOptions.dht = kadDHT({ clientMode: get(config, 'Routing.Type', 'dht') !== 'dhtserver', kBucketSize: get(options, 'dht.kBucketSize', 20), validators: { ipns: ipnsValidator }, selectors: { ipns: ipnsSelector } }) } const boostrapNodes = get(options, 'config.Bootstrap', get(config, 'Bootstrap', [])) if (boostrapNodes.length > 0) { libp2pOptions.peerDiscovery?.push( bootstrap({ list: boostrapNodes }) ) } /** @type {import('libp2p').Libp2pOptions | undefined} */ let constructorOptions = get(options, 'libp2p', undefined) if (typeof constructorOptions === 'function') { constructorOptions = undefined } // Merge defaults with Node.js/browser/other environments options and configuration /** @type {Libp2pOptions} */ const libp2pFinalConfig = mergeOptions( libp2pDefaults, getEnvLibp2pOptions(), libp2pOptions, constructorOptions ) // Set up Delegate Routing based on the presence of Delegates in the config const delegateHosts = get(options, 'config.Addresses.Delegates', get(config, 'Addresses.Delegates', []) ) if (delegateHosts.length > 0) { // Pick a random delegate host const delegateString = delegateHosts[Math.floor(Math.random() * delegateHosts.length)] const delegateAddr = multiaddr(delegateString).toOptions() const delegateApiOptions = { host: delegateAddr.host, // port is a string atm, so we need to convert for the check // @ts-expect-error - parseInt(input:string) => number protocol: parseInt(delegateAddr.port) === 443 ? 'https' : 'http', port: delegateAddr.port } const delegateHttpClient = ipfsHttpClient(delegateApiOptions) libp2pFinalConfig.contentRouters?.push(delegatedContentRouting(delegateHttpClient)) libp2pFinalConfig.peerRouters?.push(delegatedPeerRouting(delegateHttpClient)) } // TODO: fixme if (!get(options, 'config.Discovery.MDNS.Enabled', get(config, 'Discovery.MDNS.Enabled', true))) { libp2pFinalConfig.peerDiscovery = libp2pFinalConfig.peerDiscovery?.filter(d => { try { if (typeof d === 'function') { // @ts-expect-error not components return d({})[Symbol.toStringTag] !== '@libp2p/mdns' } } catch {} return true }) } if (libp2pFinalConfig.transports == null) { libp2pFinalConfig.transports = [] } // add WebSocket transport if not overridden by user config if (libp2pFinalConfig.transports.find(t => { try { if (typeof t === 'function') { return t({})[Symbol.toStringTag] === '@libp2p/websockets' } } catch {} return false }) == null) { libp2pFinalConfig.transports.push(webSockets()) } return libp2pFinalConfig }