Compare commits

..

133 Commits

Author SHA1 Message Date
e6e5b872dc chore: release version v0.23.0 2018-07-27 14:01:36 +02:00
550af3cbde chore: update contributors 2018-07-27 14:01:36 +02:00
5aa9ebbbe8 chore: update deps 2018-07-27 13:57:15 +02:00
7f68a13433 docs: updte release template - no sharness or interop tests for now :( 2018-07-24 19:55:59 +02:00
2b7cc55c88 feat: add check for protector and enforced pnet
fix: update protector config and tests
docs: add private network info to the readme
test: fix an issue with config
2018-07-24 19:53:53 +02:00
40739e9639 docs: update release template 2018-07-22 19:32:07 +01:00
6106915923 fix: start and stop connection manager with libp2p
test: add test to verify libp2p starts and stops the right things
test: add test for verifying disabled modules
fix: linting
2018-07-22 19:29:19 +01:00
d9059dbad9 chore: update deps 2018-07-13 18:10:03 +02:00
187d584086 docs: update logo url 2018-07-06 19:16:21 +02:00
7502ba86a5 docs: better packages table (#218) 2018-07-05 17:17:47 +02:00
cc51fa59f9 docs: add missing commas to readme example (#216) 2018-07-05 11:47:00 +02:00
1c10842bd3 docs: 2018 Q3 OKRs (#207)
* docs: 2018 Q3 OKRs placeholder

* docs: first draft

* Update OKR.md

* Update OKR.md

* Update OKR.md

* Update OKR.md

* Update OKR.md

* Update OKR.md

* adding 3 more KRs

@jacobheun what you think about taking on these?

* Update OKR.md

* Update OKR.md

* Update OKR.md
2018-07-04 14:03:25 +02:00
c07ffa1317 docs: add release checklist 2018-07-02 14:43:34 +02:00
b7f67f2764 chore: release version v0.22.0 2018-06-29 23:27:29 +01:00
8665286764 chore: update contributors 2018-06-29 23:27:28 +01:00
a43d73eea7 chore: update deps 2018-06-29 23:23:54 +01:00
4ad70efb00 fix: remove peer discovery module config checks
Configuration for the peer discovery modules is now optional so this does not need to be validated. This also cleans up the config module to reduce repetition.

License: MIT
Signed-off-by: Alan Shaw <alan@tableflip.io>
2018-06-29 23:42:43 +02:00
1af5ba9093 fix: typo in fixture and fail for correct reason
License: MIT
Signed-off-by: Alan Shaw <alan@tableflip.io>
2018-06-29 23:42:43 +02:00
be9eafe20f fix: remove .only
License: MIT
Signed-off-by: Alan Shaw <alan@tableflip.io>
2018-06-29 23:42:43 +02:00
27c6587747 test: add test for default registration of function module
License: MIT
Signed-off-by: Alan Shaw <alan@tableflip.io>
2018-06-29 23:42:43 +02:00
9521e79061 test: add tests for peer discovery module registration
License: MIT
Signed-off-by: Alan Shaw <alan@tableflip.io>
2018-06-29 23:42:43 +02:00
80f0b6077a fix: add null property guards
License: MIT
Signed-off-by: Alan Shaw <alan@tableflip.io>
2018-06-29 23:42:43 +02:00
ac5cacba33 fix: do not mutate the config object
License: MIT
Signed-off-by: Alan Shaw <alan@tableflip.io>
2018-06-29 23:42:43 +02:00
e320854db7 feat: enable peer discovery modules by default
This PR will enable any provided peer discovery modules by default if no configuration for the module is supplied/needed.

As before, modules can be explicitly disabled or enabled by passing config.

This also enables pre-configured modules (instances) to be passed and enabled without them having to have a `tag` and an unused config section.

License: MIT
Signed-off-by: Alan Shaw <alan@tableflip.io>
2018-06-29 23:42:43 +02:00
501cc22fb4 test: prevent ci collision 2018-06-29 20:38:40 +02:00
a57f1b22d0 chore: update latest webrtc 2018-06-29 20:38:40 +02:00
7baf9f47ac chore: remove travis CI badge 2018-06-28 10:35:18 +02:00
4e8ac0b7a8 chore: release version v0.21.0 2018-06-28 10:34:11 +02:00
b593adef56 chore: update contributors 2018-06-28 10:34:11 +02:00
8c803d5901 chore: remove pre-push 2018-06-28 10:11:54 +02:00
9fadd1d7b8 chore: update mplex 2018-06-28 10:11:29 +02:00
6905f1ba41 feat: (BREAKING CHANGE) overhaul libp2p config and constructor
* docs: update chat example and add info to its readme
* docs: update echo example
* docs: update libp2p in browser example
* docs: update pubsub example
* docs: update peer and content routing examples
* docs: update discovery mechanisms example
* docs: update encrypted comms example
* docs: update protocol and stream muxing example
* feat: add config validation
* test: update CI configs, use only node 8
2018-06-28 10:06:25 +02:00
b80e89269c docs: add weekly core dev calls url 2018-06-22 00:05:53 +01:00
deba7ea28e docs: add mgmt 2018-06-22 00:03:52 +01:00
d5972045da feat: set and hook up libp2p-connection-manager (#184) 2018-06-20 11:19:37 +01:00
28ffa0c7dc chore: update libp2p-switch 2018-06-07 19:58:22 +01:00
1790ded144 docs: Minor fixes in Transport example readme (#199)
* docs: Fixes typos, adds instructions

* docs: adds .md bash syntax highlight
2018-06-04 09:56:57 +01:00
abc6257bf7 docs: update badges 2018-05-29 13:45:48 +01:00
dd84190d47 docs: libp2p-swarm was renamed to libp2p-switch (#196)
* docs: libp2p-swarm was renamed to libp2p-switch

Update the README accordingly.

* Fixing another typo
2018-05-28 14:29:43 +01:00
65073792aa fix: lock wrtc to 0.1.1
* chore: update deps

* fix: use only wrtc 0.1.1

* chore: update webrtc-star

* chore: update circle

* chore: no node10
2018-05-12 19:45:05 +01:00
0bf203b087 docs: lead maintainer (#190) 2018-05-06 18:32:26 +02:00
37471135fa chore: release version v0.20.4 2018-04-30 22:41:32 +01:00
2a21c20ea3 chore: update contributors 2018-04-30 22:41:32 +01:00
9a2d4e3d72 chore: release version v0.20.3 2018-04-30 22:41:08 +01:00
7c2a19e3cc chore: update deps 2018-04-30 22:41:08 +01:00
26d5e69c62 chore: update deps 2018-04-30 22:41:08 +01:00
5042e09bb4 docs: typo in examples readme.md 2018-04-30 15:29:12 +01:00
409e7a8e1f chore: release version v0.20.2 2018-04-10 14:04:44 +09:00
52938e9f39 chore: update contributors 2018-04-10 14:04:44 +09:00
32941a807a chore: fix deps 2018-04-10 14:03:29 +09:00
32d34d3b83 chore: release version v0.20.1 2018-04-10 12:15:57 +09:00
074b8af09d chore: update contributors 2018-04-10 12:15:57 +09:00
4117bd7552 chore: add needed dep (websockets) 2018-04-10 12:13:11 +09:00
db3f6dbb06 chore: release version v0.20.0 2018-04-06 17:03:47 +01:00
3808c365b1 chore: update contributors 2018-04-06 17:03:47 +01:00
19528ef15e chore: update deps 2018-04-06 17:02:51 +01:00
bb0c9905ed feat: use class-is for type checks 2018-04-05 19:47:02 +01:00
141920cd14 chore: update deps 2018-04-05 19:47:02 +01:00
2dc94cd907 chore: release version v0.19.2 2018-03-28 15:34:00 -07:00
7fc1cd0f7c chore: update contributors 2018-03-28 15:33:59 -07:00
26f3f9a319 chore: release version v0.19.1 2018-03-28 15:32:20 -07:00
e9ce4ac795 chore: update deps 2018-03-28 15:32:20 -07:00
bca86873cc docs: enable missing syntax highlighting (#178)
Enable missing syntax highlighting for example code in `discovery-mechanisms/README.md`.
2018-03-20 21:43:56 -07:00
bcca813171 chore: release version v0.19.0 2018-03-15 08:16:12 -07:00
cfc5958a4b chore: update contributors 2018-03-15 08:16:12 -07:00
4527d5fff1 stats: tests: bind to localhost only 2018-03-15 08:09:56 -07:00
2c04a71007 stats: exposed and documented 2018-03-15 08:09:56 -07:00
642b8ad751 chore: update deps 2018-03-15 07:57:30 -07:00
ada73221b0 chore: update deps 2018-03-14 06:27:53 -07:00
2e40e9dda1 docs: update echo and chat examples to use libp2p-switch (#174) 2018-03-06 13:25:55 +01:00
e531b1bf3d docs: fix pubsub example, solves #167 2018-02-22 07:06:21 +00:00
fdfc237780 chore: release version v0.18.0 2018-02-19 09:59:47 +00:00
83a09dbc0e chore: update contributors 2018-02-19 09:59:47 +00:00
b28eba067e chore: update deps 2018-02-19 09:58:08 +00:00
b871bb0a1a docs: update examples to use libp2p-mplex module 2018-02-19 09:58:08 +00:00
14d3578eaf test: update tests to use libp2p-mplex module 2018-02-19 09:58:08 +00:00
10a8ec3f31 chore: release version v0.17.0 2018-02-16 18:26:06 +00:00
41d202c4ba chore: update contributors 2018-02-16 18:26:06 +00:00
a5fd05875c chore: make tests faster 2018-02-16 19:23:35 +01:00
379febb610 chore: fix linting and move linting to circle 2018-02-16 19:23:35 +01:00
99873e877b docs: update pubsub example 2018-02-16 19:23:35 +01:00
4e01c094bc test: test pubsub on and off 2018-02-16 19:23:35 +01:00
8fcafe2d90 docs: update pubsub example 2018-02-16 19:23:35 +01:00
947eaf166b fix: use correct reference to floodSub 2018-02-16 19:23:35 +01:00
1ebf725ac4 docs: add docs 2018-02-16 19:23:35 +01:00
0c543b7180 feat: add pubsub to libp2p 2018-02-16 19:23:35 +01:00
beeb36c10c chore: release version v0.16.5 2018-02-14 10:55:16 +01:00
0acc7e5d72 chore: update contributors 2018-02-14 10:55:16 +01:00
9fd94b98a1 chore: update deps 2018-02-14 10:48:21 +01:00
5c3037037a chore: release version v0.16.4 2018-02-09 12:45:17 +00:00
362217c8da chore: update contributors 2018-02-09 12:45:17 +00:00
7733ba5cd7 chore: release version v0.16.3 2018-02-08 08:11:24 +00:00
52bf826ec6 chore: update contributors 2018-02-08 08:11:24 +00:00
10619afbe6 chore: release version v0.16.2 2018-02-07 09:50:30 +00:00
3debabdd26 chore: update contributors 2018-02-07 09:50:30 +00:00
56e095983a chore: update npm ignore 2018-02-07 09:50:01 +00:00
ebdb696742 chore: release version v0.16.1 2018-02-07 09:45:01 +00:00
721e6ee9ce chore: update contributors 2018-02-07 09:45:01 +00:00
98f2903088 chore: release version v0.16.0 2018-02-07 08:39:08 +00:00
3dda282dfd chore: update contributors 2018-02-07 08:39:08 +00:00
03faf69212 test: fix linting 2018-02-07 08:37:03 +00:00
0062a4b5eb docs: update examples 2018-02-07 08:37:03 +00:00
f7f85dce0a test: update tests to new API 2018-02-07 08:37:03 +00:00
59df82a675 docs: dialProtocol 2018-02-07 08:37:03 +00:00
6651401f0b feat: dialProtocol and small refactor 2018-02-07 08:37:03 +00:00
cd43863db6 chore: use pre-push 2018-02-07 07:31:10 +00:00
23e8293b75 feat: use libp2p-switch 2018-02-07 07:31:10 +00:00
68c170a40d docs: Add ws-star and ws-star-rendezvous (#158) 2018-02-07 07:07:53 +00:00
bd8a35aaf9 feat: add explicit error for case peer id not included in multiaddr (#155) 2018-02-05 12:36:20 +01:00
248d86d050 fix typo (#152) 2018-01-27 23:26:57 -08:00
8225b11082 fix typo (#153) 2018-01-27 23:26:52 -08:00
1355af2b51 docs: fix typos (#154) 2018-01-27 23:26:46 -08:00
a85f041843 chore: release version v0.15.2 2018-01-27 18:51:45 -08:00
2c0cda2a7c chore: update contributors 2018-01-27 18:51:45 -08:00
b4ba267589 chore: update deps 2018-01-27 18:48:44 -08:00
4c81f39ebe chore: release version v0.15.1 2018-01-16 06:16:15 -08:00
76922383ab chore: update contributors 2018-01-16 06:16:15 -08:00
3fc57ff397 chore: bump timeout on after 2018-01-16 06:13:42 -08:00
13b36dcaa6 chore: update deps 2018-01-16 06:03:04 -08:00
61bebd10fc fix: typo in DHT setup (#151)
I think there was a typo that would have ignored passed options and fallback to defaults for DHT datastore.
2018-01-16 05:58:12 -08:00
97cde1ccb4 test: Various pdd fixes (#150)
* fix(pdd): url for interop pkg was pointing to non-existing branch

* fix(pdd): case-sensitive names

peer-B.json does not _actually_ exist

* fix(pdd): story 2 peer b
2018-01-10 14:53:24 +00:00
07b0cdc30e docs: note the Connection type on .handle and .dial docs (#149) 2018-01-07 18:47:02 +00:00
73b6d60e32 docs: update to new URL format on the browser example 2018-01-07 17:17:22 +00:00
808f7495a9 chore: release version v0.15.0 2018-01-07 17:09:57 +00:00
ae21a6facf chore: update contributors 2018-01-07 17:09:56 +00:00
3aba3fd176 chore: update deps (#148) 2018-01-07 17:00:24 +00:00
dc2da39a19 docs: tcp/443 on webrtc-star example. (#147)
according to https://github.com/libp2p/js-libp2p-webrtc-star/issues/134 and my own fiddling with this example.
2018-01-06 14:28:20 +00:00
f8b441fff5 docs: Update README.md (#143)
added missing backtick
2018-01-02 07:18:41 +00:00
190ad54f9e PDD Compliance Tests (#137)
* transport story 1 done

* c

* transport series 2

* transports story 3

* the ipfs-bundle story 1
2017-12-18 10:23:34 +00:00
582f8bed2e docs: fix links on examples peer and content routing (#141) 2017-12-18 09:53:30 +00:00
59de0da19c chore: release version v0.14.3 2017-12-15 08:25:08 +00:00
0caf600c9a chore: update contributors 2017-12-15 08:25:08 +00:00
a4943b4509 chore: update npm ignore 2017-12-15 08:24:49 +00:00
99 changed files with 3475 additions and 1186 deletions

View File

@ -5,10 +5,11 @@ const PeerId = require('peer-id')
const pull = require('pull-stream') const pull = require('pull-stream')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const rawPeer = require('./test/fixtures/test-peer.json')
const Node = require('./test/utils/bundle.node.js')
const sigServer = require('libp2p-webrtc-star/src/sig-server')
const WebSocketStarRendezvous = require('libp2p-websocket-star-rendezvous') const WebSocketStarRendezvous = require('libp2p-websocket-star-rendezvous')
const sigServer = require('libp2p-webrtc-star/src/sig-server')
const rawPeer = require('./test/fixtures/test-peer.json')
const Node = require('./test/utils/bundle-nodejs.js')
let wrtcRendezvous let wrtcRendezvous
let wsRendezvous let wsRendezvous
@ -21,7 +22,9 @@ const before = (done) => {
port: 15555 port: 15555
// cryptoChallenge: true TODO: needs https://github.com/libp2p/js-libp2p-webrtc-star/issues/128 // cryptoChallenge: true TODO: needs https://github.com/libp2p/js-libp2p-webrtc-star/issues/128
}, (err, server) => { }, (err, server) => {
if (err) { return cb(err) } if (err) {
return cb(err)
}
wrtcRendezvous = server wrtcRendezvous = server
cb() cb()
}) })
@ -33,7 +36,9 @@ const before = (done) => {
strictMultiaddr: false, strictMultiaddr: false,
cryptoChallenge: true cryptoChallenge: true
}, (err, _server) => { }, (err, _server) => {
if (err) { return cb(err) } if (err) {
return cb(err)
}
wsRendezvous = _server wsRendezvous = _server
cb() cb()
}) })
@ -47,7 +52,9 @@ const before = (done) => {
peer.multiaddrs.add('/ip4/127.0.0.1/tcp/9200/ws') peer.multiaddrs.add('/ip4/127.0.0.1/tcp/9200/ws')
node = new Node(peer) node = new Node({
peerInfo: peer
})
node.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) node.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
node.start(cb) node.start(cb)
}) })
@ -56,11 +63,11 @@ const before = (done) => {
} }
const after = (done) => { const after = (done) => {
setTimeout(() => parallel( setTimeout(() =>
[node, wrtcRendezvous, wsRendezvous].map((s) => { parallel(
return (cb) => s.stop(cb) [node, wrtcRendezvous, wsRendezvous].map((s) => (cb) => s.stop(cb)),
}) done),
, done), 2000) 2000)
} }
module.exports = { module.exports = {

View File

@ -1,7 +1,16 @@
**/node_modules/
**/*.log
test/repo-tests*
img
docs
examples
# Logs # Logs
logs logs
*.log *.log
coverage
# Runtime data # Runtime data
pids pids
*.pid *.pid
@ -16,13 +25,13 @@ coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt .grunt
# Compiled binary addons (http://nodejs.org/api/addons.html) # node-waf configuration
build/Release .lock-wscript
# Optional npm cache directory build
.npm
# Optional REPL history # Dependency directory
.node_repl_history # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
test test

View File

@ -1,32 +0,0 @@
# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
sudo: false
language: node_js
matrix:
include:
- node_js: 6
env: CXX=g++-4.8
- node_js: 8
env: CXX=g++-4.8
# - node_js: stable
# env: CXX=g++-4.8
script:
- npm run lint
- npm run test
- npm run coverage
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
after_success:
- npm run coverage-publish
addons:
firefox: 'latest'
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8

View File

@ -1,3 +1,180 @@
<a name="0.23.0"></a>
# [0.23.0](https://github.com/libp2p/js-libp2p/compare/v0.22.0...v0.23.0) (2018-07-27)
### Bug Fixes
* start and stop connection manager with libp2p ([6106915](https://github.com/libp2p/js-libp2p/commit/6106915))
### Features
* add check for protector and enforced pnet ([2b7cc55](https://github.com/libp2p/js-libp2p/commit/2b7cc55))
<a name="0.22.0"></a>
# [0.22.0](https://github.com/libp2p/js-libp2p/compare/v0.21.0...v0.22.0) (2018-06-29)
### Bug Fixes
* add null property guards ([80f0b60](https://github.com/libp2p/js-libp2p/commit/80f0b60))
* do not mutate the config object ([ac5cacb](https://github.com/libp2p/js-libp2p/commit/ac5cacb))
* remove .only ([be9eafe](https://github.com/libp2p/js-libp2p/commit/be9eafe))
* remove peer discovery module config checks ([4ad70ef](https://github.com/libp2p/js-libp2p/commit/4ad70ef))
* typo in fixture and fail for correct reason ([1af5ba9](https://github.com/libp2p/js-libp2p/commit/1af5ba9))
### Features
* enable peer discovery modules by default ([e320854](https://github.com/libp2p/js-libp2p/commit/e320854))
<a name="0.21.0"></a>
# [0.21.0](https://github.com/libp2p/js-libp2p/compare/v0.20.4...v0.21.0) (2018-06-28)
### Bug Fixes
* lock wrtc to 0.1.1 ([6507379](https://github.com/libp2p/js-libp2p/commit/6507379))
### Features
* (BREAKING CHANGE) overhaul libp2p config and constructor ([6905f1b](https://github.com/libp2p/js-libp2p/commit/6905f1b))
* set and hook up libp2p-connection-manager ([#184](https://github.com/libp2p/js-libp2p/issues/184)) ([d597204](https://github.com/libp2p/js-libp2p/commit/d597204))
<a name="0.20.4"></a>
## [0.20.4](https://github.com/libp2p/js-libp2p/compare/v0.20.2...v0.20.4) (2018-04-30)
<a name="0.20.3"></a>
## [0.20.3](https://github.com/libp2p/js-libp2p/compare/v0.20.2...v0.20.3) (2018-04-30)
<a name="0.20.2"></a>
## [0.20.2](https://github.com/libp2p/js-libp2p/compare/v0.20.1...v0.20.2) (2018-04-10)
<a name="0.20.1"></a>
## [0.20.1](https://github.com/libp2p/js-libp2p/compare/v0.20.0...v0.20.1) (2018-04-10)
<a name="0.20.0"></a>
# [0.20.0](https://github.com/libp2p/js-libp2p/compare/v0.19.2...v0.20.0) (2018-04-06)
### Features
* use class-is for type checks ([bb0c990](https://github.com/libp2p/js-libp2p/commit/bb0c990))
<a name="0.19.2"></a>
## [0.19.2](https://github.com/libp2p/js-libp2p/compare/v0.19.0...v0.19.2) (2018-03-28)
<a name="0.19.1"></a>
## [0.19.1](https://github.com/libp2p/js-libp2p/compare/v0.19.0...v0.19.1) (2018-03-28)
<a name="0.19.0"></a>
# [0.19.0](https://github.com/libp2p/js-libp2p/compare/v0.18.0...v0.19.0) (2018-03-15)
<a name="0.18.0"></a>
# [0.18.0](https://github.com/libp2p/js-libp2p/compare/v0.17.0...v0.18.0) (2018-02-19)
<a name="0.17.0"></a>
# [0.17.0](https://github.com/libp2p/js-libp2p/compare/v0.16.5...v0.17.0) (2018-02-16)
### Bug Fixes
* use correct reference to floodSub ([947eaf1](https://github.com/libp2p/js-libp2p/commit/947eaf1))
### Features
* add pubsub to libp2p ([0c543b7](https://github.com/libp2p/js-libp2p/commit/0c543b7))
<a name="0.16.5"></a>
## [0.16.5](https://github.com/libp2p/js-libp2p/compare/v0.16.4...v0.16.5) (2018-02-14)
<a name="0.16.4"></a>
## [0.16.4](https://github.com/libp2p/js-libp2p/compare/v0.16.3...v0.16.4) (2018-02-09)
<a name="0.16.3"></a>
## [0.16.3](https://github.com/libp2p/js-libp2p/compare/v0.16.2...v0.16.3) (2018-02-08)
<a name="0.16.2"></a>
## [0.16.2](https://github.com/libp2p/js-libp2p/compare/v0.16.1...v0.16.2) (2018-02-07)
<a name="0.16.1"></a>
## [0.16.1](https://github.com/libp2p/js-libp2p/compare/v0.16.0...v0.16.1) (2018-02-07)
<a name="0.16.0"></a>
# [0.16.0](https://github.com/libp2p/js-libp2p/compare/v0.15.2...v0.16.0) (2018-02-07)
### Features
* add explicit error for case peer id not included in multiaddr ([#155](https://github.com/libp2p/js-libp2p/issues/155)) ([bd8a35a](https://github.com/libp2p/js-libp2p/commit/bd8a35a))
* dialProtocol and small refactor ([6651401](https://github.com/libp2p/js-libp2p/commit/6651401))
* use libp2p-switch ([23e8293](https://github.com/libp2p/js-libp2p/commit/23e8293))
<a name="0.15.2"></a>
## [0.15.2](https://github.com/libp2p/js-libp2p/compare/v0.15.1...v0.15.2) (2018-01-28)
<a name="0.15.1"></a>
## [0.15.1](https://github.com/libp2p/js-libp2p/compare/v0.15.0...v0.15.1) (2018-01-16)
### Bug Fixes
* typo in DHT setup ([#151](https://github.com/libp2p/js-libp2p/issues/151)) ([61bebd1](https://github.com/libp2p/js-libp2p/commit/61bebd1))
<a name="0.15.0"></a>
# [0.15.0](https://github.com/libp2p/js-libp2p/compare/v0.14.3...v0.15.0) (2018-01-07)
<a name="0.14.3"></a>
## [0.14.3](https://github.com/libp2p/js-libp2p/compare/v0.14.2...v0.14.3) (2017-12-15)
<a name="0.14.2"></a> <a name="0.14.2"></a>
## [0.14.2](https://github.com/libp2p/js-libp2p/compare/v0.14.1...v0.14.2) (2017-12-15) ## [0.14.2](https://github.com/libp2p/js-libp2p/compare/v0.14.1...v0.14.2) (2017-12-15)

View File

@ -2,7 +2,7 @@
libp2p as a project, including js-libp2p and all of its modules, follows the [standard IPFS Community contributing guidelines](https://github.com/ipfs/community/blob/master/contribution-guidelines.md). libp2p as a project, including js-libp2p and all of its modules, follows the [standard IPFS Community contributing guidelines](https://github.com/ipfs/community/blob/master/contribution-guidelines.md).
We also adhere to the [IPFS JavaScript Community contributing guidelines](https://github.com/ipfs/community/blob/master/js-project-guidelines.md) which provide additional information of how to collaborate and contribute in the JavaScript implementation of libp2p. We also adhere to the [IPFS JavaScript Community contributing guidelines](https://github.com/ipfs/community/blob/master/js-code-guidelines.md) which provide additional information of how to collaborate and contribute in the JavaScript implementation of libp2p.
We appreciate your time and attention for going over these. Please open an issue on [ipfs/community](https://github.com/ipfs/community) if you have any question. We appreciate your time and attention for going over these. Please open an issue on [ipfs/community](https://github.com/ipfs/community) if you have any question.

36
MGMT.md Normal file
View File

@ -0,0 +1,36 @@
# Core Dev Team Work Tracking & Managment
## How work gets organized (a tl;dr;)
The js-ipfs core working group follows the OKR structure established for the IPFS project to set the quarterly targets. Within each quarter, work gets tracked using Github and Waffle.
- Github is used for discussions and track current endeavours.
- Waffle gives us a [Kanban](https://en.wikipedia.org/wiki/Kanban) view over the work at hand.
![](https://ipfs.io/ipfs/QmWNd86qtjyFnygSAHkZDy4fUB1WnRa4WNt8gt1rSiq7of)
In the Waffle board, we have 4 columns:
- **Inbox** - New issues or PRs that haven't been evaluated yet
- **Backlog** - Issues that are blocked or discussion threads that are not currently active
- **Ready** - Issues Ready to be worked on
- **In Progress** - Issues that someone is already tackling. Contributors should focus on a few things rather than many at once.
- **Done** - Issues are automatically moved here when the issue is closed or the PR merged.
We track work for the JavaScript implementation of the IPFS protocol in 3 separate waffle boards:
- [js-ipfs](http://waffle.io/ipfs/js-ipfs)
- [js-libp2p](http://waffle.io/libp2p/js-libp2p)
- [js-ipld](http://waffle.io/ipld/js-ipld)
## Issue labels and how to use filters
We use labels to tag urgency and the difficulty of an issue. The current label system has:
- `difficulty:{easy, moderate, hard}` - This is an instinctive measure give by the project lead or leads. It is a subjective best guess, however the current golden rule is that an issue with difficulty:easy should not require more than a morning (3~4 hours) to do and it should not require having to mess with multiple modules to complete. Issues with difficulty moderate or hard might require some discussion around the problem or even request that another team (i.e go-ipfs) makes some changes. The length of moderate or hard issue might be a day to ad-aeternum.
- `priority (P0, P1, P2, P3, P4)` - P0 is the most important while P4 is the least.
- `help wanted` - Issues perfect for new contributors. They will have the information necessary or the pointers for a new contributor to figure out what is required. These issues are never blocked on some other issue be done first.
## Weekly Core Dev Team Calls
[⚡️ⒿⓈ Core Dev Team Weekly Sync 🙌🏽](https://github.com/ipfs/pm/issues/650)

11
OKR.md Normal file
View File

@ -0,0 +1,11 @@
# Quarterly Objectives and Key Results
We try to frame our ongoing work using a process based on quarterly Objectives and Key Results (OKRs). Objectives reflect outcomes that are challenging, but realistic. Results are tangible and measurable.
## 2018 Q3
Find the js-libp2p OKRs for 2018 Q2 at the [2018 Q3 libp2p OKRs Spreadsheet](https://docs.google.com/spreadsheets/d/1HTXfgR5FyPTFhsTkFPRThkeMvHvCgJOaAs7BSl_vQ_0/edit#gid=1241853194)
## Previous Quarters
For the quarters before 2018 Q3, js-libp2p shared their KRs with the [IPFS OKRs](https://github.com/ipfs/js-ipfs/blob/master/OKR.md).

350
README.md
View File

@ -1,20 +1,19 @@
<h1 align="center"> <h1 align="center">
<a href="libp2p.io"><img width="250" src="https://github.com/libp2p/libp2p/blob/master/logo/alternates/libp2p-logo-alt-2.png?raw=true" alt="libp2p hex logo" /></a> <a href="libp2p.io"><img width="250" src="https://github.com/libp2p/libp2p/blob/master/logo/black-bg-2.png?raw=true" alt="libp2p hex logo" /></a>
</h1> </h1>
<h3 align="center">The JavaScript implementation of the libp2p Networking Stack.</h3> <h3 align="center">The JavaScript implementation of the libp2p Networking Stack.</h3>
<p align="center"> <p align="center">
<a href="http://ipn.io"><img src="https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square" /></a> <a href="http://ipn.io"><img src="https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square" /></a>
<a href="http://libp2p.io/"><img src="https://img.shields.io/badge/project-libp2p-blue.svg?style=flat-square" /></a> <a href="http://libp2p.io/"><img src="https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square" /></a>
<a href="http://webchat.freenode.net/?channels=%23ipfs"><img src="https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square" /></a> <a href="http://webchat.freenode.net/?channels=%23libp2p"><img src="https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square" /></a>
<a href="https://waffle.io/libp2p/libp2p"><img src="https://img.shields.io/badge/pm-waffle-blue.svg?style=flat-square" /></a> <a href="https://waffle.io/libp2p/libp2p"><img src="https://img.shields.io/badge/pm-waffle-yellow.svg?style=flat-square" /></a>
</p> </p>
<p align="center"> <p align="center">
<a href="https://travis-ci.org/libp2p/js-libp2p"><img src="https://travis-ci.org/libp2p/js-libp2p.svg?branch=master" /></a> <a href="https://ci.ipfs.team/job/libp2p/job/js-libp2p/job/master/"><img src="https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p/master" /></a>
<a href="https://circleci.com/gh/libp2p/js-libp2p"><img src="https://circleci.com/gh/libp2p/js-libp2p.svg?style=svg" /></a> <a href="https://codecov.io/gh/libp2p/js-libp2p"><img src="https://codecov.io/gh/libp2p/js-libp2p/branch/master/graph/badge.svg"></a>
<a href="https://coveralls.io/github/libp2p/js-libp2p?branch=master"><img src="https://coveralls.io/repos/github/libp2p/js-libp2p/badge.svg?branch=master"></a>
<br> <br>
<a href="https://david-dm.org/libp2p/js-libp2p"><img src="https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square" /></a> <a href="https://david-dm.org/libp2p/js-libp2p"><img src="https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square" /></a>
<a href="https://github.com/feross/standard"><img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square"></a> <a href="https://github.com/feross/standard"><img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square"></a>
@ -32,6 +31,16 @@ We've come a long way, but this project is still in Alpha, lots of development i
[![Throughput Graph](https://graphs.waffle.io/libp2p/js-libp2p/throughput.svg)](https://waffle.io/libp2p/js-libp2p/metrics/throughput) [![Throughput Graph](https://graphs.waffle.io/libp2p/js-libp2p/throughput.svg)](https://waffle.io/libp2p/js-libp2p/metrics/throughput)
[**`Weekly Core Dev Calls`**](https://github.com/ipfs/pm/issues/650)
## Tech Lead
[David Dias](https://github.com/diasdavid/)
## Lead Maintainer
[David Dias](https://github.com/diasdavid/)
## Table of Contents ## Table of Contents
- [Background](#background) - [Background](#background)
@ -93,47 +102,88 @@ libp2p becomes very simple and basically acts as a glue for every module that co
```JavaScript ```JavaScript
// Creating a bundle that adds: // Creating a bundle that adds:
// transport: websockets + tcp // transport: websockets + tcp
// stream-muxing: SPDY // stream-muxing: spdy & mplex
// crypto-channel: secio // crypto-channel: secio
// discovery: multicast-dns // discovery: multicast-dns
const libp2p = require('libp2p') const libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets') const WS = require('libp2p-websockets')
const spdy = require('libp2p-spdy') const SPDY = require('libp2p-spdy')
const secio = require('libp2p-secio') const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const MulticastDNS = require('libp2p-mdns') const MulticastDNS = require('libp2p-mdns')
const DHT = require('libp2p-kad-dht') const DHT = require('libp2p-kad-dht')
const defaultsDeep = require('@nodeutils/defaults-deep')
const Protector = require('libp2p-pnet')
class Node extends libp2p { class Node extends libp2p {
constructor (peerInfo, peerBook, options) { constructor (_peerInfo, _peerBook, _options) {
options = options || {} const defaults = {
peerInfo: _peerInfo, // The Identity of your Peer
peerBook: _peerBook, // Where peers get tracked, if undefined libp2p will create one instance
const modules = { // The libp2p modules for this libp2p bundle
transport: [ modules: {
new TCP(), transport: [
new WS() TCP,
], new WS() // It can take instances too!
connection: {
muxer: [
spdy
], ],
crypto: [ streamMuxer: [
secio SPDY,
] MPLEX
],
connEncryption: [
SECIO
],
connProtector: new Protector(/*protector specific opts*/),
peerDiscovery: [
MulticastDNS
],
peerRouting: {}, // Currently both peerRouting and contentRouting are patched through the DHT,
contentRouting: {}, // this will change once we factor that into two modules, for now do the following line:
dht: DHT // DHT enables PeerRouting, ContentRouting and DHT itself components
}, },
discovery: [
new MulticastDNS(peerInfo) // libp2p config options (typically found on a config.json)
], config: { // The config object is the part of the config that can go into a file, config.json.
// DHT is passed as its own enabling PeerRouting, ContentRouting and DHT itself components peerDiscovery: {
dht: DHT mdns: { // mdns options
interval: 1000 // ms
enabled: true
},
webrtcStar: { // webrtc-star options
interval: 1000 // ms
enabled: false
}
// .. other discovery module options.
},
peerRouting: {},
contentRouting: {},
relay: { // Circuit Relay options
enabled: false,
hop: {
enabled: false,
active: false
}
},
dht: {
kBucketSize: 20
},
// Enable/Disable Experimental features
EXPERIMENTAL: { // Experimental features ("behind a flag")
pubsub: false,
dht: false
}
}
} }
super(modules, peerInfo, peerBook, options) // overload any defaults of your bundle using https://github.com/nodeutils/defaults-deep
super(defaultsDeep(_options, defaults))
} }
} }
// Now all the nodes you create, will have TCP, WebSockets, SPDY, SECIO and MulticastDNS support. // Now all the nodes you create, will have TCP, WebSockets, SPDY, MPLEX, SECIO and MulticastDNS support.
``` ```
### API ### API
@ -158,16 +208,26 @@ class Node extends libp2p {
`callback` is a function with the following `function (err) {}` signature, where `err` is an Error in case stopping the node fails. `callback` is a function with the following `function (err) {}` signature, where `err` is an Error in case stopping the node fails.
#### `libp2p.dial(peer [, protocol, callback])` #### `libp2p.dial(peer, callback)`
> Dials to another peer in the network. > Dials to another peer in the network, establishes the connection.
- `peer`: can be an instance of [PeerInfo][], [PeerId][] or [multiaddr][] - `peer`: can be an instance of [PeerInfo][], [PeerId][], [multiaddr][], or a multiaddr string
- `protocol`: String that defines the protocol (e.g '/ipfs/bitswap/1.1.0') - `callback`: Function with signature `function (err, conn) {}` where `conn` is a [Connection](https://github.com/libp2p/interface-connection) object
`callback` is a function with the following `function (err, conn) {}` signature, where `err` is an Error in of failure to dial the connection and `conn` is a [Connection][] instance in case of a protocol selected, if not it is undefined. `callback` is a function with the following `function (err, conn) {}` signature, where `err` is an Error in of failure to dial the connection and `conn` is a [Connection][] instance in case of a protocol selected, if not it is undefined.
#### `libp2p.hangUp(peer, callback) #### `libp2p.dialProtocol(peer, protocol, callback)`
> Dials to another peer in the network and selects a protocol to talk with that peer.
- `peer`: can be an instance of [PeerInfo][], [PeerId][], [multiaddr][], or a multiaddr string
- `protocol`: String that defines the protocol (e.g '/ipfs/bitswap/1.1.0')
- `callback`: Function with signature `function (err, conn) {}` where `conn` is a [Connection](https://github.com/libp2p/interface-connection) object
`callback` is a function with the following `function (err, conn) {}` signature, where `err` is an Error in of failure to dial the connection and `conn` is a [Connection][] instance in case of a protocol selected, if not it is undefined.
#### `libp2p.hangUp(peer, callback)`
> Closes an open connection with a peer, graciously. > Closes an open connection with a peer, graciously.
@ -196,7 +256,7 @@ class Node extends libp2p {
> Handle new protocol > Handle new protocol
- `protocol`: String that defines the protocol (e.g '/ipfs/bitswap/1.1.0') - `protocol`: String that defines the protocol (e.g '/ipfs/bitswap/1.1.0')
- `handlerFunc`: Function with signature `function (protocol, conn) {}` - `handlerFunc`: Function with signature `function (protocol, conn) {}` where `conn` is a [Connection](https://github.com/libp2p/interface-connection) object
- `matchFunc`: Function for matching on protocol (exact matching, semver, etc). Default to exact match. - `matchFunc`: Function for matching on protocol (exact matching, semver, etc). Default to exact match.
#### `libp2p.unhandle(protocol)` #### `libp2p.unhandle(protocol)`
@ -239,9 +299,13 @@ class Node extends libp2p {
> PeerInfo instance of the node > PeerInfo instance of the node
#### `libp2p.pubsub`
> Same API as IPFS PubSub, defined in the [CORE API Spec](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PUBSUB.md). Just replace `ipfs` by `libp2p` and you are golden.
--------------------- ---------------------
`DHT methods exposed` `DHT methods also exposed for the time being`
#### `libp2p.dht.put(key, value, callback)` #### `libp2p.dht.put(key, value, callback)`
@ -263,6 +327,123 @@ class Node extends libp2p {
[multiaddr]: https://github.com/multiformats/js-multiaddr [multiaddr]: https://github.com/multiformats/js-multiaddr
[Connection]: https://github.com/libp2p/interface-connection [Connection]: https://github.com/libp2p/interface-connection
-------
### Switch Stats API
##### `libp2p.stats.emit('update')`
Every time any stat value changes, this object emits an `update` event.
#### Global stats
##### `libp2p.stats.global.snapshot`
Should return a stats snapshot, which is an object containing the following keys and respective values:
- dataSent: amount of bytes sent, [Big](https://github.com/MikeMcl/big.js#readme) number
- dataReceived: amount of bytes received, [Big](https://github.com/MikeMcl/big.js#readme) number
##### `libp2p.stats.global.movingAverages`
Returns an object containing the following keys:
- dataSent
- dataReceived
Each one of them contains an object that has a key for each interval (`60000`, `300000` and `900000` miliseconds).
Each one of these values is [an exponential moving-average instance](https://github.com/pgte/moving-average#readme).
#### Per-transport stats
##### `libp2p.stats.transports()`
Returns an array containing the tags (string) for each observed transport.
##### `libp2p.stats.forTransport(transportTag).snapshot`
Should return a stats snapshot, which is an object containing the following keys and respective values:
- dataSent: amount of bytes sent, [Big](https://github.com/MikeMcl/big.js#readme) number
- dataReceived: amount of bytes received, [Big](https://github.com/MikeMcl/big.js#readme) number
##### `libp2p.stats.forTransport(transportTag).movingAverages`
Returns an object containing the following keys:
dataSent
dataReceived
Each one of them contains an object that has a key for each interval (`60000`, `300000` and `900000` miliseconds).
Each one of these values is [an exponential moving-average instance](https://github.com/pgte/moving-average#readme).
#### Per-protocol stats
##### `libp2p.stats.protocols()`
Returns an array containing the tags (string) for each observed protocol.
##### `libp2p.stats.forProtocol(protocolTag).snapshot`
Should return a stats snapshot, which is an object containing the following keys and respective values:
- dataSent: amount of bytes sent, [Big](https://github.com/MikeMcl/big.js#readme) number
- dataReceived: amount of bytes received, [Big](https://github.com/MikeMcl/big.js#readme) number
##### `libp2p.stats.forProtocol(protocolTag).movingAverages`
Returns an object containing the following keys:
- dataSent
- dataReceived
Each one of them contains an object that has a key for each interval (`60000`, `300000` and `900000` miliseconds).
Each one of these values is [an exponential moving-average instance](https://github.com/pgte/moving-average#readme).
#### Per-peer stats
##### `libp2p.stats.peers()`
Returns an array containing the peerIDs (B58-encoded string) for each observed peer.
##### `libp2p.stats.forPeer(peerId:String).snapshot`
Should return a stats snapshot, which is an object containing the following keys and respective values:
- dataSent: amount of bytes sent, [Big](https://github.com/MikeMcl/big.js#readme) number
- dataReceived: amount of bytes received, [Big](https://github.com/MikeMcl/big.js#readme) number
##### `libp2p.stats.forPeer(peerId:String).movingAverages`
Returns an object containing the following keys:
- dataSent
- dataReceived
Each one of them contains an object that has a key for each interval (`60000`, `300000` and `900000` miliseconds).
Each one of these values is [an exponential moving-average instance](https://github.com/pgte/moving-average#readme).
#### Stats update interval
Stats are not updated in real-time. Instead, measurements are buffered and stats are updated at an interval. The maximum interval can be defined through the `Switch` constructor option `stats.computeThrottleTimeout`, defined in miliseconds.
### Private Networks
#### Enforcement
Libp2p provides support for connection protection, such as for private networks. You can enforce network protection by setting the environment variable `LIBP2P_FORCE_PNET=1`. When this variable is on, if no protector is set via `options.connProtector`, Libp2p will throw an error upon creation.
#### Protectors
Some available network protectors:
* [libp2p-pnet](https://github.com/libp2p/js-libp2p-pnet)
## Development ## Development
**Clone and install dependencies:** **Clone and install dependencies:**
@ -304,37 +485,70 @@ N/A
List of packages currently in existence for libp2p List of packages currently in existence for libp2p
| Package | Version | Dependencies | DevDependencies | | Package | Version | Deps | CI | Coverage |
|---------|---------|--------------|-----------------| | ---------|---------|---------|---------|--------- |
| **Transports** | | **Libp2p** |
| [`libp2p-utp`](//github.com/libp2p/js-libp2p-utp) | [![npm](https://img.shields.io/npm/v/libp2p-utp.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-utp/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-utp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-utp) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-utp/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-utp?type=dev) | | [`interface-libp2p`](//github.com/libp2p/interface-libp2p) | [![npm](https://img.shields.io/npm/v/interface-libp2p.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-libp2p/releases) | [![Deps](https://david-dm.org/libp2p/interface-libp2p.svg?style=flat-square)](https://david-dm.org/libp2p/interface-libp2p) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/interface-libp2p/master)](https://ci.ipfs.team/job/libp2p/job/interface-libp2p/job/master/) | [![codecov](https://codecov.io/gh/libp2p/interface-libp2p/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-libp2p) |
| [`libp2p-websockets`](//github.com/libp2p/js-libp2p-websockets) | [![npm](https://img.shields.io/npm/v/libp2p-websockets.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websockets/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-websockets.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websockets) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-websockets/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websockets?type=dev) | | [`libp2p`](//github.com/libp2p/js-libp2p) | [![npm](https://img.shields.io/npm/v/libp2p.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p) |
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-webrtc-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-webrtc-star/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star?type=dev) | | **Connection** |
| **Connection Upgrades** | | [`interface-connection`](//github.com/libp2p/interface-connection) | [![npm](https://img.shields.io/npm/v/interface-connection.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-connection/releases) | [![Deps](https://david-dm.org/libp2p/interface-connection.svg?style=flat-square)](https://david-dm.org/libp2p/interface-connection) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/interface-connection/master)](https://ci.ipfs.team/job/libp2p/job/interface-connection/job/master/) | [![codecov](https://codecov.io/gh/libp2p/interface-connection/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-connection) |
| [`interface-connection`](//github.com/libp2p/interface-connection) | [![npm](https://img.shields.io/npm/v/interface-connection.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-connection/releases) | [![Dependency Status](https://david-dm.org/libp2p/interface-connection.svg?style=flat-square)](https://david-dm.org/libp2p/interface-connection) | [![devDependency Status](https://david-dm.org/libp2p/interface-connection/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/interface-connection?type=dev) | | **Transport** |
| **Stream Muxers** | | [`interface-transport`](//github.com/libp2p/interface-transport) | [![npm](https://img.shields.io/npm/v/interface-transport.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-transport/releases) | [![Deps](https://david-dm.org/libp2p/interface-transport.svg?style=flat-square)](https://david-dm.org/libp2p/interface-transport) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/interface-transport/master)](https://ci.ipfs.team/job/libp2p/job/interface-transport/job/master/) | [![codecov](https://codecov.io/gh/libp2p/interface-transport/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-transport) |
| [`interface-stream-muxer`](//github.com/libp2p/interface-stream-muxer) | [![npm](https://img.shields.io/npm/v/interface-stream-muxer.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-stream-muxer/releases) | [![Dependency Status](https://david-dm.org/libp2p/interface-stream-muxer.svg?style=flat-square)](https://david-dm.org/libp2p/interface-stream-muxer) | [![devDependency Status](https://david-dm.org/libp2p/interface-stream-muxer/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/interface-stream-muxer?type=dev) | | [`libp2p-circuit`](//github.com/libp2p/js-libp2p-circuit) | [![npm](https://img.shields.io/npm/v/libp2p-circuit.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-circuit/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-circuit.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-circuit) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-circuit/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-circuit/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-circuit/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-circuit) |
| [`libp2p-spdy`](//github.com/libp2p/js-libp2p-spdy) | [![npm](https://img.shields.io/npm/v/libp2p-spdy.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-spdy/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-spdy.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-spdy) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-spdy/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-spdy?type=dev) | | [`libp2p-tcp`](//github.com/libp2p/js-libp2p-tcp) | [![npm](https://img.shields.io/npm/v/libp2p-tcp.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-tcp/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-tcp) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-tcp/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-tcp/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-tcp/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-tcp) |
| [`libp2p-multiplex`](https://github.com/libp2p/js-libp2p-multiplex) | [`libp2p-udp`](//github.com/libp2p/js-libp2p-udp) | [![npm](https://img.shields.io/npm/v/libp2p-udp.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-udp/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-udp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-udp) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-udp/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-udp/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-udp/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-udp) |
| **Discovery** | | [`libp2p-udt`](//github.com/libp2p/js-libp2p-udt) | [![npm](https://img.shields.io/npm/v/libp2p-udt.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-udt/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-udt.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-udt) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-udt/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-udt/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-udt/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-udt) |
| [`libp2p-mdns-discovery`](//github.com/libp2p/js-libp2p-mdns-discovery) | [![npm](https://img.shields.io/npm/v/libp2p-mdns-discovery.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-mdns-discovery/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-mdns-discovery.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mdns-discovery) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-mdns-discovery/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mdns-discovery?type=dev) | | [`libp2p-utp`](//github.com/libp2p/js-libp2p-utp) | [![npm](https://img.shields.io/npm/v/libp2p-utp.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-utp/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-utp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-utp) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-utp/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-utp/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-utp/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-utp) |
| [`libp2p-railing`](//github.com/libp2p/js-libp2p-railing) | [![npm](https://img.shields.io/npm/v/libp2p-railing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-railing/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-railing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-railing) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-railing/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-railing?type=dev) | | [`libp2p-webrtc-direct`](//github.com/libp2p/js-libp2p-webrtc-direct) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-direct.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-direct/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-direct.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-direct) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-webrtc-direct/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-webrtc-direct/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct) |
| **Crypto Channels** | | [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-webrtc-star/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-webrtc-star/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) |
| [`libp2p-secio`](//github.com/libp2p/js-libp2p-secio) | [![npm](https://img.shields.io/npm/v/libp2p-secio.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-secio/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-secio.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-secio) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-secio/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-secio?type=dev) | | [`libp2p-websockets`](//github.com/libp2p/js-libp2p-websockets) | [![npm](https://img.shields.io/npm/v/libp2p-websockets.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websockets/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websockets.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websockets) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-websockets/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-websockets/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websockets/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websockets) |
| **Peer Routing** | | [`libp2p-websocket-star`](//github.com/libp2p/js-libp2p-websocket-star) | [![npm](https://img.shields.io/npm/v/libp2p-websocket-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websocket-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websocket-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-websocket-star/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-websocket-star/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websocket-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websocket-star) |
| [`libp2p-kad-routing`](//github.com/libp2p/js-libp2p-kad-routing) | [![npm](https://img.shields.io/npm/v/libp2p-kad-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-routing/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-kad-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-routing) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-kad-routing/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-routing?type=dev) | | [`libp2p-websocket-star-rendezvous`](//github.com/libp2p/js-libp2p-websocket-star-rendezvous) | [![npm](https://img.shields.io/npm/v/libp2p-websocket-star-rendezvous.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websocket-star-rendezvous/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websocket-star-rendezvous.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star-rendezvous) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-websocket-star-rendezvous/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-websocket-star-rendezvous/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websocket-star-rendezvous/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websocket-star-rendezvous) |
| **Content Routing** | | **Crypto Channels** |
| [`interface-record-store`](//github.com/libp2p/interface-record-store) | [![npm](https://img.shields.io/npm/v/interface-record-store.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-record-store/releases) | [![Dependency Status](https://david-dm.org/libp2p/interface-record-store.svg?style=flat-square)](https://david-dm.org/libp2p/interface-record-store) | [![devDependency Status](https://david-dm.org/libp2p/interface-record-store/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/interface-record-store?type=dev) | | [`libp2p-secio`](//github.com/libp2p/js-libp2p-secio) | [![npm](https://img.shields.io/npm/v/libp2p-secio.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-secio/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-secio.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-secio) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-secio/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-secio/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-secio/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-secio) |
| [`libp2p-record`](//github.com/libp2p/js-libp2p-record) | [![npm](https://img.shields.io/npm/v/libp2p-record.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-record/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-record.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-record) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-record/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-record?type=dev) | | **Stream Muxers** |
| [`libp2p-distributed-record-store`](//github.com/libp2p/js-libp2p-distributed-record-store) | [![npm](https://img.shields.io/npm/v/undefined.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-distributed-record-store/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-distributed-record-store.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-distributed-record-store) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-distributed-record-store/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-distributed-record-store?type=dev) | | [`interface-stream-muxer`](//github.com/libp2p/interface-stream-muxer) | [![npm](https://img.shields.io/npm/v/interface-stream-muxer.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-stream-muxer/releases) | [![Deps](https://david-dm.org/libp2p/interface-stream-muxer.svg?style=flat-square)](https://david-dm.org/libp2p/interface-stream-muxer) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/interface-stream-muxer/master)](https://ci.ipfs.team/job/libp2p/job/interface-stream-muxer/job/master/) | [![codecov](https://codecov.io/gh/libp2p/interface-stream-muxer/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-stream-muxer) |
| [`libp2p-kad-record-store`](//github.com/libp2p/js-libp2p-kad-record-store) | [![npm](https://img.shields.io/npm/v/libp2p-kad-record-store.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-record-store/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-kad-record-store.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-record-store) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-kad-record-store/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-record-store?type=dev) | | [`libp2p-mplex`](//github.com/libp2p/js-libp2p-mplex) | [![npm](https://img.shields.io/npm/v/libp2p-mplex.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-mplex/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-mplex.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mplex) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-mplex/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-mplex/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-mplex/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-mplex) |
| **Generics** | | [`libp2p-spdy`](//github.com/libp2p/js-libp2p-spdy) | [![npm](https://img.shields.io/npm/v/libp2p-spdy.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-spdy/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-spdy.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-spdy) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-spdy/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-spdy/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-spdy/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-spdy) |
| [`libp2p-swarm`](//github.com/libp2p/js-libp2p-swarm) | [![npm](https://img.shields.io/npm/v/libp2p-swarm.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-swarm/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-swarm.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-swarm) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-swarm/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-swarm?type=dev) | | **Discovery** |
| [`libp2p-ping`](//github.com/libp2p/js-libp2p-ping) | [![npm](https://img.shields.io/npm/v/libp2p-ping.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-ping/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-ping.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-ping) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-ping/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-ping?type=dev) | | [`interface-peer-discovery`](//github.com/libp2p/interface-peer-discovery) | [![npm](https://img.shields.io/npm/v/interface-peer-discovery.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-peer-discovery/releases) | [![Deps](https://david-dm.org/libp2p/interface-peer-discovery.svg?style=flat-square)](https://david-dm.org/libp2p/interface-peer-discovery) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/interface-peer-discovery/master)](https://ci.ipfs.team/job/libp2p/job/interface-peer-discovery/job/master/) | [![codecov](https://codecov.io/gh/libp2p/interface-peer-discovery/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-peer-discovery) |
| [`multistream-select`](//github.com/libp2p/js-multistream) | [![npm](https://img.shields.io/npm/v/multistream-select.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-multistream/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-multistream.svg?style=flat-square)](https://david-dm.org/libp2p/js-multistream) | [![devDependency Status](https://david-dm.org/libp2p/js-multistream/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-multistream?type=dev) | | [`libp2p-bootstrap`](//github.com/libp2p/js-libp2p-bootstrap) | [![npm](https://img.shields.io/npm/v/libp2p-bootstrap.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-bootstrap/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-bootstrap.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-bootstrap) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-bootstrap/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-bootstrap/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-bootstrap/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-bootstrap) |
| **Data Types** | | [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-kad-dht/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-kad-dht/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) |
| [`peer-book`](//github.com/libp2p/js-peer-book) | [![npm](https://img.shields.io/npm/v/peer-book.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-book/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-peer-book.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-book) | [![devDependency Status](https://david-dm.org/libp2p/js-peer-book/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-book?type=dev) | | [`libp2p-mdns`](//github.com/libp2p/js-libp2p-mdns) | [![npm](https://img.shields.io/npm/v/libp2p-mdns.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-mdns/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-mdns.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mdns) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-mdns/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-mdns/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-mdns/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-mdns) |
| [`peer-id`](https://github.com/libp2p/js-peer-id) | [`libp2p-rendezvous`](//github.com/libp2p/js-libp2p-rendezvous) | [![npm](https://img.shields.io/npm/v/libp2p-rendezvous.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-rendezvous/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-rendezvous.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-rendezvous) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-rendezvous/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-rendezvous/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-rendezvous/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-rendezvous) |
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-webrtc-star/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-webrtc-star/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) |
| [`libp2p-websocket-star`](//github.com/libp2p/js-libp2p-websocket-star) | [![npm](https://img.shields.io/npm/v/libp2p-websocket-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websocket-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websocket-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-websocket-star/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-websocket-star/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websocket-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websocket-star) |
| **NAT Traversal** |
| [`libp2p-circuit`](//github.com/libp2p/js-libp2p-circuit) | [![npm](https://img.shields.io/npm/v/libp2p-circuit.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-circuit/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-circuit.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-circuit) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-circuit/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-circuit/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-circuit/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-circuit) |
| [`libp2p-nat-mngr`](//github.com/libp2p/js-libp2p-nat-mngr) | [![npm](https://img.shields.io/npm/v/libp2p-nat-mngr.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-nat-mngr/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-nat-mngr.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-nat-mngr) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-nat-mngr/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-nat-mngr/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-nat-mngr/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-nat-mngr) |
| **Data Types** |
| [`peer-book`](//github.com/libp2p/js-peer-book) | [![npm](https://img.shields.io/npm/v/peer-book.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-book/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-book.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-book) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-peer-book/master)](https://ci.ipfs.team/job/libp2p/job/js-peer-book/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-peer-book/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-peer-book) |
| [`peer-id`](//github.com/libp2p/js-peer-id) | [![npm](https://img.shields.io/npm/v/peer-id.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-id/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-id.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-id) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-peer-id/master)](https://ci.ipfs.team/job/libp2p/job/js-peer-id/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-peer-id/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-peer-id) |
| [`peer-info`](//github.com/libp2p/js-peer-info) | [![npm](https://img.shields.io/npm/v/peer-info.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-info/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-info.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-info) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-peer-info/master)](https://ci.ipfs.team/job/libp2p/job/js-peer-info/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-peer-info/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-peer-info) |
| **Content Routing** |
| [`interface-content-routing`](//github.com/libp2p/interface-content-routing) | [![npm](https://img.shields.io/npm/v/interface-content-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-content-routing/releases) | [![Deps](https://david-dm.org/libp2p/interface-content-routing.svg?style=flat-square)](https://david-dm.org/libp2p/interface-content-routing) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/interface-content-routing/master)](https://ci.ipfs.team/job/libp2p/job/interface-content-routing/job/master/) | [![codecov](https://codecov.io/gh/libp2p/interface-content-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-content-routing) |
| [`libp2p-delegated-content-routing`](//github.com/libp2p/js-libp2p-delegated-content-routing) | [![npm](https://img.shields.io/npm/v/libp2p-delegated-content-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-delegated-content-routing/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-delegated-content-routing/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-delegated-content-routing/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-delegated-content-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-delegated-content-routing) |
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-kad-dht/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-kad-dht/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) |
| **Peer Routing** |
| [`interface-peer-routing`](//github.com/libp2p/interface-peer-routing) | [![npm](https://img.shields.io/npm/v/interface-peer-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-peer-routing/releases) | [![Deps](https://david-dm.org/libp2p/interface-peer-routing.svg?style=flat-square)](https://david-dm.org/libp2p/interface-peer-routing) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/interface-peer-routing/master)](https://ci.ipfs.team/job/libp2p/job/interface-peer-routing/job/master/) | [![codecov](https://codecov.io/gh/libp2p/interface-peer-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-peer-routing) |
| [`libp2p-delegated-peer-routing`](//github.com/libp2p/js-libp2p-delegated-peer-routing) | [![npm](https://img.shields.io/npm/v/libp2p-delegated-peer-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-delegated-peer-routing/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-delegated-peer-routing/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-delegated-peer-routing/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing) |
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-kad-dht/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-kad-dht/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) |
| **Record Store** |
| [`interface-record-store`](//github.com/libp2p/interface-record-store) | [![npm](https://img.shields.io/npm/v/interface-record-store.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-record-store/releases) | [![Deps](https://david-dm.org/libp2p/interface-record-store.svg?style=flat-square)](https://david-dm.org/libp2p/interface-record-store) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/interface-record-store/master)](https://ci.ipfs.team/job/libp2p/job/interface-record-store/job/master/) | [![codecov](https://codecov.io/gh/libp2p/interface-record-store/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-record-store) |
| [`libp2p-record`](//github.com/libp2p/js-libp2p-record) | [![npm](https://img.shields.io/npm/v/libp2p-record.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-record/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-record.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-record) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-record/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-record/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-record/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-record) |
| **Generics** |
| [`libp2p-connection-manager`](//github.com/libp2p/js-libp2p-connection-manager) | [![npm](https://img.shields.io/npm/v/libp2p-connection-manager.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-connection-manager/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-connection-manager.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-connection-manager) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-connection-manager/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-connection-manager/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-connection-manager/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-connection-manager) |
| [`libp2p-crypto`](//github.com/libp2p/js-libp2p-crypto) | [![npm](https://img.shields.io/npm/v/libp2p-crypto.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-crypto/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-crypto.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-crypto/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-crypto/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-crypto/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-crypto) |
| [`libp2p-crypto-secp256k1`](//github.com/libp2p/js-libp2p-crypto-secp256k1) | [![npm](https://img.shields.io/npm/v/libp2p-crypto-secp256k1.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-crypto-secp256k1/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-crypto-secp256k1/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-crypto-secp256k1/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1) |
| [`libp2p-switch`](//github.com/libp2p/js-libp2p-switch) | [![npm](https://img.shields.io/npm/v/libp2p-switch.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-switch/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-switch.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-switch) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-switch/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-switch/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-switch/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-switch) |
| **Extensions** |
| [`libp2p-floodsub`](//github.com/libp2p/js-libp2p-floodsub) | [![npm](https://img.shields.io/npm/v/libp2p-floodsub.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-floodsub/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-floodsub.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-floodsub) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-floodsub/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-floodsub/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-floodsub/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-floodsub) |
| [`libp2p-identify`](//github.com/libp2p/js-libp2p-identify) | [![npm](https://img.shields.io/npm/v/libp2p-identify.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-identify/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-identify.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-identify) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-identify/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-identify/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-identify/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-identify) |
| [`libp2p-keychain`](//github.com/libp2p/js-libp2p-keychain) | [![npm](https://img.shields.io/npm/v/libp2p-keychain.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-keychain/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-keychain.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-keychain) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-keychain/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-keychain/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-keychain/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-keychain) |
| [`libp2p-ping`](//github.com/libp2p/js-libp2p-ping) | [![npm](https://img.shields.io/npm/v/libp2p-ping.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-ping/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-ping.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-ping) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-ping/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-ping/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-ping/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-ping) |
| [`libp2p-pnet`](//github.com/libp2p/js-libp2p-pnet) | [![npm](https://img.shields.io/npm/v/libp2p-pnet.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-pnet/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-pnet.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-pnet) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-pnet/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-pnet/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-pnet/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-pnet) |
| **Utilities** |
| [`p2pcat`](//github.com/libp2p/js-p2pcat) | [![npm](https://img.shields.io/npm/v/p2pcat.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-p2pcat/releases) | [![Deps](https://david-dm.org/libp2p/js-p2pcat.svg?style=flat-square)](https://david-dm.org/libp2p/js-p2pcat) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-p2pcat/master)](https://ci.ipfs.team/job/libp2p/job/js-p2pcat/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-p2pcat/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-p2pcat) |
## Contribute ## Contribute

41
RELEASE.md Normal file
View File

@ -0,0 +1,41 @@
# Release Template
> short tl;dr; of the release
# 🗺 What's left for release
# 🔦 Highlights
# 🏗 API Changes
# ✅ Release Checklist
- Robustness and quality
- [ ] Ensure that all tests are passing, this includes:
- [ ] unit
- [ ] Run tests of the following projects with the new release:
- [ ] [js-ipfs](https://github.com/ipfs/js-ipfs)
- Documentation
- [ ] Ensure that README.md is up to date
- [ ] Ensure that all the examples run
- Communication
- [ ] Create the release issue
- [ ] Announcements (both pre-release and post-release)
- [ ] Twitter
- [ ] IRC
- [ ] Reddit
- [ ] Blog post
# 🙌🏽 Want to contribute?
Would you like to contribute to the libp2p project and don't know how? Well, there are a few places you can get started:
- Check the issues with the `help wanted` label at the Ready column in our waffle board - https://waffle.io/libp2p/js-libp2p?label=help%20wanted
- Join an IPFS All Hands, introduce yourself and let us know where you would like to contribute - https://github.com/ipfs/pm/#all-hands-call
- Hack with IPFS and show us what you made! The All Hands call is also the perfect venue for demos, join in and show us what you built
- Join the discussion at http://discuss.ipfs.io/ and help users finding their answers.
- Join the [⚡️ⒿⓈ Core Dev Team Weekly Sync 🙌🏽 ](https://github.com/ipfs/pm/issues/650) and be part of the Sprint action!
# ⁉️ Do you have questions?
The best place to ask your questions about libp2p, how it works and what you can do with it is at [discuss.ipfs.io](http://discuss.ipfs.io). We are also available at the #libp2p channel on Freenode.

View File

@ -1,6 +1,10 @@
machine: machine:
node: node:
version: stable version: 8.11.3
test:
post:
- npm run coverage -- --upload --providers coveralls
dependencies: dependencies:
pre: pre:
@ -15,4 +19,4 @@ dependencies:
- sudo apt-get install -f - sudo apt-get install -f
- sudo apt-get install --only-upgrade lsb-base - sudo apt-get install --only-upgrade lsb-base
- sudo dpkg -i google-chrome.deb - sudo dpkg -i google-chrome.deb
- google-chrome --version - google-chrome --version

View File

@ -10,8 +10,8 @@ Let us know if you find any issue or if you want to contribute and add a new tut
- [Protocol and Stream Muxing](./protocol-and-stream-muxing) - [Protocol and Stream Muxing](./protocol-and-stream-muxing)
- [Encrypted Communications](./encrypted-communications) - [Encrypted Communications](./encrypted-communications)
- [Discovery Mechanisms](./discovery-mechanisms) - [Discovery Mechanisms](./discovery-mechanisms)
- [Peer Routing](./peer-routing) - [Peer Routing](./peer-and-content-routing)
- [Content Routing](./content-routing) - [Content Routing](./peer-and-content-routing)
- [PubSub](./pubsub) - [PubSub](./pubsub)
- [NAT Traversal](./nat-traversal) - [NAT Traversal](./nat-traversal)
- Circuit Relay (future) - Circuit Relay (future)
@ -22,4 +22,4 @@ Let us know if you find any issue or if you want to contribute and add a new tut
- [Running libp2p in the Browser](./libp2p-in-the-browser) - [Running libp2p in the Browser](./libp2p-in-the-browser)
- Running libp2p in the Electron (future) - Running libp2p in the Electron (future)
- [The standard echo net example with libp2p](./echo) - [The standard echo net example with libp2p](./echo)
- [A simple chat app with](./chat) - [A simple chat app with libp2p](./chat)

View File

@ -1 +1,13 @@
# Chat example with libp2p # Chat example with libp2p
This example creates a simple chat app in your terminal.
## Setup
1. Install the modules, `npm install`.
2. Open 2 terminal windows in the `./src` directory.
## Running
1. Run the listener in window 1, `node listener.js`
2. Run the dialer in window 2, `node dialer.js`
3. Type a message in either window and hit _enter_
4. Tell youself secrets to your hearts content!

View File

@ -31,7 +31,9 @@ async.parallel([
if (err) throw err if (err) throw err
const peerDialer = new PeerInfo(ids[0]) const peerDialer = new PeerInfo(ids[0])
peerDialer.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerDialer.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const nodeDialer = new Node(peerDialer) const nodeDialer = new Node({
peerInfo: peerDialer
})
const peerListener = new PeerInfo(ids[1]) const peerListener = new PeerInfo(ids[1])
idListener = ids[1] idListener = ids[1]
@ -47,7 +49,7 @@ async.parallel([
console.log(ma.toString() + '/ipfs/' + idListener.toB58String()) console.log(ma.toString() + '/ipfs/' + idListener.toB58String())
}) })
nodeDialer.dial(peerListener, '/chat/1.0.0', (err, conn) => { nodeDialer.dialProtocol(peerListener, '/chat/1.0.0', (err, conn) => {
if (err) { if (err) {
throw err throw err
} }

View File

@ -3,11 +3,12 @@
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MulticastDNS = require('libp2p-mdns') const MulticastDNS = require('libp2p-mdns')
const WS = require('libp2p-websockets') const WS = require('libp2p-websockets')
const Railing = require('libp2p-railing') const Bootstrap = require('libp2p-railing')
const spdy = require('libp2p-spdy') const spdy = require('libp2p-spdy')
const KadDHT = require('libp2p-kad-dht') const KadDHT = require('libp2p-kad-dht')
const multiplex = require('libp2p-multiplex') const mplex = require('libp2p-mplex')
const secio = require('libp2p-secio') const secio = require('libp2p-secio')
const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../..') const libp2p = require('../../..')
function mapMuxers (list) { function mapMuxers (list) {
@ -17,7 +18,7 @@ function mapMuxers (list) {
} }
switch (pref.trim().toLowerCase()) { switch (pref.trim().toLowerCase()) {
case 'spdy': return spdy case 'spdy': return spdy
case 'multiplex': return multiplex case 'mplex': return mplex
default: default:
throw new Error(pref + ' muxer not available') throw new Error(pref + ' muxer not available')
} }
@ -31,49 +32,45 @@ function getMuxers (muxers) {
} else if (muxers) { } else if (muxers) {
return mapMuxers(muxers) return mapMuxers(muxers)
} else { } else {
return [multiplex, spdy] return [mplex, spdy]
} }
} }
class Node extends libp2p { class Node extends libp2p {
constructor (peerInfo, peerBook, options) { constructor (_options) {
options = options || {} const defaults = {
modules: {
const modules = { transport: [
transport: [ TCP,
new TCP(), WS
new WS() ],
], streamMuxer: getMuxers(_options.muxer),
connection: { connEncryption: [ secio ],
muxer: getMuxers(options.muxer), peerDiscovery: [
crypto: [ secio ] MulticastDNS,
Bootstrap
],
dht: KadDHT
}, },
discovery: [] config: {
peerDiscovery: {
mdns: {
interval: 10000,
enabled: false
},
bootstrap: {
interval: 10000,
enabled: false,
list: _options.bootstrapList
}
},
dht: {
kBucketSize: 20
}
}
} }
if (options.dht) { super(defaultsDeep(_options, defaults))
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)
} }
} }

View File

@ -14,14 +14,16 @@ PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
} }
const peerListener = new PeerInfo(idListener) const peerListener = new PeerInfo(idListener)
peerListener.multiaddrs.add('/ip4/0.0.0.0/tcp/10333') peerListener.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
const nodeListener = new Node(peerListener) const nodeListener = new Node({
peerInfo: peerListener
})
nodeListener.start((err) => { nodeListener.start((err) => {
if (err) { if (err) {
throw err throw err
} }
nodeListener.swarm.on('peer-mux-established', (peerInfo) => { nodeListener.on('peer:connect', (peerInfo) => {
console.log(peerInfo.id.toB58String()) console.log(peerInfo.id.toB58String())
}) })

View File

@ -1,12 +1,13 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const Multiplex = require('libp2p-multiplex') const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const Railing = require('libp2p-railing') const Bootstrap = require('libp2p-railing')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const defaultsDeep = require('@nodeutils/defaults-deep')
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-nodejs.json // Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-nodejs.json
const bootstrapers = [ const bootstrapers = [
@ -22,16 +23,26 @@ const bootstrapers = [
] ]
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [Multiplex], streamMuxer: [ Mplex ],
crypto: [SECIO] connEncryption: [ SECIO ],
peerDiscovery: [ Bootstrap ]
}, },
discovery: [new Railing(bootstrapers)] config: {
peerDiscovery: {
bootstrap: {
interval: 2000,
enabled: true,
list: bootstrapers
}
}
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -41,7 +52,9 @@ waterfall([
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({
peerInfo
})
node.start(cb) node.start(cb)
} }
], (err) => { ], (err) => {

View File

@ -1,25 +1,35 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const Multiplex = require('libp2p-multiplex') const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const MulticastDNS = require('libp2p-mdns') const MulticastDNS = require('libp2p-mdns')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [Multiplex], streamMuxer: [ Mplex ],
crypto: [SECIO] connEncryption: [ SECIO ],
peerDiscovery: [ MulticastDNS ]
}, },
discovery: [new MulticastDNS(peerInfo, { interval: 1000 })] config: {
peerDiscovery: {
mdns: {
interval: 1000,
enabled: true
}
}
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -30,7 +40,9 @@ function createNode (callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({
peerInfo
})
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))

View File

@ -8,22 +8,33 @@ These mechanisms save configuration and enable a node to operate without any exp
## 1. Bootstrap list of Peers when booting a node ## 1. Bootstrap list of Peers when booting a node
For this demo, we will connect to IPFS default bootstrapper nodes and so, we will need to support the same set of features those nodes have, that are: TCP, multiplex and SECIO. You can see the complete example at [1.js](./1.js). For this demo, we will connect to IPFS default bootstrapper nodes and so, we will need to support the same set of features those nodes have, that are: TCP, mplex and SECIO. You can see the complete example at [1.js](./1.js).
First, we create our libp2p bundle. First, we create our libp2p bundle.
```JavaScript ```JavaScript
const Bootstrap = require('libp2p-railing')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (peerInfo) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [Multiplex], streamMuxer: [ Mplex ],
crypto: [SECIO] connEncryption: [ SECIO ],
peerDiscovery: [ Bootstrap ]
}, },
discovery: [new Railing(bootstrapers)] config: {
peerDiscovery: {
bootstrap: {
interval: 2000,
enabled: true,
list: bootstrapers
}
}
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
``` ```
@ -46,14 +57,16 @@ const bootstrapers = [
Now, once we create and start the node, we can listen for events such as `peer:discovery` and `peer:connect`, these events tell us when we found a peer, independently of the discovery mechanism used and when we actually dialed to that peer. Now, once we create and start the node, we can listen for events such as `peer:discovery` and `peer:connect`, these events tell us when we found a peer, independently of the discovery mechanism used and when we actually dialed to that peer.
``` ```JavaScript
let node let node
waterfall([ waterfall([
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({
peerInfo
})
node.start(cb) node.start(cb)
} }
], (err) => { ], (err) => {
@ -108,17 +121,25 @@ Update your libp2p bundle to include MulticastDNS.
```JavaScript ```JavaScript
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (peerInfo) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [Multiplex], streamMuxer: [ Mplex ],
crypto: [SECIO] connEncryption: [ SECIO ],
peerDiscovery: [ MulticastDNS ]
}, },
// We set the interval here to 1 second so that is faster to observe. The config: {
// default is 10 seconds. peerDiscovery: {
discovery: [new MulticastDNS(peerInfo, { interval: 1000 })] mdns: {
// Run at 1s so we can observe more quickly, default is 10s
interval: 1000,
enabled: true
}
}
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
``` ```

View File

@ -1 +1,13 @@
# Echo example with libp2p # Echo example with libp2p
This example performs a simple echo from the listener to the dialer.
## Setup
1. Install the modules, `npm install`.
2. Open 2 terminal windows in the `./src` directory.
## Running
1. Run the listener in window 1, `node listener.js`
2. Run the dialer in window 2, `node dialer.js`
3. You should see console logs showing the dial, and the received echo of _hey_
4. If you look at the listener window, you will see it receiving the dial

View File

@ -21,7 +21,9 @@ async.parallel([
const dialerId = ids[0] const dialerId = ids[0]
const dialerPeerInfo = new PeerInfo(dialerId) const dialerPeerInfo = new PeerInfo(dialerId)
dialerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') dialerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const dialerNode = new Node(dialerPeerInfo) const dialerNode = new Node({
peerInfo: dialerPeerInfo
})
// Peer to Dial // Peer to Dial
const listenerPeerInfo = new PeerInfo(ids[1]) const listenerPeerInfo = new PeerInfo(ids[1])
@ -38,7 +40,7 @@ async.parallel([
'/ipfs/' + dialerId.toB58String())) '/ipfs/' + dialerId.toB58String()))
console.log('Dialing to peer:', listenerMultiaddr.toString()) console.log('Dialing to peer:', listenerMultiaddr.toString())
dialerNode.dial(listenerPeerInfo, '/echo/1.0.0', (err, conn) => { dialerNode.dialProtocol(listenerPeerInfo, '/echo/1.0.0', (err, conn) => {
if (err) { throw err } if (err) { throw err }
console.log('nodeA dialed to nodeB on protocol: /echo/1.0.0') console.log('nodeA dialed to nodeB on protocol: /echo/1.0.0')

View File

@ -3,11 +3,12 @@
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MulticastDNS = require('libp2p-mdns') const MulticastDNS = require('libp2p-mdns')
const WS = require('libp2p-websockets') const WS = require('libp2p-websockets')
const Railing = require('libp2p-railing') const Bootstrap = require('libp2p-railing')
const spdy = require('libp2p-spdy') const spdy = require('libp2p-spdy')
const KadDHT = require('libp2p-kad-dht') const KadDHT = require('libp2p-kad-dht')
const multiplex = require('libp2p-multiplex') const mplex = require('libp2p-mplex')
const secio = require('libp2p-secio') const secio = require('libp2p-secio')
const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../..') const libp2p = require('../../..')
function mapMuxers (list) { function mapMuxers (list) {
@ -17,7 +18,7 @@ function mapMuxers (list) {
} }
switch (pref.trim().toLowerCase()) { switch (pref.trim().toLowerCase()) {
case 'spdy': return spdy case 'spdy': return spdy
case 'multiplex': return multiplex case 'mplex': return mplex
default: default:
throw new Error(pref + ' muxer not available') throw new Error(pref + ' muxer not available')
} }
@ -31,49 +32,45 @@ function getMuxers (muxers) {
} else if (muxers) { } else if (muxers) {
return mapMuxers(muxers) return mapMuxers(muxers)
} else { } else {
return [multiplex, spdy] return [mplex, spdy]
} }
} }
class Node extends libp2p { class Node extends libp2p {
constructor (peerInfo, peerBook, options) { constructor (_options) {
options = options || {} const defaults = {
modules: {
const modules = { transport: [
transport: [ TCP,
new TCP(), WS
new WS() ],
], streamMuxer: getMuxers(_options.muxer),
connection: { connEncryption: [ secio ],
muxer: getMuxers(options.muxer), peerDiscovery: [
crypto: [ secio ] MulticastDNS,
Bootstrap
],
dht: KadDHT
}, },
discovery: [] config: {
peerDiscovery: {
mdns: {
interval: 10000,
enabled: false
},
bootstrap: {
interval: 10000,
enabled: false,
list: _options.bootstrapList
}
},
dht: {
kBucketSize: 20
}
}
} }
if (options.dht) { super(defaultsDeep(_options, defaults))
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)
} }
} }

View File

@ -25,9 +25,11 @@ series([
(cb) => { (cb) => {
const listenerPeerInfo = new PeerInfo(listenerId) const listenerPeerInfo = new PeerInfo(listenerId)
listenerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/10333') listenerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
listenerNode = new Node(listenerPeerInfo) listenerNode = new Node({
peerInfo: listenerPeerInfo
})
listenerNode.swarm.on('peer-mux-established', (peerInfo) => { listenerNode.on('peer:connect', (peerInfo) => {
console.log('received dial to me from:', peerInfo.id.toB58String()) console.log('received dial to me from:', peerInfo.id.toB58String())
}) })

View File

@ -1,6 +1,6 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const SPDY = require('libp2p-spdy') const SPDY = require('libp2p-spdy')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
@ -8,17 +8,19 @@ const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const pull = require('pull-stream') const pull = require('pull-stream')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [SPDY], streamMuxer: [ SPDY ],
crypto: [SECIO] connEncryption: [ SECIO ]
} }
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -29,7 +31,9 @@ function createNode (callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({
peerInfo
})
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))
@ -52,7 +56,7 @@ parallel([
) )
}) })
node1.dial(node2.peerInfo, '/a-protocol', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/a-protocol', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['This information is sent out encrypted to the other peer']), conn) pull(pull.values(['This information is sent out encrypted to the other peer']), conn)
}) })

View File

@ -19,15 +19,16 @@ const SECIO = require('libp2p-secio')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (peerInfo) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [SPDY], streamMuxer: [ SPDY ],
// Attach secio as the crypto channel to use // Attach secio as the crypto channel to use
crypto: [SECIO] connEncryption: [ SECIO ]
} }
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
``` ```

View File

@ -13,17 +13,16 @@
"browserify": "^14.5.0", "browserify": "^14.5.0",
"concat-stream": "^1.6.0", "concat-stream": "^1.6.0",
"detect-dom-ready": "^1.0.2", "detect-dom-ready": "^1.0.2",
"node-static": "^0.7.10" "node-static": "~0.7.10"
}, },
"dependencies": { "dependencies": {
"detect-dom-ready": "^1.0.2", "detect-dom-ready": "^1.0.2",
"libp2p": "^0.13.0", "libp2p-mplex": "~0.8.0",
"libp2p-multiplex": "^0.5.0", "libp2p-railing": "~0.9.1",
"libp2p-railing": "^0.7.1", "libp2p-secio": "~0.10.0",
"libp2p-secio": "^0.8.1", "libp2p-spdy": "~0.12.1",
"libp2p-spdy": "^0.11.0", "libp2p-webrtc-star": "~0.15.3",
"libp2p-webrtc-star": "^0.13.2", "libp2p-websockets": "~0.12.0",
"libp2p-websockets": "^0.10.4", "peer-info": "~0.14.1"
"peer-info": "^0.11.0"
} }
} }

View File

@ -2,13 +2,12 @@
const WebRTCStar = require('libp2p-webrtc-star') const WebRTCStar = require('libp2p-webrtc-star')
const WebSockets = require('libp2p-websockets') const WebSockets = require('libp2p-websockets')
const Mplex = require('libp2p-mplex')
const Multiplex = require('libp2p-multiplex')
const SPDY = require('libp2p-spdy') const SPDY = require('libp2p-spdy')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
const Bootstrap = require('libp2p-railing')
const Railing = require('libp2p-railing') const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('libp2p') const libp2p = require('../../../../')
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-browser.json // Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-browser.json
const bootstrapers = [ const bootstrapers = [
@ -25,30 +24,56 @@ const bootstrapers = [
] ]
class Node extends libp2p { class Node extends libp2p {
constructor (peerInfo, peerBook, options) { constructor (_options) {
options = options || {} const wrtcStar = new WebRTCStar({ id: _options.peerInfo.id })
const wstar = new WebRTCStar() const defaults = {
modules: {
const modules = { transport: [
transport: [ wrtcStar,
wstar, new WebSockets()
new WebSockets() ],
], streamMuxer: [
connection: { Mplex,
muxer: [
Multiplex,
SPDY SPDY
], ],
crypto: [SECIO] connEncryption: [
SECIO
],
peerDiscovery: [
wrtcStar.discovery,
Bootstrap
]
}, },
discovery: [ config: {
wstar.discovery, peerDiscovery: {
new Railing(bootstrapers) webRTCStar: {
] enabled: true
},
websocketStar: {
enabled: true
},
bootstrap: {
interval: 10000,
enabled: false,
list: bootstrapers
}
},
relay: {
enabled: false,
hop: {
enabled: false,
active: false
}
},
EXPERIMENTAL: {
dht: false,
pubsub: false
}
}
} }
super(modules, peerInfo, peerBook, options) super(defaultsDeep(_options, defaults))
} }
} }

View File

@ -10,11 +10,13 @@ function createNode (callback) {
} }
const peerIdStr = peerInfo.id.toB58String() const peerIdStr = peerInfo.id.toB58String()
const ma = `/dns4/star-signal.cloud.ipfs.team/wss/p2p-webrtc-star/ipfs/${peerIdStr}` const ma = `/dns4/star-signal.cloud.ipfs.team/tcp/443/wss/p2p-webrtc-star/ipfs/${peerIdStr}`
peerInfo.multiaddrs.add(ma) peerInfo.multiaddrs.add(ma)
const node = new Node(peerInfo) const node = new Node({
peerInfo
})
node.idStr = peerIdStr node.idStr = peerIdStr
callback(null, node) callback(null, node)

View File

@ -1,27 +1,36 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const Multiplex = require('libp2p-multiplex') const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const KadDHT = require('libp2p-kad-dht') const KadDHT = require('libp2p-kad-dht')
const defaultsDeep = require('@nodeutils/defaults-deep')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const parallel = require('async/parallel') const parallel = require('async/parallel')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [Multiplex], streamMuxer: [ Mplex ],
crypto: [SECIO] connEncryption: [ SECIO ],
// we add the DHT module that will enable Peer and Content Routing
dht: KadDHT
}, },
// we add the DHT module that will enable Peer and Content Routing config: {
DHT: KadDHT dht: {
kBucketSize: 20
},
EXPERIMENTAL: {
dht: true
}
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -32,7 +41,9 @@ function createNode (callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({
peerInfo
})
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))

View File

@ -1,28 +1,37 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const Multiplex = require('libp2p-multiplex') const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const CID = require('cids') const CID = require('cids')
const KadDHT = require('libp2p-kad-dht') const KadDHT = require('libp2p-kad-dht')
const defaultsDeep = require('@nodeutils/defaults-deep')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const parallel = require('async/parallel') const parallel = require('async/parallel')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [Multiplex], streamMuxer: [ Mplex ],
crypto: [SECIO] connEncryption: [ SECIO ],
// we add the DHT module that will enable Peer and Content Routing
dht: KadDHT
}, },
// we add the DHT module that will enable Peer and Content Routing config: {
DHT: KadDHT dht: {
kBucketSize: 20
},
EXPERIMENTAL: {
dht: true
}
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -33,7 +42,9 @@ function createNode (callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({
peerInfo
})
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))

View File

@ -14,17 +14,27 @@ First, let's update our bundle to support Peer Routing and Content Routing.
```JavaScript ```JavaScript
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [Multiplex], streamMuxer: [ Mplex ],
crypto: [SECIO] connEncryption: [ SECIO ],
// we add the DHT module that will enable Peer and Content Routing
dht: KadDHT
}, },
// we add the DHT module that will enable Peer and Content Routing config: {
DHT: KadDHT dht: {
kBucketSize: 20
},
EXPERIMENTAL: {
// dht must be enabled
dht: true
}
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
``` ```
@ -44,7 +54,7 @@ parallel([
], (err) => { ], (err) => {
if (err) { throw err } if (err) { throw err }
// //
node1.peerRouting.findPeer(node3.peerInfo.id, (err, peer) => { node1.peerRouting.findPeer(node3.peerInfo.id, (err, peer) => {
if (err) { throw err } if (err) { throw err }

View File

@ -1,18 +1,22 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const pull = require('pull-stream') const pull = require('pull-stream')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()] modules: {
transport: [ TCP ]
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -23,7 +27,9 @@ function createNode (callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({
peerInfo
})
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))
@ -75,20 +81,20 @@ parallel([
}) })
*/ */
node1.dial(node2.peerInfo, '/your-protocol', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/your-protocol', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['my own protocol, wow!']), conn) pull(pull.values(['my own protocol, wow!']), conn)
}) })
/* /*
node1.dial(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['semver me please']), conn) pull(pull.values(['semver me please']), conn)
}) })
*/ */
/* /*
node1.dial(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['do I fall into your criteria?']), conn) pull(pull.values(['do I fall into your criteria?']), conn)
}) })

View File

@ -1,6 +1,6 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const SPDY = require('libp2p-spdy') const SPDY = require('libp2p-spdy')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
@ -8,16 +8,18 @@ const waterfall = require('async/waterfall')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const series = require('async/series') const series = require('async/series')
const pull = require('pull-stream') const pull = require('pull-stream')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [SPDY] streamMuxer: [ SPDY ]
} }
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -28,7 +30,9 @@ function createNode (callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({
peerInfo
})
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))
@ -60,17 +64,17 @@ parallel([
}) })
series([ series([
(cb) => node1.dial(node2.peerInfo, '/a', (err, conn) => { (cb) => node1.dialProtocol(node2.peerInfo, '/a', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['protocol (a)']), conn) pull(pull.values(['protocol (a)']), conn)
cb() cb()
}), }),
(cb) => node1.dial(node2.peerInfo, '/b', (err, conn) => { (cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['protocol (b)']), conn) pull(pull.values(['protocol (b)']), conn)
cb() cb()
}), }),
(cb) => node1.dial(node2.peerInfo, '/b', (err, conn) => { (cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['another conn on protocol (b)']), conn) pull(pull.values(['another conn on protocol (b)']), conn)
cb() cb()

View File

@ -1,6 +1,6 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const SPDY = require('libp2p-spdy') const SPDY = require('libp2p-spdy')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
@ -8,16 +8,18 @@ const waterfall = require('async/waterfall')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const series = require('async/series') const series = require('async/series')
const pull = require('pull-stream') const pull = require('pull-stream')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [SPDY] streamMuxer: [ SPDY ]
} }
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -28,7 +30,9 @@ function createNode (callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({
peerInfo
})
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))
@ -60,12 +64,12 @@ parallel([
}) })
series([ series([
(cb) => node1.dial(node2.peerInfo, '/node-2', (err, conn) => { (cb) => node1.dialProtocol(node2.peerInfo, '/node-2', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['from 1 to 2']), conn) pull(pull.values(['from 1 to 2']), conn)
cb() cb()
}), }),
(cb) => node2.dial(node1.peerInfo, '/node-1', (err, conn) => { (cb) => node2.dialProtocol(node1.peerInfo, '/node-1', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['from 2 to 1']), conn) pull(pull.values(['from 2 to 1']), conn)
cb() cb()

View File

@ -8,7 +8,7 @@ The feature of agreeing on a protocol over an established connection is what we
Let's see _protocol multiplexing_ in action! You will need the following modules for this example: `libp2p`, `libp2p-tcp`, `peer-info`, `async` and `pull-stream`. This example reuses the base left by the [Transports](../transports) example. You can see the complete solution at [1.js](./1.js). Let's see _protocol multiplexing_ in action! You will need the following modules for this example: `libp2p`, `libp2p-tcp`, `peer-info`, `async` and `pull-stream`. This example reuses the base left by the [Transports](../transports) example. You can see the complete solution at [1.js](./1.js).
After creating the nodes, we need to tell libp2p which protocols to handle. After creating the nodes, we need to tell libp2p which protocols to handle.
```JavaScript ```JavaScript
// ... // ...
@ -30,7 +30,7 @@ node2.handle('/your-protocol', (protocol, conn) => {
After the protocol is _handled_, now we can dial to it. After the protocol is _handled_, now we can dial to it.
```JavaScript ```JavaScript
node1.dial(node2.peerInfo, '/your-protocol', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/your-protocol', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['my own protocol, wow!']), conn) pull(pull.values(['my own protocol, wow!']), conn)
}) })
@ -47,7 +47,7 @@ node2.handle('/another-protocol/1.0.1', (protocol, conn) => {
) )
}) })
// ... // ...
node1.dial(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['semver me please']), conn) pull(pull.values(['semver me please']), conn)
}) })
@ -55,7 +55,7 @@ node1.dial(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
This feature is super power for network protocols. It works in the same way as versioning your RPC/REST API, but for anything that goes in the wire. We had to use this feature to upgrade protocols within the IPFS Stack (i.e Bitswap) and we successfully managed to do so without any network splits. This feature is super power for network protocols. It works in the same way as versioning your RPC/REST API, but for anything that goes in the wire. We had to use this feature to upgrade protocols within the IPFS Stack (i.e Bitswap) and we successfully managed to do so without any network splits.
There is still one last feature, you can create your custom match functions. There is still one last feature, you can create your custom match functions.
```JavaScript ```JavaScript
node2.handle('/custom-match-func', (protocol, conn) => { node2.handle('/custom-match-func', (protocol, conn) => {
@ -74,7 +74,7 @@ node2.handle('/custom-match-func', (protocol, conn) => {
} }
}) })
// ... // ...
node1.dial(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['do I fall into your criteria?']), conn) pull(pull.values(['do I fall into your criteria?']), conn)
}) })
@ -88,23 +88,24 @@ The example above would require a node to create a whole new connection for ever
Stream multiplexing is a old concept, in fact it happens in many of the layers of the [OSI System](https://en.wikipedia.org/wiki/OSI_model), in libp2p we make this feature to our avail by letting the user pick which module for stream multiplexing to use. Stream multiplexing is a old concept, in fact it happens in many of the layers of the [OSI System](https://en.wikipedia.org/wiki/OSI_model), in libp2p we make this feature to our avail by letting the user pick which module for stream multiplexing to use.
Currently, we have two available [libp2p-spdy](https://github.com/libp2p/js-libp2p-spdy) and [libp2p-multiplex](https://github.com/libp2p/js-libp2p-multiplex) and pluging them in is as easy as adding another transport. Let's revisit our libp2p bundle. Currently, we have two available [libp2p-spdy](https://github.com/libp2p/js-libp2p-spdy) and [libp2p-mplex](https://github.com/libp2p/js-libp2p-mplex) and pluging them in is as easy as adding another transport. Let's revisit our libp2p bundle.
```JavaScript ```JavaScript
const SPDY = require('libp2p-spdy') const SPDY = require('libp2p-spdy')
//... //...
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
// Here we are adding the SPDY muxer to our libp2p bundle. transport: [ TCP ],
// Here we are adding the SPDY muxer to our libp2p bundle.
// Thanks to protocol muxing, a libp2p bundle can support multiple Stream Muxers at the same // Thanks to protocol muxing, a libp2p bundle can support multiple Stream Muxers at the same
// time and pick the right one when dialing to a node // time and pick the right one when dialing to a node
connection: { streamMuxer: [ SPDY ]
muxer: [SPDY]
} }
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
``` ```
@ -129,17 +130,17 @@ node2.handle('/b', (protocol, conn) => {
}) })
series([ series([
(cb) => node1.dial(node2.peerInfo, '/a', (err, conn) => { (cb) => node1.dialProtocol(node2.peerInfo, '/a', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['protocol (a)']), conn) pull(pull.values(['protocol (a)']), conn)
cb() cb()
}), }),
(cb) => node1.dial(node2.peerInfo, '/b', (err, conn) => { (cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['protocol (b)']), conn) pull(pull.values(['protocol (b)']), conn)
cb() cb()
}), }),
(cb) => node1.dial(node2.peerInfo, '/b', (err, conn) => { (cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['another conn on protocol (b)']), conn) pull(pull.values(['another conn on protocol (b)']), conn)
cb() cb()
@ -158,9 +159,9 @@ another protocol (b)
# 3. Bidirectional connections # 3. Bidirectional connections
There is one last trick on _protocol and stream multiplexing_ that libp2p uses to make everyone's life easier and that is _biderectional connection_. There is one last trick on _protocol and stream multiplexing_ that libp2p uses to make everyone's life easier and that is _bidirectional connection_.
With the aid of both mechanisms, we can reuse an incomming connection to dial streams out too, this is specially useful when you are behind tricky NAT, firewalls or if you are running in a browser, where you can have listening addrs, but you can dial out. By dialing out, you enable other peers to talk with you in Protocols that they want, simply by opening a new multiplexed stream. With the aid of both mechanisms, we can reuse an incomming connection to dial streams out too, this is specially useful when you are behind tricky NAT, firewalls or if you are running in a browser, where you can't have listening addrs, but you can dial out. By dialing out, you enable other peers to talk with you in Protocols that they want, simply by opening a new multiplexed stream.
You can see this working on example [3.js](./3.js). The result should look like the following: You can see this working on example [3.js](./3.js). The result should look like the following:

View File

@ -1,27 +1,39 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const Multiplex = require('libp2p-multiplex') const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const MulticastDNS = require('libp2p-mdns') const MulticastDNS = require('libp2p-mdns')
const FloodSub = require('libp2p-floodsub') const defaultsDeep = require('@nodeutils/defaults-deep')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const series = require('async/series') const series = require('async/series')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()], modules: {
connection: { transport: [ TCP ],
muxer: [Multiplex], streamMuxer: [ Mplex ],
crypto: [SECIO] connEncryption: [ SECIO ],
peerDiscovery: [ MulticastDNS ]
}, },
discovery: [new MulticastDNS(peerInfo, { interval: 2000 })] config: {
peerDiscovery: {
mdns: {
interval: 2000,
enabled: true
}
},
EXPERIMENTAL: {
pubsub: true
}
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -32,7 +44,9 @@ function createNode (callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({
peerInfo
})
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))
@ -47,22 +61,25 @@ parallel([
const node1 = nodes[0] const node1 = nodes[0]
const node2 = nodes[1] const node2 = nodes[1]
const fs1 = new FloodSub(node1)
const fs2 = new FloodSub(node2)
series([ series([
(cb) => fs1.start(cb),
(cb) => fs2.start(cb),
(cb) => node1.once('peer:discovery', (peer) => node1.dial(peer, cb)), (cb) => node1.once('peer:discovery', (peer) => node1.dial(peer, cb)),
(cb) => setTimeout(cb, 500) (cb) => setTimeout(cb, 500)
], (err) => { ], (err) => {
if (err) { throw err } if (err) { throw err }
fs2.on('news', (msg) => console.log(msg.from, msg.data.toString())) // Subscribe to the topic 'news'
fs2.subscribe('news') node1.pubsub.subscribe('news',
(msg) => console.log(msg.from, msg.data.toString()),
setInterval(() => { () => {
fs1.publish('news', Buffer.from('Bird bird bird, bird is the word!')) setInterval(() => {
}, 1000) // Publish the message on topic 'news'
node2.pubsub.publish(
'news',
Buffer.from('Bird bird bird, bird is the word!'),
() => {}
)
}, 1000)
}
)
}) })
}) })

View File

@ -1,8 +1,6 @@
# Publish Subscribe # Publish Subscribe
Publish Subscribe is something out of scope for the modular networking stack that is libp2p, however, it is something that is enabled through the primitives that libp2p offers and so it has become one of the most interesting use cases for libp2p. Publish Subscribe is also included on the stack. Currently, we have on PubSub implementation which we ship by default [libp2p-floodsub](https://github.com/libp2p/js-libp2p-floodsub), with many more being researched at [research-pubsub](https://github.com/libp2p/research-pubsub).
Currently, we have a PubSub implementation, [libp2p-floodsub](https://github.com/libp2p/js-libp2p-floodsub) and many more being researched at [research-pubsub](https://github.com/libp2p/research-pubsub).
We've seen many interesting use cases appear with this, here are some highlights: We've seen many interesting use cases appear with this, here are some highlights:
@ -12,30 +10,31 @@ We've seen many interesting use cases appear with this, here are some highlights
## 1. Setting up a simple PubSub network on top of libp2p ## 1. Setting up a simple PubSub network on top of libp2p
For this example, we will use MulticastDNS for automatic Peer Discovery and libp2p-floodsub to give us the PubSub primitives we are looking for. This example is based the previous examples found in [Discovery Mechanisms](../discovery-mechanisms). You can find the complete version at [1.js](./1.js). For this example, we will use MulticastDNS for automatic Peer Discovery. This example is based the previous examples found in [Discovery Mechanisms](../discovery-mechanisms). You can find the complete version at [1.js](./1.js).
Getting PubSub is super simple, all you have to do is require the FloodSub module and pass it in a libp2p node, once you have done that you can start subscribing and publishing in any topic. Using PubSub is super simple, all you have to do is start a libp2p node, PubSub will be enabled by default.
```JavaScript ```JavaScript
const FloodSub = require('libp2p-floodsub')
const fs1 = new FloodSub(node1)
const fs2 = new FloodSub(node2)
series([ series([
(cb) => fs1.start(cb),
(cb) => fs2.start(cb),
(cb) => node1.once('peer:discovery', (peer) => node1.dial(peer, cb)), (cb) => node1.once('peer:discovery', (peer) => node1.dial(peer, cb)),
(cb) => setTimeout(cb, 500) (cb) => setTimeout(cb, 500)
], (err) => { ], (err) => {
if (err) { throw err } if (err) { throw err }
fs2.on('news', (msg) => console.log(msg.from, msg.data.toString())) // Subscribe to the topic 'news'
fs2.subscribe('news') node1.pubsub.subscribe('news',
(msg) => console.log(msg.from, msg.data.toString()),
setInterval(() => { () => {
fs1.publish('news', Buffer.from('Bird bird bird, bird is the word!')) setInterval(() => {
}, 1000) // Publish the message on topic 'news'
node2.pubsub.publish(
'news',
Buffer.from('Bird bird bird, bird is the word!'),
() => {}
)
}, 1000)
}
)
}) })
``` ```

View File

@ -1,16 +1,22 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()] modules: {
transport: [
TCP
]
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -20,7 +26,7 @@ waterfall([
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({ peerInfo: peerInfo })
node.start(cb) node.start(cb)
} }
], (err) => { ], (err) => {

View File

@ -1,18 +1,24 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const defaultsDeep = require('@nodeutils/defaults-deep')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const pull = require('pull-stream') const pull = require('pull-stream')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP()] modules: {
transport: [
TCP
]
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -23,7 +29,7 @@ function createNode (callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({ peerInfo: peerInfo })
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))
@ -54,7 +60,7 @@ parallel([
) )
}) })
node1.dial(node2.peerInfo, '/print', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['Hello', ' ', 'p2p', ' ', 'world', '!']), conn) pull(pull.values(['Hello', ' ', 'p2p', ' ', 'world', '!']), conn)

View File

@ -1,19 +1,26 @@
'use strict' 'use strict'
const libp2p = require('libp2p') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const WebSockets = require('libp2p-websockets') const WebSockets = require('libp2p-websockets')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const defaultsDeep = require('@nodeutils/defaults-deep')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const pull = require('pull-stream') const pull = require('pull-stream')
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP(), new WebSockets()] modules: {
transport: [
TCP,
WebSockets
]
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
@ -28,7 +35,7 @@ function createNode (addrs, callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
addrs.forEach((addr) => peerInfo.multiaddrs.add(addr)) addrs.forEach((addr) => peerInfo.multiaddrs.add(addr))
node = new MyBundle(peerInfo) node = new MyBundle({ peerInfo: peerInfo })
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))
@ -66,19 +73,19 @@ parallel([
node2.handle('/print', print) node2.handle('/print', print)
node3.handle('/print', print) node3.handle('/print', print)
node1.dial(node2.peerInfo, '/print', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['node 1 dialed to node 2 successfully']), conn) pull(pull.values(['node 1 dialed to node 2 successfully']), conn)
}) })
node2.dial(node3.peerInfo, '/print', (err, conn) => { node2.dialProtocol(node3.peerInfo, '/print', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['node 2 dialed to node 3 successfully']), conn) pull(pull.values(['node 2 dialed to node 3 successfully']), conn)
}) })
node3.dial(node1.peerInfo, '/print', (err, conn) => { node3.dialProtocol(node1.peerInfo, '/print', (err, conn) => {
if (err) { if (err) {
console.log('node 3 failed to dial to node 1 with:', err.message) console.log('node 3 failed to dial to node 1 with:', err.message)
} }

View File

@ -10,10 +10,10 @@ A more complete definition of what is a transport can be found on the [interface
When using libp2p, you always want to create your own libp2p Bundle, that is, pick your set of modules and create your network stack with the properties you need. In this example, we will create a bundle with TCP. You can find the complete solution on the file [1.js](./1.js). When using libp2p, you always want to create your own libp2p Bundle, that is, pick your set of modules and create your network stack with the properties you need. In this example, we will create a bundle with TCP. You can find the complete solution on the file [1.js](./1.js).
You will need 4 deps total, so go ahead and install all of them with: You will need 5 deps total, so go ahead and install all of them with:
``` ```bash
> npm install libp2p libp2p-tcp peer-info async > npm install libp2p libp2p-tcp peer-info async @nodeutils/defaults-deep
``` ```
Then, on your favorite text editor create a file with the `.js` extension. I've called mine `1.js`. Then, on your favorite text editor create a file with the `.js` extension. I've called mine `1.js`.
@ -27,16 +27,22 @@ const libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const defaultsDeep = require('@nodeutils/defaults-deep')
// This MyBundle class is your libp2p bundle packed with TCP // This MyBundle class is your libp2p bundle packed with TCP
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
// modules is a JS object that will describe the components const defaults = {
// we want for our libp2p bundle // modules is a JS object that will describe the components
const modules = { // we want for our libp2p bundle
transport: [new TCP()] modules: {
transport: [
TCP
]
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
``` ```
@ -57,7 +63,7 @@ waterfall([
// the multiaddr format, a self describable address // the multiaddr format, a self describable address
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
// Now we can create a node with that PeerInfo object // Now we can create a node with that PeerInfo object
node = new MyBundle(peerInfo) node = new MyBundle({ peerInfo: peerInfo })
// Last, we start the node! // Last, we start the node!
node.start(cb) node.start(cb)
} }
@ -76,7 +82,7 @@ waterfall([
}) })
``` ```
Running this should result in somehting like: Running this should result in something like:
```bash ```bash
> node 1.js > node 1.js
@ -98,6 +104,12 @@ For this step, we will need one more dependency.
> npm install pull-stream > npm install pull-stream
``` ```
And we also need to import the module on our .js file:
```js
const pull = require('pull-stream')
```
We are going to reuse the MyBundle class from step 1, but this time to make things simpler, we will create two functions, one to create nodes and another to print the addrs to avoid duplicating code. We are going to reuse the MyBundle class from step 1, but this time to make things simpler, we will create two functions, one to create nodes and another to print the addrs to avoid duplicating code.
```JavaScript ```JavaScript
@ -108,7 +120,7 @@ function createNode (callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle(peerInfo) node = new MyBundle({ peerInfo: peerInfo })
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))
@ -118,11 +130,18 @@ function printAddrs (node, number) {
console.log('node %s is listening on:', number) console.log('node %s is listening on:', number)
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString())) node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
} }
```
Now we are going to use `async/parallel` to create two nodes, print their addresses and dial from one node to the other.
``` ```
Now we are going to use `async/parallel` to create two nodes, print their addresses and dial from one node to the other. We already added `async` as a dependency, but still need to import `async/parallel`:
```js
const parallel = require('async/parallel')
```
Then,
```js
parallel([ parallel([
(cb) => createNode(cb), (cb) => createNode(cb),
(cb) => createNode(cb) (cb) => createNode(cb)
@ -143,7 +162,7 @@ parallel([
) )
}) })
node1.dial(node2.peerInfo, '/print', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['Hello', ' ', 'p2p', ' ', 'world', '!']), conn) pull(pull.values(['Hello', ' ', 'p2p', ' ', 'world', '!']), conn)
@ -172,7 +191,7 @@ What we are going to do in this step is to create 3 nodes, one with TCP, another
In this example, we will need to also install `libp2p-websockets`, go ahead and install: In this example, we will need to also install `libp2p-websockets`, go ahead and install:
```sh ```bash
> npm install libp2p-websockets > npm install libp2p-websockets
``` ```
@ -183,11 +202,17 @@ const WebSockets = require('libp2p-websockets')
// ... // ...
class MyBundle extends libp2p { class MyBundle extends libp2p {
constructor (peerInfo) { constructor (_options) {
const modules = { const defaults = {
transport: [new TCP(), new WebSockets()] modules: {
transport: [
TCP,
WebSockets
]
}
} }
super(modules, peerInfo)
super(defaultsDeep(_options, defaults))
} }
} }
``` ```
@ -206,7 +231,7 @@ function createNode (addrs, callback) {
(cb) => PeerInfo.create(cb), (cb) => PeerInfo.create(cb),
(peerInfo, cb) => { (peerInfo, cb) => {
addrs.forEach((addr) => peerInfo.multiaddrs.add(addr)) addrs.forEach((addr) => peerInfo.multiaddrs.add(addr))
node = new MyBundle(peerInfo) node = new MyBundle({ peerInfo: peerInfo })
node.start(cb) node.start(cb)
} }
], (err) => callback(err, node)) ], (err) => callback(err, node))
@ -240,21 +265,21 @@ parallel([
node3.handle('/print', print) node3.handle('/print', print)
// node 1 (TCP) dials to node 2 (TCP+WebSockets) // node 1 (TCP) dials to node 2 (TCP+WebSockets)
node1.dial(node2.peerInfo, '/print', (err, conn) => { node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['node 1 dialed to node 2 successfully']), conn) pull(pull.values(['node 1 dialed to node 2 successfully']), conn)
}) })
// node 2 (TCP+WebSockets) dials to node 2 (WebSockets) // node 2 (TCP+WebSockets) dials to node 2 (WebSockets)
node2.dial(node3.peerInfo, '/print', (err, conn) => { node2.dialProtocol(node3.peerInfo, '/print', (err, conn) => {
if (err) { throw err } if (err) { throw err }
pull(pull.values(['node 2 dialed to node 3 successfully']), conn) pull(pull.values(['node 2 dialed to node 3 successfully']), conn)
}) })
// node 3 (WebSockets) attempts to dial to node 1 (TCP) // node 3 (WebSockets) attempts to dial to node 1 (TCP)
node3.dial(node1.peerInfo, '/print', (err, conn) => { node3.dialProtocol(node1.peerInfo, '/print', (err, conn) => {
if (err) { if (err) {
console.log('node 3 failed to dial to node 1 with:', err.message) console.log('node 3 failed to dial to node 1 with:', err.message)
} }
@ -298,8 +323,8 @@ As expected, we created 3 nodes, node 1 with TCP, node 2 with TCP+WebSockets and
Today there are already 3 transports available, one in the works and plenty to come, you can find these at [interface-transport implementations](https://github.com/libp2p/interface-transport#modules-that-implement-the-interface) list. Today there are already 3 transports available, one in the works and plenty to come, you can find these at [interface-transport implementations](https://github.com/libp2p/interface-transport#modules-that-implement-the-interface) list.
Adding more transports is done through the same way as you added TCP and WebSockets. Some transports might offer extra functionalities but for what is libp2p concern, as long as it follows the interface defined at the [spec](https://github.com/libp2p/interface-transport#api), it will be able to use it. Adding more transports is done through the same way as you added TCP and WebSockets. Some transports might offer extra functionalities, but as far as libp2p is concerned, if it follows the interface defined at the [spec](https://github.com/libp2p/interface-transport#api) it will be able to use it.
If you decide to implement a transport yourself, please consider adding to the list so that others can use it as well. If you decide to implement a transport yourself, please consider adding to the list so that others can use it as well.
Hope this tutorial was useful. We are always looking to improve it, contributions are welcome! Hope this tutorial was useful. We are always looking to improve it, so contributions are welcome!

View File

@ -1,7 +1,8 @@
{ {
"name": "libp2p", "name": "libp2p",
"version": "0.14.2", "version": "0.23.0",
"description": "JavaScript base class for libp2p bundles", "description": "JavaScript base class for libp2p bundles",
"leadMaintainer": "David Dias <daviddias@ipfs.io>",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
"lint": "aegir lint", "lint": "aegir lint",
@ -26,68 +27,87 @@
"node": ">=6.0.0", "node": ">=6.0.0",
"npm": ">=3.0.0" "npm": ">=3.0.0"
}, },
"pre-commit": [
"lint",
"test"
],
"author": "David Dias <daviddias@ipfs.io>",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/libp2p/js-libp2p/issues" "url": "https://github.com/libp2p/js-libp2p/issues"
}, },
"homepage": "https://github.com/libp2p/js-libp2p", "homepage": "https://github.com/libp2p/js-libp2p",
"browser": {
"joi": "joi-browser",
"./test/utils/bundle-nodejs": "./test/utils/bundle-browser"
},
"dependencies": { "dependencies": {
"async": "^2.6.0", "async": "^2.6.1",
"libp2p-ping": "~0.6.0", "joi": "^13.4.0",
"libp2p-swarm": "~0.34.0", "joi-browser": "^13.4.0",
"mafmt": "^3.0.2", "libp2p-connection-manager": "~0.0.2",
"multiaddr": "^3.0.1", "libp2p-floodsub": "~0.15.0",
"peer-book": "~0.5.2", "libp2p-ping": "~0.8.0",
"peer-id": "~0.10.3", "libp2p-switch": "~0.40.7",
"peer-info": "~0.11.3" "libp2p-websockets": "~0.12.0",
"mafmt": "^6.0.0",
"multiaddr": "^5.0.0",
"peer-book": "~0.8.0",
"peer-id": "~0.11.0",
"peer-info": "~0.14.1"
}, },
"devDependencies": { "devDependencies": {
"aegir": "^12.2.0", "@nodeutils/defaults-deep": "^1.1.0",
"aegir": "^15.1.0",
"chai": "^4.1.2", "chai": "^4.1.2",
"cids": "~0.5.2", "cids": "~0.5.3",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"electron-webrtc": "~0.3.0", "electron-webrtc": "~0.3.0",
"libp2p-circuit": "~0.1.4", "libp2p-circuit": "~0.2.0",
"libp2p-kad-dht": "~0.6.0", "libp2p-kad-dht": "~0.10.1",
"libp2p-mdns": "~0.9.1", "libp2p-mdns": "~0.12.0",
"libp2p-multiplex": "~0.5.1", "libp2p-mplex": "~0.8.0",
"libp2p-railing": "~0.7.1", "libp2p-railing": "~0.9.2",
"libp2p-secio": "~0.8.1", "libp2p-secio": "~0.10.0",
"libp2p-spdy": "~0.11.0", "libp2p-spdy": "~0.12.1",
"libp2p-tcp": "~0.11.1", "libp2p-tcp": "~0.12.0",
"libp2p-webrtc-star": "~0.13.3", "libp2p-webrtc-star": "~0.15.3",
"libp2p-websockets": "~0.10.4", "libp2p-websocket-star": "~0.8.1",
"libp2p-websocket-star": "~0.7.2", "libp2p-websocket-star-rendezvous": "~0.2.3",
"libp2p-websocket-star-rendezvous": "~0.2.2",
"lodash.times": "^4.3.2", "lodash.times": "^4.3.2",
"pre-commit": "^1.2.2",
"pull-goodbye": "0.0.2", "pull-goodbye": "0.0.2",
"pull-serializer": "~0.3.2", "pull-serializer": "~0.3.2",
"pull-stream": "^3.6.1", "pull-stream": "^3.6.8",
"safe-buffer": "^5.1.1", "sinon": "^6.1.4",
"sinon": "^4.1.3", "webrtcsupport": "^2.2.0",
"wrtc": "0.0.63" "wrtc": "~0.1.6"
}, },
"contributors": [ "contributors": [
"Alan Shaw <alan@tableflip.io>",
"Chris Bratlien <chrisbratlien@gmail.com>", "Chris Bratlien <chrisbratlien@gmail.com>",
"Chris Dostert <chrisdostert@users.noreply.github.com>",
"Daijiro Wachi <daijiro.wachi@gmail.com>", "Daijiro Wachi <daijiro.wachi@gmail.com>",
"David Dias <daviddias.p@gmail.com>", "David Dias <daviddias.p@gmail.com>",
"Diogo Silva <fsdiogo@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>", "Dmitriy Ryajov <dryajov@gmail.com>",
"Elven <mon.samuel@qq.com>", "Elven <mon.samuel@qq.com>",
"Florian-Merle <florian.david.merle@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>", "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Giovanni T. Parra <fiatjaf@gmail.com>",
"Hugo Dias <hugomrdias@gmail.com>",
"Irakli Gozalishvili <rfobic@gmail.com>",
"Jacob Heun <jacobheun@gmail.com>",
"Joel Gustafson <joelg@mit.edu>", "Joel Gustafson <joelg@mit.edu>",
"John Rees <johnrees@users.noreply.github.com>",
"João Santos <joaosantos15@users.noreply.github.com>",
"Kevin Kwok <antimatter15@gmail.com>",
"Lars Gierth <lgierth@users.noreply.github.com>", "Lars Gierth <lgierth@users.noreply.github.com>",
"Maciej Krüger <mkg20001@gmail.com>", "Maciej Krüger <mkg20001@gmail.com>",
"Nuno Nogueira <nunofmn@gmail.com>", "Nuno Nogueira <nunofmn@gmail.com>",
"Pedro Teixeira <pedro@protocol.ai>",
"Pedro Teixeira <i@pgte.me>", "Pedro Teixeira <i@pgte.me>",
"RasmusErik Voel Jensen <github@solsort.com>", "RasmusErik Voel Jensen <github@solsort.com>",
"Richard Littauer <richard.littauer@gmail.com>", "Richard Littauer <richard.littauer@gmail.com>",
"Ryan Bell <ryan@piing.net>", "Ryan Bell <ryan@piing.net>",
"Sönke Hahn <soenkehahn@gmail.com>",
"Tiago Alves <alvesjtiago@gmail.com>",
"Volker Mische <volker.mische@gmail.com>",
"Zane Starr <zcstarr@gmail.com>",
"greenkeeperio-bot <support@greenkeeper.io>", "greenkeeperio-bot <support@greenkeeper.io>",
"mayerwin <mayerwin@users.noreply.github.com>", "mayerwin <mayerwin@users.noreply.github.com>",
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>" "ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>"

3
pdd/README.md Normal file
View File

@ -0,0 +1,3 @@
# PDD Test Stories Implementation
> Implementation of the Compliance tests from https://github.com/libp2p/interop

20
pdd/package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "pdd-impl",
"version": "0.0.0",
"description": "PDD Test Stories implementation",
"repository": {
"type": "git",
"url": " "
},
"keywords": [
"PDD",
"libp2p"
],
"author": "David Dias <daviddias@ipfs.io>",
"license": "MIT",
"dependencies": {
"libp2p": "file:./..",
"libp2p-interop": "github:libp2p/interop#master",
"tape": "^4.8.0"
}
}

View File

@ -0,0 +1,104 @@
'use strict'
const test = require('tape')
const libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const WebSockets = require('libp2p-websockets')
const SECIO = require('libp2p-secio')
const Multiplex = require('libp2p-multiplex')
const Railing = require('libp2p-railing')
const MulticastDNS = require('libp2p-mdns')
const KadDHT = require('libp2p-kad-dht')
const PeerInfo = require('peer-info')
const pull = require('pull-stream')
const waterfall = require('async/waterfall')
const series = require('async/series')
const PeerA = require('libp2p-interop/peer-a.json')
const PeerB = require('libp2p-interop/peer-b.json')
class IPFSBundle extends libp2p {
constructor (peerInfo, options) {
options = Object.assign({ bootstrap: [] }, options)
const modules = {
transport: [
new TCP(),
new WebSockets()
],
connection: {
muxer: [
Multiplex
],
crypto: [
SECIO
]
},
discovery: [
new MulticastDNS(peerInfo, 'ipfs.local'),
new Railing(options.bootstrap)
],
DHT: KadDHT
}
super(modules, peerInfo, undefined, options)
}
}
test('story 1 - peerA', (t) => {
t.plan(10)
let node
waterfall([
(cb) => PeerInfo.create(PeerA, cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10000')
node = new IPFSBundle(peerInfo)
node.start(cb)
}
], (err) => {
t.ifErr(err, 'created Node successfully')
t.ok(node.isStarted(), 'PeerA is Running')
const peerBAddr = `/ip4/127.0.0.1/tcp/10001/ipfs/${PeerB.id}`
node.handle('/time/1.0.0', (protocol, conn) => {
pull(
pull.values([Date.now().toString()]),
conn,
pull.onEnd((err) => {
t.ifErr(err)
t.pass('Sent time successfully')
})
)
})
series([
(cb) => setTimeout(cb, 5 * 1000), // time to run both scripts
(cb) => node.ping(peerBAddr, (err, p) => {
t.ifErr(err, 'initiated Ping to PeerB')
p.once('error', (err) => t.ifErr(err, 'Ping should not fail'))
p.once('ping', (time) => {
t.pass('ping PeerB successfully')
p.stop()
cb()
})
}),
(cb) => node.dial(peerBAddr, '/echo/1.0.0', (err, conn) => {
t.ifErr(err, 'dial successful')
const data = Buffer.from('Hey')
pull(
pull.values([data]),
conn,
pull.collect((err, values) => {
t.ifErr(err, 'Received echo back')
t.deepEqual(values[0], data)
cb()
})
)
}),
(cb) => setTimeout(cb, 2 * 1000) // time to both finish
], () => node.stop((err) => t.ifErr(err, 'PeerA has stopped')))
})
})

View File

@ -0,0 +1,98 @@
'use strict'
const test = require('tape')
const libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const WebSockets = require('libp2p-websockets')
const SECIO = require('libp2p-secio')
const Multiplex = require('libp2p-multiplex')
const Railing = require('libp2p-railing')
const MulticastDNS = require('libp2p-mdns')
const KadDHT = require('libp2p-kad-dht')
const PeerInfo = require('peer-info')
const pull = require('pull-stream')
const waterfall = require('async/waterfall')
const series = require('async/series')
const PeerA = require('libp2p-interop/peer-a.json')
const PeerB = require('libp2p-interop/peer-b.json')
class IPFSBundle extends libp2p {
constructor (peerInfo, options) {
options = Object.assign({ bootstrap: [] }, options)
const modules = {
transport: [
new TCP(),
new WebSockets()
],
connection: {
muxer: [
Multiplex
],
crypto: [
SECIO
]
},
discovery: [
new MulticastDNS(peerInfo, 'ipfs.local'),
new Railing(options.bootstrap)
],
DHT: KadDHT
}
super(modules, peerInfo, undefined, options)
}
}
test('story 1 - peerA', (t) => {
t.plan(8)
let node
waterfall([
(cb) => PeerInfo.create(PeerB, cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10001')
node = new IPFSBundle(peerInfo)
node.start(cb)
}
], (err) => {
t.ifErr(err, 'created Node successfully')
t.ok(node.isStarted(), 'PeerB is Running')
const peerAAddr = `/ip4/127.0.0.1/tcp/10000/ipfs/${PeerA.id}`
node.handle('/echo/1.0.0', (protocol, conn) => {
pull(
conn,
conn,
pull.onEnd((err) => t.ifErr(err, 'echo was successful'))
)
})
series([
(cb) => setTimeout(cb, 5 * 1000), // time to run both scripts
(cb) => node.ping(peerAAddr, (err, p) => {
t.ifErr(err, 'initiated Ping to PeerA')
p.once('error', (err) => t.ifErr(err, 'Ping should not fail'))
p.once('ping', (time) => {
t.pass('ping PeerA successfully')
p.stop()
cb()
})
}),
(cb) => node.dial(peerAAddr, '/time/1.0.0', (err, conn) => {
t.ifErr(err, 'dial successful')
pull(
pull.values([]),
conn,
pull.collect((err, values) => {
t.ifErr(err, 'Received time')
cb()
})
)
}),
(cb) => setTimeout(cb, 2 * 1000) // time to both finish
], () => node.stop((err) => t.ifErr(err, 'PeerB has stopped')))
})
})

View File

@ -0,0 +1,54 @@
'use strict'
const test = require('tape')
const libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const pull = require('pull-stream')
const PeerA = require('libp2p-interop/peer-a.json')
const PeerB = require('libp2p-interop/peer-b.json')
class MyBundle extends libp2p {
constructor (peerInfo) {
const modules = {
transport: [new TCP()]
}
super(modules, peerInfo)
}
}
test('story 1 - peerA', (t) => {
t.plan(6)
let node
waterfall([
(cb) => PeerInfo.create(PeerA, cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10000')
node = new MyBundle(peerInfo)
node.start(cb)
}
], (err) => {
t.ifErr(err, 'created Node')
t.ok(node.isStarted(), 'PeerA is running')
const PeerBAddr = `/ip4/127.0.0.1/tcp/10001/ipfs/${PeerB.id}`
node.dial(PeerBAddr, '/echo/1.0.0', (err, conn) => {
t.ifErr(err, 'dial successful')
const data = Buffer.from('Heey')
pull(
pull.values([data]),
conn,
pull.collect((err, values) => {
t.ifErr(err, 'Received echo back')
t.deepEqual(values[0], data)
node.stop((err) => t.ifErr(err, 'PeerA has stopped'))
})
)
})
})
})

View File

@ -0,0 +1,49 @@
'use strict'
const test = require('tape')
const libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const pull = require('pull-stream')
const PeerB = require('libp2p-interop/peer-b.json')
class MyBundle extends libp2p {
constructor (peerInfo) {
const modules = {
transport: [new TCP()]
}
super(modules, peerInfo)
}
}
test('story 1 - peerB', (t) => {
t.plan(5)
let node
waterfall([
(cb) => PeerInfo.create(PeerB, cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10001')
node = new MyBundle(peerInfo)
node.start(cb)
}
], (err) => {
t.ifErr(err)
t.ok(node.isStarted(), 'PeerB is running')
node.handle('/echo/1.0.0', (protocol, conn) => {
pull(
conn,
conn,
pull.onEnd((err) => {
t.ifErr(err)
t.pass('Received End of Connection')
node.stop((err) => {
t.ifErr(err, 'PeerB has stopped')
})
})
)
})
})
})

View File

@ -0,0 +1,54 @@
'use strict'
const test = require('tape')
const libp2p = require('libp2p')
const WebSockets = require('libp2p-websockets')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const pull = require('pull-stream')
const PeerA = require('libp2p-interop/peer-a.json')
const PeerB = require('libp2p-interop/peer-b.json')
class MyBundle extends libp2p {
constructor (peerInfo) {
const modules = {
transport: [new WebSockets()]
}
super(modules, peerInfo)
}
}
test('story 2 - peerA', (t) => {
t.plan(6)
let node
waterfall([
(cb) => PeerInfo.create(PeerA, cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10000/ws')
node = new MyBundle(peerInfo)
node.start(cb)
}
], (err) => {
t.ifErr(err, 'created Node')
t.ok(node.isStarted(), 'PeerA is running')
const PeerBAddr = `/ip4/127.0.0.1/tcp/10001/ws/ipfs/${PeerB.id}`
node.dial(PeerBAddr, '/echo/1.0.0', (err, conn) => {
t.ifErr(err, 'dial successful')
const data = Buffer.from('Heey')
pull(
pull.values([data]),
conn,
pull.collect((err, values) => {
t.ifErr(err, 'Received echo back')
t.deepEqual(values[0], data)
node.stop((err) => t.ifErr(err, 'PeerA has stopped'))
})
)
})
})
})

View File

@ -0,0 +1,49 @@
'use strict'
const test = require('tape')
const libp2p = require('libp2p')
const WebSockets = require('libp2p-websockets')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const pull = require('pull-stream')
const PeerB = require('libp2p-interop/peer-b.json')
class MyBundle extends libp2p {
constructor (peerInfo) {
const modules = {
transport: [new WebSockets()]
}
super(modules, peerInfo)
}
}
test('story 2 - peerB', (t) => {
t.plan(5)
let node
waterfall([
(cb) => PeerInfo.create(PeerB, cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10001/ws')
node = new MyBundle(peerInfo)
node.start(cb)
}
], (err) => {
t.ifErr(err)
t.ok(node.isStarted(), 'PeerB is running')
node.handle('/echo/1.0.0', (protocol, conn) => {
pull(
conn,
pull.through(v => v, err => {
t.ifErr(err)
t.pass('Received End of Connection')
node.stop((err) => {
t.ifErr(err, 'PeerB has stopped')
})
}),
conn
)
})
})
})

View File

@ -0,0 +1,42 @@
'use strict'
const test = require('tape')
const libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const PeerA = require('libp2p-interop/peer-a.json')
const PeerB = require('libp2p-interop/peer-b.json')
class MyBundle extends libp2p {
constructor (peerInfo) {
const modules = {
transport: [new TCP()]
}
super(modules, peerInfo)
}
}
test('story 3 - peerA', (t) => {
t.plan(4)
let node
waterfall([
(cb) => PeerInfo.create(PeerA, cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10000')
node = new MyBundle(peerInfo)
node.start(cb)
}
], (err) => {
t.ifErr(err, 'created Node')
t.ok(node.isStarted(), 'PeerA is running')
const PeerBAddr = `/ip4/127.0.0.1/tcp/10001/ws/ipfs/${PeerB.id}`
setTimeout(() => node.dial(PeerBAddr, '/echo/1.0.0', (err, conn) => {
t.ok(err, 'dial failed')
node.stop((err) => t.ifErr(err, 'PeerA has stopped'))
}), 1000)
})
})

View File

@ -0,0 +1,42 @@
'use strict'
const test = require('tape')
const libp2p = require('libp2p')
const WebSockets = require('libp2p-websockets')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const PeerA = require('libp2p-interop/peer-a.json')
const PeerB = require('libp2p-interop/peer-b.json')
class MyBundle extends libp2p {
constructor (peerInfo) {
const modules = {
transport: [new WebSockets()]
}
super(modules, peerInfo)
}
}
test('story 3 - peerB', (t) => {
t.plan(4)
let node
waterfall([
(cb) => PeerInfo.create(PeerB, cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10000/ws')
node = new MyBundle(peerInfo)
node.start(cb)
}
], (err) => {
t.ifErr(err, 'created Node')
t.ok(node.isStarted(), 'PeerA is running')
const PeerAAddr = `/ip4/127.0.0.1/tcp/10000/ws/ipfs/${PeerA.id}`
setTimeout(() => node.dial(PeerAAddr, '/echo/1.0.0', (err, conn) => {
t.ok(err, 'dial failed')
node.stop((err) => t.ifErr(err, 'PeerA has stopped'))
}), 1000)
})
})

View File

@ -0,0 +1,146 @@
#! /usr/bin/env node
'use strict'
// This script generates the table of packages you can see in the readme
// Columns to show at the header of the table
const columns = [
'Package',
'Version',
'Deps',
'CI',
'Coverage'
]
// Headings are a string
// Arrays are packages. Index 0 is the GitHub repo and index 1 is the npm package
const rows = [
'Libp2p',
['libp2p/interface-libp2p', 'interface-libp2p'],
['libp2p/js-libp2p', 'libp2p'],
'Connection',
['libp2p/interface-connection', 'interface-connection'],
'Transport',
['libp2p/interface-transport', 'interface-transport'],
['libp2p/js-libp2p-circuit', 'libp2p-circuit'], // should this be NAT Traversal only?
['libp2p/js-libp2p-tcp', 'libp2p-tcp'],
['libp2p/js-libp2p-udp', 'libp2p-udp'],
['libp2p/js-libp2p-udt', 'libp2p-udt'],
['libp2p/js-libp2p-utp', 'libp2p-utp'],
['libp2p/js-libp2p-webrtc-direct', 'libp2p-webrtc-direct'],
['libp2p/js-libp2p-webrtc-star', 'libp2p-webrtc-star'],
['libp2p/js-libp2p-websockets', 'libp2p-websockets'],
['libp2p/js-libp2p-websocket-star', 'libp2p-websocket-star'],
['libp2p/js-libp2p-websocket-star-rendezvous', 'libp2p-websocket-star-rendezvous'],
'Crypto Channels',
['libp2p/js-libp2p-secio', 'libp2p-secio'],
'Stream Muxers',
['libp2p/interface-stream-muxer', 'interface-stream-muxer'],
['libp2p/js-libp2p-mplex', 'libp2p-mplex'],
['libp2p/js-libp2p-spdy', 'libp2p-spdy'],
'Discovery',
['libp2p/interface-peer-discovery', 'interface-peer-discovery'],
['libp2p/js-libp2p-bootstrap', 'libp2p-bootstrap'],
['libp2p/js-libp2p-kad-dht', 'libp2p-kad-dht'], // should this be here?
['libp2p/js-libp2p-mdns', 'libp2p-mdns'],
['libp2p/js-libp2p-rendezvous', 'libp2p-rendezvous'],
['libp2p/js-libp2p-webrtc-star', 'libp2p-webrtc-star'],
['libp2p/js-libp2p-websocket-star', 'libp2p-websocket-star'],
'NAT Traversal',
['libp2p/js-libp2p-circuit', 'libp2p-circuit'],
['libp2p/js-libp2p-nat-mngr', 'libp2p-nat-mngr'],
'Data Types',
['libp2p/js-peer-book', 'peer-book'],
['libp2p/js-peer-id', 'peer-id'],
['libp2p/js-peer-info', 'peer-info'],
'Content Routing',
['libp2p/interface-content-routing', 'interface-content-routing'],
['libp2p/js-libp2p-delegated-content-routing', 'libp2p-delegated-content-routing'],
['libp2p/js-libp2p-kad-dht', 'libp2p-kad-dht'],
'Peer Routing',
['libp2p/interface-peer-routing', 'interface-peer-routing'],
['libp2p/js-libp2p-delegated-peer-routing', 'libp2p-delegated-peer-routing'],
['libp2p/js-libp2p-kad-dht', 'libp2p-kad-dht'],
'Record Store',
['libp2p/interface-record-store', 'interface-record-store'],
['libp2p/js-libp2p-record', 'libp2p-record'],
'Generics',
['libp2p/js-libp2p-connection-manager', 'libp2p-connection-manager'],
['libp2p/js-libp2p-crypto', 'libp2p-crypto'],
['libp2p/js-libp2p-crypto-secp256k1', 'libp2p-crypto-secp256k1'],
['libp2p/js-libp2p-switch', 'libp2p-switch'],
'Extensions',
['libp2p/js-libp2p-floodsub', 'libp2p-floodsub'],
['libp2p/js-libp2p-identify', 'libp2p-identify'],
['libp2p/js-libp2p-keychain', 'libp2p-keychain'],
['libp2p/js-libp2p-ping', 'libp2p-ping'],
['libp2p/js-libp2p-pnet', 'libp2p-pnet'],
'Utilities',
['libp2p/js-p2pcat', 'p2pcat']
]
const isItemPackage = (item) => {
return Array.isArray(item)
}
const packageBadges = [
// Package
(gh, npm) => `[\`${npm}\`](//github.com/${gh})`,
// Version
(gh, npm) => `[![npm](https://img.shields.io/npm/v/${npm}.svg?maxAge=86400&style=flat-square)](//github.com/${gh}/releases)`,
// Deps
(gh, npm) => `[![Deps](https://david-dm.org/${gh}.svg?style=flat-square)](https://david-dm.org/${gh})`,
// CI
(gh, npm) => {
// Need to fix the path for jenkins links, as jenkins adds `/job/` between everything
const jenkinsPath = gh.split('/').join('/job/')
return `[![jenkins](https://ci.ipfs.team/buildStatus/icon?job=${gh}/master)](https://ci.ipfs.team/job/${jenkinsPath}/job/master/)`
},
// Coverage
(gh, npm) => `[![codecov](https://codecov.io/gh/${gh}/branch/master/graph/badge.svg)](https://codecov.io/gh/${gh})`
]
// Creates the table row for a package
const generatePackageRow = (item) => {
const row = packageBadges.map((func) => {
// First string is GitHub path, second is npm package name
return func(item[0], item[1])
}).join(' | ')
const fullRow = `| ${row} |`
return fullRow
}
// Generates a row for the table, depending if it's a package or a heading
const generateRow = (item) => {
if (isItemPackage(item)) {
return generatePackageRow(item)
} else {
return `| **${item}** |`
}
}
const header = `| ${columns.join(' | ')} |`
const hr = `| ${columns.map(() => '---------').join('|')} |`
const toPrint = [
header,
hr,
rows.map((row) => generateRow(row)).join('\n')
]
toPrint.forEach((t) => console.log(t))

50
src/config.js Normal file
View File

@ -0,0 +1,50 @@
'use strict'
const Joi = require('joi')
const ModuleSchema = Joi.alternatives().try(Joi.func(), Joi.object())
const OptionsSchema = Joi.object({
// TODO: create proper validators for the generics
connectionManager: Joi.object(),
peerInfo: Joi.object().required(),
peerBook: Joi.object(),
modules: Joi.object().keys({
transport: Joi.array().items(ModuleSchema).min(1).required(),
streamMuxer: Joi.array().items(ModuleSchema).allow(null),
connEncryption: Joi.array().items(ModuleSchema).allow(null),
connProtector: Joi.object().keys({
protect: Joi.func().required()
}).unknown(),
peerDiscovery: Joi.array().items(ModuleSchema).allow(null),
dht: ModuleSchema.allow(null)
}).required(),
config: Joi.object().keys({
peerDiscovery: Joi.object().allow(null),
relay: Joi.object().keys({
enabled: Joi.boolean().default(false),
hop: Joi.object().keys({
enabled: Joi.boolean().default(false),
active: Joi.boolean().default(false)
})
}).default(),
dht: Joi.object().keys({
kBucketSize: Joi.number().allow(null)
}),
EXPERIMENTAL: Joi.object().keys({
dht: Joi.boolean().default(false),
pubsub: Joi.boolean().default(false)
}).default()
}).default()
})
module.exports.validate = (options) => {
options = Joi.attempt(options, OptionsSchema)
// Ensure dht is correct
if (options.config.EXPERIMENTAL.dht) {
Joi.assert(options.modules.dht, ModuleSchema.required())
}
return options
}

20
src/content-routing.js Normal file
View File

@ -0,0 +1,20 @@
'use strict'
module.exports = (node) => {
return {
findProviders: (key, timeout, callback) => {
if (!node._dht) {
return callback(new Error('DHT is not available'))
}
node._dht.findProviders(key, timeout, callback)
},
provide: (key, callback) => {
if (!node._dht) {
return callback(new Error('DHT is not available'))
}
node._dht.provide(key, callback)
}
}
}

27
src/dht.js Normal file
View File

@ -0,0 +1,27 @@
'use strict'
module.exports = (node) => {
return {
put: (key, value, callback) => {
if (!node._dht) {
return callback(new Error('DHT is not available'))
}
node._dht.put(key, value, callback)
},
get: (key, callback) => {
if (!node._dht) {
return callback(new Error('DHT is not available'))
}
node._dht.get(key, callback)
},
getMany (key, nVals, callback) {
if (!node._dht) {
return callback(new Error('DHT is not available'))
}
node._dht.getMany(key, nVals, callback)
}
}
}

3
src/error-messages.js Normal file
View File

@ -0,0 +1,3 @@
'use strict'
exports.NOT_STARTED_YET = 'The libp2p node is not started yet'

48
src/get-peer-info.js Normal file
View File

@ -0,0 +1,48 @@
'use strict'
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const multiaddr = require('multiaddr')
module.exports = (node) => {
/*
* Helper method to check the data type of peer and convert it to PeerInfo
*/
return function (peer, callback) {
let p
// PeerInfo
if (PeerInfo.isPeerInfo(peer)) {
p = peer
// Multiaddr instance or Multiaddr String
} else if (multiaddr.isMultiaddr(peer) || typeof peer === 'string') {
if (typeof peer === 'string') {
peer = multiaddr(peer)
}
const peerIdB58Str = peer.getPeerId()
if (!peerIdB58Str) {
throw new Error(`peer multiaddr instance or string must include peerId`)
}
try {
p = node.peerBook.get(peerIdB58Str)
} catch (err) {
p = new PeerInfo(PeerId.createFromB58String(peerIdB58Str))
}
p.multiaddrs.add(peer)
// PeerId
} else if (PeerId.isPeerId(peer)) {
const peerIdB58Str = peer.toB58String()
try {
p = node.peerBook.get(peerIdB58Str)
} catch (err) {
return node.peerRouting.findPeer(peer, callback)
}
} else {
return setImmediate(() => callback(new Error('peer type not recognized')))
}
setImmediate(() => callback(null, p))
}
}

View File

@ -3,140 +3,110 @@
const EventEmitter = require('events').EventEmitter const EventEmitter = require('events').EventEmitter
const assert = require('assert') const assert = require('assert')
const setImmediate = require('async/setImmediate')
const each = require('async/each') const each = require('async/each')
const series = require('async/series') const series = require('async/series')
const parallel = require('async/parallel')
const Ping = require('libp2p-ping')
const Swarm = require('libp2p-swarm')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const PeerBook = require('peer-book') const PeerBook = require('peer-book')
const multiaddr = require('multiaddr') const Switch = require('libp2p-switch')
const Ping = require('libp2p-ping')
const WebSockets = require('libp2p-websockets')
const ConnectionManager = require('libp2p-connection-manager')
const peerRouting = require('./peer-routing')
const contentRouting = require('./content-routing')
const dht = require('./dht')
const pubsub = require('./pubsub')
const getPeerInfo = require('./get-peer-info')
const validateConfig = require('./config').validate
exports = module.exports exports = module.exports
const NOT_STARTED_ERROR_MESSAGE = 'The libp2p node is not started yet' const NOT_STARTED_ERROR_MESSAGE = 'The libp2p node is not started yet'
class Node extends EventEmitter { class Node extends EventEmitter {
constructor (_modules, _peerInfo, _peerBook, _options) { constructor (_options) {
super() super()
assert(_modules, 'requires modules to equip libp2p with features') // validateConfig will ensure the config is correct,
assert(_peerInfo, 'requires a PeerInfo instance') // and add default values where appropriate
_options = validateConfig(_options)
this.modules = _modules this.peerInfo = _options.peerInfo
this.peerInfo = _peerInfo this.peerBook = _options.peerBook || new PeerBook()
this.peerBook = _peerBook || new PeerBook()
_options = _options || {}
this._modules = _options.modules
this._config = _options.config
this._isStarted = false this._isStarted = false
this._transport = [] // Transport instances/references
this._discovery = [] // Discovery service instances/references
this.swarm = new Swarm(this.peerInfo, this.peerBook) this._switch = new Switch(this.peerInfo, this.peerBook, _options.switch)
this.stats = this._switch.stats
this.connectionManager = new ConnectionManager(this, _options.connectionManager)
// Attach stream multiplexers // Attach stream multiplexers
if (this.modules.connection && this.modules.connection.muxer) { if (this._modules.streamMuxer) {
let muxers = this.modules.connection.muxer let muxers = this._modules.streamMuxer
muxers = Array.isArray(muxers) ? muxers : [muxers] muxers.forEach((muxer) => this._switch.connection.addStreamMuxer(muxer))
muxers.forEach((muxer) => this.swarm.connection.addStreamMuxer(muxer))
// If muxer exists, we can use Identify // If muxer exists
this.swarm.connection.reuse() // we can use Identify
this._switch.connection.reuse()
// we can use Relay for listening/dialing
this._switch.connection.enableCircuitRelay(this._config.relay)
// If muxer exists, we can use Relay for listening/dialing // Received incomming dial and muxer upgrade happened,
this.swarm.connection.enableCircuitRelay(_options.relay)
// Received incommind dial and muxer upgrade happened,
// reuse this muxed connection // reuse this muxed connection
this.swarm.on('peer-mux-established', (peerInfo) => { this._switch.on('peer-mux-established', (peerInfo) => {
this.emit('peer:connect', peerInfo) this.emit('peer:connect', peerInfo)
this.peerBook.put(peerInfo) this.peerBook.put(peerInfo)
}) })
this.swarm.on('peer-mux-closed', (peerInfo) => { this._switch.on('peer-mux-closed', (peerInfo) => {
this.emit('peer:disconnect', peerInfo) this.emit('peer:disconnect', peerInfo)
}) })
} }
// Attach crypto channels // Attach crypto channels
if (this.modules.connection && this.modules.connection.crypto) { if (this._modules.connEncryption) {
let cryptos = this.modules.connection.crypto let cryptos = this._modules.connEncryption
cryptos = Array.isArray(cryptos) ? cryptos : [cryptos]
cryptos.forEach((crypto) => { cryptos.forEach((crypto) => {
this.swarm.connection.crypto(crypto.tag, crypto.encrypt) this._switch.connection.crypto(crypto.tag, crypto.encrypt)
}) })
} }
// Attach discovery mechanisms // Attach private network protector
if (this.modules.discovery) { if (this._modules.connProtector) {
let discoveries = this.modules.discovery this._switch.protector = this._modules.connProtector
discoveries = Array.isArray(discoveries) ? discoveries : [discoveries] } else if (process.env.LIBP2P_FORCE_PNET) {
throw new Error('Private network is enforced, but no protector was provided')
discoveries.forEach((discovery) => {
discovery.on('peer', (peerInfo) => this.emit('peer:discovery', peerInfo))
})
} }
// Mount default protocols
Ping.mount(this.swarm)
// dht provided components (peerRouting, contentRouting, dht) // dht provided components (peerRouting, contentRouting, dht)
if (_modules.DHT) { if (this._config.EXPERIMENTAL.dht) {
this._dht = new this.modules.DHT(this.swarm, { const DHT = this._modules.dht
kBucketSize: 20, this._dht = new DHT(this._switch, {
datastoer: _options.DHT && _options.DHT.datastore kBucketSize: this._config.dht.kBucketSize || 20,
// TODO make datastore an option of libp2p itself so
// that other things can use it as well
datastore: dht.datastore
}) })
} }
this.peerRouting = { // enable/disable pubsub
findPeer: (id, callback) => { if (this._config.EXPERIMENTAL.pubsub) {
if (!this._dht) { this.pubsub = pubsub(this)
return callback(new Error('DHT is not available'))
}
this._dht.findPeer(id, callback)
}
} }
this.contentRouting = { // Attach remaining APIs
findProviders: (key, timeout, callback) => { this.peerRouting = peerRouting(this)
if (!this._dht) { this.contentRouting = contentRouting(this)
return callback(new Error('DHT is not available')) this.dht = dht(this)
}
this._dht.findProviders(key, timeout, callback) this._getPeerInfo = getPeerInfo(this)
},
provide: (key, callback) => {
if (!this._dht) {
return callback(new Error('DHT is not available'))
}
this._dht.provide(key, callback) // Mount default protocols
} Ping.mount(this._switch)
}
this.dht = {
put: (key, value, callback) => {
if (!this._dht) {
return callback(new Error('DHT is not available'))
}
this._dht.put(key, value, callback)
},
get: (key, callback) => {
if (!this._dht) {
return callback(new Error('DHT is not available'))
}
this._dht.get(key, callback)
},
getMany (key, nVals, callback) {
if (!this._dht) {
return callback(new Error('DHT is not available'))
}
this._dht.getMany(key, nVals, callback)
}
}
} }
/* /*
@ -144,14 +114,11 @@ class Node extends EventEmitter {
* - create listeners on the multiaddrs the Peer wants to listen * - create listeners on the multiaddrs the Peer wants to listen
*/ */
start (callback) { start (callback) {
if (!this.modules.transport) { if (!this._modules.transport) {
return callback(new Error('no transports were present')) return callback(new Error('no transports were present'))
} }
let ws let ws
let transports = this.modules.transport
transports = Array.isArray(transports) ? transports : [transports]
// so that we can have webrtc-star addrs without adding manually the id // so that we can have webrtc-star addrs without adding manually the id
const maOld = [] const maOld = []
@ -165,51 +132,101 @@ class Node extends EventEmitter {
this.peerInfo.multiaddrs.replace(maOld, maNew) this.peerInfo.multiaddrs.replace(maOld, maNew)
const multiaddrs = this.peerInfo.multiaddrs.toArray() const multiaddrs = this.peerInfo.multiaddrs.toArray()
transports.forEach((transport) => {
if (transport.filter(multiaddrs).length > 0) { this._modules.transport.forEach((Transport) => {
this.swarm.transport.add( let t
transport.tag || transport.constructor.name, transport)
} else if (transport.constructor && if (typeof Transport === 'function') {
transport.constructor.name === 'WebSockets') { t = new Transport()
// TODO find a cleaner way to signal that a transport is always } else {
// used for dialing, even if no listener t = Transport
ws = transport
} }
if (t.filter(multiaddrs).length > 0) {
this._switch.transport.add(t.tag || t.constructor.name, t)
} else if (WebSockets.isWebSockets(t)) {
// TODO find a cleaner way to signal that a transport is always used
// for dialing, even if no listener
ws = t
}
this._transport.push(t)
}) })
series([ series([
(cb) => this.swarm.listen(cb), (cb) => {
this.connectionManager.start()
this._switch.start(cb)
},
(cb) => { (cb) => {
if (ws) { if (ws) {
// always add dialing on websockets // always add dialing on websockets
this.swarm.transport.add(ws.tag || ws.constructor.name, ws) this._switch.transport.add(ws.tag || ws.constructor.name, ws)
} }
// all transports need to be setup before discover starts // all transports need to be setup before discover starts
if (this.modules.discovery) { if (this._modules.peerDiscovery) {
return each(this.modules.discovery, (d, cb) => d.start(cb), cb) each(this._modules.peerDiscovery, (D, _cb) => {
let config = {}
if (D.tag &&
this._config.peerDiscovery &&
this._config.peerDiscovery[D.tag]) {
config = this._config.peerDiscovery[D.tag]
}
// If not configured to be enabled/disabled then enable by default
const enabled = config.enabled == null ? true : config.enabled
// If enabled then start it
if (enabled) {
let d
if (typeof D === 'function') {
d = new D(Object.assign({}, config, { peerInfo: this.peerInfo }))
} else {
d = D
}
d.on('peer', (peerInfo) => this.emit('peer:discovery', peerInfo))
this._discovery.push(d)
d.start(_cb)
} else {
_cb()
}
}, cb)
} else {
cb()
} }
cb()
}, },
(cb) => { (cb) => {
// TODO: chicken-and-egg problem: // TODO: chicken-and-egg problem #1:
// have to set started here because DHT requires libp2p is already started // have to set started here because DHT requires libp2p is already started
this._isStarted = true this._isStarted = true
if (this._dht) { if (this._dht) {
return this._dht.start(cb) this._dht.start(cb)
} else {
cb()
} }
cb()
}, },
(cb) => {
// TODO: chicken-and-egg problem #2:
// have to set started here because FloodSub requires libp2p is already started
if (this._floodSub) {
this._floodSub.start(cb)
} else {
cb()
}
},
(cb) => { (cb) => {
// detect which multiaddrs we don't have a transport for and remove them // detect which multiaddrs we don't have a transport for and remove them
const multiaddrs = this.peerInfo.multiaddrs.toArray() const multiaddrs = this.peerInfo.multiaddrs.toArray()
transports.forEach((transport) => {
multiaddrs.forEach((multiaddr) => { multiaddrs.forEach((multiaddr) => {
if (!multiaddr.toString().match(/\/p2p-circuit($|\/)/) && if (!multiaddr.toString().match(/\/p2p-circuit($|\/)/) &&
!transports.find((transport) => transport.filter(multiaddr).length > 0)) { !this._transport.find((transport) => transport.filter(multiaddr).length > 0)) {
this.peerInfo.multiaddrs.delete(multiaddr) this.peerInfo.multiaddrs.delete(multiaddr)
} }
})
}) })
cb() cb()
}, },
@ -224,20 +241,35 @@ class Node extends EventEmitter {
* Stop the libp2p node by closing its listeners and open connections * Stop the libp2p node by closing its listeners and open connections
*/ */
stop (callback) { stop (callback) {
if (this.modules.discovery) {
this.modules.discovery.forEach((discovery) => {
setImmediate(() => discovery.stop(() => {}))
})
}
series([ series([
(cb) => {
if (this._modules.peerDiscovery) {
// stop all discoveries before continuing with shutdown
return parallel(
this._discovery.map((d) => {
return (_cb) => d.stop(() => { _cb() })
}),
cb
)
}
cb()
},
(cb) => {
if (this._floodSub) {
return this._floodSub.stop(cb)
}
cb()
},
(cb) => { (cb) => {
if (this._dht) { if (this._dht) {
return this._dht.stop(cb) return this._dht.stop(cb)
} }
cb() cb()
}, },
(cb) => this.swarm.close(cb), (cb) => {
this.connectionManager.stop()
this._switch.stop(cb)
},
(cb) => { (cb) => {
this.emit('stop') this.emit('stop')
cb() cb()
@ -252,18 +284,22 @@ class Node extends EventEmitter {
return this._isStarted return this._isStarted
} }
ping (peer, callback) { dial (peer, callback) {
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE) assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
this._getPeerInfo(peer, (err, peerInfo) => {
if (err) {
return callback(err)
}
callback(null, new Ping(this.swarm, peerInfo)) this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { return callback(err) }
this._switch.dial(peerInfo, (err) => {
if (err) { return callback(err) }
this.peerBook.put(peerInfo)
callback()
})
}) })
} }
dial (peer, protocol, callback) { dialProtocol (peer, protocol, callback) {
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE) assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
if (typeof protocol === 'function') { if (typeof protocol === 'function') {
@ -272,14 +308,10 @@ class Node extends EventEmitter {
} }
this._getPeerInfo(peer, (err, peerInfo) => { this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { if (err) { return callback(err) }
return callback(err)
}
this.swarm.dial(peerInfo, protocol, (err, conn) => { this._switch.dial(peerInfo, protocol, (err, conn) => {
if (err) { if (err) { return callback(err) }
return callback(err)
}
this.peerBook.put(peerInfo) this.peerBook.put(peerInfo)
callback(null, conn) callback(null, conn)
}) })
@ -290,52 +322,30 @@ class Node extends EventEmitter {
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE) assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
this._getPeerInfo(peer, (err, peerInfo) => { this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { if (err) { return callback(err) }
return callback(err)
}
this.swarm.hangUp(peerInfo, callback) this._switch.hangUp(peerInfo, callback)
})
}
ping (peer, callback) {
if (!this.isStarted()) {
return callback(new Error(NOT_STARTED_ERROR_MESSAGE))
}
this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { return callback(err) }
callback(null, new Ping(this._switch, peerInfo))
}) })
} }
handle (protocol, handlerFunc, matchFunc) { handle (protocol, handlerFunc, matchFunc) {
this.swarm.handle(protocol, handlerFunc, matchFunc) this._switch.handle(protocol, handlerFunc, matchFunc)
} }
unhandle (protocol) { unhandle (protocol) {
this.swarm.unhandle(protocol) this._switch.unhandle(protocol)
}
/*
* Helper method to check the data type of peer and convert it to PeerInfo
*/
_getPeerInfo (peer, callback) {
let p
// PeerInfo
if (PeerInfo.isPeerInfo(peer)) {
p = peer
// Multiaddr instance (not string)
} else if (multiaddr.isMultiaddr(peer)) {
const peerIdB58Str = peer.getPeerId()
try {
p = this.peerBook.get(peerIdB58Str)
} catch (err) {
p = new PeerInfo(PeerId.createFromB58String(peerIdB58Str))
}
p.multiaddrs.add(peer)
// PeerId
} else if (PeerId.isPeerId(peer)) {
const peerIdB58Str = peer.toB58String()
try {
p = this.peerBook.get(peerIdB58Str)
} catch (err) {
return this.peerRouting.findPeer(peer, callback)
}
} else {
return setImmediate(() => callback(new Error('peer type not recognized')))
}
setImmediate(() => callback(null, p))
} }
} }

13
src/peer-routing.js Normal file
View File

@ -0,0 +1,13 @@
'use strict'
module.exports = (node) => {
return {
findPeer: (id, callback) => {
if (!node._dht) {
return callback(new Error('DHT is not available'))
}
node._dht.findPeer(id, callback)
}
}
}

93
src/pubsub.js Normal file
View File

@ -0,0 +1,93 @@
'use strict'
const setImmediate = require('async/setImmediate')
const NOT_STARTED_YET = require('./error-messages').NOT_STARTED_YET
const FloodSub = require('libp2p-floodsub')
module.exports = (node) => {
const floodSub = new FloodSub(node)
node._floodSub = floodSub
return {
subscribe: (topic, options, handler, callback) => {
if (typeof options === 'function') {
callback = handler
handler = options
options = {}
}
if (!node.isStarted() && !floodSub.started) {
return setImmediate(() => callback(new Error(NOT_STARTED_YET)))
}
function subscribe (cb) {
if (floodSub.listenerCount(topic) === 0) {
floodSub.subscribe(topic)
}
floodSub.on(topic, handler)
setImmediate(cb)
}
subscribe(callback)
},
unsubscribe: (topic, handler) => {
if (!node.isStarted() && !floodSub.started) {
throw new Error(NOT_STARTED_YET)
}
floodSub.removeListener(topic, handler)
if (floodSub.listenerCount(topic) === 0) {
floodSub.unsubscribe(topic)
}
},
publish: (topic, data, callback) => {
if (!node.isStarted() && !floodSub.started) {
return setImmediate(() => callback(new Error(NOT_STARTED_YET)))
}
if (!Buffer.isBuffer(data)) {
return setImmediate(() => callback(new Error('data must be a Buffer')))
}
floodSub.publish(topic, data)
setImmediate(() => callback())
},
ls: (callback) => {
if (!node.isStarted() && !floodSub.started) {
return setImmediate(() => callback(new Error(NOT_STARTED_YET)))
}
const subscriptions = Array.from(floodSub.subscriptions)
setImmediate(() => callback(null, subscriptions))
},
peers: (topic, callback) => {
if (!node.isStarted() && !floodSub.started) {
return setImmediate(() => callback(new Error(NOT_STARTED_YET)))
}
if (typeof topic === 'function') {
callback = topic
topic = null
}
const peers = Array.from(floodSub.peers.values())
.filter((peer) => topic ? peer.topics.has(topic) : true)
.map((peer) => peer.info.id.toB58String())
setImmediate(() => callback(null, peers))
},
setMaxListeners (n) {
return floodSub.setMaxListeners(n)
}
}
}

View File

@ -1,14 +0,0 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect
const libp2p = require('../src')
describe('libp2p', () => {
it('the skeleton is fine, now go build your own libp2p bundle', () => {
expect(libp2p).to.exist()
})
})

View File

@ -1,4 +1,3 @@
'use strict' 'use strict'
require('./base')
require('./transports.browser') require('./transports.browser')

View File

@ -1,21 +1,21 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const pull = require('pull-stream') const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect
const sinon = require('sinon')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const series = require('async/series') const series = require('async/series')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const utils = require('./utils/node')
const Circuit = require('libp2p-circuit') const Circuit = require('libp2p-circuit')
const multiaddr = require('multiaddr') const multiaddr = require('multiaddr')
const chai = require('chai') const createNode = require('./utils/create-node')
chai.use(require('dirty-chai')) const tryEcho = require('./utils/try-echo')
const echo = require('./utils/echo')
const expect = chai.expect describe('circuit relay', () => {
const sinon = require('sinon')
describe('circuit relay', function () {
let handlerSpies = [] let handlerSpies = []
let relayNode1 let relayNode1
let relayNode2 let relayNode2
@ -24,29 +24,32 @@ describe('circuit relay', function () {
let nodeTCP1 let nodeTCP1
let nodeTCP2 let nodeTCP2
function setupNode (addrs, options, cb) { function setupNode (addrs, options, callback) {
if (typeof options === 'function') { if (typeof options === 'function') {
cb = options callback = options
options = {} options = {}
} }
options = options || {} options = options || {}
return utils.createNode(addrs, options, (err, node) => { return createNode(addrs, options, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
node.handle('/echo/1.0.0', utils.echo) node.handle('/echo/1.0.0', echo)
node.start((err) => { node.start((err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
handlerSpies.push(sinon.spy(node.swarm.transports[Circuit.tag].listeners[0].hopHandler, 'handle')) handlerSpies.push(sinon.spy(
cb(node) node._switch.transports[Circuit.tag].listeners[0].hopHandler, 'handle'
))
callback(node)
}) })
}) })
} }
before(function (done) { before(function (done) {
this.timeout(20000) this.timeout(20 * 1000)
waterfall([ waterfall([
// set up passive relay // set up passive relay
@ -54,11 +57,13 @@ describe('circuit relay', function () {
'/ip4/0.0.0.0/tcp/0/ws', '/ip4/0.0.0.0/tcp/0/ws',
'/ip4/0.0.0.0/tcp/0' '/ip4/0.0.0.0/tcp/0'
], { ], {
relay: { config: {
enabled: true, relay: {
hop: {
enabled: true, enabled: true,
active: false // passive relay hop: {
enabled: true,
active: false // passive relay
}
} }
} }
}, (node) => { }, (node) => {
@ -70,11 +75,13 @@ describe('circuit relay', function () {
'/ip4/0.0.0.0/tcp/0/ws', '/ip4/0.0.0.0/tcp/0/ws',
'/ip4/0.0.0.0/tcp/0' '/ip4/0.0.0.0/tcp/0'
], { ], {
relay: { config: {
enabled: true, relay: {
hop: {
enabled: true, enabled: true,
active: false // passive relay hop: {
enabled: true,
active: false // passive relay
}
} }
} }
}, (node) => { }, (node) => {
@ -85,8 +92,10 @@ describe('circuit relay', function () {
(cb) => setupNode([ (cb) => setupNode([
'/ip4/0.0.0.0/tcp/0/ws' '/ip4/0.0.0.0/tcp/0/ws'
], { ], {
relay: { config: {
enabled: true relay: {
enabled: true
}
} }
}, (node) => { }, (node) => {
nodeWS1 = node nodeWS1 = node
@ -96,8 +105,10 @@ describe('circuit relay', function () {
(cb) => setupNode([ (cb) => setupNode([
'/ip4/0.0.0.0/tcp/0/ws' '/ip4/0.0.0.0/tcp/0/ws'
], { ], {
relay: { config: {
enabled: true relay: {
enabled: true
}
} }
}, (node) => { }, (node) => {
nodeWS2 = node nodeWS2 = node
@ -108,8 +119,10 @@ describe('circuit relay', function () {
'/ip4/0.0.0.0/tcp/0', '/ip4/0.0.0.0/tcp/0',
`/ipfs/${relayNode1.peerInfo.id.toB58String()}/p2p-circuit` `/ipfs/${relayNode1.peerInfo.id.toB58String()}/p2p-circuit`
], { ], {
relay: { config: {
enabled: true relay: {
enabled: true
}
} }
}, (node) => { }, (node) => {
nodeTCP1 = node nodeTCP1 = node
@ -120,8 +133,10 @@ describe('circuit relay', function () {
'/ip4/0.0.0.0/tcp/0', '/ip4/0.0.0.0/tcp/0',
`/ip4/0.0.0.0/tcp/0/ipfs/${relayNode2.peerInfo.id.toB58String()}/p2p-circuit` `/ip4/0.0.0.0/tcp/0/ipfs/${relayNode2.peerInfo.id.toB58String()}/p2p-circuit`
], { ], {
relay: { config: {
enabled: true relay: {
enabled: true
}
} }
}, (node) => { }, (node) => {
nodeTCP2 = node nodeTCP2 = node
@ -153,25 +168,16 @@ describe('circuit relay', function () {
describe('any relay', function () { describe('any relay', function () {
this.timeout(20 * 1000) this.timeout(20 * 1000)
it('should dial from WS1 to TCP1 over any R', (done) => { it('dial from WS1 to TCP1 over any R', (done) => {
nodeWS1.dial(nodeTCP1.peerInfo, '/echo/1.0.0', (err, conn) => { nodeWS1.dialProtocol(nodeTCP1.peerInfo, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(conn).to.exist() expect(conn).to.exist()
tryEcho(conn, done)
pull(
pull.values(['hello']),
conn,
pull.collect((err, result) => {
expect(err).to.not.exist()
expect(result[0].toString()).to.equal('hello')
done()
})
)
}) })
}) })
it('should not dial - no R from WS2 to TCP1', (done) => { it('fail to dial - no R from WS2 to TCP1', (done) => {
nodeWS2.dial(nodeTCP2.peerInfo, '/echo/1.0.0', (err, conn) => { nodeWS2.dialProtocol(nodeTCP2.peerInfo, '/echo/1.0.0', (err, conn) => {
expect(err).to.exist() expect(err).to.exist()
expect(conn).to.not.exist() expect(conn).to.not.exist()
done() done()
@ -182,43 +188,29 @@ describe('circuit relay', function () {
describe('explicit relay', function () { describe('explicit relay', function () {
this.timeout(20 * 1000) this.timeout(20 * 1000)
it('should dial from WS1 to TCP1 over R1', (done) => { it('dial from WS1 to TCP1 over R1', (done) => {
nodeWS1.dial(nodeTCP1.peerInfo, '/echo/1.0.0', (err, conn) => { nodeWS1.dialProtocol(nodeTCP1.peerInfo, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(conn).to.exist() expect(conn).to.exist()
pull( tryEcho(conn, () => {
pull.values(['hello']), const addr = multiaddr(handlerSpies[0].args[2][0].dstPeer.addrs[0]).toString()
conn, expect(addr).to.equal(`/ipfs/${nodeTCP1.peerInfo.id.toB58String()}`)
pull.collect((err, result) => { done()
expect(err).to.not.exist() })
expect(result[0].toString()).to.equal('hello')
const addr = multiaddr(handlerSpies[0].args[2][0].dstPeer.addrs[0]).toString()
expect(addr).to.equal(`/ipfs/${nodeTCP1.peerInfo.id.toB58String()}`)
done()
})
)
}) })
}) })
it('should dial from WS1 to TCP2 over R2', (done) => { it('dial from WS1 to TCP2 over R2', (done) => {
nodeWS1.dial(nodeTCP2.peerInfo, '/echo/1.0.0', (err, conn) => { nodeWS1.dialProtocol(nodeTCP2.peerInfo, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(conn).to.exist() expect(conn).to.exist()
pull( tryEcho(conn, () => {
pull.values(['hello']), const addr = multiaddr(handlerSpies[1].args[2][0].dstPeer.addrs[0]).toString()
conn, expect(addr).to.equal(`/ipfs/${nodeTCP2.peerInfo.id.toB58String()}`)
pull.collect((err, result) => { done()
expect(err).to.not.exist() })
expect(result[0].toString()).to.equal('hello')
const addr = multiaddr(handlerSpies[1].args[2][0].dstPeer.addrs[0]).toString()
expect(addr).to.equal(`/ipfs/${nodeTCP2.peerInfo.id.toB58String()}`)
done()
})
)
}) })
}) })
}) })

116
test/config.spec.js Normal file
View File

@ -0,0 +1,116 @@
/* 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 waterfall = require('async/waterfall')
const WS = require('libp2p-websockets')
const Bootstrap = require('libp2p-railing')
const validateConfig = require('../src/config').validate
describe('configuration', () => {
let peerInfo
before((done) => {
waterfall([
(cb) => PeerId.create({ bits: 512 }, cb),
(peerId, cb) => PeerInfo.create(peerId, cb),
(info, cb) => {
peerInfo = info
cb()
}
], () => done())
})
it('should throw an error if peerInfo is missing', () => {
expect(() => {
validateConfig({
modules: {
transport: [ WS ]
}
})
}).to.throw()
})
it('should throw an error if modules is missing', () => {
expect(() => {
validateConfig({
peerInfo
})
}).to.throw()
})
it('should throw an error if there are no transports', () => {
expect(() => {
validateConfig({
peerInfo,
modules: {
transport: [ ]
}
})
}).to.throw()
})
it('should add defaults to missing items', () => {
const options = {
peerInfo,
modules: {
transport: [ WS ],
peerDiscovery: [ Bootstrap ]
},
config: {
peerDiscovery: {
bootstrap: {
interval: 1000,
enabled: true
}
}
}
}
const expected = {
peerInfo,
modules: {
transport: [ WS ],
peerDiscovery: [ Bootstrap ]
},
config: {
peerDiscovery: {
bootstrap: {
interval: 1000,
enabled: true
}
},
EXPERIMENTAL: {
pubsub: false,
dht: false
},
relay: {
enabled: false
}
}
}
expect(validateConfig(options)).to.deep.equal(expected)
})
it('should not allow for dht to be enabled without it being provided', () => {
const options = {
peerInfo,
modules: {
transport: [ WS ]
},
config: {
EXPERIMENTAL: {
dht: true
}
}
}
expect(() => validateConfig(options)).to.throw()
})
})

View File

@ -9,8 +9,8 @@ const expect = chai.expect
const parallel = require('async/parallel') const parallel = require('async/parallel')
const _times = require('lodash.times') const _times = require('lodash.times')
const CID = require('cids') const CID = require('cids')
const utils = require('./utils/node')
const createNode = utils.createNode const createNode = require('./utils/create-node')
describe('.contentRouting', () => { describe('.contentRouting', () => {
let nodeA let nodeA
@ -23,8 +23,11 @@ describe('.contentRouting', () => {
this.timeout(5 * 1000) this.timeout(5 * 1000)
const tasks = _times(5, () => (cb) => { const tasks = _times(5, () => (cb) => {
createNode('/ip4/0.0.0.0/tcp/0', { createNode('/ip4/0.0.0.0/tcp/0', {
mdns: false, config: {
dht: true EXPERIMENTAL: {
dht: true
}
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
node.start((err) => cb(err, node)) node.start((err) => cb(err, node))

79
test/create.spec.js Normal file
View File

@ -0,0 +1,79 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect
const series = require('async/series')
const createNode = require('./utils/create-node')
const sinon = require('sinon')
describe('libp2p creation', () => {
it('should be able to start and stop successfully', (done) => {
createNode([], {
config: {
EXPERIMENTAL: {
dht: true,
pubsub: true
}
}
}, (err, node) => {
expect(err).to.not.exist()
let sw = node._switch
let cm = node.connectionManager
let dht = node._dht
let pub = node._floodSub
sinon.spy(sw, 'start')
sinon.spy(cm, 'start')
sinon.spy(dht, 'start')
sinon.spy(pub, 'start')
sinon.spy(sw, 'stop')
sinon.spy(cm, 'stop')
sinon.spy(dht, 'stop')
sinon.spy(pub, 'stop')
sinon.spy(node, 'emit')
series([
(cb) => node.start(cb),
(cb) => {
expect(sw.start.calledOnce).to.equal(true)
expect(cm.start.calledOnce).to.equal(true)
expect(dht.start.calledOnce).to.equal(true)
expect(pub.start.calledOnce).to.equal(true)
expect(node.emit.calledWith('start')).to.equal(true)
cb()
},
(cb) => node.stop(cb)
], (err) => {
expect(err).to.not.exist()
expect(sw.stop.calledOnce).to.equal(true)
expect(cm.stop.calledOnce).to.equal(true)
expect(dht.stop.calledOnce).to.equal(true)
expect(pub.stop.calledOnce).to.equal(true)
expect(node.emit.calledWith('stop')).to.equal(true)
done()
})
})
})
it('should not create disabled modules', (done) => {
createNode([], {
config: {
EXPERIMENTAL: {
dht: false,
pubsub: false
}
}
}, (err, node) => {
expect(err).to.not.exist()
expect(node._dht).to.not.exist()
expect(node._floodSub).to.not.exist()
done()
})
})
})

View File

@ -5,7 +5,8 @@ const chai = require('chai')
chai.use(require('dirty-chai')) chai.use(require('dirty-chai'))
const expect = chai.expect const expect = chai.expect
const series = require('async/series') const series = require('async/series')
const createNode = require('./utils/node').createNode
const createNode = require('./utils/create-node')
describe('multiaddr trim', () => { describe('multiaddr trim', () => {
it('non used multiaddrs get trimmed', (done) => { it('non used multiaddrs get trimmed', (done) => {
@ -20,7 +21,6 @@ describe('multiaddr trim', () => {
expect(err).to.not.exist() expect(err).to.not.exist()
node = _node node = _node
const multiaddrs = node.peerInfo.multiaddrs.toArray() const multiaddrs = node.peerInfo.multiaddrs.toArray()
// multiaddrs.forEach((ma) => console.log(ma.toString()))
expect(multiaddrs).to.have.length(3) expect(multiaddrs).to.have.length(3)
cb() cb()
}), }),
@ -29,11 +29,10 @@ describe('multiaddr trim', () => {
expect(err).to.not.exist() expect(err).to.not.exist()
const multiaddrs = node.peerInfo.multiaddrs.toArray() const multiaddrs = node.peerInfo.multiaddrs.toArray()
// console.log('--')
// multiaddrs.forEach((ma) => console.log(ma.toString()))
expect(multiaddrs.length).to.at.least(2) expect(multiaddrs.length).to.at.least(2)
expect(multiaddrs[0].toString()).to.match(/^\/ip4\/127\.0\.0\.1\/tcp\/[0-9]+\/ws\/ipfs\/\w+$/) expect(multiaddrs[0].toString())
.to.match(/^\/ip4\/127\.0\.0\.1\/tcp\/[0-9]+\/ws\/ipfs\/\w+$/)
node.stop(done) node.stop(done)
}) })
}) })

View File

@ -1,10 +1,12 @@
'use strict' 'use strict'
require('./base') require('./pnet.node')
require('./transports.node') require('./transports.node')
require('./stream-muxing.node') require('./stream-muxing.node')
require('./peer-discovery.node') require('./peer-discovery.node')
require('./pubsub.node')
require('./peer-routing.node') require('./peer-routing.node')
require('./content-routing.node') require('./content-routing.node')
require('./circuit-relay.node') require('./circuit-relay.node')
require('./multiaddr-trim') require('./multiaddr-trim.node')
require('./stats')

View File

@ -4,11 +4,13 @@
const chai = require('chai') const chai = require('chai')
chai.use(require('dirty-chai')) chai.use(require('dirty-chai'))
const expect = chai.expect const expect = chai.expect
const sinon = require('sinon')
const signalling = require('libp2p-webrtc-star/src/sig-server') const signalling = require('libp2p-webrtc-star/src/sig-server')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const utils = require('./utils/node') const crypto = require('crypto')
const createNode = utils.createNode
const echo = utils.echo const createNode = require('./utils/create-node')
const echo = require('./utils/echo')
describe('peer discovery', () => { describe('peer discovery', () => {
let nodeA let nodeA
@ -52,13 +54,202 @@ describe('peer discovery', () => {
parallel([ parallel([
(cb) => nodeA.stop(cb), (cb) => nodeA.stop(cb),
(cb) => nodeB.stop(cb), (cb) => nodeB.stop(cb),
(cb) => ss.stop(done) (cb) => ss.stop(cb)
], done) ], done)
}) })
} }
describe('module registration', () => {
it('should enable by default a module passed as an object', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0)
}
const options = { modules: { peerDiscovery: [ mockDiscovery ] } }
createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()
node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.true()
node.stop(done)
})
})
})
it('should enable by default a module passed as a function', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0)
}
const MockDiscovery = sinon.stub().returns(mockDiscovery)
const options = { modules: { peerDiscovery: [ MockDiscovery ] } }
createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()
node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.true()
node.stop(done)
})
})
})
it('should enable module by configutation', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0),
tag: 'mockDiscovery'
}
const enabled = sinon.stub().returns(true)
const options = {
modules: { peerDiscovery: [ mockDiscovery ] },
config: {
peerDiscovery: {
mockDiscovery: {
get enabled () {
return enabled()
}
}
}
}
}
createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()
node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.true()
expect(enabled.called).to.be.true()
node.stop(done)
})
})
})
it('should disable module by configutation', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0),
tag: 'mockDiscovery'
}
const disabled = sinon.stub().returns(false)
const options = {
modules: { peerDiscovery: [ mockDiscovery ] },
config: {
peerDiscovery: {
mockDiscovery: {
get enabled () {
return disabled()
}
}
}
}
}
createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()
node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.false()
expect(disabled.called).to.be.true()
node.stop(done)
})
})
})
it('should register module passed as function', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0)
}
const MockDiscovery = sinon.stub().returns(mockDiscovery)
MockDiscovery.tag = 'mockDiscovery'
const options = {
modules: { peerDiscovery: [ MockDiscovery ] },
config: {
peerDiscovery: {
mockDiscovery: {
enabled: true,
time: Date.now()
}
}
}
}
createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()
node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.true()
expect(MockDiscovery.called).to.be.true()
// Ensure configuration was passed
expect(MockDiscovery.firstCall.args[0])
.to.deep.include(options.config.peerDiscovery.mockDiscovery)
node.stop(done)
})
})
})
it('should register module passed as object', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0),
tag: 'mockDiscovery'
}
const options = {
modules: { peerDiscovery: [ mockDiscovery ] },
config: {
peerDiscovery: {
mockDiscovery: { enabled: true }
}
}
}
createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()
node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.true()
node.stop(done)
})
})
})
})
describe('MulticastDNS', () => { describe('MulticastDNS', () => {
setup({ mdns: true }) setup({
config: {
peerDiscovery: {
mdns: {
enabled: true,
// use a random tag to prevent CI collision
serviceTag: crypto.randomBytes(10).toString('hex')
}
}
}
})
it('find a peer', function (done) { it('find a peer', function (done) {
this.timeout(15 * 1000) this.timeout(15 * 1000)
@ -73,7 +264,15 @@ describe('peer discovery', () => {
// TODO needs a delay (this test is already long) // TODO needs a delay (this test is already long)
describe.skip('WebRTCStar', () => { describe.skip('WebRTCStar', () => {
setup({ webRTCStar: true }) setup({
config: {
peerDiscovery: {
webRTCStar: {
enabled: true
}
}
}
})
it('find a peer', function (done) { it('find a peer', function (done) {
this.timeout(15 * 1000) this.timeout(15 * 1000)
@ -87,8 +286,18 @@ describe('peer discovery', () => {
describe('MulticastDNS + WebRTCStar', () => { describe('MulticastDNS + WebRTCStar', () => {
setup({ setup({
webRTCStar: true, config: {
mdns: true peerDiscovery: {
mdns: {
enabled: true,
// use a random tag to prevent CI collision
serviceTag: crypto.randomBytes(10).toString('hex')
},
webRTCStar: {
enabled: true
}
}
}
}) })
it('find a peer', function (done) { it('find a peer', function (done) {

View File

@ -8,8 +8,8 @@ chai.use(require('dirty-chai'))
const expect = chai.expect const expect = chai.expect
const parallel = require('async/parallel') const parallel = require('async/parallel')
const _times = require('lodash.times') const _times = require('lodash.times')
const utils = require('./utils/node')
const createNode = utils.createNode const createNode = require('./utils/create-node')
describe('.peerRouting', () => { describe('.peerRouting', () => {
let nodeA let nodeA
@ -23,8 +23,11 @@ describe('.peerRouting', () => {
const tasks = _times(5, () => (cb) => { const tasks = _times(5, () => (cb) => {
createNode('/ip4/0.0.0.0/tcp/0', { createNode('/ip4/0.0.0.0/tcp/0', {
mdns: false, config: {
dht: true EXPERIMENTAL: {
dht: true
}
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
node.start((err) => cb(err, node)) node.start((err) => cb(err, node))

90
test/pnet.node.js Normal file
View File

@ -0,0 +1,90 @@
/* 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 waterfall = require('async/waterfall')
const WS = require('libp2p-websockets')
const defaultsDeep = require('@nodeutils/defaults-deep')
const Libp2p = require('../src')
describe('private network', () => {
let config
before((done) => {
waterfall([
(cb) => PeerId.create({ bits: 512 }, cb),
(peerId, cb) => PeerInfo.create(peerId, cb),
(peerInfo, cb) => {
config = {
peerInfo,
modules: {
transport: [ WS ]
}
}
cb()
}
], () => done())
})
describe('enforced network protection', () => {
before(() => {
process.env.LIBP2P_FORCE_PNET = 1
})
after(() => {
delete process.env.LIBP2P_FORCE_PNET
})
it('should throw an error without a provided protector', () => {
expect(() => {
return new Libp2p(config)
}).to.throw('Private network is enforced, but no protector was provided')
})
it('should create a libp2p node with a provided protector', () => {
let node
let protector = {
psk: '123',
tag: '/psk/1.0.0',
protect: () => { }
}
expect(() => {
let options = defaultsDeep(config, {
modules: {
connProtector: protector
}
})
node = new Libp2p(options)
return node
}).to.not.throw()
expect(node._switch.protector).to.deep.equal(protector)
})
it('should throw an error if the protector does not have a protect method', () => {
expect(() => {
let options = defaultsDeep(config, {
modules: {
connProtector: { }
}
})
return new Libp2p(options)
}).to.throw()
})
})
describe('network protection not enforced', () => {
it('should not throw an error with no provided protector', () => {
expect(() => {
return new Libp2p(config)
}).to.not.throw()
})
})
})

98
test/pubsub.node.js Normal file
View File

@ -0,0 +1,98 @@
/* 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 waterfall = require('async/waterfall')
const _times = require('lodash.times')
const createNode = require('./utils/create-node')
function startTwo (callback) {
const tasks = _times(2, () => (cb) => {
createNode('/ip4/0.0.0.0/tcp/0', {
config: {
peerDiscovery: {
mdns: {
enabled: false
}
},
EXPERIMENTAL: {
pubsub: true
}
}
}, (err, node) => {
expect(err).to.not.exist()
node.start((err) => cb(err, node))
})
})
parallel(tasks, (err, nodes) => {
expect(err).to.not.exist()
nodes[0].dial(nodes[1].peerInfo, (err) => callback(err, nodes))
})
}
function stopTwo (nodes, callback) {
parallel([
(cb) => nodes[0].stop(cb),
(cb) => nodes[1].stop(cb)
], callback)
}
// There is a vast test suite on PubSub through js-ipfs
// https://github.com/ipfs/interface-ipfs-core/blob/master/js/src/pubsub.js
// and libp2p-floodsub itself
// https://github.com/libp2p/js-libp2p-floodsub/tree/master/test
// TODO: consider if all or some of those should come here
describe('.pubsub', () => {
describe('.pubsub on (default)', (done) => {
it('start two nodes and send one message', (done) => {
waterfall([
(cb) => startTwo(cb),
(nodes, cb) => {
const data = Buffer.from('test')
nodes[0].pubsub.subscribe('pubsub',
(msg) => {
expect(msg.data).to.eql(data)
cb(null, nodes)
},
(err) => {
expect(err).to.not.exist()
setTimeout(() => nodes[1].pubsub.publish('pubsub', data, (err) => {
expect(err).to.not.exist()
}), 500)
}
)
},
(nodes, cb) => stopTwo(nodes, cb)
], done)
})
})
describe('.pubsub off', () => {
it('fail to use pubsub if disabled', (done) => {
createNode('/ip4/0.0.0.0/tcp/0', {
config: {
peerDiscovery: {
mdns: {
enabled: false
}
},
EXPERIMENTAL: {
pubsub: false
}
}
}, (err, node) => {
expect(err).to.not.exist()
expect(node.pubsub).to.not.exist()
done()
})
})
})
})

32
test/stats.js Normal file
View File

@ -0,0 +1,32 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect
const createNode = require('./utils/create-node')
describe('libp2p', () => {
it('has stats', (done) => {
createNode('/ip4/127.0.0.1/tcp/0', {
config: {
peerDiscovery: {
mdns: {
enabled: false
}
},
EXPERIMENTAL: {
dht: true
}
}
}, (err, node) => {
expect(err).to.not.exist()
node.start((err) => {
expect(err).to.not.exist()
expect(node.stats).to.exist()
node.stop(done)
})
})
})
})

View File

@ -6,24 +6,16 @@ chai.use(require('dirty-chai'))
const expect = chai.expect const expect = chai.expect
const parallel = require('async/parallel') const parallel = require('async/parallel')
const series = require('async/series') const series = require('async/series')
const pull = require('pull-stream') const Mplex = require('libp2p-mplex')
const utils = require('./utils/node') const SPDY = require('libp2p-spdy')
const createNode = utils.createNode const createNode = require('./utils/create-node')
const echo = utils.echo const tryEcho = require('./utils/try-echo')
const echo = require('./utils/echo')
function test (nodeA, nodeB, callback) { function test (nodeA, nodeB, callback) {
nodeA.dial(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => { nodeA.dialProtocol(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
tryEcho(conn, callback)
pull(
pull.values([Buffer.from('hey')]),
conn,
pull.collect((err, data) => {
expect(err).to.not.exist()
expect(data).to.be.eql([Buffer.from('hey')])
callback()
})
)
}) })
} }
@ -44,7 +36,9 @@ describe('stream muxing', () => {
function setup (callback) { function setup (callback) {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
muxer: ['spdy'] modules: {
streamMuxer: [ SPDY ]
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeA = node nodeA = node
@ -52,7 +46,9 @@ describe('stream muxing', () => {
node.start(cb) node.start(cb)
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
muxer: ['spdy'] modules: {
streamMuxer: [ SPDY ]
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeB = node nodeB = node
@ -69,14 +65,16 @@ describe('stream muxing', () => {
], done) ], done)
}) })
it('multiplex only', (done) => { it('mplex only', (done) => {
let nodeA let nodeA
let nodeB let nodeB
function setup (callback) { function setup (callback) {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
muxer: ['multiplex'] modules: {
streamMuxer: [ Mplex ]
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeA = node nodeA = node
@ -84,7 +82,9 @@ describe('stream muxing', () => {
node.start(cb) node.start(cb)
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
muxer: ['multiplex'] modules: {
streamMuxer: [ Mplex ]
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeB = node nodeB = node
@ -101,7 +101,7 @@ describe('stream muxing', () => {
], done) ], done)
}) })
it('spdy + multiplex', function (done) { it('spdy + mplex', function (done) {
this.timeout(5000) this.timeout(5000)
let nodeA let nodeA
@ -110,7 +110,9 @@ describe('stream muxing', () => {
function setup (callback) { function setup (callback) {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
muxer: ['spdy', 'multiplex'] modules: {
streamMuxer: [ Mplex ]
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeA = node nodeA = node
@ -118,7 +120,9 @@ describe('stream muxing', () => {
node.start(cb) node.start(cb)
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
muxer: ['spdy', 'multiplex'] modules: {
streamMuxer: [ SPDY, Mplex ]
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeB = node nodeB = node
@ -135,8 +139,8 @@ describe('stream muxing', () => {
], done) ], done)
}) })
it('spdy + multiplex switched order', function (done) { it('spdy + mplex switched order', function (done) {
this.timeout(5000) this.timeout(5 * 1000)
let nodeA let nodeA
let nodeB let nodeB
@ -144,7 +148,9 @@ describe('stream muxing', () => {
function setup (callback) { function setup (callback) {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
muxer: ['spdy', 'multiplex'] modules: {
streamMuxer: [ SPDY, Mplex ]
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeA = node nodeA = node
@ -152,7 +158,9 @@ describe('stream muxing', () => {
node.start(cb) node.start(cb)
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
muxer: ['multiplex', 'spdy'] modules: {
streamMuxer: [ Mplex, SPDY ]
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeB = node nodeB = node
@ -170,7 +178,7 @@ describe('stream muxing', () => {
}) })
it('one without the other fails to establish a muxedConn', function (done) { it('one without the other fails to establish a muxedConn', function (done) {
this.timeout(5000) this.timeout(5 * 1000)
let nodeA let nodeA
let nodeB let nodeB
@ -178,7 +186,9 @@ describe('stream muxing', () => {
function setup (callback) { function setup (callback) {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
muxer: ['spdy'] modules: {
streamMuxer: [ SPDY ]
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeA = node nodeA = node
@ -186,7 +196,9 @@ describe('stream muxing', () => {
node.start(cb) node.start(cb)
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
muxer: ['multiplex'] modules: {
streamMuxer: [ Mplex ]
}
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeB = node nodeB = node
@ -200,12 +212,12 @@ describe('stream muxing', () => {
(cb) => setup(cb), (cb) => setup(cb),
(cb) => { (cb) => {
// it will just 'warm up a conn' // it will just 'warm up a conn'
expect(Object.keys(nodeA.swarm.muxers)).to.have.length(1) expect(Object.keys(nodeA._switch.muxers)).to.have.length(1)
expect(Object.keys(nodeB.swarm.muxers)).to.have.length(1) expect(Object.keys(nodeB._switch.muxers)).to.have.length(1)
nodeA.dial(nodeB.peerInfo, (err) => { nodeA.dial(nodeB.peerInfo, (err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeA._switch.muxedConns)).to.have.length(0)
cb() cb()
}) })
}, },

View File

@ -7,52 +7,57 @@ chai.use(require('dirty-chai'))
const expect = chai.expect const expect = chai.expect
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const PeerId = require('peer-id') const PeerId = require('peer-id')
const Mplex = require('libp2p-mplex')
const pull = require('pull-stream') const pull = require('pull-stream')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const goodbye = require('pull-goodbye') const goodbye = require('pull-goodbye')
const serializer = require('pull-serializer') const serializer = require('pull-serializer')
const w = require('webrtcsupport') const w = require('webrtcsupport')
const tryEcho = require('./utils/try-echo')
const Node = require('./utils/bundle.browser') const Node = require('./utils/bundle-browser')
const rawPeer = require('./fixtures/test-peer.json') const jsonPeerId = require('./fixtures/test-peer.json')
describe('transports', () => { describe('transports', () => {
describe('websockets', () => { describe('websockets', () => {
let peerB let peerB
let peerBMultiaddr = '/ip4/127.0.0.1/tcp/9200/ws/ipfs/' + jsonPeerId.id
let nodeA let nodeA
before((done) => { before((done) => {
const ma = '/ip4/127.0.0.1/tcp/9200/ws/ipfs/' + rawPeer.id PeerId.createFromPrivKey(jsonPeerId.privKey, (err, id) => {
expect(err).to.not.exist()
PeerId.createFromPrivKey(rawPeer.privKey, (err, id) => {
if (err) {
return done(err)
}
peerB = new PeerInfo(id) peerB = new PeerInfo(id)
peerB.multiaddrs.add(ma) peerB.multiaddrs.add(peerBMultiaddr)
done() done()
}) })
}) })
after((done) => nodeA.stop(done)) after((done) => nodeA.stop(done))
it('create libp2pNode', (done) => { it('create a libp2p Node', (done) => {
PeerInfo.create((err, peerInfo) => { PeerInfo.create((err, peerInfo) => {
expect(err).to.not.exist() expect(err).to.not.exist()
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
nodeA = new Node(peerInfo) nodeA = new Node({
peerInfo: peerInfo
})
done() done()
}) })
}) })
it('create libp2pNode with multiplex only', (done) => { it('create a libp2p Node with mplex only', (done) => {
PeerInfo.create((err, peerInfo) => { PeerInfo.create((err, peerInfo) => {
expect(err).to.not.exist() expect(err).to.not.exist()
const b = new Node(peerInfo, null, { muxer: ['multiplex'] }) const b = new Node({
expect(b.modules.connection.muxer).to.eql([require('libp2p-multiplex')]) peerInfo: peerInfo,
modules: {
streamMuxer: [ Mplex ]
}
})
expect(b._modules.streamMuxer).to.eql([require('libp2p-mplex')])
done() done()
}) })
}) })
@ -63,8 +68,8 @@ describe('transports', () => {
// General connectivity tests // General connectivity tests
it('libp2p.dial using Multiaddr nodeA to nodeB', (done) => { it('.dial using Multiaddr', (done) => {
nodeA.dial(peerB.multiaddrs.toArray()[0], (err) => { nodeA.dial(peerBMultiaddr, (err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
setTimeout(check, 500) // Some time for Identify to finish setTimeout(check, 500) // Some time for Identify to finish
@ -77,27 +82,19 @@ describe('transports', () => {
}) })
}) })
it('libp2p.dial using Multiaddr on Protocol nodeA to nodeB', (done) => { it('.dialProtocol using Multiaddr', (done) => {
nodeA.dial(peerB.multiaddrs.toArray()[0], '/echo/1.0.0', (err, conn) => { nodeA.dialProtocol(peerBMultiaddr, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
const peers = nodeA.peerBook.getAll() const peers = nodeA.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
pull( tryEcho(conn, done)
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) => { it('.hangUp using Multiaddr', (done) => {
nodeA.hangUp(peerB.multiaddrs.toArray()[0], (err) => { nodeA.hangUp(peerBMultiaddr, (err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
setTimeout(check, 500) setTimeout(check, 500)
@ -105,13 +102,13 @@ describe('transports', () => {
function check () { function check () {
const peers = nodeA.peerBook.getAll() const peers = nodeA.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeA._switch.muxedConns)).to.have.length(0)
done() done()
} }
}) })
}) })
it('libp2p.dial using PeerInfo nodeA to nodeB', (done) => { it('.dial using PeerInfo', (done) => {
nodeA.dial(peerB, (err) => { nodeA.dial(peerB, (err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@ -125,26 +122,18 @@ describe('transports', () => {
}) })
}) })
it('libp2p.dial using PeerInfo on Protocol nodeA to nodeB', (done) => { it('.dialProtocol using PeerInfo', (done) => {
nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => { nodeA.dialProtocol(peerB, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
const peers = nodeA.peerBook.getAll() const peers = nodeA.peerBook.getAll()
expect(err).to.not.exist() expect(err).to.not.exist()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
pull( tryEcho(conn, done)
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) => { it('.hangUp using PeerInfo', (done) => {
nodeA.hangUp(peerB, (err) => { nodeA.hangUp(peerB, (err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
setTimeout(check, 500) setTimeout(check, 500)
@ -153,7 +142,7 @@ describe('transports', () => {
const peers = nodeA.peerBook.getAll() const peers = nodeA.peerBook.getAll()
expect(err).to.not.exist() expect(err).to.not.exist()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeA._switch.muxedConns)).to.have.length(0)
done() done()
} }
}) })
@ -161,7 +150,7 @@ describe('transports', () => {
describe('stress', () => { describe('stress', () => {
it('one big write', (done) => { it('one big write', (done) => {
nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => { nodeA.dialProtocol(peerB, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
const rawMessage = Buffer.alloc(100000) const rawMessage = Buffer.alloc(100000)
rawMessage.fill('a') rawMessage.fill('a')
@ -180,7 +169,7 @@ describe('transports', () => {
}) })
it('many writes', (done) => { it('many writes', (done) => {
nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => { nodeA.dialProtocol(peerB, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
const s = serializer(goodbye({ const s = serializer(goodbye({
@ -212,8 +201,8 @@ describe('transports', () => {
it('create two peerInfo with webrtc-star addrs', (done) => { it('create two peerInfo with webrtc-star addrs', (done) => {
parallel([ parallel([
(cb) => PeerId.create({ bits: 1024 }, cb), (cb) => PeerId.create({ bits: 512 }, cb),
(cb) => PeerId.create({ bits: 1024 }, cb) (cb) => PeerId.create({ bits: 512 }, cb)
], (err, ids) => { ], (err, ids) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@ -230,44 +219,39 @@ describe('transports', () => {
}) })
it('create two libp2p nodes with those peers', (done) => { it('create two libp2p nodes with those peers', (done) => {
node1 = new Node(peer1, null, { webRTCStar: true }) node1 = new Node({
node2 = new Node(peer2, null, { webRTCStar: true }) peerInfo: peer1
})
node2 = new Node({
peerInfo: peer2
})
done() done()
}) })
it('listen on the two libp2p nodes', (done) => { it('start two libp2p nodes', (done) => {
parallel([ parallel([
(cb) => node1.start(cb), (cb) => node1.start(cb),
(cb) => node2.start(cb) (cb) => node2.start(cb)
], done) ], done)
}) })
it('handle a protocol on the first node', () => { it('.handle echo on first node', () => {
node2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) node2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
}) })
it('dial from the second node to the first node', (done) => { it('.dialProtocol from the second node to the first node', (done) => {
node1.dial(peer2, '/echo/1.0.0', (err, conn) => { node1.dialProtocol(peer2, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
setTimeout(check, 500) setTimeout(check, 500)
function check () { function check () {
const text = 'hello'
const peers1 = node1.peerBook.getAll() const peers1 = node1.peerBook.getAll()
expect(Object.keys(peers1)).to.have.length(1) expect(Object.keys(peers1)).to.have.length(1)
const peers2 = node2.peerBook.getAll() const peers2 = node2.peerBook.getAll()
expect(Object.keys(peers2)).to.have.length(1) expect(Object.keys(peers2)).to.have.length(1)
pull( tryEcho(conn, done)
pull.values([Buffer.from(text)]),
conn,
pull.collect((err, data) => {
expect(err).to.not.exist()
expect(data[0].toString()).to.equal(text)
done()
})
)
} }
}) })
}) })
@ -280,24 +264,26 @@ describe('transports', () => {
function check () { function check () {
const peers = node1.peerBook.getAll() const peers = node1.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(node1.swarm.muxedConns)).to.have.length(0) expect(Object.keys(node1._switch.muxedConns)).to.have.length(0)
done() done()
} }
}) })
}) })
it('create a third node and check that discovery works', (done) => { it('create a third node and check that discovery works', function (done) {
this.timeout(60 * 1000)
let counter = 0 let counter = 0
function check () { function check () {
if (++counter === 3) { if (++counter === 3) {
expect(Object.keys(node1.swarm.muxedConns).length).to.equal(1) expect(Object.keys(node1._switch.muxedConns).length).to.equal(1)
expect(Object.keys(node2.swarm.muxedConns).length).to.equal(1) expect(Object.keys(node2._switch.muxedConns).length).to.equal(1)
done() done()
} }
} }
PeerId.create((err, id3) => { PeerId.create({ bits: 512 }, (err, id3) => {
expect(err).to.not.exist() expect(err).to.not.exist()
const peer3 = new PeerInfo(id3) const peer3 = new PeerInfo(id3)
@ -307,7 +293,9 @@ describe('transports', () => {
node1.on('peer:discovery', (peerInfo) => node1.dial(peerInfo, check)) node1.on('peer:discovery', (peerInfo) => node1.dial(peerInfo, check))
node2.on('peer:discovery', (peerInfo) => node2.dial(peerInfo, check)) node2.on('peer:discovery', (peerInfo) => node2.dial(peerInfo, check))
const node3 = new Node(peer3, null, { webRTCStar: true }) const node3 = new Node({
peerInfo: peer3
})
node3.start(check) node3.start(check)
}) })
}) })
@ -321,8 +309,8 @@ describe('transports', () => {
it('create two peerInfo with websocket-star addrs', (done) => { it('create two peerInfo with websocket-star addrs', (done) => {
parallel([ parallel([
(cb) => PeerId.create({ bits: 1024 }, cb), (cb) => PeerId.create({ bits: 512 }, cb),
(cb) => PeerId.create({ bits: 1024 }, cb) (cb) => PeerId.create({ bits: 512 }, cb)
], (err, ids) => { ], (err, ids) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@ -339,8 +327,12 @@ describe('transports', () => {
}) })
it('create two libp2p nodes with those peers', (done) => { it('create two libp2p nodes with those peers', (done) => {
node1 = new Node(peer1, null, { wsStar: true }) node1 = new Node({
node2 = new Node(peer2, null, { wsStar: true }) peerInfo: peer1
})
node2 = new Node({
peerInfo: peer2
})
done() done()
}) })
@ -355,28 +347,19 @@ describe('transports', () => {
node2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) node2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
}) })
it('dial from the second node to the first node', (done) => { it('.dialProtocol from the second node to the first node', (done) => {
node1.dial(peer2, '/echo/1.0.0', (err, conn) => { node1.dialProtocol(peer2, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
setTimeout(check, 500) setTimeout(check, 500)
function check () { function check () {
const text = 'hello'
const peers1 = node1.peerBook.getAll() const peers1 = node1.peerBook.getAll()
expect(Object.keys(peers1)).to.have.length(1) expect(Object.keys(peers1)).to.have.length(1)
const peers2 = node2.peerBook.getAll() const peers2 = node2.peerBook.getAll()
expect(Object.keys(peers2)).to.have.length(1) expect(Object.keys(peers2)).to.have.length(1)
pull( tryEcho(conn, done)
pull.values([Buffer.from(text)]),
conn,
pull.collect((err, data) => {
expect(err).to.not.exist()
expect(data[0].toString()).to.equal(text)
done()
})
)
} }
}) })
}) })
@ -389,19 +372,21 @@ describe('transports', () => {
function check () { function check () {
const peers = node1.peerBook.getAll() const peers = node1.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(node1.swarm.muxedConns)).to.have.length(0) expect(Object.keys(node1._switch.muxedConns)).to.have.length(0)
done() done()
} }
}) })
}) })
it('create a third node and check that discovery works', (done) => { it('create a third node and check that discovery works', function (done) {
this.timeout(10 * 1000)
let counter = 0 let counter = 0
function check () { function check () {
if (++counter === 3) { if (++counter === 3) {
expect(Object.keys(node1.swarm.muxedConns).length).to.equal(1) expect(Object.keys(node1._switch.muxedConns).length).to.equal(1)
expect(Object.keys(node2.swarm.muxedConns).length).to.equal(1) expect(Object.keys(node2._switch.muxedConns).length).to.equal(1)
done() done()
} }
} }
@ -416,7 +401,9 @@ describe('transports', () => {
node1.on('peer:discovery', (peerInfo) => node1.dial(peerInfo, check)) node1.on('peer:discovery', (peerInfo) => node1.dial(peerInfo, check))
node2.on('peer:discovery', (peerInfo) => node2.dial(peerInfo, check)) node2.on('peer:discovery', (peerInfo) => node2.dial(peerInfo, check))
const node3 = new Node(peer3, null, { wsStar: true }) const node3 = new Node({
peerInfo: peer3
})
node3.start(check) node3.start(check)
}) })
}) })

View File

@ -6,16 +6,17 @@ chai.use(require('dirty-chai'))
const expect = chai.expect const expect = chai.expect
const parallel = require('async/parallel') const parallel = require('async/parallel')
const series = require('async/series') const series = require('async/series')
const pull = require('pull-stream')
const utils = require('./utils/node.js')
const signalling = require('libp2p-webrtc-star/src/sig-server') const signalling = require('libp2p-webrtc-star/src/sig-server')
const rendezvous = require('libp2p-websocket-star-rendezvous') const rendezvous = require('libp2p-websocket-star-rendezvous')
const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets')
const WSStar = require('libp2p-websocket-star') const WSStar = require('libp2p-websocket-star')
const WRTCStar = require('libp2p-webrtc-star') const WRTCStar = require('libp2p-webrtc-star')
const wrtc = require('wrtc') const wrtc = require('wrtc')
const createNode = utils.createNode const createNode = require('./utils/create-node.js')
const echo = utils.echo const tryEcho = require('./utils/try-echo')
const echo = require('./utils/echo')
describe('transports', () => { describe('transports', () => {
describe('TCP only', () => { describe('TCP only', () => {
@ -73,18 +74,10 @@ describe('transports', () => {
}) })
it('nodeA.dial nodeB using PeerInfo', (done) => { it('nodeA.dial nodeB using PeerInfo', (done) => {
nodeA.dial(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => { nodeA.dialProtocol(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist() expect(err).to.not.exist()
pull( tryEcho(conn, done)
pull.values([Buffer.from('hey')]),
conn,
pull.collect((err, data) => {
expect(err).to.not.exist()
expect(data).to.be.eql([Buffer.from('hey')])
done()
})
)
}) })
}) })
@ -98,14 +91,14 @@ describe('transports', () => {
(cb) => { (cb) => {
const peers = nodeA.peerBook.getAll() const peers = nodeA.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeA._switch.muxedConns)).to.have.length(0)
cb() cb()
}, },
(cb) => { (cb) => {
const peers = nodeB.peerBook.getAll() const peers = nodeB.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeB._switch.muxedConns)).to.have.length(0)
cb() cb()
} }
], done) ], done)
@ -113,8 +106,8 @@ describe('transports', () => {
}) })
}) })
it('nodeA.dial nodeB using multiaddr', (done) => { it('nodeA.dialProtocol nodeB using multiaddr', (done) => {
nodeA.dial(nodeB.peerInfo.multiaddrs.toArray()[0], '/echo/1.0.0', (err, conn) => { nodeA.dialProtocol(nodeB.peerInfo.multiaddrs.toArray()[0], '/echo/1.0.0', (err, conn) => {
// Some time for Identify to finish // Some time for Identify to finish
setTimeout(check, 500) setTimeout(check, 500)
@ -125,27 +118,17 @@ describe('transports', () => {
const peers = nodeA.peerBook.getAll() const peers = nodeA.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1) expect(Object.keys(nodeA._switch.muxedConns)).to.have.length(1)
cb() cb()
}, },
(cb) => { (cb) => {
const peers = nodeB.peerBook.getAll() const peers = nodeB.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1) expect(Object.keys(nodeA._switch.muxedConns)).to.have.length(1)
cb() cb()
} }
], () => { ], () => tryEcho(conn, done))
pull(
pull.values([Buffer.from('hey')]),
conn,
pull.collect((err, data) => {
expect(err).to.not.exist()
expect(data).to.be.eql([Buffer.from('hey')])
done()
})
)
})
} }
}) })
}) })
@ -161,14 +144,14 @@ describe('transports', () => {
const peers = nodeA.peerBook.getAll() const peers = nodeA.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeA._switch.muxedConns)).to.have.length(0)
cb() cb()
}, },
(cb) => { (cb) => {
const peers = nodeB.peerBook.getAll() const peers = nodeB.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeB._switch.muxedConns)).to.have.length(0)
cb() cb()
} }
], done) ], done)
@ -176,8 +159,8 @@ describe('transports', () => {
}) })
}) })
it('nodeA.dial nodeB using PeerId', (done) => { it('nodeA.dialProtocol nodeB using PeerId', (done) => {
nodeA.dial(nodeB.peerInfo.id, '/echo/1.0.0', (err, conn) => { nodeA.dialProtocol(nodeB.peerInfo.id, '/echo/1.0.0', (err, conn) => {
// Some time for Identify to finish // Some time for Identify to finish
setTimeout(check, 500) setTimeout(check, 500)
@ -187,26 +170,16 @@ describe('transports', () => {
(cb) => { (cb) => {
const peers = nodeA.peerBook.getAll() const peers = nodeA.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1) expect(Object.keys(nodeA._switch.muxedConns)).to.have.length(1)
cb() cb()
}, },
(cb) => { (cb) => {
const peers = nodeB.peerBook.getAll() const peers = nodeB.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1) expect(Object.keys(nodeA._switch.muxedConns)).to.have.length(1)
cb() cb()
} }
], () => { ], () => tryEcho(conn, done))
pull(
pull.values([Buffer.from('hey')]),
conn,
pull.collect((err, data) => {
expect(err).to.not.exist()
expect(data).to.eql([Buffer.from('hey')])
done()
})
)
})
} }
}) })
}) })
@ -221,13 +194,13 @@ describe('transports', () => {
(cb) => { (cb) => {
const peers = nodeA.peerBook.getAll() const peers = nodeA.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeA._switch.muxedConns)).to.have.length(0)
cb() cb()
}, },
(cb) => { (cb) => {
const peers = nodeB.peerBook.getAll() const peers = nodeB.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeB._switch.muxedConns)).to.have.length(0)
cb() cb()
} }
], done) ], done)
@ -291,13 +264,13 @@ describe('transports', () => {
(cb) => { (cb) => {
const peers = nodeTCP.peerBook.getAll() const peers = nodeTCP.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(1) expect(Object.keys(nodeTCP._switch.muxedConns)).to.have.length(1)
cb() cb()
}, },
(cb) => { (cb) => {
const peers = nodeTCPnWS.peerBook.getAll() const peers = nodeTCPnWS.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(1) expect(Object.keys(nodeTCPnWS._switch.muxedConns)).to.have.length(1)
cb() cb()
} }
], done) ], done)
@ -315,14 +288,14 @@ describe('transports', () => {
(cb) => { (cb) => {
const peers = nodeTCP.peerBook.getAll() const peers = nodeTCP.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeTCP._switch.muxedConns)).to.have.length(0)
cb() cb()
}, },
(cb) => { (cb) => {
const peers = nodeTCPnWS.peerBook.getAll() const peers = nodeTCPnWS.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeTCPnWS._switch.muxedConns)).to.have.length(0)
cb() cb()
} }
], done) ], done)
@ -342,13 +315,13 @@ describe('transports', () => {
(cb) => { (cb) => {
const peers = nodeTCPnWS.peerBook.getAll() const peers = nodeTCPnWS.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(2) expect(Object.keys(peers)).to.have.length(2)
expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(1) expect(Object.keys(nodeTCPnWS._switch.muxedConns)).to.have.length(1)
cb() cb()
}, },
(cb) => { (cb) => {
const peers = nodeWS.peerBook.getAll() const peers = nodeWS.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(1) expect(Object.keys(nodeWS._switch.muxedConns)).to.have.length(1)
cb() cb()
} }
], done) ], done)
@ -366,14 +339,14 @@ describe('transports', () => {
(cb) => { (cb) => {
const peers = nodeTCPnWS.peerBook.getAll() const peers = nodeTCPnWS.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(2) expect(Object.keys(peers)).to.have.length(2)
expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeTCPnWS._switch.muxedConns)).to.have.length(0)
cb() cb()
}, },
(cb) => { (cb) => {
const peers = nodeWS.peerBook.getAll() const peers = nodeWS.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeWS._switch.muxedConns)).to.have.length(0)
cb() cb()
} }
], done) ], done)
@ -395,7 +368,7 @@ describe('transports', () => {
let nodeAll let nodeAll
let nodeTCP let nodeTCP
let nodeWS let nodeWS
let nodeWStar let nodeWebRTCStar
let ss let ss
@ -403,23 +376,33 @@ describe('transports', () => {
this.timeout(5 * 1000) this.timeout(5 * 1000)
parallel([ parallel([
(cb) => { (cb) => signalling.start({ port: 24642 }, (err, server) => {
signalling.start({ port: 24642 }, (err, server) => { expect(err).to.not.exist()
expect(err).to.not.exist() ss = server
ss = server cb()
cb() }),
})
},
(cb) => { (cb) => {
const wstar = new WRTCStar({wrtc: wrtc}) const wstar = new WRTCStar({wrtc: wrtc})
createNode([ createNode([
'/ip4/0.0.0.0/tcp/0', '/ip4/0.0.0.0/tcp/0',
'/ip4/127.0.0.1/tcp/25011/ws', '/ip4/127.0.0.1/tcp/25011/ws',
'/ip4/127.0.0.1/tcp/24642/ws/p2p-webrtc-star' '/ip4/127.0.0.1/tcp/24642/ws/p2p-webrtc-star'
], { ], {
modules: { modules: {
transport: [wstar], transport: [
discovery: [wstar.discovery] TCP,
WS,
wstar
],
peerDiscovery: [wstar.discovery]
},
config: {
peerDiscovery: {
[wstar.discovery.tag]: {
enabled: true
}
}
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@ -453,11 +436,18 @@ describe('transports', () => {
], { ], {
modules: { modules: {
transport: [wstar], transport: [wstar],
discovery: [wstar.discovery] peerDiscovery: [wstar.discovery]
},
config: {
peerDiscovery: {
[wstar.discovery.tag]: {
enabled: true
}
}
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeWStar = node nodeWebRTCStar = node
node.handle('/echo/1.0.0', echo) node.handle('/echo/1.0.0', echo)
node.start(cb) node.start(cb)
}) })
@ -465,12 +455,14 @@ describe('transports', () => {
], done) ], done)
}) })
after((done) => { after(function (done) {
this.timeout(10 * 1000)
parallel([ parallel([
(cb) => nodeAll.stop(cb), (cb) => nodeAll.stop(cb),
(cb) => nodeTCP.stop(cb), (cb) => nodeTCP.stop(cb),
(cb) => nodeWS.stop(cb), (cb) => nodeWS.stop(cb),
(cb) => nodeWStar.stop(cb), (cb) => nodeWebRTCStar.stop(cb),
(cb) => ss.stop(cb) (cb) => ss.stop(cb)
], done) ], done)
}) })
@ -479,7 +471,7 @@ describe('transports', () => {
let i = 1; let i = 1;
[nodeAll, otherNode].forEach((node) => { [nodeAll, otherNode].forEach((node) => {
expect(Object.keys(node.peerBook.getAll())).to.have.length(i-- ? peers : 1) expect(Object.keys(node.peerBook.getAll())).to.have.length(i-- ? peers : 1)
expect(Object.keys(node.swarm.muxedConns)).to.have.length(muxed) expect(Object.keys(node._switch.muxedConns)).to.have.length(muxed)
}) })
callback() callback()
} }
@ -516,20 +508,20 @@ describe('transports', () => {
}) })
}) })
it('nodeAll.dial nodeWStar using PeerInfo', function (done) { it('nodeAll.dial nodeWebRTCStar using PeerInfo', function (done) {
this.timeout(40 * 1000) this.timeout(40 * 1000)
nodeAll.dial(nodeWStar.peerInfo, (err) => { nodeAll.dial(nodeWebRTCStar.peerInfo, (err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
// Some time for Identify to finish // Some time for Identify to finish
setTimeout(() => check(nodeWStar, 1, 3, done), 500) setTimeout(() => check(nodeWebRTCStar, 1, 3, done), 500)
}) })
}) })
it('nodeAll.hangUp nodeWStar using PeerInfo', (done) => { it('nodeAll.hangUp nodeWebRTCStar using PeerInfo', (done) => {
nodeAll.hangUp(nodeWStar.peerInfo, (err) => { nodeAll.hangUp(nodeWebRTCStar.peerInfo, (err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
setTimeout(() => check(nodeWStar, 0, 3, done), 500) setTimeout(() => check(nodeWebRTCStar, 0, 3, done), 500)
}) })
}) })
}) })
@ -538,7 +530,7 @@ describe('transports', () => {
let nodeAll let nodeAll
let nodeTCP let nodeTCP
let nodeWS let nodeWS
let nodeWStar let nodeWebSocketStar
let ss let ss
@ -553,14 +545,26 @@ describe('transports', () => {
}, },
(cb) => { (cb) => {
const wstar = new WSStar() const wstar = new WSStar()
createNode([ createNode([
'/ip4/0.0.0.0/tcp/0', '/ip4/0.0.0.0/tcp/0',
'/ip4/127.0.0.1/tcp/25011/ws', '/ip4/127.0.0.1/tcp/25011/ws',
'/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star' '/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star'
], { ], {
modules: { modules: {
transport: [wstar], transport: [
discovery: [wstar.discovery] TCP,
WS,
wstar
],
peerDiscovery: [wstar.discovery]
},
config: {
peerDiscovery: {
[wstar.discovery.tag]: {
enabled: true
}
}
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@ -595,11 +599,18 @@ describe('transports', () => {
], { ], {
modules: { modules: {
transport: [wstar], transport: [wstar],
discovery: [wstar.discovery] peerDiscovery: [wstar.discovery]
},
config: {
peerDiscovery: {
[wstar.discovery.tag]: {
enabled: true
}
}
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeWStar = node nodeWebSocketStar = node
wstar.lazySetId(node.peerInfo.id) wstar.lazySetId(node.peerInfo.id)
node.handle('/echo/1.0.0', echo) node.handle('/echo/1.0.0', echo)
node.start(cb) node.start(cb)
@ -613,7 +624,7 @@ describe('transports', () => {
(cb) => nodeAll.stop(cb), (cb) => nodeAll.stop(cb),
(cb) => nodeTCP.stop(cb), (cb) => nodeTCP.stop(cb),
(cb) => nodeWS.stop(cb), (cb) => nodeWS.stop(cb),
(cb) => nodeWStar.stop(cb), (cb) => nodeWebSocketStar.stop(cb),
(cb) => ss.stop(cb) (cb) => ss.stop(cb)
], done) ], done)
}) })
@ -622,7 +633,7 @@ describe('transports', () => {
let i = 1; let i = 1;
[nodeAll, otherNode].forEach((node) => { [nodeAll, otherNode].forEach((node) => {
expect(Object.keys(node.peerBook.getAll())).to.have.length(i-- ? peers : 1) expect(Object.keys(node.peerBook.getAll())).to.have.length(i-- ? peers : 1)
expect(Object.keys(node.swarm.muxedConns)).to.have.length(muxed) expect(Object.keys(node._switch.muxedConns)).to.have.length(muxed)
}) })
done() done()
} }
@ -659,19 +670,19 @@ describe('transports', () => {
}) })
}) })
it('nodeAll.dial nodeWStar using PeerInfo', (done) => { it('nodeAll.dial nodeWebSocketStar using PeerInfo', (done) => {
nodeAll.dial(nodeWStar.peerInfo, (err) => { nodeAll.dial(nodeWebSocketStar.peerInfo, (err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
// Some time for Identify to finish // Some time for Identify to finish
setTimeout(() => check(nodeWStar, 1, 3, done), 500) setTimeout(() => check(nodeWebSocketStar, 1, 3, done), 500)
}) })
}) })
it('nodeAll.hangUp nodeWStar using PeerInfo', (done) => { it('nodeAll.hangUp nodeWebSocketStar using PeerInfo', (done) => {
nodeAll.hangUp(nodeWStar.peerInfo, (err) => { nodeAll.hangUp(nodeWebSocketStar.peerInfo, (err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
// Some time for Identify to finish // Some time for Identify to finish
setTimeout(() => check(nodeWStar, 0, 3, done), 500) setTimeout(() => check(nodeWebSocketStar, 0, 3, done), 500)
}) })
}) })
}) })

View File

@ -76,7 +76,7 @@ describe('Turbolence tests', () => {
function check () { function check () {
const peers = nodeA.peerBook.getAll() const peers = nodeA.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1) expect(Object.keys(peers)).to.have.length(1)
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0) expect(Object.keys(nodeA.switch.muxedConns)).to.have.length(0)
done() done()
} }
}) })

View File

@ -0,0 +1,95 @@
'use strict'
const WS = require('libp2p-websockets')
const WebRTCStar = require('libp2p-webrtc-star')
const WebSocketStar = require('libp2p-websocket-star')
const Bootstrap = require('libp2p-railing')
const SPDY = require('libp2p-spdy')
const MPLEX = require('libp2p-mplex')
const KadDHT = require('libp2p-kad-dht')
const SECIO = require('libp2p-secio')
const defaultsDeep = require('@nodeutils/defaults-deep')
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 'mplex': return MPLEX
default:
throw new Error(pref + ' muxer not available')
}
})
}
function getMuxers (options) {
if (options) {
return mapMuxers(options)
} else {
return [MPLEX, SPDY]
}
}
class Node extends libp2p {
constructor (_options) {
_options = _options || {}
const starOpts = { id: _options.peerInfo.id }
const wrtcStar = new WebRTCStar(starOpts)
const wsStar = new WebSocketStar(starOpts)
const defaults = {
modules: {
transport: [
wrtcStar,
wsStar,
new WS()
],
streamMuxer: getMuxers(_options.muxer),
connEncryption: [
SECIO
],
peerDiscovery: [
wrtcStar.discovery,
wsStar.discovery,
Bootstrap
],
dht: KadDHT
},
config: {
peerDiscovery: {
webRTCStar: {
enabled: true
},
websocketStar: {
enabled: true
},
bootstrap: {
interval: 10000,
enabled: false,
list: _options.boostrapList
}
},
relay: {
enabled: false,
hop: {
enabled: false,
active: false
}
},
dht: {
kBucketSize: 20
},
EXPERIMENTAL: {
dht: false,
pubsub: false
}
}
}
super(defaultsDeep(_options, defaults))
}
}
module.exports = Node

View File

@ -0,0 +1,88 @@
'use strict'
const TCP = require('libp2p-tcp')
const MulticastDNS = require('libp2p-mdns')
const WS = require('libp2p-websockets')
const Bootstrap = require('libp2p-railing')
const SPDY = require('libp2p-spdy')
const KadDHT = require('libp2p-kad-dht')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const defaultsDeep = require('@nodeutils/defaults-deep')
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 'mplex': return MPLEX
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 [MPLEX, SPDY]
}
}
class Node extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [
TCP,
WS
],
streamMuxer: getMuxers(_options.muxer),
connEncryption: [
SECIO
],
peerDiscovery: [
MulticastDNS,
Bootstrap
],
dht: KadDHT
},
config: {
peerDiscovery: {
mdns: {
interval: 10000,
enabled: false
},
bootstrap: {
interval: 10000,
enabled: false,
list: _options.bootstrapList
}
},
relay: {
enabled: false,
hop: {
enabled: false,
active: false
}
},
dht: {
kBucketSize: 20
},
EXPERIMENTAL: {
dht: false,
pubsub: false
}
}
}
super(defaultsDeep(_options, defaults))
}
}
module.exports = Node

View File

@ -1,74 +0,0 @@
'use strict'
const WS = require('libp2p-websockets')
const WebRTCStar = require('libp2p-webrtc-star')
const WebSocketStar = require('libp2p-websocket-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 wrtcStar = new WebRTCStar({ id: peerInfo.id })
const wsStar = new WebSocketStar({ id: peerInfo.id })
const modules = {
transport: [
new WS(),
wrtcStar,
wsStar
],
connection: {
muxer: getMuxers(options.muxer),
crypto: [
secio
]
},
discovery: []
}
if (options.webRTCStar) {
modules.discovery.push(wrtcStar.discovery)
}
if (options.wsStar) {
modules.discovery.push(wsStar.discovery)
}
if (options.bootstrap) {
const r = new Railing(options.bootstrap)
modules.discovery.push(r)
}
super(modules, peerInfo, peerBook, options)
}
}
module.exports = Node

View File

@ -1,80 +0,0 @@
'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

View File

@ -3,11 +3,10 @@
const chai = require('chai') const chai = require('chai')
chai.use(require('dirty-chai')) chai.use(require('dirty-chai'))
const Node = require('./bundle.node')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const PeerId = require('peer-id') const PeerId = require('peer-id')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const pull = require('pull-stream') const Node = require('./bundle-nodejs')
function createNode (multiaddrs, options, callback) { function createNode (multiaddrs, options, callback) {
if (typeof options === 'function') { if (typeof options === 'function') {
@ -22,24 +21,14 @@ function createNode (multiaddrs, options, callback) {
} }
waterfall([ waterfall([
(cb) => PeerId.create({ bits: 1024 }, cb), (cb) => PeerId.create({ bits: 512 }, cb),
(peerId, cb) => PeerInfo.create(peerId, cb), (peerId, cb) => PeerInfo.create(peerId, cb),
(peerInfo, cb) => { (peerInfo, cb) => {
multiaddrs.map((ma) => peerInfo.multiaddrs.add(ma)) multiaddrs.map((ma) => peerInfo.multiaddrs.add(ma))
cb(null, peerInfo) options.peerInfo = peerInfo
}, cb(null, new Node(options))
(peerInfo, cb) => {
const node = new Node(peerInfo, undefined, options)
cb(null, node)
} }
], callback) ], callback)
} }
function echo (protocol, conn) { module.exports = createNode
pull(conn, conn)
}
module.exports = {
createNode: createNode,
echo: echo
}

10
test/utils/echo.js Normal file
View File

@ -0,0 +1,10 @@
/* eslint-env mocha */
'use strict'
const pull = require('pull-stream')
function echo (protocol, conn) {
pull(conn, conn)
}
module.exports = echo

21
test/utils/try-echo.js Normal file
View File

@ -0,0 +1,21 @@
'use strict'
const pull = require('pull-stream')
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
module.exports = (conn, callback) => {
const values = [Buffer.from('echo')]
pull(
pull.values(values),
conn,
pull.collect((err, _values) => {
expect(err).to.not.exist()
expect(_values).to.eql(values)
callback()
})
)
}