mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-07-07 21:01:36 +00:00
Compare commits
287 Commits
docs/deleg
...
docs/updat
Author | SHA1 | Date | |
---|---|---|---|
edf8baf221 | |||
7da9ad44ab | |||
3896941128 | |||
856b38de67 | |||
798d7b73c1 | |||
f2d0d8b51d | |||
999c1b7740 | |||
99ff361a55 | |||
6115f8b680 | |||
9ccab40fc8 | |||
619e5dd73c | |||
0fd23f6a5f | |||
5a84dd56d0 | |||
7b05d6922b | |||
08a4fad80b | |||
51da8874d8 | |||
5cd8c19567 | |||
db766eaca9 | |||
a1308d640d | |||
19e7254c3d | |||
f4898eb883 | |||
9bec719fce | |||
b664c0aafd | |||
b524bbf627 | |||
07bd269fb0 | |||
a8219e61a0 | |||
a1590acc8b | |||
a0cf83c640 | |||
b56cdda0ef | |||
b621fbdfdc | |||
24dd1d22c5 | |||
a1a1213bdc | |||
90a9d93968 | |||
8f680e20e9 | |||
afafd08943 | |||
c038550fad | |||
d60a3215d0 | |||
1e51295150 | |||
5299995998 | |||
64a66f08ac | |||
e2a63bab62 | |||
294b032b0b | |||
72f37acd4a | |||
52a615f922 | |||
698c1df1b4 | |||
7f4662f8d8 | |||
84b935f682 | |||
0fbb59748e | |||
56316b8c1e | |||
aaa1155633 | |||
aa5e232479 | |||
eb7adcf1ab | |||
0be74e6a61 | |||
6065923356 | |||
2b45fee0ed | |||
8bf5a70bb8 | |||
6627278a87 | |||
65e87460b0 | |||
7b8d01697d | |||
ce38033c10 | |||
3f2b06dc26 | |||
1e3d6f4b56 | |||
48a9a3eecc | |||
9ea9287bea | |||
5123a8357b | |||
43630f1e0b | |||
aaf62a40ec | |||
7fbd1556e8 | |||
51474c334a | |||
d75cc97ced | |||
2a7967c1cc | |||
9e9ec0b575 | |||
d3a4bf0a3f | |||
d33919d0b3 | |||
c215339a27 | |||
cb597b57d7 | |||
12e48adafa | |||
ed6d5bb4b4 | |||
e9d225c9dc | |||
2aac3b0f69 | |||
9862653051 | |||
b781911ac2 | |||
3fa1fa3a0b | |||
dfed982404 | |||
60b6a55906 | |||
aadeb73c94 | |||
ea6b1ecce4 | |||
d3ab238738 | |||
97455957ac | |||
5255d87d0a | |||
03ce7c3103 | |||
ea0621b2f6 | |||
9197df309b | |||
8dd361338b | |||
9b13fe321a | |||
1f85e0213a | |||
09a604b5e5 | |||
ed16b69057 | |||
c940f2d384 | |||
2620d46f01 | |||
e192eb6508 | |||
b57de4ed0c | |||
760d8b4c3a | |||
7b56f559cb | |||
0fd5188176 | |||
bd7fd0f755 | |||
da83721d6d | |||
1414e45969 | |||
9e35fbc316 | |||
30728753cf | |||
afdbe3deac | |||
ce58805e90 | |||
b5303fd788 | |||
aa78d2ef15 | |||
5a4641980d | |||
f4ec35573e | |||
524e6f8433 | |||
fad9cb22e0 | |||
e1b158fbb9 | |||
1837d6e95e | |||
4bce6aa0b9 | |||
4d11edd62c | |||
a8984c6cd3 | |||
0882dcea3b | |||
b3d8132ea6 | |||
a317a8b011 | |||
8bed8f39ff | |||
58c0c7c03e | |||
f662fdcf36 | |||
5608178247 | |||
dcd58693f5 | |||
8bf05e6db6 | |||
0589d53616 | |||
eb2528c1d6 | |||
141cf90ca0 | |||
f4ec459a7b | |||
56a7842664 | |||
be8fc9daf1 | |||
f39e8f0da9 | |||
ed0c2cb6c1 | |||
c6592c5797 | |||
33cef10721 | |||
2d6408eb30 | |||
29a96690ad | |||
8b94cd1e2e | |||
b83880765f | |||
560d7084fe | |||
d21b7ce4ff | |||
c1038bec92 | |||
bb2e56e6c7 | |||
4222c49556 | |||
2f2ba420ef | |||
e9d16ba190 | |||
afb552c063 | |||
721151b9fc | |||
efc96c2f19 | |||
ba4681b312 | |||
6b00eeab0a | |||
c339be10ee | |||
7b326cc525 | |||
835a689965 | |||
b08d81cee7 | |||
6445fda050 | |||
4ca481b869 | |||
5d7ee50e76 | |||
48fd64182b | |||
58db09a9d3 | |||
15f7c2a974 | |||
4384d139d2 | |||
bd1068e5d9 | |||
854ac49a10 | |||
f9d0047bf6 | |||
ae5ce9a66f | |||
14a1955a78 | |||
f1eb373235 | |||
dabee00127 | |||
17b6a3fd73 | |||
4f7586886c | |||
71f46bf4a6 | |||
cba035a75b | |||
2c5183bd70 | |||
a586efb5f7 | |||
1db69c5f27 | |||
9e1a9f04ae | |||
287f1b98e5 | |||
90ecc57dbc | |||
ae52d483fc | |||
2570a1ba30 | |||
633b0c291f | |||
7d505494be | |||
1df52ae205 | |||
ba02764c5f | |||
404fa69513 | |||
85988cfaab | |||
def3574ba0 | |||
6b1da8326f | |||
a12006a4fa | |||
466b4dfd70 | |||
98e82df5e1 | |||
6ad840c3f3 | |||
7ac5e42a1a | |||
b86ef64904 | |||
b6cd48b798 | |||
a5b54a7017 | |||
e1e3be8106 | |||
b7432bd02b | |||
7c2fefda46 | |||
2788982c53 | |||
76ec1ccd21 | |||
e445a17278 | |||
2329ef3ea3 | |||
ce8e60bb0d | |||
12fc069873 | |||
9a6e07d70b | |||
8da9fc97d4 | |||
a020db183a | |||
f77ce39484 | |||
18a062ed12 | |||
1999606ecc | |||
472e14f2b4 | |||
953d185c39 | |||
c563e06a60 | |||
f28b09fc0d | |||
2104578924 | |||
bcad60995e | |||
432b0997ae | |||
2afdbb71b2 | |||
c7a54f34f7 | |||
9d52b80c45 | |||
582094a834 | |||
fe2a8eddbb | |||
5e1dbc21a2 | |||
b37ccc7279 | |||
440fbf0c04 | |||
0cef51d4c2 | |||
b277b26043 | |||
bc071ce7d7 | |||
01730214d6 | |||
0826531e31 | |||
35ac02dcb5 | |||
b73348078d | |||
21cd9c67bc | |||
0fc4537a5e | |||
6a05f3e6e7 | |||
93a1e42ef3 | |||
35aa45ce92 | |||
b11c6fc7e9 | |||
ebedd3510b | |||
ae6af20e8e | |||
2a80618740 | |||
5b1bd389f8 | |||
3e31c2d0df | |||
8079c2078b | |||
80cf0777b5 | |||
60b0cbc179 | |||
3eef695bc0 | |||
b3deb356f1 | |||
299cfefa01 | |||
aa95ab9928 | |||
b0f124b5ff | |||
b294301456 | |||
d92306f222 | |||
fd738f9d51 | |||
d788433b43 | |||
d5a977b227 | |||
0489972b4b | |||
3f31b1f422 | |||
a2b3446ed7 | |||
ff7a6c86a0 | |||
9a8d609a59 | |||
9fef58cb7d | |||
684f283aec | |||
3e95e6f9e4 | |||
f4f3f0f03a | |||
7c2c852fc0 | |||
e8d8aab278 | |||
dd48d268ec | |||
99a53592e2 | |||
2a2e7a1012 | |||
791f39a09b | |||
65d52857a5 | |||
48b1b442e9 | |||
9554b05c6f | |||
df6ef45a2d | |||
b4a70ea476 | |||
45716da465 | |||
905c911946 |
105
.aegir.js
105
.aegir.js
@ -1,86 +1,51 @@
|
||||
'use strict'
|
||||
|
||||
const pull = require('pull-stream')
|
||||
const parallel = require('async/parallel')
|
||||
const WebSocketStarRendezvous = require('libp2p-websocket-star-rendezvous')
|
||||
const sigServer = require('libp2p-webrtc-star/src/sig-server')
|
||||
const Libp2p = require('./src')
|
||||
const { MULTIADDRS_WEBSOCKETS } = require('./test/fixtures/browser')
|
||||
const Peers = require('./test/fixtures/peers')
|
||||
const PeerId = require('peer-id')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const Muxer = require('libp2p-mplex')
|
||||
const { NOISE: Crypto } = require('libp2p-noise')
|
||||
const pipe = require('it-pipe')
|
||||
let libp2p
|
||||
|
||||
const Node = require('./test/utils/bundle-nodejs.js')
|
||||
const {
|
||||
getPeerRelay,
|
||||
WRTC_RENDEZVOUS_MULTIADDR,
|
||||
WS_RENDEZVOUS_MULTIADDR
|
||||
} = require('./test/utils/constants')
|
||||
const before = async () => {
|
||||
// Use the last peer
|
||||
const peerId = await PeerId.createFromJSON(Peers[Peers.length - 1])
|
||||
|
||||
let wrtcRendezvous
|
||||
let wsRendezvous
|
||||
let node
|
||||
|
||||
const before = (done) => {
|
||||
parallel([
|
||||
(cb) => {
|
||||
sigServer.start({
|
||||
port: WRTC_RENDEZVOUS_MULTIADDR.nodeAddress().port
|
||||
// cryptoChallenge: true TODO: needs https://github.com/libp2p/js-libp2p-webrtc-star/issues/128
|
||||
})
|
||||
.then(server => {
|
||||
wrtcRendezvous = server
|
||||
cb()
|
||||
})
|
||||
.catch(cb)
|
||||
libp2p = new Libp2p({
|
||||
addresses: {
|
||||
listen: [MULTIADDRS_WEBSOCKETS[0]]
|
||||
},
|
||||
(cb) => {
|
||||
WebSocketStarRendezvous.start({
|
||||
port: WS_RENDEZVOUS_MULTIADDR.nodeAddress().port,
|
||||
refreshPeerListIntervalMS: 1000,
|
||||
strictMultiaddr: false,
|
||||
cryptoChallenge: true
|
||||
}, (err, _server) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
wsRendezvous = _server
|
||||
cb()
|
||||
})
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [WebSockets],
|
||||
streamMuxer: [Muxer],
|
||||
connEncryption: [Crypto]
|
||||
},
|
||||
(cb) => {
|
||||
getPeerRelay((err, peerInfo) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
config: {
|
||||
relay: {
|
||||
enabled: true,
|
||||
hop: {
|
||||
enabled: true,
|
||||
active: false
|
||||
}
|
||||
|
||||
node = new Node({
|
||||
peerInfo,
|
||||
config: {
|
||||
relay: {
|
||||
enabled: true,
|
||||
hop: {
|
||||
enabled: true,
|
||||
active: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
node.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
|
||||
node.start(cb)
|
||||
})
|
||||
}
|
||||
}
|
||||
], done)
|
||||
})
|
||||
// Add the echo protocol
|
||||
libp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
|
||||
|
||||
await libp2p.start()
|
||||
}
|
||||
|
||||
const after = (done) => {
|
||||
setTimeout(() =>
|
||||
parallel([
|
||||
(cb) => wrtcRendezvous.stop().then(cb).catch(cb),
|
||||
...[node, wsRendezvous].map((s) => (cb) => s.stop(cb)),
|
||||
], done),
|
||||
2000
|
||||
)
|
||||
const after = async () => {
|
||||
await libp2p.stop()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
bundlesize: { maxSize: '220kB' },
|
||||
bundlesize: { maxSize: '202kB' },
|
||||
hooks: {
|
||||
pre: before,
|
||||
post: after
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,12 +3,14 @@ docs
|
||||
**/*.log
|
||||
test/repo-tests*
|
||||
**/bundle.js
|
||||
.cache
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
|
16
.travis.yml
16
.travis.yml
@ -21,7 +21,8 @@ jobs:
|
||||
- stage: check
|
||||
script:
|
||||
- npx aegir build --bundlesize
|
||||
- npx aegir dep-check -- -i wrtc -i electron-webrtc
|
||||
# Remove pull libs once ping is async
|
||||
- npx aegir dep-check -- -i pull-handshake -i pull-stream
|
||||
- npm run lint
|
||||
|
||||
- stage: test
|
||||
@ -29,16 +30,21 @@ jobs:
|
||||
addons:
|
||||
chrome: stable
|
||||
script:
|
||||
- npx aegir test -t browser
|
||||
- npx aegir test -t webworker
|
||||
- npx aegir test -t browser -t webworker
|
||||
|
||||
- stage: test
|
||||
name: firefox
|
||||
addons:
|
||||
firefox: latest
|
||||
script:
|
||||
- npx aegir test -t browser -- --browsers FirefoxHeadless
|
||||
- npx aegir test -t webworker -- --browsers FirefoxHeadless
|
||||
- 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
|
417
CHANGELOG.md
417
CHANGELOG.md
@ -1,3 +1,420 @@
|
||||
<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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* reset discovery services upon stop ([#618](https://github.com/libp2p/js-libp2p/issues/618)) ([ea0621b](https://github.com/libp2p/js-libp2p/commit/ea0621b))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.7"></a>
|
||||
## [0.27.7](https://github.com/libp2p/js-libp2p/compare/v0.27.6...v0.27.7) (2020-04-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove node global ([#587](https://github.com/libp2p/js-libp2p/issues/587)) ([9b13fe3](https://github.com/libp2p/js-libp2p/commit/9b13fe3))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.6"></a>
|
||||
## [0.27.6](https://github.com/libp2p/js-libp2p/compare/v0.27.5...v0.27.6) (2020-04-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add null check in libp2p.hangUp() ([c940f2d](https://github.com/libp2p/js-libp2p/commit/c940f2d))
|
||||
* make circuit relay listening addresses more forgiving ([#604](https://github.com/libp2p/js-libp2p/issues/604)) ([e192eb6](https://github.com/libp2p/js-libp2p/commit/e192eb6))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.5"></a>
|
||||
## [0.27.5](https://github.com/libp2p/js-libp2p/compare/v0.27.4...v0.27.5) (2020-04-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* await peer discovery start in libp2p start ([#600](https://github.com/libp2p/js-libp2p/issues/600)) ([bd7fd0f](https://github.com/libp2p/js-libp2p/commit/bd7fd0f))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.4"></a>
|
||||
## [0.27.4](https://github.com/libp2p/js-libp2p/compare/v0.27.3...v0.27.4) (2020-03-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* only use a single export ([#596](https://github.com/libp2p/js-libp2p/issues/596)) ([3072875](https://github.com/libp2p/js-libp2p/commit/3072875))
|
||||
* pass libp2p to discovery services ([#597](https://github.com/libp2p/js-libp2p/issues/597)) ([9e35fbc](https://github.com/libp2p/js-libp2p/commit/9e35fbc))
|
||||
* **test:** improve flakey random walk discovery test ([#574](https://github.com/libp2p/js-libp2p/issues/574)) ([f4ec355](https://github.com/libp2p/js-libp2p/commit/f4ec355))
|
||||
* remove use of assert module ([#561](https://github.com/libp2p/js-libp2p/issues/561)) ([a8984c6](https://github.com/libp2p/js-libp2p/commit/a8984c6))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.3"></a>
|
||||
## [0.27.3](https://github.com/libp2p/js-libp2p/compare/v0.27.2...v0.27.3) (2020-02-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* dont allow multiaddr dials without a peer id ([#558](https://github.com/libp2p/js-libp2p/issues/558)) ([a317a8b](https://github.com/libp2p/js-libp2p/commit/a317a8b))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.2"></a>
|
||||
## [0.27.2](https://github.com/libp2p/js-libp2p/compare/v0.27.1...v0.27.2) (2020-02-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ensure identify streams are closed ([#551](https://github.com/libp2p/js-libp2p/issues/551)) ([f662fdc](https://github.com/libp2p/js-libp2p/commit/f662fdc))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.1"></a>
|
||||
## [0.27.1](https://github.com/libp2p/js-libp2p/compare/v0.27.0...v0.27.1) (2020-02-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* stop stream after first pong received ([#545](https://github.com/libp2p/js-libp2p/issues/545)) ([be8fc9d](https://github.com/libp2p/js-libp2p/commit/be8fc9d))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.0"></a>
|
||||
# [0.27.0](https://github.com/libp2p/js-libp2p/compare/v0.26.2...v0.27.0) (2020-01-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* clean up peer discovery flow ([#494](https://github.com/libp2p/js-libp2p/issues/494)) ([12fc069](https://github.com/libp2p/js-libp2p/commit/12fc069))
|
||||
* clean up pending dials abort per feedback ([633b0c2](https://github.com/libp2p/js-libp2p/commit/633b0c2))
|
||||
* conn mngr min/max connection values ([#528](https://github.com/libp2p/js-libp2p/issues/528)) ([ba4681b](https://github.com/libp2p/js-libp2p/commit/ba4681b))
|
||||
* correct release readme ([ce8e60b](https://github.com/libp2p/js-libp2p/commit/ce8e60b))
|
||||
* examples readme typos ([#481](https://github.com/libp2p/js-libp2p/issues/481)) ([35ac02d](https://github.com/libp2p/js-libp2p/commit/35ac02d))
|
||||
* make dialer configurable ([#521](https://github.com/libp2p/js-libp2p/issues/521)) ([4ca481b](https://github.com/libp2p/js-libp2p/commit/4ca481b))
|
||||
* performance bottleneck in stat.js ([#463](https://github.com/libp2p/js-libp2p/issues/463)) ([93a1e42](https://github.com/libp2p/js-libp2p/commit/93a1e42))
|
||||
* registrar should filter the disconnected conn ([#532](https://github.com/libp2p/js-libp2p/issues/532)) ([bb2e56e](https://github.com/libp2p/js-libp2p/commit/bb2e56e))
|
||||
* release tokens as soon as they are available ([2570a1b](https://github.com/libp2p/js-libp2p/commit/2570a1b))
|
||||
* replace peerInfo addresses with listen addresses ([#485](https://github.com/libp2p/js-libp2p/issues/485)) ([1999606](https://github.com/libp2p/js-libp2p/commit/1999606))
|
||||
* stop discoveries ([#530](https://github.com/libp2p/js-libp2p/issues/530)) ([4222c49](https://github.com/libp2p/js-libp2p/commit/4222c49))
|
||||
* token release logic ([90ecc57](https://github.com/libp2p/js-libp2p/commit/90ecc57))
|
||||
* upgrader should not need muxers ([#517](https://github.com/libp2p/js-libp2p/issues/517)) ([5d7ee50](https://github.com/libp2p/js-libp2p/commit/5d7ee50))
|
||||
* use toB58String everywhere to be consistent ([#537](https://github.com/libp2p/js-libp2p/issues/537)) ([c1038be](https://github.com/libp2p/js-libp2p/commit/c1038be))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* abort all pending dials on stop ([ba02764](https://github.com/libp2p/js-libp2p/commit/ba02764))
|
||||
* add early token recycling in ([a5b54a7](https://github.com/libp2p/js-libp2p/commit/a5b54a7))
|
||||
* add libp2p.connections getter ([#522](https://github.com/libp2p/js-libp2p/issues/522)) ([6445fda](https://github.com/libp2p/js-libp2p/commit/6445fda))
|
||||
* add token based dialer ([e445a17](https://github.com/libp2p/js-libp2p/commit/e445a17))
|
||||
* allow transport options to be passed on creation ([#524](https://github.com/libp2p/js-libp2p/issues/524)) ([c339be1](https://github.com/libp2p/js-libp2p/commit/c339be1))
|
||||
* coalescing dial support ([#518](https://github.com/libp2p/js-libp2p/issues/518)) ([15f7c2a](https://github.com/libp2p/js-libp2p/commit/15f7c2a))
|
||||
* discovery modules ([#486](https://github.com/libp2p/js-libp2p/issues/486)) ([18a062e](https://github.com/libp2p/js-libp2p/commit/18a062e))
|
||||
* discovery modules from transports should be added ([#510](https://github.com/libp2p/js-libp2p/issues/510)) ([f1eb373](https://github.com/libp2p/js-libp2p/commit/f1eb373))
|
||||
* peer store ([#470](https://github.com/libp2p/js-libp2p/issues/470)) ([582094a](https://github.com/libp2p/js-libp2p/commit/582094a))
|
||||
* registrar ([#471](https://github.com/libp2p/js-libp2p/issues/471)) ([9d52b80](https://github.com/libp2p/js-libp2p/commit/9d52b80))
|
||||
* support peer-id instances in peer store operations ([#491](https://github.com/libp2p/js-libp2p/issues/491)) ([8da9fc9](https://github.com/libp2p/js-libp2p/commit/8da9fc9))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.0-rc.0"></a>
|
||||
# [0.27.0-rc.0](https://github.com/libp2p/js-libp2p/compare/v0.27.0-pre.2...v0.27.0-rc.0) (2020-01-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* registrar should filter the disconnected conn ([#532](https://github.com/libp2p/js-libp2p/issues/532)) ([83409de](https://github.com/libp2p/js-libp2p/commit/83409de))
|
||||
* stop discoveries ([#530](https://github.com/libp2p/js-libp2p/issues/530)) ([c44e6e3](https://github.com/libp2p/js-libp2p/commit/c44e6e3))
|
||||
* use toB58String everywhere to be consistent ([#537](https://github.com/libp2p/js-libp2p/issues/537)) ([31d1b23](https://github.com/libp2p/js-libp2p/commit/31d1b23))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.0-pre.2"></a>
|
||||
# [0.27.0-pre.2](https://github.com/libp2p/js-libp2p/compare/v0.27.0-pre.1...v0.27.0-pre.2) (2020-01-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* conn mngr min/max connection values ([#528](https://github.com/libp2p/js-libp2p/issues/528)) ([a1717da](https://github.com/libp2p/js-libp2p/commit/a1717da))
|
||||
* make dialer configurable ([#521](https://github.com/libp2p/js-libp2p/issues/521)) ([24c3ce6](https://github.com/libp2p/js-libp2p/commit/24c3ce6))
|
||||
* upgrader should not need muxers ([#517](https://github.com/libp2p/js-libp2p/issues/517)) ([56a1825](https://github.com/libp2p/js-libp2p/commit/56a1825))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add libp2p.connections getter ([#522](https://github.com/libp2p/js-libp2p/issues/522)) ([6ca19c5](https://github.com/libp2p/js-libp2p/commit/6ca19c5))
|
||||
* allow transport options to be passed on creation ([#524](https://github.com/libp2p/js-libp2p/issues/524)) ([0d4b2bd](https://github.com/libp2p/js-libp2p/commit/0d4b2bd))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.0-pre.1"></a>
|
||||
# [0.27.0-pre.1](https://github.com/libp2p/js-libp2p/compare/v0.27.0-pre.0...v0.27.0-pre.1) (2019-12-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* coalescing dial support ([#518](https://github.com/libp2p/js-libp2p/issues/518)) ([4a871bb](https://github.com/libp2p/js-libp2p/commit/4a871bb))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.0-pre.0"></a>
|
||||
# [0.27.0-pre.0](https://github.com/libp2p/js-libp2p/compare/v0.26.2...v0.27.0-pre.0) (2019-12-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* clean up peer discovery flow ([#494](https://github.com/libp2p/js-libp2p/issues/494)) ([f3eb1f1](https://github.com/libp2p/js-libp2p/commit/f3eb1f1))
|
||||
* clean up pending dials abort per feedback ([7c3371b](https://github.com/libp2p/js-libp2p/commit/7c3371b))
|
||||
* correct release readme ([c4bc00b](https://github.com/libp2p/js-libp2p/commit/c4bc00b))
|
||||
* examples readme typos ([#481](https://github.com/libp2p/js-libp2p/issues/481)) ([35ac02d](https://github.com/libp2p/js-libp2p/commit/35ac02d))
|
||||
* performance bottleneck in stat.js ([#463](https://github.com/libp2p/js-libp2p/issues/463)) ([93a1e42](https://github.com/libp2p/js-libp2p/commit/93a1e42))
|
||||
* release tokens as soon as they are available ([43440aa](https://github.com/libp2p/js-libp2p/commit/43440aa))
|
||||
* replace peerInfo addresses with listen addresses ([#485](https://github.com/libp2p/js-libp2p/issues/485)) ([acbbc0f](https://github.com/libp2p/js-libp2p/commit/acbbc0f))
|
||||
* token release logic ([1838a64](https://github.com/libp2p/js-libp2p/commit/1838a64))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* abort all pending dials on stop ([754fbc2](https://github.com/libp2p/js-libp2p/commit/754fbc2))
|
||||
* add early token recycling in ([24c6037](https://github.com/libp2p/js-libp2p/commit/24c6037))
|
||||
* add token based dialer ([f8540fa](https://github.com/libp2p/js-libp2p/commit/f8540fa))
|
||||
* discovery modules ([#486](https://github.com/libp2p/js-libp2p/issues/486)) ([997ee16](https://github.com/libp2p/js-libp2p/commit/997ee16))
|
||||
* discovery modules from transports should be added ([#510](https://github.com/libp2p/js-libp2p/issues/510)) ([af96dcc](https://github.com/libp2p/js-libp2p/commit/af96dcc))
|
||||
* peer store ([#470](https://github.com/libp2p/js-libp2p/issues/470)) ([f3e276e](https://github.com/libp2p/js-libp2p/commit/f3e276e))
|
||||
* registrar ([#471](https://github.com/libp2p/js-libp2p/issues/471)) ([797d8f0](https://github.com/libp2p/js-libp2p/commit/797d8f0))
|
||||
* support peer-id instances in peer store operations ([#491](https://github.com/libp2p/js-libp2p/issues/491)) ([11ed6bd](https://github.com/libp2p/js-libp2p/commit/11ed6bd))
|
||||
|
||||
|
||||
|
||||
<a name="0.26.2"></a>
|
||||
## [0.26.2](https://github.com/libp2p/js-libp2p/compare/v0.26.1...v0.26.2) (2019-09-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* pubsub promisify ([#456](https://github.com/libp2p/js-libp2p/issues/456)) ([ae6af20](https://github.com/libp2p/js-libp2p/commit/ae6af20))
|
||||
|
||||
|
||||
|
||||
<a name="0.26.1"></a>
|
||||
## [0.26.1](https://github.com/libp2p/js-libp2p/compare/v0.26.0...v0.26.1) (2019-08-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* avoid using superstruct interface ([aa95ab9](https://github.com/libp2p/js-libp2p/commit/aa95ab9))
|
||||
* improve config defaults ([#409](https://github.com/libp2p/js-libp2p/issues/409)) ([3eef695](https://github.com/libp2p/js-libp2p/commit/3eef695)), closes [#406](https://github.com/libp2p/js-libp2p/issues/406)
|
||||
* pubsub configuration ([#404](https://github.com/libp2p/js-libp2p/issues/404)) ([b0f124b](https://github.com/libp2p/js-libp2p/commit/b0f124b)), closes [#401](https://github.com/libp2p/js-libp2p/issues/401) [#401](https://github.com/libp2p/js-libp2p/issues/401) [#401](https://github.com/libp2p/js-libp2p/issues/401) [#401](https://github.com/libp2p/js-libp2p/issues/401) [#401](https://github.com/libp2p/js-libp2p/issues/401)
|
||||
* reference files directly to avoid npm install failures ([#408](https://github.com/libp2p/js-libp2p/issues/408)) ([b3deb35](https://github.com/libp2p/js-libp2p/commit/b3deb35))
|
||||
* reject rather than throw in get peer info ([#410](https://github.com/libp2p/js-libp2p/issues/410)) ([60b0cbc](https://github.com/libp2p/js-libp2p/commit/60b0cbc)), closes [#400](https://github.com/libp2p/js-libp2p/issues/400)
|
||||
|
||||
|
||||
|
||||
<a name="0.26.0"></a>
|
||||
# [0.26.0](https://github.com/libp2p/js-libp2p/compare/v0.26.0-rc.3...v0.26.0) (2019-08-07)
|
||||
|
||||
|
||||
|
||||
<a name="0.26.0-rc.3"></a>
|
||||
# [0.26.0-rc.3](https://github.com/libp2p/js-libp2p/compare/v0.26.0-rc.2...v0.26.0-rc.3) (2019-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* promisified methods ([#398](https://github.com/libp2p/js-libp2p/issues/398)) ([ff7a6c8](https://github.com/libp2p/js-libp2p/commit/ff7a6c8))
|
||||
|
||||
|
||||
|
||||
<a name="0.26.0-rc.2"></a>
|
||||
# [0.26.0-rc.2](https://github.com/libp2p/js-libp2p/compare/v0.26.0-rc.1...v0.26.0-rc.2) (2019-08-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* dont override methods of created instance ([#394](https://github.com/libp2p/js-libp2p/issues/394)) ([3e95e6f](https://github.com/libp2p/js-libp2p/commit/3e95e6f))
|
||||
* pubsub default config ([#393](https://github.com/libp2p/js-libp2p/issues/393)) ([f4f3f0f](https://github.com/libp2p/js-libp2p/commit/f4f3f0f))
|
||||
|
||||
|
||||
### Chores
|
||||
|
||||
* update switch ([#395](https://github.com/libp2p/js-libp2p/issues/395)) ([684f283](https://github.com/libp2p/js-libp2p/commit/684f283))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* switch configuration has changed.
|
||||
'blacklistTTL' is now 'denyTTL' and 'blackListAttempts' is now 'denyAttempts'
|
||||
|
||||
|
||||
|
||||
<a name="0.26.0-rc.1"></a>
|
||||
# [0.26.0-rc.1](https://github.com/libp2p/js-libp2p/compare/v0.26.0-rc.0...v0.26.0-rc.1) (2019-07-31)
|
||||
|
||||
|
||||
|
||||
<a name="0.26.0-rc.0"></a>
|
||||
# [0.26.0-rc.0](https://github.com/libp2p/js-libp2p/compare/v0.25.5...v0.26.0-rc.0) (2019-07-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* make subscribe comply with ipfs interface ([#389](https://github.com/libp2p/js-libp2p/issues/389)) ([9554b05](https://github.com/libp2p/js-libp2p/commit/9554b05))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* integrate gossipsub by default ([#365](https://github.com/libp2p/js-libp2p/issues/365)) ([791f39a](https://github.com/libp2p/js-libp2p/commit/791f39a))
|
||||
* promisify all api methods that accept callbacks ([#381](https://github.com/libp2p/js-libp2p/issues/381)) ([df6ef45](https://github.com/libp2p/js-libp2p/commit/df6ef45))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* new configuration for deciding the implementation of pubsub to be used.
|
||||
In this context, the experimental flags were also removed. See the README for the latest usage.
|
||||
* The ipfs interface specified that options
|
||||
should be provided after the handler, not before.
|
||||
https://github.com/ipfs/interface-js-ipfs-core/blob/v0.109.0/SPEC/PUBSUB.md#pubsubsubscribe
|
||||
|
||||
This corrects the order of parameters. See the jsdocs examples
|
||||
for subscribe to see how it should be used.
|
||||
|
||||
|
||||
|
||||
<a name="0.25.5"></a>
|
||||
## [0.25.5](https://github.com/libp2p/js-libp2p/compare/v0.25.4...v0.25.5) (2019-07-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* peer routing for delegate router ([#377](https://github.com/libp2p/js-libp2p/issues/377)) ([905c911](https://github.com/libp2p/js-libp2p/commit/905c911)), closes [/github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L15-L24](https://github.com//github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go/issues/L15-L24)
|
||||
|
||||
|
||||
|
||||
<a name="0.25.4"></a>
|
||||
## [0.25.4](https://github.com/libp2p/js-libp2p/compare/v0.25.3...v0.25.4) (2019-06-07)
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Contributing guidelines
|
||||
|
||||
libp2p as a project, including js-libp2p and all of its modules, follows the [standard IPFS Community contributing guidelines](https://github.com/ipfs/community/blob/master/contribution-guidelines.md).
|
||||
libp2p as a project, including js-libp2p and all of its modules, follows the [standard IPFS Community contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md).
|
||||
|
||||
We also adhere to the [IPFS JavaScript Community contributing guidelines](https://github.com/ipfs/community/blob/master/js-code-guidelines.md) which provide additional information of how to collaborate and contribute in the JavaScript implementation of libp2p.
|
||||
We also adhere to the [IPFS JavaScript Community contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) which provide additional information of how to collaborate and contribute in the JavaScript implementation of libp2p.
|
||||
|
||||
We appreciate your time and attention for going over these. Please open an issue on [ipfs/community](https://github.com/ipfs/community) if you have any question.
|
||||
|
||||
|
@ -37,7 +37,7 @@ One of following:
|
||||
|
||||
<!--
|
||||
This is for you! Please read, and then delete this text before posting it.
|
||||
The js-ipfs issues are only for bug reports and directly actionable features.
|
||||
The js-libp2p issues are only for bug reports and directly actionable features.
|
||||
|
||||
Read https://github.com/ipfs/community/blob/master/CONTRIBUTING.md#reporting-issues if your issue doesn't fit either of those categories.
|
||||
-->
|
||||
|
45
MIGRATION_TEMPLATE.md
Normal file
45
MIGRATION_TEMPLATE.md
Normal 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
|
||||
|
||||
```
|
572
README.md
572
README.md
@ -5,11 +5,14 @@
|
||||
<h3 align="center">The JavaScript implementation of the libp2p Networking Stack.</h3>
|
||||
|
||||
<p align="center">
|
||||
<a href="http://ipn.io"><img src="https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square" /></a>
|
||||
<a href="http://protocol.ai"><img src="https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square" /></a>
|
||||
<a href="http://libp2p.io/"><img src="https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square" /></a>
|
||||
<a href="http://webchat.freenode.net/?channels=%23libp2p"><img src="https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square" /></a>
|
||||
<a href="https://riot.im/app/#/room/#libp2p:matrix.org"><img src="https://img.shields.io/badge/matrix-%23libp2p%3Apermaweb.io-blue.svg?style=flat-square" /> </a>
|
||||
<a href="https://discord.gg/66KBrm2"><img src="https://img.shields.io/discord/475789330380488707?color=blueviolet&label=discord&style=flat-square" /></a>
|
||||
<a href="https://discuss.libp2p.io"><img src="https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg" /></a>
|
||||
<a href="https://waffle.io/libp2p/libp2p"><img src="https://img.shields.io/badge/pm-waffle-yellow.svg?style=flat-square" /></a>
|
||||
<a href="https://www.npmjs.com/package/libp2p"><img src="https://img.shields.io/npm/dm/libp2p.svg" /></a>
|
||||
<a href="https://www.jsdelivr.com/package/npm/libp2p"><img src="https://data.jsdelivr.com/v1/package/npm/libp2p/badge"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@ -29,15 +32,14 @@
|
||||
|
||||
We've come a long way, but this project is still in Alpha, lots of development is happening, API might change, beware of the Dragons 🐉..
|
||||
|
||||
**Want to get started?** Check our [examples folder](/examples). You can check the development status at the [Waffle Board](https://waffle.io/libp2p/js-libp2p).
|
||||
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.
|
||||
|
||||
[](https://waffle.io/libp2p/js-libp2p/metrics/throughput)
|
||||
**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).
|
||||
|
||||
## Tech Lead
|
||||
|
||||
[David Dias](https://github.com/diasdavid/)
|
||||
[**`Weekly Core Dev Calls`**](https://github.com/libp2p/team-mgmt/issues/16)
|
||||
|
||||
## Lead Maintainer
|
||||
|
||||
@ -46,12 +48,12 @@ We've come a long way, but this project is still in Alpha, lots of development i
|
||||
## Table of Contents
|
||||
|
||||
- [Background](#background)
|
||||
- [Bundles](#bundles)
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Configuration](#configuration)
|
||||
- [API](#api)
|
||||
- [Events](#events)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Tutorials and Examples](#tutorials-and-examples)
|
||||
- [Development](#development)
|
||||
- [Tests](#tests)
|
||||
- [Packages](#packages)
|
||||
@ -60,7 +62,7 @@ We've come a long way, but this project is still in Alpha, lots of development i
|
||||
|
||||
## Background
|
||||
|
||||
libp2p is the product of a long and arduous quest to understand the evolution of the Internet networking stack. In order to build P2P applications, devs have long had to made custom ad-hoc solutions to fit their needs, sometimes making some hard assumptions about their runtimes and the state of the network at the time of their development. Today, looking back more than 20 years, we see a clear pattern in the types of mechanisms built around the Internet Protocol, IP, which can be found throughout many layers of the OSI layer system, libp2p distils these mechanisms into flat categories and defines clear interfaces that once exposed, enable other protocols and applications to use and swap them, enabling upgradability and adaptability for the runtime, without breaking the API.
|
||||
libp2p is the product of a long and arduous quest to understand the evolution of the Internet networking stack. In order to build P2P applications, devs have long had to make custom ad-hoc solutions to fit their needs, sometimes making some hard assumptions about their runtimes and the state of the network at the time of their development. Today, looking back more than 20 years, we see a clear pattern in the types of mechanisms built around the Internet Protocol, IP, which can be found throughout many layers of the OSI layer system, libp2p distils these mechanisms into flat categories and defines clear interfaces that once exposed, enable other protocols and applications to use and swap them, enabling upgradability and adaptability for the runtime, without breaking the API.
|
||||
|
||||
We are in the process of writing better documentation, blog posts, tutorials and a formal specification. Today you can find:
|
||||
|
||||
@ -75,460 +77,37 @@ We are in the process of writing better documentation, blog posts, tutorials and
|
||||
|
||||
To sum up, libp2p is a "network stack" -- a protocol suite -- that cleanly separates concerns, and enables sophisticated applications to only use the protocols they absolutely need, without giving up interoperability and upgradeability. libp2p grew out of IPFS, but it is built so that lots of people can use it, for lots of different projects.
|
||||
|
||||
## Bundles
|
||||
|
||||
With its modular nature, libp2p can be found being used in different projects with different sets of features, while preserving the same top level API. `js-libp2p` is only a skeleton and should not be installed directly, if you are looking for a prebundled libp2p stack, please check:
|
||||
|
||||
- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-nodejs.js) - The libp2p build used by js-ipfs when run in Node.js
|
||||
- [libp2p-ipfs-browser](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-browser.js) - The libp2p build used by js-ipfs when run in a Browser (that supports WebRTC)
|
||||
|
||||
If you have developed a libp2p bundle, please consider submitting it to this list so that it can be found easily by the users of libp2p.
|
||||
|
||||
## Install
|
||||
|
||||
Again, as noted above, this module is only a skeleton and should not be used directly other than libp2p bundle implementors that want to extend its code.
|
||||
|
||||
```sh
|
||||
npm install --save libp2p
|
||||
npm install libp2p
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### [Tutorials and Examples](/examples)
|
||||
### Configuration
|
||||
|
||||
You can find multiple examples on the [examples folder](/examples) that will guide you through using libp2p for several scenarios.
|
||||
|
||||
### Creating your own libp2p bundle
|
||||
|
||||
The libp2p module acts as a glue for every libp2p module that you can use to create your own libp2p bundle. Creating your own libp2p bundle gives you a lot of freedom when it comes to customize it with features and default setup. We recommend creating your own libp2p bundle for the app you are developing that takes in account your needs (e.g. for a browser working version of libp2p that acts as the network layer of IPFS, we have a built one that leverages the Browser transports).
|
||||
|
||||
**Example:**
|
||||
|
||||
```JavaScript
|
||||
// Creating a bundle that adds:
|
||||
// transport: websockets + tcp
|
||||
// stream-muxing: spdy & mplex
|
||||
// crypto-channel: secio
|
||||
// discovery: multicast-dns
|
||||
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const WS = require('libp2p-websockets')
|
||||
const SPDY = require('libp2p-spdy')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const DHT = require('libp2p-kad-dht')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const Protector = require('libp2p-pnet')
|
||||
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
|
||||
const DelegatedContentRouter = require('libp2p-delegated-content-routing')
|
||||
|
||||
class Node extends Libp2p {
|
||||
constructor (_options) {
|
||||
const peerInfo = _options.peerInfo
|
||||
const defaults = {
|
||||
// The libp2p modules for this libp2p bundle
|
||||
modules: {
|
||||
transport: [
|
||||
TCP,
|
||||
new WS() // It can take instances too!
|
||||
],
|
||||
streamMuxer: [
|
||||
SPDY,
|
||||
MPLEX
|
||||
],
|
||||
connEncryption: [
|
||||
SECIO
|
||||
],
|
||||
/** Encryption for private networks. Needs additional private key to work **/
|
||||
// connProtector: new Protector(/*protector specific opts*/),
|
||||
/** Enable custom content routers, such as delegated routing **/
|
||||
// contentRouting: [
|
||||
// new DelegatedContentRouter(peerInfo.id)
|
||||
// ],
|
||||
/** Enable custom peer routers, such as delegated routing **/
|
||||
// peerRouting: [
|
||||
// new DelegatedPeerRouter()
|
||||
// ],
|
||||
peerDiscovery: [
|
||||
MulticastDNS
|
||||
],
|
||||
dht: DHT // DHT enables PeerRouting, ContentRouting and DHT itself components
|
||||
},
|
||||
|
||||
// libp2p config options (typically found on a config.json)
|
||||
config: { // The config object is the part of the config that can go into a file, config.json.
|
||||
peerDiscovery: {
|
||||
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minPeers)
|
||||
mdns: { // mdns options
|
||||
interval: 1000, // ms
|
||||
enabled: true
|
||||
},
|
||||
webrtcStar: { // webrtc-star options
|
||||
interval: 1000, // ms
|
||||
enabled: false
|
||||
}
|
||||
// .. other discovery module options.
|
||||
},
|
||||
relay: { // Circuit Relay options
|
||||
enabled: true,
|
||||
hop: {
|
||||
enabled: false,
|
||||
active: false
|
||||
}
|
||||
},
|
||||
dht: {
|
||||
kBucketSize: 20,
|
||||
enabled: true,
|
||||
randomWalk: {
|
||||
enabled: true, // Allows to disable discovery (enabled by default)
|
||||
interval: 300e3,
|
||||
timeout: 10e3
|
||||
}
|
||||
},
|
||||
// Enable/Disable Experimental features
|
||||
EXPERIMENTAL: { // Experimental features ("behind a flag")
|
||||
pubsub: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// overload any defaults of your bundle using https://github.com/nodeutils/defaults-deep
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
|
||||
// Now all the nodes you create, will have TCP, WebSockets, SPDY, MPLEX, SECIO and MulticastDNS support.
|
||||
```
|
||||
For all the information on how you can configure libp2p see [CONFIGURATION.md](./doc/CONFIGURATION.md).
|
||||
|
||||
### API
|
||||
|
||||
#### Create a Node - `Libp2p.createLibp2p(options, callback)`
|
||||
The specification is available on [API.md](./doc/API.md).
|
||||
|
||||
> Behaves exactly like `new Libp2p(options)`, but doesn't require a PeerInfo. One will be generated instead
|
||||
### Getting started
|
||||
|
||||
```js
|
||||
const { createLibp2p } = require('libp2p')
|
||||
createLibp2p(options, (err, libp2p) => {
|
||||
if (err) throw err
|
||||
libp2p.start((err) => {
|
||||
if (err) throw err
|
||||
})
|
||||
})
|
||||
```
|
||||
If you are starting your journey with `js-libp2p`, read the [GETTING_STARTED.md](./doc/GETTING_STARTED.md) guide.
|
||||
|
||||
- `options`: Object of libp2p configuration options
|
||||
- `callback`: Function with signature `function (Error, Libp2p) {}`
|
||||
### Tutorials and Examples
|
||||
|
||||
#### Create a Node alternative - `new Libp2p(options)`
|
||||
|
||||
> Creates an instance of Libp2p with a custom `PeerInfo` provided via `options.peerInfo`.
|
||||
|
||||
Required keys in the `options` object:
|
||||
|
||||
- `peerInfo`: instance of [PeerInfo][] that contains the [PeerId][], Keys and [multiaddrs][multiaddr] of the libp2p Node.
|
||||
- `modules.transport`: An array that must include at least 1 transport, such as `libp2p-tcp`.
|
||||
|
||||
#### `libp2p.start(callback)`
|
||||
|
||||
> Start the libp2p Node.
|
||||
|
||||
`callback` following signature `function (err) {}`, where `err` is an Error in case starting the node fails.
|
||||
|
||||
#### `libp2p.stop(callback)`
|
||||
|
||||
> Stop the libp2p Node.
|
||||
|
||||
`callback` following signature `function (err) {}`, where `err` is an Error in case stopping the node fails.
|
||||
|
||||
#### `libp2p.dial(peer, callback)`
|
||||
|
||||
> Dials to another peer in the network, establishes the connection.
|
||||
|
||||
- `peer`: can be an instance of [PeerInfo][], [PeerId][], [multiaddr][], or a multiaddr string
|
||||
- `callback` following signature `function (err, conn) {}`, where `err` is an Error in of failure to dial the connection and `conn` is a [Connection][] instance in case of a protocol selected, if not it is undefined.
|
||||
|
||||
#### `libp2p.dialProtocol(peer, protocol, callback)`
|
||||
|
||||
> Dials to another peer in the network and selects a protocol to talk with that peer.
|
||||
|
||||
- `peer`: can be an instance of [PeerInfo][], [PeerId][], [multiaddr][], or a multiaddr string
|
||||
- `protocol`: String that defines the protocol (e.g '/ipfs/bitswap/1.1.0')
|
||||
- `callback`: Function with signature `function (err, conn) {}`, where `conn` is a [Connection](https://github.com/libp2p/interface-connection) object
|
||||
|
||||
`callback` following signature `function (err, conn) {}`, where `err` is an Error in of failure to dial the connection and `conn` is a [Connection][] instance in case of a protocol selected, if not it is undefined.
|
||||
|
||||
#### `libp2p.dialFSM(peer, protocol, callback)`
|
||||
|
||||
> Behaves like `.dial` and `.dialProtocol` but calls back with a Connection State Machine
|
||||
|
||||
- `peer`: can be an instance of [PeerInfo][], [PeerId][], [multiaddr][], or a multiaddr string
|
||||
- `protocol`: an optional String that defines the protocol (e.g '/ipfs/bitswap/1.1.0')
|
||||
- `callback`: following signature `function (err, connFSM) {}`, where `connFSM` is a [Connection State Machine](https://github.com/libp2p/js-libp2p-switch#connection-state-machine)
|
||||
|
||||
#### `libp2p.hangUp(peer, callback)`
|
||||
|
||||
> Closes an open connection with a peer, graciously.
|
||||
|
||||
- `peer`: can be an instance of [PeerInfo][], [PeerId][] or [multiaddr][]
|
||||
|
||||
`callback` following signature `function (err) {}`, where `err` is an Error in case stopping the node fails.
|
||||
|
||||
#### `libp2p.peerRouting.findPeer(id, options, callback)`
|
||||
|
||||
> Looks up for multiaddrs of a peer in the DHT
|
||||
|
||||
- `id`: instance of [PeerId][]
|
||||
- `options`: object of options
|
||||
- `options.maxTimeout`: Number milliseconds
|
||||
|
||||
#### `libp2p.contentRouting.findProviders(key, options, callback)`
|
||||
|
||||
- `key`: Buffer
|
||||
- `options`: object of options
|
||||
- `options.maxTimeout`: Number milliseconds
|
||||
- `options.maxNumProviders` maximum number of providers to find
|
||||
|
||||
#### `libp2p.contentRouting.provide(key, callback)`
|
||||
|
||||
- `key`: Buffer
|
||||
|
||||
#### `libp2p.handle(protocol, handlerFunc [, matchFunc])`
|
||||
|
||||
> Handle new protocol
|
||||
|
||||
- `protocol`: String that defines the protocol (e.g '/ipfs/bitswap/1.1.0')
|
||||
- `handlerFunc`: following signature `function (protocol, conn) {}`, where `conn` is a [Connection](https://github.com/libp2p/interface-connection) object
|
||||
- `matchFunc`: Function for matching on protocol (exact matching, semver, etc). Default to exact match.
|
||||
|
||||
#### `libp2p.unhandle(protocol)`
|
||||
|
||||
> Stop handling protocol
|
||||
|
||||
- `protocol`: String that defines the protocol (e.g '/ipfs/bitswap/1.1.0')
|
||||
|
||||
#### Events
|
||||
|
||||
##### `libp2p.on('start', () => {})`
|
||||
|
||||
> Libp2p has started, along with all its services.
|
||||
|
||||
##### `libp2p.on('stop', () => {})`
|
||||
|
||||
> Libp2p has stopped, along with all its services.
|
||||
|
||||
##### `libp2p.on('error', (err) => {})`
|
||||
|
||||
> An error has occurred
|
||||
|
||||
- `err`: instance of `Error`
|
||||
|
||||
##### `libp2p.on('peer:discovery', (peer) => {})`
|
||||
|
||||
> Peer has been discovered.
|
||||
|
||||
If `autoDial` is `true`, applications should **not** attempt to connect to the peer
|
||||
unless they are performing a specific action. See [peer discovery and auto dial](./PEER_DISCOVERY.md) for more information.
|
||||
|
||||
- `peer`: instance of [PeerInfo][]
|
||||
|
||||
##### `libp2p.on('peer:connect', (peer) => {})`
|
||||
|
||||
> We have a new muxed connection to a peer
|
||||
|
||||
- `peer`: instance of [PeerInfo][]
|
||||
|
||||
##### `libp2p.on('peer:disconnect', (peer) => {})`
|
||||
|
||||
> We have closed a connection to a peer
|
||||
|
||||
- `peer`: instance of [PeerInfo][]
|
||||
|
||||
##### `libp2p.on('connection:start', (peer) => {})`
|
||||
|
||||
> We created a new connection to a peer
|
||||
|
||||
- `peer`: instance of [PeerInfo][]
|
||||
|
||||
##### `libp2p.on('connection:end', (peer) => {})`
|
||||
|
||||
> We closed a connection to a peer
|
||||
|
||||
- `peer`: instance of [PeerInfo][]
|
||||
|
||||
#### `libp2p.isStarted()`
|
||||
|
||||
> Check if libp2p is started
|
||||
|
||||
#### `libp2p.ping(peer [, options], callback)`
|
||||
|
||||
> Ping a node in the network
|
||||
|
||||
#### `libp2p.peerBook`
|
||||
|
||||
> PeerBook instance of the node
|
||||
|
||||
#### `libp2p.peerInfo`
|
||||
|
||||
> PeerInfo instance of the node
|
||||
|
||||
#### `libp2p.pubsub`
|
||||
|
||||
> Same API as IPFS PubSub, defined in the [CORE API Spec](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PUBSUB.md). Just replace `ipfs` by `libp2p` and you are golden.
|
||||
|
||||
---------------------
|
||||
|
||||
`DHT methods also exposed for the time being`
|
||||
|
||||
#### `libp2p.dht.put(key, value, callback)`
|
||||
|
||||
- `key`: Buffer
|
||||
- `value`: Buffer
|
||||
|
||||
#### `libp2p.dht.get(key, options, callback)`
|
||||
|
||||
- `key`: Buffer
|
||||
- `options`: object of options
|
||||
- `options.maxTimeout`: Number milliseconds
|
||||
|
||||
#### `libp2p.dht.getMany(key, nVals, options, callback)`
|
||||
|
||||
- `key`: Buffer
|
||||
- `nVals`: Number
|
||||
- `options`: object of options
|
||||
- `options.maxTimeout`: Number milliseconds
|
||||
|
||||
[PeerInfo]: https://github.com/libp2p/js-peer-info
|
||||
[PeerId]: https://github.com/libp2p/js-peer-id
|
||||
[PeerBook]: https://github.com/libp2p/js-peer-book
|
||||
[multiaddr]: https://github.com/multiformats/js-multiaddr
|
||||
[Connection]: https://github.com/libp2p/interface-connection
|
||||
|
||||
-------
|
||||
|
||||
### Switch Stats API
|
||||
|
||||
##### `libp2p.stats.emit('update')`
|
||||
|
||||
Every time any stat value changes, this object emits an `update` event.
|
||||
|
||||
#### Global stats
|
||||
|
||||
##### `libp2p.stats.global.snapshot`
|
||||
|
||||
Should return a stats snapshot, which is an object containing the following keys and respective values:
|
||||
|
||||
- dataSent: amount of bytes sent, [Big](https://github.com/MikeMcl/big.js#readme) number
|
||||
- dataReceived: amount of bytes received, [Big](https://github.com/MikeMcl/big.js#readme) number
|
||||
|
||||
##### `libp2p.stats.global.movingAverages`
|
||||
|
||||
Returns an object containing the following keys:
|
||||
|
||||
- dataSent
|
||||
- dataReceived
|
||||
|
||||
Each one of them contains an object that has a key for each interval (`60000`, `300000` and `900000` miliseconds).
|
||||
|
||||
Each one of these values is [an exponential moving-average instance](https://github.com/pgte/moving-average#readme).
|
||||
|
||||
#### Per-transport stats
|
||||
|
||||
##### `libp2p.stats.transports()`
|
||||
|
||||
Returns an array containing the tags (string) for each observed transport.
|
||||
|
||||
##### `libp2p.stats.forTransport(transportTag).snapshot`
|
||||
|
||||
Should return a stats snapshot, which is an object containing the following keys and respective values:
|
||||
|
||||
- dataSent: amount of bytes sent, [Big](https://github.com/MikeMcl/big.js#readme) number
|
||||
- dataReceived: amount of bytes received, [Big](https://github.com/MikeMcl/big.js#readme) number
|
||||
|
||||
##### `libp2p.stats.forTransport(transportTag).movingAverages`
|
||||
|
||||
Returns an object containing the following keys:
|
||||
|
||||
dataSent
|
||||
dataReceived
|
||||
|
||||
Each one of them contains an object that has a key for each interval (`60000`, `300000` and `900000` miliseconds).
|
||||
|
||||
Each one of these values is [an exponential moving-average instance](https://github.com/pgte/moving-average#readme).
|
||||
|
||||
#### Per-protocol stats
|
||||
|
||||
##### `libp2p.stats.protocols()`
|
||||
|
||||
Returns an array containing the tags (string) for each observed protocol.
|
||||
|
||||
##### `libp2p.stats.forProtocol(protocolTag).snapshot`
|
||||
|
||||
Should return a stats snapshot, which is an object containing the following keys and respective values:
|
||||
|
||||
- dataSent: amount of bytes sent, [Big](https://github.com/MikeMcl/big.js#readme) number
|
||||
- dataReceived: amount of bytes received, [Big](https://github.com/MikeMcl/big.js#readme) number
|
||||
|
||||
|
||||
##### `libp2p.stats.forProtocol(protocolTag).movingAverages`
|
||||
|
||||
Returns an object containing the following keys:
|
||||
|
||||
- dataSent
|
||||
- dataReceived
|
||||
|
||||
Each one of them contains an object that has a key for each interval (`60000`, `300000` and `900000` miliseconds).
|
||||
|
||||
Each one of these values is [an exponential moving-average instance](https://github.com/pgte/moving-average#readme).
|
||||
|
||||
#### Per-peer stats
|
||||
|
||||
##### `libp2p.stats.peers()`
|
||||
|
||||
Returns an array containing the peerIDs (B58-encoded string) for each observed peer.
|
||||
|
||||
##### `libp2p.stats.forPeer(peerId:String).snapshot`
|
||||
|
||||
Should return a stats snapshot, which is an object containing the following keys and respective values:
|
||||
|
||||
- dataSent: amount of bytes sent, [Big](https://github.com/MikeMcl/big.js#readme) number
|
||||
- dataReceived: amount of bytes received, [Big](https://github.com/MikeMcl/big.js#readme) number
|
||||
|
||||
|
||||
##### `libp2p.stats.forPeer(peerId:String).movingAverages`
|
||||
|
||||
Returns an object containing the following keys:
|
||||
|
||||
- dataSent
|
||||
- dataReceived
|
||||
|
||||
Each one of them contains an object that has a key for each interval (`60000`, `300000` and `900000` miliseconds).
|
||||
|
||||
Each one of these values is [an exponential moving-average instance](https://github.com/pgte/moving-average#readme).
|
||||
|
||||
#### Stats update interval
|
||||
|
||||
Stats are not updated in real-time. Instead, measurements are buffered and stats are updated at an interval. The maximum interval can be defined through the `Switch` constructor option `stats.computeThrottleTimeout`, defined in miliseconds.
|
||||
|
||||
### Private Networks
|
||||
|
||||
#### Enforcement
|
||||
|
||||
Libp2p provides support for connection protection, such as for private networks. You can enforce network protection by setting the environment variable `LIBP2P_FORCE_PNET=1`. When this variable is on, if no protector is set via `options.connProtector`, Libp2p will throw an error upon creation.
|
||||
|
||||
#### Protectors
|
||||
|
||||
Some available network protectors:
|
||||
* [libp2p-pnet](https://github.com/libp2p/js-libp2p-pnet)
|
||||
You can find multiple examples on the [examples folder](./examples) that will guide you through using libp2p for several scenarios.
|
||||
|
||||
## Development
|
||||
|
||||
**Clone and install dependencies:**
|
||||
|
||||
```sh
|
||||
> git clone https://github.com/ipfs/js-ipfs.git
|
||||
> cd js-ipfs
|
||||
> git clone https://github.com/libp2p/js-libp2p.git
|
||||
> cd js-libp2p
|
||||
> npm install
|
||||
```
|
||||
|
||||
@ -555,67 +134,46 @@ List of packages currently in existence for libp2p
|
||||
|
||||
| Package | Version | Deps | CI | Coverage | Lead Maintainer |
|
||||
| ---------|---------|---------|---------|---------|--------- |
|
||||
| **Libp2p** |
|
||||
| [`interface-libp2p`](//github.com/libp2p/interface-libp2p) | [](//github.com/libp2p/interface-libp2p/releases) | [](https://david-dm.org/libp2p/interface-libp2p) | [](https://travis-ci.com/libp2p/interface-libp2p) | [](https://codecov.io/gh/libp2p/interface-libp2p) | N/A |
|
||||
| [`libp2p`](//github.com/libp2p/js-libp2p) | [](//github.com/libp2p/js-libp2p/releases) | [](https://david-dm.org/libp2p/js-libp2p) | [](https://travis-ci.com/libp2p/js-libp2p) | [](https://codecov.io/gh/libp2p/js-libp2p) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **Connection** |
|
||||
| [`interface-connection`](//github.com/libp2p/interface-connection) | [](//github.com/libp2p/interface-connection/releases) | [](https://david-dm.org/libp2p/interface-connection) | [](https://travis-ci.com/libp2p/interface-connection) | [](https://codecov.io/gh/libp2p/interface-connection) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **Transport** |
|
||||
| [`interface-transport`](//github.com/libp2p/interface-transport) | [](//github.com/libp2p/interface-transport/releases) | [](https://david-dm.org/libp2p/interface-transport) | [](https://travis-ci.com/libp2p/interface-transport) | [](https://codecov.io/gh/libp2p/interface-transport) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-tcp`](//github.com/libp2p/js-libp2p-tcp) | [](//github.com/libp2p/js-libp2p-tcp/releases) | [](https://david-dm.org/libp2p/js-libp2p-tcp) | [](https://travis-ci.com/libp2p/js-libp2p-tcp) | [](https://codecov.io/gh/libp2p/js-libp2p-tcp) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-udp`](//github.com/libp2p/js-libp2p-udp) | [](//github.com/libp2p/js-libp2p-udp/releases) | [](https://david-dm.org/libp2p/js-libp2p-udp) | [](https://travis-ci.com/libp2p/js-libp2p-udp) | [](https://codecov.io/gh/libp2p/js-libp2p-udp) | N/A |
|
||||
| [`libp2p-udt`](//github.com/libp2p/js-libp2p-udt) | [](//github.com/libp2p/js-libp2p-udt/releases) | [](https://david-dm.org/libp2p/js-libp2p-udt) | [](https://travis-ci.com/libp2p/js-libp2p-udt) | [](https://codecov.io/gh/libp2p/js-libp2p-udt) | N/A |
|
||||
| [`libp2p-utp`](//github.com/libp2p/js-libp2p-utp) | [](//github.com/libp2p/js-libp2p-utp/releases) | [](https://david-dm.org/libp2p/js-libp2p-utp) | [](https://travis-ci.com/libp2p/js-libp2p-utp) | [](https://codecov.io/gh/libp2p/js-libp2p-utp) | N/A |
|
||||
| [`libp2p-webrtc-direct`](//github.com/libp2p/js-libp2p-webrtc-direct) | [](//github.com/libp2p/js-libp2p-webrtc-direct/releases) | [](https://david-dm.org/libp2p/js-libp2p-webrtc-direct) | [](https://travis-ci.com/libp2p/js-libp2p-webrtc-direct) | [](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-websockets`](//github.com/libp2p/js-libp2p-websockets) | [](//github.com/libp2p/js-libp2p-websockets/releases) | [](https://david-dm.org/libp2p/js-libp2p-websockets) | [](https://travis-ci.com/libp2p/js-libp2p-websockets) | [](https://codecov.io/gh/libp2p/js-libp2p-websockets) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-websocket-star`](//github.com/libp2p/js-libp2p-websocket-star) | [](//github.com/libp2p/js-libp2p-websocket-star/releases) | [](https://david-dm.org/libp2p/js-libp2p-websocket-star) | [](https://travis-ci.com/libp2p/js-libp2p-websocket-star) | [](https://codecov.io/gh/libp2p/js-libp2p-websocket-star) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-websocket-star-rendezvous`](//github.com/libp2p/js-libp2p-websocket-star-rendezvous) | [](//github.com/libp2p/js-libp2p-websocket-star-rendezvous/releases) | [](https://david-dm.org/libp2p/js-libp2p-websocket-star-rendezvous) | [](https://travis-ci.com/libp2p/js-libp2p-websocket-star-rendezvous) | [](https://codecov.io/gh/libp2p/js-libp2p-websocket-star-rendezvous) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **Crypto Channels** |
|
||||
| [`libp2p-secio`](//github.com/libp2p/js-libp2p-secio) | [](//github.com/libp2p/js-libp2p-secio/releases) | [](https://david-dm.org/libp2p/js-libp2p-secio) | [](https://travis-ci.com/libp2p/js-libp2p-secio) | [](https://codecov.io/gh/libp2p/js-libp2p-secio) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
|
||||
| **Stream Muxers** |
|
||||
| [`interface-stream-muxer`](//github.com/libp2p/interface-stream-muxer) | [](//github.com/libp2p/interface-stream-muxer/releases) | [](https://david-dm.org/libp2p/interface-stream-muxer) | [](https://travis-ci.com/libp2p/interface-stream-muxer) | [](https://codecov.io/gh/libp2p/interface-stream-muxer) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-mplex`](//github.com/libp2p/js-libp2p-mplex) | [](//github.com/libp2p/js-libp2p-mplex/releases) | [](https://david-dm.org/libp2p/js-libp2p-mplex) | [](https://travis-ci.com/libp2p/js-libp2p-mplex) | [](https://codecov.io/gh/libp2p/js-libp2p-mplex) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-spdy`](//github.com/libp2p/js-libp2p-spdy) | [](//github.com/libp2p/js-libp2p-spdy/releases) | [](https://david-dm.org/libp2p/js-libp2p-spdy) | [](https://travis-ci.com/libp2p/js-libp2p-spdy) | [](https://codecov.io/gh/libp2p/js-libp2p-spdy) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **Discovery** |
|
||||
| [`interface-peer-discovery`](//github.com/libp2p/interface-peer-discovery) | [](//github.com/libp2p/interface-peer-discovery/releases) | [](https://david-dm.org/libp2p/interface-peer-discovery) | [](https://travis-ci.com/libp2p/interface-peer-discovery) | [](https://codecov.io/gh/libp2p/interface-peer-discovery) | N/A |
|
||||
| [`libp2p-bootstrap`](//github.com/libp2p/js-libp2p-bootstrap) | [](//github.com/libp2p/js-libp2p-bootstrap/releases) | [](https://david-dm.org/libp2p/js-libp2p-bootstrap) | [](https://travis-ci.com/libp2p/js-libp2p-bootstrap) | [](https://codecov.io/gh/libp2p/js-libp2p-bootstrap) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [](//github.com/libp2p/js-libp2p-kad-dht/releases) | [](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-mdns`](//github.com/libp2p/js-libp2p-mdns) | [](//github.com/libp2p/js-libp2p-mdns/releases) | [](https://david-dm.org/libp2p/js-libp2p-mdns) | [](https://travis-ci.com/libp2p/js-libp2p-mdns) | [](https://codecov.io/gh/libp2p/js-libp2p-mdns) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-rendezvous`](//github.com/libp2p/js-libp2p-rendezvous) | [](//github.com/libp2p/js-libp2p-rendezvous/releases) | [](https://david-dm.org/libp2p/js-libp2p-rendezvous) | [](https://travis-ci.com/libp2p/js-libp2p-rendezvous) | [](https://codecov.io/gh/libp2p/js-libp2p-rendezvous) | N/A |
|
||||
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-websocket-star`](//github.com/libp2p/js-libp2p-websocket-star) | [](//github.com/libp2p/js-libp2p-websocket-star/releases) | [](https://david-dm.org/libp2p/js-libp2p-websocket-star) | [](https://travis-ci.com/libp2p/js-libp2p-websocket-star) | [](https://codecov.io/gh/libp2p/js-libp2p-websocket-star) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **NAT Traversal** |
|
||||
| [`libp2p-circuit`](//github.com/libp2p/js-libp2p-circuit) | [](//github.com/libp2p/js-libp2p-circuit/releases) | [](https://david-dm.org/libp2p/js-libp2p-circuit) | [](https://travis-ci.com/libp2p/js-libp2p-circuit) | [](https://codecov.io/gh/libp2p/js-libp2p-circuit) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-nat-mngr`](//github.com/libp2p/js-libp2p-nat-mngr) | [](//github.com/libp2p/js-libp2p-nat-mngr/releases) | [](https://david-dm.org/libp2p/js-libp2p-nat-mngr) | [](https://travis-ci.com/libp2p/js-libp2p-nat-mngr) | [](https://codecov.io/gh/libp2p/js-libp2p-nat-mngr) | N/A |
|
||||
| **Data Types** |
|
||||
| [`peer-book`](//github.com/libp2p/js-peer-book) | [](//github.com/libp2p/js-peer-book/releases) | [](https://david-dm.org/libp2p/js-peer-book) | [](https://travis-ci.com/libp2p/js-peer-book) | [](https://codecov.io/gh/libp2p/js-peer-book) | [Pedro Teixeira](mailto:i@pgte.me) |
|
||||
| [`peer-id`](//github.com/libp2p/js-peer-id) | [](//github.com/libp2p/js-peer-id/releases) | [](https://david-dm.org/libp2p/js-peer-id) | [](https://travis-ci.com/libp2p/js-peer-id) | [](https://codecov.io/gh/libp2p/js-peer-id) | [Pedro Teixeira](mailto:i@pgte.me) |
|
||||
| [`peer-info`](//github.com/libp2p/js-peer-info) | [](//github.com/libp2p/js-peer-info/releases) | [](https://david-dm.org/libp2p/js-peer-info) | [](https://travis-ci.com/libp2p/js-peer-info) | [](https://codecov.io/gh/libp2p/js-peer-info) | [Pedro Teixeira](mailto:i@pgte.me) |
|
||||
| **Content Routing** |
|
||||
| [`interface-content-routing`](//github.com/libp2p/interface-content-routing) | [](//github.com/libp2p/interface-content-routing/releases) | [](https://david-dm.org/libp2p/interface-content-routing) | [](https://travis-ci.com/libp2p/interface-content-routing) | [](https://codecov.io/gh/libp2p/interface-content-routing) | N/A |
|
||||
| [`libp2p-delegated-content-routing`](//github.com/libp2p/js-libp2p-delegated-content-routing) | [](//github.com/libp2p/js-libp2p-delegated-content-routing/releases) | [](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing) | [](https://travis-ci.com/libp2p/js-libp2p-delegated-content-routing) | [](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) | [](//github.com/libp2p/js-libp2p-kad-dht/releases) | [](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| **Peer Routing** |
|
||||
| [`interface-peer-routing`](//github.com/libp2p/interface-peer-routing) | [](//github.com/libp2p/interface-peer-routing/releases) | [](https://david-dm.org/libp2p/interface-peer-routing) | [](https://travis-ci.com/libp2p/interface-peer-routing) | [](https://codecov.io/gh/libp2p/interface-peer-routing) | N/A |
|
||||
| [`libp2p-delegated-peer-routing`](//github.com/libp2p/js-libp2p-delegated-peer-routing) | [](//github.com/libp2p/js-libp2p-delegated-peer-routing/releases) | [](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing) | [](https://travis-ci.com/libp2p/js-libp2p-delegated-peer-routing) | [](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [](//github.com/libp2p/js-libp2p-kad-dht/releases) | [](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| **Record Store** |
|
||||
| [`interface-record-store`](//github.com/libp2p/interface-record-store) | [](//github.com/libp2p/interface-record-store/releases) | [](https://david-dm.org/libp2p/interface-record-store) | [](https://travis-ci.com/libp2p/interface-record-store) | [](https://codecov.io/gh/libp2p/interface-record-store) | N/A |
|
||||
| [`libp2p-record`](//github.com/libp2p/js-libp2p-record) | [](//github.com/libp2p/js-libp2p-record/releases) | [](https://david-dm.org/libp2p/js-libp2p-record) | [](https://travis-ci.com/libp2p/js-libp2p-record) | [](https://codecov.io/gh/libp2p/js-libp2p-record) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| **Generics** |
|
||||
| [`libp2p-connection-manager`](//github.com/libp2p/js-libp2p-connection-manager) | [](//github.com/libp2p/js-libp2p-connection-manager/releases) | [](https://david-dm.org/libp2p/js-libp2p-connection-manager) | [](https://travis-ci.com/libp2p/js-libp2p-connection-manager) | [](https://codecov.io/gh/libp2p/js-libp2p-connection-manager) | N/A |
|
||||
| [`libp2p-crypto`](//github.com/libp2p/js-libp2p-crypto) | [](//github.com/libp2p/js-libp2p-crypto/releases) | [](https://david-dm.org/libp2p/js-libp2p-crypto) | [](https://travis-ci.com/libp2p/js-libp2p-crypto) | [](https://codecov.io/gh/libp2p/js-libp2p-crypto) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
|
||||
| [`libp2p-crypto-secp256k1`](//github.com/libp2p/js-libp2p-crypto-secp256k1) | [](//github.com/libp2p/js-libp2p-crypto-secp256k1/releases) | [](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1) | [](https://travis-ci.com/libp2p/js-libp2p-crypto-secp256k1) | [](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
|
||||
| [`libp2p-switch`](//github.com/libp2p/js-libp2p-switch) | [](//github.com/libp2p/js-libp2p-switch/releases) | [](https://david-dm.org/libp2p/js-libp2p-switch) | [](https://travis-ci.com/libp2p/js-libp2p-switch) | [](https://codecov.io/gh/libp2p/js-libp2p-switch) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **Extensions** |
|
||||
| [`libp2p-floodsub`](//github.com/libp2p/js-libp2p-floodsub) | [](//github.com/libp2p/js-libp2p-floodsub/releases) | [](https://david-dm.org/libp2p/js-libp2p-floodsub) | [](https://travis-ci.com/libp2p/js-libp2p-floodsub) | [](https://codecov.io/gh/libp2p/js-libp2p-floodsub) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-identify`](//github.com/libp2p/js-libp2p-identify) | [](//github.com/libp2p/js-libp2p-identify/releases) | [](https://david-dm.org/libp2p/js-libp2p-identify) | [](https://travis-ci.com/libp2p/js-libp2p-identify) | [](https://codecov.io/gh/libp2p/js-libp2p-identify) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-keychain`](//github.com/libp2p/js-libp2p-keychain) | [](//github.com/libp2p/js-libp2p-keychain/releases) | [](https://david-dm.org/libp2p/js-libp2p-keychain) | [](https://travis-ci.com/libp2p/js-libp2p-keychain) | [](https://codecov.io/gh/libp2p/js-libp2p-keychain) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-ping`](//github.com/libp2p/js-libp2p-ping) | [](//github.com/libp2p/js-libp2p-ping/releases) | [](https://david-dm.org/libp2p/js-libp2p-ping) | [](https://travis-ci.com/libp2p/js-libp2p-ping) | [](https://codecov.io/gh/libp2p/js-libp2p-ping) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-pnet`](//github.com/libp2p/js-libp2p-pnet) | [](//github.com/libp2p/js-libp2p-pnet/releases) | [](https://david-dm.org/libp2p/js-libp2p-pnet) | [](https://travis-ci.com/libp2p/js-libp2p-pnet) | [](https://codecov.io/gh/libp2p/js-libp2p-pnet) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **Utilities** |
|
||||
| [`p2pcat`](//github.com/libp2p/js-p2pcat) | [](//github.com/libp2p/js-p2pcat/releases) | [](https://david-dm.org/libp2p/js-p2pcat) | [](https://travis-ci.com/libp2p/js-p2pcat) | [](https://codecov.io/gh/libp2p/js-p2pcat) | N/A |
|
||||
| **libp2p** |
|
||||
| [`libp2p`](//github.com/libp2p/js-libp2p) | [](//github.com/libp2p/js-libp2p/releases) | [](https://david-dm.org/libp2p/js-libp2p) | [](https://travis-ci.com/libp2p/js-libp2p) | [](https://codecov.io/gh/libp2p/js-libp2p) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-daemon`](//github.com/libp2p/js-libp2p-daemon) | [](//github.com/libp2p/js-libp2p-daemon/releases) | [](https://david-dm.org/libp2p/js-libp2p-daemon) | [](https://travis-ci.com/libp2p/js-libp2p-daemon) | [](https://codecov.io/gh/libp2p/js-libp2p-daemon) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-daemon-client`](//github.com/libp2p/js-libp2p-daemon-client) | [](//github.com/libp2p/js-libp2p-daemon-client/releases) | [](https://david-dm.org/libp2p/js-libp2p-daemon-client) | [](https://travis-ci.com/libp2p/js-libp2p-daemon-client) | [](https://codecov.io/gh/libp2p/js-libp2p-daemon-client) | [Vasco Santos](mailto:santos.vasco10@gmail.com) |
|
||||
| [`libp2p-interfaces`](//github.com/libp2p/js-interfaces) | [](//github.com/libp2p/js-interfaces/releases) | [](https://david-dm.org/libp2p/js-interfaces) | [](https://travis-ci.com/libp2p/js-interfaces) | [](https://codecov.io/gh/libp2p/js-interfaces) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`interop-libp2p`](//github.com/libp2p/interop) | [](//github.com/libp2p/interop/releases) | [](https://david-dm.org/libp2p/interop) | [](https://travis-ci.com/libp2p/interop) | [](https://codecov.io/gh/libp2p/interop) | [Vasco Santos](mailto:santos.vasco10@gmail.com) |
|
||||
| **transports** |
|
||||
| [`libp2p-tcp`](//github.com/libp2p/js-libp2p-tcp) | [](//github.com/libp2p/js-libp2p-tcp/releases) | [](https://david-dm.org/libp2p/js-libp2p-tcp) | [](https://travis-ci.com/libp2p/js-libp2p-tcp) | [](https://codecov.io/gh/libp2p/js-libp2p-tcp) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-webrtc-direct`](//github.com/libp2p/js-libp2p-webrtc-direct) | [](//github.com/libp2p/js-libp2p-webrtc-direct/releases) | [](https://david-dm.org/libp2p/js-libp2p-webrtc-direct) | [](https://travis-ci.com/libp2p/js-libp2p-webrtc-direct) | [](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-websockets`](//github.com/libp2p/js-libp2p-websockets) | [](//github.com/libp2p/js-libp2p-websockets/releases) | [](https://david-dm.org/libp2p/js-libp2p-websockets) | [](https://travis-ci.com/libp2p/js-libp2p-websockets) | [](https://codecov.io/gh/libp2p/js-libp2p-websockets) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **secure channels** |
|
||||
| [`libp2p-noise`](//github.com/NodeFactoryIo/js-libp2p-noise) | [](//github.com/NodeFactoryIo/js-libp2p-noise/releases) | [](https://david-dm.org/NodeFactoryIo/js-libp2p-noise) | [](https://travis-ci.com/NodeFactoryIo/js-libp2p-noise) | [](https://codecov.io/gh/NodeFactoryIo/js-libp2p-noise) | N/A |
|
||||
| [`libp2p-secio`](//github.com/libp2p/js-libp2p-secio) | [](//github.com/libp2p/js-libp2p-secio/releases) | [](https://david-dm.org/libp2p/js-libp2p-secio) | [](https://travis-ci.com/libp2p/js-libp2p-secio) | [](https://codecov.io/gh/libp2p/js-libp2p-secio) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
|
||||
| **stream multiplexers** |
|
||||
| [`libp2p-mplex`](//github.com/libp2p/js-libp2p-mplex) | [](//github.com/libp2p/js-libp2p-mplex/releases) | [](https://david-dm.org/libp2p/js-libp2p-mplex) | [](https://travis-ci.com/libp2p/js-libp2p-mplex) | [](https://codecov.io/gh/libp2p/js-libp2p-mplex) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| **peer discovery** |
|
||||
| [`libp2p-bootstrap`](//github.com/libp2p/js-libp2p-bootstrap) | [](//github.com/libp2p/js-libp2p-bootstrap/releases) | [](https://david-dm.org/libp2p/js-libp2p-bootstrap) | [](https://travis-ci.com/libp2p/js-libp2p-bootstrap) | [](https://codecov.io/gh/libp2p/js-libp2p-bootstrap) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [](//github.com/libp2p/js-libp2p-kad-dht/releases) | [](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-mdns`](//github.com/libp2p/js-libp2p-mdns) | [](//github.com/libp2p/js-libp2p-mdns/releases) | [](https://david-dm.org/libp2p/js-libp2p-mdns) | [](https://travis-ci.com/libp2p/js-libp2p-mdns) | [](https://codecov.io/gh/libp2p/js-libp2p-mdns) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`@chainsafe/discv5`](//github.com/ChainSafe/discv5) | [](//github.com/ChainSafe/discv5/releases) | [](https://david-dm.org/ChainSafe/discv5) | [](https://travis-ci.com/ChainSafe/discv5) | [](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) | [](//github.com/libp2p/js-libp2p-delegated-content-routing/releases) | [](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing) | [](https://travis-ci.com/libp2p/js-libp2p-delegated-content-routing) | [](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) | [](//github.com/libp2p/js-libp2p-kad-dht/releases) | [](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| **peer routing** |
|
||||
| [`libp2p-delegated-peer-routing`](//github.com/libp2p/js-libp2p-delegated-peer-routing) | [](//github.com/libp2p/js-libp2p-delegated-peer-routing/releases) | [](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing) | [](https://travis-ci.com/libp2p/js-libp2p-delegated-peer-routing) | [](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [](//github.com/libp2p/js-libp2p-kad-dht/releases) | [](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| **utilities** |
|
||||
| [`libp2p-crypto`](//github.com/libp2p/js-libp2p-crypto) | [](//github.com/libp2p/js-libp2p-crypto/releases) | [](https://david-dm.org/libp2p/js-libp2p-crypto) | [](https://travis-ci.com/libp2p/js-libp2p-crypto) | [](https://codecov.io/gh/libp2p/js-libp2p-crypto) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-crypto-secp256k1`](//github.com/libp2p/js-libp2p-crypto-secp256k1) | [](//github.com/libp2p/js-libp2p-crypto-secp256k1/releases) | [](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1) | [](https://travis-ci.com/libp2p/js-libp2p-crypto-secp256k1) | [](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) | [](//github.com/libp2p/js-peer-id/releases) | [](https://david-dm.org/libp2p/js-peer-id) | [](https://travis-ci.com/libp2p/js-peer-id) | [](https://codecov.io/gh/libp2p/js-peer-id) | [Vasco Santos](mailto:santos.vasco10@gmail.com) |
|
||||
| **pubsub** |
|
||||
| [`libp2p-pubsub`](//github.com/libp2p/js-libp2p-pubsub) | [](//github.com/libp2p/js-libp2p-pubsub/releases) | [](https://david-dm.org/libp2p/js-libp2p-pubsub) | [](https://travis-ci.com/libp2p/js-libp2p-pubsub) | [](https://codecov.io/gh/libp2p/js-libp2p-pubsub) | [Vasco Santos](mailto:santos.vasco10@gmail.com) |
|
||||
| [`libp2p-floodsub`](//github.com/libp2p/js-libp2p-floodsub) | [](//github.com/libp2p/js-libp2p-floodsub/releases) | [](https://david-dm.org/libp2p/js-libp2p-floodsub) | [](https://travis-ci.com/libp2p/js-libp2p-floodsub) | [](https://codecov.io/gh/libp2p/js-libp2p-floodsub) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-gossipsub`](//github.com/ChainSafe/js-libp2p-gossipsub) | [](//github.com/ChainSafe/js-libp2p-gossipsub/releases) | [](https://david-dm.org/ChainSafe/js-libp2p-gossipsub) | [](https://travis-ci.com/ChainSafe/js-libp2p-gossipsub) | [](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) | [](//github.com/libp2p/js-libp2p-nat-mgnr/releases) | [](https://david-dm.org/libp2p/js-libp2p-nat-mgnr) | [](https://travis-ci.com/libp2p/js-libp2p-nat-mgnr) | [](https://codecov.io/gh/libp2p/js-libp2p-nat-mgnr) | N/A |
|
||||
| [`libp2p-utils`](//github.com/libp2p/js-libp2p-utils) | [](//github.com/libp2p/js-libp2p-utils/releases) | [](https://david-dm.org/libp2p/js-libp2p-utils) | [](https://travis-ci.com/libp2p/js-libp2p-utils) | [](https://codecov.io/gh/libp2p/js-libp2p-utils) | [Vasco Santos](mailto:santos.vasco10@gmail.com) |
|
||||
|
||||
## Contribute
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
- Documentation
|
||||
- [ ] Ensure that README.md is up to date
|
||||
- [ ] Ensure that all the examples run
|
||||
- [ ] 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)
|
||||
@ -50,8 +51,8 @@ Would you like to contribute to the libp2p project and don't know how? Well, the
|
||||
- Check the issues with the `help wanted` label in the [libp2p repo](https://github.com/libp2p/js-libp2p/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
|
||||
- Join an IPFS All Hands, introduce yourself and let us know where you would like to contribute - https://github.com/ipfs/team-mgmt#all-hands-call
|
||||
- Hack with IPFS and show us what you made! The All Hands call is also the perfect venue for demos, join in and show us what you built
|
||||
- Join the discussion at http://discuss.ipfs.io/ and help users finding their answers.
|
||||
- Join the [⚡️ⒿⓈ Core Dev Team Weekly Sync 🙌🏽 ](https://github.com/ipfs/team-mgmt/issues/650) and be part of the Sprint action!
|
||||
- Join the discussion at http://discuss.libp2p.io/ and help users finding their answers.
|
||||
- Join the [⚡️libp2p Weekly Sync 🙌🏽](https://github.com/libp2p/team-mgmt/issues/16) and be part of the Sprint action!
|
||||
|
||||
# ⁉️ Do you have questions?
|
||||
|
||||
|
1888
doc/API.md
Normal file
1888
doc/API.md
Normal file
File diff suppressed because it is too large
Load Diff
657
doc/CONFIGURATION.md
Normal file
657
doc/CONFIGURATION.md
Normal file
@ -0,0 +1,657 @@
|
||||
# Configuration
|
||||
|
||||
- [Configuration](#configuration)
|
||||
- [Overview](#overview)
|
||||
- [Modules](#modules)
|
||||
- [Transport](#transport)
|
||||
- [Stream Multiplexing](#stream-multiplexing)
|
||||
- [Connection Encryption](#connection-encryption)
|
||||
- [Peer Discovery](#peer-discovery)
|
||||
- [Content Routing](#content-routing)
|
||||
- [Peer Routing](#peer-routing)
|
||||
- [DHT](#dht)
|
||||
- [Pubsub](#pubsub)
|
||||
- [Customizing libp2p](#customizing-libp2p)
|
||||
- [Examples](#examples)
|
||||
- [Basic setup](#basic-setup)
|
||||
- [Customizing Peer Discovery](#customizing-peer-discovery)
|
||||
- [Setup webrtc transport and discovery](#setup-webrtc-transport-and-discovery)
|
||||
- [Customizing Pubsub](#customizing-pubsub)
|
||||
- [Customizing DHT](#customizing-dht)
|
||||
- [Setup with Content and Peer Routing](#setup-with-content-and-peer-routing)
|
||||
- [Setup with Relay](#setup-with-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)
|
||||
- [Configuration examples](#configuration-examples)
|
||||
|
||||
## Overview
|
||||
|
||||
libp2p is a modular networking stack. It's designed to be able to suit a variety of project needs. The configuration of libp2p is a key part of its structure. It enables you to bring exactly what you need, and only what you need. This document is a guide on how to configure libp2p for your particular project. Check out the [Configuration examples](#configuration-examples) section if you're just looking to leverage an existing configuration.
|
||||
|
||||
Regardless of how you configure libp2p, the top level [API](./API.md) will always remain the same. **Note**: if some modules are not configured, like Content Routing, using those methods will throw errors.
|
||||
|
||||
## Modules
|
||||
|
||||
`js-libp2p` acts as the composer for this modular p2p networking stack using libp2p compatible modules as its subsystems. For getting an instance of `js-libp2p` compliant with all types of networking requirements, it is possible to specify the following subsystems:
|
||||
|
||||
- Transports
|
||||
- Multiplexers
|
||||
- Connection encryption mechanisms
|
||||
- Peer discovery protocols
|
||||
- Content routing protocols
|
||||
- Peer routing protocols
|
||||
- DHT implementation
|
||||
- Pubsub router
|
||||
|
||||
The libp2p ecosystem contains at least one module for each of these subsystems. The user should install and import the modules that are relevant for their requirements. Moreover, thanks to the existing interfaces it is easy to create a libp2p compatible module and use it.
|
||||
|
||||
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.
|
||||
|
||||
### Transport
|
||||
|
||||
> In a p2p system, we need to interact with other peers in the network. Transports are used to establish connections between peers. The libp2p transports to use should be decided according to the environment where your node will live, as well as other requirements that you might have.
|
||||
|
||||
Some available transports are:
|
||||
|
||||
- [libp2p/js-libp2p-tcp](https://github.com/libp2p/js-libp2p-tcp)
|
||||
- [libp2p/js-libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star)
|
||||
- [libp2p/js-libp2p-webrtc-direct](https://github.com/libp2p/js-libp2p-webrtc-direct)
|
||||
- [libp2p/js-libp2p-websockets](https://github.com/libp2p/js-libp2p-websockets)
|
||||
- [libp2p/js-libp2p-utp](https://github.com/libp2p/js-libp2p-utp) (Work in Progress)
|
||||
|
||||
You should take into consideration that `js-libp2p-tcp` and `js-libp2p-utp` are not available in a **browser** environment.
|
||||
|
||||
If none of the available transports fulfills your needs, you can create a libp2p compatible transport. A libp2p transport just needs to be compliant with the [Transport Interface](https://github.com/libp2p/js-interfaces/tree/master/src/transport).
|
||||
|
||||
If you want to know more about libp2p transports, you should read the following content:
|
||||
|
||||
- https://docs.libp2p.io/concepts/transport
|
||||
- https://github.com/libp2p/specs/tree/master/connections
|
||||
|
||||
### Stream Multiplexing
|
||||
|
||||
> Libp2p peers will need to communicate with each other through several protocols during their life. Stream multiplexing allows multiple independent logical streams to share a common underlying transport medium, instead of creating a new connection with the same peer per needed protocol.
|
||||
|
||||
Some available stream multiplexers are:
|
||||
|
||||
- [libp2p/js-libp2p-mplex](https://github.com/libp2p/js-libp2p-mplex)
|
||||
|
||||
If none of the available stream multiplexers fulfills your needs, you can create a libp2p compatible stream multiplexer. A libp2p multiplexer just needs to be compliant with the [Stream Muxer Interface](https://github.com/libp2p/js-interfaces/tree/master/src/stream-muxer).
|
||||
|
||||
If you want to know more about libp2p stream multiplexing, you should read the following content:
|
||||
|
||||
- https://docs.libp2p.io/concepts/stream-multiplexing
|
||||
- https://github.com/libp2p/specs/tree/master/connections
|
||||
- https://github.com/libp2p/specs/tree/master/mplex
|
||||
|
||||
### Connection Encryption
|
||||
|
||||
> A connection encryption mechanism must be used, in order to ensure all exchanged data between two peers is encrypted.
|
||||
|
||||
Some available connection encryption protocols:
|
||||
|
||||
- [NodeFactoryIo/js-libp2p-noise](https://github.com/NodeFactoryIo/js-libp2p-noise)
|
||||
- [libp2p/js-libp2p-secio](https://github.com/libp2p/js-libp2p-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).
|
||||
|
||||
If you want to know more about libp2p connection encryption, you should read the following content:
|
||||
|
||||
- https://docs.libp2p.io/concepts/secure-comms
|
||||
- https://github.com/libp2p/specs/tree/master/connections
|
||||
|
||||
### Peer Discovery
|
||||
|
||||
> In a p2p network, peer discovery is critical to a functioning system.
|
||||
|
||||
Some available peer discovery modules are:
|
||||
|
||||
- [js-libp2p-mdns](https://github.com/libp2p/js-libp2p-mdns)
|
||||
- [js-libp2p-bootstrap](https://github.com/libp2p/js-libp2p-bootstrap)
|
||||
- [js-libp2p-kad-dht](https://github.com/libp2p/js-libp2p-kad-dht)
|
||||
- [js-libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star)
|
||||
- [discv5](https://github.com/chainsafe/discv5)
|
||||
|
||||
**Note**: `peer-discovery` services within transports (such as `js-libp2p-webrtc-star`) are automatically gathered from the `transport`, via it's `discovery` property. As such, they do not need to be added in the discovery modules. However, these transports can also be configured and disabled as the other ones.
|
||||
|
||||
If none of the available peer discovery protocols fulfills your needs, you can create a libp2p compatible one. A libp2p peer discovery protocol just needs to be compliant with the [Peer Discovery Interface](https://github.com/libp2p/js-interfaces/tree/master/src/peer-discovery).
|
||||
|
||||
If you want to know more about libp2p peer discovery, you should read the following content:
|
||||
|
||||
- https://github.com/libp2p/specs/blob/master/discovery/mdns.md
|
||||
|
||||
### Content Routing
|
||||
|
||||
> Content routing provides a way to find where content lives in the network. It works in two steps: 1) Peers provide (announce) to the network that they are holders of specific content and 2) Peers issue queries to find where that content lives. A Content Routing mechanism could be as complex as a DHT or as simple as a registry somewhere in the network.
|
||||
|
||||
Some available content routing modules are:
|
||||
|
||||
- [js-libp2p-kad-dht](https://github.com/libp2p/js-libp2p-kad-dht)
|
||||
- [js-libp2p-delegated-content-routing](https://github.com/libp2p/js-libp2p-delegated-content-routing)
|
||||
|
||||
If none of the available content routing protocols fulfills your needs, you can create a libp2p compatible one. A libp2p content routing protocol just needs to be compliant with the [Content Routing Interface](https://github.com/libp2p/js-interfaces/tree/master/src/content-routing). **(WIP: This module is not yet implemented)**
|
||||
|
||||
If you want to know more about libp2p content routing, you should read the following content:
|
||||
|
||||
- https://docs.libp2p.io/concepts/content-routing
|
||||
|
||||
### Peer Routing
|
||||
|
||||
> Peer Routing offers a way to find other peers in the network by issuing queries using a Peer Routing algorithm, which may be iterative or recursive. If the algorithm is unable to find the target peer, it will return the peers that are "closest" to the target peer, using a distance metric defined by the algorithm.
|
||||
|
||||
Some available peer routing modules are:
|
||||
|
||||
- [js-libp2p-kad-dht](https://github.com/libp2p/js-libp2p-kad-dht)
|
||||
- [js-libp2p-delegated-peer-routing](https://github.com/libp2p/js-libp2p-delegated-peer-routing)
|
||||
|
||||
If none of the available peer routing protocols fulfills your needs, you can create a libp2p compatible one. A libp2p peer routing protocol just needs to be compliant with the [Peer Routing Interface](https://github.com/libp2p/js-interfaces/tree/master/src/peer-routing). **(WIP: This module is not yet implemented)**
|
||||
|
||||
If you want to know more about libp2p peer routing, you should read the following content:
|
||||
|
||||
- https://docs.libp2p.io/concepts/peer-routing
|
||||
|
||||
### DHT
|
||||
|
||||
> A DHT can provide content and peer routing capabilities in a p2p system, as well as peer discovery capabilities.
|
||||
|
||||
The DHT implementation currently available is [libp2p/js-libp2p-kad-dht](https://github.com/libp2p/js-libp2p-kad-dht). This implementation is largely based on the Kademlia whitepaper, augmented with notions from S/Kademlia, Coral and mainlineDHT.
|
||||
|
||||
If this DHT implementation does not fulfill your needs and you want to create or use your own implementation, please get in touch with us through a github issue. We plan to work on improving the ability to bring your own DHT in a future release.
|
||||
|
||||
If you want to know more about libp2p DHT, you should read the following content:
|
||||
|
||||
- https://docs.libp2p.io/concepts/protocols/#kad-dht
|
||||
- https://github.com/libp2p/specs/pull/108
|
||||
|
||||
### Pubsub
|
||||
|
||||
> Publish/Subscribe is a system where peers congregate around topics they are interested in. Peers interested in a topic are said to be subscribed to that topic and should receive the data published on it from other peers.
|
||||
|
||||
Some available pubsub routers are:
|
||||
|
||||
- [libp2p/js-libp2p-floodsub](https://github.com/libp2p/js-libp2p-floodsub)
|
||||
- [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.
|
||||
|
||||
If you want to know more about libp2p pubsub, you should read the following content:
|
||||
|
||||
- https://docs.libp2p.io/concepts/publish-subscribe
|
||||
- https://github.com/libp2p/specs/tree/master/pubsub
|
||||
|
||||
## Customizing libp2p
|
||||
|
||||
When [creating a libp2p node](./API.md#create), the modules needed should be specified as follows:
|
||||
|
||||
```js
|
||||
const modules = {
|
||||
transport: [],
|
||||
streamMuxer: [],
|
||||
connEncryption: [],
|
||||
contentRouting: [],
|
||||
peerRouting: [],
|
||||
peerDiscovery: [],
|
||||
dht: dhtImplementation,
|
||||
pubsub: pubsubImplementation
|
||||
}
|
||||
```
|
||||
|
||||
Moreover, the majority of the modules can be customized via option parameters. This way, it is also possible to provide this options through a `config` object. This config object should have the property name of each building block to configure, the same way as the modules specification.
|
||||
|
||||
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.
|
||||
- `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 `noAnnounce` properties with `Array<string>`:
|
||||
- `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.
|
||||
- `noAnnounce` addresses will be used as a filter to compute the advertises that the node should advertise to the network.
|
||||
|
||||
### Examples
|
||||
|
||||
#### Basic setup
|
||||
|
||||
```js
|
||||
// Creating a libp2p node with:
|
||||
// transport: websockets + tcp
|
||||
// stream-muxing: mplex
|
||||
// crypto-channel: secio
|
||||
// discovery: multicast-dns
|
||||
// dht: kad-dht
|
||||
// pubsub: gossipsub
|
||||
|
||||
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 MulticastDNS = require('libp2p-mdns')
|
||||
const DHT = require('libp2p-kad-dht')
|
||||
const GossipSub = require('libp2p-gossipsub')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [
|
||||
TCP,
|
||||
new WS() // It can take instances too!
|
||||
],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO],
|
||||
peerDiscovery: [MulticastDNS],
|
||||
dht: DHT,
|
||||
pubsub: GossipSub
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Customizing Peer Discovery
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO],
|
||||
peerDiscovery: [MulticastDNS]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
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
|
||||
}
|
||||
// .. other discovery module options.
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Setup webrtc transport and discovery
|
||||
|
||||
```js
|
||||
|
||||
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 node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [
|
||||
WS,
|
||||
WebRTCStar
|
||||
],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO],
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
[WebRTCStar.tag]: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Customizing Pubsub
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const GossipSub = require('libp2p-gossipsub')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO],
|
||||
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
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Customizing DHT
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const DHT = require('libp2p-kad-dht')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO],
|
||||
dht: DHT
|
||||
},
|
||||
config: {
|
||||
dht: { // The DHT options (and defaults) can be found in its documentation
|
||||
kBucketSize: 20,
|
||||
enabled: true,
|
||||
randomWalk: {
|
||||
enabled: true, // Allows to disable discovery (enabled by default)
|
||||
interval: 300e3,
|
||||
timeout: 10e3
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Setup with Content and Peer Routing
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
|
||||
const DelegatedContentRouter = require('libp2p-delegated-content-routing')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
// create a peerId
|
||||
const peerId = await PeerId.create()
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO],
|
||||
contentRouting: [
|
||||
new DelegatedContentRouter(peerId)
|
||||
],
|
||||
peerRouting: [
|
||||
new DelegatedPeerRouter()
|
||||
],
|
||||
},
|
||||
peerId
|
||||
})
|
||||
```
|
||||
|
||||
#### Setup with Relay
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
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.
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### 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 SECIO = require('libp2p-secio')
|
||||
const LevelStore = require('datastore-level')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
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. |
|
||||
|
||||
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 SECIO = require('libp2p-secio')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
dialer: {
|
||||
maxParallelDials: 100,
|
||||
maxDialsPerPeer: 4,
|
||||
dialTimeout: 30e3
|
||||
}
|
||||
```
|
||||
|
||||
#### Configuring Connection Manager
|
||||
|
||||
The Connection Manager prunes Connections in libp2p whenever certain limits are exceeded. If Metrics are enabled, you can also configure the Connection Manager to monitor the bandwidth of libp2p and prune connections as needed. You can read more about what Connection Manager does at [./CONNECTION_MANAGER.md](./CONNECTION_MANAGER.md). The configuration values below show the defaults for Connection Manager. See [./CONNECTION_MANAGER.md](./CONNECTION_MANAGER.md#options) for a full description of the parameters.
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
connectionManager: {
|
||||
maxConnections: Infinity,
|
||||
minConnections: 0,
|
||||
pollInterval: 2000,
|
||||
defaultPeerValue: 1,
|
||||
// The below values will only be taken into account when Metrics are enabled
|
||||
maxData: Infinity,
|
||||
maxSentData: Infinity,
|
||||
maxReceivedData: Infinity,
|
||||
maxEventLoopDelay: Infinity,
|
||||
movingAverageInterval: 60000
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Configuring Transport Manager
|
||||
|
||||
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 { FaultTolerance } = require('libp2p/src/transport-manager')}
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
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 SECIO = require('libp2p-secio')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
metrics: {
|
||||
enabled: true,
|
||||
computeThrottleMaxQueueSize: 1000,
|
||||
computeThrottleTimeout: 2000,
|
||||
movingAverageIntervals: [
|
||||
60 * 1000, // 1 minute
|
||||
5 * 60 * 1000, // 5 minutes
|
||||
15 * 60 * 1000 // 15 minutes
|
||||
],
|
||||
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 SECIO = require('libp2p-secio')
|
||||
|
||||
const LevelStore = require('datastore-level')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
datastore: new LevelStore('path/to/store'),
|
||||
peerStore: {
|
||||
persistence: true,
|
||||
threshold: 5
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Customizing Transports
|
||||
|
||||
Some Transports can be passed additional options when they are created. For example, `libp2p-webrtc-star` accepts an optional, custom `wrtc` implementation. In addition to libp2p passing itself and an `Upgrader` to handle connection upgrading, libp2p will also pass the options, if they are provided, from `config.transport`.
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const WebRTCStar = require('libp2p-webrtc-star')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const wrtc = require('wrtc')
|
||||
|
||||
const transportKey = WebRTCStar.prototype[Symbol.toStringTag]
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [WebRTCStar],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
config: {
|
||||
transport: {
|
||||
[transportKey]: {
|
||||
wrtc // You can use `wrtc` when running in Node.js
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 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/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.
|
||||
|
||||
The [examples](../examples) are also a good source of help for finding a configuration that suits your needs.
|
20
doc/CONNECTION_MANAGER.md
Normal file
20
doc/CONNECTION_MANAGER.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Connection Manager
|
||||
|
||||
The Connection Manager works with the Registrar to keep connections across libp2p within acceptable ranges, which can be configured. By default Connection Manager will monitor:
|
||||
- The total number of open connections
|
||||
- The latency/delay of the event loop
|
||||
|
||||
If Metrics are enabled for libp2p, see [./CONFIGURATION.md#configuring-metrics](./CONFIGURATION.md#configuring-metrics) on how to configure metrics, the Connection Manager can be used to prune connections when certain limits are exceeded.
|
||||
|
||||
The following is a list of available options for setting limits for the Connection Manager to enforce.
|
||||
|
||||
## Options
|
||||
- `maxConnections`: the maximum number of connections libp2p is willing to have before it starts disconnecting. Defaults to `Infinity`
|
||||
- `minConnections`: the minimum number of connections below which libp2p not activate preemptive disconnections. Defaults to `0`.
|
||||
- `maxData`: sets the maximum data — in bytes per second - (sent and received) this node is willing to endure before it starts disconnecting peers. Defaults to `Infinity`.
|
||||
- `maxSentData`: sets the maximum sent data — in bytes per second - this node is willing to endure before it starts disconnecting peers. Defaults to `Infinity`.
|
||||
- `maxReceivedData`: sets the maximum received data — in bytes per second - this node is willing to endure before it starts disconnecting peers. Defaults to `Infinity`.
|
||||
- `maxEventLoopDelay`: sets the maximum event loop delay (measured in milliseconds) this node is willing to endure before it starts disconnecting peers. Defaults to `Infinity`.
|
||||
- `pollInterval`: sets the poll interval (in milliseconds) for assessing the current state and determining if this peer needs to force a disconnect. Defaults to `2000` (2 seconds).
|
||||
- `movingAverageInterval`: the interval used to calculate moving averages (in milliseconds). Defaults to `60000` (1 minute). This must be an available interval configured in `Metrics`
|
||||
- `defaultPeerValue`: number between 0 and 1. Defaults to 1.
|
32
doc/DIALER.md
Normal file
32
doc/DIALER.md
Normal file
@ -0,0 +1,32 @@
|
||||
# js-libp2p Dialer
|
||||
|
||||
**Synopsis**
|
||||
* Parallel dials to the same peer will yield the same connection/error when the first dial settles.
|
||||
* All Dial Requests in js-libp2p must request a token(s) from the Dialer.
|
||||
* The number of tokens requested should be between 1 and the MAX_PER_PEER_DIALS max set in the Dialer.
|
||||
* If the number of available tokens is less than requested, the Dialer may return less than requested.
|
||||
* The number of tokens a DialRequest obtains reflects the maximum number of parallel Multiaddr Dials it can make.
|
||||
* If no tokens are available a DialRequest should immediately end and throw.
|
||||
* As tokens are limited, DialRequests should be given a prioritized list of Multiaddrs to minimize the potential request time.
|
||||
* Once a Multiaddr Dial has succeeded, all pending dials in that Dial Request should be aborted.
|
||||
* If DIAL_TIMEOUT time has elapsed before any one Multiaddr Dial succeeds, all remaining dials in the DialRequest should be aborted.
|
||||
* When a Multiaddr Dial is settled, if there are no more addresses to dial, its token should be released back to the dialer.
|
||||
* Once the DialRequest is settled, any remaining tokens should be released to the dialer.
|
||||
|
||||
## Multiaddr Confidence
|
||||
|
||||
An effective dialing system should involve the inclusion of a Confidence system for Multiaddrs. This enables ranking of Multiaddrs so that a prioritized list can be passed to DialRequests to maximize usage of Dialer Tokens, and minimize connection times.
|
||||
|
||||
**Not Yet Implemented**: This system will be designed and implemented in a future update.
|
||||
|
||||
## Notes
|
||||
|
||||
* A DialRequest gets a set of tokens from the Dialer, up to the MAX_PER_PEER_DIALS max.
|
||||
* A DialRequest SHOULD fail if no dial tokens are available. The DialRequest MAY proceed without tokens, but this should be reserved for High Priority actions and should be rare.
|
||||
* A DialRequest MUST NOT request more tokens than it has addresses to dial. Example: If the MAX_PER_PEER_DIALS is 4 and a DialRequest has 1 address, it should only request 1 token.
|
||||
* A DialRequest SHOULD execute parallel dials for each of its addresses up the total number of tokens it has.
|
||||
* On a successful dial, the DialRequest MUST abort any other in progress dials, return the successful connection and release all tokens.
|
||||
* A new DialRequest SHOULD be given a descending list of prioritized Multiaddrs, based on their confidence. Having higher confidence Multiaddrs first can help minimize the time a DialRequest is active.
|
||||
* A failed dial to a Multiaddr SHOULD add that Multiaddr to a temporary denyList.
|
||||
* A failed dial to a Multiaddr SHOULD lower the confidence of that Multiaddr.
|
||||
* A successful dial to a Multiaddr SHOULD increase the confidence of that Multiaddr.
|
256
doc/GETTING_STARTED.md
Normal file
256
doc/GETTING_STARTED.md
Normal file
@ -0,0 +1,256 @@
|
||||
# Getting Started
|
||||
|
||||
Welcome to libp2p! This guide will walk you through setting up a fully functional libp2p node 🚀
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
- [Install](#install)
|
||||
- [Configuring libp2p](#configuring-libp2p)
|
||||
- [Basic setup](#basic-setup)
|
||||
- [Transports](#transports)
|
||||
- [Connection Encryption](#connection-encryption)
|
||||
- [Multiplexing](#multiplexing)
|
||||
- [Running Libp2p](#running-libp2p)
|
||||
- [Custom setup](#custom-setup)
|
||||
- [Peer Discovery](#peer-discovery)
|
||||
- [Pubsub](#pubsub)
|
||||
- [What is next](#what-is-next)
|
||||
|
||||
## Install
|
||||
|
||||
The first step is to install libp2p in your project:
|
||||
|
||||
```sh
|
||||
npm install libp2p
|
||||
```
|
||||
|
||||
## Configuring libp2p
|
||||
|
||||
If you're new to libp2p, we recommend configuring your node in stages, as this can make troubleshooting configuration issues much easier. In this guide, we'll do just that. If you're more experienced with libp2p, you may wish to jump to the [Configuration readme](./CONFIGURATION.md).
|
||||
|
||||
### Basic setup
|
||||
|
||||
Now that we have libp2p installed, let's configure the minimum needed to get your node running. The only modules libp2p requires are a [**Transport**][transport] and [**Crypto**][crypto] module. However, we recommend that a basic setup should also have a [**Stream Multiplexer**](streamMuxer) configured, which we will explain shortly. Let's start by setting up a Transport.
|
||||
|
||||
#### Transports
|
||||
|
||||
Libp2p uses Transports to establish connections between peers over the network. Transports are the components responsible for performing the actual exchange of data between libp2p nodes. You can configure any number of Transports, but you only need 1 to start with. Supporting more Transports will improve the ability of your node to speak to a larger number of nodes on the network, as matching Transports are required for two nodes to communicate with one another.
|
||||
|
||||
You should select Transports according to the runtime of your application; Node.js or the browser. You can see a list of some of the available Transports in the [configuration readme](./CONFIGURATION.md#transport). For this guide let's install `libp2p-websockets`, as it can be used in both Node.js and the browser.
|
||||
|
||||
Start by installing `libp2p-websockets`:
|
||||
|
||||
```sh
|
||||
npm install libp2p-websockets
|
||||
```
|
||||
|
||||
Now that we have the module installed, let's configure libp2p to use the Transport. We'll use the [`Libp2p.create`](./API.md#create) method, which takes a single configuration object as its only parameter. We can add the Transport by passing it into the `modules.transport` array:
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [WebSockets]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
There are multiple libp2p transports available, you should evaluate the needs of your application and select the Transport(s) that best suit your requirements. You can add as many transports as you like to `modules.transport` in order to establish connections with as many peers as possible.
|
||||
|
||||
<details><summary>Read More</summary>
|
||||
If you want to know more about libp2p transports, you should read the following content:
|
||||
|
||||
- https://docs.libp2p.io/concepts/transport
|
||||
- https://github.com/libp2p/specs/tree/master/connections
|
||||
</details>
|
||||
|
||||
#### Connection Encryption
|
||||
|
||||
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-noise` module.
|
||||
|
||||
```sh
|
||||
npm install libp2p-noise
|
||||
```
|
||||
|
||||
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 { NOISE } = require('libp2p-noise')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [WebSockets],
|
||||
connEncryption: [NOISE]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
<details><summary>Read More</summary>
|
||||
If you want to know more about libp2p connection encryption, you should read the following content:
|
||||
|
||||
- https://docs.libp2p.io/concepts/secure-comms
|
||||
- https://github.com/libp2p/specs/tree/master/connections
|
||||
</details>
|
||||
|
||||
#### Multiplexing
|
||||
|
||||
While multiplexers are not strictly required, they are highly recommended as they improve the effectiveness and efficiency of connections for the various protocols libp2p runs. Adding a multiplexer to your configuration will allow libp2p to run several of its internal protocols, like Identify, as well as allow your application to easily run any number of protocols over a single connection.
|
||||
|
||||
Looking at the [available stream multiplexing](./CONFIGURATION.md#stream-multiplexing) modules, js-libp2p currently only supports `libp2p-mplex`, so we will use that here. Bear in mind that future libp2p Transports might have `multiplexing` capabilities already built-in (such as `QUIC`).
|
||||
|
||||
You can install `libp2p-mplex` and add it to your libp2p node as follows in the next example.
|
||||
|
||||
```sh
|
||||
npm install libp2p-mplex
|
||||
```
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [WebSockets],
|
||||
connEncryption: [SECIO],
|
||||
streamMuxer: [MPLEX]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
<details><summary>Read More</summary>
|
||||
If you want to know more about libp2p stream multiplexing, you should read the following content:
|
||||
|
||||
- https://docs.libp2p.io/concepts/stream-multiplexing
|
||||
- https://github.com/libp2p/specs/tree/master/connections
|
||||
- https://github.com/libp2p/specs/tree/master/mplex
|
||||
</details>
|
||||
|
||||
#### Running Libp2p
|
||||
|
||||
Now that you have configured a [**Transport**][transport], [**Crypto**][crypto] and [**Stream Multiplexer**](streamMuxer) module, you can start your libp2p node. We can start and stop libp2p using the [`libp2p.start()`](./API.md#start) and [`libp2p.stop()`](./API.md#stop) methods.
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const SECIO = require('libp2p-secio')
|
||||
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],
|
||||
streamMuxer: [MPLEX]
|
||||
}
|
||||
})
|
||||
|
||||
// start libp2p
|
||||
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')
|
||||
```
|
||||
|
||||
### Custom setup
|
||||
|
||||
Once your libp2p node is running, it is time to get it connected to the public network. We can do this via peer discovery.
|
||||
|
||||
#### Peer Discovery
|
||||
|
||||
Peer discovery is an important part of creating a well connected libp2p node. A static list of peers will often be used to join the network, but it's useful to couple other discovery mechanisms to ensure you're able to discover other peers that are important to your application.
|
||||
|
||||
For each discovered peer libp2p will emit a `peer:discovery` event which includes metadata about that peer. You can read the [Events](./API.md#events) in the API doc to learn more.
|
||||
|
||||
Looking at the [available peer discovery](./CONFIGURATION.md#peer-discovery) protocols, there are several options to be considered:
|
||||
- If you already know the addresses of some other network peers, you should consider using `libp2p-bootstrap` as this is the easiest way of getting your peer into the network.
|
||||
- If it is likely that you will have other peers on your local network, `libp2p-mdns` is a must if you're node is not running in the browser. It allows peers to discover each other when on the same local network.
|
||||
- If your application is browser based you can use the `libp2p-webrtc-star` Transport, which includes a rendezvous based peer sharing service.
|
||||
- A random walk approach can be used via `libp2p-kad-dht`, to crawl the network and find new peers along the way.
|
||||
|
||||
For this guide we will configure `libp2p-bootstrap` as this is useful for joining the public network.
|
||||
|
||||
Let's install `libp2p-bootstrap`.
|
||||
|
||||
```sh
|
||||
npm install libp2p-bootstrap
|
||||
```
|
||||
|
||||
We can provide specific configurations for each protocol within a `config.peerDiscovery` property in the options as shown below.
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const SECIO = require('libp2p-secio')
|
||||
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'
|
||||
]
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [WebSockets],
|
||||
connEncryption: [SECIO],
|
||||
streamMuxer: [MPLEX],
|
||||
peerDiscovery: [Bootstrap]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
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]: {
|
||||
enabled: true,
|
||||
list: bootstrapMultiaddrs // provide array of multiaddrs
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
// start libp2p
|
||||
await node.start()
|
||||
```
|
||||
|
||||
<details><summary>Read More</summary>
|
||||
If you want to know more about libp2p peer discovery, you should read the following content:
|
||||
|
||||
- https://github.com/libp2p/specs/blob/master/discovery/mdns.md
|
||||
</details>
|
||||
|
||||
## What is next
|
||||
|
||||
There are a lot of other concepts within `libp2p`, that are not covered in this guide. For additional configuration options we recommend checking out the [Configuration Readme](./CONFIGURATION.md) and the [examples folder](../examples). If you have any problems getting started, or if anything isn't clear, please let us know by submitting an issue!
|
||||
|
||||
|
||||
[transport]: https://github.com/libp2p/js-interfaces/tree/master/src/transport
|
||||
[crypto]: https://github.com/libp2p/js-interfaces/tree/master/src/crypto
|
||||
[streamMuxer]: https://github.com/libp2p/js-interfaces/tree/master/src/stream-muxer
|
28
doc/METRICS.md
Normal file
28
doc/METRICS.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Bandwidth Metrics
|
||||
|
||||
- Metrics gathering is optional, as there is a performance hit to using it.
|
||||
- Metrics do NOT currently contain OS level stats, only libp2p application level Metrics. For example, TCP messages (ACK, FIN, etc) are not accounted for.
|
||||
- See the [API](./API.md) for Metrics usage. Metrics in libp2p do not emit events, as such applications wishing to read Metrics will need to do so actively. This ensures that the system is not unnecessarily firing update notifications.
|
||||
|
||||
## Tracking
|
||||
- When a transport hands off a connection for upgrading, Metrics are hooked up if enabled.
|
||||
- When a stream is created, Metrics will be tracked on that stream and associated to that streams protocol.
|
||||
- Tracked Metrics are associated to a specific peer, and count towards global bandwidth Metrics.
|
||||
|
||||
### Metrics Processing
|
||||
- The main Metrics object consists of individual `Stats` objects
|
||||
- The following categories are tracked:
|
||||
- Global stats; every byte in and out
|
||||
- Peer stats; every byte in and out, per peer
|
||||
- Protocol stats; every byte in and out, per protocol
|
||||
- When a message goes through Metrics:
|
||||
- It is added to the global stat
|
||||
- It is added to the stats for the remote peer
|
||||
- It is added to the protocol stats if there is one
|
||||
- When data is pushed onto a `Stat` it is added to a queue
|
||||
- The queue is processed at the earliest of either (configurable):
|
||||
- every 2 seconds after the last item was added to the queue
|
||||
- or once 1000 items have been queued
|
||||
- When the queue is processed:
|
||||
- The data length is added to either the `in` or `out` stat
|
||||
- The moving averages is calculated since the last queue processing (based on most recently processed item timestamp)
|
164
doc/STREAMING_ITERABLES.md
Normal file
164
doc/STREAMING_ITERABLES.md
Normal file
@ -0,0 +1,164 @@
|
||||
# Iterable Streams
|
||||
|
||||
> This document is a guide on how to use Iterable Streams in Libp2p. As a part of the [refactor away from callbacks](https://github.com/ipfs/js-ipfs/issues/1670), we have also moved to using Iterable Streams instead of [pull-streams](https://pull-stream.github.io/). If there are missing usage guides you feel should be added, please submit a PR!
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Iterable Streams](#iterable-streams)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Usage Guide](#usage-guide)
|
||||
- [Transforming Bidirectional Data](#transforming-bidirectional-data)
|
||||
- [Iterable Stream Types](#iterable-stream-types)
|
||||
- [Source](#source)
|
||||
- [Sink](#sink)
|
||||
- [Transform](#transform)
|
||||
- [Duplex](#duplex)
|
||||
- [Iterable Modules](#iterable-modules)
|
||||
|
||||
## Usage Guide
|
||||
|
||||
### Transforming Bidirectional Data
|
||||
|
||||
Sometimes you may need to wrap an existing duplex stream in order to perform incoming and outgoing [transforms](#transform) on data. This type of wrapping is commonly used in stream encryption/decryption. Using [it-pair][it-pair] and [it-pipe][it-pipe], we can do this rather easily, given an existing [duplex iterable](#duplex).
|
||||
|
||||
```js
|
||||
const duplexPair = require('it-pair/duplex')
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
// Wrapper is what we will write and read from
|
||||
// This gives us two duplex iterables that are internally connected
|
||||
const [internal, external] = duplexPair()
|
||||
|
||||
// Now we can pipe our wrapper to the existing duplex iterable
|
||||
pipe(
|
||||
external, // The external half of the pair interacts with the existing duplex
|
||||
outgoingTransform, // A transform iterable to send data through (ie: encrypting)
|
||||
existingDuplex, // The original duplex iterable we are wrapping
|
||||
incomingTransform, // A transform iterable to read data through (ie: decrypting)
|
||||
external
|
||||
)
|
||||
|
||||
// We can now read and write from the other half of our pair
|
||||
pipe(
|
||||
['some data'],
|
||||
internal, // The internal half of the pair is what we will interact with to read/write data
|
||||
async (source) => {
|
||||
for await (const chunk of source) {
|
||||
console.log('Data: %s', chunk.toString())
|
||||
// > Data: some data
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## Iterable Stream Types
|
||||
|
||||
These types are pulled from [@alanshaw's gist](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9) on streaming iterables.
|
||||
|
||||
### Source
|
||||
|
||||
A "source" is something that can be consumed. It is an iterable object.
|
||||
|
||||
```js
|
||||
const ints = {
|
||||
[Symbol.asyncIterator] () {
|
||||
let i = 0
|
||||
return {
|
||||
async next () {
|
||||
return { done: false, value: i++ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// or, more succinctly using a generator and for/await:
|
||||
|
||||
const ints = (async function * () {
|
||||
let i = 0
|
||||
while (true) yield i++
|
||||
})()
|
||||
```
|
||||
|
||||
### Sink
|
||||
|
||||
A "sink" is something that consumes (or drains) a source. It is a function that takes a source and iterates over it. It optionally returns a value.
|
||||
|
||||
```js
|
||||
const logger = async source => {
|
||||
const it = source[Symbol.asyncIterator]()
|
||||
while (true) {
|
||||
const { done, value } = await it.next()
|
||||
if (done) break
|
||||
console.log(value) // prints 0, 1, 2, 3...
|
||||
}
|
||||
}
|
||||
|
||||
// or, more succinctly using a generator and for/await:
|
||||
|
||||
const logger = async source => {
|
||||
for await (const chunk of source) {
|
||||
console.log(chunk) // prints 0, 1, 2, 3...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Transform
|
||||
|
||||
A "transform" is both a sink _and_ a source where the values it consumes and the values that can be consumed from it are connected in some way. It is a function that takes a source and returns a source.
|
||||
|
||||
```js
|
||||
const doubler = source => {
|
||||
return {
|
||||
[Symbol.asyncIterator] () {
|
||||
const it = source[Symbol.asyncIterator]()
|
||||
return {
|
||||
async next () {
|
||||
const { done, value } = await it.next()
|
||||
if (done) return { done }
|
||||
return { done, value: value * 2 }
|
||||
}
|
||||
return () {
|
||||
return it.return && it.return()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// or, more succinctly using a generator and for/await:
|
||||
|
||||
const doubler = source => (async function * () {
|
||||
for await (const chunk of source) {
|
||||
yield chunk * 2
|
||||
}
|
||||
})()
|
||||
```
|
||||
|
||||
### Duplex
|
||||
|
||||
A "duplex" is similar to a transform but the values it consumes are not necessarily connected to the values that can be consumed from it. It is an object with two properties, `sink` and `source`.
|
||||
|
||||
```js
|
||||
const duplex = {
|
||||
sink: async source => {/* ... */},
|
||||
source: { [Symbol.asyncIterator] () {/* ... */} }
|
||||
}
|
||||
```
|
||||
|
||||
## Iterable Modules
|
||||
|
||||
- [it-handshake][it-handshake] Handshakes for binary protocols with iterable streams.
|
||||
- [it-length-prefixed][it-length-prefixed] Streaming length prefixed buffers with async iterables.
|
||||
- [it-pair][it-pair] Paired streams that are internally connected.
|
||||
- [it-pipe][it-pipe] Create a pipeline of iterables. Works with duplex streams.
|
||||
- [it-pushable][it-pushable] An iterable that you can push values into.
|
||||
- [it-reader][it-reader] Read an exact number of bytes from a binary, async iterable.
|
||||
- [streaming-iterables][streaming-iterables] A Swiss army knife for async iterables.
|
||||
|
||||
[it-handshake]: https://github.com/jacobheun/it-handshake
|
||||
[it-length-prefixed]: https://github.com/alanshaw/it-length-prefixed
|
||||
[it-pair]: https://github.com/alanshaw/it-pair
|
||||
[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/reconbot/streaming-iterables
|
178
doc/migrations/v0.26-v0.27.md
Normal file
178
doc/migrations/v0.26-v0.27.md
Normal file
@ -0,0 +1,178 @@
|
||||
# Migrating to the libp2p@0.27 API
|
||||
|
||||
A migration guide for refactoring your application code from libp2p v0.26.x to v0.27.0.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Migrating from callbacks](#migrating-from-callbacks)
|
||||
- [Pull Streams to Streaming Iterables](#pull-streams-to-streaming-iterables)
|
||||
- [Sample API Migrations](#sample-api-migrations)
|
||||
- [Registering Protocol Handlers](#registering-protocol-handlers)
|
||||
- [Dialing and Sending Data](#dialing-and-sending-data)
|
||||
- [Checking if a peer is connected](#checking-if-a-peer-is-connected)
|
||||
- [Pinging another peer](#pinging-another-peer)
|
||||
- [Pubsub](#pubsub)
|
||||
- [Getting subscribers](#getting-subscribers)
|
||||
- [Getting subscribed topics](#getting-subscribed-topics)
|
||||
|
||||
## Migrating from callbacks
|
||||
|
||||
Callbacks are no longer supported in the libp2p API, as the API has now fully moved to async / await. You can see a full list of the available methods in the [API readme][api]
|
||||
|
||||
**Before**
|
||||
```js
|
||||
libp2p.start((err) => {
|
||||
if (err) throw err
|
||||
console.log('libp2p started')
|
||||
})
|
||||
```
|
||||
|
||||
**After**
|
||||
```js
|
||||
await libp2p.start()
|
||||
console.log('libp2p started')
|
||||
```
|
||||
|
||||
## Pull Streams to Streaming Iterables
|
||||
|
||||
The libp2p API no longer supports Pull Streams and has migrated to [Streaming Iterables][streaming_iterable]. If you would like to continue using Pull Streams in your application code, or need additional time to migrate your code base, you can leverage the conversion modules [async-iterator-to-pull-stream](https://github.com/alanshaw/async-iterator-to-pull-stream) and [pull-stream-to-async-iterator](https://github.com/alanshaw/pull-stream-to-async-iterator).
|
||||
|
||||
For a growing list of async iterator modules, you should follow the [it-awesome repo][it_awesome].
|
||||
|
||||
## Sample API Migrations
|
||||
|
||||
### Registering Protocol Handlers
|
||||
|
||||
Protocol registration is very similar to how it previously was, however, the handler now takes a single parameter containing the incoming stream and its protocol. Additionally, you can now pass an array of protocols to `.handle`, but a single string is still supported.
|
||||
|
||||
**Before**
|
||||
```js
|
||||
const pull = require('pull-stream')
|
||||
libp2p.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
|
||||
```
|
||||
|
||||
**After**
|
||||
```js
|
||||
const pipe = require('it-pipe')
|
||||
libp2p.handle(['/echo/1.0.0'], ({ protocol, stream }) => pipe(stream, stream))
|
||||
```
|
||||
|
||||
### Dialing and Sending Data
|
||||
|
||||
`dialProtocol` no longer takes a callback, and will now return a [Streaming Iterable][streaming_iterable] and the protocol that was successfully negotiated. The new stream can be used with async iterator modules, see [it-awesome][it_awesome], instead of pull streams.
|
||||
|
||||
**Before**
|
||||
```js
|
||||
const pull = require('pull-stream')
|
||||
libp2p.dialProtocol(peerInfo, '/echo/1.0.0', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(
|
||||
pull.values(['hey']),
|
||||
conn,
|
||||
pull.drain((data) => {
|
||||
console.log('received echo:', data.toString())
|
||||
}, (err) => {
|
||||
if (err) { throw err }
|
||||
})
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
**After**
|
||||
```js
|
||||
const pipe = require('it-pipe')
|
||||
const { protocol, stream } = await libp2p.dialProtocol(peerInfo, '/echo/1.0.0')
|
||||
await pipe(
|
||||
['hey'],
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const data of source) {
|
||||
console.log('received echo:', data.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Checking if a peer is connected
|
||||
|
||||
`peerInfo.isConnected` has been deprecated. libp2p now tracks all connections centrally and will no longer update the state of `peerInfo.isConnected`. Consumers should switch to using `libp2p.registrar.getConnection(peerInfo)`, which will return an open connection to that peer if one exists.
|
||||
|
||||
**Before**
|
||||
```js
|
||||
if (peerInfo.isConnected()) {
|
||||
// ...do something if connected
|
||||
}
|
||||
```
|
||||
|
||||
**After**
|
||||
```js
|
||||
const connection = libp2p.registrar.getConnection(peerInfo)
|
||||
if (connection) {
|
||||
// ...do something if connected
|
||||
}
|
||||
```
|
||||
|
||||
### Pinging another peer
|
||||
|
||||
`libp2p.ping` will no longer callback with a `Ping` event emitter. The internal logic has been simplified to give more flexibility to the API. `libp2p.ping` will now execute a single ping and return the latency.
|
||||
|
||||
**Before**
|
||||
```js
|
||||
libp2p.ping(peerInfo, (err, ping) => {
|
||||
if (err) throw err
|
||||
ping.once('ping', (latency) => {
|
||||
console.log('Latency is %s ms', latency)
|
||||
ping.stop()
|
||||
})
|
||||
|
||||
ping.start()
|
||||
})
|
||||
```
|
||||
|
||||
**After**
|
||||
```js
|
||||
const latency = await libp2p.ping(peerInfo)
|
||||
console.log('Latency is %s ms', latency)
|
||||
```
|
||||
|
||||
### Pubsub
|
||||
|
||||
#### Getting subscribers
|
||||
|
||||
`libp2p.pubsub.peers()` is now `libp2p.pubsub.getSubscribers()` and is no longer an asynchronous action.
|
||||
|
||||
**Before**
|
||||
```js
|
||||
libp2p.pubsub.peers(topic, (err, subscribers) => {
|
||||
if (err) throw err
|
||||
console.log('Subscribers:', subscribers)
|
||||
})
|
||||
```
|
||||
|
||||
**After**
|
||||
```js
|
||||
const subscribers = libp2p.pubsub.getSubscribers(topic)
|
||||
console.log('Subscribers:', subscribers)
|
||||
```
|
||||
|
||||
#### Getting subscribed topics
|
||||
|
||||
`libp2p.pubsub.ls()` is now `libp2p.pubsub.getTopics()` and is no longer an asynchronous action.
|
||||
|
||||
**Before**
|
||||
```js
|
||||
libp2p.pubsub.ls((err, topics) => {
|
||||
if (err) throw err
|
||||
console.log('Topics:', topics)
|
||||
})
|
||||
```
|
||||
|
||||
**After**
|
||||
```js
|
||||
const topics = libp2p.pubsub.getTopics()
|
||||
console.log('Topics:', topics)
|
||||
```
|
||||
|
||||
[api]: ../API.md
|
||||
[it_awesome]: https://github.com/alanshaw/it-awesome
|
||||
[streaming_iterable]: ../STREAMING_ITERABLES.md
|
343
doc/migrations/v0.27-v.28.md
Normal file
343
doc/migrations/v0.27-v.28.md
Normal 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
|
@ -1,8 +1,8 @@
|
||||
# `js-libp2p` Examples and Tutorials
|
||||
|
||||
In this folder, you can find a variety of examples to help you get started in using js-libp2p, in Node.js and in the Browser. Every example as a specific purpose and some of each incorporate a full tutorial that you can follow through, helping you expand your knowledge about libp2p and p2p networks in general.
|
||||
In this folder, you can find a variety of examples to help you get started in using js-libp2p, in Node.js and in the Browser. Every example has a specific purpose and some incorporate a full tutorial that you can follow through, helping you expand your knowledge about libp2p and p2p networks in general.
|
||||
|
||||
Let us know if you find any issue or if you want to contribute and add a new tutorial, feel welcome to submit a PR, thank you!
|
||||
Let us know if you find any issues, or if you want to contribute and add a new tutorial, feel free to submit a PR, thank you!
|
||||
|
||||
## Understanding how libp2p works
|
||||
|
||||
@ -22,3 +22,5 @@ Let us know if you find any issue or if you want to contribute and add a new tut
|
||||
- Running libp2p in the Electron (future)
|
||||
- [The standard echo net example with libp2p](./echo)
|
||||
- [A simple chat app with libp2p](./chat)
|
||||
|
||||
For go-libp2p examples, check out https://github.com/libp2p/go-libp2p-examples#examples-and-tutorials
|
||||
|
@ -2,78 +2,44 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const multiaddr = require('multiaddr')
|
||||
const Node = require('./libp2p-bundle')
|
||||
const pull = require('pull-stream')
|
||||
const async = require('async')
|
||||
const Pushable = require('pull-pushable')
|
||||
const p = Pushable()
|
||||
let idListener
|
||||
const { stdinToStream, streamToConsole } = require('./stream')
|
||||
|
||||
async.parallel([
|
||||
(callback) => {
|
||||
PeerId.createFromJSON(require('./peer-id-dialer'), (err, idDialer) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
callback(null, idDialer)
|
||||
})
|
||||
},
|
||||
(callback) => {
|
||||
PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
callback(null, idListener)
|
||||
})
|
||||
}
|
||||
], (err, ids) => {
|
||||
if (err) throw err
|
||||
const peerDialer = new PeerInfo(ids[0])
|
||||
peerDialer.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
async function run() {
|
||||
const [idDialer, idListener] = await Promise.all([
|
||||
PeerId.createFromJSON(require('./peer-id-dialer')),
|
||||
PeerId.createFromJSON(require('./peer-id-listener'))
|
||||
])
|
||||
|
||||
// Create a new libp2p node on localhost with a randomly chosen port
|
||||
const nodeDialer = new Node({
|
||||
peerInfo: peerDialer
|
||||
})
|
||||
|
||||
const peerListener = new PeerInfo(ids[1])
|
||||
idListener = ids[1]
|
||||
peerListener.multiaddrs.add('/ip4/127.0.0.1/tcp/10333')
|
||||
nodeDialer.start((err) => {
|
||||
if (err) {
|
||||
throw err
|
||||
peerId: idDialer,
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
}
|
||||
|
||||
console.log('Dialer ready, listening on:')
|
||||
|
||||
peerListener.multiaddrs.forEach((ma) => {
|
||||
console.log(ma.toString() + '/p2p/' + idListener.toB58String())
|
||||
})
|
||||
|
||||
nodeDialer.dialProtocol(peerListener, '/chat/1.0.0', (err, conn) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
console.log('nodeA dialed to nodeB on protocol: /chat/1.0.0')
|
||||
console.log('Type a message and see what happens')
|
||||
// Write operation. Data sent as a buffer
|
||||
pull(
|
||||
p,
|
||||
conn
|
||||
)
|
||||
// Sink, data converted from buffer to utf8 string
|
||||
pull(
|
||||
conn,
|
||||
pull.map((data) => {
|
||||
return data.toString('utf8').replace('\n', '')
|
||||
}),
|
||||
pull.drain(console.log)
|
||||
)
|
||||
|
||||
process.stdin.setEncoding('utf8')
|
||||
process.openStdin().on('data', (chunk) => {
|
||||
var data = chunk.toString()
|
||||
p.push(data)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Start the libp2p host
|
||||
await nodeDialer.start()
|
||||
|
||||
// Output this node's address
|
||||
console.log('Dialer ready, listening on:')
|
||||
nodeDialer.multiaddrs.forEach((ma) => {
|
||||
console.log(ma.toString() + '/p2p/' + idDialer.toB58String())
|
||||
})
|
||||
|
||||
// Dial to the remote peer (the "listener")
|
||||
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')
|
||||
|
||||
// Send stdin to the stream
|
||||
stdinToStream(stream)
|
||||
// Read the stream and output to console
|
||||
streamToConsole(stream)
|
||||
}
|
||||
|
||||
run()
|
||||
|
@ -1,41 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const WS = require('libp2p-websockets')
|
||||
const Bootstrap = require('libp2p-bootstrap')
|
||||
const spdy = require('libp2p-spdy')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
const mplex = require('libp2p-mplex')
|
||||
const secio = require('libp2p-secio')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const libp2p = require('../../..')
|
||||
|
||||
function mapMuxers (list) {
|
||||
return list.map((pref) => {
|
||||
if (typeof pref !== 'string') {
|
||||
return pref
|
||||
}
|
||||
switch (pref.trim().toLowerCase()) {
|
||||
case 'spdy': return spdy
|
||||
case 'mplex': return mplex
|
||||
default:
|
||||
throw new Error(pref + ' muxer not available')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getMuxers (muxers) {
|
||||
const muxerPrefs = process.env.LIBP2P_MUXER
|
||||
if (muxerPrefs && !muxers) {
|
||||
return mapMuxers(muxerPrefs.split(','))
|
||||
} else if (muxers) {
|
||||
return mapMuxers(muxers)
|
||||
} else {
|
||||
return [mplex, spdy]
|
||||
}
|
||||
}
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
@ -44,29 +16,8 @@ class Node extends libp2p {
|
||||
TCP,
|
||||
WS
|
||||
],
|
||||
streamMuxer: getMuxers(_options.muxer),
|
||||
connEncryption: [ secio ],
|
||||
peerDiscovery: [
|
||||
MulticastDNS,
|
||||
Bootstrap
|
||||
],
|
||||
dht: KadDHT
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
mdns: {
|
||||
interval: 10000,
|
||||
enabled: false
|
||||
},
|
||||
bootstrap: {
|
||||
interval: 10000,
|
||||
enabled: false,
|
||||
list: _options.bootstrapList
|
||||
}
|
||||
},
|
||||
dht: {
|
||||
kBucketSize: 20
|
||||
}
|
||||
streamMuxer: [ mplex ],
|
||||
connEncryption: [ NOISE, SECIO ]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,56 +1,42 @@
|
||||
'use strict'
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const multaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const Node = require('./libp2p-bundle.js')
|
||||
const pull = require('pull-stream')
|
||||
const Pushable = require('pull-pushable')
|
||||
const p = Pushable()
|
||||
const { stdinToStream, streamToConsole } = require('./stream')
|
||||
|
||||
PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
const peerListener = new PeerInfo(idListener)
|
||||
peerListener.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
|
||||
async function run() {
|
||||
// Create a new libp2p node with the given multi-address
|
||||
const idListener = await PeerId.createFromJSON(require('./peer-id-listener'))
|
||||
const nodeListener = new Node({
|
||||
peerInfo: peerListener
|
||||
})
|
||||
|
||||
nodeListener.start((err) => {
|
||||
if (err) {
|
||||
throw err
|
||||
peerId: idListener,
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/10333']
|
||||
}
|
||||
|
||||
nodeListener.on('peer:connect', (peerInfo) => {
|
||||
console.log(peerInfo.id.toB58String())
|
||||
})
|
||||
|
||||
nodeListener.handle('/chat/1.0.0', (protocol, conn) => {
|
||||
pull(
|
||||
p,
|
||||
conn
|
||||
)
|
||||
|
||||
pull(
|
||||
conn,
|
||||
pull.map((data) => {
|
||||
return data.toString('utf8').replace('\n', '')
|
||||
}),
|
||||
pull.drain(console.log)
|
||||
)
|
||||
|
||||
process.stdin.setEncoding('utf8')
|
||||
process.openStdin().on('data', (chunk) => {
|
||||
var data = chunk.toString()
|
||||
p.push(data)
|
||||
})
|
||||
})
|
||||
|
||||
console.log('Listener ready, listening on:')
|
||||
peerListener.multiaddrs.forEach((ma) => {
|
||||
console.log(ma.toString() + '/p2p/' + idListener.toB58String())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Log a message when a remote peer connects to us
|
||||
nodeListener.connectionManager.on('peer:connect', (connection) => {
|
||||
console.log('connected to: ', connection.remotePeer.toB58String())
|
||||
})
|
||||
|
||||
// Handle messages for the protocol
|
||||
await nodeListener.handle('/chat/1.0.0', async ({ stream }) => {
|
||||
// Send stdin to the stream
|
||||
stdinToStream(stream)
|
||||
// Read the stream and output to console
|
||||
streamToConsole(stream)
|
||||
})
|
||||
|
||||
// Start listening
|
||||
await nodeListener.start()
|
||||
|
||||
// Output listen addresses to the console
|
||||
console.log('Listener ready, listening on:')
|
||||
nodeListener.multiaddrs.forEach((ma) => {
|
||||
console.log(ma.toString() + '/p2p/' + idListener.toB58String())
|
||||
})
|
||||
}
|
||||
|
||||
run()
|
||||
|
40
examples/chat/src/stream.js
Normal file
40
examples/chat/src/stream.js
Normal file
@ -0,0 +1,40 @@
|
||||
'use strict'
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const pipe = require('it-pipe')
|
||||
const lp = require('it-length-prefixed')
|
||||
|
||||
function stdinToStream(stream) {
|
||||
// Read utf-8 from stdin
|
||||
process.stdin.setEncoding('utf8')
|
||||
pipe(
|
||||
// Read from stdin (the source)
|
||||
process.stdin,
|
||||
// Encode with length prefix (so receiving side knows how much data is coming)
|
||||
lp.encode(),
|
||||
// Write to the stream (the sink)
|
||||
stream.sink
|
||||
)
|
||||
}
|
||||
|
||||
function streamToConsole(stream) {
|
||||
pipe(
|
||||
// Read from the stream (the source)
|
||||
stream.source,
|
||||
// Decode length-prefixed data
|
||||
lp.decode(),
|
||||
// Sink function
|
||||
async function (source) {
|
||||
// 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', ''))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
stdinToStream,
|
||||
streamToConsole
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
❗❗Outdated: This example is still not refactored with the `0.27.*` release.
|
||||
WIP on [libp2p/js-libp2p#507](https://github.com/libp2p/js-libp2p/pull/507)
|
||||
======
|
||||
|
||||
# Delegated Routing with Libp2p and IPFS
|
||||
|
||||
This example shows how to use delegated peer and content routing. The [Peer and Content Routing Example](../peer-and-content-routing) focuses
|
||||
@ -10,17 +14,18 @@ The starting [Libp2p Bundle](./src/libp2p-bundle.js) in this example starts by d
|
||||
Once you've completed the example, you should try enabled the DHT and see what kind of results you get! You can also enable the
|
||||
various Peer Discovery modules and see the impact it has on your Peer count.
|
||||
|
||||
## Prerequisite
|
||||
**NOTE**: This example is currently dependent on a clone of the [delegated routing support branch of go-ipfs](https://github.com/ipfs/go-ipfs/pull/4595).
|
||||
|
||||
## Running this example
|
||||
|
||||
1. Install go-ipfs locally if you dont already have it. [Install Guide](https://docs.ipfs.io/introduction/install/).
|
||||
1. Run the IPFS daemon: `ipfs daemon`.
|
||||
1. The daemon will output a line about its API address, like `API server listening on /ip4/127.0.0.1/tcp/8080`.
|
||||
1. In another window, while the daemon is running, Configure the IPFS Gateway to support delegate routing `ipfs config Gateway.APICommands --json '["dht/findprovs", "dht/findpeer", "refs", "swarm/connect"]'`.
|
||||
1. In the same window, output the addresses of the node: `ipfs id`. Make note of the websocket address, it will contain `/ws` in the address, like `/ip4/127.0.0.1/tcp/8081/ws`.
|
||||
1. In `./src/libp2p-bundle.js` check if the host and port of your node are correct, according to the previous step. If they are different, replace them.
|
||||
1. In `./src/App.js` replace `BootstrapNode` with your nodes Websocket address from step 3. **Also**, in `./src/App.js` set BootstrapNodeID to your nodes id, which is displayed when running `ipfs id` in the `ID` property.
|
||||
1. Start this example:
|
||||
1. Install IPFS locally if you dont already have it. [Install Guide](https://docs.ipfs.io/introduction/install/)
|
||||
2. Run the IPFS daemon: `ipfs daemon`
|
||||
3. The daemon will output a line about its API address, like `API server listening on /ip4/127.0.0.1/tcp/8080`
|
||||
4. In another window output the addresses of the node: `ipfs id`. Make note of the websocket address, it will contain `/ws/` in the address.
|
||||
5. In `./src/libp2p-bundle.js` check if the host and port of your node are correct, according to the previous step. If they are different, replace them.
|
||||
6. In `./src/App.js` replace `BootstrapNode` with your nodes Websocket address from step 4.
|
||||
7. Start this example:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
@ -46,12 +51,3 @@ This will do a few things:
|
||||
2. Copy one of the CIDs from the list of peer addresses, this will be the last portion of the address and will look something like `QmdoG8DpzYUZMVP5dGmgmigZwR1RE8Cf6SxMPg1SBXJAQ8`.
|
||||
3. In your browser, paste the CID into the *Peer* field and hit `Find`.
|
||||
4. You should see information about the peer including its addresses.
|
||||
|
||||
### Adding Content via the Delegate
|
||||
1. In one browser, such as Firefox, enter some text in the `Add Data` field and click `Add`.
|
||||
2. Once added, JSON will be printed in the browser. Copy the value of `"hash"`.
|
||||
3. Close your Firefox tab. Open a new browser window in a **different** browser, such as Chrome.
|
||||
4. In the new window, copy the hash from step 2 into the `Hash` field and click `Find`.
|
||||
5. The text you entered in step 1 should display.
|
||||
|
||||
**Note**: By closing the first window before fetching the content, you are ensuring that data cannot be retrieved from it, and instead the delegate must be relied on.
|
||||
|
@ -3,16 +3,16 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"ipfs": "~0.36.1",
|
||||
"ipfs": "~0.34.4",
|
||||
"libp2p": "github:libp2p/js-libp2p#master",
|
||||
"libp2p-delegated-content-routing": "~0.2.2",
|
||||
"libp2p-delegated-peer-routing": "~0.2.2",
|
||||
"libp2p-kad-dht": "~0.15.0",
|
||||
"libp2p-kad-dht": "~0.14.12",
|
||||
"libp2p-mplex": "~0.8.5",
|
||||
"libp2p-secio": "~0.11.1",
|
||||
"libp2p-webrtc-star": "~0.16.1",
|
||||
"libp2p-webrtc-star": "~0.15.8",
|
||||
"libp2p-websocket-star": "~0.10.2",
|
||||
"libp2p-websockets": "~0.12.2",
|
||||
"pull-mplex": "~0.1.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "2.1.8"
|
||||
|
@ -6,10 +6,7 @@ import Ipfs from 'ipfs'
|
||||
import libp2pBundle from './libp2p-bundle'
|
||||
const Component = React.Component
|
||||
|
||||
/* TODO: Add your go IPFS nodes ID here by running `ipfs id` */
|
||||
const BootstrapNodeID = 'Qm...'
|
||||
/* TODO: Ensure the IP and port match your nodes Websocket address shown when running the daemon `ipfs daemon` */
|
||||
const BootstrapNode = `/ip4/127.0.0.1/tcp/8081/ws/p2p/${BootstrapNodeID}`
|
||||
const BootstrapNode = '/ip4/127.0.0.1/tcp/8081/ws/p2p/QmdoG8DpzYUZMVP5dGmgmigZwR1RE8Cf6SxMPg1SBXJAQ8'
|
||||
|
||||
class App extends Component {
|
||||
constructor (props) {
|
||||
@ -20,7 +17,6 @@ class App extends Component {
|
||||
hash: 'QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB',
|
||||
// This peer is one of the Bootstrap nodes for IPFS
|
||||
peer: 'QmV6kA2fB8kTr6jc3pL5zbNsjKbmPUHAPKKHRBYe1kDEyc',
|
||||
data: '',
|
||||
isLoading: 0
|
||||
}
|
||||
this.peerInterval = null
|
||||
@ -29,8 +25,6 @@ class App extends Component {
|
||||
this.handleHashSubmit = this.handleHashSubmit.bind(this)
|
||||
this.handlePeerChange = this.handlePeerChange.bind(this)
|
||||
this.handlePeerSubmit = this.handlePeerSubmit.bind(this)
|
||||
this.handleDataChange = this.handleDataChange.bind(this)
|
||||
this.handleDataSubmit = this.handleDataSubmit.bind(this)
|
||||
}
|
||||
|
||||
handleHashChange (event) {
|
||||
@ -43,11 +37,6 @@ class App extends Component {
|
||||
peer: event.target.value
|
||||
})
|
||||
}
|
||||
handleDataChange (event) {
|
||||
this.setState({
|
||||
data: event.target.value
|
||||
})
|
||||
}
|
||||
|
||||
handleHashSubmit (event) {
|
||||
event.preventDefault()
|
||||
@ -70,22 +59,7 @@ class App extends Component {
|
||||
isLoading: this.state.isLoading + 1
|
||||
})
|
||||
|
||||
this.ipfs.dht.findPeer(this.state.peer, (err, results) => {
|
||||
if (err) console.log('Error', err)
|
||||
|
||||
this.setState({
|
||||
response: JSON.stringify(results, null, 2),
|
||||
isLoading: this.state.isLoading - 1
|
||||
})
|
||||
})
|
||||
}
|
||||
handleDataSubmit (event) {
|
||||
event.preventDefault()
|
||||
this.setState({
|
||||
isLoading: this.state.isLoading + 1
|
||||
})
|
||||
|
||||
this.ipfs.add(Buffer.from(this.state.data), (err, results) => {
|
||||
this.ipfs.dht.findpeer(this.state.peer, (err, results) => {
|
||||
if (err) console.log('Error', err)
|
||||
|
||||
this.setState({
|
||||
@ -161,13 +135,6 @@ class App extends Component {
|
||||
<input type="submit" value="Find" />
|
||||
</label>
|
||||
</form>
|
||||
<form onSubmit={this.handleDataSubmit}>
|
||||
<label>
|
||||
Add Data:
|
||||
<input type="text" value={this.state.data} onChange={this.handleDataChange} />
|
||||
<input type="submit" value="Add" />
|
||||
</label>
|
||||
</form>
|
||||
</section>
|
||||
<section className={[this.state.isLoading > 0 ? 'loading' : '', 'loader'].join(' ')}>
|
||||
<div className="lds-ripple"><div></div><div></div></div>
|
||||
|
@ -5,7 +5,7 @@ const Libp2p = require('libp2p')
|
||||
const Websockets = require('libp2p-websockets')
|
||||
const WebSocketStar = require('libp2p-websocket-star')
|
||||
const WebRTCStar = require('libp2p-webrtc-star')
|
||||
const MPLEX = require('pull-mplex')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
|
||||
@ -14,9 +14,8 @@ const DelegatedContentRouter = require('libp2p-delegated-content-routing')
|
||||
export default function Libp2pBundle ({peerInfo, peerBook}) {
|
||||
const wrtcstar = new WebRTCStar({id: peerInfo.id})
|
||||
const wsstar = new WebSocketStar({id: peerInfo.id})
|
||||
/* TODO: Ensure the delegatedApiOptions match your IPFS nodes API server */
|
||||
const delegatedApiOptions = {
|
||||
host: '127.0.0.1',
|
||||
host: '0.0.0.0',
|
||||
protocol: 'http',
|
||||
port: '8080'
|
||||
}
|
||||
@ -26,8 +25,7 @@ export default function Libp2pBundle ({peerInfo, peerBook}) {
|
||||
peerBook,
|
||||
// Lets limit the connection managers peers and have it check peer health less frequently
|
||||
connectionManager: {
|
||||
minPeers: 20,
|
||||
maxPeers: 50,
|
||||
maxPeers: 10,
|
||||
pollInterval: 5000
|
||||
},
|
||||
modules: {
|
||||
|
@ -1,14 +1,12 @@
|
||||
/* eslint-disable no-console */
|
||||
'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 Bootstrap = require('libp2p-bootstrap')
|
||||
const waterfall = require('async/waterfall')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
|
||||
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-nodejs.json
|
||||
const bootstrapers = [
|
||||
@ -23,51 +21,36 @@ const bootstrapers = [
|
||||
'/ip4/104.236.151.122/tcp/4001/p2p/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx'
|
||||
]
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ SECIO ],
|
||||
peerDiscovery: [ Bootstrap ]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
autoDial: true,
|
||||
bootstrap: {
|
||||
interval: 20e3,
|
||||
enabled: true,
|
||||
list: bootstrapers
|
||||
}
|
||||
;(async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [NOISE, SECIO],
|
||||
peerDiscovery: [Bootstrap]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
bootstrap: {
|
||||
interval: 60e3,
|
||||
enabled: true,
|
||||
list: bootstrapers
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
node.connectionManager.on('peer:connect', (connection) => {
|
||||
console.log('Connection established to:', connection.remotePeer.toB58String()) // Emitted when a peer has been found
|
||||
})
|
||||
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({
|
||||
peerInfo
|
||||
})
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
node.on('peer:discovery', (peer) => {
|
||||
node.on('peer:discovery', (peerId) => {
|
||||
// No need to dial, autoDial is on
|
||||
console.log('Discovered:', peer.id.toB58String())
|
||||
console.log('Discovered:', peerId.toB58String())
|
||||
})
|
||||
|
||||
node.on('peer:connect', (peer) => {
|
||||
console.log('Connection established to:', peer.id.toB58String())
|
||||
})
|
||||
})
|
||||
await node.start()
|
||||
})();
|
||||
|
@ -1,63 +1,48 @@
|
||||
/* eslint-disable no-console */
|
||||
'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 MulticastDNS = require('libp2p-mdns')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ SECIO ],
|
||||
peerDiscovery: [ MulticastDNS ]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
mdns: {
|
||||
interval: 20e3,
|
||||
enabled: true
|
||||
}
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [NOISE, SECIO],
|
||||
peerDiscovery: [MulticastDNS]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
mdns: {
|
||||
interval: 20e3,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
;(async () => {
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({
|
||||
peerInfo
|
||||
})
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
node1.on('peer:discovery', (peerId) => console.log('Discovered:', peerId.toB58String()))
|
||||
node2.on('peer:discovery', (peerId) => console.log('Discovered:', peerId.toB58String()))
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node1.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
|
||||
node2.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
|
||||
})
|
||||
await Promise.all([
|
||||
node1.start(),
|
||||
node2.start()
|
||||
])
|
||||
})();
|
||||
|
@ -4,42 +4,38 @@ 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).
|
||||
|
||||
First, we create our libp2p bundle.
|
||||
First, we create our libp2p node.
|
||||
|
||||
```JavaScript
|
||||
const Bootstrap = require('libp2p-railing')
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ SECIO ],
|
||||
peerDiscovery: [ Bootstrap ]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
bootstrap: {
|
||||
interval: 2000,
|
||||
enabled: true,
|
||||
list: bootstrapers
|
||||
}
|
||||
}
|
||||
const Libp2p = require('libp2p')
|
||||
const Bootstrap = require('libp2p-bootstrap')
|
||||
|
||||
const node = Libp2p.create({
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ NOISE, SECIO ],
|
||||
peerDiscovery: [ Bootstrap ]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
bootstrap: {
|
||||
interval: 60e3,
|
||||
enabled: true,
|
||||
list: bootstrapers
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
In this bundle, we use a `bootstrappers` array listing peers to connect _on boot_. Here is the list used by js-ipfs and go-ipfs.
|
||||
In this configuration, we use a `bootstrappers` array listing peers to connect _on boot_. Here is the list used by js-ipfs and go-ipfs.
|
||||
|
||||
```JavaScript
|
||||
const bootstrapers = [
|
||||
@ -58,34 +54,38 @@ const bootstrapers = [
|
||||
Now, once we create and start the node, we can listen for events such as `peer:discovery` and `peer:connect`, these events tell us when we found a peer, independently of the discovery mechanism used and when we actually dialed to that peer.
|
||||
|
||||
```JavaScript
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({
|
||||
peerInfo
|
||||
})
|
||||
node.start(cb)
|
||||
const node = await Libp2p.create({
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
}
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ NOISE, SECIO ],
|
||||
peerDiscovery: [ Bootstrap ]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
bootstrap: {
|
||||
interval: 60e3,
|
||||
enabled: true,
|
||||
list: bootstrapers
|
||||
}
|
||||
}
|
||||
}
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
// Emitted when a peer has been found
|
||||
node.on('peer:discovery', (peer) => {
|
||||
console.log('Discovered:', peer.id.toB58String())
|
||||
// Note how we need to dial, even if just to warm up the Connection (by not
|
||||
// picking any protocol) in order to get a full Connection. The Peer Discovery
|
||||
// doesn't make any decisions for you.
|
||||
node.dial(peer, () => {})
|
||||
})
|
||||
|
||||
// Once the dial is complete, this event is emitted.
|
||||
node.on('peer:connect', (peer) => {
|
||||
console.log('Connection established to:', peer.id.toB58String())
|
||||
})
|
||||
})
|
||||
|
||||
node.connectionManager.on('peer:connect', (connection) => {
|
||||
console.log('Connection established to:', connection.remotePeer.toB58String()) // Emitted when a new connection has been created
|
||||
})
|
||||
|
||||
node.on('peer:discovery', (peerId) => {
|
||||
// No need to dial, autoDial is on
|
||||
console.log('Discovered:', peerId.toB58String())
|
||||
})
|
||||
|
||||
await node.start()
|
||||
```
|
||||
|
||||
From running [1.js](./1.js), you should see the following:
|
||||
@ -101,76 +101,68 @@ Discovered: QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64
|
||||
Discovered: QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd
|
||||
Discovered: QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3
|
||||
Discovered: QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
Connection established to: QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3
|
||||
Connection established to: QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd
|
||||
Connection established to: QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64
|
||||
Connection established to: QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm
|
||||
Connection established to: QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM
|
||||
Connection established to: QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
Connection established to: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
|
||||
Connection established to: QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z
|
||||
Connection established to: QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM
|
||||
Connection established to: QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm
|
||||
Connection established to: QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu
|
||||
Connection established to: QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64
|
||||
Connection established to: QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd
|
||||
Connection established to: QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3
|
||||
Connection established to: QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
```
|
||||
|
||||
## 2. MulticastDNS to find other peers in the network
|
||||
|
||||
For this example, we need `libp2p-mdns`, go ahead and `npm install` it. You can find the complete solution at [2.js](./2.js).
|
||||
|
||||
Update your libp2p bundle to include MulticastDNS.
|
||||
Update your libp2p configuration to include MulticastDNS.
|
||||
|
||||
```JavaScript
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ SECIO ],
|
||||
peerDiscovery: [ MulticastDNS ]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
mdns: {
|
||||
// Run at 1s so we can observe more quickly, default is 10s
|
||||
interval: 1000,
|
||||
enabled: true
|
||||
}
|
||||
const Libp2p = require('libp2p')
|
||||
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: [ NOISE, SECIO ],
|
||||
peerDiscovery: [ MulticastDNS ]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
mdns: {
|
||||
interval: 20e3,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
To observe it working, spawn two nodes.
|
||||
|
||||
```JavaScript
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node1.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
|
||||
node2.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
|
||||
})
|
||||
node1.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
|
||||
node2.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
|
||||
```
|
||||
|
||||
If you run this example, you will see a continuous stream of each peer discovering each other.
|
||||
If you run this example, you will see the other peers being discovered.
|
||||
|
||||
```bash
|
||||
> node 2.js
|
||||
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
|
||||
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
|
||||
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
|
||||
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
|
||||
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
|
||||
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
|
||||
```
|
||||
|
||||
## 3. Where to find other Peer Discovery Mechanisms
|
||||
@ -178,5 +170,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!
|
||||
|
@ -5,54 +5,55 @@
|
||||
* Dialer Node
|
||||
*/
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const Node = require('./libp2p-bundle')
|
||||
const pull = require('pull-stream')
|
||||
const async = require('async')
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
async.parallel([
|
||||
(cb) => PeerId.createFromJSON(require('./id-d'), cb),
|
||||
(cb) => PeerId.createFromJSON(require('./id-l'), cb)
|
||||
], (err, ids) => {
|
||||
if (err) { throw err }
|
||||
async function run() {
|
||||
const [dialerId, listenerId] = await Promise.all([
|
||||
PeerId.createFromJSON(require('./id-d')),
|
||||
PeerId.createFromJSON(require('./id-l'))
|
||||
])
|
||||
|
||||
// Dialer
|
||||
const dialerId = ids[0]
|
||||
const dialerPeerInfo = new PeerInfo(dialerId)
|
||||
dialerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
const dialerNode = new Node({
|
||||
peerInfo: dialerPeerInfo
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
peerId: dialerId
|
||||
})
|
||||
|
||||
// Peer to Dial
|
||||
const listenerPeerInfo = new PeerInfo(ids[1])
|
||||
const listenerId = ids[1]
|
||||
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()
|
||||
|
||||
dialerNode.start((err) => {
|
||||
if (err) { throw err }
|
||||
// Start the dialer libp2p node
|
||||
await dialerNode.start()
|
||||
|
||||
console.log('Dialer ready, listening on:')
|
||||
dialerPeerInfo.multiaddrs.forEach((ma) => console.log(ma.toString() +
|
||||
'/p2p/' + dialerId.toB58String()))
|
||||
console.log('Dialer ready, listening on:')
|
||||
dialerNode.multiaddrs.forEach((ma) => console.log(ma.toString() +
|
||||
'/p2p/' + dialerId.toB58String()))
|
||||
|
||||
console.log('Dialing to peer:', listenerMultiaddr.toString())
|
||||
dialerNode.dialProtocol(listenerPeerInfo, '/echo/1.0.0', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
// Dial the listener node
|
||||
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')
|
||||
console.log('nodeA dialed to nodeB on protocol: /echo/1.0.0')
|
||||
|
||||
pull(
|
||||
pull.values(['hey']),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
if (err) { throw err }
|
||||
console.log('received echo:', data.toString())
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
pipe(
|
||||
// Source data
|
||||
['hey'],
|
||||
// Write to the stream, and pass its output to the next function
|
||||
stream,
|
||||
// Sink function
|
||||
async function (source) {
|
||||
// For each chunk of data
|
||||
for await (const data of source) {
|
||||
// Output the data
|
||||
console.log('received echo:', data.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
run()
|
||||
|
@ -1,41 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const WS = require('libp2p-websockets')
|
||||
const Bootstrap = require('libp2p-bootstrap')
|
||||
const spdy = require('libp2p-spdy')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
const mplex = require('libp2p-mplex')
|
||||
const secio = require('libp2p-secio')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const libp2p = require('../../..')
|
||||
|
||||
function mapMuxers (list) {
|
||||
return list.map((pref) => {
|
||||
if (typeof pref !== 'string') {
|
||||
return pref
|
||||
}
|
||||
switch (pref.trim().toLowerCase()) {
|
||||
case 'spdy': return spdy
|
||||
case 'mplex': return mplex
|
||||
default:
|
||||
throw new Error(pref + ' muxer not available')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getMuxers (muxers) {
|
||||
const muxerPrefs = process.env.LIBP2P_MUXER
|
||||
if (muxerPrefs && !muxers) {
|
||||
return mapMuxers(muxerPrefs.split(','))
|
||||
} else if (muxers) {
|
||||
return mapMuxers(muxers)
|
||||
} else {
|
||||
return [mplex, spdy]
|
||||
}
|
||||
}
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
@ -44,29 +17,8 @@ class Node extends libp2p {
|
||||
TCP,
|
||||
WS
|
||||
],
|
||||
streamMuxer: getMuxers(_options.muxer),
|
||||
connEncryption: [ secio ],
|
||||
peerDiscovery: [
|
||||
MulticastDNS,
|
||||
Bootstrap
|
||||
],
|
||||
dht: KadDHT
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
mdns: {
|
||||
interval: 10000,
|
||||
enabled: false
|
||||
},
|
||||
bootstrap: {
|
||||
interval: 10000,
|
||||
enabled: false,
|
||||
list: _options.bootstrapList
|
||||
}
|
||||
},
|
||||
dht: {
|
||||
kBucketSize: 20
|
||||
}
|
||||
streamMuxer: [ mplex ],
|
||||
connEncryption: [ NOISE, SECIO ]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,41 +6,36 @@
|
||||
*/
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const Node = require('./libp2p-bundle')
|
||||
const pull = require('pull-stream')
|
||||
const series = require('async/series')
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
let listenerId
|
||||
let listenerNode
|
||||
async function run() {
|
||||
const listenerId = await PeerId.createFromJSON(require('./id-l'))
|
||||
|
||||
series([
|
||||
(cb) => {
|
||||
PeerId.createFromJSON(require('./id-l'), (err, id) => {
|
||||
if (err) { return cb(err) }
|
||||
listenerId = id
|
||||
cb()
|
||||
})
|
||||
},
|
||||
(cb) => {
|
||||
const listenerPeerInfo = new PeerInfo(listenerId)
|
||||
listenerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
|
||||
listenerNode = new Node({
|
||||
peerInfo: listenerPeerInfo
|
||||
})
|
||||
// Listener libp2p node
|
||||
const listenerNode = new Node({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/10333']
|
||||
},
|
||||
peerId: listenerId
|
||||
})
|
||||
|
||||
listenerNode.on('peer:connect', (peerInfo) => {
|
||||
console.log('received dial to me from:', peerInfo.id.toB58String())
|
||||
})
|
||||
// Log a message when we receive a connection
|
||||
listenerNode.connectionManager.on('peer:connect', (connection) => {
|
||||
console.log('received dial to me from:', connection.remotePeer.toB58String())
|
||||
})
|
||||
|
||||
listenerNode.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
|
||||
listenerNode.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
// Handle incoming connections for the protocol by piping from the stream
|
||||
// back to itself (an echo)
|
||||
await listenerNode.handle('/echo/1.0.0', ({ stream }) => pipe(stream.source, stream.sink))
|
||||
|
||||
// Start listening
|
||||
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())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
run()
|
||||
|
@ -1,63 +1,53 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('../../')
|
||||
const Libp2p = require('../../')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const SPDY = require('libp2p-spdy')
|
||||
const Mplex = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const pull = require('pull-stream')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ SPDY ],
|
||||
connEncryption: [ SECIO ]
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [NOISE, SECIO]
|
||||
}
|
||||
})
|
||||
|
||||
await node.start()
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
|
||||
node2.handle('/a-protocol', ({ stream }) => {
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({
|
||||
peerInfo
|
||||
})
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node2.handle('/a-protocol', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
node1.dialProtocol(node2.peerInfo, '/a-protocol', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['This information is sent out encrypted to the other peer']), conn)
|
||||
})
|
||||
})
|
||||
const { stream } = await node1.dialProtocol(node2.peerId, '/a-protocol')
|
||||
|
||||
await pipe(
|
||||
['This information is sent out encrypted to the other peer'],
|
||||
stream
|
||||
)
|
||||
})();
|
||||
|
@ -4,37 +4,35 @@ libp2p can leverage the encrypted communications from the transports it uses (i.
|
||||
|
||||
We call this usage a _connection upgrade_ where given a connection between peer A to peer B, a protocol handshake can be performed that gives that connection new properties.
|
||||
|
||||
A byproduct of having these encrypted communications modules is that we can authenticate the peers we are dialing to. You might have noticed that every time we dial to a peer in libp2p space, we always use its PeerId at the end (e.g /ip4/127.0.0.1/tcp/89765/ipfs/QmWCbVw1XZ8hiYBwwshPce2yaTDYTqTaP7GCHGpry3ykWb), this PeerId is generated by hashing the Public Key of the peer. With this, we can create a crypto challenge when dialing to another peer and prove that peer is the owner of a PrivateKey that matches the Public Key we know.
|
||||
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 modules `libp2p-secio`<sup>*</sup> and `libp2p-noise` to complete it, go ahead and `npm install libp2p-secio 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 bundle, 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 { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ SPDY ],
|
||||
// Attach secio as the crypto channel to use
|
||||
connEncryption: [ SECIO ]
|
||||
}
|
||||
const createNode = () => {
|
||||
return Libp2p.create({
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
// Attach secio as the crypto channel to use
|
||||
connEncryption: [ NOISE, SECIO ]
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
And that's it, from now on, all your libp2p communications are encrypted. Try running the example [1.js](./1.js) to see it working.
|
||||
|
||||
_<sup>*</sup> 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._
|
||||
|
||||
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.
|
||||
|
4
examples/libp2p-in-the-browser/.babelrc
Normal file
4
examples/libp2p-in-the-browser/.babelrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env"],
|
||||
"plugins": ["syntax-async-functions","transform-regenerator"]
|
||||
}
|
1
examples/libp2p-in-the-browser/1/.gitignore
vendored
1
examples/libp2p-in-the-browser/1/.gitignore
vendored
@ -1 +0,0 @@
|
||||
bundle.js
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "libp2p-in-the-browser",
|
||||
"version": "0.1.0",
|
||||
"description": "See other nodes in the network using WebRTC Star discovery mechanism",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"bundle": "browserify src/index.js > public/bundle.js",
|
||||
"serve": "static public -p 9090 -H '{\"Cache-Control\": \"no-cache, must-revalidate\"}'",
|
||||
"start": "npm run bundle && npm run serve"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"browserify": "^14.5.0",
|
||||
"concat-stream": "^1.6.0",
|
||||
"detect-dom-ready": "^1.0.2",
|
||||
"node-static": "~0.7.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"detect-dom-ready": "^1.0.2",
|
||||
"libp2p-bootstrap": "~0.9.7",
|
||||
"libp2p-mplex": "~0.8.5",
|
||||
"libp2p-secio": "~0.11.1",
|
||||
"libp2p-spdy": "~0.13.3",
|
||||
"libp2p-webrtc-star": "~0.15.8",
|
||||
"libp2p-websocket-star": "~0.10.2",
|
||||
"libp2p-websockets": "~0.12.2",
|
||||
"peer-info": "~0.15.1"
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>libp2p in the browser</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>libp2p node running \o/</h1>
|
||||
<div id="my-peer"></div>
|
||||
<div id="swarm"></div>
|
||||
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,93 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const WebRTCStar = require('libp2p-webrtc-star')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const WebSocketStar = require('libp2p-websocket-star')
|
||||
const Mplex = require('libp2p-mplex')
|
||||
const SPDY = require('libp2p-spdy')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const Bootstrap = require('libp2p-bootstrap')
|
||||
const DHT = require('libp2p-kad-dht')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const libp2p = require('../../../../')
|
||||
|
||||
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-browser.json
|
||||
const bootstrapList = [
|
||||
'/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
|
||||
'/dns4/sfo-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx',
|
||||
'/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
|
||||
'/dns4/sfo-2.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
|
||||
'/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',
|
||||
'/dns4/node0.preload.ipfs.io/tcp/443/wss/p2p/QmZMxNdpMkewiVZLMRxaNxUeZpDUb34pWjZ1kZvsd16Zic',
|
||||
'/dns4/node0.preload.ipfs.io/tcp/443/wss/p2p/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6'
|
||||
]
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (_options) {
|
||||
const wrtcStar = new WebRTCStar({ id: _options.peerInfo.id })
|
||||
const wsstar = new WebSocketStar({ id: _options.peerInfo.id })
|
||||
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [
|
||||
wrtcStar,
|
||||
WebSockets,
|
||||
wsstar
|
||||
],
|
||||
streamMuxer: [
|
||||
Mplex,
|
||||
SPDY
|
||||
],
|
||||
connEncryption: [
|
||||
SECIO
|
||||
],
|
||||
peerDiscovery: [
|
||||
wrtcStar.discovery,
|
||||
wsstar.discovery,
|
||||
Bootstrap
|
||||
],
|
||||
dht: DHT
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
autoDial: true,
|
||||
webRTCStar: {
|
||||
enabled: true
|
||||
},
|
||||
websocketStar: {
|
||||
enabled: true
|
||||
},
|
||||
bootstrap: {
|
||||
interval: 20e3,
|
||||
enabled: true,
|
||||
list: bootstrapList
|
||||
}
|
||||
},
|
||||
relay: {
|
||||
enabled: true,
|
||||
hop: {
|
||||
enabled: false,
|
||||
active: false
|
||||
}
|
||||
},
|
||||
dht: {
|
||||
enabled: false
|
||||
},
|
||||
EXPERIMENTAL: {
|
||||
pubsub: false
|
||||
}
|
||||
},
|
||||
connectionManager: {
|
||||
minPeers: 10,
|
||||
maxPeers: 50
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Node
|
@ -1,26 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const PeerInfo = require('peer-info')
|
||||
const Node = require('./browser-bundle')
|
||||
|
||||
function createNode (callback) {
|
||||
PeerInfo.create((err, peerInfo) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
const peerIdStr = peerInfo.id.toB58String()
|
||||
const ma = `/dns4/star-signal.cloud.ipfs.team/tcp/443/wss/p2p-webrtc-star/p2p/${peerIdStr}`
|
||||
|
||||
peerInfo.multiaddrs.add(ma)
|
||||
|
||||
const node = new Node({
|
||||
peerInfo
|
||||
})
|
||||
|
||||
node.idStr = peerIdStr
|
||||
callback(null, node)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = createNode
|
@ -1,54 +0,0 @@
|
||||
/* eslint no-console: ["error", { allow: ["log"] }] */
|
||||
/* eslint max-nested-callbacks: ["error", 5] */
|
||||
'use strict'
|
||||
|
||||
const domReady = require('detect-dom-ready')
|
||||
const createNode = require('./create-node')
|
||||
|
||||
domReady(() => {
|
||||
const myPeerDiv = document.getElementById('my-peer')
|
||||
const swarmDiv = document.getElementById('swarm')
|
||||
|
||||
createNode((err, node) => {
|
||||
if (err) {
|
||||
return console.log('Could not create the Node, check if your browser has WebRTC Support', err)
|
||||
}
|
||||
|
||||
node.on('peer:discovery', (peerInfo) => {
|
||||
console.log('Discovered a peer:', peerInfo.id.toB58String())
|
||||
})
|
||||
|
||||
node.on('peer:connect', (peerInfo) => {
|
||||
const idStr = peerInfo.id.toB58String()
|
||||
console.log('Got connection to: ' + idStr)
|
||||
const connDiv = document.createElement('div')
|
||||
connDiv.innerHTML = 'Connected to: ' + idStr
|
||||
connDiv.id = idStr
|
||||
swarmDiv.append(connDiv)
|
||||
})
|
||||
|
||||
node.on('peer:disconnect', (peerInfo) => {
|
||||
const idStr = peerInfo.id.toB58String()
|
||||
const el = document.getElementById(idStr)
|
||||
el && el.remove()
|
||||
})
|
||||
|
||||
node.start((err) => {
|
||||
if (err) {
|
||||
return console.log(err)
|
||||
}
|
||||
|
||||
const idStr = node.peerInfo.id.toB58String()
|
||||
|
||||
const idDiv = document
|
||||
.createTextNode('Node is ready. ID: ' + idStr)
|
||||
|
||||
myPeerDiv.append(idDiv)
|
||||
|
||||
console.log('Node is listening o/')
|
||||
|
||||
// NOTE: to stop the node
|
||||
// node.stop((err) => {})
|
||||
})
|
||||
})
|
||||
})
|
@ -1,24 +1,46 @@
|
||||
# libp2p running in the Browser
|
||||
# libp2p in the browser
|
||||
|
||||
One of the primary goals with libp2p P2P was to get it fully working in the browser and interopable with the versions running in Go and in Node.js.
|
||||
This example leverages the [Parcel.js bundler](https://parceljs.org/) to compile and serve the libp2p code in the browser. Parcel uses [Babel](https://babeljs.io/) to handle transpilation of the code. You can use other bundlers such as Webpack or Browserify, but we will not be covering them here.
|
||||
|
||||
# 1. Setting up a simple app that lists connections to other nodes
|
||||
## Setup
|
||||
|
||||
Start by installing libp2p's dependencies.
|
||||
In order to run the example, first install the dependencies from same directory as this README:
|
||||
|
||||
```bash
|
||||
> cd ../../
|
||||
> npm install
|
||||
> cd examples/libp2p-in-the-browser
|
||||
```
|
||||
cd ./examples/libp2p-in-the-browser
|
||||
npm install
|
||||
```
|
||||
|
||||
Then simply go into the folder [1](./1) and execute the following
|
||||
## Running the examples
|
||||
|
||||
```bash
|
||||
> cd 1
|
||||
> npm install
|
||||
> npm start
|
||||
# open your browser in port :9090
|
||||
Start by running the Parcel server:
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
[Version Published on IPFS](http://ipfs.io/ipfs/Qmbc1J7ehw1dNYachbkCWPto4RsnVvqCKNVzmYEod2gXcy)
|
||||
The output should look something like this:
|
||||
|
||||
```log
|
||||
$ npm start
|
||||
|
||||
> libp2p-in-browser@1.0.0 start
|
||||
> parcel index.html
|
||||
|
||||
Server running at http://localhost:1234
|
||||
✨ Built in 1000ms.
|
||||
```
|
||||
|
||||
This will compile the code and start a server listening on port [http://localhost:1234](http://localhost:1234). Now open your browser to `http://localhost:1234`. You should see a log of your node's Peer ID, the discovered peers from the Bootstrap module, and connections to those peers as they are created.
|
||||
|
||||
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.
|
||||
|
23
examples/libp2p-in-the-browser/index.html
Normal file
23
examples/libp2p-in-the-browser/index.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>js-libp2p parcel.js browser example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1 id="status">Starting libp2p...</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<pre id="output"></pre>
|
||||
</main>
|
||||
|
||||
<script src="./index.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
77
examples/libp2p-in-the-browser/index.js
Normal file
77
examples/libp2p-in-the-browser/index.js
Normal file
@ -0,0 +1,77 @@
|
||||
import 'babel-polyfill'
|
||||
import Libp2p from 'libp2p'
|
||||
import Websockets from 'libp2p-websockets'
|
||||
import WebRTCStar from 'libp2p-webrtc-star'
|
||||
import { NOISE } from 'libp2p-noise'
|
||||
import Secio from 'libp2p-secio'
|
||||
import Mplex from 'libp2p-mplex'
|
||||
import Boostrap 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: [NOISE, Secio],
|
||||
streamMuxer: [Mplex],
|
||||
peerDiscovery: [Boostrap]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
bootstrap: {
|
||||
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'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// UI elements
|
||||
const status = document.getElementById('status')
|
||||
const output = document.getElementById('output')
|
||||
|
||||
output.textContent = ''
|
||||
|
||||
function log (txt) {
|
||||
console.info(txt)
|
||||
output.textContent += `${txt.trim()}\n`
|
||||
}
|
||||
|
||||
// Listen for new peers
|
||||
libp2p.on('peer:discovery', (peerId) => {
|
||||
log(`Found peer ${peerId.toB58String()}`)
|
||||
})
|
||||
|
||||
// Listen for new connections to peers
|
||||
libp2p.connectionManager.on('peer:connect', (connection) => {
|
||||
log(`Connected to ${connection.remotePeer.toB58String()}`)
|
||||
})
|
||||
|
||||
// Listen for peers disconnecting
|
||||
libp2p.connectionManager.on('peer:disconnect', (connection) => {
|
||||
log(`Disconnected from ${connection.remotePeer.toB58String()}`)
|
||||
})
|
||||
|
||||
await libp2p.start()
|
||||
status.innerText = 'libp2p started!'
|
||||
log(`libp2p id is ${libp2p.peerId.toB58String()}`)
|
||||
|
||||
// Export libp2p to the window so you can play with the API
|
||||
window.libp2p = libp2p
|
||||
})
|
34
examples/libp2p-in-the-browser/package.json
Normal file
34
examples/libp2p-in-the-browser/package.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "libp2p-in-browser",
|
||||
"version": "1.0.0",
|
||||
"description": "A libp2p node running in the browser",
|
||||
"main": "index.js",
|
||||
"browserslist": [
|
||||
"last 2 Chrome versions"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "parcel index.html"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-env": "^7.8.3",
|
||||
"libp2p": "../../",
|
||||
"libp2p-bootstrap": "^0.11",
|
||||
"libp2p-mplex": "^0.9.3",
|
||||
"libp2p-noise": "^1.1.0",
|
||||
"libp2p-secio": "^0.12.2",
|
||||
"libp2p-webrtc-star": "^0.18.0",
|
||||
"libp2p-websockets": "^0.13.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.3",
|
||||
"@babel/core": "^7.8.3",
|
||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-transform-regenerator": "^6.26.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"parcel-bundler": "^1.12.4"
|
||||
}
|
||||
}
|
@ -1,77 +1,57 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('../../')
|
||||
const Libp2p = require('../../')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Mplex = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const PeerInfo = require('peer-info')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ SECIO ],
|
||||
// we add the DHT module that will enable Peer and Content Routing
|
||||
dht: KadDHT
|
||||
},
|
||||
config: {
|
||||
dht: {
|
||||
enabled: true,
|
||||
kBucketSize: 20
|
||||
}
|
||||
const delay = require('delay')
|
||||
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [NOISE, SECIO],
|
||||
dht: KadDHT
|
||||
},
|
||||
config: {
|
||||
dht: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({
|
||||
peerInfo
|
||||
})
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
const node3 = nodes[2]
|
||||
|
||||
parallel([
|
||||
(cb) => node1.dial(node2.peerInfo, cb),
|
||||
(cb) => node2.dial(node3.peerInfo, cb),
|
||||
// Set up of the cons might take time
|
||||
(cb) => setTimeout(cb, 300)
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
node1.peerRouting.findPeer(node3.peerInfo.id, (err, peer) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Found it, multiaddrs are:')
|
||||
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
await node.start()
|
||||
return node
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
const [node1, node2, node3] = await Promise.all([
|
||||
createNode(),
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
|
||||
|
||||
await Promise.all([
|
||||
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.peerId)
|
||||
|
||||
console.log('Found it, multiaddrs are:')
|
||||
peer.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${peer.id.toB58String()}`))
|
||||
})();
|
||||
|
@ -1,85 +1,66 @@
|
||||
/* eslint-disable no-console */
|
||||
'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 CID = require('cids')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ SECIO ],
|
||||
// we add the DHT module that will enable Peer and Content Routing
|
||||
dht: KadDHT
|
||||
},
|
||||
config: {
|
||||
dht: {
|
||||
enabled: true,
|
||||
kBucketSize: 20
|
||||
}
|
||||
const all = require('it-all')
|
||||
const delay = require('delay')
|
||||
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [NOISE, SECIO],
|
||||
dht: KadDHT
|
||||
},
|
||||
config: {
|
||||
dht: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({
|
||||
peerInfo
|
||||
})
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
const node3 = nodes[2]
|
||||
|
||||
parallel([
|
||||
(cb) => node1.dial(node2.peerInfo, cb),
|
||||
(cb) => node2.dial(node3.peerInfo, cb),
|
||||
// Set up of the cons might take time
|
||||
(cb) => setTimeout(cb, 300)
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
|
||||
|
||||
node1.contentRouting.provide(cid, (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Node %s is providing %s', node1.peerInfo.id.toB58String(), cid.toBaseEncodedString())
|
||||
|
||||
node3.contentRouting.findProviders(cid, 5000, (err, providers) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Found provider:', providers[0].id.toB58String())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
await node.start()
|
||||
return node
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
const [node1, node2, node3] = await Promise.all([
|
||||
createNode(),
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
|
||||
|
||||
await Promise.all([
|
||||
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.peerId.toB58String(), cid.toBaseEncodedString())
|
||||
|
||||
// wait for propagation
|
||||
await delay(300)
|
||||
|
||||
const providers = await all(node3.contentRouting.findProviders(cid, { timeout: 3000 }))
|
||||
|
||||
console.log('Found provider:', providers[0].id.toB58String())
|
||||
})();
|
||||
|
@ -10,31 +10,30 @@ Content Routing is the category of modules that offer a way to find where conten
|
||||
|
||||
This example builds on top of the [Protocol and Stream Muxing](../protocol-and-stream-muxing). We need to install `libp2p-kad-dht`, go ahead and `npm install libp2p-kad-dht`. If you want to see the final version, open [1.js](./1.js).
|
||||
|
||||
First, let's update our bundle to support Peer Routing and Content Routing.
|
||||
First, let's update our config to support Peer Routing and Content Routing.
|
||||
|
||||
```JavaScript
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ SECIO ],
|
||||
// we add the DHT module that will enable Peer and Content Routing
|
||||
dht: KadDHT
|
||||
},
|
||||
config: {
|
||||
dht: {
|
||||
// dht must be enabled
|
||||
enabled: true,
|
||||
kBucketSize: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
const Libp2p = require('libp2p')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ NOISE, SECIO ],
|
||||
// we add the DHT module that will enable Peer and Content Routing
|
||||
dht: KadDHT
|
||||
},
|
||||
config: {
|
||||
dht: {
|
||||
// dht must be enabled
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Once that is done, we can use the createNode function we developed in the previous example to create 3 nodes. Connect node 1 to node 2 and node 2 to node 3. We will use node 2 as a way to find the whereabouts of node 3
|
||||
@ -44,22 +43,21 @@ const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
const node3 = nodes[2]
|
||||
|
||||
parallel([
|
||||
(cb) => node1.dial(node2.peerInfo, cb),
|
||||
(cb) => node2.dial(node3.peerInfo, cb),
|
||||
// Set up of the cons might take time
|
||||
(cb) => setTimeout(cb, 100)
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
|
||||
|
||||
//
|
||||
node1.peerRouting.findPeer(node3.peerInfo.id, (err, peer) => {
|
||||
if (err) { throw err }
|
||||
await Promise.all([
|
||||
node1.dial(node2.peerId),
|
||||
node2.dial(node3.peerId)
|
||||
])
|
||||
|
||||
console.log('Found it, multiaddrs are:')
|
||||
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
})
|
||||
})
|
||||
// Set up of the cons might take time
|
||||
await delay(100)
|
||||
|
||||
const peer = await node1.peerRouting.findPeer(node3.peerId)
|
||||
|
||||
console.log('Found it, multiaddrs are:')
|
||||
peer.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${peer.id.toB58String()}`))
|
||||
```
|
||||
|
||||
You should see the output being something like:
|
||||
@ -67,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.
|
||||
@ -82,17 +80,12 @@ You can find this example completed in [2.js](./2.js), however as you will see i
|
||||
Instead of calling `peerRouting.findPeer`, we will use `contentRouting.provide` and `contentRouting.findProviders`.
|
||||
|
||||
```JavaScript
|
||||
node1.contentRouting.provide(cid, (err) => {
|
||||
if (err) { throw err }
|
||||
await node1.contentRouting.provide(cid)
|
||||
console.log('Node %s is providing %s', node1.peerId.toB58String(), cid.toBaseEncodedString())
|
||||
|
||||
console.log('Node %s is providing %s', node1.peerInfo.id.toB58String(), cid.toBaseEncodedString())
|
||||
const provs = await all(node3.contentRouting.findProviders(cid, { timeout: 5000 }))
|
||||
|
||||
node3.contentRouting.findProviders(cid, 5000, (err, providers) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Found provider:', providers[0].id.toB58String())
|
||||
})
|
||||
})
|
||||
console.log('Found provider:', providers[0].id.toB58String())
|
||||
```
|
||||
|
||||
The output of your program should look like:
|
||||
|
1
examples/pnet/.gitignore
vendored
Normal file
1
examples/pnet/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
tmp/
|
29
examples/pnet/README.md
Normal file
29
examples/pnet/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Private Networking
|
||||
This example shows how to set up a private network of libp2p nodes.
|
||||
|
||||
## Setup
|
||||
Install dependencies:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
## Run
|
||||
Running the example will cause two nodes with the same swarm key to be started and exchange basic information.
|
||||
|
||||
```
|
||||
node index.js
|
||||
```
|
||||
|
||||
### Using different keys
|
||||
This example includes `TASK` comments that can be used to try the example with different swarm keys. This will
|
||||
allow you to see how nodes will fail to connect if they are on different private networks and try to connect to
|
||||
one another.
|
||||
|
||||
To change the swarm key of one of the nodes, look through `index.js` for comments starting with `TASK` to indicate
|
||||
where lines are that pertain to changing the swarm key of node 2.
|
||||
|
||||
### Exploring the repos
|
||||
Once you've run the example you can take a look at the repos in the `./tmp` directory to see how they differ, including
|
||||
the swarm keys. You should see a `swarm.key` file in each of the repos and when the nodes are on the same private network
|
||||
this contents of the `swarm.key` files should be the same.
|
53
examples/pnet/index.js
Normal file
53
examples/pnet/index.js
Normal file
@ -0,0 +1,53 @@
|
||||
/* 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)
|
||||
generate(swarmKey)
|
||||
|
||||
// This key is for testing a different key not working
|
||||
const otherSwarmKey = Buffer.alloc(95)
|
||||
generate(otherSwarmKey)
|
||||
|
||||
;(async () => {
|
||||
const node1 = await privateLibp2pNode(swarmKey)
|
||||
|
||||
// TASK: switch the commented out line below so we're using a different key, to see the nodes fail to connect
|
||||
const node2 = await privateLibp2pNode(swarmKey)
|
||||
// const node2 = await privateLibp2pNode(otherSwarmKey)
|
||||
|
||||
await Promise.all([
|
||||
node1.start(),
|
||||
node2.start()
|
||||
])
|
||||
|
||||
console.log('nodes started...')
|
||||
|
||||
// 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(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
const { stream } = await node1.dialProtocol(node2.peerId, '/private')
|
||||
|
||||
await pipe(
|
||||
['This message is sent on a private network'],
|
||||
stream
|
||||
)
|
||||
})()
|
39
examples/pnet/libp2p-node.js
Normal file
39
examples/pnet/libp2p-node.js
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
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 with the given `swarmKey` to create the Protector
|
||||
*
|
||||
* @param {Buffer} swarmKey
|
||||
* @returns {Promise<libp2p>} Returns a libp2pNode function for use in IPFS creation
|
||||
*/
|
||||
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: [NOISE, SECIO],
|
||||
// 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(swarmKey)
|
||||
}
|
||||
})
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
module.exports = privateLibp2pNode
|
20
examples/pnet/package.json
Normal file
20
examples/pnet/package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"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-noise": "^1.1.0",
|
||||
"libp2p-secio": "^0.12.1",
|
||||
"libp2p-tcp": "^0.14.2"
|
||||
}
|
||||
}
|
28
examples/pnet/utils.js
Normal file
28
examples/pnet/utils.js
Normal file
@ -0,0 +1,28 @@
|
||||
'use strict'
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
/**
|
||||
* mkdirp recursively creates needed folders for the given dir path
|
||||
* @param {string} dir
|
||||
* @returns {string} The path that was created
|
||||
*/
|
||||
module.exports.mkdirp = (dir) => {
|
||||
return path
|
||||
.resolve(dir)
|
||||
.split(path.sep)
|
||||
.reduce((acc, cur) => {
|
||||
const currentPath = path.normalize(acc + path.sep + cur)
|
||||
|
||||
try {
|
||||
fs.statSync(currentPath)
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
fs.mkdirSync(currentPath)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
return currentPath
|
||||
}, '')
|
||||
}
|
@ -1,102 +1,81 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('../../')
|
||||
const Libp2p = require('../../')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const pull = require('pull-stream')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ]
|
||||
}
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [NOISE, SECIO]
|
||||
}
|
||||
})
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
await node.start()
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
;(async () => {
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({
|
||||
peerInfo
|
||||
})
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
// Add node's 2 data to the PeerStore
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
|
||||
// exact matching
|
||||
node2.handle('/your-protocol', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
node2.handle('/your-protocol', ({ stream }) => {
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// semver matching
|
||||
// multiple protocols
|
||||
/*
|
||||
node2.handle('/another-protocol/1.0.1', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
*/
|
||||
|
||||
// custom func matching
|
||||
/*
|
||||
node2.handle('/custom-match-func', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
}, (myProtocol, requestedProtocol, callback) => {
|
||||
if (myProtocol.indexOf(requestedProtocol)) {
|
||||
callback(null, true)
|
||||
} else {
|
||||
callback(null, false)
|
||||
node2.handle(['/another-protocol/1.0.0', '/another-protocol/2.0.0'], ({ protocol, stream }) => {
|
||||
if (protocol === '/another-protocol/2.0.0') {
|
||||
// handle backwards compatibility
|
||||
}
|
||||
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
*/
|
||||
|
||||
node1.dialProtocol(node2.peerInfo, '/your-protocol', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['my own protocol, wow!']), conn)
|
||||
})
|
||||
const { stream } = await node1.dialProtocol(node2.peerId, ['/your-protocol'])
|
||||
await pipe(
|
||||
['my own protocol, wow!'],
|
||||
stream
|
||||
)
|
||||
|
||||
/*
|
||||
node1.dialProtocol(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['semver me please']), conn)
|
||||
})
|
||||
*/
|
||||
const { stream } = node1.dialProtocol(node2.peerId, ['/another-protocol/1.0.0'])
|
||||
|
||||
/*
|
||||
node1.dialProtocol(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['do I fall into your criteria?']), conn)
|
||||
})
|
||||
await pipe(
|
||||
['my own protocol, wow!'],
|
||||
stream
|
||||
)
|
||||
*/
|
||||
})
|
||||
})();
|
||||
|
@ -1,83 +1,65 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('../../')
|
||||
const Libp2p = require('../../')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const SPDY = require('libp2p-spdy')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const series = require('async/series')
|
||||
const pull = require('pull-stream')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ SPDY ]
|
||||
}
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [NOISE, SECIO]
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({
|
||||
peerInfo
|
||||
})
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node2.handle('/a', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
node2.handle('/b', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
await node.start()
|
||||
|
||||
series([
|
||||
(cb) => node1.dialProtocol(node2.peerInfo, '/a', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['protocol (a)']), conn)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['protocol (b)']), conn)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['another conn on protocol (b)']), conn)
|
||||
cb()
|
||||
})
|
||||
return node
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(),
|
||||
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,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(`from: ${protocol}, msg: ${msg.toString()}`)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
const { stream: stream1 } = await node1.dialProtocol(node2.peerId, ['/a'])
|
||||
await pipe(
|
||||
['protocol (a)'],
|
||||
stream1
|
||||
)
|
||||
|
||||
const { stream: stream2 } = await node1.dialProtocol(node2.peerId, ['/b'])
|
||||
await pipe(
|
||||
['protocol (b)'],
|
||||
stream2
|
||||
)
|
||||
|
||||
const { stream: stream3 } = await node1.dialProtocol(node2.peerId, ['/b'])
|
||||
await pipe(
|
||||
['another stream on protocol (b)'],
|
||||
stream3
|
||||
)
|
||||
})();
|
||||
|
@ -1,88 +1,71 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('../../')
|
||||
const Libp2p = require('../../')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const SPDY = require('libp2p-spdy')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const series = require('async/series')
|
||||
const pull = require('pull-stream')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ SPDY ]
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [NOISE, SECIO]
|
||||
}
|
||||
})
|
||||
|
||||
await node.start()
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
// Add node's 2 data to the PeerStore
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
|
||||
node1.handle('/node-1', ({ stream }) => {
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({
|
||||
peerInfo
|
||||
})
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node1.handle('/node-1', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
node2.handle('/node-2', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
node2.handle('/node-2', ({ stream }) => {
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
series([
|
||||
(cb) => node1.dialProtocol(node2.peerInfo, '/node-2', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['from 1 to 2']), conn)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node2.dialProtocol(node1.peerInfo, '/node-1', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['from 2 to 1']), conn)
|
||||
cb()
|
||||
})
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
console.log('Addresses by which both peers are connected')
|
||||
node1.peerBook
|
||||
.getAllArray()
|
||||
.forEach((peer) => console.log('node 1 to node 2:', peer.isConnected().toString()))
|
||||
node2.peerBook
|
||||
.getAllArray()
|
||||
.forEach((peer) => console.log('node 2 to node 1:', peer.isConnected().toString()))
|
||||
})
|
||||
})
|
||||
const { stream: stream1 } = await node1.dialProtocol(node2.peerId, ['/node-2'])
|
||||
await pipe(
|
||||
['from 1 to 2'],
|
||||
stream1
|
||||
)
|
||||
|
||||
const { stream: stream2 } = await node2.dialProtocol(node1.peerId, ['/node-1'])
|
||||
await pipe(
|
||||
['from 2 to 1'],
|
||||
stream2
|
||||
)
|
||||
})();
|
||||
|
@ -6,23 +6,33 @@ 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`, `async` and `pull-stream`. This example reuses the base left by the [Transports](../transports) example. You can see the complete solution at [1.js](./1.js).
|
||||
Let's see _protocol multiplexing_ in action! You will need the following modules for this example: `libp2p`, `libp2p-tcp`, `peer-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.
|
||||
|
||||
```JavaScript
|
||||
const pipe = require('it-pipe')
|
||||
const { map } = require('streaming-iterables')
|
||||
const { toBuffer } = require('it-buffer')
|
||||
|
||||
// ...
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
// Here we are telling libp2p that is someone dials this node to talk with the `/your-protocol`
|
||||
// multicodec, the protocol identifier, please call this callback and give it the connection
|
||||
// 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
|
||||
node2.handle('/your-protocol', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
node2.handle('/your-protocol', ({ stream }) => {
|
||||
pipe(
|
||||
stream,
|
||||
source => (async function () {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
})()
|
||||
)
|
||||
})
|
||||
```
|
||||
@ -30,53 +40,54 @@ node2.handle('/your-protocol', (protocol, conn) => {
|
||||
After the protocol is _handled_, now we can dial to it.
|
||||
|
||||
```JavaScript
|
||||
node1.dialProtocol(node2.peerInfo, '/your-protocol', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['my own protocol, wow!']), conn)
|
||||
})
|
||||
const { stream } = await node1.dialProtocol(node2.peerId, ['/your-protocol'])
|
||||
|
||||
await pipe(
|
||||
['my own protocol, wow!'],
|
||||
stream
|
||||
)
|
||||
```
|
||||
|
||||
You might have seen this in the [Transports](../transports) examples. However, what it was not explained is that you can do more than exact string matching, for example, you can use semver.
|
||||
|
||||
```JavaScript
|
||||
node2.handle('/another-protocol/1.0.1', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
node2.handle('/another-protocol/1.0.1', ({ stream }) => {
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
// ...
|
||||
node1.dialProtocol(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['semver me please']), conn)
|
||||
})
|
||||
const { stream } = await node1.dialProtocol(node2.peerId, ['/another-protocol/1.0.0'])
|
||||
|
||||
await pipe(
|
||||
['my own protocol, wow!'],
|
||||
stream
|
||||
)
|
||||
```
|
||||
|
||||
This feature is super power for network protocols. It works in the same way as versioning your RPC/REST API, but for anything that goes in the wire. We had to use this feature to upgrade protocols within the IPFS Stack (i.e Bitswap) and we successfully managed to do so without any network splits.
|
||||
|
||||
There is still one last feature, you can create your custom match functions.
|
||||
There is still one last feature, you can provide multiple protocols for the same handler. If you have a backwards incompatible change, but it only requires minor changes to the code, you may prefer to do protocol checking instead of having multiple handlers
|
||||
|
||||
```JavaScript
|
||||
node2.handle('/custom-match-func', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
}, (myProtocol, requestedProtocol, callback) => {
|
||||
// This is all custom. I'm checking the base path matches, think of this
|
||||
// as a HTTP routing table.
|
||||
if (myProtocol.indexOf(requestedProtocol)) {
|
||||
callback(null, true)
|
||||
} else {
|
||||
callback(null, false)
|
||||
node2.handle(['/another-protocol/1.0.0', '/another-protocol/2.0.0'], ({ protocol, stream }) => {
|
||||
if (protocol === '/another-protocol/2.0.0') {
|
||||
// handle backwards compatibility
|
||||
}
|
||||
})
|
||||
// ...
|
||||
node1.dialProtocol(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['do I fall into your criteria?']), conn)
|
||||
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
@ -84,77 +95,68 @@ Try all of this out by executing [1.js](./1.js).
|
||||
|
||||
# 2. Reuse existing connection
|
||||
|
||||
The example above would require a node to create a whole new connection for every time it dials in one of the protocols, this is a waste of resources and also it might be simply not possible (e.g lack of file descriptors, not enough ports being open, etc). What we really want is to dial a connection once and then multiplex several virtual connections (stream) over a single connection, this is where _stream multiplexing_ comes into play.
|
||||
The examples above would require a node to create a whole new connection for every time it dials in one of the protocols, this is a waste of resources and also it might be simply not possible (e.g lack of file descriptors, not enough ports being open, etc). What we really want is to dial a connection once and then multiplex several virtual connections (stream) over a single connection, this is where _stream multiplexing_ comes into play.
|
||||
|
||||
Stream multiplexing is a old concept, in fact it happens in many of the layers of the [OSI System](https://en.wikipedia.org/wiki/OSI_model), in libp2p we make this feature to our avail by letting the user pick which module for stream multiplexing to use.
|
||||
Stream multiplexing is an old concept, in fact it happens in many of the layers of the [OSI System](https://en.wikipedia.org/wiki/OSI_model). In libp2p, we make this feature to our avail by letting the user pick which module for stream multiplexing to use.
|
||||
|
||||
Currently, we have two available [libp2p-spdy](https://github.com/libp2p/js-libp2p-spdy) and [libp2p-mplex](https://github.com/libp2p/js-libp2p-mplex) and pluging them in is as easy as adding another transport. Let's revisit our libp2p bundle.
|
||||
Currently, we have [libp2p-mplex](https://github.com/libp2p/js-libp2p-mplex) and pluging it in is as easy as adding a transport. Let's revisit our libp2p configuration.
|
||||
|
||||
```JavaScript
|
||||
const SPDY = require('libp2p-spdy')
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
//...
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
// Here we are adding the SPDY muxer to our libp2p bundle.
|
||||
// Thanks to protocol muxing, a libp2p bundle can support multiple Stream Muxers at the same
|
||||
// time and pick the right one when dialing to a node
|
||||
streamMuxer: [ SPDY ]
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
const createNode = () => {
|
||||
return Libp2p.create({
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ]
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
With this, we can dial as many times as we want to a peer and always reuse the same established underlying connection.
|
||||
|
||||
```JavaScript
|
||||
node2.handle('/a', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
node2.handle(['/a', '/b'], ({ protocol, stream }) => {
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(`from: ${protocol}, msg: ${msg.toString()}`)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
node2.handle('/b', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
const { stream } = await node1.dialProtocol(node2.peerId, ['/a'])
|
||||
await pipe(
|
||||
['protocol (a)'],
|
||||
stream
|
||||
)
|
||||
|
||||
series([
|
||||
(cb) => node1.dialProtocol(node2.peerInfo, '/a', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['protocol (a)']), conn)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['protocol (b)']), conn)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['another conn on protocol (b)']), conn)
|
||||
cb()
|
||||
})
|
||||
])
|
||||
const { stream: stream2 } = await node1.dialProtocol(node2.peerId, ['/b'])
|
||||
await pipe(
|
||||
['protocol (b)'],
|
||||
stream2
|
||||
)
|
||||
|
||||
const { stream: stream3 } = await node1.dialProtocol(node2.peerId, ['/b'])
|
||||
await pipe(
|
||||
['another stream on protocol (b)'],
|
||||
stream3
|
||||
)
|
||||
```
|
||||
|
||||
By running [2.js](./2.js) you should see the following result:
|
||||
|
||||
```
|
||||
> node 2.js
|
||||
protocol (a)
|
||||
protocol (b)
|
||||
another protocol (b)
|
||||
from: /a, msg: protocol (a)
|
||||
from: /b, msg: protocol (b)
|
||||
from: /b, msg: another stream on protocol (b)
|
||||
```
|
||||
|
||||
# 3. Bidirectional connections
|
||||
@ -168,8 +170,5 @@ You can see this working on example [3.js](./3.js). The result should look like
|
||||
```Bash
|
||||
> node 3.js
|
||||
from 1 to 2
|
||||
Addresses by which both peers are connected
|
||||
node 1 to node 2: /ip4/127.0.0.1/tcp/50629/p2p/QmZwMKTo6wG4Te9A6M2eJnWDpR8uhsGed4YRegnV5DcKiv
|
||||
node 2 to node 1: /ip4/127.0.0.1/tcp/50630/p2p/QmRgormJQeDyXhDKma11eUtksoh8vWmeBoxghVt4meauW9
|
||||
from 2 to 1
|
||||
```
|
||||
|
@ -1,83 +1,53 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('../../')
|
||||
const { Buffer } = require('buffer')
|
||||
const Libp2p = require('../../')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Mplex = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const PeerInfo = require('peer-info')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const series = require('async/series')
|
||||
const Gossipsub = require('libp2p-gossipsub')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ SECIO ],
|
||||
peerDiscovery: [ MulticastDNS ]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
mdns: {
|
||||
interval: 2000,
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
EXPERIMENTAL: {
|
||||
pubsub: true
|
||||
}
|
||||
}
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [NOISE, SECIO],
|
||||
pubsub: Gossipsub
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({
|
||||
peerInfo
|
||||
})
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node1.once('peer:connect', (peer) => {
|
||||
console.log('connected to %s', peer.id.toB58String())
|
||||
|
||||
// Subscribe to the topic 'news'
|
||||
node1.pubsub.subscribe('news',
|
||||
(msg) => console.log(msg.from, msg.data.toString()),
|
||||
() => {
|
||||
setInterval(() => {
|
||||
// Publish the message on topic 'news'
|
||||
node2.pubsub.publish(
|
||||
'news',
|
||||
Buffer.from('Bird bird bird, bird is the word!'),
|
||||
() => {}
|
||||
)
|
||||
}, 1000)
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
await node.start()
|
||||
return node
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
const topic = 'news'
|
||||
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
// 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()}`)
|
||||
})
|
||||
|
||||
await node2.pubsub.subscribe(topic, (msg) => {
|
||||
console.log(`node2 received: ${msg.data.toString()}`)
|
||||
})
|
||||
|
||||
// node2 publishes "news" every second
|
||||
setInterval(() => {
|
||||
node2.pubsub.publish(topic, Buffer.from('Bird bird bird, bird is the word!'))
|
||||
}, 1000)
|
||||
})()
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Publish Subscribe
|
||||
|
||||
Publish Subscribe is also included on the stack. Currently, we have on PubSub implementation which we ship by default [libp2p-floodsub](https://github.com/libp2p/js-libp2p-floodsub), with many more being researched at [research-pubsub](https://github.com/libp2p/research-pubsub).
|
||||
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:
|
||||
|
||||
@ -12,39 +12,77 @@ We've seen many interesting use cases appear with this, here are some highlights
|
||||
|
||||
For this example, we will use MulticastDNS for automatic Peer Discovery. This example is based the previous examples found in [Discovery Mechanisms](../discovery-mechanisms). You can find the complete version at [1.js](./1.js).
|
||||
|
||||
Using PubSub is super simple, all you have to do is start a libp2p node with `EXPERIMENTAL.pubsub` set to true.
|
||||
Using PubSub is super simple, you only need to provide the implementation of your choice and you are ready to go. No need for extra configuration.
|
||||
|
||||
First, let's update our libp2p configuration with a pubsub implementation.
|
||||
|
||||
```JavaScript
|
||||
node1.once('peer:connect', (peer) => {
|
||||
console.log('connected to %s', peer.id.toB58String())
|
||||
const Libp2p = require('libp2p')
|
||||
const Gossipsub = require('libp2p-gossipsub')
|
||||
|
||||
// Subscribe to the topic 'news'
|
||||
node1.pubsub.subscribe('news',
|
||||
(msg) => console.log(msg.from, msg.data.toString()),
|
||||
() => {
|
||||
setInterval(() => {
|
||||
// Publish the message on topic 'news'
|
||||
node2.pubsub.publish(
|
||||
'news',
|
||||
Buffer.from('Bird bird bird, bird is the word!'),
|
||||
() => {}
|
||||
)
|
||||
}, 1000)
|
||||
}
|
||||
)
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
connEncryption: [ NOISE, SECIO ],
|
||||
// we add the Pubsub module we want
|
||||
pubsub: Gossipsub
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Once that is done, we only need to create a few libp2p nodes, connect them and everything is ready to start using pubsub.
|
||||
|
||||
```JavaScript
|
||||
const topic = 'news'
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
// 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()}`)
|
||||
})
|
||||
|
||||
await node2.pubsub.subscribe(topic, (msg) => {
|
||||
console.log(`node2 received: ${msg.data.toString()}`)
|
||||
})
|
||||
|
||||
// node2 publishes "news" every second
|
||||
setInterval(() => {
|
||||
node2.pubsub.publish(topic, Buffer.from('Bird bird bird, bird is the word!'))
|
||||
}, 1000)
|
||||
```
|
||||
|
||||
The output of the program should look like:
|
||||
|
||||
```
|
||||
> node 1.js
|
||||
connected to QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82
|
||||
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
|
||||
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
|
||||
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
|
||||
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
|
||||
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
|
||||
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.
|
||||
|
||||
```JavaScript
|
||||
const defaults = {
|
||||
config: {
|
||||
pubsub: {
|
||||
enabled: true,
|
||||
emitSelf: false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Future work
|
||||
|
@ -1,39 +1,32 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('../../')
|
||||
const Libp2p = require('../..')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [
|
||||
TCP
|
||||
]
|
||||
}
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
// To signall 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, SECIO]
|
||||
}
|
||||
})
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
await node.start()
|
||||
return node
|
||||
}
|
||||
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({ peerInfo: peerInfo })
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
;(async () => {
|
||||
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()}`))
|
||||
})();
|
||||
|
@ -1,69 +1,60 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('../../')
|
||||
const Libp2p = require('../..')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const parallel = require('async/parallel')
|
||||
const pull = require('pull-stream')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [
|
||||
TCP
|
||||
]
|
||||
}
|
||||
const pipe = require('it-pipe')
|
||||
const concat = require('it-concat')
|
||||
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
// To signall 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, SECIO],
|
||||
streamMuxer: [MPLEX]
|
||||
}
|
||||
})
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({ peerInfo: peerInfo })
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
await node.start()
|
||||
return node
|
||||
}
|
||||
|
||||
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()}`))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
;(async () => {
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
printAddrs(node1, '1')
|
||||
printAddrs(node2, '2')
|
||||
|
||||
node2.handle('/print', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
node2.handle('/print', async ({ stream }) => {
|
||||
const result = await pipe(
|
||||
stream,
|
||||
concat
|
||||
)
|
||||
console.log(result.toString())
|
||||
})
|
||||
|
||||
node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
const { stream } = await node1.dialProtocol(node2.peerId, '/print')
|
||||
|
||||
pull(pull.values(['Hello', ' ', 'p2p', ' ', 'world', '!']), conn)
|
||||
})
|
||||
})
|
||||
await pipe(
|
||||
['Hello', ' ', 'p2p', ' ', 'world', '!'],
|
||||
stream
|
||||
)
|
||||
})();
|
||||
|
@ -1,70 +1,57 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('../../')
|
||||
const Libp2p = require('../..')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const parallel = require('async/parallel')
|
||||
const pull = require('pull-stream')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [
|
||||
TCP,
|
||||
WebSockets
|
||||
]
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
const createNode = async (transports, addresses = []) => {
|
||||
if (!Array.isArray(addresses)) {
|
||||
addresses = [addresses]
|
||||
}
|
||||
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: addresses.map((a) => a)
|
||||
},
|
||||
modules: {
|
||||
transport: transports,
|
||||
connEncryption: [NOISE, SECIO],
|
||||
streamMuxer: [MPLEX]
|
||||
}
|
||||
})
|
||||
|
||||
await node.start()
|
||||
return node
|
||||
}
|
||||
|
||||
function printAddrs(node, number) {
|
||||
console.log('node %s is listening on:', number)
|
||||
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
|
||||
}
|
||||
|
||||
function print ({ stream }) {
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (addrs, callback) {
|
||||
if (!Array.isArray(addrs)) {
|
||||
addrs = [addrs]
|
||||
}
|
||||
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
addrs.forEach((addr) => peerInfo.multiaddrs.add(addr))
|
||||
node = new MyBundle({ peerInfo: peerInfo })
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
function printAddrs (node, number) {
|
||||
console.log('node %s is listening on:', number)
|
||||
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
}
|
||||
|
||||
function print (protocol, conn) {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', cb),
|
||||
(cb) => createNode(['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws'], cb),
|
||||
(cb) => createNode('/ip4/127.0.0.1/tcp/20000/ws', cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
const node3 = nodes[2]
|
||||
;(async () => {
|
||||
const [node1, node2, node3] = await Promise.all([
|
||||
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')
|
||||
printAddrs(node2, '2')
|
||||
@ -74,21 +61,28 @@ parallel([
|
||||
node2.handle('/print', print)
|
||||
node3.handle('/print', print)
|
||||
|
||||
node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
|
||||
node3.peerStore.addressBook.set(node1.peerId, node1.multiaddrs)
|
||||
|
||||
pull(pull.values(['node 1 dialed to node 2 successfully']), conn)
|
||||
})
|
||||
// node 1 (TCP) dials to node 2 (TCP+WebSockets)
|
||||
const { stream } = await node1.dialProtocol(node2.peerId, '/print')
|
||||
await pipe(
|
||||
['node 1 dialed to node 2 successfully'],
|
||||
stream
|
||||
)
|
||||
|
||||
node2.dialProtocol(node3.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
// node 2 (TCP+WebSockets) dials to node 2 (WebSockets)
|
||||
const { stream: stream2 } = await node2.dialProtocol(node3.peerId, '/print')
|
||||
await pipe(
|
||||
['node 2 dialed to node 3 successfully'],
|
||||
stream2
|
||||
)
|
||||
|
||||
pull(pull.values(['node 2 dialed to node 3 successfully']), conn)
|
||||
})
|
||||
|
||||
node3.dialProtocol(node1.peerInfo, '/print', (err, conn) => {
|
||||
if (err) {
|
||||
console.log('node 3 failed to dial to node 1 with:', err.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
// node 3 (listening WebSockets) can dial node 1 (TCP)
|
||||
try {
|
||||
await node3.dialProtocol(node1.peerId, '/print')
|
||||
} catch (err) {
|
||||
console.log('node 3 failed to dial to node 1 with:', err.message)
|
||||
}
|
||||
})();
|
||||
|
@ -1,85 +1,65 @@
|
||||
# [Transports](http://libp2p.io/implementations/#transports)
|
||||
|
||||
libp2p doesn't make assumptions for you, instead, it enables you as the developer of the application to pick the modules you need to run your application, which can vary depending on the runtime you are executing. A libp2p node can use one or more Transports to dial and listen for Connections. These transports are modules that offer a clean interface for dialing and listening, defined by the [interface-transport](https://github.com/libp2p/interface-transport) specification. Some examples of possible transports are: TCP, UTP, WebRTC, QUIC, HTTP, Pigeon and so on.
|
||||
libp2p doesn't make assumptions for you, instead, it enables you as the developer of the application to pick the modules you need to run your application, which can vary depending on the runtime you are executing. A libp2p node can use one or more Transports to dial and listen for Connections. These transports are modules that offer a clean interface for dialing and listening, defined by the [interface-transport](https://github.com/libp2p/js-interfaces/tree/master/src/transport) specification. Some examples of possible transports are: TCP, UTP, WebRTC, QUIC, HTTP, Pigeon and so on.
|
||||
|
||||
A more complete definition of what is a transport can be found on the [interface-transport](https://github.com/libp2p/interface-transport) specification. A way to recognize a candidate transport is through the badge:
|
||||
A more complete definition of what is a transport can be found on the [interface-transport](https://github.com/libp2p/js-interfaces/tree/master/src/transport) specification. A way to recognize a candidate transport is through the badge:
|
||||
|
||||
[](https://raw.githubusercontent.com/diasdavid/interface-transport/master/img/badge.png)
|
||||
|
||||
## 1. Creating a libp2p Bundle with TCP
|
||||
## 1. Creating a libp2p node with TCP
|
||||
|
||||
When using libp2p, you always want to create your own libp2p Bundle, that is, pick your set of modules and create your network stack with the properties you need. In this example, we will create a bundle with TCP. You can find the complete solution on the file [1.js](./1.js).
|
||||
When using libp2p, you need properly configure it, that is, pick your set of modules and create your network stack with the properties you need. In this example, we will create a libp2p node TCP. You can find the complete solution on the file [1.js](./1.js).
|
||||
|
||||
You will need 5 deps total, so go ahead and install all of them with:
|
||||
You will need 4 dependencies total, so go ahead and install all of them with:
|
||||
|
||||
```bash
|
||||
> npm install libp2p libp2p-tcp peer-info async @nodeutils/defaults-deep
|
||||
> npm install libp2p libp2p-tcp libp2p-secio peer-info
|
||||
```
|
||||
|
||||
Then, on your favorite text editor create a file with the `.js` extension. I've called mine `1.js`.
|
||||
|
||||
First thing is to create our own bundle! Insert:
|
||||
First thing is to create our own libp2p node! Insert:
|
||||
|
||||
```JavaScript
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
// This MyBundle class is your libp2p bundle packed with TCP
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
// modules is a JS object that will describe the components
|
||||
// we want for our libp2p bundle
|
||||
modules: {
|
||||
transport: [
|
||||
TCP
|
||||
]
|
||||
}
|
||||
const createNode = async () => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
// To signall 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, SECIO ]
|
||||
}
|
||||
})
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
await node.start()
|
||||
return node
|
||||
}
|
||||
```
|
||||
|
||||
Now that we have our own MyBundle class that extends libp2p, let's create a node with it. We will use `async/waterfall` just for code structure, but you don't need to. Append to the same file:
|
||||
Now that we have a function to create our own libp2p node, let's create a node with it.
|
||||
|
||||
```JavaScript
|
||||
let node
|
||||
const node = await createNode()
|
||||
|
||||
waterfall([
|
||||
// First we create a PeerInfo object, which will pack the
|
||||
// info about our peer. Creating a PeerInfo is an async
|
||||
// operation because we use the WebCrypto API
|
||||
// (yeei Universal JS)
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
// To signall the addresses we want to be available, we use
|
||||
// the multiaddr format, a self describable address
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
// Now we can create a node with that PeerInfo object
|
||||
node = new MyBundle({ peerInfo: peerInfo })
|
||||
// Last, we start the node!
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
// At this point the node has started
|
||||
console.log('node has started (true/false):', node.isStarted())
|
||||
// And we can print the now listening addresses.
|
||||
// If you are familiar with TCP, you might have noticed
|
||||
// that we specified the node to listen in 0.0.0.0 and port
|
||||
// 0, which means "listen in any network interface and pick
|
||||
// a port for me
|
||||
console.log('listening on:')
|
||||
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
})
|
||||
// At this point the node has started
|
||||
console.log('node has started (true/false):', node.isStarted())
|
||||
// And we can print the now listening addresses.
|
||||
// If you are familiar with TCP, you might have noticed
|
||||
// that we specified the node to listen in 0.0.0.0 and port
|
||||
// 0, which means "listen in any network interface and pick
|
||||
// a port for me
|
||||
console.log('listening on:')
|
||||
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
|
||||
```
|
||||
|
||||
Running this should result in something like:
|
||||
@ -96,78 +76,61 @@ That `QmW2cKTakTYqbQkUzBTEGXgWYFj1YEPeUndE1YWs6CBzDQ` is the PeerId that was cre
|
||||
|
||||
## 2. Dialing from one node to another node
|
||||
|
||||
Now that we have our bundle, let's create two nodes and make them dial to each other! You can find the complete solution at [2.js](./2.js).
|
||||
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.
|
||||
|
||||
```bash
|
||||
> npm install pull-stream
|
||||
> npm install it-pipe it-buffer
|
||||
```
|
||||
|
||||
And we also need to import the module on our .js file:
|
||||
|
||||
```js
|
||||
const pull = require('pull-stream')
|
||||
const pipe = require('it-pipe')
|
||||
const { toBuffer } = require('it-buffer')
|
||||
```
|
||||
|
||||
We are going to reuse the MyBundle class from step 1, but this time to make things simpler, we will create two functions, one to create nodes and another to print the addrs to avoid duplicating code.
|
||||
We are going to reuse the `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.
|
||||
|
||||
```JavaScript
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle({ peerInfo: peerInfo })
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
function printAddrs (node, number) {
|
||||
console.log('node %s is listening on:', number)
|
||||
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Now we are going to use `async/parallel` to create two nodes, print their addresses and dial from one node to the other. We already added `async` as a dependency, but still need to import `async/parallel`:
|
||||
|
||||
```js
|
||||
const parallel = require('async/parallel')
|
||||
```
|
||||
|
||||
Then,
|
||||
|
||||
```js
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
;(async () => {
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
printAddrs(node1, '1')
|
||||
printAddrs(node2, '2')
|
||||
|
||||
node2.handle('/print', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
node2.handle('/print', ({ stream }) => {
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
const { stream } = await node1.dialProtocol(node2.peerId, '/print')
|
||||
|
||||
pull(pull.values(['Hello', ' ', 'p2p', ' ', 'world', '!']), conn)
|
||||
})
|
||||
})
|
||||
await pipe(
|
||||
['Hello', ' ', 'p2p', ' ', 'world', '!'],
|
||||
stream
|
||||
)
|
||||
})();
|
||||
```
|
||||
|
||||
The result should be look like:
|
||||
@ -195,46 +158,29 @@ In this example, we will need to also install `libp2p-websockets`, go ahead and
|
||||
> npm install libp2p-websockets
|
||||
```
|
||||
|
||||
We want to create 3 nodes, one with TCP, one with TCP+WebSockets and one with just WebSockets. We need to update our `MyBundle` class to contemplate WebSockets as well:
|
||||
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:
|
||||
|
||||
```JavaScript
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
// ...
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [
|
||||
TCP,
|
||||
WebSockets
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now that we have our bundle ready, let's upgrade our createNode function to enable us to pick the addrs in which a node will start a listener.
|
||||
|
||||
```JavaScript
|
||||
function createNode (addrs, callback) {
|
||||
if (!Array.isArray(addrs)) {
|
||||
addrs = [addrs]
|
||||
const createNode = async (transports, multiaddrs = []) => {
|
||||
if (!Array.isArray(multiaddrs)) {
|
||||
multiaddrs = [multiaddrs]
|
||||
}
|
||||
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
addrs.forEach((addr) => peerInfo.multiaddrs.add(addr))
|
||||
node = new MyBundle({ peerInfo: peerInfo })
|
||||
node.start(cb)
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: multiaddrs.map((a) => multiaddr(a))
|
||||
},
|
||||
modules: {
|
||||
transport: transports,
|
||||
connEncryption: [SECIO],
|
||||
streamMuxer: [MPLEX]
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
})
|
||||
|
||||
await node.start()
|
||||
return node
|
||||
}
|
||||
```
|
||||
|
||||
@ -243,58 +189,60 @@ As a rule, a libp2p node will only be capable of using a transport if: a) it has
|
||||
Let's update our flow to create nodes and see how they behave when dialing to each other:
|
||||
|
||||
```JavaScript
|
||||
parallel([
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', cb),
|
||||
// Here we add an extra multiaddr that has a /ws at the end, this means that we want
|
||||
// to create a TCP socket, but mount it as WebSockets instead.
|
||||
(cb) => createNode(['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws'], cb),
|
||||
(cb) => createNode('/ip4/127.0.0.1/tcp/20000/ws', cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const TCP = require('libp2p-tcp')
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
const node3 = nodes[2]
|
||||
const [node1, node2, node3] = await Promise.all([
|
||||
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')
|
||||
printAddrs(node2, '2')
|
||||
printAddrs(node3, '3')
|
||||
printAddrs(node1, '1')
|
||||
printAddrs(node2, '2')
|
||||
printAddrs(node3, '3')
|
||||
|
||||
node1.handle('/print', print)
|
||||
node2.handle('/print', print)
|
||||
node3.handle('/print', print)
|
||||
node1.handle('/print', print)
|
||||
node2.handle('/print', print)
|
||||
node3.handle('/print', print)
|
||||
|
||||
// node 1 (TCP) dials to node 2 (TCP+WebSockets)
|
||||
node1.dialProtocol(node2.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
|
||||
node3.peerStore.addressBook.set(node1.peerId, node1.multiaddrs)
|
||||
|
||||
pull(pull.values(['node 1 dialed to node 2 successfully']), conn)
|
||||
})
|
||||
// node 1 (TCP) dials to node 2 (TCP+WebSockets)
|
||||
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)
|
||||
node2.dialProtocol(node3.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
// node 2 (TCP+WebSockets) dials to node 2 (WebSockets)
|
||||
const { stream: stream2 } = await node2.dialProtocol(node3.peerId, '/print')
|
||||
await pipe(
|
||||
['node 2 dialed to node 3 successfully'],
|
||||
stream2
|
||||
)
|
||||
|
||||
pull(pull.values(['node 2 dialed to node 3 successfully']), conn)
|
||||
})
|
||||
|
||||
// node 3 (WebSockets) attempts to dial to node 1 (TCP)
|
||||
node3.dialProtocol(node1.peerInfo, '/print', (err, conn) => {
|
||||
if (err) {
|
||||
console.log('node 3 failed to dial to node 1 with:', err.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
// node 3 (WebSockets) attempts to dial to node 1 (TCP)
|
||||
try {
|
||||
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:
|
||||
|
||||
```JavaScript
|
||||
function print (protocol, conn) {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
function print ({ stream }) {
|
||||
pipe(
|
||||
stream,
|
||||
async function (source) {
|
||||
for await (const msg of source) {
|
||||
console.log(msg.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
```
|
||||
@ -312,18 +260,19 @@ node 2 is listening on:
|
||||
/ip4/192.168.2.156/tcp/62619/p2p/QmWAQtWdzWXibgfyc7WRHhhv6MdqVKzXvyfSTnN2aAvixX
|
||||
node 3 is listening on:
|
||||
/ip4/127.0.0.1/tcp/20000/ws/p2p/QmVq1PWh3VSDYdFqYMtqp4YQyXcrH27N7968tGdM1VQPj1
|
||||
node 3 failed to dial to node 1 with: No available transport to dial to
|
||||
node 1 dialed to node 2 successfully
|
||||
node 2 dialed to node 3 successfully
|
||||
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.
|
||||
|
||||
## 4. How to create a new libp2p transport
|
||||
|
||||
Today there are already 3 transports available, one in the works and plenty to come, you can find these at [interface-transport implementations](https://github.com/libp2p/interface-transport#modules-that-implement-the-interface) list.
|
||||
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/interface-transport#api) it will be able to use it.
|
||||
Adding more transports is done through the same way as you added TCP and WebSockets. Some transports might offer extra functionalities, but as far as libp2p is concerned, if it follows the interface defined at the [spec](https://github.com/libp2p/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.
|
||||
|
||||
|
@ -3,84 +3,60 @@
|
||||
"Package",
|
||||
"Version",
|
||||
"Deps",
|
||||
"CI/Travis",
|
||||
"CI",
|
||||
"Coverage",
|
||||
"Lead Maintainer"
|
||||
],
|
||||
"rows": [
|
||||
"Libp2p",
|
||||
["libp2p/interface-libp2p", "interface-libp2p"],
|
||||
"libp2p",
|
||||
["libp2p/js-libp2p", "libp2p"],
|
||||
["libp2p/js-libp2p-daemon", "libp2p-daemon"],
|
||||
["libp2p/js-libp2p-daemon-client", "libp2p-daemon-client"],
|
||||
["libp2p/js-interfaces", "libp2p-interfaces"],
|
||||
["libp2p/interop", "interop-libp2p"],
|
||||
|
||||
"Connection",
|
||||
["libp2p/interface-connection", "interface-connection"],
|
||||
|
||||
"Transport",
|
||||
["libp2p/interface-transport", "interface-transport"],
|
||||
"transports",
|
||||
["libp2p/js-libp2p-tcp", "libp2p-tcp"],
|
||||
["libp2p/js-libp2p-udp", "libp2p-udp"],
|
||||
["libp2p/js-libp2p-udt", "libp2p-udt"],
|
||||
["libp2p/js-libp2p-utp", "libp2p-utp"],
|
||||
["libp2p/js-libp2p-webrtc-direct", "libp2p-webrtc-direct"],
|
||||
["libp2p/js-libp2p-webrtc-star", "libp2p-webrtc-star"],
|
||||
["libp2p/js-libp2p-websockets", "libp2p-websockets"],
|
||||
["libp2p/js-libp2p-websocket-star", "libp2p-websocket-star"],
|
||||
["libp2p/js-libp2p-websocket-star-rendezvous", "libp2p-websocket-star-rendezvous"],
|
||||
|
||||
"Crypto Channels",
|
||||
"secure channels",
|
||||
["NodeFactoryIo/js-libp2p-noise", "libp2p-noise"],
|
||||
["libp2p/js-libp2p-secio", "libp2p-secio"],
|
||||
|
||||
"Stream Muxers",
|
||||
["libp2p/interface-stream-muxer", "interface-stream-muxer"],
|
||||
"stream multiplexers",
|
||||
["libp2p/js-libp2p-mplex", "libp2p-mplex"],
|
||||
["libp2p/js-libp2p-spdy", "libp2p-spdy"],
|
||||
|
||||
"Discovery",
|
||||
["libp2p/interface-peer-discovery", "interface-peer-discovery"],
|
||||
"peer discovery",
|
||||
["libp2p/js-libp2p-bootstrap", "libp2p-bootstrap"],
|
||||
["libp2p/js-libp2p-kad-dht", "libp2p-kad-dht"],
|
||||
["libp2p/js-libp2p-mdns", "libp2p-mdns"],
|
||||
["libp2p/js-libp2p-rendezvous", "libp2p-rendezvous"],
|
||||
["libp2p/js-libp2p-webrtc-star", "libp2p-webrtc-star"],
|
||||
["libp2p/js-libp2p-websocket-star", "libp2p-websocket-star"],
|
||||
["ChainSafe/discv5", "@chainsafe/discv5"],
|
||||
|
||||
"NAT Traversal",
|
||||
["libp2p/js-libp2p-circuit", "libp2p-circuit"],
|
||||
["libp2p/js-libp2p-nat-mngr", "libp2p-nat-mngr"],
|
||||
|
||||
"Data Types",
|
||||
["libp2p/js-peer-book", "peer-book"],
|
||||
["libp2p/js-peer-id", "peer-id"],
|
||||
["libp2p/js-peer-info", "peer-info"],
|
||||
|
||||
"Content Routing",
|
||||
["libp2p/interface-content-routing", "interface-content-routing"],
|
||||
"content routing",
|
||||
["libp2p/js-libp2p-delegated-content-routing", "libp2p-delegated-content-routing"],
|
||||
["libp2p/js-libp2p-kad-dht", "libp2p-kad-dht"],
|
||||
|
||||
"Peer Routing",
|
||||
["libp2p/interface-peer-routing", "interface-peer-routing"],
|
||||
"peer routing",
|
||||
["libp2p/js-libp2p-delegated-peer-routing", "libp2p-delegated-peer-routing"],
|
||||
["libp2p/js-libp2p-kad-dht", "libp2p-kad-dht"],
|
||||
|
||||
"Record Store",
|
||||
["libp2p/interface-record-store", "interface-record-store"],
|
||||
["libp2p/js-libp2p-record", "libp2p-record"],
|
||||
|
||||
"Generics",
|
||||
["libp2p/js-libp2p-connection-manager", "libp2p-connection-manager"],
|
||||
"utilities",
|
||||
["libp2p/js-libp2p-crypto", "libp2p-crypto"],
|
||||
["libp2p/js-libp2p-crypto-secp256k1", "libp2p-crypto-secp256k1"],
|
||||
["libp2p/js-libp2p-switch", "libp2p-switch"],
|
||||
|
||||
"Extensions",
|
||||
"data types",
|
||||
["libp2p/js-peer-id", "peer-id"],
|
||||
|
||||
"pubsub",
|
||||
["libp2p/js-libp2p-pubsub", "libp2p-pubsub"],
|
||||
["libp2p/js-libp2p-floodsub", "libp2p-floodsub"],
|
||||
["libp2p/js-libp2p-identify", "libp2p-identify"],
|
||||
["libp2p/js-libp2p-keychain", "libp2p-keychain"],
|
||||
["libp2p/js-libp2p-ping", "libp2p-ping"],
|
||||
["libp2p/js-libp2p-pnet", "libp2p-pnet"],
|
||||
["ChainSafe/js-libp2p-gossipsub", "libp2p-gossipsub"],
|
||||
|
||||
"Utilities",
|
||||
["libp2p/js-p2pcat", "p2pcat"]
|
||||
"extensions",
|
||||
["libp2p/js-libp2p-nat-mgnr", "libp2p-nat-mgnr"],
|
||||
["libp2p/js-libp2p-utils", "libp2p-utils"]
|
||||
]
|
||||
}
|
||||
|
199
package.json
199
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "libp2p",
|
||||
"version": "0.25.4",
|
||||
"version": "0.28.7",
|
||||
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
|
||||
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
|
||||
"main": "src/index.js",
|
||||
@ -11,12 +11,13 @@
|
||||
"scripts": {
|
||||
"lint": "aegir lint",
|
||||
"build": "aegir build",
|
||||
"test": "aegir test -t node -t browser",
|
||||
"test:node": "aegir test -t node",
|
||||
"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",
|
||||
"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"
|
||||
"release-major": "aegir release --type major -t node -t browser",
|
||||
"coverage": "nyc --reporter=text --reporter=lcov npm run test:node"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -35,109 +36,133 @@
|
||||
},
|
||||
"homepage": "https://libp2p.io",
|
||||
"license": "MIT",
|
||||
"browser": {
|
||||
"./test/utils/bundle-nodejs": "./test/utils/bundle-browser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0",
|
||||
"npm": ">=6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "^2.6.2",
|
||||
"abort-controller": "^3.0.0",
|
||||
"aggregate-error": "^3.0.1",
|
||||
"any-signal": "^1.1.0",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"class-is": "^1.1.0",
|
||||
"debug": "^4.1.1",
|
||||
"err-code": "^1.1.2",
|
||||
"fsm-event": "^2.1.0",
|
||||
"libp2p-connection-manager": "^0.1.0",
|
||||
"libp2p-floodsub": "^0.16.1",
|
||||
"libp2p-ping": "^0.8.5",
|
||||
"libp2p-switch": "^0.42.12",
|
||||
"libp2p-websockets": "^0.12.2",
|
||||
"mafmt": "^6.0.7",
|
||||
"multiaddr": "^6.1.0",
|
||||
"once": "^1.4.0",
|
||||
"peer-book": "^0.9.1",
|
||||
"peer-id": "^0.12.2",
|
||||
"peer-info": "^0.15.1",
|
||||
"superstruct": "^0.6.0"
|
||||
"err-code": "^2.0.0",
|
||||
"events": "^3.1.0",
|
||||
"hashlru": "^2.3.0",
|
||||
"interface-datastore": "^1.0.4",
|
||||
"ipfs-utils": "^2.2.0",
|
||||
"it-all": "^1.0.1",
|
||||
"it-buffer": "^0.1.2",
|
||||
"it-handshake": "^1.0.1",
|
||||
"it-length-prefixed": "^3.0.1",
|
||||
"it-pipe": "^1.1.0",
|
||||
"it-protocol-buffers": "^0.2.0",
|
||||
"libp2p-crypto": "^0.17.6",
|
||||
"libp2p-interfaces": "^0.3.1",
|
||||
"libp2p-utils": "^0.1.2",
|
||||
"mafmt": "^7.0.0",
|
||||
"merge-options": "^2.0.0",
|
||||
"moving-average": "^1.0.0",
|
||||
"multiaddr": "^7.4.3",
|
||||
"multistream-select": "^0.15.0",
|
||||
"mutable-proxy": "^1.0.0",
|
||||
"node-forge": "^0.9.1",
|
||||
"p-any": "^3.0.0",
|
||||
"p-fifo": "^1.0.0",
|
||||
"p-settle": "^4.0.1",
|
||||
"peer-id": "^0.13.11",
|
||||
"protons": "^1.0.1",
|
||||
"retimer": "^2.0.0",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"streaming-iterables": "^4.1.0",
|
||||
"timeout-abort-controller": "^1.0.0",
|
||||
"xsalsa20": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nodeutils/defaults-deep": "^1.1.0",
|
||||
"aegir": "^19.0.3",
|
||||
"abortable-iterator": "^3.0.0",
|
||||
"aegir": "^22.0.0",
|
||||
"chai": "^4.2.0",
|
||||
"chai-checkmark": "^1.0.1",
|
||||
"cids": "^0.7.1",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-bytes": "^0.1.2",
|
||||
"chai-string": "^1.5.0",
|
||||
"cids": "^0.8.0",
|
||||
"datastore-fs": "^1.1.0",
|
||||
"datastore-level": "^1.1.0",
|
||||
"delay": "^4.3.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"electron-webrtc": "^0.3.0",
|
||||
"interface-datastore": "^0.6.0",
|
||||
"libp2p-bootstrap": "^0.9.7",
|
||||
"libp2p-circuit": "^0.3.7",
|
||||
"libp2p-delegated-content-routing": "^0.2.2",
|
||||
"libp2p-delegated-peer-routing": "^0.2.2",
|
||||
"libp2p-kad-dht": "^0.15.2",
|
||||
"libp2p-mdns": "^0.12.3",
|
||||
"libp2p-mplex": "^0.8.4",
|
||||
"libp2p-secio": "^0.11.1",
|
||||
"libp2p-spdy": "^0.13.2",
|
||||
"libp2p-tcp": "^0.13.0",
|
||||
"libp2p-webrtc-star": "^0.16.1",
|
||||
"libp2p-websocket-star": "~0.10.2",
|
||||
"libp2p-websocket-star-rendezvous": "~0.3.0",
|
||||
"lodash.times": "^4.3.2",
|
||||
"nock": "^10.0.6",
|
||||
"pull-goodbye": "0.0.2",
|
||||
"pull-mplex": "^0.1.2",
|
||||
"pull-serializer": "^0.3.2",
|
||||
"pull-stream": "^3.6.12",
|
||||
"sinon": "^7.2.7",
|
||||
"wrtc": "^0.4.1"
|
||||
"interop-libp2p": "^0.1.0",
|
||||
"ipfs-http-client": "^44.0.0",
|
||||
"it-concat": "^1.0.0",
|
||||
"it-pair": "^1.0.0",
|
||||
"it-pushable": "^1.4.0",
|
||||
"level": "^6.0.1",
|
||||
"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.6",
|
||||
"libp2p-kad-dht": "^0.19.1",
|
||||
"libp2p-mdns": "^0.14.1",
|
||||
"libp2p-mplex": "^0.9.5",
|
||||
"libp2p-noise": "^1.1.1",
|
||||
"libp2p-secio": "^0.12.4",
|
||||
"libp2p-tcp": "^0.14.1",
|
||||
"libp2p-webrtc-star": "^0.18.0",
|
||||
"libp2p-websockets": "^0.13.1",
|
||||
"multihashes": "^0.4.19",
|
||||
"nock": "^12.0.3",
|
||||
"p-defer": "^3.0.0",
|
||||
"p-times": "^3.0.0",
|
||||
"p-wait-for": "^3.1.0",
|
||||
"promisify-es6": "^1.0.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"sinon": "^9.0.2"
|
||||
},
|
||||
"contributors": [
|
||||
"Aditya Bose <13054902+adbose@users.noreply.github.com>",
|
||||
"Alan Shaw <alan.shaw@protocol.ai>",
|
||||
"Alan Shaw <alan@tableflip.io>",
|
||||
"Andrew Nesbitt <andrewnez@gmail.com>",
|
||||
"Chris Bratlien <chrisbratlien@gmail.com>",
|
||||
"Chris Dostert <chrisdostert@users.noreply.github.com>",
|
||||
"Daijiro Wachi <daijiro.wachi@gmail.com>",
|
||||
"David Dias <daviddias.p@gmail.com>",
|
||||
"Diogo Silva <fsdiogo@gmail.com>",
|
||||
"Dmitriy Ryajov <dryajov@gmail.com>",
|
||||
"Elven <mon.samuel@qq.com>",
|
||||
"Florian-Merle <florian.david.merle@gmail.com>",
|
||||
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||
"Giovanni T. Parra <fiatjaf@gmail.com>",
|
||||
"Guy Sviry <32539816+guysv@users.noreply.github.com>",
|
||||
"Henrique Dias <hacdias@gmail.com>",
|
||||
"Hugo Dias <mail@hugodias.me>",
|
||||
"Hugo Dias <hugomrdias@gmail.com>",
|
||||
"Irakli Gozalishvili <rfobic@gmail.com>",
|
||||
"Jacob Heun <jacobheun@gmail.com>",
|
||||
"Joel Gustafson <joelg@mit.edu>",
|
||||
"John Rees <johnrees@users.noreply.github.com>",
|
||||
"João Santos <joaosantos15@users.noreply.github.com>",
|
||||
"Kevin Kwok <antimatter15@gmail.com>",
|
||||
"Lars Gierth <lgierth@users.noreply.github.com>",
|
||||
"Maciej Krüger <mkg20001@gmail.com>",
|
||||
"Marcin Tojek <mtojek@users.noreply.github.com>",
|
||||
"Nuno Nogueira <nunofmn@gmail.com>",
|
||||
"Pedro Teixeira <pedro@protocol.ai>",
|
||||
"Pedro Teixeira <i@pgte.me>",
|
||||
"RasmusErik Voel Jensen <github@solsort.com>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"Ryan Bell <ryan@piing.net>",
|
||||
"Soeren <nikorpoulsen@gmail.com>",
|
||||
"Sönke Hahn <soenkehahn@gmail.com>",
|
||||
"Thomas Eizinger <thomas@eizinger.io>",
|
||||
"Tiago Alves <alvesjtiago@gmail.com>",
|
||||
"Vasco Santos <vasco.santos@ua.pt>",
|
||||
"Vasco Santos <vasco.santos@moxy.studio>",
|
||||
"Alan Shaw <alan@tableflip.io>",
|
||||
"Cayman <caymannava@gmail.com>",
|
||||
"Pedro Teixeira <i@pgte.me>",
|
||||
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||
"Alex Potsides <alex@achingbrain.net>",
|
||||
"Maciej Krüger <mkg20001@gmail.com>",
|
||||
"Hugo Dias <mail@hugodias.me>",
|
||||
"Volker Mische <volker.mische@gmail.com>",
|
||||
"dirkmc <dirkmdev@gmail.com>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"Thomas Eizinger <thomas@eizinger.io>",
|
||||
"Ryan Bell <ryan@piing.net>",
|
||||
"Giovanni T. Parra <fiatjaf@gmail.com>",
|
||||
"Andrew Nesbitt <andrewnez@gmail.com>",
|
||||
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
|
||||
"Elven <mon.samuel@qq.com>",
|
||||
"Didrik Nordström <didrik.nordstrom@gmail.com>",
|
||||
"Tiago Alves <alvesjtiago@gmail.com>",
|
||||
"Yusef Napora <yusef@napora.org>",
|
||||
"Zane Starr <zcstarr@gmail.com>",
|
||||
"ebinks <elizabethjbinks@gmail.com>",
|
||||
"greenkeeperio-bot <support@greenkeeper.io>",
|
||||
"isan_rivkin <isanrivkin@gmail.com>",
|
||||
"mayerwin <mayerwin@users.noreply.github.com>",
|
||||
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>"
|
||||
"robertkiel <robert.kiel@validitylabs.org>",
|
||||
"RasmusErik Voel Jensen <github@solsort.com>",
|
||||
"Bernd Strehl <bernd.strehl@gmail.com>",
|
||||
"Chris Bratlien <chrisbratlien@gmail.com>",
|
||||
"Daijiro Wachi <daijiro.wachi@gmail.com>",
|
||||
"Diogo Silva <fsdiogo@gmail.com>",
|
||||
"Dmitriy Ryajov <dryajov@gmail.com>",
|
||||
"Fei Liu <liu.feiwood@gmail.com>",
|
||||
"Florian-Merle <florian.david.merle@gmail.com>",
|
||||
"Francis Gulotta <wizard@roborooter.com>",
|
||||
"Henrique Dias <hacdias@gmail.com>",
|
||||
"Irakli Gozalishvili <rfobic@gmail.com>",
|
||||
"Joel Gustafson <joelg@mit.edu>",
|
||||
"Julien Bouquillon <contact@revolunet.com>",
|
||||
"Kevin Kwok <antimatter15@gmail.com>",
|
||||
"Nuno Nogueira <nunofmn@gmail.com>",
|
||||
"Soeren <nikorpoulsen@gmail.com>",
|
||||
"Sönke Hahn <soenkehahn@gmail.com>"
|
||||
]
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
# PDD Test Stories Implementation
|
||||
|
||||
> Implementation of the Compliance tests from https://github.com/libp2p/interop
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "pdd-impl",
|
||||
"version": "0.0.0",
|
||||
"description": "PDD Test Stories implementation",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": " "
|
||||
},
|
||||
"keywords": [
|
||||
"PDD",
|
||||
"libp2p"
|
||||
],
|
||||
"author": "David Dias <daviddias@ipfs.io>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"libp2p": "file:./..",
|
||||
"libp2p-interop": "github:libp2p/interop#master",
|
||||
"tape": "^4.8.0"
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('tape')
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const Multiplex = require('libp2p-multiplex')
|
||||
const Railing = require('libp2p-railing')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
const PeerInfo = require('peer-info')
|
||||
const pull = require('pull-stream')
|
||||
const waterfall = require('async/waterfall')
|
||||
const series = require('async/series')
|
||||
const PeerA = require('libp2p-interop/peer-a.json')
|
||||
const PeerB = require('libp2p-interop/peer-b.json')
|
||||
|
||||
class IPFSBundle extends libp2p {
|
||||
constructor (peerInfo, options) {
|
||||
options = Object.assign({ bootstrap: [] }, options)
|
||||
|
||||
const modules = {
|
||||
transport: [
|
||||
new TCP(),
|
||||
new WebSockets()
|
||||
],
|
||||
connection: {
|
||||
muxer: [
|
||||
Multiplex
|
||||
],
|
||||
crypto: [
|
||||
SECIO
|
||||
]
|
||||
},
|
||||
discovery: [
|
||||
new MulticastDNS(peerInfo, 'ipfs.local'),
|
||||
new Railing(options.bootstrap)
|
||||
],
|
||||
DHT: KadDHT
|
||||
}
|
||||
|
||||
super(modules, peerInfo, undefined, options)
|
||||
}
|
||||
}
|
||||
|
||||
test('story 1 - peerA', (t) => {
|
||||
t.plan(10)
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(PeerA, cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10000')
|
||||
node = new IPFSBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
t.ifErr(err, 'created Node successfully')
|
||||
t.ok(node.isStarted(), 'PeerA is Running')
|
||||
|
||||
const peerBAddr = `/ip4/127.0.0.1/tcp/10001/p2p/${PeerB.id}`
|
||||
|
||||
node.handle('/time/1.0.0', (protocol, conn) => {
|
||||
pull(
|
||||
pull.values([Date.now().toString()]),
|
||||
conn,
|
||||
pull.onEnd((err) => {
|
||||
t.ifErr(err)
|
||||
t.pass('Sent time successfully')
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
series([
|
||||
(cb) => setTimeout(cb, 5 * 1000), // time to run both scripts
|
||||
(cb) => node.ping(peerBAddr, (err, p) => {
|
||||
t.ifErr(err, 'initiated Ping to PeerB')
|
||||
p.once('error', (err) => t.ifErr(err, 'Ping should not fail'))
|
||||
p.once('ping', (time) => {
|
||||
t.pass('ping PeerB successfully')
|
||||
p.stop()
|
||||
cb()
|
||||
})
|
||||
}),
|
||||
(cb) => node.dial(peerBAddr, '/echo/1.0.0', (err, conn) => {
|
||||
t.ifErr(err, 'dial successful')
|
||||
|
||||
const data = Buffer.from('Hey')
|
||||
|
||||
pull(
|
||||
pull.values([data]),
|
||||
conn,
|
||||
pull.collect((err, values) => {
|
||||
t.ifErr(err, 'Received echo back')
|
||||
t.deepEqual(values[0], data)
|
||||
cb()
|
||||
})
|
||||
)
|
||||
}),
|
||||
(cb) => setTimeout(cb, 2 * 1000) // time to both finish
|
||||
], () => node.stop((err) => t.ifErr(err, 'PeerA has stopped')))
|
||||
})
|
||||
})
|
@ -1,98 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('tape')
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const Multiplex = require('libp2p-multiplex')
|
||||
const Railing = require('libp2p-railing')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
const PeerInfo = require('peer-info')
|
||||
const pull = require('pull-stream')
|
||||
const waterfall = require('async/waterfall')
|
||||
const series = require('async/series')
|
||||
const PeerA = require('libp2p-interop/peer-a.json')
|
||||
const PeerB = require('libp2p-interop/peer-b.json')
|
||||
|
||||
class IPFSBundle extends libp2p {
|
||||
constructor (peerInfo, options) {
|
||||
options = Object.assign({ bootstrap: [] }, options)
|
||||
|
||||
const modules = {
|
||||
transport: [
|
||||
new TCP(),
|
||||
new WebSockets()
|
||||
],
|
||||
connection: {
|
||||
muxer: [
|
||||
Multiplex
|
||||
],
|
||||
crypto: [
|
||||
SECIO
|
||||
]
|
||||
},
|
||||
discovery: [
|
||||
new MulticastDNS(peerInfo, 'ipfs.local'),
|
||||
new Railing(options.bootstrap)
|
||||
],
|
||||
DHT: KadDHT
|
||||
}
|
||||
|
||||
super(modules, peerInfo, undefined, options)
|
||||
}
|
||||
}
|
||||
|
||||
test('story 1 - peerA', (t) => {
|
||||
t.plan(8)
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(PeerB, cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10001')
|
||||
node = new IPFSBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
t.ifErr(err, 'created Node successfully')
|
||||
t.ok(node.isStarted(), 'PeerB is Running')
|
||||
|
||||
const peerAAddr = `/ip4/127.0.0.1/tcp/10000/p2p/${PeerA.id}`
|
||||
|
||||
node.handle('/echo/1.0.0', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
conn,
|
||||
pull.onEnd((err) => t.ifErr(err, 'echo was successful'))
|
||||
)
|
||||
})
|
||||
|
||||
series([
|
||||
(cb) => setTimeout(cb, 5 * 1000), // time to run both scripts
|
||||
(cb) => node.ping(peerAAddr, (err, p) => {
|
||||
t.ifErr(err, 'initiated Ping to PeerA')
|
||||
p.once('error', (err) => t.ifErr(err, 'Ping should not fail'))
|
||||
p.once('ping', (time) => {
|
||||
t.pass('ping PeerA successfully')
|
||||
p.stop()
|
||||
cb()
|
||||
})
|
||||
}),
|
||||
(cb) => node.dial(peerAAddr, '/time/1.0.0', (err, conn) => {
|
||||
t.ifErr(err, 'dial successful')
|
||||
|
||||
pull(
|
||||
pull.values([]),
|
||||
conn,
|
||||
pull.collect((err, values) => {
|
||||
t.ifErr(err, 'Received time')
|
||||
cb()
|
||||
})
|
||||
)
|
||||
}),
|
||||
(cb) => setTimeout(cb, 2 * 1000) // time to both finish
|
||||
], () => node.stop((err) => t.ifErr(err, 'PeerB has stopped')))
|
||||
})
|
||||
})
|
@ -1,54 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('tape')
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const pull = require('pull-stream')
|
||||
const PeerA = require('libp2p-interop/peer-a.json')
|
||||
const PeerB = require('libp2p-interop/peer-b.json')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
test('story 1 - peerA', (t) => {
|
||||
t.plan(6)
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(PeerA, cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10000')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
t.ifErr(err, 'created Node')
|
||||
t.ok(node.isStarted(), 'PeerA is running')
|
||||
|
||||
const PeerBAddr = `/ip4/127.0.0.1/tcp/10001/p2p/${PeerB.id}`
|
||||
|
||||
node.dial(PeerBAddr, '/echo/1.0.0', (err, conn) => {
|
||||
t.ifErr(err, 'dial successful')
|
||||
|
||||
const data = Buffer.from('Heey')
|
||||
|
||||
pull(
|
||||
pull.values([data]),
|
||||
conn,
|
||||
pull.collect((err, values) => {
|
||||
t.ifErr(err, 'Received echo back')
|
||||
t.deepEqual(values[0], data)
|
||||
node.stop((err) => t.ifErr(err, 'PeerA has stopped'))
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
@ -1,49 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('tape')
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const pull = require('pull-stream')
|
||||
const PeerB = require('libp2p-interop/peer-b.json')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
test('story 1 - peerB', (t) => {
|
||||
t.plan(5)
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(PeerB, cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10001')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
t.ifErr(err)
|
||||
t.ok(node.isStarted(), 'PeerB is running')
|
||||
|
||||
node.handle('/echo/1.0.0', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
conn,
|
||||
pull.onEnd((err) => {
|
||||
t.ifErr(err)
|
||||
t.pass('Received End of Connection')
|
||||
node.stop((err) => {
|
||||
t.ifErr(err, 'PeerB has stopped')
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
@ -1,54 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('tape')
|
||||
const libp2p = require('libp2p')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const pull = require('pull-stream')
|
||||
const PeerA = require('libp2p-interop/peer-a.json')
|
||||
const PeerB = require('libp2p-interop/peer-b.json')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new WebSockets()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
test('story 2 - peerA', (t) => {
|
||||
t.plan(6)
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(PeerA, cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10000/ws')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
t.ifErr(err, 'created Node')
|
||||
t.ok(node.isStarted(), 'PeerA is running')
|
||||
|
||||
const PeerBAddr = `/ip4/127.0.0.1/tcp/10001/p2p/${PeerB.id}`
|
||||
|
||||
node.dial(PeerBAddr, '/echo/1.0.0', (err, conn) => {
|
||||
t.ifErr(err, 'dial successful')
|
||||
|
||||
const data = Buffer.from('Heey')
|
||||
|
||||
pull(
|
||||
pull.values([data]),
|
||||
conn,
|
||||
pull.collect((err, values) => {
|
||||
t.ifErr(err, 'Received echo back')
|
||||
t.deepEqual(values[0], data)
|
||||
node.stop((err) => t.ifErr(err, 'PeerA has stopped'))
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
@ -1,49 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('tape')
|
||||
const libp2p = require('libp2p')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const pull = require('pull-stream')
|
||||
const PeerB = require('libp2p-interop/peer-b.json')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new WebSockets()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
test('story 2 - peerB', (t) => {
|
||||
t.plan(5)
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(PeerB, cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10001/ws')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
t.ifErr(err)
|
||||
t.ok(node.isStarted(), 'PeerB is running')
|
||||
|
||||
node.handle('/echo/1.0.0', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.through(v => v, err => {
|
||||
t.ifErr(err)
|
||||
t.pass('Received End of Connection')
|
||||
node.stop((err) => {
|
||||
t.ifErr(err, 'PeerB has stopped')
|
||||
})
|
||||
}),
|
||||
conn
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
@ -1,42 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('tape')
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const PeerA = require('libp2p-interop/peer-a.json')
|
||||
const PeerB = require('libp2p-interop/peer-b.json')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
test('story 3 - peerA', (t) => {
|
||||
t.plan(4)
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(PeerA, cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10000')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
t.ifErr(err, 'created Node')
|
||||
t.ok(node.isStarted(), 'PeerA is running')
|
||||
|
||||
const PeerBAddr = `/ip4/127.0.0.1/tcp/10001/ws/p2p/${PeerB.id}`
|
||||
|
||||
setTimeout(() => node.dial(PeerBAddr, '/echo/1.0.0', (err, conn) => {
|
||||
t.ok(err, 'dial failed')
|
||||
node.stop((err) => t.ifErr(err, 'PeerA has stopped'))
|
||||
}), 1000)
|
||||
})
|
||||
})
|
@ -1,42 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('tape')
|
||||
const libp2p = require('libp2p')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const PeerA = require('libp2p-interop/peer-a.json')
|
||||
const PeerB = require('libp2p-interop/peer-b.json')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new WebSockets()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
test('story 3 - peerB', (t) => {
|
||||
t.plan(4)
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(PeerB, cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10000/ws')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
t.ifErr(err, 'created Node')
|
||||
t.ok(node.isStarted(), 'PeerA is running')
|
||||
|
||||
const PeerAAddr = `/ip4/127.0.0.1/tcp/10000/ws/p2p/${PeerA.id}`
|
||||
|
||||
setTimeout(() => node.dial(PeerAAddr, '/echo/1.0.0', (err, conn) => {
|
||||
t.ok(err, 'dial failed')
|
||||
node.stop((err) => t.ifErr(err, 'PeerA has stopped'))
|
||||
}), 1000)
|
||||
})
|
||||
})
|
49
src/address-manager/README.md
Normal file
49
src/address-manager/README.md
Normal file
@ -0,0 +1,49 @@
|
||||
# Address Manager
|
||||
|
||||
The Address manager is responsible for keeping an updated register of the peer's addresses. It includes 3 different types of Addresses: `Listen Addresses`, `Announce Addresses` and `No 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.
|
||||
|
||||
## No Announce Addresses
|
||||
|
||||
While we need to add Announce Addresses to enable peers' connectivity, we should also avoid announcing addresses that will not be reachable. No Announce Addresses should be specified so that they are filtered from the advertised multiaddrs.
|
||||
|
||||
As stated in the Listen Addresses section, Listen Addresses might be modified by libp2p transports after the successfully bind to those addresses. Libp2p should also take these changes into account so that they can be matched when No Announce Addresses are being filtered out of the advertised multiaddrs.
|
||||
|
||||
## 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 and noAnnounce 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.
|
55
src/address-manager/index.js
Normal file
55
src/address-manager/index.js
Normal file
@ -0,0 +1,55 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:addresses')
|
||||
log.error = debug('libp2p:addresses:error')
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
|
||||
/**
|
||||
* Responsible for managing this peers addresses.
|
||||
* Peers can specify their listen, announce and noAnnounce addresses.
|
||||
* The listen addresses will be used by the libp2p transports to listen for new connections,
|
||||
* while the announce an noAnnounce addresses will be combined with the listen addresses for
|
||||
* address adverstising to other peers in the network.
|
||||
*/
|
||||
class AddressManager {
|
||||
/**
|
||||
* @constructor
|
||||
* @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.
|
||||
* @param {Array<string>} [options.noAnnounce = []] list of multiaddrs string representation to not announce.
|
||||
*/
|
||||
constructor ({ listen = [], announce = [], noAnnounce = [] } = {}) {
|
||||
this.listen = new Set(listen)
|
||||
this.announce = new Set(announce)
|
||||
this.noAnnounce = new Set(noAnnounce)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peer listen multiaddrs.
|
||||
* @return {Array<Multiaddr>}
|
||||
*/
|
||||
getListenAddrs () {
|
||||
return Array.from(this.listen).map((a) => multiaddr(a))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peer announcing multiaddrs.
|
||||
* @return {Array<Multiaddr>}
|
||||
*/
|
||||
getAnnounceAddrs () {
|
||||
return Array.from(this.announce).map((a) => multiaddr(a))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peer noAnnouncing multiaddrs.
|
||||
* @return {Array<Multiaddr>}
|
||||
*/
|
||||
getNoAnnounceAddrs () {
|
||||
return Array.from(this.noAnnounce).map((a) => multiaddr(a))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AddressManager
|
128
src/circuit/IMPLEMENTATION_NOTES.md
Normal file
128
src/circuit/IMPLEMENTATION_NOTES.md
Normal file
@ -0,0 +1,128 @@
|
||||
EDIT: This document is outdated and here only for historical purposes
|
||||
|
||||
NOTE: This document is structured in an `if-then/else[if]-then` manner, each line is a precondition for following lines with a higher number of indentation
|
||||
|
||||
Example:
|
||||
|
||||
- if there are apples
|
||||
- eat them
|
||||
- if not, check for pears
|
||||
- then eat them
|
||||
- if not, check for cherries
|
||||
- then eat them
|
||||
|
||||
Or,
|
||||
|
||||
- if there are apples
|
||||
- eat them
|
||||
- if not
|
||||
- check for pears
|
||||
- then eat them
|
||||
- if not
|
||||
- check for cherries
|
||||
- then eat them
|
||||
|
||||
In order to minimize nesting, the first example is preferred
|
||||
|
||||
# Relay flow
|
||||
|
||||
## Relay transport (dialer/listener)
|
||||
|
||||
- ### Dial over a relay
|
||||
- See if there is a relay that's already connected to the destination peer, if not
|
||||
- Ask all the peer's known relays to dial the destination peer until an active relay (one that can dial on behalf of other peers), or a relay that may have recently acquired a connection to the destination peer is successful.
|
||||
- If successful
|
||||
- Write the `/ipfs/relay/circuit/1.0.0` header to the relay, followed by the destination address
|
||||
- e.g. `/ipfs/relay/circuit/1.0.0\n/p2p-circuit/ipfs/QmDest`.
|
||||
- If no relays could connect, fail the same way a regular transport would
|
||||
- Once the connection has been established, the swarm should treat it as a regular connection,
|
||||
- i.e. muxing, encrypt, etc should all be performed on the relayed connection
|
||||
|
||||
- ### Listen for relayed connections
|
||||
- Peer mounts the `/ipfs/relay/circuit/1.0.0` proto and listens for relayed connections
|
||||
- A connection arrives
|
||||
- read the address of the source peer from the incoming connection stream
|
||||
- if valid, create a PeerInfo object for that peer and add the incoming address to its multiaddresses list
|
||||
- pass the connection to `protocolMuxer(swarm.protocols, conn)` to have it go through the regular muxing/encryption flow
|
||||
|
||||
- ### Relay discovery and static relay addresses in swarm config
|
||||
|
||||
- #### Relay address in swarm config
|
||||
- A peer has relay addresses in its swarm config section
|
||||
- On node startup, connect to the relays in swarm config
|
||||
- if successful add address to swarms PeerInfo's multiaddresses
|
||||
- `identify` should take care of announcing that the peer is reachable over the listed relays
|
||||
|
||||
- #### Passive relay discovery
|
||||
- A peer that can dial over `/ipfs/relay/circuit/1.0.0` listens for the `peer-mux-established` swarm event, every time a new muxed connection arrives, it checks if the incoming peer is a relay. (How would this work? Some way of discovering if its a relay is required.)
|
||||
- *Useful in cases when the peer/node doesn't know of any relays on startup and also, to learn of as many additional relays in the network as possible*
|
||||
- *Useful during startup, when connecting to bootstrap nodes. It allows us to implicitly learn if its a relay without having to explicitly add `/p2p-circuit` addresses to the bootstrap list*
|
||||
- *Also useful if the relay communicates its capabilities upon connecting to it, as to avoid additional unnecessary requests/queries. I.e. if it supports weather its able to forward connections and weather it supports the `ls` or other commands.*
|
||||
- *Should it be possible to disable passive relay discovery?*
|
||||
- This could be useful when the peer wants to be reachable **only** over the listed relays
|
||||
- If the incoming peer is a relay, send an `ls` and record its peers
|
||||
|
||||
## Relay Nodes
|
||||
|
||||
- ### Passive relay node
|
||||
- *A passive relay does not explicitly dial into any requested peer, only those that it's swarm already has connections to.*
|
||||
- When the relay gets a request, read the the destination peer's multiaddr from the connection stream and if its a valid address and peer id
|
||||
- check its swarm's peerbook(?) see if its a known peer, if it is
|
||||
- use the swarms existing connection and
|
||||
- send the multistream header and the source peer address to the dest peer
|
||||
- e.g. `/ipfs/relay/circuit/1.0.0\n/p2p-circuit/ipfs/QmSource`
|
||||
- circuit the source and dest connections
|
||||
- if couldn't dial, or the connection/stream to the dest peer closed prematurelly
|
||||
- close the src stream
|
||||
|
||||
|
||||
- ### Active relay node
|
||||
- *An active relay node can dial other peers even if its swarm doesnt know about those peers*
|
||||
- When the relay gets a request, read the the destination peer's multiaddr from the connection stream and if its a valid address and peer id
|
||||
- use the swarm to dial to the dest node
|
||||
- send the multistream header and the source peer address to the dest peer
|
||||
- e.g. `/ipfs/relay/circuit/1.0.0\n/p2p-circuit/ipfs/QmSource`
|
||||
- circuit the source and dest connections
|
||||
- if couldn't dial, or the connection/stream to the dest peer closed prematurely
|
||||
- close the src stream
|
||||
|
||||
- ### `ls` command
|
||||
- *A relay node can allow the peers known to it's swarm to be listed*
|
||||
- *this should be possible to enable/disable from the config*
|
||||
- when a relay gets the `ls` request
|
||||
- if enabled, get its swarm's peerbook's known peers and return their ids and multiaddrs
|
||||
- e.g `[{id: /ipfs/QmPeerId, addrs: ['ma1', 'ma2', 'ma3']}, ...]`
|
||||
- if disabled, respond with `na`
|
||||
|
||||
|
||||
## Relay Implementation notes
|
||||
|
||||
- ### Relay transport
|
||||
- Currently I've implemented the dialer and listener parts of the relay as a transport, meaning that it *tries* to implement the `interface-transport` interface as closely as possible. This seems to work pretty well and it's makes the dialer/listener parts really easy to plug in into the swarm. I think this is the cleanest solution.
|
||||
|
||||
- ### `circuit-relay`
|
||||
- This is implemented as a separate piece (not a transport), and it can be enabled/disabled with a config. The transport listener however, will do the initial parsing of the incoming header and figure out weather it's a connection that's needs to be handled by the circuit-relay, or its a connection that is being relayed from a circuit-relay.
|
||||
|
||||
## Relay swarm integration
|
||||
|
||||
- The relay transport is mounted explicitly by calling the `swarm.connection.relay(config.relay)` from libp2p
|
||||
- Swarm will register the dialer and listener using the swarm `transport.add` and `transport.listen` methods
|
||||
|
||||
- ### Listener
|
||||
- the listener registers itself as a multistream handler on the `/ipfs/relay/circuit/1.0.0` proto
|
||||
- if `circuit-relay` is enabled, the listener will delegate connections to it if appropriate
|
||||
- when the listener receives a connection, it will read the multiaddr and determine if its a connection that needs to be relayed, or its a connection that is being relayed
|
||||
|
||||
- ### Dialer
|
||||
- When the swarm attempts to dial to a peer, it will filter the protocols that the peer can be reached on
|
||||
- *The relay will be used in two cases*
|
||||
- If the peer has an explicit relay address that it can be reached on
|
||||
- no other transport is available
|
||||
- The relay will attempt to dial the peer over that relay
|
||||
- If no explicit relay address is provided
|
||||
- no other transport is available
|
||||
- A generic circuit address will be added to the peers multiaddr list
|
||||
- i.e. `/p2p-circuit/ipfs/QmDest`
|
||||
- If another transport is available, then use that instead of the relay
|
||||
|
||||
|
106
src/circuit/README.md
Normal file
106
src/circuit/README.md
Normal file
@ -0,0 +1,106 @@
|
||||
# js-libp2p-circuit
|
||||
|
||||
> 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. You can read more about this in its [spec](https://github.com/libp2p/specs/tree/master/relay).
|
||||
|
||||
## 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?
|
||||
|
||||
`circuit-relaying` uses additional nodes in order to transfer traffic between two otherwise unreachable nodes. This allows nodes that don't speak the same protocols or are running in limited environments, e.g. browsers and IoT devices, to communicate, which would otherwise be impossible given the fact that for example browsers don't have any socket support and as such cannot be directly dialed.
|
||||
|
||||
The use of circuit-relaying is not limited to routing traffic between browser nodes, other uses include:
|
||||
- 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.
|
||||
|
||||
### 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.
|
||||
|
||||
## Usage
|
||||
|
||||
Libp2p circuit configuration can be seen at [Setup with Relay](../../doc/CONFIGURATION.md#setup-with-relay).
|
||||
|
||||
Once you have a circuit relay node running, you can configure other nodes to use it as a relay as follows:
|
||||
|
||||
```js
|
||||
const multiaddr = require('multiaddr')
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
const relayAddr = ...
|
||||
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: [multiaddr(`${relayAddr}/p2p-circuit`)]
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
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.
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
[](https://github.com/libp2p/interface-transport)
|
||||
|
||||
`libp2p-circuit` accepts Circuit addresses for both IPFS and non IPFS encapsulated addresses, i.e:
|
||||
|
||||
`/p2p-circuit/ip4/127.0.0.1/tcp/4001/p2p/QmHash`
|
||||
|
||||
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`. 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)
|
||||
+-------------------------------------------------+ +--------------------------+
|
||||
| +---------------------------------+ | | |
|
||||
| | | | | +------------------+ |
|
||||
| | | | circuit-relay listens for the HOP | | | |
|
||||
| | libp2p <------------------------------------------------| circuit-relay | |
|
||||
| | | | message to handle incomming relay | | | |
|
||||
| | | | requests from other nodes | +------------------+ |
|
||||
| +---------------------------------+ | | |
|
||||
| ^ ^ ^ ^ ^ ^ | | +------------------+ |
|
||||
| | | | | | | | | | +-------------+ | |
|
||||
| | | | | | | | dialer uses libp2p to dial | | | | | |
|
||||
| | | | +----------------------------------------------------------------------> dialer | | |
|
||||
| | | transports | | to a circuit-relay node using the | | | | | |
|
||||
| | | | | | | HOP message | | +-------------+ | |
|
||||
| | | | | | | | | | |
|
||||
| v v | v v | | | | |
|
||||
|+------------------|----------------------------+| | | +-------------+ | |
|
||||
|| | | | | || | | | | | |
|
||||
||libp2p-tcp |libp2p-ws | .... |libp2p-circuit || listener handles STOP messages from| | | listener | | |
|
||||
|| | +--------------------------------------------------------------------------> | | |
|
||||
|| | | |plugs in just || circuit-relay nodes | | +-------------+ | |
|
||||
|| | | |as any other || | | | |
|
||||
|| | | |transport || | +------------------+ |
|
||||
|+-----------------------------------------------+| | |
|
||||
+-------------------------------------------------+ +--------------------------+
|
||||
```
|
135
src/circuit/circuit/hop.js
Normal file
135
src/circuit/circuit/hop.js
Normal file
@ -0,0 +1,135 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:circuit:hop')
|
||||
log.error = debug('libp2p:circuit:hop:error')
|
||||
|
||||
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 { codes: Errors } = require('../../errors')
|
||||
|
||||
const { stop } = require('./stop')
|
||||
|
||||
const multicodec = require('./../multicodec')
|
||||
|
||||
module.exports.handleHop = async function handleHop ({
|
||||
connection,
|
||||
request,
|
||||
streamHandler,
|
||||
circuit
|
||||
}) {
|
||||
// Ensure hop is enabled
|
||||
if (!circuit._options.hop.enabled) {
|
||||
log('HOP request received but we are not acting as a relay')
|
||||
return streamHandler.end({
|
||||
type: CircuitPB.Type.STATUS,
|
||||
code: CircuitPB.Status.HOP_CANT_SPEAK_RELAY
|
||||
})
|
||||
}
|
||||
|
||||
// Validate the HOP request has the required input
|
||||
try {
|
||||
validateAddrs(request, streamHandler)
|
||||
} catch (err) {
|
||||
return log.error('invalid hop request via peer %s', connection.remotePeer.toB58String(), err)
|
||||
}
|
||||
|
||||
// Get the connection to the destination (stop) peer
|
||||
const destinationPeer = new PeerId(request.dstPeer.id)
|
||||
|
||||
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({
|
||||
type: CircuitPB.Type.STATUS,
|
||||
code: CircuitPB.Status.HOP_NO_CONN_TO_DST
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Handle being an active relay
|
||||
|
||||
// Handle the incoming HOP request by performing a STOP request
|
||||
const stopRequest = {
|
||||
type: CircuitPB.Type.STOP,
|
||||
dstPeer: request.dstPeer,
|
||||
srcPeer: request.srcPeer
|
||||
}
|
||||
|
||||
let destinationStream
|
||||
try {
|
||||
destinationStream = await stop({
|
||||
connection: destinationConnection,
|
||||
request: stopRequest,
|
||||
circuit
|
||||
})
|
||||
} catch (err) {
|
||||
return log.error(err)
|
||||
}
|
||||
|
||||
log('hop request from %s is valid', connection.remotePeer.toB58String())
|
||||
streamHandler.write({
|
||||
type: CircuitPB.Type.STATUS,
|
||||
code: CircuitPB.Status.SUCCESS
|
||||
})
|
||||
const sourceStream = streamHandler.rest()
|
||||
|
||||
// Short circuit the two streams to create the relayed connection
|
||||
return pipe(
|
||||
sourceStream,
|
||||
destinationStream,
|
||||
sourceStream
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a HOP request to a relay peer, to request a connection to another
|
||||
* 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
|
||||
* @returns {Promise<Connection>}
|
||||
*/
|
||||
module.exports.hop = async function hop ({
|
||||
connection,
|
||||
request
|
||||
}) {
|
||||
// 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(request)
|
||||
|
||||
const response = await streamHandler.read()
|
||||
|
||||
if (response.code === CircuitPB.Status.SUCCESS) {
|
||||
log('hop request was successful')
|
||||
return streamHandler.rest()
|
||||
}
|
||||
|
||||
log('hop request failed with code %d, closing stream', response.code)
|
||||
streamHandler.close()
|
||||
throw errCode(new Error(`HOP request failed with code ${response.code}`), Errors.ERR_HOP_REQUEST_FAILED)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unencoded CAN_HOP response based on the Circuits configuration
|
||||
* @private
|
||||
*/
|
||||
module.exports.handleCanHop = function handleCanHop ({
|
||||
connection,
|
||||
streamHandler,
|
||||
circuit
|
||||
}) {
|
||||
const canHop = circuit._options.hop.enabled
|
||||
log('can hop (%s) request from %s', canHop, connection.remotePeer.toB58String())
|
||||
streamHandler.end({
|
||||
type: CircuitPB.Type.STATUS,
|
||||
code: canHop ? CircuitPB.Status.SUCCESS : CircuitPB.Status.HOP_CANT_SPEAK_RELAY
|
||||
})
|
||||
}
|
69
src/circuit/circuit/stop.js
Normal file
69
src/circuit/circuit/stop.js
Normal file
@ -0,0 +1,69 @@
|
||||
'use strict'
|
||||
|
||||
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')
|
||||
|
||||
/**
|
||||
* Handles incoming STOP requests
|
||||
*
|
||||
* @private
|
||||
* @param {*} options
|
||||
* @param {Connection} options.connection
|
||||
* @param {*} options.request The CircuitRelay protobuf request (unencoded)
|
||||
* @param {StreamHandler} options.streamHandler
|
||||
* @returns {Promise<*>} Resolves a duplex iterable
|
||||
*/
|
||||
module.exports.handleStop = function handleStop ({
|
||||
connection,
|
||||
request,
|
||||
streamHandler
|
||||
}) {
|
||||
// Validate the STOP request has the required input
|
||||
try {
|
||||
validateAddrs(request, streamHandler)
|
||||
} catch (err) {
|
||||
return log.error('invalid stop request via peer %s', connection.remotePeer.toB58String(), err)
|
||||
}
|
||||
|
||||
// The request is valid
|
||||
log('stop request is valid')
|
||||
streamHandler.write({
|
||||
type: CircuitPB.Type.STATUS,
|
||||
code: CircuitPB.Status.SUCCESS
|
||||
})
|
||||
return streamHandler.rest()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a STOP request
|
||||
* @private
|
||||
* @param {*} options
|
||||
* @param {Connection} options.connection
|
||||
* @param {*} options.request The CircuitRelay protobuf request (unencoded)
|
||||
* @returns {Promise<*>} Resolves a duplex iterable
|
||||
*/
|
||||
module.exports.stop = async function stop ({
|
||||
connection,
|
||||
request
|
||||
}) {
|
||||
const { stream } = await connection.newStream([multicodec.relay])
|
||||
log('starting stop request to %s', connection.remotePeer.toB58String())
|
||||
const streamHandler = new StreamHandler({ stream })
|
||||
|
||||
streamHandler.write(request)
|
||||
const response = await streamHandler.read()
|
||||
|
||||
if (response.code === CircuitPB.Status.SUCCESS) {
|
||||
log('stop request to %s was successful', connection.remotePeer.toB58String())
|
||||
return streamHandler.rest()
|
||||
}
|
||||
|
||||
log('stop request failed with code %d', response.code)
|
||||
streamHandler.close()
|
||||
}
|
80
src/circuit/circuit/stream-handler.js
Normal file
80
src/circuit/circuit/stream-handler.js
Normal file
@ -0,0 +1,80 @@
|
||||
'use strict'
|
||||
|
||||
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')
|
||||
|
||||
class StreamHandler {
|
||||
/**
|
||||
* Create a stream handler for connection
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {*} options.stream - A duplex iterable
|
||||
* @param {Number} options.maxLength - max bytes length of message
|
||||
*/
|
||||
constructor ({ stream, maxLength = 4096 }) {
|
||||
this.stream = stream
|
||||
|
||||
this.shake = handshake(this.stream)
|
||||
this.decoder = lp.decode.fromReader(this.shake.reader, { maxDataLength: maxLength })
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and decode message
|
||||
* @async
|
||||
* @returns {void}
|
||||
*/
|
||||
async read () {
|
||||
const msg = await this.decoder.next()
|
||||
if (msg.value) {
|
||||
const value = CircuitPB.decode(msg.value.slice())
|
||||
log('read message type', value.type)
|
||||
return value
|
||||
}
|
||||
|
||||
log('read received no value, closing stream')
|
||||
// End the stream, we didn't get data
|
||||
this.close()
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode and write array of buffers
|
||||
*
|
||||
* @param {*} msg An unencoded CircuitRelay protobuf message
|
||||
*/
|
||||
write (msg) {
|
||||
log('write message type %s', msg.type)
|
||||
this.shake.write(lp.encode.single(CircuitPB.encode(msg)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the handshake rest stream and invalidate handler
|
||||
*
|
||||
* @return {*} A duplex iterable
|
||||
*/
|
||||
rest () {
|
||||
this.shake.rest()
|
||||
return this.shake.stream
|
||||
}
|
||||
|
||||
end (msg) {
|
||||
this.write(msg)
|
||||
this.close()
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the stream
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
close () {
|
||||
log('closing the stream')
|
||||
this.rest().sink([])
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StreamHandler
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user