diff --git a/package.json b/package.json index 065e877..0165a2c 100644 --- a/package.json +++ b/package.json @@ -28,17 +28,15 @@ "npm": ">=3.0.0" }, "devDependencies": { - "aegir": "^18.2.1", - "chai": "^4.2.0", - "libp2p-tcp": "~0.13.0" + "aegir": "^20.0.0", + "chai": "^4.2.0" }, "dependencies": { - "async": "^2.6.2", "debug": "^4.1.1", "mafmt": "^6.0.7", "multiaddr": "^6.0.6", - "peer-id": "~0.12.2", - "peer-info": "~0.15.1" + "peer-id": "^0.13.2", + "peer-info": "^0.16.0" }, "pre-push": [ "lint", diff --git a/src/index.js b/src/index.js index b692399..16a1dd4 100644 --- a/src/index.js +++ b/src/index.js @@ -6,20 +6,22 @@ const multiaddr = require('multiaddr') const mafmt = require('mafmt') const EventEmitter = require('events').EventEmitter const debug = require('debug') -const nextTick = require('async/nextTick') const log = debug('libp2p:bootstrap') log.error = debug('libp2p:bootstrap:error') -function isIPFS (addr) { - try { - return mafmt.IPFS.matches(addr) - } catch (e) { - return false - } -} - +/** + * Emits 'peer' events on a regular interval for each peer in the provided list. + */ class Bootstrap extends EventEmitter { + /** + * Constructs a new Bootstrap. + * + * @param {Object} options + * @param {Array} options.list - the list of peer addresses in multi-address format + * @param {number} options.interval - the interval between emitting addresses (in milli-seconds) + * + */ constructor (options) { super() this._list = options.list @@ -27,38 +29,46 @@ class Bootstrap extends EventEmitter { this._timer = null } - start (callback) { + /** + * Start emitting events. + */ + start () { if (this._timer) { - return nextTick(() => callback()) + return } this._timer = setInterval(() => this._discoverBootstrapPeers(), this._interval) - nextTick(() => { - callback() - this._discoverBootstrapPeers() - }) + this._discoverBootstrapPeers() } + /** + * Emit each address in the list as a PeerInfo. + */ _discoverBootstrapPeers () { - this._list.forEach((candidate) => { - if (!isIPFS(candidate)) { return log.error('Invalid multiaddr') } + this._list.forEach(async (candidate) => { + if (!mafmt.IPFS.matches(candidate)) { + return log.error('Invalid multiaddr') + } const ma = multiaddr(candidate) const peerId = PeerId.createFromB58String(ma.getPeerId()) - PeerInfo.create(peerId, (err, peerInfo) => { - if (err) { return log.error('Invalid bootstrap peer id', err) } + try { + const peerInfo = await PeerInfo.create(peerId) peerInfo.multiaddrs.add(ma) this.emit('peer', peerInfo) - }) + } catch (err) { + log.error('Invalid bootstrap peer id', err) + } }) } - stop (callback) { - nextTick(callback) - + /** + * Stop emitting events. + */ + stop () { if (this._timer) { clearInterval(this._timer) this._timer = null diff --git a/test/bootstrap.spec.js b/test/bootstrap.spec.js index c7397f8..e529f64 100644 --- a/test/bootstrap.spec.js +++ b/test/bootstrap.spec.js @@ -8,18 +8,19 @@ const { expect } = require('chai') const mafmt = require('mafmt') describe('bootstrap', () => { - it('find the other peer', function (done) { + it('find the other peer', function () { this.timeout(5 * 1000) const r = new Bootstrap({ list: peerList, interval: 2000 }) - r.once('peer', (peer) => done()) - r.start(() => {}) + const p = new Promise((resolve) => r.once('peer', resolve)) + r.start() + return p }) - it('not fail on malformed peers in peer list', function (done) { + it('not fail on malformed peers in peer list', function () { this.timeout(5 * 1000) const r = new Bootstrap({ @@ -27,13 +28,41 @@ describe('bootstrap', () => { interval: 2000 }) - r.start(() => { }) + const p = new Promise((resolve) => { + r.once('peer', (peer) => { + const peerList = peer.multiaddrs.toArray() + expect(peerList.length).to.eq(1) + expect(mafmt.IPFS.matches(peerList[0].toString())).equals(true) + resolve() + }) + }) + + r.start() - r.on('peer', (peer) => { - const peerList = peer.multiaddrs.toArray() - expect(peerList.length).to.eq(1) - expect(mafmt.IPFS.matches(peerList[0].toString())) - done() + return p + }) + + it('stop emitting events when stop() called', async function () { + const interval = 100 + const r = new Bootstrap({ + list: peerList, + interval }) + + let emitted = [] + r.on('peer', p => emitted.push(p)) + + // Should fire emit event for each peer in list on start, + // so wait 50 milliseconds then check + const p = new Promise((resolve) => setTimeout(resolve, 50)) + r.start() + await p + expect(emitted).to.have.length(peerList.length) + + // After stop is called, no more peers should be emitted + emitted = [] + r.stop() + await new Promise((resolve) => setTimeout(resolve, interval)) + expect(emitted).to.have.length(0) }) })