diff --git a/README.md b/README.md index 45a97cfbdc..57ca948416 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ libp2p is the product of a long and arduous quest to understand the evolution of We are in the process of writting better documentation, blog posts, tutorials and a formal specification. Today you can find: -- [libp2p.io - The libp2p Website (WIP)](https://github.com/libp2p/website) +- [libp2p.io](https://libp2p.io) - [Specification (WIP)](https://github.com/libp2p/specs) - Talks - [`libp2p <3 ethereum` at DEVCON2](https://ethereumfoundation.org/devcon/?session=libp2p) [video](https://www.youtube.com/watch?v=HxueJbeMVG4) [slides](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p-HEART-devp2p-IPFS-PLUS-Ethereum-networking.pdf) [demo-1](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p_demo1-1.mp4) [demo-2](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p_demo2-1.mp4) @@ -41,8 +41,8 @@ To sum up, libp2p is a "network stack" -- a protocol suite -- that cleanly separ With its modular nature, libp2p can be found being used in different projects with different sets of features, while preserving the same top level API. `js-libp2p` is only a skeleton and should not be installed directly, if you are looking for a prebundled libp2p stack, please check: -- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-libp2p-ipfs-nodejs) - The libp2p build used by js-ipfs when run in Node.js -- [libp2p-ipfs-browser](https://github.com/ipfs/js-libp2p-ipfs-browser) - The libp2p build used by js-ipfs when run in a Browser (that supports WebRTC) +- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-nodejs.js) - The libp2p build used by js-ipfs when run in Node.js +- [libp2p-ipfs-browser](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-browser.js) - The libp2p build used by js-ipfs when run in a Browser (that supports WebRTC) If you have developed a libp2p bundle, please consider submitting it to this list so that it can be found easily by the users of libp2p. diff --git a/examples/README.md b/examples/README.md index fc8ae2e299..d0d3503ab4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,6 +1,13 @@ -### Examples list +# `js-libp2p` Examples and Tutorials -Here are some examples built with libp2p bundles. +In this folder, you can find a variety of examples to help you get started in using js-libp2p, in Node.js and in the Browser. Every example as a specific purpose and some of each incorporate a full tutorial that you can follow through, helping you expand your knowledge about libp2p and p2p networks in general. -- https://github.com/ipfs/js-libp2p-ipfs/tree/master/examples/echo -- https://github.com/ipfs/js-libp2p-ipfs/tree/master/examples/chat +Let us know if you find any issue or if you want to contribute and add a new tutorial, feel welcome to submit a PR, thank you! + +## Examples + +- [In Node.js](./nodejs) + - [echo](./nodejs/echo) + - [chat](./nodejs/chat) +- [In the browser](./browser) + - [mapper](./browser/mapper) diff --git a/examples/archived/README.md b/examples/archived/README.md deleted file mode 100644 index 293f632229..0000000000 --- a/examples/archived/README.md +++ /dev/null @@ -1,4 +0,0 @@ -Using libp2p-swarm -================== - - diff --git a/examples/archived/node-1.js b/examples/archived/node-1.js deleted file mode 100644 index f6c35228b6..0000000000 --- a/examples/archived/node-1.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict' - -var Swarm = require('libp2p-swarm') -var tcp = require('libp2p-tcp') -var multiaddr = require('multiaddr') -var Id = require('peer-id') -var Spdy = require('libp2p-spdy') -var Libp2p = require('../../src') -var Peer = require('peer-info') - -// set up - -var mh = multiaddr('/ip4/127.0.0.1/tcp/8010') -var p = new Peer(Id.create(), []) -var sw = new Swarm(p) - -// create a libp2p node - -var node = new Libp2p(sw) - -node.swarm.addTransport('tcp', tcp, {multiaddr: mh}, {}, {port: 8010}, function () { - // Ready to receive incoming connections - - sw.addStreamMuxer('spdy', Spdy, {}) - - // dial to another node - - var mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020') - var p2 = new Peer(Id.create(), [mh2]) - - node.swarm.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { - if (err) { - return console.error(err) - } - - console.log('-> connection is ready') - process.stdin.pipe(conn).pipe(process.stdout) - }) -}) diff --git a/examples/archived/node-2.js b/examples/archived/node-2.js deleted file mode 100644 index 4e25ad9fab..0000000000 --- a/examples/archived/node-2.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' - -var Swarm = require('libp2p-swarm') -var tcp = require('libp2p-tcp') -var multiaddr = require('multiaddr') -var Id = require('peer-id') -var Spdy = require('libp2p-spdy') -var Libp2p = require('../../src') -var Peer = require('peer-info') - -// set up - -var mh = multiaddr('/ip4/127.0.0.1/tcp/8020') -var p = new Peer(Id.create(), []) -var sw = new Swarm(p) - -sw.addTransport('tcp', tcp, {multiaddr: mh}, {}, {port: 8020}, function () { - // Ready to receive incoming connections - - sw.addStreamMuxer('spdy', Spdy, {}) - - // create a libp2p node - - var node = new Libp2p(sw) - - // handle/mount a protocol - - node.swarm.handleProtocol('/sparkles/1.0.0', function (conn) { - process.stdin.pipe(conn).pipe(process.stdout) - }) -}) diff --git a/examples/browser-chat/.gitkeep b/examples/browser-chat/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/browser-echo/README.md b/examples/browser-echo/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/browser/mapper/.gitignore b/examples/browser/mapper/.gitignore new file mode 100644 index 0000000000..4187d67743 --- /dev/null +++ b/examples/browser/mapper/.gitignore @@ -0,0 +1 @@ +bundle.js \ No newline at end of file diff --git a/examples/browser/mapper/package.json b/examples/browser/mapper/package.json new file mode 100644 index 0000000000..b9c2724b42 --- /dev/null +++ b/examples/browser/mapper/package.json @@ -0,0 +1,23 @@ +{ + "name": "mapper", + "version": "0.0.0", + "description": "", + "main": "src/index.js", + "scripts": { + "bundle": "browserify src/index.js --require browserify-zlib-next:zlib > public/bundle.js", + "serve": "static public -p 9090 -H '{\"Cache-Control\": \"no-cache, must-revalidate\"}'", + "mon": "nodemon --exec \"npm run start\" --ignore public/bundle.js", + "start": "npm run bundle && npm run serve" + }, + "license": "MIT", + "devDependencies": { + "browserify": "^14.0.0", + "browserify-optional": "^1.0.0", + "browserify-zlib-next": "^1.0.1", + "concat-stream": "^1.6.0", + "detect-dom-ready": "^1.0.2", + "node-static": "^0.7.9", + "nodemon": "^1.11.0" + }, + "dependencies": {} +} diff --git a/examples/browser/mapper/public/index.html b/examples/browser/mapper/public/index.html new file mode 100644 index 0000000000..7d77a39c12 --- /dev/null +++ b/examples/browser/mapper/public/index.html @@ -0,0 +1,14 @@ + + + + + p2p mapper + + +

p2p mapper

+
+
+ + + + diff --git a/examples/browser/mapper/src/create-node.js b/examples/browser/mapper/src/create-node.js new file mode 100644 index 0000000000..6e364560f5 --- /dev/null +++ b/examples/browser/mapper/src/create-node.js @@ -0,0 +1,24 @@ +'use strict' + +const PeerInfo = require('peer-info') +const Node = require('../../../../test/browser-bundle/browser-bundle.js') + +function createNode (callback) { + PeerInfo.create((err, peerInfo) => { + if (err) { + return callback(err) + } + + const peerIdStr = peerInfo.id.toB58String() + const ma = `/libp2p-webrtc-star/dns4/star-signal.cloud.ipfs.team/wss/ipfs/${peerIdStr}` + + peerInfo.multiaddrs.add(ma) + + const node = new Node(peerInfo, undefined, { webRTCStar: true }) + + node.idStr = peerIdStr + callback(null, node) + }) +} + +module.exports = createNode diff --git a/examples/browser/mapper/src/index.js b/examples/browser/mapper/src/index.js new file mode 100644 index 0000000000..7100454b8a --- /dev/null +++ b/examples/browser/mapper/src/index.js @@ -0,0 +1,58 @@ +'use strict' + +const domReady = require('detect-dom-ready') +const createNode = require('./create-node') + +domReady(() => { + const myPeerDiv = document.getElementById('my-peer') + const swarmDiv = document.getElementById('swarm') + + createNode((err, node) => { + if (err) { + return console.log('Could not create the Node, check if your browser has WebRTC Support', err) + } + + node.on('peer:discovery', (peerInfo) => { + console.log('Discovered a peer') + const idStr = peerInfo.id.toB58String() + console.log('Discovered: ' + idStr) + + node.dial(peerInfo, (err, conn) => { + if (err) { return console.log('Failed to dial:', idStr) } + }) + }) + + node.on('peer:connect', (peerInfo) => { + const idStr = peerInfo.id.toB58String() + console.log('Got connection to: ' + idStr) + const connDiv = document.createElement('div') + connDiv.innerHTML = 'Connected to: ' + idStr + connDiv.id = idStr + swarmDiv.append(connDiv) + }) + + node.on('peer:disconnect', (peerInfo) => { + const idStr = peerInfo.id.toB58String() + console.log('Lost connection to: ' + idStr) + document.getElementById(idStr).remove() + }) + + node.start((err) => { + if (err) { + return console.log('WebRTC not supported') + } + + const idStr = node.peerInfo.id.toB58String() + + const idDiv = document + .createTextNode('Node is ready. ID: ' + idStr) + + myPeerDiv.append(idDiv) + + console.log('Node is listening o/') + + // NOTE: to stop the node + // node.stop((err) => {}) + }) + }) +}) diff --git a/examples/node-browser-chat/README.md b/examples/node-browser-chat/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/node-browser-echo/README.md b/examples/node-browser-echo/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/node-chat/.gitkeep b/examples/node-chat/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/node-echo/README.md b/examples/node-echo/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/nodejs/chat/dialer.js b/examples/nodejs/chat/dialer.js new file mode 100644 index 0000000000..88ae5cd4c6 --- /dev/null +++ b/examples/nodejs/chat/dialer.js @@ -0,0 +1,78 @@ +'use strict' +/* eslint-disable no-console */ + +const PeerId = require('peer-id') +const PeerInfo = require('peer-info') +const Node = require('../../../../test/nodejs-bundle/nodejs-bundle.js') +const multiaddr = require('multiaddr') +const pull = require('pull-stream') +const async = require('async') +const Pushable = require('pull-pushable') +const p = Pushable() +let idListener + +async.parallel([ + (callback) => { + PeerId.createFromJSON(require('./peer-id-dialer'), (err, idDialer) => { + if (err) { + throw err + } + callback(null, idDialer) + }) + }, + (callback) => { + PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => { + if (err) { + throw err + } + callback(null, idListener) + }) + } +], (err, ids) => { + if (err) throw err + const peerDialer = new PeerInfo(ids[0]) + peerDialer.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/0')) + const nodeDialer = new Node(peerDialer) + + const peerListener = new PeerInfo(ids[1]) + idListener = ids[1] + peerListener.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/10333')) + nodeDialer.start((err) => { + if (err) { + throw err + } + + console.log('Dialer ready, listening on:') + + peerListener.multiaddrs.forEach((ma) => { + console.log(ma.toString() + '/ipfs/' + idListener.toB58String()) + }) + + nodeDialer.dialByPeerInfo(peerListener, '/chat/1.0.0', (err, conn) => { + if (err) { + throw err + } + console.log('nodeA dialed to nodeB on protocol: /chat/1.0.0') + console.log('Type a message and see what happens') + // Write operation. Data sent as a buffer + pull( + p, + conn + ) + // Sink, data converted from buffer to utf8 string + pull( + conn, + pull.map((data) => { + return data.toString('utf8').replace('\n', '') + }), + pull.drain(console.log) + ) + + process.stdin.setEncoding('utf8') + process.openStdin().on('data', (chunk) => { + var data = chunk.toString() + p.push(data) + }) + }) + }) +}) diff --git a/examples/nodejs/chat/listener.js b/examples/nodejs/chat/listener.js new file mode 100644 index 0000000000..435ab8ff80 --- /dev/null +++ b/examples/nodejs/chat/listener.js @@ -0,0 +1,55 @@ +'use strict' +/* eslint-disable no-console */ + +const PeerId = require('peer-id') +const PeerInfo = require('peer-info') +const Node = require('../../../../test/nodejs-bundle/nodejs-bundle.js') +const multiaddr = require('multiaddr') +const pull = require('pull-stream') +const Pushable = require('pull-pushable') +const p = Pushable() + +PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => { + if (err) { + throw err + } + const peerListener = new PeerInfo(idListener) + peerListener.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/10333')) + const nodeListener = new Node(peerListener) + + nodeListener.start((err) => { + if (err) { + throw err + } + + nodeListener.swarm.on('peer-mux-established', (peerInfo) => { + console.log(peerInfo.id.toB58String()) + }) + + nodeListener.handle('/chat/1.0.0', (protocol, conn) => { + pull( + p, + conn + ) + + pull( + conn, + pull.map((data) => { + return data.toString('utf8').replace('\n', '') + }), + pull.drain(console.log) + ) + + process.stdin.setEncoding('utf8') + process.openStdin().on('data', (chunk) => { + var data = chunk.toString() + p.push(data) + }) + }) + + console.log('Listener ready, listening on:') + peerListener.multiaddrs.forEach((ma) => { + console.log(ma.toString() + '/ipfs/' + idListener.toB58String()) + }) + }) +}) diff --git a/examples/nodejs/chat/peer-id-dialer.json b/examples/nodejs/chat/peer-id-dialer.json new file mode 100644 index 0000000000..5716d74baf --- /dev/null +++ b/examples/nodejs/chat/peer-id-dialer.json @@ -0,0 +1,5 @@ +{ + "id": "Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP", + "privKey": "CAASpwkwggSjAgEAAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAECggEAZnrCJ6IYiLyyRdr9SbKXCNDb4YByGYPEi/HT1aHgIJfFE1PSMjxcdytxfyjP4JJpVtPjiT9JFVU2ddoYu5qJN6tGwjVwgJEWg1UXmPaAw1T/drjS94kVsAs82qICtFmwp52Apg3dBZ0Qwq/8qE1XbG7lLyohIbfCBiL0tiPYMfkcsN9gnFT/kFCX0LVs2pa9fHCRMY9rqCc4/rWJa1w8sMuQ23y4lDaxKF9OZVvOHFQkbBDrkquWHE4r55fchCz/rJklkPJUNENuncBRu0/2X+p4IKFD1DnttXNwb8j4LPiSlLro1T0hiUr5gO2QmdYwXFF63Q3mjQy0+5I4eNbjjQKBgQDZvZy3gUKS/nQNkYfq9za80uLbIj/cWbO+ZZjXCsj0fNIcQFJcKMBoA7DjJvu2S/lf86/41YHkPdmrLAEQAkJ+5BBNOycjYK9minTEjIMMmZDTXXugZ62wnU6F46uLkgEChTqEP57Y6xwwV+JaEDFEsW5N1eE9lEVX9nGIr4phMwKBgQC1TazLuEt1WBx/iUT83ita7obXqoKNzwsS/MWfY2innzYZKDOqeSYZzLtt9uTtp4X4uLyPbYs0qFYhXLsUYMoGHNN8+NdjoyxCjQRJRBkMtaNR0lc5lVDWl3bTuJovjFCgAr9uqJrmI5OHcCIk/cDpdWb3nWaMihVlePmiTcTy9wKBgQCU0u7c1jKkudqks4XM6a+2HAYGdUBk4cLjLhnrUWnNAcuyl5wzdX8dGPi8KZb+IKuQE8WBNJ2VXVj7kBYh1QmSJVunDflQSvNYCOaKuOeRoxzD+y9Wkca74qkbBmPn/6FFEb7PSZTO+tPHjyodGNgz9XpJJRjQuBk1aDJtlF3m1QKBgE5SAr5ym65SZOU3UGUIOKRsfDW4Q/OsqDUImvpywCgBICaX9lHDShFFHwau7FA52ScL7vDquoMB4UtCOtLfyQYA9995w9oYCCurrVlVIJkb8jSLcADBHw3EmqF1kq3NqJqm9TmBfoDCh52vdCCUufxgKh33kfBOSlXuf7B8dgMbAoGAZ3r0/mBQX6S+s5+xCETMTSNv7TQzxgtURIpVs+ZVr2cMhWhiv+n0Omab9X9Z50se8cWl5lkvx8vn3D/XHHIPrMF6qk7RAXtvReb+PeitNvm0odqjFv0J2qki6fDs0HKwq4kojAXI1Md8Th0eobNjsy21fEEJT7uKMJdovI/SErI=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAE=" +} diff --git a/examples/nodejs/chat/peer-id-listener.json b/examples/nodejs/chat/peer-id-listener.json new file mode 100644 index 0000000000..7acb97c8c1 --- /dev/null +++ b/examples/nodejs/chat/peer-id-listener.json @@ -0,0 +1,5 @@ +{ + "id": "QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm", + "privKey": "CAASqAkwggSkAgEAAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAECggEAQj0obPnVyjxLFZFnsFLgMHDCv9Fk5V5bOYtmxfvcm50us6ye+T8HEYWGUa9RrGmYiLweuJD34gLgwyzE1RwptHPj3tdNsr4NubefOtXwixlWqdNIjKSgPlaGULQ8YF2tm/kaC2rnfifwz0w1qVqhPReO5fypL+0ShyANVD3WN0Fo2ugzrniCXHUpR2sHXSg6K+2+qWdveyjNWog34b7CgpV73Ln96BWae6ElU8PR5AWdMnRaA9ucA+/HWWJIWB3Fb4+6uwlxhu2L50Ckq1gwYZCtGw63q5L4CglmXMfIKnQAuEzazq9T4YxEkp+XDnVZAOgnQGUBYpetlgMmkkh9qQKBgQDvsEs0ThzFLgnhtC2Jy//ZOrOvIAKAZZf/mS08AqWH3L0/Rjm8ZYbLsRcoWU78sl8UFFwAQhMRDBP9G+RPojWVahBL/B7emdKKnFR1NfwKjFdDVaoX5uNvZEKSl9UubbC4WZJ65u/cd5jEnj+w3ir9G8n+P1gp/0yBz02nZXFgSwKBgQDZPQr4HBxZL7Kx7D49ormIlB7CCn2i7mT11Cppn5ifUTrp7DbFJ2t9e8UNk6tgvbENgCKXvXWsmflSo9gmMxeEOD40AgAkO8Pn2R4OYhrwd89dECiKM34HrVNBzGoB5+YsAno6zGvOzLKbNwMG++2iuNXqXTk4uV9GcI8OnU5ZPQKBgCZUGrKSiyc85XeiSGXwqUkjifhHNh8yH8xPwlwGUFIZimnD4RevZI7OEtXw8iCWpX2gg9XGuyXOuKORAkF5vvfVriV4e7c9Ad4Igbj8mQFWz92EpV6NHXGCpuKqRPzXrZrNOA9PPqwSs+s9IxI1dMpk1zhBCOguWx2m+NP79NVhAoGBAI6WSoTfrpu7ewbdkVzTWgQTdLzYNe6jmxDf2ZbKclrf7lNr/+cYIK2Ud5qZunsdBwFdgVcnu/02czeS42TvVBgs8mcgiQc/Uy7yi4/VROlhOnJTEMjlU2umkGc3zLzDgYiRd7jwRDLQmMrYKNyEr02HFKFn3w8kXSzW5I8rISnhAoGBANhchHVtJd3VMYvxNcQb909FiwTnT9kl9pkjhwivx+f8/K8pDfYCjYSBYCfPTM5Pskv5dXzOdnNuCj6Y2H/9m2SsObukBwF0z5Qijgu1DsxvADVIKZ4rzrGb4uSEmM6200qjJ/9U98fVM7rvOraakrhcf9gRwuspguJQnSO9cLj6", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAE=" +} diff --git a/examples/nodejs/echo/dialer.js b/examples/nodejs/echo/dialer.js new file mode 100644 index 0000000000..c3093292f6 --- /dev/null +++ b/examples/nodejs/echo/dialer.js @@ -0,0 +1,57 @@ +'use strict' +/* eslint-disable no-console */ + +/* + * Dialer Node + */ + +const PeerId = require('peer-id') +const PeerInfo = require('peer-info') +const Node = require('../../../../test/nodejs-bundle/nodejs-bundle.js') +const multiaddr = require('multiaddr') +const pull = require('pull-stream') +const async = require('async') + +async.parallel([ + (cb) => PeerId.createFromJSON(require('./id-d'), cb), + (cb) => PeerId.createFromJSON(require('./id-l'), cb) +], (err, ids) => { + if (err) { throw err } + + // Dialer + const dialerId = ids[0] + const dialerPeerInfo = new PeerInfo(dialerId) + dialerPeerInfo.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/0')) + const dialerNode = new Node(dialerPeerInfo) + + // Peer to Dial + const listenerPeerInfo = new PeerInfo(ids[1]) + const listenerId = ids[1] + const listenerMultiaddr = multiaddr('/ip4/127.0.0.1/tcp/10333/ipfs/' + + listenerId.toB58String()) + listenerPeerInfo.multiaddr.add(listenerMultiaddr) + + dialerNode.start((err) => { + if (err) { throw err } + + console.log('Dialer ready, listening on:') + dialerPeerInfo.multiaddrs.forEach((ma) => console.log(ma.toString() + + '/ipfs/' + dialerId.toB58String())) + + console.log('Dialing to peer:', listenerMultiaddr.toString()) + dialerNode.dialByPeerInfo(listenerPeerInfo, '/echo/1.0.0', (err, conn) => { + if (err) { throw err } + + console.log('nodeA dialed to nodeB on protocol: /echo/1.0.0') + + pull( + pull.values(['hey']), + conn, + pull.collect((err, data) => { + if (err) { throw err } + console.log('received echo:', data.toString()) + }) + ) + }) + }) +}) diff --git a/examples/nodejs/echo/id-d.json b/examples/nodejs/echo/id-d.json new file mode 100644 index 0000000000..5716d74baf --- /dev/null +++ b/examples/nodejs/echo/id-d.json @@ -0,0 +1,5 @@ +{ + "id": "Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP", + "privKey": "CAASpwkwggSjAgEAAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAECggEAZnrCJ6IYiLyyRdr9SbKXCNDb4YByGYPEi/HT1aHgIJfFE1PSMjxcdytxfyjP4JJpVtPjiT9JFVU2ddoYu5qJN6tGwjVwgJEWg1UXmPaAw1T/drjS94kVsAs82qICtFmwp52Apg3dBZ0Qwq/8qE1XbG7lLyohIbfCBiL0tiPYMfkcsN9gnFT/kFCX0LVs2pa9fHCRMY9rqCc4/rWJa1w8sMuQ23y4lDaxKF9OZVvOHFQkbBDrkquWHE4r55fchCz/rJklkPJUNENuncBRu0/2X+p4IKFD1DnttXNwb8j4LPiSlLro1T0hiUr5gO2QmdYwXFF63Q3mjQy0+5I4eNbjjQKBgQDZvZy3gUKS/nQNkYfq9za80uLbIj/cWbO+ZZjXCsj0fNIcQFJcKMBoA7DjJvu2S/lf86/41YHkPdmrLAEQAkJ+5BBNOycjYK9minTEjIMMmZDTXXugZ62wnU6F46uLkgEChTqEP57Y6xwwV+JaEDFEsW5N1eE9lEVX9nGIr4phMwKBgQC1TazLuEt1WBx/iUT83ita7obXqoKNzwsS/MWfY2innzYZKDOqeSYZzLtt9uTtp4X4uLyPbYs0qFYhXLsUYMoGHNN8+NdjoyxCjQRJRBkMtaNR0lc5lVDWl3bTuJovjFCgAr9uqJrmI5OHcCIk/cDpdWb3nWaMihVlePmiTcTy9wKBgQCU0u7c1jKkudqks4XM6a+2HAYGdUBk4cLjLhnrUWnNAcuyl5wzdX8dGPi8KZb+IKuQE8WBNJ2VXVj7kBYh1QmSJVunDflQSvNYCOaKuOeRoxzD+y9Wkca74qkbBmPn/6FFEb7PSZTO+tPHjyodGNgz9XpJJRjQuBk1aDJtlF3m1QKBgE5SAr5ym65SZOU3UGUIOKRsfDW4Q/OsqDUImvpywCgBICaX9lHDShFFHwau7FA52ScL7vDquoMB4UtCOtLfyQYA9995w9oYCCurrVlVIJkb8jSLcADBHw3EmqF1kq3NqJqm9TmBfoDCh52vdCCUufxgKh33kfBOSlXuf7B8dgMbAoGAZ3r0/mBQX6S+s5+xCETMTSNv7TQzxgtURIpVs+ZVr2cMhWhiv+n0Omab9X9Z50se8cWl5lkvx8vn3D/XHHIPrMF6qk7RAXtvReb+PeitNvm0odqjFv0J2qki6fDs0HKwq4kojAXI1Md8Th0eobNjsy21fEEJT7uKMJdovI/SErI=", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAE=" +} diff --git a/examples/nodejs/echo/id-l.json b/examples/nodejs/echo/id-l.json new file mode 100644 index 0000000000..7acb97c8c1 --- /dev/null +++ b/examples/nodejs/echo/id-l.json @@ -0,0 +1,5 @@ +{ + "id": "QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm", + "privKey": "CAASqAkwggSkAgEAAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAECggEAQj0obPnVyjxLFZFnsFLgMHDCv9Fk5V5bOYtmxfvcm50us6ye+T8HEYWGUa9RrGmYiLweuJD34gLgwyzE1RwptHPj3tdNsr4NubefOtXwixlWqdNIjKSgPlaGULQ8YF2tm/kaC2rnfifwz0w1qVqhPReO5fypL+0ShyANVD3WN0Fo2ugzrniCXHUpR2sHXSg6K+2+qWdveyjNWog34b7CgpV73Ln96BWae6ElU8PR5AWdMnRaA9ucA+/HWWJIWB3Fb4+6uwlxhu2L50Ckq1gwYZCtGw63q5L4CglmXMfIKnQAuEzazq9T4YxEkp+XDnVZAOgnQGUBYpetlgMmkkh9qQKBgQDvsEs0ThzFLgnhtC2Jy//ZOrOvIAKAZZf/mS08AqWH3L0/Rjm8ZYbLsRcoWU78sl8UFFwAQhMRDBP9G+RPojWVahBL/B7emdKKnFR1NfwKjFdDVaoX5uNvZEKSl9UubbC4WZJ65u/cd5jEnj+w3ir9G8n+P1gp/0yBz02nZXFgSwKBgQDZPQr4HBxZL7Kx7D49ormIlB7CCn2i7mT11Cppn5ifUTrp7DbFJ2t9e8UNk6tgvbENgCKXvXWsmflSo9gmMxeEOD40AgAkO8Pn2R4OYhrwd89dECiKM34HrVNBzGoB5+YsAno6zGvOzLKbNwMG++2iuNXqXTk4uV9GcI8OnU5ZPQKBgCZUGrKSiyc85XeiSGXwqUkjifhHNh8yH8xPwlwGUFIZimnD4RevZI7OEtXw8iCWpX2gg9XGuyXOuKORAkF5vvfVriV4e7c9Ad4Igbj8mQFWz92EpV6NHXGCpuKqRPzXrZrNOA9PPqwSs+s9IxI1dMpk1zhBCOguWx2m+NP79NVhAoGBAI6WSoTfrpu7ewbdkVzTWgQTdLzYNe6jmxDf2ZbKclrf7lNr/+cYIK2Ud5qZunsdBwFdgVcnu/02czeS42TvVBgs8mcgiQc/Uy7yi4/VROlhOnJTEMjlU2umkGc3zLzDgYiRd7jwRDLQmMrYKNyEr02HFKFn3w8kXSzW5I8rISnhAoGBANhchHVtJd3VMYvxNcQb909FiwTnT9kl9pkjhwivx+f8/K8pDfYCjYSBYCfPTM5Pskv5dXzOdnNuCj6Y2H/9m2SsObukBwF0z5Qijgu1DsxvADVIKZ4rzrGb4uSEmM6200qjJ/9U98fVM7rvOraakrhcf9gRwuspguJQnSO9cLj6", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAE=" +} diff --git a/examples/nodejs/echo/listener.js b/examples/nodejs/echo/listener.js new file mode 100644 index 0000000000..812dfc43d3 --- /dev/null +++ b/examples/nodejs/echo/listener.js @@ -0,0 +1,46 @@ +'use strict' +/* eslint-disable no-console */ + +/* + * Listener Node + */ + +const PeerId = require('peer-id') +const PeerInfo = require('peer-info') +const Node = require('../../../../test/nodejs-bundle/nodejs-bundle.js') +const multiaddr = require('multiaddr') +const pull = require('pull-stream') +const series = require('async/series') + +let listenerId +let listenerNode + +series([ + (cb) => { + PeerId.createFromJSON(require('./id-l'), (err, id) => { + if (err) { return cb(err) } + listenerId = id + cb() + }) + }, + (cb) => { + const listenerPeerInfo = new PeerInfo(listenerId) + listenerPeerInfo.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/10333')) + listenerNode = new Node(listenerPeerInfo) + + listenerNode.swarm.on('peer-mux-established', (peerInfo) => { + console.log('received dial to me from:', peerInfo.id.toB58String()) + }) + + listenerNode.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) + + listenerNode.start(cb) + } +], (err) => { + if (err) { throw err } + + console.log('Listener ready, listening on:') + listenerNode.peerInfo.multiaddrs.forEach((ma) => { + console.log(ma.toString() + '/ipfs/' + listenerId.toB58String()) + }) +}) diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000000..694c65b55c --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,55 @@ +'use strict' + +const gulp = require('gulp') +const Node = require('./test/nodejs-bundle/nodejs-bundle.js') +const PeerInfo = require('peer-info') +const PeerId = require('peer-id') +const pull = require('pull-stream') + +const sigServer = require('libp2p-webrtc-star/src/sig-server') +let server + +let node +const rawPeer = require('./test/browser-bundle/peer.json') + +gulp.task('libnode:start', (done) => { + let count = 0 + const ready = () => ++count === 2 ? done() : null + + sigServer.start({ port: 15555 }, (err, _server) => { + if (err) { + throw err + } + server = _server + ready() + }) + + PeerId.createFromJSON(rawPeer, (err, peerId) => { + if (err) { + return done(err) + } + const peer = new PeerInfo(peerId) + + peer.multiaddrs.add('/ip4/127.0.0.1/tcp/9200/ws') + + node = new Node(peer) + node.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) + node.start(() => ready()) + }) +}) + +gulp.task('libnode:stop', (done) => { + setTimeout(() => node.stop((err) => { + if (err) { + return done(err) + } + server.stop(done) + }), 2000) +}) + +gulp.task('test:browser:before', ['libnode:start']) +gulp.task('test:node:before', ['libnode:start']) +gulp.task('test:browser:after', ['libnode:stop']) +gulp.task('test:node:after', ['libnode:stop']) + +require('aegir/gulp')(gulp) diff --git a/package.json b/package.json index d8372ce1f5..f847287de1 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,14 @@ "description": "JavaScript Skeleton for libp2p bundles", "main": "src/index.js", "scripts": { - "test": "aegir-test node", + "test": "gulp test", + "test:node": "gulp test:node", + "test:browser": "gulp test:browser --dom", + "release": "gulp release --dom", + "release-minor": "gulp release --type minor --dom", + "release-major": "gulp release --type major --dom", + "build": "gulp build", "lint": "aegir-lint", - "release": "aegir-release node", - "release-minor": "aegir-release --type minor", - "release-major": "aegir-release --type major", "coverage": "aegir-coverage", "coverage-publish": "aegir-coverage publish" }, @@ -34,13 +37,32 @@ }, "homepage": "https://github.com/libp2p/js-libp2p", "devDependencies": { - "aegir": "^11.0.1", - "chai": "^3.5.0", - "dirty-chai": "^1.2.2", - "pre-commit": "^1.2.2" + "aegir": "^11.0.2", + "chai": "^4.0.2", + "cids": "^0.5.0", + "dirty-chai": "^2.0.0", + "electron-webrtc": "^0.3.0", + "libp2p-kad-dht": "^0.1.0", + "libp2p-mdns": "^0.7.0", + "libp2p-multiplex": "^0.4.3", + "libp2p-railing": "^0.5.1", + "libp2p-secio": "^0.6.8", + "libp2p-spdy": "^0.10.6", + "libp2p-swarm": "^0.29.1", + "libp2p-tcp": "^0.10.1", + "libp2p-webrtc-star": "^0.11.0", + "libp2p-websockets": "^0.10.0", + "lodash.times": "^4.3.2", + "mafmt": "^2.1.8", + "pre-commit": "^1.2.2", + "pull-goodbye": "0.0.2", + "pull-serializer": "^0.3.2", + "pull-stream": "^3.6.0", + "safe-buffer": "^5.1.1", + "wrtc": "0.0.62" }, "dependencies": { - "async": "^2.4.1", + "async": "^2.5.0", "libp2p-ping": "~0.3.2", "libp2p-swarm": "~0.29.1", "mafmt": "^2.1.8", @@ -56,4 +78,4 @@ "greenkeeperio-bot ", "mayerwin " ] -} \ No newline at end of file +} diff --git a/test/index.spec.js b/test/base.js similarity index 100% rename from test/index.spec.js rename to test/base.js diff --git a/test/browser-bundle/browser-bundle.js b/test/browser-bundle/browser-bundle.js new file mode 100644 index 0000000000..9d80ad1948 --- /dev/null +++ b/test/browser-bundle/browser-bundle.js @@ -0,0 +1,67 @@ +'use strict' + +const WS = require('libp2p-websockets') +const WebRTCStar = require('libp2p-webrtc-star') +const spdy = require('libp2p-spdy') +const multiplex = require('libp2p-multiplex') +const secio = require('libp2p-secio') +const Railing = require('libp2p-railing') +const libp2p = require('../..') + +function mapMuxers (list) { + return list.map((pref) => { + if (typeof pref !== 'string') { + return pref + } + switch (pref.trim().toLowerCase()) { + case 'spdy': + return spdy + case 'multiplex': + return multiplex + default: + throw new Error(pref + ' muxer not available') + } + }) +} + +function getMuxers (options) { + if (options) { + return mapMuxers(options) + } else { + return [multiplex, spdy] + } +} + +class Node extends libp2p { + constructor (peerInfo, peerBook, options) { + options = options || {} + const webRTCStar = new WebRTCStar() + + const modules = { + transport: [ + new WS(), + webRTCStar + ], + connection: { + muxer: getMuxers(options.muxer), + crypto: [ + secio + ] + }, + discovery: [] + } + + if (options.webRTCStar) { + modules.discovery.push(webRTCStar.discovery) + } + + if (options.bootstrap) { + const r = new Railing(options.bootstrap) + modules.discovery.push(r) + } + + super(modules, peerInfo, peerBook, options) + } +} + +module.exports = Node diff --git a/test/browser-bundle/peer.json b/test/browser-bundle/peer.json new file mode 100644 index 0000000000..ba789ca32d --- /dev/null +++ b/test/browser-bundle/peer.json @@ -0,0 +1,5 @@ +{ + "id": "Qmex1SSsueWFsUfjdkugJ5zhcnjddAt8TxcnDLUXKD9Sx7", + "privKey": "CAASqAkwggSkAgEAAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAECggEAdBUzV/GaQ0nmoQrWvOnUxmFIho7kCjkh1NwnNVPNc+Msa1r7pcI9wJNPwap8j1w4L/cZuYhOJgcg+o2mWFiuULKZ4F9Ro/M89gZ038457g2/2pPu43c/Xoi/2YcAHXg0Gr+OCe2zCIyITBWKAFqyAzL6DubAxrJW2Ezj1LrZ+EZgMyzbh/go/eEGSJaaGkINeAkY144DqDWWWvzyhKhryipsGkZGEkVy9xJgMEI3ipVvuPez2XAvoyyeuinBBLe+Z2vY5G50XXzbIMhIQGLncHf9MwTv6wt1ilyOSLOXK0BoQbB76J3R3is5dSULXXP9r8VocjLBEkmBuf4FXAKzoQKBgQDNNS4F1XE1gxD8LPkL+aB/hi6eVHVPhr+w0I/9ATikcLGeUfBM2Gd6cZRPFtNVrv1p6ZF1D1UyGDknGbDBSQd9wLUgb0fDoo3jKYMGWq6G+VvaP5rzWQeBV8YV2EhSmUk1i6kiYe2ZE8WyrPie7iwpQIY60e2A8Ly0GKZiBZUcHQKBgQC9YDAVsGnEHFVFkTDpvw5HwEzCgTb2A3NgkGY3rTYZ7L6AFjqCYmUwFB8Fmbyc4kdFWNh8wfmq5Qrvl49NtaeukiqWKUUlB8uPdztB1P0IahA2ks0owStZlRifmwfgYyMd4xE17lhaOgQQJZZPxmP0F6mdOvb3YJafNURCdMS51wKBgEvvIM+h0tmFXXSjQ6kNvzlRMtD92ccKysYn9xAdMpOO6/r0wSH+dhQWEVZO0PcE4NsfReb2PIVj90ojtIdhebcr5xpQc1LORQjJJKXmSmzBux6AqNrhl+hhzXfp56FA/Zkly/lgGWaqrV5XqUxOP+Mn8EO1yNgMvRc7g94DyNB1AoGBAKLBuXHalXwDsdHBUB2Eo3xNLGt6bEcRfia+0+sEBdxQGQWylQScFkU09dh1YaIf44sZKa5HdBFJGpYCVxo9hmjFnK5Dt/Z0daHOonIY4INLzLVqg8KECoLKXkhGEIXsDjFQhukn+G1LMVTDSSU055DQiWjlVX4UWD9qo0jOXIkvAoGBAMP50p2X6PsWWZUuuR7i1JOJHRyQZPWdHh9p8SSLnCtEpHYZfJr4INXNmhnSiB/3TUnHix2vVKjosjMTCk/CjfzXV2H41WPOLZ2/Pi3SxCicWIRj4kCcWhkEuIF2jGkg1+jmNiCl/zNMaBOAIP3QbDPtqOWbYlPd2YIzdj6WQ6R4", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAE=" +} diff --git a/test/browser-bundle/webrtc-star-only.js b/test/browser-bundle/webrtc-star-only.js new file mode 100644 index 0000000000..6fbe112f84 --- /dev/null +++ b/test/browser-bundle/webrtc-star-only.js @@ -0,0 +1,121 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +chai.use(require('dirty-chai')) +const expect = chai.expect +const PeerInfo = require('peer-info') +const PeerId = require('peer-id') +const parallel = require('async/parallel') +const pull = require('pull-stream') + +const Node = require('./browser-bundle') + +describe('libp2p-ipfs-browser (webrtc only)', () => { + let peer1 + let peer2 + let node1 + let node2 + + it('create two peerInfo with webrtc-star addrs', (done) => { + parallel([ + (cb) => PeerId.create({ bits: 1024 }, cb), + (cb) => PeerId.create({ bits: 1024 }, cb) + ], (err, ids) => { + expect(err).to.not.exist() + + peer1 = new PeerInfo(ids[0]) + const ma1 = '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + ids[0].toB58String() + peer1.multiaddrs.add(ma1) + + peer2 = new PeerInfo(ids[1]) + const ma2 = '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + ids[1].toB58String() + peer2.multiaddrs.add(ma2) + + done() + }) + }) + + it('create two libp2p nodes with those peers', (done) => { + node1 = new Node(peer1, null, { webRTCStar: true }) + node2 = new Node(peer2, null, { webRTCStar: true }) + done() + }) + + it('listen on the two libp2p nodes', (done) => { + parallel([ + (cb) => node1.start(cb), + (cb) => node2.start(cb) + ], done) + }) + + it('handle a protocol on the first node', () => { + node2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) + }) + + it('dial from the second node to the first node', (done) => { + node1.dial(peer2, '/echo/1.0.0', (err, conn) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + const text = 'hello' + const peers1 = node1.peerBook.getAll() + expect(Object.keys(peers1)).to.have.length(1) + + const peers2 = node2.peerBook.getAll() + expect(Object.keys(peers2)).to.have.length(1) + + pull( + pull.values([Buffer(text)]), + conn, + pull.collect((err, data) => { + expect(err).to.not.exist() + expect(data[0].toString()).to.equal(text) + done() + }) + ) + } + }) + }) + + it('node1 hangUp node2', (done) => { + node1.hangUp(peer2, (err) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + const peers = node1.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(node1.swarm.muxedConns)).to.have.length(0) + done() + } + }) + }) + + it('create a third node and check that discovery works', (done) => { + let counter = 0 + + function check () { + if (++counter === 3) { + expect(Object.keys(node1.swarm.muxedConns).length).to.equal(1) + expect(Object.keys(node2.swarm.muxedConns).length).to.equal(1) + done() + } + } + + PeerId.create((err, id3) => { + expect(err).to.not.exist() + + const peer3 = new PeerInfo(id3) + const ma3 = '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + id3.toB58String() + peer3.multiaddrs.add(ma3) + + node1.on('peer:discovery', (peerInfo) => node1.dial(peerInfo, check)) + node2.on('peer:discovery', (peerInfo) => node2.dial(peerInfo, check)) + + const node3 = new Node(peer3, null, { webRTCStar: true }) + node3.start(check) + }) + }) +}) diff --git a/test/browser-bundle/websockets-only.js b/test/browser-bundle/websockets-only.js new file mode 100644 index 0000000000..34b7ea3aaa --- /dev/null +++ b/test/browser-bundle/websockets-only.js @@ -0,0 +1,201 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +chai.use(require('dirty-chai')) +const expect = chai.expect +const PeerInfo = require('peer-info') +const PeerId = require('peer-id') +const pull = require('pull-stream') +const goodbye = require('pull-goodbye') +const serializer = require('pull-serializer') +const Buffer = require('safe-buffer').Buffer + +const Node = require('./browser-bundle') +const rawPeer = require('./peer.json') + +describe('libp2p-ipfs-browser (websockets only)', () => { + let peerB + let nodeA + + before((done) => { + const ma = '/ip4/127.0.0.1/tcp/9200/ws/ipfs/' + rawPeer.id + + PeerId.createFromPrivKey(rawPeer.privKey, (err, id) => { + if (err) { + return done(err) + } + + peerB = new PeerInfo(id) + peerB.multiaddrs.add(ma) + done() + }) + }) + + after((done) => nodeA.stop(done)) + + it('create libp2pNode', (done) => { + PeerInfo.create((err, peerInfo) => { + expect(err).to.not.exist() + peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') + + nodeA = new Node(peerInfo) + done() + }) + }) + + it('create libp2pNode with multiplex only', (done) => { + PeerInfo.create((err, peerInfo) => { + expect(err).to.not.exist() + + const b = new Node(peerInfo, null, { muxer: ['multiplex'] }) + expect(b.modules.connection.muxer).to.eql([require('libp2p-multiplex')]) + done() + }) + }) + + it('start libp2pNode', (done) => { + nodeA.start(done) + }) + + // General connectivity tests + + it('libp2p.dial using Multiaddr nodeA to nodeB', (done) => { + nodeA.dial(peerB.multiaddrs.toArray()[0], (err) => { + expect(err).to.not.exist() + + setTimeout(check, 500) // Some time for Identify to finish + + function check () { + const peers = nodeA.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + done() + } + }) + }) + + it('libp2p.dial using Multiaddr on Protocol nodeA to nodeB', (done) => { + nodeA.dial(peerB.multiaddrs.toArray()[0], '/echo/1.0.0', (err, conn) => { + expect(err).to.not.exist() + + const peers = nodeA.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + + pull( + pull.values([Buffer.from('hey')]), + conn, + pull.collect((err, data) => { + expect(err).to.not.exist() + expect(data).to.eql([Buffer.from('hey')]) + done() + }) + ) + }) + }) + + it('libp2p.hangUp using Multiaddr nodeA to nodeB', (done) => { + nodeA.hangUp(peerB.multiaddrs.toArray()[0], (err) => { + expect(err).to.not.exist() + + setTimeout(check, 500) + + function check () { + const peers = nodeA.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) + done() + } + }) + }) + + it('libp2p.dial using PeerInfo nodeA to nodeB', (done) => { + nodeA.dial(peerB, (err) => { + expect(err).to.not.exist() + + setTimeout(check, 500) // Some time for Identify to finish + + function check () { + const peers = nodeA.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + done() + } + }) + }) + + it('libp2p.dial using PeerInfo on Protocol nodeA to nodeB', (done) => { + nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => { + expect(err).to.not.exist() + const peers = nodeA.peerBook.getAll() + expect(err).to.not.exist() + expect(Object.keys(peers)).to.have.length(1) + + pull( + pull.values([Buffer.from('hey')]), + conn, + pull.collect((err, data) => { + expect(err).to.not.exist() + expect(data).to.eql([Buffer.from('hey')]) + done() + }) + ) + }) + }) + + it('libp2p.hangUp using PeerInfo nodeA to nodeB', (done) => { + nodeA.hangUp(peerB, (err) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + const peers = nodeA.peerBook.getAll() + expect(err).to.not.exist() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) + done() + } + }) + }) + + describe('stress', () => { + it('one big write', (done) => { + nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => { + expect(err).to.not.exist() + const rawMessage = Buffer.alloc(100000) + rawMessage.fill('a') + + const s = serializer(goodbye({ + source: pull.values([rawMessage]), + sink: pull.collect((err, results) => { + expect(err).to.not.exist() + expect(results).to.have.length(1) + expect(Buffer.from(results[0])).to.have.length(rawMessage.length) + done() + }) + })) + pull(s, conn, s) + }) + }) + + it('many writes', (done) => { + nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => { + expect(err).to.not.exist() + + const s = serializer(goodbye({ + source: pull( + pull.infinite(), + pull.take(1000), + pull.map((val) => Buffer.from(val.toString())) + ), + sink: pull.collect((err, result) => { + expect(err).to.not.exist() + expect(result).to.have.length(1000) + done() + }) + })) + + pull(s, conn, s) + }) + }) + }) +}) diff --git a/test/browser.js b/test/browser.js new file mode 100644 index 0000000000..6bbaf816a9 --- /dev/null +++ b/test/browser.js @@ -0,0 +1,7 @@ +'use strict' + +const w = require('webrtcsupport') + +require('./base') +require('./browser-bundle/websockets-only') +if (w.support) { require('./browser-bundle/webrtc-star-only') } diff --git a/test/node.js b/test/node.js new file mode 100644 index 0000000000..f66c017b50 --- /dev/null +++ b/test/node.js @@ -0,0 +1,10 @@ +'use strict' + +require('./base') +require('./nodejs-bundle/tcp') +require('./nodejs-bundle/tcp+websockets') +require('./nodejs-bundle/tcp+websockets+webrtc-star') +require('./nodejs-bundle/stream-muxing') +require('./nodejs-bundle/discovery') +require('./nodejs-bundle/peer-routing') +require('./nodejs-bundle/content-routing') diff --git a/test/nodejs-bundle/content-routing.js b/test/nodejs-bundle/content-routing.js new file mode 100644 index 0000000000..6a46a2aaa1 --- /dev/null +++ b/test/nodejs-bundle/content-routing.js @@ -0,0 +1,90 @@ +/* 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 parallel = require('async/parallel') +const utils = require('./utils') +const createNode = utils.createNode +const _times = require('lodash.times') +const CID = require('cids') + +describe('.contentRouting', () => { + let nodeA + let nodeB + let nodeC + let nodeD + let nodeE + + before((done) => { + const tasks = _times(5, () => (cb) => { + createNode('/ip4/0.0.0.0/tcp/0', { + mdns: false, + dht: true + }, (err, node) => { + expect(err).to.not.exist() + node.start((err) => cb(err, node)) + }) + }) + + parallel(tasks, (err, nodes) => { + expect(err).to.not.exist() + nodeA = nodes[0] + nodeB = nodes[1] + nodeC = nodes[2] + nodeD = nodes[3] + nodeE = nodes[4] + + parallel([ + (cb) => nodeA.dial(nodeB.peerInfo, cb), + (cb) => nodeB.dial(nodeC.peerInfo, cb), + (cb) => nodeC.dial(nodeD.peerInfo, cb), + (cb) => nodeD.dial(nodeE.peerInfo, cb), + (cb) => nodeE.dial(nodeA.peerInfo, cb) + ], done) + }) + }) + + after((done) => { + parallel([ + (cb) => nodeA.stop(cb), + (cb) => nodeB.stop(cb), + (cb) => nodeC.stop(cb), + (cb) => nodeD.stop(cb), + (cb) => nodeE.stop(cb) + ], done) + }) + + describe('le ring', () => { + const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL') + + it('let kbucket get filled', (done) => { + setTimeout(() => done(), 250) + }) + + it('nodeA.contentRouting.provide', (done) => { + nodeA.contentRouting.provide(cid, done) + }) + + it('nodeE.contentRouting.findProviders for existing record', (done) => { + nodeE.contentRouting.findProviders(cid, 5000, (err, providers) => { + expect(err).to.not.exist() + expect(providers).to.have.length.above(0) + done() + }) + }) + + it('nodeC.contentRouting.findProviders for non existing record (timeout)', (done) => { + const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSnnnn') + + nodeE.contentRouting.findProviders(cid, 5000, (err, providers) => { + expect(err).to.not.exist() + expect(providers).to.have.length(0) + done() + }) + }) + }) +}) diff --git a/test/nodejs-bundle/discovery.js b/test/nodejs-bundle/discovery.js new file mode 100644 index 0000000000..4cdab4560c --- /dev/null +++ b/test/nodejs-bundle/discovery.js @@ -0,0 +1,99 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +chai.use(require('dirty-chai')) +const expect = chai.expect +const signalling = require('libp2p-webrtc-star/src/sig-server') +const parallel = require('async/parallel') +const utils = require('./utils') +const createNode = utils.createNode +const echo = utils.echo + +describe('discovery', () => { + let nodeA + let nodeB + let port = 24642 + let ss + + function setup (options) { + before((done) => { + port++ + parallel([ + (cb) => { + signalling.start({ port: port }, (err, server) => { + expect(err).to.not.exist() + ss = server + cb() + }) + }, + (cb) => createNode([ + '/ip4/0.0.0.0/tcp/0', + `/libp2p-webrtc-star/ip4/127.0.0.1/tcp/${port}/ws` + ], options, (err, node) => { + expect(err).to.not.exist() + nodeA = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + (cb) => createNode([ + '/ip4/0.0.0.0/tcp/0', + `/libp2p-webrtc-star/ip4/127.0.0.1/tcp/${port}/ws` + ], options, (err, node) => { + expect(err).to.not.exist() + nodeB = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }) + ], done) + }) + + after((done) => { + parallel([ + (cb) => nodeA.stop(cb), + (cb) => nodeB.stop(cb), + (cb) => ss.stop(done) + ], done) + }) + } + + describe('MulticastDNS', () => { + setup({ mdns: true }) + + it('find a peer', (done) => { + nodeA.once('peer:discovery', (peerInfo) => { + expect(nodeB.peerInfo.id.toB58String()) + .to.eql(peerInfo.id.toB58String()) + done() + }) + }) + }) + + // TODO needs a delay (this test is already long) + describe.skip('WebRTCStar', () => { + setup({ webRTCStar: true }) + + it('find a peer', (done) => { + nodeA.once('peer:discovery', (peerInfo) => { + expect(nodeB.peerInfo.id.toB58String()) + .to.eql(peerInfo.id.toB58String()) + done() + }) + }) + }) + + describe('MulticastDNS + WebRTCStar', () => { + setup({ + webRTCStar: true, + mdns: true + }) + + it('find a peer', (done) => { + nodeA.once('peer:discovery', (peerInfo) => { + expect(nodeB.peerInfo.id.toB58String()) + .to.eql(peerInfo.id.toB58String()) + done() + }) + }) + }) +}) diff --git a/test/nodejs-bundle/nodejs-bundle.js b/test/nodejs-bundle/nodejs-bundle.js new file mode 100644 index 0000000000..5d67075210 --- /dev/null +++ b/test/nodejs-bundle/nodejs-bundle.js @@ -0,0 +1,80 @@ +'use strict' + +const TCP = require('libp2p-tcp') +const MulticastDNS = require('libp2p-mdns') +const WS = require('libp2p-websockets') +const Railing = require('libp2p-railing') +const spdy = require('libp2p-spdy') +const KadDHT = require('libp2p-kad-dht') +const multiplex = require('libp2p-multiplex') +const secio = require('libp2p-secio') +const libp2p = require('../..') + +function mapMuxers (list) { + return list.map((pref) => { + if (typeof pref !== 'string') { + return pref + } + switch (pref.trim().toLowerCase()) { + case 'spdy': return spdy + case 'multiplex': return multiplex + default: + throw new Error(pref + ' muxer not available') + } + }) +} + +function getMuxers (muxers) { + const muxerPrefs = process.env.LIBP2P_MUXER + if (muxerPrefs && !muxers) { + return mapMuxers(muxerPrefs.split(',')) + } else if (muxers) { + return mapMuxers(muxers) + } else { + return [multiplex, spdy] + } +} + +class Node extends libp2p { + constructor (peerInfo, peerBook, options) { + options = options || {} + + const modules = { + transport: [ + new TCP(), + new WS() + ], + connection: { + muxer: getMuxers(options.muxer), + crypto: [ secio ] + }, + discovery: [] + } + + if (options.dht) { + modules.DHT = KadDHT + } + + if (options.mdns) { + const mdns = new MulticastDNS(peerInfo, 'ipfs.local') + modules.discovery.push(mdns) + } + + if (options.bootstrap) { + const r = new Railing(options.bootstrap) + modules.discovery.push(r) + } + + if (options.modules && options.modules.transport) { + options.modules.transport.forEach((t) => modules.transport.push(t)) + } + + if (options.modules && options.modules.discovery) { + options.modules.discovery.forEach((d) => modules.discovery.push(d)) + } + + super(modules, peerInfo, peerBook, options) + } +} + +module.exports = Node diff --git a/test/nodejs-bundle/peer-routing.js b/test/nodejs-bundle/peer-routing.js new file mode 100644 index 0000000000..03243102e8 --- /dev/null +++ b/test/nodejs-bundle/peer-routing.js @@ -0,0 +1,94 @@ +/* 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 parallel = require('async/parallel') +const utils = require('./utils') +const createNode = utils.createNode +const _times = require('lodash.times') + +describe('.peerRouting', () => { + let nodeA + let nodeB + let nodeC + let nodeD + let nodeE + + before((done) => { + const tasks = _times(5, () => (cb) => { + createNode('/ip4/0.0.0.0/tcp/0', { + mdns: false, + dht: true + }, (err, node) => { + expect(err).to.not.exist() + node.start((err) => cb(err, node)) + }) + }) + + parallel(tasks, (err, nodes) => { + expect(err).to.not.exist() + nodeA = nodes[0] + nodeB = nodes[1] + nodeC = nodes[2] + nodeD = nodes[3] + nodeE = nodes[4] + + parallel([ + (cb) => nodeA.dial(nodeB.peerInfo, cb), + (cb) => nodeB.dial(nodeC.peerInfo, cb), + (cb) => nodeC.dial(nodeD.peerInfo, cb), + (cb) => nodeD.dial(nodeE.peerInfo, cb), + (cb) => nodeE.dial(nodeA.peerInfo, cb) + ], done) + }) + }) + + after((done) => { + parallel([ + (cb) => nodeA.stop(cb), + (cb) => nodeB.stop(cb), + (cb) => nodeC.stop(cb), + (cb) => nodeD.stop(cb), + (cb) => nodeE.stop(cb) + ], done) + }) + + describe('el ring', () => { + it('let kbucket get filled', (done) => { + setTimeout(() => done(), 250) + }) + + it('nodeA.dial by Id to node C', (done) => { + nodeA.dial(nodeC.peerInfo.id, (err) => { + expect(err).to.not.exist() + done() + }) + }) + + it('nodeB.dial by Id to node D', (done) => { + nodeB.dial(nodeD.peerInfo.id, (err) => { + expect(err).to.not.exist() + done() + }) + }) + + it('nodeC.dial by Id to node E', (done) => { + nodeC.dial(nodeE.peerInfo.id, (err) => { + expect(err).to.not.exist() + done() + }) + }) + + it('nodeB.peerRouting.findPeer(nodeE.peerInfo.id)', (done) => { + nodeB.peerRouting.findPeer(nodeE.peerInfo.id, (err, peerInfo) => { + expect(err).to.not.exist() + expect(nodeE.peerInfo.id.toB58String()).to.equal(peerInfo.id.toB58String()) + done() + }) + }) + }) +}) diff --git a/test/nodejs-bundle/spawn-libp2p-node.js b/test/nodejs-bundle/spawn-libp2p-node.js new file mode 100755 index 0000000000..cd412074cb --- /dev/null +++ b/test/nodejs-bundle/spawn-libp2p-node.js @@ -0,0 +1,30 @@ +#! /usr/bin/env node + +'use strict' + +const Node = require('./nodejs-bundle') +const PeerInfo = require('peer-info') +const PeerId = require('peer-id') +const pull = require('pull-stream') + +const idBak = require('./test-data/test-id.json') + +PeerId.createFromJSON(idBak, (err, peerId) => { + if (err) { + throw err + } + + const peerInfo = new PeerInfo(peerId) + + peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/12345') + + const node = new Node(peerInfo) + + node.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) + + node.start((err) => { + if (err) { throw err } + + console.log('Spawned node started, env:', process.env.LIBP2P_MUXER) + }) +}) diff --git a/test/nodejs-bundle/stream-muxing.js b/test/nodejs-bundle/stream-muxing.js new file mode 100644 index 0000000000..72304245ed --- /dev/null +++ b/test/nodejs-bundle/stream-muxing.js @@ -0,0 +1,207 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +chai.use(require('dirty-chai')) +const expect = chai.expect +const parallel = require('async/parallel') +const series = require('async/series') +const pull = require('pull-stream') +const utils = require('./utils') +const createNode = utils.createNode +const echo = utils.echo + +function test (nodeA, nodeB, callback) { + nodeA.dial(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => { + expect(err).to.not.exist() + + pull( + pull.values([new Buffer('hey')]), + conn, + pull.collect((err, data) => { + expect(err).to.not.exist() + expect(data).to.be.eql([new Buffer('hey')]) + callback() + }) + ) + }) +} + +function teardown (nodeA, nodeB, callback) { + parallel([ + (cb) => nodeA.stop(cb), + (cb) => nodeB.stop(cb) + ], callback) +} + +describe('stream muxing', (done) => { + it('spdy only', (done) => { + let nodeA + let nodeB + + function setup (callback) { + parallel([ + (cb) => createNode('/ip4/0.0.0.0/tcp/0', { + muxer: ['spdy'] + }, (err, node) => { + expect(err).to.not.exist() + nodeA = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + (cb) => createNode('/ip4/0.0.0.0/tcp/0', { + muxer: ['spdy'] + }, (err, node) => { + expect(err).to.not.exist() + nodeB = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }) + ], callback) + } + + series([ + (cb) => setup(cb), + (cb) => test(nodeA, nodeB, cb), + (cb) => teardown(nodeA, nodeB, cb) + ], done) + }) + + it('multiplex only', (done) => { + let nodeA + let nodeB + + function setup (callback) { + parallel([ + (cb) => createNode('/ip4/0.0.0.0/tcp/0', { + muxer: ['multiplex'] + }, (err, node) => { + expect(err).to.not.exist() + nodeA = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + (cb) => createNode('/ip4/0.0.0.0/tcp/0', { + muxer: ['multiplex'] + }, (err, node) => { + expect(err).to.not.exist() + nodeB = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }) + ], callback) + } + + series([ + (cb) => setup(cb), + (cb) => test(nodeA, nodeB, cb), + (cb) => teardown(nodeA, nodeB, cb) + ], done) + }) + + it('spdy + multiplex', (done) => { + let nodeA + let nodeB + + function setup (callback) { + parallel([ + (cb) => createNode('/ip4/0.0.0.0/tcp/0', { + muxer: ['spdy', 'multiplex'] + }, (err, node) => { + expect(err).to.not.exist() + nodeA = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + (cb) => createNode('/ip4/0.0.0.0/tcp/0', { + muxer: ['spdy', 'multiplex'] + }, (err, node) => { + expect(err).to.not.exist() + nodeB = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }) + ], callback) + } + + series([ + (cb) => setup(cb), + (cb) => test(nodeA, nodeB, cb), + (cb) => teardown(nodeA, nodeB, cb) + ], done) + }) + + it('spdy + multiplex switched order', (done) => { + let nodeA + let nodeB + + function setup (callback) { + parallel([ + (cb) => createNode('/ip4/0.0.0.0/tcp/0', { + muxer: ['spdy', 'multiplex'] + }, (err, node) => { + expect(err).to.not.exist() + nodeA = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + (cb) => createNode('/ip4/0.0.0.0/tcp/0', { + muxer: ['multiplex', 'spdy'] + }, (err, node) => { + expect(err).to.not.exist() + nodeB = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }) + ], callback) + } + + series([ + (cb) => setup(cb), + (cb) => test(nodeA, nodeB, cb), + (cb) => teardown(nodeA, nodeB, cb) + ], done) + }) + + it('one without the other fails to establish a muxedConn', (done) => { + let nodeA + let nodeB + + function setup (callback) { + parallel([ + (cb) => createNode('/ip4/0.0.0.0/tcp/0', { + muxer: ['spdy'] + }, (err, node) => { + expect(err).to.not.exist() + nodeA = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + (cb) => createNode('/ip4/0.0.0.0/tcp/0', { + muxer: ['multiplex'] + }, (err, node) => { + expect(err).to.not.exist() + nodeB = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }) + ], callback) + } + + series([ + (cb) => setup(cb), + (cb) => { + // it will just 'warm up a conn' + expect(Object.keys(nodeA.swarm.muxers)).to.have.length(1) + expect(Object.keys(nodeB.swarm.muxers)).to.have.length(1) + + nodeA.dial(nodeB.peerInfo, (err) => { + expect(err).to.not.exist() + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) + cb() + }) + }, + (cb) => teardown(nodeA, nodeB, cb) + ], done) + }) +}) diff --git a/test/nodejs-bundle/tcp+websockets+webrtc-star.js b/test/nodejs-bundle/tcp+websockets+webrtc-star.js new file mode 100644 index 0000000000..a7992f3861 --- /dev/null +++ b/test/nodejs-bundle/tcp+websockets+webrtc-star.js @@ -0,0 +1,246 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +chai.use(require('dirty-chai')) +const expect = chai.expect +const parallel = require('async/parallel') +const signalling = require('libp2p-webrtc-star/src/sig-server') +const WStar = require('libp2p-webrtc-star') +const wrtc = require('wrtc') +const utils = require('./utils') +const createNode = utils.createNode +const echo = utils.echo + +describe('TCP + WebSockets + WebRTCStar', () => { + let nodeAll + let nodeTCP + let nodeWS + let nodeWStar + + let ss + + before((done) => { + parallel([ + (cb) => { + signalling.start({ port: 24642 }, (err, server) => { + expect(err).to.not.exist() + ss = server + cb() + }) + }, + (cb) => { + const wstar = new WStar({wrtc: wrtc}) + createNode([ + '/ip4/0.0.0.0/tcp/0', + '/ip4/127.0.0.1/tcp/25011/ws', + '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/24642/ws' + ], { + modules: { + transport: [wstar], + discovery: [wstar.discovery] + } + }, (err, node) => { + expect(err).to.not.exist() + nodeAll = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }) + }, + (cb) => createNode([ + '/ip4/0.0.0.0/tcp/0' + ], (err, node) => { + expect(err).to.not.exist() + nodeTCP = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + (cb) => createNode([ + '/ip4/127.0.0.1/tcp/25022/ws' + ], (err, node) => { + expect(err).to.not.exist() + nodeWS = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + + (cb) => { + const wstar = new WStar({wrtc: wrtc}) + + createNode([ + '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/24642/ws' + ], { + modules: { + transport: [wstar], + discovery: [wstar.discovery] + } + }, (err, node) => { + expect(err).to.not.exist() + nodeWStar = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }) + } + ], done) + }) + + after((done) => { + parallel([ + (cb) => nodeAll.stop(cb), + (cb) => nodeTCP.stop(cb), + (cb) => nodeWS.stop(cb), + (cb) => nodeWStar.stop(cb), + (cb) => ss.stop(done) + ], done) + }) + + it('nodeAll.dial nodeTCP using PeerInfo', (done) => { + nodeAll.dial(nodeTCP.peerInfo, (err) => { + expect(err).to.not.exist() + + // Some time for Identify to finish + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeAll.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(1) + cb() + }, + (cb) => { + const peers = nodeTCP.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(1) + cb() + } + ], done) + } + }) + }) + + it('nodeAll.hangUp nodeTCP using PeerInfo', (done) => { + nodeAll.hangUp(nodeTCP.peerInfo, (err) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeAll.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(0) + cb() + }, + (cb) => { + const peers = nodeTCP.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(0) + cb() + } + ], done) + } + }) + }) + + it('nodeAll.dial nodeWS using PeerInfo', (done) => { + nodeAll.dial(nodeWS.peerInfo, (err) => { + expect(err).to.not.exist() + + // Some time for Identify to finish + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeAll.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(2) + expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(1) + cb() + }, + (cb) => { + const peers = nodeWS.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(1) + cb() + } + ], done) + } + }) + }) + + it('nodeAll.hangUp nodeWS using PeerInfo', (done) => { + nodeAll.hangUp(nodeWS.peerInfo, (err) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeAll.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(2) + expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(0) + cb() + }, + (cb) => { + const peers = nodeWS.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(0) + cb() + } + ], done) + } + }) + }) + + it('nodeAll.dial nodeWStar using PeerInfo', (done) => { + nodeAll.dial(nodeWStar.peerInfo, (err) => { + expect(err).to.not.exist() + + // Some time for Identify to finish + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeAll.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(3) + expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(1) + cb() + }, + (cb) => { + const peers = nodeWStar.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(1) + cb() + } + ], done) + } + }) + }) + + it('nodeAll.hangUp nodeWStar using PeerInfo', (done) => { + nodeAll.hangUp(nodeWStar.peerInfo, (err) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeAll.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(3) + expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(0) + cb() + }, + (cb) => { + const peers = nodeWStar.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeWStar.swarm.muxedConns)).to.have.length(0) + cb() + } + ], done) + } + }) + }) +}) diff --git a/test/nodejs-bundle/tcp+websockets.js b/test/nodejs-bundle/tcp+websockets.js new file mode 100644 index 0000000000..609eedea27 --- /dev/null +++ b/test/nodejs-bundle/tcp+websockets.js @@ -0,0 +1,167 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +chai.use(require('dirty-chai')) +const expect = chai.expect +const parallel = require('async/parallel') +// const multiaddr = require('multiaddr') +// const pull = require('pull-stream') +const utils = require('./utils') +const createNode = utils.createNode +const echo = utils.echo + +describe('TCP + WebSockets', () => { + let nodeTCP + let nodeTCPnWS + let nodeWS + + before((done) => { + parallel([ + (cb) => createNode([ + '/ip4/0.0.0.0/tcp/0' + ], (err, node) => { + expect(err).to.not.exist() + nodeTCP = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + (cb) => createNode([ + '/ip4/0.0.0.0/tcp/0', + '/ip4/127.0.0.1/tcp/25011/ws' + ], (err, node) => { + expect(err).to.not.exist() + nodeTCPnWS = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + (cb) => createNode([ + '/ip4/127.0.0.1/tcp/25022/ws' + ], (err, node) => { + expect(err).to.not.exist() + nodeWS = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }) + ], done) + }) + + after((done) => { + parallel([ + (cb) => nodeTCP.stop(cb), + (cb) => nodeTCPnWS.stop(cb), + (cb) => nodeWS.stop(cb) + ], done) + }) + + it('nodeTCP.dial nodeTCPnWS using PeerInfo', (done) => { + nodeTCP.dial(nodeTCPnWS.peerInfo, (err) => { + expect(err).to.not.exist() + + // Some time for Identify to finish + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeTCP.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(1) + cb() + }, + (cb) => { + const peers = nodeTCPnWS.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(1) + cb() + } + ], done) + } + }) + }) + + it('nodeTCP.hangUp nodeTCPnWS using PeerInfo', (done) => { + nodeTCP.hangUp(nodeTCPnWS.peerInfo, (err) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeTCP.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(0) + + cb() + }, + (cb) => { + const peers = nodeTCPnWS.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(0) + cb() + } + ], done) + } + }) + }) + + it('nodeTCPnWS.dial nodeWS using PeerInfo', (done) => { + nodeTCPnWS.dial(nodeWS.peerInfo, (err) => { + expect(err).to.not.exist() + + // Some time for Identify to finish + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeTCPnWS.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(2) + expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(1) + cb() + }, + (cb) => { + const peers = nodeWS.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(1) + cb() + } + ], done) + } + }) + }) + + it('nodeTCPnWS.hangUp nodeWS using PeerInfo', (done) => { + nodeTCPnWS.hangUp(nodeWS.peerInfo, (err) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeTCPnWS.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(2) + expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(0) + + cb() + }, + (cb) => { + const peers = nodeWS.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(0) + cb() + } + ], done) + } + }) + }) + + // Until https://github.com/libp2p/js-libp2p/issues/46 is resolved + // Everynode will be able to dial in WebSockets + it.skip('nodeTCP.dial nodeWS using PeerInfo is unsuccesful', (done) => { + nodeTCP.dial(nodeWS.peerInfo, (err) => { + expect(err).to.exist() + done() + }) + }) +}) diff --git a/test/nodejs-bundle/tcp.js b/test/nodejs-bundle/tcp.js new file mode 100644 index 0000000000..36fe530a39 --- /dev/null +++ b/test/nodejs-bundle/tcp.js @@ -0,0 +1,234 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +chai.use(require('dirty-chai')) +const expect = chai.expect +const parallel = require('async/parallel') +const series = require('async/series') +const pull = require('pull-stream') +const utils = require('./utils') +const createNode = utils.createNode +const echo = utils.echo + +describe('TCP only', () => { + let nodeA + let nodeB + + before((done) => { + parallel([ + (cb) => createNode('/ip4/0.0.0.0/tcp/0', (err, node) => { + expect(err).to.not.exist() + nodeA = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }), + (cb) => createNode('/ip4/0.0.0.0/tcp/0', (err, node) => { + expect(err).to.not.exist() + nodeB = node + node.handle('/echo/1.0.0', echo) + node.start(cb) + }) + ], done) + }) + + after((done) => { + parallel([ + (cb) => nodeA.stop(cb), + (cb) => nodeB.stop(cb) + ], done) + }) + + it('nodeA.dial nodeB using PeerInfo without proto (warmup)', (done) => { + nodeA.dial(nodeB.peerInfo, (err) => { + expect(err).to.not.exist() + + // Some time for Identify to finish + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeA.peerBook.getAll() + expect(err).to.not.exist() + expect(Object.keys(peers)).to.have.length(1) + cb() + }, + (cb) => { + const peers = nodeB.peerBook.getAll() + expect(err).to.not.exist() + expect(Object.keys(peers)).to.have.length(1) + cb() + } + ], done) + } + }) + }) + + it('nodeA.dial nodeB using PeerInfo', (done) => { + nodeA.dial(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => { + expect(err).to.not.exist() + + pull( + pull.values([new Buffer('hey')]), + conn, + pull.collect((err, data) => { + expect(err).to.not.exist() + expect(data).to.be.eql([new Buffer('hey')]) + done() + }) + ) + }) + }) + + it('nodeA.hangUp nodeB using PeerInfo (first)', (done) => { + nodeA.hangUp(nodeB.peerInfo, (err) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeA.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) + cb() + }, + (cb) => { + const peers = nodeB.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + + expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0) + cb() + } + ], done) + } + }) + }) + + it('nodeA.dial nodeB using multiaddr', (done) => { + nodeA.dial(nodeB.peerInfo.multiaddrs.toArray()[0], '/echo/1.0.0', (err, conn) => { + // Some time for Identify to finish + setTimeout(check, 500) + + function check () { + expect(err).to.not.exist() + series([ + (cb) => { + const peers = nodeA.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1) + cb() + }, + (cb) => { + const peers = nodeB.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1) + cb() + } + ], () => { + pull( + pull.values([new Buffer('hey')]), + conn, + pull.collect((err, data) => { + expect(err).to.not.exist() + expect(data).to.be.eql([new Buffer('hey')]) + done() + }) + ) + }) + } + }) + }) + + it('nodeA.hangUp nodeB using multiaddr (second)', (done) => { + nodeA.hangUp(nodeB.peerInfo.multiaddrs.toArray()[0], (err) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeA.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) + cb() + }, + (cb) => { + const peers = nodeB.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + + expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0) + cb() + } + ], done) + } + }) + }) + + it('nodeA.dial nodeB using PeerId', (done) => { + nodeA.dial(nodeB.peerInfo.id, '/echo/1.0.0', (err, conn) => { + // Some time for Identify to finish + setTimeout(check, 500) + + function check () { + expect(err).to.not.exist() + series([ + (cb) => { + const peers = nodeA.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1) + cb() + }, + (cb) => { + const peers = nodeB.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1) + cb() + } + ], () => { + pull( + pull.values([new Buffer('hey')]), + conn, + pull.collect((err, data) => { + expect(err).to.not.exist() + expect(data).to.be.eql([new Buffer('hey')]) + done() + }) + ) + }) + } + }) + }) + + it('nodeA.hangUp nodeB using PeerId (third)', (done) => { + nodeA.hangUp(nodeB.peerInfo.multiaddrs.toArray()[0], (err) => { + expect(err).to.not.exist() + setTimeout(check, 500) + + function check () { + parallel([ + (cb) => { + const peers = nodeA.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) + cb() + }, + (cb) => { + const peers = nodeB.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + + expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0) + cb() + } + ], done) + } + }) + }) +}) diff --git a/test/nodejs-bundle/test-data/test-id.json b/test/nodejs-bundle/test-data/test-id.json new file mode 100644 index 0000000000..0b37a6cc9c --- /dev/null +++ b/test/nodejs-bundle/test-data/test-id.json @@ -0,0 +1,5 @@ +{ + "id": "QmaG17D4kfTB2RNUCr16bSfVvUVt2Xn3rPYeqQDvnVcXFr", + "privKey": "CAASqAkwggSkAgEAAoIBAQDBpXRrSLoVhP8C4YI0nm+YTb7UIe+xT9dwaMzKcGsH2zzz1lfxl54e1XNO+6Ut+If5jswpydgHhn9nGPod53sUIR2m+BiHOAH/Blgfa1nUKUkspts1MH3z5ZaO6Xo336Y0Uaw7UqfeIzKliTM6bpev2XIHyu0v/VJ2mylzfbDLMWqZs/shE3xwCJCD/PxoVpTlr/SzzF7MdgDMxkyiC3iLZ5Zkm+baPbi3mpKM0ue25Thedcc0KFjhQrjBfy5FPamrsMn5fnnoHwnQl9u7UWigzeC+7X+38EML1sVrV37ExxHPtM6882Ivjc7VN6zFHOHD2c9eVfbShkIf8YkVQUcFAgMBAAECggEAVE1mgGo58LJknml0WNn8tS5rfEiF5AhhPyOwvBTy04nDYFgZEykxgjTkrSbqgzfmYmOjSDICJUyNXGHISYqDz4CXOyBY9U0RuWeWp58BjVan75N4bRB+VNbHk9HbDkYEQlSoCW9ze0aRfvVa4v5QdRLSDMhwN+stokrsYcX/WIWYTM2e2jW+qQOzS8SJl7wYsgtd3WikrxwXkRL3sCMHEcgcPhoKacoD5Yr9cB0IC5vzhu4t/WMa+N2UEndcKGAbXsh8kA7BPFM6lqnEpOHpWEVEAYasAwFGUvUN9GwhtqpaNNS2sG6Nrz95cC99Nqx58uIXcTAJm3Fh/WfKJ6I1xQKBgQD+g7A5OSWw+i/zhTKVPJg93/eohViL0dGZT9Tf0/VslsFl00FwnZmBKA6BJ6ZL3hD00OcqIL3m6EzZ4q38U97XZxf2OUsPPJtl+Avqtlk16AHRHB9I17LGXJ30xZRkxL665oLms0D2T4NIZZX/uVMoS18lRvCZj1aEYQFCrZYgowKBgQDCxtA695S0vl6E3Q4G6MrDZK+2JqjaGL0XTnpHWiAjnk2lnV2CCZnWpEHT+ebF2fWx5nYQo5sugc6bS+4k9jRNUgxf2sQieZYCBjbnjCEVrPTm/dPTkaw1CQ/ox5/R1/Elbw8vteF9uUAvR0FL8Ss1Dqw6B2SxdTowxMy6qQ7sNwKBgG2N3eMj2DeP2egm45kdliK8L2yYyX6V+HTXyjf2kuQFGIZuIvMIw7S2u1eY65ooon/fFEIsCdJFGB+J1X6R05BAzi2sh8StP+7qkKadi1UK4w1R352JS2jbIRrlmXSuw7LL2njXnBTqMQaOw7xp14O2vePb32EaNBGTd+ltsvulAoGBALGIc4370oA4MIDb2Ag2MXKNmJbnf+piuB/BOTVGEZtFlDKLUArR43W/+/xRgKX/97FyhVS/OxfV21Kzj9oCy0NasMrB5RojRraLoYnFsPZH0mWlIGlsEtG4c9bR9XtYX4WmR+pN1r04mCc/xGWK6b4PpK2zxXT2i9ad2pmctGxbAoGBAIcp0UML5QCqvLmcob2/6PCRaYAxJBb9lDqOHredMgQih2hGnHFCyKk9eBAbFf/KN0guJTBDaAJRclcxsLLn7rV6grMNt+0EUepm7tWT0z5j8gNGbGGhuGDdqcmfJTc2EMdQrfhzYDN3rL1v3l+Ujwla2khL2ozE7SQ/KVeA1saY", + "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBpXRrSLoVhP8C4YI0nm+YTb7UIe+xT9dwaMzKcGsH2zzz1lfxl54e1XNO+6Ut+If5jswpydgHhn9nGPod53sUIR2m+BiHOAH/Blgfa1nUKUkspts1MH3z5ZaO6Xo336Y0Uaw7UqfeIzKliTM6bpev2XIHyu0v/VJ2mylzfbDLMWqZs/shE3xwCJCD/PxoVpTlr/SzzF7MdgDMxkyiC3iLZ5Zkm+baPbi3mpKM0ue25Thedcc0KFjhQrjBfy5FPamrsMn5fnnoHwnQl9u7UWigzeC+7X+38EML1sVrV37ExxHPtM6882Ivjc7VN6zFHOHD2c9eVfbShkIf8YkVQUcFAgMBAAE=" +} diff --git a/test/nodejs-bundle/turbolence.js b/test/nodejs-bundle/turbolence.js new file mode 100644 index 0000000000..4455322697 --- /dev/null +++ b/test/nodejs-bundle/turbolence.js @@ -0,0 +1,84 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +chai.use(require('dirty-chai')) +const expect = chai.expect +const multiaddr = require('multiaddr') +const spawn = require('child_process').spawn +const path = require('path') +// const map = require('async/map') +const pull = require('pull-stream') +const utils = require('./utils') +const createNode = utils.createNode +const echo = utils.echo + +describe('Turbolence tests', () => { + let nodeA + let nodeSpawn + + before((done) => { + createNode('/ip4/0.0.0.0/tcp/0', (err, node) => { + expect(err).to.not.exist() + nodeA = node + node.handle('/echo/1.0.0', echo) + node.start(done) + }) + }) + + after((done) => nodeA.stop(done)) + + it('spawn a node in a different process', (done) => { + const filePath = path.join(__dirname, './spawn-libp2p-node.js') + + nodeSpawn = spawn(filePath, { env: process.env }) + + let spawned = false + + nodeSpawn.stdout.on('data', (data) => { + // console.log(data.toString()) + if (!spawned) { + spawned = true + done() + } + }) + + nodeSpawn.stderr.on('data', (data) => console.log(data.toString())) + }) + + it('connect nodeA to that node', (done) => { + const spawnedId = require('./test-data/test-id.json') + const maddr = multiaddr('/ip4/127.0.0.1/tcp/12345/ipfs/' + spawnedId.id) + + nodeA.dial(maddr, '/echo/1.0.0', (err, conn) => { + expect(err).to.not.exist() + const peers = nodeA.peerBook.getAll() + + expect(Object.keys(peers)).to.have.length(1) + + pull( + pull.values([new Buffer('hey')]), + conn, + pull.collect((err, data) => { + expect(err).to.not.exist() + expect(data).to.eql([new Buffer('hey')]) + done() + }) + ) + }) + }) + + it('crash that node, ensure nodeA continues going steady', (done) => { + // TODO investigate why CI crashes + setTimeout(() => nodeSpawn.kill('SIGKILL'), 1000) + // nodeSpawn.kill('SIGKILL') + setTimeout(check, 5000) + + function check () { + const peers = nodeA.peerBook.getAll() + expect(Object.keys(peers)).to.have.length(1) + expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) + done() + } + }) +}) diff --git a/test/nodejs-bundle/utils.js b/test/nodejs-bundle/utils.js new file mode 100644 index 0000000000..4c26b66f9f --- /dev/null +++ b/test/nodejs-bundle/utils.js @@ -0,0 +1,43 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +chai.use(require('dirty-chai')) +const Node = require('./nodejs-bundle') +const PeerInfo = require('peer-info') +const PeerId = require('peer-id') +const waterfall = require('async/waterfall') +const pull = require('pull-stream') + +function createNode (multiaddrs, options, callback) { + if (typeof options === 'function') { + callback = options + options = {} + } + + if (!Array.isArray(multiaddrs)) { + multiaddrs = [multiaddrs] + } + + waterfall([ + (cb) => PeerId.create({ bits: 1024 }, cb), + (peerId, cb) => PeerInfo.create(peerId, cb), + (peerInfo, cb) => { + multiaddrs.map((ma) => peerInfo.multiaddrs.add(ma)) + cb(null, peerInfo) + }, + (peerInfo, cb) => { + const node = new Node(peerInfo, undefined, options) + cb(null, node) + } + ], callback) +} + +function echo (protocol, conn) { + pull(conn, conn) +} + +module.exports = { + createNode: createNode, + echo: echo +}