From 45b705dd33986af461a55c3e860a410c6eccce8a Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 19 Jun 2018 23:28:35 -0700 Subject: [PATCH] feat: implement bitswap.wantlist peerid and bitswap.unwant (#1349) * feat(bitswap.unwant) expose bitswap.unwant to cli and http api * feat(tests) move some bitswap tests to interface Will require including the version of interface-ipfs-core that they are moved to * feat(tests) move some bitswap tests to interface Will require including the version of interface-ipfs-core that they are moved to * feat(bitswap.wantlist) add peer parameter * fix(test) fix cli tests for both bitswap.unwant and bitswap.wantlist peerId * fix: pick peer property from querystring, fix tests License: MIT Signed-off-by: Alan Shaw * chore: update interface-ipfs-core dependency License: MIT Signed-off-by: Alan Shaw * fix: don't add newline to cli block.get output * chore: increase timeout for bitswap cli test setup License: MIT Signed-off-by: Alan Shaw * chore: update interface-ipfs-core dependency License: MIT Signed-off-by: Alan Shaw --- package.json | 2 +- src/cli/commands/bitswap/stat.js | 7 +-- src/cli/commands/bitswap/unwant.js | 18 ++++++- src/cli/commands/bitswap/wantlist.js | 9 ++-- src/cli/commands/block/get.js | 7 ++- src/core/components/bitswap.js | 50 +++++++++++++---- src/http/api/resources/bitswap.js | 78 +++++++++++++++------------ src/http/api/resources/block.js | 8 ++- src/http/api/routes/bitswap.js | 4 +- src/http/api/routes/stats.js | 2 +- test/cli/bitswap.js | 27 ++++++++-- test/core/bitswap.spec.js | 74 ------------------------- test/core/interface/bitswap.js | 35 ++++++++++++ test/core/interface/interface.spec.js | 1 + test/http-api/index.js | 4 +- test/http-api/interface/bitswap.js | 31 +++++++++++ 16 files changed, 213 insertions(+), 144 deletions(-) create mode 100644 test/core/interface/bitswap.js create mode 100644 test/http-api/interface/bitswap.js diff --git a/package.json b/package.json index 737988ff32..413db75db0 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "expose-loader": "~0.7.5", "form-data": "^2.3.2", "hat": "0.0.3", - "interface-ipfs-core": "~0.66.2", + "interface-ipfs-core": "~0.68.2", "ipfsd-ctl": "~0.37.3", "lodash": "^4.17.10", "mocha": "^5.1.1", diff --git a/src/cli/commands/bitswap/stat.js b/src/cli/commands/bitswap/stat.js index 081dceb595..d7a26680f1 100644 --- a/src/cli/commands/bitswap/stat.js +++ b/src/cli/commands/bitswap/stat.js @@ -1,6 +1,5 @@ 'use strict' -const CID = require('cids') const print = require('../../utils').print module.exports = { @@ -17,11 +16,7 @@ module.exports = { } stats.wantlist = stats.wantlist || [] - stats.wantlist = stats.wantlist.map((entry) => { - const buf = Buffer.from(entry.cid.hash.data) - const cid = new CID(entry.cid.version, entry.cid.codec, buf) - return cid.toBaseEncodedString() - }) + stats.wantlist = stats.wantlist.map(entry => entry['/']) stats.peers = stats.peers || [] print(`bitswap status diff --git a/src/cli/commands/bitswap/unwant.js b/src/cli/commands/bitswap/unwant.js index 840276e9b2..ea676450fa 100644 --- a/src/cli/commands/bitswap/unwant.js +++ b/src/cli/commands/bitswap/unwant.js @@ -1,11 +1,25 @@ 'use strict' +const print = require('../../utils').print + module.exports = { command: 'unwant ', - describe: 'Remove a given block from your wantlist.', + describe: 'Removes a given block from your wantlist.', + builder: { + key: { + alias: 'k', + describe: 'Key to remove from your wantlist', + type: 'string' + } + }, handler (argv) { - throw new Error('Not implemented yet') + argv.ipfs.bitswap.unwant(argv.key, (err) => { + if (err) { + throw err + } + print(`Key ${argv.key} removed from wantlist`) + }) } } diff --git a/src/cli/commands/bitswap/wantlist.js b/src/cli/commands/bitswap/wantlist.js index 63692a993a..9397f492ae 100644 --- a/src/cli/commands/bitswap/wantlist.js +++ b/src/cli/commands/bitswap/wantlist.js @@ -3,7 +3,7 @@ const print = require('../../utils').print module.exports = { - command: 'wantlist', + command: 'wantlist [peer]', describe: 'Print out all blocks currently on the bitswap wantlist for the local peer.', @@ -16,13 +16,12 @@ module.exports = { }, handler (argv) { - // TODO: handle argv.peer - argv.ipfs.bitswap.wantlist((err, res) => { + argv.ipfs.bitswap.wantlist(argv.peer, (err, res) => { if (err) { throw err } - res.Keys.forEach((cidStr) => { - print(cidStr) + res.Keys.forEach((cid) => { + print(cid['/']) }) }) } diff --git a/src/cli/commands/block/get.js b/src/cli/commands/block/get.js index ed753aeadb..9fb61f4434 100644 --- a/src/cli/commands/block/get.js +++ b/src/cli/commands/block/get.js @@ -1,6 +1,7 @@ 'use strict' const CID = require('cids') +const print = require('../../utils').print module.exports = { command: 'get ', @@ -17,7 +18,11 @@ module.exports = { throw err } - process.stdout.write(block.data) + if (block) { + print(block.data, false) + } else { + print('Block was unwanted before it could be remotely retrieved') + } }) } } diff --git a/src/core/components/bitswap.js b/src/core/components/bitswap.js index e148f8c165..bd59eb3b16 100644 --- a/src/core/components/bitswap.js +++ b/src/core/components/bitswap.js @@ -4,21 +4,42 @@ const OFFLINE_ERROR = require('../utils').OFFLINE_ERROR const promisify = require('promisify-es6') const setImmediate = require('async/setImmediate') const Big = require('big.js') +const CID = require('cids') +const PeerId = require('peer-id') function formatWantlist (list) { - return Array.from(list).map((e) => e[1]) + return Array.from(list).map((e) => ({ '/': e[1].cid.toBaseEncodedString() })) } module.exports = function bitswap (self) { return { - wantlist: () => { + wantlist: promisify((peerId, callback) => { + if (!callback) { + callback = peerId + peerId = undefined + } + if (!self.isOnline()) { - throw new Error(OFFLINE_ERROR) + return setImmediate(() => callback(new Error(OFFLINE_ERROR))) } - const list = self._bitswap.getWantlist() - return formatWantlist(list) - }, + let list + if (peerId) { + try { + peerId = PeerId.createFromB58String(peerId) + } catch (e) { + peerId = null + } + if (!peerId) { + return setImmediate(() => callback(new Error('Invalid peerId'))) + } + list = self._bitswap.wantlistForPeer(peerId) + } else { + list = self._bitswap.getWantlist() + } + list = formatWantlist(list) + return setImmediate(() => callback(null, { Keys: list })) + }), stat: promisify((callback) => { if (!self.isOnline()) { @@ -40,12 +61,21 @@ module.exports = function bitswap (self) { }) }), - unwant: (key) => { + unwant: promisify((keys, callback) => { if (!self.isOnline()) { - throw new Error(OFFLINE_ERROR) + return setImmediate(() => callback(new Error(OFFLINE_ERROR))) } - // TODO: implement when https://github.com/ipfs/js-ipfs-bitswap/pull/10 is merged - } + if (!Array.isArray(keys)) { + keys = [keys] + } + keys = keys.map((key) => { + if (CID.isCID(key)) { + return key + } + return new CID(key) + }) + return setImmediate(() => callback(null, self._bitswap.unwant(keys))) + }) } } diff --git a/src/http/api/resources/bitswap.js b/src/http/api/resources/bitswap.js index da830c6415..33eccd7bcb 100644 --- a/src/http/api/resources/bitswap.js +++ b/src/http/api/resources/bitswap.js @@ -6,50 +6,58 @@ const parseKey = require('./block').parseKey exports = module.exports -exports.wantlist = (request, reply) => { - let list - try { - list = request.server.app.ipfs.bitswap.wantlist() - list = list.map((entry) => entry.cid.toBaseEncodedString()) - } catch (err) { - return reply(boom.badRequest(err)) +exports.wantlist = { + handler: (request, reply) => { + const peerId = request.query.peer + request.server.app.ipfs.bitswap.wantlist(peerId, (err, list) => { + if (err) { + return reply(boom.badRequest(err)) + } + reply(list) + }) } - - reply({ - Keys: list - }) } -exports.stat = (request, reply) => { - const ipfs = request.server.app.ipfs - - ipfs.bitswap.stat((err, stats) => { - if (err) { - return reply({ - Message: err.toString(), - Code: 0 - }).code(500) - } - - reply({ - ProvideBufLen: stats.provideBufLen, - BlocksReceived: stats.blocksReceived, - Wantlist: stats.wantlist, - Peers: stats.peers, - DupBlksReceived: stats.dupBlksReceived, - DupDataReceived: stats.dupDataReceived, - DataReceived: stats.dataReceived, - BlocksSent: stats.blocksSent, - DataSent: stats.dataSent +exports.stat = { + handler: (request, reply) => { + const ipfs = request.server.app.ipfs + + ipfs.bitswap.stat((err, stats) => { + if (err) { + return reply({ + Message: err.toString(), + Code: 0 + }).code(500) + } + + reply({ + ProvideBufLen: stats.provideBufLen, + BlocksReceived: stats.blocksReceived, + Wantlist: stats.wantlist, + Peers: stats.peers, + DupBlksReceived: stats.dupBlksReceived, + DupDataReceived: stats.dupDataReceived, + DataReceived: stats.dataReceived, + BlocksSent: stats.blocksSent, + DataSent: stats.dataSent + }) }) - }) + } } exports.unwant = { - // uses common parseKey method that returns a `key` + // uses common parseKey method that assigns a `key` to request.pre.args parseArgs: parseKey, + // main route handler which is called after the above `parseArgs`, but only if the args were valid handler: (request, reply) => { - reply(boom.badRequest(new Error('Not implemented yet'))) + const key = request.pre.args.key + const ipfs = request.server.app.ipfs + ipfs.bitswap.unwant(key, (err) => { + if (err) { + return reply(boom.badRequest(err)) + } + reply({ key: key.toBaseEncodedString() }) + }) } } diff --git a/src/http/api/resources/block.js b/src/http/api/resources/block.js index 77553cbcaf..f536fb2a22 100644 --- a/src/http/api/resources/block.js +++ b/src/http/api/resources/block.js @@ -48,7 +48,13 @@ exports.get = { }).code(500) } - return reply(block.data) + if (block) { + return reply(block.data) + } + return reply({ + Message: 'Block was unwanted before it could be remotely retrieved', + Code: 0 + }).code(404) }) } } diff --git a/src/http/api/routes/bitswap.js b/src/http/api/routes/bitswap.js index 23c75b9a86..04def2fb91 100644 --- a/src/http/api/routes/bitswap.js +++ b/src/http/api/routes/bitswap.js @@ -9,7 +9,7 @@ module.exports = (server) => { method: '*', path: '/api/v0/bitswap/wantlist', config: { - handler: resources.bitswap.wantlist + handler: resources.bitswap.wantlist.handler } }) @@ -17,7 +17,7 @@ module.exports = (server) => { method: '*', path: '/api/v0/bitswap/stat', config: { - handler: resources.bitswap.stat + handler: resources.bitswap.stat.handler } }) diff --git a/src/http/api/routes/stats.js b/src/http/api/routes/stats.js index 35ec768083..1fe0707c1b 100644 --- a/src/http/api/routes/stats.js +++ b/src/http/api/routes/stats.js @@ -9,7 +9,7 @@ module.exports = (server) => { method: '*', path: '/api/v0/stats/bitswap', config: { - handler: resources.stats.bitswap + handler: resources.stats.bitswap.handler } }) diff --git a/test/cli/bitswap.js b/test/cli/bitswap.js index 6652364f9b..8b6b8eea2a 100644 --- a/test/cli/bitswap.js +++ b/test/cli/bitswap.js @@ -3,17 +3,24 @@ const expect = require('chai').expect const runOn = require('../utils/on-and-off').on +const PeerId = require('peer-id') describe('bitswap', () => runOn((thing) => { let ipfs + let peerId const key = 'QmUBdnXXPyoDFXj3Hj39dNJ5VkN3QFRskXxcGaYFBB8CNR' - before((done) => { + before(function (done) { + this.timeout(60 * 1000) ipfs = thing.ipfs ipfs('block get ' + key) .then(() => {}) .catch(() => {}) - setTimeout(done, 800) + PeerId.create((err, peer) => { + expect(err).to.not.exist() + peerId = peer.toB58String() + done() + }) }) it('wantlist', function () { @@ -23,8 +30,14 @@ describe('bitswap', () => runOn((thing) => { }) }) - // TODO @hacdias fix this with https://github.com/ipfs/js-ipfs/pull/1198 - it.skip('stat', function () { + it('wantlist peerid', function () { + this.timeout(20 * 1000) + return ipfs('bitswap wantlist ' + peerId).then((out) => { + expect(out).to.eql('') + }) + }) + + it('stat', function () { this.timeout(20 * 1000) return ipfs('bitswap stat').then((out) => { @@ -40,4 +53,10 @@ describe('bitswap', () => runOn((thing) => { ].join('\n') + '\n') }) }) + + it('unwant', function () { + return ipfs('bitswap unwant ' + key).then((out) => { + expect(out).to.eql(`Key ${key} removed from wantlist\n`) + }) + }) })) diff --git a/test/core/bitswap.spec.js b/test/core/bitswap.spec.js index d9c9db978c..645581f2c1 100644 --- a/test/core/bitswap.spec.js +++ b/test/core/bitswap.spec.js @@ -18,9 +18,6 @@ const CID = require('cids') const IPFSFactory = require('ipfsd-ctl') -// This gets replaced by '../utils/create-repo-browser.js' in the browser -const createTempRepo = require('../utils/create-repo-nodejs.js') - const IPFS = require('../../src/core') // TODO bitswap tests on windows is failing, missing proper shutdown of daemon @@ -247,75 +244,4 @@ skipOnWindows('bitswap', function () { }) }) }) - - describe('api', () => { - let node - - before(function (done) { - this.timeout(40 * 1000) - - node = new IPFS({ - repo: createTempRepo(), - start: false, - config: { - Addresses: { - Swarm: [] - }, - Discovery: { - MDNS: { - Enabled: false - } - } - } - }) - node.on('ready', () => done()) - }) - - describe('while offline', () => { - it('.wantlist throws if offline', () => { - expect(() => node.bitswap.wantlist()).to.throw(/online/) - }) - - it('.stat gives error while offline', () => { - node.bitswap.stat((err, stats) => { - expect(err).to.exist() - expect(stats).to.not.exist() - }) - }) - - it('throws if offline', () => { - expect(() => node.bitswap.unwant('my key')).to.throw(/online/) - }) - }) - - describe('while online', () => { - before(function (done) { - this.timeout(80 * 1000) - - node.start(() => done()) - }) - - it('.wantlist returns an array of wanted blocks', () => { - expect(node.bitswap.wantlist()).to.eql([]) - }) - - it('returns the stats', (done) => { - node.bitswap.stat((err, stats) => { - expect(err).to.not.exist() - expect(stats).to.have.keys([ - 'blocksReceived', - 'blocksSent', - 'dataReceived', - 'dataSent', - 'wantlist', - 'peers', - 'provideBufLen', - 'dupDataReceived', - 'dupBlksReceived' - ]) - done() - }) - }) - }) - }) }) diff --git a/test/core/interface/bitswap.js b/test/core/interface/bitswap.js new file mode 100644 index 0000000000..b7927dd5dd --- /dev/null +++ b/test/core/interface/bitswap.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ +'use strict' + +const test = require('interface-ipfs-core') +const parallel = require('async/parallel') + +const IPFS = require('../../../src') + +const DaemonFactory = require('ipfsd-ctl') +const df = DaemonFactory.create({ type: 'proc', exec: IPFS }) + +const nodes = [] +const common = { + setup: function (callback) { + callback(null, { + spawnNode: (cb) => { + df.spawn({ + initOptions: { bits: 512 } + }, (err, _ipfsd) => { + if (err) { + return cb(err) + } + + nodes.push(_ipfsd) + cb(null, _ipfsd.api) + }) + } + }) + }, + teardown: function (callback) { + parallel(nodes.map((node) => (cb) => node.stop(cb)), callback) + } +} + +test.bitswap(common) diff --git a/test/core/interface/interface.spec.js b/test/core/interface/interface.spec.js index 356036adeb..6bc0cdbe67 100644 --- a/test/core/interface/interface.spec.js +++ b/test/core/interface/interface.spec.js @@ -15,6 +15,7 @@ describe('interface-ipfs-core tests', () => { require('./stats') require('./key') if (isNode) { + require('./bitswap') require('./swarm') require('./ping') require('./pubsub') diff --git a/test/http-api/index.js b/test/http-api/index.js index 44d48f7f1f..d619014896 100644 --- a/test/http-api/index.js +++ b/test/http-api/index.js @@ -1,11 +1,11 @@ 'use strict' -require('./interface') -require('./inject') require('./block') require('./bootstrap') require('./config') require('./dns') require('./id') +require('./inject') +require('./interface') require('./object') require('./version') diff --git a/test/http-api/interface/bitswap.js b/test/http-api/interface/bitswap.js new file mode 100644 index 0000000000..231a41cfe2 --- /dev/null +++ b/test/http-api/interface/bitswap.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +'use strict' + +const test = require('interface-ipfs-core') +const parallel = require('async/parallel') + +const DaemonFactory = require('ipfsd-ctl') +const df = DaemonFactory.create({ exec: 'src/cli/bin.js' }) + +const nodes = [] +const common = { + setup: function (callback) { + callback(null, { + spawnNode: (cb) => { + df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => { + if (err) { + return cb(err) + } + + nodes.push(_ipfsd) + cb(null, _ipfsd.api) + }) + } + }) + }, + teardown: function (callback) { + parallel(nodes.map((node) => (cb) => node.stop(cb)), callback) + } +} + +test.bitswap(common)