diff --git a/.aegir.js b/.aegir.js index 317aff17..ea7050c5 100644 --- a/.aegir.js +++ b/.aegir.js @@ -1,6 +1,7 @@ 'use strict' module.exports = { + bundlesize: { maxSize: '210kB' }, karma: { files: [{ pattern: 'test/test-data/**/*', diff --git a/.travis.yml b/.travis.yml index d0cf2810..f435392a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,13 @@ language: node_js cache: npm + stages: - check - test - cov node_js: + - '12' - '10' os: @@ -20,7 +22,7 @@ jobs: include: - stage: check script: - - npx aegir commitlint --travis + - npx aegir build --bundlesize - npx aegir dep-check - npm run lint @@ -36,5 +38,17 @@ jobs: firefox: latest script: npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless + - stage: test + name: electron-main + os: osx + script: + - npx aegir test -t electron-main --bail + + - stage: test + name: electron-renderer + os: osx + script: + - npx aegir test -t electron-renderer --bail + notifications: email: false diff --git a/benchmarks/index.js b/benchmarks/index.js index 084dc0aa..12ca6207 100644 --- a/benchmarks/index.js +++ b/benchmarks/index.js @@ -1,110 +1,76 @@ -/* eslint max-nested-callbacks: ["error", 8] */ /* eslint-disable no-console */ 'use strict' -const series = require('async/series') -const parallel = require('async/parallel') -const map = require('async/map') -const mapSeries = require('async/mapSeries') -const each = require('async/each') -const _ = require('lodash') -const Block = require('ipfs-block') const assert = require('assert') -const crypto = require('crypto') -const CID = require('cids') -const multihashing = require('multihashing-async') +const range = require('lodash.range') -const utils = require('../test/utils') +const makeBlock = require('../test/utils/make-block') +const genBitswapNetwork = require('../test/utils/mocks').genBitswapNetwork const nodes = [2, 5, 10, 20] const blockFactors = [1, 10, 100] -console.log('-- start') -mapSeries(nodes, (n, cb) => { - mapSeries(blockFactors, (blockFactor, cb) => { - utils.genBitswapNetwork(n, (err, nodeArr) => { - if (err) { - return cb(err) - } - - round(nodeArr, blockFactor, n, (err) => { - if (err) { - return cb(err) - } - - shutdown(nodeArr, cb) - }) +;(async function () { + console.log('-- start') + await Promise.all( + nodes.map(async nodeCount => { + await Promise.all( + blockFactors.map(async blockFactor => { + const nodeArr = await genBitswapNetwork(nodeCount) + await round(nodeArr, blockFactor, nodeCount) + await shutdown(nodeArr) + }) + ) }) - }, cb) -}, (err) => { - if (err) { - throw err - } + ) + console.log('-- finished') -}) +})() -function shutdown (nodeArr, cb) { - each(nodeArr, (node, cb) => { - node.bitswap.stop() - node.libp2p.stop(cb) - }, cb) +async function shutdown (nodeArr) { + await Promise.all( + nodeArr.map(async node => { + await node.bitswap.stop() + await node.libp2p.stop() + }) + ) } -function round (nodeArr, blockFactor, n, cb) { - createBlocks(n, blockFactor, (err, blocks) => { - if (err) { - return cb(err) - } - const cids = blocks.map((b) => b.cid) - let d - series([ - // put blockFactor amount of blocks per node - (cb) => parallel(_.map(nodeArr, (node, i) => (callback) => { - node.bitswap.start() - - const data = _.map(_.range(blockFactor), (j) => { +async function round (nodeArr, blockFactor, n) { + const blocks = await makeBlock(n * blockFactor) + const cids = blocks.map((b) => b.cid) + + console.info('put blockFactor amount of blocks per node') + + await Promise.all( + nodeArr.map(async (node, i) => { + await node.bitswap.start() + + await Promise.all( + range(blockFactor).map(async j => { const index = i * blockFactor + j - return blocks[index] - }) - each( - data, - (d, cb) => node.bitswap.put(d, cb), - callback - ) - }), cb), - (cb) => { - d = (new Date()).getTime() - cb() - }, - // fetch all blocks on every node - (cb) => parallel(_.map(nodeArr, (node, i) => (callback) => { - map(cids, (cid, cb) => node.bitswap.get(cid, cb), (err, res) => { - if (err) { - return callback(err) - } - - assert(res.length === blocks.length) - callback() + + await node.bitswap.put(blocks[index]) }) - }), cb) - ], (err) => { - if (err) { - return cb(err) - } - console.log(' %s nodes - %s blocks/node - %sms', n, blockFactor, (new Date()).getTime() - d) - cb() + ) }) - }) -} + ) + + console.info('fetch all blocks on every node') -function createBlocks (n, blockFactor, callback) { - map(_.range(n * blockFactor), (i, cb) => { - const data = crypto.randomBytes(n * blockFactor) - multihashing(data, 'sha2-256', (err, hash) => { - if (err) { - return cb(err) + const d = Date.now() + + await Promise.all( + nodeArr.map(async node => { + let count = 0 + + for await (const _ of node.bitswap.getMany(cids)) { // eslint-disable-line no-unused-vars + count++ } - cb(null, new Block(data, new CID(hash))) + + assert(count === blocks.length) }) - }, callback) + ) + + console.log(' %s nodes - %s blocks/node - %sms', n, blockFactor, Date.now() - d) } diff --git a/benchmarks/put-get.js b/benchmarks/put-get.js index 0a3a5cdf..6d02efa5 100644 --- a/benchmarks/put-get.js +++ b/benchmarks/put-get.js @@ -1,47 +1,36 @@ -/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint max-nested-callbacks: ["error", 5] */ /* eslint-disable no-console */ 'use strict' const Benchmark = require('benchmark') -const _ = require('lodash') -const Block = require('ipfs-block') const assert = require('assert') -const series = require('async/series') -const map = require('async/map') -const crypto = require('crypto') -const CID = require('cids') -const multihashing = require('multihashing-async') - -const utils = require('../test/utils') +const all = require('async-iterator-all') +const makeBlock = require('../test/utils/make-block') +const genBitswapNetwork = require('../test/utils/mocks').genBitswapNetwork const suite = new Benchmark.Suite('put-get') const blockCounts = [1, 10, 1000] const blockSizes = [10, 1024, 10 * 1024] -utils.genBitswapNetwork(1, (err, nodes) => { - if (err) { - throw err - } - const node = nodes[0] +;(async function () { + const [ + node + ] = await genBitswapNetwork(1) + const bitswap = node.bitswap blockCounts.forEach((n) => blockSizes.forEach((k) => { - suite.add(`put-get ${n} blocks of size ${k}`, (defer) => { - createBlocks(n, k, (err, blocks) => { - if (err) { - throw err - } - series([ - (cb) => bitswap.putMany(blocks, cb), - (cb) => get(blocks, bitswap, cb) - ], (err) => { - if (err) { - throw err - } - defer.resolve() - }) - }) + suite.add(`put-get ${n} blocks of size ${k}`, async (defer) => { + const blocks = await makeBlock(n, k) + + await bitswap.putMany(blocks) + + const res = await all(bitswap.getMany(blocks.map(block => block.cid))) + + assert(res.length === blocks.length) + + defer.resolve() }, { defer: true }) @@ -57,29 +46,4 @@ utils.genBitswapNetwork(1, (err, nodes) => { .run({ async: true }) -}) - -function createBlocks (n, k, callback) { - map(_.range(n), (i, cb) => { - const data = crypto.randomBytes(k) - multihashing(data, 'sha2-256', (err, hash) => { - if (err) { - return cb(err) - } - cb(null, new Block(data, new CID(hash))) - }) - }, callback) -} - -function get (blocks, bs, callback) { - map(blocks, (b, cb) => { - bs.get(b.cid, cb) - }, (err, res) => { - if (err) { - return callback(err) - } - - assert(res.length === blocks.length) - callback() - }) -} +})() diff --git a/package.json b/package.json index 29dab02c..890135cc 100644 --- a/package.json +++ b/package.json @@ -43,31 +43,35 @@ "homepage": "https://github.com/ipfs/js-ipfs-bitswap#readme", "devDependencies": { "@nodeutils/defaults-deep": "^1.1.0", - "aegir": "^18.2.1", + "aegir": "^20.3.1", + "async-iterator-all": "^1.0.0", "benchmark": "^2.1.4", "chai": "^4.2.0", "dirty-chai": "^2.0.1", - "ipfs-repo": "~0.26.3", - "libp2p": "~0.24.2", - "libp2p-kad-dht": "~0.15.0", - "libp2p-mplex": "~0.8.4", + "ipfs-repo": "^0.28.0", + "libp2p": "^0.26.1", + "libp2p-kad-dht": "^0.16.0", + "libp2p-mplex": "^0.8.0", "libp2p-secio": "~0.11.1", - "libp2p-tcp": "~0.13.0", - "lodash": "^4.17.11", + "libp2p-tcp": "^0.13.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", "lodash.range": "^3.2.0", "lodash.without": "^4.4.0", "ncp": "^2.0.0", + "p-event": "^4.1.0", "peer-book": "~0.9.0", - "peer-id": "~0.12.0", - "peer-info": "~0.15.0", - "rimraf": "^2.6.2", + "peer-id": "^0.12.2", + "peer-info": "~0.15.1", + "promisify-es6": "^1.0.3", + "rimraf": "^3.0.0", "safe-buffer": "^5.1.2", "stats-lite": "^2.2.0", "uuid": "^3.3.2" }, "dependencies": { - "async": "^2.6.1", - "bignumber.js": "^8.0.1", + "bignumber.js": "^9.0.0", + "callbackify": "^1.1.0", "cids": "~0.7.0", "debug": "^4.1.0", "ipfs-block": "~0.8.0", @@ -75,7 +79,7 @@ "lodash.isequalwith": "^4.4.0", "moving-average": "^1.0.0", "multicodec": "~0.5.0", - "multihashing-async": "~0.5.1", + "multihashing-async": "^0.8.0", "protons": "^1.0.1", "pull-length-prefixed": "^1.3.1", "pull-stream": "^3.6.9", diff --git a/src/decision-engine/index.js b/src/decision-engine/index.js index af13eaca..99a4882f 100644 --- a/src/decision-engine/index.js +++ b/src/decision-engine/index.js @@ -1,11 +1,5 @@ 'use strict' -const each = require('async/each') -const eachSeries = require('async/eachSeries') -const waterfall = require('async/waterfall') -const nextTick = require('async/nextTick') - -const map = require('async/map') const debounce = require('just-debounce-it') const Message = require('../types/message') @@ -32,21 +26,22 @@ class DecisionEngine { this._outbox = debounce(this._processTasks.bind(this), 100) } - _sendBlocks (peer, blocks, cb) { - // split into messges of max 512 * 1024 bytes + async _sendBlocks (peer, blocks) { + // split into messages of max 512 * 1024 bytes const total = blocks.reduce((acc, b) => { return acc + b.data.byteLength }, 0) if (total < MAX_MESSAGE_SIZE) { - return this._sendSafeBlocks(peer, blocks, cb) + await this._sendSafeBlocks(peer, blocks) + return } let size = 0 let batch = [] let outstanding = blocks.length - eachSeries(blocks, (b, cb) => { + for (const b of blocks) { outstanding-- batch.push(b) size += b.data.byteLength @@ -57,28 +52,24 @@ class DecisionEngine { size = 0 const nextBatch = batch.slice() batch = [] - this._sendSafeBlocks(peer, nextBatch, (err) => { - if (err) { - this._log('sendblock error: %s', err.message) - } - // not returning the error, so we send as much as we can - // as otherwise `eachSeries` would cancel - cb() - }) - } else { - nextTick(cb) + try { + await this._sendSafeBlocks(peer, nextBatch) + } catch (err) { + // catch the error so as to send as many blocks as we can + this._log('sendblock error: %s', err.message) + } } - }, cb) + } } - _sendSafeBlocks (peer, blocks, cb) { + async _sendSafeBlocks (peer, blocks) { const msg = new Message(false) blocks.forEach((b) => msg.addBlock(b)) - this.network.sendMessage(peer, msg, cb) + await this.network.sendMessage(peer, msg) } - _processTasks () { + async _processTasks () { if (!this._running || !this._tasks.length) { return } @@ -90,35 +81,26 @@ class DecisionEngine { const uniqCids = uniqWith((a, b) => a.equals(b), cids) const groupedTasks = groupBy(task => task.target.toB58String(), tasks) - waterfall([ - (callback) => map(uniqCids, (cid, cb) => { - this.blockstore.get(cid, cb) - }, callback), - (blocks, callback) => each(Object.values(groupedTasks), (tasks, cb) => { - // all tasks have the same target - const peer = tasks[0].target - const blockList = cids.map((cid) => { - return blocks.find(b => b.cid.equals(cid)) - }) + const blocks = await Promise.all(uniqCids.map(cid => this.blockstore.get(cid))) - this._sendBlocks(peer, blockList, (err) => { - if (err) { - // `_sendBlocks` actually doesn't return any errors - this._log.error('should never happen: ', err) - } else { - blockList.forEach((block) => this.messageSent(peer, block)) - } + await Promise.all(Object.values(groupedTasks).map(async (tasks) => { + // all tasks in the group have the same target + const peer = tasks[0].target + const blockList = cids.map((cid) => blocks.find(b => b.cid.equals(cid))) - cb() - }) - }, callback) - ], (err) => { - this._tasks = [] - - if (err) { - this._log.error(err) + try { + await this._sendBlocks(peer, blockList) + } catch (err) { + // `_sendBlocks` actually doesn't return any errors + this._log.error('should never happen: ', err) + return } - }) + for (const block of blockList) { + this.messageSent(peer, block) + } + })) + + this._tasks = [] } wantlistForPeer (peerId) { @@ -170,11 +152,11 @@ class DecisionEngine { } // Handle incoming messages - messageReceived (peerId, msg, cb) { + async messageReceived (peerId, msg) { const ledger = this._findOrCreate(peerId) if (msg.empty) { - return nextTick(cb) + return } // If the message was a full wantlist clear the current one @@ -185,11 +167,11 @@ class DecisionEngine { this._processBlocks(msg.blocks, ledger) if (msg.wantlist.size === 0) { - return nextTick(cb) + return } - let cancels = [] - let wants = [] + const cancels = [] + const wants = [] msg.wantlist.forEach((entry) => { if (entry.cancel) { ledger.cancelWant(entry.cid) @@ -201,7 +183,7 @@ class DecisionEngine { }) this._cancelWants(ledger, peerId, cancels) - this._addWants(ledger, peerId, wants, cb) + await this._addWants(ledger, peerId, wants) } _cancelWants (ledger, peerId, entries) { @@ -214,27 +196,29 @@ class DecisionEngine { }, this._tasks, entries) } - _addWants (ledger, peerId, entries, callback) { - each(entries, (entry, cb) => { + async _addWants (ledger, peerId, entries) { + await Promise.all(entries.map(async (entry) => { // If we already have the block, serve it - this.blockstore.has(entry.cid, (err, exists) => { - if (err) { - this._log.error('failed existence check') - } else if (exists) { - this._tasks.push({ - entry: entry.entry, - target: peerId - }) - } - cb() - }) - }, () => { - this._outbox() - callback() - }) + let exists + try { + exists = await this.blockstore.has(entry.cid) + } catch (err) { + this._log.error('failed blockstore existence check for ' + entry.cid) + return + } + + if (exists) { + this._tasks.push({ + entry: entry.entry, + target: peerId + }) + } + })) + + this._outbox() } - _processBlocks (blocks, ledger, callback) { + _processBlocks (blocks, ledger) { const cids = [] blocks.forEach((b, cidStr) => { this._log('got block (%s bytes)', b.data.length) @@ -287,14 +271,12 @@ class DecisionEngine { return l } - start (callback) { + start () { this._running = true - nextTick(() => callback()) } - stop (callback) { + stop () { this._running = false - nextTick(() => callback()) } } diff --git a/src/index.js b/src/index.js index 6cb299fd..dca53913 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,5 @@ 'use strict' -const waterfall = require('async/waterfall') -const reject = require('async/reject') -const each = require('async/each') -const series = require('async/series') -const map = require('async/map') -const nextTick = require('async/nextTick') - const WantManager = require('./want-manager') const Network = require('./network') const DecisionEngine = require('./decision-engine') @@ -37,6 +30,7 @@ const statsKeys = [ * * @param {Libp2p} libp2p * @param {Blockstore} blockstore + * @param {Object} options */ class Bitswap { constructor (libp2p, blockstore, options) { @@ -71,53 +65,46 @@ class Bitswap { } // handle messages received through the network - _receiveMessage (peerId, incoming, callback) { - this.engine.messageReceived(peerId, incoming, (err) => { - if (err) { - // Only logging the issue to process as much as possible - // of the message. Currently `messageReceived` does not - // return any errors, but this could change in the future. - this._log('failed to receive message', incoming) - } + async _receiveMessage (peerId, incoming) { + try { + await this.engine.messageReceived(peerId, incoming) + } catch (err) { + // Only logging the issue to process as much as possible + // of the message. Currently `messageReceived` does not + // throw any errors, but this could change in the future. + this._log('failed to receive message', incoming) + } - if (incoming.blocks.size === 0) { - return callback() - } + if (incoming.blocks.size === 0) { + return + } - const blocks = Array.from(incoming.blocks.values()) + const blocks = Array.from(incoming.blocks.values()) - // quickly send out cancels, reduces chances of duplicate block receives - const wanted = blocks - .filter((b) => this.wm.wantlist.contains(b.cid)) - .map((b) => b.cid) + // quickly send out cancels, reduces chances of duplicate block receives + const wanted = blocks + .filter((b) => this.wm.wantlist.contains(b.cid)) + .map((b) => b.cid) - this.wm.cancelWants(wanted) + this.wm.cancelWants(wanted) - each( - blocks, - (b, cb) => { - const wasWanted = wanted.includes(b.cid) - this._handleReceivedBlock(peerId, b, wasWanted, cb) - }, - callback - ) - }) + await Promise.all(blocks.map(async (b) => { + const wasWanted = wanted.includes(b.cid) + await this._handleReceivedBlock(peerId, b, wasWanted) + })) } - _handleReceivedBlock (peerId, block, wasWanted, callback) { + async _handleReceivedBlock (peerId, block, wasWanted) { this._log('received block') - waterfall([ - (cb) => this.blockstore.has(block.cid, cb), - (has, cb) => { - this._updateReceiveCounters(peerId.toB58String(), block, has) - if (has || !wasWanted) { - return nextTick(cb) - } + const has = await this.blockstore.has(block.cid) + this._updateReceiveCounters(peerId.toB58String(), block, has) - this._putBlock(block, cb) - } - ], callback) + if (has || !wasWanted) { + return + } + + await this.put(block) } _updateReceiveCounters (peerId, block, exists) { @@ -147,28 +134,16 @@ class Bitswap { this._stats.disconnected(peerId) } - _putBlock (block, callback) { - this.blockstore.put(block, (err) => { - if (err) { - return callback(err) - } - - this.notifications.hasBlock(block) - this.network.provide(block.cid, (err) => { - if (err) { - this._log.error('Failed to provide: %s', err.message) - } - }) - - this.engine.receivedBlocks([block.cid]) - callback() - }) - } - + /** + * @returns {void} + */ enableStats () { this._stats.enable() } + /** + * @returns {void} + */ disableStats () { this._stats.disable() } @@ -177,7 +152,7 @@ class Bitswap { * Return the current wantlist for a given `peerId` * * @param {PeerId} peerId - * @returns {Wantlist} + * @returns {Map} */ wantlistForPeer (peerId) { return this.engine.wantlistForPeer(peerId) @@ -187,7 +162,7 @@ class Bitswap { * Return ledger information for a given `peerId` * * @param {PeerId} peerId - * @returns {?Object} + * @returns {Object} */ ledgerForPeer (peerId) { return this.engine.ledgerForPeer(peerId) @@ -198,90 +173,69 @@ class Bitswap { * blockstore it is returned, otherwise the block is added to the wantlist and returned once another node sends it to us. * * @param {CID} cid - * @param {function(Error, Block)} callback - * @returns {void} + * @returns {Promise} */ - get (cid, callback) { - this.getMany([cid], (err, blocks) => { - if (err) { - return callback(err) - } - - if (blocks && blocks.length > 0) { - callback(null, blocks[0]) - } else { - // when a unwant happens - callback() - } - }) + async get (cid) { + for await (const block of this.getMany([cid])) { + return block + } } /** * Fetch a a list of blocks by cid. If the blocks are in the local * blockstore they are returned, otherwise the blocks are added to the wantlist and returned once another node sends them to us. * - * @param {Array} cids - * @param {function(Error, Blocks)} callback - * @returns {void} + * @param {Iterable} cids + * @returns {Promise>} */ - getMany (cids, callback) { + async * getMany (cids) { let pendingStart = cids.length const wantList = [] let promptedNetwork = false - const getFromOutside = (cid, cb) => { + const fetchFromNetwork = async (cid) => { wantList.push(cid) - this.notifications.wantBlock( - cid, - // called on block receive - (block) => { - this.wm.cancelWants([cid]) - cb(null, block) - }, - // called on unwant - () => { - this.wm.cancelWants([cid]) - cb(null, undefined) - } - ) + const blockP = this.notifications.wantBlock(cid) if (!pendingStart) { this.wm.wantBlocks(wantList) } + + const block = await blockP + this.wm.cancelWants([cid]) + + return block } - map(cids, (cid, cb) => { - waterfall( - [ - (cb) => this.blockstore.has(cid, cb), - (has, cb) => { - pendingStart-- - if (has) { - if (!pendingStart) { - this.wm.wantBlocks(wantList) - } - return this.blockstore.get(cid, cb) - } - - if (!promptedNetwork) { - promptedNetwork = true - this.network.findAndConnect(cids[0], (err) => { - if (err) { - this._log.error(err) - } - }) - } - - // we don't have the block here - getFromOutside(cid, cb) - } - ], - cb) - }, callback) + for (const cid of cids) { + const has = await this.blockstore.has(cid) + pendingStart-- + if (has) { + if (!pendingStart) { + this.wm.wantBlocks(wantList) + } + yield this.blockstore.get(cid) + + continue + } + + if (!promptedNetwork) { + promptedNetwork = true + this.network.findAndConnect(cids[0]).catch((err) => this._log.error(err)) + } + + // we don't have the block locally so fetch it from the network + yield fetchFromNetwork(cid) + } } - // removes the given cids from the wantlist independent of any ref counts + /** + * Removes the given CIDs from the wantlist independent of any ref counts + * + * @param {Iterable} cids + * @returns {void} + */ unwant (cids) { if (!Array.isArray(cids)) { cids = [cids] @@ -291,7 +245,12 @@ class Bitswap { cids.forEach((cid) => this.notifications.unwantBlock(cid)) } - // removes the given keys from the want list + /** + * Removes the given keys from the want list + * + * @param {Iterable} cids + * @returns {void} + */ cancelWants (cids) { if (!Array.isArray(cids)) { cids = [cids] @@ -304,54 +263,38 @@ class Bitswap { * send it to nodes that have it in their wantlist. * * @param {Block} block - * @param {function(Error)} callback - * @returns {void} + * @returns {Promise} */ - put (block, callback) { - this._log('putting block') - - waterfall([ - (cb) => this.blockstore.has(block.cid, cb), - (has, cb) => { - if (has) { - return nextTick(cb) - } - - this._putBlock(block, cb) - } - ], callback) + async put (block) { // eslint-disable-line require-await + return this.putMany([block]) } /** * Put the given blocks to the underlying blockstore and * send it to nodes that have it them their wantlist. * - * @param {Array} blocks - * @param {function(Error)} callback - * @returns {void} + * @param {AsyncIterable|Iterable} blocks + * @returns {Promise} */ - putMany (blocks, callback) { - waterfall([ - (cb) => reject(blocks, (b, cb) => { - this.blockstore.has(b.cid, cb) - }, cb), - (newBlocks, cb) => this.blockstore.putMany(newBlocks, (err) => { - if (err) { - return cb(err) + async putMany (blocks) { // eslint-disable-line require-await + const self = this + + return this.blockstore.putMany(async function * () { + for await (const block of blocks) { + if (await self.blockstore.has(block.cid)) { + continue } - newBlocks.forEach((block) => { - this.notifications.hasBlock(block) - this.engine.receivedBlocks([block.cid]) - this.network.provide(block.cid, (err) => { - if (err) { - this._log.error('Failed to provide: %s', err.message) - } - }) + yield block + + self.notifications.hasBlock(block) + self.engine.receivedBlocks([block.cid]) + // Note: Don't wait for provide to finish before returning + self.network.provide(block.cid).catch((err) => { + self._log.error('Failed to provide: %s', err.message) }) - cb() - }) - ], callback) + } + }()) } /** @@ -366,7 +309,7 @@ class Bitswap { /** * Get the current list of partners. * - * @returns {Array} + * @returns {Iterator} */ peers () { return this.engine.peers() @@ -384,32 +327,24 @@ class Bitswap { /** * Start the bitswap node. * - * @param {function(Error)} callback - * * @returns {void} */ - start (callback) { - series([ - (cb) => this.wm.start(cb), - (cb) => this.network.start(cb), - (cb) => this.engine.start(cb) - ], callback) + start () { + this.wm.start() + this.network.start() + this.engine.start() } /** * Stop the bitswap node. * - * @param {function(Error)} callback - * * @returns {void} */ - stop (callback) { + stop () { this._stats.stop() - series([ - (cb) => this.wm.stop(cb), - (cb) => this.network.stop(cb), - (cb) => this.engine.stop(cb) - ], callback) + this.wm.stop() + this.network.stop() + this.engine.stop() } } diff --git a/src/network.js b/src/network.js index 37ea7cb9..a3ef9561 100644 --- a/src/network.js +++ b/src/network.js @@ -2,9 +2,7 @@ const lp = require('pull-length-prefixed') const pull = require('pull-stream') -const waterfall = require('async/waterfall') -const each = require('async/each') -const nextTick = require('async/nextTick') +const callbackify = require('callbackify') const Message = require('./types/message') const CONSTANTS = require('./constants') @@ -25,7 +23,7 @@ class Network { this._running = false } - start (callback) { + start () { this._running = true // bind event listeners this._onPeerConnect = this._onPeerConnect.bind(this) @@ -43,11 +41,9 @@ class Network { .getAllArray() .filter((peer) => peer.isConnected()) .forEach((peer) => this._onPeerConnect((peer))) - - nextTick(() => callback()) } - stop (callback) { + stop () { this._running = false this.libp2p.unhandle(BITSWAP100) @@ -55,8 +51,6 @@ class Network { this.libp2p.removeListener('peer:connect', this._onPeerConnect) this.libp2p.removeListener('peer:disconnect', this._onPeerDisconnect) - - nextTick(() => callback()) } // Handles both types of bitswap messgages @@ -67,13 +61,14 @@ class Network { pull( conn, lp.decode(), - pull.asyncMap((data, cb) => Message.deserialize(data, cb)), + pull.asyncMap((data, cb) => callbackify(Message.deserialize)(data, cb)), pull.asyncMap((msg, cb) => { conn.getPeerInfo((err, peerInfo) => { - if (err) { return cb(err) } + if (err) { + return cb(err) + } - // this._log('data from', peerInfo.id.toB58String()) - this.bitswap._receiveMessage(peerInfo.id, msg, cb) + callbackify(this.bitswap._receiveMessage.bind(this.bitswap))(peerInfo.id, msg, cb) }) }), pull.onEnd((err) => { @@ -97,89 +92,101 @@ class Network { this.bitswap._onPeerDisconnected(peerInfo.id) } - findProviders (cid, maxProviders, callback) { - this.libp2p.contentRouting.findProviders(cid, { - maxTimeout: CONSTANTS.providerRequestTimeout, - maxNumProviders: maxProviders - }, callback) + /** + * Find providers given a `cid`. + * + * @param {CID} cid + * @param {number} maxProviders + * @returns {Promise>} + */ + findProviders (cid, maxProviders) { + return this.libp2p.contentRouting.findProviders( + cid, + { + maxTimeout: CONSTANTS.providerRequestTimeout, + maxNumProviders: maxProviders + } + ) } - findAndConnect (cid, callback) { - waterfall([ - (cb) => this.findProviders(cid, CONSTANTS.maxProvidersPerRequest, cb), - (provs, cb) => { - this._log('connecting to providers', provs.map((p) => p.id.toB58String())) - each(provs, (p, cb) => this.connectTo(p, cb)) - } - ], callback) + /** + * Find the providers of a given `cid` and connect to them. + * + * @param {CID} cid + * @returns {void} + */ + async findAndConnect (cid) { + const provs = await this.findProviders(cid, CONSTANTS.maxProvidersPerRequest) + this._log('connecting to providers', provs.map((p) => p.id.toB58String())) + await Promise.all(provs.map((p) => this.connectTo(p))) } - provide (cid, callback) { - this.libp2p.contentRouting.provide(cid, callback) + async provide (cid) { + await this.libp2p.contentRouting.provide(cid) } // Connect to the given peer // Send the given msg (instance of Message) to the given peer - sendMessage (peer, msg, callback) { - if (!this._running) { return callback(new Error(`network isn't running`)) } + async sendMessage (peer, msg) { + if (!this._running) throw new Error('network isn\'t running') const stringId = peer.toB58String() ? peer.toB58String() : peer.id.toB58String() this._log('sendMessage to %s', stringId, msg) - this._dialPeer(peer, (err, conn, protocol) => { - if (err) { - return callback(err) - } + const { conn, protocol } = await this._dialPeer(peer) + + let serialized + switch (protocol) { + case BITSWAP100: + serialized = msg.serializeToBitswap100() + break + case BITSWAP110: + serialized = msg.serializeToBitswap110() + break + default: + throw new Error('Unknown protocol: ' + protocol) + } - let serialized - switch (protocol) { - case BITSWAP100: - serialized = msg.serializeToBitswap100() - break - case BITSWAP110: - serialized = msg.serializeToBitswap110() - break - default: - return callback(new Error('Unkown protocol: ' + protocol)) - } - // TODO: why doesn't the error get propageted back?? - writeMessage(conn, serialized, (err) => { - if (err) { - this._log.error(err) - } - }) - callback() - this._updateSentStats(peer, msg.blocks) - }) + // Note: Don't wait for writeMessage() to complete + writeMessage(conn, serialized, this._log) + + this._updateSentStats(peer, msg.blocks) } - connectTo (peer, callback) { - if (!this._running) { return callback(new Error(`network isn't running`)) } + /** + * Connects to another peer + * + * @param {PeerInfo|PeerId|Multiaddr} peer + * @returns {Promise.} + */ + async connectTo (peer) { // eslint-disable-line require-await + if (!this._running) { + throw new Error('network isn\'t running') + } - this.libp2p.dial(peer, callback) + return this.libp2p.dial(peer) } // Dial to the peer and try to use the most recent Bitswap - _dialPeer (peer, callback) { - // Attempt Bitswap 1.1.0 - this.libp2p.dialProtocol(peer, BITSWAP110, (err, conn) => { - if (err) { - // Attempt Bitswap 1.0.0 - this.libp2p.dialProtocol(peer, BITSWAP100, (err, conn) => { - if (err) { return callback(err) } - - callback(null, conn, BITSWAP100) - }) - - return + async _dialPeer (peer) { + try { + // Attempt Bitswap 1.1.0 + return { + conn: await this.libp2p.dialProtocol(peer, BITSWAP110), + protocol: BITSWAP110 } - - callback(null, conn, BITSWAP110) - }) + } catch (err) { + // Attempt Bitswap 1.0.0 + return { + conn: await this.libp2p.dialProtocol(peer, BITSWAP100), + protocol: BITSWAP100 + } + } } _updateSentStats (peer, blocks) { const peerId = peer.toB58String() + if (this._stats) { blocks.forEach((block) => this._stats.push(peerId, 'dataSent', block.data.length)) this._stats.push(peerId, 'blocksSent', blocks.size) @@ -187,12 +194,16 @@ class Network { } } -function writeMessage (conn, msg, callback) { +function writeMessage (conn, msg, log) { pull( pull.values([msg]), lp.encode(), - conn, - pull.onEnd(callback) + conn.conn, + pull.onEnd((err) => { + if (err) { + log(err) + } + }) ) } diff --git a/src/notifications.js b/src/notifications.js index 46ef4270..f6024cfd 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -43,35 +43,37 @@ class Notifications extends EventEmitter { /** * Signal the system that we are waiting to receive the * block associated with the given `cid`. + * Returns a Promise that resolves to the block when it is received, + * or undefined when the block is unwanted. * * @param {CID} cid - * @param {function(Block)} onBlock - called when the block is received - * @param {function()} onUnwant - called when the block is unwanted - * @returns {void} + * @returns {Promise} */ - wantBlock (cid, onBlock, onUnwant) { + wantBlock (cid) { const cidStr = cid.toString('base58btc') this._log(`wantBlock:${cidStr}`) - this._unwantListeners[cidStr] = () => { - this._log(`manual unwant: ${cidStr}`) - this._cleanup(cidStr) - onUnwant() - } + return new Promise((resolve, reject) => { + this._unwantListeners[cidStr] = () => { + this._log(`manual unwant: ${cidStr}`) + this._cleanup(cidStr) + resolve() + } - this._blockListeners[cidStr] = (block) => { - this._cleanup(cidStr) - onBlock(block) - } + this._blockListeners[cidStr] = (block) => { + this._cleanup(cidStr) + resolve(block) + } - this.once( - unwantEvent(cidStr), - this._unwantListeners[cidStr] - ) - this.once( - blockEvent(cidStr), - this._blockListeners[cidStr] - ) + this.once( + unwantEvent(cidStr), + this._unwantListeners[cidStr] + ) + this.once( + blockEvent(cidStr), + this._blockListeners[cidStr] + ) + }) } /** diff --git a/src/stats/index.js b/src/stats/index.js index b364784d..a7a9e90f 100644 --- a/src/stats/index.js +++ b/src/stats/index.js @@ -50,7 +50,7 @@ class Stats extends EventEmitter { stop () { this._enabled = false this._global.stop() - for (let peerStat of this._peers) { + for (const peerStat of this._peers) { peerStat[1].stop() } } @@ -67,6 +67,7 @@ class Stats extends EventEmitter { if (peerId.toB58String) { peerId = peerId.toB58String() } + return this._peers.get(peerId) } diff --git a/src/stats/stat.js b/src/stats/stat.js index 8a3d2203..15e146f7 100644 --- a/src/stats/stat.js +++ b/src/stats/stat.js @@ -74,6 +74,7 @@ class Stats extends EventEmitter { _update () { this._timeout = null + if (this._queue.length) { let last while (this._queue.length) { @@ -125,7 +126,7 @@ class Stats extends EventEmitter { let n - if (!this._stats.hasOwnProperty(key)) { + if (!Object.prototype.hasOwnProperty.call(this._stats, key)) { n = this._stats[key] = Big(0) } else { n = this._stats[key] diff --git a/src/types/message/entry.js b/src/types/message/entry.js index 0c9f3fd6..04ae9192 100644 --- a/src/types/message/entry.js +++ b/src/types/message/entry.js @@ -1,12 +1,9 @@ 'use strict' const WantlistEntry = require('../wantlist').Entry -const CID = require('cids') -const assert = require('assert') module.exports = class BitswapMessageEntry { constructor (cid, priority, cancel) { - assert(CID.isCID(cid), 'needs valid cid') this.entry = new WantlistEntry(cid, priority) this.cancel = Boolean(cancel) } diff --git a/src/types/message/index.js b/src/types/message/index.js index 6cf79240..b28f5de5 100644 --- a/src/types/message/index.js +++ b/src/types/message/index.js @@ -3,9 +3,6 @@ const protons = require('protons') const Block = require('ipfs-block') const isEqualWith = require('lodash.isequalwith') -const assert = require('assert') -const each = require('async/each') -const nextTick = require('async/nextTick') const CID = require('cids') const codecName = require('multicodec/src/name-table') const vd = require('varint-decoder') @@ -27,7 +24,6 @@ class BitswapMessage { } addEntry (cid, priority, cancel) { - assert(cid && CID.isCID(cid), 'must be a valid cid') const cidStr = cid.toString('base58btc') const entry = this.wantlist.get(cidStr) @@ -41,13 +37,11 @@ class BitswapMessage { } addBlock (block) { - assert(Block.isBlock(block), 'must be a valid cid') const cidStr = block.cid.toString('base58btc') this.blocks.set(cidStr, block) } cancel (cid) { - assert(CID.isCID(cid), 'must be a valid cid') const cidStr = cid.toString('base58btc') this.wantlist.delete(cidStr) this.addEntry(cid, 0, true) @@ -135,13 +129,8 @@ class BitswapMessage { } } -BitswapMessage.deserialize = (raw, callback) => { - let decoded - try { - decoded = pbm.Message.decode(raw) - } catch (err) { - return nextTick(() => callback(err)) - } +BitswapMessage.deserialize = async (raw) => { + const decoded = pbm.Message.decode(raw) const isFull = (decoded.wantlist && decoded.wantlist.full) || false const msg = new BitswapMessage(isFull) @@ -149,12 +138,7 @@ BitswapMessage.deserialize = (raw, callback) => { if (decoded.wantlist) { decoded.wantlist.entries.forEach((entry) => { // note: entry.block is the CID here - let cid - try { - cid = new CID(entry.block) - } catch (err) { - return callback(err) - } + const cid = new CID(entry.block) msg.addEntry(cid, entry.priority, entry.cancel) }) } @@ -162,63 +146,33 @@ BitswapMessage.deserialize = (raw, callback) => { // Bitswap 1.0.0 // decoded.blocks are just the byte arrays if (decoded.blocks.length > 0) { - return each(decoded.blocks, (b, cb) => { - multihashing(b, 'sha2-256', (err, hash) => { - if (err) { - return cb(err) - } - let cid - try { - cid = new CID(hash) - } catch (err) { - return callback(err) - } - msg.addBlock(new Block(b, cid)) - cb() - }) - }, (err) => { - if (err) { - return callback(err) - } - callback(null, msg) - }) + await Promise.all(decoded.blocks.map(async (b) => { + const hash = await multihashing(b, 'sha2-256') + const cid = new CID(hash) + msg.addBlock(new Block(b, cid)) + })) + return msg } // Bitswap 1.1.0 if (decoded.payload.length > 0) { - return each(decoded.payload, (p, cb) => { + await Promise.all(decoded.payload.map(async (p) => { if (!p.prefix || !p.data) { - return nextTick(cb) + return } const values = vd(p.prefix) const cidVersion = values[0] const multicodec = values[1] const hashAlg = values[2] // const hashLen = values[3] // We haven't need to use this so far - multihashing(p.data, hashAlg, (err, hash) => { - if (err) { - return cb(err) - } - - let cid - try { - cid = new CID(cidVersion, codecName[multicodec.toString('16')], hash) - } catch (err) { - return cb(err) - } - - msg.addBlock(new Block(p.data, cid)) - cb() - }) - }, (err) => { - if (err) { - return callback(err) - } - callback(null, msg) - }) + const hash = await multihashing(p.data, hashAlg) + const cid = new CID(cidVersion, codecName[multicodec.toString('16')], hash) + msg.addBlock(new Block(p.data, cid)) + })) + return msg } - callback(null, msg) + return msg } BitswapMessage.Entry = Entry diff --git a/src/types/wantlist/entry.js b/src/types/wantlist/entry.js index ef99de56..a8519dca 100644 --- a/src/types/wantlist/entry.js +++ b/src/types/wantlist/entry.js @@ -1,12 +1,7 @@ 'use strict' -const assert = require('assert') -const CID = require('cids') - class WantListEntry { constructor (cid, priority) { - assert(CID.isCID(cid), 'must be valid CID') - // Keep track of how many requests we have for this key this._refCounter = 1 diff --git a/src/want-manager/index.js b/src/want-manager/index.js index bbb4391a..d5b4a7dd 100644 --- a/src/want-manager/index.js +++ b/src/want-manager/index.js @@ -1,6 +1,5 @@ 'use strict' -const nextTick = require('async/nextTick') const Message = require('../types/message') const Wantlist = require('../types/wantlist') const CONSTANTS = require('../constants') @@ -39,7 +38,7 @@ module.exports = class WantManager { }) // broadcast changes - for (let p of this.peers.values()) { + for (const p of this.peers.values()) { p.addEntries(entries) } } @@ -57,7 +56,7 @@ module.exports = class WantManager { // new peer, give them the full wantlist const fullwantlist = new Message(true) - for (let entry of this.wantlist.entries()) { + for (const entry of this.wantlist.entries()) { fullwantlist.addEntry(entry[1].cid, entry[1].priority) } @@ -112,7 +111,7 @@ module.exports = class WantManager { this._stopPeerHandler(peerId) } - start (callback) { + start () { // resend entire wantlist every so often this.timer = setInterval(() => { this._log('resend full-wantlist') @@ -123,14 +122,11 @@ module.exports = class WantManager { this.peers.forEach((p) => p.addMessage(fullwantlist)) }, 60 * 1000) - - nextTick(() => callback()) } - stop (callback) { + stop () { this.peers.forEach((mq) => this.disconnected(mq.peerId)) clearInterval(this.timer) - nextTick(() => callback()) } } diff --git a/src/want-manager/msg-queue.js b/src/want-manager/msg-queue.js index 62abd05f..2423be04 100644 --- a/src/want-manager/msg-queue.js +++ b/src/want-manager/msg-queue.js @@ -46,19 +46,19 @@ module.exports = class MsgQueue { this.addMessage(msg) } - send (msg) { - this.network.connectTo(this.peerId, (err) => { - if (err) { - this._log.error('cant connect to peer %s: %s', this.peerId.toB58String(), err.message) - return - } + async send (msg) { + try { + await this.network.connectTo(this.peerId) + } catch (err) { + this._log.error('cant connect to peer %s: %s', this.peerId.toB58String(), err.message) + return + } + + this._log('sending message to peer %s', this.peerId.toB58String()) - this._log('sending message') - this.network.sendMessage(this.peerId, msg, (err) => { - if (err) { - this._log.error('send error: %s', err.message) - } - }) + // Note: Don't wait for sendMessage() to complete + this.network.sendMessage(this.peerId, msg).catch((err) => { + this._log.error('send error: %s', err.message) }) } } diff --git a/test/benchmarks/get-many.js b/test/benchmarks/get-many.js index 670e192b..46c7871f 100644 --- a/test/benchmarks/get-many.js +++ b/test/benchmarks/get-many.js @@ -4,11 +4,14 @@ const distributionTest = require('../utils/distribution-test') const print = require('./helpers/print-swarm-results') +const EventEmitter = require('events') -print('10 nodes, 10 blocks, 5 iterations', distributionTest(10, 10, 5, (err) => { - if (err) { - throw err - } +;(async function () { + const emitter = new EventEmitter() + + print('10 nodes, 10 blocks, 5 iterations', emitter) + + await distributionTest(10, 10, 5, emitter) console.log('Finished. Can kill now...') -})) +})() diff --git a/test/bitswap-mock-internals.js b/test/bitswap-mock-internals.js index 0f291440..b4827edb 100644 --- a/test/bitswap-mock-internals.js +++ b/test/bitswap-mock-internals.js @@ -1,18 +1,14 @@ /* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint max-nested-callbacks: ["error", 5] */ 'use strict' -const eachSeries = require('async/eachSeries') -const waterfall = require('async/waterfall') -const map = require('async/map') -const parallel = require('async/parallel') -const setImmediate = require('async/setImmediate') -const _ = require('lodash') +const range = require('lodash.range') const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect const PeerId = require('peer-id') - +const promisify = require('promisify-es6') +const all = require('async-iterator-all') const Message = require('../src/types/message') const Bitswap = require('../src') @@ -22,6 +18,7 @@ const applyNetwork = require('./utils/mocks').applyNetwork const mockLibp2pNode = require('./utils/mocks').mockLibp2pNode const storeHasBlocks = require('./utils/store-has-blocks') const makeBlock = require('./utils/make-block') +const makePeerId = require('./utils/make-peer-id') const orderedFinish = require('./utils/helpers').orderedFinish describe('bitswap with mocks', function () { @@ -31,249 +28,189 @@ describe('bitswap with mocks', function () { let blocks let ids - before((done) => { - parallel([ - (cb) => createTempRepo(cb), - (cb) => map(_.range(15), (i, cb) => makeBlock(cb), cb), - (cb) => map(_.range(2), (i, cb) => PeerId.create({ bits: 512 }, cb), cb) - ], (err, results) => { - if (err) { - return done(err) - } - - repo = results[0] - blocks = results[1] - ids = results[2] - - done() - }) + before(async () => { + repo = await createTempRepo() + blocks = await makeBlock(15) + ids = await makePeerId(2) }) - after((done) => { - repo.teardown(done) - }) + after(() => repo.teardown()) describe('receive message', () => { - it('simple block message', (done) => { + it('simple block message', async () => { const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - bs.start((err) => { - expect(err).to.not.exist() + bs.start() - const other = ids[1] + const other = ids[1] - const b1 = blocks[0] - const b2 = blocks[1] + const b1 = blocks[0] + const b2 = blocks[1] - bs.wm.wantBlocks([b1.cid, b2.cid]) + bs.wm.wantBlocks([b1.cid, b2.cid]) - const msg = new Message(false) - msg.addBlock(b1) - msg.addBlock(b2) - - bs._receiveMessage(other, msg, (err) => { - expect(err).to.not.exist() - - map([b1.cid, b2.cid], (cid, cb) => repo.blocks.get(cid, cb), (err, blocks) => { - expect(err).to.not.exist() - - expect(blocks[0].data).to.eql(b1.data) - expect(blocks[1].data).to.eql(b2.data) - - const ledger = bs.ledgerForPeer(other) - expect(ledger.peer).to.equal(other.toPrint()) - expect(ledger.value).to.equal(0) - expect(ledger.sent).to.equal(0) - expect(ledger.recv).to.equal(96) - expect(ledger.exchanged).to.equal(2) - done() - }) - }) - }) + const msg = new Message(false) + msg.addBlock(b1) + msg.addBlock(b2) + + await bs._receiveMessage(other, msg) + + const blks = await Promise.all([ + b1.cid, b2.cid + ].map((cid) => repo.blocks.get(cid))) + + expect(blks[0].data).to.eql(b1.data) + expect(blks[1].data).to.eql(b2.data) + + const ledger = bs.ledgerForPeer(other) + expect(ledger.peer).to.equal(other.toPrint()) + expect(ledger.value).to.equal(0) + expect(ledger.sent).to.equal(0) + expect(ledger.recv).to.equal(96) + expect(ledger.exchanged).to.equal(2) }) - it('simple want message', (done) => { + it('simple want message', async () => { const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - bs.start((err) => { - expect(err).to.not.exist() - const other = ids[1] - const b1 = blocks[0] - const b2 = blocks[1] + bs.start() - const msg = new Message(false) + const other = ids[1] + const b1 = blocks[0] + const b2 = blocks[1] - msg.addEntry(b1.cid, 1, false) - msg.addEntry(b2.cid, 1, false) + const msg = new Message(false) - bs._receiveMessage(other, msg, (err) => { - expect(err).to.not.exist() + msg.addEntry(b1.cid, 1, false) + msg.addEntry(b2.cid, 1, false) - const wl = bs.wantlistForPeer(other) + await bs._receiveMessage(other, msg) - expect(wl.has(b1.cid.toString('base58btc'))).to.eql(true) - expect(wl.has(b2.cid.toString('base58btc'))).to.eql(true) + const wl = bs.wantlistForPeer(other) - done() - }) - }) + expect(wl.has(b1.cid.toString('base58btc'))).to.eql(true) + expect(wl.has(b2.cid.toString('base58btc'))).to.eql(true) }) - it('multi peer', function (done) { + it('multi peer', async function () { this.timeout(80 * 1000) const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - let others - let blocks + bs.start() - bs.start((err) => { - expect(err).to.not.exist() + const others = await makePeerId(5) + const blocks = await makeBlock(10) - parallel([ - (cb) => map(_.range(5), (i, cb) => PeerId.create({ bits: 512 }, cb), cb), - (cb) => map(_.range(10), (i, cb) => makeBlock(cb), cb) - ], (err, results) => { - expect(err).to.not.exist() - - others = results[0] - blocks = results[1] - test() - }) - - function test () { - map(_.range(5), (i, cb) => { - const msg = new Message(false) - msg.addBlock(blocks[i]) - msg.addBlock(blocks[i + 5]) - cb(null, msg) - }, (err, messages) => { - expect(err).to.not.exist() - let i = 0 - eachSeries(others, (other, cb) => { - const msg = messages[i] - i++ - - const cids = [...msg.blocks.values()].map(b => b.cid) - bs.wm.wantBlocks(cids) - - bs._receiveMessage(other, msg, (err) => { - expect(err).to.not.exist() - storeHasBlocks(msg, repo.blocks, cb) - }) - }, done) - }) - } - }) + const messages = await Promise.all(range(5).map((i) => { + const msg = new Message(false) + msg.addBlock(blocks[i]) + msg.addBlock(blocks[i + 5]) + return msg + })) + + let i = 0 + for (const other of others) { + const msg = messages[i] + i++ + + const cids = [...msg.blocks.values()].map(b => b.cid) + bs.wm.wantBlocks(cids) + + await bs._receiveMessage(other, msg) + await storeHasBlocks(msg, repo.blocks) + } }) - it('ignore unwanted blocks', (done) => { + it('ignore unwanted blocks', async () => { const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - bs.start((err) => { - expect(err).to.not.exist() + bs.start() - const other = ids[1] + const other = ids[1] - const b1 = blocks[2] - const b2 = blocks[3] - const b3 = blocks[4] + const b1 = blocks[2] + const b2 = blocks[3] + const b3 = blocks[4] - bs.wm.wantBlocks([b2.cid]) + bs.wm.wantBlocks([b2.cid]) - const msg = new Message(false) - msg.addBlock(b1) - msg.addBlock(b2) - msg.addBlock(b3) - - bs._receiveMessage(other, msg, (err) => { - expect(err).to.not.exist() - - map([b1.cid, b2.cid, b3.cid], (cid, cb) => repo.blocks.has(cid, cb), (err, res) => { - expect(err).to.not.exist() - - expect(res).to.eql([false, true, false]) - - const ledger = bs.ledgerForPeer(other) - expect(ledger.peer).to.equal(other.toPrint()) - expect(ledger.value).to.equal(0) - - // Note: Keeping track of received bytes for blocks affects the - // debt ratio, which in future may be used as part of fairness - // algorithms when prioritizing who to send blocks to. - // So we may want to revise whether we record received blocks from - // a peer even if we didn't ask for the blocks. - // For now keeping it liks this to match the go implementation: - // https://github.com/ipfs/go-bitswap/blob/acc22c283722c15436120ae522c8e8021d0b06f8/bitswap.go#L293 - expect(ledger.sent).to.equal(0) - expect(ledger.recv).to.equal(144) - expect(ledger.exchanged).to.equal(3) - done() - }) - }) - }) + const msg = new Message(false) + msg.addBlock(b1) + msg.addBlock(b2) + msg.addBlock(b3) + + await bs._receiveMessage(other, msg) + + const res = await Promise.all([b1.cid, b2.cid, b3.cid].map((cid) => repo.blocks.has(cid))) + expect(res).to.eql([false, true, false]) + + const ledger = bs.ledgerForPeer(other) + expect(ledger.peer).to.equal(other.toPrint()) + expect(ledger.value).to.equal(0) + + // Note: Keeping track of received bytes for blocks affects the + // debt ratio, which in future may be used as part of fairness + // algorithms when prioritizing who to send blocks to. + // So we may want to revise whether we record received blocks from + // a peer even if we didn't ask for the blocks. + // For now keeping it liks this to match the go implementation: + // https://github.com/ipfs/go-bitswap/blob/acc22c283722c15436120ae522c8e8021d0b06f8/bitswap.go#L293 + expect(ledger.sent).to.equal(0) + expect(ledger.recv).to.equal(144) + expect(ledger.exchanged).to.equal(3) }) }) describe('get', () => { - it('fails on requesting empty block', (done) => { + it('fails on requesting empty block', async () => { const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - bs.get(null, (err, res) => { + try { + await bs.get(null) + } catch (err) { expect(err).to.exist() expect(err.message).to.equal('Not a valid cid') - done() - }) + } }) - it('block exists locally', (done) => { + it('block exists locally', async () => { const block = blocks[4] + await repo.blocks.put(block) + const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - repo.blocks.put(block, (err) => { - expect(err).to.not.exist() - const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - - bs.get(block.cid, (err, res) => { - expect(err).to.not.exist() - expect(res).to.eql(block) - done() - }) - }) + const retrievedBlock = await bs.get(block.cid) + expect(retrievedBlock).to.eql(block) }) - it('blocks exist locally', (done) => { + it('blocks exist locally', async () => { const b1 = blocks[3] const b2 = blocks[14] const b3 = blocks[13] - repo.blocks.putMany([b1, b2, b3], (err) => { - expect(err).to.not.exist() + await repo.blocks.putMany([b1, b2, b3]) + const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - const bs = new Bitswap(mockLibp2pNode(), repo.blocks) + const retrievedBlocks = await all(bs.getMany([b1.cid, b2.cid, b3.cid])) - bs.getMany([b1.cid, b2.cid, b3.cid], (err, res) => { - expect(err).to.not.exist() - expect(res).to.be.eql([b1, b2, b3]) - done() - }) - }) + expect(retrievedBlocks).to.be.eql([b1, b2, b3]) }) - it('getMany', (done) => { + it('getMany', async () => { const b1 = blocks[5] const b2 = blocks[6] const b3 = blocks[7] - repo.blocks.putMany([b1, b2, b3], (err) => { - expect(err).to.not.exist() + await repo.blocks.putMany([b1, b2, b3]) + const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - const bs = new Bitswap(mockLibp2pNode(), repo.blocks) + const block1 = await bs.get(b1.cid) + expect(block1).to.eql(b1) - map([b1.cid, b2.cid, b3.cid], (cid, cb) => bs.get(cid, cb), (err, res) => { - expect(err).to.not.exist() - expect(res).to.eql([b1, b2, b3]) - done() - }) - }) + const block2 = await bs.get(b2.cid) + expect(block2).to.eql(b2) + + const block3 = await bs.get(b3.cid) + expect(block3).to.eql(b3) }) - it('block is added locally afterwards', (done) => { - const finish = orderedFinish(2, done) + it('block is added locally afterwards', async () => { + const finish = orderedFinish(2) const block = blocks[9] const bs = new Bitswap(mockLibp2pNode(), repo.blocks) const net = mockNetwork() @@ -281,175 +218,148 @@ describe('bitswap with mocks', function () { bs.network = net bs.wm.network = net bs.engine.network = net - bs.start((err) => { - expect(err).to.not.exist() - bs.get(block.cid, (err, res) => { - expect(err).to.not.exist() - expect(res).to.eql(block) - finish(2) - }) - - setTimeout(() => { - finish(1) - bs.put(block, () => {}) - }, 200) - }) + bs.start() + const get = bs.get(block.cid) + + setTimeout(() => { + finish(1) + bs.put(block, () => {}) + }, 200) + + const res = await get + expect(res).to.eql(block) + finish(2) + + finish.assert() }) - it('block is sent after local add', (done) => { + it('block is sent after local add', async () => { const me = ids[0] const other = ids[1] const block = blocks[10] - let bs1 - let bs2 const n1 = { - connectTo (id, cb) { - let err + connectTo (id) { if (id.toHexString() !== other.toHexString()) { - err = new Error('unknown peer') + throw new Error('unknown peer') } - setImmediate(() => cb(err)) + + return Promise.resolve() }, - sendMessage (id, msg, cb) { + sendMessage (id, msg) { if (id.toHexString() === other.toHexString()) { - bs2._receiveMessage(me, msg, cb) - } else { - setImmediate(() => cb(new Error('unkown peer'))) + return bs2._receiveMessage(me, msg) } + throw new Error('unkown peer') }, - start (callback) { - setImmediate(() => callback()) + start () { + return Promise.resolve() }, - stop (callback) { - setImmediate(() => callback()) + stop () { + return Promise.resolve() }, - findAndConnect (cid, callback) { - setImmediate(() => callback()) + findAndConnect (cid) { + return Promise.resolve() }, - provide (cid, callback) { - setImmediate(() => callback()) + provide (cid) { + return Promise.resolve() } } const n2 = { - connectTo (id, cb) { - let err + connectTo (id) { if (id.toHexString() !== me.toHexString()) { - err = new Error('unkown peer') + throw new Error('unknown peer') } - setImmediate(() => cb(err)) + + return Promise.resolve() }, - sendMessage (id, msg, cb) { + sendMessage (id, msg) { if (id.toHexString() === me.toHexString()) { - bs1._receiveMessage(other, msg, cb) - } else { - setImmediate(() => cb(new Error('unkown peer'))) + return bs1._receiveMessage(other, msg) } + throw new Error('unkown peer') }, - start (callback) { - setImmediate(() => callback()) + start () { + return Promise.resolve() }, - stop (callback) { - setImmediate(() => callback()) + stop () { + return Promise.resolve() }, - findAndConnect (cid, callback) { - setImmediate(() => callback()) + findAndConnect (cid) { + return Promise.resolve() }, - provide (cid, callback) { - setImmediate(() => callback()) + provide (cid) { + return Promise.resolve() } } - bs1 = new Bitswap(mockLibp2pNode(), repo.blocks) + + // Create and start bs1 + const bs1 = new Bitswap(mockLibp2pNode(), repo.blocks) applyNetwork(bs1, n1) + bs1.start() - bs1.start((err) => { - expect(err).to.not.exist() + // Create and start bs2 + const repo2 = await createTempRepo() + const bs2 = new Bitswap(mockLibp2pNode(), repo2.blocks) + applyNetwork(bs2, n2) + bs2.start() - let repo2 - - waterfall([ - (cb) => createTempRepo(cb), - (repo, cb) => { - repo2 = repo - bs2 = new Bitswap(mockLibp2pNode(), repo2.blocks) - applyNetwork(bs2, n2) - bs2.start((err) => { - expect(err).to.not.exist() - - bs1._onPeerConnected(other) - bs2._onPeerConnected(me) - - bs1.get(block.cid, (err, res) => { - expect(err).to.not.exist() - cb(null, res) - }) - setTimeout(() => bs2.put(block, () => {}), 1000) - }) - }, - (res, cb) => { - expect(res).to.eql(block) - cb() - } - ], done) - }) + bs1._onPeerConnected(other) + bs2._onPeerConnected(me) + + const p1 = bs1.get(block.cid) + setTimeout(() => { + bs2.put(block, () => {}) + }, 1000) + + const b1 = await p1 + expect(b1).to.eql(block) }) - it('double get', (done) => { + it('double get', async () => { const block = blocks[11] const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - parallel( - [ - (cb) => bs.get(block.cid, cb), - (cb) => bs.get(block.cid, cb) - ], - (err, res) => { - expect(err).to.not.exist() - expect(res[0]).to.eql(block) - expect(res[1]).to.eql(block) - done() - } - ) + const resP = Promise.all([ + bs.get(block.cid), + bs.get(block.cid) + ]) bs.put(block, (err) => { expect(err).to.not.exist() }) + + const res = await resP + expect(res[0]).to.eql(block) + expect(res[1]).to.eql(block) }) }) describe('unwant', () => { - it('removes blocks that are wanted multiple times', (done) => { + it('removes blocks that are wanted multiple times', async () => { const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - bs.start((err) => { - expect(err).to.not.exist() - const b = blocks[12] - - let counter = 0 - const check = (err, res) => { - expect(err).to.not.exist() - expect(res).to.not.exist() + bs.start() - if (++counter === 2) { done() } - } + const b = blocks[12] + const p = Promise.all([ + bs.get(b.cid), + bs.get(b.cid) + ]) - bs.get(b.cid, check) - bs.get(b.cid, check) + setTimeout(() => bs.unwant(b.cid), 10) - setTimeout(() => bs.unwant(b.cid), 10) - }) + const res = await p + expect(res[1]).to.not.exist() }) }) describe('ledgerForPeer', () => { - it('returns null for unknown peer', (done) => { + it('returns null for unknown peer', async () => { const bs = new Bitswap(mockLibp2pNode(), repo.blocks) - PeerId.create({ bits: 512 }, (err, id) => { - expect(err).to.not.exist() - const ledger = bs.ledgerForPeer(id) - expect(ledger).to.equal(null) - done() - }) + const id = await promisify(PeerId.create)({ bits: 512 }) + const ledger = bs.ledgerForPeer(id) + expect(ledger).to.equal(null) }) }) }) diff --git a/test/bitswap-stats.js b/test/bitswap-stats.js index 5e0a8521..c30751e0 100644 --- a/test/bitswap-stats.js +++ b/test/bitswap-stats.js @@ -1,23 +1,17 @@ /* eslint-env mocha */ 'use strict' -const map = require('async/map') -const each = require('async/each') -const eachOf = require('async/eachOf') -const parallel = require('async/parallel') -const _ = require('lodash') const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect -const PeerId = require('peer-id') - +const pEvent = require('p-event') const Message = require('../src/types/message') const Bitswap = require('../src') const createTempRepo = require('./utils/create-temp-repo-nodejs') const createLibp2pNode = require('./utils/create-libp2p-node') const makeBlock = require('./utils/make-block') -const countToFinish = require('./utils/helpers').countToFinish +const makePeerId = require('./utils/make-peer-id') const expectedStats = [ 'blocksReceived', @@ -37,7 +31,6 @@ const expectedTimeWindows = [ ] describe('bitswap stats', () => { - const nodes = [0, 1] let libp2pNodes let repos let bitswaps @@ -45,59 +38,44 @@ describe('bitswap stats', () => { let blocks let ids - before((done) => { - parallel({ - blocks: (cb) => map(_.range(2), (i, cb) => makeBlock(cb), cb), - ids: (cb) => map(_.range(2), (i, cb) => PeerId.create({ bits: 512 }, cb), cb) - }, - (err, results) => { - expect(err).to.not.exist() - - blocks = results.blocks - ids = results.ids - done() - }) - }) + before(async () => { + const nodes = [0, 1] + blocks = await makeBlock(2) + ids = await makePeerId(2) - before((done) => { // create 2 temp repos - map(nodes, (n, cb) => createTempRepo(cb), (err, _repos) => { - expect(err).to.not.exist() - repos = _repos - done() - }) - }) + repos = await Promise.all(nodes.map(() => createTempRepo())) - before((done) => { // create 2 libp2p nodes - map(nodes, (n, cb) => createLibp2pNode({ - DHT: repos[n].datastore - }, cb), (err, _libp2pNodes) => { - expect(err).to.not.exist() + libp2pNodes = await Promise.all(nodes.map((i) => createLibp2pNode({ + DHT: repos[i].datastore + }))) - libp2pNodes = _libp2pNodes - done() - }) - }) - - before(() => { - bitswaps = nodes.map((node, i) => - new Bitswap(libp2pNodes[i], repos[i].blocks, { + // create bitswaps + bitswaps = libp2pNodes.map((node, i) => + new Bitswap(node, repos[i].blocks, { statsEnabled: true, statsComputeThrottleTimeout: 500 // fast update interval for tests - })) + }) + ) bs = bitswaps[0] bs.wm.wantBlocks(blocks.map(b => b.cid)) - }) - - // start the first bitswap - before((done) => bs.start(done)) - after((done) => each(bitswaps, (bs, cb) => bs.stop(cb), done)) - - after((done) => each(repos, (repo, cb) => repo.teardown(cb), done)) + // start the first bitswap + bs.start() + }) - after((done) => each(libp2pNodes, (n, cb) => n.stop(cb), done)) + after(async () => { + await Promise.all( + bitswaps.map((bs) => bs.stop()) + ) + await Promise.all( + repos.map(repo => repo.teardown()) + ) + await Promise.all( + libp2pNodes.map((n) => n.stop()) + ) + }) it('has initial stats', () => { const stats = bs.stat() @@ -157,9 +135,7 @@ describe('bitswap stats', () => { const msg = new Message(false) blocks.forEach((block) => msg.addBlock(block)) - bs._receiveMessage(other, msg, (err) => { - expect(err).to.not.exist() - }) + bs._receiveMessage(other, msg) }) it('updates duplicate blocks counters', (done) => { @@ -179,86 +155,79 @@ describe('bitswap stats', () => { const msg = new Message(false) blocks.forEach((block) => msg.addBlock(block)) - bs._receiveMessage(other, msg, (err) => { - expect(err).to.not.exist() - }) + bs._receiveMessage(other, msg) }) describe('connected to another bitswap', () => { let bs2 let block - before((done) => { - eachOf( - libp2pNodes, - (node, i, cb) => node.dial(libp2pNodes[(i + 1) % nodes.length].peerInfo, cb), - done) - }) + before(async () => { + await Promise.all([ + libp2pNodes[0].dial(libp2pNodes[1].peerInfo), + libp2pNodes[1].dial(libp2pNodes[0].peerInfo) + ]) - before((done) => { bs2 = bitswaps[1] - bs2.start(done) - }) + bs2.start() - after((done) => { - bs2.stop(done) - }) + block = await makeBlock() - before((done) => { - makeBlock((err, _block) => { - expect(err).to.not.exist() - expect(_block).to.exist() - block = _block - done() - }) + await bs.put(block) }) - before((done) => { - bs.put(block, done) + after(() => { + bs2.stop() }) - it('updates stats on transfer', (done) => { - const finish = countToFinish(2, done) - bs.stat().once('update', (stats) => { - expect(stats.blocksReceived.eq(4)).to.be.true() - expect(stats.dataReceived.eq(192)).to.be.true() - expect(stats.dupBlksReceived.eq(2)).to.be.true() - expect(stats.dupDataReceived.eq(96)).to.be.true() - expect(stats.blocksSent.eq(1)).to.be.true() - expect(stats.dataSent.eq(48)).to.be.true() - expect(stats.providesBufferLength.eq(0)).to.be.true() - expect(stats.wantListLength.eq(0)).to.be.true() - expect(stats.peerCount.eq(2)).to.be.true() - finish() - }) + it('updates stats on transfer', async () => { + const originalStats = bs.stat().snapshot - bs2.get(block.cid, (err, block) => { - expect(err).to.not.exist() - expect(block).to.exist() - finish() - }) + expect(originalStats.blocksReceived.toNumber()).to.equal(4) + expect(originalStats.dataReceived.toNumber()).to.equal(192) + expect(originalStats.dupBlksReceived.toNumber()).to.equal(2) + expect(originalStats.dupDataReceived.toNumber()).to.equal(96) + expect(originalStats.blocksSent.toNumber()).to.equal(0) + expect(originalStats.dataSent.toNumber()).to.equal(0) + expect(originalStats.providesBufferLength.toNumber()).to.equal(0) + expect(originalStats.wantListLength.toNumber()).to.equal(0) + expect(originalStats.peerCount.toNumber()).to.equal(1) + + // pull block from bs to bs2 + await bs2.get(block.cid) + + const nextStats = await pEvent(bs.stat(), 'update') + + expect(nextStats.blocksReceived.toNumber()).to.equal(4) + expect(nextStats.dataReceived.toNumber()).to.equal(192) + expect(nextStats.dupBlksReceived.toNumber()).to.equal(2) + expect(nextStats.dupDataReceived.toNumber()).to.equal(96) + expect(nextStats.blocksSent.toNumber()).to.equal(1) + expect(nextStats.dataSent.toNumber()).to.equal(48) + expect(nextStats.providesBufferLength.toNumber()).to.equal(0) + expect(nextStats.wantListLength.toNumber()).to.equal(0) + expect(nextStats.peerCount.toNumber()).to.equal(2) }) - it('has peer stats', (done) => { - const peerIds = libp2pNodes.map((node) => node.peerInfo.id.toB58String()) - const peerStats = bs2.stat().forPeer(peerIds[0]) - peerStats.once('update', (stats) => { - expect(stats.blocksReceived.eq(1)).to.be.true() - expect(stats.dataReceived.eq(48)).to.be.true() - expect(stats.dupBlksReceived.eq(0)).to.be.true() - expect(stats.dupDataReceived.eq(0)).to.be.true() - expect(stats.blocksSent.eq(0)).to.be.true() - expect(stats.dataSent.eq(0)).to.be.true() - expect(stats.providesBufferLength.eq(0)).to.be.true() - expect(stats.wantListLength.eq(0)).to.be.true() - expect(stats.peerCount.eq(1)).to.be.true() - - const ma = peerStats.movingAverages.dataReceived[60000] - expect(ma.movingAverage()).to.be.above(0) - expect(ma.variance()).to.be.above(0) + it('has peer stats', async () => { + const peerStats = bs2.stat().forPeer(libp2pNodes[0].peerInfo.id) + expect(peerStats).to.exist() - done() - }) + const stats = await pEvent(peerStats, 'update') + + expect(stats.blocksReceived.toNumber()).to.equal(1) + expect(stats.dataReceived.toNumber()).to.equal(48) + expect(stats.dupBlksReceived.toNumber()).to.equal(0) + expect(stats.dupDataReceived.toNumber()).to.equal(0) + expect(stats.blocksSent.toNumber()).to.equal(0) + expect(stats.dataSent.toNumber()).to.equal(0) + expect(stats.providesBufferLength.toNumber()).to.equal(0) + expect(stats.wantListLength.toNumber()).to.equal(0) + expect(stats.peerCount.toNumber()).to.equal(1) + + const ma = peerStats.movingAverages.dataReceived[60000] + expect(ma.movingAverage()).to.be.above(0) + expect(ma.variance()).to.be.above(0) }) }) }) diff --git a/test/bitswap.js b/test/bitswap.js index ec1b52f2..511b30da 100644 --- a/test/bitswap.js +++ b/test/bitswap.js @@ -1,12 +1,6 @@ /* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 8] */ 'use strict' -const waterfall = require('async/waterfall') -const series = require('async/series') -const each = require('async/each') -const parallel = require('async/parallel') - const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect @@ -19,27 +13,14 @@ const makeBlock = require('./utils/make-block') const orderedFinish = require('./utils/helpers').orderedFinish // Creates a repo + libp2pNode + Bitswap with or without DHT -function createThing (dht, callback) { - waterfall([ - (cb) => createTempRepo(cb), - (repo, cb) => { - createLibp2pNode({ - DHT: dht - }, (err, node) => cb(err, repo, node)) - }, - (repo, libp2pNode, cb) => { - const bitswap = new Bitswap(libp2pNode, repo.blocks) - bitswap.start((err) => cb(err, repo, libp2pNode, bitswap)) - } - ], (err, repo, libp2pNode, bitswap) => { - expect(err).to.not.exist() - - callback(null, { - repo: repo, - libp2pNode: libp2pNode, - bitswap: bitswap - }) +async function createThing (dht) { + const repo = await createTempRepo() + const libp2pNode = await createLibp2pNode({ + DHT: dht }) + const bitswap = new Bitswap(libp2pNode, repo.blocks) + bitswap.start() + return { repo, libp2pNode, bitswap } } describe('bitswap without DHT', function () { @@ -47,55 +28,46 @@ describe('bitswap without DHT', function () { let nodes - before((done) => { - parallel([ - (cb) => createThing(false, cb), - (cb) => createThing(false, cb), - (cb) => createThing(false, cb) - ], (err, results) => { - expect(err).to.not.exist() - expect(results).to.have.length(3) - nodes = results - done() - }) + before(async () => { + nodes = await Promise.all([ + createThing(false), + createThing(false), + createThing(false) + ]) + + // connect 0 -> 1 && 1 -> 2 + await Promise.all([ + nodes[0].libp2pNode.dial(nodes[1].libp2pNode.peerInfo), + nodes[1].libp2pNode.dial(nodes[2].libp2pNode.peerInfo) + ]) }) - after((done) => { - each(nodes, (node, cb) => { - series([ - (cb) => node.bitswap.stop(cb), - (cb) => node.libp2pNode.stop(cb), - (cb) => node.repo.teardown(cb) - ], cb) - }, done) + after(async () => { + await Promise.all(nodes.map((node) => Promise.all([ + node.bitswap.stop(), + node.libp2pNode.stop(), + node.repo.teardown() + ]))) }) - it('connect 0 -> 1 && 1 -> 2', (done) => { - parallel([ - (cb) => nodes[0].libp2pNode.dial(nodes[1].libp2pNode.peerInfo, cb), - (cb) => nodes[1].libp2pNode.dial(nodes[2].libp2pNode.peerInfo, cb) - ], done) - }) + it('put a block in 2, fail to get it in 0', async () => { + const finish = orderedFinish(2) + + const block = await makeBlock() + await nodes[2].bitswap.put(block) + + const node0Get = nodes[0].bitswap.get(block.cid) - it('put a block in 2, fail to get it in 0', (done) => { - const finish = orderedFinish(2, done) - - waterfall([ - (cb) => makeBlock(cb), - (block, cb) => nodes[2].bitswap.put(block, () => cb(null, block)) - ], (err, block) => { - expect(err).to.not.exist() - nodes[0].bitswap.get(block.cid, (err, block) => { - expect(err).to.not.exist() - expect(block).to.not.exist() - finish(2) - }) - - setTimeout(() => { - finish(1) - nodes[0].bitswap.unwant(block.cid) - }, 200) - }) + setTimeout(() => { + finish(1) + nodes[0].bitswap.unwant(block.cid) + }, 200) + + const b = await node0Get + expect(b).to.not.exist() + finish(2) + + finish.assert() }) }) @@ -104,46 +76,36 @@ describe('bitswap with DHT', function () { let nodes - before((done) => { - parallel([ - (cb) => createThing(true, cb), - (cb) => createThing(true, cb), - (cb) => createThing(true, cb) - ], (err, results) => { - expect(err).to.not.exist() - expect(results).to.have.length(3) - nodes = results - done() - }) + before(async () => { + nodes = await Promise.all([ + createThing(true), + createThing(true), + createThing(true) + ]) + + // connect 0 -> 1 && 1 -> 2 + await Promise.all([ + nodes[0].libp2pNode.dial(nodes[1].libp2pNode.peerInfo), + nodes[1].libp2pNode.dial(nodes[2].libp2pNode.peerInfo) + ]) }) - after((done) => { - each(nodes, (node, cb) => { - series([ - (cb) => node.bitswap.stop(cb), - (cb) => node.libp2pNode.stop(cb), - (cb) => node.repo.teardown(cb) - ], cb) - }, done) + after(async () => { + await Promise.all(nodes.map((node) => Promise.all([ + node.bitswap.stop(), + node.libp2pNode.stop(), + node.repo.teardown() + ]))) }) - it('connect 0 -> 1 && 1 -> 2', (done) => { - parallel([ - (cb) => nodes[0].libp2pNode.dial(nodes[1].libp2pNode.peerInfo, cb), - (cb) => nodes[1].libp2pNode.dial(nodes[2].libp2pNode.peerInfo, cb) - ], done) - }) + it('put a block in 2, get it in 0', async () => { + const block = await makeBlock() + nodes[2].bitswap.put(block) + + await nodes[2].bitswap.put(block) - it('put a block in 2, get it in 0', function (done) { - waterfall([ - (cb) => makeBlock(cb), - (block, cb) => nodes[2].bitswap.put(block, () => cb(null, block)), - (block, cb) => nodes[0].bitswap.get(block.cid, (err, blockRetrieved) => { - expect(err).to.not.exist() - expect(block.data).to.eql(blockRetrieved.data) - expect(block.cid).to.eql(blockRetrieved.cid) - cb() - }) - ], done) + const blockRetrieved = await nodes[0].bitswap.get(block.cid) + expect(block.data).to.eql(blockRetrieved.data) + expect(block.cid).to.eql(blockRetrieved.cid) }) }) diff --git a/test/decision-engine/decision-engine.js b/test/decision-engine/decision-engine.js index 7850f46f..2e036cd2 100644 --- a/test/decision-engine/decision-engine.js +++ b/test/decision-engine/decision-engine.js @@ -1,4 +1,3 @@ -/* eslint max-nested-callbacks: ["error", 8] */ /* eslint-env mocha */ 'use strict' @@ -6,17 +5,14 @@ const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect const PeerId = require('peer-id') -const _ = require('lodash') +const range = require('lodash.range') +const difference = require('lodash.difference') +const flatten = require('lodash.flatten') const Block = require('ipfs-block') -const parallel = require('async/parallel') -const series = require('async/series') -const map = require('async/map') -const each = require('async/each') -const waterfall = require('async/waterfall') -const eachSeries = require('async/eachSeries') const CID = require('cids') const multihashing = require('multihashing-async') const Buffer = require('safe-buffer').Buffer +const promisify = require('promisify-es6') const Message = require('../../src/types/message') const DecisionEngine = require('../../src/decision-engine') @@ -30,92 +26,76 @@ function messageToString (m) { } function stringifyMessages (messages) { - return _.flatten(messages.map(messageToString)) + return flatten(messages.map(messageToString)) } -function newEngine (network, callback) { - parallel([ - (cb) => createTempRepo(cb), - (cb) => PeerId.create({ bits: 512 }, cb) - ], (err, results) => { - if (err) { - return callback(err) - } - const blockstore = results[0].blocks - const peerId = results[1] - const engine = new DecisionEngine(peerId, blockstore, network || mockNetwork()) - engine.start((err) => callback(err, { peer: peerId, engine: engine })) - }) +async function newEngine (network) { + const results = await Promise.all([ + createTempRepo(), + promisify(PeerId.create)({ bits: 512 }) + ]) + const blockstore = results[0].blocks + const peerId = results[1] + const engine = new DecisionEngine(peerId, blockstore, network || mockNetwork()) + engine.start() + return { peer: peerId, engine: engine } } describe('Engine', () => { - it('consistent accounting', (done) => { - parallel([ - (cb) => newEngine(false, cb), - (cb) => newEngine(false, cb) - ], (err, res) => { - expect(err).to.not.exist() - - const sender = res[0] - const receiver = res[1] - - map(_.range(1000), (i, cb) => { - const data = Buffer.from(`this is message ${i}`) - multihashing(data, 'sha2-256', (err, hash) => { - expect(err).to.not.exist() - - const m = new Message(false) - const block = new Block(data, new CID(hash)) - m.addBlock(block) - sender.engine.messageSent(receiver.peer, block) - receiver.engine.messageReceived(sender.peer, m, cb) - }) - }, (err) => { - expect(err).to.not.exist() - expect(sender.engine.numBytesSentTo(receiver.peer)) - .to.be.above(0) + it('consistent accounting', async () => { + const res = await Promise.all([ + newEngine(false), + newEngine(false) + ]) - expect(sender.engine.numBytesSentTo(receiver.peer)) - .to.eql(receiver.engine.numBytesReceivedFrom(sender.peer)) + const sender = res[0] + const receiver = res[1] - expect(receiver.engine.numBytesSentTo(sender.peer)) - .to.eql(0) + await Promise.all(range(1000).map(async (i) => { + const data = Buffer.from(`this is message ${i}`) + const hash = await multihashing(data, 'sha2-256') - expect(sender.engine.numBytesReceivedFrom(receiver.peer)) - .to.eql(0) + const m = new Message(false) + const block = new Block(data, new CID(hash)) + m.addBlock(block) + sender.engine.messageSent(receiver.peer, block) + await receiver.engine.messageReceived(sender.peer, m) + })) - done() - }) - }) - }) + expect(sender.engine.numBytesSentTo(receiver.peer)) + .to.be.above(0) - it('peer is added to peers when message receiver or sent', (done) => { - parallel([ - (cb) => newEngine(false, cb), - (cb) => newEngine(false, cb) - ], (err, res) => { - expect(err).to.not.exist() + expect(sender.engine.numBytesSentTo(receiver.peer)) + .to.eql(receiver.engine.numBytesReceivedFrom(sender.peer)) - const sanfrancisco = res[0] - const seattle = res[1] + expect(receiver.engine.numBytesSentTo(sender.peer)) + .to.eql(0) - const m = new Message(true) - sanfrancisco.engine.messageSent(seattle.peer) + expect(sender.engine.numBytesReceivedFrom(receiver.peer)) + .to.eql(0) + }) - seattle.engine.messageReceived(sanfrancisco.peer, m, (err) => { - expect(err).to.not.exist() + it('peer is added to peers when message received or sent', async () => { + const res = await Promise.all([ + newEngine(false), + newEngine(false) + ]) - expect(seattle.peer.toHexString()) - .to.not.eql(sanfrancisco.peer.toHexString()) - expect(sanfrancisco.engine.peers()).to.include(seattle.peer) - expect(seattle.engine.peers()).to.include(sanfrancisco.peer) + const sanfrancisco = res[0] + const seattle = res[1] - done() - }) - }) + const m = new Message(true) + sanfrancisco.engine.messageSent(seattle.peer) + + await seattle.engine.messageReceived(sanfrancisco.peer, m) + + expect(seattle.peer.toHexString()) + .to.not.eql(sanfrancisco.peer.toHexString()) + expect(sanfrancisco.engine.peers()).to.include(seattle.peer) + expect(seattle.engine.peers()).to.include(sanfrancisco.peer) }) - it('partner wants then cancels', function (done) { + it('partner wants then cancels', async function () { this.timeout(40 * 1000) const numRounds = 10 @@ -123,130 +103,99 @@ describe('Engine', () => { const vowels = 'aeiou'.split('') const testCases = [ [alphabet, vowels], - [alphabet, _.difference(alphabet, vowels)] + [alphabet, difference(alphabet, vowels)] ] - function partnerWants (dEngine, values, partner, cb) { + async function partnerWants (dEngine, values, partner) { const message = new Message(false) - map(values, (v, cb) => multihashing(Buffer.from(v), 'sha2-256', cb), (err, hashes) => { - expect(err).to.not.exist() - hashes.forEach((hash, i) => { - message.addEntry(new CID(hash), Math.pow(2, 32) - 1 - i) - }) - - dEngine.messageReceived(partner, message, cb) + const hashes = await Promise.all(values.map((v) => multihashing(Buffer.from(v), 'sha2-256'))) + hashes.forEach((hash, i) => { + message.addEntry(new CID(hash), Math.pow(2, 32) - 1 - i) }) + await dEngine.messageReceived(partner, message) } - function partnerCancels (dEngine, values, partner, cb) { + async function partnerCancels (dEngine, values, partner, cb) { const message = new Message(false) - map(values, (v, cb) => multihashing(Buffer.from(v), 'sha2-256', cb), (err, hashes) => { - expect(err).to.not.exist() - hashes.forEach((hash) => { - message.cancel(new CID(hash)) - }) - dEngine.messageReceived(partner, message, cb) + const hashes = await Promise.all(values.map((v) => multihashing(Buffer.from(v), 'sha2-256'))) + hashes.forEach((hash) => { + message.cancel(new CID(hash)) }) + await dEngine.messageReceived(partner, message, cb) } - createTempRepo((err, repo) => { - expect(err).to.not.exist() - - waterfall([ - (cb) => map(alphabet, - (v, cb) => multihashing(Buffer.from(v), 'sha2-256', cb), - cb - ), - (hashes, cb) => each( - hashes.map((h, i) => { - return new Block(Buffer.from(alphabet[i]), new CID(h)) - }), - (b, cb) => repo.blocks.put(b, cb), - cb - ), - (cb) => eachSeries(_.range(numRounds), (i, cb) => { - // 2 test cases - // a) want alphabet - cancel vowels - // b) want alphabet - cancels everything except vowels - - eachSeries(testCases, (testcase, innerCb) => { - const set = testcase[0] - const cancels = testcase[1] - const keeps = _.difference(set, cancels) - - const network = mockNetwork(1, (res) => { - const msgs = stringifyMessages(res.messages) - expect(msgs.sort()).to.eql(keeps.sort()) - innerCb() - }) - - PeerId.create({ bits: 512 }, (err, id) => { - expect(err).to.not.exist() - const dEngine = new DecisionEngine(id, repo.blocks, network) - dEngine.start((err) => { - expect(err).to.not.exist() - - let partner - series([ - (cb) => PeerId.create({ bits: 512 }, (err, id) => { - if (err) { return cb(err) } - partner = id - cb() - }), - (cb) => partnerWants(dEngine, set, partner, cb), - (cb) => partnerCancels(dEngine, cancels, partner, cb) - ], (err) => { - expect(err).to.not.exist() - }) - }) - }) - }, cb) - }, cb) - ], done) - }) + const repo = await createTempRepo() + + const hashes = await Promise.all(alphabet.map(v => multihashing(Buffer.from(v), 'sha2-256'))) + const blocks = hashes.map((h, i) => new Block(Buffer.from(alphabet[i]), new CID(h))) + await Promise.all(blocks.map(b => repo.blocks.put(b))) + + for (let i = 0; i < numRounds; i++) { + // 2 test cases + // a) want alphabet - cancel vowels + // b) want alphabet - cancels everything except vowels + + for (const testcase of testCases) { + const set = testcase[0] + const cancels = testcase[1] + const keeps = difference(set, cancels) + + const network = mockNetwork(1, (res) => { + const msgs = stringifyMessages(res.messages) + expect(msgs.sort()).to.eql(keeps.sort()) + }) + + const id = await promisify(PeerId.create)({ bits: 512 }) + const dEngine = new DecisionEngine(id, repo.blocks, network) + dEngine.start() + + const partner = await promisify(PeerId.create)({ bits: 512 }) + await partnerWants(dEngine, set, partner) + await partnerCancels(dEngine, cancels, partner) + } + } }) - it('splits large block messages', (done) => { - const data = _.range(10).map((i) => { + it('splits large block messages', () => { + const data = range(10).map((i) => { const b = Buffer.alloc(1024 * 256) b.fill(i) return b }) - const net = mockNetwork(5, (res) => { - res.messages.forEach((message) => { - // The batch size is big enough to hold two blocks, so every - // message should contain two blocks - expect(message[1].blocks.size).to.eql(2) + return new Promise((resolve, reject) => { + const net = mockNetwork(5, (res) => { + res.messages.forEach((message) => { + // The batch size is big enough to hold two blocks, so every + // message should contain two blocks + expect(message[1].blocks.size).to.eql(2) + }) + resolve() }) - done() - }) - parallel([ - (cb) => newEngine(net, cb), - (cb) => map(data, (d, cb) => multihashing(d, 'sha2-256', (err, hash) => { - expect(err).to.not.exist() - cb(null, new Block(d, new CID(hash))) - }), cb) - ], (err, res) => { - expect(err).to.not.exist() - const sf = res[0].engine - const id = res[0].peer - - const blocks = res[1] - const cids = blocks.map((b) => b.cid) - - each(blocks, (b, cb) => sf.blockstore.put(b, cb), (err) => { - expect(err).to.not.exist() - const msg = new Message(false) - cids.forEach((c, i) => msg.addEntry(c, Math.pow(2, 32) - 1 - i)) - - sf.messageReceived(id, msg, (err) => { - expect(err).to.not.exist() + Promise.all([ + newEngine(net), + Promise.all(data.map(async (d) => { + const hash = await multihashing(d, 'sha2-256') + return new Block(d, new CID(hash)) + })) + ]) + .then(async (res) => { + const sf = res[0].engine + const id = res[0].peer + + const blocks = res[1] + const cids = blocks.map((b) => b.cid) + + await Promise.all((blocks.map((b) => sf.blockstore.put(b)))) + const msg = new Message(false) + cids.forEach((c, i) => msg.addEntry(c, Math.pow(2, 32) - 1 - i)) + + sf.messageReceived(id, msg) }) - }) + .catch(reject) }) }) }) diff --git a/test/decision-engine/ledger.spec.js b/test/decision-engine/ledger.spec.js index 149bbb87..da04a010 100644 --- a/test/decision-engine/ledger.spec.js +++ b/test/decision-engine/ledger.spec.js @@ -5,6 +5,7 @@ const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect const PeerId = require('peer-id') +const promisify = require('promisify-es6') const Ledger = require('../../src/decision-engine/ledger') @@ -12,15 +13,8 @@ describe('Ledger', () => { let peerId let ledger - before((done) => { - PeerId.create({ bits: 512 }, (err, _peerId) => { - if (err) { - return done(err) - } - - peerId = _peerId - done() - }) + before(async () => { + peerId = await promisify(PeerId.create)({ bits: 512 }) }) beforeEach(() => { diff --git a/test/network/gen-bitswap-network.node.js b/test/network/gen-bitswap-network.node.js index 2cde6492..d7a10d1d 100644 --- a/test/network/gen-bitswap-network.node.js +++ b/test/network/gen-bitswap-network.node.js @@ -1,4 +1,3 @@ -/* eslint max-nested-callbacks: ["error", 8] */ /* eslint-env mocha */ /* eslint-disable no-console */ 'use strict' @@ -6,16 +5,12 @@ const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect -const series = require('async/series') -const parallel = require('async/parallel') -const map = require('async/map') -const each = require('async/each') -const _ = require('lodash') const Block = require('ipfs-block') const Buffer = require('safe-buffer').Buffer const crypto = require('crypto') const CID = require('cids') const multihashing = require('multihashing-async') +const range = require('lodash.range') const genBitswapNetwork = require('../utils/mocks').genBitswapNetwork @@ -23,135 +18,88 @@ describe('gen Bitswap network', function () { // CI is very slow this.timeout(300 * 1000) - it('retrieves local blocks', (done) => { - genBitswapNetwork(1, (err, nodes) => { - expect(err).to.not.exist() - - const node = nodes[0] - let blocks - - series([ - (cb) => map(_.range(100), (k, cb) => { - const b = Buffer.alloc(1024) - b.fill(k) - multihashing(b, 'sha2-256', (err, hash) => { - expect(err).to.not.exist() - const cid = new CID(hash) - cb(null, new Block(b, cid)) - }) - }, (err, _blocks) => { - expect(err).to.not.exist() - blocks = _blocks - cb() - }), - (cb) => each( - blocks, - (b, cb) => node.bitswap.put(b, cb), - cb - ), - (cb) => map(_.range(100), (i, cb) => { - node.bitswap.get(blocks[i].cid, cb) - }, (err, res) => { - expect(err).to.not.exist() - expect(res).to.have.length(blocks.length) - cb() - }) - ], (err) => { - expect(err).to.not.exist() - node.bitswap.stop() - node.libp2p.stop(done) - }) - }) + it('retrieves local blocks', async () => { + const nodes = await genBitswapNetwork(1) + + const node = nodes[0] + const blocks = await Promise.all(range(100).map(async (k) => { + const b = Buffer.alloc(1024) + b.fill(k) + const hash = await multihashing(b, 'sha2-256') + const cid = new CID(hash) + return new Block(b, cid) + })) + + await Promise.all(blocks.map(b => node.bitswap.put(b))) + const res = await Promise.all(range(100).map((i) => { + return node.bitswap.get(blocks[i].cid) + })) + expect(res).to.have.length(blocks.length) + + node.bitswap.stop() + await node.libp2p.stop() }) describe('distributed blocks', () => { - it('with 2 nodes', (done) => { + it('with 2 nodes', async () => { const n = 2 - genBitswapNetwork(n, (err, nodeArr) => { - expect(err).to.not.exist() - nodeArr.forEach((node) => { - expect( - Object.keys(node.libp2p._switch.conns) - ).to.be.empty() - - // Parallel dials may result in 1 or 2 connections - // depending on when they're executed. - expect( - Object.keys(node.libp2p._switch.connection.getAll()) - ).to.have.a.lengthOf.at.least(n - 1) - }) - - // -- actual test - round(nodeArr, n, (err) => { - if (err) { - return done(err) - } - - each(nodeArr, (node, cb) => { - node.bitswap.stop() - node.libp2p.stop(cb) - }, done) - }) + const nodeArr = await genBitswapNetwork(n) + + nodeArr.forEach((node) => { + expect( + Object.keys(node.libp2p._switch.conns) + ).to.be.empty() + + // Parallel dials may result in 1 or 2 connections + // depending on when they're executed. + expect( + Object.keys(node.libp2p._switch.connection.getAll()) + ).to.have.a.lengthOf.at.least(n - 1) }) + + // -- actual test + await round(nodeArr, n) + await Promise.all(nodeArr.map(node => { + node.bitswap.stop() + return node.libp2p.stop() + })) }) }) }) -function round (nodeArr, n, cb) { +async function round (nodeArr, n) { const blockFactor = 10 - createBlocks(n, blockFactor, (err, blocks) => { - if (err) { - return cb(err) - } - const cids = blocks.map((b) => b.cid) - let d - series([ - // put blockFactor amount of blocks per node - (cb) => parallel(_.map(nodeArr, (node, i) => (cb) => { - node.bitswap.start() - - const data = _.map(_.range(blockFactor), (j) => { - const index = i * blockFactor + j - return blocks[index] - }) - - each(data, (d, cb) => node.bitswap.put(d, cb), cb) - }), cb), - (cb) => { - d = (new Date()).getTime() - // fetch all blocks on every node - parallel(_.map(nodeArr, (node, i) => (cb) => { - map(cids, (cid, cb) => { - node.bitswap.get(cid, cb) - }, (err, res) => { - if (err) { - return cb(err) - } - - expect(res).to.have.length(blocks.length) - cb() - }) - }), cb) - } - ], (err) => { - if (err) { - return cb(err) - } - console.log(' time -- %s', (new Date()).getTime() - d) - cb() + const blocks = await createBlocks(n, blockFactor) + + const cids = blocks.map((b) => b.cid) + + // put blockFactor amount of blocks per node + await Promise.all(nodeArr.map(async (node, i) => { + node.bitswap.start() + + const data = range(blockFactor).map((j) => { + const index = i * blockFactor + j + return blocks[index] }) - }) + + await Promise.all(data.map((d) => node.bitswap.put(d))) + })) + + const d = Date.now() + + // fetch all blocks on every node + await Promise.all(nodeArr.map(async (node) => { + const bs = await Promise.all(cids.map((cid) => node.bitswap.get(cid))) + expect(bs).to.deep.equal(blocks) + })) + + console.log(' time -- %s', (Date.now() - d)) } -function createBlocks (n, blockFactor, callback) { - map(_.map(_.range(n * blockFactor), (k) => { - return crypto.randomBytes(n * blockFactor) - }), (d, cb) => { - multihashing(d, 'sha2-256', (err, hash) => { - if (err) { - return cb(err) - } - cb(null, new Block(d, new CID(hash))) - }) - }, callback) +function createBlocks (n, blockFactor) { + return Promise.all([...new Array(n * blockFactor)].map(async (k) => { + const d = crypto.randomBytes(n * blockFactor) + const hash = await multihashing(d, 'sha2-256') + return new Block(d, new CID(hash)) + })) } diff --git a/test/network/network.node.js b/test/network/network.node.js index cfea21d9..2e1f8796 100644 --- a/test/network/network.node.js +++ b/test/network/network.node.js @@ -4,43 +4,13 @@ const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') const lp = require('pull-length-prefixed') const pull = require('pull-stream') -const parallel = require('async/parallel') -const waterfall = require('async/waterfall') -const map = require('async/map') -const _ = require('lodash') - -const Node = require('../utils/create-libp2p-node').bundle +const createLibp2pNode = require('../utils/create-libp2p-node') const makeBlock = require('../utils/make-block') const Network = require('../../src/network') const Message = require('../../src/types/message') -// TODO send this to utils -function createP2PNode (multiaddrs, options, callback) { - if (typeof options === 'function') { - callback = options - options = {} - } - - if (!Array.isArray(multiaddrs)) { - multiaddrs = [multiaddrs] - } - - waterfall([ - (cb) => PeerId.create({ bits: 512 }, cb), - (peerId, cb) => PeerInfo.create(peerId, cb), - (peerInfo, cb) => { - multiaddrs.map((ma) => peerInfo.multiaddrs.add(ma)) - options.peerInfo = peerInfo - const node = new Node(options) - cb(null, node) - } - ], callback) -} - describe('network', () => { let p2pA let networkA @@ -53,59 +23,43 @@ describe('network', () => { let blocks - before((done) => { - parallel([ - (cb) => createP2PNode('/ip4/127.0.0.1/tcp/0', cb), - (cb) => createP2PNode('/ip4/127.0.0.1/tcp/0', cb), - (cb) => createP2PNode('/ip4/127.0.0.1/tcp/0', cb), - (cb) => map(_.range(2), (i, cb) => makeBlock(cb), cb) - ], (err, results) => { - expect(err).to.not.exist() - - p2pA = results[0] - p2pB = results[1] - p2pC = results[2] - - blocks = results[3] - - parallel([ - (cb) => p2pA.start(cb), - (cb) => p2pB.start(cb), - (cb) => p2pC.start(cb) - ], done) - }) + before(async () => { + [p2pA, p2pB, p2pC] = await Promise.all([ + createLibp2pNode(), + createLibp2pNode(), + createLibp2pNode() + ]) + blocks = await makeBlock(2) }) - after((done) => { - parallel([ - (cb) => p2pA.stop(cb), - (cb) => p2pB.stop(cb), - (cb) => p2pC.stop(cb) - ], done) + after(() => { + p2pA.stop() + p2pB.stop() + p2pC.stop() }) - let bitswapMockA = { - _receiveMessage: () => {}, - _receiveError: () => {}, - _onPeerConnected: () => {}, - _onPeerDisconnected: () => {} + const bitswapMockA = { + _receiveMessage: async () => {}, + _receiveError: async () => {}, + _onPeerConnected: async () => {}, + _onPeerDisconnected: async () => {} } - let bitswapMockB = { - _receiveMessage: () => {}, - _receiveError: () => {}, - _onPeerConnected: () => {}, - _onPeerDisconnected: () => {} + const bitswapMockB = { + _receiveMessage: async () => {}, + _receiveError: async () => {}, + _onPeerConnected: async () => {}, + _onPeerDisconnected: async () => {} } - let bitswapMockC = { - _receiveMessage: () => {}, - _receiveError: () => {}, - _onPeerConnected: () => {}, - _onPeerDisconnected: () => {} + const bitswapMockC = { + _receiveMessage: async () => {}, + _receiveError: async () => {}, + _onPeerConnected: async () => {}, + _onPeerDisconnected: async () => {} } - it('instantiate the network obj', (done) => { + it('instantiate the network obj', () => { networkA = new Network(p2pA, bitswapMockA) networkB = new Network(p2pB, bitswapMockB) // only bitswap100 @@ -115,18 +69,18 @@ describe('network', () => { expect(networkB).to.exist() expect(networkC).to.exist() - parallel([ - (cb) => networkA.start(cb), - (cb) => networkB.start(cb), - (cb) => networkC.start(cb) - ], done) + networkA.start() + networkB.start() + networkC.start() }) - it('connectTo fail', (done) => { - networkA.connectTo(p2pB.peerInfo.id, (err) => { + it('connectTo fail', async () => { + try { + await networkA.connectTo(p2pB.peerInfo.id) + chai.assert.fail() + } catch (err) { expect(err).to.exist() - done() - }) + } }) it('onPeerConnected success', (done) => { @@ -159,8 +113,8 @@ describe('network', () => { } }) - it('connectTo success', (done) => { - networkA.connectTo(p2pB.peerInfo, done) + it('connectTo success', async () => { + await networkA.connectTo(p2pB.peerInfo) }) it('._receiveMessage success from Bitswap 1.0.0', (done) => { @@ -172,11 +126,11 @@ describe('network', () => { msg.addBlock(b1) msg.addBlock(b2) - bitswapMockB._receiveMessage = (peerId, msgReceived) => { + bitswapMockB._receiveMessage = async (peerId, msgReceived) => { // eslint-disable-line require-await expect(msg).to.eql(msgReceived) - bitswapMockB._receiveMessage = () => {} - bitswapMockB._receiveError = () => {} + bitswapMockB._receiveMessage = async () => {} + bitswapMockB._receiveError = async () => {} done() } @@ -206,10 +160,10 @@ describe('network', () => { msg.addBlock(b1) msg.addBlock(b2) - bitswapMockB._receiveMessage = (peerId, msgReceived) => { + bitswapMockB._receiveMessage = async (peerId, msgReceived) => { // eslint-disable-line require-await expect(msg).to.eql(msgReceived) - bitswapMockB._receiveMessage = () => {} - bitswapMockB._receiveError = () => {} + bitswapMockB._receiveMessage = async () => {} + bitswapMockB._receiveError = async () => {} done() } @@ -239,10 +193,10 @@ describe('network', () => { msg.addBlock(b1) msg.addBlock(b2) - bitswapMockB._receiveMessage = (peerId, msgReceived) => { + bitswapMockB._receiveMessage = async (peerId, msgReceived) => { // eslint-disable-line require-await expect(msg).to.eql(msgReceived) - bitswapMockB._receiveMessage = () => {} - bitswapMockB._receiveError = () => {} + bitswapMockB._receiveMessage = async () => {} + bitswapMockB._receiveError = async () => {} done() } @@ -279,7 +233,9 @@ describe('network', () => { function finish () { bitswapMockA._onPeerConnected = () => {} bitswapMockC._onPeerConnected = () => {} - networkA.connectTo(p2pC.peerInfo.id, done) + networkA.connectTo(p2pC.peerInfo.id).then(() => { + done() + }) } }) @@ -292,10 +248,10 @@ describe('network', () => { msg.addBlock(b1) msg.addBlock(b2) - bitswapMockC._receiveMessage = (peerId, msgReceived) => { + bitswapMockC._receiveMessage = async (peerId, msgReceived) => { // eslint-disable-line require-await expect(msg).to.eql(msgReceived) - bitswapMockC._receiveMessage = () => {} - bitswapMockC._receiveError = () => {} + bitswapMockC._receiveMessage = async () => {} + bitswapMockC._receiveError = async () => {} done() } diff --git a/test/notifications.spec.js b/test/notifications.spec.js index 0d815715..cb538019 100644 --- a/test/notifications.spec.js +++ b/test/notifications.spec.js @@ -3,34 +3,23 @@ const chai = require('chai') chai.use(require('dirty-chai')) + const expect = chai.expect -const map = require('async/map') -const parallel = require('async/parallel') -const PeerId = require('peer-id') const CID = require('cids') const Block = require('ipfs-block') const Notifications = require('../src/notifications') const makeBlock = require('./utils/make-block') +const makePeerId = require('./utils/make-peer-id') describe('Notifications', () => { let blocks let peerId - before((done) => { - parallel([ - (cb) => map([0, 1, 2], (i, cb) => makeBlock(cb), (err, res) => { - expect(err).to.not.exist() - blocks = res - cb() - }), - (cb) => PeerId.create({ bits: 512 }, (err, id) => { - expect(err).to.not.exist() - peerId = id - cb() - }) - ], done) + before(async () => { + blocks = await makeBlock(3) + peerId = await makePeerId() }) it('hasBlock', (done) => { @@ -44,68 +33,70 @@ describe('Notifications', () => { }) describe('wantBlock', () => { - it('receive block', (done) => { + it('receive block', async () => { const n = new Notifications(peerId) const b = blocks[0] - n.wantBlock(b.cid, (block) => { - expect(b).to.eql(block) - - // check that internal cleanup works as expected - expect(Object.keys(n._blockListeners)).to.have.length(0) - expect(Object.keys(n._unwantListeners)).to.have.length(0) - done() - }, () => { - done(new Error('should never happen')) - }) + const p = n.wantBlock(b.cid) n.hasBlock(b) + + const block = await p + + expect(b).to.eql(block) + + // check that internal cleanup works as expected + expect(Object.keys(n._blockListeners)).to.have.length(0) + expect(Object.keys(n._unwantListeners)).to.have.length(0) }) - it('unwant block', (done) => { + it('unwant block', async () => { const n = new Notifications() const b = blocks[0] - n.wantBlock(b.cid, () => { - done(new Error('should never happen')) - }, done) + const p = n.wantBlock(b.cid) n.unwantBlock(b.cid) + + const block = await p + + expect(block).to.be.undefined() }) }) describe('wantBlock with same cid derived from distinct encodings', () => { - it('receive block', (done) => { + it('receive block', async () => { const n = new Notifications(peerId) const cid = new CID(blocks[0].cid.toV1().toString('base64')) const b = new Block(blocks[0].data, cid) const cid2 = new CID(b.cid.toString('base32')) - n.wantBlock(cid2, (block) => { - expect(b).to.eql(block) - - // check that internal cleanup works as expected - expect(Object.keys(n._blockListeners)).to.have.length(0) - expect(Object.keys(n._unwantListeners)).to.have.length(0) - done() - }, () => { - done(new Error('should never happen')) - }) + const p = n.wantBlock(cid2) n.hasBlock(b) + + const block = await p + + expect(b).to.eql(block) + + // check that internal cleanup works as expected + expect(Object.keys(n._blockListeners)).to.have.length(0) + expect(Object.keys(n._unwantListeners)).to.have.length(0) }) - it('unwant block', (done) => { + it('unwant block', async () => { const n = new Notifications() const cid = new CID(blocks[0].cid.toV1().toString('base64')) const b = new Block(blocks[0].data, cid) const cid2 = new CID(b.cid.toString('base32')) - n.wantBlock(cid2, () => { - done(new Error('should never happen')) - }, done) + const p = n.wantBlock(cid2) n.unwantBlock(b.cid) + + const block = await p + + expect(block).to.be.undefined() }) }) }) diff --git a/test/swarms.js b/test/swarms.js index 0714240f..cef9f218 100644 --- a/test/swarms.js +++ b/test/swarms.js @@ -5,53 +5,73 @@ const stats = require('stats-lite') const distributionTest = require('./utils/distribution-test') +const EventEmitter = require('events') const test = it describe.skip('swarms', () => { const print = Boolean(process.env.PRINT) + let emitter + + before(() => { + emitter = new EventEmitter() + }) after(() => { process.exit() }) - test('2 nodes, 2 blocks', function (done) { + test('2 nodes, 2 blocks', async function () { this.timeout(10 * 1000) - maybePrint('2 nodes, 2 blocks', distributionTest(2, 2, done)) + maybePrint('2 nodes, 2 blocks', emitter) + + await distributionTest(2, 2, 1, emitter) }) - test('10 nodes, 2 blocks', function (done) { + test('10 nodes, 2 blocks', async function () { this.timeout(30 * 1000) - maybePrint('10 nodes, 2 blocks', distributionTest(10, 2, done)) + maybePrint('10 nodes, 2 blocks', emitter) + + await distributionTest(10, 2, 1, emitter) }) - test('10 nodes, 10 blocks', function (done) { + test('10 nodes, 10 blocks', async function () { this.timeout(30 * 1000) - maybePrint('10 nodes, 10 blocks', distributionTest(10, 10, 1, done)) + maybePrint('10 nodes, 10 blocks', emitter) + + await distributionTest(10, 10, 1, emitter) }) - test('10 nodes, 20 blocks', function (done) { + test('10 nodes, 20 blocks', async function () { this.timeout(30 * 1000) - maybePrint('10 nodes, 20 blocks', distributionTest(10, 20, done)) + maybePrint('10 nodes, 20 blocks', emitter) + + await distributionTest(10, 20, 1, emitter) }) - test('50 nodes, 2 blocks', function (done) { + test('50 nodes, 2 blocks', async function () { this.timeout(600 * 1000) - maybePrint('50 nodes, 2 blocks', distributionTest(50, 2, done)) + maybePrint('50 nodes, 2 blocks', emitter) + + await distributionTest(50, 2, 1, emitter) }) - test.skip('100 nodes, 2 blocks', function (done) { + test.skip('100 nodes, 2 blocks', async function () { this.timeout(600 * 1000) - maybePrint('100 nodes, 2 blocks', distributionTest(100, 2, done)) + maybePrint('100 nodes, 2 blocks', emitter) + + await distributionTest(100, 2, 1, emitter) }) - test('10 nodes, 100 blocks', function (done) { + test('10 nodes, 100 blocks', async function () { this.timeout(600 * 1000) - maybePrint('10 nodes, 100 blocks', distributionTest(10, 100, done)) + maybePrint('10 nodes, 100 blocks', emitter) + + await distributionTest(10, 100, 1, emitter) }) function maybePrint (suite, emitter) { diff --git a/test/types/message.spec.js b/test/types/message.spec.js index a56fe6e9..8913487c 100644 --- a/test/types/message.spec.js +++ b/test/types/message.spec.js @@ -1,14 +1,11 @@ /* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 8] */ 'use strict' const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect const protons = require('protons') -const map = require('async/map') const CID = require('cids') -const _ = require('lodash') const Buffer = require('safe-buffer').Buffer const loadFixture = require('aegir/fixtures') const testDataPath = 'test/fixtures/serialized-from-go' @@ -24,13 +21,9 @@ describe('BitswapMessage', () => { let blocks let cids - before((done) => { - map(_.range(3), (i, cb) => makeBlock(cb), (err, res) => { - expect(err).to.not.exist() - blocks = res - cids = blocks.map((b) => b.cid) - done() - }) + before(async () => { + blocks = await makeBlock(3) + cids = blocks.map((b) => b.cid) }) it('.addEntry - want block', () => { @@ -65,7 +58,7 @@ describe('BitswapMessage', () => { expect(decoded.payload[0].data).to.eql(block.data) }) - it('.deserialize a Bitswap100 Message', (done) => { + it('.deserialize a Bitswap100 Message', async () => { const cid0 = cids[0] const cid1 = cids[1] const cid2 = cids[2] @@ -87,27 +80,23 @@ describe('BitswapMessage', () => { ] }) - BitswapMessage.deserialize(raw, (err, msg) => { - expect(err).to.not.exist() - expect(msg.full).to.equal(true) - expect(Array.from(msg.wantlist)) - .to.eql([[ - cid0.toString('base58btc'), - new BitswapMessage.Entry(cid0, 0, false) - ]]) - - expect( - Array.from(msg.blocks).map((b) => [b[0], b[1].data]) - ).to.eql([ - [cid1.toString('base58btc'), b1.data], - [cid2.toString('base58btc'), b2.data] - ]) - - done() - }) + const msg = await BitswapMessage.deserialize(raw) + expect(msg.full).to.equal(true) + expect(Array.from(msg.wantlist)) + .to.eql([[ + cid0.toString('base58btc'), + new BitswapMessage.Entry(cid0, 0, false) + ]]) + + expect( + Array.from(msg.blocks).map((b) => [b[0], b[1].data]) + ).to.eql([ + [cid1.toString('base58btc'), b1.data], + [cid2.toString('base58btc'), b2.data] + ]) }) - it('.deserialize a Bitswap110 Message', (done) => { + it('.deserialize a Bitswap110 Message', async () => { const cid0 = cids[0] const cid1 = cids[1] const cid2 = cids[2] @@ -132,27 +121,23 @@ describe('BitswapMessage', () => { }] }) - BitswapMessage.deserialize(raw, (err, msg) => { - expect(err).to.not.exist() - expect(msg.full).to.equal(true) - expect(Array.from(msg.wantlist)) - .to.eql([[ - cid0.toString('base58btc'), - new BitswapMessage.Entry(cid0, 0, false) - ]]) - - expect( - Array.from(msg.blocks).map((b) => [b[0], b[1].data]) - ).to.eql([ - [cid1.toString('base58btc'), b1.data], - [cid2.toString('base58btc'), b2.data] - ]) - - done() - }) + const msg = await BitswapMessage.deserialize(raw) + expect(msg.full).to.equal(true) + expect(Array.from(msg.wantlist)) + .to.eql([[ + cid0.toString('base58btc'), + new BitswapMessage.Entry(cid0, 0, false) + ]]) + + expect( + Array.from(msg.blocks).map((b) => [b[0], b[1].data]) + ).to.eql([ + [cid1.toString('base58btc'), b1.data], + [cid2.toString('base58btc'), b2.data] + ]) }) - it('duplicates', (done) => { + it('ignores duplicates', () => { const b = blocks[0] const cid = cids[0] const m = new BitswapMessage(true) @@ -164,7 +149,6 @@ describe('BitswapMessage', () => { m.addBlock(b) m.addBlock(b) expect(m.blocks.size).to.be.eql(1) - done() }) it('.empty', () => { @@ -172,7 +156,7 @@ describe('BitswapMessage', () => { expect(m.empty).to.equal(true) }) - it('non full wantlist message', () => { + it('non-full wantlist message', () => { const msg = new BitswapMessage(false) const serialized = msg.serializeToBitswap100() @@ -180,7 +164,7 @@ describe('BitswapMessage', () => { }) describe('.equals', () => { - it('true, same message', (done) => { + it('true, same message', () => { const b = blocks[0] const cid = cids[0] const m1 = new BitswapMessage(true) @@ -192,10 +176,9 @@ describe('BitswapMessage', () => { m1.addBlock(b) m2.addBlock(b) expect(m1.equals(m2)).to.equal(true) - done() }) - it('false, different entries', (done) => { + it('false, different entries', () => { const b = blocks[0] const cid = cids[0] const m1 = new BitswapMessage(true) @@ -207,10 +190,9 @@ describe('BitswapMessage', () => { m1.addBlock(b) m2.addBlock(b) expect(m1.equals(m2)).to.equal(false) - done() }) - it('true, same cid derived from distinct encoding', (done) => { + it('true, same cid derived from distinct encoding', () => { const b = blocks[0] const cid = cids[0].toV1() const cid1 = new CID(cid.toBaseEncodedString('base32')) @@ -224,7 +206,6 @@ describe('BitswapMessage', () => { m1.addBlock(b) m2.addBlock(b) expect(m1.equals(m2)).to.equal(true) - done() }) }) @@ -258,41 +239,32 @@ describe('BitswapMessage', () => { }) describe('go interop', () => { - it('bitswap 1.0.0 message', (done) => { + it('bitswap 1.0.0 message', async () => { const goEncoded = Buffer.from('CioKKAoiEiAs8k26X7CjDiboOyrFueKeGxYeXB+nQl5zBDNik4uYJBAKGAA=', 'base64') const msg = new BitswapMessage(false) const cid = new CID('QmRN6wdp1S2A5EtjW9A3M1vKSBuQQGcgvuhoMUoEz4iiT5') msg.addEntry(cid, 10) - BitswapMessage.deserialize(goEncoded, (err, res) => { - expect(err).to.not.exist() - expect(res).to.eql(msg) - expect(msg.serializeToBitswap100()).to.eql(goEncoded) - done() - }) + const res = await BitswapMessage.deserialize(goEncoded) + expect(res).to.eql(msg) + expect(msg.serializeToBitswap100()).to.eql(goEncoded) }) describe.skip('bitswap 1.1.0 message', () => { // TODO check with whyrusleeping the quality of the raw protobufs // deserialization is just failing on the first and the second has a // payload but empty - it('full wantlist message', (done) => { - BitswapMessage.deserialize(rawMessageFullWantlist, (err, message) => { - expect(err).to.not.exist() - // TODO - // check the deserialised message - done() - }) + it('full wantlist message', async () => { + await BitswapMessage.deserialize(rawMessageFullWantlist) + // TODO + // check the deserialised message }) - it('one block message', (done) => { - BitswapMessage.deserialize(rawMessageOneBlock, (err, message) => { - expect(err).to.not.exist() - // TODO - // check the deserialised message - done() - }) + it('one block message', async () => { + await BitswapMessage.deserialize(rawMessageOneBlock) + // TODO + // check the deserialised message }) }) }) diff --git a/test/types/wantlist.spec.js b/test/types/wantlist.spec.js index 11dcae80..f116a815 100644 --- a/test/types/wantlist.spec.js +++ b/test/types/wantlist.spec.js @@ -4,9 +4,7 @@ const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect -const map = require('async/map') const CID = require('cids') -const _ = require('lodash') const multihashing = require('multihashing-async') const Wantlist = require('../../src/types/wantlist') @@ -16,12 +14,8 @@ describe('Wantlist', () => { let wm let blocks - before((done) => { - map(_.range(2), (i, cb) => makeBlock(cb), (err, res) => { - expect(err).to.not.exist() - blocks = res - done() - }) + before(async () => { + blocks = await makeBlock(2) }) beforeEach(() => { @@ -116,21 +110,19 @@ describe('Wantlist', () => { expect(wm.contains(b2.cid)).to.not.exist() }) - it('with cidV1', (done) => { + it('with cidV1', async () => { const b = blocks[0] - multihashing(b.data, 'sha2-256', (err, hash) => { - expect(err).to.not.exist() - const cid = new CID(1, 'dag-pb', hash) - wm.add(cid, 2) - - expect( - Array.from(wm.entries()) - ).to.be.eql([[ - cid.toString('base58btc'), - new Wantlist.Entry(cid, 2) - ]]) - done() - }) + const hash = await multihashing(b.data, 'sha2-256') + + const cid = new CID(1, 'dag-pb', hash) + wm.add(cid, 2) + + expect( + Array.from(wm.entries()) + ).to.be.eql([[ + cid.toString('base58btc'), + new Wantlist.Entry(cid, 2) + ]]) }) it('matches same cid derived from distinct encodings', () => { diff --git a/test/utils.spec.js b/test/utils.spec.js index 6458677b..b418e0ba 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -6,7 +6,7 @@ const expect = chai.expect const { groupBy, uniqWith, pullAllWith, includesWith, sortBy } = require('../src/utils') describe('utils spec', function () { - it('grouby', (done) => { + it('groupBy', () => { const list = [ { name: 'name1', score: 1 }, { name: 'name2', score: 1 }, @@ -21,11 +21,9 @@ describe('utils spec', function () { ], b: [{ name: 'name3', score: 2 }] }) - - done() }) - it('pullAllWith', (done) => { + it('pullAllWith', () => { var array = [{ x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }] const actual = pullAllWith( @@ -35,11 +33,9 @@ describe('utils spec', function () { ) expect(actual).to.deep.equal([{ x: 1, y: 2 }, { x: 5, y: 6 }]) - - done() }) - it('uniqWith', (done) => { + it('uniqWith', () => { class T { constructor (id) { this.id = id @@ -54,13 +50,13 @@ describe('utils spec', function () { const r = uniqWith((a, b) => a.equals(b), list) if (r[0].id === 1 && r[1].id === 2) { - return done() + return } - return done(new Error('no match')) + throw new Error('no match') }) - it('includesWith', (done) => { + it('includesWith', () => { class T { constructor (id) { this.id = id @@ -76,11 +72,9 @@ describe('utils spec', function () { const r2 = includesWith((a, b) => a.equals(b), new T(4), list) expect(r1).to.be.true() expect(r2).to.be.false() - - done() }) - it('sortBy', (done) => { + it('sortBy', () => { const list = [ { id: 3, @@ -99,13 +93,11 @@ describe('utils spec', function () { const groupedList1 = sortBy(o => o.name, list) const groupedList2 = sortBy(o => o.id, list) - expect(groupedList1).to.be.deep.equal([ { id: 2, name: 'a' }, + expect(groupedList1).to.be.deep.equal([{ id: 2, name: 'a' }, { id: 3, name: 'b' }, - { id: 1, name: 'c' } ]) - expect(groupedList2).to.be.deep.equal([ { id: 1, name: 'c' }, + { id: 1, name: 'c' }]) + expect(groupedList2).to.be.deep.equal([{ id: 1, name: 'c' }, { id: 2, name: 'a' }, - { id: 3, name: 'b' } ]) - - done() + { id: 3, name: 'b' }]) }) }) diff --git a/test/utils/connect-all.js b/test/utils/connect-all.js index cb9c7bc6..100bebd2 100644 --- a/test/utils/connect-all.js +++ b/test/utils/connect-all.js @@ -1,15 +1,11 @@ 'use strict' -const eachSeries = require('async/eachSeries') const without = require('lodash.without') -module.exports = (nodes, callback) => { - eachSeries(nodes, (node, cb) => { - eachSeries( - without(nodes, node), - (otherNode, cb) => { - node.libp2pNode.dial(otherNode.bitswap.peerInfo, cb) - }, - cb) - }, callback) +module.exports = async (nodes) => { + for (const node of nodes) { + for (const otherNode of without(nodes, node)) { + await node.libp2pNode.dial(otherNode.bitswap.peerInfo) + } + } } diff --git a/test/utils/create-bitswap.js b/test/utils/create-bitswap.js index 332f529a..e500134f 100644 --- a/test/utils/create-bitswap.js +++ b/test/utils/create-bitswap.js @@ -1,26 +1,15 @@ 'use strict' -const waterfall = require('async/waterfall') - const Bitswap = require('../..') const createTempRepo = require('./create-temp-repo-nodejs') const createLibp2pNode = require('./create-libp2p-node') -module.exports = (callback) => { - waterfall([ - (cb) => createTempRepo(cb), - (repo, cb) => { - createLibp2pNode({ - DHT: repo.datastore - }, (err, node) => cb(err, repo, node)) - }, - (repo, libp2pNode, cb) => { - const bitswap = new Bitswap(libp2pNode, repo.blocks) - bitswap.start((err) => cb(err, { - bitswap: bitswap, - repo: repo, - libp2pNode: libp2pNode - })) - } - ], callback) +module.exports = async () => { + const repo = await createTempRepo() + const libp2pNode = await createLibp2pNode({ + DHT: repo.datastore + }) + const bitswap = new Bitswap(libp2pNode, repo.blocks) + bitswap.start() + return { bitswap, repo, libp2pNode } } diff --git a/test/utils/create-libp2p-node.js b/test/utils/create-libp2p-node.js index 9577821f..9ec9d798 100644 --- a/test/utils/create-libp2p-node.js +++ b/test/utils/create-libp2p-node.js @@ -5,10 +5,10 @@ const MPLEX = require('libp2p-mplex') const SECIO = require('libp2p-secio') const libp2p = require('libp2p') const KadDHT = require('libp2p-kad-dht') -const waterfall = require('async/waterfall') const PeerInfo = require('peer-info') const PeerId = require('peer-id') const defaultsDeep = require('@nodeutils/defaults-deep') +const promisify = require('promisify-es6') class Node extends libp2p { constructor (_options) { @@ -23,12 +23,11 @@ class Node extends libp2p { connEncryption: [ SECIO ], - dht: _options.DHT ? KadDHT : undefined + dht: KadDHT }, config: { - dht: {}, - EXPERIMENTAL: { - dht: Boolean(_options.DHT) + dht: { + enabled: Boolean(_options.DHT) } } } @@ -38,19 +37,15 @@ class Node extends libp2p { } } -function createLibp2pNode (options, callback) { - let node +async function createLibp2pNode (options = {}) { + const id = await promisify(PeerId.create)({ bits: 512 }) + const peerInfo = await promisify(PeerInfo.create)(id) + peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') + options.peerInfo = peerInfo + const node = new Node(options) + await node.start() - waterfall([ - (cb) => PeerId.create({ bits: 512 }, cb), - (id, cb) => PeerInfo.create(id, cb), - (peerInfo, cb) => { - peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') - options.peerInfo = peerInfo - node = new Node(options) - node.start(cb) - } - ], (err) => callback(err, node)) + return node } exports = module.exports = createLibp2pNode diff --git a/test/utils/create-temp-repo-browser.js b/test/utils/create-temp-repo-browser.js index 1a0c9338..c06a7725 100644 --- a/test/utils/create-temp-repo-browser.js +++ b/test/utils/create-temp-repo-browser.js @@ -2,34 +2,26 @@ 'use strict' const IPFSRepo = require('ipfs-repo') -const series = require('async/series') const idb = self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB -function createTempRepo (callback) { +async function createTempRepo () { const date = Date.now().toString() const path = `/bitswap-tests-${date}-${Math.random()}` const repo = new IPFSRepo(path) + await repo.init({}) + await repo.open() - series([ - (cb) => repo.init({}, cb), - (cb) => repo.open(cb) - ], (err) => { - if (err) { - return callback(err) - } - repo.teardown = (callback) => { - idb.deleteDatabase(path) - idb.deleteDatabase(`${path}/blocks`) - callback() - } + repo.teardown = () => { + idb.deleteDatabase(path) + idb.deleteDatabase(`${path}/blocks`) + } - callback(null, repo) - }) + return repo } module.exports = createTempRepo diff --git a/test/utils/create-temp-repo-nodejs.js b/test/utils/create-temp-repo-nodejs.js index b656bcf2..519ab085 100644 --- a/test/utils/create-temp-repo-nodejs.js +++ b/test/utils/create-temp-repo-nodejs.js @@ -5,31 +5,26 @@ const pathJoin = require('path').join const os = require('os') const ncp = require('ncp') const rimraf = require('rimraf') -const series = require('async/series') +const promisify = require('promisify-es6') const baseRepo = pathJoin(__dirname, '../fixtures/repo') -function createTempRepo (callback) { +async function createTempRepo () { const date = Date.now().toString() const path = pathJoin(os.tmpdir(), `bitswap-tests-${date}-${Math.random()}`) - ncp(baseRepo, path, (err) => { - if (err) { return callback(err) } + await promisify(ncp)(baseRepo, path) - const repo = new IPFSRepo(path) + const repo = new IPFSRepo(path) - repo.teardown = (done) => { - series([ - (cb) => repo.close(cb), - (cb) => rimraf(path, cb) - ], (err) => done(err)) - } + repo.teardown = async () => { + await repo.close() + await promisify(rimraf)(path) + } - repo.open((err) => { - if (err) { return callback(err) } - callback(null, repo) - }) - }) + await repo.open() + + return repo } module.exports = createTempRepo diff --git a/test/utils/distribution-test.js b/test/utils/distribution-test.js index 3f27fc36..52aacb14 100644 --- a/test/utils/distribution-test.js +++ b/test/utils/distribution-test.js @@ -1,105 +1,70 @@ 'use strict' const range = require('lodash.range') -const map = require('async/map') -const each = require('async/each') -const whilst = require('async/whilst') -const series = require('async/series') -const waterfall = require('async/waterfall') const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect -const EventEmitter = require('events') const createBitswap = require('./create-bitswap') const makeBlock = require('./make-block') const connectAll = require('./connect-all') -module.exports = (instanceCount, blockCount, repeats, callback) => { +module.exports = async (instanceCount, blockCount, repeats, events) => { let pendingRepeats = repeats - let nodes - const events = new EventEmitter() - - waterfall([ - (cb) => map(range(instanceCount), (_, cb) => createBitswap(cb), cb), - (_nodes, cb) => { - nodes = _nodes - events.emit('start') - cb() - }, - (cb) => { - connectAll(nodes, cb) - }, - (cb) => { - events.emit('all connected') - whilst(() => pendingRepeats > 0, (cb) => { - const first = nodes[0] - let blocks - waterfall([ - (cb) => map(range(blockCount), (_, cb) => makeBlock(cb), cb), - (_blocks, cb) => { - blocks = _blocks - cb() - }, - (cb) => each(blocks, first.bitswap.put.bind(first.bitswap), (err) => { - events.emit('first put') - cb(err) - }), - (cb) => map(nodes, (node, cb) => { - events.emit('getting many') - const cids = blocks.map((block) => block.cid) - const start = Date.now() - node.bitswap.getMany(cids, (err, result) => { - if (err) { - cb(err) - } else { - const elapsed = Date.now() - start - events.emit('got block', elapsed) - cb(null, result) - } - }) - }, cb), - (results, cb) => { - try { - expect(results).have.lengthOf(instanceCount) - results.forEach((nodeBlocks) => { - expect(nodeBlocks).to.have.lengthOf(blocks.length) - nodeBlocks.forEach((block, i) => { - expect(block.data).to.deep.equal(blocks[i].data) - }) - }) - } catch (err) { - return cb(err) - } - cb() - }, - (cb) => { - pendingRepeats-- - cb() - } - ], cb) - }, cb) - } - ], - (err) => { - events.emit('stop') - each( - nodes, - (node, cb) => { - series( - [ - (cb) => node.bitswap.stop(cb), - (cb) => node.libp2pNode.stop(cb), - (cb) => node.repo.teardown(cb) - ], - cb) - }, - (err2) => { - events.emit('stopped') - callback(err) - } + + const nodes = await Promise.all(range(instanceCount).map(() => createBitswap())) + events.emit('start') + + await connectAll(nodes) + + events.emit('all connected') + + while (pendingRepeats > 0) { + const first = nodes[0] + const blocks = await makeBlock(blockCount) + + await Promise.all( + blocks.map(block => first.bitswap.put(block)) ) - }) - return events + events.emit('first put') + + const results = await Promise.all( + nodes.map(async node => { + events.emit('getting many') + + const cids = blocks.map((block) => block.cid) + const start = Date.now() + const result = await node.bitswap.getMany(cids) + const elapsed = Date.now() - start + events.emit('got block', elapsed) + + return result + }) + ) + + try { + expect(results).have.lengthOf(instanceCount) + results.forEach((nodeBlocks) => { + expect(nodeBlocks).to.have.lengthOf(blocks.length) + nodeBlocks.forEach((block, i) => { + expect(block.data).to.deep.equal(blocks[i].data) + }) + }) + } finally { + pendingRepeats-- + } + } + + events.emit('stop') + + await Promise.all( + nodes.map(async node => { + await node.bitswap.stop() + await node.libp2pNode.stop() + await node.repo.teardown() + }) + ) + + events.emit('stopped') } diff --git a/test/utils/helpers.js b/test/utils/helpers.js index 98a1be5d..042a7e9a 100644 --- a/test/utils/helpers.js +++ b/test/utils/helpers.js @@ -1,31 +1,34 @@ 'use strict' -const _ = require('lodash') - -exports.orderedFinish = (n, callback) => { - const r = _.range(1, n + 1) - const finishs = [] - - return (i) => { - finishs.push(i) - if (finishs.length === n) { - if (!_.isEqual(r, finishs)) { - return callback(new Error('Invalid finish order: ' + finishs)) - } - callback() - } +const range = require('lodash.range') +const expect = require('chai').expect + +exports.orderedFinish = (n) => { + const r = range(1, n + 1) + const finishes = [] + + const output = (i) => { + finishes.push(i) + } + + output.assert = () => { + expect(finishes.length).to.equal(n) + expect(r).to.deep.equal(finishes, 'Invalid finish order: ' + finishes) } + + return output } -exports.countToFinish = (n, callback) => { +exports.countToFinish = (n) => { let pending = n - return () => { + const output = () => { pending-- - if (pending === 0) { - callback() - } else if (pending < 0) { - callback(new Error('too many finishes, expected only ' + n)) - } } + + output.assert = () => { + expect(pending).to.equal(0, 'too many finishes, expected only ' + n) + } + + return output } diff --git a/test/utils/make-block.js b/test/utils/make-block.js index 66b69193..a071b31f 100644 --- a/test/utils/make-block.js +++ b/test/utils/make-block.js @@ -3,14 +3,19 @@ const multihashing = require('multihashing-async') const CID = require('cids') const Block = require('ipfs-block') +const crypto = require('crypto') +const range = require('lodash.range') const Buffer = require('safe-buffer').Buffer const uuid = require('uuid/v4') -module.exports = (callback) => { - const data = Buffer.from(`hello world ${uuid()}`) +module.exports = async (count, size) => { + const blocks = await Promise.all( + range(count || 1).map(async () => { + const data = size ? crypto.randomBytes(size) : Buffer.from(`hello world ${uuid()}`) + const hash = await multihashing(data, 'sha2-256') + return new Block(data, new CID(hash)) + }) + ) - multihashing(data, 'sha2-256', (err, hash) => { - if (err) { return callback(err) } - callback(null, new Block(data, new CID(hash))) - }) + return count ? blocks : blocks[0] } diff --git a/test/utils/make-peer-id.js b/test/utils/make-peer-id.js new file mode 100644 index 00000000..e597a0e8 --- /dev/null +++ b/test/utils/make-peer-id.js @@ -0,0 +1,11 @@ +'use strict' + +const PeerId = require('peer-id') +const promisify = require('promisify-es6') + +module.exports = async (count) => { + const peerIds = await Promise.all([...new Array(count || 1)].map(() => { + return promisify(PeerId.create)({ bits: 512 }) + })) + return count ? peerIds : peerIds[0] +} diff --git a/test/utils/mocks.js b/test/utils/mocks.js index 3f84fedb..b9acc0da 100644 --- a/test/utils/mocks.js +++ b/test/utils/mocks.js @@ -1,12 +1,6 @@ 'use strict' -const each = require('async/each') -const eachSeries = require('async/eachSeries') -const map = require('async/map') -const parallel = require('async/parallel') -const setImmediate = require('async/setImmediate') -const series = require('async/series') -const _ = require('lodash') +const range = require('lodash.range') const PeerId = require('peer-id') const PeerInfo = require('peer-info') const PeerBook = require('peer-book') @@ -14,6 +8,7 @@ const Node = require('./create-libp2p-node').bundle const os = require('os') const Repo = require('ipfs-repo') const EventEmitter = require('events') +const promisify = require('promisify-es6') const Bitswap = require('../../src') @@ -28,15 +23,14 @@ exports.mockLibp2pNode = () => { handle () {}, unhandle () {}, contentRouting: { - provide: (cid, callback) => callback(), - findProviders: (cid, timeout, callback) => callback(null, []) + provide: async (cid) => {}, // eslint-disable-line require-await + findProviders: async (cid, timeout) => { return [] } // eslint-disable-line require-await }, on () {}, - dial (peer, callback) { - setImmediate(() => callback()) + async dial (peer) { // eslint-disable-line require-await }, - dialProtocol (peer, protocol, callback) { - setImmediate(() => callback()) + async dialProtocol (peer, protocol) { // eslint-disable-line require-await + }, swarm: { setMaxListeners () {} @@ -62,27 +56,28 @@ exports.mockNetwork = (calls, done) => { } return { - connectTo (p, cb) { + connectTo (p) { setImmediate(() => { connects.push(p) - cb() }) }, - sendMessage (p, msg, cb) { + sendMessage (p, msg) { + messages.push([p, msg]) + setImmediate(() => { - messages.push([p, msg]) - cb() finish() }) + + return Promise.resolve() }, - start (callback) { - setImmediate(() => callback()) + start () { + return Promise.resolve() }, - findAndConnect (cid, callback) { - setImmediate(() => callback()) + findAndConnect () { + return Promise.resolve() }, - provide (cid, callback) { - setImmediate(() => callback()) + provide () { + return Promise.resolve() } } } @@ -90,49 +85,47 @@ exports.mockNetwork = (calls, done) => { /* * Create a mock test network */ -exports.createMockTestNet = (repo, count, cb) => { - parallel([ - (cb) => map(_.range(count), (i, cb) => repo.create(`repo-${i}`), cb), - (cb) => map(_.range(count), (i, cb) => PeerId.create({ bits: 512 }, cb), cb) - ], (err, results) => { - if (err) { - return cb(err) - } - const stores = results[0].map((r) => r.blockstore) - const ids = results[1] - - const hexIds = ids.map((id) => id.toHexString()) - const bitswaps = _.range(count).map((i) => new Bitswap({}, stores[i])) - const networks = _.range(count).map((i) => { - return { - connectTo (id, cb) { - const done = (err) => setImmediate(() => cb(err)) - if (!_.includes(hexIds, id.toHexString())) { - return done(new Error('unkown peer')) +exports.createMockTestNet = async (repo, count) => { + const results = await Promise.all([ + range(count).map((i) => repo.create(`repo-${i}`)), + range(count).map((i) => promisify(PeerId.create)({ bits: 512 })) + ]) + + const stores = results[0].map((r) => r.blockstore) + const ids = results[1] + + const hexIds = ids.map((id) => id.toHexString()) + const bitswaps = range(count).map((i) => new Bitswap({}, stores[i])) + const networks = range(count).map((i) => { + return { + connectTo (id) { + return new Promise((resolve, reject) => { + if (!hexIds.includes(hexIds, id.toHexString())) { + return reject(new Error('unkown peer')) } - done() - }, - sendMessage (id, msg, cb) { - const j = _.findIndex(hexIds, (el) => el === id.toHexString()) - bitswaps[j]._receiveMessage(ids[i], msg, cb) - }, - start () { - } + resolve() + }) + }, + sendMessage (id, msg) { + const j = hexIds.findIndex((el) => el === id.toHexString()) + return bitswaps[j]._receiveMessage(ids[i], msg) + }, + start () { } - }) - - _.range(count).forEach((i) => { - exports.applyNetwork(bitswaps[i], networks[i]) - bitswaps[i].start() - }) + } + }) - cb(null, { - ids, - stores, - bitswaps, - networks - }) + range(count).forEach((i) => { + exports.applyNetwork(bitswaps[i], networks[i]) + bitswaps[i].start() }) + + return { + ids, + stores, + bitswaps, + networks + } } exports.applyNetwork = (bs, n) => { @@ -141,93 +134,71 @@ exports.applyNetwork = (bs, n) => { bs.engine.network = n } -exports.genBitswapNetwork = (n, callback) => { +let basePort = 12000 + +exports.genBitswapNetwork = async (n) => { const netArray = [] // bitswap, peerBook, libp2p, peerInfo, repo - const basePort = 12000 // create PeerInfo and libp2p.Node for each - map(_.range(n), (i, cb) => PeerInfo.create(cb), (err, peers) => { - if (err) { - return callback(err) - } + const peers = await Promise.all( + range(n).map(i => promisify(PeerInfo.create)()) + ) - peers.forEach((p, i) => { - const ma1 = '/ip4/127.0.0.1/tcp/' + (basePort + i) + - '/ipfs/' + p.id.toB58String() - p.multiaddrs.add(ma1) + peers.forEach((p, i) => { + basePort++ + p.multiaddrs.add('/ip4/127.0.0.1/tcp/' + basePort + '/ipfs/' + p.id.toB58String()) - const l = new Node({ peerInfo: p }) - netArray.push({ peerInfo: p, libp2p: l }) - }) + const l = new Node({ peerInfo: p }) + netArray.push({ peerInfo: p, libp2p: l }) + }) - // create PeerBook and populate peerBook - netArray.forEach((net, i) => { - const pb = netArray[i].libp2p.peerBook - netArray.forEach((net, j) => { - if (i === j) { - return - } - pb.put(net.peerInfo) - }) - netArray[i].peerBook = pb + // create PeerBook and populate peerBook + netArray.forEach((net, i) => { + const pb = netArray[i].libp2p.peerBook + netArray.forEach((net, j) => { + if (i === j) { + return + } + pb.put(net.peerInfo) }) + netArray[i].peerBook = pb + }) - // create the repos - const tmpDir = os.tmpdir() - netArray.forEach((net, i) => { - const repoPath = tmpDir + '/' + net.peerInfo.id.toB58String() - net.repo = new Repo(repoPath) - }) + // create the repos + const tmpDir = os.tmpdir() + netArray.forEach((net, i) => { + const repoPath = tmpDir + '/' + net.peerInfo.id.toB58String() + net.repo = new Repo(repoPath) + }) - each(netArray, (net, cb) => { + await Promise.all( + netArray.map(async (net) => { const repoPath = tmpDir + '/' + net.peerInfo.id.toB58String() net.repo = new Repo(repoPath) - series([ - (cb) => net.repo.init({}, cb), - (cb) => net.repo.open(cb) - ], cb) - }, (err) => { - if (err) { - throw err - } - startLibp2p() + await net.repo.init({}) + await net.repo.open() }) + ) - function startLibp2p () { - // start every libp2pNode - each(netArray, (net, cb) => net.libp2p.start(cb), (err) => { - if (err) { - throw err - } - createBitswaps() - }) - } - // create every BitSwap - function createBitswaps () { - netArray.forEach((net) => { - net.bitswap = new Bitswap(net.libp2p, net.repo.blocks, net.peerBook) - }) - establishLinks() - } + // start every libp2pNode + await Promise.all( + netArray.map((net) => net.libp2p.start()) + ) - // connect all the nodes between each other - function establishLinks () { - eachSeries(netArray, (from, cbI) => { - eachSeries(netArray, (to, cbJ) => { - if (from.peerInfo.id.toB58String() === to.peerInfo.id.toB58String()) { - return cbJ() - } + // create every BitSwap + netArray.forEach((net) => { + net.bitswap = new Bitswap(net.libp2p, net.repo.blocks, net.peerBook) + }) - from.libp2p.dial(to.peerInfo, cbJ) - }, cbI) - }, finish) + // connect all the nodes between each other + for (const from of netArray) { + for (const to of netArray) { + if (from.peerInfo.id.toB58String() !== to.peerInfo.id.toB58String()) { + await from.libp2p.dial(to.peerInfo) + } } + } - // callback with netArray - function finish (err) { - if (err) { throw err } - callback(null, netArray) - } - }) + return netArray } diff --git a/test/utils/store-has-blocks.js b/test/utils/store-has-blocks.js index 3533dc1c..5aa12fdf 100644 --- a/test/utils/store-has-blocks.js +++ b/test/utils/store-has-blocks.js @@ -1,19 +1,11 @@ 'use strict' -const each = require('async/each') +const expect = require('chai').expect -function storeHasBlocks (message, store, callback) { - each(message.blocks.values(), (b, callback) => { - store.has(b.cid, (err, has) => { - if (err) { - return callback(err) - } - if (!has) { - return callback(new Error('missing block')) - } - callback() - }) - }, callback) +async function storeHasBlocks (message, store) { + for (const b of message.blocks.values()) { + expect(await store.has(b.cid)).to.be.true('missing block') + } } module.exports = storeHasBlocks diff --git a/test/wantmanager/index.spec.js b/test/wantmanager/index.spec.js index abcb18af..55cab2f9 100644 --- a/test/wantmanager/index.spec.js +++ b/test/wantmanager/index.spec.js @@ -4,11 +4,6 @@ const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect -const PeerId = require('peer-id') -const parallel = require('async/parallel') -const series = require('async/series') -const map = require('async/map') -const _ = require('lodash') const cs = require('../../src/constants') const Message = require('../../src/types/message') @@ -16,49 +11,35 @@ const WantManager = require('../../src/want-manager') const mockNetwork = require('../utils/mocks').mockNetwork const makeBlock = require('../utils/make-block') +const makePeerId = require('../utils/make-peer-id') describe('WantManager', () => { - it('sends wantlist to all connected peers', function (done) { + it('sends wantlist to all connected peers', async function () { this.timeout(80 * 1000) - let cids - let blocks - - parallel([ - (cb) => PeerId.create({ bits: 512 }, cb), - (cb) => PeerId.create({ bits: 512 }, cb), - (cb) => PeerId.create({ bits: 512 }, cb), - (cb) => { - map(_.range(3), (i, cb) => makeBlock(cb), (err, res) => { - expect(err).to.not.exist() - blocks = res - cids = blocks.map((b) => b.cid) - cb() - }) - } - ], (err, peerIds) => { - if (err) { - return done(err) - } + const peerIds = await makePeerId(3) + const blocks = await makeBlock(3) + const cids = blocks.map((b) => b.cid) - const peer1 = peerIds[0] - const peer2 = peerIds[1] - const cid1 = cids[0] - const cid2 = cids[1] - const cid3 = cids[2] + const peer1 = peerIds[0] + const peer2 = peerIds[1] + const cid1 = cids[0] + const cid2 = cids[1] + const cid3 = cids[2] - const m1 = new Message(true) - m1.addEntry(cid1, cs.kMaxPriority) - m1.addEntry(cid2, cs.kMaxPriority - 1) + const m1 = new Message(true) + m1.addEntry(cid1, cs.kMaxPriority) + m1.addEntry(cid2, cs.kMaxPriority - 1) - const m2 = new Message(false) - m2.cancel(cid2) + const m2 = new Message(false) + m2.cancel(cid2) - const m3 = new Message(false) - m3.addEntry(cid3, cs.kMaxPriority) + const m3 = new Message(false) + m3.addEntry(cid3, cs.kMaxPriority) - const msgs = [m1, m1, m2, m2, m3, m3] + const msgs = [m1, m1, m2, m2, m3, m3] + return new Promise((resolve, reject) => { const network = mockNetwork(6, (calls) => { expect(calls.connects).to.have.length(6) expect(calls.messages).to.have.length(6) @@ -68,36 +49,30 @@ describe('WantManager', () => { const connect = calls.connects[ii] expect(message[0]).to.be.eql(connect) if (!message[1].equals(msgs[ii])) { - return done( + return reject( new Error(`expected ${message[1].toString()} to equal ${msgs[ii].toString()}`) ) } } - done() + resolve() }) const wantManager = new WantManager(peerIds[2], network) - wantManager.start((err) => { - expect(err).to.not.exist() - wantManager.wantBlocks([cid1, cid2]) - - wantManager.connected(peer1) - wantManager.connected(peer2) - - series([ - (cb) => setTimeout(cb, 200), - (cb) => { - wantManager.cancelWants([cid2]) - cb() - }, - (cb) => setTimeout(cb, 200) - ], (err) => { - expect(err).to.not.exist() + wantManager.start() + wantManager.wantBlocks([cid1, cid2]) + + wantManager.connected(peer1) + wantManager.connected(peer2) + + new Promise(resolve => setTimeout(resolve, 200)) + .then(async () => { + wantManager.cancelWants([cid2]) + await new Promise(resolve => setTimeout(resolve, 200)) wantManager.wantBlocks([cid3]) }) - }) + .catch(reject) }) }) }) diff --git a/test/wantmanager/msg-queue.spec.js b/test/wantmanager/msg-queue.spec.js index 1cb4a612..55c59565 100644 --- a/test/wantmanager/msg-queue.spec.js +++ b/test/wantmanager/msg-queue.spec.js @@ -5,12 +5,10 @@ const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect const PeerId = require('peer-id') -const map = require('async/map') -const parallel = require('async/parallel') const CID = require('cids') const multihashing = require('multihashing-async') const Buffer = require('safe-buffer').Buffer - +const promisify = require('promisify-es6') const Message = require('../../src/types/message') const MsgQueue = require('../../src/want-manager/msg-queue') @@ -18,25 +16,15 @@ describe('MessageQueue', () => { let peerIds let cids - before((done) => { - parallel([ - (cb) => map([0, 1], (i, cb) => PeerId.create({ bits: 512 }, cb), (err, res) => { - expect(err).to.not.exist() - peerIds = res - cb() - }), - (cb) => { - const data = ['1', '2', '3', '4', '5', '6'].map((d) => Buffer.from(d)) - map(data, (d, cb) => multihashing(d, 'sha2-256', cb), (err, hashes) => { - expect(err).to.not.exist() - cids = hashes.map((h) => new CID(h)) - cb() - }) - } - ], done) + before(async () => { + peerIds = await Promise.all([0, 1].map(() => promisify(PeerId.create)({ bits: 512 }))) + + const data = ['1', '2', '3', '4', '5', '6'].map((d) => Buffer.from(d)) + const hashes = await Promise.all(data.map((d) => multihashing(d, 'sha2-256'))) + cids = hashes.map((h) => new CID(h)) }) - it('connects and sends messages', (done) => { + it('connects and sends messages', () => { const msg = new Message(true) const cid1 = cids[0] const cid2 = cids[1] @@ -52,56 +40,56 @@ describe('MessageQueue', () => { const connects = [] let i = 0 - const finish = () => { - i++ - if (i === 2) { - expect(connects).to.be.eql([peerIds[1], peerIds[1]]) - - const m1 = new Message(false) - m1.addEntry(cid3, 1) - m1.addEntry(cid4, 2) - m1.cancel(cid5) - m1.cancel(cid6) - - expect( - messages - ).to.be.eql([ - [peerIds[1], msg], - [peerIds[1], m1] - ]) - - done() + return new Promise((resolve) => { + const finish = () => { + i++ + if (i === 2) { + expect(connects).to.be.eql([peerIds[1], peerIds[1]]) + + const m1 = new Message(false) + m1.addEntry(cid3, 1) + m1.addEntry(cid4, 2) + m1.cancel(cid5) + m1.cancel(cid6) + + expect( + messages + ).to.be.eql([ + [peerIds[1], msg], + [peerIds[1], m1] + ]) + + resolve() + } } - } - - const network = { - connectTo (p, cb) { - connects.push(p) - cb() - }, - sendMessage (p, msg, cb) { - messages.push([p, msg]) - cb() - finish() + + const network = { + async connectTo (p) { // eslint-disable-line require-await + connects.push(p) + }, + async sendMessage (p, msg) { // eslint-disable-line require-await + messages.push([p, msg]) + finish() + } } - } - const mq = new MsgQueue(peerIds[0], peerIds[1], network) + const mq = new MsgQueue(peerIds[0], peerIds[1], network) - expect(mq.refcnt).to.equal(1) + expect(mq.refcnt).to.equal(1) - const batch1 = [ - new Message.Entry(cid3, 1, false), - new Message.Entry(cid4, 2, false) - ] + const batch1 = [ + new Message.Entry(cid3, 1, false), + new Message.Entry(cid4, 2, false) + ] - const batch2 = [ - new Message.Entry(cid5, 1, true), - new Message.Entry(cid6, 2, true) - ] + const batch2 = [ + new Message.Entry(cid5, 1, true), + new Message.Entry(cid6, 2, true) + ] - mq.addEntries(batch1) - mq.addEntries(batch2) - mq.addMessage(msg) + mq.addEntries(batch1) + mq.addEntries(batch2) + mq.addMessage(msg) + }) }) })