Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit deab67d

Browse files
committed
feat: add libp2p generator config option with example
1 parent 1f63e8c commit deab67d

File tree

8 files changed

+434
-42
lines changed

8 files changed

+434
-42
lines changed

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,12 @@ Creates and returns an instance of an IPFS node. Use the `options` argument to s
238238

239239
- `config` (object) Modify the default IPFS node config. Find the Node.js defaults at [`src/core/runtime/config-nodejs.js`](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/config-nodejs.js) and the browser defaults at [`src/core/runtime/config-browser.js`](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/config-browser.js). This object will be *merged* with the default config; it will not replace it.
240240

241-
- `libp2p` (object) add custom modules to the libp2p stack of your node
241+
- `libp2p` (object or function(ipfs, config)) add custom modules to the libp2p stack of your node
242+
243+
The libp2p option allows you to build your libp2p node by configuration, or via a generator. If you are looking to just modify the below options, using the object format is the quickest way to get the default features of libp2p. If you need to create a more customized libp2p node, such as with custom transports or peer/content routers that need some of the ipfs data on startup, a generator is a great way to achieve this.
244+
245+
You can see the generator in action in the [custom libp2p example](examples/custom-libp2p).
246+
242247
- `modules` (object):
243248
- `transport` (Array<[libp2p.Transport](https://github.com/libp2p/interface-transport)>): An array of Libp2p transport classes/instances to use _instead_ of the defaults. See [libp2p/interface-transport](https://github.com/libp2p/interface-transport) for details.
244249
- `peerDiscovery` (Array<[libp2p.PeerDiscovery](https://github.com/libp2p/interface-peer-discovery)>): An array of Libp2p peer discovery classes/instances to use _instead_ of the defaults. See [libp2p/peer-discovery](https://github.com/libp2p/interface-peer-discovery) for details. If passing a class, configuration can be passed using the config section below under the key corresponding to you module's unique `tag` (a static property on the class)
@@ -360,10 +365,10 @@ The core API is grouped into several areas:
360365
- [`ipfs.files.addPullStream([options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesaddpullstream)
361366
- [`ipfs.files.addReadableStream([options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesaddreadablestream)
362367
- [`ipfs.files.cat(ipfsPath, [options], [callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filescat). Alias to `ipfs.cat`.
363-
- [`ipfs.files.catPullStream(ipfsPath, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filescatpullstream)
368+
- [`ipfs.files.catPullStream(ipfsPath, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filescatpullstream)
364369
- [`ipfs.files.catReadableStream(ipfsPath, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filescatreadablestream)
365370
- [`ipfs.files.get(ipfsPath, [options], [callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesget). Alias to `ipfs.get`.
366-
- [`ipfs.files.getPullStream(ipfsPath, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesgetpullstream)
371+
- [`ipfs.files.getPullStream(ipfsPath, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesgetpullstream)
367372
- [`ipfs.files.getReadableStream(ipfsPath, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesgetreadablestream)
368373
- [`ipfs.ls(ipfsPath, [callback])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#ls)
369374
- [`ipfs.lsPullStream(ipfsPath)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#lspullstream)

examples/custom-libp2p/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Customizing the libp2p node
2+
3+
This example shows you how to make full use of the ipfs configuration to create a libp2p generator function. As IPFS applications become more complex, their needs for a custom libp2p node also grow. Instead of fighting with configuration options, you can use your own libp2p generator to get exactly what you need. This example shows you how.
4+
5+
## Run this example
6+
7+
Running this example should result in metrics being logged out to the console every few seconds.
8+
9+
```
10+
> npm install
11+
> npm start
12+
```
13+
14+
## Play with the configuration!
15+
16+
With the metrics for peers and bandwidth stats being logged out, try playing around with the nodes configuration to see what kind of metrics you can get. How many peers are you getting? What does your bandwidth look like?
17+
18+
This is also a good opportunity to explore the various stats that ipfs offers! Not seeing a statistic you think would be useful? We'd love to have you [contribute](https://github.com/ipfs/js-ipfs/blob/master/CONTRIBUTING.md)!

examples/custom-libp2p/index.js

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
'use strict'
2+
3+
const Libp2p = require('libp2p')
4+
const IPFS = require('ipfs')
5+
const TCP = require('libp2p-tcp')
6+
const MulticastDNS = require('libp2p-mdns')
7+
const WebSocketStar = require('libp2p-websocket-star')
8+
const Bootstrap = require('libp2p-railing')
9+
const SPDY = require('libp2p-spdy')
10+
const KadDHT = require('libp2p-kad-dht')
11+
const MPLEX = require('libp2p-mplex')
12+
const SECIO = require('libp2p-secio')
13+
const assert = require('assert')
14+
15+
/**
16+
* This is the generator we will use to generate our fully customized libp2p node.
17+
*
18+
* @param {*} _ipfsNode The ipfs node. This houses the PeerInfo and PeerBook that modules may need
19+
* @param {*} _ipfsConfig The config that is fetched from the ipfs-repo
20+
* @returns {Libp2p} Our new libp2p node
21+
*/
22+
const libp2pGenerator = (_ipfsNode, _ipfsConfig) => {
23+
// Set convenience variables to clearly showcase some of the useful things that are available
24+
const peerInfo = _ipfsNode._peerInfo
25+
const peerBook = _ipfsNode._peerBook
26+
const bootstrapList = _ipfsConfig.Bootstrap
27+
28+
// Create our WebSocketStar transport and give it our PeerId, straight from the ipfs node
29+
const wsstar = new WebSocketStar({
30+
id: peerInfo.id
31+
})
32+
33+
// Build and return our libp2p node
34+
return new Libp2p({
35+
peerInfo,
36+
peerBook,
37+
// Lets limit the connection managers peers and have it check peer health less frequently
38+
connectionManager: {
39+
maxPeers: 25,
40+
pollInterval: 5000
41+
},
42+
modules: {
43+
transport: [
44+
TCP,
45+
wsstar
46+
],
47+
streamMuxer: [
48+
MPLEX,
49+
SPDY
50+
],
51+
connEncryption: [
52+
SECIO
53+
],
54+
peerDiscovery: [
55+
MulticastDNS,
56+
Bootstrap,
57+
wsstar.discovery
58+
],
59+
dht: KadDHT
60+
},
61+
config: {
62+
peerDiscovery: {
63+
mdns: {
64+
interval: 10000,
65+
enabled: true
66+
},
67+
bootstrap: {
68+
interval: 10000,
69+
enabled: true,
70+
list: bootstrapList
71+
}
72+
},
73+
// Turn on relay with hop active so we can connect to more peers
74+
relay: {
75+
enabled: true,
76+
hop: {
77+
enabled: true,
78+
active: true
79+
}
80+
},
81+
dht: {
82+
kBucketSize: 20
83+
},
84+
EXPERIMENTAL: {
85+
dht: true,
86+
pubsub: true
87+
}
88+
}
89+
})
90+
}
91+
92+
// Now that we have our custom generator, let's start up the ipfs node!
93+
const node = new IPFS({
94+
libp2p: libp2pGenerator
95+
})
96+
97+
// Listen for the node to start, so we can log out some metrics
98+
node.once('start', (err) => {
99+
assert.ifError(err, 'Should startup without issue')
100+
101+
// Lets log out the number of peers we have every 2 seconds
102+
setInterval(() => {
103+
node.swarm.peers((err, peers) => {
104+
if (err) {
105+
console.log('An error occurred trying to check our peers:', err)
106+
process.exit(1)
107+
}
108+
console.log(`The node now has ${peers.length} peers.`)
109+
})
110+
}, 2000)
111+
112+
// Log out the bandwidth stats every 4 seconds so we can see how our configuration is doing
113+
setInterval(() => {
114+
node.stats.bw((err, stats) => {
115+
if (err) {
116+
console.log('An error occurred trying to check our stats:', err)
117+
}
118+
console.log(`\nBandwidth Stats: ${JSON.stringify(stats, null, 2)}\n`)
119+
})
120+
}, 4000)
121+
})

examples/custom-libp2p/package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "custom-libp2p",
3+
"version": "0.1.0",
4+
"description": "Customizing your libp2p node",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1",
8+
"start": "node index.js"
9+
},
10+
"license": "MIT",
11+
"dependencies": {
12+
"ipfs": "file:../../",
13+
"libp2p": "~0.22.0",
14+
"libp2p-kad-dht": "~0.10.1",
15+
"libp2p-mdns": "~0.12.0",
16+
"libp2p-mplex": "~0.8.0",
17+
"libp2p-railing": "~0.9.2",
18+
"libp2p-secio": "~0.10.0",
19+
"libp2p-spdy": "~0.12.1",
20+
"libp2p-tcp": "~0.12.0",
21+
"libp2p-websocket-star": "~0.8.1"
22+
}
23+
}

src/core/components/libp2p.js

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,49 +16,59 @@ module.exports = function libp2p (self) {
1616
return callback(err)
1717
}
1818

19-
const libp2pDefaults = {
20-
peerInfo: self._peerInfo,
21-
peerBook: self._peerInfoBook,
22-
config: {
23-
peerDiscovery: {
24-
mdns: {
25-
enabled: get(self._options, 'config.Discovery.MDNS.Enabled',
26-
get(config, 'Discovery.MDNS.Enabled', true))
19+
const defaultGenerator = (_ipfs, _config) => {
20+
const libp2pDefaults = {
21+
peerInfo: _ipfs._peerInfo,
22+
peerBook: _ipfs._peerInfoBook,
23+
config: {
24+
peerDiscovery: {
25+
mdns: {
26+
enabled: get(_ipfs._options, 'config.Discovery.MDNS.Enabled',
27+
get(_config, 'Discovery.MDNS.Enabled', true))
28+
},
29+
webRTCStar: {
30+
enabled: get(_ipfs._options, 'config.Discovery.webRTCStar.Enabled',
31+
get(_config, 'Discovery.webRTCStar.Enabled', true))
32+
},
33+
bootstrap: {
34+
list: get(_ipfs._options, 'config.Bootstrap',
35+
get(_config, 'Bootstrap', []))
36+
}
2737
},
28-
webRTCStar: {
29-
enabled: get(self._options, 'config.Discovery.webRTCStar.Enabled',
30-
get(config, 'Discovery.webRTCStar.Enabled', true))
38+
relay: {
39+
enabled: get(_ipfs._options, 'relay.enabled',
40+
get(_config, 'relay.enabled', false)),
41+
hop: {
42+
enabled: get(_ipfs._options, 'relay.hop.enabled',
43+
get(_config, 'relay.hop.enabled', false)),
44+
active: get(_ipfs._options, 'relay.hop.active',
45+
get(_config, 'relay.hop.active', false))
46+
}
3147
},
32-
bootstrap: {
33-
list: get(self._options, 'config.Bootstrap',
34-
get(config, 'Bootstrap', []))
48+
EXPERIMENTAL: {
49+
dht: get(_ipfs._options, 'EXPERIMENTAL.dht', false),
50+
pubsub: get(_ipfs._options, 'EXPERIMENTAL.pubsub', false)
3551
}
3652
},
37-
relay: {
38-
enabled: get(self._options, 'relay.enabled',
39-
get(config, 'relay.enabled', false)),
40-
hop: {
41-
enabled: get(self._options, 'relay.hop.enabled',
42-
get(config, 'relay.hop.enabled', false)),
43-
active: get(self._options, 'relay.hop.active',
44-
get(config, 'relay.hop.active', false))
45-
}
46-
},
47-
EXPERIMENTAL: {
48-
dht: get(self._options, 'EXPERIMENTAL.dht', false),
49-
pubsub: get(self._options, 'EXPERIMENTAL.pubsub', false)
50-
}
51-
},
52-
connectionManager: get(self._options, 'connectionManager',
53-
get(config, 'connectionManager', {}))
53+
connectionManager: get(_ipfs._options, 'connectionManager',
54+
get(_config, 'connectionManager', {}))
55+
}
56+
57+
const libp2pOptions = defaultsDeep(
58+
get(self._options, 'libp2p', {}),
59+
libp2pDefaults
60+
)
61+
62+
return new Node(libp2pOptions)
5463
}
5564

56-
const libp2pOptions = defaultsDeep(
57-
get(self._options, 'libp2p', {}),
58-
libp2pDefaults
59-
)
65+
// Always create libp2p via a generator
66+
let libp2pGenerator = get(self._options, 'libp2p', null)
67+
if (typeof libp2pGenerator !== 'function') {
68+
libp2pGenerator = defaultGenerator
69+
}
6070

61-
self._libp2pNode = new Node(libp2pOptions)
71+
self._libp2pNode = libp2pGenerator(self, config)
6272

6373
self._libp2pNode.on('peer:discovery', (peerInfo) => {
6474
const dial = () => {

src/core/config.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,12 @@ const schema = Joi.object().keys({
4444
}).allow(null),
4545
Bootstrap: Joi.array().items(Joi.multiaddr().IPFS().options({ convert: false }))
4646
}).allow(null),
47-
libp2p: Joi.object().keys({
48-
modules: Joi.object().allow(null) // TODO: schemas for libp2p modules?
49-
}).allow(null)
47+
libp2p: Joi.alternatives().try(
48+
Joi.func(),
49+
Joi.object().keys({
50+
modules: Joi.object().allow(null) // TODO: schemas for libp2p modules?
51+
})
52+
).allow(null)
5053
}).options({ allowUnknown: true })
5154

5255
module.exports.validate = (config) => Joi.attempt(config, schema)

test/core/config.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ describe('config', () => {
226226
{ libp2p: { modules: null } },
227227
{ libp2p: { modules: undefined } },
228228
{ libp2p: { unknown: 'value' } },
229+
{ libp2p: () => {} },
229230
{ libp2p: null },
230231
{ libp2p: undefined }
231232
]

0 commit comments

Comments
 (0)