Compare commits

...

229 Commits

Author SHA1 Message Date
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
c128873a07 chore: release version v0.14.2 2017-12-15 07:15:05 +00:00
5e2b6df04a chore: update contributors 2017-12-15 07:15:05 +00:00
8d2d8c98a0 chore: release version v0.14.1 2017-12-15 07:10:42 +00:00
30bf5bb161 chore: update contributors 2017-12-15 07:10:42 +00:00
c88eaf416c fix: prevent "The libp2p node is not started yet" when stopping (#138) 2017-12-15 07:08:44 +00:00
c7f20383d9 chore: release version v0.14.0 2017-12-14 07:39:37 +00:00
a86a95c19f chore: update contributors 2017-12-14 07:39:37 +00:00
1b7360fcfa fix: remove innactive multiaddrs (#131) 2017-12-14 07:27:13 +00:00
67ca37c356 chore: updating CI files (#132)
* Updating CI files

This commit updates all CI scripts to the latest version

* chore: update deps

* bump timout

* fix circle
2017-12-14 07:14:36 +00:00
0870068ebc docs: fix typo (#136) 2017-12-11 12:16:05 +00:00
8563395d32 chore: release version v0.13.3 2017-12-01 09:32:49 +00:00
ef3cae5b5a chore: update contributors 2017-12-01 09:32:49 +00:00
f602cd9f79 test: use that crypto puzzle! (#135) 2017-12-01 09:19:44 +00:00
ec956c1d42 chore: release version v0.13.2 2017-11-27 09:13:13 +00:00
c906f698e4 chore: update contributors 2017-11-27 09:13:12 +00:00
95f029eb7a feat: Bring libp2p-websocket-star to the Transports family! 🌟 (#122)
* test: add libp2p-websocket-star tests

* refactor: websocket-star-{signal => rendezvous}

* misc: Apply requested changes

* chore: update deps

* refactor: considerable amount of refactor, clean up test structure

* use right flag

* test: refactor transport tests
2017-11-27 09:10:36 +00:00
38a68a01b7 chore: Upgrade wrtc to 0.0.63 (#133)
For testing on CIs
2017-11-22 16:19:27 +00:00
c08aa517a0 chore: release version v0.13.1 2017-11-12 10:53:37 +00:00
8eac0f806e chore: update contributors 2017-11-12 10:53:37 +00:00
9fd092fddf chore: update deps 2017-11-12 10:51:13 +00:00
87ddc25bee chore: update example 2017-11-04 16:44:09 +00:00
efb2b67f19 chore: update example 2017-11-04 16:25:58 +00:00
7c0b4daf49 chore: update deps on browser example 2017-11-04 16:21:11 +00:00
aeb3ae1301 chore: update deps 2017-11-04 16:20:28 +00:00
97df76f9c5 chore: gitignore 2017-10-26 13:03:40 +01:00
3ad696445d chore: release version v0.13.0 2017-10-26 13:02:16 +01:00
30bec8d53b chore: update contributors 2017-10-26 13:02:16 +01:00
db2b5300b4 chore: update deps 2017-10-26 12:57:33 +01:00
df4d60df88 docs(examples): update browser /wss bootstrap list (#129) 2017-10-26 12:52:01 +01:00
29cc0afc64 feat: enable and test Circuit Relay
* feat: new super simplified API

* feat: append peer id to multiaddr if not there

* [WIP] Awesome DHT (#86)

* feat: integrate dht

* better interfaces

* docs: add documentation for peerRouting, contentRouting, dht

* fix: take in passed datastore

* fix: update usage of _getPeerInfo

* fix: getPeerInfo

* docs: update docs

* moar

feat: correctly handle p2p-circuit addrs when creating a peer info object

refactor: rework config options

* feat: adding circuit relaying

* feat: rework circuit relay for protobufs

* feat: circuit loading and tests

* fix: clean up _getPeerInfo to work with /p2p-circuit

* wip: tests cleaup

* test: clean up

* wip

* fix: bringing back test reworks and new aegir

* test: group tests

* test: clean up

* test: adjust test

* fix: use getPeerId to determine if the ipfs fragment is missing

* feat: adding circuit relaying

* feat: circuit loading and tests

* test: clean up

* wip

* feat: upgrade to latest aegir

* fix: removing unused tests

* feat: cleanup tests

* fix: create node defautl options

* chore: upgrade swarm to latest version

* fix: updated aegir and adjust timeouts

* feat: more timeouts

* chore: updating deps

* fix: circle ci builds

* test: timeouts
2017-10-26 12:51:36 +01:00
2504cbeb26 docs: add missing sections to the README (#128) 2017-10-22 10:27:36 +01:00
468cc421fa docs: update README.md (#124) 2017-10-22 10:19:50 +01:00
0b991e19d1 docs(examples): fix peer-content routing example scripts (#126). (#127) 2017-10-22 10:19:16 +01:00
2a81fc75a8 chore: update deps 2017-09-10 05:20:56 +01:00
e04c249d4d chore: update deps 2017-09-10 04:52:07 +01:00
4a995613ae chore: release version v0.12.4 2017-09-07 17:17:38 +01:00
066c4976bb chore: update contributors 2017-09-07 17:17:38 +01:00
429cf9c361 chore: update deps 2017-09-07 17:15:08 +01:00
01c20f8ab9 chore: release version v0.12.3 2017-09-07 11:18:58 +01:00
c8adce3906 chore: update contributors 2017-09-07 11:18:57 +01:00
6e3c5427de chore: update deps 2017-09-07 11:17:39 +01:00
3e140d2c47 chore: release version v0.12.2 2017-09-07 10:54:49 +01:00
f0858c20bd chore: update contributors 2017-09-07 10:54:48 +01:00
ef9c2a6296 chore: release version v0.12.1 2017-09-07 10:53:25 +01:00
090d75d383 chore: update contributors 2017-09-07 10:53:25 +01:00
04a4ffcc36 chore: update deps 2017-09-07 10:53:25 +01:00
7f7278fe82 docs: fix typo in readme (#121) 2017-09-06 09:55:12 +01:00
2c03222136 chore: release version v0.12.0 2017-09-03 14:43:35 +01:00
65857838f9 chore: update contributors 2017-09-03 14:43:34 +01:00
cad173e3bd feat: p2p addrs situation (#119)
* chore: update gitignore and CI

* chore: update deps

* test: update tests to use p2p-webrtc-star
2017-09-03 14:39:55 +01:00
19ce266f1b chore: update deps and readme of browser example 2017-08-24 10:58:06 +01:00
a2a85eb8d6 chore: update deps 2017-08-24 10:53:05 +01:00
b674bccfc2 chane three links~😉 (#115) 2017-07-31 02:35:21 -07:00
4ae8e43b8b Fix transport example (#114)
* fix the readme of transport-example-1

* change wrong node.isOn to node.isStarted()
2017-07-31 02:35:08 -07:00
6b059a0ba8 docs: fix link to contribution guidelines (#112) 2017-07-24 01:53:06 -07:00
363259f832 docs: add project status and throughput graph 2017-07-23 11:20:18 -07:00
56dbd9e7c2 docs: add issue template 2017-07-23 11:18:44 -07:00
314b45897f chore: release version v0.11.0 2017-07-22 15:21:07 -07:00
0a6f1faf82 chore: update contributors 2017-07-22 15:21:07 -07:00
5a638794c5 chore: update deps - no moar webcrypto-ossl 2017-07-22 15:17:41 -07:00
d3b0f380bb chore: release version v0.10.2 2017-07-21 10:26:13 -07:00
c1ccc1d5fb chore: update contributors 2017-07-21 10:26:12 -07:00
49484f734c chore: update deps 2017-07-21 10:12:19 -07:00
43345cee6d docs(examples): PubSub 1 and 2 (#103) 2017-07-20 19:42:32 -07:00
06eb7a19f3 docs(examples): Peer and Content Routing 1 and 2 (#107) 2017-07-20 14:19:36 -07:00
76ef5fdf50 chore: no more Sauce 2017-07-20 11:28:31 -07:00
c8f4eaf982 chore: update deps 2017-07-20 11:18:34 -07:00
abe5c8c171 test: fix travis 2017-07-12 07:37:25 +01:00
a01221f89c Update CODE_OF_CONDUCT.md 2017-07-12 07:04:29 +01:00
4224c1fe61 fix: circle ci, thanks victor! 2017-07-11 13:51:07 +01:00
019cd4715d docs(examples): fix dial function name and multiaddr variable name in echo and chat dialer/listener examples (#109) 2017-07-11 07:39:57 +01:00
f877609a58 chore: release version v0.10.1 2017-07-10 10:52:44 +01:00
4a7eb34056 chore: update contributors 2017-07-10 10:52:43 +01:00
fb0c64f9d4 Merge pull request #105 from libp2p/docs/libp2p-browser-example
docs(examples): libp2p in the browser 1
2017-07-09 16:53:58 +01:00
7ef1c0a598 docs(examples): libp2p in the browser 1 2017-07-09 16:52:37 +01:00
c23f147bda docs: fix API docs 2017-07-09 12:18:05 +01:00
c1a471793d Merge pull request #102 from libp2p/docs/discovery-example
docs(examples): Peer Discovery Mechanisms 1, 2 and 3
2017-07-09 11:14:19 +01:00
39145e9a91 docs(examples): Peer Discovery Mechanisms 1, 2 and 3 2017-07-09 11:08:06 +01:00
74b6f5f793 chore: update deps 2017-07-08 22:17:14 +01:00
a4e5247ca2 Create CONTRIBUTING.md 2017-07-07 22:41:04 +01:00
0349119536 docs: Merge pull request #101 from libp2p/docs/add-code-of-conduct-1
Create CODE_OF_CONDUCT.md
2017-07-07 22:37:11 +01:00
5eb97a66df Create CODE_OF_CONDUCT.md 2017-07-07 22:36:57 +01:00
8da166e2a9 Merge pull request #99 from libp2p/docs/stellar-examples
docs: examples - transports, crypto channels, protocol and stream muxing
2017-07-07 21:42:36 +01:00
f583b1b2cd docs(examples): encrypted communicationsls 2017-07-07 20:28:11 +01:00
5f5ee98a50 docs(examples): protocols and stream muxing 1, 2 and 3 2017-07-07 18:46:35 +01:00
7df0074667 docs: add disclaimers for each example 2017-07-07 16:02:32 +01:00
b14a2ff717 docs(examples): transports 4 2017-07-07 15:49:14 +01:00
7b78736e24 docs(examples): transports 3 2017-07-07 15:44:06 +01:00
2b02be08f7 docs(examples): transports 2 2017-07-07 14:37:41 +01:00
32cb59fa18 docs(examples): transports 1 2017-07-07 14:37:41 +01:00
53cce8d8f6 docs: list examples 2017-07-07 14:37:16 +01:00
b6e41922a7 chore: release version v0.10.0 2017-07-07 13:27:04 +01:00
195673c2d3 chore: update contributors 2017-07-07 13:27:04 +01:00
647d10fe10 chore: update deps 2017-07-07 13:25:34 +01:00
73f2f6d050 feat: state events and query changes (#100) 2017-07-07 12:56:46 +01:00
2d40f3ad6a docs: update header of the README (#97) 2017-07-05 18:18:31 +01:00
13452c959e docs: rm bundles from table 2017-07-05 18:08:10 +01:00
547c4dbd33 docs: refactor examples (#96) 2017-07-05 12:12:51 +01:00
606fa737b8 WIP - feat: convergence (#94)
* feat: convergence

* structure examples
2017-07-04 11:43:45 +01:00
e3870f3f8a Merge pull request #90 from libp2p/fix/missing-dep-async
fix: added missing dep async
2017-06-28 19:29:44 +01:00
45b0f61824 fix: added missing dep async 2017-06-17 14:29:19 +01:00
0263b899e3 chore: release version v0.9.1 2017-04-16 16:54:49 +01:00
fb92e672ee chore: update contributors 2017-04-16 16:54:49 +01:00
2e326e1619 fix: do not use assert in async funcs (#88) 2017-04-16 16:54:31 +01:00
e4e9761c99 Fix docs 2017-04-16 11:56:34 +01:00
a5b7de1b6b chore: release version v0.9.0 2017-04-06 15:50:57 -04:00
9a7a381f85 chore: update contributors 2017-04-06 15:50:56 -04:00
1f00855e6e chore: update deps 2017-04-06 15:46:13 -04:00
8aa932a491 [WIP] Awesome DHT (#86)
* feat: integrate dht

* better interfaces

* docs: add documentation for peerRouting, contentRouting, dht

* fix: take in passed datastore

* fix: update usage of _getPeerInfo

* fix: getPeerInfo

* docs: update docs

* moar
2017-04-06 15:45:23 -04:00
111 changed files with 7133 additions and 367 deletions

78
.aegir.js Normal file
View File

@ -0,0 +1,78 @@
'use strict'
const PeerInfo = require('peer-info')
const PeerId = require('peer-id')
const pull = require('pull-stream')
const parallel = require('async/parallel')
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 wsRendezvous
let node
const before = (done) => {
parallel([
(cb) => {
sigServer.start({
port: 15555
// cryptoChallenge: true TODO: needs https://github.com/libp2p/js-libp2p-webrtc-star/issues/128
}, (err, server) => {
if (err) {
return cb(err)
}
wrtcRendezvous = server
cb()
})
},
(cb) => {
WebSocketStarRendezvous.start({
port: 14444,
refreshPeerListIntervalMS: 1000,
strictMultiaddr: false,
cryptoChallenge: true
}, (err, _server) => {
if (err) {
return cb(err)
}
wsRendezvous = _server
cb()
})
},
(cb) => {
PeerId.createFromJSON(rawPeer, (err, peerId) => {
if (err) {
return done(err)
}
const peer = new PeerInfo(peerId)
peer.multiaddrs.add('/ip4/127.0.0.1/tcp/9200/ws')
node = new Node({
peerInfo: peer
})
node.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
node.start(cb)
})
}
], done)
}
const after = (done) => {
setTimeout(() =>
parallel(
[node, wrtcRendezvous, wsRendezvous].map((s) => (cb) => s.stop(cb)),
done),
2000)
}
module.exports = {
hooks: {
pre: before,
post: after
}
}

26
.gitignore vendored
View File

@ -1,7 +1,14 @@
docs
**/node_modules/
**/*.log
test/repo-tests*
**/bundle.js
# Logs
logs
*.log
npm-debug.log*
coverage
# Runtime data
pids
@ -20,16 +27,17 @@ coverage
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
build
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
lib
dist
test/test-data/go-ipfs-repo/LOCK
test/test-data/go-ipfs-repo/LOG
test/test-data/go-ipfs-repo/LOG.old
# while testing npm5
package-lock.json

View File

@ -1,7 +1,16 @@
**/node_modules/
**/*.log
test/repo-tests*
img
docs
examples
# Logs
logs
*.log
coverage
# Runtime data
pids
*.pid
@ -16,13 +25,13 @@ coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# node-waf configuration
.lock-wscript
# Optional npm cache directory
.npm
build
# Optional REPL history
.node_repl_history
# Dependency directory
# 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,36 +0,0 @@
sudo: false
language: node_js
matrix:
include:
- node_js: 4
env: CXX=g++-4.8
- node_js: 6
env:
- SAUCE=true
- CXX=g++-4.8
- node_js: stable
env: CXX=g++-4.8
# Make sure we have new NPM.
before_install:
- npm install -g npm
script:
- npm run lint
- npm 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,300 @@
<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>
## [0.14.2](https://github.com/libp2p/js-libp2p/compare/v0.14.1...v0.14.2) (2017-12-15)
<a name="0.14.1"></a>
## [0.14.1](https://github.com/libp2p/js-libp2p/compare/v0.14.0...v0.14.1) (2017-12-15)
### Bug Fixes
* prevent "The libp2p node is not started yet" when stopping ([#138](https://github.com/libp2p/js-libp2p/issues/138)) ([c88eaf4](https://github.com/libp2p/js-libp2p/commit/c88eaf4))
<a name="0.14.0"></a>
# [0.14.0](https://github.com/libp2p/js-libp2p/compare/v0.13.3...v0.14.0) (2017-12-14)
### Bug Fixes
* remove innactive multiaddrs ([#131](https://github.com/libp2p/js-libp2p/issues/131)) ([1b7360f](https://github.com/libp2p/js-libp2p/commit/1b7360f))
<a name="0.13.3"></a>
## [0.13.3](https://github.com/libp2p/js-libp2p/compare/v0.13.2...v0.13.3) (2017-12-01)
<a name="0.13.2"></a>
## [0.13.2](https://github.com/libp2p/js-libp2p/compare/v0.13.1...v0.13.2) (2017-11-27)
### Features
* Bring libp2p-websocket-star to the Transports family! 🌟 ([#122](https://github.com/libp2p/js-libp2p/issues/122)) ([95f029e](https://github.com/libp2p/js-libp2p/commit/95f029e))
<a name="0.13.1"></a>
## [0.13.1](https://github.com/libp2p/js-libp2p/compare/v0.13.0...v0.13.1) (2017-11-12)
<a name="0.13.0"></a>
# [0.13.0](https://github.com/libp2p/js-libp2p/compare/v0.12.4...v0.13.0) (2017-10-26)
### Features
* enable and test Circuit Relay ([29cc0af](https://github.com/libp2p/js-libp2p/commit/29cc0af))
<a name="0.12.4"></a>
## [0.12.4](https://github.com/libp2p/js-libp2p/compare/v0.12.3...v0.12.4) (2017-09-07)
<a name="0.12.3"></a>
## [0.12.3](https://github.com/libp2p/js-libp2p/compare/v0.12.2...v0.12.3) (2017-09-07)
<a name="0.12.2"></a>
## [0.12.2](https://github.com/libp2p/js-libp2p/compare/v0.12.0...v0.12.2) (2017-09-07)
<a name="0.12.1"></a>
## [0.12.1](https://github.com/libp2p/js-libp2p/compare/v0.12.0...v0.12.1) (2017-09-07)
<a name="0.12.0"></a>
# [0.12.0](https://github.com/libp2p/js-libp2p/compare/v0.11.0...v0.12.0) (2017-09-03)
### Features
* p2p addrs situation ([#119](https://github.com/libp2p/js-libp2p/issues/119)) ([cad173e](https://github.com/libp2p/js-libp2p/commit/cad173e))
<a name="0.11.0"></a>
# [0.11.0](https://github.com/libp2p/js-libp2p/compare/v0.10.2...v0.11.0) (2017-07-22)
<a name="0.10.2"></a>
## [0.10.2](https://github.com/libp2p/js-libp2p/compare/v0.10.1...v0.10.2) (2017-07-21)
### Bug Fixes
* circle ci, thanks victor! ([4224c1f](https://github.com/libp2p/js-libp2p/commit/4224c1f))
<a name="0.10.1"></a>
## [0.10.1](https://github.com/libp2p/js-libp2p/compare/v0.10.0...v0.10.1) (2017-07-10)
<a name="0.10.0"></a>
# [0.10.0](https://github.com/libp2p/js-libp2p/compare/v0.9.1...v0.10.0) (2017-07-07)
### Bug Fixes
* added missing dep async ([45b0f61](https://github.com/libp2p/js-libp2p/commit/45b0f61))
### Features
* state events and query changes ([#100](https://github.com/libp2p/js-libp2p/issues/100)) ([73f2f6d](https://github.com/libp2p/js-libp2p/commit/73f2f6d))
<a name="0.9.1"></a>
## [0.9.1](https://github.com/libp2p/js-libp2p/compare/v0.9.0...v0.9.1) (2017-04-16)
### Bug Fixes
* do not use assert in async funcs ([#88](https://github.com/libp2p/js-libp2p/issues/88)) ([2e326e1](https://github.com/libp2p/js-libp2p/commit/2e326e1))
<a name="0.9.0"></a>
# [0.9.0](https://github.com/libp2p/js-libp2p/compare/v0.8.0...v0.9.0) (2017-04-06)
<a name="0.8.0"></a>
# [0.8.0](https://github.com/libp2p/js-libp2p/compare/v0.7.0...v0.8.0) (2017-03-31)

3
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,3 @@
# Contributor Code of Conduct
The `js-libp2p` project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md)

9
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,9 @@
# Contributing guidelines
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-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.
Thank you.

43
ISSUE_TEMPLATE.md Normal file
View File

@ -0,0 +1,43 @@
<!--
Thank you for reporting an issue.
This issue tracker is for bugs and issues found within the JavaScript implementation of libp2p.
If you require more general support please file an issue on our discuss forum. https://discuss.ipfs.io/
Please fill in as much of the template below as you're able.
Version: package.json version or the commit you have installed.
Platform: output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows). If using in a Browser, please share the browser version as well.
Subsystem: if known, please specify affected core module name (e.g Transports, SECIO, etc).
If possible, please provide code that demonstrates the problem, keeping it as
simple and free of external dependencies as you are able.
-->
- **Version**:
- **Platform**:
- **Subsystem**:
<!-- Bug, Feature, Question, Enhancement, Etc -->
#### Type:
<!--
One of following:
Critical - System crash, application panic.
High - The main functionality of the application does not work, API breakage, repo format breakage, etc.
Medium - A non-essential functionality does not work, performance issues, etc.
Low - An optional functionality does not work.
Very Low - Translation or documentation mistake. Something that won't give anyone a bad day.
-->
#### Severity:
#### Description:
#### Steps to reproduce the error:
<!--
This is for you! Please read, and then delete this text before posting it.
The js-ipfs issues are only for bug reports and directly actionable features.
Read https://github.com/ipfs/community/blob/master/contributing.md#reporting-issues if your issue doesn't fit either of those categories.
-->

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)

358
README.md
View File

@ -1,13 +1,45 @@
![](https://github.com/libp2p/js-libp2p/blob/fe2d097bfa072ef6733f2defb568bdc526052ee4/img/js-libp2p.png?raw=true)
<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>
</h1>
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
[![Build Status](https://travis-ci.org/libp2p/js-libp2p.svg?style=flat-square)](https://travis-ci.org/libp2p/js-libp2p)
![coverage](https://img.shields.io/badge/coverage-%3F-yellow.svg?style=flat-square)
[![Dependency Status](https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
<h3 align="center">The JavaScript implementation of the libp2p Networking Stack.</h3>
> libp2p is the networking stack of IPFS, a modular networking library to solve P2P application needs.
<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://libp2p.io/"><img src="https://img.shields.io/badge/project-libp2p-yellow.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-yellow.svg?style=flat-square" /></a>
</p>
<p align="center">
<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://coveralls.io/github/libp2p/js-libp2p?branch=master"><img src="https://coveralls.io/repos/github/libp2p/js-libp2p/badge.svg?branch=master"></a>
<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://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/RichardLitt/standard-readme"><img src="https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square" /></a>
<a href=""><img src="https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square" /></a>
<a href=""><img src="https://img.shields.io/badge/Node.js-%3E%3D6.0.0-orange.svg?style=flat-square" /></a>
<br>
</p>
### Project status
We've come a long way, but this project is still in Alpha, lots of development is happening, API might change, beware of the Dragons 🐉..
**Want to get started?** Check our [examples folder](/examples). You can check the development status at the [Waffle Board](https://waffle.io/libp2p/js-libp2p).
[![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
@ -15,6 +47,7 @@
- [Bundles](#bundles)
- [Usage](#usage)
- [Install](#install)
- [Usage](#usage)
- [API](#api)
- [Development](#development)
- [Tests](#tests)
@ -24,14 +57,14 @@
## Background
libp2p is the product of a long and arduous quest to understand the evolution of the Internet networking stack. In order to build P2P applications, dev have long had to made custom ad-hoc solutions to fit their needs, sometimes making some hard assumptions about their runtimes and the state of the network at the time of their development. Today, looking back more than 20 years, we see a clear pattern in the types of mechanisms built around the Internet Protocol, IP, which can be found throughout many layers of the OSI layer system, libp2p distils these mechanisms into flat categories and defines clear interfaces that once exposed, enable other protocols and applications to use and swap them, enabling upgradability and adaptability for the runtime, without breaking the API.
libp2p is the product of a long and arduous quest to understand the evolution of the Internet networking stack. In order to build P2P applications, devs have long had to made custom ad-hoc solutions to fit their needs, sometimes making some hard assumptions about their runtimes and the state of the network at the time of their development. Today, looking back more than 20 years, we see a clear pattern in the types of mechanisms built around the Internet Protocol, IP, which can be found throughout many layers of the OSI layer system, libp2p distils these mechanisms into flat categories and defines clear interfaces that once exposed, enable other protocols and applications to use and swap them, enabling upgradability and adaptability for the runtime, without breaking the API.
We are in the process of writting better documentation, blog posts, tutorials and a formal specification. Today you can find:
We are in the process of writing better documentation, blog posts, tutorials and a formal specification. Today you can find:
- [libp2p.io - The libp2p Website (WIP)](https://github.com/libp2p/website)
- [libp2p.io](https://libp2p.io)
- [Specification (WIP)](https://github.com/libp2p/specs)
- Talks
- [`libp2p <3 ethereum` at DEVCON2](https://ethereumfoundation.org/devcon/?session=libp2p) [video](https://www.youtube.com/watch?v=HxueJbeMVG4) [slides](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p-HEART-devp2p-IPFS-PLUS-Ethereum-networking.pdf) [demo-1](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p_demo1-1.mp4) [demo-2](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p_demo2-1.mp4)
- [`libp2p <3 ethereum` at DEVCON2](https://ethereumfoundation.org/devcon/?session=libp2p) [📼 video](https://www.youtube.com/watch?v=HxueJbeMVG4) [slides](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p-HEART-devp2p-IPFS-PLUS-Ethereum-networking.pdf) [📼 demo-1](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p_demo1-1.mp4) [📼 demo-2](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p_demo2-1.mp4)
- Articles
- [The overview of libp2p](https://github.com/libp2p/libp2p#description)
@ -41,8 +74,8 @@ To sum up, libp2p is a "network stack" -- a protocol suite -- that cleanly separ
With its modular nature, libp2p can be found being used in different projects with different sets of features, while preserving the same top level API. `js-libp2p` is only a skeleton and should not be installed directly, if you are looking for a prebundled libp2p stack, please check:
- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-libp2p-ipfs-nodejs) - The libp2p build used by js-ipfs when run in Node.js
- [libp2p-ipfs-browser](https://github.com/ipfs/js-libp2p-ipfs-browser) - The libp2p build used by js-ipfs when run in a Browser (that supports WebRTC)
- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-nodejs.js) - The libp2p build used by js-ipfs when run in Node.js
- [libp2p-ipfs-browser](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-browser.js) - The libp2p build used by js-ipfs when run in a Browser (that supports WebRTC)
If you have developed a libp2p bundle, please consider submitting it to this list so that it can be found easily by the users of libp2p.
@ -56,6 +89,10 @@ npm install --save libp2p
## Usage
### [Tutorials and Examples](/examples)
You can find multiple examples on the [examples folder](/examples) that will guide you through using libp2p for several scenarios.
### Extending libp2p skeleton
libp2p becomes very simple and basically acts as a glue for every module that compose this library. Since it can be highly customized, it requires some setup. What we recommend is to have a libp2p build for the system you are developing taking into account in your needs (e.g. for a browser working version of libp2p that acts as the network layer of IPFS, we have a built and minified version that browsers can require).
@ -65,44 +102,83 @@ libp2p becomes very simple and basically acts as a glue for every module that co
```JavaScript
// Creating a bundle that adds:
// transport: websockets + tcp
// stream-muxing: SPDY
// stream-muxing: spdy & mplex
// crypto-channel: secio
// discovery: multicast-dns
const libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets')
const spdy = require('libp2p-spdy')
const secio = require('libp2p-secio')
const SPDY = require('libp2p-spdy')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const MulticastDNS = require('libp2p-mdns')
const DHT = require('libp2p-kad-dht')
const defaultsDeep = require('@nodeutils/defaults-deep')
class Node extends libp2p {
constructor (peerInfo, peerBook, options) {
options = options || {}
constructor (_peerInfo, _peerBook, _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 = {
transport: [
new TCP(),
new WS()
],
connection: {
muxer: [
spdy
// The libp2p modules for this libp2p bundle
modules: {
transport: [
TCP,
new WS() // It can take instances too!
],
crypto: [
secio
streamMuxer: [
SPDY,
MPLEX
],
connEncryption: [
SECIO
]
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
},
// 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.
peerDiscovery: {
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
}
}
// Enable/Disable Experimental features
EXPERIMENTAL: { // Experimental features ("behind a flag")
pubsub: false,
dht: false
}
},
discovery: [
new MulticastDNS(peerInfo, 'your-identifier')
]
}
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
@ -127,16 +203,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.
#### `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][]
- `protocol`: String that defines the protocol (e.g '/ipfs/bitswap/1.1.0')
- `peer`: can be an instance of [PeerInfo][], [PeerId][], [multiaddr][], or a multiaddr string
- `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)
#### `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.
@ -144,15 +230,31 @@ 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.
#### `libp2p.peerRouting.findPeer(id, callback)`
> Looks up for multiaddrs of a peer in the DHT
- `id`: instance of [PeerId][]
#### `libp2p.contentRouting.findProviders(key, timeout, callback)`
- `key`: Buffer
- `timeout`: Number miliseconds
#### `libp2p.contentRouting.provide(key, callback)`
- `key`: Buffer
#### `libp2p.handle(protocol, handlerFunc [, matchFunc])`
> Handle new protocol
- `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.
#### `libp2p.unhandle(protocol)
#### `libp2p.unhandle(protocol)`
> Stop handling protocol
@ -176,7 +278,7 @@ class Node extends libp2p {
- `peer`: instance of [PeerInfo][]
#### `libp2p.isOn()`
#### `libp2p.isStarted()`
> Check if libp2p is started
@ -192,17 +294,27 @@ class Node extends libp2p {
> 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.
---------------------
`SOON™`
`DHT methods also exposed for the time being`
#### `libp2p.findPeers`
#### `libp2p.dht.put(key, value, callback)`
#### `libp2p.findProviders`
- `key`: Buffer
- `value`: Buffer
#### `libp2p.record.put`
#### `libp2p.dht.get(key, callback)`
#### `libp2p.record.get`
- `key`: Buffer
#### `libp2p.dht.getMany(key, nVals, callback)`
- `key`: Buffer
- `nVals`: Number
[PeerInfo]: https://github.com/libp2p/js-peer-info
[PeerId]: https://github.com/libp2p/js-peer-id
@ -210,6 +322,149 @@ class Node extends libp2p {
[multiaddr]: https://github.com/multiformats/js-multiaddr
[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.
## Development
**Clone and install dependencies:**
```sh
> git clone https://github.com/ipfs/js-ipfs.git
> cd js-ipfs
> npm install
```
### Tests
#### Run unit tests
```sh
# run all the unit tsts
> npm test
# run just Node.js tests
> npm run test:node
# run just Browser tests (Chrome)
> npm run test:browser
```
#### Run interop tests
```sh
N/A
```
#### Run benchmark tests
```sh
N/A
```
### Packages
@ -217,13 +472,12 @@ List of packages currently in existence for libp2p
| Package | Version | Dependencies | DevDependencies |
|---------|---------|--------------|-----------------|
| **Bundles** |
| [`libp2p-ipfs-nodejs`](//github.com/ipfs/js-libp2p-ipfs-nodejs) | [![npm](https://img.shields.io/npm/v/libp2p-ipfs-nodejs.svg?maxAge=86400&style=flat-square)](//github.com/ipfs/js-libp2p-ipfs-nodejs/releases) | [![Dependency Status](https://david-dm.org/ipfs/js-libp2p-ipfs-nodejs.svg?style=flat-square)](https://david-dm.org/ipfs/js-libp2p-ipfs-nodejs) | [![devDependency Status](https://david-dm.org/ipfs/js-libp2p-ipfs-nodejs/dev-status.svg?style=flat-square)](https://david-dm.org/ipfs/js-libp2p-ipfs-nodejs?type=dev) |
| [`libp2p-ipfs-browser`](//github.com/ipfs/js-libp2p-ipfs-browser) | [![npm](https://img.shields.io/npm/v/libp2p-ipfs-browser.svg?maxAge=86400&style=flat-square)](//github.com/ipfs/js-libp2p-ipfs-browser/releases) | [![Dependency Status](https://david-dm.org/ipfs/js-libp2p-ipfs-browser.svg?style=flat-square)](https://david-dm.org/ipfs/js-libp2p-ipfs-browser) | [![devDependency Status](https://david-dm.org/ipfs/js-libp2p-ipfs-browser/dev-status.svg?style=flat-square)](https://david-dm.org/ipfs/js-libp2p-ipfs-browser?type=dev) |
| **Transports** |
| [`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) |
| [`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-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) |
| [`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) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-websocket-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-websocket-star/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star?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) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-websocket-star-rendezvous.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star-rendezvous) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-websocket-star-rendezvous/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star-rendezvous?type=dev) |
| **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) | [![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) |
| **Stream Muxers** |
@ -243,7 +497,7 @@ List of packages currently in existence for libp2p
| [`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) |
| [`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) |
| **Generics** |
| [`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) |
| [`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) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-switch.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-switch) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-switch/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-switch?type=dev) |
| [`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) |
| [`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) |
| **Data Types** |
@ -254,7 +508,7 @@ List of packages currently in existence for libp2p
The libp2p implementation in JavaScript is a work in progress. As such, there are a few things you can do right now to help out:
- Go through the modules and **check out existing issues**. This would be especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrasture behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically.
- Go through the modules and **check out existing issues**. This would be especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically.
- **Perform code reviews**. Most of this has been developed by @diasdavid, which means that more eyes will help a) speed the project along b) ensure quality and c) reduce possible future bugs.
- **Add tests**. There can never be enough tests.

29
appveyor.yml Normal file
View File

@ -0,0 +1,29 @@
# 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.
version: "{build}"
environment:
matrix:
- nodejs_version: "6"
- nodejs_version: "8"
matrix:
fast_finish: true
install:
# Install Node.js
- ps: Install-Product node $env:nodejs_version
# Upgrade npm
- npm install -g npm
# Output our current versions for debugging
- node --version
- npm --version
# Install our package dependencies
- npm install
test_script:
- npm run test:node
build: off

2
ci/Jenkinsfile vendored Normal file
View File

@ -0,0 +1,2 @@
// 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.
javascript()

View File

@ -1,12 +1,22 @@
machine:
node:
version: stable
version: 8.11.3
test:
post:
- npm run coverage -- --upload --providers coveralls
dependencies:
pre:
- google-chrome --version
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
- sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
- curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- for v in $(curl http://archive.ubuntu.com/ubuntu/pool/main/n/nss/ | grep "href=" | grep "libnss3.*deb\"" -o | grep -o "libnss3.*deb" | grep "3.28" | grep "14.04"); do curl -L -o $v http://archive.ubuntu.com/ubuntu/pool/main/n/nss/$v; done && rm libnss3-tools*_i386.deb libnss3-dev*_i386.deb
- sudo dpkg -i google-chrome.deb || true
- sudo dpkg -i libnss3*.deb || true
- sudo apt-get update
- sudo apt-get --only-upgrade install google-chrome-stable
- sudo apt-get install -f || true
- sudo dpkg -i libnss3*.deb
- sudo apt-get install -f
- sudo apt-get install --only-upgrade lsb-base
- sudo dpkg -i google-chrome.deb
- google-chrome --version

View File

@ -1,6 +1,25 @@
### Examples list
# `js-libp2p` Examples and Tutorials
Here are some examples built with libp2p bundles.
In this folder, you can find a variety of examples to help you get started in using js-libp2p, in Node.js and in the Browser. Every example as a specific purpose and some of each incorporate a full tutorial that you can follow through, helping you expand your knowledge about libp2p and p2p networks in general.
- https://github.com/ipfs/js-libp2p-ipfs/tree/master/examples/echo
- https://github.com/ipfs/js-libp2p-ipfs/tree/master/examples/chat
Let us know if you find any issue or if you want to contribute and add a new tutorial, feel welcome to submit a PR, thank you!
## Understanding how libp2p works
- [Transports](./transports)
- [Protocol and Stream Muxing](./protocol-and-stream-muxing)
- [Encrypted Communications](./encrypted-communications)
- [Discovery Mechanisms](./discovery-mechanisms)
- [Peer Routing](./peer-and-content-routing)
- [Content Routing](./peer-and-content-routing)
- [PubSub](./pubsub)
- [NAT Traversal](./nat-traversal)
- Circuit Relay (future)
- Naming (future)
## Other examples
- [Running libp2p in the Browser](./libp2p-in-the-browser)
- Running libp2p in the Electron (future)
- [The standard echo net example with libp2p](./echo)
- [A simple chat app with libp2p](./chat)

View File

@ -1,4 +0,0 @@
Using libp2p-swarm
==================

View File

@ -1,39 +0,0 @@
'use strict'
var Swarm = require('libp2p-swarm')
var tcp = require('libp2p-tcp')
var multiaddr = require('multiaddr')
var Id = require('peer-id')
var Spdy = require('libp2p-spdy')
var Libp2p = require('../../src')
var Peer = require('peer-info')
// set up
var mh = multiaddr('/ip4/127.0.0.1/tcp/8010')
var p = new Peer(Id.create(), [])
var sw = new Swarm(p)
// create a libp2p node
var node = new Libp2p(sw)
node.swarm.addTransport('tcp', tcp, {multiaddr: mh}, {}, {port: 8010}, function () {
// Ready to receive incoming connections
sw.addStreamMuxer('spdy', Spdy, {})
// dial to another node
var mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020')
var p2 = new Peer(Id.create(), [mh2])
node.swarm.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) {
if (err) {
return console.error(err)
}
console.log('-> connection is ready')
process.stdin.pipe(conn).pipe(process.stdout)
})
})

View File

@ -1,31 +0,0 @@
'use strict'
var Swarm = require('libp2p-swarm')
var tcp = require('libp2p-tcp')
var multiaddr = require('multiaddr')
var Id = require('peer-id')
var Spdy = require('libp2p-spdy')
var Libp2p = require('../../src')
var Peer = require('peer-info')
// set up
var mh = multiaddr('/ip4/127.0.0.1/tcp/8020')
var p = new Peer(Id.create(), [])
var sw = new Swarm(p)
sw.addTransport('tcp', tcp, {multiaddr: mh}, {}, {port: 8020}, function () {
// Ready to receive incoming connections
sw.addStreamMuxer('spdy', Spdy, {})
// create a libp2p node
var node = new Libp2p(sw)
// handle/mount a protocol
node.swarm.handleProtocol('/sparkles/1.0.0', function (conn) {
process.stdin.pipe(conn).pipe(process.stdout)
})
})

13
examples/chat/README.md Normal file
View File

@ -0,0 +1,13 @@
# 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

@ -0,0 +1,79 @@
'use strict'
/* eslint-disable no-console */
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const Node = require('./libp2p-bundle')
const pull = require('pull-stream')
const async = require('async')
const Pushable = require('pull-pushable')
const p = Pushable()
let idListener
async.parallel([
(callback) => {
PeerId.createFromJSON(require('./peer-id-dialer'), (err, idDialer) => {
if (err) {
throw err
}
callback(null, idDialer)
})
},
(callback) => {
PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
if (err) {
throw err
}
callback(null, idListener)
})
}
], (err, ids) => {
if (err) throw err
const peerDialer = new PeerInfo(ids[0])
peerDialer.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const nodeDialer = new Node({
peerInfo: peerDialer
})
const peerListener = new PeerInfo(ids[1])
idListener = ids[1]
peerListener.multiaddrs.add('/ip4/127.0.0.1/tcp/10333')
nodeDialer.start((err) => {
if (err) {
throw err
}
console.log('Dialer ready, listening on:')
peerListener.multiaddrs.forEach((ma) => {
console.log(ma.toString() + '/ipfs/' + idListener.toB58String())
})
nodeDialer.dialProtocol(peerListener, '/chat/1.0.0', (err, conn) => {
if (err) {
throw err
}
console.log('nodeA dialed to nodeB on protocol: /chat/1.0.0')
console.log('Type a message and see what happens')
// Write operation. Data sent as a buffer
pull(
p,
conn
)
// Sink, data converted from buffer to utf8 string
pull(
conn,
pull.map((data) => {
return data.toString('utf8').replace('\n', '')
}),
pull.drain(console.log)
)
process.stdin.setEncoding('utf8')
process.openStdin().on('data', (chunk) => {
var data = chunk.toString()
p.push(data)
})
})
})
})

View File

@ -0,0 +1,77 @@
'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
}
},
dht: {
kBucketSize: 20
}
}
}
super(defaultsDeep(_options, defaults))
}
}
module.exports = Node

View File

@ -0,0 +1,56 @@
'use strict'
/* eslint-disable no-console */
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const Node = require('./libp2p-bundle.js')
const pull = require('pull-stream')
const Pushable = require('pull-pushable')
const p = Pushable()
PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
if (err) {
throw err
}
const peerListener = new PeerInfo(idListener)
peerListener.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
const nodeListener = new Node({
peerInfo: peerListener
})
nodeListener.start((err) => {
if (err) {
throw err
}
nodeListener.on('peer:connect', (peerInfo) => {
console.log(peerInfo.id.toB58String())
})
nodeListener.handle('/chat/1.0.0', (protocol, conn) => {
pull(
p,
conn
)
pull(
conn,
pull.map((data) => {
return data.toString('utf8').replace('\n', '')
}),
pull.drain(console.log)
)
process.stdin.setEncoding('utf8')
process.openStdin().on('data', (chunk) => {
var data = chunk.toString()
p.push(data)
})
})
console.log('Listener ready, listening on:')
peerListener.multiaddrs.forEach((ma) => {
console.log(ma.toString() + '/ipfs/' + idListener.toB58String())
})
})
})

View File

@ -0,0 +1,5 @@
{
"id": "Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP",
"privKey": "CAASpwkwggSjAgEAAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAECggEAZnrCJ6IYiLyyRdr9SbKXCNDb4YByGYPEi/HT1aHgIJfFE1PSMjxcdytxfyjP4JJpVtPjiT9JFVU2ddoYu5qJN6tGwjVwgJEWg1UXmPaAw1T/drjS94kVsAs82qICtFmwp52Apg3dBZ0Qwq/8qE1XbG7lLyohIbfCBiL0tiPYMfkcsN9gnFT/kFCX0LVs2pa9fHCRMY9rqCc4/rWJa1w8sMuQ23y4lDaxKF9OZVvOHFQkbBDrkquWHE4r55fchCz/rJklkPJUNENuncBRu0/2X+p4IKFD1DnttXNwb8j4LPiSlLro1T0hiUr5gO2QmdYwXFF63Q3mjQy0+5I4eNbjjQKBgQDZvZy3gUKS/nQNkYfq9za80uLbIj/cWbO+ZZjXCsj0fNIcQFJcKMBoA7DjJvu2S/lf86/41YHkPdmrLAEQAkJ+5BBNOycjYK9minTEjIMMmZDTXXugZ62wnU6F46uLkgEChTqEP57Y6xwwV+JaEDFEsW5N1eE9lEVX9nGIr4phMwKBgQC1TazLuEt1WBx/iUT83ita7obXqoKNzwsS/MWfY2innzYZKDOqeSYZzLtt9uTtp4X4uLyPbYs0qFYhXLsUYMoGHNN8+NdjoyxCjQRJRBkMtaNR0lc5lVDWl3bTuJovjFCgAr9uqJrmI5OHcCIk/cDpdWb3nWaMihVlePmiTcTy9wKBgQCU0u7c1jKkudqks4XM6a+2HAYGdUBk4cLjLhnrUWnNAcuyl5wzdX8dGPi8KZb+IKuQE8WBNJ2VXVj7kBYh1QmSJVunDflQSvNYCOaKuOeRoxzD+y9Wkca74qkbBmPn/6FFEb7PSZTO+tPHjyodGNgz9XpJJRjQuBk1aDJtlF3m1QKBgE5SAr5ym65SZOU3UGUIOKRsfDW4Q/OsqDUImvpywCgBICaX9lHDShFFHwau7FA52ScL7vDquoMB4UtCOtLfyQYA9995w9oYCCurrVlVIJkb8jSLcADBHw3EmqF1kq3NqJqm9TmBfoDCh52vdCCUufxgKh33kfBOSlXuf7B8dgMbAoGAZ3r0/mBQX6S+s5+xCETMTSNv7TQzxgtURIpVs+ZVr2cMhWhiv+n0Omab9X9Z50se8cWl5lkvx8vn3D/XHHIPrMF6qk7RAXtvReb+PeitNvm0odqjFv0J2qki6fDs0HKwq4kojAXI1Md8Th0eobNjsy21fEEJT7uKMJdovI/SErI=",
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAE="
}

View File

@ -0,0 +1,5 @@
{
"id": "QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm",
"privKey": "CAASqAkwggSkAgEAAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAECggEAQj0obPnVyjxLFZFnsFLgMHDCv9Fk5V5bOYtmxfvcm50us6ye+T8HEYWGUa9RrGmYiLweuJD34gLgwyzE1RwptHPj3tdNsr4NubefOtXwixlWqdNIjKSgPlaGULQ8YF2tm/kaC2rnfifwz0w1qVqhPReO5fypL+0ShyANVD3WN0Fo2ugzrniCXHUpR2sHXSg6K+2+qWdveyjNWog34b7CgpV73Ln96BWae6ElU8PR5AWdMnRaA9ucA+/HWWJIWB3Fb4+6uwlxhu2L50Ckq1gwYZCtGw63q5L4CglmXMfIKnQAuEzazq9T4YxEkp+XDnVZAOgnQGUBYpetlgMmkkh9qQKBgQDvsEs0ThzFLgnhtC2Jy//ZOrOvIAKAZZf/mS08AqWH3L0/Rjm8ZYbLsRcoWU78sl8UFFwAQhMRDBP9G+RPojWVahBL/B7emdKKnFR1NfwKjFdDVaoX5uNvZEKSl9UubbC4WZJ65u/cd5jEnj+w3ir9G8n+P1gp/0yBz02nZXFgSwKBgQDZPQr4HBxZL7Kx7D49ormIlB7CCn2i7mT11Cppn5ifUTrp7DbFJ2t9e8UNk6tgvbENgCKXvXWsmflSo9gmMxeEOD40AgAkO8Pn2R4OYhrwd89dECiKM34HrVNBzGoB5+YsAno6zGvOzLKbNwMG++2iuNXqXTk4uV9GcI8OnU5ZPQKBgCZUGrKSiyc85XeiSGXwqUkjifhHNh8yH8xPwlwGUFIZimnD4RevZI7OEtXw8iCWpX2gg9XGuyXOuKORAkF5vvfVriV4e7c9Ad4Igbj8mQFWz92EpV6NHXGCpuKqRPzXrZrNOA9PPqwSs+s9IxI1dMpk1zhBCOguWx2m+NP79NVhAoGBAI6WSoTfrpu7ewbdkVzTWgQTdLzYNe6jmxDf2ZbKclrf7lNr/+cYIK2Ud5qZunsdBwFdgVcnu/02czeS42TvVBgs8mcgiQc/Uy7yi4/VROlhOnJTEMjlU2umkGc3zLzDgYiRd7jwRDLQmMrYKNyEr02HFKFn3w8kXSzW5I8rISnhAoGBANhchHVtJd3VMYvxNcQb909FiwTnT9kl9pkjhwivx+f8/K8pDfYCjYSBYCfPTM5Pskv5dXzOdnNuCj6Y2H/9m2SsObukBwF0z5Qijgu1DsxvADVIKZ4rzrGb4uSEmM6200qjJ/9U98fVM7rvOraakrhcf9gRwuspguJQnSO9cLj6",
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAE="
}

View File

@ -0,0 +1,71 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const Bootstrap = require('libp2p-railing')
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
const bootstrapers = [
'/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
'/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
'/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
'/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx'
]
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
peerDiscovery: [ Bootstrap ]
},
config: {
peerDiscovery: {
bootstrap: {
interval: 2000,
enabled: true,
list: bootstrapers
}
}
}
}
super(defaultsDeep(_options, defaults))
}
}
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => {
if (err) { throw err }
node.on('peer:discovery', (peer) => {
console.log('Discovered:', peer.id.toB58String())
node.dial(peer, () => {})
})
node.on('peer:connect', (peer) => {
console.log('Connection established to:', peer.id.toB58String())
})
})

View File

@ -0,0 +1,62 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const MulticastDNS = require('libp2p-mdns')
const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
peerDiscovery: [ MulticastDNS ]
},
config: {
peerDiscovery: {
mdns: {
interval: 1000,
enabled: true
}
}
}
}
super(defaultsDeep(_options, defaults))
}
}
function createNode (callback) {
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => callback(err, node))
}
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
node1.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
node2.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
})

View File

@ -0,0 +1,182 @@
# Peer Discovery Mechanisms
A Peer Discovery module enables libp2p to find peers to connect to. Think of these mechanisms as ways to join the rest of the network, as railing points.
With these system, a libp2p node can both have a set of nodes to always connect on boot (bootstraper nodes), discover nodes through locality (e.g connected in the same LAN) or through serendipity (random walks on a DHT).
These mechanisms save configuration and enable a node to operate without any explicit dials, it will just work.
## 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, mplex and SECIO. You can see the complete example at [1.js](./1.js).
First, we create our libp2p bundle.
```JavaScript
const Bootstrap = require('libp2p-railing')
class MyBundle extends libp2p {
constructor (peerInfo) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
peerDiscovery: [ Bootstrap ]
},
config: {
peerDiscovery: {
bootstrap: {
interval: 2000,
enabled: true,
list: bootstrapers
}
}
}
}
super(defaultsDeep(_options, defaults))
}
}
```
In this bundle, we use a `bootstrappers` array listing peers to connect _on boot_. Here is the list used by js-ipfs and go-ipfs.
```JavaScript
const bootstrapers = [
'/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
'/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
'/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
'/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx'
]
```
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
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => {
if (err) { throw err }
// Emitted when a peer has been found
node.on('peer:discovery', (peer) => {
console.log('Discovered:', peer.id.toB58String())
// Note how we need to dial, even if just to warm up the Connection (by not
// picking any protocol) in order to get a full Connection. The Peer Discovery
// doesn't make any decisions for you.
node.dial(peer, () => {})
})
// Once the dial is complete, this event is emitted.
node.on('peer:connect', (peer) => {
console.log('Connection established to:', peer.id.toB58String())
})
})
```
From running [1.js](./1.js), you should see the following:
```bash
> node 1.js
Discovered: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
Discovered: QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z
Discovered: QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM
Discovered: QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm
Discovered: QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu
Discovered: QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64
Discovered: QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd
Discovered: QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3
Discovered: QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
Connection established to: QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3
Connection established to: QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd
Connection established to: QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64
Connection established to: QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm
Connection established to: QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM
Connection established to: QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
Connection established to: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
Connection established to: QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z
Connection established to: QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu
```
## 2. MulticastDNS to find other peers in the network
For this example, we need `libp2p-mdns`, go ahead and `npm install` it. You can find the complete solution at [2.js](./2.js).
Update your libp2p bundle to include MulticastDNS.
```JavaScript
class MyBundle extends libp2p {
constructor (peerInfo) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
peerDiscovery: [ MulticastDNS ]
},
config: {
peerDiscovery: {
mdns: {
// Run at 1s so we can observe more quickly, default is 10s
interval: 1000,
enabled: true
}
}
}
}
super(defaultsDeep(_options, defaults))
}
}
```
To observe it working, spawn two nodes.
```JavaScript
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
node1.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
node2.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
})
```
If you run this example, you will see a continuous stream of each peer discovering each other.
```bash
> node 2.js
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
```
## 3. Where to find other Peer Discovery Mechanisms
There are plenty more Peer Discovery Mechanisms out there, you can:
- Find one in [libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star). Yes, a transport with discovery capabilities! This happens because WebRTC requires a rendezvous point for peers to exchange [SDP](https://tools.ietf.org/html/rfc4317) offer, which means we have one or more points that can introduce peers to each other. Think of it as MulticastDNS for the Web, as in MulticastDNS only works in LAN.
- Any DHT will offer you a discovery capability. You can simple _random-walk_ the routing tables to find other peers to connect to.
- You can create your own Discovery service, a registry, a list, a radio beacon, you name it!

13
examples/echo/README.md Normal file
View File

@ -0,0 +1,13 @@
# 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

@ -0,0 +1,58 @@
'use strict'
/* eslint-disable no-console */
/*
* Dialer Node
*/
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const Node = require('./libp2p-bundle')
const pull = require('pull-stream')
const async = require('async')
async.parallel([
(cb) => PeerId.createFromJSON(require('./id-d'), cb),
(cb) => PeerId.createFromJSON(require('./id-l'), cb)
], (err, ids) => {
if (err) { throw err }
// Dialer
const dialerId = ids[0]
const dialerPeerInfo = new PeerInfo(dialerId)
dialerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const dialerNode = new Node({
peerInfo: dialerPeerInfo
})
// Peer to Dial
const listenerPeerInfo = new PeerInfo(ids[1])
const listenerId = ids[1]
const listenerMultiaddr = '/ip4/127.0.0.1/tcp/10333/ipfs/' +
listenerId.toB58String()
listenerPeerInfo.multiaddrs.add(listenerMultiaddr)
dialerNode.start((err) => {
if (err) { throw err }
console.log('Dialer ready, listening on:')
dialerPeerInfo.multiaddrs.forEach((ma) => console.log(ma.toString() +
'/ipfs/' + dialerId.toB58String()))
console.log('Dialing to peer:', listenerMultiaddr.toString())
dialerNode.dialProtocol(listenerPeerInfo, '/echo/1.0.0', (err, conn) => {
if (err) { throw err }
console.log('nodeA dialed to nodeB on protocol: /echo/1.0.0')
pull(
pull.values(['hey']),
conn,
pull.collect((err, data) => {
if (err) { throw err }
console.log('received echo:', data.toString())
})
)
})
})
})

View File

@ -0,0 +1,5 @@
{
"id": "Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP",
"privKey": "CAASpwkwggSjAgEAAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAECggEAZnrCJ6IYiLyyRdr9SbKXCNDb4YByGYPEi/HT1aHgIJfFE1PSMjxcdytxfyjP4JJpVtPjiT9JFVU2ddoYu5qJN6tGwjVwgJEWg1UXmPaAw1T/drjS94kVsAs82qICtFmwp52Apg3dBZ0Qwq/8qE1XbG7lLyohIbfCBiL0tiPYMfkcsN9gnFT/kFCX0LVs2pa9fHCRMY9rqCc4/rWJa1w8sMuQ23y4lDaxKF9OZVvOHFQkbBDrkquWHE4r55fchCz/rJklkPJUNENuncBRu0/2X+p4IKFD1DnttXNwb8j4LPiSlLro1T0hiUr5gO2QmdYwXFF63Q3mjQy0+5I4eNbjjQKBgQDZvZy3gUKS/nQNkYfq9za80uLbIj/cWbO+ZZjXCsj0fNIcQFJcKMBoA7DjJvu2S/lf86/41YHkPdmrLAEQAkJ+5BBNOycjYK9minTEjIMMmZDTXXugZ62wnU6F46uLkgEChTqEP57Y6xwwV+JaEDFEsW5N1eE9lEVX9nGIr4phMwKBgQC1TazLuEt1WBx/iUT83ita7obXqoKNzwsS/MWfY2innzYZKDOqeSYZzLtt9uTtp4X4uLyPbYs0qFYhXLsUYMoGHNN8+NdjoyxCjQRJRBkMtaNR0lc5lVDWl3bTuJovjFCgAr9uqJrmI5OHcCIk/cDpdWb3nWaMihVlePmiTcTy9wKBgQCU0u7c1jKkudqks4XM6a+2HAYGdUBk4cLjLhnrUWnNAcuyl5wzdX8dGPi8KZb+IKuQE8WBNJ2VXVj7kBYh1QmSJVunDflQSvNYCOaKuOeRoxzD+y9Wkca74qkbBmPn/6FFEb7PSZTO+tPHjyodGNgz9XpJJRjQuBk1aDJtlF3m1QKBgE5SAr5ym65SZOU3UGUIOKRsfDW4Q/OsqDUImvpywCgBICaX9lHDShFFHwau7FA52ScL7vDquoMB4UtCOtLfyQYA9995w9oYCCurrVlVIJkb8jSLcADBHw3EmqF1kq3NqJqm9TmBfoDCh52vdCCUufxgKh33kfBOSlXuf7B8dgMbAoGAZ3r0/mBQX6S+s5+xCETMTSNv7TQzxgtURIpVs+ZVr2cMhWhiv+n0Omab9X9Z50se8cWl5lkvx8vn3D/XHHIPrMF6qk7RAXtvReb+PeitNvm0odqjFv0J2qki6fDs0HKwq4kojAXI1Md8Th0eobNjsy21fEEJT7uKMJdovI/SErI=",
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAE="
}

View File

@ -0,0 +1,5 @@
{
"id": "QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm",
"privKey": "CAASqAkwggSkAgEAAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAECggEAQj0obPnVyjxLFZFnsFLgMHDCv9Fk5V5bOYtmxfvcm50us6ye+T8HEYWGUa9RrGmYiLweuJD34gLgwyzE1RwptHPj3tdNsr4NubefOtXwixlWqdNIjKSgPlaGULQ8YF2tm/kaC2rnfifwz0w1qVqhPReO5fypL+0ShyANVD3WN0Fo2ugzrniCXHUpR2sHXSg6K+2+qWdveyjNWog34b7CgpV73Ln96BWae6ElU8PR5AWdMnRaA9ucA+/HWWJIWB3Fb4+6uwlxhu2L50Ckq1gwYZCtGw63q5L4CglmXMfIKnQAuEzazq9T4YxEkp+XDnVZAOgnQGUBYpetlgMmkkh9qQKBgQDvsEs0ThzFLgnhtC2Jy//ZOrOvIAKAZZf/mS08AqWH3L0/Rjm8ZYbLsRcoWU78sl8UFFwAQhMRDBP9G+RPojWVahBL/B7emdKKnFR1NfwKjFdDVaoX5uNvZEKSl9UubbC4WZJ65u/cd5jEnj+w3ir9G8n+P1gp/0yBz02nZXFgSwKBgQDZPQr4HBxZL7Kx7D49ormIlB7CCn2i7mT11Cppn5ifUTrp7DbFJ2t9e8UNk6tgvbENgCKXvXWsmflSo9gmMxeEOD40AgAkO8Pn2R4OYhrwd89dECiKM34HrVNBzGoB5+YsAno6zGvOzLKbNwMG++2iuNXqXTk4uV9GcI8OnU5ZPQKBgCZUGrKSiyc85XeiSGXwqUkjifhHNh8yH8xPwlwGUFIZimnD4RevZI7OEtXw8iCWpX2gg9XGuyXOuKORAkF5vvfVriV4e7c9Ad4Igbj8mQFWz92EpV6NHXGCpuKqRPzXrZrNOA9PPqwSs+s9IxI1dMpk1zhBCOguWx2m+NP79NVhAoGBAI6WSoTfrpu7ewbdkVzTWgQTdLzYNe6jmxDf2ZbKclrf7lNr/+cYIK2Ud5qZunsdBwFdgVcnu/02czeS42TvVBgs8mcgiQc/Uy7yi4/VROlhOnJTEMjlU2umkGc3zLzDgYiRd7jwRDLQmMrYKNyEr02HFKFn3w8kXSzW5I8rISnhAoGBANhchHVtJd3VMYvxNcQb909FiwTnT9kl9pkjhwivx+f8/K8pDfYCjYSBYCfPTM5Pskv5dXzOdnNuCj6Y2H/9m2SsObukBwF0z5Qijgu1DsxvADVIKZ4rzrGb4uSEmM6200qjJ/9U98fVM7rvOraakrhcf9gRwuspguJQnSO9cLj6",
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAE="
}

View File

@ -0,0 +1,77 @@
'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
}
},
dht: {
kBucketSize: 20
}
}
}
super(defaultsDeep(_options, defaults))
}
}
module.exports = Node

View File

@ -0,0 +1,46 @@
'use strict'
/* eslint-disable no-console */
/*
* Listener Node
*/
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const Node = require('./libp2p-bundle')
const pull = require('pull-stream')
const series = require('async/series')
let listenerId
let listenerNode
series([
(cb) => {
PeerId.createFromJSON(require('./id-l'), (err, id) => {
if (err) { return cb(err) }
listenerId = id
cb()
})
},
(cb) => {
const listenerPeerInfo = new PeerInfo(listenerId)
listenerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
listenerNode = new Node({
peerInfo: listenerPeerInfo
})
listenerNode.on('peer:connect', (peerInfo) => {
console.log('received dial to me from:', peerInfo.id.toB58String())
})
listenerNode.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
listenerNode.start(cb)
}
], (err) => {
if (err) { throw err }
console.log('Listener ready, listening on:')
listenerNode.peerInfo.multiaddrs.forEach((ma) => {
console.log(ma.toString() + '/ipfs/' + listenerId.toB58String())
})
})

View File

@ -0,0 +1,63 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const SPDY = require('libp2p-spdy')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
const pull = require('pull-stream')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ SPDY ],
connEncryption: [ SECIO ]
}
}
super(defaultsDeep(_options, defaults))
}
}
function createNode (callback) {
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => callback(err, node))
}
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
node2.handle('/a-protocol', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
node1.dialProtocol(node2.peerInfo, '/a-protocol', (err, conn) => {
if (err) { throw err }
pull(pull.values(['This information is sent out encrypted to the other peer']), conn)
})
})

View File

@ -0,0 +1,40 @@
# Encrypted Communications
libp2p can leverage the encrypted communications from the transports it uses (i.e WebRTC). To ensure that every connection is encrypted, independently of how it was set up, libp2p also supports a set of modules that encrypt every communication established.
We call this usage a _connection upgrade_ where given a connection between peer A to peer B, a protocol handshake can be performed that gives that connection new properties.
A byproduct of having these encrypted communications modules is that we can authenticate the peers we are dialing to. You might have noticed that every time we dial to a peer in libp2p space, we always use its PeerId at the end (e.g /ip4/127.0.0.1/tcp/89765/ipfs/QmWCbVw1XZ8hiYBwwshPce2yaTDYTqTaP7GCHGpry3ykWb), this PeerId is generated by hashing the Public Key of the peer. With this, we can create a crypto challenge when dialing to another peer and prove that peer is the owner of a PrivateKey that matches the Public Key we know.
# 1. Set up encrypted communications with SECIO
We will build this example on top of example for [Protocol and Stream Multiplexing](../protocol-and-stream-multiplexing). You will need the module `libp2p-secio` to complete it, go ahead and `npm install libp2p-secio`.
SECIO is the crypto channel developed for IPFS, it is a TLS 1.3 like crypto channel that established an encrypted communication channel between two peers.
To add it to your libp2p bundle, all you have to do is:
```JavaScript
const SECIO = require('libp2p-secio')
class MyBundle extends libp2p {
constructor (peerInfo) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ SPDY ],
// Attach secio as the crypto channel to use
connEncryption: [ SECIO ]
}
}
super(defaultsDeep(_options, defaults))
}
}
```
And that's it, from now on, all your libp2p communications are encrypted. Try running the example [1.js](./1.js) to see it working.
If you want to want to learn more about how SECIO works, you can read the [great write up done by Dominic Tarr](https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel).
Importante note: SECIO hasn't been audited and so, we do not recommend to trust its security. We intent to move to TLS 1.3 once the specification is finalized and an implementation exists that we can use.

View File

@ -0,0 +1 @@
bundle.js

View File

@ -0,0 +1,28 @@
{
"name": "libp2p-in-the-browser",
"version": "0.1.0",
"description": "See other nodes in the network using WebRTC Star discovery mechanism",
"main": "src/index.js",
"scripts": {
"bundle": "browserify src/index.js > public/bundle.js",
"serve": "static public -p 9090 -H '{\"Cache-Control\": \"no-cache, must-revalidate\"}'",
"start": "npm run bundle && npm run serve"
},
"license": "MIT",
"devDependencies": {
"browserify": "^14.5.0",
"concat-stream": "^1.6.0",
"detect-dom-ready": "^1.0.2",
"node-static": "~0.7.10"
},
"dependencies": {
"detect-dom-ready": "^1.0.2",
"libp2p-mplex": "~0.8.0",
"libp2p-railing": "~0.9.1",
"libp2p-secio": "~0.10.0",
"libp2p-spdy": "~0.12.1",
"libp2p-webrtc-star": "~0.15.3",
"libp2p-websockets": "~0.12.0",
"peer-info": "~0.14.1"
}
}

View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>libp2p in the browser</title>
</head>
<body>
<h1>libp2p node running \o/</h1>
<div id="my-peer"></div>
<div id="swarm"></div>
<script src="bundle.js"></script>
</body>
</html>

View File

@ -0,0 +1,80 @@
'use strict'
const WebRTCStar = require('libp2p-webrtc-star')
const WebSockets = require('libp2p-websockets')
const Mplex = require('libp2p-mplex')
const SPDY = require('libp2p-spdy')
const SECIO = require('libp2p-secio')
const Bootstrap = require('libp2p-railing')
const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../../../')
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-browser.json
const bootstrapers = [
'/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/dns4/sfo-1.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx',
'/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/dns4/sfo-2.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
'/dns4/sfo-3.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/dns4/sgp-1.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/dns4/nyc-1.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/dns4/nyc-2.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
'/dns4/wss0.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmZMxNdpMkewiVZLMRxaNxUeZpDUb34pWjZ1kZvsd16Zic',
'/dns4/wss1.bootstrap.libp2p.io/tcp/443/wss/ipfs/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6'
]
class Node extends libp2p {
constructor (_options) {
const wrtcStar = new WebRTCStar({ id: _options.peerInfo.id })
const defaults = {
modules: {
transport: [
wrtcStar,
new WebSockets()
],
streamMuxer: [
Mplex,
SPDY
],
connEncryption: [
SECIO
],
peerDiscovery: [
wrtcStar.discovery,
Bootstrap
]
},
config: {
peerDiscovery: {
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(defaultsDeep(_options, defaults))
}
}
module.exports = Node

View File

@ -0,0 +1,26 @@
'use strict'
const PeerInfo = require('peer-info')
const Node = require('./browser-bundle')
function createNode (callback) {
PeerInfo.create((err, peerInfo) => {
if (err) {
return callback(err)
}
const peerIdStr = peerInfo.id.toB58String()
const ma = `/dns4/star-signal.cloud.ipfs.team/tcp/443/wss/p2p-webrtc-star/ipfs/${peerIdStr}`
peerInfo.multiaddrs.add(ma)
const node = new Node({
peerInfo
})
node.idStr = peerIdStr
callback(null, node)
})
}
module.exports = createNode

View File

@ -0,0 +1,58 @@
'use strict'
const domReady = require('detect-dom-ready')
const createNode = require('./create-node')
domReady(() => {
const myPeerDiv = document.getElementById('my-peer')
const swarmDiv = document.getElementById('swarm')
createNode((err, node) => {
if (err) {
return console.log('Could not create the Node, check if your browser has WebRTC Support', err)
}
node.on('peer:discovery', (peerInfo) => {
console.log('Discovered a peer')
const idStr = peerInfo.id.toB58String()
console.log('Discovered: ' + idStr)
node.dial(peerInfo, (err, conn) => {
if (err) { return console.log('Failed to dial:', idStr) }
})
})
node.on('peer:connect', (peerInfo) => {
const idStr = peerInfo.id.toB58String()
console.log('Got connection to: ' + idStr)
const connDiv = document.createElement('div')
connDiv.innerHTML = 'Connected to: ' + idStr
connDiv.id = idStr
swarmDiv.append(connDiv)
})
node.on('peer:disconnect', (peerInfo) => {
const idStr = peerInfo.id.toB58String()
console.log('Lost connection to: ' + idStr)
document.getElementById(idStr).remove()
})
node.start((err) => {
if (err) {
return console.log('WebRTC not supported')
}
const idStr = node.peerInfo.id.toB58String()
const idDiv = document
.createTextNode('Node is ready. ID: ' + idStr)
myPeerDiv.append(idDiv)
console.log('Node is listening o/')
// NOTE: to stop the node
// node.stop((err) => {})
})
})
})

View File

@ -0,0 +1,16 @@
# libp2p running in the Browser
One of the primary goals with libp2p P2P was to get it fully working in the browser and interopable with the versions running in Go and in Node.js.
# 1. Setting up a simple app that lists connections to other nodes
Simple go into the folder [1](./1) and execute the following
```bash
> cd 1
> npm install
> npm start
# open your browser in port :9090
```
[Version Published on IPFS](http://ipfs.io/ipfs/Qmbc1J7ehw1dNYachbkCWPto4RsnVvqCKNVzmYEod2gXcy)

View File

@ -0,0 +1,2 @@
# WIP - This example is still in the works
![](http://1.bp.blogspot.com/-tNvSnCW0KlQ/U-KOKGVoJkI/AAAAAAAAA3Q/aiSLMeSJFtw/s1600/WIP-sign.jpg)

View File

@ -0,0 +1,78 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const KadDHT = require('libp2p-kad-dht')
const defaultsDeep = require('@nodeutils/defaults-deep')
const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
// we add the DHT module that will enable Peer and Content Routing
dht: KadDHT
},
config: {
dht: {
kBucketSize: 20
},
EXPERIMENTAL: {
dht: true
}
}
}
super(defaultsDeep(_options, defaults))
}
}
function createNode (callback) {
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => callback(err, node))
}
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
const node3 = nodes[2]
parallel([
(cb) => node1.dial(node2.peerInfo, cb),
(cb) => node2.dial(node3.peerInfo, cb),
// Set up of the cons might take time
(cb) => setTimeout(cb, 300)
], (err) => {
if (err) { throw err }
node1.peerRouting.findPeer(node3.peerInfo.id, (err, peer) => {
if (err) { throw err }
console.log('Found it, multiaddrs are:')
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
})
})
})

View File

@ -0,0 +1,86 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const CID = require('cids')
const KadDHT = require('libp2p-kad-dht')
const defaultsDeep = require('@nodeutils/defaults-deep')
const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
// we add the DHT module that will enable Peer and Content Routing
dht: KadDHT
},
config: {
dht: {
kBucketSize: 20
},
EXPERIMENTAL: {
dht: true
}
}
}
super(defaultsDeep(_options, defaults))
}
}
function createNode (callback) {
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => callback(err, node))
}
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
const node3 = nodes[2]
parallel([
(cb) => node1.dial(node2.peerInfo, cb),
(cb) => node2.dial(node3.peerInfo, cb),
// Set up of the cons might take time
(cb) => setTimeout(cb, 300)
], (err) => {
if (err) { throw err }
const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
node1.contentRouting.provide(cid, (err) => {
if (err) { throw err }
console.log('Node %s is providing %s', node1.peerInfo.id.toB58String(), cid.toBaseEncodedString())
node3.contentRouting.findProviders(cid, 5000, (err, providers) => {
if (err) { throw err }
console.log('Found provider:', providers[0].id.toB58String())
})
})
})
})

View File

@ -0,0 +1,116 @@
# Peer and Content Routing
DHTs (Distributed Hash Tables) are one of the most common building blocks used when creating P2P networks. However, the name doesn't make justice to all the benefits it brings and putting the whole set of features in one box has proven to be limiting when we want to integrate multiple pieces together. With this in mind, we've come up with a new definition for what a DHT offers: Peer Routing and Content Routing.
Peer Routing is the category of modules that offer a way to find other peers in the network by intentionally issuing queries, iterative or recursive, until a Peer is found or the closest Peers, given the Peer Routing algorithm strategy are found.
Content Routing is the category of modules that offer a way to find where content lives in the network, it works in two steps: 1) Peers provide (announce) to the network that they are holders of specific content (multihashes) and 2) Peers issue queries to find where that content lives. A Content Routing mechanism could be as complex as a Kademlia DHT or a simple registry somewhere in the network.
# 1. Using Peer Routing to find other peers
This example builds on top of the [Protocol and Stream Muxing](../protocol-and-stream-muxing). We need to install `libp2p-kad-dht`, go ahead and `npm install libp2p-kad-dht`. If you want to see the final version, open [1.js](./1.js).
First, let's update our bundle to support Peer Routing and Content Routing.
```JavaScript
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
// we add the DHT module that will enable Peer and Content Routing
dht: KadDHT
},
config: {
dht: {
kBucketSize: 20
},
EXPERIMENTAL: {
// dht must be enabled
dht: true
}
}
}
super(defaultsDeep(_options, defaults))
}
}
```
Once that is done, we can use the createNode function we developed in the previous example to create 3 nodes. Connect node 1 to node 2 and node 2 to node 3. We will use node 2 as a way to find the whereabouts of node 3
```JavaScript
const node1 = nodes[0]
const node2 = nodes[1]
const node3 = nodes[2]
parallel([
(cb) => node1.dial(node2.peerInfo, cb),
(cb) => node2.dial(node3.peerInfo, cb),
// Set up of the cons might take time
(cb) => setTimeout(cb, 100)
], (err) => {
if (err) { throw err }
//
node1.peerRouting.findPeer(node3.peerInfo.id, (err, peer) => {
if (err) { throw err }
console.log('Found it, multiaddrs are:')
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
})
})
```
You should see the output being something like:
```Bash
> node 1.js
Found it, multiaddrs are:
/ip4/127.0.0.1/tcp/63617/ipfs/QmWrFXvZr9S4iDqycyoyc2zDdrT1jg9wpdenUTdd1LTar6
/ip4/192.168.86.41/tcp/63617/ipfs/QmWrFXvZr9S4iDqycyoyc2zDdrT1jg9wpdenUTdd1LTar6
```
You have successfully used Peer Routing to find a peer that you were not directly connected. Now all you have to do is to dial to the multiaddrs you discovered.
# 2. Using Content Routing to find providers of content
With Content Routing, you can create records that are stored in multiple points in the network, these records can be resolved by you or other peers and they act as memos or rendezvous points. A great usage of this feature is to support discovery of content, where one node holds a file and instead of using a centralized tracker to inform other nodes that it holds that file, it simply puts a record in the network that can be resolved by other peers. Peer Routing and Content Routing are commonly known as Distributed Hash Tables, DHT.
You can find this example completed in [2.js](./2.js), however as you will see it is very simple to update the previous example.
Instead of calling `peerRouting.findPeer`, we will use `contentRouting.provide` and `contentRouting.findProviders`.
```JavaScript
node1.contentRouting.provide(cid, (err) => {
if (err) { throw err }
console.log('Node %s is providing %s', node1.peerInfo.id.toB58String(), cid.toBaseEncodedString())
node3.contentRouting.findProviders(cid, 5000, (err, providers) => {
if (err) { throw err }
console.log('Found provider:', providers[0].id.toB58String())
})
})
```
The output of your program should look like:
```bash
> node 2.js
Node QmSsmVPoTy3WpzwiNPnsKmonBaZjK2HitFs2nWUvwK31Pz is providing QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL
Found provider: QmSsmVPoTy3WpzwiNPnsKmonBaZjK2HitFs2nWUvwK31Pz
```
That's it, now you know how to find peers that have pieces of information that interest you!
# 3. Future Work
Currently, the only mechanisms for Peer and Content Routing come from the DHT, however we do have the intention to support:
- Multiple Peer Routing Mechanisms, including ones that do recursive searches (i.e [webrtc-explorer](http://daviddias.me/blog/webrtc-explorer-2-0-0-alpha-release/) like packet switching or [CJDNS](https://github.com/cjdelisle/cjdns) path finder)
- Content Routing via PubSub
- Content Routing via centralized index (i.e a tracker)

View File

@ -0,0 +1,102 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
const pull = require('pull-stream')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [ TCP ]
}
}
super(defaultsDeep(_options, defaults))
}
}
function createNode (callback) {
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => callback(err, node))
}
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
// exact matching
node2.handle('/your-protocol', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
// semver matching
/*
node2.handle('/another-protocol/1.0.1', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
*/
// custom func matching
/*
node2.handle('/custom-match-func', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
}, (myProtocol, requestedProtocol, callback) => {
if (myProtocol.indexOf(requestedProtocol)) {
callback(null, true)
} else {
callback(null, false)
}
})
*/
node1.dialProtocol(node2.peerInfo, '/your-protocol', (err, conn) => {
if (err) { throw err }
pull(pull.values(['my own protocol, wow!']), conn)
})
/*
node1.dialProtocol(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
if (err) { throw err }
pull(pull.values(['semver me please']), conn)
})
*/
/*
node1.dialProtocol(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => {
if (err) { throw err }
pull(pull.values(['do I fall into your criteria?']), conn)
})
*/
})

View File

@ -0,0 +1,83 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const SPDY = require('libp2p-spdy')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
const series = require('async/series')
const pull = require('pull-stream')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ SPDY ]
}
}
super(defaultsDeep(_options, defaults))
}
}
function createNode (callback) {
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => callback(err, node))
}
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
node2.handle('/a', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
node2.handle('/b', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
series([
(cb) => node1.dialProtocol(node2.peerInfo, '/a', (err, conn) => {
if (err) { throw err }
pull(pull.values(['protocol (a)']), conn)
cb()
}),
(cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
if (err) { throw err }
pull(pull.values(['protocol (b)']), conn)
cb()
}),
(cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
if (err) { throw err }
pull(pull.values(['another conn on protocol (b)']), conn)
cb()
})
])
})

View File

@ -0,0 +1,87 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const SPDY = require('libp2p-spdy')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
const series = require('async/series')
const pull = require('pull-stream')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [ TCP ],
streamMuxer: [ SPDY ]
}
}
super(defaultsDeep(_options, defaults))
}
}
function createNode (callback) {
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => callback(err, node))
}
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
node1.handle('/node-1', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
node2.handle('/node-2', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
series([
(cb) => node1.dialProtocol(node2.peerInfo, '/node-2', (err, conn) => {
if (err) { throw err }
pull(pull.values(['from 1 to 2']), conn)
cb()
}),
(cb) => node2.dialProtocol(node1.peerInfo, '/node-1', (err, conn) => {
if (err) { throw err }
pull(pull.values(['from 2 to 1']), conn)
cb()
})
], (err) => {
if (err) { throw err }
console.log('Addresses by which both peers are connected')
node1.peerBook
.getAllArray()
.forEach((peer) => console.log('node 1 to node 2:', peer.isConnected().toString()))
node2.peerBook
.getAllArray()
.forEach((peer) => console.log('node 2 to node 1:', peer.isConnected().toString()))
})
})

View File

@ -0,0 +1,175 @@
# Protocol and Stream Multiplexing (aka muxing)
One of the specialties of libp2p is solving the bane of protocol discovery and handshake between machines. Before libp2p, you would have to assign a listener to a port and then through some process of formal specification you would assign ports to special protocols so that other hosts would know before hand which port to dial (e.g ssh (22), http (80), https (443), ftp (21), etc). With libp2p you don't need to do that anymore, not only you don't have to assign ports before hand, you don't even need to think about ports at all since all the protocol handshaking happens in the wire!
The feature of agreeing on a protocol over an established connection is what we call _protocol multiplexing_ and it is possible through [multistream-select](https://github.com/multiformats/multistream), another protocol that lets you agree per connection (or stream) which protocol is going to be talked over that connection (select), it also enables you to request the other end to tell you which protocols it supports (ls). You can learn more about multistream-select at its [specification repo](https://github.com/multiformats/multistream).
# 1. Handle multiple protocols
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.
```JavaScript
// ...
const node1 = nodes[0]
const node2 = nodes[1]
// Here we are telling libp2p that is someone dials this node to talk with the `/your-protocol`
// multicodec, the protocol identifier, please call this callback and give it the connection
// so that incomming data can be handled
node2.handle('/your-protocol', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
```
After the protocol is _handled_, now we can dial to it.
```JavaScript
node1.dialProtocol(node2.peerInfo, '/your-protocol', (err, conn) => {
if (err) { throw err }
pull(pull.values(['my own protocol, wow!']), conn)
})
```
You might have seen this in the [Transports](../transports) examples. However, what it was not explained is that you can do more than exact string matching, for example, you can use semver.
```JavaScript
node2.handle('/another-protocol/1.0.1', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
// ...
node1.dialProtocol(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
if (err) { throw err }
pull(pull.values(['semver me please']), 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.
There is still one last feature, you can create your custom match functions.
```JavaScript
node2.handle('/custom-match-func', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
}, (myProtocol, requestedProtocol, callback) => {
// This is all custom. I'm checking the base path matches, think of this
// as a HTTP routing table.
if (myProtocol.indexOf(requestedProtocol)) {
callback(null, true)
} else {
callback(null, false)
}
})
// ...
node1.dialProtocol(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => {
if (err) { throw err }
pull(pull.values(['do I fall into your criteria?']), conn)
})
```
Try all of this out by executing [1.js](./1.js).
# 2. Reuse existing connection
The example above would require a node to create a whole new connection for every time it dials in one of the protocols, this is a waste of resources and also it might be simply not possible (e.g lack of file descriptors, not enough ports being open, etc). What we really want is to dial a connection once and then multiplex several virtual connections (stream) over a single connection, this is where _stream multiplexing_ comes into play.
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-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
const SPDY = require('libp2p-spdy')
//...
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
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
// time and pick the right one when dialing to a node
streamMuxer: [ SPDY ]
}
}
super(defaultsDeep(_options, defaults))
}
}
```
With this, we can dial as many times as we want to a peer and always reuse the same established underlying connection.
```JavaScript
node2.handle('/a', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
node2.handle('/b', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
series([
(cb) => node1.dialProtocol(node2.peerInfo, '/a', (err, conn) => {
if (err) { throw err }
pull(pull.values(['protocol (a)']), conn)
cb()
}),
(cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
if (err) { throw err }
pull(pull.values(['protocol (b)']), conn)
cb()
}),
(cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
if (err) { throw err }
pull(pull.values(['another conn on protocol (b)']), conn)
cb()
})
])
```
By running [2.js](./2.js) you should see the following result:
```
> node 2.js
protocol (a)
protocol (b)
another protocol (b)
```
# 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 _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'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:
```Bash
> node 3.js
from 1 to 2
Addresses by which both peers are connected
node 1 to node 2: /ip4/127.0.0.1/tcp/50629/ipfs/QmZwMKTo6wG4Te9A6M2eJnWDpR8uhsGed4YRegnV5DcKiv
node 2 to node 1: /ip4/127.0.0.1/tcp/50630/ipfs/QmRgormJQeDyXhDKma11eUtksoh8vWmeBoxghVt4meauW9
from 2 to 1
```

85
examples/pubsub/1.js Normal file
View File

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

59
examples/pubsub/README.md Normal file
View File

@ -0,0 +1,59 @@
# Publish Subscribe
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).
We've seen many interesting use cases appear with this, here are some highlights:
- [Collaborative Text Editing](https://www.youtube.com/watch?v=-kdx8rJd8rQ)
- [IPFS PubSub (using libp2p-floodsub) for IoT](https://www.youtube.com/watch?v=qLpM5pBDGiE).
- [Real Time distributed Applications](https://www.youtube.com/watch?v=vQrbxyDPSXg)
## 1. Setting up a simple PubSub network on top of libp2p
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).
Using PubSub is super simple, all you have to do is start a libp2p node, PubSub will be enabled by default.
```JavaScript
series([
(cb) => node1.once('peer:discovery', (peer) => node1.dial(peer, cb)),
(cb) => setTimeout(cb, 500)
], (err) => {
if (err) { throw err }
// Subscribe to the topic 'news'
node1.pubsub.subscribe('news',
(msg) => console.log(msg.from, msg.data.toString()),
() => {
setInterval(() => {
// Publish the message on topic 'news'
node2.pubsub.publish(
'news',
Buffer.from('Bird bird bird, bird is the word!'),
() => {}
)
}, 1000)
}
)
})
```
The output of the program should look like:
```
> node 1.js
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
```
## 2. Future work
libp2p/IPFS PubSub is enabling a whole set of Distributed Real Time applications using CRDT (Conflict-Free Replicated Data Types). It is still going through heavy research (and hacking) and we invite you to join the conversation at [research-CRDT](https://github.com/ipfs/research-CRDT). Here is a list of some of the exciting examples:
- [PubSub Room](https://github.com/ipfs-labs/ipfs-pubsub-room)
- [Live DB - A always in Sync DB using CRDT](https://github.com/ipfs-labs/ipfs-live-db)
- [IIIF Annotations over IPFS, CRDT and libp2p](https://www.youtube.com/watch?v=hmAniA6g9D0&feature=youtu.be&t=10m40s)
- [orbit.chat - p2p chat application, fully running in the browser with js-ipfs, js-libp2p and orbit-db](http://orbit.chat/)

38
examples/transports/1.js Normal file
View File

@ -0,0 +1,38 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const defaultsDeep = require('@nodeutils/defaults-deep')
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [
TCP
]
}
}
super(defaultsDeep(_options, defaults))
}
}
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({ peerInfo: peerInfo })
node.start(cb)
}
], (err) => {
if (err) { throw err }
console.log('node has started (true/false):', node.isStarted())
console.log('listening on:')
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
})

68
examples/transports/2.js Normal file
View File

@ -0,0 +1,68 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const defaultsDeep = require('@nodeutils/defaults-deep')
const parallel = require('async/parallel')
const pull = require('pull-stream')
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [
TCP
]
}
}
super(defaultsDeep(_options, defaults))
}
}
function createNode (callback) {
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({ peerInfo: peerInfo })
node.start(cb)
}
], (err) => callback(err, node))
}
function printAddrs (node, number) {
console.log('node %s is listening on:', number)
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
}
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
printAddrs(node1, '1')
printAddrs(node2, '2')
node2.handle('/print', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
if (err) { throw err }
pull(pull.values(['Hello', ' ', 'p2p', ' ', 'world', '!']), conn)
})
})

93
examples/transports/3.js Normal file
View File

@ -0,0 +1,93 @@
'use strict'
const libp2p = require('../../')
const TCP = require('libp2p-tcp')
const WebSockets = require('libp2p-websockets')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const defaultsDeep = require('@nodeutils/defaults-deep')
const parallel = require('async/parallel')
const pull = require('pull-stream')
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [
TCP,
WebSockets
]
}
}
super(defaultsDeep(_options, defaults))
}
}
function createNode (addrs, callback) {
if (!Array.isArray(addrs)) {
addrs = [addrs]
}
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
addrs.forEach((addr) => peerInfo.multiaddrs.add(addr))
node = new MyBundle({ peerInfo: peerInfo })
node.start(cb)
}
], (err) => callback(err, node))
}
function printAddrs (node, number) {
console.log('node %s is listening on:', number)
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
}
function print (protocol, conn) {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
}
parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', cb),
(cb) => createNode(['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws'], cb),
(cb) => createNode('/ip4/127.0.0.1/tcp/20000/ws', cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
const node3 = nodes[2]
printAddrs(node1, '1')
printAddrs(node2, '2')
printAddrs(node3, '3')
node1.handle('/print', print)
node2.handle('/print', print)
node3.handle('/print', print)
node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
if (err) { throw err }
pull(pull.values(['node 1 dialed to node 2 successfully']), conn)
})
node2.dialProtocol(node3.peerInfo, '/print', (err, conn) => {
if (err) { throw err }
pull(pull.values(['node 2 dialed to node 3 successfully']), conn)
})
node3.dialProtocol(node1.peerInfo, '/print', (err, conn) => {
if (err) {
console.log('node 3 failed to dial to node 1 with:', err.message)
}
})
})

View File

@ -0,0 +1,330 @@
# [Transports](http://libp2p.io/implementations/#transports)
libp2p doesn't make assumptions for you, instead, it enables you as the developer of the application to pick the modules you need to run your application, which can vary depending on the runtime you are executing. A libp2p node can use one or more Transports to dial and listen for Connections. These transports are modules that offer a clean interface for dialing and listening, defined by the [interface-transport](https://github.com/libp2p/interface-transport) specification. Some examples of possible transports are: TCP, UTP, WebRTC, QUIC, HTTP, Pigeon and so on.
A more complete definition of what is a transport can be found on the [interface-transport](https://github.com/libp2p/interface-transport) specification. A way to recognize a candidate transport is through the badge:
[![](https://raw.githubusercontent.com/diasdavid/interface-transport/master/img/badge.png)](https://raw.githubusercontent.com/diasdavid/interface-transport/master/img/badge.png)
## 1. Creating a libp2p Bundle with TCP
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 5 deps total, so go ahead and install all of them with:
```bash
> 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`.
First thing is to create our own bundle! Insert:
```JavaScript
'use strict'
const libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const PeerInfo = require('peer-info')
const waterfall = require('async/waterfall')
const defaultsDeep = require('@nodeutils/defaults-deep')
// This MyBundle class is your libp2p bundle packed with TCP
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
// modules is a JS object that will describe the components
// we want for our libp2p bundle
modules: {
transport: [
TCP
]
}
}
super(defaultsDeep(_options, defaults))
}
}
```
Now that we have our own MyBundle class that extends libp2p, let's create a node with it. We will use `async/waterfall` just for code structure, but you don't need to. Append to the same file:
```JavaScript
let node
waterfall([
// First we create a PeerInfo object, which will pack the
// info about our peer. Creating a PeerInfo is an async
// operation because we use the WebCrypto API
// (yeei Universal JS)
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
// To signall the addresses we want to be available, we use
// the multiaddr format, a self describable address
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
// Now we can create a node with that PeerInfo object
node = new MyBundle({ peerInfo: peerInfo })
// Last, we start the node!
node.start(cb)
}
], (err) => {
if (err) { throw err }
// At this point the node has started
console.log('node has started (true/false):', node.isStarted())
// And we can print the now listening addresses.
// If you are familiar with TCP, you might have noticed
// that we specified the node to listen in 0.0.0.0 and port
// 0, which means "listen in any network interface and pick
// a port for me
console.log('listening on:')
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
})
```
Running this should result in something like:
```bash
> node 1.js
node has started (true/false): true
listening on:
/ip4/127.0.0.1/tcp/61329/ipfs/QmW2cKTakTYqbQkUzBTEGXgWYFj1YEPeUndE1YWs6CBzDQ
/ip4/192.168.2.156/tcp/61329/ipfs/QmW2cKTakTYqbQkUzBTEGXgWYFj1YEPeUndE1YWs6CBzDQ
```
That `QmW2cKTakTYqbQkUzBTEGXgWYFj1YEPeUndE1YWs6CBzDQ` is the PeerId that was created during the PeerInfo generation.
## 2. Dialing from one node to another node
Now that we have our bundle, let's create two nodes and make them dial to each other! You can find the complete solution at [2.js](./2.js).
For this step, we will need one more dependency.
```bash
> 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.
```JavaScript
function createNode (callback) {
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({ peerInfo: peerInfo })
node.start(cb)
}
], (err) => callback(err, node))
}
function printAddrs (node, number) {
console.log('node %s is listening on:', number)
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. We already added `async` as a dependency, but still need to import `async/parallel`:
```js
const parallel = require('async/parallel')
```
Then,
```js
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
printAddrs(node1, '1')
printAddrs(node2, '2')
node2.handle('/print', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
})
node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
if (err) { throw err }
pull(pull.values(['Hello', ' ', 'p2p', ' ', 'world', '!']), conn)
})
})
```
The result should be look like:
```bash
> node 2.js
node 1 is listening on:
/ip4/127.0.0.1/tcp/62279/ipfs/QmeM4wNWv1uci7UJjUXZYfvcy9uqAbw7G9icuxdqy88Mj9
/ip4/192.168.2.156/tcp/62279/ipfs/QmeM4wNWv1uci7UJjUXZYfvcy9uqAbw7G9icuxdqy88Mj9
node 2 is listening on:
/ip4/127.0.0.1/tcp/62278/ipfs/QmWp58xJgzbouNJcyiNNTpZuqQCJU8jf6ixc7TZT9xEZhV
/ip4/192.168.2.156/tcp/62278/ipfs/QmWp58xJgzbouNJcyiNNTpZuqQCJU8jf6ixc7TZT9xEZhV
Hello p2p world!
```
## 3. Using multiple transports
Next, we want to be available in multiple transports to increase our chances of having common transports in the network. A simple scenario, a node running in the browser only has access to HTTP, WebSockets and WebRTC since the browser doesn't let you open any other kind of transport, for this node to dial to some other node, that other node needs to share a common transport.
What we are going to do in this step is to create 3 nodes, one with TCP, another with TCP+WebSockets and another one with just WebSockets. The full solution can be found on [3.js](./3.js).
In this example, we will need to also install `libp2p-websockets`, go ahead and install:
```bash
> npm install libp2p-websockets
```
We want to create 3 nodes, one with TCP, one with TCP+WebSockets and one with just WebSockets. We need to update our `MyBundle` class to contemplate WebSockets as well:
```JavaScript
const WebSockets = require('libp2p-websockets')
// ...
class MyBundle extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [
TCP,
WebSockets
]
}
}
super(defaultsDeep(_options, defaults))
}
}
```
Now that we have our bundle ready, let's upgrade our createNode function to enable us to pick the addrs in which a node will start a listener.
```JavaScript
function createNode (addrs, callback) {
if (!Array.isArray(addrs)) {
addrs = [addrs]
}
let node
waterfall([
(cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
addrs.forEach((addr) => peerInfo.multiaddrs.add(addr))
node = new MyBundle({ peerInfo: peerInfo })
node.start(cb)
}
], (err) => callback(err, node))
}
```
As a rule, a libp2p node will only be capable of using a transport if: a) it has the module for it and b) it was given a multiaddr to listen on. The only exception to this rule is WebSockets in the browser, where a node can dial out, but unfortunately cannot open a socket.
Let's update our flow to create nodes and see how they behave when dialing to each other:
```JavaScript
parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', cb),
// Here we add an extra multiaddr that has a /ws at the end, this means that we want
// to create a TCP socket, but mount it as WebSockets instead.
(cb) => createNode(['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws'], cb),
(cb) => createNode('/ip4/127.0.0.1/tcp/20000/ws', cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
const node3 = nodes[2]
printAddrs(node1, '1')
printAddrs(node2, '2')
printAddrs(node3, '3')
node1.handle('/print', print)
node2.handle('/print', print)
node3.handle('/print', print)
// node 1 (TCP) dials to node 2 (TCP+WebSockets)
node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
if (err) { throw err }
pull(pull.values(['node 1 dialed to node 2 successfully']), conn)
})
// node 2 (TCP+WebSockets) dials to node 2 (WebSockets)
node2.dialProtocol(node3.peerInfo, '/print', (err, conn) => {
if (err) { throw err }
pull(pull.values(['node 2 dialed to node 3 successfully']), conn)
})
// node 3 (WebSockets) attempts to dial to node 1 (TCP)
node3.dialProtocol(node1.peerInfo, '/print', (err, conn) => {
if (err) {
console.log('node 3 failed to dial to node 1 with:', err.message)
}
})
})
```
`print` is a function created using the code from 2.js, but factored into its own function to save lines, here it is:
```JavaScript
function print (protocol, conn) {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
)
}
```
If everything was set correctly, you now should see the following after you run the script:
```Bash
> node 3.js
node 1 is listening on:
/ip4/127.0.0.1/tcp/62620/ipfs/QmWpWmcVJkF6EpmAaVDauku8g1uFGuxPsGP35XZp9GYEqs
/ip4/192.168.2.156/tcp/62620/ipfs/QmWpWmcVJkF6EpmAaVDauku8g1uFGuxPsGP35XZp9GYEqs
node 2 is listening on:
/ip4/127.0.0.1/tcp/10000/ws/ipfs/QmWAQtWdzWXibgfyc7WRHhhv6MdqVKzXvyfSTnN2aAvixX
/ip4/127.0.0.1/tcp/62619/ipfs/QmWAQtWdzWXibgfyc7WRHhhv6MdqVKzXvyfSTnN2aAvixX
/ip4/192.168.2.156/tcp/62619/ipfs/QmWAQtWdzWXibgfyc7WRHhhv6MdqVKzXvyfSTnN2aAvixX
node 3 is listening on:
/ip4/127.0.0.1/tcp/20000/ws/ipfs/QmVq1PWh3VSDYdFqYMtqp4YQyXcrH27N7968tGdM1VQPj1
node 3 failed to dial to node 1 with: No available transport to dial to
node 1 dialed to node 2 successfully
node 2 dialed to node 3 successfully
```
As expected, we created 3 nodes, node 1 with TCP, node 2 with TCP+WebSockets and node 3 with just WebSockets. node 1 -> node 2 and node 2 -> node 3 managed to dial correctly because they shared a common transport, however, node 3 -> node 1 failed because they didn't share any.
## 4. How to create a new libp2p transport
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 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.
Hope this tutorial was useful. We are always looking to improve it, so contributions are welcome!

View File

@ -1,16 +1,20 @@
{
"name": "libp2p",
"version": "0.8.0",
"description": "JavaScript Skeleton for libp2p bundles",
"version": "0.22.0",
"description": "JavaScript base class for libp2p bundles",
"leadMaintainer": "David Dias <daviddias@ipfs.io>",
"main": "src/index.js",
"scripts": {
"test": "aegir-test node",
"lint": "aegir-lint",
"release": "aegir-release node",
"release-minor": "aegir-release --type minor",
"release-major": "aegir-release --type major",
"coverage": "aegir-coverage",
"coverage-publish": "aegir-coverage publish"
"lint": "aegir lint",
"build": "aegir build",
"test": "aegir test -t node -t browser",
"test:node": "aegir test -t node",
"test:browser": "aegir test -t browser",
"release": "aegir release -t node -t browser",
"release-minor": "aegir release --type minor -t node -t browser",
"release-major": "aegir release --type major -t node -t browser",
"coverage": "aegir coverage",
"coverage-publish": "aegir coverage --provider coveralls"
},
"repository": {
"type": "git",
@ -20,39 +24,90 @@
"IPFS"
],
"engines": {
"node": ">=4.0.0",
"node": ">=6.0.0",
"npm": ">=3.0.0"
},
"pre-commit": [
"lint",
"test"
],
"author": "David Dias <daviddias@ipfs.io>",
"license": "MIT",
"bugs": {
"url": "https://github.com/libp2p/js-libp2p/issues"
},
"homepage": "https://github.com/libp2p/js-libp2p",
"devDependencies": {
"aegir": "^11.0.1",
"chai": "^3.5.0",
"dirty-chai": "^1.2.2",
"pre-commit": "^1.2.2"
"browser": {
"joi": "joi-browser"
},
"dependencies": {
"libp2p-ping": "~0.3.2",
"libp2p-swarm": "~0.29.0",
"mafmt": "^2.1.8",
"multiaddr": "^2.3.0",
"peer-book": "~0.4.0",
"peer-id": "~0.8.6",
"peer-info": "~0.9.2"
"async": "^2.6.1",
"joi": "^13.4.0",
"joi-browser": "^13.4.0",
"libp2p-connection-manager": "~0.0.2",
"libp2p-floodsub": "~0.15.0",
"libp2p-ping": "~0.8.0",
"libp2p-switch": "~0.40.4",
"libp2p-websockets": "~0.12.0",
"mafmt": "^6.0.0",
"multiaddr": "^5.0.0",
"peer-book": "~0.8.0",
"peer-id": "~0.10.7",
"peer-info": "~0.14.1"
},
"devDependencies": {
"@nodeutils/defaults-deep": "^1.1.0",
"aegir": "^14.0.0",
"chai": "^4.1.2",
"cids": "~0.5.3",
"dirty-chai": "^2.0.1",
"electron-webrtc": "~0.3.0",
"libp2p-circuit": "~0.2.0",
"libp2p-kad-dht": "~0.10.0",
"libp2p-mdns": "~0.12.0",
"libp2p-mplex": "~0.8.0",
"libp2p-railing": "~0.9.2",
"libp2p-secio": "~0.10.0",
"libp2p-spdy": "~0.12.1",
"libp2p-tcp": "~0.12.0",
"libp2p-webrtc-star": "~0.15.3",
"libp2p-websocket-star": "~0.8.1",
"libp2p-websocket-star-rendezvous": "~0.2.3",
"lodash.times": "^4.3.2",
"pull-goodbye": "0.0.2",
"pull-serializer": "~0.3.2",
"pull-stream": "^3.6.8",
"sinon": "^6.0.1",
"wrtc": "~0.1.6"
},
"contributors": [
"Alan Shaw <alan@tableflip.io>",
"Chris Bratlien <chrisbratlien@gmail.com>",
"Chris Dostert <chrisdostert@users.noreply.github.com>",
"Daijiro Wachi <daijiro.wachi@gmail.com>",
"David Dias <daviddias.p@gmail.com>",
"Diogo Silva <fsdiogo@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"Elven <mon.samuel@qq.com>",
"Florian-Merle <florian.david.merle@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>",
"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>",
"Maciej Krüger <mkg20001@gmail.com>",
"Nuno Nogueira <nunofmn@gmail.com>",
"Pedro Teixeira <pedro@protocol.ai>",
"Pedro Teixeira <i@pgte.me>",
"RasmusErik Voel Jensen <github@solsort.com>",
"Richard Littauer <richard.littauer@gmail.com>",
"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>",
"mayerwin <mayerwin@users.noreply.github.com>"
"mayerwin <mayerwin@users.noreply.github.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)
})
})

47
src/config.js Normal file
View File

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

@ -1,81 +1,105 @@
'use strict'
const Swarm = require('libp2p-swarm')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const mafmt = require('mafmt')
const PeerBook = require('peer-book')
const multiaddr = require('multiaddr')
const EventEmitter = require('events').EventEmitter
const assert = require('assert')
const each = require('async/each')
const series = require('async/series')
const parallel = require('async/parallel')
const PeerBook = require('peer-book')
const Switch = require('libp2p-switch')
const Ping = require('libp2p-ping')
const setImmediate = require('async/setImmediate')
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
const OFFLINE_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 {
constructor (_modules, _peerInfo, _peerBook, _options) {
constructor (_options) {
super()
assert(_modules, 'requires modules to equip libp2p with features')
assert(_peerInfo, 'requires a PeerInfo instance')
// validateConfig will ensure the config is correct,
// and add default values where appropriate
_options = validateConfig(_options)
this.modules = _modules
this.peerInfo = _peerInfo
this.peerBook = _peerBook || new PeerBook()
this.isOnline = false
this.peerInfo = _options.peerInfo
this.peerBook = _options.peerBook || new PeerBook()
this.swarm = new Swarm(this.peerInfo, this.peerBook)
this._modules = _options.modules
this._config = _options.config
this._isStarted = false
this._transport = [] // Transport instances/references
this._discovery = [] // Discovery service instances/references
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
if (this.modules.connection.muxer) {
let muxers = this.modules.connection.muxer
muxers = Array.isArray(muxers) ? muxers : [muxers]
muxers.forEach((muxer) => {
this.swarm.connection.addStreamMuxer(muxer)
})
if (this._modules.streamMuxer) {
let muxers = this._modules.streamMuxer
muxers.forEach((muxer) => this._switch.connection.addStreamMuxer(muxer))
// If muxer exists, we can use Identify
this.swarm.connection.reuse()
// If muxer exists
// we can use Identify
this._switch.connection.reuse()
// we can use Relay for listening/dialing
this._switch.connection.enableCircuitRelay(this._config.relay)
// Received incommind dial and muxer upgrade happened,
// Received incomming dial and muxer upgrade happened,
// reuse this muxed connection
this.swarm.on('peer-mux-established', (peerInfo) => {
this._switch.on('peer-mux-established', (peerInfo) => {
this.emit('peer:connect', peerInfo)
this.peerBook.put(peerInfo)
})
this.swarm.on('peer-mux-closed', (peerInfo) => {
this._switch.on('peer-mux-closed', (peerInfo) => {
this.emit('peer:disconnect', peerInfo)
})
}
// Attach crypto channels
if (this.modules.connection.crypto) {
let cryptos = this.modules.connection.crypto
cryptos = Array.isArray(cryptos) ? cryptos : [cryptos]
if (this._modules.connEncryption) {
let cryptos = this._modules.connEncryption
cryptos.forEach((crypto) => {
this.swarm.connection.crypto(crypto.tag, crypto.encrypt)
this._switch.connection.crypto(crypto.tag, crypto.encrypt)
})
}
// Attach discovery mechanisms
if (this.modules.discovery) {
let discoveries = this.modules.discovery
discoveries = Array.isArray(discoveries) ? discoveries : [discoveries]
discoveries.forEach((discovery) => {
discovery.on('peer', (peerInfo) => this.emit('peer:discovery', peerInfo))
// dht provided components (peerRouting, contentRouting, dht)
if (this._config.EXPERIMENTAL.dht) {
const DHT = this._modules.dht
this._dht = new DHT(this._switch, {
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
})
}
// enable/disable pubsub
if (this._config.EXPERIMENTAL.pubsub) {
this.pubsub = pubsub(this)
}
// Attach remaining APIs
this.peerRouting = peerRouting(this)
this.contentRouting = contentRouting(this)
this.dht = dht(this)
this._getPeerInfo = getPeerInfo(this)
// Mount default protocols
Ping.mount(this.swarm)
// Not fully implemented in js-libp2p yet
this.routing = undefined
this.records = undefined
Ping.mount(this._switch)
}
/*
@ -83,20 +107,17 @@ class Node extends EventEmitter {
* - create listeners on the multiaddrs the Peer wants to listen
*/
start (callback) {
if (!this.modules.transport) {
if (!this._modules.transport) {
return callback(new Error('no transports were present'))
}
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
const maOld = []
const maNew = []
this.peerInfo.multiaddrs.forEach((ma) => {
if (!mafmt.IPFS.matches(ma)) {
this.peerInfo.multiaddrs.toArray().forEach((ma) => {
if (!ma.getPeerId()) {
maOld.push(ma)
maNew.push(ma.encapsulate('/ipfs/' + this.peerInfo.id.toB58String()))
}
@ -105,130 +126,213 @@ class Node extends EventEmitter {
const multiaddrs = this.peerInfo.multiaddrs.toArray()
transports.forEach((transport) => {
if (transport.filter(multiaddrs).length > 0) {
this.swarm.transport.add(
transport.tag || transport.constructor.name, transport)
} else if (transport.constructor &&
transport.constructor.name === 'WebSockets') {
// TODO find a cleaner way to signal that a transport is always
// used for dialing, even if no listener
ws = transport
this._modules.transport.forEach((Transport) => {
let t
if (typeof Transport === 'function') {
t = new Transport()
} else {
t = 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)
})
this.swarm.listen((err) => {
if (err) {
return callback(err)
}
if (ws) {
this.swarm.transport.add(ws.tag || ws.constructor.name, ws)
}
series([
(cb) => this._switch.start(cb),
(cb) => {
if (ws) {
// always add dialing on websockets
this._switch.transport.add(ws.tag || ws.constructor.name, ws)
}
this.isOnline = true
// all transports need to be setup before discover starts
if (this._modules.peerDiscovery) {
each(this._modules.peerDiscovery, (D, _cb) => {
let config = {}
if (this.modules.discovery) {
this.modules.discovery.forEach((discovery) => {
setImmediate(() => discovery.start(() => {}))
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) => {
// TODO: chicken-and-egg problem #1:
// have to set started here because DHT requires libp2p is already started
this._isStarted = true
if (this._dht) {
this._dht.start(cb)
} else {
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) => {
// detect which multiaddrs we don't have a transport for and remove them
const multiaddrs = this.peerInfo.multiaddrs.toArray()
multiaddrs.forEach((multiaddr) => {
if (!multiaddr.toString().match(/\/p2p-circuit($|\/)/) &&
!this._transport.find((transport) => transport.filter(multiaddr).length > 0)) {
this.peerInfo.multiaddrs.delete(multiaddr)
}
})
cb()
},
(cb) => {
this.emit('start')
cb()
}
callback()
})
], callback)
}
/*
* Stop the libp2p node by closing its listeners and open connections
*/
stop (callback) {
this.isOnline = false
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) => {
if (this._dht) {
return this._dht.stop(cb)
}
cb()
},
(cb) => this._switch.stop(cb),
(cb) => {
this.emit('stop')
cb()
}
], (err) => {
this._isStarted = false
callback(err)
})
}
if (this.modules.discovery) {
this.modules.discovery.forEach((discovery) => {
setImmediate(() => discovery.stop(() => {}))
isStarted () {
return this._isStarted
}
dial (peer, callback) {
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
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()
})
}
this.swarm.close(callback)
})
}
isOn () {
return this.isOnline
}
ping (peer, callback) {
assert(this.isOn(), OFFLINE_ERROR_MESSAGE)
const peerInfo = this._getPeerInfo(peer)
callback(null, new Ping(this.swarm, peerInfo))
}
dial (peer, protocol, callback) {
assert(this.isOn(), OFFLINE_ERROR_MESSAGE)
dialProtocol (peer, protocol, callback) {
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
if (typeof protocol === 'function') {
callback = protocol
protocol = undefined
}
let peerInfo
try {
peerInfo = this._getPeerInfo(peer)
} catch (err) {
return callback(err)
}
this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { return callback(err) }
this.swarm.dial(peerInfo, protocol, (err, conn) => {
if (err) {
return callback(err)
}
this.peerBook.put(peerInfo)
callback(null, conn)
this._switch.dial(peerInfo, protocol, (err, conn) => {
if (err) { return callback(err) }
this.peerBook.put(peerInfo)
callback(null, conn)
})
})
}
hangUp (peer, callback) {
assert(this.isOn(), OFFLINE_ERROR_MESSAGE)
const peerInfo = this._getPeerInfo(peer)
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
this.swarm.hangUp(peerInfo, callback)
this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { return callback(err) }
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) {
this.swarm.handle(protocol, handlerFunc, matchFunc)
this._switch.handle(protocol, handlerFunc, matchFunc)
}
unhandle (protocol) {
this.swarm.unhandle(protocol)
}
/*
* Helper method to check the data type of peer and convert it to PeerInfo
*/
_getPeerInfo (peer) {
let p
if (PeerInfo.isPeerInfo(peer)) {
p = peer
} 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)
} else if (PeerId.isPeerId(peer)) {
const peerIdB58Str = peer.toB58String()
try {
p = this.peerBook.get(peerIdB58Str)
} catch (err) {
// TODO this is where PeerRouting comes into place
throw new Error('No knowledge about: ' + peerIdB58Str)
}
} else {
throw new Error('peer type not recognized')
}
return p
this._switch.unhandle(protocol)
}
}

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)
}
}
}

3
test/browser.js Normal file
View File

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

217
test/circuit-relay.node.js Normal file
View File

@ -0,0 +1,217 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect
const sinon = require('sinon')
const waterfall = require('async/waterfall')
const series = require('async/series')
const parallel = require('async/parallel')
const Circuit = require('libp2p-circuit')
const multiaddr = require('multiaddr')
const createNode = require('./utils/create-node')
const tryEcho = require('./utils/try-echo')
const echo = require('./utils/echo')
describe('circuit relay', () => {
let handlerSpies = []
let relayNode1
let relayNode2
let nodeWS1
let nodeWS2
let nodeTCP1
let nodeTCP2
function setupNode (addrs, options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}
options = options || {}
return createNode(addrs, options, (err, node) => {
expect(err).to.not.exist()
node.handle('/echo/1.0.0', echo)
node.start((err) => {
expect(err).to.not.exist()
handlerSpies.push(sinon.spy(
node._switch.transports[Circuit.tag].listeners[0].hopHandler, 'handle'
))
callback(node)
})
})
}
before(function (done) {
this.timeout(20 * 1000)
waterfall([
// set up passive relay
(cb) => setupNode([
'/ip4/0.0.0.0/tcp/0/ws',
'/ip4/0.0.0.0/tcp/0'
], {
config: {
relay: {
enabled: true,
hop: {
enabled: true,
active: false // passive relay
}
}
}
}, (node) => {
relayNode1 = node
cb()
}),
// setup active relay
(cb) => setupNode([
'/ip4/0.0.0.0/tcp/0/ws',
'/ip4/0.0.0.0/tcp/0'
], {
config: {
relay: {
enabled: true,
hop: {
enabled: true,
active: false // passive relay
}
}
}
}, (node) => {
relayNode2 = node
cb()
}),
// setup node with WS
(cb) => setupNode([
'/ip4/0.0.0.0/tcp/0/ws'
], {
config: {
relay: {
enabled: true
}
}
}, (node) => {
nodeWS1 = node
cb()
}),
// setup node with WS
(cb) => setupNode([
'/ip4/0.0.0.0/tcp/0/ws'
], {
config: {
relay: {
enabled: true
}
}
}, (node) => {
nodeWS2 = node
cb()
}),
// set up node with TCP and listening on relay1
(cb) => setupNode([
'/ip4/0.0.0.0/tcp/0',
`/ipfs/${relayNode1.peerInfo.id.toB58String()}/p2p-circuit`
], {
config: {
relay: {
enabled: true
}
}
}, (node) => {
nodeTCP1 = node
cb()
}),
// set up node with TCP and listening on relay2 over TCP transport
(cb) => setupNode([
'/ip4/0.0.0.0/tcp/0',
`/ip4/0.0.0.0/tcp/0/ipfs/${relayNode2.peerInfo.id.toB58String()}/p2p-circuit`
], {
config: {
relay: {
enabled: true
}
}
}, (node) => {
nodeTCP2 = node
cb()
})
], (err) => {
expect(err).to.not.exist()
series([
(cb) => nodeWS1.dial(relayNode1.peerInfo, cb),
(cb) => nodeWS1.dial(relayNode2.peerInfo, cb),
(cb) => nodeTCP1.dial(relayNode1.peerInfo, cb),
(cb) => nodeTCP2.dial(relayNode2.peerInfo, cb)
], done)
})
})
after((done) => {
parallel([
(cb) => relayNode1.stop(cb),
(cb) => relayNode2.stop(cb),
(cb) => nodeWS1.stop(cb),
(cb) => nodeWS2.stop(cb),
(cb) => nodeTCP1.stop(cb),
(cb) => nodeTCP2.stop(cb)
], done)
})
describe('any relay', function () {
this.timeout(20 * 1000)
it('dial from WS1 to TCP1 over any R', (done) => {
nodeWS1.dialProtocol(nodeTCP1.peerInfo, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist()
expect(conn).to.exist()
tryEcho(conn, done)
})
})
it('fail to dial - no R from WS2 to TCP1', (done) => {
nodeWS2.dialProtocol(nodeTCP2.peerInfo, '/echo/1.0.0', (err, conn) => {
expect(err).to.exist()
expect(conn).to.not.exist()
done()
})
})
})
describe('explicit relay', function () {
this.timeout(20 * 1000)
it('dial from WS1 to TCP1 over R1', (done) => {
nodeWS1.dialProtocol(nodeTCP1.peerInfo, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist()
expect(conn).to.exist()
tryEcho(conn, () => {
const addr = multiaddr(handlerSpies[0].args[2][0].dstPeer.addrs[0]).toString()
expect(addr).to.equal(`/ipfs/${nodeTCP1.peerInfo.id.toB58String()}`)
done()
})
})
})
it('dial from WS1 to TCP2 over R2', (done) => {
nodeWS1.dialProtocol(nodeTCP2.peerInfo, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist()
expect(conn).to.exist()
tryEcho(conn, () => {
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

@ -0,0 +1,94 @@
/* eslint-env mocha */
/* eslint max-nested-callbacks: ["error", 8] */
'use strict'
const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect
const parallel = require('async/parallel')
const _times = require('lodash.times')
const CID = require('cids')
const createNode = require('./utils/create-node')
describe('.contentRouting', () => {
let nodeA
let nodeB
let nodeC
let nodeD
let nodeE
before(function (done) {
this.timeout(5 * 1000)
const tasks = _times(5, () => (cb) => {
createNode('/ip4/0.0.0.0/tcp/0', {
config: {
EXPERIMENTAL: {
dht: true
}
}
}, (err, node) => {
expect(err).to.not.exist()
node.start((err) => cb(err, node))
})
})
parallel(tasks, (err, nodes) => {
expect(err).to.not.exist()
nodeA = nodes[0]
nodeB = nodes[1]
nodeC = nodes[2]
nodeD = nodes[3]
nodeE = nodes[4]
parallel([
(cb) => nodeA.dial(nodeB.peerInfo, cb),
(cb) => nodeB.dial(nodeC.peerInfo, cb),
(cb) => nodeC.dial(nodeD.peerInfo, cb),
(cb) => nodeD.dial(nodeE.peerInfo, cb),
(cb) => nodeE.dial(nodeA.peerInfo, cb)
], done)
})
})
after((done) => {
parallel([
(cb) => nodeA.stop(cb),
(cb) => nodeB.stop(cb),
(cb) => nodeC.stop(cb),
(cb) => nodeD.stop(cb),
(cb) => nodeE.stop(cb)
], done)
})
describe('le ring', () => {
const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
it('let kbucket get filled', (done) => {
setTimeout(() => done(), 250)
})
it('nodeA.contentRouting.provide', (done) => {
nodeA.contentRouting.provide(cid, done)
})
it('nodeE.contentRouting.findProviders for existing record', (done) => {
nodeE.contentRouting.findProviders(cid, 5000, (err, providers) => {
expect(err).to.not.exist()
expect(providers).to.have.length.above(0)
done()
})
})
it('nodeC.contentRouting.findProviders for non existing record (timeout)', (done) => {
const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSnnnn')
nodeE.contentRouting.findProviders(cid, 5000, (err, providers) => {
expect(err).to.not.exist()
expect(providers).to.have.length(0)
done()
})
})
})
})

5
test/fixtures/test-data/test-id.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"id": "QmaG17D4kfTB2RNUCr16bSfVvUVt2Xn3rPYeqQDvnVcXFr",
"privKey": "CAASqAkwggSkAgEAAoIBAQDBpXRrSLoVhP8C4YI0nm+YTb7UIe+xT9dwaMzKcGsH2zzz1lfxl54e1XNO+6Ut+If5jswpydgHhn9nGPod53sUIR2m+BiHOAH/Blgfa1nUKUkspts1MH3z5ZaO6Xo336Y0Uaw7UqfeIzKliTM6bpev2XIHyu0v/VJ2mylzfbDLMWqZs/shE3xwCJCD/PxoVpTlr/SzzF7MdgDMxkyiC3iLZ5Zkm+baPbi3mpKM0ue25Thedcc0KFjhQrjBfy5FPamrsMn5fnnoHwnQl9u7UWigzeC+7X+38EML1sVrV37ExxHPtM6882Ivjc7VN6zFHOHD2c9eVfbShkIf8YkVQUcFAgMBAAECggEAVE1mgGo58LJknml0WNn8tS5rfEiF5AhhPyOwvBTy04nDYFgZEykxgjTkrSbqgzfmYmOjSDICJUyNXGHISYqDz4CXOyBY9U0RuWeWp58BjVan75N4bRB+VNbHk9HbDkYEQlSoCW9ze0aRfvVa4v5QdRLSDMhwN+stokrsYcX/WIWYTM2e2jW+qQOzS8SJl7wYsgtd3WikrxwXkRL3sCMHEcgcPhoKacoD5Yr9cB0IC5vzhu4t/WMa+N2UEndcKGAbXsh8kA7BPFM6lqnEpOHpWEVEAYasAwFGUvUN9GwhtqpaNNS2sG6Nrz95cC99Nqx58uIXcTAJm3Fh/WfKJ6I1xQKBgQD+g7A5OSWw+i/zhTKVPJg93/eohViL0dGZT9Tf0/VslsFl00FwnZmBKA6BJ6ZL3hD00OcqIL3m6EzZ4q38U97XZxf2OUsPPJtl+Avqtlk16AHRHB9I17LGXJ30xZRkxL665oLms0D2T4NIZZX/uVMoS18lRvCZj1aEYQFCrZYgowKBgQDCxtA695S0vl6E3Q4G6MrDZK+2JqjaGL0XTnpHWiAjnk2lnV2CCZnWpEHT+ebF2fWx5nYQo5sugc6bS+4k9jRNUgxf2sQieZYCBjbnjCEVrPTm/dPTkaw1CQ/ox5/R1/Elbw8vteF9uUAvR0FL8Ss1Dqw6B2SxdTowxMy6qQ7sNwKBgG2N3eMj2DeP2egm45kdliK8L2yYyX6V+HTXyjf2kuQFGIZuIvMIw7S2u1eY65ooon/fFEIsCdJFGB+J1X6R05BAzi2sh8StP+7qkKadi1UK4w1R352JS2jbIRrlmXSuw7LL2njXnBTqMQaOw7xp14O2vePb32EaNBGTd+ltsvulAoGBALGIc4370oA4MIDb2Ag2MXKNmJbnf+piuB/BOTVGEZtFlDKLUArR43W/+/xRgKX/97FyhVS/OxfV21Kzj9oCy0NasMrB5RojRraLoYnFsPZH0mWlIGlsEtG4c9bR9XtYX4WmR+pN1r04mCc/xGWK6b4PpK2zxXT2i9ad2pmctGxbAoGBAIcp0UML5QCqvLmcob2/6PCRaYAxJBb9lDqOHredMgQih2hGnHFCyKk9eBAbFf/KN0guJTBDaAJRclcxsLLn7rV6grMNt+0EUepm7tWT0z5j8gNGbGGhuGDdqcmfJTc2EMdQrfhzYDN3rL1v3l+Ujwla2khL2ozE7SQ/KVeA1saY",
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBpXRrSLoVhP8C4YI0nm+YTb7UIe+xT9dwaMzKcGsH2zzz1lfxl54e1XNO+6Ut+If5jswpydgHhn9nGPod53sUIR2m+BiHOAH/Blgfa1nUKUkspts1MH3z5ZaO6Xo336Y0Uaw7UqfeIzKliTM6bpev2XIHyu0v/VJ2mylzfbDLMWqZs/shE3xwCJCD/PxoVpTlr/SzzF7MdgDMxkyiC3iLZ5Zkm+baPbi3mpKM0ue25Thedcc0KFjhQrjBfy5FPamrsMn5fnnoHwnQl9u7UWigzeC+7X+38EML1sVrV37ExxHPtM6882Ivjc7VN6zFHOHD2c9eVfbShkIf8YkVQUcFAgMBAAE="
}

5
test/fixtures/test-peer.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"id": "Qmex1SSsueWFsUfjdkugJ5zhcnjddAt8TxcnDLUXKD9Sx7",
"privKey": "CAASqAkwggSkAgEAAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAECggEAdBUzV/GaQ0nmoQrWvOnUxmFIho7kCjkh1NwnNVPNc+Msa1r7pcI9wJNPwap8j1w4L/cZuYhOJgcg+o2mWFiuULKZ4F9Ro/M89gZ038457g2/2pPu43c/Xoi/2YcAHXg0Gr+OCe2zCIyITBWKAFqyAzL6DubAxrJW2Ezj1LrZ+EZgMyzbh/go/eEGSJaaGkINeAkY144DqDWWWvzyhKhryipsGkZGEkVy9xJgMEI3ipVvuPez2XAvoyyeuinBBLe+Z2vY5G50XXzbIMhIQGLncHf9MwTv6wt1ilyOSLOXK0BoQbB76J3R3is5dSULXXP9r8VocjLBEkmBuf4FXAKzoQKBgQDNNS4F1XE1gxD8LPkL+aB/hi6eVHVPhr+w0I/9ATikcLGeUfBM2Gd6cZRPFtNVrv1p6ZF1D1UyGDknGbDBSQd9wLUgb0fDoo3jKYMGWq6G+VvaP5rzWQeBV8YV2EhSmUk1i6kiYe2ZE8WyrPie7iwpQIY60e2A8Ly0GKZiBZUcHQKBgQC9YDAVsGnEHFVFkTDpvw5HwEzCgTb2A3NgkGY3rTYZ7L6AFjqCYmUwFB8Fmbyc4kdFWNh8wfmq5Qrvl49NtaeukiqWKUUlB8uPdztB1P0IahA2ks0owStZlRifmwfgYyMd4xE17lhaOgQQJZZPxmP0F6mdOvb3YJafNURCdMS51wKBgEvvIM+h0tmFXXSjQ6kNvzlRMtD92ccKysYn9xAdMpOO6/r0wSH+dhQWEVZO0PcE4NsfReb2PIVj90ojtIdhebcr5xpQc1LORQjJJKXmSmzBux6AqNrhl+hhzXfp56FA/Zkly/lgGWaqrV5XqUxOP+Mn8EO1yNgMvRc7g94DyNB1AoGBAKLBuXHalXwDsdHBUB2Eo3xNLGt6bEcRfia+0+sEBdxQGQWylQScFkU09dh1YaIf44sZKa5HdBFJGpYCVxo9hmjFnK5Dt/Z0daHOonIY4INLzLVqg8KECoLKXkhGEIXsDjFQhukn+G1LMVTDSSU055DQiWjlVX4UWD9qo0jOXIkvAoGBAMP50p2X6PsWWZUuuR7i1JOJHRyQZPWdHh9p8SSLnCtEpHYZfJr4INXNmhnSiB/3TUnHix2vVKjosjMTCk/CjfzXV2H41WPOLZ2/Pi3SxCicWIRj4kCcWhkEuIF2jGkg1+jmNiCl/zNMaBOAIP3QbDPtqOWbYlPd2YIzdj6WQ6R4",
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAE="
}

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

@ -0,0 +1,39 @@
/* 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')
describe('multiaddr trim', () => {
it('non used multiaddrs get trimmed', (done) => {
let node
series([
(cb) => createNode([
'/ip4/0.0.0.0/tcp/999/wss/p2p-webrtc-direct',
'/ip4/127.0.0.1/tcp/55555/ws',
'/ip4/0.0.0.0/tcp/0/'
], (err, _node) => {
expect(err).to.not.exist()
node = _node
const multiaddrs = node.peerInfo.multiaddrs.toArray()
expect(multiaddrs).to.have.length(3)
cb()
}),
(cb) => node.start(cb)
], (err) => {
expect(err).to.not.exist()
const multiaddrs = node.peerInfo.multiaddrs.toArray()
expect(multiaddrs.length).to.at.least(2)
expect(multiaddrs[0].toString())
.to.match(/^\/ip4\/127\.0\.0\.1\/tcp\/[0-9]+\/ws\/ipfs\/\w+$/)
node.stop(done)
})
})
})

11
test/node.js Normal file
View File

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

312
test/peer-discovery.node.js Normal file
View File

@ -0,0 +1,312 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect
const sinon = require('sinon')
const signalling = require('libp2p-webrtc-star/src/sig-server')
const parallel = require('async/parallel')
const crypto = require('crypto')
const createNode = require('./utils/create-node')
const echo = require('./utils/echo')
describe('peer discovery', () => {
let nodeA
let nodeB
let port = 24642
let ss
function setup (options) {
before((done) => {
port++
parallel([
(cb) => {
signalling.start({ port: port }, (err, server) => {
expect(err).to.not.exist()
ss = server
cb()
})
},
(cb) => createNode([
'/ip4/0.0.0.0/tcp/0',
`/ip4/127.0.0.1/tcp/${port}/ws/p2p-webrtc-star`
], options, (err, node) => {
expect(err).to.not.exist()
nodeA = node
node.handle('/echo/1.0.0', echo)
node.start(cb)
}),
(cb) => createNode([
'/ip4/0.0.0.0/tcp/0',
`/ip4/127.0.0.1/tcp/${port}/ws/p2p-webrtc-star`
], options, (err, node) => {
expect(err).to.not.exist()
nodeB = node
node.handle('/echo/1.0.0', echo)
node.start(cb)
})
], done)
})
after((done) => {
parallel([
(cb) => nodeA.stop(cb),
(cb) => nodeB.stop(cb),
(cb) => ss.stop(cb)
], 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', () => {
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) {
this.timeout(15 * 1000)
nodeA.once('peer:discovery', (peerInfo) => {
expect(nodeB.peerInfo.id.toB58String())
.to.eql(peerInfo.id.toB58String())
done()
})
})
})
// TODO needs a delay (this test is already long)
describe.skip('WebRTCStar', () => {
setup({
config: {
peerDiscovery: {
webRTCStar: {
enabled: true
}
}
}
})
it('find a peer', function (done) {
this.timeout(15 * 1000)
nodeA.once('peer:discovery', (peerInfo) => {
expect(nodeB.peerInfo.id.toB58String())
.to.eql(peerInfo.id.toB58String())
done()
})
})
})
describe('MulticastDNS + WebRTCStar', () => {
setup({
config: {
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) {
this.timeout(15 * 1000)
nodeA.once('peer:discovery', (peerInfo) => {
expect(nodeB.peerInfo.id.toB58String())
.to.eql(peerInfo.id.toB58String())
done()
})
})
})
})

99
test/peer-routing.node.js Normal file
View File

@ -0,0 +1,99 @@
/* 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 _times = require('lodash.times')
const createNode = require('./utils/create-node')
describe('.peerRouting', () => {
let nodeA
let nodeB
let nodeC
let nodeD
let nodeE
before(function (done) {
this.timeout(5 * 1000)
const tasks = _times(5, () => (cb) => {
createNode('/ip4/0.0.0.0/tcp/0', {
config: {
EXPERIMENTAL: {
dht: true
}
}
}, (err, node) => {
expect(err).to.not.exist()
node.start((err) => cb(err, node))
})
})
parallel(tasks, (err, nodes) => {
expect(err).to.not.exist()
nodeA = nodes[0]
nodeB = nodes[1]
nodeC = nodes[2]
nodeD = nodes[3]
nodeE = nodes[4]
parallel([
(cb) => nodeA.dial(nodeB.peerInfo, cb),
(cb) => nodeB.dial(nodeC.peerInfo, cb),
(cb) => nodeC.dial(nodeD.peerInfo, cb),
(cb) => nodeD.dial(nodeE.peerInfo, cb),
(cb) => nodeE.dial(nodeA.peerInfo, cb)
], done)
})
})
after((done) => {
parallel([
(cb) => nodeA.stop(cb),
(cb) => nodeB.stop(cb),
(cb) => nodeC.stop(cb),
(cb) => nodeD.stop(cb),
(cb) => nodeE.stop(cb)
], done)
})
describe('el ring', () => {
it('let kbucket get filled', (done) => {
setTimeout(() => done(), 250)
})
it('nodeA.dial by Id to node C', (done) => {
nodeA.dial(nodeC.peerInfo.id, (err) => {
expect(err).to.not.exist()
done()
})
})
it('nodeB.dial by Id to node D', (done) => {
nodeB.dial(nodeD.peerInfo.id, (err) => {
expect(err).to.not.exist()
done()
})
})
it('nodeC.dial by Id to node E', (done) => {
nodeC.dial(nodeE.peerInfo.id, (err) => {
expect(err).to.not.exist()
done()
})
})
it('nodeB.peerRouting.findPeer(nodeE.peerInfo.id)', (done) => {
nodeB.peerRouting.findPeer(nodeE.peerInfo.id, (err, peerInfo) => {
expect(err).to.not.exist()
expect(nodeE.peerInfo.id.toB58String()).to.equal(peerInfo.id.toB58String())
done()
})
})
})
})

Some files were not shown because too many files have changed in this diff Show More