Compare commits

..

247 Commits

Author SHA1 Message Date
8d3b61710a chore: release version v0.30.6 2021-01-29 14:39:41 +01:00
5dbbeef311 chore: update contributors 2021-01-29 14:39:41 +01:00
3e7594f697 fix: peer discovery type in config (#878)
to any
2021-01-29 14:32:13 +01:00
ce2a624a09 fix: unref nat manager retries (#877)
The retry operation in the NAT Manager can prevent node from shutting
down, so unref the retries so they don't keep adding work to the
event loop.
2021-01-29 14:09:59 +01:00
a64c02838c chore: release version v0.30.5 2021-01-28 16:50:46 +01:00
74d07e5e8c chore: update contributors 2021-01-28 16:50:46 +01:00
eeda056883 fix: create has optional peer id type (#875) 2021-01-28 16:41:04 +01:00
f06e06a006 chore: update bootstrapers example url 2021-01-28 11:06:11 +01:00
28f52bbf75 chore: release version v0.30.4 2021-01-27 15:23:56 +01:00
ed5f8f853f chore: update contributors 2021-01-27 15:23:56 +01:00
0a6bc0d101 feat: add UPnP NAT manager (#810)
* feat: add uPnP nat manager

Adds a really basic nat manager that attempts to use UPnP to punch
a hole through your router for any IPV4 tcp addresses you have
configured.

Adds any configured addresses to the node's observed addresses list
and adds observed addresses to `libp2p.multiaddrs` so we exchange
them with peers when performing `identify` and people can dial you.

Adds configuration options under `config.nat`

Hole punching is async to not affect start up time.

Co-authored-by: Vasco Santos <vasco.santos@moxy.studio>
2021-01-27 14:55:26 +01:00
b5c9e48b68 chore: release version v0.30.3 2021-01-27 14:33:33 +01:00
9942cbd50c chore: update contributors 2021-01-27 14:33:32 +01:00
037c965a67 chore: update deps (#869) 2021-01-27 09:45:31 +01:00
748b552876 chore: pnet example test (#845) 2021-01-22 10:24:15 +01:00
961b48bb8d chore: release version v0.30.2 2021-01-21 13:50:49 +01:00
000826db21 chore: update contributors 2021-01-21 13:50:48 +01:00
45c33675a7 fix: store multiaddrs during content and peer routing queries (#865)
* fix: store provider multiaddrs during find providers

Changes the behaviour of `libp2p.contentRouting.findProviders` to store
the multiaddrs reported by the routers before yielding results to
the caller, so when they try to dial the provider, the multiaddrs are
already in the peer store's address book.

Also dedupes providers reported by routers but keeps all of the addresses
reported, even for duplicates.

Also, also fixes a performance bug where the previous implementation would
wait for any router to completely finish finding providers before sending
any results to the caller.  It'll now yield results as they come in which
makes it much, much faster.
2021-01-21 13:41:27 +01:00
a28c878f4a chore: fix close for ConnectionManager (#861) 2021-01-21 12:09:53 +01:00
67067c97d5 chore: connection encryption example test (#843) 2021-01-21 09:27:27 +01:00
f45cd1c4b5 chore: echo example test (#842) 2021-01-20 10:46:04 +01:00
0a02207116 chore: add discovery example tests (#841) 2021-01-19 11:02:56 +01:00
0b854a949f chore: add browser example test (#846) 2021-01-19 09:57:56 +01:00
9014ea657a chore: release version v0.30.1 2021-01-18 17:14:31 +01:00
f40697975e chore: update contributors 2021-01-18 17:14:30 +01:00
6c41e30456 fix: event emitter types with local types (#864) 2021-01-18 17:07:30 +01:00
77e8273a64 chore: add chat example (#840) 2021-01-18 11:15:02 +01:00
d60922b799 docs: Add bootstrap to custom peer discovery (#859) 2021-01-15 10:27:23 +01:00
42b51d8f01 chore: add github actions badge and fix codecov (#837) 2020-12-17 14:22:36 +01:00
d19401aa4c chore: release version v0.30.0 2020-12-16 14:03:09 +01:00
24bb8df521 chore: update contributors 2020-12-16 14:03:09 +01:00
58d4f9a915 chore: release version v0.30.0-rc.2 2020-12-16 13:56:41 +01:00
239413e331 chore: update contributors 2020-12-16 13:56:41 +01:00
01d43a7b60 chore: fix multicodec updates (#835)
* chore: fix specific multicodec version

* chore: fix multicodec issues

* chore: remove prepare script
2020-12-16 13:56:41 +01:00
37d66fd88c chore: release version v0.30.0-rc.1 2020-12-16 13:56:41 +01:00
21e8ced81a chore: update contributors 2020-12-16 13:56:41 +01:00
9ae1b758e9 fix: types from ipfs integration (#832) 2020-12-16 13:56:41 +01:00
408868655c chore: remove secio from packages table (#833) 2020-12-16 13:56:41 +01:00
c5f61ac05f chore: release version v0.30.0-rc.0 2020-12-16 13:56:41 +01:00
5d0ac529e4 chore: update contributors 2020-12-16 13:56:41 +01:00
bc05083207 docs: production guide base setup (#804) 2020-12-16 13:56:41 +01:00
169bb806a7 chore: add typedefs (#802) 2020-12-16 13:56:41 +01:00
7809e6444e chore: auto relay configuration example with noise (#828) 2020-12-16 13:56:41 +01:00
f7e1426b9e chore: update pubsub example by disabled emit self (#823) 2020-12-16 13:56:41 +01:00
7d76ba1367 docs: migration 0.29 to 0.30 (#808) 2020-12-16 13:56:41 +01:00
b538ebdc0a chore: use set-delayed-interval module on circuit (#809) 2020-12-16 13:56:41 +01:00
baedf3fe5a feat: discover and connect to closest peers (#798) 2020-12-16 13:56:41 +01:00
4ebcdb085c chore: update websockets (#806)
* chore: update websockets
2020-12-16 13:56:41 +01:00
4448de8432 docs: auto relay example (#795)
* chore: auto relay example

* chore: update examples to use process arguments

* chore: add test setup for node tests and test for auto-relay

* chore: apply suggestions from code review

* chore: do not use promise for multiaddrs event on example
2020-12-16 13:56:41 +01:00
585ad52b4c feat: custom dialer addr sorter (#792)
* feat: custom dialer addr sorter

* chore: use libp2p utils sorter via addressBook getMultiaddrsForPeer

* chore: use new libp2p utils

* chore: apply suggestions from code review

Co-authored-by: Jacob Heun <jacobheun@gmail.com>

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-12-16 13:56:41 +01:00
e50c6abcf2 chore: update pubsub (#801)
BREAKING CHANGE: pubsub signing policy properties were changed according to libp2p-interfaces changes to a single property. The emitSelf option default value was also modified to match the routers value
2020-12-16 13:56:41 +01:00
49fffda23c test: custom announce filter 2020-12-16 13:56:41 +01:00
689c35ed1c fix: remove test/dialing/utils extra file 2020-12-16 13:56:41 +01:00
1a13e2c6ca chore: update address manager readme 2020-12-16 13:56:41 +01:00
5758db8ea9 chore: remove noAnnounce from address manager 2020-12-16 13:56:41 +01:00
ef9d3ca2c6 feat: custom announce filter 2020-12-16 13:56:41 +01:00
97e3633f47 chore: store self protocols in protobook (#760) 2020-12-16 13:56:41 +01:00
e36b67a212 chore: improve logging for auto relay active listen 2020-12-16 13:56:41 +01:00
e977039c8a chore: sort relay addresses to listen for public first 2020-12-16 13:56:41 +01:00
a5337c1797 chore: lint issues fixed 2020-12-16 13:56:41 +01:00
ee23fb9508 chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-12-16 13:56:41 +01:00
11a46ea71e chore: add configuration docs for auto relay and hop service 2020-12-16 13:56:41 +01:00
5c72424e57 chore: address review 2020-12-16 13:56:41 +01:00
0bf0b7cf89 feat: auto relay network query for new relays 2020-12-16 13:56:41 +01:00
55020056ee chore: lint issue fixed 0.30 2020-12-16 13:56:41 +01:00
bb83cacb5a chore: address review 2020-12-16 13:56:41 +01:00
447d0ed0dd chore: add identify test for multiaddr change 2020-12-16 13:56:41 +01:00
43eda43f06 chore: create signed peer record on new listen addresses in transport manager 2020-12-16 13:56:41 +01:00
7b93ece7f2 chore: use listening events to create self peer record on updates 2020-12-16 13:56:41 +01:00
74bdfd1024 chore: _isStarted is false when stop starts 2020-12-16 13:56:41 +01:00
4d1fcdb3d2 chore: auto relay multiaddr update push 2020-12-16 13:56:41 +01:00
caf66ea143 feat: auto relay (#723)
* feat: auto relay

* fix: leverage protoBook events to ask relay peers if they support hop

* chore: refactor disconnect

* chore: do not listen on a relayed conn

* chore: tweaks

* chore: improve _listenOnAvailableHopRelays logic

* chore: default value of 1 to maxListeners on auto-relay
2020-12-16 13:56:41 +01:00
48656712ea chore: release version v0.29.4 2020-12-09 16:42:21 +01:00
1a5ae74741 chore: update contributors 2020-12-09 16:42:20 +01:00
8691465a52 feat: support custom listener options (#822)
* support custom listener options

* fix get listener options

* add doc to explain custom listener options

* add ut

* fix code style

* Apply suggestions from code review

Co-authored-by: Vasco Santos <vasco.santos@ua.pt>

* add missing comma

Co-authored-by: Vasco Santos <vasco.santos@ua.pt>
2020-12-09 16:31:17 +01:00
6350a187c7 fix: dial self (#826) 2020-12-09 16:13:25 +01:00
8e3bb09279 chore: remove references to Solarnet (#820)
Co-authored-by: Vasco Santos <vasco.santos@ua.pt>
2020-12-01 19:10:47 +01:00
73204958ee docs: use Libp2p.create() in examples (#811) (#814) 2020-11-30 11:15:09 +01:00
e9e4b731a5 docs: fix JSDOc for stop and create (#812) (#813) 2020-11-27 10:50:35 +01:00
d0a9fada32 feat: custom and store self agent version + store self protocol version (#800)
* feat: custom and store self protocol and agent version

* fix: do not enable custom protocolVersion
2020-11-20 15:14:01 +01:00
824a444f56 docs(fix): fix contentRouting.getMany (#803) 2020-11-18 10:28:43 +01:00
fef54b2b2c chore: release version v0.29.3 2020-11-04 14:05:08 +01:00
8f29a667a1 chore: update contributors 2020-11-04 14:05:08 +01:00
093c0ea13f feat: resolve multiaddrs before dial (#782) 2020-11-04 13:54:50 +01:00
61c36f9e09 chore: release version v0.29.2 2020-10-23 15:40:54 +02:00
f82da56901 chore: update contributors 2020-10-23 15:40:53 +02:00
06f26e586f fix: cleanup open streams on conn close (#791) 2020-10-23 15:34:59 +02:00
8879634363 chore: release version v0.29.1 2020-10-22 14:33:29 +02:00
4a80afce8f chore: update contributors 2020-10-22 14:33:28 +02:00
f75ae341bb test: lock ci on node 14 2020-10-22 14:29:52 +02:00
f2d010a3ab chore: update mplex 2020-10-22 14:29:52 +02:00
e04224a1e2 fix: catch error in upgrader close call 2020-10-22 14:29:52 +02:00
4c6be91588 fix: ensure streams are closed on connection close 2020-10-22 14:29:52 +02:00
5f50054d94 docs: fix typo in transports example readme (#788) 2020-10-22 11:02:00 +02:00
d7d8439e71 docs: update transport example (#770) 2020-10-15 17:28:01 +02:00
4c7a89b710 doc(pubsub): add topicValidators links in API.md table of contents 2020-10-12 12:57:33 +02:00
4eabe07bde chore: update node badge 2020-10-12 12:48:48 +02:00
2fd3b0a0e5 chore: examples not using secio (#747)
* chore: examples not using secio

* chore(docs): remove unused dep

* chore(docs): remove reference of secio in setup

* chore(docs): replace circuit secio reference with noise

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-10-07 16:16:36 +02:00
ec6f7d1cfd chore: lint issue fixed (#775) 2020-10-07 15:39:24 +02:00
a1053bdc54 chore: remove outdated events from libp2p js docs (#766) 2020-10-07 14:51:51 +02:00
0d48fc4f5a test: use ed25519 keys in tests (#669)
* chore: use ed25519 keys in tests

* fix: persisted keybook recheck keybook content for delete

* chore: only store if key not inline

* chore: update peer id

* chore: identify wait for closed streams
2020-10-07 14:50:01 +02:00
60d437f595 fix: flakey identify test firefox (#774) 2020-10-06 15:37:01 +02:00
96df4b7dc4 chore: update aegir and jsdocs for eslint changes (#773) 2020-10-06 14:59:43 +02:00
bb59b518f1 chore: complement 0.29 migration for pubsub subscribe (#755)
* chore: complement 0.29 migration for pubsub subscribe

* chore: update doc/migrations/v0.28-v0.29.md

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-09-16 15:46:05 +02:00
fb4b2734d3 chore: update delegate deps (#753) 2020-09-15 12:47:16 +02:00
0087218194 chore: update getting started connect event (#752) 2020-09-14 13:23:28 +02:00
58b793d700 chore: add libp2p examples repo to release checklist (#746) 2020-09-08 14:47:25 +02:00
63ba2f8fa3 chore: update docs after secio deprecation announcement (#745) 2020-09-08 14:07:06 +02:00
bd26bde876 chore: remove libp2p-pubsub from package table (#743) 2020-08-31 15:19:43 +02:00
3c2a45a9d2 docs: update examples for 0.29 (#742)
* docs: update examples for 0.29

* fix: update examples/libp2p-in-the-browser/package.json

Co-authored-by: Vasco Santos <vasco.santos@moxy.studio>

Co-authored-by: Vasco Santos <vasco.santos@moxy.studio>
2020-08-27 18:13:38 +02:00
bbf8ef7065 chore: release version v0.29.0 2020-08-27 16:56:17 +02:00
d6d1a746ea chore: update contributors 2020-08-27 16:56:16 +02:00
28b79a76a8 test: fix pubsub intermittent test (#741) 2020-08-27 16:42:07 +02:00
81e70df742 chore: update interop version (#740) 2020-08-27 15:38:01 +02:00
e9478cee2e chore: release version v0.29.0-rc.1 2020-08-27 15:38:01 +02:00
7be17a3ce1 chore: update contributors 2020-08-27 15:38:01 +02:00
93dda74085 fix: peer record interop with go (#739)
* test: add go peer record interop test

* fix: correct the payload type of peer records

* chore: fix linting

* test: fix envelope test
2020-08-27 15:38:01 +02:00
cfbd52d7f7 chore: migration to 0.29 should use webrtc-star0.20 2020-08-27 15:38:01 +02:00
6cd23ea6c9 chore: use gossipsub0.6 2020-08-27 15:38:01 +02:00
9b75a0f184 chore: bump libp2p-webrtc-star 2020-08-27 15:38:01 +02:00
b606ce0e91 chore: release version v0.29.0-rc.0 2020-08-27 15:38:01 +02:00
64c8c0f097 chore: update contributors 2020-08-27 15:38:01 +02:00
9be582e222 docs: migration 0.28 to 0.29 (#736)
* docs: migration 0.28 to 0.29

* chore: finish pubsub migration

* chore: add uintarray migration and modules

* chore: apply suggestions from code review

Co-authored-by: Jacob Heun <jacobheun@gmail.com>

* chore: add uint8array module reference

* chore: rename files

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-08-27 15:38:01 +02:00
55c9bfac44 feat: gossipsub 1.1 (#733)
* feat: gossipsub 1.1

BREAKING CHANGE: pubsub implementation is now directly exposed and its API was updated according to the new pubsub interface in js-libp2p-interfaces repo

* chore: use gossipsub branch with src added

* fix: add pubsub handlers adapter

* chore: fix deps

* chore: update pubsub docs and examples

* chore: apply suggestions from code review

Co-authored-by: Jacob Heun <jacobheun@gmail.com>

* chore: use new floodsub

* chore: change validator doc set

Co-authored-by: Jacob Heun <jacobheun@gmail.com>

* chore: add new gossipsub src

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-08-27 15:38:01 +02:00
1e869717ff fix: replace node buffers with uint8arrays (#730)
* fix: replace node buffers with uint8arrays

Upgrades all deps and replaces all use of node Buffers with Uint8Arrays

BREAKING CHANGES:

- All deps used by this module now use Uint8Arrays in place of node Buffers

* chore: browser fixes

* chore: remove .only

* chore: stringify uint8array before parsing

* chore: update interop suite

* chore: remove ts from build command

* chore: update deps

* fix: update records to use uint8array

* chore: fix lint

* chore: update deps

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-08-27 15:38:01 +02:00
9107efe121 chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-08-27 15:38:01 +02:00
cd09327eb6 chore: add notice for addressBook.set 2020-08-27 15:38:01 +02:00
ca57e65ecc chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-08-27 15:38:01 +02:00
f574e82a5d chore: apply suggestions from code review 2020-08-27 15:38:01 +02:00
15613ccf19 fix: do not return self on peerstore.peers 2020-08-27 15:38:01 +02:00
dab1c8b2a5 chore: increase bundle size 2020-08-27 15:38:01 +02:00
d437defede chore: rename isEqual to equals in tests 2020-08-27 15:38:01 +02:00
74d414c21f chore: add certified peer records to persisted peer store 2020-08-27 15:38:01 +02:00
8f2e69048f feat: cerified addressbook 2020-08-27 15:38:01 +02:00
b0a36ccbc8 chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-08-27 15:38:01 +02:00
3158366579 fix: revert new identify protocol versions 2020-08-27 15:38:01 +02:00
1d318e12d8 chore: address review 2020-08-27 15:38:01 +02:00
8a97dded26 feat: create self peer record in identify 2020-08-27 15:38:01 +02:00
e50f0eeb7b feat: exchange signed peer records in identify 2020-08-27 15:38:01 +02:00
ee57a643cc chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-08-27 15:38:01 +02:00
89658dd655 chore: address review 2020-08-27 15:38:01 +02:00
4ab125e017 fix: signature compliant with spec 2020-08-27 15:38:01 +02:00
71daac24b1 chore: refactor and better docs 2020-08-27 15:38:01 +02:00
02a5095b9c chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-08-27 15:38:01 +02:00
3e5d450eca feat: signed peer records record manager 2020-08-27 15:38:01 +02:00
098f3d1dd3 chore: update travis to use node lts and stable
BREAKING CHANGE: this drops testing support in node 10.
2020-08-27 15:38:01 +02:00
689f90a698 revert: reapply "fix: throw if no conn encryption module provided (#665)"
This reapplies commit b621fbdfdc.
2020-08-27 15:38:01 +02:00
0e3cc5866b docs: update peer store api with metadata (#735) 2020-08-21 13:03:29 +02:00
4851680c4d docs: link to official streaming iterables document (#729) 2020-08-06 13:23:18 +02:00
0e18735b8c chore: release version v0.28.10 2020-08-05 19:07:08 +02:00
f68ff35625 chore: update contributors 2020-08-05 19:07:07 +02:00
8c56ec0d23 fix: allow certain keychain operations without a password (#726)
* fix: allow certain keychain operations without a password

Listing, removing, renaming etc keys do not require a password so
the user should not be required to provide one.

This means we don't have to prompt the user to create a password
when they aren't going to do any operations that require a password.

* fix: make keychain pass optional

* fix: support libp2p creation without keychain pass

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-08-05 19:03:11 +02:00
fa5ee873e3 chore: update node and npm badges (#727) 2020-08-05 18:26:50 +02:00
51d7ca44c1 feat(keychain): add support for ed25519 and secp keys (#725)
* feat(keychain): add support for ed25519 and secp keys

* chore: bump crypto

* refactor: cleanup keychain usage
2020-08-05 18:19:10 +02:00
726a746479 fix(identify): make agentversion dynamic and add it to the peerstore (#724) 2020-08-04 18:39:05 +02:00
a331b84f13 docs(example): add pubssub message filter example
moved the filter folder

rename folder

docs(refactor): clean up pubsub validator example
2020-07-31 12:09:34 +02:00
78d152dd68 chore: release version v0.28.9 2020-07-27 11:58:53 +02:00
7e14aa19b5 chore: update contributors 2020-07-27 11:58:52 +02:00
2440c872df fix: ping multiaddr from peer not previously stored in peerstore (#719) 2020-07-27 10:53:23 +02:00
6c7e5e5eef chore: release version v0.28.8 2020-07-20 17:23:01 +02:00
388df6b6e6 chore: update contributors 2020-07-20 17:23:00 +02:00
7dbfe6ab1a chore: update libp2p-crypto (#717)
This includes a patch for ed25519 interop with Go
2020-07-20 17:18:09 +02:00
cea59a1fe4 docs: update addressBook get and getMultiaddrsForPeer return values (#716) 2020-07-20 16:17:39 +02:00
7da9ad44ab fix: create dial target for peer with no known addrs (#715) 2020-07-20 14:54:03 +02:00
3896941128 docs: use tag for webrtc-star discovery config (#713) 2020-07-20 14:51:24 +02:00
856b38de67 chore: add migration guide template (#711) 2020-07-16 18:14:02 +02:00
798d7b73c1 chore: release version v0.28.7 2020-07-14 19:13:55 +02:00
f2d0d8b51d chore: update contributors 2020-07-14 19:13:54 +02:00
999c1b7740 fix: retimer reschedule does not work as interval (#710)
* fix: retimer reschedule does not work as interval

* chore: apply suggestions from code review

Co-authored-by: Jacob Heun <jacobheun@gmail.com>

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-07-14 19:11:29 +02:00
99ff361a55 chore: release version v0.28.6 2020-07-14 16:13:00 +02:00
6115f8b680 chore: update contributors 2020-07-14 16:12:59 +02:00
9ccab40fc8 fix: not dial all known peers in parallel on startup (#698)
* fix: not dial all known peers on startup

* feat: connection manager should proactively connect to peers from peerStore

* chore: increase bundle size

* fix: do connMgr proactive dial on an interval

* chore: address review

* chore: use retimer reschedule

* chore: address review

* fix: use minConnections in default config

* chore: minPeers to minConnections everywhere
2020-07-14 16:05:26 +02:00
619e5dd73c chore: release version v0.28.5 2020-07-10 17:26:21 +02:00
0fd23f6a5f chore: update contributors 2020-07-10 17:26:21 +02:00
5a84dd56d0 fix: pass libp2p to the dht (#700) 2020-07-10 17:16:45 +02:00
7b05d6922b docs: add dht discovery documentation reference (#697)
Extended the description with a link for how to configure the libp2p-kad-dht for peer discovery.
2020-07-08 15:03:56 +02:00
08a4fad80b chore: add modules to update in 0.28.x migration (#695) 2020-07-08 14:12:40 +02:00
51da8874d8 test: add pubsub reconnect test (#693)
* test: add pubsub reconnect test

* chore: dep bump

* chore: remove temp pubsub dep

Co-authored-by: Vasco Santos <vasco.santos@moxy.studio>

Co-authored-by: Vasco Santos <vasco.santos@moxy.studio>
2020-07-07 18:31:51 +02:00
5cd8c19567 docs(fix): update link to ipfs config files (#690) 2020-07-07 12:56:17 +02:00
db766eaca9 chore: update circuit readme (#689)
* chore: update circuit readme

* chore: address review
2020-07-07 12:55:52 +02:00
a1308d640d chore: release version v0.28.4 2020-07-03 16:14:47 +02:00
19e7254c3d chore: update contributors 2020-07-03 16:14:47 +02:00
f4898eb883 chore: add test for pubsub on reconnect (#691)
* chore: add test for pubsub on reconnect

* chore: update interface dep

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-07-03 16:08:37 +02:00
9bec719fce docs(fix): link to streaming-iterables' repo (#688) 2020-07-02 13:34:01 +02:00
b664c0aafd chore: libp2p in the browser using webrtc servers (#684) 2020-06-29 16:31:07 +02:00
b524bbf627 chore: release version v0.28.3 2020-06-18 15:40:38 +02:00
07bd269fb0 chore: update contributors 2020-06-18 15:40:38 +02:00
a8219e61a0 fix: catch pipe errors (#678)
* fix: catch pipe errors

There were some pipe errors not being caught. This can result in unhandled exceptions being thrown

* fix: catch pipe errors in identify push handler
2020-06-18 15:33:08 +02:00
a1590acc8b chore: add libp2p docs website to release checklist 2020-06-18 11:48:31 +02:00
a0cf83c640 chore: release version v0.28.2 2020-06-15 12:48:25 +02:00
b56cdda0ef chore: update contributors 2020-06-15 12:48:25 +02:00
b621fbdfdc revert: "fix: throw if no conn encryption module provided (#665)"
This reverts commit c038550fad.
2020-06-15 12:45:49 +02:00
24dd1d22c5 chore: release version v0.28.1 2020-06-12 16:48:30 +02:00
a1a1213bdc chore: update contributors 2020-06-12 16:48:29 +02:00
90a9d93968 chore: refactor ConnectionManager#get 2020-06-11 16:21:51 +02:00
8f680e20e9 feat: add ConnectionManager#getAll 2020-06-11 16:21:51 +02:00
afafd08943 chore: fix discv5 npm link
chore: update package name for discv5
2020-06-11 13:03:31 +02:00
c038550fad fix: throw if no conn encryption module provided (#665)
* fix: throw if no conn encryption module provided

* chore: address review

* chore: apply suggestions from code review

Co-authored-by: Jacob Heun <jacobheun@gmail.com>

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-06-11 12:49:32 +02:00
d60a3215d0 chore: update interface datastore (#663)
* chore: update interface datastore

Updates to v1.x.x to not have multiple versions of this module in the ipfs browser bundle.

* fix: let batch commits complete before continuing tests

Batch commits are async but the tests weren't waiting for them to complete,
mainly because they are triggered by events.

There's no way that I can see of waiting for the batch commit to finish so
I've added delays to the tests. Not great but a start.

* chore: use error log

* test: wait for commit spies to complete

* chore: bump interface-datastore

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-06-10 23:39:40 +02:00
1e51295150 chore: use interop release 2020-06-08 12:20:48 +02:00
5299995998 chore: release version v0.28.0 2020-06-05 15:32:03 +02:00
64a66f08ac chore: update contributors 2020-06-05 15:32:03 +02:00
e2a63bab62 chore: release version v0.28.0-rc.0 2020-05-28 12:52:25 +02:00
294b032b0b chore: update contributors 2020-05-28 12:52:24 +02:00
72f37acd4a fix: always emit when a connection is made 2020-05-28 12:37:48 +02:00
52a615f922 fix: expose the muxed stream interface on inbound streams 2020-05-28 12:37:48 +02:00
698c1df1b4 feat: support dial only on transport manager to tolerate errors (#643)
* feat: support dial only on transport manager to tolerate errors

* chore: address review

* chore: add jsdoc to transport manager tolerance errors
2020-05-28 12:37:48 +02:00
7f4662f8d8 chore: fix api formatting (#644) 2020-05-28 12:37:48 +02:00
84b935f682 feat: metadata book (#638)
* feat: metadata book

* chore: address review

* chore: address review
2020-05-28 12:37:48 +02:00
0fbb59748e docs: update examples to 0.28.x api (#625)
* chore: update examples to 0.28 api

* chore: use libp2p-noise in examples

* chore: examples using multiaddrs property of libp2p

Co-authored-by: Jacob Heun <jacobheun@gmail.com>

* docs: update language around secio in crypto example

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
56316b8c1e docs: migration 0.27 to 0.28 (#637)
* docs: migration 0.27 to 0.28

* chore: apply suggestions from code review

Co-authored-by: Jacob Heun <jacobheun@gmail.com>

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
aaa1155633 chore: uncomment local peer public key after connect test 2020-05-28 12:37:48 +02:00
aa5e232479 chore: rewrite peer-store submit and retrieve docs (#605)
* chore: rewrite peer-store submit and retrieve docs

* chore: address review
2020-05-28 12:37:48 +02:00
eb7adcf1ab docs: libp2p components options specified 2020-05-28 12:37:48 +02:00
0be74e6a61 chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
6065923356 chore: integrate libp2p-keychain into js-libp2p (#633)
Integrates the libp2p-keychain codebase into this repo
2020-05-28 12:37:48 +02:00
2b45fee0ed fix: onConnect should not add addr to the addressBook 2020-05-28 12:37:48 +02:00
8bf5a70bb8 chore: integrate libp2p-keychain into js-libp2p (#633)
Integrates the libp2p-keychain codebase into this repo

Co-authored-by: David Dias <daviddias.p@gmail.com>
Co-authored-by: Richard Schneider <makaretu@gmail.com>
Co-authored-by: Maciej Krüger <mkg20001@gmail.com>
Co-authored-by: Victor Bjelkholm <victorbjelkholm@gmail.com>
Co-authored-by: Masahiro Saito <camelmasa@gmail.com>
Co-authored-by: Alan Shaw <alan.shaw@protocol.ai>
Co-authored-by: Hugo Dias <mail@hugodias.me>
Co-authored-by: Alberto Elias <hi@albertoelias.me>
Co-authored-by: Alex Potsides <alex@achingbrain.net>
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
6627278a87 chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
65e87460b0 chore: add keys to keybook on connection upgraded 2020-05-28 12:37:48 +02:00
7b8d01697d chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
ce38033c10 feat: keybook 2020-05-28 12:37:48 +02:00
3f2b06dc26 chore: remove peer-info from package table 2020-05-28 12:37:48 +02:00
1e3d6f4b56 chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
48a9a3eecc refactor: peristent peer-store extended class and disabled by defaul 2020-05-28 12:37:48 +02:00
9ea9287bea chore: persist delete with test 2020-05-28 12:37:48 +02:00
5123a8357b feat: peerStore persistence 2020-05-28 12:37:48 +02:00
43630f1e0b test: use libp2p-noise (#585)
* chore: use libp2p-noise

* chore: address review
2020-05-28 12:37:48 +02:00
aaf62a40ec fix: libp2p connections getter 2020-05-28 12:37:48 +02:00
7fbd1556e8 fix: use libp2p.multiaddrs instead of listen
Co-Authored-By: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
51474c334a chore: rename address functions and apply suggestions from code review
Co-Authored-By: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
d75cc97ced chore: update api docs 2020-05-28 12:37:48 +02:00
2a7967c1cc feat: address manager 2020-05-28 12:37:48 +02:00
9e9ec0b575 chore: apply suggestions from code review
Co-Authored-By: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
d3a4bf0a3f chore: refactor connection manager and registrar 2020-05-28 12:37:48 +02:00
d33919d0b3 chore: use kad-dht with renamed peer-store properties 2020-05-28 12:37:48 +02:00
c215339a27 chore: rename peer-store properties 2020-05-28 12:37:48 +02:00
cb597b57d7 chore: apply suggestions from code review
Co-Authored-By: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
12e48adafa chore: remove peer-info usage
BREAKING CHANGE: all API methods with peer-info parameters or return values were changed. You can check the API.md document, in order to check the new values to use
2020-05-28 12:37:48 +02:00
ed6d5bb4b4 chore: deprecate old peer store api (#598)
* chore: deprecate old peer-store api

BREAKING CHANGE: the peer-store api changed. Check the API docs for the new specification.

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
e9d225c9dc feat: address and proto books (#590)
* feat: address and proto books

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>

* chore: minor fixes and initial tests added

* chore: integrate new peer-store with code using adapters for other modules

* chore: do not use peerstore.put on get-peer-info

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>

* chore: add new peer store tests

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-05-28 12:37:48 +02:00
2aac3b0f69 chore: update libp2p-gossipsub url 2020-05-27 23:14:54 +02:00
9862653051 chore: add readme reference to migrations 2020-05-25 14:54:03 +02:00
b781911ac2 fix(example): rename misleading variable (#645)
* fix(example): rename misleading variable

* fix: typo
2020-05-25 13:14:07 +02:00
3fa1fa3a0b chore: increase max bundle size to 185kB 2020-05-11 11:00:13 +02:00
dfed982404 chore(deps-dev): bump libp2p-gossipsub from 0.2.6 to 0.3.0
Bumps [libp2p-gossipsub](https://github.com/ChainSafe/gossipsub-js) from 0.2.6 to 0.3.0.
- [Release notes](https://github.com/ChainSafe/gossipsub-js/releases)
- [Changelog](https://github.com/ChainSafe/gossipsub-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ChainSafe/gossipsub-js/compare/v0.2.6...v0.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-11 11:00:13 +02:00
60b6a55906 chore(deps-dev): bump aegir from 21.10.2 to 22.0.0 (#632)
Bumps [aegir](https://github.com/ipfs/aegir) from 21.10.2 to 22.0.0.
- [Release notes](https://github.com/ipfs/aegir/releases)
- [Changelog](https://github.com/ipfs/aegir/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ipfs/aegir/compare/v21.10.2...v22.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-08 10:52:49 +02:00
200 changed files with 17925 additions and 3313 deletions

View File

@ -4,21 +4,21 @@ const Libp2p = require('./src')
const { MULTIADDRS_WEBSOCKETS } = require('./test/fixtures/browser')
const Peers = require('./test/fixtures/peers')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const WebSockets = require('libp2p-websockets')
const Muxer = require('libp2p-mplex')
const Crypto = require('libp2p-secio')
const { NOISE: Crypto } = require('libp2p-noise')
const pipe = require('it-pipe')
let libp2p
const before = async () => {
// Use the last peer
const peerId = await PeerId.createFromJSON(Peers[Peers.length - 1])
const peerInfo = new PeerInfo(peerId)
peerInfo.multiaddrs.add(MULTIADDRS_WEBSOCKETS[0])
libp2p = new Libp2p({
peerInfo,
addresses: {
listen: [MULTIADDRS_WEBSOCKETS[0]]
},
peerId,
modules: {
transport: [WebSockets],
streamMuxer: [Muxer],
@ -31,6 +31,9 @@ const before = async () => {
enabled: true,
active: false
}
},
nat: {
enabled: false
}
}
})
@ -45,9 +48,15 @@ const after = async () => {
}
module.exports = {
bundlesize: { maxSize: '179kB' },
bundlesize: { maxSize: '215kB' },
hooks: {
pre: before,
post: after
},
webpack: {
node: {
// needed by bcrypto
Buffer: true
}
}
}

109
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,109 @@
name: ci
on:
push:
branches:
- master
pull_request:
branches:
- '**'
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: yarn lint
- uses: gozala/typescript-error-reporter-action@v1.0.8
- run: yarn build
- run: yarn aegir dep-check
- uses: ipfs/aegir/actions/bundle-size@master
name: size
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
test-node:
needs: check
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
node: [12, 14]
fail-fast: true
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- run: yarn
- run: npx nyc --reporter=lcov aegir test -t node -- --bail
- uses: codecov/codecov-action@v1
test-chrome:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: npx aegir test -t browser -t webworker --bail
test-firefox:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: npx aegir test -t browser -t webworker --bail -- --browsers FirefoxHeadless
test-interop:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd node_modules/interop-libp2p && yarn && LIBP2P_JS=${GITHUB_WORKSPACE}/src/index.js npx aegir test -t node --bail
test-auto-relay-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- auto-relay
test-chat-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- chat
test-connection-encryption-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- connection-encryption
test-echo-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- echo
test-libp2p-in-the-browser-example:
needs: check
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- libp2p-in-the-browser
test-discovery-mechanisms-example:
needs: check
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- discovery-mechanisms
test-pnet-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- pnet

View File

@ -1,50 +0,0 @@
language: node_js
cache: npm
stages:
- check
- test
- cov
node_js:
- '10'
- '12'
os:
- linux
- osx
script: npx nyc -s npm run test:node -- --bail
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
jobs:
include:
- stage: check
script:
- npx aegir build --bundlesize
# Remove pull libs once ping is async
- npx aegir dep-check -- -i pull-handshake -i pull-stream
- npm run lint
- stage: test
name: chrome
addons:
chrome: stable
script:
- npx aegir test -t browser -t webworker
- stage: test
name: firefox
addons:
firefox: latest
script:
- npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless
- stage: test
name: interop
script:
- cd node_modules/interop-libp2p
- npm install
- LIBP2P_JS=${TRAVIS_BUILD_DIR}/src/index.js npx aegir test -t node --bail
notifications:
email: false

View File

@ -1,3 +1,502 @@
## [0.30.6](https://github.com/libp2p/js-libp2p/compare/v0.30.5...v0.30.6) (2021-01-29)
### Bug Fixes
* peer discovery type in config ([#878](https://github.com/libp2p/js-libp2p/issues/878)) ([3e7594f](https://github.com/libp2p/js-libp2p/commit/3e7594f69733bf374b374a6065458fa6cae81c5f))
* unref nat manager retries ([#877](https://github.com/libp2p/js-libp2p/issues/877)) ([ce2a624](https://github.com/libp2p/js-libp2p/commit/ce2a624a09b3107c0b2b4752e666804ecea54fb5))
## [0.30.5](https://github.com/libp2p/js-libp2p/compare/v0.30.4...v0.30.5) (2021-01-28)
### Bug Fixes
* create has optional peer id type ([#875](https://github.com/libp2p/js-libp2p/issues/875)) ([eeda056](https://github.com/libp2p/js-libp2p/commit/eeda05688330c17b810bf47544ef977386623317))
## [0.30.4](https://github.com/libp2p/js-libp2p/compare/v0.30.3...v0.30.4) (2021-01-27)
### Features
* add UPnP NAT manager ([#810](https://github.com/libp2p/js-libp2p/issues/810)) ([0a6bc0d](https://github.com/libp2p/js-libp2p/commit/0a6bc0d1013dfd80ab600e8f74c1544b433ece29))
## [0.30.3](https://github.com/libp2p/js-libp2p/compare/v0.30.2...v0.30.3) (2021-01-27)
## [0.30.2](https://github.com/libp2p/js-libp2p/compare/v0.30.1...v0.30.2) (2021-01-21)
### Bug Fixes
* store multiaddrs during content and peer routing queries ([#865](https://github.com/libp2p/js-libp2p/issues/865)) ([45c3367](https://github.com/libp2p/js-libp2p/commit/45c33675a7412c66d0fd4e113ef8506077b6f492))
## [0.30.1](https://github.com/libp2p/js-libp2p/compare/v0.30.0...v0.30.1) (2021-01-18)
### Bug Fixes
* event emitter types with local types ([#864](https://github.com/libp2p/js-libp2p/issues/864)) ([6c41e30](https://github.com/libp2p/js-libp2p/commit/6c41e3045608bcae8061d20501be5751dad8157a))
# [0.30.0](https://github.com/libp2p/js-libp2p/compare/v0.29.4...v0.30.0) (2020-12-16)
### Bug Fixes
* remove test/dialing/utils extra file ([689c35e](https://github.com/libp2p/js-libp2p/commit/689c35ed1c68e514293a9895d496e2e8440454e9))
* types from ipfs integration ([#832](https://github.com/libp2p/js-libp2p/issues/832)) ([9ae1b75](https://github.com/libp2p/js-libp2p/commit/9ae1b758e99e3fc9067e26b4eae4c15ccb1ba303))
### chore
* update pubsub ([#801](https://github.com/libp2p/js-libp2p/issues/801)) ([e50c6ab](https://github.com/libp2p/js-libp2p/commit/e50c6abcf2ebc80ebf2dfadd015ab21a20cffadc))
### Features
* auto relay ([#723](https://github.com/libp2p/js-libp2p/issues/723)) ([caf66ea](https://github.com/libp2p/js-libp2p/commit/caf66ea1439f6b75a0c321a16bd5c5d7d6a2bd47))
* auto relay network query for new relays ([0bf0b7c](https://github.com/libp2p/js-libp2p/commit/0bf0b7cf8968d55002ac4c559ffb59985feeb092))
* custom announce filter ([ef9d3ca](https://github.com/libp2p/js-libp2p/commit/ef9d3ca2c6f35d692d6079e74088c5146d46eebe))
* custom dialer addr sorter ([#792](https://github.com/libp2p/js-libp2p/issues/792)) ([585ad52](https://github.com/libp2p/js-libp2p/commit/585ad52b4c71dd7514e99a287e0318b2b837ec48))
* discover and connect to closest peers ([#798](https://github.com/libp2p/js-libp2p/issues/798)) ([baedf3f](https://github.com/libp2p/js-libp2p/commit/baedf3fe5ab946e938db1415d1662452cdfc0cc1))
### BREAKING CHANGES
* pubsub signing policy properties were changed according to libp2p-interfaces changes to a single property. The emitSelf option default value was also modified to match the routers value
# [0.30.0-rc.2](https://github.com/libp2p/js-libp2p/compare/v0.30.0-rc.1...v0.30.0-rc.2) (2020-12-15)
# [0.30.0-rc.1](https://github.com/libp2p/js-libp2p/compare/v0.30.0-rc.0...v0.30.0-rc.1) (2020-12-11)
### Bug Fixes
* types from ipfs integration ([#832](https://github.com/libp2p/js-libp2p/issues/832)) ([216eb97](https://github.com/libp2p/js-libp2p/commit/216eb9730ef473f73a974c3dbaf306ecdc815c8b))
# [0.30.0-rc.0](https://github.com/libp2p/js-libp2p/compare/v0.29.4...v0.30.0-rc.0) (2020-12-10)
### Bug Fixes
* remove test/dialing/utils extra file ([3f1dc20](https://github.com/libp2p/js-libp2p/commit/3f1dc20caf1c80078f403deb9174cd06d08567ab))
### chore
* update pubsub ([#801](https://github.com/libp2p/js-libp2p/issues/801)) ([9205fce](https://github.com/libp2p/js-libp2p/commit/9205fce34d0cd8dd5d32988be34c110fc0a5b6e2))
### Features
* auto relay ([#723](https://github.com/libp2p/js-libp2p/issues/723)) ([65ec267](https://github.com/libp2p/js-libp2p/commit/65ec267e7f4826caacd042213c3fbacce589ab5b))
* auto relay network query for new relays ([9faf1bf](https://github.com/libp2p/js-libp2p/commit/9faf1bfcf61581acc715b9be78b71dc14501835a))
* custom announce filter ([48476d5](https://github.com/libp2p/js-libp2p/commit/48476d504a98b7b51b3e2dc64eab93670fde0c7b))
* custom dialer addr sorter ([#792](https://github.com/libp2p/js-libp2p/issues/792)) ([91b15b6](https://github.com/libp2p/js-libp2p/commit/91b15b6790952b4db11264961d9c6f2a96d1fe43))
* discover and connect to closest peers ([#798](https://github.com/libp2p/js-libp2p/issues/798)) ([b73106e](https://github.com/libp2p/js-libp2p/commit/b73106eba2d559621f427f7aa788e9b0ef47d135))
### BREAKING CHANGES
* pubsub signing policy properties were changed according to libp2p-interfaces changes to a single property. The emitSelf option default value was also modified to match the routers value
<a name="0.29.4"></a>
## [0.29.4](https://github.com/libp2p/js-libp2p/compare/v0.29.3...v0.29.4) (2020-12-09)
### Bug Fixes
* dial self ([#826](https://github.com/libp2p/js-libp2p/issues/826)) ([6350a18](https://github.com/libp2p/js-libp2p/commit/6350a18))
### Features
* custom and store self agent version + store self protocol version ([#800](https://github.com/libp2p/js-libp2p/issues/800)) ([d0a9fad](https://github.com/libp2p/js-libp2p/commit/d0a9fad))
* support custom listener options ([#822](https://github.com/libp2p/js-libp2p/issues/822)) ([8691465](https://github.com/libp2p/js-libp2p/commit/8691465))
<a name="0.29.3"></a>
## [0.29.3](https://github.com/libp2p/js-libp2p/compare/v0.29.2...v0.29.3) (2020-11-04)
### Features
* resolve multiaddrs before dial ([#782](https://github.com/libp2p/js-libp2p/issues/782)) ([093c0ea](https://github.com/libp2p/js-libp2p/commit/093c0ea))
<a name="0.29.2"></a>
## [0.29.2](https://github.com/libp2p/js-libp2p/compare/v0.29.1...v0.29.2) (2020-10-23)
### Bug Fixes
* cleanup open streams on conn close ([#791](https://github.com/libp2p/js-libp2p/issues/791)) ([06f26e5](https://github.com/libp2p/js-libp2p/commit/06f26e5))
<a name="0.29.1"></a>
## [0.29.1](https://github.com/libp2p/js-libp2p/compare/v0.29.0...v0.29.1) (2020-10-22)
### Bug Fixes
* catch error in upgrader close call ([e04224a](https://github.com/libp2p/js-libp2p/commit/e04224a))
* ensure streams are closed on connection close ([4c6be91](https://github.com/libp2p/js-libp2p/commit/4c6be91))
* flakey identify test firefox ([#774](https://github.com/libp2p/js-libp2p/issues/774)) ([60d437f](https://github.com/libp2p/js-libp2p/commit/60d437f))
<a name="0.29.0"></a>
# [0.29.0](https://github.com/libp2p/js-libp2p/compare/v0.28.10...v0.29.0) (2020-08-27)
### Bug Fixes
* do not return self on peerstore.peers ([15613cc](https://github.com/libp2p/js-libp2p/commit/15613cc))
* peer record interop with go ([#739](https://github.com/libp2p/js-libp2p/issues/739)) ([93dda74](https://github.com/libp2p/js-libp2p/commit/93dda74))
* replace node buffers with uint8arrays ([#730](https://github.com/libp2p/js-libp2p/issues/730)) ([1e86971](https://github.com/libp2p/js-libp2p/commit/1e86971))
* revert new identify protocol versions ([3158366](https://github.com/libp2p/js-libp2p/commit/3158366))
* signature compliant with spec ([4ab125e](https://github.com/libp2p/js-libp2p/commit/4ab125e))
### Chores
* update travis to use node lts and stable ([098f3d1](https://github.com/libp2p/js-libp2p/commit/098f3d1))
### Features
* cerified addressbook ([8f2e690](https://github.com/libp2p/js-libp2p/commit/8f2e690))
* create self peer record in identify ([8a97dde](https://github.com/libp2p/js-libp2p/commit/8a97dde))
* exchange signed peer records in identify ([e50f0ee](https://github.com/libp2p/js-libp2p/commit/e50f0ee))
* gossipsub 1.1 ([#733](https://github.com/libp2p/js-libp2p/issues/733)) ([55c9bfa](https://github.com/libp2p/js-libp2p/commit/55c9bfa))
* signed peer records record manager ([3e5d450](https://github.com/libp2p/js-libp2p/commit/3e5d450))
### Reverts
* reapply "fix: throw if no conn encryption module provided ([#665](https://github.com/libp2p/js-libp2p/issues/665))" ([689f90a](https://github.com/libp2p/js-libp2p/commit/689f90a))
### BREAKING CHANGES
* pubsub implementation is now directly exposed and its API was updated according to the new pubsub interface in js-libp2p-interfaces repo
* chore: use gossipsub branch with src added
* fix: add pubsub handlers adapter
* chore: fix deps
* chore: update pubsub docs and examples
* chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* chore: use new floodsub
* chore: change validator doc set
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* chore: add new gossipsub src
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* - All deps used by this module now use Uint8Arrays in place of node Buffers
* chore: browser fixes
* chore: remove .only
* chore: stringify uint8array before parsing
* chore: update interop suite
* chore: remove ts from build command
* chore: update deps
* fix: update records to use uint8array
* chore: fix lint
* chore: update deps
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* this drops testing support in node 10.
<a name="0.29.0-rc.1"></a>
# [0.29.0-rc.1](https://github.com/libp2p/js-libp2p/compare/v0.29.0-rc.0...v0.29.0-rc.1) (2020-08-27)
### Bug Fixes
* peer record interop with go ([#739](https://github.com/libp2p/js-libp2p/issues/739)) ([c4c7ef9](https://github.com/libp2p/js-libp2p/commit/c4c7ef9))
<a name="0.29.0-rc.0"></a>
# [0.29.0-rc.0](https://github.com/libp2p/js-libp2p/compare/v0.28.10...v0.29.0-rc.0) (2020-08-25)
### Bug Fixes
* do not return self on peerstore.peers ([e1b8edc](https://github.com/libp2p/js-libp2p/commit/e1b8edc))
* replace node buffers with uint8arrays ([#730](https://github.com/libp2p/js-libp2p/issues/730)) ([507f8c4](https://github.com/libp2p/js-libp2p/commit/507f8c4))
* revert new identify protocol versions ([a798c65](https://github.com/libp2p/js-libp2p/commit/a798c65))
* signature compliant with spec ([97b5d2a](https://github.com/libp2p/js-libp2p/commit/97b5d2a))
### Chores
* update travis to use node lts and stable ([c272288](https://github.com/libp2p/js-libp2p/commit/c272288))
### Features
* cerified addressbook ([e0ed258](https://github.com/libp2p/js-libp2p/commit/e0ed258))
* create self peer record in identify ([83922a7](https://github.com/libp2p/js-libp2p/commit/83922a7))
* exchange signed peer records in identify ([f835457](https://github.com/libp2p/js-libp2p/commit/f835457))
* gossipsub 1.1 ([#733](https://github.com/libp2p/js-libp2p/issues/733)) ([e14ce40](https://github.com/libp2p/js-libp2p/commit/e14ce40))
* signed peer records record manager ([f95edf1](https://github.com/libp2p/js-libp2p/commit/f95edf1))
### Reverts
* reapply "fix: throw if no conn encryption module provided ([#665](https://github.com/libp2p/js-libp2p/issues/665))" ([ad7f02e](https://github.com/libp2p/js-libp2p/commit/ad7f02e))
### BREAKING CHANGES
* pubsub implementation is now directly exposed and its API was updated according to the new pubsub interface in js-libp2p-interfaces repo
* chore: use gossipsub branch with src added
* fix: add pubsub handlers adapter
* chore: fix deps
* chore: update pubsub docs and examples
* chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* chore: use new floodsub
* chore: change validator doc set
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* chore: add new gossipsub src
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* - All deps used by this module now use Uint8Arrays in place of node Buffers
* chore: browser fixes
* chore: remove .only
* chore: stringify uint8array before parsing
* chore: update interop suite
* chore: remove ts from build command
* chore: update deps
* fix: update records to use uint8array
* chore: fix lint
* chore: update deps
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* this drops testing support in node 10.
<a name="0.28.10"></a>
## [0.28.10](https://github.com/libp2p/js-libp2p/compare/v0.28.9...v0.28.10) (2020-08-05)
### Bug Fixes
* allow certain keychain operations without a password ([#726](https://github.com/libp2p/js-libp2p/issues/726)) ([8c56ec0](https://github.com/libp2p/js-libp2p/commit/8c56ec0))
* **identify:** make agentversion dynamic and add it to the peerstore ([#724](https://github.com/libp2p/js-libp2p/issues/724)) ([726a746](https://github.com/libp2p/js-libp2p/commit/726a746))
### Features
* **keychain:** add support for ed25519 and secp keys ([#725](https://github.com/libp2p/js-libp2p/issues/725)) ([51d7ca4](https://github.com/libp2p/js-libp2p/commit/51d7ca4))
<a name="0.28.9"></a>
## [0.28.9](https://github.com/libp2p/js-libp2p/compare/v0.28.8...v0.28.9) (2020-07-27)
### Bug Fixes
* ping multiaddr from peer not previously stored in peerstore ([#719](https://github.com/libp2p/js-libp2p/issues/719)) ([2440c87](https://github.com/libp2p/js-libp2p/commit/2440c87))
<a name="0.28.8"></a>
## [0.28.8](https://github.com/libp2p/js-libp2p/compare/v0.28.7...v0.28.8) (2020-07-20)
### Bug Fixes
* create dial target for peer with no known addrs ([#715](https://github.com/libp2p/js-libp2p/issues/715)) ([7da9ad4](https://github.com/libp2p/js-libp2p/commit/7da9ad4))
<a name="0.28.7"></a>
## [0.28.7](https://github.com/libp2p/js-libp2p/compare/v0.28.6...v0.28.7) (2020-07-14)
### Bug Fixes
* retimer reschedule does not work as interval ([#710](https://github.com/libp2p/js-libp2p/issues/710)) ([999c1b7](https://github.com/libp2p/js-libp2p/commit/999c1b7))
<a name="0.28.6"></a>
## [0.28.6](https://github.com/libp2p/js-libp2p/compare/v0.28.5...v0.28.6) (2020-07-14)
### Bug Fixes
* not dial all known peers in parallel on startup ([#698](https://github.com/libp2p/js-libp2p/issues/698)) ([9ccab40](https://github.com/libp2p/js-libp2p/commit/9ccab40))
<a name="0.28.5"></a>
## [0.28.5](https://github.com/libp2p/js-libp2p/compare/v0.28.4...v0.28.5) (2020-07-10)
### Bug Fixes
* pass libp2p to the dht ([#700](https://github.com/libp2p/js-libp2p/issues/700)) ([5a84dd5](https://github.com/libp2p/js-libp2p/commit/5a84dd5))
<a name="0.28.4"></a>
## [0.28.4](https://github.com/libp2p/js-libp2p/compare/v0.28.3...v0.28.4) (2020-07-03)
<a name="0.28.3"></a>
## [0.28.3](https://github.com/libp2p/js-libp2p/compare/v0.28.2...v0.28.3) (2020-06-18)
### Bug Fixes
* catch pipe errors ([#678](https://github.com/libp2p/js-libp2p/issues/678)) ([a8219e6](https://github.com/libp2p/js-libp2p/commit/a8219e6))
<a name="0.28.2"></a>
## [0.28.2](https://github.com/libp2p/js-libp2p/compare/v0.28.1...v0.28.2) (2020-06-15)
### Reverts
* "fix: throw if no conn encryption module provided ([#665](https://github.com/libp2p/js-libp2p/issues/665))" ([b621fbd](https://github.com/libp2p/js-libp2p/commit/b621fbd))
<a name="0.28.1"></a>
## [0.28.1](https://github.com/libp2p/js-libp2p/compare/v0.28.0...v0.28.1) (2020-06-12)
### Bug Fixes
* throw if no conn encryption module provided ([#665](https://github.com/libp2p/js-libp2p/issues/665)) ([c038550](https://github.com/libp2p/js-libp2p/commit/c038550))
### Features
* add ConnectionManager#getAll ([8f680e2](https://github.com/libp2p/js-libp2p/commit/8f680e2))
<a name="0.28.0"></a>
# [0.28.0](https://github.com/libp2p/js-libp2p/compare/v0.28.0-rc.0...v0.28.0) (2020-06-05)
<a name="0.28.0-rc.0"></a>
# [0.28.0-rc.0](https://github.com/libp2p/js-libp2p/compare/v0.27.8...v0.28.0-rc.0) (2020-05-28)
### Bug Fixes
* always emit when a connection is made ([72f37ac](https://github.com/libp2p/js-libp2p/commit/72f37ac))
* expose the muxed stream interface on inbound streams ([52a615f](https://github.com/libp2p/js-libp2p/commit/52a615f))
* libp2p connections getter ([aaf62a4](https://github.com/libp2p/js-libp2p/commit/aaf62a4))
* onConnect should not add addr to the addressBook ([2b45fee](https://github.com/libp2p/js-libp2p/commit/2b45fee))
* use libp2p.multiaddrs instead of listen ([7fbd155](https://github.com/libp2p/js-libp2p/commit/7fbd155))
* **example:** rename misleading variable ([#645](https://github.com/libp2p/js-libp2p/issues/645)) ([b781911](https://github.com/libp2p/js-libp2p/commit/b781911))
### Chores
* deprecate old peer store api ([#598](https://github.com/libp2p/js-libp2p/issues/598)) ([ed6d5bb](https://github.com/libp2p/js-libp2p/commit/ed6d5bb))
* remove peer-info usage ([12e48ad](https://github.com/libp2p/js-libp2p/commit/12e48ad))
### Features
* address and proto books ([#590](https://github.com/libp2p/js-libp2p/issues/590)) ([e9d225c](https://github.com/libp2p/js-libp2p/commit/e9d225c))
* address manager ([2a7967c](https://github.com/libp2p/js-libp2p/commit/2a7967c))
* keybook ([ce38033](https://github.com/libp2p/js-libp2p/commit/ce38033))
* metadata book ([#638](https://github.com/libp2p/js-libp2p/issues/638)) ([84b935f](https://github.com/libp2p/js-libp2p/commit/84b935f))
* peerStore persistence ([5123a83](https://github.com/libp2p/js-libp2p/commit/5123a83))
* support dial only on transport manager to tolerate errors ([#643](https://github.com/libp2p/js-libp2p/issues/643)) ([698c1df](https://github.com/libp2p/js-libp2p/commit/698c1df))
### BREAKING CHANGES
* all API methods with peer-info parameters or return values were changed. You can check the API.md document, in order to check the new values to use
* the peer-store api changed. Check the API docs for the new specification.
* chore: apply suggestions from code review
Co-Authored-By: Jacob Heun <jacobheun@gmail.com>
* chore: apply suggestions from code review
Co-Authored-By: Jacob Heun <jacobheun@gmail.com>
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
<a name="0.27.8"></a>
## [0.27.8](https://github.com/libp2p/js-libp2p/compare/v0.27.7...v0.27.8) (2020-05-06)

45
MIGRATION_TEMPLATE.md Normal file
View File

@ -0,0 +1,45 @@
<!--Specify versions for migration below-->
# Migrating to libp2p@__
A migration guide for refactoring your application code from libp2p v__ to v__.
## Table of Contents
- [API](#api)
- [Module Updates](#module-updates)
## API
<!--Describe breaking APIs with examples for Before and After
Example:
### Peer Discovery
__Describe__
**Before**
```js
```
**After**
```js
```
-->
## Module Updates
With this release you should update the following libp2p modules if you are relying on them:
<!--Specify module versions in JSON for migration below.
It's recommended to check package.json changes for this:
`git diff <release> <prev> -- package.json`
-->
```json
```

View File

@ -16,15 +16,15 @@
</p>
<p align="center">
<a href="https://travis-ci.com/libp2p/js-libp2p"><img src="https://flat.badgen.net/travis/libp2p/js-libp2p" /></a>
<a href="https://codecov.io/gh/libp2p/js-libp2p"><img src="https://img.shields.io/codecov/c/github/ipfs/js-ipfs-multipart/master.svg?style=flat-square"></a>
<a href="https://github.com/libp2p/js-libp2p/actions?query=branch%3Amaster+workflow%3Aci+"><img src="https://img.shields.io/github/workflow/status/libp2p/js-libp2p/ci?label=ci&style=flat-square" /></a>
<a href="https://codecov.io/gh/libp2p/js-libp2p"><img src="https://img.shields.io/codecov/c/github/libp2p/js-libp2p/master.svg?style=flat-square"></a>
<a href="https://bundlephobia.com/result?p=ipfsd-ctl"><img src="https://flat.badgen.net/bundlephobia/minzip/ipfsd-ctl"></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>
<a href=""><img src="https://img.shields.io/badge/npm-%3E%3D6.0.0-orange.svg?style=flat-square" /></a>
<a href=""><img src="https://img.shields.io/badge/Node.js-%3E%3D12.0.0-orange.svg?style=flat-square" /></a>
<br>
</p>
@ -35,9 +35,11 @@ We've come a long way, but this project is still in Alpha, lots of development i
The documentation in the master branch may contain changes from a pre-release.
If you are looking for the documentation of the latest release, you can view the latest release on [**npm**](https://www.npmjs.com/package/libp2p), or select the tag in github that matches the version you are looking for.
**Want to get started?** Check our [examples folder](/examples).
**Want to get started?** Check our [GETTING_STARTED.md](./doc/GETTING_STARTED.md) guide and [examples folder](/examples).
[**`Weekly Core Dev Calls`**](https://github.com/ipfs/pm/issues/650)
**Want to update libp2p in your project?** Check our [migrations folder](./doc/migrations).
[**`Weekly Core Dev Calls`**](https://github.com/libp2p/team-mgmt/issues/16)
## Lead Maintainer
@ -144,7 +146,7 @@ List of packages currently in existence for libp2p
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-webrtc-star/master)](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star/branch/master/graph/badge.svg?style=flat-square)](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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-websockets/master)](https://travis-ci.com/libp2p/js-libp2p-websockets) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websockets/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-websockets) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| **secure 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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-secio/master)](https://travis-ci.com/libp2p/js-libp2p-secio) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-secio/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-secio) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
| [`libp2p-noise`](//github.com/NodeFactoryIo/js-libp2p-noise) | [![npm](https://img.shields.io/npm/v/libp2p-noise.svg?maxAge=86400&style=flat-square)](//github.com/NodeFactoryIo/js-libp2p-noise/releases) | [![Deps](https://david-dm.org/NodeFactoryIo/js-libp2p-noise.svg?style=flat-square)](https://david-dm.org/NodeFactoryIo/js-libp2p-noise) | [![Travis CI](https://flat.badgen.net/travis/NodeFactoryIo/js-libp2p-noise/master)](https://travis-ci.com/NodeFactoryIo/js-libp2p-noise) | [![codecov](https://codecov.io/gh/NodeFactoryIo/js-libp2p-noise/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/NodeFactoryIo/js-libp2p-noise) | N/A |
| **stream multiplexers** |
| [`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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-mplex/master)](https://travis-ci.com/libp2p/js-libp2p-mplex) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-mplex/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-mplex) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| **peer discovery** |
@ -152,7 +154,7 @@ List of packages currently in existence for libp2p
| [`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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-kad-dht/master)](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg?style=flat-square)](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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-mdns/master)](https://travis-ci.com/libp2p/js-libp2p-mdns) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-mdns/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-mdns) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-webrtc-star/master)](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`discv5`](//github.com/ChainSafe/discv5) | [![npm](https://img.shields.io/npm/v/discv5.svg?maxAge=86400&style=flat-square)](//github.com/ChainSafe/discv5/releases) | [![Deps](https://david-dm.org/ChainSafe/discv5.svg?style=flat-square)](https://david-dm.org/ChainSafe/discv5) | [![Travis CI](https://flat.badgen.net/travis/ChainSafe/discv5/master)](https://travis-ci.com/ChainSafe/discv5) | [![codecov](https://codecov.io/gh/ChainSafe/discv5/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ChainSafe/discv5) | [Cayman Nava](mailto:caymannava@gmail.com) |
| [`@chainsafe/discv5`](//github.com/ChainSafe/discv5) | [![npm](https://img.shields.io/npm/v/@chainsafe/discv5.svg?maxAge=86400&style=flat-square)](//github.com/ChainSafe/discv5/releases) | [![Deps](https://david-dm.org/ChainSafe/discv5.svg?style=flat-square)](https://david-dm.org/ChainSafe/discv5) | [![Travis CI](https://flat.badgen.net/travis/ChainSafe/discv5/master)](https://travis-ci.com/ChainSafe/discv5) | [![codecov](https://codecov.io/gh/ChainSafe/discv5/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ChainSafe/discv5) | [Cayman Nava](mailto:caymannava@gmail.com) |
| **content routing** |
| [`libp2p-delegated-content-routing`](//github.com/libp2p/js-libp2p-delegated-content-routing) | [![npm](https://img.shields.io/npm/v/libp2p-delegated-content-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-delegated-content-routing/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-delegated-content-routing/master)](https://travis-ci.com/libp2p/js-libp2p-delegated-content-routing) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-delegated-content-routing/branch/master/graph/badge.svg?style=flat-square)](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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-kad-dht/master)](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
@ -164,11 +166,9 @@ List of packages currently in existence for libp2p
| [`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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-crypto-secp256k1/master)](https://travis-ci.com/libp2p/js-libp2p-crypto-secp256k1) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
| **data types** |
| [`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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-peer-id/master)](https://travis-ci.com/libp2p/js-peer-id) | [![codecov](https://codecov.io/gh/libp2p/js-peer-id/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-peer-id) | [Vasco Santos](mailto:santos.vasco10@gmail.com) |
| [`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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-peer-info/master)](https://travis-ci.com/libp2p/js-peer-info) | [![codecov](https://codecov.io/gh/libp2p/js-peer-info/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-peer-info) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| **pubsub** |
| [`libp2p-pubsub`](//github.com/libp2p/js-libp2p-pubsub) | [![npm](https://img.shields.io/npm/v/libp2p-pubsub.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-pubsub/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-pubsub.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-pubsub) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-pubsub/master)](https://travis-ci.com/libp2p/js-libp2p-pubsub) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-pubsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-pubsub) | [Vasco Santos](mailto:santos.vasco10@gmail.com) |
| [`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) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-floodsub/master)](https://travis-ci.com/libp2p/js-libp2p-floodsub) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-floodsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-floodsub) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-gossipsub`](//github.com/ChainSafe/gossipsub-js) | [![npm](https://img.shields.io/npm/v/libp2p-gossipsub.svg?maxAge=86400&style=flat-square)](//github.com/ChainSafe/gossipsub-js/releases) | [![Deps](https://david-dm.org/ChainSafe/gossipsub-js.svg?style=flat-square)](https://david-dm.org/ChainSafe/gossipsub-js) | [![Travis CI](https://flat.badgen.net/travis/ChainSafe/gossipsub-js/master)](https://travis-ci.com/ChainSafe/gossipsub-js) | [![codecov](https://codecov.io/gh/ChainSafe/gossipsub-js/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ChainSafe/gossipsub-js) | [Cayman Nava](mailto:caymannava@gmail.com) |
| [`libp2p-gossipsub`](//github.com/ChainSafe/js-libp2p-gossipsub) | [![npm](https://img.shields.io/npm/v/libp2p-gossipsub.svg?maxAge=86400&style=flat-square)](//github.com/ChainSafe/js-libp2p-gossipsub/releases) | [![Deps](https://david-dm.org/ChainSafe/js-libp2p-gossipsub.svg?style=flat-square)](https://david-dm.org/ChainSafe/js-libp2p-gossipsub) | [![Travis CI](https://flat.badgen.net/travis/ChainSafe/js-libp2p-gossipsub/master)](https://travis-ci.com/ChainSafe/js-libp2p-gossipsub) | [![codecov](https://codecov.io/gh/ChainSafe/js-libp2p-gossipsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ChainSafe/js-libp2p-gossipsub) | [Cayman Nava](mailto:caymannava@gmail.com) |
| **extensions** |
| [`libp2p-nat-mgnr`](//github.com/libp2p/js-libp2p-nat-mgnr) | [![npm](https://img.shields.io/npm/v/libp2p-nat-mgnr.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-nat-mgnr/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-nat-mgnr.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-nat-mgnr) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-nat-mgnr/master)](https://travis-ci.com/libp2p/js-libp2p-nat-mgnr) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-nat-mgnr/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-nat-mgnr) | N/A |
| [`libp2p-utils`](//github.com/libp2p/js-libp2p-utils) | [![npm](https://img.shields.io/npm/v/libp2p-utils.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-utils/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-utils.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-utils) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-utils/master)](https://travis-ci.com/libp2p/js-libp2p-utils) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-utils/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-utils) | [Vasco Santos](mailto:santos.vasco10@gmail.com) |

View File

@ -26,6 +26,8 @@
- Documentation
- [ ] Ensure that README.md is up to date
- [ ] Ensure that all the examples run
- [ ] Ensure [libp2p/js-libp2p-examples](https://github.com/libp2p/js-libp2p-examples) is updated
- [ ] Ensure that [libp2p/docs](https://github.com/libp2p/docs) is updated
- Communication
- [ ] Create the release issue
- [ ] Take a snapshot between of everyone that has contributed to this release (including its subdeps in IPFS, libp2p, IPLD and multiformats) using [`name-your-contributors`](https://www.npmjs.com/package/name-your-contributors). Generate a nice markdown list with [this script](https://gist.github.com/jacobheun/d2ff479ca991733c13cdcf688a1317e5)

1451
doc/API.md

File diff suppressed because it is too large Load Diff

View File

@ -20,10 +20,17 @@
- [Customizing DHT](#customizing-dht)
- [Setup with Content and Peer Routing](#setup-with-content-and-peer-routing)
- [Setup with Relay](#setup-with-relay)
- [Setup with Auto Relay](#setup-with-auto-relay)
- [Setup with Keychain](#setup-with-keychain)
- [Configuring Dialing](#configuring-dialing)
- [Configuring Connection Manager](#configuring-connection-manager)
- [Configuring Transport Manager](#configuring-transport-manager)
- [Configuring Metrics](#configuring-metrics)
- [Configuring PeerStore](#configuring-peerstore)
- [Customizing Transports](#customizing-transports)
- [Configuring the NAT Manager](#configuring-the-nat-manager)
- [Browser support](#browser-support)
- [UPnP and NAT-PMP](#upnp-and-nat-pmp)
- [Configuration examples](#configuration-examples)
## Overview
@ -49,7 +56,7 @@ The libp2p ecosystem contains at least one module for each of these subsystems.
After selecting the modules to use, it is also possible to configure each one according to your needs.
Bear in mind that only a **transport** and **connection encryption** are required, while all the other subsystems are optional.
Bear in mind that a **transport** and **connection encryption** module are **required**, while all the other subsystems are optional.
### Transport
@ -94,7 +101,8 @@ If you want to know more about libp2p stream multiplexing, you should read the f
Some available connection encryption protocols:
- [libp2p/js-libp2p-secio](https://github.com/libp2p/js-libp2p-secio)
- [NodeFactoryIo/js-libp2p-noise](https://github.com/NodeFactoryIo/js-libp2p-noise)
- [libp2p/js-libp2p-secio](https://github.com/libp2p/js-libp2p-secio) ⚠️ [DEPRECATED](https://blog.ipfs.io/2020-08-07-deprecating-secio)
If none of the available connection encryption mechanisms fulfills your needs, you can create a libp2p compatible one. A libp2p connection encryption protocol just needs to be compliant with the [Crypto Interface](https://github.com/libp2p/js-interfaces/tree/master/src/crypto).
@ -173,7 +181,7 @@ If you want to know more about libp2p DHT, you should read the following content
Some available pubsub routers are:
- [libp2p/js-libp2p-floodsub](https://github.com/libp2p/js-libp2p-floodsub)
- [ChainSafe/gossipsub-js](https://github.com/ChainSafe/gossipsub-js)
- [ChainSafe/js-libp2p-gossipsub](https://github.com/ChainSafe/js-libp2p-gossipsub)
If none of the available pubsub routers fulfills your needs, you can create a libp2p compatible one. A libp2p pubsub router just needs to be created on top of [libp2p/js-libp2p-pubsub](https://github.com/libp2p/js-libp2p-pubsub), which ensures `js-libp2p` API expectations.
@ -204,8 +212,12 @@ Moreover, the majority of the modules can be customized via option parameters. T
Besides the `modules` and `config`, libp2p allows other internal options and configurations:
- `datastore`: an instance of [ipfs/interface-datastore](https://github.com/ipfs/interface-datastore/) modules.
- This is used in modules such as the DHT. If it is not provided, `js-libp2p` will use an in memory datastore.
- `peerInfo`: a previously created instance of [libp2p/js-peer-info](https://github.com/libp2p/js-peer-info).
- `peerId`: the identity of the node, an instance of [libp2p/js-peer-id](https://github.com/libp2p/js-peer-id).
- This is particularly useful if you want to reuse the same `peer-id`, as well as for modules like `libp2p-delegated-content-routing`, which need a `peer-id` in their instantiation.
- `addresses`: an object containing `listen`, `announce` and `announceFilter`:
- `listen` addresses will be provided to the libp2p underlying transports for listening on them.
- `announce` addresses will be used to compute the advertises that the node should advertise to the network.
- `announceFilter`: filter function used to filter announced addresses programmatically: `(ma: Array<multiaddr>) => Array<multiaddr>`. Default: returns all addresses. [`libp2p-utils`](https://github.com/libp2p/js-libp2p-utils) provides useful [multiaddr utilities](https://github.com/libp2p/js-libp2p-utils/blob/master/API.md#multiaddr-isloopbackma) to create your filters.
### Examples
@ -215,7 +227,7 @@ Besides the `modules` and `config`, libp2p allows other internal options and con
// Creating a libp2p node with:
// transport: websockets + tcp
// stream-muxing: mplex
// crypto-channel: secio
// crypto-channel: noise
// discovery: multicast-dns
// dht: kad-dht
// pubsub: gossipsub
@ -224,7 +236,7 @@ const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const MulticastDNS = require('libp2p-mdns')
const DHT = require('libp2p-kad-dht')
const GossipSub = require('libp2p-gossipsub')
@ -236,7 +248,7 @@ const node = await Libp2p.create({
new WS() // It can take instances too!
],
streamMuxer: [MPLEX],
connEncryption: [SECIO],
connEncryption: [NOISE],
peerDiscovery: [MulticastDNS],
dht: DHT,
pubsub: GossipSub
@ -250,24 +262,34 @@ const node = await Libp2p.create({
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const MulticastDNS = require('libp2p-mdns')
const Bootstrap = require('libp2p-bootstrap')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO],
peerDiscovery: [MulticastDNS]
connEncryption: [NOISE],
peerDiscovery: [MulticastDNS, Bootstrap]
},
config: {
peerDiscovery: {
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minPeers)
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minConnections)
// The `tag` property will be searched when creating the instance of your Peer Discovery service.
// The associated object, will be passed to the service when it is instantiated.
[MulticastDNS.tag]: {
interval: 1000,
enabled: true
},
[Bootstrap.tag:] {
list: [ // A list of bootstrap peers to connect to starting up the node
"/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
"/dnsaddr/bootstrap.libp2p.io/ipfs/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"/dnsaddr/bootstrap.libp2p.io/ipfs/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
],
interval: 2000,
enabled: true
}
// .. other discovery module options.
}
@ -283,7 +305,7 @@ const Libp2p = require('libp2p')
const WS = require('libp2p-websockets')
const WebRTCStar = require('libp2p-webrtc-star')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
@ -292,11 +314,11 @@ const node = await Libp2p.create({
WebRTCStar
],
streamMuxer: [MPLEX],
connEncryption: [SECIO],
connEncryption: [NOISE],
},
config: {
peerDiscovery: {
webRTCStar: {
[WebRTCStar.tag]: {
enabled: true
}
}
@ -310,22 +332,23 @@ const node = await Libp2p.create({
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const GossipSub = require('libp2p-gossipsub')
const { SignaturePolicy } = require('libp2p-interfaces/src/pubsub/signature-policy')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO],
connEncryption: [NOISE],
pubsub: GossipSub
},
config: {
pubsub: { // The pubsub options (and defaults) can be found in the pubsub router documentation
enabled: true,
emitSelf: true, // whether the node should emit to self on publish
signMessages: true, // if messages should be signed
strictSigning: true // if message signing should be required
emitSelf: false, // whether the node should emit to self on publish
globalSignaturePolicy: SignaturePolicy.StrictSign // message signing policy
}
}
})
@ -337,14 +360,14 @@ const node = await Libp2p.create({
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const DHT = require('libp2p-kad-dht')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO],
connEncryption: [NOISE],
dht: DHT
},
config: {
@ -367,27 +390,34 @@ const node = await Libp2p.create({
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
const DelegatedContentRouter = require('libp2p-delegated-content-routing')
const PeerInfo = require('peer-info')
const PeerId = require('peer-id')
// create a peerInfo
const peerInfo = await PeerInfo.create()
// create a peerId
const peerId = await PeerId.create()
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO],
connEncryption: [NOISE],
contentRouting: [
new DelegatedContentRouter(peerInfo.id)
new DelegatedContentRouter(peerId)
],
peerRouting: [
new DelegatedPeerRouter()
],
},
peerInfo
peerId,
peerRouting: { // Peer routing configuration
refreshManager: { // Refresh known and connected closest peers
enabled: true, // Should find the closest peers.
interval: 6e5, // Interval for getting the new for closest peers of 10min
bootDelay: 10e3 // Delay for the initial query for closest peers
}
}
})
```
@ -397,13 +427,13 @@ const node = await Libp2p.create({
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO]
connEncryption: [NOISE]
},
config: {
relay: { // Circuit Relay options (this config is part of libp2p core configurations)
@ -411,32 +441,111 @@ const node = await Libp2p.create({
hop: {
enabled: true, // Allows you to be a relay for other peers
active: true // You will attempt to dial destination peers if you are not connected to them
},
advertise: {
bootDelay: 15 * 60 * 1000, // Delay before HOP relay service is advertised on the network
enabled: true, // Allows you to disable the advertise of the Hop service
ttl: 30 * 60 * 1000 // Delay Between HOP relay service advertisements on the network
}
}
}
})
```
#### Configuring Dialing
Dialing in libp2p can be configured to limit the rate of dialing, and how long dials are allowed to take. The below configuration example shows the default values for the dialer.
#### Setup with Auto Relay
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO]
connEncryption: [NOISE]
},
config: {
relay: { // Circuit Relay options (this config is part of libp2p core configurations)
enabled: true, // Allows you to dial and accept relayed connections. Does not make you a relay.
autoRelay: {
enabled: true, // Allows you to bind to relays with HOP enabled for improving node dialability
maxListeners: 2 // Configure maximum number of HOP relays to use
}
}
}
})
```
#### Setup with Keychain
Libp2p allows you to setup a secure keychain to manage your keys. The keychain configuration object should have the following properties:
| Name | Type | Description |
|------|------|-------------|
| pass | `string` | Passphrase to use in the keychain (minimum of 20 characters). |
| datastore | `object` | must implement [ipfs/interface-datastore](https://github.com/ipfs/interface-datastore) |
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const LevelStore = require('datastore-level')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
keychain: {
pass: 'notsafepassword123456789',
datastore: new LevelStore('path/to/store')
}
})
await libp2p.loadKeychain()
```
#### Configuring Dialing
Dialing in libp2p can be configured to limit the rate of dialing, and how long dials are allowed to take. The dialer configuration object should have the following properties:
| Name | Type | Description |
|------|------|-------------|
| maxParallelDials | `number` | How many multiaddrs we can dial in parallel. |
| maxDialsPerPeer | `number` | How many multiaddrs we can dial per peer, in parallel. |
| dialTimeout | `number` | Second dial timeout per peer in ms. |
| resolvers | `object` | Dial [Resolvers](https://github.com/multiformats/js-multiaddr/blob/master/src/resolvers/index.js) for resolving multiaddrs |
| addressSorter | `(Array<Address>) => Array<Address>` | Sort the known addresses of a peer before trying to dial. |
The below configuration example shows how the dialer should be configured, with the current defaults:
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const { dnsaddrResolver } = require('multiaddr/src/resolvers')
const { publicAddressesFirst } = require('libp2p-utils/src/address-sort')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
dialer: {
maxParallelDials: 100, // How many multiaddrs we can dial in parallel
maxDialsPerPeer: 4, // How many multiaddrs we can dial per peer, in parallel
dialTimeout: 30e3 // 30 second dial timeout per peer
maxParallelDials: 100,
maxDialsPerPeer: 4,
dialTimeout: 30e3,
resolvers: {
dnsaddr: dnsaddrResolver
},
addressSorter: publicAddressesFirst
}
```
@ -448,13 +557,13 @@ The Connection Manager prunes Connections in libp2p whenever certain limits are
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO]
connEncryption: [NOISE]
},
connectionManager: {
maxConnections: Infinity,
@ -471,32 +580,101 @@ const node = await Libp2p.create({
})
```
#### Configuring Metrics
#### Configuring Transport Manager
Metrics are disabled in libp2p by default. You can enable and configure them as follows. Aside from enabled being `false` by default, the configuration options listed here are the current defaults.
The Transport Manager is responsible for managing the libp2p transports life cycle. This includes starting listeners for the provided listen addresses, closing these listeners and dialing using the provided transports. By default, if a libp2p node has a list of multiaddrs for listenning on and there are no valid transports for those multiaddrs, libp2p will throw an error on startup and shutdown. However, for some applications it is perfectly acceptable for libp2p nodes to start in dial only mode if all the listen multiaddrs failed. This error tolerance can be enabled as follows:
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const { FaultTolerance } = require('libp2p/src/transport-manager')}
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO]
connEncryption: [NOISE]
},
transportManager: {
faultTolerance: FaultTolerance.NO_FATAL
}
})
```
#### Configuring Metrics
Metrics are disabled in libp2p by default. You can enable and configure them as follows:
| Name | Type | Description |
|------|------|-------------|
| enabled | `boolean` | Enabled metrics collection. |
| computeThrottleMaxQueueSize | `number` | How many messages a stat will queue before processing. |
| computeThrottleTimeout | `number` | Time in milliseconds a stat will wait, after the last item was added, before processing. |
| movingAverageIntervals | `Array<number>` | The moving averages that will be computed. |
| maxOldPeersRetention | `number` | How many disconnected peers we will retain stats for. |
The below configuration example shows how the metrics should be configured. Aside from enabled being `false` by default, the following default configuration options are listed below:
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
metrics: {
enabled: true,
computeThrottleMaxQueueSize: 1000, // How many messages a stat will queue before processing
computeThrottleTimeout: 2000, // Time in milliseconds a stat will wait, after the last item was added, before processing
movingAverageIntervals: [ // The moving averages that will be computed
computeThrottleMaxQueueSize: 1000,
computeThrottleTimeout: 2000,
movingAverageIntervals: [
60 * 1000, // 1 minute
5 * 60 * 1000, // 5 minutes
15 * 60 * 1000 // 15 minutes
],
maxOldPeersRetention: 50 // How many disconnected peers we will retain stats for
maxOldPeersRetention: 50
}
})
```
#### Configuring PeerStore
PeerStore persistence is disabled in libp2p by default. You can enable and configure it as follows. Aside from enabled being `false` by default, it will need an implementation of a [datastore](https://github.com/ipfs/interface-datastore). Take into consideration that using the memory datastore will be ineffective for persistence.
The threshold number represents the maximum number of "dirty peers" allowed in the PeerStore, i.e. peers that are not updated in the datastore. In this context, browser nodes should use a threshold of 1, since they might not "stop" properly in several scenarios and the PeerStore might end up with unflushed records when the window is closed.
| Name | Type | Description |
|------|------|-------------|
| persistence | `boolean` | Is persistence enabled. |
| threshold | `number` | Number of dirty peers allowed. |
The below configuration example shows how the PeerStore should be configured. Aside from persistence being `false` by default, the following default configuration options are listed below:
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const LevelStore = require('datastore-level')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
datastore: new LevelStore('path/to/store'),
peerStore: {
persistence: true,
threshold: 5
}
})
```
@ -509,7 +687,7 @@ Some Transports can be passed additional options when they are created. For exam
const Libp2p = require('libp2p')
const WebRTCStar = require('libp2p-webrtc-star')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const wrtc = require('wrtc')
const transportKey = WebRTCStar.prototype[Symbol.toStringTag]
@ -517,7 +695,7 @@ const node = await Libp2p.create({
modules: {
transport: [WebRTCStar],
streamMuxer: [MPLEX],
connEncryption: [SECIO]
connEncryption: [NOISE]
},
config: {
transport: {
@ -529,13 +707,75 @@ const node = await Libp2p.create({
})
```
During Libp2p startup, transport listeners will be created for the configured listen multiaddrs. Some transports support custom listener options and you can set them using the `listenerOptions` in the transport configuration. For example, [libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star) transport listener supports the configuration of its underlying [simple-peer](https://github.com/feross/simple-peer) ice server(STUN/TURN) config as follows:
```js
const transportKey = WebRTCStar.prototype[Symbol.toStringTag]
const node = await Libp2p.create({
modules: {
transport: [WebRTCStar],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
addresses: {
listen: ['/dns4/your-wrtc-star.pub/tcp/443/wss/p2p-webrtc-star'] // your webrtc dns multiaddr
},
config: {
transport: {
[transportKey]: {
listenerOptions: {
config: {
iceServers: [
{"urls": ["turn:YOUR.TURN.SERVER:3478"], "username": "YOUR.USER", "credential": "YOUR.PASSWORD"},
{"urls": ["stun:YOUR.STUN.SERVER:3478"], "username": "", "credential": ""}]
}
}
}
}
}
})
```
#### Configuring the NAT Manager
Network Address Translation (NAT) is a function performed by your router to enable multiple devices on your local network to share a single IPv4 address. It's done transparently for outgoing connections, ensuring the correct response traffic is routed to your computer, but if you wish to accept incoming connections some configuration is necessary.
The NAT manager can be configured as follows:
```js
const node = await Libp2p.create({
config: {
nat: {
description: 'my-node', // set as the port mapping description on the router, defaults the current libp2p version and your peer id
enabled: true, // defaults to true
gateway: '192.168.1.1', // leave unset to auto-discover
externalIp: '80.1.1.1', // leave unset to auto-discover
ttl: 7200, // TTL for port mappings (min 20 minutes)
keepAlive: true, // Refresh port mapping after TTL expires
pmp: {
enabled: false, // defaults to false
}
}
}
})
```
##### Browser support
Browsers cannot open TCP ports or send the UDP datagrams necessary to configure external port mapping - to accept incoming connections in the browser please use a WebRTC transport.
##### UPnP and NAT-PMP
By default under nodejs libp2p will attempt to use [UPnP](https://en.wikipedia.org/wiki/Universal_Plug_and_Play) to configure your router to allow incoming connections to any TCP transports that have been configured.
[NAT-PMP](http://miniupnp.free.fr/nat-pmp.html) is a feature of some modern routers which performs a similar job to UPnP. NAT-PMP is disabled by default, if enabled libp2p will try to use NAT-PMP and will fall back to UPnP if it fails.
## Configuration examples
As libp2p is designed to be a modular networking library, its usage will vary based on individual project needs. We've included links to some existing project configurations for your reference, in case you wish to replicate their configuration:
- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-nodejs.js) - libp2p configuration used by js-ipfs when running in Node.js
- [libp2p-ipfs-browser](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-browser.js) - libp2p configuration used by js-ipfs when running in a Browser (that supports WebRTC)
- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-ipfs/blob/master/packages/ipfs/src/core/runtime/libp2p-nodejs.js) - libp2p configuration used by js-ipfs when running in Node.js
- [libp2p-ipfs-browser](https://github.com/ipfs/js-ipfs/blob/master/packages/ipfs/src/core/runtime/libp2p-browser.js) - libp2p configuration used by js-ipfs when running in a Browser (that supports WebRTC)
If you have developed a project using `js-libp2p`, please consider submitting your configuration to this list so that it can be found easily by other users.

View File

@ -69,23 +69,23 @@ If you want to know more about libp2p transports, you should read the following
Encryption is an important part of communicating on the libp2p network. Every connection must be encrypted to help ensure security for everyone. As such, Connection Encryption (Crypto) is a required component of libp2p.
There are a growing number of Crypto modules being developed for libp2p. As those are released they will be tracked in the [Connection Encryption section of the configuration readme](./CONFIGURATION.md#connection-encryption). For now, we are going to configure our node to use the `libp2p-secio` module, which is widely supported across the various libp2p implementations.
There are a growing number of Crypto modules being developed for libp2p. As those are released they will be tracked in the [Connection Encryption section of the configuration readme](./CONFIGURATION.md#connection-encryption). For now, we are going to configure our node to use the `libp2p-noise` module.
```sh
npm install libp2p-secio
npm install libp2p-noise
```
With `libp2p-secio` installed, we can add it to our existing configuration by importing it and adding it to the `modules.connEncryption` array:
With `libp2p-noise` installed, we can add it to our existing configuration by importing it and adding it to the `modules.connEncryption` array:
```js
const Libp2p = require('libp2p')
const WebSockets = require('libp2p-websockets')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
transport: [WebSockets],
connEncryption: [SECIO]
connEncryption: [NOISE]
}
})
```
@ -112,13 +112,13 @@ npm install libp2p-mplex
```js
const Libp2p = require('libp2p')
const WebSockets = require('libp2p-websockets')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const node = await Libp2p.create({
modules: {
transport: [WebSockets],
connEncryption: [SECIO],
connEncryption: [NOISE],
streamMuxer: [MPLEX]
}
})
@ -139,13 +139,16 @@ Now that you have configured a [**Transport**][transport], [**Crypto**][crypto]
```js
const Libp2p = require('libp2p')
const WebSockets = require('libp2p-websockets')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/127.0.0.1/tcp/8000/ws']
},
modules: {
transport: [WebSockets],
connEncryption: [SECIO],
connEncryption: [NOISE],
streamMuxer: [MPLEX]
}
})
@ -154,6 +157,12 @@ const node = await Libp2p.create({
await node.start()
console.log('libp2p has started')
const listenAddrs = node.transportManager.getAddrs()
console.log('libp2p is listening on the following addresses: ', listenAddrs)
const advertiseAddrs = node.multiaddrs
console.log('libp2p is advertising the following addresses: ', advertiseAddrs)
// stop libp2p
await node.stop()
console.log('libp2p has stopped')
@ -188,27 +197,27 @@ We can provide specific configurations for each protocol within a `config.peerDi
```js
const Libp2p = require('libp2p')
const WebSockets = require('libp2p-websockets')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const Bootstrap = require('libp2p-bootstrap')
// Known peers addresses
const bootstrapMultiaddrs = [
'/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3'
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN'
]
const node = await Libp2p.create({
modules: {
transport: [WebSockets],
connEncryption: [SECIO],
connEncryption: [NOISE],
streamMuxer: [MPLEX],
peerDiscovery: [Bootstrap]
},
config: {
peerDiscovery: {
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minPeers)
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minConnections)
// The `tag` property will be searched when creating the instance of your Peer Discovery service.
// The associated object, will be passed to the service when it is instantiated.
[Bootstrap.tag]: {
@ -223,9 +232,9 @@ node.on('peer:discovery', (peer) => {
console.log('Discovered %s', peer.id.toB58String()) // Log discovered peer
})
node.on('peer:connect', (peer) => {
console.log('Connected to %s', peer.id.toB58String()) // Log connected peer
})
node.connectionManager.on('peer:connect', (connection) => {
console.log('Connected to %s', connection.remotePeer.toB58String()) // Log connected peer
})
// start libp2p
await node.start()

View File

@ -161,4 +161,4 @@ const duplex = {
[it-pipe]: https://github.com/alanshaw/it-pipe
[it-pushable]: https://github.com/alanshaw/it-pushable
[it-reader]: https://github.com/alanshaw/it-reader
[streaming-iterables]: https://github.com/bustle/streaming-iterables
[streaming-iterables]: https://github.com/reconbot/streaming-iterables

View File

@ -1,4 +1,4 @@
# Migrating to the new API
# Migrating to the libp2p@0.27 API
A migration guide for refactoring your application code from libp2p v0.26.x to v0.27.0.

View File

@ -0,0 +1,343 @@
# Migrating to the libp2p@0.28 API
A migration guide for refactoring your application code from libp2p v0.27.x to v0.28.0.
## Table of Contents
- [PeerStore API](#peerstore-api)
- [Migrating from Peer Info](#migrating-from-peer-info)
- [Create](#create)
- [API Implications](#api-implications)
- [Connection Manager and Registrar](#connection-manager-and-registrar)
- [Events](#events)
- [Module Updates](#module-updates)
## PeerStore API
In `libp2p@0.27` we integrated the PeerStore (former [peer-book](https://github.com/libp2p/js-peer-book)) into the codebase. By that time, it was not documented in the [API DOC](../API.md) since it kept the same API as the `peer-book` and it was expected to be completelly rewritten in `libp2p@0.28`.
Moving towards a separation of concerns regarding known peers' data, as well as enabling PeerStore persistence, the PeerStore is now divided into four main components: `AddressBook`, `ProtoBook`, `KeyBook` and `MetadataBook`. This resulted in API changes in the PeerStore, since each type of peer data should now be added in an atomic fashion.
### Adding a Peer
**Before**
```js
const peerId = ...
const peerInfo = new PeerInfo(peerId)
peerInfo.protocols.add('/ping/1.0.0')
peerInfo.protocols.add('/ping/2.0.0')
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/0')
libp2p.peerStore.put(peerInfo)
```
**After**
```js
const peerId = ...
const protocols = ['/ping/1.0.0', 'ping/2.0.0']
const multiaddrs = ['/ip4/127.0.0.1/tcp/0']
libp2p.peerStore.protoBook.add(peerId, protocols)
libp2p.peerStore.addressBook.add(peerId, multiaddrs)
```
### Getting a Peer
**Before**
```js
const peerId = ...
const peerInfo = libp2p.peerStore.get(peerId)
// { id: PeerId, multiaddrs: MultiaddrSet, protocols: Set<string>}
```
**After**
```js
const peerId = ...
const peer = libp2p.peerStore.get(peerId)
// { id: PeerId, addresses: Array<{ multiaddr: Multiaddr }>, protocols: Array<string> }
```
### Checking for a Peer
**Before**
```js
const peerId = ...
const hasData = libp2p.peerStore.has(peerId)
```
**After**
```js
const peerId = ...
const hasData = Boolean(libp2p.peerStore.get(peerId))
```
### Removing a Peer
**Before**
```js
libp2p.peerStore.remove(peerId)
```
**After**
```js
// Atomic
libp2p.peerStore.protoBook.delete(peerId)
libp2p.peerStore.addressBook.delete(peerId)
// Remove the peer and ALL of its associated data
libp2p.peerStore.delete(peerId)
```
### Get all known Peers
**Before**
```js
const peers = libp2p.peerStore.peers
// Map<string, PeerInfo>
```
**After**
```js
const peers = libp2p.peerStore.peers
// Similar to libp2p.peerStore.get()
// Map<string, { id: PeerId, addresses: Array<{ multiaddr: Multiaddr }>, protocols: Array<string> }
```
## Migrating from Peer Info
[`PeerInfo`][peer-info] is a libp2p peer abstraction layer that combines a [`PeerId`][peer-id] with known data of the peer, namely its multiaddrs and protocols. It has been used for a long time by `js-libp2p` and its modules to carry this data around the libp2p stack, as well as by the libp2p API, both for providing this data to the users or to receive it from them.
Since this PeerInfo instances were navigating through the entire codebases, some data inconsistencies could be observed in libp2p. Different libp2p subsystems were running with different visions of the known peers data. For instance, a libp2p subsystem receives a copy of this instance with the peer multiaddrs and protocols, but if new data of the peer is obtained from other subsystem, it would not be updated on the former. Moreover, considering that several subsystems were modifying the peer data, libp2p had no way to determine the accurate data.
Considering the complete revamp of the libp2p PeerStore towards its second version, the PeerStore now acts as the single source of truth, we do not need to carry [`PeerInfo`][peer-info] instances around. This also solves all the problems stated above, since subsystems will report new observations to the PeerStore.
### Create
While it was possible to create a libp2p node without providing a [`PeerInfo`][peer-info], there were 2 use cases where a [`PeerInfo`][peer-info] was provided when creating a libp2p node.
#### Using an existing PeerId
`libp2p.create` receives a `peerId` property instead of a `peerInfo` property.
**Before**
```js
const peerId = ...
const peerInfo = new PeerInfo(peerId)
const libp2p = await Libp2p.create({
peerInfo
// ...
})
```
**After**
```js
const peerId = ...
const libp2p = await Libp2p.create({
peerId
// ...
})
```
#### Providing listen addresses
**Before**
```js
const peerId = ...
const peerInfo = new PeerInfo(peerId)
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/0')
const libp2p = await Libp2p.create({
peerInfo
// ...
})
await libp2p.start()
```
**After**
```js
const peerId = ...
const libp2p = await Libp2p.create({
peerId,
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0']
}
// ...
})
await libp2p.start()
```
There is also a bonus regarding the peer addresses. `libp2p@0.28` comes with an AddressManager that also allows the configuration of `announce` and `noAnnounce` addresses.
This was possible to achieve before, but in a hacky way by removing or adding addresses to the `peerInfo`, after the node starts.
**Before**
```js
const peerId = ...
const peerInfo = new PeerInfo(peerId)
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/8000')
const libp2p = await Libp2p.create({
peerInfo
// ...
})
await libp2p.start()
peerInfo.multiaddrs.add('/dns4/peer.io') // Announce
peerInfo.multiaddrs.delete('/ip4/127.0.0.1/tcp/8000') // Not announce
```
**After**
```js
const peerId = ...
const libp2p = await Libp2p.create({
peerId,
addresses: {
listen: ['/ip4/127.0.0.1/tcp/8000'],
announce: ['/dns4/peer.io'],
noAnnounce: ['/ip4/127.0.0.1/tcp/8000']
}
// ...
})
await libp2p.start()
```
### API Implications
#### Peer Dialing, Hangup and Ping
`libp2p.dial`, `libp2p.dialProtocol`, `libp2p.hangup` and `libp2p.ping` supported as the target parameter a [`PeerInfo`](peer-info), a [`PeerId`](peer-id), a [`Multiaddr`][multiaddr] and a string representation of the multiaddr. Considering that [`PeerInfo`](peer-info) is being removed from libp2p, all these methods will now support the other 3 possibilities.
There is one relevant aspect to consider with this change. When using a [`PeerId`](peer-id), the PeerStore **MUST** have known addresses for that peer in its AddressBook, so that it can perform the request. This was also true in the past, but it is important pointing it out because it might not be enough to switch from using [`PeerInfo`](peer-info) to [`PeerId`](peer-id). When using a [`PeerInfo`](peer-info), the PeerStore was not required to have the multiaddrs when they existed on the PeerInfo instance.
**Before**
```js
const peerInfo = ... // PeerInfo containing its multiaddrs
const connection = await libp2p.dial(peerInfo)
```
**After**
```js
const peerId = ...
// Known multiaddrs should be added to the PeerStore
libp2p.peerStore.addressBook.add(peerId, multiaddrs)
const connection = await libp2p.dial(peerId)
```
#### Content Routing and Peer Routing
Both [content-routing](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/content-routing) and [peer-routing](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/peer-routing) interfaces were modified to not return a ['PeerInfo'][peer-info] instance.
**Before**
```js
for await (const peerInfo of libp2p.contentRouting.findProviders(cid)) {
// peerInfo is a PeerInfo instance
}
```
**After**
```js
for await (const peer of libp2p.contentRouting.findProviders(cid)) {
// { id: PeerId, multiaddrs: Multiaddr[] }
}
```
**Before**
```js
const peerInfo = await libp2p.peerRouting.findPeer(peerId)
// peerInfo is a PeerInfo instance
```
**After**
```js
const peer = await libp2p.peerRouting.findPeer(peerId)
// { id: PeerId, multiaddrs: Multiaddr[] }
```
## Connection Manager and Registrar
Registrar was introduced in `libp2p@0.27` along with [libp2p topologies](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/topology). `Registrar` and `ConnectionManager` were both listening on new connections and keeping their record of the open connections with other peers.
The registrar API was not documented in the [API DOC](../API.md). However, it exposed a useful method for some libp2p users, `libp2p.registrar.getConnection()`. On the other hand, the connection Manager did not provide any methods to access its stored connections. On `libp2p@0.28` we removed this data duplication and the connections are handled solely by the `ConnectionManager`.
**Before**
```js
const connection = libp2p.registrar.getConnection(peerId)
```
**After**
```js
const connection = libp2p.connectionManager.get(peerId)
```
## Events
### Connection Events
Libp2p emits events whenever new connections are established. These emitted events previously providing the [`PeerInfo`](peer-info) of the peer that connected. In `libp2p@0.28` these events are now emitted from the Connection Manager and will now emit the [`connection`](connection) itself.
**Before**
```js
libp2p.on('peer:connect', (peerInfo) => {
// PeerInfo instance
})
libp2p.on('peer:disconnect', (peerInfo) => {
// PeerInfo instance
})
```
**After**
```js
libp2p.connectionManager.on('peer:connect', (connection) => {
// Connection instance
})
libp2p.connectionManager.on('peer:disconnect', (connection) => {
// Connection instance
})
```
### Peer Discovery
**Before**
```js
libp2p.on('peer:discovery', (peerInfo) => {
// PeerInfo instance
})
```
**After**
```js
libp2p.on('peer:discovery', (peerId) => {
// peerId instance
})
```
## Module Updates
With `libp2p@0.28` you should update the following libp2p modules if you are relying on them:
```json
"libp2p-bootstrap": "^0.11.0",
"libp2p-delegated-content-routing": "^0.5.0",
"libp2p-delegated-peer-routing": "^0.5.0",
"libp2p-floodsub": "^0.21.0",
"libp2p-gossipsub": "^0.4.0",
"libp2p-kad-dht": "^0.19.1",
"libp2p-mdns": "^0.14.1",
"libp2p-webrtc-star": "^0.18.0"
```
[connection]: https://github.com/libp2p/js-interfaces/tree/master/src/connection
[multiaddr]: https://github.com/multiformats/js-multiaddr
[peer-id]: https://github.com/libp2p/js-peer-id
[peer-info]: https://github.com/libp2p/js-peer-info

View File

@ -0,0 +1,312 @@
<!--Specify versions for migration below-->
# Migrating to libp2p@0.29
A migration guide for refactoring your application code from libp2p v0.28.x to v0.29.0.
## Table of Contents
- [API](#api)
- [Pubsub](#pubsub)
- [Uint8Arrays replace node Buffers](#uint8arrays-replace-node-buffers)
- [Module Updates](#module-updates)
## API
### Pubsub
The [`libp2p-gossipsub`](https://github.com/ChainSafe/js-libp2p-gossipsub) javascript implementation is now upgraded according to the Gossipsub v1.1 [spec](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md) and it packs several security hardening extensions. You can read more about it in its [blogpost](https://blog.ipfs.io/2020-05-20-gossipsub-v1.1/).
We leveraged this update to rethink the pubsub interface, in order to make it easier, as well as to be consistent with the API of the routers. Moreover, the interface was also reconstructed to ease new pubsub router implementations.
#### Access router instance
Libp2p prior to 0.29 unnecessarily added a layer of abstraction over the pubsub routers. We now expose the pubsub router API directly and have a test suite in the [interface-pubsub](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/pubsub) to guarantee routers compliance. This enables more advanced usage of the underlying router.
**Before**
```js
libp2p.pubsub._pubsub.*
libp2p.pubsub._pubsub.topicValidators.set(topic, validator)
```
**After**
```js
libp2p.pubsub.*
libp2p.pubsub.topicValidators.set(topic, validator)
```
#### Publish
Publish uses `Uint8Array` data instead of `Buffer`.
**Before**
```js
const topic = 'topic'
const data = Buffer.from('data')
await libp2p.pubsub.publish(topic, data)
```
**After**
```js
const uint8ArrayFromString = require('uint8arrays/from-string')
const topic = 'topic'
const data = uint8ArrayFromString('data')
await libp2p.pubsub.publish(topic, data)
```
#### Subscribe
Handlers should no longer be passed when subscribing, instead, applications should bind event handlers for each topic they wish to subscribe too. This enables more flexibility at the application level without changing the underlying subscriptions.
Message data is now a `Uint8Array` instead of `Buffer`.
**Before**
```js
const topic = 'topic'
const handler = (msg) => {
// msg.data - pubsub data received
const data = msg.data.toString()
}
libp2p.pubsub.subscribe(topic, handler)
```
**After**
```js
const uint8ArrayToString = require('uint8arrays/to-string')
const topic = 'topic'
const handler = (msg) => {
// msg.data - pubsub data received
const data = uint8ArrayToString(msg.data)
}
libp2p.pubsub.on(topic, handler)
libp2p.pubsub.subscribe(topic)
```
In the latest release, despite not being documented in `libp2p` the underlying pubsub routers supported subscribing to multiple topics at the same time. We removed that code complexity, since this is easily achieved in the application layer if needed.
**Before**
```js
const topics = ['a', 'b']
const handler = (msg) => {
// msg.data - pubsub data received
const data = msg.data.toString()
}
libp2p.pubsub.subscribe(topics, handler)
```
**After**
```js
const uint8ArrayToString = require('uint8arrays/to-string')
const topics = ['a', 'b']
const handler = (msg) => {
// msg.data - pubsub data received
const data = uint8ArrayToString(msg.data)
}
topics.forEach((topic) => {
libp2p.pubsub.on(topic, handler)
libp2p.pubsub.subscribe(topic)
})
```
#### Unsubscribe
Handlers should not be directly bound to the subscription anymore.
**Before**
```js
const topic = 'topic'
const handler = (msg) => {
// msg.data - pubsub data received
}
libp2p.pubsub.unsubscribe(topic, handler)
```
**After**
```js
const topic = 'topic'
const handler = (msg) => {
// msg.data - pubsub data received
}
libp2p.pubsub.removeListener(topic, handler)
libp2p.pubsub.unsubscribe(topic)
```
#### Topic Validators
The validator function does not include the peer parameter anymore. It was redundant since it is included in the message and it could lead to issues as the peer that sent the message might not be the one who created the message in first place. The validator function should also throw an error instead of returning `false` when the message is not valid.
**Before**
```js
const validator = (msgTopic, peer, msg) => {
// process message
return false
}
libp2p.pubsub._pubsub.topicValidators.set(topic, validator)
```
**After**
```js
const validator = (msgTopic, msg) => {
const from = msg.from
// process message
throw new Error('not a valid message')
}
libp2p.pubsub.topicValidators.set(topic, validator)
```
### Uint8Arrays replace node Buffers
Aiming to improve libp2p browser support, we are moving away from node core modules unless we can guarantee that the code we are writing will not run in a browser. It is worth mentioning that modern JavaScript runtimes have TypedArrays such as Uint8Array backed by ArrayBuffers. All libp2p dependencies were also updated to use Uint8Array.
We use the [uint8arrays](https://www.npmjs.com/package/uint8arrays) utilities module to deal with `Uint8Arrays` easily and we recommend its usage in the application layer. Thanks for the module [@achingbrain](https://github.com/achingbrain)! It includes utilities like `compare`, `concat`, `equals`, `fromString` and `toString`. In this migration examples, we will be using the following:
```js
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayToString = require('uint8arrays/to-string')
```
#### contentRouting.put
**Before**
```js
const key = '/key'
const value = Buffer.from('oh hello there')
await libp2p.contentRouting.put(key, value)
```
**After**
```js
const key = '/key'
const value = uint8ArrayFromString('oh hello there')
await libp2p.contentRouting.put(key, value)
```
#### contentRouting.get
**Before**
```js
const key = '/key'
const value = await libp2p.contentRouting.put(key)
console.log('store value is: ', value.toString())
```
**After**
```js
const key = '/key'
const value = await libp2p.contentRouting.put(key)
console.log('store value is: ', uint8ArrayToString(value))
```
#### metadataBook.set
**Before**
```js
peerStore.metadataBook.set(peerId, 'location', Buffer.from('Saturn'))
```
**After**
```js
peerStore.metadataBook.set(peerId, 'location', uint8ArrayFromString('Saturn'))
```
#### metadataBook.get
**Before**
```js
const data = peerStore.metadataBook.get(peerId)
console.log('stored location: ', data.get('location').toString())
```
**After**
```js
const data = peerStore.metadataBook.get(peerId)
console.log('stored location: ', uint8ArrayToString(data.get('location')))
```
#### metadataBook.getValue
**Before**
```js
const location = peerStore.metadataBook.getValue(peerId, 'location')
console.log('stored location: ', location.toString())
```
**After**
```js
const location = peerStore.metadataBook.getValue(peerId, 'location')
console.log('stored location: ', uint8ArrayToString(location))
```
#### keychain.cms.encrypt
**Before**
```js
const keyInfo = await libp2p.keychain.createKey('keyTest', 'rsa', 4096)
const enc = await libp2p.keychain.cms.encrypt('keyTest', Buffer.from('data'))
```
**After**
```js
const keyInfo = await libp2p.keychain.createKey('keyTest', 'rsa', 4096)
const enc = await libp2p.keychain.cms.encrypt('keyTest', uint8ArrayFromString('data'))
```
#### pubsub
Already specified in its own chapter above.
## Module Updates
With this release you should update the following libp2p modules if you are relying on them:
```json
"libp2p-bootstrap": "^0.12.0",
"libp2p-delegated-content-routing": "^0.6.0",
"libp2p-delegated-peer-routing": "^0.6.0",
"libp2p-floodsub": "^0.23.0",
"libp2p-gossipsub": "^0.6.0",
"libp2p-kad-dht": "^0.20.0",
"libp2p-mdns": "^0.15.0",
"libp2p-mplex": "^0.10.0",
"libp2p-noise": "^2.0.0",
"libp2p-secio": "^0.13.1",
"libp2p-tcp": "^0.15.1",
"libp2p-webrtc-star": "^0.20.0",
"libp2p-websockets": "^0.14.0",
```

View File

@ -0,0 +1,185 @@
<!--Specify versions for migration below-->
# Migrating to libp2p@30
A migration guide for refactoring your application code from libp2p v0.29.x to v0.30.0.
## Table of Contents
- [API](#api)
- [Development and Testing](#development-and-testing)
- [Module Updates](#module-updates)
## API
### Pubsub
`js-libp2p` nodes prior to this version were emitting to self on publish by default.
This default value was changed on the pubsub router layer in the past, but we kept it overwritten in libp2p to avoid an upstream breaking change.
Now `js-libp2p` does not overwrite the pubsub router options anymore. Upstream projects that want this feature should enable it on their libp2p configuration.
**Before**
```js
const Gossipsub = require('libp2p-gossipsub')
const Libp2p = require('libp2p')
const libp2p = await Libp2p.create({
modules: {
// ... Add required modules according to the Configuration docs
pubsub: Gossipsub
}
})
```
**After**
```js
const Gossipsub = require('libp2p-gossipsub')
const Libp2p = require('libp2p')
const libp2p = await Libp2p.create({
modules: {
// ... Add required modules according to the Configuration docs
pubsub: Gossipsub
},
config: {
pubsub: {
emitSelf: true
}
}
})
```
The [Pubsub interface](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/pubsub) was updated on its message signing properties, taking into account the Gossipsub spec updates on [libp2p/specs#294](https://github.com/libp2p/specs/pull/294) and [libp2p/specs#299](https://github.com/libp2p/specs/pull/299)
The signing property is now based on a `globalSignaturePolicy` option instead of the previous `signMessages` and `strictSigning` options. The default to strict signing pubsub messages was kept, but if you would like to disable it, the properties should be changed as follows:
**Before**
```js
const Gossipsub = require('libp2p-gossipsub')
const Libp2p = require('libp2p')
const libp2p = await Libp2p.create({
modules: {
// ... Add required modules according to the Configuration docs
pubsub: Gossipsub
},
config: {
pubsub: {
signMessages: false,
strictSigning: false
}
}
})
```
**After**
```js
const Gossipsub = require('libp2p-gossipsub')
const { SignaturePolicy } = require('libp2p-interfaces/src/pubsub/signature-policy')
const Libp2p = require('libp2p')
const libp2p = await Libp2p.create({
modules: {
// ... Add required modules according to the Configuration docs
pubsub: Gossipsub
},
config: {
pubsub: {
globalSignaturePolicy: SignaturePolicy.StrictNoSign
}
}
})
```
### Addresses
Libp2p has supported `noAnnounce` addresses configuration for some time now. However, it did not provide the best developer experience. In this release, we dropped the `noAnnounce` configuration property in favor of an `announceFilter` property function.
**Before**
```js
const Libp2p = require('libp2p')
const libp2p = await Libp2p.create({
addresses: {
listen: ['/ip4/127.0.0.1/tcp/8000/ws'],
noAnnounce: ['/ip4/127.0.0.1/tcp/8000/ws'],
},
// ... additional configuration per the Configuration docs
})
```
**After**
```js
const Libp2p = require('libp2p')
// Libp2p utils has several multiaddr utils you can leverage
const isPrivate = require('libp2p-utils/src/multiaddr/is-private')
const libp2p = await Libp2p.create({
addresses: {
listen: ['/ip4/127.0.0.1/tcp/8000/ws'],
// Filter function: (ma: Array<multiaddr>) => Array<multiaddr>
announceFilter: (multiaddrs) => multiaddrs.filter(m => !isPrivate(m))
},
// ... additional configuration per the Configuration docs
})
```
It is important pointing out another change regarding address advertising. This is not an API breaking change, but it might have influence on your libp2p setup.
Previously, when using the addresses `announce` property, its multiaddrs were concatenated with the `listen` multiaddrs and then they were filtered out by the `noAnnounce` multiaddrs, in order to create the list of multiaddrs to advertise.
In `libp2p@0.30` the logic now operates as follows:
- If `announce` addresses are provided, only they will be announced (no filters are applied)
- If `announce` is not provided, the transport addresses will be filtered (if a filter is provided)
- if the `announceFilter` is provide it will be passed the transport addresses
## Development and Testing
While this is not an API breaking change, there was a behavioral breaking change on the Websockets transport when in a browser environment. This change might create issues on local test setups.
`libp2p-websockets` has allowed `TCP` and `DNS` addresses, both with `ws` or `wss` to be used for dial purposes. Taking into account security (and browser policies), we are now restricting addresses to `DNS` + `wss` in the browser
With this new behavior, if you need to use non DNS addresses, you can configure your libp2p node as follows:
```js
const Websockets = require('libp2p-websockets')
const filters = require('libp2p-websockets/src/filters')
const Libp2p = require('libp2p')
const transportKey = Websockets.prototype[Symbol.toStringTag]
const libp2p = await Libp2p.create({
modules: {
transport: [Websockets]
// ... Add required modules according to the Configuration docs
},
config: {
transport: {
[transportKey]: {
filter: filters.all
}
}
}
})
```
## Module Updates
With this release you should update the following libp2p modules if you are relying on them:
<!--Specify module versions in JSON for migration below.
It's recommended to check package.json changes for this:
`git diff <release> <prev> -- package.json`
-->
```json
"libp2p-delegated-content-routing": "^0.8.0",
"libp2p-delegated-peer-routing": "^0.8.0",
"libp2p-floodsub": "^0.24.0",
"libp2p-gossipsub": "^0.7.0",
"libp2p-websockets": "^0.15.0",
```
Note that some of them do not need to be updated for this libp2p version to work as expected, but we suggest you to keep them updated as part of this release.

View File

@ -0,0 +1,3 @@
# Delegate Nodes
[TODO](https://github.com/libp2p/js-libp2p/pull/718)

65
doc/production/README.md Normal file
View File

@ -0,0 +1,65 @@
# Production
Nowadays, you can run JavaScript code in several different environments, some of them with their own particularities. Moreover, you can use `js-libp2p` for a wide range of use cases. Different environments and different use cases mean different configurations and challenges in the network.
Libp2p nodes can vary from nodes behind an application, to infrastructure nodes that enable the network to operate and to be efficient. In this context, the Libp2p project provides public infrastructure to boost the network, enable nodes connectivity and improve constrained nodes performance. This public infrastructure should be leveraged for learning the concepts and experimenting. When an application on top of libp2p aims to move into production, its own infrastructure should be setup as the public nodes will be intensively used by others and its availability is not guaranteed.
This guide aims to guide you from using the public infrastructure into setting up your own.
## Table of Contents
* [Joining the Network](#joining-the-network)
* [Connecting to Nodes with connectivity limitations](#connecting-to-nodes-with-connectivity-limitations)
* [`webrtc-star` servers](#webrtc-star-servers)
* [Circuit Relay](#circuit-relay)
* [Querying the network from the browser](#querying-the-network-from-the-browser)
* [Others](#others)
* [SSL](#ssl)
## Joining the Network
Once a libp2p node stars, it will need to connect to a set of peers in order to establish its overlay network.
Currently `js-libp2p` is not the best choice for being a bootstrap node. Its DHT needs to be improved, in order to become an effective server to enable other nodes to properly bootstrap their network.
Setting up a fleet of [`go-libp2p`](https://github.com/libp2p/go-libp2p) nodes is the recommended way to proceed here.
## Connecting to Nodes with connectivity limitations
While the libp2p core codebase aims to work in multiple environments, there are some limitations that are not possible to overcome at the time of writing. These limitations include browser nodes, nodes behind NAT, reverse proxies, firewalls, or lack of compatible transports.
In the browser, libp2p supports two transports: `websockets` and `webrtc-star`. Nowadays, browsers do not support listening for connections, but only to dial known addresses. `webrtc-star` servers can be used to enable libp2p nodes to discover other nodes running on the browser and to help them establish a connection.
For nodes that cannot be dialed (including browser), circuit relay nodes should be used.
### `webrtc-star` servers
Regarding `webRTC` connections, a set of star servers are needed to act as a rendezvous point, where peers can learn about other peers (`peer-discovery`), as well as exchange their SDP offers (signaling data).
You can read on how to setup your own star servers in [libp2p/js-libp2p-webrtc-star/DEPLOYMENT.md](https://github.com/libp2p/js-libp2p-webrtc-star/blob/master/DEPLOYMENT.md).
It is worth pointing out that with new discovery protocols on the way, as well as support for distributed signaling, the star servers should be deprecated on the long run.
### Circuit Relay
Libp2p nodes acting as circuit relay aim to establish connectivity between libp2p nodes (e.g. IPFS nodes) that wouldn't otherwise be able to establish a direct connection to each other.
A relay is needed in situations where nodes are behind NAT, reverse proxies, firewalls and/or simply don't support the same transports (e.g. go-libp2p vs. browser-libp2p). The circuit relay protocol exists to overcome those scenarios. Nodes with the `auto-relay` feature enabled can automatically bind themselves on a relay to listen for connections on their behalf.
You can use [libp2p/js-libp2p-relay-server](https://github.com/libp2p/js-libp2p-relay-server) to setup your own relay server. This also includes an easy to customize Docker setup for a HOP Relay.
## Querying the network from the browser
Libp2p nodes in scenarios such as browser environment and constrained devices will not be an efficient node in the libp2p DHT overlay, as a consequence of their known limitations regarding connectivity and performance.
Aiming to support these type of nodes to find other peers and content in the network, delegate nodes can be setup. With a set of well known IPFS delegate nodes, nodes with limitations in the network can leverage them to perform peer and content routing queries.
Currently, delegate nodes must be IPFS nodes as the IPFS HTTP API is leveraged by them to make routing queries.
You can read on how to setup your own set of delegated nodes in [DELEGATE_NODES.md](./DELEGATE_NODES.md).
## Others
### SSL
TODO

View File

@ -0,0 +1,192 @@
# Auto relay
Auto Relay enables libp2p nodes to dynamically find and bind to relays on the network. Once binding (listening) is done, the node can and should advertise its addresses on the network, allowing any other node to dial it over its bound relay(s).
While direct connections to nodes are preferable, it's not always possible to do so due to NATs or browser limitations.
## 0. Setup the example
Before moving into the examples, you should run `npm install` on the top level `js-libp2p` folder, in order to install all the dependencies needed for this example. Once the install finishes, you should move into the example folder with `cd examples/auto-relay`.
This example comes with 3 main files. A `relay.js` file to be used in the first step, a `listener.js` file to be used in the second step and a `dialer.js` file to be used on the third step. All of these scripts will run their own libp2p node, which will interact with the previous ones. All nodes must be running in order for you to proceed.
## 1. Set up a relay node
In the first step of this example, we need to configure and run a relay node in order for our target node to bind to for accepting inbound connections.
The relay node will need to have its relay subsystem enabled, as well as its HOP capability. It can be configured as follows:
```js
const Libp2p = require('libp2p')
const Websockets = require('libp2p-websockets')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const node = await Libp2p.create({
modules: {
transport: [Websockets],
connEncryption: [NOISE],
streamMuxer: [MPLEX]
},
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0/ws']
// TODO check "What is next?" section
// announce: ['/dns4/auto-relay.libp2p.io/tcp/443/wss/p2p/QmWDn2LY8nannvSWJzruUYoLZ4vV83vfCBwd8DipvdgQc3']
},
config: {
relay: {
enabled: true,
hop: {
enabled: true
},
advertise: {
enabled: true,
}
}
}
})
await node.start()
console.log(`Node started with id ${node.peerId.toB58String()}`)
console.log('Listening on:')
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
```
The Relay HOP advertise functionality is **NOT** required to be enabled. However, if you are interested in advertising on the network that this node is available to be used as a HOP Relay you can enable it. A content router module or Rendezvous needs to be configured to leverage this option.
You should now run the following to start the relay node:
```sh
node relay.js
```
This should print out something similar to the following:
```sh
Node started with id QmWDn2LY8nannvSWJzruUYoLZ4vV83vfCBwd8DipvdgQc3
Listening on:
/ip4/127.0.0.1/tcp/61592/ws/p2p/QmWDn2LY8nannvSWJzruUYoLZ4vV83vfCBwd8DipvdgQc3
/ip4/192.168.1.120/tcp/61592/ws/p2p/QmWDn2LY8nannvSWJzruUYoLZ4vV83vfCBwd8DipvdgQc3
```
## 2. Set up a listener node with Auto Relay Enabled
One of the typical use cases for Auto Relay is nodes behind a NAT or browser nodes due to their inability to expose a public address. For running a libp2p node that automatically binds itself to connected HOP relays, you can see the following:
```js
const Libp2p = require('libp2p')
const Websockets = require('libp2p-websockets')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const relayAddr = process.argv[2]
if (!relayAddr) {
throw new Error('the relay address needs to be specified as a parameter')
}
const node = await Libp2p.create({
modules: {
transport: [Websockets],
connEncryption: [NOISE],
streamMuxer: [MPLEX]
},
config: {
relay: {
enabled: true,
autoRelay: {
enabled: true,
maxListeners: 2
}
}
}
})
await node.start()
console.log(`Node started with id ${node.peerId.toB58String()}`)
const conn = await node.dial(relayAddr)
// Wait for connection and relay to be bind for the example purpose
await new Promise((resolve) => {
node.peerStore.on('change:multiaddrs', ({ peerId }) => {
// Updated self multiaddrs?
if (peerId.equals(node.peerId)) {
resolve()
}
})
})
console.log(`Connected to the HOP relay ${conn.remotePeer.toString()}`)
console.log(`Advertising with a relay address of ${node.multiaddrs[0].toString()}/p2p/${node.peerId.toB58String()}`)
```
As you can see in the code, we need to provide the relay address, `relayAddr`, as a process argument. This node will dial the provided relay address and automatically bind to it.
You should now run the following to start the node running Auto Relay:
```sh
node listener.js /ip4/192.168.1.120/tcp/58941/ws/p2p/QmQKCBm87HQMbFqy14oqC85pMmnRrj6iD46ggM6reqNpsd
```
This should print out something similar to the following:
```sh
Node started with id QmerrWofKF358JE6gv3z74cEAyL7z1KqhuUoVfGEynqjRm
Connected to the HOP relay QmWDn2LY8nannvSWJzruUYoLZ4vV83vfCBwd8DipvdgQc3
Advertising with a relay address of /ip4/192.168.1.120/tcp/61592/ws/p2p/QmWDn2LY8nannvSWJzruUYoLZ4vV83vfCBwd8DipvdgQc3/p2p-circuit/p2p/QmerrWofKF358JE6gv3z74cEAyL7z1KqhuUoVfGEynqjRm
```
Per the address, it is possible to verify that the auto relay node is listening on the circuit relay node address.
Instead of dialing this relay manually, you could set up this node with the Bootstrap module and provide it in the bootstrap list. Moreover, you can use other `peer-discovery` modules to discover peers in the network and the node will automatically bind to the relays that support HOP until reaching the maximum number of listeners.
## 3. Set up a dialer node for testing connectivity
Now that you have a relay node and a node bound to that relay, you can test connecting to the auto relay node via the relay.
```js
const Libp2p = require('libp2p')
const Websockets = require('libp2p-websockets')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const autoRelayNodeAddr = process.argv[2]
if (!autoRelayNodeAddr) {
throw new Error('the auto relay node address needs to be specified')
}
const node = await Libp2p.create({
modules: {
transport: [Websockets],
connEncryption: [NOISE],
streamMuxer: [MPLEX]
}
})
await node.start()
console.log(`Node started with id ${node.peerId.toB58String()}`)
const conn = await node.dial(autoRelayNodeAddr)
console.log(`Connected to the auto relay node via ${conn.remoteAddr.toString()}`)
```
You should now run the following to start the relay node using the listen address from step 2:
```sh
node dialer.js /ip4/192.168.1.120/tcp/58941/ws/p2p/QmQKCBm87HQMbFqy14oqC85pMmnRrj6iD46ggM6reqNpsd
```
Once you start your test node, it should print out something similar to the following:
```sh
Node started: Qme7iEzDxFoFhhkrsrkHkMnM11aPYjysaehP4NZeUfVMKG
Connected to the auto relay node via /ip4/192.168.1.120/tcp/61592/ws/p2p/QmWDn2LY8nannvSWJzruUYoLZ4vV83vfCBwd8DipvdgQc3/p2p-circuit/p2p/QmerrWofKF358JE6gv3z74cEAyL7z1KqhuUoVfGEynqjRm
```
As you can see from the output, the remote address of the established connection uses the relayed connection.
## 4. What is next?
Before moving into production, there are a few things that you should take into account.
A relay node should not advertise its private address in a real world scenario, as the node would not be reachable by others. You should provide an array of public addresses in the libp2p `addresses.announce` option. If you are using websockets, bear in mind that due to browsers security policies you cannot establish unencrypted connection from secure context. The simplest solution is to setup SSL with nginx and proxy to the node and setup a domain name for the certificate.

View File

@ -0,0 +1,29 @@
'use strict'
const Libp2p = require('libp2p')
const Websockets = require('libp2p-websockets')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
async function main () {
const autoRelayNodeAddr = process.argv[2]
if (!autoRelayNodeAddr) {
throw new Error('the auto relay node address needs to be specified')
}
const node = await Libp2p.create({
modules: {
transport: [Websockets],
connEncryption: [NOISE],
streamMuxer: [MPLEX]
}
})
await node.start()
console.log(`Node started with id ${node.peerId.toB58String()}`)
const conn = await node.dial(autoRelayNodeAddr)
console.log(`Connected to the auto relay node via ${conn.remoteAddr.toString()}`)
}
main()

View File

@ -0,0 +1,47 @@
'use strict'
const Libp2p = require('libp2p')
const Websockets = require('libp2p-websockets')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
async function main () {
const relayAddr = process.argv[2]
if (!relayAddr) {
throw new Error('the relay address needs to be specified as a parameter')
}
const node = await Libp2p.create({
modules: {
transport: [Websockets],
connEncryption: [NOISE],
streamMuxer: [MPLEX]
},
config: {
relay: {
enabled: true,
autoRelay: {
enabled: true,
maxListeners: 2
}
}
}
})
await node.start()
console.log(`Node started with id ${node.peerId.toB58String()}`)
const conn = await node.dial(relayAddr)
console.log(`Connected to the HOP relay ${conn.remotePeer.toString()}`)
// Wait for connection and relay to be bind for the example purpose
node.peerStore.on('change:multiaddrs', ({ peerId }) => {
// Updated self multiaddrs?
if (peerId.equals(node.peerId)) {
console.log(`Advertising with a relay address of ${node.multiaddrs[0].toString()}/p2p/${node.peerId.toB58String()}`)
}
})
}
main()

View File

@ -0,0 +1,40 @@
'use strict'
const Libp2p = require('libp2p')
const Websockets = require('libp2p-websockets')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
async function main () {
const node = await Libp2p.create({
modules: {
transport: [Websockets],
connEncryption: [NOISE],
streamMuxer: [MPLEX]
},
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0/ws']
// TODO check "What is next?" section
// announce: ['/dns4/auto-relay.libp2p.io/tcp/443/wss/p2p/QmWDn2LY8nannvSWJzruUYoLZ4vV83vfCBwd8DipvdgQc3']
},
config: {
relay: {
enabled: true,
hop: {
enabled: true
},
advertise: {
enabled: true,
}
}
}
})
await node.start()
console.log(`Node started with id ${node.peerId.toB58String()}`)
console.log('Listening on:')
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
}
main()

View File

@ -0,0 +1,94 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pDefer = require('p-defer')
const uint8ArrayToString = require('uint8arrays/to-string')
function startProcess (name, args = []) {
return execa('node', [path.join(__dirname, name), ...args], {
cwd: path.resolve(__dirname),
all: true
})
}
async function test () {
let output1 = ''
let output2 = ''
let output3 = ''
let relayAddr
let autoRelayAddr
const proc1Ready = pDefer()
const proc2Ready = pDefer()
// Step 1 process
process.stdout.write('relay.js\n')
const proc1 = startProcess('relay.js')
proc1.all.on('data', async (data) => {
process.stdout.write(data)
output1 += uint8ArrayToString(data)
if (output1.includes('Listening on:') && output1.includes('/p2p/')) {
relayAddr = output1.trim().split('Listening on:\n')[1].split('\n')[0]
proc1Ready.resolve()
}
})
await proc1Ready.promise
process.stdout.write('==================================================================\n')
// Step 2 process
process.stdout.write('listener.js\n')
const proc2 = startProcess('listener.js', [relayAddr])
proc2.all.on('data', async (data) => {
process.stdout.write(data)
output2 += uint8ArrayToString(data)
if (output2.includes('Advertising with a relay address of') && output2.includes('/p2p/')) {
autoRelayAddr = output2.trim().split('Advertising with a relay address of ')[1]
proc2Ready.resolve()
}
})
await proc2Ready.promise
process.stdout.write('==================================================================\n')
// Step 3 process
process.stdout.write('dialer.js\n')
const proc3 = startProcess('dialer.js', [autoRelayAddr])
proc3.all.on('data', async (data) => {
process.stdout.write(data)
output3 += uint8ArrayToString(data)
if (output3.includes('Connected to the auto relay node via')) {
const remoteAddr = output3.trim().split('Connected to the auto relay node via ')[1]
if (remoteAddr === autoRelayAddr) {
proc3.kill()
proc2.kill()
proc1.kill()
} else {
throw new Error('dialer did not dial through the relay')
}
}
})
await Promise.all([
proc1,
proc2,
proc3
]).catch((err) => {
if (err.signal !== 'SIGTERM') {
throw err
}
})
}
module.exports = test

View File

@ -2,8 +2,8 @@
/* eslint-disable no-console */
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const Node = require('./libp2p-bundle')
const multiaddr = require('multiaddr')
const createLibp2p = require('./libp2p')
const { stdinToStream, streamToConsole } = require('./stream')
async function run() {
@ -13,27 +13,25 @@ async function run() {
])
// Create a new libp2p node on localhost with a randomly chosen port
const peerDialer = new PeerInfo(idDialer)
peerDialer.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const nodeDialer = new Node({
peerInfo: peerDialer
const nodeDialer = await createLibp2p({
peerId: idDialer,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
}
})
// Create a PeerInfo with the listening peer's address
const peerListener = new PeerInfo(idListener)
peerListener.multiaddrs.add('/ip4/127.0.0.1/tcp/10333')
// Start the libp2p host
await nodeDialer.start()
// Output this node's address
console.log('Dialer ready, listening on:')
peerListener.multiaddrs.forEach((ma) => {
console.log(ma.toString() + '/p2p/' + idListener.toB58String())
nodeDialer.multiaddrs.forEach((ma) => {
console.log(ma.toString() + '/p2p/' + idDialer.toB58String())
})
// Dial to the remote peer (the "listener")
const { stream } = await nodeDialer.dialProtocol(peerListener, '/chat/1.0.0')
const listenerMa = multiaddr(`/ip4/127.0.0.1/tcp/10333/p2p/${idListener.toB58String()}`)
const { stream } = await nodeDialer.dialProtocol(listenerMa, '/chat/1.0.0')
console.log('Dialer dialed to listener on protocol: /chat/1.0.0')
console.log('Type a message and see what happens')

View File

@ -1,27 +0,0 @@
'use strict'
const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets')
const mplex = require('libp2p-mplex')
const secio = require('libp2p-secio')
const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../..')
class Node extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [
TCP,
WS
],
streamMuxer: [ mplex ],
connEncryption: [ secio ]
}
}
super(defaultsDeep(_options, defaults))
}
}
module.exports = Node

View File

@ -0,0 +1,22 @@
'use strict'
const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets')
const mplex = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../..')
async function createLibp2p(_options) {
const defaults = {
modules: {
transport: [TCP, WS],
streamMuxer: [mplex],
connEncryption: [NOISE],
},
}
return libp2p.create(defaultsDeep(_options, defaults))
}
module.exports = createLibp2p

View File

@ -2,22 +2,22 @@
/* eslint-disable no-console */
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const Node = require('./libp2p-bundle.js')
const createLibp2p = require('./libp2p.js')
const { stdinToStream, streamToConsole } = require('./stream')
async function run() {
// Create a new libp2p node with the given multi-address
const idListener = await PeerId.createFromJSON(require('./peer-id-listener'))
const peerListener = new PeerInfo(idListener)
peerListener.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
const nodeListener = new Node({
peerInfo: peerListener
const nodeListener = await createLibp2p({
peerId: idListener,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/10333']
}
})
// Log a message when a remote peer connects to us
nodeListener.on('peer:connect', (peerInfo) => {
console.log(peerInfo.id.toB58String())
nodeListener.connectionManager.on('peer:connect', (connection) => {
console.log('connected to: ', connection.remotePeer.toB58String())
})
// Handle messages for the protocol
@ -33,7 +33,7 @@ async function run() {
// Output listen addresses to the console
console.log('Listener ready, listening on:')
peerListener.multiaddrs.forEach((ma) => {
nodeListener.multiaddrs.forEach((ma) => {
console.log(ma.toString() + '/p2p/' + idListener.toB58String())
})
}

View File

@ -28,7 +28,7 @@ function streamToConsole(stream) {
// For each chunk of data
for await (const msg of source) {
// Output the data as a utf8 string
console.log('> ' + msg.toString('utf8').replace('\n', ''))
console.log('> ' + msg.toString().replace('\n', ''))
}
}
)

77
examples/chat/test.js Normal file
View File

@ -0,0 +1,77 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pDefer = require('p-defer')
const uint8ArrayToString = require('uint8arrays/to-string')
function startProcess(name) {
return execa('node', [path.join(__dirname, name)], {
cwd: path.resolve(__dirname),
all: true
})
}
async function test () {
const message = 'test message'
let listenerOutput = ''
let dialerOutput = ''
let isListening = false
let messageSent = false
const listenerReady = pDefer()
const dialerReady = pDefer()
const messageReceived = pDefer()
// Step 1 process
process.stdout.write('node listener.js\n')
const listenerProc = startProcess('src/listener.js')
listenerProc.all.on('data', async (data) => {
process.stdout.write(data)
listenerOutput += uint8ArrayToString(data)
if (!isListening && listenerOutput.includes('Listener ready, listening on')) {
listenerReady.resolve()
isListening = true
} else if (isListening && listenerOutput.includes(message)) {
messageReceived.resolve()
}
})
await listenerReady.promise
process.stdout.write('==================================================================\n')
// Step 2 process
process.stdout.write('node dialer.js\n')
const dialerProc = startProcess('src/dialer.js')
dialerProc.all.on('data', async (data) => {
process.stdout.write(data)
dialerOutput += uint8ArrayToString(data)
if (!messageSent && dialerOutput.includes('Type a message and see what happens')) {
dialerReady.resolve()
dialerProc.stdin.write(message)
dialerProc.stdin.write('\n')
messageSent = true
}
})
await dialerReady.promise
process.stdout.write('==================================================================\n')
await messageReceived.promise
process.stdout.write('chat message received\n')
listenerProc.kill()
dialerProc.kill()
await Promise.all([
listenerProc,
dialerProc
]).catch((err) => {
if (err.signal !== 'SIGTERM') {
throw err
}
})
}
module.exports = test

View File

@ -1,23 +1,21 @@
'use strict'
const Libp2p = require('../../')
const Libp2p = require('../..')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const { NOISE } = require('libp2p-noise')
const pipe = require('it-pipe')
const createNode = async () => {
const peerInfo = await PeerInfo.create()
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const node = await Libp2p.create({
peerInfo,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [Mplex],
connEncryption: [SECIO]
connEncryption: [NOISE]
}
})
@ -32,6 +30,8 @@ const createNode = async () => {
createNode()
])
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
node2.handle('/a-protocol', ({ stream }) => {
pipe(
stream,
@ -43,7 +43,7 @@ const createNode = async () => {
)
})
const { stream } = await node1.dialProtocol(node2.peerInfo, '/a-protocol')
const { stream } = await node1.dialProtocol(node2.peerId, '/a-protocol')
await pipe(
['This information is sent out encrypted to the other peer'],

View File

@ -1,4 +1,4 @@
# Encrypted Communications
# Connection Encryption
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.
@ -6,32 +6,26 @@ We call this usage a _connection upgrade_ where given a connection between peer
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/p2p/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
# 1. Set up encrypted communications
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`.
We will build this example on top of example for [Protocol and Stream Multiplexing](../protocol-and-stream-multiplexing). You will need the `libp2p-noise` module to complete it, go ahead and `npm install libp2p-noise`.
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 configuration, all you have to do is:
To add them to your libp2p configuration, all you have to do is:
```JavaScript
const Libp2p = require('libp2p')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const createNode = () => {
return Libp2p.create({
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
// Attach secio as the crypto channel to use
connEncryption: [ SECIO ]
// Attach noise as the crypto channel to use
connEncryption: [ NOISE ]
}
})
}
```
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,30 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pDefer = require('p-defer')
const uint8ArrayToString = require('uint8arrays/to-string')
async function test () {
const messageReceived = pDefer()
process.stdout.write('1.js\n')
const proc = execa('node', [path.join(__dirname, '1.js')], {
cwd: path.resolve(__dirname),
all: true
})
proc.all.on('data', async (data) => {
process.stdout.write(data)
const s = uint8ArrayToString(data)
if (s.includes('This information is sent out encrypted to the other peer')) {
messageReceived.resolve()
}
})
await messageReceived.promise
proc.kill()
}
module.exports = test

View File

@ -4,28 +4,20 @@
const Libp2p = require('../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const Bootstrap = require('libp2p-bootstrap')
// 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/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
'/ip4/104.236.176.52/tcp/4001/p2p/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
'/ip4/104.236.179.241/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/ip4/162.243.248.213/tcp/4001/p2p/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/ip4/128.199.219.111/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/ip4/104.236.76.40/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
'/ip4/178.62.158.247/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/ip4/178.62.61.185/tcp/4001/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/ip4/104.236.151.122/tcp/4001/p2p/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx'
]
const bootstrapers = require('./bootstrapers')
;(async () => {
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [Mplex],
connEncryption: [SECIO],
connEncryption: [NOISE],
peerDiscovery: [Bootstrap]
},
config: {
@ -39,15 +31,13 @@ const bootstrapers = [
}
})
node.peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node.on('peer:connect', (peer) => {
console.log('Connection established to:', peer.id.toB58String()) // Emitted when a peer has been found
node.connectionManager.on('peer:connect', (connection) => {
console.log('Connection established to:', connection.remotePeer.toB58String()) // Emitted when a peer has been found
})
node.on('peer:discovery', (peer) => {
node.on('peer:discovery', (peerId) => {
// No need to dial, autoDial is on
console.log('Discovered:', peer.id.toB58String())
console.log('Discovered:', peerId.toB58String())
})
await node.start()

View File

@ -4,27 +4,29 @@
const Libp2p = require('../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const MulticastDNS = require('libp2p-mdns')
const createNode = async () => {
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [Mplex],
connEncryption: [SECIO],
connEncryption: [NOISE],
peerDiscovery: [MulticastDNS]
},
config: {
peerDiscovery: {
mdns: {
[MulticastDNS.tag]: {
interval: 20e3,
enabled: true
}
}
}
})
node.peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
return node
}
@ -35,8 +37,8 @@ const createNode = async () => {
createNode()
])
node1.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
node2.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
node1.on('peer:discovery', (peerId) => console.log('Discovered:', peerId.toB58String()))
node2.on('peer:discovery', (peerId) => console.log('Discovered:', peerId.toB58String()))
await Promise.all([
node1.start(),

View File

@ -4,11 +4,11 @@ A Peer Discovery module enables libp2p to find peers to connect to. Think of the
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.
These mechanisms save configuration and enable a node to operate without any explicit dials, it will just work. Once new peers are discovered, their known data is stored in the peer's PeerStore.
## 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).
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 NOISE. You can see the complete example at [1.js](./1.js).
First, we create our libp2p node.
@ -20,7 +20,7 @@ const node = Libp2p.create({
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
connEncryption: [ NOISE ],
peerDiscovery: [ Bootstrap ]
},
config: {
@ -40,14 +40,11 @@ In this configuration, we use a `bootstrappers` array listing peers to connect _
```JavaScript
const bootstrapers = [
'/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
'/ip4/104.236.176.52/tcp/4001/p2p/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
'/ip4/104.236.179.241/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/ip4/162.243.248.213/tcp/4001/p2p/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/ip4/128.199.219.111/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/ip4/104.236.76.40/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
'/ip4/178.62.158.247/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/ip4/178.62.61.185/tcp/4001/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/ip4/104.236.151.122/tcp/4001/p2p/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx'
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
]
```
@ -55,11 +52,14 @@ Now, once we create and start the node, we can listen for events such as `peer:d
```JavaScript
const node = await Libp2p.create({
peerInfo,
peerId,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
}
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
connEncryption: [ NOISE ],
peerDiscovery: [ Bootstrap ]
},
config: {
@ -73,15 +73,13 @@ const node = await Libp2p.create({
}
})
node.peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node.on('peer:connect', (peer) => {
console.log('Connection established to:', peer.id.toB58String()) // Emitted when a peer has been found
node.connectionManager.on('peer:connect', (connection) => {
console.log('Connection established to:', connection.remotePeer.toB58String()) // Emitted when a new connection has been created
})
// Emitted when a peer has been found
node.on('peer:discovery', (peer) => {
console.log('Discovered:', peer.id.toB58String())
node.on('peer:discovery', (peerId) => {
// No need to dial, autoDial is on
console.log('Discovered:', peerId.toB58String())
})
await node.start()
@ -92,14 +90,17 @@ 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
Discovered: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN
Discovered: QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb
Discovered: QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp
Discovered: QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa
Discovered: QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt
Connection established to: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
Connection established to: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN
Connection established to: QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp
Connection established to: QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa
Connection established to: QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt
Connection established to: QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb
```
## 2. MulticastDNS to find other peers in the network
@ -114,10 +115,13 @@ const MulticastDNS = require('libp2p-mdns')
const createNode = () => {
return Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
}
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
connEncryption: [ NOISE ],
peerDiscovery: [ MulticastDNS ]
},
config: {
@ -157,5 +161,5 @@ Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
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.
- Any DHT will offer you a discovery capability. You can simple _random-walk_ the routing tables to find other peers to connect to. For example [libp2p-kad-dht](https://github.com/libp2p/js-libp2p-kad-dht) can be used for peer discovery. An example how to configure it to enable random walks can be found [here](https://github.com/libp2p/js-libp2p/blob/v0.28.4/doc/CONFIGURATION.md#customizing-dht).
- You can create your own Discovery service, a registry, a list, a radio beacon, you name it!

View File

@ -0,0 +1,13 @@
'use strict'
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/packages/ipfs-core/src/runtime/config-nodejs.js
const bootstrapers = [
'/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
]
module.exports = bootstrapers

View File

@ -0,0 +1,42 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pWaitFor = require('p-wait-for')
const uint8ArrayToString = require('uint8arrays/to-string')
const bootstrapers = require('./bootstrapers')
const discoveredCopy = 'Discovered:'
const connectedCopy = 'Connection established to:'
async function test () {
const discoveredNodes = []
const connectedNodes = []
process.stdout.write('1.js\n')
const proc = execa('node', [path.join(__dirname, '1.js')], {
cwd: path.resolve(__dirname),
all: true
})
proc.all.on('data', async (data) => {
process.stdout.write(data)
const line = uint8ArrayToString(data)
// Discovered or Connected
if (line.includes(discoveredCopy)) {
const id = line.trim().split(discoveredCopy)[1]
discoveredNodes.push(id)
} else if (line.includes(connectedCopy)) {
const id = line.trim().split(connectedCopy)[1]
connectedNodes.push(id)
}
})
await pWaitFor(() => discoveredNodes.length === bootstrapers.length && connectedNodes.length === bootstrapers.length)
proc.kill()
}
module.exports = test

View File

@ -0,0 +1,35 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pWaitFor = require('p-wait-for')
const uint8ArrayToString = require('uint8arrays/to-string')
const discoveredCopy = 'Discovered:'
async function test() {
const discoveredNodes = []
process.stdout.write('2.js\n')
const proc = execa('node', [path.join(__dirname, '2.js')], {
cwd: path.resolve(__dirname),
all: true
})
proc.all.on('data', async (data) => {
process.stdout.write(data)
const line = uint8ArrayToString(data)
if (line.includes(discoveredCopy)) {
const id = line.trim().split(discoveredCopy)[1]
discoveredNodes.push(id)
}
})
await pWaitFor(() => discoveredNodes.length === 2)
proc.kill()
}
module.exports = test

View File

@ -0,0 +1,11 @@
'use strict'
const test1 = require('./test-1')
const test2 = require('./test-2')
async function test () {
await test1()
await test2()
}
module.exports = test

View File

@ -6,8 +6,7 @@
*/
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const Node = require('./libp2p-bundle')
const createLibp2p = require('./libp2p')
const pipe = require('it-pipe')
async function run() {
@ -17,28 +16,26 @@ async function run() {
])
// Dialer
const dialerPeerInfo = new PeerInfo(dialerId)
dialerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const dialerNode = new Node({
peerInfo: dialerPeerInfo
const dialerNode = await createLibp2p({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
peerId: dialerId
})
// Peer to Dial (the listener)
const listenerPeerInfo = new PeerInfo(listenerId)
const listenerMultiaddr = '/ip4/127.0.0.1/tcp/10333/p2p/' +
listenerId.toB58String()
listenerPeerInfo.multiaddrs.add(listenerMultiaddr)
// Add peer to Dial (the listener) into the PeerStore
const listenerMultiaddr = '/ip4/127.0.0.1/tcp/10333/p2p/' + listenerId.toB58String()
// Start the dialer libp2p node
await dialerNode.start()
console.log('Dialer ready, listening on:')
dialerPeerInfo.multiaddrs.forEach((ma) => console.log(ma.toString() +
dialerNode.multiaddrs.forEach((ma) => console.log(ma.toString() +
'/p2p/' + dialerId.toB58String()))
// Dial the listener node
console.log('Dialing to peer:', listenerMultiaddr.toString())
const { stream } = await dialerNode.dialProtocol(listenerPeerInfo, '/echo/1.0.0')
console.log('Dialing to peer:', listenerMultiaddr)
const { stream } = await dialerNode.dialProtocol(listenerMultiaddr, '/echo/1.0.0')
console.log('nodeA dialed to nodeB on protocol: /echo/1.0.0')

View File

@ -1,27 +0,0 @@
'use strict'
const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets')
const mplex = require('libp2p-mplex')
const secio = require('libp2p-secio')
const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../..')
class Node extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [
TCP,
WS
],
streamMuxer: [ mplex ],
connEncryption: [ secio ]
}
}
super(defaultsDeep(_options, defaults))
}
}
module.exports = Node

View File

@ -0,0 +1,23 @@
'use strict'
const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets')
const mplex = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../..')
async function createLibp2p(_options) {
const defaults = {
modules: {
transport: [TCP, WS],
streamMuxer: [mplex],
connEncryption: [NOISE],
},
}
return libp2p.create(defaultsDeep(_options, defaults))
}
module.exports = createLibp2p

View File

@ -6,23 +6,23 @@
*/
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const Node = require('./libp2p-bundle')
const createLibp2p = require('./libp2p')
const pipe = require('it-pipe')
async function run() {
const listenerId = await PeerId.createFromJSON(require('./id-l'))
// Listener libp2p node
const listenerPeerInfo = new PeerInfo(listenerId)
listenerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
const listenerNode = new Node({
peerInfo: listenerPeerInfo
const listenerNode = await createLibp2p({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/10333']
},
peerId: listenerId
})
// Log a message when we receive a connection
listenerNode.on('peer:connect', (peerInfo) => {
console.log('received dial to me from:', peerInfo.id.toB58String())
listenerNode.connectionManager.on('peer:connect', (connection) => {
console.log('received dial to me from:', connection.remotePeer.toB58String())
})
// Handle incoming connections for the protocol by piping from the stream
@ -33,7 +33,7 @@ async function run() {
await listenerNode.start()
console.log('Listener ready, listening on:')
listenerNode.peerInfo.multiaddrs.forEach((ma) => {
listenerNode.multiaddrs.forEach((ma) => {
console.log(ma.toString() + '/p2p/' + listenerId.toB58String())
})
}

61
examples/echo/test.js Normal file
View File

@ -0,0 +1,61 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pDefer = require('p-defer')
const uint8ArrayToString = require('uint8arrays/to-string')
function startProcess(name) {
return execa('node', [path.join(__dirname, name)], {
cwd: path.resolve(__dirname),
all: true
})
}
async function test () {
const listenerReady = pDefer()
const messageReceived = pDefer()
// Step 1 process
process.stdout.write('node listener.js\n')
const listenerProc = startProcess('src/listener.js')
listenerProc.all.on('data', async (data) => {
process.stdout.write(data)
const s = uint8ArrayToString(data)
if (s.includes('Listener ready, listening on:')) {
listenerReady.resolve()
}
})
await listenerReady.promise
process.stdout.write('==================================================================\n')
// Step 2 process
process.stdout.write('node dialer.js\n')
const dialerProc = startProcess('src/dialer.js')
dialerProc.all.on('data', async (data) => {
process.stdout.write(data)
const s = uint8ArrayToString(data)
if (s.includes('received echo:')) {
messageReceived.resolve()
}
})
await messageReceived.promise
process.stdout.write('echo message received\n')
listenerProc.kill()
dialerProc.kill()
await Promise.all([
listenerProc,
dialerProc
]).catch((err) => {
if (err.signal !== 'SIGTERM') {
throw err
}
})
}
module.exports = test

View File

@ -11,26 +11,9 @@ cd ./examples/libp2p-in-the-browser
npm install
```
## Signaling Server
This example uses the `libp2p-webrtc-star` module, which enables libp2p browser nodes to establish direct connections to one another via a central signaling server. For this example, we are using the signaling server that ships with `libp2p-webrtc-star`.
You can start the server by running `npm run server`. This will start a signaling server locally on port `9090`. If you'd like to run a signaling server outside of this example, you can see instructions on how to do so in the [`libp2p-webrtc-star` README](https://github.com/libp2p/js-libp2p-webrtc-star).
When you run the server, you should see output that looks something like this:
```log
$ npm run server
> libp2p-in-browser@1.0.0 server
> star-signal
Listening on: http://0.0.0.0:9090
```
## Running the examples
Once you have started the signaling server, you can run the Parcel server.
Start by running the Parcel server:
```
npm start
@ -53,3 +36,11 @@ This will compile the code and start a server listening on port [http://localhos
Now, if you open a second browser tab to `http://localhost:1234`, you should discover your node from the previous tab. This is due to the fact that the `libp2p-webrtc-star` transport also acts as a Peer Discovery interface. Your node will be notified of any peer that connects to the same signaling server you are connected to. Once libp2p discovers this new peer, it will attempt to establish a direct WebRTC connection.
**Note**: In the example we assign libp2p to `window.libp2p`, in case you would like to play around with the API directly in the browser. You can of course make changes to `index.js` and Parcel will automatically rebuild and reload the browser tabs.
## Going to production?
This example uses public `libp2p-webrtc-star` servers. These servers should be used for experimenting and demos, they **MUST** not be used in production as there is no guarantee on availability.
You can see how to deploy your own signaling server in [libp2p/js-libp2p-webrtc-star/DEPLOYMENT.md](https://github.com/libp2p/js-libp2p-webrtc-star/blob/master/DEPLOYMENT.md).
Once you have your own server running, you should add its listen address in your libp2p node configuration.

View File

@ -2,30 +2,40 @@ import 'babel-polyfill'
import Libp2p from 'libp2p'
import Websockets from 'libp2p-websockets'
import WebRTCStar from 'libp2p-webrtc-star'
import Secio from 'libp2p-secio'
import { NOISE } from 'libp2p-noise'
import Mplex from 'libp2p-mplex'
import Boostrap from 'libp2p-bootstrap'
import Bootstrap from 'libp2p-bootstrap'
document.addEventListener('DOMContentLoaded', async () => {
// Create our libp2p node
const libp2p = await Libp2p.create({
addresses: {
// Add the signaling server address, along with our PeerId to our multiaddrs list
// libp2p will automatically attempt to dial to the signaling server so that it can
// receive inbound connections from other peers
listen: [
'/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star',
'/dns4/wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star'
]
},
modules: {
transport: [Websockets, WebRTCStar],
connEncryption: [Secio],
connEncryption: [NOISE],
streamMuxer: [Mplex],
peerDiscovery: [Boostrap]
peerDiscovery: [Bootstrap]
},
config: {
peerDiscovery: {
bootstrap: {
// The `tag` property will be searched when creating the instance of your Peer Discovery service.
// The associated object, will be passed to the service when it is instantiated.
[Bootstrap.tag]: {
enabled: true,
list: [
'/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/dns4/sfo-3.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/dns4/sgp-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/dns4/nyc-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/dns4/nyc-2.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64'
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
]
}
}
@ -43,30 +53,24 @@ document.addEventListener('DOMContentLoaded', async () => {
output.textContent += `${txt.trim()}\n`
}
// Add the signaling server address, along with our PeerId to our multiaddrs list
// libp2p will automatically attempt to dial to the signaling server so that it can
// receive inbound connections from other peers
const webrtcAddr = '/ip4/0.0.0.0/tcp/9090/wss/p2p-webrtc-star'
libp2p.peerInfo.multiaddrs.add(webrtcAddr)
// Listen for new peers
libp2p.on('peer:discovery', (peerInfo) => {
log(`Found peer ${peerInfo.id.toB58String()}`)
libp2p.on('peer:discovery', (peerId) => {
log(`Found peer ${peerId.toB58String()}`)
})
// Listen for new connections to peers
libp2p.on('peer:connect', (peerInfo) => {
log(`Connected to ${peerInfo.id.toB58String()}`)
libp2p.connectionManager.on('peer:connect', (connection) => {
log(`Connected to ${connection.remotePeer.toB58String()}`)
})
// Listen for peers disconnecting
libp2p.on('peer:disconnect', (peerInfo) => {
log(`Disconnected from ${peerInfo.id.toB58String()}`)
libp2p.connectionManager.on('peer:disconnect', (connection) => {
log(`Disconnected from ${connection.remotePeer.toB58String()}`)
})
await libp2p.start()
status.innerText = 'libp2p started!'
log(`libp2p id is ${libp2p.peerInfo.id.toB58String()}`)
log(`libp2p id is ${libp2p.peerId.toB58String()}`)
// Export libp2p to the window so you can play with the API
window.libp2p = libp2p

View File

@ -8,8 +8,8 @@
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "parcel index.html",
"server": "star-signal"
"build": "parcel build index.html",
"start": "parcel index.html"
},
"keywords": [],
"author": "",
@ -17,11 +17,11 @@
"dependencies": {
"@babel/preset-env": "^7.8.3",
"libp2p": "../../",
"libp2p-bootstrap": "^0.10.3",
"libp2p-mplex": "^0.9.3",
"libp2p-secio": "^0.12.2",
"libp2p-webrtc-star": "^0.17.3",
"libp2p-websockets": "^0.13.2"
"libp2p-bootstrap": "^0.12.1",
"libp2p-mplex": "^0.10.0",
"libp2p-noise": "^2.0.0",
"libp2p-webrtc-star": "^0.20.0",
"libp2p-websockets": "^0.14.0"
},
"devDependencies": {
"@babel/cli": "^7.8.3",

View File

@ -0,0 +1,52 @@
'use strict'
const execa = require('execa')
const { chromium } = require('playwright');
async function run() {
let url = ''
const proc = execa('parcel', ['./index.html'], {
preferLocal: true,
localDir: __dirname,
cwd: __dirname,
all: true
})
proc.all.on('data', async (chunk) => {
/**@type {string} */
const out = chunk.toString()
if (out.includes('Server running at')) {
url = out.replace('Server running at ', '')
}
if (out.includes('✨ Built in ')) {
try {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(url);
await page.waitForFunction(selector => document.querySelector(selector).innerText === 'libp2p started!', '#status')
await page.waitForFunction(
selector => {
const text = document.querySelector(selector).innerText
return text.includes('libp2p id is') &&
text.includes('Found peer') &&
text.includes('Connected to')
},
'#output',
{ timeout: 5000 }
)
await browser.close();
} catch (err) {
console.error(err)
process.exit(1)
} finally {
proc.cancel()
}
}
})
}
module.exports = run

19
examples/package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "libp2p-examples",
"version": "1.0.0",
"description": "Examples of how to use libp2p",
"scripts": {
"test": "node ./test.js",
"test:all": "node ./test-all.js"
},
"license": "MIT",
"dependencies": {
"execa": "^2.1.0",
"fs-extra": "^8.1.0",
"p-defer": "^3.0.0",
"which": "^2.0.1"
},
"devDependencies": {
"playwright": "^1.7.1"
}
}

View File

@ -4,22 +4,20 @@
const Libp2p = require('../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const { NOISE } = require('libp2p-noise')
const KadDHT = require('libp2p-kad-dht')
const delay = require('delay')
const createNode = async () => {
const peerInfo = await PeerInfo.create()
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const node = await Libp2p.create({
peerInfo,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [Mplex],
connEncryption: [SECIO],
connEncryption: [NOISE],
dht: KadDHT
},
config: {
@ -40,16 +38,19 @@ const createNode = async () => {
createNode()
])
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
await Promise.all([
node1.dial(node2.peerInfo),
node2.dial(node3.peerInfo)
node1.dial(node2.peerId),
node2.dial(node3.peerId)
])
// The DHT routing tables need a moment to populate
await delay(100)
const peer = await node1.peerRouting.findPeer(node3.peerInfo.id)
const peer = await node1.peerRouting.findPeer(node3.peerId)
console.log('Found it, multiaddrs are:')
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
peer.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${peer.id.toB58String()}`))
})();

View File

@ -4,8 +4,7 @@
const Libp2p = require('../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const { NOISE } = require('libp2p-noise')
const CID = require('cids')
const KadDHT = require('libp2p-kad-dht')
@ -13,15 +12,14 @@ const all = require('it-all')
const delay = require('delay')
const createNode = async () => {
const peerInfo = await PeerInfo.create()
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const node = await Libp2p.create({
peerInfo,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [Mplex],
connEncryption: [SECIO],
connEncryption: [NOISE],
dht: KadDHT
},
config: {
@ -42,15 +40,21 @@ const createNode = async () => {
createNode()
])
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
await Promise.all([
node1.dial(node2.peerInfo),
node2.dial(node3.peerInfo)
node1.dial(node2.peerId),
node2.dial(node3.peerId)
])
// Wait for onConnect handlers in the DHT
await delay(100)
const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
await node1.contentRouting.provide(cid)
console.log('Node %s is providing %s', node1.peerInfo.id.toB58String(), cid.toBaseEncodedString())
console.log('Node %s is providing %s', node1.peerId.toB58String(), cid.toBaseEncodedString())
// wait for propagation
await delay(300)

View File

@ -17,10 +17,13 @@ const Libp2p = require('libp2p')
const KadDHT = require('libp2p-kad-dht')
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
connEncryption: [ NOISE ],
// we add the DHT module that will enable Peer and Content Routing
dht: KadDHT
},
@ -40,18 +43,21 @@ const node1 = nodes[0]
const node2 = nodes[1]
const node3 = nodes[2]
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
await Promise.all([
node1.dial(node2.peerInfo),
node2.dial(node3.peerInfo)
node1.dial(node2.peerId),
node2.dial(node3.peerId)
])
// Set up of the cons might take time
await delay(100)
const peer = await node1.peerRouting.findPeer(node3.peerInfo.id)
const peer = await node1.peerRouting.findPeer(node3.peerId)
console.log('Found it, multiaddrs are:')
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
peer.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${peer.id.toB58String()}`))
```
You should see the output being something like:
@ -59,8 +65,8 @@ You should see the output being something like:
```Bash
> node 1.js
Found it, multiaddrs are:
/ip4/127.0.0.1/tcp/63617/p2p/QmWrFXvZr9S4iDqycyoyc2zDdrT1jg9wpdenUTdd1LTar6
/ip4/192.168.86.41/tcp/63617/p2p/QmWrFXvZr9S4iDqycyoyc2zDdrT1jg9wpdenUTdd1LTar6
/ip4/127.0.0.1/tcp/63617
/ip4/192.168.86.41/tcp/63617
```
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.
@ -75,7 +81,7 @@ Instead of calling `peerRouting.findPeer`, we will use `contentRouting.provide`
```JavaScript
await node1.contentRouting.provide(cid)
console.log('Node %s is providing %s', node1.peerInfo.id.toB58String(), cid.toBaseEncodedString())
console.log('Node %s is providing %s', node1.peerId.toB58String(), cid.toBaseEncodedString())
const provs = await all(node3.contentRouting.findProviders(cid, { timeout: 5000 }))

View File

@ -2,11 +2,7 @@
This example shows how to set up a private network of libp2p nodes.
## Setup
Install dependencies:
```
npm install
```
1. Install the modules in the libp2p root directory, `npm install`.
## Run
Running the example will cause two nodes with the same swarm key to be started and exchange basic information.

View File

@ -1,18 +1,17 @@
/* eslint no-console: ["off"] */
'use strict'
const { Buffer } = require('buffer')
const { generate } = require('libp2p/src/pnet')
const privateLibp2pNode = require('./libp2p-node')
const pipe = require('it-pipe')
// Create a buffer and write the swarm key to it
const swarmKey = Buffer.alloc(95)
// Create a Uint8Array and write the swarm key to it
const swarmKey = new Uint8Array(95)
generate(swarmKey)
// This key is for testing a different key not working
const otherSwarmKey = Buffer.alloc(95)
const otherSwarmKey = new Uint8Array(95)
generate(otherSwarmKey)
;(async () => {
@ -29,7 +28,9 @@ generate(otherSwarmKey)
console.log('nodes started...')
await node1.dial(node2.peerInfo)
// Add node 2 data to node1's PeerStore
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
await node1.dial(node2.peerId)
node2.handle('/private', ({ stream }) => {
pipe(
@ -42,7 +43,7 @@ generate(otherSwarmKey)
)
})
const { stream } = await node1.dialProtocol(node2.peerInfo, '/private')
const { stream } = await node1.dialProtocol(node2.peerId, '/private')
await pipe(
['This message is sent on a private network'],

View File

@ -3,33 +3,35 @@
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const Protector = require('libp2p/src/pnet')
/**
* privateLibp2pNode returns a libp2p node function that will use the swarm
* key at the given `swarmKeyPath` to create the Protector
* key with the given `swarmKey` to create the Protector
*
* @param {Buffer} swarmKey
* @param {Uint8Array} swarmKey
* @returns {Promise<libp2p>} Returns a libp2pNode function for use in IPFS creation
*/
const privateLibp2pNode = async (swarmKeyPath) => {
const privateLibp2pNode = async (swarmKey) => {
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP], // We're only using the TCP transport for this example
streamMuxer: [MPLEX], // We're only using mplex muxing
// Let's make sure to use identifying crypto in our pnet since the protector doesn't
// care about node identity, and only the presence of private keys
connEncryption: [SECIO],
connEncryption: [NOISE],
// Leave peer discovery empty, we don't want to find peers. We could omit the property, but it's
// being left in for explicit readability.
// We should explicitly dial pnet peers, or use a custom discovery service for finding nodes in our pnet
peerDiscovery: [],
connProtector: new Protector(swarmKeyPath)
connProtector: new Protector(swarmKey)
}
})
node.peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
return node
}

View File

@ -1,19 +0,0 @@
{
"name": "pnet-ipfs-example",
"version": "1.0.0",
"description": "An example of private networking with IPFS",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"libp2p": "../..",
"libp2p-mplex": "^0.9.3",
"libp2p-secio": "^0.12.1",
"libp2p-tcp": "^0.14.2"
}
}

30
examples/pnet/test.js Normal file
View File

@ -0,0 +1,30 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pDefer = require('p-defer')
const uint8ArrayToString = require('uint8arrays/to-string')
async function test () {
const messageReceived = pDefer()
process.stdout.write('index.js\n')
const proc = execa('node', [path.join(__dirname, 'index.js')], {
cwd: path.resolve(__dirname),
all: true
})
proc.all.on('data', async (data) => {
process.stdout.write(data)
const s = uint8ArrayToString(data)
if (s.includes('This message is sent on a private network')) {
messageReceived.resolve()
}
})
await messageReceived.promise
proc.kill()
}
module.exports = test

View File

@ -3,21 +3,19 @@
const Libp2p = require('../../')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const { NOISE } = require('libp2p-noise')
const pipe = require('it-pipe')
const createNode = async () => {
const peerInfo = await PeerInfo.create()
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const node = await Libp2p.create({
peerInfo,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO]
connEncryption: [NOISE]
}
})
@ -32,6 +30,9 @@ const createNode = async () => {
createNode()
])
// Add node's 2 data to the PeerStore
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
// exact matching
node2.handle('/your-protocol', ({ stream }) => {
pipe(
@ -62,14 +63,14 @@ const createNode = async () => {
})
*/
const { stream } = await node1.dialProtocol(node2.peerInfo, ['/your-protocol'])
const { stream } = await node1.dialProtocol(node2.peerId, ['/your-protocol'])
await pipe(
['my own protocol, wow!'],
stream
)
/*
const { stream } = node1.dialProtocol(node2.peerInfo, ['/another-protocol/1.0.0'])
const { stream } = node1.dialProtocol(node2.peerId, ['/another-protocol/1.0.0'])
await pipe(
['my own protocol, wow!'],

View File

@ -3,21 +3,19 @@
const Libp2p = require('../../')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const { NOISE } = require('libp2p-noise')
const pipe = require('it-pipe')
const createNode = async () => {
const peerInfo = await PeerInfo.create()
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const node = await Libp2p.create({
peerInfo,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO]
connEncryption: [NOISE]
}
})
@ -32,6 +30,9 @@ const createNode = async () => {
createNode()
])
// Add node's 2 data to the PeerStore
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
node2.handle(['/a', '/b'], ({ protocol, stream }) => {
pipe(
stream,
@ -43,19 +44,19 @@ const createNode = async () => {
)
})
const { stream: stream1 } = await node1.dialProtocol(node2.peerInfo, ['/a'])
const { stream: stream1 } = await node1.dialProtocol(node2.peerId, ['/a'])
await pipe(
['protocol (a)'],
stream1
)
const { stream: stream2 } = await node1.dialProtocol(node2.peerInfo, ['/b'])
const { stream: stream2 } = await node1.dialProtocol(node2.peerId, ['/b'])
await pipe(
['protocol (b)'],
stream2
)
const { stream: stream3 } = await node1.dialProtocol(node2.peerInfo, ['/b'])
const { stream: stream3 } = await node1.dialProtocol(node2.peerId, ['/b'])
await pipe(
['another stream on protocol (b)'],
stream3

View File

@ -4,21 +4,19 @@
const Libp2p = require('../../')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const { NOISE } = require('libp2p-noise')
const pipe = require('it-pipe')
const createNode = async () => {
const peerInfo = await PeerInfo.create()
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const node = await Libp2p.create({
peerInfo,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [SECIO]
connEncryption: [NOISE]
}
})
@ -33,6 +31,9 @@ const createNode = async () => {
createNode()
])
// Add node's 2 data to the PeerStore
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
node1.handle('/node-1', ({ stream }) => {
pipe(
stream,
@ -55,13 +56,13 @@ const createNode = async () => {
)
})
const { stream: stream1 } = await node1.dialProtocol(node2.peerInfo, ['/node-2'])
const { stream: stream1 } = await node1.dialProtocol(node2.peerId, ['/node-2'])
await pipe(
['from 1 to 2'],
stream1
)
const { stream: stream2 } = await node2.dialProtocol(node1.peerInfo, ['/node-1'])
const { stream: stream2 } = await node2.dialProtocol(node1.peerId, ['/node-1'])
await pipe(
['from 2 to 1'],
stream2

View File

@ -6,7 +6,7 @@ The feature of agreeing on a protocol over an established connection is what we
# 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`, `it-pipe`, `it-buffer` and `streaming-iterables`. This example reuses the base left by the [Transports](../transports) example. You can see the complete solution at [1.js](./1.js).
Let's see _protocol multiplexing_ in action! You will need the following modules for this example: `libp2p`, `libp2p-tcp`, `peer-id`, `it-pipe`, `it-buffer` and `streaming-iterables`. 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.
@ -19,6 +19,9 @@ const { toBuffer } = require('it-buffer')
const node1 = nodes[0]
const node2 = nodes[1]
// Add node's 2 data to the PeerStore
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
// Here we are telling libp2p that if someone dials this node to talk with the `/your-protocol`
// multicodec, the protocol identifier, please call this handler and give it the stream
// so that incomming data can be handled
@ -37,7 +40,7 @@ node2.handle('/your-protocol', ({ stream }) => {
After the protocol is _handled_, now we can dial to it.
```JavaScript
const { stream } = await node1.dialProtocol(node2.peerInfo, ['/your-protocol'])
const { stream } = await node1.dialProtocol(node2.peerId, ['/your-protocol'])
await pipe(
['my own protocol, wow!'],
@ -59,7 +62,7 @@ node2.handle('/another-protocol/1.0.1', ({ stream }) => {
)
})
// ...
const { stream } = await node1.dialProtocol(node2.peerInfo, ['/another-protocol/1.0.0'])
const { stream } = await node1.dialProtocol(node2.peerId, ['/another-protocol/1.0.0'])
await pipe(
['my own protocol, wow!'],
@ -128,19 +131,19 @@ node2.handle(['/a', '/b'], ({ protocol, stream }) => {
)
})
const { stream } = await node1.dialProtocol(node2.peerInfo, ['/a'])
const { stream } = await node1.dialProtocol(node2.peerId, ['/a'])
await pipe(
['protocol (a)'],
stream
)
const { stream: stream2 } = await node1.dialProtocol(node2.peerInfo, ['/b'])
const { stream: stream2 } = await node1.dialProtocol(node2.peerId, ['/b'])
await pipe(
['protocol (b)'],
stream2
)
const { stream: stream3 } = await node1.dialProtocol(node2.peerInfo, ['/b'])
const { stream: stream3 } = await node1.dialProtocol(node2.peerId, ['/b'])
await pipe(
['another stream on protocol (b)'],
stream3

View File

@ -1,24 +1,23 @@
/* eslint-disable no-console */
'use strict'
const { Buffer } = require('buffer')
const Libp2p = require('../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const { NOISE } = require('libp2p-noise')
const Gossipsub = require('libp2p-gossipsub')
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayToString = require('uint8arrays/to-string')
const createNode = async () => {
const peerInfo = await PeerInfo.create()
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const node = await Libp2p.create({
peerInfo,
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [Mplex],
connEncryption: [SECIO],
connEncryption: [NOISE],
pubsub: Gossipsub
}
})
@ -35,18 +34,23 @@ const createNode = async () => {
createNode()
])
await node1.dial(node2.peerInfo)
// Add node's 2 data to the PeerStore
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
await node1.dial(node2.peerId)
await node1.pubsub.subscribe(topic, (msg) => {
console.log(`node1 received: ${msg.data.toString()}`)
node1.pubsub.on(topic, (msg) => {
console.log(`node1 received: ${uint8ArrayToString(msg.data)}`)
})
await node1.pubsub.subscribe(topic)
await node2.pubsub.subscribe(topic, (msg) => {
console.log(`node2 received: ${msg.data.toString()}`)
// Will not receive own published messages by default
node2.pubsub.on(topic, (msg) => {
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`)
})
await node2.pubsub.subscribe(topic)
// node2 publishes "news" every second
setInterval(() => {
node2.pubsub.publish(topic, Buffer.from('Bird bird bird, bird is the word!'))
node2.pubsub.publish(topic, uint8ArrayFromString('Bird bird bird, bird is the word!'))
}, 1000)
})()

View File

@ -1,6 +1,6 @@
# Publish Subscribe
Publish Subscribe is also included on the stack. Currently, we have two PubSub implementation available [libp2p-floodsub](https://github.com/libp2p/js-libp2p-floodsub) and [libp2p-gossipsub](https://github.com/ChainSafe/gossipsub-js), with many more being researched at [research-pubsub](https://github.com/libp2p/research-pubsub).
Publish Subscribe is also included on the stack. Currently, we have two PubSub implementation available [libp2p-floodsub](https://github.com/libp2p/js-libp2p-floodsub) and [libp2p-gossipsub](https://github.com/ChainSafe/js-libp2p-gossipsub), 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:
@ -21,10 +21,13 @@ const Libp2p = require('libp2p')
const Gossipsub = require('libp2p-gossipsub')
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ SECIO ],
connEncryption: [ NOISE ],
// we add the Pubsub module we want
pubsub: Gossipsub
}
@ -39,19 +42,24 @@ const topic = 'news'
const node1 = nodes[0]
const node2 = nodes[1]
await node1.dial(node2.peerInfo)
// Add node's 2 data to the PeerStore
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
await node1.dial(node2.peerId)
await node1.pubsub.subscribe(topic, (msg) => {
console.log(`node1 received: ${msg.data.toString()}`)
node1.pubsub.on(topic, (msg) => {
console.log(`node1 received: ${uint8ArrayToString(msg.data)}`)
})
await node1.pubsub.subscribe(topic)
await node2.pubsub.subscribe(topic, (msg) => {
console.log(`node2 received: ${msg.data.toString()}`)
// Will not receive own published messages by default
node2.pubsub.on(topic, (msg) => {
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`)
})
await node2.pubsub.subscribe(topic)
// node2 publishes "news" every second
setInterval(() => {
node2.pubsub.publish(topic, Buffer.from('Bird bird bird, bird is the word!'))
node2.pubsub.publish(topic, uint8ArrayFromString('Bird bird bird, bird is the word!'))
}, 1000)
```
@ -60,25 +68,34 @@ The output of the program should look like:
```
> node 1.js
connected to QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82
node2 received: Bird bird bird, bird is the word!
node1 received: Bird bird bird, bird is the word!
node2 received: Bird bird bird, bird is the word!
node1 received: Bird bird bird, bird is the word!
```
You can change the pubsub `emitSelf` option if you don't want the publishing node to receive its own messages.
You can change the pubsub `emitSelf` option if you want the publishing node to receive its own messages.
```JavaScript
const defaults = {
config: {
pubsub: {
enabled: true,
emitSelf: false
emitSelf: true
}
}
}
```
The output of the program should look like:
```
> node 1.js
connected to QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82
node1 received: Bird bird bird, bird is the word!
node2 received: Bird bird bird, bird is the word!
node1 received: Bird bird bird, bird is the word!
node2 received: 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:

View File

@ -0,0 +1,88 @@
/* eslint-disable no-console */
'use strict'
const Libp2p = require('../../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const Gossipsub = require('libp2p-gossipsub')
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayToString = require('uint8arrays/to-string')
const createNode = async () => {
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [Mplex],
connEncryption: [NOISE],
pubsub: Gossipsub
}
})
await node.start()
return node
}
(async () => {
const topic = 'fruit'
const [node1, node2, node3] = await Promise.all([
createNode(),
createNode(),
createNode(),
])
// node1 conect to node2 and node2 conect to node3
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
await node1.dial(node2.peerId)
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
await node2.dial(node3.peerId)
//subscribe
node1.pubsub.on(topic, (msg) => {
// Will not receive own published messages by default
console.log(`node1 received: ${uint8ArrayToString(msg.data)}`)
})
await node1.pubsub.subscribe(topic)
node2.pubsub.on(topic, (msg) => {
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`)
})
await node2.pubsub.subscribe(topic)
node3.pubsub.on(topic, (msg) => {
console.log(`node3 received: ${uint8ArrayToString(msg.data)}`)
})
await node3.pubsub.subscribe(topic)
const validateFruit = (msgTopic, msg) => {
const fruit = uint8ArrayToString(msg.data)
const validFruit = ['banana', 'apple', 'orange']
if (!validFruit.includes(fruit)) {
throw new Error('no valid fruit received')
}
}
//validate fruit
node1.pubsub.topicValidators.set(topic, validateFruit)
node2.pubsub.topicValidators.set(topic, validateFruit)
node3.pubsub.topicValidators.set(topic, validateFruit)
// node1 publishes "fruits" every five seconds
var count = 0;
const myFruits = ['banana', 'apple', 'car', 'orange'];
// car is not a fruit !
setInterval(() => {
console.log('############## fruit ' + myFruits[count] + ' ##############')
node1.pubsub.publish(topic, uint8ArrayFromString(myFruits[count]))
count++
if (count == myFruits.length) {
count = 0
}
}, 5000)
})()

View File

@ -0,0 +1,110 @@
# Filter Messages
To prevent undesired data from being propagated on the network, we can apply a filter to Gossipsub. Messages that fail validation in the filter will not be re-shared.
## 1. Setting up a PubSub network with three nodes
First, let's update our libp2p configuration with a pubsub implementation.
```JavaScript
const Libp2p = require('libp2p')
const Gossipsub = require('libp2p-gossipsub')
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ NOISE ],
pubsub: Gossipsub
}
})
```
Then, create three nodes and connect them together. In this example, we will connect the nodes in series. Node 1 connected with node 2 and node 2 connected with node 3.
```JavaScript
const [node1, node2, node3] = await Promise.all([
createNode(),
createNode(),
createNode(),
])
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
await node1.dial(node2.peerId)
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
await node2.dial(node3.peerId)
```
Now we' can subscribe to the fruit topic and log incoming messages.
```JavaScript
const topic = 'fruit'
node1.pubsub.on(topic, (msg) => {
console.log(`node1 received: ${uint8ArrayToString(msg.data)}`)
})
await node1.pubsub.subscribe(topic)
node2.pubsub.on(topic, (msg) => {
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`)
})
await node2.pubsub.subscribe(topic)
node3.pubsub.on(topic, (msg) => {
console.log(`node3 received: ${uint8ArrayToString(msg.data)}`)
})
await node3.pubsub.subscribe(topic)
```
Finally, let's define the additional filter in the fruit topic.
```JavaScript
const validateFruit = (msgTopic, msg) => {
const fruit = uint8ArrayToString(msg.data)
const validFruit = ['banana', 'apple', 'orange']
if (!validFruit.includes(fruit)) {
throw new Error('no valid fruit received')
}
}
node1.pubsub.topicValidators.set(topic, validateFruit)
node2.pubsub.topicValidators.set(topic, validateFruit)
node3.pubsub.topicValidators.set(topic, validateFruit)
```
In this example, node one has an outdated version of the system, or is a malicious node. When it tries to publish fruit, the messages are re-shared and all the nodes share the message. However, when it tries to publish a vehicle the message is not re-shared.
```JavaScript
var count = 0;
const myFruits = ['banana', 'apple', 'car', 'orange'];
setInterval(() => {
console.log('############## fruit ' + myFruits[count] + ' ##############')
node1.pubsub.publish(topic, new TextEncoder().encode(myFruits[count]))
count++
if (count == myFruits.length) {
count = 0
}
}, 5000)
```
Result
```
> node 1.js
############## fruit banana ##############
node2 received: banana
node3 received: banana
############## fruit apple ##############
node2 received: apple
node3 received: apple
############## fruit car ##############
############## fruit orange ##############
node1 received: orange
node2 received: orange
node3 received: orange
```

33
examples/test-all.js Normal file
View File

@ -0,0 +1,33 @@
'use strict'
process.on('unhandedRejection', (err) => {
console.error(err)
process.exit(1)
})
const path = require('path')
const fs = require('fs')
const {
waitForOutput
} = require('./utils')
async function testAll () {
for (const dir of fs.readdirSync(__dirname)) {
if (dir === 'node_modules' || dir === 'tests_output') {
continue
}
const stats = fs.statSync(path.join(__dirname, dir))
if (!stats.isDirectory()) {
continue
}
await waitForOutput('npm info ok', 'npm', ['test', '--', dir], {
cwd: __dirname
})
}
}
testAll()

94
examples/test.js Normal file
View File

@ -0,0 +1,94 @@
'use strict'
process.env.NODE_ENV = 'test'
process.env.CI = true // needed for some "clever" build tools
const fs = require('fs-extra')
const path = require('path')
const execa = require('execa')
const dir = path.join(__dirname, process.argv[2])
testExample(dir)
.then(() => {}, (err) => {
if (err.exitCode) {
process.exit(err.exitCode)
}
console.error(err)
process.exit(1)
})
async function testExample (dir) {
await installDeps(dir)
await build(dir)
await runTest(dir)
}
async function installDeps (dir) {
if (!fs.existsSync(path.join(dir, 'package.json'))) {
console.info('Nothing to install in', dir)
return
}
if (fs.existsSync(path.join(dir, 'node_modules'))) {
console.info('Dependencies already installed in', dir)
return
}
const proc = execa.command('npm install', {
cwd: dir
})
proc.all.on('data', (data) => {
process.stdout.write(data)
})
await proc
}
async function build (dir) {
const pkgJson = path.join(dir, 'package.json')
if (!fs.existsSync(pkgJson)) {
console.info('Nothing to build in', dir)
return
}
const pkg = require(pkgJson)
let build
if (pkg.scripts.bundle) {
build = 'bundle'
}
if (pkg.scripts.build) {
build = 'build'
}
if (!build) {
console.info('No "build" or "bundle" script in', pkgJson)
return
}
const proc = execa('npm', ['run', build], {
cwd: dir
})
proc.all.on('data', (data) => {
process.stdout.write(data)
})
await proc
}
async function runTest (dir) {
console.info('Running node tests in', dir)
const testFile = path.join(dir, 'test.js')
if (!fs.existsSync(testFile)) {
console.info('Nothing to test in', dir)
return
}
const test = require(testFile)
await test()
}

View File

@ -3,19 +3,18 @@
const Libp2p = require('../..')
const TCP = require('libp2p-tcp')
const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const createNode = async (peerInfo) => {
// 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')
const { NOISE } = require('libp2p-noise')
const createNode = async () => {
const node = await Libp2p.create({
peerInfo,
addresses: {
// To signal the addresses we want to be available, we use
// the multiaddr format, a self describable address
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
connEncryption: [SECIO]
connEncryption: [NOISE]
}
})
@ -24,10 +23,9 @@ const createNode = async (peerInfo) => {
}
;(async () => {
const peerInfo = await PeerInfo.create()
const node = await createNode(peerInfo)
const node = await createNode()
console.log('node has started (true/false):', node.isStarted())
console.log('listening on:')
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
})();

View File

@ -3,23 +3,22 @@
const Libp2p = require('../..')
const TCP = require('libp2p-tcp')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const PeerInfo = require('peer-info')
const pipe = require('it-pipe')
const concat = require('it-concat')
const createNode = async (peerInfo) => {
// 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')
const createNode = async () => {
const node = await Libp2p.create({
peerInfo,
addresses: {
// To signal the addresses we want to be available, we use
// the multiaddr format, a self describable address
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
connEncryption: [SECIO],
connEncryption: [NOISE],
streamMuxer: [MPLEX]
}
})
@ -30,17 +29,13 @@ const createNode = async (peerInfo) => {
function printAddrs (node, number) {
console.log('node %s is listening on:', number)
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
}
;(async () => {
const [peerInfo1, peerInfo2] = await Promise.all([
PeerInfo.create(),
PeerInfo.create()
])
const [node1, node2] = await Promise.all([
createNode(peerInfo1),
createNode(peerInfo2)
createNode(),
createNode()
])
printAddrs(node1, '1')
@ -54,7 +49,8 @@ function printAddrs (node, number) {
console.log(result.toString())
})
const { stream } = await node1.dialProtocol(node2.peerInfo, '/print')
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
const { stream } = await node1.dialProtocol(node2.peerId, '/print')
await pipe(
['Hello', ' ', 'p2p', ' ', 'world', '!'],

View File

@ -4,24 +4,23 @@
const Libp2p = require('../..')
const TCP = require('libp2p-tcp')
const WebSockets = require('libp2p-websockets')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const PeerInfo = require('peer-info')
const pipe = require('it-pipe')
const createNode = async (peerInfo, transports, multiaddrs = []) => {
if (!Array.isArray(multiaddrs)) {
multiaddrs = [multiaddrs]
const createNode = async (transports, addresses = []) => {
if (!Array.isArray(addresses)) {
addresses = [addresses]
}
multiaddrs.forEach((addr) => peerInfo.multiaddrs.add(addr))
const node = await Libp2p.create({
peerInfo,
addresses: {
listen: addresses
},
modules: {
transport: transports,
connEncryption: [SECIO],
connEncryption: [NOISE],
streamMuxer: [MPLEX]
}
})
@ -32,7 +31,7 @@ const createNode = async (peerInfo, transports, multiaddrs = []) => {
function printAddrs(node, number) {
console.log('node %s is listening on:', number)
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
}
function print ({ stream }) {
@ -47,15 +46,10 @@ function print ({ stream }) {
}
;(async () => {
const [peerInfo1, peerInfo2, peerInfo3] = await Promise.all([
PeerInfo.create(),
PeerInfo.create(),
PeerInfo.create()
])
const [node1, node2, node3] = await Promise.all([
createNode(peerInfo1, [TCP], '/ip4/0.0.0.0/tcp/0'),
createNode(peerInfo2, [TCP, WebSockets], ['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws']),
createNode(peerInfo3, [WebSockets], '/ip4/127.0.0.1/tcp/20000/ws')
createNode([TCP], '/ip4/0.0.0.0/tcp/0'),
createNode([TCP, WebSockets], ['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws']),
createNode([WebSockets], '/ip4/127.0.0.1/tcp/20000/ws')
])
printAddrs(node1, '1')
@ -66,15 +60,19 @@ function print ({ stream }) {
node2.handle('/print', print)
node3.handle('/print', print)
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
node3.peerStore.addressBook.set(node1.peerId, node1.multiaddrs)
// node 1 (TCP) dials to node 2 (TCP+WebSockets)
const { stream } = await node1.dialProtocol(node2.peerInfo, '/print')
const { stream } = await node1.dialProtocol(node2.peerId, '/print')
await pipe(
['node 1 dialed to node 2 successfully'],
stream
)
// node 2 (TCP+WebSockets) dials to node 2 (WebSockets)
const { stream: stream2 } = await node2.dialProtocol(node3.peerInfo, '/print')
const { stream: stream2 } = await node2.dialProtocol(node3.peerId, '/print')
await pipe(
['node 2 dialed to node 3 successfully'],
stream2
@ -82,7 +80,7 @@ function print ({ stream }) {
// node 3 (listening WebSockets) can dial node 1 (TCP)
try {
await node3.dialProtocol(node1.peerInfo, '/print')
await node3.dialProtocol(node1.peerId, '/print')
} catch (err) {
console.log('node 3 failed to dial to node 1 with:', err.message)
}

View File

@ -13,10 +13,10 @@ When using libp2p, you need properly configure it, that is, pick your set of mod
You will need 4 dependencies total, so go ahead and install all of them with:
```bash
> npm install libp2p libp2p-tcp libp2p-secio peer-info
> npm install libp2p libp2p-tcp libp2p-noise
```
Then, on your favorite text editor create a file with the `.js` extension. I've called mine `1.js`.
Then, in your favorite text editor create a file with the `.js` extension. I've called mine `1.js`.
First thing is to create our own libp2p node! Insert:
@ -25,18 +25,18 @@ First thing is to create our own libp2p node! Insert:
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const SECIO = require('libp2p-secio')
const createNode = async (peerInfo) => {
// 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')
const { NOISE } = require('libp2p-noise')
const createNode = async () => {
const node = await Libp2p.create({
peerInfo,
addresses: {
// To signal the addresses we want to be available, we use
// the multiaddr format, a self describable address
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [ TCP ],
connEncryption: [ SECIO ]
connEncryption: [ NOISE ]
}
})
@ -48,8 +48,7 @@ const createNode = async (peerInfo) => {
Now that we have a function to create our own libp2p node, let's create a node with it.
```JavaScript
const peerInfo = await PeerInfo.create()
const node = await createNode(peerInfo)
const node = await createNode()
// At this point the node has started
console.log('node has started (true/false):', node.isStarted())
@ -59,7 +58,7 @@ console.log('node has started (true/false):', node.isStarted())
// 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()))
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
```
Running this should result in something like:
@ -78,36 +77,53 @@ That `QmW2cKTakTYqbQkUzBTEGXgWYFj1YEPeUndE1YWs6CBzDQ` is the PeerId that was cre
Now that we have our `createNode` function, 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.
For this step, we will need some more dependencies.
```bash
> npm install it-pipe it-buffer
> npm install it-pipe it-concat libp2p-mplex
```
And we also need to import the module on our .js file:
And we also need to import the modules on our .js file:
```js
const pipe = require('it-pipe')
const { toBuffer } = require('it-buffer')
const concat = require('it-concat')
const MPLEX = require('libp2p-mplex')
```
We are going to reuse the `createNode` function from step 1, but this time to make things simpler, we will create another function to print the addrs to avoid duplicating code.
We are going to reuse the `createNode` function from step 1, but this time add a stream multiplexer from `libp2p-mplex`.
```js
const createNode = async () => {
const node = await Libp2p.create({
addresses: {
// To signal the addresses we want to be available, we use
// the multiaddr format, a self describable address
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
connEncryption: [NOISE],
streamMuxer: [MPLEX] // <--- Add this line
}
})
await node.start()
return node
}
```
We will also make things simpler by creating another function to print the multiaddresses to avoid duplicating code.
```JavaScript
function printAddrs (node, number) {
console.log('node %s is listening on:', number)
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
}
```
Then,
Then add,
```js
;(async () => {
const [peerInfo1, peerInfo2] = await Promise.all([
PeerInfo.create(),
PeerInfo.create()
])
const [node1, node2] = await Promise.all([
createNode(),
createNode()
@ -116,18 +132,16 @@ Then,
printAddrs(node1, '1')
printAddrs(node2, '2')
node2.handle('/print', ({ stream }) => {
pipe(
node2.handle('/print', async ({ stream }) => {
const result = await pipe(
stream,
async function (source) {
for await (const msg of source) {
console.log(msg.toString())
}
}
concat
)
console.log(result.toString())
})
const { stream } = await node1.dialProtocol(node2.peerInfo, '/print')
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
const { stream } = await node1.dialProtocol(node2.peerId, '/print')
await pipe(
['Hello', ' ', 'p2p', ' ', 'world', '!'],
@ -135,8 +149,9 @@ Then,
)
})();
```
For more information refer to the [docs](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md).
The result should be look like:
The result should look like:
```bash
> node 2.js
@ -151,33 +166,34 @@ 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.
Next, we want nodes to have multiple transports available to increase their chances of having a common transport in the network to communicate over. A simple scenario is a node running in the browser only having 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).
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:
In this example, we will need to also install `libp2p-websockets`:
```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 `createNode` function to contemplate WebSockets as well. Moreover, let's upgrade our function to enable us to pick the addrs in which a node will start a listener:
We want to create 3 nodes: one with TCP, one with TCP+WebSockets and one with just WebSockets. We need to update our `createNode` function to accept WebSocket connections as well. Moreover, let's upgrade our function to enable us to pick the addresses over which a node will start a listener:
```JavaScript
// ...
const createNode = async (peerInfo, transports, multiaddrs = []) => {
if (!Array.isArray(multiaddrs)) {
multiaddrs = [multiaddrs]
const createNode = async (transports, addresses = []) => {
if (!Array.isArray(addresses)) {
addresses = [addresses]
}
multiaddrs.forEach((addr) => peerInfo.multiaddrs.add(addr))
const node = await Libp2p.create({
peerInfo,
addresses: {
listen: addresses
},
modules: {
transport: transports,
connEncryption: [ SECIO ]
connEncryption: [NOISE],
streamMuxer: [MPLEX]
}
})
@ -194,15 +210,10 @@ Let's update our flow to create nodes and see how they behave when dialing to ea
const WebSockets = require('libp2p-websockets')
const TCP = require('libp2p-tcp')
const [peerInfo1, peerInfo2, peerInfo3] = await Promise.all([
PeerInfo.create(),
PeerInfo.create(),
PeerInfo.create()
])
const [node1, node2, node3] = await Promise.all([
createNode(peerInfo1, [TCP], '/ip4/0.0.0.0/tcp/0'),
createNode(peerInfo2, [TCP, WebSockets], ['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws']),
createNode(peerInfo3, [WebSockets], '/ip4/127.0.0.1/tcp/20000/ws')
createNode([TCP], '/ip4/0.0.0.0/tcp/0'),
createNode([TCP, WebSockets], ['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws']),
createNode([WebSockets], '/ip4/127.0.0.1/tcp/20000/ws')
])
printAddrs(node1, '1')
@ -213,15 +224,19 @@ node1.handle('/print', print)
node2.handle('/print', print)
node3.handle('/print', print)
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
node3.peerStore.addressBook.set(node1.peerId, node1.multiaddrs)
// node 1 (TCP) dials to node 2 (TCP+WebSockets)
const { stream } = await node1.dialProtocol(node2.peerInfo, '/print')
const { stream } = await node1.dialProtocol(node2.peerId, '/print')
await pipe(
['node 1 dialed to node 2 successfully'],
stream
)
// node 2 (TCP+WebSockets) dials to node 2 (WebSockets)
const { stream: stream2 } = await node2.dialProtocol(node3.peerInfo, '/print')
const { stream: stream2 } = await node2.dialProtocol(node3.peerId, '/print')
await pipe(
['node 2 dialed to node 3 successfully'],
stream2
@ -229,13 +244,13 @@ await pipe(
// node 3 (WebSockets) attempts to dial to node 1 (TCP)
try {
await node3.dialProtocol(node1.peerInfo, '/print')
await node3.dialProtocol(node1.peerId, '/print')
} catch (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:
`print` is a function that prints each piece of data from a stream onto a new line but factored into its own function to save lines:
```JavaScript
function print ({ stream }) {
@ -250,7 +265,7 @@ function print ({ stream }) {
}
```
If everything was set correctly, you now should see the following after you run the script:
If everything was set correctly, you now should see something similar to the following after running the script:
```Bash
> node 3.js
@ -269,13 +284,13 @@ node 3 failed to dial to node 1 with:
Error: No transport available for address /ip4/127.0.0.1/tcp/51482
```
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.
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 several transports available and plenty to come, you can find these at [interface-transport implementations](https://github.com/libp2p/js-interfaces/tree/master/src/transport#modules-that-implement-the-interface) list.
Today there are already several transports available and plenty to come. You can find these at [interface-transport implementations](https://github.com/libp2p/js-interfaces/tree/master/src/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/js-interfaces/tree/master/src/transport#api) it will be able to use it.
Adding more transports is done through the same way as you added TCP and WebSockets. Some transports might offer extra functionalities, but as far as libp2p is concerned, if it follows the interface defined in the [spec](https://github.com/libp2p/js-interfaces/tree/master/src/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.

61
examples/utils.js Normal file
View File

@ -0,0 +1,61 @@
'use strict'
const execa = require('execa')
const fs = require('fs-extra')
const which = require('which')
async function isExecutable (command) {
try {
await fs.access(command, fs.constants.X_OK)
return true
} catch (err) {
if (err.code === 'ENOENT') {
return isExecutable(await which(command))
}
if (err.code === 'EACCES') {
return false
}
throw err
}
}
async function waitForOutput (expectedOutput, command, args = [], opts = {}) {
if (!await isExecutable(command)) {
args.unshift(command)
command = 'node'
}
const proc = execa(command, args, opts)
let output = ''
let time = 120000
let timeout = setTimeout(() => {
throw new Error(`Did not see "${expectedOutput}" in output from "${[command].concat(args).join(' ')}" after ${time/1000}s`)
}, time)
proc.all.on('data', (data) => {
process.stdout.write(data)
output += data.toString('utf8')
if (output.includes(expectedOutput)) {
clearTimeout(timeout)
proc.kill()
}
})
try {
await proc
} catch (err) {
if (!err.killed) {
throw err
}
}
}
module.exports = {
waitForOutput
}

View File

@ -22,7 +22,7 @@
["libp2p/js-libp2p-websockets", "libp2p-websockets"],
"secure channels",
["libp2p/js-libp2p-secio", "libp2p-secio"],
["NodeFactoryIo/js-libp2p-noise", "libp2p-noise"],
"stream multiplexers",
["libp2p/js-libp2p-mplex", "libp2p-mplex"],
@ -32,7 +32,7 @@
["libp2p/js-libp2p-kad-dht", "libp2p-kad-dht"],
["libp2p/js-libp2p-mdns", "libp2p-mdns"],
["libp2p/js-libp2p-webrtc-star", "libp2p-webrtc-star"],
["ChainSafe/discv5", "discv5"],
["ChainSafe/discv5", "@chainsafe/discv5"],
"content routing",
["libp2p/js-libp2p-delegated-content-routing", "libp2p-delegated-content-routing"],
@ -48,12 +48,10 @@
"data types",
["libp2p/js-peer-id", "peer-id"],
["libp2p/js-peer-info", "peer-info"],
"pubsub",
["libp2p/js-libp2p-pubsub", "libp2p-pubsub"],
["libp2p/js-libp2p-floodsub", "libp2p-floodsub"],
["ChainSafe/gossipsub-js", "libp2p-gossipsub"],
["ChainSafe/js-libp2p-gossipsub", "libp2p-gossipsub"],
"extensions",
["libp2p/js-libp2p-nat-mgnr", "libp2p-nat-mgnr"],

View File

@ -1,9 +1,18 @@
{
"name": "libp2p",
"version": "0.27.8",
"version": "0.30.6",
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js",
"types": "dist/src/index.d.ts",
"typesVersions": {
"*": {
"src/*": [
"dist/src/*",
"dist/src/*/index"
]
}
},
"files": [
"dist",
"src"
@ -14,6 +23,7 @@
"test": "npm run test:node && npm run test:browser",
"test:node": "aegir test -t node -f \"./test/**/*.{node,spec}.js\"",
"test:browser": "aegir test -t browser",
"test:examples": "cd examples && npm run test:all",
"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",
@ -37,117 +47,152 @@
"homepage": "https://libp2p.io",
"license": "MIT",
"engines": {
"node": ">=10.0.0",
"node": ">=12.0.0",
"npm": ">=6.0.0"
},
"browser": {
"@motrix/nat-api": false
},
"dependencies": {
"@motrix/nat-api": "^0.3.1",
"abort-controller": "^3.0.0",
"aggregate-error": "^3.0.1",
"any-signal": "^1.1.0",
"bignumber.js": "^9.0.0",
"aggregate-error": "^3.1.0",
"any-signal": "^2.1.1",
"bignumber.js": "^9.0.1",
"cids": "^1.1.5",
"class-is": "^1.1.0",
"debug": "^4.1.1",
"debug": "^4.3.1",
"err-code": "^2.0.0",
"events": "^3.1.0",
"events": "^3.2.0",
"hashlru": "^2.3.0",
"ipfs-utils": "^2.2.0",
"it-all": "^1.0.1",
"interface-datastore": "^3.0.3",
"ipfs-utils": "^6.0.0",
"it-all": "^1.0.4",
"it-buffer": "^0.1.2",
"it-handshake": "^1.0.1",
"it-length-prefixed": "^3.0.1",
"it-drain": "^1.0.3",
"it-filter": "^1.0.1",
"it-first": "^1.0.4",
"it-handshake": "^1.0.2",
"it-length-prefixed": "^3.1.0",
"it-map": "^1.0.4",
"it-merge": "1.0.0",
"it-pipe": "^1.1.0",
"it-protocol-buffers": "^0.2.0",
"libp2p-crypto": "^0.17.6",
"libp2p-interfaces": "^0.2.8",
"libp2p-utils": "^0.1.2",
"mafmt": "^7.0.0",
"merge-options": "^2.0.0",
"it-take": "1.0.0",
"libp2p-crypto": "^0.19.0",
"libp2p-interfaces": "^0.8.1",
"libp2p-utils": "^0.2.2",
"mafmt": "^8.0.0",
"merge-options": "^3.0.4",
"moving-average": "^1.0.0",
"multiaddr": "^7.4.3",
"multistream-select": "^0.15.0",
"multiaddr": "^8.1.0",
"multicodec": "^2.1.0",
"multihashing-async": "^2.0.1",
"multistream-select": "^1.0.0",
"mutable-proxy": "^1.0.0",
"node-forge": "^0.10.0",
"p-any": "^3.0.0",
"p-fifo": "^1.0.0",
"p-retry": "^4.2.0",
"p-settle": "^4.0.1",
"peer-id": "^0.13.11",
"peer-info": "^0.17.0",
"protons": "^1.0.1",
"peer-id": "^0.14.2",
"private-ip": "^2.0.0",
"promisify-es6": "^1.0.3",
"protons": "^2.0.0",
"retimer": "^2.0.0",
"streaming-iterables": "^4.1.0",
"timeout-abort-controller": "^1.0.0",
"sanitize-filename": "^1.6.3",
"set-delayed-interval": "^1.0.0",
"streaming-iterables": "^5.0.2",
"timeout-abort-controller": "^1.1.1",
"varint": "^6.0.0",
"xsalsa20": "^1.0.2"
},
"devDependencies": {
"@nodeutils/defaults-deep": "^1.1.0",
"abortable-iterator": "^3.0.0",
"aegir": "^21.10.1",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"cids": "^0.8.0",
"delay": "^4.3.0",
"dirty-chai": "^2.0.1",
"interop-libp2p": "~0.0.1",
"aegir": "^29.2.0",
"chai-bytes": "^0.1.2",
"chai-string": "^1.5.0",
"delay": "^4.4.0",
"interop-libp2p": "^0.3.0",
"into-stream": "^6.0.0",
"ipfs-http-client": "^48.2.2",
"it-concat": "^1.0.0",
"it-pair": "^1.0.0",
"it-pushable": "^1.4.0",
"libp2p-bootstrap": "^0.10.3",
"libp2p-delegated-content-routing": "^0.4.5",
"libp2p-delegated-peer-routing": "^0.4.3",
"libp2p-floodsub": "^0.20.0",
"libp2p-gossipsub": "^0.2.6",
"libp2p-kad-dht": "^0.18.6",
"libp2p-mdns": "^0.13.0",
"libp2p-mplex": "^0.9.5",
"libp2p-secio": "^0.12.4",
"libp2p-tcp": "^0.14.1",
"libp2p-webrtc-star": "^0.17.9",
"libp2p-websockets": "^0.13.1",
"nock": "^12.0.3",
"libp2p": ".",
"libp2p-bootstrap": "^0.12.0",
"libp2p-delegated-content-routing": "^0.9.0",
"libp2p-delegated-peer-routing": "^0.8.0",
"libp2p-floodsub": "^0.24.0",
"libp2p-gossipsub": "^0.8.0",
"libp2p-kad-dht": "^0.20.5",
"libp2p-mdns": "^0.15.0",
"libp2p-mplex": "^0.10.1",
"libp2p-noise": "^2.0.0",
"libp2p-secio": "^0.13.1",
"libp2p-tcp": "^0.15.1",
"libp2p-webrtc-star": "^0.20.0",
"libp2p-websockets": "^0.15.0",
"multihashes": "^3.0.1",
"nock": "^13.0.3",
"p-defer": "^3.0.0",
"p-times": "^3.0.0",
"p-wait-for": "^3.1.0",
"sinon": "^9.0.2"
"p-wait-for": "^3.2.0",
"rimraf": "^3.0.2",
"sinon": "^9.2.4",
"uint8arrays": "^2.0.5"
},
"contributors": [
"David Dias <daviddias.p@gmail.com>",
"Jacob Heun <jacobheun@gmail.com>",
"Vasco Santos <vasco.santos@moxy.studio>",
"Alan Shaw <alan@tableflip.io>",
"Alex Potsides <alex@achingbrain.net>",
"Cayman <caymannava@gmail.com>",
"Pedro Teixeira <i@pgte.me>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Maciej Krüger <mkg20001@gmail.com>",
"Alex Potsides <alex@achingbrain.net>",
"Hugo Dias <mail@hugodias.me>",
"dirkmc <dirkmdev@gmail.com>",
"Volker Mische <volker.mische@gmail.com>",
"dirkmc <dirkmdev@gmail.com>",
"Richard Littauer <richard.littauer@gmail.com>",
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
"a1300 <matthias-knopp@gmx.net>",
"Ryan Bell <ryan@piing.net>",
"Andrew Nesbitt <andrewnez@gmail.com>",
"Cayman <caymannava@gmail.com>",
"Elven <mon.samuel@qq.com>",
"Giovanni T. Parra <fiatjaf@gmail.com>",
"Ryan Bell <ryan@piing.net>",
"Samlior <samlior@foxmail.com>",
"Thomas Eizinger <thomas@eizinger.io>",
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
"Didrik Nordström <didrik@betamos.se>",
"Irakli Gozalishvili <rfobic@gmail.com>",
"Joel Gustafson <joelg@mit.edu>",
"Julien Bouquillon <contact@revolunet.com>",
"Kevin Kwok <antimatter15@gmail.com>",
"Kevin Lacker <lacker@gmail.com>",
"Ethan Lam <elmemphis2000@gmail.com>",
"Nuno Nogueira <nunofmn@gmail.com>",
"Florian-Merle <florian.david.merle@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"RasmusErik Voel Jensen <github@solsort.com>",
"Fei Liu <liu.feiwood@gmail.com>",
"Diogo Silva <fsdiogo@gmail.com>",
"Smite Chow <xiaopengyou@live.com>",
"Soeren <nikorpoulsen@gmail.com>",
"Sönke Hahn <soenkehahn@gmail.com>",
"Tiago Alves <alvesjtiago@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"Diogo Silva <fsdiogo@gmail.com>",
"Daijiro Wachi <daijiro.wachi@gmail.com>",
"Yusef Napora <yusef@napora.org>",
"Zane Starr <zcstarr@gmail.com>",
"Daijiro Wachi <daijiro.wachi@gmail.com>",
"ebinks <elizabethjbinks@gmail.com>",
"Chris Bratlien <chrisbratlien@gmail.com>",
"isan_rivkin <isanrivkin@gmail.com>",
"robertkiel <robert.kiel@validitylabs.org>",
"Irakli Gozalishvili <rfobic@gmail.com>",
"Henrique Dias <hacdias@gmail.com>"
"Cindy Wu <ciindy.wu@gmail.com>",
"Chris Bratlien <chrisbratlien@gmail.com>",
"ebinks <elizabethjbinks@gmail.com>",
"Francis Gulotta <wizard@roborooter.com>",
"Florian-Merle <florian.david.merle@gmail.com>",
"Bernd Strehl <bernd.strehl@gmail.com>",
"Henrique Dias <hacdias@gmail.com>",
"isan_rivkin <isanrivkin@gmail.com>",
"Felipe Martins <felipebrasil93@gmail.com>",
"Fei Liu <liu.feiwood@gmail.com>"
]
}

View File

@ -0,0 +1,43 @@
# Address Manager
The Address manager is responsible for keeping an updated register of the peer's addresses. It includes 2 different types of Addresses: `Listen Addresses` and `Announce Addresses`.
These Addresses should be specified in your libp2p [configuration](../../doc/CONFIGURATION.md) when you create your node.
## Listen Addresses
A libp2p node should have a set of listen addresses, which will be used by libp2p underlying transports to listen for dials from other nodes in the network.
Before a libp2p node starts, its configured listen addresses will be passed to the AddressManager, so that during startup the libp2p transports can use them to listen for connections. Accordingly, listen addresses should be specified through the libp2p configuration, in order to have the `AddressManager` created with them.
It is important pointing out that libp2p accepts ephemeral listening addresses. In this context, the provided listen addresses might not be exactly the same as the ones used by the transports. For example TCP may replace `/ip4/0.0.0.0/tcp/0` with something like `/ip4/127.0.0.1/tcp/8989`. As a consequence, libp2p should take into account this when determining its advertised addresses.
## Announce Addresses
In some scenarios, a libp2p node will need to announce addresses that it is not listening on. In other words, Announce Addresses are an amendment to the Listen Addresses that aim to enable other nodes to achieve connectivity to this node.
Scenarios for Announce Addresses include:
- when you setup a libp2p node in your private network at home, but you need to announce your public IP Address to the outside world;
- when you want to announce a DNS address, which maps to your public IP Address.
## Implementation
When a libp2p node is created, the Address Manager will be populated from the provided addresses through the libp2p configuration. Once the node is started, the Transport Manager component will gather the listen addresses from the Address Manager, so that the libp2p transports can attempt to bind to them.
Libp2p will use the the Address Manager as the source of truth when advertising the peers addresses. After all transports are ready, other libp2p components/subsystems will kickoff, namely the Identify Service and the DHT. Both of them will announce the node addresses to the other peers in the network. The announce addresses will have an important role here and will be gathered by libp2p to compute its current addresses to advertise everytime it is needed.
## Future Considerations
### Dynamic address modifications
In a future iteration, we can enable these addresses to be modified in runtime. For this, the Address Manager should be responsible for notifying interested subsystems of these changes, through an Event Emitter.
#### Modify Listen Addresses
While adding new addresses to listen on runtime should be trivial, removing a listen address might have bad implications for the node, since all the connections using that listen address will be closed. However, libp2p should provide a mechanism for both adding and removing listen addresses in the future.
Every time a new listen address is added, the Address Manager should emit an event with the new multiaddrs to listen. The Transport Manager should listen to this events and act accordingly.
#### Modify Announce Addresses
When the announce addresses are modified, the Address Manager should emit an event so that other subsystems can act accordingly. For example, libp2p identify service should use the libp2p push protocol to inform other peers about these changes.

View File

@ -0,0 +1,102 @@
'use strict'
/** @typedef {import('../types').EventEmitterFactory} Events */
/** @type Events */
const EventEmitter = require('events')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
/**
* @typedef {import('multiaddr')} Multiaddr
*/
/**
* @typedef {Object} AddressManagerOptions
* @property {string[]} [listen = []] - list of multiaddrs string representation to listen.
* @property {string[]} [announce = []] - list of multiaddrs string representation to announce.
*/
/**
* @fires AddressManager#change:addresses Emitted when a addresses change.
*/
class AddressManager extends EventEmitter {
/**
* Responsible for managing the peer addresses.
* Peers can specify their listen and announce addresses.
* The listen addresses will be used by the libp2p transports to listen for new connections,
* while the announce addresses will be used for the peer addresses' to other peers in the network.
*
* @class
* @param {PeerId} peerId - The Peer ID of the node
* @param {object} [options]
* @param {Array<string>} [options.listen = []] - list of multiaddrs string representation to listen.
* @param {Array<string>} [options.announce = []] - list of multiaddrs string representation to announce.
*/
constructor (peerId, { listen = [], announce = [] } = {}) {
super()
this.peerId = peerId
this.listen = new Set(listen.map(ma => ma.toString()))
this.announce = new Set(announce.map(ma => ma.toString()))
this.observed = new Set()
}
/**
* Get peer listen multiaddrs.
*
* @returns {Multiaddr[]}
*/
getListenAddrs () {
return Array.from(this.listen).map((a) => multiaddr(a))
}
/**
* Get peer announcing multiaddrs.
*
* @returns {Multiaddr[]}
*/
getAnnounceAddrs () {
return Array.from(this.announce).map((a) => multiaddr(a))
}
/**
* Get observed multiaddrs.
*
* @returns {Array<Multiaddr>}
*/
getObservedAddrs () {
return Array.from(this.observed).map((a) => multiaddr(a))
}
/**
* Add peer observed addresses
*
* @param {string | Multiaddr} addr
*/
addObservedAddr (addr) {
let ma = multiaddr(addr)
const remotePeer = ma.getPeerId()
// strip our peer id if it has been passed
if (remotePeer) {
const remotePeerId = PeerId.createFromB58String(remotePeer)
// use same encoding for comparison
if (remotePeerId.equals(this.peerId)) {
ma = ma.decapsulate(multiaddr(`/p2p/${this.peerId}`))
}
}
const addrString = ma.toString()
// do not trigger the change:addresses event if we already know about this address
if (this.observed.has(addrString)) {
return
}
this.observed.add(addrString)
this.emit('change:addresses')
}
}
module.exports = AddressManager

View File

@ -1,12 +1,20 @@
# js-libp2p-circuit
> Node.js implementation of the Circuit module that libp2p uses, which implements the [interface-connection](https://github.com/libp2p/js-interfaces/tree/master/src/connection) interface for dial/listen.
> Node.js implementation of the Circuit module that libp2p uses, which implements the [interface-connection](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/connection) interface for dial/listen.
**Note**: git history prior to merging into js-libp2p can be found in the original repository, https://github.com/libp2p/js-libp2p-circuit.
`libp2p-circuit` implements the circuit-relay mechanism that allows nodes that don't speak the same protocol to communicate using a third _relay_ node.
`libp2p-circuit` implements the circuit-relay mechanism that allows nodes that don't speak the same protocol to communicate using a third _relay_ node. You can read more about this in its [spec](https://github.com/libp2p/specs/tree/master/relay).
This module uses [pull-streams](https://pull-stream.github.io) for all stream based interfaces.
## Table of Contents
- [js-libp2p-circuit](#js-libp2p-circuit)
- [Why?](#why)
- [libp2p-circuit and IPFS](#libp2p-circuit-and-ipfs)
- [Table of Contents](#table-of-contents)
- [Usage](#usage)
- [API](#api)
- [Implementation rational](#implementation-rational)
### Why?
@ -16,77 +24,42 @@ The use of circuit-relaying is not limited to routing traffic between browser no
- routing traffic between private nets and circumventing NAT layers
- route mangling for better privacy (matreshka/shallot dialing).
It's also possible to use it for clients that implement exotic transports such as devices that only have bluetooth radios to be reachable over bluetooth enabled relays and become full p2p nodes.
It's also possible to use it for clients that implement exotic transports such as devices that only have bluetooth radios to be reachable over bluetooth enabled relays and become full p2p nodes.
### libp2p-circuit and IPFS
Prior to `libp2p-circuit` there was a rift in the IPFS network, were IPFS nodes could only access content from nodes that speak the same protocol, for example TCP only nodes could only dial to other TCP only nodes, same for any other protocol combination. In practice, this limitation was most visible in JS-IPFS browser nodes, since they can only dial out but not be dialed in over WebRTC or WebSockets, hence any content that the browser node held was not reachable by the rest of the network even through it was announced on the DHT. Non browser IPFS nodes would usually speak more than one protocol such as TCP, WebSockets and/or WebRTC, this made the problem less severe outside of the browser. `libp2p-circuit` solves this problem completely, as long as there are `relay nodes` capable of routing traffic between those nodes their content should be available to the rest of the IPFS network.
## Table of Contents
- [js-libp2p-circuit](#js-libp2p-circuit)
- [Why?](#why)
- [libp2p-circuit and IPFS](#libp2p-circuit-and-ipfs)
- [Table of Contents](#table-of-contents)
- [Usage](#usage)
- [Example](#example)
- [Create dialer/listener](#create-dialerlistener)
- [Create `relay`](#create-relay)
- [API](#api)
- [Implementation rational](#implementation-rational)
## Usage
### Example
Libp2p circuit configuration can be seen at [Setup with Relay](../../doc/CONFIGURATION.md#setup-with-relay).
#### Create dialer/listener
Once you have a circuit relay node running, you can configure other nodes to use it as a relay as follows:
```js
const Circuit = require('libp2p-circuit')
const multiaddr = require('multiaddr')
const pull = require('pull-stream')
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const mh1 = multiaddr('/p2p-circuit/p2p/QmHash') // dial /p2p/QmHash over any circuit
const relayAddr = ...
const circuit = new Circuit(swarmInstance, options) // pass swarm instance and options
const listener = circuit.createListener(mh1, (connection) => {
console.log('new connection opened')
pull(
pull.values(['hello']),
socket
)
const node = await Libp2p.create({
addresses: {
listen: [multiaddr(`${relayAddr}/p2p-circuit`)]
},
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
config: {
relay: { // Circuit Relay options (this config is part of libp2p core configurations)
enabled: true // Allows you to dial and accept relayed connections. Does not make you a relay.
}
}
})
listener.listen(() => {
console.log('listening')
pull(
circuit.dial(mh1),
pull.log,
pull.onEnd(() => {
circuit.close()
})
)
})
```
Outputs:
```sh
listening
new connection opened
hello
```
#### Create `relay`
```js
const Relay = require('libp2p-circuit').Relay
const relay = new Relay(options)
relay.mount(swarmInstance) // start relaying traffic
```
## API
@ -101,7 +74,7 @@ Both for dialing and listening.
### Implementation rational
This module is not a transport, however it implements `interface-transport` interface in order to allow circuit to be plugged with `libp2p-swarm`. The rational behind it is that, `libp2p-circuit` has a dial and listen flow, which fits nicely with other transports, moreover, it requires the _raw_ connection to be encrypted and muxed just as a regular transport's connection does. All in all, `interface-transport` ended up being the correct level of abstraction for circuit, as well as allowed us to reuse existing integration points in `libp2p-swarm` and `libp2p` without adding any ad-hoc logic. All parts of `interface-transport` are used, including `.getAddr` which returns a list of `/p2p-circuit` addresses that circuit is currently listening.
This module is not a transport, however it implements `interface-transport` interface in order to allow circuit to be plugged with `libp2p`. The rational behind it is that, `libp2p-circuit` has a dial and listen flow, which fits nicely with other transports, moreover, it requires the _raw_ connection to be encrypted and muxed just as a regular transport's connection does. All in all, `interface-transport` ended up being the correct level of abstraction for circuit, as well as allowed us to reuse existing integration points in `libp2p` and `libp2p` without adding any ad-hoc logic. All parts of `interface-transport` are used, including `.getAddr` which returns a list of `/p2p-circuit` addresses that circuit is currently listening.
```
libp2p libp2p-circuit (transport)
@ -109,13 +82,13 @@ libp2p
| +---------------------------------+ | | |
| | | | | +------------------+ |
| | | | circuit-relay listens for the HOP | | | |
| | libp2p-swarm <------------------------------------------------| circuit-relay | |
| | libp2p <------------------------------------------------| circuit-relay | |
| | | | message to handle incomming relay | | | |
| | | | requests from other nodes | +------------------+ |
| +---------------------------------+ | | |
| ^ ^ ^ ^ ^ ^ | | +------------------+ |
| | | | | | | | | | +-------------+ | |
| | | | | | | | dialer uses libp2p-swarm to dial | | | | | |
| | | | | | | | dialer uses libp2p to dial | | | | | |
| | | | +----------------------------------------------------------------------> dialer | | |
| | | transports | | to a circuit-relay node using the | | | | | |
| | | | | | | HOP message | | +-------------+ | |

268
src/circuit/auto-relay.js Normal file
View File

@ -0,0 +1,268 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:auto-relay'), {
error: debug('libp2p:auto-relay:err')
})
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayToString = require('uint8arrays/to-string')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const { relay: multicodec } = require('./multicodec')
const { canHop } = require('./circuit/hop')
const { namespaceToCid } = require('./utils')
const {
CIRCUIT_PROTO_CODE,
HOP_METADATA_KEY,
HOP_METADATA_VALUE,
RELAY_RENDEZVOUS_NS
} = require('./constants')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('../peer-store/address-book').Address} Address
*/
/**
* @typedef {Object} AutoRelayProperties
* @property {import('../')} libp2p
*
* @typedef {Object} AutoRelayOptions
* @property {number} [maxListeners = 1] - maximum number of relays to listen.
*/
class AutoRelay {
/**
* Creates an instance of AutoRelay.
*
* @class
* @param {AutoRelayProperties & AutoRelayOptions} props
*/
constructor ({ libp2p, maxListeners = 1 }) {
this._libp2p = libp2p
this._peerId = libp2p.peerId
this._peerStore = libp2p.peerStore
this._connectionManager = libp2p.connectionManager
this._transportManager = libp2p.transportManager
this._addressSorter = libp2p.dialer.addressSorter
this.maxListeners = maxListeners
/**
* @type {Set<string>}
*/
this._listenRelays = new Set()
this._onProtocolChange = this._onProtocolChange.bind(this)
this._onPeerDisconnected = this._onPeerDisconnected.bind(this)
this._peerStore.on('change:protocols', this._onProtocolChange)
this._connectionManager.on('peer:disconnect', this._onPeerDisconnected)
}
/**
* Check if a peer supports the relay protocol.
* If the protocol is not supported, check if it was supported before and remove it as a listen relay.
* If the protocol is supported, check if the peer supports **HOP** and add it as a listener if
* inside the threshold.
*
* @param {Object} props
* @param {PeerId} props.peerId
* @param {string[]} props.protocols
* @returns {Promise<void>}
*/
async _onProtocolChange ({ peerId, protocols }) {
const id = peerId.toB58String()
// Check if it has the protocol
const hasProtocol = protocols.find(protocol => protocol === multicodec)
// If no protocol, check if we were keeping the peer before as a listenRelay
if (!hasProtocol && this._listenRelays.has(id)) {
this._removeListenRelay(id)
return
} else if (!hasProtocol || this._listenRelays.has(id)) {
return
}
// If protocol, check if can hop, store info in the metadataBook and listen on it
try {
const connection = this._connectionManager.get(peerId)
if (!connection) {
return
}
// Do not hop on a relayed connection
if (connection.remoteAddr.protoCodes().includes(CIRCUIT_PROTO_CODE)) {
log(`relayed connection to ${id} will not be used to hop on`)
return
}
const supportsHop = await canHop({ connection })
if (supportsHop) {
this._peerStore.metadataBook.set(peerId, HOP_METADATA_KEY, uint8ArrayFromString(HOP_METADATA_VALUE))
await this._addListenRelay(connection, id)
}
} catch (err) {
log.error(err)
}
}
/**
* Peer disconnects.
*
* @param {Connection} connection - connection to the peer
* @returns {void}
*/
_onPeerDisconnected (connection) {
const peerId = connection.remotePeer
const id = peerId.toB58String()
// Not listening on this relay
if (!this._listenRelays.has(id)) {
return
}
this._removeListenRelay(id)
}
/**
* Attempt to listen on the given relay connection.
*
* @private
* @param {Connection} connection - connection to the peer
* @param {string} id - peer identifier string
* @returns {Promise<void>}
*/
async _addListenRelay (connection, id) {
// Check if already listening on enough relays
if (this._listenRelays.size >= this.maxListeners) {
return
}
// Get peer known addresses and sort them per public addresses first
const remoteAddrs = this._peerStore.addressBook.getMultiaddrsForPeer(
connection.remotePeer, this._addressSorter
)
if (!remoteAddrs || !remoteAddrs.length) {
return
}
const listenAddr = `${remoteAddrs[0].toString()}/p2p-circuit`
this._listenRelays.add(id)
// Attempt to listen on relay
try {
await this._transportManager.listen([multiaddr(listenAddr)])
// Announce multiaddrs will update on listen success by TransportManager event being triggered
} catch (err) {
log.error(err)
this._listenRelays.delete(id)
}
}
/**
* Remove listen relay.
*
* @private
* @param {string} id - peer identifier string.
* @returns {void}
*/
_removeListenRelay (id) {
if (this._listenRelays.delete(id)) {
// TODO: this should be responsibility of the connMgr
this._listenOnAvailableHopRelays([id])
}
}
/**
* Try to listen on available hop relay connections.
* The following order will happen while we do not have enough relays.
* 1. Check the metadata store for known relays, try to listen on the ones we are already connected.
* 2. Dial and try to listen on the peers we know that support hop but are not connected.
* 3. Search the network.
*
* @param {string[]} [peersToIgnore]
* @returns {Promise<void>}
*/
async _listenOnAvailableHopRelays (peersToIgnore = []) {
// TODO: The peer redial issue on disconnect should be handled by connection gating
// Check if already listening on enough relays
if (this._listenRelays.size >= this.maxListeners) {
return
}
const knownHopsToDial = []
// Check if we have known hop peers to use and attempt to listen on the already connected
for (const [id, metadataMap] of this._peerStore.metadataBook.data.entries()) {
// Continue to next if listening on this or peer to ignore
if (this._listenRelays.has(id) || peersToIgnore.includes(id)) {
continue
}
const supportsHop = metadataMap.get(HOP_METADATA_KEY)
// Continue to next if it does not support Hop
if (!supportsHop || uint8ArrayToString(supportsHop) !== HOP_METADATA_VALUE) {
continue
}
const peerId = PeerId.createFromCID(id)
const connection = this._connectionManager.get(peerId)
// If not connected, store for possible later use.
if (!connection) {
knownHopsToDial.push(peerId)
continue
}
await this._addListenRelay(connection, id)
// Check if already listening on enough relays
if (this._listenRelays.size >= this.maxListeners) {
return
}
}
// Try to listen on known peers that are not connected
for (const peerId of knownHopsToDial) {
const connection = await this._libp2p.dial(peerId)
await this._addListenRelay(connection, peerId.toB58String())
// Check if already listening on enough relays
if (this._listenRelays.size >= this.maxListeners) {
return
}
}
// Try to find relays to hop on the network
try {
const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS)
for await (const provider of this._libp2p.contentRouting.findProviders(cid)) {
if (!provider.multiaddrs.length) {
continue
}
const peerId = provider.id
this._peerStore.addressBook.add(peerId, provider.multiaddrs)
const connection = await this._libp2p.dial(peerId)
await this._addListenRelay(connection, peerId.toB58String())
// Check if already listening on enough relays
if (this._listenRelays.size >= this.maxListeners) {
return
}
}
} catch (err) {
log.error(err)
}
}
}
module.exports = AutoRelay

View File

@ -1,23 +1,42 @@
'use strict'
const debug = require('debug')
const PeerInfo = require('peer-info')
const log = Object.assign(debug('libp2p:circuit:hop'), {
error: debug('libp2p:circuit:hop:err')
})
const errCode = require('err-code')
const PeerId = require('peer-id')
const { validateAddrs } = require('./utils')
const StreamHandler = require('./stream-handler')
const { CircuitRelay: CircuitPB } = require('../protocol')
const pipe = require('it-pipe')
const errCode = require('err-code')
const { pipe } = require('it-pipe')
const { codes: Errors } = require('../../errors')
const { stop } = require('./stop')
const multicodec = require('./../multicodec')
const log = debug('libp2p:circuit:hop')
log.error = debug('libp2p:circuit:hop:error')
/**
* @typedef {import('../../types').CircuitRequest} CircuitRequest
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('./stream-handler')<CircuitRequest>} StreamHandlerT
* @typedef {import('../transport')} Transport
*/
module.exports.handleHop = async function handleHop ({
/**
* @typedef {Object} HopRequest
* @property {Connection} connection
* @property {CircuitRequest} request
* @property {StreamHandlerT} streamHandler
* @property {Transport} circuit
*/
/**
* @param {HopRequest} options
* @returns {Promise<void>}
*/
async function handleHop ({
connection,
request,
streamHandler,
@ -42,7 +61,7 @@ module.exports.handleHop = async function handleHop ({
// Get the connection to the destination (stop) peer
const destinationPeer = new PeerId(request.dstPeer.id)
const destinationConnection = circuit._registrar.getConnection(new PeerInfo(destinationPeer))
const destinationConnection = circuit._connectionManager.get(destinationPeer)
if (!destinationConnection && !circuit._options.hop.active) {
log('HOP request received but we are not connected to the destination peer')
return streamHandler.end({
@ -52,6 +71,9 @@ module.exports.handleHop = async function handleHop ({
}
// TODO: Handle being an active relay
if (!destinationConnection) {
return
}
// Handle the incoming HOP request by performing a STOP request
const stopRequest = {
@ -64,8 +86,7 @@ module.exports.handleHop = async function handleHop ({
try {
destinationStream = await stop({
connection: destinationConnection,
request: stopRequest,
circuit
request: stopRequest
})
} catch (err) {
return log.error(err)
@ -91,12 +112,11 @@ module.exports.handleHop = async function handleHop ({
* peer. A new, virtual, connection will be created between the two via the relay.
*
* @param {object} options
* @param {Connection} options.connection Connection to the relay
* @param {*} options.request
* @param {Circuit} options.circuit
* @param {Connection} options.connection - Connection to the relay
* @param {CircuitRequest} options.request
* @returns {Promise<Connection>}
*/
module.exports.hop = async function hop ({
async function hop ({
connection,
request
}) {
@ -118,11 +138,44 @@ module.exports.hop = async function hop ({
throw errCode(new Error(`HOP request failed with code ${response.code}`), Errors.ERR_HOP_REQUEST_FAILED)
}
/**
* Performs a CAN_HOP request to a relay peer, in order to understand its capabilities.
*
* @param {object} options
* @param {Connection} options.connection - Connection to the relay
* @returns {Promise<boolean>}
*/
async function canHop ({
connection
}) {
// Create a new stream to the relay
const { stream } = await connection.newStream([multicodec.relay])
// Send the HOP request
const streamHandler = new StreamHandler({ stream })
streamHandler.write({
type: CircuitPB.Type.CAN_HOP
})
const response = await streamHandler.read()
await streamHandler.close()
if (response.code !== CircuitPB.Status.SUCCESS) {
return false
}
return true
}
/**
* Creates an unencoded CAN_HOP response based on the Circuits configuration
*
* @param {Object} options
* @param {Connection} options.connection
* @param {StreamHandlerT} options.streamHandler
* @param {Transport} options.circuit
* @private
*/
module.exports.handleCanHop = function handleCanHop ({
function handleCanHop ({
connection,
streamHandler,
circuit
@ -134,3 +187,10 @@ module.exports.handleCanHop = function handleCanHop ({
code: canHop ? CircuitPB.Status.SUCCESS : CircuitPB.Status.HOP_CANT_SPEAK_RELAY
})
}
module.exports = {
handleHop,
hop,
canHop,
handleCanHop
}

View File

@ -1,23 +1,31 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:circuit:stop'), {
error: debug('libp2p:circuit:stop:err')
})
const { CircuitRelay: CircuitPB } = require('../protocol')
const multicodec = require('../multicodec')
const StreamHandler = require('./stream-handler')
const { validateAddrs } = require('./utils')
const debug = require('debug')
const log = debug('libp2p:circuit:stop')
log.error = debug('libp2p:circuit:stop:error')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
* @typedef {import('../../types').CircuitRequest} CircuitRequest
* @typedef {import('./stream-handler')<CircuitRequest>} StreamHandlerT
*/
/**
* Handles incoming STOP requests
*
* @private
* @param {*} options
* @param {Object} options
* @param {Connection} options.connection
* @param {*} options.request The CircuitRelay protobuf request (unencoded)
* @param {StreamHandler} options.streamHandler
* @returns {Promise<*>} Resolves a duplex iterable
* @param {CircuitRequest} options.request - The CircuitRelay protobuf request (unencoded)
* @param {StreamHandlerT} options.streamHandler
* @returns {Promise<MuxedStream>|void} Resolves a duplex iterable
*/
module.exports.handleStop = function handleStop ({
connection,
@ -42,11 +50,12 @@ module.exports.handleStop = function handleStop ({
/**
* Creates a STOP request
*
* @private
* @param {*} options
* @param {Object} options
* @param {Connection} options.connection
* @param {*} options.request The CircuitRelay protobuf request (unencoded)
* @returns {Promise<*>} Resolves a duplex iterable
* @param {CircuitRequest} options.request - The CircuitRelay protobuf request (unencoded)
* @returns {Promise<MuxedStream|void>} Resolves a duplex iterable
*/
module.exports.stop = async function stop ({
connection,

View File

@ -1,20 +1,29 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:circuit:stream-handler'), {
error: debug('libp2p:circuit:stream-handler:err')
})
const lp = require('it-length-prefixed')
const handshake = require('it-handshake')
const { CircuitRelay: CircuitPB } = require('../protocol')
const debug = require('debug')
const log = debug('libp2p:circuit:stream-handler')
log.error = debug('libp2p:circuit:stream-handler:error')
/**
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
*/
/**
* @template T
*/
class StreamHandler {
/**
* Create a stream handler for connection
*
* @class
* @param {object} options
* @param {*} options.stream - A duplex iterable
* @param {Number} options.maxLength - max bytes length of message
* @param {MuxedStream} options.stream - A duplex iterable
* @param {number} [options.maxLength = 4096] - max bytes length of message
*/
constructor ({ stream, maxLength = 4096 }) {
this.stream = stream
@ -25,8 +34,9 @@ class StreamHandler {
/**
* Read and decode message
*
* @async
* @returns {void}
* @returns {Promise<T|undefined>}
*/
async read () {
const msg = await this.decoder.next()
@ -44,17 +54,19 @@ class StreamHandler {
/**
* Encode and write array of buffers
*
* @param {*} msg An unencoded CircuitRelay protobuf message
* @param {CircuitPB} msg - An unencoded CircuitRelay protobuf message
* @returns {void}
*/
write (msg) {
log('write message type %s', msg.type)
// @ts-ignore lp.encode expects type type 'Buffer | BufferList', not 'Uint8Array'
this.shake.write(lp.encode.single(CircuitPB.encode(msg)))
}
/**
* Return the handshake rest stream and invalidate handler
*
* @return {*} A duplex iterable
* @returns {*} A duplex iterable
*/
rest () {
this.shake.rest()

View File

@ -3,11 +3,16 @@
const multiaddr = require('multiaddr')
const { CircuitRelay } = require('../protocol')
/**
* @typedef {import('./stream-handler')} StreamHandler
* @typedef {import('../../types').CircuitStatus} CircuitStatus
*/
/**
* Write a response
*
* @param {StreamHandler} streamHandler
* @param {CircuitRelay.Status} status
* @param {CircuitStatus} status
*/
function writeResponse (streamHandler, status) {
streamHandler.write({
@ -19,7 +24,7 @@ function writeResponse (streamHandler, status) {
/**
* Validate incomming HOP/STOP message
*
* @param {*} msg A CircuitRelay unencoded protobuf message
* @param {*} msg - A CircuitRelay unencoded protobuf message
* @param {StreamHandler} streamHandler
*/
function validateAddrs (msg, streamHandler) {

12
src/circuit/constants.js Normal file
View File

@ -0,0 +1,12 @@
'use strict'
const minute = 60 * 1000
module.exports = {
ADVERTISE_BOOT_DELAY: 15 * minute, // Delay before HOP relay service is advertised on the network
ADVERTISE_TTL: 30 * minute, // Delay Between HOP relay service advertisements on the network
CIRCUIT_PROTO_CODE: 290, // Multicodec code
HOP_METADATA_KEY: 'hop_relay', // PeerStore metadaBook key for HOP relay service
HOP_METADATA_VALUE: 'true', // PeerStore metadaBook value for HOP relay service
RELAY_RENDEZVOUS_NS: '/libp2p/relay' // Relay HOP relay service namespace for discovery
}

View File

@ -1,186 +1,109 @@
'use strict'
const mafmt = require('mafmt')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const withIs = require('class-is')
const { CircuitRelay: CircuitPB } = require('./protocol')
const debug = require('debug')
const log = debug('libp2p:circuit')
log.error = debug('libp2p:circuit:error')
const toConnection = require('libp2p-utils/src/stream-to-ma-conn')
const log = Object.assign(debug('libp2p:relay'), {
error: debug('libp2p:relay:err')
})
const { relay: multicodec } = require('./multicodec')
const createListener = require('./listener')
const { handleCanHop, handleHop, hop } = require('./circuit/hop')
const { handleStop } = require('./circuit/stop')
const StreamHandler = require('./circuit/stream-handler')
const {
setDelayedInterval,
clearDelayedInterval
} = require('set-delayed-interval')
class Circuit {
/**
* Creates an instance of Circuit.
const AutoRelay = require('./auto-relay')
const { namespaceToCid } = require('./utils')
const {
ADVERTISE_BOOT_DELAY,
ADVERTISE_TTL,
RELAY_RENDEZVOUS_NS
} = require('./constants')
/**
* @typedef {import('../')} Libp2p
*
* @constructor
* @param {object} options
* @param {Libp2p} options.libp2p
* @param {Upgrader} options.upgrader
*/
constructor ({ libp2p, upgrader }) {
this._dialer = libp2p.dialer
this._registrar = libp2p.registrar
this._upgrader = upgrader
this._options = libp2p._config.relay
this.peerInfo = libp2p.peerInfo
this._registrar.handle(multicodec, this._onProtocol.bind(this))
}
async _onProtocol ({ connection, stream, protocol }) {
const streamHandler = new StreamHandler({ stream })
const request = await streamHandler.read()
const circuit = this
let virtualConnection
switch (request.type) {
case CircuitPB.Type.CAN_HOP: {
log('received CAN_HOP request from %s', connection.remotePeer.toB58String())
await handleCanHop({ circuit, connection, streamHandler })
break
}
case CircuitPB.Type.HOP: {
log('received HOP request from %s', connection.remotePeer.toB58String())
virtualConnection = await handleHop({
connection,
request,
streamHandler,
circuit
})
break
}
case CircuitPB.Type.STOP: {
log('received STOP request from %s', connection.remotePeer.toB58String())
virtualConnection = await handleStop({
connection,
request,
streamHandler,
circuit
})
break
}
default: {
log('Request of type %s not supported', request.type)
}
}
if (virtualConnection) {
const remoteAddr = multiaddr(request.dstPeer.addrs[0])
const localAddr = multiaddr(request.srcPeer.addrs[0])
const maConn = toConnection({
stream: virtualConnection,
remoteAddr,
localAddr
})
const type = CircuitPB.Type === CircuitPB.Type.HOP ? 'relay' : 'inbound'
log('new %s connection %s', type, maConn.remoteAddr)
const conn = await this._upgrader.upgradeInbound(maConn)
log('%s connection %s upgraded', type, maConn.remoteAddr)
this.handler && this.handler(conn)
}
}
/**
* Dial a peer over a relay
* @typedef {Object} RelayAdvertiseOptions
* @property {number} [bootDelay = ADVERTISE_BOOT_DELAY]
* @property {boolean} [enabled = true]
* @property {number} [ttl = ADVERTISE_TTL]
*
* @param {multiaddr} ma - the multiaddr of the peer to dial
* @param {Object} options - dial options
* @param {AbortSignal} [options.signal] - An optional abort signal
* @returns {Connection} - the connection
* @typedef {Object} HopOptions
* @property {boolean} [enabled = false]
* @property {boolean} [active = false]
*
* @typedef {Object} AutoRelayOptions
* @property {number} [maxListeners = 2] - maximum number of relays to listen.
* @property {boolean} [enabled = false]
*/
async dial (ma, options) {
// Check the multiaddr to see if it contains a relay and a destination peer
const addrs = ma.toString().split('/p2p-circuit')
const relayAddr = multiaddr(addrs[0])
const destinationAddr = multiaddr(addrs[addrs.length - 1])
const relayPeer = PeerId.createFromCID(relayAddr.getPeerId())
const destinationPeer = PeerId.createFromCID(destinationAddr.getPeerId())
let disconnectOnFailure = false
let relayConnection = this._registrar.getConnection(new PeerInfo(relayPeer))
if (!relayConnection) {
relayConnection = await this._dialer.connectToPeer(relayAddr, options)
disconnectOnFailure = true
}
try {
const virtualConnection = await hop({
connection: relayConnection,
circuit: this,
request: {
type: CircuitPB.Type.HOP,
srcPeer: {
id: this.peerInfo.id.toBytes(),
addrs: this.peerInfo.multiaddrs.toArray().map(addr => addr.buffer)
class Relay {
/**
* Creates an instance of Relay.
*
* @class
* @param {Libp2p} libp2p
*/
constructor (libp2p) {
this._libp2p = libp2p
this._options = {
advertise: {
bootDelay: ADVERTISE_BOOT_DELAY,
enabled: true,
ttl: ADVERTISE_TTL,
...libp2p._config.relay.advertise
},
dstPeer: {
id: destinationPeer.toBytes(),
addrs: [multiaddr(destinationAddr).buffer]
...libp2p._config.relay
}
// Create autoRelay if enabled
this._autoRelay = this._options.autoRelay.enabled && new AutoRelay({ libp2p, ...this._options.autoRelay })
this._advertiseService = this._advertiseService.bind(this)
}
/**
* Start Relay service.
*
* @returns {void}
*/
start () {
// Advertise service if HOP enabled
const canHop = this._options.hop.enabled
if (canHop && this._options.advertise.enabled) {
this._timeout = setDelayedInterval(
this._advertiseService, this._options.advertise.ttl, this._options.advertise.bootDelay
)
}
}
})
const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerInfo.id.toB58String()}`)
const maConn = toConnection({
stream: virtualConnection,
remoteAddr: ma,
localAddr
})
log('new outbound connection %s', maConn.remoteAddr)
/**
* Stop Relay service.
*
* @returns {void}
*/
stop () {
clearDelayedInterval(this._timeout)
}
return this._upgrader.upgradeOutbound(maConn)
/**
* Advertise hop relay service in the network.
*
* @returns {Promise<void>}
*/
async _advertiseService () {
try {
const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS)
await this._libp2p.contentRouting.provide(cid)
} catch (err) {
log.error('Circuit relay dial failed', err)
disconnectOnFailure && await relayConnection.close()
throw err
if (err.code === 'NO_ROUTERS_AVAILABLE') {
log.error('a content router, such as a DHT, must be provided in order to advertise the relay service', err)
// Stop the advertise
this.stop()
} else {
log.error(err)
}
}
/**
* Create a listener
*
* @param {any} options
* @param {Function} handler
* @return {listener}
*/
createListener (options, handler) {
if (typeof options === 'function') {
handler = options
options = {}
}
// Called on successful HOP and STOP requests
this.handler = handler
return createListener(this, options)
}
/**
* Filter check for all Multiaddrs that this transport can dial on
*
* @param {Array<Multiaddr>} multiaddrs
* @returns {Array<Multiaddr>}
*/
filter (multiaddrs) {
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
return multiaddrs.filter((ma) => {
return mafmt.Circuit.matches(ma)
})
}
}
/**
* @type {Circuit}
*/
module.exports = withIs(Circuit, { className: 'Circuit', symbolName: '@libp2p/js-libp2p-circuit/circuit' })
module.exports = Relay

View File

@ -1,43 +1,36 @@
'use strict'
const EventEmitter = require('events')
const { EventEmitter } = require('events')
const multiaddr = require('multiaddr')
const debug = require('debug')
const log = debug('libp2p:circuit:listener')
log.err = debug('libp2p:circuit:error:listener')
/**
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('libp2p-interfaces/src/transport/types').Listener} Listener
*/
/**
* @param {*} circuit
* @param {import('../')} libp2p
* @returns {Listener} a transport listener
*/
module.exports = (circuit) => {
const listener = new EventEmitter()
module.exports = (libp2p) => {
const listeningAddrs = new Map()
/**
* Add swarm handler and listen for incoming connections
*
* @param {Multiaddr} addr
* @return {void}
* @returns {Promise<void>}
*/
listener.listen = async (addr) => {
async function listen (addr) {
const addrString = String(addr).split('/p2p-circuit').find(a => a !== '')
const relayConn = await circuit._dialer.connectToPeer(multiaddr(addrString))
const relayConn = await libp2p.dial(multiaddr(addrString))
const relayedAddr = relayConn.remoteAddr.encapsulate('/p2p-circuit')
listeningAddrs.set(relayConn.remotePeer.toB58String(), relayedAddr)
listener.emit('listening')
}
/**
* TODO: Remove the peers from our topology
*
* @return {void}
*/
listener.close = () => {}
/**
* Get fixed up multiaddrs
*
@ -52,9 +45,9 @@ module.exports = (circuit) => {
* the encapsulated transport address. This is useful when for example, a peer should only
* be dialed over TCP rather than any other transport
*
* @return {Multiaddr[]}
* @returns {Multiaddr[]}
*/
listener.getAddrs = () => {
function getAddrs () {
const addrs = []
for (const addr of listeningAddrs.values()) {
addrs.push(addr)
@ -62,5 +55,22 @@ module.exports = (circuit) => {
return addrs
}
/** @type Listener */
const listener = Object.assign(new EventEmitter(), {
close: () => Promise.resolve(),
listen,
getAddrs
})
// Remove listeningAddrs when a peer disconnects
libp2p.connectionManager.on('peer:disconnect', (connection) => {
const deleted = listeningAddrs.delete(connection.remotePeer.toB58String())
if (deleted) {
// Announce listen addresses change
listener.emit('close')
}
})
return listener
}

View File

@ -1,5 +1,7 @@
'use strict'
const protobuf = require('protons')
/** @type {{CircuitRelay: import('../../types').CircuitMessageProto}} */
module.exports = protobuf(`
message CircuitRelay {

218
src/circuit/transport.js Normal file
View File

@ -0,0 +1,218 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:circuit'), {
error: debug('libp2p:circuit:err')
})
const mafmt = require('mafmt')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const { CircuitRelay: CircuitPB } = require('./protocol')
const toConnection = require('libp2p-utils/src/stream-to-ma-conn')
const { relay: multicodec } = require('./multicodec')
const createListener = require('./listener')
const { handleCanHop, handleHop, hop } = require('./circuit/hop')
const { handleStop } = require('./circuit/stop')
const StreamHandler = require('./circuit/stream-handler')
const transportSymbol = Symbol.for('@libp2p/js-libp2p-circuit/circuit')
/**
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('libp2p-interfaces/src/stream-muxer/types').MuxedStream} MuxedStream
* @typedef {import('../types').CircuitRequest} CircuitRequest
*/
class Circuit {
/**
* Creates an instance of the Circuit Transport.
*
* @class
* @param {object} options
* @param {import('../')} options.libp2p
* @param {import('../upgrader')} options.upgrader
*/
constructor ({ libp2p, upgrader }) {
this._dialer = libp2p.dialer
this._registrar = libp2p.registrar
this._connectionManager = libp2p.connectionManager
this._upgrader = upgrader
this._options = libp2p._config.relay
this._libp2p = libp2p
this.peerId = libp2p.peerId
this._registrar.handle(multicodec, this._onProtocol.bind(this))
}
/**
* @param {Object} props
* @param {Connection} props.connection
* @param {MuxedStream} props.stream
*/
async _onProtocol ({ connection, stream }) {
/** @type {import('./circuit/stream-handler')<CircuitRequest>} */
const streamHandler = new StreamHandler({ stream })
const request = await streamHandler.read()
if (!request) {
return
}
const circuit = this
let virtualConnection
switch (request.type) {
case CircuitPB.Type.CAN_HOP: {
log('received CAN_HOP request from %s', connection.remotePeer.toB58String())
await handleCanHop({ circuit, connection, streamHandler })
break
}
case CircuitPB.Type.HOP: {
log('received HOP request from %s', connection.remotePeer.toB58String())
virtualConnection = await handleHop({
connection,
request,
streamHandler,
circuit
})
break
}
case CircuitPB.Type.STOP: {
log('received STOP request from %s', connection.remotePeer.toB58String())
virtualConnection = await handleStop({
connection,
request,
streamHandler
})
break
}
default: {
log('Request of type %s not supported', request.type)
}
}
if (virtualConnection) {
const remoteAddr = multiaddr(request.dstPeer.addrs[0])
const localAddr = multiaddr(request.srcPeer.addrs[0])
const maConn = toConnection({
stream: virtualConnection,
remoteAddr,
localAddr
})
const type = request.type === CircuitPB.Type.HOP ? 'relay' : 'inbound'
log('new %s connection %s', type, maConn.remoteAddr)
const conn = await this._upgrader.upgradeInbound(maConn)
log('%s connection %s upgraded', type, maConn.remoteAddr)
this.handler && this.handler(conn)
}
}
/**
* Dial a peer over a relay
*
* @param {Multiaddr} ma - the multiaddr of the peer to dial
* @param {Object} options - dial options
* @param {AbortSignal} [options.signal] - An optional abort signal
* @returns {Promise<Connection>} - the connection
*/
async dial (ma, options) {
// Check the multiaddr to see if it contains a relay and a destination peer
const addrs = ma.toString().split('/p2p-circuit')
const relayAddr = multiaddr(addrs[0])
const destinationAddr = multiaddr(addrs[addrs.length - 1])
const relayPeer = PeerId.createFromCID(relayAddr.getPeerId())
const destinationPeer = PeerId.createFromCID(destinationAddr.getPeerId())
let disconnectOnFailure = false
let relayConnection = this._connectionManager.get(relayPeer)
if (!relayConnection) {
relayConnection = await this._dialer.connectToPeer(relayAddr, options)
disconnectOnFailure = true
}
try {
const virtualConnection = await hop({
connection: relayConnection,
request: {
type: CircuitPB.Type.HOP,
srcPeer: {
id: this.peerId.toBytes(),
addrs: this._libp2p.multiaddrs.map(addr => addr.bytes)
},
dstPeer: {
id: destinationPeer.toBytes(),
addrs: [multiaddr(destinationAddr).bytes]
}
}
})
const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toB58String()}`)
const maConn = toConnection({
stream: virtualConnection,
remoteAddr: ma,
localAddr
})
log('new outbound connection %s', maConn.remoteAddr)
return this._upgrader.upgradeOutbound(maConn)
} catch (err) {
log.error('Circuit relay dial failed', err)
disconnectOnFailure && await relayConnection.close()
throw err
}
}
/**
* Create a listener
*
* @param {any} options
* @param {Function} handler
* @returns {import('libp2p-interfaces/src/transport/types').Listener}
*/
createListener (options, handler) {
if (typeof options === 'function') {
handler = options
options = {}
}
// Called on successful HOP and STOP requests
this.handler = handler
return createListener(this._libp2p)
}
/**
* Filter check for all Multiaddrs that this transport can dial on
*
* @param {Multiaddr[]} multiaddrs
* @returns {Multiaddr[]}
*/
filter (multiaddrs) {
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
return multiaddrs.filter((ma) => {
return mafmt.Circuit.matches(ma)
})
}
get [Symbol.toStringTag] () {
return 'Circuit'
}
/**
* Checks if the given value is a Transport instance.
*
* @param {any} other
* @returns {other is Transport}
*/
static isTransport (other) {
return Boolean(other && other[transportSymbol])
}
}
module.exports = Circuit

19
src/circuit/utils.js Normal file
View File

@ -0,0 +1,19 @@
'use strict'
const CID = require('cids')
const multihashing = require('multihashing-async')
const TextEncoder = require('ipfs-utils/src/text-encoder')
/**
* Convert a namespace string into a cid.
*
* @param {string} namespace
* @returns {Promise<CID>}
*/
module.exports.namespaceToCid = async (namespace) => {
const bytes = new TextEncoder().encode(namespace)
const hash = await multihashing(bytes, 'sha2-256')
return new CID(hash)
}

View File

@ -1,20 +1,53 @@
'use strict'
const mergeOptions = require('merge-options')
const { dnsaddrResolver } = require('multiaddr/src/resolvers')
const Constants = require('./constants')
const { AGENT_VERSION } = require('./identify/consts')
const RelayConstants = require('./circuit/constants')
const { publicAddressesFirst } = require('libp2p-utils/src/address-sort')
const { FaultTolerance } = require('./transport-manager')
const DefaultConfig = {
addresses: {
listen: [],
announce: [],
noAnnounce: []
},
connectionManager: {
minPeers: 25
minConnections: 25
},
transportManager: {
faultTolerance: FaultTolerance.FATAL_ALL
},
dialer: {
maxParallelDials: Constants.MAX_PARALLEL_DIALS,
maxDialsPerPeer: Constants.MAX_PER_PEER_DIALS,
dialTimeout: Constants.DIAL_TIMEOUT
dialTimeout: Constants.DIAL_TIMEOUT,
resolvers: {
dnsaddr: dnsaddrResolver
},
addressSorter: publicAddressesFirst
},
host: {
agentVersion: AGENT_VERSION
},
metrics: {
enabled: false
},
peerStore: {
persistence: false,
threshold: 5
},
peerRouting: {
refreshManager: {
enabled: true,
interval: 6e5,
bootDelay: 10e3
}
},
config: {
dht: {
enabled: false,
@ -26,20 +59,36 @@ const DefaultConfig = {
timeout: 10e3
}
},
nat: {
enabled: true,
ttl: 7200,
keepAlive: true,
gateway: null,
externalIp: null,
pmp: {
enabled: false
}
},
peerDiscovery: {
autoDial: true
},
pubsub: {
enabled: true,
emitSelf: true,
signMessages: true,
strictSigning: true
enabled: true
},
relay: {
enabled: true,
advertise: {
bootDelay: RelayConstants.ADVERTISE_BOOT_DELAY,
enabled: false,
ttl: RelayConstants.ADVERTISE_TTL
},
hop: {
enabled: false,
active: false
},
autoRelay: {
enabled: false,
maxListeners: 2
}
},
transport: {}

View File

@ -1,13 +1,23 @@
'use strict'
const debug = require('debug')
const log = Object.assign(debug('libp2p:connection-manager'), {
error: debug('libp2p:connection-manager:err')
})
const errcode = require('err-code')
const mergeOptions = require('merge-options')
const LatencyMonitor = require('./latency-monitor')
const debug = require('debug')('libp2p:connection-manager')
const retimer = require('retimer')
/** @typedef {import('../types').EventEmitterFactory} Events */
/** @type Events */
const EventEmitter = require('events')
const PeerId = require('peer-id')
const {
ERR_INVALID_PARAMETERS
codes: { ERR_INVALID_PARAMETERS }
} = require('../errors')
const defaultOptions = {
@ -18,42 +28,84 @@ const defaultOptions = {
maxReceivedData: Infinity,
maxEventLoopDelay: Infinity,
pollInterval: 2000,
autoDialInterval: 10000,
movingAverageInterval: 60000,
defaultPeerValue: 1
}
class ConnectionManager {
/**
* @constructor
* @param {Libp2p} libp2p
* @param {object} options
* @param {Number} options.maxConnections The maximum number of connections allowed. Default=Infinity
* @param {Number} options.minConnections The minimum number of connections to avoid pruning. Default=0
* @param {Number} options.maxData The max data (in and out), per average interval to allow. Default=Infinity
* @param {Number} options.maxSentData The max outgoing data, per average interval to allow. Default=Infinity
* @param {Number} options.maxReceivedData The max incoming data, per average interval to allow.. Default=Infinity
* @param {Number} options.maxEventLoopDelay The upper limit the event loop can take to run. Default=Infinity
* @param {Number} options.pollInterval How often, in milliseconds, metrics and latency should be checked. Default=2000
* @param {Number} options.movingAverageInterval How often, in milliseconds, to compute averages. Default=60000
* @param {Number} options.defaultPeerValue The value of the peer. Default=1
/**
* @typedef {import('../')} Libp2p
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
*/
constructor (libp2p, options) {
/**
* @typedef {Object} ConnectionManagerOptions
* @property {number} [maxConnections = Infinity] - The maximum number of connections allowed.
* @property {number} [minConnections = 0] - The minimum number of connections to avoid pruning.
* @property {number} [maxData = Infinity] - The max data (in and out), per average interval to allow.
* @property {number} [maxSentData = Infinity] - The max outgoing data, per average interval to allow.
* @property {number} [maxReceivedData = Infinity] - The max incoming data, per average interval to allow.
* @property {number} [maxEventLoopDelay = Infinity] - The upper limit the event loop can take to run.
* @property {number} [pollInterval = 2000] - How often, in milliseconds, metrics and latency should be checked.
* @property {number} [movingAverageInterval = 60000] - How often, in milliseconds, to compute averages.
* @property {number} [defaultPeerValue = 1] - The value of the peer.
* @property {boolean} [autoDial = true] - Should preemptively guarantee connections are above the low watermark.
* @property {number} [autoDialInterval = 10000] - How often, in milliseconds, it should preemptively guarantee connections are above the low watermark.
*/
/**
*
* @fires ConnectionManager#peer:connect Emitted when a new peer is connected.
* @fires ConnectionManager#peer:disconnect Emitted when a peer is disconnected.
*/
class ConnectionManager extends EventEmitter {
/**
* Responsible for managing known connections.
*
* @class
* @param {Libp2p} libp2p
* @param {ConnectionManagerOptions} options
*/
constructor (libp2p, options = {}) {
super()
this._libp2p = libp2p
this._registrar = libp2p.registrar
this._peerId = libp2p.peerInfo.id.toB58String()
this._peerId = libp2p.peerId.toB58String()
this._options = mergeOptions.call({ ignoreUndefined: true }, defaultOptions, options)
if (this._options.maxConnections < this._options.minConnections) {
throw errcode(new Error('Connection Manager maxConnections must be greater than minConnections'), ERR_INVALID_PARAMETERS)
}
debug('options: %j', this._options)
this._metrics = libp2p.metrics
log('options: %j', this._options)
/**
* Map of peer identifiers to their peer value for pruning connections.
*
* @type {Map<string, number>}
*/
this._peerValues = new Map()
this._connections = new Map()
/**
* Map of connections per peer
*
* @type {Map<string, Connection[]>}
*/
this.connections = new Map()
this._started = false
this._timer = null
this._autoDialTimeout = null
this._checkMetrics = this._checkMetrics.bind(this)
this._autoDial = this._autoDial.bind(this)
}
/**
* Get current number of open connections.
*/
get size () {
return Array.from(this.connections.values())
.reduce((accumulator, value) => accumulator + value.length, 0)
}
/**
@ -61,7 +113,7 @@ class ConnectionManager {
* only event loop and connection limits will be monitored.
*/
start () {
if (this._metrics) {
if (this._libp2p.metrics) {
this._timer = this._timer || retimer(this._checkMetrics, this._options.pollInterval)
}
@ -72,114 +124,254 @@ class ConnectionManager {
})
this._onLatencyMeasure = this._onLatencyMeasure.bind(this)
this._latencyMonitor.on('data', this._onLatencyMeasure)
debug('started')
this._started = true
log('started')
this._options.autoDial && this._autoDial()
}
/**
* Stops the Connection Manager
*
* @async
*/
stop () {
async stop () {
this._autoDialTimeout && this._autoDialTimeout.clear()
this._timer && this._timer.clear()
this._latencyMonitor && this._latencyMonitor.removeListener('data', this._onLatencyMeasure)
debug('stopped')
this._started = false
await this._close()
log('stopped')
}
/**
* Cleans up the connections
*
* @async
*/
async _close () {
// Close all connections we're tracking
const tasks = []
for (const connectionList of this.connections.values()) {
for (const connection of connectionList) {
tasks.push(connection.close())
}
}
await Promise.all(tasks)
this.connections.clear()
}
/**
* Sets the value of the given peer. Peers with lower values
* will be disconnected first.
*
* @param {PeerId} peerId
* @param {number} value A number between 0 and 1
* @param {number} value - A number between 0 and 1
* @returns {void}
*/
setPeerValue (peerId, value) {
if (value < 0 || value > 1) {
throw new Error('value should be a number between 0 and 1')
}
if (peerId.toB58String) {
peerId = peerId.toB58String()
}
this._peerValues.set(peerId, value)
this._peerValues.set(peerId.toB58String(), value)
}
/**
* Checks the libp2p metrics to determine if any values have exceeded
* the configured maximums.
*
* @private
*/
_checkMetrics () {
const movingAverages = this._metrics.global.movingAverages
if (this._libp2p.metrics) {
const movingAverages = this._libp2p.metrics.global.movingAverages
const received = movingAverages.dataReceived[this._options.movingAverageInterval].movingAverage()
this._checkLimit('maxReceivedData', received)
this._checkMaxLimit('maxReceivedData', received)
const sent = movingAverages.dataSent[this._options.movingAverageInterval].movingAverage()
this._checkLimit('maxSentData', sent)
this._checkMaxLimit('maxSentData', sent)
const total = received + sent
this._checkLimit('maxData', total)
debug('metrics update', total)
this._timer.reschedule(this._options.pollInterval)
this._checkMaxLimit('maxData', total)
log('metrics update', total)
this._timer = retimer(this._checkMetrics, this._options.pollInterval)
}
}
/**
* Tracks the incoming connection and check the connection limit
*
* @param {Connection} connection
* @returns {void}
*/
onConnect (connection) {
const peerId = connection.remotePeer.toB58String()
this._connections.set(connection.id, connection)
if (!this._peerValues.has(peerId)) {
this._peerValues.set(peerId, this._options.defaultPeerValue)
const peerId = connection.remotePeer
const peerIdStr = peerId.toB58String()
const storedConn = this.connections.get(peerIdStr)
this.emit('peer:connect', connection)
if (storedConn) {
storedConn.push(connection)
} else {
this.connections.set(peerIdStr, [connection])
}
this._checkLimit('maxConnections', this._connections.size)
this._libp2p.peerStore.keyBook.set(peerId, peerId.pubKey)
if (!this._peerValues.has(peerIdStr)) {
this._peerValues.set(peerIdStr, this._options.defaultPeerValue)
}
this._checkMaxLimit('maxConnections', this.size)
}
/**
* Removes the connection from tracking
*
* @param {Connection} connection
* @returns {void}
*/
onDisconnect (connection) {
this._connections.delete(connection.id)
const peerId = connection.remotePeer.toB58String()
let storedConn = this.connections.get(peerId)
if (storedConn && storedConn.length > 1) {
storedConn = storedConn.filter((conn) => conn.id !== connection.id)
this.connections.set(peerId, storedConn)
} else if (storedConn) {
this.connections.delete(peerId)
this._peerValues.delete(connection.remotePeer.toB58String())
this.emit('peer:disconnect', connection)
}
}
/**
* Get a connection with a peer.
*
* @param {PeerId} peerId
* @returns {Connection|null}
*/
get (peerId) {
const connections = this.getAll(peerId)
if (connections.length) {
return connections[0]
}
return null
}
/**
* Get all open connections with a peer.
*
* @param {PeerId} peerId
* @returns {Connection[]}
*/
getAll (peerId) {
if (!PeerId.isPeerId(peerId)) {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
const id = peerId.toB58String()
const connections = this.connections.get(id)
// Return all open connections
if (connections) {
return connections.filter(connection => connection.stat.status === 'open')
}
return []
}
/**
* If the event loop is slow, maybe close a connection
*
* @private
* @param {*} summary The LatencyMonitor summary
* @param {*} summary - The LatencyMonitor summary
*/
_onLatencyMeasure (summary) {
this._checkLimit('maxEventLoopDelay', summary.avgMs)
this._checkMaxLimit('maxEventLoopDelay', summary.avgMs)
}
/**
* If the `value` of `name` has exceeded its limit, maybe close a connection
*
* @private
* @param {string} name The name of the field to check limits for
* @param {number} value The current value of the field
* @param {string} name - The name of the field to check limits for
* @param {number} value - The current value of the field
*/
_checkLimit (name, value) {
_checkMaxLimit (name, value) {
const limit = this._options[name]
debug('checking limit of %s. current value: %d of %d', name, value, limit)
log('checking limit of %s. current value: %d of %d', name, value, limit)
if (value > limit) {
debug('%s: limit exceeded: %s, %d', this._peerId, name, value)
log('%s: limit exceeded: %s, %d', this._peerId, name, value)
this._maybeDisconnectOne()
}
}
/**
* Proactively tries to connect to known peers stored in the PeerStore.
* It will keep the number of connections below the upper limit and sort
* the peers to connect based on wether we know their keys and protocols.
*
* @async
* @private
*/
async _autoDial () {
const minConnections = this._options.minConnections
// Already has enough connections
if (this.size >= minConnections) {
this._autoDialTimeout = retimer(this._autoDial, this._options.autoDialInterval)
return
}
// Sort peers on wether we know protocols of public keys for them
const peers = Array.from(this._libp2p.peerStore.peers.values())
.sort((a, b) => {
if (b.protocols && b.protocols.length && (!a.protocols || !a.protocols.length)) {
return 1
} else if (b.id.pubKey && !a.id.pubKey) {
return 1
}
return -1
})
for (let i = 0; i < peers.length && this.size < minConnections; i++) {
if (!this.get(peers[i].id)) {
log('connecting to a peerStore stored peer %s', peers[i].id.toB58String())
try {
await this._libp2p.dialer.connectToPeer(peers[i].id)
// Connection Manager was stopped
if (!this._started) {
return
}
} catch (err) {
log.error('could not connect to peerStore stored peer', err)
}
}
}
this._autoDialTimeout = retimer(this._autoDial, this._options.autoDialInterval)
}
/**
* If we have more connections than our maximum, close a connection
* to the lowest valued peer.
*
* @private
*/
_maybeDisconnectOne () {
if (this._options.minConnections < this._connections.size) {
if (this._options.minConnections < this.connections.size) {
const peerValues = Array.from(this._peerValues).sort(byPeerValue)
debug('%s: sorted peer values: %j', this._peerId, peerValues)
log('%s: sorted peer values: %j', this._peerId, peerValues)
const disconnectPeer = peerValues[0]
if (disconnectPeer) {
const peerId = disconnectPeer[0]
debug('%s: lowest value peer is %s', this._peerId, peerId)
debug('%s: closing a connection to %j', this._peerId, peerId)
for (const connection of this._connections.values()) {
if (connection.remotePeer.toB58String() === peerId) {
connection.close()
log('%s: lowest value peer is %s', this._peerId, peerId)
log('%s: closing a connection to %j', this._peerId, peerId)
for (const connections of this.connections.values()) {
if (connections[0].remotePeer.toB58String() === peerId) {
connections[0].close()
break
}
}

View File

@ -1,22 +1,29 @@
// @ts-nocheck
'use strict'
/**
* This code is based on `latency-monitor` (https://github.com/mlucool/latency-monitor) by `mlucool` (https://github.com/mlucool), available under Apache License 2.0 (https://github.com/mlucool/latency-monitor/blob/master/LICENSE)
*/
/* global window */
const globalThis = require('ipfs-utils/src/globalthis')
/** @typedef {import('../types').EventEmitterFactory} Events */
/** @type Events */
const EventEmitter = require('events')
const VisibilityChangeEmitter = require('./visibility-change-emitter')
const debug = require('debug')('latency-monitor:LatencyMonitor')
/**
* @typedef {Object} SummaryObject
* @property {Number} events How many events were called
* @property {Number} minMS What was the min time for a cb to be called
* @property {Number} maxMS What was the max time for a cb to be called
* @property {Number} avgMs What was the average time for a cb to be called
* @property {Number} lengthMs How long this interval was in ms
* @property {number} events How many events were called
* @property {number} minMS What was the min time for a cb to be called
* @property {number} maxMS What was the max time for a cb to be called
* @property {number} avgMs What was the average time for a cb to be called
* @property {number} lengthMs How long this interval was in ms
*
* @typedef {Object} LatencyMonitorOptions
* @property {number} [latencyCheckIntervalMs=500] - How often to add a latency check event (ms)
* @property {number} [dataEmitIntervalMs=5000] - How often to summarize latency check events. null or 0 disables event firing
* @property {Function} [asyncTestFn] - What cb-style async function to use
* @property {number} [latencyRandomPercentage=5] - What percent (+/-) of latencyCheckIntervalMs should we randomly use? This helps avoid alignment to other events.
*/
/**
@ -24,6 +31,8 @@ const debug = require('debug')('latency-monitor:LatencyMonitor')
* the asyncTestFn and timing how long it takes the callback to be called. It can also periodically emit stats about this.
* This can be disabled and stats can be pulled via setting dataEmitIntervalMs = 0.
*
* @extends {EventEmitter}
*
* The default implementation is an event loop latency monitor. This works by firing periodic events into the event loop
* and timing how long it takes to get back.
*
@ -37,10 +46,8 @@ const debug = require('debug')('latency-monitor:LatencyMonitor')
*/
class LatencyMonitor extends EventEmitter {
/**
* @param {Number} [latencyCheckIntervalMs=500] How often to add a latency check event (ms)
* @param {Number} [dataEmitIntervalMs=5000] How often to summarize latency check events. null or 0 disables event firing
* @param {function} [asyncTestFn] What cb-style async function to use
* @param {Number} [latencyRandomPercentage=5] What percent (+/-) of latencyCheckIntervalMs should we randomly use? This helps avoid alignment to other events.
* @class
* @param {LatencyMonitorOptions} [options]
*/
constructor ({ latencyCheckIntervalMs, dataEmitIntervalMs, asyncTestFn, latencyRandomPercentage } = {}) {
super()
@ -65,9 +72,9 @@ class LatencyMonitor extends EventEmitter {
that.asyncTestFn = asyncTestFn // If there is no asyncFn, we measure latency
// If process: use high resolution timer
if (globalThis.process && globalThis.process.hrtime) {
if (globalThis.process && globalThis.process.hrtime) { // eslint-disable-line no-undef
debug('Using process.hrtime for timing')
that.now = globalThis.process.hrtime
that.now = globalThis.process.hrtime // eslint-disable-line no-undef
that.getDeltaMS = (startTime) => {
const hrtime = that.now(startTime)
return (hrtime[0] * 1000) + (hrtime[1] / 1000000)
@ -90,6 +97,7 @@ class LatencyMonitor extends EventEmitter {
// See: http://stackoverflow.com/questions/6032429/chrome-timeouts-interval-suspended-in-background-tabs
if (isBrowser()) {
that._visibilityChangeEmitter = new VisibilityChangeEmitter()
that._visibilityChangeEmitter.on('visibilityChange', (pageInFocus) => {
if (pageInFocus) {
that._startTimers()
@ -107,6 +115,7 @@ class LatencyMonitor extends EventEmitter {
/**
* Start internal timers
*
* @private
*/
_startTimers () {
@ -125,6 +134,7 @@ class LatencyMonitor extends EventEmitter {
/**
* Stop internal timers
*
* @private
*/
_stopTimers () {
@ -140,6 +150,7 @@ class LatencyMonitor extends EventEmitter {
/**
* Emit summary only if there were events. It might not have any events if it was forced via a page hidden/show
*
* @private
*/
_emitSummary () {
@ -152,6 +163,7 @@ class LatencyMonitor extends EventEmitter {
/**
* Calling this function will end the collection period. If a timing event was already fired and somewhere in the queue,
* it will not count for this time period
*
* @returns {SummaryObject}
*/
getSummary () {

View File

@ -1,9 +1,13 @@
// @ts-nocheck
/* global document */
/**
* This code is based on `latency-monitor` (https://github.com/mlucool/latency-monitor) by `mlucool` (https://github.com/mlucool), available under Apache License 2.0 (https://github.com/mlucool/latency-monitor/blob/master/LICENSE)
*/
'use strict'
/** @typedef {import('../types').EventEmitterFactory} Events */
/** @type Events */
const EventEmitter = require('events')
const debug = require('debug')('latency-monitor:VisibilityChangeEmitter')
@ -29,12 +33,12 @@ const debug = require('debug')('latency-monitor:VisibilityChangeEmitter')
* });
* // To access the visibility state directly, call:
* console.log('Am I focused now? ' + myVisibilityEmitter.isVisible());
*
* @class VisibilityChangeEmitter
*/
module.exports = class VisibilityChangeEmitter extends EventEmitter {
class VisibilityChangeEmitter extends EventEmitter {
/**
* Creates a VisibilityChangeEmitter
*
* @class
*/
constructor () {
super()
@ -52,6 +56,7 @@ module.exports = class VisibilityChangeEmitter extends EventEmitter {
* the appropriate name based on the browser being used. Once executed, tha actual names of
* document.hidden and document.visibilityChange are found in this._hidden and this._visibilityChange
* respectively
*
* @private
*/
_initializeVisibilityVarNames () {
@ -77,6 +82,7 @@ module.exports = class VisibilityChangeEmitter extends EventEmitter {
/**
* Adds an event listener on the document that listens to changes in document.visibilityChange
* (or whatever name by which the visibilityChange variable is known in the browser)
*
* @private
*/
_addVisibilityChangeListener () {
@ -92,7 +98,8 @@ module.exports = class VisibilityChangeEmitter extends EventEmitter {
/**
* The function returns ```true``` if the page is visible or ```false``` if the page is not visible and
* ```undefined``` if the page visibility API is not supported by the browser.
* @returns {Boolean|void} whether the page is now visible or not (undefined is unknown)
*
* @returns {boolean | void} whether the page is now visible or not (undefined is unknown)
*/
isVisible () {
if (this._hidden === undefined || document[this._hidden] === undefined) {
@ -116,3 +123,5 @@ module.exports = class VisibilityChangeEmitter extends EventEmitter {
this.emit('visibilityChange', visible)
}
}
module.exports = VisibilityChangeEmitter

View File

@ -1,113 +0,0 @@
'use strict'
const errCode = require('err-code')
const { messages, codes } = require('./errors')
const all = require('it-all')
const pAny = require('p-any')
module.exports = (node) => {
const routers = node._modules.contentRouting || []
const dht = node._dht
// If we have the dht, make it first
if (dht) {
routers.unshift(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.timeout] How long the query should run
* @param {number} [options.maxNumProviders] - maximum number of providers to find
* @returns {AsyncIterable<PeerInfo>}
*/
async * findProviders (key, options) {
if (!routers.length) {
throw errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE')
}
const result = await pAny(
routers.map(async (router) => {
const provs = await all(router.findProviders(key, options))
if (!provs || !provs.length) {
throw errCode(new Error('not found'), 'NOT_FOUND')
}
return provs
})
)
for (const pInfo of result) {
yield pInfo
}
},
/**
* 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
* @returns {Promise<void>}
*/
async provide (key) { // eslint-disable-line require-await
if (!routers.length) {
throw errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE')
}
return Promise.all(routers.map((router) => router.provide(key)))
},
/**
* Store the given key/value pair in the DHT.
* @param {Buffer} key
* @param {Buffer} value
* @param {Object} [options] - put options
* @param {number} [options.minPeers] - minimum number of peers required to successfully put
* @returns {Promise<void>}
*/
async put (key, value, options) { // eslint-disable-line require-await
if (!node.isStarted() || !dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return dht.put(key, value, options)
},
/**
* Get the value to the given key.
* Times out after 1 minute by default.
* @param {Buffer} key
* @param {Object} [options] - get options
* @param {number} [options.timeout] - optional timeout (default: 60000)
* @returns {Promise<{from: PeerId, val: Buffer}>}
*/
async get (key, options) { // eslint-disable-line require-await
if (!node.isStarted() || !dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return dht.get(key, options)
},
/**
* Get the `n` values to the given key without sorting.
* @param {Buffer} key
* @param {number} nVals
* @param {Object} [options] - get options
* @param {number} [options.timeout] - optional timeout (default: 60000)
* @returns {Promise<Array<{from: PeerId, val: Buffer}>>}
*/
async getMany (key, nVals, options) { // eslint-disable-line require-await
if (!node.isStarted() || !dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return dht.getMany(key, nVals, options)
}
}
}

View File

@ -0,0 +1,135 @@
'use strict'
const errCode = require('err-code')
const { messages, codes } = require('../errors')
const {
storeAddresses,
uniquePeers,
requirePeers,
maybeLimitSource
} = require('./utils')
const merge = require('it-merge')
const { pipe } = require('it-pipe')
/**
* @typedef {import('peer-id')} PeerId
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('cids')} CID
*/
/**
* @typedef {Object} GetData
* @property {PeerId} from
* @property {Uint8Array} val
*/
class ContentRouting {
/**
* @class
* @param {import('..')} libp2p
*/
constructor (libp2p) {
this.libp2p = libp2p
this.routers = libp2p._modules.contentRouting || []
this.dht = libp2p._dht
// If we have the dht, add it to the available content routers
if (this.dht) {
this.routers.push(this.dht)
}
}
/**
* Iterates over all content routers in parallel to find providers of the given key.
*
* @param {CID} key - The CID key of the content to find
* @param {object} [options]
* @param {number} [options.timeout] - How long the query should run
* @param {number} [options.maxNumProviders] - maximum number of providers to find
* @returns {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>}
*/
async * findProviders (key, options = {}) {
if (!this.routers.length) {
throw errCode(new Error('No content this.routers available'), 'NO_ROUTERS_AVAILABLE')
}
yield * pipe(
merge(
...this.routers.map(router => router.findProviders(key, options))
),
(source) => storeAddresses(source, this.libp2p.peerStore),
(source) => uniquePeers(source),
(source) => maybeLimitSource(source, options.maxNumProviders),
(source) => requirePeers(source)
)
}
/**
* 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
* @returns {Promise<void>}
*/
async provide (key) {
if (!this.routers.length) {
throw errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE')
}
await Promise.all(this.routers.map((router) => router.provide(key)))
}
/**
* Store the given key/value pair in the DHT.
*
* @param {Uint8Array} key
* @param {Uint8Array} value
* @param {Object} [options] - put options
* @param {number} [options.minPeers] - minimum number of peers required to successfully put
* @returns {Promise<void>}
*/
put (key, value, options) {
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return this.dht.put(key, value, options)
}
/**
* Get the value to the given key.
* Times out after 1 minute by default.
*
* @param {Uint8Array} key
* @param {Object} [options] - get options
* @param {number} [options.timeout] - optional timeout (default: 60000)
* @returns {Promise<GetData>}
*/
get (key, options) {
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return this.dht.get(key, options)
}
/**
* Get the `n` values to the given key without sorting.
*
* @param {Uint8Array} key
* @param {number} nVals
* @param {Object} [options] - get options
* @param {number} [options.timeout] - optional timeout (default: 60000)
* @returns {Promise<GetData[]>}
*/
async getMany (key, nVals, options) { // eslint-disable-line require-await
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
}
return this.dht.getMany(key, nVals, options)
}
}
module.exports = ContentRouting

View File

@ -0,0 +1,89 @@
'use strict'
const errCode = require('err-code')
const filter = require('it-filter')
const map = require('it-map')
const take = require('it-take')
/**
* @typedef {import('peer-id')} PeerId
* @typedef {import('multiaddr')} Multiaddr
*/
/**
* Store the multiaddrs from every peer in the passed peer store
*
* @param {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>} source
* @param {import('../peer-store')} peerStore
*/
function storeAddresses (source, peerStore) {
return map(source, (peer) => {
// ensure we have the addresses for a given peer
peerStore.addressBook.add(peer.id, peer.multiaddrs)
return peer
})
}
/**
* Filter peers by unique peer id
*
* @param {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>} source
*/
function uniquePeers (source) {
/** @type Set<string> */
const seen = new Set()
return filter(source, (peer) => {
// dedupe by peer id
if (seen.has(peer.id.toString())) {
return false
}
seen.add(peer.id.toString())
return true
})
}
/**
* Require at least `min` peers to be yielded from `source`
*
* @param {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>} source
* @param {number} min
*/
async function * requirePeers (source, min = 1) {
let seen = 0
for await (const peer of source) {
seen++
yield peer
}
if (seen < min) {
throw errCode(new Error('not found'), 'NOT_FOUND')
}
}
/**
* If `max` is passed, only take that number of peers from the source
* otherwise take all the peers
*
* @param {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>} source
* @param {number} [max]
*/
function maybeLimitSource (source, max) {
if (max) {
return take(source, max)
}
return source
}
module.exports = {
storeAddresses,
uniquePeers,
requirePeers,
maybeLimitSource
}

View File

@ -1,14 +1,27 @@
'use strict'
const AbortController = require('abort-controller')
const anySignal = require('any-signal')
const debug = require('debug')
const errCode = require('err-code')
const log = debug('libp2p:dialer:request')
log.error = debug('libp2p:dialer:request:error')
const AbortController = require('abort-controller').default
const { anySignal } = require('any-signal')
const FIFO = require('p-fifo')
const pAny = require('p-any')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('./')} Dialer
* @typedef {import('multiaddr')} Multiaddr
*/
/**
* @typedef {Object} DialOptions
* @property {AbortSignal} signal
*
* @typedef {Object} DialRequestOptions
* @property {Multiaddr[]} addrs
* @property {(m: Multiaddr, options: DialOptions) => Promise<Connection>} dialAction
* @property {Dialer} dialer
*/
class DialRequest {
/**
* Manages running the `dialAction` on multiple provided `addrs` in parallel
@ -16,10 +29,9 @@ class DialRequest {
* from `dialer.getTokens`. Once a DialRequest is created, it can be
* started using `DialRequest.run(options)`. Once a single dial has succeeded,
* all other dials in the request will be cancelled.
* @param {object} options
* @param {Multiaddr[]} options.addrs
* @param {function(Multiaddr):Promise<Connection>} options.dialAction
* @param {Dialer} options.dialer
*
* @class
* @param {DialRequestOptions} options
*/
constructor ({
addrs,
@ -33,11 +45,11 @@ class DialRequest {
/**
* @async
* @param {object} options
* @param {AbortSignal} options.signal An AbortController signal
* @returns {Connection}
* @param {object} [options]
* @param {AbortSignal} [options.signal] - An AbortController signal
* @returns {Promise<Connection>}
*/
async run (options) {
async run (options = {}) {
const tokens = this.dialer.getTokens(this.addrs.length)
// If no tokens are available, throw
if (tokens.length < 1) {
@ -55,7 +67,7 @@ class DialRequest {
let conn
try {
const signal = dialAbortControllers[i].signal
conn = await this.dialAction(addr, { ...options, signal: anySignal([signal, options.signal]) })
conn = await this.dialAction(addr, { ...options, signal: options.signal ? anySignal([signal, options.signal]) : signal })
// Remove the successful AbortController so it is not aborted
dialAbortControllers.splice(i, 1)
} finally {
@ -77,4 +89,4 @@ class DialRequest {
}
}
module.exports.DialRequest = DialRequest
module.exports = DialRequest

View File

@ -1,15 +1,17 @@
'use strict'
const multiaddr = require('multiaddr')
const errCode = require('err-code')
const TimeoutController = require('timeout-abort-controller')
const anySignal = require('any-signal')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const debug = require('debug')
const log = debug('libp2p:dialer')
log.error = debug('libp2p:dialer:error')
const { DialRequest } = require('./dial-request')
const log = Object.assign(debug('libp2p:dialer'), {
error: debug('libp2p:dialer:err')
})
const errCode = require('err-code')
const multiaddr = require('multiaddr')
const TimeoutController = require('timeout-abort-controller')
const { anySignal } = require('any-signal')
const DialRequest = require('./dial-request')
const { publicAddressesFirst } = require('libp2p-utils/src/address-sort')
const getPeer = require('../get-peer')
const { codes } = require('../errors')
const {
@ -18,29 +20,66 @@ const {
MAX_PER_PEER_DIALS
} = require('../constants')
/**
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
* @typedef {import('multiaddr')} Multiaddr
* @typedef {import('peer-id')} PeerId
* @typedef {import('../peer-store')} PeerStore
* @typedef {import('../peer-store/address-book').Address} Address
* @typedef {import('../transport-manager')} TransportManager
*/
/**
* @typedef {Object} DialerProperties
* @property {PeerStore} peerStore
* @property {TransportManager} transportManager
*
* @typedef {(addr:Multiaddr) => Promise<string[]>} Resolver
*
* @typedef {Object} DialerOptions
* @property {(addresses: Address[]) => Address[]} [options.addressSorter = publicAddressesFirst] - Sort the known addresses of a peer before trying to dial.
* @property {number} [concurrency = MAX_PARALLEL_DIALS] - Number of max concurrent dials.
* @property {number} [perPeerLimit = MAX_PER_PEER_DIALS] - Number of max concurrent dials per peer.
* @property {number} [timeout = DIAL_TIMEOUT] - How long a dial attempt is allowed to take.
* @property {Record<string, Resolver>} [resolvers = {}] - multiaddr resolvers to use when dialing
*
* @typedef DialTarget
* @property {string} id
* @property {Multiaddr[]} addrs
*
* @typedef PendingDial
* @property {DialRequest} dialRequest
* @property {TimeoutController} controller
* @property {Promise} promise
* @property {function():void} destroy
*/
class Dialer {
/**
* @constructor
* @param {object} options
* @param {TransportManager} options.transportManager
* @param {Peerstore} peerStore
* @param {number} options.concurrency Number of max concurrent dials. Defaults to `MAX_PARALLEL_DIALS`
* @param {number} options.timeout How long a dial attempt is allowed to take. Defaults to `DIAL_TIMEOUT`
* @class
* @param {DialerProperties & DialerOptions} options
*/
constructor ({
transportManager,
peerStore,
addressSorter = publicAddressesFirst,
concurrency = MAX_PARALLEL_DIALS,
timeout = DIAL_TIMEOUT,
perPeerLimit = MAX_PER_PEER_DIALS
perPeerLimit = MAX_PER_PEER_DIALS,
resolvers = {}
}) {
this.transportManager = transportManager
this.peerStore = peerStore
this.addressSorter = addressSorter
this.concurrency = concurrency
this.timeout = timeout
this.perPeerLimit = perPeerLimit
this.tokens = [...new Array(concurrency)].map((_, index) => index)
this._pendingDials = new Map()
for (const [key, value] of Object.entries(resolvers)) {
multiaddr.resolvers.set(key, value)
}
}
/**
@ -58,18 +97,19 @@ class Dialer {
}
/**
* Connects to a given `PeerId` or `Multiaddr` by dialing all of its known addresses.
* Connects to a given `peer` by dialing all of its known addresses.
* The dial to the first address that is successfully able to upgrade a connection
* will be used.
*
* @param {PeerInfo|Multiaddr} peer The peer to dial
* @param {PeerId|Multiaddr|string} peer - The peer to dial
* @param {object} [options]
* @param {AbortSignal} [options.signal] An AbortController signal
* @param {AbortSignal} [options.signal] - An AbortController signal
* @returns {Promise<Connection>}
*/
async connectToPeer (peer, options = {}) {
const dialTarget = this._createDialTarget(peer)
if (dialTarget.addrs.length === 0) {
const dialTarget = await this._createDialTarget(peer)
if (!dialTarget.addrs.length) {
throw errCode(new Error('The dial request has no addresses'), codes.ERR_NO_VALID_ADDRESSES)
}
const pendingDial = this._pendingDials.get(dialTarget.id) || this._createPendingDial(dialTarget, options)
@ -90,51 +130,53 @@ class Dialer {
}
}
/**
* @typedef DialTarget
* @property {string} id
* @property {Multiaddr[]} addrs
*/
/**
* Creates a DialTarget. The DialTarget is used to create and track
* the DialRequest to a given peer.
* If a multiaddr is received it should be the first address attempted.
*
* @private
* @param {PeerInfo|Multiaddr} peer A PeerId or Multiaddr
* @returns {DialTarget}
* @param {PeerId|Multiaddr|string} peer - A PeerId or Multiaddr
* @returns {Promise<DialTarget>}
*/
_createDialTarget (peer) {
const dialable = Dialer.getDialable(peer)
if (multiaddr.isMultiaddr(dialable)) {
return {
id: dialable.toString(),
addrs: [dialable]
async _createDialTarget (peer) {
const { id, multiaddrs } = getPeer(peer)
if (multiaddrs) {
this.peerStore.addressBook.add(id, multiaddrs)
}
let knownAddrs = this.peerStore.addressBook.getMultiaddrsForPeer(id, this.addressSorter) || []
// If received a multiaddr to dial, it should be the first to use
// But, if we know other multiaddrs for the peer, we should try them too.
if (multiaddr.isMultiaddr(peer)) {
knownAddrs = knownAddrs.filter((addr) => !peer.equals(addr))
knownAddrs.unshift(peer)
}
const addrs = this.peerStore.multiaddrsForPeer(dialable)
const addrs = []
for (const a of knownAddrs) {
const resolvedAddrs = await this._resolve(a)
resolvedAddrs.forEach(ra => addrs.push(ra))
}
return {
id: dialable.id.toB58String(),
id: id.toB58String(),
addrs
}
}
/**
* @typedef PendingDial
* @property {DialRequest} dialRequest
* @property {TimeoutController} controller
* @property {Promise} promise
* @property {function():void} destroy
*/
/**
* Creates a PendingDial that wraps the underlying DialRequest
*
* @private
* @param {DialTarget} dialTarget
* @param {object} [options]
* @param {AbortSignal} [options.signal] An AbortController signal
* @param {AbortSignal} [options.signal] - An AbortController signal
* @returns {PendingDial}
*/
_createPendingDial (dialTarget, options) {
_createPendingDial (dialTarget, options = {}) {
const dialAction = (addr, options) => {
if (options.signal.aborted) throw errCode(new Error('already aborted'), codes.ERR_ALREADY_ABORTED)
return this.transportManager.dial(addr, options)
@ -180,34 +222,51 @@ class Dialer {
}
/**
* Converts the given `peer` into a `PeerInfo` or `Multiaddr`.
* @static
* @param {PeerInfo|PeerId|Multiaddr|string} peer
* @returns {PeerInfo|Multiaddr}
* Resolve multiaddr recursively.
*
* @param {Multiaddr} ma
* @returns {Promise<Multiaddr[]>}
*/
static getDialable (peer) {
if (PeerInfo.isPeerInfo(peer)) return peer
if (typeof peer === 'string') {
peer = multiaddr(peer)
async _resolve (ma) {
// TODO: recursive logic should live in multiaddr once dns4/dns6 support is in place
// Now only supporting resolve for dnsaddr
const resolvableProto = ma.protoNames().includes('dnsaddr')
// Multiaddr is not resolvable? End recursion!
if (!resolvableProto) {
return [ma]
}
let addr
if (multiaddr.isMultiaddr(peer)) {
addr = peer
const resolvedMultiaddrs = await this._resolveRecord(ma)
const recursiveMultiaddrs = await Promise.all(resolvedMultiaddrs.map((nm) => {
return this._resolve(nm)
}))
const addrs = recursiveMultiaddrs.flat()
return addrs.reduce((array, newM) => {
if (!array.find(m => m.equals(newM))) {
array.push(newM)
}
return array
}, /** @type {Multiaddr[]} */([]))
}
/**
* Resolve a given multiaddr. If this fails, an empty array will be returned
*
* @param {Multiaddr} ma
* @returns {Promise<Multiaddr[]>}
*/
async _resolveRecord (ma) {
try {
peer = PeerId.createFromCID(peer.getPeerId())
} catch (err) {
throw errCode(new Error('The multiaddr did not contain a valid peer id'), codes.ERR_INVALID_PEER)
ma = multiaddr(ma.toString()) // Use current multiaddr module
const multiaddrs = await ma.resolve()
return multiaddrs
} catch (_) {
log.error(`multiaddr ${ma} could not be resolved`)
return []
}
}
if (PeerId.isPeerId(peer)) {
peer = new PeerInfo(peer)
}
addr && peer.multiaddrs.add(addr)
return peer
}
}
module.exports = Dialer

View File

@ -2,18 +2,21 @@
exports.messages = {
NOT_STARTED_YET: 'The libp2p node is not started yet',
DHT_DISABLED: 'DHT is not available'
DHT_DISABLED: 'DHT is not available',
CONN_ENCRYPTION_REQUIRED: 'At least one connection encryption module is required'
}
exports.codes = {
DHT_DISABLED: 'ERR_DHT_DISABLED',
PUBSUB_NOT_STARTED: 'ERR_PUBSUB_NOT_STARTED',
DHT_NOT_STARTED: 'ERR_DHT_NOT_STARTED',
CONN_ENCRYPTION_REQUIRED: 'ERR_CONN_ENCRYPTION_REQUIRED',
ERR_CONNECTION_ENDED: 'ERR_CONNECTION_ENDED',
ERR_CONNECTION_FAILED: 'ERR_CONNECTION_FAILED',
ERR_NODE_NOT_STARTED: 'ERR_NODE_NOT_STARTED',
ERR_ALREADY_ABORTED: 'ERR_ALREADY_ABORTED',
ERR_NO_VALID_ADDRESSES: 'ERR_NO_VALID_ADDRESSES',
ERR_DIALED_SELF: 'ERR_DIALED_SELF',
ERR_DISCOVERED_SELF: 'ERR_DISCOVERED_SELF',
ERR_DUPLICATE_TRANSPORT: 'ERR_DUPLICATE_TRANSPORT',
ERR_ENCRYPTION_FAILED: 'ERR_ENCRYPTION_FAILED',
@ -26,5 +29,7 @@ exports.codes = {
ERR_TIMEOUT: 'ERR_TIMEOUT',
ERR_TRANSPORT_UNAVAILABLE: 'ERR_TRANSPORT_UNAVAILABLE',
ERR_TRANSPORT_DIAL_FAILED: 'ERR_TRANSPORT_DIAL_FAILED',
ERR_UNSUPPORTED_PROTOCOL: 'ERR_UNSUPPORTED_PROTOCOL'
ERR_UNSUPPORTED_PROTOCOL: 'ERR_UNSUPPORTED_PROTOCOL',
ERR_INVALID_MULTIADDR: 'ERR_INVALID_MULTIADDR',
ERR_SIGNATURE_NOT_VALID: 'ERR_SIGNATURE_NOT_VALID'
}

View File

@ -1,73 +0,0 @@
'use strict'
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const multiaddr = require('multiaddr')
const errCode = require('err-code')
/**
* Converts the given `peer` to a `PeerInfo` instance.
* The `PeerStore` will be checked for the resulting peer, and
* the peer will be updated in the `PeerStore`.
*
* @param {PeerInfo|PeerId|Multiaddr|string} peer
* @param {PeerStore} peerStore
* @returns {PeerInfo}
*/
function getPeerInfo (peer, peerStore) {
if (typeof peer === 'string') {
peer = multiaddr(peer)
}
let addr
if (multiaddr.isMultiaddr(peer)) {
addr = peer
try {
peer = PeerId.createFromB58String(peer.getPeerId())
} catch (err) {
throw errCode(
new Error(`${peer} is not a valid peer type`),
'ERR_INVALID_MULTIADDR'
)
}
}
if (PeerId.isPeerId(peer)) {
peer = new PeerInfo(peer)
}
addr && peer.multiaddrs.add(addr)
return peerStore ? peerStore.put(peer) : peer
}
/**
* If `getPeerInfo` does not return a peer with multiaddrs,
* the `libp2p` PeerRouter will be used to attempt to find the peer.
*
* @async
* @param {PeerInfo|PeerId|Multiaddr|string} peer
* @param {Libp2p} libp2p
* @returns {Promise<PeerInfo>}
*/
function getPeerInfoRemote (peer, libp2p) {
let peerInfo
try {
peerInfo = getPeerInfo(peer, libp2p.peerStore)
} catch (err) {
throw errCode(err, 'ERR_INVALID_PEER_TYPE')
}
// If we don't have an address for the peer, attempt to find it
if (peerInfo.multiaddrs.size < 1) {
return libp2p.peerRouting.findPeer(peerInfo.id)
}
return peerInfo
}
module.exports = {
getPeerInfoRemote,
getPeerInfo
}

44
src/get-peer.js Normal file
View File

@ -0,0 +1,44 @@
'use strict'
const PeerId = require('peer-id')
const multiaddr = require('multiaddr')
const errCode = require('err-code')
const { codes } = require('./errors')
/**
* @typedef {import('multiaddr')} Multiaddr
*/
/**
* Converts the given `peer` to a `Peer` object.
* If a multiaddr is received, the addressBook is updated.
*
* @param {PeerId|Multiaddr|string} peer
* @returns {{ id: PeerId, multiaddrs: Multiaddr[]|undefined }}
*/
function getPeer (peer) {
if (typeof peer === 'string') {
peer = multiaddr(peer)
}
let addr
if (multiaddr.isMultiaddr(peer)) {
addr = peer
try {
peer = PeerId.createFromB58String(peer.getPeerId())
} catch (err) {
throw errCode(
new Error(`${peer} is not a valid peer type`),
codes.ERR_INVALID_MULTIADDR
)
}
}
return {
id: peer,
multiaddrs: addr ? [addr] : undefined
}
}
module.exports = getPeer

View File

@ -1,6 +1,9 @@
'use strict'
// @ts-ignore file not listed within the file list of projects
const libp2pVersion = require('../../package.json').version
module.exports.PROTOCOL_VERSION = 'ipfs/0.1.0'
module.exports.AGENT_VERSION = 'js-libp2p/0.1.0'
module.exports.AGENT_VERSION = `js-libp2p/${libp2pVersion}`
module.exports.MULTICODEC_IDENTIFY = '/ipfs/id/1.0.0'
module.exports.MULTICODEC_IDENTIFY_PUSH = '/ipfs/id/push/1.0.0'

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