Skip to content

Commit 18a062e

Browse files
vasco-santosjacobheun
authored andcommitted
feat: discovery modules (#486)
* feat: discovery modules * chore: address review
1 parent 1999606 commit 18a062e

File tree

4 files changed

+206
-4
lines changed

4 files changed

+206
-4
lines changed

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@
8888
"glob": "^7.1.4",
8989
"interface-datastore": "^0.6.0",
9090
"it-pair": "^1.0.0",
91-
"libp2p-bootstrap": "^0.9.7",
91+
"libp2p-bootstrap": "^0.10.3",
9292
"libp2p-delegated-content-routing": "^0.2.2",
9393
"libp2p-delegated-peer-routing": "^0.2.2",
9494
"libp2p-floodsub": "^0.19.0",
9595
"libp2p-gossipsub": "^0.1.0",
9696
"libp2p-kad-dht": "~0.17.0",
97-
"libp2p-mdns": "^0.12.3",
97+
"libp2p-mdns": "^0.13.0",
9898
"libp2p-mplex": "^0.9.1",
9999
"libp2p-pnet": "~0.1.0",
100100
"libp2p-secio": "^0.12.1",

src/index.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ class Libp2p extends EventEmitter {
168168
await this.stop()
169169
throw err
170170
}
171-
this._isStarted = true
172171
}
173172

174173
/**
@@ -317,8 +316,13 @@ class Libp2p extends EventEmitter {
317316
this.pubsub && this.pubsub.start()
318317
}
319318

319+
// DHT subsystem
320320
if (this._config.dht.enabled) {
321321
this._dht && this._dht.start()
322+
323+
// TODO: this should be modified once random-walk is used as
324+
// the other discovery modules
325+
this._dht._dht.on('peer', this._peerDiscovered)
322326
}
323327
}
324328

@@ -327,6 +331,11 @@ class Libp2p extends EventEmitter {
327331
* @private
328332
*/
329333
_onDidStart () {
334+
this._isStarted = true
335+
336+
// Peer discovery
337+
this._setupPeerDiscovery()
338+
330339
// Once we start, emit and dial any peers we may have already discovered
331340
for (const peerInfo of this.peerStore.peers.values()) {
332341
this.emit('peer:discovery', peerInfo)
@@ -385,7 +394,7 @@ class Libp2p extends EventEmitter {
385394
* @returns {Promise<void>}
386395
*/
387396
_setupPeerDiscovery () {
388-
for (const DiscoveryService of this._modules.peerDiscovery) {
397+
for (const DiscoveryService of this._modules.peerDiscovery || []) {
389398
let config = {
390399
enabled: true // on by default
391400
}

test/peer-discovery/index.node.js

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
'use strict'
2+
/* eslint-env mocha */
3+
4+
const chai = require('chai')
5+
chai.use(require('dirty-chai'))
6+
7+
const defer = require('p-defer')
8+
const mergeOptions = require('merge-options')
9+
10+
const Bootstrap = require('libp2p-bootstrap')
11+
const crypto = require('libp2p-crypto')
12+
const KadDht = require('libp2p-kad-dht')
13+
const MulticastDNS = require('libp2p-mdns')
14+
const multiaddr = require('multiaddr')
15+
16+
const Libp2p = require('../../src')
17+
const baseOptions = require('../utils/base-options')
18+
const { createPeerInfoFromFixture } = require('../utils/creators/peer')
19+
20+
describe('peer discovery scenarios', () => {
21+
let peerInfo, remotePeerInfo1, remotePeerInfo2
22+
let libp2p
23+
24+
before(async () => {
25+
[peerInfo, remotePeerInfo1, remotePeerInfo2] = await createPeerInfoFromFixture(3)
26+
27+
peerInfo.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
28+
remotePeerInfo1.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
29+
remotePeerInfo2.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
30+
})
31+
32+
afterEach(async () => {
33+
libp2p && await libp2p.stop()
34+
})
35+
36+
it('bootstrap should discover all peers in the list', async () => {
37+
const deferred = defer()
38+
39+
const bootstrappers = [
40+
...remotePeerInfo1.multiaddrs.toArray().map((ma) => `${ma}/p2p/${remotePeerInfo1.id.toB58String()}`),
41+
...remotePeerInfo2.multiaddrs.toArray().map((ma) => `${ma}/p2p/${remotePeerInfo2.id.toB58String()}`)
42+
]
43+
44+
libp2p = new Libp2p(mergeOptions(baseOptions, {
45+
peerInfo,
46+
modules: {
47+
peerDiscovery: [Bootstrap]
48+
},
49+
config: {
50+
peerDiscovery: {
51+
bootstrap: {
52+
enabled: true,
53+
list: bootstrappers
54+
}
55+
}
56+
}
57+
}))
58+
59+
const expectedPeers = new Set([
60+
remotePeerInfo1.id.toB58String(),
61+
remotePeerInfo2.id.toB58String()
62+
])
63+
64+
libp2p.on('peer:discovery', (peerInfo) => {
65+
expectedPeers.delete(peerInfo.id.toB58String())
66+
if (expectedPeers.size === 0) {
67+
libp2p.removeAllListeners('peer:discovery')
68+
deferred.resolve()
69+
}
70+
})
71+
72+
await libp2p.start()
73+
74+
return deferred.promise
75+
})
76+
77+
it('MulticastDNS should discover all peers on the local network', async () => {
78+
const deferred = defer()
79+
80+
const getConfig = (peerInfo) => mergeOptions(baseOptions, {
81+
peerInfo,
82+
modules: {
83+
peerDiscovery: [MulticastDNS]
84+
},
85+
config: {
86+
peerDiscovery: {
87+
mdns: {
88+
enabled: true,
89+
interval: 200, // discover quickly
90+
// use a random tag to prevent CI collision
91+
serviceTag: crypto.randomBytes(10).toString('hex')
92+
}
93+
}
94+
}
95+
})
96+
97+
libp2p = new Libp2p(getConfig(peerInfo))
98+
const remoteLibp2p1 = new Libp2p(getConfig(remotePeerInfo1))
99+
const remoteLibp2p2 = new Libp2p(getConfig(remotePeerInfo2))
100+
101+
const expectedPeers = new Set([
102+
remotePeerInfo1.id.toB58String(),
103+
remotePeerInfo2.id.toB58String()
104+
])
105+
106+
libp2p.on('peer:discovery', (peerInfo) => {
107+
expectedPeers.delete(peerInfo.id.toB58String())
108+
if (expectedPeers.size === 0) {
109+
libp2p.removeAllListeners('peer:discovery')
110+
deferred.resolve()
111+
}
112+
})
113+
114+
await remoteLibp2p1.start()
115+
await remoteLibp2p2.start()
116+
await libp2p.start()
117+
118+
await deferred.promise
119+
120+
await remoteLibp2p1.stop()
121+
await remoteLibp2p2.stop()
122+
})
123+
124+
it('kad-dht should discover other peers', async () => {
125+
const deferred = defer()
126+
127+
const getConfig = (peerInfo) => mergeOptions(baseOptions, {
128+
peerInfo,
129+
modules: {
130+
dht: KadDht
131+
},
132+
config: {
133+
dht: {
134+
randomWalk: {
135+
enabled: true,
136+
delay: 100,
137+
interval: 200, // start the query sooner
138+
timeout: 3000
139+
},
140+
enabled: true
141+
}
142+
}
143+
})
144+
145+
libp2p = new Libp2p(getConfig(peerInfo))
146+
const remoteLibp2p1 = new Libp2p(getConfig(remotePeerInfo1))
147+
const remoteLibp2p2 = new Libp2p(getConfig(remotePeerInfo2))
148+
149+
libp2p.on('peer:discovery', (peerInfo) => {
150+
if (peerInfo.id.toB58String() === remotePeerInfo2.id.toB58String()) {
151+
libp2p.removeAllListeners('peer:discovery')
152+
deferred.resolve()
153+
}
154+
})
155+
156+
await remoteLibp2p1.start()
157+
await remoteLibp2p2.start()
158+
await libp2p.start()
159+
160+
// Topology:
161+
// A -> B
162+
// C -> B
163+
await Promise.all([
164+
libp2p.dial(remotePeerInfo1),
165+
remoteLibp2p2.dial(remotePeerInfo1)
166+
])
167+
168+
await deferred.promise
169+
await remoteLibp2p1.stop()
170+
await remoteLibp2p2.stop()
171+
})
172+
})

test/peer-discovery/index.spec.js

+21
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ const chai = require('chai')
55
chai.use(require('dirty-chai'))
66
const { expect } = chai
77
const sinon = require('sinon')
8+
89
const defer = require('p-defer')
10+
const mergeOptions = require('merge-options')
11+
12+
const MulticastDNS = require('libp2p-mdns')
913

1014
const Libp2p = require('../../src')
1115
const baseOptions = require('../utils/base-options.browser')
@@ -22,6 +26,7 @@ describe('peer discovery', () => {
2226

2327
afterEach(async () => {
2428
libp2p && await libp2p.stop()
29+
sinon.reset()
2530
})
2631

2732
it('should dial know peers on startup', async () => {
@@ -42,4 +47,20 @@ describe('peer discovery', () => {
4247
await deferred.promise
4348
expect(spy.getCall(0).args).to.eql([remotePeerInfo])
4449
})
50+
51+
it('should ignore self on discovery', async () => {
52+
libp2p = new Libp2p(mergeOptions(baseOptions, {
53+
peerInfo,
54+
modules: {
55+
peerDiscovery: [MulticastDNS]
56+
}
57+
}))
58+
59+
await libp2p.start()
60+
const discoverySpy = sinon.spy()
61+
libp2p.on('peer:discovery', discoverySpy)
62+
libp2p._discovery[0].emit('peer', libp2p.peerInfo)
63+
64+
expect(discoverySpy.called).to.eql(false)
65+
})
4566
})

0 commit comments

Comments
 (0)