Skip to content

Commit

Permalink
feat: add support for arbitrary service modules
Browse files Browse the repository at this point in the history
Updates the libp2p init args to accept an object of service
factory functions that can use internal libp2p components.

The returned libp2p object has a `.services` key that corresponds
to the service factory keys.
  • Loading branch information
saul-jb authored and achingbrain committed Apr 27, 2023
1 parent ba47c95 commit f65b83b
Show file tree
Hide file tree
Showing 74 changed files with 2,088 additions and 1,990 deletions.
15 changes: 12 additions & 3 deletions .aegir.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export default {
const { plaintext } = await import('./dist/src/insecure/index.js')
const { default: Peers } = await import('./dist/test/fixtures/peers.js')
const { circuitRelayServer, circuitRelayTransport } = await import('./dist/src/circuit-relay/index.js')
const { identifyService } = await import('./dist/src/identify/index.js')
const { pingService } = await import('./dist/src/ping/index.js')
const { fetchService } = await import('./dist/src/fetch/index.js')

// Use the last peer
const peerId = await createFromJSON(Peers[Peers.length - 1])
Expand All @@ -39,9 +42,15 @@ export default {
noise(),
plaintext()
],
relay: circuitRelayServer(),
nat: {
enabled: false
services: {
identify: identifyService(),
ping: pingService(),
fetch: fetchService(),
relay: circuitRelayServer({
reservations: {
maxReservations: Infinity
}
})
}
})
// Add the echo protocol
Expand Down
100 changes: 60 additions & 40 deletions doc/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
- [Configuring Metrics](#configuring-metrics)
- [Configuring PeerStore](#configuring-peerstore)
- [Customizing Transports](#customizing-transports)
- [Configuring the NAT Manager](#configuring-the-nat-manager)
- [Configuring UPnP NAT Traversal](#configuring-upnp-nat-traversal)
- [Browser support](#browser-support)
- [UPnP and NAT-PMP](#upnp-and-nat-pmp)
- [Configuring protocol name](#configuring-protocol-name)
Expand Down Expand Up @@ -142,8 +142,9 @@ Some available content routing modules are:

- [@libp2p/kad-dht](https://github.com/libp2p/js-libp2p-kad-dht)
- [@libp2p/delegated-content-routing](https://github.com/libp2p/js-libp2p-delegated-content-routing)
- [@libp2p/ipni-content-routing](https://github.com/libp2p/js-ipni-content-routing)

If none of the available content routing protocols fulfills your needs, you can create a libp2p compatible one. A libp2p content routing protocol just needs to be compliant with the [Content Routing Interface](https://github.com/libp2p/js-interfaces/tree/master/src/content-routing). **(WIP: This module is not yet implemented)**
If none of the available content routing protocols fulfil your needs, you can create a libp2p compatible one. A libp2p content routing protocol just needs to be compliant with the [Content Routing Interface](https://github.com/libp2p/js-interfaces/tree/master/src/content-routing).

If you want to know more about libp2p content routing, you should read the following content:

Expand Down Expand Up @@ -205,8 +206,9 @@ const modules = {
contentRouting: [],
peerRouting: [],
peerDiscovery: [],
dht: dhtImplementation,
pubsub: pubsubImplementation
services: {
serviceKey: serviceImplementation
}
}
```

Expand Down Expand Up @@ -252,8 +254,10 @@ const node = await createLibp2p({
streamMuxers: [mplex()],
connectionEncryption: [noise()],
peerDiscovery: [MulticastDNS],
dht: kadDHT(),
pubsub: gossipsub()
services: {
dht: kadDHT(),
pubsub: gossipsub()
}
})
```

Expand Down Expand Up @@ -335,10 +339,12 @@ const node = await createLibp2p({
connectionEncryption: [
noise()
],
pubsub: gossipsub({
emitSelf: false, // whether the node should emit to self on publish
globalSignaturePolicy: SignaturePolicy.StrictSign // message signing policy
})
services: {
pubsub: gossipsub({
emitSelf: false, // whether the node should emit to self on publish
globalSignaturePolicy: SignaturePolicy.StrictSign // message signing policy
})
}
}
})
```
Expand All @@ -362,10 +368,12 @@ const node = await createLibp2p({
connectionEncryption: [
noise()
],
dht: kadDHT({
kBucketSize: 20,
clientMode: false // Whether to run the WAN DHT in client or server mode (default: client mode)
})
services: {
dht: kadDHT({
kBucketSize: 20,
clientMode: false // Whether to run the WAN DHT in client or server mode (default: client mode)
})
}
})
```

Expand Down Expand Up @@ -435,21 +443,6 @@ const node = await createLibp2p({
],
streamMuxers: [mplex()],
connectionEncryption: [noise()],
relay: circuitRelayServer({ // makes the node function as a relay server
hopTimeout: 30 * 1000, // incoming relay requests must be resolved within this time limit
advertise: { // if set, use content routing to broadcast availability of this relay
bootDelay: 30 * 1000 // how long to wait after startup before broadcast
},
reservations: {
maxReservations: 15 // how many peers are allowed to reserve relay slots on this server
reservationClearInterval: 300 * 1000 // how often to reclaim stale reservations
applyDefaultLimit: true // whether to apply default data/duration limits to each relayed connection
defaultDurationLimit: 2 * 60 * 1000 // the default maximum amount of time a relayed connection can be open for
defaultDataLimit: BigInt(2 << 7) // the default maximum number of bytes that can be transferred over a relayed connection
maxInboundHopStreams: 32 // how many inbound HOP streams are allow simultaneously
maxOutboundHopStreams: 64 // how many outbound HOP streams are allow simultaneously
}
}),
connectionGater: {
// used by the server - return true to deny a reservation to the remote peer
denyInboundRelayReservation: (source: PeerId) => Promise<boolean>
Expand All @@ -459,6 +452,23 @@ const node = await createLibp2p({

// used by the client - return true to deny a relay connection from the remote relay and peer
denyInboundRelayedConnection: (relay: PeerId, remotePeer: PeerId) => Promise<boolean>
},
services: {
relay: circuitRelayServer({ // makes the node function as a relay server
hopTimeout: 30 * 1000, // incoming relay requests must be resolved within this time limit
advertise: { // if set, use content routing to broadcast availability of this relay
bootDelay: 30 * 1000 // how long to wait after startup before broadcast
},
reservations: {
maxReservations: 15 // how many peers are allowed to reserve relay slots on this server
reservationClearInterval: 300 * 1000 // how often to reclaim stale reservations
applyDefaultLimit: true // whether to apply default data/duration limits to each relayed connection
defaultDurationLimit: 2 * 60 * 1000 // the default maximum amount of time a relayed connection can be open for
defaultDataLimit: BigInt(2 << 7) // the default maximum number of bytes that can be transferred over a relayed connection
maxInboundHopStreams: 32 // how many inbound HOP streams are allow simultaneously
maxOutboundHopStreams: 64 // how many outbound HOP streams are allow simultaneously
}
}),
}
})
```
Expand Down Expand Up @@ -844,24 +854,28 @@ const node = await createLibp2p({
})
```

#### Configuring the NAT Manager
#### Configuring UPnP NAT Traversal

Network Address Translation (NAT) is a function performed by your router to enable multiple devices on your local network to share a single IPv4 address. It's done transparently for outgoing connections, ensuring the correct response traffic is routed to your computer, but if you wish to accept incoming connections some configuration is necessary.

The NAT manager can be configured as follows:
Some home routers support [UPnP NAT](https://en.wikipedia.org/wiki/Universal_Plug_and_Play) which allows network devices to request traffic to be forwarded from public facing ports that would otherwise be firewalled.

If your router supports this, libp2p can be configured to use it as follows:

```js
import { createLibp2p } from 'libp2p'
import { uPnPNAT } from 'libp2p/upnp-nat'

const node = await createLibp2p({
config: {
nat: {
enabled: true, // defaults to true
services: {
nat: uPnPNAT({
description: 'my-node', // set as the port mapping description on the router, defaults the current libp2p version and your peer id
gateway: '192.168.1.1', // leave unset to auto-discover
externalIp: '80.1.1.1', // leave unset to auto-discover
localAddress: '129.168.1.123', // leave unset to auto-discover
ttl: 7200, // TTL for port mappings (min 20 minutes)
keepAlive: true, // Refresh port mapping after TTL expires
}
})
}
})
```
Expand All @@ -881,12 +895,18 @@ By default under nodejs libp2p will attempt to use [UPnP](https://en.wikipedia.o
Changing the protocol name prefix can isolate default public network (IPFS) for custom purposes.

```js
import { createLibp2p } from 'libp2p'
import { identifyService } from 'libp2p/identify'
import { pingService } from 'libp2p/ping'

const node = await createLibp2p({
identify: {
protocolPrefix: 'ipfs' // default
},
ping: {
protocolPrefix: 'ipfs' // default
services: {
identify: identifyService({
protocolPrefix: 'ipfs' // default
}),
ping: pingService({
protocolPrefix: 'ipfs' // default
})
}
})
/*
Expand Down
12 changes: 6 additions & 6 deletions doc/LIMITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ We can also limit the number of connections in a "pending" state. These connecti
All fields are optional. The default values are defined in [src/connection-manager/index.ts](https://github.com/libp2p/js-libp2p/blob/master/src/connection-manager/index.ts) - please see that file for the current values.

```ts
const node = await createLibp2pNode({
const node = await createLibp2p({
connectionManager: {
/**
* The total number of connections allowed to be open at one time
Expand Down Expand Up @@ -69,7 +69,7 @@ To prevent individual peers from opening multiple connections to a node, an `inb
All fields are optional. The default values are defined in [src/connection-manager/index.ts](https://github.com/libp2p/js-libp2p/blob/master/src/connection-manager/index.ts) - please see that file for the current values.

```ts
const node = await createLibp2pNode({
const node = await createLibp2p({
connectionManager: {
/**
* A remote peer may attempt to open up to this many connections per second,
Expand All @@ -93,7 +93,7 @@ These settings are done on a per-muxer basis, please see the README of the relev
All fields are optional. The default values are defined in [@libp2p/mplex/src/mplex.ts](https://github.com/libp2p/js-libp2p-mplex/blob/master/src/mplex.ts) - please see that file for the current values.

```ts
const node = await createLibp2pNode({
const node = await createLibp2p({
muxers: [
mplex({
/**
Expand Down Expand Up @@ -133,7 +133,7 @@ const node = await createLibp2pNode({
All fields are optional. The default values are defined in [@chainsafe/libp2p-yamux/src/config.ts](https://github.com/ChainSafe/js-libp2p-yamux/blob/master/src/config.ts) - please see that file for the current values.

```ts
const node = await createLibp2pNode({
const node = await createLibp2p({
muxers: [
yamux({
/**
Expand Down Expand Up @@ -186,7 +186,7 @@ The [@libp2p/tcp](https://github.com/libp2p/js-libp2p-tcp) transport allows addi
All fields are optional. The full list of options is defined in [@libp2p/tcp/src/index.ts](https://github.com/libp2p/js-libp2p-tcp/blob/master/src/index.ts) - please see that file for more details.

```ts
const node = await createLibp2pNode({
const node = await createLibp2p({
transports: [
tcp({
/**
Expand Down Expand Up @@ -215,7 +215,7 @@ const node = await createLibp2pNode({
It is possible to configure some hosts to always accept connections from and some to always reject connections from.

```js
const node = await createLibp2pNode({
const node = await createLibp2p({
connectionManager: {
/**
* A list of multiaddrs, any connection with a `remoteAddress` property
Expand Down
6 changes: 3 additions & 3 deletions doc/METRICS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ Although designed to primarily integrate with tools such as [Prometheus](https:/
First enable metrics tracking by supplying a [Metrics](https://www.npmjs.com/package/@libp2p/interface-metrics) implementation:

```js
import { createLibp2pNode } from 'libp2p'
import { createLibp2p } from 'libp2p'
import { prometheusMetrics } from '@libp2p/prometheus-metrics'

const node = await createLibp2pNode({
const node = await createLibp2p({
metrics: prometheusMetrics()
//... other config
})
Expand Down Expand Up @@ -164,7 +164,7 @@ Metrics implementations will allow extracting the values for presentation in an
import { prometheusMetrics } from '@libp2p/prometheus-metrics'
import client from 'prom-client'

const libp2p = createLibp2pNode({
const libp2p = createLibp2p({
metrics: prometheusMetrics()
//... other config
})
Expand Down
70 changes: 70 additions & 0 deletions doc/migrations/v0.44-v0.45.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ A migration guide for refactoring your application code from libp2p v0.44.x to v

## Table of Contents <!-- omit in toc -->

- [Services](#services)
- [Events](#events)
- [Emitters](#emitters)
- [Event changes](#event-changes)
Expand All @@ -13,6 +14,75 @@ A migration guide for refactoring your application code from libp2p v0.44.x to v
- [`self:peer:update`](#selfpeerupdate)
- [Atomic peer store methods](#atomic-peer-store-methods)

## Services

libp2p now accepts arbitrary service modules that can use internal components to fulfil their functions.

This reduces the attack surface area of libp2p nodes as less functionality is enabled by default, and with tree shaking less code will be included in bundles making for faster downloads when used in browsers.

Several optional modules have been removed and must now be configured as services:

**Before**

```js
import { createLibp2p } from 'libp2p'
import { circuitRelayServer } from 'libp2p/circuit-relay'
import { kadDHT } from '@libp2p/kad-dht'
import { gossipSub } from '@ChainSafe/libp2p-gossipsub'

const node = createLibp2p({
// ... other options here
identify: {
/** identify options **/
},
ping: {
/** ping options **/
},
fetch: {
/** fetch options **/
},
pubsub: gossipSub(),
dht: kadDHT(),
relay: circuitRelayServer()
})
```

**After**

```js
import { createLibp2p } from 'libp2p'
import { circuitRelayServer } from 'libp2p/circuit-relay'
import { identifyService } from 'libp2p/identify'
import { pingService } from 'libp2p/ping'
import { fetchService } from 'libp2p/fetch'
import { kadDHT } from '@libp2p/kad-dht'
import { gossipSub } from '@ChainSafe/libp2p-gossipsub'

const node = createLibp2p({
// ... other options here
services: {
identify: identifyService({
/** identify options **/
}),
ping: pingService({
/** ping options **/
}),
fetch: fetchService({
/** fetch options **/
}),
pubsub: gossipSub(),
dht: kadDHT(),
relay: circuitRelayServer()
}
})
```

Configured services can be accessed via the `.services` key:

```js
const result = await node.services.ping.ping(multiaddr('...'))
```

## Events

The events emitted by libp2p have been refactored to be more consistent and to give more insight into the inner workings of libp2p.
Expand Down
6 changes: 5 additions & 1 deletion examples/auto-relay/dialer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { noise } from '@chainsafe/libp2p-noise'
import { mplex } from '@libp2p/mplex'
import { multiaddr } from '@multiformats/multiaddr'
import { circuitRelayTransport } from 'libp2p/circuit-relay'
import { identifyService } from 'libp2p/identify'

async function main () {
const autoRelayNodeAddr = process.argv[2]
Expand All @@ -21,7 +22,10 @@ async function main () {
],
streamMuxers: [
mplex()
]
],
services: {
identify: identifyService()
}
})

console.log(`Node started with id ${node.peerId.toString()}`)
Expand Down
Loading

0 comments on commit f65b83b

Please sign in to comment.