Compare commits

...

326 Commits

Author SHA1 Message Date
15bdb795a4 chore: release version v0.24.2 2018-12-04 17:10:52 +01:00
7d78728f54 chore: update contributors 2018-12-04 17:10:52 +01:00
53ed3bdb99 fix: use symbol instead of constructor name (#292) 2018-12-04 16:04:17 +01:00
ae513887f5 chore: release version v0.24.1 2018-12-03 12:56:51 +01:00
7c78faa171 chore: update contributors 2018-12-03 12:56:50 +01:00
7d12eb9e26 feat: allow configurable validators and selectors to the dht (#288)
* feat: allow configurable validators and selectors to the dht

* chore: remove fallback
2018-11-29 15:10:23 +01:00
581a1de472 docs: merge example links: Peer and Content Routing (#285) 2018-11-20 13:40:38 +01:00
288ac17954 chore: update changelog 2018-11-16 14:31:13 +01:00
2e4459b315 chore: release version v0.24.0 2018-11-16 14:12:01 +01:00
2a5232b541 chore: update contributors 2018-11-16 14:12:01 +01:00
44915b3723 0.24.0-rc.3 2018-11-15 18:59:22 +01:00
64bba57255 chore: add publish files to package 2018-11-15 18:58:40 +01:00
88ebd1fc09 test: improve multiaddr trim test 2018-11-15 18:01:31 +01:00
92cd591da4 chore: update deps 2018-11-15 18:01:31 +01:00
320d84f541 docs: update examples (#271)
* docs: fix examples

* chore: remove non jenkins ci files

* chore: update libp2p-spdy

* chore: update libp2p-spdy

* docs: update example language
2018-11-14 18:50:17 +01:00
970deec2a4 feat: add maxNumProviders to findprovs (#283)
* feat: add maxNumProviders to findprovs

* chore: upgrade libp2p-kad-dht
2018-11-13 11:46:51 +01:00
714b6ec2b9 fix: improve get peer info errors 2018-11-12 19:26:40 +01:00
f71fdfdf35 feat: conditionally emit errors
test: add tests for emit override
2018-11-12 19:26:04 +01:00
e92053da9a Chore/update deps (#279)
* chore: update deps

* test: remove unneeded timeout

* chore: make nock a dev dep, it was not
2018-11-06 22:52:16 +01:00
17b5f73b3d fix: dont call callback before it's properly set 2018-11-05 15:43:59 +01:00
c18d2a4147 0.24.0-rc.2 2018-11-01 15:10:36 +01:00
f1baa7e0b1 chore: update switch version 2018-11-01 15:10:35 +01:00
4abc868ab3 0.24.0-rc.1 2018-11-01 15:10:35 +01:00
40e840d5fd feat: add datastore to config 2018-10-31 14:43:16 +01:00
9518eb44b3 docs: improve browser example connectability (#240) 2018-10-31 14:42:24 +01:00
0b75f99d75 feat: make libp2p a state machine (#257)
* docs: add events to readme
2018-10-19 17:37:34 +02:00
686379efb0 feat: enable relay by default (no hop) (#254)
docs: update readme default relay
2018-10-19 16:31:40 +02:00
a95389a28e feat: add delegated peer and content routing support (#242)
* feat: allow for configuring content and peer routing

* feat: support multiple peer and content routing modules

* docs: add delegated routing example
2018-10-19 16:28:28 +02:00
3226632d83 docs: add lead maintainer to package table 2018-10-07 20:42:12 +03:00
dd934b9690 fix: start kad dht random walk (#251)
* fix: start kad dht random walk

* chore: added tests and stop random walk

* chore: allows to disable discovery for dht

* chore: upgrade kad-dht version
2018-10-04 14:40:32 +02:00
cef3c8b5cc chore: change dependency name from libp2p-railing to libp2p-bootstrap (#256)
* chore: change dependency name from libp2p-railing to libp2p-bootstrap

* fix: changed require on tests
2018-10-02 15:09:22 +02:00
eedb20e9a3 chore: upgrade libp2p-mplex 2018-10-01 17:26:41 +02:00
e51260434c docs: add 2018 q4 okrs 2018-09-25 14:03:15 +02:00
7e6c9eeb38 test: increase timeout for many writes 2018-09-24 17:24:32 +02:00
c537140fbc test: improve deterministic browser tests
test: remove unneeded test timeout
2018-09-24 17:24:32 +02:00
3b7c4b5eb0 chore: update deps 2018-09-24 17:24:32 +02:00
4460e8246c fix: dht get options 2018-09-24 17:03:43 +02:00
a63432e24b feat: use package-table vs custom script
docs: add note on how to generate table
2018-09-24 15:42:16 +02:00
69f7264123 fix: add maxtimeout to dht get (#248)
* fix: add maxtimeout to dht get

* chore: add tests
2018-09-19 19:31:36 +02:00
e052021397 docs: Improve wording on the Readme 2018-08-22 09:12:58 +02:00
fdd714ee60 docs: update API for node creation (#236)
The API changed recently, update the README to reflect that.
2018-08-14 14:23:55 +02:00
65e7223ce0 docs: make example from README copy & pastable (#235)
The example in the README can now be used with simply copy & pasting,
no errors will be thrown.

Fixes #234.
2018-08-14 14:17:18 +02:00
eddec7d2e4 docs: fix example in README (#233)
The JSON in the example was missing some commas.
2018-08-13 17:10:40 +02:00
7b6c921d36 chore: release version v0.23.1 2018-08-13 15:21:08 +02:00
05c16e4262 chore: update contributors 2018-08-13 15:21:07 +02:00
c8a86db310 fix: callback with error for invalid or non-peer multiaddr (#232)
License: MIT
Signed-off-by: Alan Shaw <alan@tableflip.io>
2018-08-13 15:11:21 +02:00
ce29902691 docs: moved to ipfs/pm
See https://github.com/ipfs/pm/blob/master/JS_CORE_DEV_MGMT.md
2018-08-11 13:08:53 +02:00
0b729621db chore: update lead maintainer 2018-07-27 16:04:15 +02:00
e6e5b872dc chore: release version v0.23.0 2018-07-27 14:01:36 +02:00
550af3cbde chore: update contributors 2018-07-27 14:01:36 +02:00
5aa9ebbbe8 chore: update deps 2018-07-27 13:57:15 +02:00
7f68a13433 docs: updte release template - no sharness or interop tests for now :( 2018-07-24 19:55:59 +02:00
2b7cc55c88 feat: add check for protector and enforced pnet
fix: update protector config and tests
docs: add private network info to the readme
test: fix an issue with config
2018-07-24 19:53:53 +02:00
40739e9639 docs: update release template 2018-07-22 19:32:07 +01:00
6106915923 fix: start and stop connection manager with libp2p
test: add test to verify libp2p starts and stops the right things
test: add test for verifying disabled modules
fix: linting
2018-07-22 19:29:19 +01:00
d9059dbad9 chore: update deps 2018-07-13 18:10:03 +02:00
187d584086 docs: update logo url 2018-07-06 19:16:21 +02:00
7502ba86a5 docs: better packages table (#218) 2018-07-05 17:17:47 +02:00
cc51fa59f9 docs: add missing commas to readme example (#216) 2018-07-05 11:47:00 +02:00
1c10842bd3 docs: 2018 Q3 OKRs (#207)
* docs: 2018 Q3 OKRs placeholder

* docs: first draft

* Update OKR.md

* Update OKR.md

* Update OKR.md

* Update OKR.md

* Update OKR.md

* Update OKR.md

* adding 3 more KRs

@jacobheun what you think about taking on these?

* Update OKR.md

* Update OKR.md

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

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

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

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

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

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

Update the README accordingly.

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

* fix: use only wrtc 0.1.1

* chore: update webrtc-star

* chore: update circle

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

* fix(pdd): case-sensitive names

peer-B.json does not _actually_ exist

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

* c

* transport series 2

* transports story 3

* the ipfs-bundle story 1
2017-12-18 10:23:34 +00:00
582f8bed2e docs: fix links on examples peer and content routing (#141) 2017-12-18 09:53:30 +00:00
59de0da19c chore: release version v0.14.3 2017-12-15 08:25:08 +00:00
0caf600c9a chore: update contributors 2017-12-15 08:25:08 +00:00
a4943b4509 chore: update npm ignore 2017-12-15 08:24:49 +00:00
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
babb90fe17 chore: release version v0.8.0 2017-03-31 16:19:24 +01:00
2f9070a725 chore: update contributors 2017-03-31 16:19:24 +01:00
a48ec65961 Merge pull request #85 from libp2p/feat/advanced-peer-book
fix: correct method on peer-book
2017-03-31 16:18:37 +01:00
031ecb3fe7 fix: correct method on peer-book 2017-03-31 16:17:59 +01:00
c6bdd869be Merge pull request #76 from libp2p/feat/advanced-peer-book
fix: avoid deleting nodes from peerBook
2017-03-31 16:17:18 +01:00
dee0340806 chore: update to latest peer-info, peer-book and libp2p-swarm 2017-03-31 15:52:20 +01:00
a4b41b0f9a feat: not remove peer from peerBook on disconnect 2017-03-30 07:20:45 +01:00
291e79fc99 fix: addition of ipfs id appendix must come before transport filtering 2017-03-29 21:02:37 +01:00
59ea9c388f feat: append peer id to multiaddr if not there 2017-03-29 20:02:54 +01:00
300936f2f3 fix: avoid deleting nodes from peerBook 2017-03-29 18:03:00 +01:00
9f4ec2a915 chore: release version v0.7.0 2017-03-29 08:03:58 +01:00
c7f4368007 chore: update contributors 2017-03-29 08:03:58 +01:00
9ff04779f5 Merge pull request #75 from libp2p/feat/update-peer-events
feat: Feat/update peer events
2017-03-29 08:03:13 +01:00
f73c045767 feat: update events to conform with #74 2017-03-29 07:32:46 +01:00
8840c9b250 docs: document new events 2017-03-29 07:32:29 +01:00
df4c99df4a chore: release version v0.6.2 2017-03-28 12:44:52 +01:00
6e05a0b239 chore: update contributors 2017-03-28 12:44:52 +01:00
c781abc30c chore: deps 2017-03-28 12:43:00 +01:00
6a66931ba4 chore: release version v0.6.1 2017-03-27 16:48:53 +01:00
426fc27b26 chore: update contributors 2017-03-27 16:48:52 +01:00
d6325dee90 chore: update deps 2017-03-27 16:48:41 +01:00
f74bc0596d chore: release version v0.6.0 2017-03-27 15:48:46 +01:00
8f9e82b0fb chore: update contributors 2017-03-27 15:48:46 +01:00
9724fa190e chore: update deps 2017-03-27 15:46:10 +01:00
721da9ab41 Merge pull request #64 from libp2p/feat/new-api
feat: Formalising the API
2017-03-27 15:45:41 +01:00
d27bd2b912 cr 2017-03-27 15:43:08 +01:00
2c23d9a718 fix: last touches 2017-03-27 14:42:05 +01:00
a6623c1ba2 feat: new super simplified API 2017-03-27 12:26:34 +01:00
5d4d94e75f docs: finish API documentation 2017-03-24 18:20:14 +00:00
a4b97b9627 docs: libp2p API 2017-03-24 17:07:08 +00:00
3584209947 docs: update readme, add base class extending example and start listing API calls 2017-03-24 16:02:57 +00:00
b5209fc456 chore: release version v0.5.5 2017-03-21 17:06:34 +01:00
932d8772da chore: update contributors 2017-03-21 17:06:33 +01:00
baee2b7945 chore(deps): update 2017-03-21 17:04:56 +01:00
153f8884fa Merge pull request #54 from libp2p/greenkeeper-aegir-10.0.0
Update aegir to version 10.0.0 🚀
2017-02-13 18:05:01 -08:00
13355bf179 chore(package): update aegir to version 10.0.0
https://greenkeeper.io/
2017-02-07 18:45:33 +01:00
126 changed files with 9590 additions and 467 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

421
CHANGELOG.md Normal file
View File

@ -0,0 +1,421 @@
<a name="0.24.2"></a>
## [0.24.2](https://github.com/libp2p/js-libp2p/compare/v0.24.1...v0.24.2) (2018-12-04)
### Bug Fixes
* use symbol instead of constructor name ([#292](https://github.com/libp2p/js-libp2p/issues/292)) ([53ed3bd](https://github.com/libp2p/js-libp2p/commit/53ed3bd))
<a name="0.24.1"></a>
## [0.24.1](https://github.com/libp2p/js-libp2p/compare/v0.24.0...v0.24.1) (2018-12-03)
### Features
* allow configurable validators and selectors to the dht ([#288](https://github.com/libp2p/js-libp2p/issues/288)) ([7d12eb9](https://github.com/libp2p/js-libp2p/commit/7d12eb9))
<a name="0.24.0"></a>
# [0.24.0](https://github.com/libp2p/js-libp2p/compare/v0.24.0-rc.3...v0.24.0) (2018-11-16)
### Bug Fixes
* add maxtimeout to dht get ([#248](https://github.com/libp2p/js-libp2p/issues/248)) ([69f7264](https://github.com/libp2p/js-libp2p/commit/69f7264))
* dht get options ([4460e82](https://github.com/libp2p/js-libp2p/commit/4460e82))
* dont call callback before it's properly set ([17b5f73](https://github.com/libp2p/js-libp2p/commit/17b5f73))
* improve get peer info errors ([714b6ec](https://github.com/libp2p/js-libp2p/commit/714b6ec))
* start kad dht random walk ([#251](https://github.com/libp2p/js-libp2p/issues/251)) ([dd934b9](https://github.com/libp2p/js-libp2p/commit/dd934b9))
### Features
* add datastore to config ([40e840d](https://github.com/libp2p/js-libp2p/commit/40e840d))
* add delegated peer and content routing support ([#242](https://github.com/libp2p/js-libp2p/issues/242)) ([a95389a](https://github.com/libp2p/js-libp2p/commit/a95389a))
* add maxNumProviders to findprovs ([#283](https://github.com/libp2p/js-libp2p/issues/283)) ([970deec](https://github.com/libp2p/js-libp2p/commit/970deec))
* conditionally emit errors ([f71fdfd](https://github.com/libp2p/js-libp2p/commit/f71fdfd))
* enable relay by default (no hop) ([#254](https://github.com/libp2p/js-libp2p/issues/254)) ([686379e](https://github.com/libp2p/js-libp2p/commit/686379e))
* make libp2p a state machine ([#257](https://github.com/libp2p/js-libp2p/issues/257)) ([0b75f99](https://github.com/libp2p/js-libp2p/commit/0b75f99))
* use package-table vs custom script ([a63432e](https://github.com/libp2p/js-libp2p/commit/a63432e))
<a name="0.23.1"></a>
## [0.23.1](https://github.com/libp2p/js-libp2p/compare/v0.23.0...v0.23.1) (2018-08-13)
### Bug Fixes
* callback with error for invalid or non-peer multiaddr ([#232](https://github.com/libp2p/js-libp2p/issues/232)) ([c8a86db](https://github.com/libp2p/js-libp2p/commit/c8a86db))
<a name="0.23.0"></a>
# [0.23.0](https://github.com/libp2p/js-libp2p/compare/v0.22.0...v0.23.0) (2018-07-27)
### Bug Fixes
* start and stop connection manager with libp2p ([6106915](https://github.com/libp2p/js-libp2p/commit/6106915))
### Features
* add check for protector and enforced pnet ([2b7cc55](https://github.com/libp2p/js-libp2p/commit/2b7cc55))
<a name="0.22.0"></a>
# [0.22.0](https://github.com/libp2p/js-libp2p/compare/v0.21.0...v0.22.0) (2018-06-29)
### Bug Fixes
* add null property guards ([80f0b60](https://github.com/libp2p/js-libp2p/commit/80f0b60))
* do not mutate the config object ([ac5cacb](https://github.com/libp2p/js-libp2p/commit/ac5cacb))
* remove .only ([be9eafe](https://github.com/libp2p/js-libp2p/commit/be9eafe))
* remove peer discovery module config checks ([4ad70ef](https://github.com/libp2p/js-libp2p/commit/4ad70ef))
* typo in fixture and fail for correct reason ([1af5ba9](https://github.com/libp2p/js-libp2p/commit/1af5ba9))
### Features
* enable peer discovery modules by default ([e320854](https://github.com/libp2p/js-libp2p/commit/e320854))
<a name="0.21.0"></a>
# [0.21.0](https://github.com/libp2p/js-libp2p/compare/v0.20.4...v0.21.0) (2018-06-28)
### Bug Fixes
* lock wrtc to 0.1.1 ([6507379](https://github.com/libp2p/js-libp2p/commit/6507379))
### Features
* (BREAKING CHANGE) overhaul libp2p config and constructor ([6905f1b](https://github.com/libp2p/js-libp2p/commit/6905f1b))
* set and hook up libp2p-connection-manager ([#184](https://github.com/libp2p/js-libp2p/issues/184)) ([d597204](https://github.com/libp2p/js-libp2p/commit/d597204))
<a name="0.20.4"></a>
## [0.20.4](https://github.com/libp2p/js-libp2p/compare/v0.20.2...v0.20.4) (2018-04-30)
<a name="0.20.3"></a>
## [0.20.3](https://github.com/libp2p/js-libp2p/compare/v0.20.2...v0.20.3) (2018-04-30)
<a name="0.20.2"></a>
## [0.20.2](https://github.com/libp2p/js-libp2p/compare/v0.20.1...v0.20.2) (2018-04-10)
<a name="0.20.1"></a>
## [0.20.1](https://github.com/libp2p/js-libp2p/compare/v0.20.0...v0.20.1) (2018-04-10)
<a name="0.20.0"></a>
# [0.20.0](https://github.com/libp2p/js-libp2p/compare/v0.19.2...v0.20.0) (2018-04-06)
### Features
* use class-is for type checks ([bb0c990](https://github.com/libp2p/js-libp2p/commit/bb0c990))
<a name="0.19.2"></a>
## [0.19.2](https://github.com/libp2p/js-libp2p/compare/v0.19.0...v0.19.2) (2018-03-28)
<a name="0.19.1"></a>
## [0.19.1](https://github.com/libp2p/js-libp2p/compare/v0.19.0...v0.19.1) (2018-03-28)
<a name="0.19.0"></a>
# [0.19.0](https://github.com/libp2p/js-libp2p/compare/v0.18.0...v0.19.0) (2018-03-15)
<a name="0.18.0"></a>
# [0.18.0](https://github.com/libp2p/js-libp2p/compare/v0.17.0...v0.18.0) (2018-02-19)
<a name="0.17.0"></a>
# [0.17.0](https://github.com/libp2p/js-libp2p/compare/v0.16.5...v0.17.0) (2018-02-16)
### Bug Fixes
* use correct reference to floodSub ([947eaf1](https://github.com/libp2p/js-libp2p/commit/947eaf1))
### Features
* add pubsub to libp2p ([0c543b7](https://github.com/libp2p/js-libp2p/commit/0c543b7))
<a name="0.16.5"></a>
## [0.16.5](https://github.com/libp2p/js-libp2p/compare/v0.16.4...v0.16.5) (2018-02-14)
<a name="0.16.4"></a>
## [0.16.4](https://github.com/libp2p/js-libp2p/compare/v0.16.3...v0.16.4) (2018-02-09)
<a name="0.16.3"></a>
## [0.16.3](https://github.com/libp2p/js-libp2p/compare/v0.16.2...v0.16.3) (2018-02-08)
<a name="0.16.2"></a>
## [0.16.2](https://github.com/libp2p/js-libp2p/compare/v0.16.1...v0.16.2) (2018-02-07)
<a name="0.16.1"></a>
## [0.16.1](https://github.com/libp2p/js-libp2p/compare/v0.16.0...v0.16.1) (2018-02-07)
<a name="0.16.0"></a>
# [0.16.0](https://github.com/libp2p/js-libp2p/compare/v0.15.2...v0.16.0) (2018-02-07)
### Features
* add explicit error for case peer id not included in multiaddr ([#155](https://github.com/libp2p/js-libp2p/issues/155)) ([bd8a35a](https://github.com/libp2p/js-libp2p/commit/bd8a35a))
* dialProtocol and small refactor ([6651401](https://github.com/libp2p/js-libp2p/commit/6651401))
* use libp2p-switch ([23e8293](https://github.com/libp2p/js-libp2p/commit/23e8293))
<a name="0.15.2"></a>
## [0.15.2](https://github.com/libp2p/js-libp2p/compare/v0.15.1...v0.15.2) (2018-01-28)
<a name="0.15.1"></a>
## [0.15.1](https://github.com/libp2p/js-libp2p/compare/v0.15.0...v0.15.1) (2018-01-16)
### Bug Fixes
* typo in DHT setup ([#151](https://github.com/libp2p/js-libp2p/issues/151)) ([61bebd1](https://github.com/libp2p/js-libp2p/commit/61bebd1))
<a name="0.15.0"></a>
# [0.15.0](https://github.com/libp2p/js-libp2p/compare/v0.14.3...v0.15.0) (2018-01-07)
<a name="0.14.3"></a>
## [0.14.3](https://github.com/libp2p/js-libp2p/compare/v0.14.2...v0.14.3) (2017-12-15)
<a name="0.14.2"></a>
## [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)
### Bug Fixes
* addition of ipfs id appendix must come before transport filtering ([291e79f](https://github.com/libp2p/js-libp2p/commit/291e79f))
* avoid deleting nodes from peerBook ([300936f](https://github.com/libp2p/js-libp2p/commit/300936f))
* correct method on peer-book ([031ecb3](https://github.com/libp2p/js-libp2p/commit/031ecb3))
### Features
* append peer id to multiaddr if not there ([59ea9c3](https://github.com/libp2p/js-libp2p/commit/59ea9c3))
* not remove peer from peerBook on disconnect ([a4b41b0](https://github.com/libp2p/js-libp2p/commit/a4b41b0))
<a name="0.7.0"></a>
# [0.7.0](https://github.com/libp2p/js-libp2p/compare/v0.6.2...v0.7.0) (2017-03-29)
### Features
* update events to conform with [#74](https://github.com/libp2p/js-libp2p/issues/74) ([f73c045](https://github.com/libp2p/js-libp2p/commit/f73c045))
<a name="0.6.2"></a>
## [0.6.2](https://github.com/libp2p/js-libp2p/compare/v0.6.1...v0.6.2) (2017-03-28)
<a name="0.6.1"></a>
## [0.6.1](https://github.com/libp2p/js-libp2p/compare/v0.6.0...v0.6.1) (2017-03-27)
<a name="0.6.0"></a>
# [0.6.0](https://github.com/libp2p/js-libp2p/compare/v0.5.5...v0.6.0) (2017-03-27)
### Bug Fixes
* last touches ([2c23d9a](https://github.com/libp2p/js-libp2p/commit/2c23d9a))
### Features
* new super simplified API ([a6623c1](https://github.com/libp2p/js-libp2p/commit/a6623c1))
<a name="0.5.5"></a>
## [0.5.5](https://github.com/libp2p/js-libp2p/compare/v0.5.4...v0.5.5) (2017-03-21)

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.
-->

15
OKR.md Normal file
View File

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

588
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/black-bg-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://ci.ipfs.team/job/libp2p/job/js-libp2p/job/master/"><img src="https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p/master" /></a>
<a href="https://codecov.io/gh/libp2p/js-libp2p"><img src="https://codecov.io/gh/libp2p/js-libp2p/branch/master/graph/badge.svg"></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
[Jacob Heun](https://github.com/jacobheun/)
## Table of Contents
@ -15,7 +47,9 @@
- [Bundles](#bundles)
- [Usage](#usage)
- [Install](#install)
- [Usage](#usage)
- [API](#api)
- [Events](#events)
- [Development](#development)
- [Tests](#tests)
- [Packages](#packages)
@ -24,14 +58,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 +75,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,71 +90,511 @@ npm install --save libp2p
## Usage
> **Disclaimer - We haven't solidified [libp2p interface](https://github.com/libp2p/interface-libp2p) yet, it might change at anytime.**
### [Tutorials and Examples](/examples)
### Extending libp2p skeleton
You can find multiple examples on the [examples folder](/examples) that will guide you through using libp2p for several scenarios.
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).
### Creating your own libp2p bundle
### libp2p API
The libp2p module acts as a glue for every libp2p module that you can use top create your own libp2p bundle. Creating your own libp2p bundle gives you a lot of freedom when it comes to customize it with features and default setup. We recommend creating your own libp2p bundle for the app you are developing that takes in account your needs (e.g. for a browser working version of libp2p that acts as the network layer of IPFS, we have a built one that leverages the Browser transports).
Defined by [interface-libp2p](https://github.com/libp2p/interface-libp2p)
**Example:**
```JavaScript
// Creating a bundle that adds:
// transport: websockets + tcp
// 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 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')
const Protector = require('libp2p-pnet')
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
const DelegatedContentRouter = require('libp2p-delegated-content-routing')
class Node extends libp2p {
constructor (_options) {
const peerInfo = _options.peerInfo
const defaults = {
// The libp2p modules for this libp2p bundle
modules: {
transport: [
TCP,
new WS() // It can take instances too!
],
streamMuxer: [
SPDY,
MPLEX
],
connEncryption: [
SECIO
],
/** Encryption for private networks. Needs additional private key to work **/
// connProtector: new Protector(/*protector specific opts*/),
/** Enable custom content routers, such as delegated routing **/
// contentRouting: [
// new DelegatedContentRouter(peerInfo.id)
// ],
/** Enable custom peer routers, such as delegated routing **/
// peerRouting: [
// new DelegatedPeerRouter()
// ],
peerDiscovery: [
MulticastDNS
],
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.
},
relay: { // Circuit Relay options
enabled: true,
hop: {
enabled: false,
active: false
}
},
dht: {
kBucketSize: 20,
enabledDiscovery: true // Allows to disable discovery (enabled by default)
},
// Enable/Disable Experimental features
EXPERIMENTAL: { // Experimental features ("behind a flag")
pubsub: false,
dht: false
}
}
}
// 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, MPLEX, SECIO and MulticastDNS support.
```
### API
#### Create a Node - `new libp2p.Node(options)`
> Creates an instance of the libp2p.Node.
Required keys in the `options` object:
- `peerInfo`: instance of [PeerInfo][] that contains the [PeerId][], Keys and [multiaddrs][multiaddr] of the libp2p Node.
#### `libp2p.start(callback)`
> Start the libp2p Node.
`callback` following signature `function (err) {}`, where `err` is an Error in case starting the node fails.
#### `libp2p.stop(callback)`
> Stop the libp2p Node.
`callback` following signature `function (err) {}`, where `err` is an Error in case stopping the node fails.
#### `libp2p.dial(peer, callback)`
> Dials to another peer in the network, establishes the connection.
- `peer`: can be an instance of [PeerInfo][], [PeerId][], [multiaddr][], or a multiaddr string
- `callback` following signature `function (err, conn) {}`, 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.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` following signature `function (err, conn) {}`, 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.dialFSM(peer, protocol, callback)`
> Behaves like `.dial` and `.dialProtocol` but calls back with a Connection State Machine
- `peer`: can be an instance of [PeerInfo][], [PeerId][], [multiaddr][], or a multiaddr string
- `protocol`: an optional String that defines the protocol (e.g '/ipfs/bitswap/1.1.0')
- `callback`: following signature `function (err, connFSM) {}`, where `connFSM` is a [Connection State Machine](https://github.com/libp2p/js-libp2p-switch#connection-state-machine)
#### `libp2p.hangUp(peer, callback)`
> Closes an open connection with a peer, graciously.
- `peer`: can be an instance of [PeerInfo][], [PeerId][] or [multiaddr][]
`callback` following signature `function (err) {}`, where `err` is an Error in case stopping the node fails.
#### `libp2p.peerRouting.findPeer(id, options, callback)`
> Looks up for multiaddrs of a peer in the DHT
- `id`: instance of [PeerId][]
- `options`: object of options
- `options.maxTimeout`: Number milliseconds
#### `libp2p.contentRouting.findProviders(key, options, callback)`
- `key`: Buffer
- `options`: object of options
- `options.maxTimeout`: Number milliseconds
- `options.maxNumProviders` maximum number of providers to find
#### `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`: following 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)`
> Stop handling protocol
- `protocol`: String that defines the protocol (e.g '/ipfs/bitswap/1.1.0')
#### Events
##### `libp2p.on('start', () => {})`
> Libp2p has started, along with all its services.
##### `libp2p.on('stop', () => {})`
> Libp2p has stopped, along with all its services.
##### `libp2p.on('error', (err) => {})`
> An error has occurred
- `err`: instance of `Error`
##### `libp2p.on('peer:discovery', (peer) => {})`
> Peer has been discovered.
- `peer`: instance of [PeerInfo][]
##### `libp2p.on('peer:connect', (peer) => {})`
> We connected to a new peer
- `peer`: instance of [PeerInfo][]
##### `libp2p.on('peer:disconnect', (peer) => {})`
> We disconnected from Peer
- `peer`: instance of [PeerInfo][]
#### `libp2p.isStarted()`
> Check if libp2p is started
#### `libp2p.ping(peer [, options], callback)`
> Ping a node in the network
#### `libp2p.peerBook`
> PeerBook instance of the node
#### `libp2p.peerInfo`
> PeerInfo instance of the node
#### `libp2p.pubsub`
> Same API as IPFS PubSub, defined in the [CORE API Spec](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PUBSUB.md). Just replace `ipfs` by `libp2p` and you are golden.
---------------------
`DHT methods also exposed for the time being`
#### `libp2p.dht.put(key, value, callback)`
- `key`: Buffer
- `value`: Buffer
#### `libp2p.dht.get(key, options, callback)`
- `key`: Buffer
- `options`: object of options
- `options.maxTimeout`: Number milliseconds
#### `libp2p.dht.getMany(key, nVals, options, callback)`
- `key`: Buffer
- `nVals`: Number
- `options`: object of options
- `options.maxTimeout`: Number milliseconds
[PeerInfo]: https://github.com/libp2p/js-peer-info
[PeerId]: https://github.com/libp2p/js-peer-id
[PeerBook]: https://github.com/libp2p/js-peer-book
[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.
### Private Networks
#### Enforcement
Libp2p provides support for connection protection, such as for private networks. You can enforce network protection by setting the environment variable `LIBP2P_FORCE_PNET=1`. When this variable is on, if no protector is set via `options.connProtector`, Libp2p will throw an error upon creation.
#### Protectors
Some available network protectors:
* [libp2p-pnet](https://github.com/libp2p/js-libp2p-pnet)
## Development
## Linting
**Clone and install dependencies:**
```sh
> npm run lint
> 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
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) |
| **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** |
| [`interface-stream-muxer`](//github.com/libp2p/interface-stream-muxer) | [![npm](https://img.shields.io/npm/v/interface-stream-muxer.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-stream-muxer/releases) | [![Dependency Status](https://david-dm.org/libp2p/interface-stream-muxer.svg?style=flat-square)](https://david-dm.org/libp2p/interface-stream-muxer) | [![devDependency Status](https://david-dm.org/libp2p/interface-stream-muxer/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/interface-stream-muxer?type=dev) |
| [`libp2p-spdy`](//github.com/libp2p/js-libp2p-spdy) | [![npm](https://img.shields.io/npm/v/libp2p-spdy.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-spdy/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-spdy.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-spdy) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-spdy/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-spdy?type=dev) |
| [`libp2p-multiplex`](https://github.com/libp2p/js-libp2p-multiplex)
| **Discovery** |
| [`libp2p-mdns-discovery`](//github.com/libp2p/js-libp2p-mdns-discovery) | [![npm](https://img.shields.io/npm/v/libp2p-mdns-discovery.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-mdns-discovery/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-mdns-discovery.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mdns-discovery) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-mdns-discovery/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mdns-discovery?type=dev) |
| [`libp2p-railing`](//github.com/libp2p/js-libp2p-railing) | [![npm](https://img.shields.io/npm/v/libp2p-railing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-railing/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-railing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-railing) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-railing/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-railing?type=dev) |
| **Crypto Channels** |
| [`libp2p-secio`](//github.com/libp2p/js-libp2p-secio) | [![npm](https://img.shields.io/npm/v/libp2p-secio.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-secio/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-secio.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-secio) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-secio/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-secio?type=dev) |
| **Peer Routing** |
| [`libp2p-kad-routing`](//github.com/libp2p/js-libp2p-kad-routing) | [![npm](https://img.shields.io/npm/v/libp2p-kad-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-routing/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-kad-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-routing) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-kad-routing/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-routing?type=dev) |
| **Content Routing** |
| [`interface-record-store`](//github.com/libp2p/interface-record-store) | [![npm](https://img.shields.io/npm/v/interface-record-store.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-record-store/releases) | [![Dependency Status](https://david-dm.org/libp2p/interface-record-store.svg?style=flat-square)](https://david-dm.org/libp2p/interface-record-store) | [![devDependency Status](https://david-dm.org/libp2p/interface-record-store/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/interface-record-store?type=dev) |
| [`libp2p-record`](//github.com/libp2p/js-libp2p-record) | [![npm](https://img.shields.io/npm/v/libp2p-record.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-record/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-record.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-record) | [![devDependency Status](https://david-dm.org/libp2p/js-libp2p-record/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-record?type=dev) |
| [`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-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** |
| [`peer-book`](//github.com/libp2p/js-peer-book) | [![npm](https://img.shields.io/npm/v/peer-book.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-book/releases) | [![Dependency Status](https://david-dm.org/libp2p/js-peer-book.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-book) | [![devDependency Status](https://david-dm.org/libp2p/js-peer-book/dev-status.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-book?type=dev) |
| [`peer-id`](https://github.com/libp2p/js-peer-id)
> This table is generated using the module `package-table` with `package-table --data=package-list.json`.
| Package | Version | Deps | CI | Coverage | Lead Maintainer |
| ---------|---------|---------|---------|---------|--------- |
| **Libp2p** |
| [`interface-libp2p`](//github.com/libp2p/interface-libp2p) | [![npm](https://img.shields.io/npm/v/interface-libp2p.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-libp2p/releases) | [![Deps](https://david-dm.org/libp2p/interface-libp2p.svg?style=flat-square)](https://david-dm.org/libp2p/interface-libp2p) | N/A | [![codecov](https://codecov.io/gh/libp2p/interface-libp2p/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-libp2p) | N/A |
| [`libp2p`](//github.com/libp2p/js-libp2p) | [![npm](https://img.shields.io/npm/v/libp2p.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| **Connection** |
| [`interface-connection`](//github.com/libp2p/interface-connection) | [![npm](https://img.shields.io/npm/v/interface-connection.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-connection/releases) | [![Deps](https://david-dm.org/libp2p/interface-connection.svg?style=flat-square)](https://david-dm.org/libp2p/interface-connection) | N/A | [![codecov](https://codecov.io/gh/libp2p/interface-connection/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-connection) | N/A |
| **Transport** |
| [`interface-transport`](//github.com/libp2p/interface-transport) | [![npm](https://img.shields.io/npm/v/interface-transport.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-transport/releases) | [![Deps](https://david-dm.org/libp2p/interface-transport.svg?style=flat-square)](https://david-dm.org/libp2p/interface-transport) | N/A | [![codecov](https://codecov.io/gh/libp2p/interface-transport/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-transport) | N/A |
| [`libp2p-tcp`](//github.com/libp2p/js-libp2p-tcp) | [![npm](https://img.shields.io/npm/v/libp2p-tcp.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-tcp/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-tcp) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-tcp/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-tcp/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-tcp/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-tcp) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-udp`](//github.com/libp2p/js-libp2p-udp) | [![npm](https://img.shields.io/npm/v/libp2p-udp.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-udp/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-udp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-udp) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-udp/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-udp) | N/A |
| [`libp2p-udt`](//github.com/libp2p/js-libp2p-udt) | [![npm](https://img.shields.io/npm/v/libp2p-udt.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-udt/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-udt.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-udt) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-udt/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-udt) | N/A |
| [`libp2p-utp`](//github.com/libp2p/js-libp2p-utp) | [![npm](https://img.shields.io/npm/v/libp2p-utp.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-utp/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-utp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-utp) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-utp/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-utp/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-utp/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-utp) | N/A |
| [`libp2p-webrtc-direct`](//github.com/libp2p/js-libp2p-webrtc-direct) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-direct.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-direct/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-direct.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-direct) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-webrtc-direct/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-webrtc-direct/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-webrtc-star/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-webrtc-star/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-websockets`](//github.com/libp2p/js-libp2p-websockets) | [![npm](https://img.shields.io/npm/v/libp2p-websockets.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websockets/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websockets.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websockets) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websockets/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websockets) | N/A |
| [`libp2p-websocket-star`](//github.com/libp2p/js-libp2p-websocket-star) | [![npm](https://img.shields.io/npm/v/libp2p-websocket-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websocket-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websocket-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websocket-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websocket-star) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-websocket-star-rendezvous`](//github.com/libp2p/js-libp2p-websocket-star-rendezvous) | [![npm](https://img.shields.io/npm/v/libp2p-websocket-star-rendezvous.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websocket-star-rendezvous/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websocket-star-rendezvous.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star-rendezvous) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websocket-star-rendezvous/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websocket-star-rendezvous) | N/A |
| **Crypto Channels** |
| [`libp2p-secio`](//github.com/libp2p/js-libp2p-secio) | [![npm](https://img.shields.io/npm/v/libp2p-secio.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-secio/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-secio.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-secio) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-secio/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-secio) | N/A |
| **Stream Muxers** |
| [`interface-stream-muxer`](//github.com/libp2p/interface-stream-muxer) | [![npm](https://img.shields.io/npm/v/interface-stream-muxer.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-stream-muxer/releases) | [![Deps](https://david-dm.org/libp2p/interface-stream-muxer.svg?style=flat-square)](https://david-dm.org/libp2p/interface-stream-muxer) | N/A | [![codecov](https://codecov.io/gh/libp2p/interface-stream-muxer/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-stream-muxer) | N/A |
| [`libp2p-mplex`](//github.com/libp2p/js-libp2p-mplex) | [![npm](https://img.shields.io/npm/v/libp2p-mplex.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-mplex/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-mplex.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mplex) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-mplex/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-mplex) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-spdy`](//github.com/libp2p/js-libp2p-spdy) | [![npm](https://img.shields.io/npm/v/libp2p-spdy.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-spdy/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-spdy.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-spdy) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-spdy/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-spdy/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-spdy/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-spdy) | N/A |
| **Discovery** |
| [`interface-peer-discovery`](//github.com/libp2p/interface-peer-discovery) | [![npm](https://img.shields.io/npm/v/interface-peer-discovery.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-peer-discovery/releases) | [![Deps](https://david-dm.org/libp2p/interface-peer-discovery.svg?style=flat-square)](https://david-dm.org/libp2p/interface-peer-discovery) | N/A | [![codecov](https://codecov.io/gh/libp2p/interface-peer-discovery/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-peer-discovery) | N/A |
| [`libp2p-bootstrap`](//github.com/libp2p/js-libp2p-bootstrap) | [![npm](https://img.shields.io/npm/v/libp2p-bootstrap.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-bootstrap/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-bootstrap.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-bootstrap) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-bootstrap/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-bootstrap/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-bootstrap/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-bootstrap) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-kad-dht/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-kad-dht/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-mdns`](//github.com/libp2p/js-libp2p-mdns) | [![npm](https://img.shields.io/npm/v/libp2p-mdns.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-mdns/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-mdns.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mdns) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-mdns/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-mdns/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-mdns/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-mdns) | N/A |
| [`libp2p-rendezvous`](//github.com/libp2p/js-libp2p-rendezvous) | [![npm](https://img.shields.io/npm/v/libp2p-rendezvous.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-rendezvous/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-rendezvous.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-rendezvous) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-rendezvous/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-rendezvous) | N/A |
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-webrtc-star/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-webrtc-star/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-websocket-star`](//github.com/libp2p/js-libp2p-websocket-star) | [![npm](https://img.shields.io/npm/v/libp2p-websocket-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websocket-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websocket-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websocket-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websocket-star) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| **NAT Traversal** |
| [`libp2p-circuit`](//github.com/libp2p/js-libp2p-circuit) | [![npm](https://img.shields.io/npm/v/libp2p-circuit.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-circuit/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-circuit.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-circuit) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-circuit/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-circuit/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-circuit/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-circuit) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-nat-mngr`](//github.com/libp2p/js-libp2p-nat-mngr) | [![npm](https://img.shields.io/npm/v/libp2p-nat-mngr.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-nat-mngr/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-nat-mngr.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-nat-mngr) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-nat-mngr/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-nat-mngr) | N/A |
| **Data Types** |
| [`peer-book`](//github.com/libp2p/js-peer-book) | [![npm](https://img.shields.io/npm/v/peer-book.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-book/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-book.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-book) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-peer-book/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-peer-book) | [Pedro Teixeira](mailto:i@pgte.me) |
| [`peer-id`](//github.com/libp2p/js-peer-id) | [![npm](https://img.shields.io/npm/v/peer-id.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-id/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-id.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-id) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-peer-id/master)](https://ci.ipfs.team/job/libp2p/job/js-peer-id/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-peer-id/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-peer-id) | [Pedro Teixeira](mailto:i@pgte.me) |
| [`peer-info`](//github.com/libp2p/js-peer-info) | [![npm](https://img.shields.io/npm/v/peer-info.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-info/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-info.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-info) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-peer-info/master)](https://ci.ipfs.team/job/libp2p/job/js-peer-info/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-peer-info/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-peer-info) | N/A |
| **Content Routing** |
| [`interface-content-routing`](//github.com/libp2p/interface-content-routing) | [![npm](https://img.shields.io/npm/v/interface-content-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-content-routing/releases) | [![Deps](https://david-dm.org/libp2p/interface-content-routing.svg?style=flat-square)](https://david-dm.org/libp2p/interface-content-routing) | N/A | [![codecov](https://codecov.io/gh/libp2p/interface-content-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-content-routing) | N/A |
| [`libp2p-delegated-content-routing`](//github.com/libp2p/js-libp2p-delegated-content-routing) | [![npm](https://img.shields.io/npm/v/libp2p-delegated-content-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-delegated-content-routing/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-delegated-content-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-delegated-content-routing) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-kad-dht/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-kad-dht/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| **Peer Routing** |
| [`interface-peer-routing`](//github.com/libp2p/interface-peer-routing) | [![npm](https://img.shields.io/npm/v/interface-peer-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-peer-routing/releases) | [![Deps](https://david-dm.org/libp2p/interface-peer-routing.svg?style=flat-square)](https://david-dm.org/libp2p/interface-peer-routing) | N/A | [![codecov](https://codecov.io/gh/libp2p/interface-peer-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-peer-routing) | N/A |
| [`libp2p-delegated-peer-routing`](//github.com/libp2p/js-libp2p-delegated-peer-routing) | [![npm](https://img.shields.io/npm/v/libp2p-delegated-peer-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-delegated-peer-routing/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-kad-dht/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-kad-dht/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| **Record Store** |
| [`interface-record-store`](//github.com/libp2p/interface-record-store) | [![npm](https://img.shields.io/npm/v/interface-record-store.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-record-store/releases) | [![Deps](https://david-dm.org/libp2p/interface-record-store.svg?style=flat-square)](https://david-dm.org/libp2p/interface-record-store) | N/A | [![codecov](https://codecov.io/gh/libp2p/interface-record-store/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-record-store) | N/A |
| [`libp2p-record`](//github.com/libp2p/js-libp2p-record) | [![npm](https://img.shields.io/npm/v/libp2p-record.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-record/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-record.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-record) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-record/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-record/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-record/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-record) | N/A |
| **Generics** |
| [`libp2p-connection-manager`](//github.com/libp2p/js-libp2p-connection-manager) | [![npm](https://img.shields.io/npm/v/libp2p-connection-manager.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-connection-manager/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-connection-manager.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-connection-manager) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-connection-manager/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-connection-manager) | N/A |
| [`libp2p-crypto`](//github.com/libp2p/js-libp2p-crypto) | [![npm](https://img.shields.io/npm/v/libp2p-crypto.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-crypto/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-crypto.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-crypto/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-crypto/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-crypto/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-crypto) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
| [`libp2p-crypto-secp256k1`](//github.com/libp2p/js-libp2p-crypto-secp256k1) | [![npm](https://img.shields.io/npm/v/libp2p-crypto-secp256k1.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-crypto-secp256k1/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-crypto-secp256k1/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-crypto-secp256k1/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1) | N/A |
| [`libp2p-switch`](//github.com/libp2p/js-libp2p-switch) | [![npm](https://img.shields.io/npm/v/libp2p-switch.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-switch/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-switch.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-switch) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-switch/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-switch/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-switch/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-switch) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| **Extensions** |
| [`libp2p-floodsub`](//github.com/libp2p/js-libp2p-floodsub) | [![npm](https://img.shields.io/npm/v/libp2p-floodsub.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-floodsub/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-floodsub.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-floodsub) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-floodsub/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-floodsub) | N/A |
| [`libp2p-identify`](//github.com/libp2p/js-libp2p-identify) | [![npm](https://img.shields.io/npm/v/libp2p-identify.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-identify/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-identify.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-identify) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-identify/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-identify) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-keychain`](//github.com/libp2p/js-libp2p-keychain) | [![npm](https://img.shields.io/npm/v/libp2p-keychain.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-keychain/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-keychain.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-keychain) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-keychain/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-keychain) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-ping`](//github.com/libp2p/js-libp2p-ping) | [![npm](https://img.shields.io/npm/v/libp2p-ping.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-ping/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-ping.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-ping) | [![jenkins](https://ci.ipfs.team/buildStatus/icon?job=libp2p/js-libp2p-ping/master)](https://ci.ipfs.team/job/libp2p/job/js-libp2p-ping/job/master/) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-ping/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-ping) | N/A |
| [`libp2p-pnet`](//github.com/libp2p/js-libp2p-pnet) | [![npm](https://img.shields.io/npm/v/libp2p-pnet.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-pnet/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-pnet.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-pnet) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-pnet/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-pnet) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| **Utilities** |
| [`p2pcat`](//github.com/libp2p/js-p2pcat) | [![npm](https://img.shields.io/npm/v/p2pcat.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-p2pcat/releases) | [![Deps](https://david-dm.org/libp2p/js-p2pcat.svg?style=flat-square)](https://david-dm.org/libp2p/js-p2pcat) | N/A | [![codecov](https://codecov.io/gh/libp2p/js-p2pcat/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-p2pcat) | N/A |
## Contribute
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.
## License
[MIT](LICENSE) © David Dias
[MIT](LICENSE) © Protocol Labs

41
RELEASE.md Normal file
View File

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

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 +0,0 @@
machine:
node:
version: stable
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'
- sudo apt-get update
- sudo apt-get --only-upgrade install google-chrome-stable
- google-chrome --version

View File

@ -1,6 +1,24 @@
### 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 and 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 yourself 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-bootstrap')
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,49 @@
# Delegated Routing with Libp2p and IPFS
This example shows how to use delegated peer and content routing. The [Peer and Content Routing Example](../peer-and-content-routing) focuses
on the DHT implementation. This example takes that a step further and introduces delegated routing. Delegated routing is
especially useful when your libp2p node will have limited resources, making running a DHT impractical. It's
also highly useful if your node is generating content, but can't reliably be on the network. You can use delegate nodes
to provide content on your behalf.
The starting [Libp2p Bundle](./src/libp2p-bundle.js) in this example starts by disabling the DHT and adding the Delegated Peer and Content Routers.
Once you've completed the example, you should try enabled the DHT and see what kind of results you get! You can also enable the
various Peer Discovery modules and see the impact it has on your Peer count.
## Prerequisite
**NOTE**: This example is currently dependent on a clone of the [delegated routing support branch of go-ipfs](https://github.com/ipfs/go-ipfs/pull/4595).
## Running this example
1. Install IPFS locally if you dont already have it. [Install Guide](https://docs.ipfs.io/introduction/install/)
2. Run the IPFS daemon: `ipfs daemon`
3. The daemon will output a line about its API address, like `API server listening on /ip4/127.0.0.1/tcp/8080`
4. In another window output the addresses of the node: `ipfs id`. Make note of the websocket address, it will contain `/ws/` in the address.
5. In `./src/libp2p-bundle.js` check if the host and port of your node are correct, according to the previous step. If they are different, replace them.
6. In `./src/App.js` replace `BootstrapNode` with your nodes Websocket address from step 4.
7. Start this example:
```sh
npm install
npm start
```
This should open your browser to http://localhost:3000. If it does not, go ahead and do that now.
8. Your browser should show you connected to at least 1 peer.
### Finding Content via the Delegate
1. Add a file to your IPFS node. From this example root you can do `ipfs add ./README.md` to add the example readme.
2. Copy the hash from line 5, it will look something like *Qmf33vz4HJFkqgH7XPP1uA6atYKTX1BWQEQthzpKcAdeyZ*.
3. In the browser, paste the hash into the *Hash* field and hit `Find`. The readme contents should display.
This will do a few things:
* The delegate nodes api will be queried to find providers of the content
* The content will be fetched from the providers
* Since we now have the content, we tell the delegate node to fetch the content from us and become a provider
### Finding Peers via the Delegate
1. Get a list of your delegate nodes peer by querying the IPFS daemon: `ipfs swarm peers`
2. Copy one of the CIDs from the list of peer addresses, this will be the last portion of the address and will look something like `QmdoG8DpzYUZMVP5dGmgmigZwR1RE8Cf6SxMPg1SBXJAQ8`.
3. In your browser, paste the CID into the *Peer* field and hit `Find`.
4. You should see information about the peer including its addresses.

View File

@ -0,0 +1,23 @@
{
"name": "delegated-routing-example",
"version": "0.1.0",
"private": true,
"dependencies": {
"ipfs": "~0.32.2",
"libp2p": "../../",
"libp2p-delegated-content-routing": "~0.2.2",
"libp2p-delegated-peer-routing": "~0.2.2",
"libp2p-kad-dht": "~0.10.4",
"libp2p-mplex": "~0.8.0",
"libp2p-secio": "~0.10.0",
"libp2p-webrtc-star": "~0.15.5",
"libp2p-websocket-star": "~0.8.1",
"libp2p-websockets": "~0.12.0",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-scripts": "1.1.5"
},
"scripts": {
"start": "react-scripts start"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<title>Delegated Routing</title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>

View File

@ -0,0 +1,67 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
section * {
margin: 10px;
}
header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.center {
text-align: center;
}
pre {
background-color: bisque;
min-height: 100px;
margin: 0px;
padding: 10px;
}
.loader {
text-align: center;
height: 64px;
margin-bottom: -64px;
}
.loading .lds-ripple {
display: inline-block;
position: relative;
width: 64px;
height: 64px;
}
.loading .lds-ripple div {
position: absolute;
border: 4px solid #000;
opacity: 1;
border-radius: 50%;
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
margin: auto;
}
.loading .lds-ripple div:nth-child(2) {
animation-delay: -0.5s;
}
@keyframes lds-ripple {
0% {
top: 28px;
left: 28px;
width: 0;
height: 0;
opacity: 1;
}
100% {
top: -1px;
left: -1px;
width: 58px;
height: 58px;
opacity: 0;
}
}

View File

@ -0,0 +1,153 @@
// eslint-disable-next-line
'use strict'
const React = require('react')
const Component = React.Component
const Ipfs = require('ipfs')
const libp2pBundle = require('./libp2p-bundle')
// require('./App.css')
const BootstrapNode = '/ip4/127.0.0.1/tcp/8081/ws/ipfs/QmdoG8DpzYUZMVP5dGmgmigZwR1RE8Cf6SxMPg1SBXJAQ8'
class App extends Component {
constructor (props) {
super(props)
this.state = {
peers: 0,
// This hash is the IPFS readme
hash: 'QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB',
// This peer is one of the Bootstrap nodes for IPFS
peer: 'QmV6kA2fB8kTr6jc3pL5zbNsjKbmPUHAPKKHRBYe1kDEyc',
isLoading: 0
}
this.peerInterval = null
this.handleHashChange = this.handleHashChange.bind(this)
this.handleHashSubmit = this.handleHashSubmit.bind(this)
this.handlePeerChange = this.handlePeerChange.bind(this)
this.handlePeerSubmit = this.handlePeerSubmit.bind(this)
}
handleHashChange (event) {
this.setState({
hash: event.target.value
})
}
handlePeerChange (event) {
this.setState({
peer: event.target.value
})
}
handleHashSubmit (event) {
event.preventDefault()
this.setState({
isLoading: this.state.isLoading + 1
})
this.ipfs.files.cat(this.state.hash, (err, data) => {
if (err) console.log('Error', err)
this.setState({
response: data.toString(),
isLoading: this.state.isLoading - 1
})
})
}
handlePeerSubmit (event) {
event.preventDefault()
this.setState({
isLoading: this.state.isLoading + 1
})
this.ipfs.dht.findpeer(this.state.peer, (err, results) => {
if (err) console.log('Error', err)
this.setState({
response: JSON.stringify(results, null, 2),
isLoading: this.state.isLoading - 1
})
})
}
componentDidMount () {
window.ipfs = this.ipfs = new Ipfs({
config: {
Addresses: {
Swarm: []
},
Discovery: {
MDNS: {
Enabled: false
},
webRTCStar: {
Enabled: false
}
},
Bootstrap: [
BootstrapNode
]
},
preload: {
enabled: false
},
libp2p: libp2pBundle
})
this.ipfs.on('ready', () => {
if (this.peerInterval) {
clearInterval(this.peerInterval)
}
this.ipfs.swarm.connect(BootstrapNode, (err) => {
if (err) {
console.log('Error connecting to the node', err)
}
console.log('Connected!')
})
this.peerInterval = setInterval(() => {
this.ipfs.swarm.peers((err, peers) => {
if (err) console.log(err)
if (peers) this.setState({peers: peers.length})
})
}, 2500)
})
}
render () {
return (
<div>
<header className="center">
<h1>Delegated Routing</h1>
<h2>There are currently {this.state.peers} peers.</h2>
</header>
<section className="center">
<form onSubmit={this.handleHashSubmit}>
<label>
Hash:
<input type="text" value={this.state.hash} onChange={this.handleHashChange} />
<input type="submit" value="Find" />
</label>
</form>
<form onSubmit={this.handlePeerSubmit}>
<label>
Peer:
<input type="text" value={this.state.peer} onChange={this.handlePeerChange} />
<input type="submit" value="Find" />
</label>
</form>
</section>
<section className={[this.state.isLoading > 0 ? 'loading' : '', 'loader'].join(' ')}>
<div className="lds-ripple"><div></div><div></div></div>
</section>
<section>
<pre>
{this.state.response}
</pre>
</section>
</div>
)
}
}
module.exports = App

View File

@ -0,0 +1,9 @@
// eslint-disable-next-line
'use strict'
const React = require('react') // eslint-disable-line no-unused-vars
const ReactDOM = require('react-dom')
const App = require('./App') // eslint-disable-line no-unused-vars
// require('index.css')
ReactDOM.render(<App />, document.getElementById('root'))

View File

@ -0,0 +1,78 @@
// eslint-disable-next-line
'use strict'
const Libp2p = require('libp2p')
const Websockets = require('libp2p-websockets')
const WebSocketStar = require('libp2p-websocket-star')
const WebRTCStar = require('libp2p-webrtc-star')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const KadDHT = require('libp2p-kad-dht')
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
const DelegatedContentRouter = require('libp2p-delegated-content-routing')
module.exports = ({peerInfo, peerBook}) => {
const wrtcstar = new WebRTCStar({id: peerInfo.id})
const wsstar = new WebSocketStar({id: peerInfo.id})
const delegatedApiOptions = {
host: '0.0.0.0',
protocol: 'http',
port: '8080'
}
return new Libp2p({
peerInfo,
peerBook,
// Lets limit the connection managers peers and have it check peer health less frequently
connectionManager: {
maxPeers: 10,
pollInterval: 5000
},
modules: {
contentRouting: [
new DelegatedContentRouter(peerInfo.id, delegatedApiOptions)
],
peerRouting: [
new DelegatedPeerRouter(delegatedApiOptions)
],
peerDiscovery: [
wrtcstar.discovery,
wsstar.discovery
],
transport: [
wrtcstar,
wsstar,
Websockets
],
streamMuxer: [
MPLEX
],
connEncryption: [
SECIO
],
dht: KadDHT
},
config: {
peerDiscovery: {
webrtcStar: {
enabled: false
},
websocketStar: {
enabled: false
}
},
dht: {
kBucketSize: 20
},
relay: {
enabled: true,
hop: {
enabled: false
}
},
EXPERIMENTAL: {
dht: false
}
}
})
}

View File

@ -0,0 +1,72 @@
/* eslint-disable no-console */
'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-bootstrap')
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,63 @@
/* eslint-disable no-console */
'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-bootstrap')
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).
Important 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,29 @@
{
"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-bootstrap": "~0.9.3",
"libp2p-mplex": "~0.8.0",
"libp2p-secio": "~0.10.0",
"libp2p-spdy": "~0.12.1",
"libp2p-webrtc-star": "~0.15.3",
"libp2p-websocket-star": "~0.8.1",
"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,87 @@
'use strict'
const WebRTCStar = require('libp2p-webrtc-star')
const WebSockets = require('libp2p-websockets')
const WebSocketStar = require('libp2p-websocket-star')
const Mplex = require('libp2p-mplex')
const SPDY = require('libp2p-spdy')
const SECIO = require('libp2p-secio')
const Bootstrap = require('libp2p-bootstrap')
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 bootstrapList = [
'/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/node0.preload.ipfs.io/tcp/443/wss/ipfs/QmZMxNdpMkewiVZLMRxaNxUeZpDUb34pWjZ1kZvsd16Zic',
'/dns4/node0.preload.ipfs.io/tcp/443/wss/ipfs/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6'
]
class Node extends libp2p {
constructor (_options) {
const wrtcStar = new WebRTCStar({ id: _options.peerInfo.id })
const wsstar = new WebSocketStar({ id: _options.peerInfo.id })
const defaults = {
modules: {
transport: [
wrtcStar,
WebSockets,
wsstar
],
streamMuxer: [
Mplex,
SPDY
],
connEncryption: [
SECIO
],
peerDiscovery: [
wrtcStar.discovery,
wsstar.discovery,
Bootstrap
]
},
config: {
peerDiscovery: {
webRTCStar: {
enabled: true
},
websocketStar: {
enabled: true
},
bootstrap: {
interval: 10000,
enabled: true,
list: bootstrapList
}
},
relay: {
enabled: true,
hop: {
enabled: true,
active: false
}
},
EXPERIMENTAL: {
dht: false,
pubsub: false
}
},
connectionManager: {
maxPeers: 50
}
}
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,74 @@
/* eslint no-console: ["error", { allow: ["log"] }] */
/* eslint max-nested-callbacks: ["error", 5] */
'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)
}
let connections = {}
node.on('peer:discovery', (peerInfo) => {
const idStr = peerInfo.id.toB58String()
if (connections[idStr]) {
// If we're already trying to connect to this peer, dont dial again
return
}
console.log('Discovered a peer:', idStr)
connections[idStr] = true
node.dial(peerInfo, (err, conn) => {
if (err) {
// Prevent immediate connection retries from happening
// and include a 10s jitter
const timeToNextDial = 25 * 1000 + (Math.random(0) * 10000).toFixed(0)
console.log('Failed to dial:', idStr)
setTimeout(() => delete connections[idStr], timeToNextDial)
}
})
})
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()
delete connections[idStr]
console.log('Lost connection to: ' + idStr)
const el = document.getElementById(idStr)
el && el.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,79 @@
/* eslint-disable no-console */
'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,87 @@
/* eslint-disable no-console */
'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,88 @@
/* eslint-disable no-console */
'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
```

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

@ -0,0 +1,86 @@
/* eslint-disable no-console */
'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/)

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

@ -0,0 +1,39 @@
/* eslint-disable no-console */
'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()))
})

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

@ -0,0 +1,69 @@
/* eslint-disable no-console */
'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)
})
})

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

@ -0,0 +1,94 @@
/* eslint-disable no-console */
'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!

86
package-list.json Normal file
View File

@ -0,0 +1,86 @@
{
"columns": [
"Package",
"Version",
"Deps",
"CI",
"Coverage",
"Lead Maintainer"
],
"rows": [
"Libp2p",
["libp2p/interface-libp2p", "interface-libp2p"],
["libp2p/js-libp2p", "libp2p"],
"Connection",
["libp2p/interface-connection", "interface-connection"],
"Transport",
["libp2p/interface-transport", "interface-transport"],
["libp2p/js-libp2p-tcp", "libp2p-tcp"],
["libp2p/js-libp2p-udp", "libp2p-udp"],
["libp2p/js-libp2p-udt", "libp2p-udt"],
["libp2p/js-libp2p-utp", "libp2p-utp"],
["libp2p/js-libp2p-webrtc-direct", "libp2p-webrtc-direct"],
["libp2p/js-libp2p-webrtc-star", "libp2p-webrtc-star"],
["libp2p/js-libp2p-websockets", "libp2p-websockets"],
["libp2p/js-libp2p-websocket-star", "libp2p-websocket-star"],
["libp2p/js-libp2p-websocket-star-rendezvous", "libp2p-websocket-star-rendezvous"],
"Crypto Channels",
["libp2p/js-libp2p-secio", "libp2p-secio"],
"Stream Muxers",
["libp2p/interface-stream-muxer", "interface-stream-muxer"],
["libp2p/js-libp2p-mplex", "libp2p-mplex"],
["libp2p/js-libp2p-spdy", "libp2p-spdy"],
"Discovery",
["libp2p/interface-peer-discovery", "interface-peer-discovery"],
["libp2p/js-libp2p-bootstrap", "libp2p-bootstrap"],
["libp2p/js-libp2p-kad-dht", "libp2p-kad-dht"],
["libp2p/js-libp2p-mdns", "libp2p-mdns"],
["libp2p/js-libp2p-rendezvous", "libp2p-rendezvous"],
["libp2p/js-libp2p-webrtc-star", "libp2p-webrtc-star"],
["libp2p/js-libp2p-websocket-star", "libp2p-websocket-star"],
"NAT Traversal",
["libp2p/js-libp2p-circuit", "libp2p-circuit"],
["libp2p/js-libp2p-nat-mngr", "libp2p-nat-mngr"],
"Data Types",
["libp2p/js-peer-book", "peer-book"],
["libp2p/js-peer-id", "peer-id"],
["libp2p/js-peer-info", "peer-info"],
"Content Routing",
["libp2p/interface-content-routing", "interface-content-routing"],
["libp2p/js-libp2p-delegated-content-routing", "libp2p-delegated-content-routing"],
["libp2p/js-libp2p-kad-dht", "libp2p-kad-dht"],
"Peer Routing",
["libp2p/interface-peer-routing", "interface-peer-routing"],
["libp2p/js-libp2p-delegated-peer-routing", "libp2p-delegated-peer-routing"],
["libp2p/js-libp2p-kad-dht", "libp2p-kad-dht"],
"Record Store",
["libp2p/interface-record-store", "interface-record-store"],
["libp2p/js-libp2p-record", "libp2p-record"],
"Generics",
["libp2p/js-libp2p-connection-manager", "libp2p-connection-manager"],
["libp2p/js-libp2p-crypto", "libp2p-crypto"],
["libp2p/js-libp2p-crypto-secp256k1", "libp2p-crypto-secp256k1"],
["libp2p/js-libp2p-switch", "libp2p-switch"],
"Extensions",
["libp2p/js-libp2p-floodsub", "libp2p-floodsub"],
["libp2p/js-libp2p-identify", "libp2p-identify"],
["libp2p/js-libp2p-keychain", "libp2p-keychain"],
["libp2p/js-libp2p-ping", "libp2p-ping"],
["libp2p/js-libp2p-pnet", "libp2p-pnet"],
"Utilities",
["libp2p/js-p2pcat", "p2pcat"]
]
}

View File

@ -1,16 +1,24 @@
{
"name": "libp2p",
"version": "0.5.4",
"description": "JavaScript Skeleton for libp2p bundles",
"version": "0.24.2",
"description": "JavaScript base class for libp2p bundles",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js",
"files": [
"dist",
"src"
],
"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,38 +28,104 @@
"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": {
"chai": "^3.5.0",
"aegir": "^10.0.0",
"pre-commit": "^1.2.2"
"browser": {
"joi": "joi-browser",
"./test/utils/bundle-nodejs": "./test/utils/bundle-browser"
},
"dependencies": {
"libp2p-ping": "~0.3.1",
"libp2p-swarm": "~0.26.17",
"mafmt": "^2.1.6",
"multiaddr": "^2.2.1",
"peer-book": "~0.3.1",
"peer-id": "~0.8.2",
"peer-info": "~0.8.3"
"async": "^2.6.1",
"debug": "^4.1.0",
"err-code": "^1.1.2",
"fsm-event": "^2.1.0",
"joi": "^14.0.6",
"joi-browser": "^13.4.0",
"libp2p-connection-manager": "~0.0.2",
"libp2p-floodsub": "~0.15.1",
"libp2p-ping": "~0.8.3",
"libp2p-switch": "~0.41.2",
"libp2p-websockets": "~0.12.0",
"mafmt": "^6.0.2",
"multiaddr": "^5.0.2",
"peer-book": "~0.8.0",
"peer-id": "~0.12.0",
"peer-info": "~0.14.1"
},
"devDependencies": {
"@nodeutils/defaults-deep": "^1.1.0",
"aegir": "^17.0.1",
"chai": "^4.2.0",
"chai-checkmark": "^1.0.1",
"cids": "~0.5.5",
"dirty-chai": "^2.0.1",
"electron-webrtc": "~0.3.0",
"interface-datastore": "~0.6.0",
"libp2p-bootstrap": "~0.9.3",
"libp2p-circuit": "~0.3.0",
"libp2p-delegated-content-routing": "~0.2.2",
"libp2p-delegated-peer-routing": "~0.2.2",
"libp2p-kad-dht": "~0.11.1",
"libp2p-mdns": "~0.12.0",
"libp2p-mplex": "~0.8.4",
"libp2p-secio": "~0.10.1",
"libp2p-spdy": "~0.13.0",
"libp2p-tcp": "~0.13.0",
"libp2p-webrtc-star": "~0.15.5",
"libp2p-websocket-star": "~0.9.0",
"libp2p-websocket-star-rendezvous": "~0.2.4",
"lodash.times": "^4.3.2",
"nock": "^10.0.2",
"pull-goodbye": "0.0.2",
"pull-serializer": "~0.3.2",
"pull-stream": "^3.6.9",
"sinon": "^7.1.1",
"webrtcsupport": "^2.2.0",
"wrtc": "~0.3.2"
},
"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>",
"Henrique Dias <hacdias@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>",
"Marcin Tojek <mtojek@users.noreply.github.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>",
"Vasco Santos <vasco.santos@ua.pt>",
"Vasco Santos <vasco.santos@moxy.studio>",
"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)
})
})

56
src/config.js Normal file
View File

@ -0,0 +1,56 @@
'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(),
datastore: Joi.object(),
peerInfo: Joi.object().required(),
peerBook: Joi.object(),
modules: Joi.object().keys({
connEncryption: Joi.array().items(ModuleSchema).allow(null),
connProtector: Joi.object().keys({
protect: Joi.func().required()
}).unknown(),
contentRouting: Joi.array().items(Joi.object()).allow(null),
dht: ModuleSchema.allow(null),
peerDiscovery: Joi.array().items(ModuleSchema).allow(null),
peerRouting: Joi.array().items(Joi.object()).allow(null),
streamMuxer: Joi.array().items(ModuleSchema).allow(null),
transport: Joi.array().items(ModuleSchema).min(1).required()
}).required(),
config: Joi.object().keys({
peerDiscovery: Joi.object().allow(null),
relay: Joi.object().keys({
enabled: Joi.boolean().default(true),
hop: Joi.object().keys({
enabled: Joi.boolean().default(false),
active: Joi.boolean().default(false)
})
}).default(),
dht: Joi.object().keys({
kBucketSize: Joi.number().default(20),
enabledDiscovery: Joi.boolean().default(true),
validators: Joi.object().allow(null),
selectors: Joi.object().allow(null)
}).default(),
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
}

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

@ -0,0 +1,83 @@
'use strict'
const tryEach = require('async/tryEach')
const parallel = require('async/parallel')
const errCode = require('err-code')
module.exports = (node) => {
const routers = node._modules.contentRouting || []
// If we have the dht, make it first
if (node._dht) {
routers.unshift(node._dht)
}
return {
/**
* Iterates over all content routers in series to find providers of the given key.
* Once a content router succeeds, iteration will stop.
*
* @param {CID} key The CID key of the content to find
* @param {object} options
* @param {number} options.maxTimeout How long the query should run
* @param {number} options.maxNumProviders - maximum number of providers to find
* @param {function(Error, Result<Array>)} callback
* @returns {void}
*/
findProviders: (key, options, callback) => {
if (typeof options === 'function') {
callback = options
options = {}
} else if (typeof options === 'number') { // This can be deprecated in a future release
options = {
maxTimeout: options
}
}
if (!routers.length) {
return callback(errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE'))
}
const tasks = routers.map((router) => {
return (cb) => router.findProviders(key, options, (err, results) => {
if (err) {
return cb(err)
}
// If we don't have any results, we need to provide an error to keep trying
if (!results || Object.keys(results).length === 0) {
return cb(errCode(new Error('not found'), 'NOT_FOUND'), null)
}
cb(null, results)
})
})
tryEach(tasks, (err, results) => {
if (err && err.code !== 'NOT_FOUND') {
return callback(err)
}
results = results || []
callback(null, results)
})
},
/**
* Iterates over all content routers in parallel to notify it is
* a provider of the given key.
*
* @param {CID} key The CID key of the content to find
* @param {function(Error)} callback
* @returns {void}
*/
provide: (key, callback) => {
if (!routers.length) {
return callback(errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE'))
}
parallel(routers.map((router) => {
return (cb) => router.provide(key, cb)
}), callback)
}
}
}

37
src/dht.js Normal file
View File

@ -0,0 +1,37 @@
'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, options, callback) => {
if (typeof options === 'function') {
callback = options
options = {}
}
if (!node._dht) {
return callback(new Error('DHT is not available'))
}
node._dht.get(key, options, callback)
},
getMany: (key, nVals, options, callback) => {
if (typeof options === 'function') {
callback = options
options = {}
}
if (!node._dht) {
return callback(new Error('DHT is not available'))
}
node._dht.getMany(key, nVals, options, 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'

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

@ -0,0 +1,66 @@
'use strict'
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const multiaddr = require('multiaddr')
const errCode = require('err-code')
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') {
try {
peer = multiaddr(peer)
} catch (err) {
return callback(
errCode(err, 'ERR_INVALID_MULTIADDR')
)
}
}
const peerIdB58Str = peer.getPeerId()
if (!peerIdB58Str) {
return callback(
errCode(
new Error('peer multiaddr instance or string must include peerId'),
'ERR_INVALID_MULTIADDR'
)
)
}
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 callback(
errCode(
new Error(`${p} is not a valid peer type`),
'ERR_INVALID_PEER_TYPE'
)
)
}
callback(null, p)
}
}

View File

@ -1,289 +1,483 @@
'use strict'
const Swarm = require('libp2p-swarm')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const PeerBook = require('peer-book')
const multiaddr = require('multiaddr')
const mafmt = require('mafmt')
const EE = require('events').EventEmitter
const FSM = require('fsm-event')
const EventEmitter = require('events').EventEmitter
const assert = require('assert')
const debug = require('debug')
const log = debug('libp2p')
log.error = debug('libp2p:error')
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')
exports = module.exports
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
const OFFLINE_ERROR_MESSAGE = 'The libp2p node is not started yet'
const IPFS_CODE = 421
const NOT_STARTED_ERROR_MESSAGE = 'The libp2p node is not started yet'
class Node {
constructor (_modules, _peerInfo, _peerBook, _options) {
assert(_modules, 'requires modules to equip libp2p with features')
assert(_peerInfo, 'requires a PeerInfo instance')
/**
* @fires Node#error Emitted when an error occurs
* @fires Node#peer:connect Emitted when a peer is connected to this node
* @fires Node#peer:disconnect Emitted when a peer disconnects from this node
* @fires Node#peer:discovery Emitted when a peer is discovered
* @fires Node#start Emitted when the node and its services has started
* @fires Node#stop Emitted when the node and its services has stopped
*/
class Node extends EventEmitter {
constructor (_options) {
super()
// 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.datastore = _options.datastore
this.peerInfo = _options.peerInfo
this.peerBook = _options.peerBook || new PeerBook()
this.discovery = new EE()
this._modules = _options.modules
this._config = _options.config
this._isStarted = false
this._transport = [] // Transport instances/references
this._discovery = [] // Discovery service instances/references
this.swarm = new Swarm(this.peerInfo)
// create the switch, and listen for errors
this._switch = new Switch(this.peerInfo, this.peerBook, _options.switch)
this._switch.on('error', (...args) => this.emit('error', ...args))
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, reuse this
// muxed connection
this.swarm.on('peer-mux-established', (peerInfo) => {
// Received incomming dial and muxer upgrade happened,
// reuse this muxed connection
this._switch.on('peer-mux-established', (peerInfo) => {
this.emit('peer:connect', peerInfo)
this.peerBook.put(peerInfo)
})
this.swarm.on('peer-mux-closed', (peerInfo) => {
this.peerBook.removeByB58String(peerInfo.id.toB58String())
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.discovery.emit('peer', peerInfo)
})
// Attach private network protector
if (this._modules.connProtector) {
this._switch.protector = this._modules.connProtector
} else if (process.env.LIBP2P_FORCE_PNET) {
throw new Error('Private network is enforced, but no protector was provided')
}
// dht provided components (peerRouting, contentRouting, dht)
if (this._config.EXPERIMENTAL.dht) {
const DHT = this._modules.dht
const enabledDiscovery = this._config.dht.enabledDiscovery !== false
this._dht = new DHT(this._switch, {
kBucketSize: this._config.dht.kBucketSize,
enabledDiscovery,
datastore: this.datastore,
validators: this._config.dht.validators,
selectors: this._config.dht.selectors
})
}
// enable/disable pubsub
if (this._config.EXPERIMENTAL.pubsub) {
this.pubsub = pubsub(this)
}
// Attach remaining APIs
// peer and content routing will automatically get modules from _modules and _dht
this.peerRouting = peerRouting(this)
this.contentRouting = contentRouting(this)
this.dht = dht(this)
this._getPeerInfo = getPeerInfo(this)
// Mount default protocols
Ping.mount(this.swarm)
Ping.mount(this._switch)
// Not fully implemented in js-libp2p yet
this.routing = undefined
this.records = undefined
this.state = new FSM('STOPPED', {
STOPPED: {
start: 'STARTING',
stop: 'STOPPED'
},
STARTING: {
done: 'STARTED',
abort: 'STOPPED',
stop: 'STOPPING'
},
STARTED: {
stop: 'STOPPING',
start: 'STARTED'
},
STOPPING: {
stop: 'STOPPING',
done: 'STOPPED'
}
})
this.state.on('STARTING', () => {
log('libp2p is starting')
this._onStarting()
})
this.state.on('STOPPING', () => {
log('libp2p is stopping')
this._onStopping()
})
this.state.on('STARTED', () => {
log('libp2p has started')
this.emit('start')
})
this.state.on('STOPPED', () => {
log('libp2p has stopped')
this.emit('stop')
})
this.state.on('error', (err) => {
log.error(err)
this.emit('error', err)
})
}
/*
* Start the libp2p node
* - create listeners on the multiaddrs the Peer wants to listen
/**
* Overrides EventEmitter.emit to conditionally emit errors
* if there is a handler. If not, errors will be logged.
* @param {string} eventName
* @param {...any} args
* @returns {void}
*/
start (callback) {
if (!this.modules.transport) {
return callback(new Error('no transports were present'))
emit (eventName, ...args) {
if (eventName === 'error' && !this._events.error) {
log.error(...args)
} else {
super.emit(eventName, ...args)
}
let ws
let transports = this.modules.transport
transports = Array.isArray(transports) ? transports : [transports]
const multiaddrs = this.peerInfo.multiaddrs
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.swarm.listen((err) => {
if (err) {
return callback(err)
}
if (ws) {
this.swarm.transport.add(ws.tag || ws.constructor.name, ws)
}
this.isOnline = true
if (this.modules.discovery) {
this.modules.discovery.forEach((discovery) => {
setImmediate(() => discovery.start(() => {}))
})
}
callback()
})
}
/*
/**
* Starts the libp2p node and all sub services
*
* @param {function(Error)} callback
* @returns {void}
*/
start (callback = () => {}) {
this.once('start', callback)
this.state('start')
}
/**
* Stop the libp2p node by closing its listeners and open connections
*
* @param {function(Error)} callback
* @returns {void}
*/
stop (callback) {
this.isOnline = false
stop (callback = () => {}) {
this.once('stop', callback)
this.state('stop')
}
if (this.modules.discovery) {
this.modules.discovery.forEach((discovery) => {
setImmediate(() => discovery.stop(() => {}))
isStarted () {
return this.state ? this.state._state === 'STARTED' : false
}
/**
* Dials to the provided peer. If successful, the `PeerInfo` of the
* peer will be added to the nodes `PeerBook`
*
* @param {PeerInfo|PeerId|Multiaddr|string} peer The peer to dial
* @param {function(Error)} callback
* @returns {void}
*/
dial (peer, callback) {
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
this.dialProtocol(peer, null, callback)
}
/**
* Dials to the provided peer and handshakes with the given protocol.
* If successful, the `PeerInfo` of the peer will be added to the nodes `PeerBook`,
* and the `Connection` will be sent in the callback
*
* @param {PeerInfo|PeerId|Multiaddr|string} peer The peer to dial
* @param {string} protocol
* @param {function(Error, Connection)} callback
* @returns {void}
*/
dialProtocol (peer, protocol, callback) {
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
if (typeof protocol === 'function') {
callback = protocol
protocol = undefined
}
this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { return callback(err) }
this._switch.dial(peerInfo, protocol, (err, conn) => {
if (err) { return callback(err) }
this.peerBook.put(peerInfo)
callback(null, conn)
})
}
this.swarm.close(callback)
}
//
// Ping
//
// TODO
pingById (id, callback) {
assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
callback(new Error('not implemented yet'))
}
// TODO
pingByMultiaddr (maddr, callback) {
assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
callback(new Error('not implemented yet'))
}
pingByPeerInfo (peerInfo, callback) {
assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
callback(null, new Ping(this.swarm, peerInfo))
}
//
// Dialing methods
//
// TODO
dialById (id, protocol, callback) {
// NOTE: dialById only works if a previous dial was made. This will
// change once we have PeerRouting
assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
if (typeof protocol === 'function') {
callback = protocol
protocol = undefined
}
callback(new Error('not implemented yet'))
}
dialByMultiaddr (maddr, protocol, callback) {
assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
if (typeof protocol === 'function') {
callback = protocol
protocol = undefined
}
if (typeof maddr === 'string') {
maddr = multiaddr(maddr)
}
if (!mafmt.IPFS.matches(maddr.toString())) {
return callback(new Error('multiaddr not valid'))
}
const ipfsIdB58String = maddr.stringTuples().filter((tuple) => {
if (tuple[0] === IPFS_CODE) {
return true
}
})[0][1]
let peer
try {
peer = this.peerBook.getByB58String(ipfsIdB58String)
} catch (err) {
peer = new PeerInfo(PeerId.createFromB58String(ipfsIdB58String))
}
peer.multiaddr.add(maddr)
this.dialByPeerInfo(peer, protocol, callback)
}
dialByPeerInfo (peer, protocol, callback) {
assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
if (typeof protocol === 'function') {
callback = protocol
protocol = undefined
}
this.swarm.dial(peer, protocol, (err, conn) => {
if (err) {
return callback(err)
}
this.peerBook.put(peer)
callback(null, conn)
})
}
//
// Disconnecting (hangUp) methods
//
/**
* Similar to `dial` and `dialProtocol`, but the callback will contain a
* Connection State Machine.
*
* @param {PeerInfo|PeerId|Multiaddr|string} peer The peer to dial
* @param {string} protocol
* @param {function(Error, ConnectionFSM)} callback
* @returns {void}
*/
dialFSM (peer, protocol, callback) {
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
hangUpById (id, callback) {
// TODO
callback(new Error('not implemented yet'))
}
hangUpByMultiaddr (maddr, callback) {
assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
if (typeof maddr === 'string') {
maddr = multiaddr(maddr)
if (typeof protocol === 'function') {
callback = protocol
protocol = undefined
}
if (!mafmt.IPFS.matches(maddr.toString())) {
return callback(new Error('multiaddr not valid'))
}
this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { return callback(err) }
const ipfsIdB58String = maddr.stringTuples().filter((tuple) => {
if (tuple[0] === IPFS_CODE) {
return true
}
})[0][1]
const connFSM = this._switch.dialFSM(peerInfo, protocol, (err) => {
if (!err) {
this.peerBook.put(peerInfo)
}
})
try {
const pi = this.peerBook.getByB58String(ipfsIdB58String)
this.hangUpByPeerInfo(pi, callback)
} catch (err) {
// already disconnected
callback()
}
callback(null, connFSM)
})
}
hangUpByPeerInfo (peer, callback) {
assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
hangUp (peer, callback) {
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
this.peerBook.removeByB58String(peer.id.toB58String())
this.swarm.hangUp(peer, callback)
this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { return callback(err) }
this._switch.hangUp(peerInfo, callback)
})
}
//
// Protocol multiplexing handling
//
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)
this._switch.unhandle(protocol)
}
_onStarting () {
if (!this._modules.transport) {
this.emit('error', new Error('no transports were present'))
return this.state('abort')
}
let ws
// so that we can have webrtc-star addrs without adding manually the id
const maOld = []
const maNew = []
this.peerInfo.multiaddrs.toArray().forEach((ma) => {
if (!ma.getPeerId()) {
maOld.push(ma)
maNew.push(ma.encapsulate('/ipfs/' + this.peerInfo.id.toB58String()))
}
})
this.peerInfo.multiaddrs.replace(maOld, maNew)
const multiaddrs = this.peerInfo.multiaddrs.toArray()
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[Symbol.toStringTag], t)
} else if (WebSockets.isWebSockets(t)) {
// TODO find a cleaner way to signal that a transport is always used
// for dialing, even if no listener
ws = t
}
this._transport.push(t)
})
series([
(cb) => {
this.connectionManager.start()
this._switch.start(cb)
},
(cb) => {
if (ws) {
// always add dialing on websockets
this._switch.transport.add(ws.tag || ws.constructor.name, ws)
}
// all transports need to be setup before discover starts
if (this._modules.peerDiscovery) {
each(this._modules.peerDiscovery, (D, _cb) => {
let config = {}
if (D.tag &&
this._config.peerDiscovery &&
this._config.peerDiscovery[D.tag]) {
config = this._config.peerDiscovery[D.tag]
}
// If not configured to be enabled/disabled then enable by default
const enabled = config.enabled == null ? true : config.enabled
// If enabled then start it
if (enabled) {
let d
if (typeof D === 'function') {
d = new D(Object.assign({}, config, { peerInfo: this.peerInfo }))
} else {
d = D
}
d.on('peer', (peerInfo) => this.emit('peer:discovery', peerInfo))
this._discovery.push(d)
d.start(_cb)
} else {
_cb()
}
}, cb)
} else {
cb()
}
},
(cb) => {
// 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) {
return this._floodSub.start(cb)
}
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()
}
], (err) => {
if (err) {
log.error(err)
this.emit('error', err)
return this.state('stop')
}
this.state('done')
})
}
_onStopping () {
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) => {
// Ensures idempotency for restarts
this._switch.transport.removeAll(cb)
},
(cb) => {
this.connectionManager.stop()
this._switch.stop(cb)
}
], (err) => {
if (err) {
log.error(err)
this.emit('error', err)
}
this.state('done')
})
}
}

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

@ -0,0 +1,58 @@
'use strict'
const tryEach = require('async/tryEach')
const errCode = require('err-code')
module.exports = (node) => {
const routers = node._modules.peerRouting || []
// If we have the dht, make it first
if (node._dht) {
routers.unshift(node._dht)
}
return {
/**
* Iterates over all peer routers in series to find the given peer.
*
* @param {String} id The id of the peer to find
* @param {object} options
* @param {number} options.maxTimeout How long the query should run
* @param {function(Error, Result<Array>)} callback
* @returns {void}
*/
findPeer: (id, options, callback) => {
if (typeof options === 'function') {
callback = options
options = {}
}
if (!routers.length) {
callback(errCode(new Error('No peer routers available'), 'NO_ROUTERS_AVAILABLE'))
}
const tasks = routers.map((router) => {
return (cb) => router.findPeer(id, options, (err, result) => {
if (err) {
return cb(err)
}
// If we don't have a result, we need to provide an error to keep trying
if (!result || Object.keys(result).length === 0) {
return cb(errCode(new Error('not found'), 'NOT_FOUND'), null)
}
cb(null, result)
})
})
tryEach(tasks, (err, results) => {
if (err && err.code !== 'NOT_FOUND') {
return callback(err)
}
results = results || []
callback(null, results)
})
}
}
}

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

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