diff --git a/package.json b/package.json index 32881d0..eb3d5ba 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/diasdavid/js-peer-book.git" + "url": "git+https://github.com/libp2p/js-peer-book.git" }, "pre-commit": [ "lint", @@ -33,19 +33,20 @@ "author": "David Dias ", "license": "MIT", "bugs": { - "url": "https://github.com/diasdavid/js-peer-book/issues" + "url": "https://github.com/libp2p/js-peer-book/issues" }, - "homepage": "https://github.com/diasdavid/js-peer-book#readme", + "homepage": "https://github.com/libp2p/js-peer-book#readme", "dependencies": { - "bs58": "^4.0.0" + "bs58": "^4.0.0", + "peer-id": "^0.8.6", + "peer-info": "^0.9.1" }, "devDependencies": { "aegir": "^11.0.1", "async": "^2.2.0", "chai": "^3.5.0", "dirty-chai": "^1.2.2", - "multiaddr": "^2.2.3", - "peer-info": "~0.8.5", + "multiaddr": "^2.3.0", "pre-commit": "^1.2.2" }, "contributors": [ @@ -54,4 +55,4 @@ "greenkeeperio-bot ", "npmcdn-to-unpkg-bot " ] -} \ No newline at end of file +} diff --git a/src/index.js b/src/index.js index 0f7697e..206c558 100644 --- a/src/index.js +++ b/src/index.js @@ -1,72 +1,121 @@ 'use strict' const bs58 = require('bs58') +const PeerId = require('peer-id') +const PeerInfo = require('peer-info') -module.exports = PeerBook +function getB58Str (peer) { + let b58Str -function PeerBook () { - if (!(this instanceof PeerBook)) { - return new PeerBook() + if (typeof peer === 'string') { + b58Str = peer + } else if (Buffer.isBuffer(peer)) { + b58Str = bs58.encode(peer).toString() + } else if (PeerId.isPeerId(peer)) { + b58Str = peer.toB58String() + } else if (PeerInfo.isPeerInfo(peer)) { + b58Str = peer.id.toB58String() + } else { + throw new Error('not valid PeerId or PeerInfo, or B58Str') } - const peers = {} + return b58Str +} - this.put = (peerInfo, replace) => { - if (peers[peerInfo.id.toB58String()] && !replace) { - // peerInfo.replace merges by default - peers[peerInfo.id.toB58String()].multiaddr.replace([], peerInfo.multiaddrs) - } - peers[peerInfo.id.toB58String()] = peerInfo +class PeerBook { + constructor () { + this._peers = {} } - this.getAll = () => { - return peers + // checks if peer exists + // peer can be PeerId, b58String or PeerInfo + has (peer) { + const b58Str = getB58Str(peer) + return Boolean(this._peers[b58Str]) } /** - * Get the info to the given PeerId. + * Stores a peerInfo, if already exist, merges the new into the old. * - * @param {PeerId} id + * @param {PeerInfo} peerInfo + * @param {replace} boolean * @returns {PeerInfo} */ - this.get = (id) => { - return this.getByB58String(id.toB58String()) + put (peerInfo, replace) { + const localPeerInfo = this._peers[peerInfo.id.toB58String()] + + // insert if doesn't exist or replace if replace flag is true + if (!localPeerInfo || replace) { + this._peers[peerInfo.id.toB58String()] = peerInfo + return peerInfo + } + + // peerInfo.replace merges by default if none to replace are passed + peerInfo.multiaddrs.forEach((ma) => localPeerInfo.multiaddrs.add(ma)) + + // pass active connection state + const ma = peerInfo.isConnected() + if (ma) { + localPeerInfo.connect(ma) + } + + // pass known protocols + peerInfo.protocols.forEach((p) => localPeerInfo.protocols.add(p)) + + if (!localPeerInfo.id.privKey && peerInfo.id.privKey) { + localPeerInfo.id.privKey = peerInfo.id.privKey + } + + if (!localPeerInfo.id.pubKey && peerInfo.id.pubKey) { + localPeerInfo.id.pubKey = peerInfo.id.pubKey + } + + return localPeerInfo } - this.getByB58String = (b58String) => { - const peerInfo = peers[b58String] + /** + * Get the info to the given PeerId, PeerInfo or b58Str id + * + * @param {PeerId} id + * @returns {PeerInfo} + */ + get (peer) { + const b58Str = getB58Str(peer) + + const peerInfo = this._peers[b58Str] + if (peerInfo) { return peerInfo } throw new Error('PeerInfo not found') } - this.getByMultihash = (multihash) => { - const b58multihash = bs58.encode(multihash).toString() - return this.getByB58String(b58multihash) - } - - this.removeByB58String = (b58String) => { - if (peers[b58String]) { - delete peers[b58String] - } + getAll () { + return this._peers } - this.removeByMultihash = (multihash) => { - const b58multihash = bs58.encode(multihash).toString() - this.removeByB58String(b58multihash) + getAllArray () { + return Object.keys(this._peers).map((b58Str) => this._peers[b58Str]) } /** * Return all multiaddrs for a given PeerId. * - * @param {PeerId} id + * @param {PeerId, PeerInfo, Multihash, B58Str} id * @returns {Array} */ - this.getAddrs = (id) => { - const info = this.get(id) + getMultiaddrs (peer) { + const info = this.get(peer) return info.multiaddrs } - // TODO serialize PeerBook into MerkleDAG Objects + remove (peer) { + const b58Str = getB58Str(peer) + + if (this._peers[b58Str]) { + delete this._peers[b58Str] + } + } } + +module.exports = PeerBook diff --git a/test/peer-book.spec.js b/test/peer-book.spec.js index 70023ea..c1a4aab 100644 --- a/test/peer-book.spec.js +++ b/test/peer-book.spec.js @@ -8,11 +8,12 @@ chai.use(dirtyChai) const Multiaddr = require('multiaddr') const PeerInfo = require('peer-info') const async = require('async') +const utils = require('./utils') +const createPeerInfo = utils.createPeerInfo const PeerBook = require('../src') -describe('peer-book', function () { - this.timeout(50000) +describe('peer-book', () => { let pb let p1 let p2 @@ -21,10 +22,22 @@ describe('peer-book', function () { before((done) => { async.parallel([ - (cb) => PeerInfo.create(cb), - (cb) => PeerInfo.create(cb), - (cb) => PeerInfo.create(cb), - (cb) => PeerInfo.create(cb) + (cb) => createPeerInfo([ + '/tcp/1000', + '/tcp/1001' + ], cb), + (cb) => createPeerInfo([ + '/tcp/2000', + '/tcp/2001' + ], cb), + (cb) => createPeerInfo([ + '/tcp/3000', + '/tcp/3001' + ], cb), + (cb) => createPeerInfo([ + '/tcp/4000', + '/tcp/4001' + ], cb) ], (err, infos) => { if (err) { return done(err) @@ -44,44 +57,48 @@ describe('peer-book', function () { expect(pb).to.exist() }) - it('put peerInfo', () => { - pb.put(p1) - pb.put(p2) - pb.put(p3) + it('.put', () => { + expect(pb.put(p1)).to.eql(p1) + expect(pb.put(p2)).to.eql(p2) + expect(pb.put(p3)).to.eql(p3) }) - it('get all peerInfo', () => { + it('.getAll', () => { const peers = pb.getAll() expect(Object.keys(peers).length).to.equal(3) }) - it('get', () => { + it('.getAllArray', () => { + expect(pb.getAllArray()).to.have.length(3) + }) + + it('.get by PeerId', () => { const peer = pb.get(p1.id) - expect(peer).to.deep.equal(p1) + expect(peer).to.eql(p1) }) - it('getByB58String', () => { - const p1Id = p1.id.toB58String() - const peer = pb.getByB58String(p1Id) - expect(peer).to.deep.equal(p1) + it('.get by B58String ', () => { + const b58Str = p1.id.toB58String() + const peer = pb.get(b58Str) + expect(peer).to.eql(p1) }) - it('getByB58String non existent', (done) => { + it('.get by B58String non existent', (done) => { try { - pb.getByB58String(p4.id.toB58String()) + pb.get(p4.id.toB58String()) } catch (err) { expect(err).to.exist() done() } }) - it('getByMultihash', () => { - const p1Id = p1.id.toBytes() - const peer = pb.getByMultihash(p1Id) - expect(peer).to.deep.equal(p1) + it('.get by Multihash', () => { + const mh = p1.id.toBytes() + const peer = pb.get(mh) + expect(peer).to.eql(p1) }) - it('getByMultihash non existent', (done) => { + it('.get by Multihash non existent', (done) => { try { pb.getByMultihash(p4.id.toBytes()) } catch (err) { @@ -90,49 +107,45 @@ describe('peer-book', function () { } }) - it('removeByB58String', (done) => { - const p1Id = p1.id.toB58String() - pb.removeByB58String(p1Id) - try { - pb.getByB58String(p1Id) - } catch (err) { - expect(err).to.exist() - done() - } + it('.remove by B58String', () => { + const b58Str = p1.id.toB58String() + + pb.remove(b58Str) + expect(pb.has(b58Str)).to.equal(false) }) - it('removeByMultihash', (done) => { - const p1Id = p1.id.toBytes() - pb.removeByMultihash(p1Id) - try { - pb.getByMultihash(p1Id) - } catch (err) { - expect(err).to.exist() - done() - } + it('.remove by Multihash', () => { + const mh = p1.id.toBytes() + + pb.remove(mh) + expect(pb.has(mh)).to.equal(false) }) - it('add repeated Id, merge info', () => { - const peerA = new PeerInfo(p3.id) - peerA.multiaddr.add(new Multiaddr('/ip4/127.0.0.1/tcp/4001')) - pb.put(peerA) - const peerB = pb.getByB58String(p3.id.toB58String()) - expect(peerA).to.deep.equal(peerB) + it('.put repeated Id, merge info', () => { + const peer3A = new PeerInfo(p3.id) + peer3A.multiaddrs.add(new Multiaddr('/ip4/127.0.0.1/tcp/4001')) + + pb.put(peer3A) + const peer3B = pb.get(p3.id.toBytes()) + + expect(peer3B.multiaddrs.toArray()).to.have.length(3) }) - it('add repeated Id, replace info', () => { - const peerA = new PeerInfo(p3.id) - peerA.multiaddr.add(new Multiaddr('/ip4/188.0.0.1/tcp/5001')) - pb.put(peerA, true) - const peerB = pb.getByB58String(p3.id.toB58String()) - expect(peerA).to.deep.equal(peerB) + it('.put repeated Id, replace info', () => { + const peer3A = new PeerInfo(p3.id) + peer3A.multiaddrs.add(new Multiaddr('/ip4/188.0.0.1/tcp/5001')) + + pb.put(peer3A, true) + const peer3B = pb.get(p3.id.toB58String()) + expect(peer3A.multiaddrs.toArray()).to.eql(peer3B.multiaddrs.toArray()) }) - it('getAddrs', () => { + it('.getMultiaddrs', () => { const pb = new PeerBook() const peer = new PeerInfo(p3.id) - peer.multiaddr.add(new Multiaddr('/ip4/127.0.0.1/tcp/1234')) + peer.multiaddrs.add(new Multiaddr('/ip4/127.0.0.1/tcp/1234')) + pb.put(peer) - expect(pb.getAddrs(p3.id)).to.be.eql(peer.multiaddrs) + expect(pb.getMultiaddrs(p3.id)).to.be.eql(peer.multiaddrs) }) }) diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000..409580b --- /dev/null +++ b/test/utils.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +chai.use(require('dirty-chai')) +const PeerInfo = require('peer-info') +const PeerId = require('peer-id') +const waterfall = require('async/waterfall') + +function createPeerInfo (multiaddrs, options, callback) { + if (typeof options === 'function') { + callback = options + options = {} + } + + if (!Array.isArray(multiaddrs)) { + multiaddrs = [multiaddrs] + } + + waterfall([ + (cb) => PeerId.create({ bits: 1024 }, cb), + (peerId, cb) => PeerInfo.create(peerId, cb), + (peerInfo, cb) => { + multiaddrs.map((ma) => peerInfo.multiaddrs.add(ma)) + cb(null, peerInfo) + } + ], callback) +} + +module.exports = { + createPeerInfo: createPeerInfo +}