Compare commits

..

18 Commits

Author SHA1 Message Date
ddca720b2a chore: travis cache 2019-11-15 16:03:29 +01:00
1ca2d87287 chore: address review 2019-11-15 15:48:14 +01:00
340edf53e3 chore: address review 2019-11-15 15:15:12 +01:00
4cc9736485 chore: use topology interface 2019-11-14 19:13:45 +01:00
cdfd02306f chore: address review 2019-11-14 15:34:35 +01:00
dcd127b7a5 refactor: pubsub subsystem 2019-11-14 15:34:35 +01:00
751bc00f1b chore: address review 2019-11-14 15:33:12 +01:00
ec5319a50e chore: apply suggestions from code review
Co-Authored-By: Jacob Heun <jacobheun@gmail.com>
2019-11-14 15:33:12 +01:00
dca4727112 feat: peer-store v0 2019-11-14 15:33:12 +01:00
695d71e44f chore: update it-length-prefixed (#476)
fix: decode.fromReader usage
2019-11-13 16:38:07 +01:00
0fc6668321 refactor: async identify and identify push (#473)
* chore: add missing dep

* feat: import from identify push branch

https://github.com/libp2p/js-libp2p-identify/tree/feat/identify-push

* feat: add the connection to stream handlers

* refactor: identify to async/await

* chore: fix lint

* test: add identify tests

* refactor: add identify to the dialer flow

* feat: connect identify to the registrar

* fix: resolve review feedback

* fix: perform identify push when our protocols change
2019-11-07 12:11:50 +01:00
d7c38d3fec feat: registrar (#471)
* feat: peer-store v0

* feat: registrar

* chore: apply suggestions from code review

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

* chore: address review

* chore: support multiple conns

* chore: address review

* fix: no remote peer from topology on disconnect
2019-11-06 15:47:11 +01:00
53bcd2d745 feat: peer store (#470)
* feat: peer-store v0

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>
2019-11-06 15:11:13 +01:00
fba058370a refactor: crypto and pnet (#469)
* feat: add initial plaintext 2 module

* refactor: initial refactor of pnet

* chore: fix lint

* fix: update plaintext api usage

* test: use plaintext for test crypto

* chore: update deps

test: update dialer suite scope

* feat: add connection protection to the upgrader

* refactor: cleanup and lint fix

* chore: remove unncessary transforms

* chore: temporarily disable bundlesize

* chore: add missing dep

* fix: use it-handshake to prevent overreading

* chore(fix): PR feedback updates

* chore: apply suggestions from code review

Co-Authored-By: Vasco Santos <vasco.santos@moxy.studio>
2019-11-04 14:05:58 +01:00
a23d4d23cb refactor(async): add dialer and upgrader (#462)
* chore(deps): update connection and multistream

* feat: add basic dial support for addresses and peers

* test: automatically require all node test files

* fix: dont catch and log in the wrong place

* test: add direct spec test

fix: improve dial error consistency

* feat: add dial timeouts and concurrency

Queue timeouts will result in aborts of the dials

* chore: fix linting

* test: verify dialer defaults

* feat: add initial upgrader

* fix: add more test coverage and fix bugs

* feat: libp2p creates the upgrader

* feat: hook up handle to the upgrader

* feat: hook up the dialer to libp2p

test: add node dialer libp2p tests

* feat: add connection listeners to upgrader

* feat: emit connect and disconnect events

* chore: use libp2p-interfaces

* fix: address review feedback

* fix: correct import

* refactor: dedupe connection creation code
2019-10-21 16:53:58 +02:00
6ecc9b80c3 docs: add stream wrapping example (#466)
* docs: add duplex wrapping example

docs: add iterable types from @alanshaw's gist

* docs(fix): add feedback fix

Co-Authored-By: Vasco Santos <vasco.santos@moxy.studio>

* docs: clean up based on feedback
2019-10-21 13:36:05 +02:00
cd97abfcc3 refactor(async): update transports subsystem (#461)
* test: remove all tests for a clean slate

The refactor will require a large number of updates to the tests. In order
to ensure we have done a decent deduplication, and have a cleaner suite of tests
we've removed all tests. This will also allow us to more easily see tests
for the refactored systems.

We have a record of the latest test suites in master, so we are not losing any history.

* chore: update tcp and websockets
* chore: remove other transports until they are converted
* chore: use mafmt and multiaddr async versions
* chore: add and fix dependencies
* chore: clean up travis file
* feat: add new transport manager
* docs: add constructor jsdocs
* refactor(config): check that transports exist
This also removes the other logic, it can be added when those subsystems are refactored

* chore(deps): use async peer-id and peer-info
* feat: wire up the transport manager with libp2p
* chore: remove superstruct dep
2019-10-02 13:31:28 +02:00
c1da30bc74 Add streaming iterables guide (#459)
* docs: add streaming iterables guide placeholder

* chore: move peer discovery readme to doc fold:wqer

* docs: add link to async refactor issue
2019-09-30 18:20:25 +02:00
343 changed files with 10743 additions and 31642 deletions

View File

@ -1,41 +1,28 @@
'use strict' 'use strict'
const path = require('path')
const Libp2p = require('./src') const Libp2p = require('./src')
const { MULTIADDRS_WEBSOCKETS } = require('./test/fixtures/browser') const { MULTIADDRS_WEBSOCKETS } = require('./test/fixtures/browser')
const Peers = require('./test/fixtures/peers') const Peers = require('./test/fixtures/peers')
const PeerId = require('peer-id') const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const WebSockets = require('libp2p-websockets') const WebSockets = require('libp2p-websockets')
const Muxer = require('libp2p-mplex') const Muxer = require('libp2p-mplex')
const { NOISE: Crypto } = require('@chainsafe/libp2p-noise') const Crypto = require('./src/insecure/plaintext')
const pipe = require('it-pipe') const pipe = require('it-pipe')
let libp2p let libp2p
const before = async () => { const before = async () => {
// Use the last peer // Use the last peer
const peerId = await PeerId.createFromJSON(Peers[Peers.length - 1]) const peerId = await PeerId.createFromJSON(Peers[Peers.length - 1])
const peerInfo = new PeerInfo(peerId)
peerInfo.multiaddrs.add(MULTIADDRS_WEBSOCKETS[0])
libp2p = new Libp2p({ libp2p = new Libp2p({
addresses: { peerInfo,
listen: [MULTIADDRS_WEBSOCKETS[0]]
},
peerId,
modules: { modules: {
transport: [WebSockets], transport: [WebSockets],
streamMuxer: [Muxer], streamMuxer: [Muxer],
connEncryption: [Crypto] connEncryption: [Crypto]
},
config: {
relay: {
enabled: true,
hop: {
enabled: true,
active: false
}
},
nat: {
enabled: false
}
} }
}) })
// Add the echo protocol // Add the echo protocol
@ -48,23 +35,10 @@ const after = async () => {
await libp2p.stop() await libp2p.stop()
} }
/** @type {import('aegir').Options["build"]["config"]} */
const esbuild = {
inject: [path.join(__dirname, './scripts/node-globals.js')]
}
/** @type {import('aegir').PartialOptions} */
module.exports = { module.exports = {
build: { bundlesize: { maxSize: '220kB' },
bundlesizeMax: '253kB' hooks: {
}, pre: before,
test: { post: after
before,
after,
browser: {
config: {
buildConfig: esbuild
}
}
} }
} }

View File

@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: libp2p Official Forum
url: https://discuss.libp2p.io
about: For general questions, support requests and discussions

View File

@ -1,55 +0,0 @@
---
name: Open an issue
about: For reporting bugs or errors in the JavaScript libp2p implementation
title: ''
labels: need/triage
assignees: ''
---
<!--
Thank you for reporting an issue.
This issue tracker is for bugs found within the JavaScript implementation of libp2p.
If you are asking a question about how to use libp2p, please ask on https://discuss.libp2p.io
Otherwise please fill in as much of the template below as possible.
-->
- **Version**:
<!--
Check package.json version
-->
- **Platform**:
<!--
Output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows). If using in a Browser, please share the browser version as well
-->
- **Subsystem**:
<!--
If known, please specify affected core module name (e.g Dialer, Pubsub, Relay etc)
-->
#### Severity:
<!--
One of following:
Critical - System crash, application panic.
High - The main functionality of the application does not work, API breakage, repo format breakage, etc.
Medium - A non-essential functionality does not work, performance issues, etc.
Low - An optional functionality does not work.
Very Low - Translation or documentation mistake. Something that won't give anyone a bad day.
-->
#### Description:
<!--
- What you did
- What happened
- What you expected to happen
-->
#### Steps to reproduce the error:
<!--
If possible, please provide code that demonstrates the problem, keeping it as simple and free of external dependencies as you are able
-->

View File

@ -1,8 +0,0 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
time: "11:00"
open-pull-requests-limit: 10

View File

@ -1,141 +0,0 @@
name: examples
on:
push:
branches:
- master
pull_request:
branches:
- '**'
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: npx aegir lint
- run: npx aegir ts -p check
- run: npx aegir build
test-auto-relay-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- auto-relay
test-chat-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- chat
test-connection-encryption-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- connection-encryption
test-discovery-mechanisms-example:
needs: check
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- discovery-mechanisms
test-echo-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- echo
test-libp2p-in-the-browser-example:
needs: check
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- libp2p-in-the-browser
test-peer-and-content-routing-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- peer-and-content-routing
test-pnet-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- pnet
test-protocol-and-stream-muxing-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- protocol-and-stream-muxing
test-pubsub-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- pubsub
test-transports-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: cd examples && npm i && npm run test -- transports
test-webrtc-direct-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install -g @mapbox/node-pre-gyp && npm install
- run: cd examples && npm i && npm run test -- webrtc-direct

View File

@ -1,81 +0,0 @@
name: ci
on:
push:
branches:
- master
pull_request:
branches:
- '**'
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: npx aegir lint
- run: npx aegir build
- run: npx aegir dep-check
- uses: ipfs/aegir/actions/bundle-size@v32.1.0
name: size
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
test-node:
needs: check
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
node: [16]
fail-fast: true
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
- run: npm install
- run: npx aegir test -t node --cov --bail
- uses: codecov/codecov-action@v1
test-chrome:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: lts/*
- run: npm install
- run: npx aegir test -t browser -t webworker --bail
test-firefox:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: lts/*
- run: npm install
- run: npx aegir test -t browser -t webworker --bail -- --browser firefox
test-ts:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: lts/*
- run: npm install
- run: npm run test:ts
test-interop:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: lts/*
- run: npm install
- run: npm run test:interop -- --bail -- --exit

2
.gitignore vendored
View File

@ -3,8 +3,6 @@ docs
**/*.log **/*.log
test/repo-tests* test/repo-tests*
**/bundle.js **/bundle.js
.cache
.parcel-cache
# Logs # Logs
logs logs

42
.travis.yml Normal file
View File

@ -0,0 +1,42 @@
language: node_js
cache: npm
stages:
- check
- test
- cov
node_js:
- '10'
- '12'
os:
- linux
- osx
script: npx nyc -s npm run test:node -- --bail
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
jobs:
include:
- stage: check
script:
# - npx aegir build --bundlesize
- npx aegir dep-check -- -i wrtc -i electron-webrtc
- npm run lint
- stage: test
name: chrome
addons:
chrome: stable
script:
- npx aegir test -t browser -t webworker
- stage: test
name: firefox
addons:
firefox: latest
script:
- npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless
notifications:
email: false

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,7 @@ One of following:
<!-- <!--
This is for you! Please read, and then delete this text before posting it. This is for you! Please read, and then delete this text before posting it.
The js-libp2p issues are only for bug reports and directly actionable features. The js-ipfs issues are only for bug reports and directly actionable features.
Read https://github.com/ipfs/community/blob/master/CONTRIBUTING.md#reporting-issues if your issue doesn't fit either of those categories. Read https://github.com/ipfs/community/blob/master/CONTRIBUTING.md#reporting-issues if your issue doesn't fit either of those categories.
--> -->

View File

@ -1,45 +0,0 @@
<!--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
```

562
README.md
View File

@ -5,26 +5,24 @@
<h3 align="center">The JavaScript implementation of the libp2p Networking Stack.</h3> <h3 align="center">The JavaScript implementation of the libp2p Networking Stack.</h3>
<p align="center"> <p align="center">
<a href="http://protocol.ai"><img src="https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square" /></a> <a href="http://ipn.io"><img src="https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square" /></a>
<a href="http://libp2p.io/"><img src="https://img.shields.io/badge/project-libp2p-yellow.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="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://riot.permaweb.io/#/room/#libp2p:permaweb.io"><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://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://discuss.libp2p.io"><img src="https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg" /></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>
<p align="center"> <p align="center">
<a href="https://github.com/libp2p/js-libp2p/actions?query=branch%3Amaster+workflow%3Aci+"><img src="https://img.shields.io/github/workflow/status/libp2p/js-libp2p/ci?label=ci&style=flat-square" /></a> <a href="https://travis-ci.com/libp2p/js-libp2p"><img src="https://flat.badgen.net/travis/libp2p/js-libp2p" /></a>
<a href="https://codecov.io/gh/libp2p/js-libp2p"><img src="https://img.shields.io/codecov/c/github/libp2p/js-libp2p/master.svg?style=flat-square"></a> <a href="https://codecov.io/gh/libp2p/js-libp2p"><img src="https://img.shields.io/codecov/c/github/ipfs/js-ipfs-multipart/master.svg?style=flat-square"></a>
<a href="https://bundlephobia.com/result?p=ipfsd-ctl"><img src="https://flat.badgen.net/bundlephobia/minzip/ipfsd-ctl"></a> <a href="https://bundlephobia.com/result?p=ipfsd-ctl"><img src="https://flat.badgen.net/bundlephobia/minzip/ipfsd-ctl"></a>
<br> <br>
<a href="https://david-dm.org/libp2p/js-libp2p"><img src="https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square" /></a> <a href="https://david-dm.org/libp2p/js-libp2p"><img src="https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square" /></a>
<a href="https://github.com/feross/standard"><img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square"></a> <a href="https://github.com/feross/standard"><img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square"></a>
<a href="https://github.com/RichardLitt/standard-readme"><img src="https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square" /></a> <a href="https://github.com/RichardLitt/standard-readme"><img src="https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square" /></a>
<a href=""><img src="https://img.shields.io/badge/npm-%3E%3D6.0.0-orange.svg?style=flat-square" /></a> <a href=""><img src="https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square" /></a>
<a href=""><img src="https://img.shields.io/badge/Node.js-%3E%3D12.0.0-orange.svg?style=flat-square" /></a> <a href=""><img src="https://img.shields.io/badge/Node.js-%3E%3D6.0.0-orange.svg?style=flat-square" /></a>
<br> <br>
</p> </p>
@ -32,14 +30,13 @@
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 🐉.. 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 🐉..
The documentation in the master branch may contain changes from a pre-release. **Want to get started?** Check our [examples folder](/examples).
If you are looking for the documentation of the latest release, you can view the latest release on [**npm**](https://www.npmjs.com/package/libp2p), or select the tag in github that matches the version you are looking for.
**Want to get started?** Check our [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
[**`Weekly Core Dev Calls`**](https://github.com/libp2p/team-mgmt/issues/16) [David Dias](https://github.com/diasdavid/)
## Lead Maintainer ## Lead Maintainer
@ -48,12 +45,12 @@ If you are looking for the documentation of the latest release, you can view the
## Table of Contents ## Table of Contents
- [Background](#background) - [Background](#background)
- [Install](#install) - [Bundles](#bundles)
- [Usage](#usage) - [Usage](#usage)
- [Configuration](#configuration) - [Install](#install)
- [Usage](#usage)
- [API](#api) - [API](#api)
- [Getting Started](#getting-started) - [Events](#events)
- [Tutorials and Examples](#tutorials-and-examples)
- [Development](#development) - [Development](#development)
- [Tests](#tests) - [Tests](#tests)
- [Packages](#packages) - [Packages](#packages)
@ -62,7 +59,7 @@ If you are looking for the documentation of the latest release, you can view the
## Background ## 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 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. libp2p is the product of a long and arduous quest to understand the evolution of the Internet networking stack. In order to build P2P applications, devs have long had to made custom ad-hoc solutions to fit their needs, sometimes making some hard assumptions about their runtimes and the state of the network at the time of their development. Today, looking back more than 20 years, we see a clear pattern in the types of mechanisms built around the Internet Protocol, IP, which can be found throughout many layers of the OSI layer system, libp2p distils these mechanisms into flat categories and defines clear interfaces that once exposed, enable other protocols and applications to use and swap them, enabling upgradability and adaptability for the runtime, without breaking the API.
We are in the process of writing better documentation, blog posts, tutorials and a formal specification. Today you can find: We are in the process of writing better documentation, blog posts, tutorials and a formal specification. Today you can find:
@ -77,37 +74,464 @@ 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. 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 ## 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 ```sh
npm install libp2p npm install --save libp2p
``` ```
## Usage ## Usage
### Configuration **IMPORTANT NOTE**: We are currently on the way of migrating all our `libp2p` modules to use `async await` and `async iterators`, instead of callbacks and `pull-streams`. As a consequence, when you start a new libp2p project, we must check which versions of the modules you should use. For now, it is required to use the modules using callbacks with `libp2p`, while we are working on getting the remaining modules ready for a full migration. For more details, you can have a look at [libp2p/js-libp2p#266](https://github.com/libp2p/js-libp2p/issues/266).
For all the information on how you can configure libp2p see [CONFIGURATION.md](./doc/CONFIGURATION.md). ### [Tutorials and Examples](/examples)
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 GossipSub = require('libp2p-gossipsub')
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
pubsub: GossipSub
},
// 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
}
},
pubsub: {
enabled: true,
emitSelf: true, // whether the node should emit to self on publish, in the event of the topic being subscribed
signMessages: true, // if messages should be signed
strictSigning: true // if message signing should be required
}
}
}
// overload any defaults of your bundle using https://github.com/nodeutils/defaults-deep
super(defaultsDeep(_options, defaults))
}
}
// Now all the nodes you create, will have TCP, WebSockets, SPDY, MPLEX, SECIO and MulticastDNS support.
```
### API ### API
The specification is available on [API.md](./doc/API.md). **IMPORTANT NOTE**: All the methods listed in the API section that take a callback are also now Promisified. Libp2p is migrating away from callbacks to async/await, and in a future release (that will be announced in advance), callback support will be removed entirely. You can follow progress of the async/await endeavor at https://github.com/ipfs/js-ipfs/issues/1670.
### Getting started #### Create a Node - `Libp2p.create(options)`
If you are starting your journey with `js-libp2p`, read the [GETTING_STARTED.md](./doc/GETTING_STARTED.md) guide. > Behaves exactly like `new Libp2p(options)`, but doesn't require a PeerInfo. One will be generated instead
### Tutorials and Examples ```js
const { create } = require('libp2p')
const libp2p = await create(options)
You can find multiple examples on the [examples folder](./examples) that will guide you through using libp2p for several scenarios. await libp2p.start()
```
- `options`: Object of libp2p configuration options
#### 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](./doc/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)
## Development ## Development
**Clone and install dependencies:** **Clone and install dependencies:**
```sh ```sh
> git clone https://github.com/libp2p/js-libp2p.git > git clone https://github.com/ipfs/js-ipfs.git
> cd js-libp2p > cd js-ipfs
> npm install > npm install
``` ```
@ -134,44 +558,50 @@ List of packages currently in existence for libp2p
| Package | Version | Deps | CI | Coverage | Lead Maintainer | | Package | Version | Deps | CI | Coverage | Lead Maintainer |
| ---------|---------|---------|---------|---------|--------- | | ---------|---------|---------|---------|---------|--------- |
| **libp2p** | | **Libp2p** |
| [`libp2p`](//github.com/libp2p/js-libp2p) | [![npm](https://img.shields.io/npm/v/libp2p.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p/master)](https://travis-ci.com/libp2p/js-libp2p) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) | [Jacob Heun](mailto:jacobheun@gmail.com) | | [`interface-libp2p`](//github.com/libp2p/interface-libp2p) | [![npm](https://img.shields.io/npm/v/interface-libp2p.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-libp2p/releases) | [![Deps](https://david-dm.org/libp2p/interface-libp2p.svg?style=flat-square)](https://david-dm.org/libp2p/interface-libp2p) | [![Travis CI](https://travis-ci.com/libp2p/interface-libp2p.svg?branch=master)](https://travis-ci.com/libp2p/interface-libp2p) | [![codecov](https://codecov.io/gh/libp2p/interface-libp2p/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-libp2p) | N/A |
| [`libp2p-daemon`](//github.com/libp2p/js-libp2p-daemon) | [![npm](https://img.shields.io/npm/v/libp2p-daemon.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-daemon/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-daemon.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-daemon) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-daemon/master)](https://travis-ci.com/libp2p/js-libp2p-daemon) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-daemon/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-daemon) | [Jacob Heun](mailto:jacobheun@gmail.com) | | [`libp2p`](//github.com/libp2p/js-libp2p) | [![npm](https://img.shields.io/npm/v/libp2p.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-daemon-client`](//github.com/libp2p/js-libp2p-daemon-client) | [![npm](https://img.shields.io/npm/v/libp2p-daemon-client.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-daemon-client/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-daemon-client.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-daemon-client) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-daemon-client/master)](https://travis-ci.com/libp2p/js-libp2p-daemon-client) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-daemon-client/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-daemon-client) | [Vasco Santos](mailto:santos.vasco10@gmail.com) | | **Connection** |
| [`libp2p-interfaces`](//github.com/libp2p/js-interfaces) | [![npm](https://img.shields.io/npm/v/libp2p-interfaces.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-interfaces/releases) | [![Deps](https://david-dm.org/libp2p/js-interfaces.svg?style=flat-square)](https://david-dm.org/libp2p/js-interfaces) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-interfaces/master)](https://travis-ci.com/libp2p/js-interfaces) | [![codecov](https://codecov.io/gh/libp2p/js-interfaces/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-interfaces) | [Jacob Heun](mailto:jacobheun@gmail.com) | | [`interface-connection`](//github.com/libp2p/interface-connection) | [![npm](https://img.shields.io/npm/v/interface-connection.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-connection/releases) | [![Deps](https://david-dm.org/libp2p/interface-connection.svg?style=flat-square)](https://david-dm.org/libp2p/interface-connection) | [![Travis CI](https://travis-ci.com/libp2p/interface-connection.svg?branch=master)](https://travis-ci.com/libp2p/interface-connection) | [![codecov](https://codecov.io/gh/libp2p/interface-connection/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-connection) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`interop-libp2p`](//github.com/libp2p/interop) | [![npm](https://img.shields.io/npm/v/interop-libp2p.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interop/releases) | [![Deps](https://david-dm.org/libp2p/interop.svg?style=flat-square)](https://david-dm.org/libp2p/interop) | [![Travis CI](https://flat.badgen.net/travis/libp2p/interop/master)](https://travis-ci.com/libp2p/interop) | [![codecov](https://codecov.io/gh/libp2p/interop/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/interop) | [Vasco Santos](mailto:santos.vasco10@gmail.com) | | **Transport** |
| **transports** | | [`interface-transport`](//github.com/libp2p/interface-transport) | [![npm](https://img.shields.io/npm/v/interface-transport.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-transport/releases) | [![Deps](https://david-dm.org/libp2p/interface-transport.svg?style=flat-square)](https://david-dm.org/libp2p/interface-transport) | [![Travis CI](https://travis-ci.com/libp2p/interface-transport.svg?branch=master)](https://travis-ci.com/libp2p/interface-transport) | [![codecov](https://codecov.io/gh/libp2p/interface-transport/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-transport) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-tcp`](//github.com/libp2p/js-libp2p-tcp) | [![npm](https://img.shields.io/npm/v/libp2p-tcp.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-tcp/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-tcp) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-tcp/master)](https://travis-ci.com/libp2p/js-libp2p-tcp) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-tcp/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-tcp) | [Jacob Heun](mailto:jacobheun@gmail.com) | | [`libp2p-tcp`](//github.com/libp2p/js-libp2p-tcp) | [![npm](https://img.shields.io/npm/v/libp2p-tcp.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-tcp/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-tcp) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-tcp.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-tcp) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-tcp/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-tcp) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-webrtc-direct`](//github.com/libp2p/js-libp2p-webrtc-direct) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-direct.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-direct/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-direct.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-direct) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-webrtc-direct/master)](https://travis-ci.com/libp2p/js-libp2p-webrtc-direct) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct) | [Vasco Santos](mailto:vasco.santos@moxy.studio) | | [`libp2p-utp`](//github.com/libp2p/js-libp2p-utp) | [![npm](https://img.shields.io/npm/v/libp2p-utp.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-utp/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-utp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-utp) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-utp.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-utp) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-utp/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-utp) | N/A |
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-webrtc-star/master)](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) | | [`libp2p-webrtc-direct`](//github.com/libp2p/js-libp2p-webrtc-direct) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-direct.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-direct/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-direct.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-direct) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-webrtc-direct.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-webrtc-direct) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-direct) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-websockets`](//github.com/libp2p/js-libp2p-websockets) | [![npm](https://img.shields.io/npm/v/libp2p-websockets.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websockets/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websockets.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websockets) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-websockets/master)](https://travis-ci.com/libp2p/js-libp2p-websockets) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websockets/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-websockets) | [Jacob Heun](mailto:jacobheun@gmail.com) | | [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-webrtc-star.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| **secure channels** | | [`libp2p-websockets`](//github.com/libp2p/js-libp2p-websockets) | [![npm](https://img.shields.io/npm/v/libp2p-websockets.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websockets/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websockets.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websockets) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-websockets.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-websockets) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websockets/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websockets) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-noise`](//github.com/NodeFactoryIo/js-libp2p-noise) | [![npm](https://img.shields.io/npm/v/libp2p-noise.svg?maxAge=86400&style=flat-square)](//github.com/NodeFactoryIo/js-libp2p-noise/releases) | [![Deps](https://david-dm.org/NodeFactoryIo/js-libp2p-noise.svg?style=flat-square)](https://david-dm.org/NodeFactoryIo/js-libp2p-noise) | [![Travis CI](https://flat.badgen.net/travis/NodeFactoryIo/js-libp2p-noise/master)](https://travis-ci.com/NodeFactoryIo/js-libp2p-noise) | [![codecov](https://codecov.io/gh/NodeFactoryIo/js-libp2p-noise/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/NodeFactoryIo/js-libp2p-noise) | N/A | | [`libp2p-websocket-star`](//github.com/libp2p/js-libp2p-websocket-star) | [![npm](https://img.shields.io/npm/v/libp2p-websocket-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websocket-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websocket-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-websocket-star.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-websocket-star) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websocket-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websocket-star) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| **stream multiplexers** | | **Crypto Channels** |
| [`libp2p-mplex`](//github.com/libp2p/js-libp2p-mplex) | [![npm](https://img.shields.io/npm/v/libp2p-mplex.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-mplex/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-mplex.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mplex) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-mplex/master)](https://travis-ci.com/libp2p/js-libp2p-mplex) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-mplex/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-mplex) | [Vasco Santos](mailto:vasco.santos@moxy.studio) | | [`libp2p-secio`](//github.com/libp2p/js-libp2p-secio) | [![npm](https://img.shields.io/npm/v/libp2p-secio.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-secio/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-secio.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-secio) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-secio.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-secio) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-secio/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-secio) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
| **peer discovery** | | **Stream Muxers** |
| [`libp2p-bootstrap`](//github.com/libp2p/js-libp2p-bootstrap) | [![npm](https://img.shields.io/npm/v/libp2p-bootstrap.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-bootstrap/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-bootstrap.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-bootstrap) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-bootstrap/master)](https://travis-ci.com/libp2p/js-libp2p-bootstrap) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-bootstrap/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-bootstrap) | [Vasco Santos](mailto:vasco.santos@moxy.studio) | | [`interface-stream-muxer`](//github.com/libp2p/interface-stream-muxer) | [![npm](https://img.shields.io/npm/v/interface-stream-muxer.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-stream-muxer/releases) | [![Deps](https://david-dm.org/libp2p/interface-stream-muxer.svg?style=flat-square)](https://david-dm.org/libp2p/interface-stream-muxer) | [![Travis CI](https://travis-ci.com/libp2p/interface-stream-muxer.svg?branch=master)](https://travis-ci.com/libp2p/interface-stream-muxer) | [![codecov](https://codecov.io/gh/libp2p/interface-stream-muxer/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-stream-muxer) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-kad-dht/master)](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) | | [`libp2p-mplex`](//github.com/libp2p/js-libp2p-mplex) | [![npm](https://img.shields.io/npm/v/libp2p-mplex.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-mplex/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-mplex.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mplex) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-mplex.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-mplex) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-mplex/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-mplex) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-mdns`](//github.com/libp2p/js-libp2p-mdns) | [![npm](https://img.shields.io/npm/v/libp2p-mdns.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-mdns/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-mdns.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mdns) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-mdns/master)](https://travis-ci.com/libp2p/js-libp2p-mdns) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-mdns/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-mdns) | [Jacob Heun](mailto:jacobheun@gmail.com) | | [`libp2p-spdy`](//github.com/libp2p/js-libp2p-spdy) | [![npm](https://img.shields.io/npm/v/libp2p-spdy.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-spdy/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-spdy.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-spdy) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-spdy.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-spdy) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-spdy/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-spdy) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-webrtc-star/master)](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) | | **Discovery** |
| [`@chainsafe/discv5`](//github.com/ChainSafe/discv5) | [![npm](https://img.shields.io/npm/v/@chainsafe/discv5.svg?maxAge=86400&style=flat-square)](//github.com/ChainSafe/discv5/releases) | [![Deps](https://david-dm.org/ChainSafe/discv5.svg?style=flat-square)](https://david-dm.org/ChainSafe/discv5) | [![Travis CI](https://flat.badgen.net/travis/ChainSafe/discv5/master)](https://travis-ci.com/ChainSafe/discv5) | [![codecov](https://codecov.io/gh/ChainSafe/discv5/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ChainSafe/discv5) | [Cayman Nava](mailto:caymannava@gmail.com) | | [`interface-peer-discovery`](//github.com/libp2p/interface-peer-discovery) | [![npm](https://img.shields.io/npm/v/interface-peer-discovery.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-peer-discovery/releases) | [![Deps](https://david-dm.org/libp2p/interface-peer-discovery.svg?style=flat-square)](https://david-dm.org/libp2p/interface-peer-discovery) | [![Travis CI](https://travis-ci.com/libp2p/interface-peer-discovery.svg?branch=master)](https://travis-ci.com/libp2p/interface-peer-discovery) | [![codecov](https://codecov.io/gh/libp2p/interface-peer-discovery/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-peer-discovery) | N/A |
| **content routing** | | [`libp2p-bootstrap`](//github.com/libp2p/js-libp2p-bootstrap) | [![npm](https://img.shields.io/npm/v/libp2p-bootstrap.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-bootstrap/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-bootstrap.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-bootstrap) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-bootstrap.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-bootstrap) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-bootstrap/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-bootstrap) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-delegated-content-routing`](//github.com/libp2p/js-libp2p-delegated-content-routing) | [![npm](https://img.shields.io/npm/v/libp2p-delegated-content-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-delegated-content-routing/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-delegated-content-routing/master)](https://travis-ci.com/libp2p/js-libp2p-delegated-content-routing) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-delegated-content-routing/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-delegated-content-routing) | [Jacob Heun](mailto:jacobheun@gmail.com) | | [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-kad-dht.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-kad-dht/master)](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) | | [`libp2p-mdns`](//github.com/libp2p/js-libp2p-mdns) | [![npm](https://img.shields.io/npm/v/libp2p-mdns.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-mdns/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-mdns.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-mdns) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-mdns.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-mdns) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-mdns/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-mdns) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| **peer routing** | | [`libp2p-rendezvous`](//github.com/libp2p/js-libp2p-rendezvous) | [![npm](https://img.shields.io/npm/v/libp2p-rendezvous.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-rendezvous/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-rendezvous.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-rendezvous) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-rendezvous.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-rendezvous) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-rendezvous/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-rendezvous) | N/A |
| [`libp2p-delegated-peer-routing`](//github.com/libp2p/js-libp2p-delegated-peer-routing) | [![npm](https://img.shields.io/npm/v/libp2p-delegated-peer-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-delegated-peer-routing/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-delegated-peer-routing/master)](https://travis-ci.com/libp2p/js-libp2p-delegated-peer-routing) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing) | [Jacob Heun](mailto:jacobheun@gmail.com) | | [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [![npm](https://img.shields.io/npm/v/libp2p-webrtc-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-webrtc-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-webrtc-star.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-kad-dht/master)](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) | | [`libp2p-websocket-star`](//github.com/libp2p/js-libp2p-websocket-star) | [![npm](https://img.shields.io/npm/v/libp2p-websocket-star.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-websocket-star/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-websocket-star.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-websocket-star) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-websocket-star.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-websocket-star) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-websocket-star/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-websocket-star) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| **utilities** | | **Content Routing** |
| [`libp2p-crypto`](//github.com/libp2p/js-libp2p-crypto) | [![npm](https://img.shields.io/npm/v/libp2p-crypto.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-crypto/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-crypto.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-crypto/master)](https://travis-ci.com/libp2p/js-libp2p-crypto) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-crypto/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-crypto) | [Jacob Heun](mailto:jacobheun@gmail.com) | | [`interface-content-routing`](//github.com/libp2p/interface-content-routing) | [![npm](https://img.shields.io/npm/v/interface-content-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-content-routing/releases) | [![Deps](https://david-dm.org/libp2p/interface-content-routing.svg?style=flat-square)](https://david-dm.org/libp2p/interface-content-routing) | [![Travis CI](https://travis-ci.com/libp2p/interface-content-routing.svg?branch=master)](https://travis-ci.com/libp2p/interface-content-routing) | [![codecov](https://codecov.io/gh/libp2p/interface-content-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-content-routing) | N/A |
| **data types** | | [`libp2p-delegated-content-routing`](//github.com/libp2p/js-libp2p-delegated-content-routing) | [![npm](https://img.shields.io/npm/v/libp2p-delegated-content-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-delegated-content-routing/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-delegated-content-routing.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-delegated-content-routing) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-delegated-content-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-delegated-content-routing) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`peer-id`](//github.com/libp2p/js-peer-id) | [![npm](https://img.shields.io/npm/v/peer-id.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-id/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-id.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-id) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-peer-id/master)](https://travis-ci.com/libp2p/js-peer-id) | [![codecov](https://codecov.io/gh/libp2p/js-peer-id/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-peer-id) | [Vasco Santos](mailto:santos.vasco10@gmail.com) | | [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-kad-dht.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-record`](//github.com/libp2p/js-libp2p-record) | [![npm](https://img.shields.io/npm/v/libp2p-record.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-record/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-record.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-record) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-record/master)](https://travis-ci.com/libp2p/js-libp2p-record) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-record/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-record) | [Jacob Heun](mailto:santos.vasco10@gmail.com) | | **Peer Routing** |
| **pubsub** | | [`interface-peer-routing`](//github.com/libp2p/interface-peer-routing) | [![npm](https://img.shields.io/npm/v/interface-peer-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/interface-peer-routing/releases) | [![Deps](https://david-dm.org/libp2p/interface-peer-routing.svg?style=flat-square)](https://david-dm.org/libp2p/interface-peer-routing) | [![Travis CI](https://travis-ci.com/libp2p/interface-peer-routing.svg?branch=master)](https://travis-ci.com/libp2p/interface-peer-routing) | [![codecov](https://codecov.io/gh/libp2p/interface-peer-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/interface-peer-routing) | N/A |
| [`libp2p-floodsub`](//github.com/libp2p/js-libp2p-floodsub) | [![npm](https://img.shields.io/npm/v/libp2p-floodsub.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-floodsub/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-floodsub.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-floodsub) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-floodsub/master)](https://travis-ci.com/libp2p/js-libp2p-floodsub) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-floodsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-floodsub) | [Vasco Santos](mailto:vasco.santos@moxy.studio) | | [`libp2p-delegated-peer-routing`](//github.com/libp2p/js-libp2p-delegated-peer-routing) | [![npm](https://img.shields.io/npm/v/libp2p-delegated-peer-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-delegated-peer-routing/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-delegated-peer-routing.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-delegated-peer-routing) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-gossipsub`](//github.com/ChainSafe/js-libp2p-gossipsub) | [![npm](https://img.shields.io/npm/v/libp2p-gossipsub.svg?maxAge=86400&style=flat-square)](//github.com/ChainSafe/js-libp2p-gossipsub/releases) | [![Deps](https://david-dm.org/ChainSafe/js-libp2p-gossipsub.svg?style=flat-square)](https://david-dm.org/ChainSafe/js-libp2p-gossipsub) | [![Travis CI](https://flat.badgen.net/travis/ChainSafe/js-libp2p-gossipsub/master)](https://travis-ci.com/ChainSafe/js-libp2p-gossipsub) | [![codecov](https://codecov.io/gh/ChainSafe/js-libp2p-gossipsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ChainSafe/js-libp2p-gossipsub) | [Cayman Nava](mailto:caymannava@gmail.com) | | [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-kad-dht.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| **extensions** | | **Utilities** |
| [`libp2p-nat-mgnr`](//github.com/libp2p/js-libp2p-nat-mgnr) | [![npm](https://img.shields.io/npm/v/libp2p-nat-mgnr.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-nat-mgnr/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-nat-mgnr.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-nat-mgnr) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-nat-mgnr/master)](https://travis-ci.com/libp2p/js-libp2p-nat-mgnr) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-nat-mgnr/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-nat-mgnr) | N/A | | [`libp2p-crypto`](//github.com/libp2p/js-libp2p-crypto) | [![npm](https://img.shields.io/npm/v/libp2p-crypto.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-crypto/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-crypto.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-crypto.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-crypto) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-crypto/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-crypto) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
| [`libp2p-utils`](//github.com/libp2p/js-libp2p-utils) | [![npm](https://img.shields.io/npm/v/libp2p-utils.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-utils/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-utils.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-utils) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-utils/master)](https://travis-ci.com/libp2p/js-libp2p-utils) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-utils/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-utils) | [Vasco Santos](mailto:santos.vasco10@gmail.com) | | [`libp2p-crypto-secp256k1`](//github.com/libp2p/js-libp2p-crypto-secp256k1) | [![npm](https://img.shields.io/npm/v/libp2p-crypto-secp256k1.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-crypto-secp256k1/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-crypto-secp256k1.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-crypto-secp256k1) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
| **Data Types** |
| [`peer-book`](//github.com/libp2p/js-peer-book) | [![npm](https://img.shields.io/npm/v/peer-book.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-book/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-book.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-book) | [![Travis CI](https://travis-ci.com/libp2p/js-peer-book.svg?branch=master)](https://travis-ci.com/libp2p/js-peer-book) | [![codecov](https://codecov.io/gh/libp2p/js-peer-book/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-peer-book) | [Pedro Teixeira](mailto:i@pgte.me) |
| [`peer-id`](//github.com/libp2p/js-peer-id) | [![npm](https://img.shields.io/npm/v/peer-id.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-id/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-id.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-id) | [![Travis CI](https://travis-ci.com/libp2p/js-peer-id.svg?branch=master)](https://travis-ci.com/libp2p/js-peer-id) | [![codecov](https://codecov.io/gh/libp2p/js-peer-id/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-peer-id) | [Pedro Teixeira](mailto:i@pgte.me) |
| [`peer-info`](//github.com/libp2p/js-peer-info) | [![npm](https://img.shields.io/npm/v/peer-info.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-info/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-info.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-info) | [![Travis CI](https://travis-ci.com/libp2p/js-peer-info.svg?branch=master)](https://travis-ci.com/libp2p/js-peer-info) | [![codecov](https://codecov.io/gh/libp2p/js-peer-info/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-peer-info) | [Pedro Teixeira](mailto:i@pgte.me) |
| **Extensions** |
| [`libp2p-floodsub`](//github.com/libp2p/js-libp2p-floodsub) | [![npm](https://img.shields.io/npm/v/libp2p-floodsub.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-floodsub/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-floodsub.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-floodsub) | [![Travis CI](https://travis-ci.com/libp2p/js-libp2p-floodsub.svg?branch=master)](https://travis-ci.com/libp2p/js-libp2p-floodsub) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-floodsub/branch/master/graph/badge.svg)](https://codecov.io/gh/libp2p/js-libp2p-floodsub) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
## Contribute ## Contribute

View File

@ -25,7 +25,7 @@
- [ ] [js-ipfs](https://github.com/ipfs/js-ipfs) - [ ] [js-ipfs](https://github.com/ipfs/js-ipfs)
- Documentation - Documentation
- [ ] Ensure that README.md is up to date - [ ] Ensure that README.md is up to date
- [ ] Ensure that [libp2p/docs](https://github.com/libp2p/docs) is updated - [ ] Ensure that all the examples run
- Communication - Communication
- [ ] Create the release issue - [ ] 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) - [ ] 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 +50,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) - 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 - 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 - 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.libp2p.io/ and help users finding their answers. - Join the discussion at http://discuss.ipfs.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! - Join the [⚡️ⒿⓈ Core Dev Team Weekly Sync 🙌🏽 ](https://github.com/ipfs/team-mgmt/issues/650) and be part of the Sprint action!
# ⁉️ Do you have questions? # ⁉️ Do you have questions?

2092
doc/API.md

File diff suppressed because it is too large Load Diff

View File

@ -1,814 +0,0 @@
#
- [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 Auto Relay](#setup-with-auto-relay)
- [Setup with Keychain](#setup-with-keychain)
- [Configuring Dialing](#configuring-dialing)
- [Configuring Connection Manager](#configuring-connection-manager)
- [Configuring Transport Manager](#configuring-transport-manager)
- [Configuring Metrics](#configuring-metrics)
- [Configuring PeerStore](#configuring-peerstore)
- [Customizing Transports](#customizing-transports)
- [Configuring the NAT Manager](#configuring-the-nat-manager)
- [Browser support](#browser-support)
- [UPnP and NAT-PMP](#upnp-and-nat-pmp)
- [Configuring protocol name](#configuring-protocol-name)
- [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 a **transport** and **connection encryption** module 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) ⚠️ [DEPRECATED](https://blog.ipfs.io/2020-08-07-deprecating-secio)
If none of the available connection encryption mechanisms fulfills your needs, you can create a libp2p compatible one. A libp2p connection encryption protocol just needs to be compliant with the [Crypto Interface](https://github.com/libp2p/js-interfaces/tree/master/src/crypto).
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/js-ipfs-interfaces/tree/master/packages/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 `announceFilter`:
- `listen` addresses will be provided to the libp2p underlying transports for listening on them.
- `announce` addresses will be used to compute the advertises that the node should advertise to the network.
- `announceFilter`: filter function used to filter announced addresses programmatically: `(ma: Array<multiaddr>) => Array<multiaddr>`. Default: returns all addresses. [`libp2p-utils`](https://github.com/libp2p/js-libp2p-utils) provides useful [multiaddr utilities](https://github.com/libp2p/js-libp2p-utils/blob/master/API.md#multiaddr-isloopbackma) to create your filters.
### Examples
#### Basic setup
```js
// Creating a libp2p node with:
// transport: websockets + tcp
// stream-muxing: mplex
// crypto-channel: noise
// 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 { NOISE } = require('libp2p-noise')
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: [NOISE],
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 { NOISE } = require('libp2p-noise')
const MulticastDNS = require('libp2p-mdns')
const Bootstrap = require('libp2p-bootstrap')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE],
peerDiscovery: [MulticastDNS, 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.
[MulticastDNS.tag]: {
interval: 1000,
enabled: true
},
[Bootstrap.tag]: {
list: [ // A list of bootstrap peers to connect to starting up the node
"/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
"/dnsaddr/bootstrap.libp2p.io/ipfs/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"/dnsaddr/bootstrap.libp2p.io/ipfs/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
],
interval: 2000,
enabled: true
}
// .. other discovery module options.
}
}
})
```
#### 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 { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
transport: [
WS,
WebRTCStar
],
streamMuxer: [MPLEX],
connEncryption: [NOISE],
},
config: {
peerDiscovery: {
[WebRTCStar.tag]: {
enabled: true
}
}
}
})
```
#### Customizing Pubsub
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const GossipSub = require('libp2p-gossipsub')
const { SignaturePolicy } = require('libp2p-interfaces/src/pubsub/signature-policy')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE],
pubsub: GossipSub
},
config: {
pubsub: { // The pubsub options (and defaults) can be found in the pubsub router documentation
enabled: true,
emitSelf: false, // whether the node should emit to self on publish
globalSignaturePolicy: SignaturePolicy.StrictSign // message signing policy
}
}
})
```
#### Customizing DHT
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const DHT = require('libp2p-kad-dht')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE],
dht: DHT
},
config: {
dht: { // The DHT options (and defaults) can be found in its documentation
kBucketSize: 20,
enabled: true, // This flag is required for DHT to run (disabled by default)
clientMode: false // Whether to run the WAN DHT in client or server mode (default: client mode)
}
}
})
```
#### Setup with Content and Peer Routing
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const ipfsHttpClient = require('ipfs-http-client')
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 delegatedPeerRouting = new DelegatedPeerRouter(ipfsHttpClient.create({
host: 'node0.delegate.ipfs.io', // In production you should setup your own delegates
protocol: 'https',
port: 443
}))
const delegatedContentRouting = new DelegatedContentRouter(peerId, ipfsHttpClient.create({
host: 'node0.delegate.ipfs.io', // In production you should setup your own delegates
protocol: 'https',
port: 443
}))
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE],
contentRouting: [delegatedContentRouting],
peerRouting: [delegatedPeerRouting],
},
peerId,
peerRouting: { // Peer routing configuration
refreshManager: { // Refresh known and connected closest peers
enabled: true, // Should find the closest peers.
interval: 6e5, // Interval for getting the new for closest peers of 10min
bootDelay: 10e3 // Delay for the initial query for closest peers
}
}
})
```
#### Setup with Relay
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
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
},
advertise: {
bootDelay: 15 * 60 * 1000, // Delay before HOP relay service is advertised on the network
enabled: true, // Allows you to disable the advertise of the Hop service
ttl: 30 * 60 * 1000 // Delay Between HOP relay service advertisements on the network
}
}
}
})
```
#### Setup with Auto Relay
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
config: {
relay: { // Circuit Relay options (this config is part of libp2p core configurations)
enabled: true, // Allows you to dial and accept relayed connections. Does not make you a relay.
autoRelay: {
enabled: true, // Allows you to bind to relays with HOP enabled for improving node dialability
maxListeners: 2 // Configure maximum number of HOP relays to use
}
}
}
})
```
#### Setup with Keychain
Libp2p allows you to setup a secure keychain to manage your keys. The keychain configuration object should have the following properties:
| Name | Type | Description |
|------|------|-------------|
| pass | `string` | Passphrase to use in the keychain (minimum of 20 characters). |
| datastore | `object` | must implement [ipfs/interface-datastore](https://github.com/ipfs/interface-datastore) |
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const LevelStore = require('datastore-level')
const datastore = new LevelStore('path/to/store')
await datastore.open()
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
keychain: {
pass: 'notsafepassword123456789',
datastore: dsInstant,
}
})
await node.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. |
| maxAddrsToDial | `number` | How many multiaddrs is the dial allowed to dial for a single peer. |
| maxDialsPerPeer | `number` | How many multiaddrs we can dial per peer, in parallel. |
| dialTimeout | `number` | Second dial timeout per peer in ms. |
| resolvers | `object` | Dial [Resolvers](https://github.com/multiformats/js-multiaddr/blob/master/src/resolvers/index.js) for resolving multiaddrs |
| addressSorter | `(Array<Address>) => Array<Address>` | Sort the known addresses of a peer before trying to dial. |
The below configuration example shows how the dialer should be configured, with the current defaults:
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const { dnsaddrResolver } = require('multiaddr/src/resolvers')
const { publicAddressesFirst } = require('libp2p-utils/src/address-sort')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
dialer: {
maxParallelDials: 100,
maxAddrsToDial: 25,
maxDialsPerPeer: 4,
dialTimeout: 30e3,
resolvers: {
dnsaddr: dnsaddrResolver
},
addressSorter: publicAddressesFirst
}
```
#### 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 { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
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 { NOISE } = require('libp2p-noise')
const { FaultTolerance } = require('libp2p/src/transport-manager')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
transportManager: {
faultTolerance: FaultTolerance.NO_FATAL
}
})
```
#### Configuring Metrics
Metrics are disabled in libp2p by default. You can enable and configure them as follows:
| Name | Type | Description |
|------|------|-------------|
| enabled | `boolean` | Enabled metrics collection. |
| computeThrottleMaxQueueSize | `number` | How many messages a stat will queue before processing. |
| computeThrottleTimeout | `number` | Time in milliseconds a stat will wait, after the last item was added, before processing. |
| movingAverageIntervals | `Array<number>` | The moving averages that will be computed. |
| maxOldPeersRetention | `number` | How many disconnected peers we will retain stats for. |
The below configuration example shows how the metrics should be configured. Aside from enabled being `false` by default, the following default configuration options are listed below:
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
metrics: {
enabled: true,
computeThrottleMaxQueueSize: 1000,
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 { NOISE } = require('libp2p-noise')
const LevelStore = require('datastore-level')
const datastore = new LevelStore('path/to/store')
const dsInstant = await datastore.open()
const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
datastore: dsInstant,
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 { NOISE } = require('libp2p-noise')
const wrtc = require('wrtc')
const transportKey = WebRTCStar.prototype[Symbol.toStringTag]
const node = await Libp2p.create({
modules: {
transport: [WebRTCStar],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
config: {
transport: {
[transportKey]: {
wrtc // You can use `wrtc` when running in Node.js
}
}
}
})
```
During Libp2p startup, transport listeners will be created for the configured listen multiaddrs. Some transports support custom listener options and you can set them using the `listenerOptions` in the transport configuration. For example, [libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star) transport listener supports the configuration of its underlying [simple-peer](https://github.com/feross/simple-peer) ice server(STUN/TURN) config as follows:
```js
const transportKey = WebRTCStar.prototype[Symbol.toStringTag]
const node = await Libp2p.create({
modules: {
transport: [WebRTCStar],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
},
addresses: {
listen: ['/dns4/your-wrtc-star.pub/tcp/443/wss/p2p-webrtc-star'] // your webrtc dns multiaddr
},
config: {
transport: {
[transportKey]: {
listenerOptions: {
config: {
iceServers: [
{"urls": ["turn:YOUR.TURN.SERVER:3478"], "username": "YOUR.USER", "credential": "YOUR.PASSWORD"},
{"urls": ["stun:YOUR.STUN.SERVER:3478"], "username": "", "credential": ""}]
}
}
}
}
}
})
```
#### Configuring the NAT Manager
Network Address Translation (NAT) is a function performed by your router to enable multiple devices on your local network to share a single IPv4 address. It's done transparently for outgoing connections, ensuring the correct response traffic is routed to your computer, but if you wish to accept incoming connections some configuration is necessary.
The NAT manager can be configured as follows:
```js
const node = await Libp2p.create({
config: {
nat: {
description: 'my-node', // set as the port mapping description on the router, defaults the current libp2p version and your peer id
enabled: true, // defaults to true
gateway: '192.168.1.1', // leave unset to auto-discover
externalIp: '80.1.1.1', // leave unset to auto-discover
ttl: 7200, // TTL for port mappings (min 20 minutes)
keepAlive: true, // Refresh port mapping after TTL expires
pmp: {
enabled: false, // defaults to false
}
}
}
})
```
##### Browser support
Browsers cannot open TCP ports or send the UDP datagrams necessary to configure external port mapping - to accept incoming connections in the browser please use a WebRTC transport.
##### UPnP and NAT-PMP
By default under nodejs libp2p will attempt to use [UPnP](https://en.wikipedia.org/wiki/Universal_Plug_and_Play) to configure your router to allow incoming connections to any TCP transports that have been configured.
[NAT-PMP](http://miniupnp.free.fr/nat-pmp.html) is a feature of some modern routers which performs a similar job to UPnP. NAT-PMP is disabled by default, if enabled libp2p will try to use NAT-PMP and will fall back to UPnP if it fails.
#### Configuring protocol name
Changing the protocol name prefix can isolate default public network (IPFS) for custom purposes.
```js
const node = await Libp2p.create({
config: {
protocolPrefix: 'ipfs' // default
}
})
/*
protocols: [
"/ipfs/id/1.0.0", // identify service protocol (if we have multiplexers)
"/ipfs/id/push/1.0.0", // identify service push protocol (if we have multiplexers)
"/ipfs/ping/1.0.0", // built-in ping protocol
]
*/
```
## 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.

View File

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

View File

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

View File

@ -1,256 +0,0 @@
# 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 { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const node = await Libp2p.create({
modules: {
transport: [WebSockets],
connEncryption: [NOISE],
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 { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/127.0.0.1/tcp/8000/ws']
},
modules: {
transport: [WebSockets],
connEncryption: [NOISE],
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 { NOISE } = require('libp2p-noise')
const MPLEX = require('libp2p-mplex')
const Bootstrap = require('libp2p-bootstrap')
// Known peers addresses
const bootstrapMultiaddrs = [
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN'
]
const node = await Libp2p.create({
modules: {
transport: [WebSockets],
connEncryption: [NOISE],
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.connectionManager.on('peer:connect', (connection) => {
console.log('Connected to %s', connection.remotePeer.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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,123 +0,0 @@
<!--Specify versions for migration below-->
# Migrating to libp2p@31
A migration guide for refactoring your application code from libp2p v0.30.x to v0.31.0.
## Table of Contents
- [Types](#types)
- [API](#api)
- [Module Updates](#module-updates)
## Types
Most of the type definitions in the libp2p configuration were `any` or were not included before this release. This might cause breaking changes on upstream projects relying on the previous provided types, as well as to libp2p modules implemented by the libp2p community.
## API
### Core API
`libp2p.dialProtocol` does not accept empty or null protocols returning a connection anymore and `dial` must be used instead.
```js
const connection = await libp2p.dialProtocol(peerId)
```
**After**
```js
const connection = await libp2p.dial(peerId)
```
### Connection Manager Options
We updated the connection manager options naming in `libp2p@0.29` but kept it backward compatible until now.
**Before**
```js
const node = await Libp2p.create({
connectionManager: {
minPeers: 0
}
})
```
**After**
```js
const node = await Libp2p.create({
connectionManager: {
minConnections: 0
}
})
```
You can see full details on how to configure the connection manager [here](https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#configuring-connection-manager).
### Dialer and Keychain components
Internal property names to create a libp2p `Dialer` and `Keychain` were updated to reflect the properties naming in the libp2p configuration. These are internal modules of libp2p core and should not impact most of the users, but as it is possible to use them separately here follow the changes:
***Before**
```js
const dialer = new Dialer({
transportManager,
peerStore,
concurrency,
perPeerLimit,
timeout,
resolvers,
addressSorter
})
const keychain = new Keychain(datastore, {
passPhrase
})
```
**After**
```js
this.dialer = new Dialer({
transportManager,
peerStore,
maxParallelDials,
maxDialsPerPeer,
dialTimeout,
resolvers,
addressSorter
})
const keychain = new Keychain(datastore, {
pass
})
```
## Module Updates
With this release you should update the following libp2p modules if you are relying on them:
<!--Specify module versions in JSON for migration below.
It's recommended to check package.json changes for this:
`git diff <release> <prev> -- package.json`
-->
```json
"libp2p-bootstrap": "^0.12.3",
"libp2p-crypto": "^0.19.4",
"libp2p-interfaces": "^0.10.0",
"libp2p-delegated-content-routing": "^0.10.0",
"libp2p-delegated-peer-routing": "^0.9.0",
"libp2p-floodsub": "^0.25.1",
"libp2p-gossipsub": "^0.9.0",
"libp2p-kad-dht": "^0.22.0",
"libp2p-mdns": "^0.16.0",
"libp2p-noise": "^3.0.0",
"libp2p-tcp": "^0.15.4",
"libp2p-webrtc-star": "^0.22.2",
"libp2p-websockets": "^0.15.6"
```
One of the main changes in this new release is the update to `multiaddr@9.0.0`. This should also be updated in upstream projects to avoid several multiaddr versions in the bundle and to avoid potential problems when libp2p interacts with provided outdated multiaddr instances.

View File

@ -1,36 +0,0 @@
<!--Specify versions for migration below-->
# Migrating to libp2p@32
A migration guide for refactoring your application code from libp2p v0.31.x to v0.32.0.
## Table of Contents
- [Module Updates](#module-updates)
## Module Updates
With this release you should update the following libp2p modules if you are relying on them:
<!--Specify module versions in JSON for migration below.
It's recommended to check package.json changes for this:
`git diff <release> <prev> -- package.json`
-->
```json
"libp2p-bootstrap": "^0.13.0",
"libp2p-crypto": "^0.19.4",
"libp2p-interfaces": "^1.0.0",
"libp2p-delegated-content-routing": "^0.11.0",
"libp2p-delegated-peer-routing": "^0.10.0",
"libp2p-floodsub": "^0.27.0",
"libp2p-gossipsub": "^0.11.0",
"libp2p-kad-dht": "^0.23.0",
"libp2p-mdns": "^0.17.0",
"libp2p-noise": "^4.0.0",
"libp2p-tcp": "^0.17.0",
"libp2p-webrtc-direct": "^0.7.0",
"libp2p-webrtc-star": "^0.23.0",
"libp2p-websockets": "^0.16.0"
```
One of the main changes in this new release is the update to `multiaddr@10.0.0`. This should also be updated in upstream projects to avoid several multiaddr versions in the bundle and to avoid potential problems when libp2p interacts with provided outdated multiaddr instances.

View File

@ -1,14 +0,0 @@
<!--Specify versions for migration below-->
# Migrating to libp2p@33
A migration guide for refactoring your application code from libp2p v0.32.x to v0.33.0.
## Table of Contents
- [Module Updates](#module-updates)
## Module Updates
Libp2p uses a datastore implementation for Peerstore persistence and for the DHT state. While libp2p defaults to a datastore implementation, it can receive any implementation of a datastore compliant with the [interface-datastore](https://github.com/ipfs/js-ipfs-interfaces/tree/master/packages/interface-datastore) via its configuration.
In this release, we updated to `interface-datastore@6.0.0`. As a result, libp2p users relying on a configured datastore should update it to a compliant implementation for updating libp2p.

View File

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

View File

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

View File

@ -1,14 +1,14 @@
# `js-libp2p` Examples and Tutorials # `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 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. 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.
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! Let us know if you find any issue or if you want to contribute and add a new tutorial, feel welcome to submit a PR, thank you!
## Understanding how libp2p works ## Understanding how libp2p works
- [Transports](./transports) - [Transports](./transports)
- [Protocol and Stream Muxing](./protocol-and-stream-muxing) - [Protocol and Stream Muxing](./protocol-and-stream-muxing)
- [Connection Encryption](./connection-encryption) - [Encrypted Communications](./encrypted-communications)
- [Discovery Mechanisms](./discovery-mechanisms) - [Discovery Mechanisms](./discovery-mechanisms)
- [Peer and Content Routing](./peer-and-content-routing) - [Peer and Content Routing](./peer-and-content-routing)
- [PubSub](./pubsub) - [PubSub](./pubsub)
@ -22,5 +22,3 @@ Let us know if you find any issues, or if you want to contribute and add a new t
- Running libp2p in the Electron (future) - Running libp2p in the Electron (future)
- [The standard echo net example with libp2p](./echo) - [The standard echo net example with libp2p](./echo)
- [A simple chat app with libp2p](./chat) - [A simple chat app with libp2p](./chat)
For go-libp2p examples, check out https://github.com/libp2p/go-libp2p-examples#examples-and-tutorials

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,44 +2,78 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
const PeerId = require('peer-id') const PeerId = require('peer-id')
const { Multiaddr } = require('multiaddr') const PeerInfo = require('peer-info')
const createLibp2p = require('./libp2p') const Node = require('./libp2p-bundle')
const { stdinToStream, streamToConsole } = require('./stream') const pull = require('pull-stream')
const async = require('async')
const Pushable = require('pull-pushable')
const p = Pushable()
let idListener
async function run () { async.parallel([
const [idDialer, idListener] = await Promise.all([ (callback) => {
PeerId.createFromJSON(require('./peer-id-dialer')), PeerId.createFromJSON(require('./peer-id-dialer'), (err, idDialer) => {
PeerId.createFromJSON(require('./peer-id-listener')) if (err) {
]) throw err
}
callback(null, idDialer)
})
},
(callback) => {
PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
if (err) {
throw err
}
callback(null, idListener)
})
}
], (err, ids) => {
if (err) throw err
const peerDialer = new PeerInfo(ids[0])
peerDialer.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
const nodeDialer = new Node({
peerInfo: peerDialer
})
// Create a new libp2p node on localhost with a randomly chosen port const peerListener = new PeerInfo(ids[1])
const nodeDialer = await createLibp2p({ idListener = ids[1]
peerId: idDialer, peerListener.multiaddrs.add('/ip4/127.0.0.1/tcp/10333')
addresses: { nodeDialer.start((err) => {
listen: ['/ip4/0.0.0.0/tcp/0'] if (err) {
throw err
} }
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 = new 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()

View File

@ -0,0 +1,77 @@
'use strict'
const TCP = require('libp2p-tcp')
const MulticastDNS = require('libp2p-mdns')
const WS = require('libp2p-websockets')
const Bootstrap = require('libp2p-bootstrap')
const spdy = require('libp2p-spdy')
const KadDHT = require('libp2p-kad-dht')
const mplex = require('libp2p-mplex')
const secio = require('libp2p-secio')
const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../..')
function mapMuxers (list) {
return list.map((pref) => {
if (typeof pref !== 'string') {
return pref
}
switch (pref.trim().toLowerCase()) {
case 'spdy': return spdy
case 'mplex': return mplex
default:
throw new Error(pref + ' muxer not available')
}
})
}
function getMuxers (muxers) {
const muxerPrefs = process.env.LIBP2P_MUXER
if (muxerPrefs && !muxers) {
return mapMuxers(muxerPrefs.split(','))
} else if (muxers) {
return mapMuxers(muxers)
} else {
return [mplex, spdy]
}
}
class Node extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [
TCP,
WS
],
streamMuxer: getMuxers(_options.muxer),
connEncryption: [ secio ],
peerDiscovery: [
MulticastDNS,
Bootstrap
],
dht: KadDHT
},
config: {
peerDiscovery: {
mdns: {
interval: 10000,
enabled: false
},
bootstrap: {
interval: 10000,
enabled: false,
list: _options.bootstrapList
}
},
dht: {
kBucketSize: 20
}
}
}
super(defaultsDeep(_options, defaults))
}
}
module.exports = Node

View File

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

View File

@ -2,40 +2,55 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
const PeerId = require('peer-id') const PeerId = require('peer-id')
const createLibp2p = require('./libp2p.js') const PeerInfo = require('peer-info')
const { stdinToStream, streamToConsole } = require('./stream') const Node = require('./libp2p-bundle.js')
const pull = require('pull-stream')
const Pushable = require('pull-pushable')
const p = Pushable()
async function run () { PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
// Create a new libp2p node with the given multi-address if (err) {
const idListener = await PeerId.createFromJSON(require('./peer-id-listener')) throw err
const nodeListener = await createLibp2p({ }
peerId: idListener, const peerListener = new PeerInfo(idListener)
addresses: { peerListener.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
listen: ['/ip4/0.0.0.0/tcp/10333'] const nodeListener = new Node({
peerInfo: peerListener
})
nodeListener.start((err) => {
if (err) {
throw err
} }
nodeListener.on('peer:connect', (peerInfo) => {
console.log(peerInfo.id.toB58String())
})
nodeListener.handle('/chat/1.0.0', (protocol, conn) => {
pull(
p,
conn
)
pull(
conn,
pull.map((data) => {
return data.toString('utf8').replace('\n', '')
}),
pull.drain(console.log)
)
process.stdin.setEncoding('utf8')
process.openStdin().on('data', (chunk) => {
var data = chunk.toString()
p.push(data)
})
})
console.log('Listener ready, listening on:')
peerListener.multiaddrs.forEach((ma) => {
console.log(ma.toString() + '/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()

View File

@ -1,40 +0,0 @@
'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().replace('\n', ''))
}
}
)
}
module.exports = {
stdinToStream,
streamToConsole
}

View File

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

View File

@ -1,52 +0,0 @@
'use strict'
const Libp2p = require('../..')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const { NOISE } = require('@chainsafe/libp2p-noise')
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]
}
})
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())
}
}
)
})
const { stream } = await node1.dialProtocol(node2.peerId, '/a-protocol')
await pipe(
['This information is sent out encrypted to the other peer'],
stream
)
})();

View File

@ -1,31 +0,0 @@
# Connection Encryption
libp2p can leverage the encrypted communications from the transports it uses (i.e WebRTC). To ensure that every connection is encrypted, independently of how it was set up, libp2p also supports a set of modules that encrypt every communication established.
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/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
We will build this example on top of example for [Protocol and Stream Multiplexing](../protocol-and-stream-multiplexing). You will need the `@chainsafe/libp2p-noise` module to complete it, go ahead and `npm install @chainsafe/libp2p-noise`.
To add them to your libp2p configuration, all you have to do is:
```JavaScript
const Libp2p = require('libp2p')
const { NOISE } = require('@chainsafe/libp2p-noise')
const createNode = () => {
return Libp2p.create({
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
// Attach noise as the crypto channel to use
connEncryption: [ NOISE ]
}
})
}
```
And that's it, from now on, all your libp2p communications are encrypted. Try running the example [1.js](./1.js) to see it working.

View File

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

View File

@ -1,7 +1,3 @@
❗❗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 # 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 This example shows how to use delegated peer and content routing. The [Peer and Content Routing Example](../peer-and-content-routing) focuses

View File

@ -7,7 +7,7 @@
"libp2p": "github:libp2p/js-libp2p#master", "libp2p": "github:libp2p/js-libp2p#master",
"libp2p-delegated-content-routing": "~0.2.2", "libp2p-delegated-content-routing": "~0.2.2",
"libp2p-delegated-peer-routing": "~0.2.2", "libp2p-delegated-peer-routing": "~0.2.2",
"libp2p-kad-dht": "^0.26.5", "libp2p-kad-dht": "~0.14.12",
"libp2p-mplex": "~0.8.5", "libp2p-mplex": "~0.8.5",
"libp2p-secio": "~0.11.1", "libp2p-secio": "~0.11.1",
"libp2p-webrtc-star": "~0.15.8", "libp2p-webrtc-star": "~0.15.8",

View File

@ -44,7 +44,7 @@ class App extends Component {
isLoading: this.state.isLoading + 1 isLoading: this.state.isLoading + 1
}) })
this.ipfs.cat(this.state.hash, (err, data) => { this.ipfs.files.cat(this.state.hash, (err, data) => {
if (err) console.log('Error', err) if (err) console.log('Error', err)
this.setState({ this.setState({

View File

@ -1,44 +1,73 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
'use strict' 'use strict'
const Libp2p = require('../../') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex') const Mplex = require('libp2p-mplex')
const { NOISE } = require('@chainsafe/libp2p-noise') const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const Bootstrap = require('libp2p-bootstrap') const Bootstrap = require('libp2p-bootstrap')
const waterfall = require('async/waterfall')
const defaultsDeep = require('@nodeutils/defaults-deep')
const bootstrapers = require('./bootstrapers') // Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-nodejs.json
const bootstrapers = [
'/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
'/ip4/104.236.176.52/tcp/4001/p2p/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
'/ip4/104.236.179.241/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/ip4/162.243.248.213/tcp/4001/p2p/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/ip4/128.199.219.111/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/ip4/104.236.76.40/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
'/ip4/178.62.158.247/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/ip4/178.62.61.185/tcp/4001/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/ip4/104.236.151.122/tcp/4001/p2p/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx'
]
;(async () => { class MyBundle extends libp2p {
const node = await Libp2p.create({ constructor (_options) {
addresses: { const defaults = {
listen: ['/ip4/0.0.0.0/tcp/0'] modules: {
}, transport: [ TCP ],
modules: { streamMuxer: [ Mplex ],
transport: [TCP], connEncryption: [ SECIO ],
streamMuxer: [Mplex], peerDiscovery: [ Bootstrap ]
connEncryption: [NOISE], },
peerDiscovery: [Bootstrap] config: {
}, peerDiscovery: {
config: { autoDial: true,
peerDiscovery: { bootstrap: {
bootstrap: { interval: 20e3,
interval: 60e3, enabled: true,
enabled: true, list: bootstrapers
list: bootstrapers }
} }
} }
} }
})
node.connectionManager.on('peer:connect', (connection) => { super(defaultsDeep(_options, defaults))
console.log('Connection established to:', connection.remotePeer.toB58String()) // Emitted when a peer has been found }
}) }
node.on('peer:discovery', (peerId) => { 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) => {
// No need to dial, autoDial is on // No need to dial, autoDial is on
console.log('Discovered:', peerId.toB58String()) console.log('Discovered:', peer.id.toB58String())
}) })
await node.start() node.on('peer:connect', (peer) => {
})(); console.log('Connection established to:', peer.id.toB58String())
})
})

View File

@ -1,47 +1,63 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
'use strict' 'use strict'
const Libp2p = require('../../') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex') const Mplex = require('libp2p-mplex')
const { NOISE } = require('@chainsafe/libp2p-noise') const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const MulticastDNS = require('libp2p-mdns') const MulticastDNS = require('libp2p-mdns')
const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
const defaultsDeep = require('@nodeutils/defaults-deep')
const createNode = async () => { class MyBundle extends libp2p {
const node = await Libp2p.create({ constructor (_options) {
addresses: { const defaults = {
listen: ['/ip4/0.0.0.0/tcp/0'] modules: {
}, transport: [ TCP ],
modules: { streamMuxer: [ Mplex ],
transport: [TCP], connEncryption: [ SECIO ],
streamMuxer: [Mplex], peerDiscovery: [ MulticastDNS ]
connEncryption: [NOISE], },
peerDiscovery: [MulticastDNS] config: {
}, peerDiscovery: {
config: { mdns: {
peerDiscovery: { interval: 20e3,
[MulticastDNS.tag]: { enabled: true
interval: 20e3, }
enabled: true
} }
} }
} }
})
return node super(defaultsDeep(_options, defaults))
}
} }
;(async () => { function createNode (callback) {
const [node1, node2] = await Promise.all([ let node
createNode(),
createNode()
])
node1.on('peer:discovery', (peerId) => console.log('Discovered:', peerId.toB58String())) waterfall([
node2.on('peer:discovery', (peerId) => console.log('Discovered:', peerId.toB58String())) (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))
}
await Promise.all([ parallel([
node1.start(), (cb) => createNode(cb),
node2.start() (cb) => createNode(cb)
]) ], (err, nodes) => {
})(); if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
node1.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
node2.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
})

View File

@ -1,68 +0,0 @@
/* eslint-disable no-console */
'use strict'
const Libp2p = require('../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const { NOISE } = require('@chainsafe/libp2p-noise')
const Gossipsub = require('libp2p-gossipsub')
const Bootstrap = require('libp2p-bootstrap')
const PubsubPeerDiscovery = require('libp2p-pubsub-peer-discovery')
const createRelayServer = require('libp2p-relay-server')
const createNode = async (bootstrapers) => {
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [Mplex],
connEncryption: [NOISE],
pubsub: Gossipsub,
peerDiscovery: [Bootstrap, PubsubPeerDiscovery]
},
config: {
peerDiscovery: {
[PubsubPeerDiscovery.tag]: {
interval: 1000,
enabled: true
},
[Bootstrap.tag]: {
enabled: true,
list: bootstrapers
}
}
}
})
return node
}
;(async () => {
const relay = await createRelayServer({
listenAddresses: ['/ip4/0.0.0.0/tcp/0']
})
console.log(`libp2p relay starting with id: ${relay.peerId.toB58String()}`)
await relay.start()
const relayMultiaddrs = relay.multiaddrs.map((m) => `${m.toString()}/p2p/${relay.peerId.toB58String()}`)
const [node1, node2] = await Promise.all([
createNode(relayMultiaddrs),
createNode(relayMultiaddrs)
])
node1.on('peer:discovery', (peerId) => {
console.log(`Peer ${node1.peerId.toB58String()} discovered: ${peerId.toB58String()}`)
})
node2.on('peer:discovery', (peerId) => {
console.log(`Peer ${node2.peerId.toB58String()} discovered: ${peerId.toB58String()}`)
})
;[node1, node2].forEach((node, index) => console.log(`Node ${index} starting with id: ${node.peerId.toB58String()}`))
await Promise.all([
node1.start(),
node2.start()
])
})();

View File

@ -2,87 +2,90 @@
A Peer Discovery module enables libp2p to find peers to connect to. Think of these mechanisms as ways to join the rest of the network, as railing points. A Peer Discovery module enables libp2p to find peers to connect to. Think of these mechanisms as ways to join the rest of the network, as railing points.
With this 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). 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. Once new peers are discovered, their known data is stored in the peer's PeerStore. These mechanisms save configuration and enable a node to operate without any explicit dials, it will just work.
## 1. Bootstrap list of Peers when booting a node ## 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 NOISE. You can see the complete example at [1.js](./1.js). For this demo, we will connect to IPFS default bootstrapper nodes and so, we will need to support the same set of features those nodes have, that are: TCP, mplex and SECIO. You can see the complete example at [1.js](./1.js).
First, we create our libp2p node. First, we create our libp2p bundle.
```JavaScript ```JavaScript
const Libp2p = require('libp2p') const Bootstrap = require('libp2p-railing')
const Bootstrap = require('libp2p-bootstrap') class MyBundle extends libp2p {
constructor (peerInfo) {
const node = await Libp2p.create({ const defaults = {
modules: { modules: {
transport: [ TCP ], transport: [ TCP ],
streamMuxer: [ Mplex ], streamMuxer: [ Mplex ],
connEncryption: [ NOISE ], connEncryption: [ SECIO ],
peerDiscovery: [ Bootstrap ] peerDiscovery: [ Bootstrap ]
}, },
config: { config: {
peerDiscovery: { peerDiscovery: {
bootstrap: { bootstrap: {
interval: 60e3, interval: 2000,
enabled: true, enabled: true,
list: bootstrapers list: bootstrapers
}
}
} }
} }
super(defaultsDeep(_options, defaults))
} }
}) }
``` ```
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. In this bundle, we use a `bootstrappers` array listing peers to connect _on boot_. Here is the list used by js-ipfs and go-ipfs.
```JavaScript ```JavaScript
const bootstrapers = [ const bootstrapers = [
'/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ', '/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN', '/ip4/104.236.176.52/tcp/4001/p2p/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', '/ip4/104.236.179.241/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp', '/ip4/162.243.248.213/tcp/4001/p2p/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa', '/ip4/128.199.219.111/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt' '/ip4/104.236.76.40/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
'/ip4/178.62.158.247/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/ip4/178.62.61.185/tcp/4001/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/ip4/104.236.151.122/tcp/4001/p2p/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx'
] ]
``` ```
Now, once we create and start the node, we can listen for events such as `peer:discovery` and `peer:connect`, these events tell us when we found a peer, independently of the discovery mechanism used and when we actually dialed to that peer. Now, once we create and start the node, we can listen for events such as `peer:discovery` and `peer:connect`, these events tell us when we found a peer, independently of the discovery mechanism used and when we actually dialed to that peer.
```JavaScript ```JavaScript
const node = await Libp2p.create({ let node
peerId,
addresses: { waterfall([
listen: ['/ip4/0.0.0.0/tcp/0'] (cb) => PeerInfo.create(cb),
}, (peerInfo, cb) => {
modules: { peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
transport: [ TCP ], node = new MyBundle({
streamMuxer: [ Mplex ], peerInfo
connEncryption: [ NOISE ], })
peerDiscovery: [ Bootstrap ] node.start(cb)
},
config: {
peerDiscovery: {
bootstrap: {
interval: 60e3,
enabled: true,
list: bootstrapers
}
}
} }
}) ], (err) => {
if (err) { throw err }
node.connectionManager.on('peer:connect', (connection) => { // Emitted when a peer has been found
console.log('Connection established to:', connection.remotePeer.toB58String()) // Emitted when a new connection has been created 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, () => {})
})
node.on('peer:discovery', (peerId) => { // Once the dial is complete, this event is emitted.
// No need to dial, autoDial is on node.on('peer:connect', (peer) => {
console.log('Discovered:', peerId.toB58String()) console.log('Connection established to:', peer.id.toB58String())
})
}) })
await node.start()
``` ```
From running [1.js](./1.js), you should see the following: From running [1.js](./1.js), you should see the following:
@ -90,174 +93,90 @@ From running [1.js](./1.js), you should see the following:
```bash ```bash
> node 1.js > node 1.js
Discovered: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ Discovered: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
Discovered: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN Discovered: QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z
Discovered: QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb Discovered: QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM
Discovered: QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp Discovered: QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm
Discovered: QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa Discovered: QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu
Discovered: QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt 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: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
Connection established to: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN Connection established to: QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z
Connection established to: QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp Connection established to: QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu
Connection established to: QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa
Connection established to: QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt
Connection established to: QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb
``` ```
## 2. MulticastDNS to find other peers in the network ## 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). 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 configuration to include MulticastDNS. Update your libp2p bundle to include MulticastDNS.
```JavaScript ```JavaScript
const Libp2p = require('libp2p') class MyBundle extends libp2p {
const MulticastDNS = require('libp2p-mdns') constructor (peerInfo) {
const defaults = {
const createNode = () => { modules: {
return Libp2p.create({ transport: [ TCP ],
addresses: { streamMuxer: [ Mplex ],
listen: ['/ip4/0.0.0.0/tcp/0'] connEncryption: [ SECIO ],
}, peerDiscovery: [ MulticastDNS ]
modules: { },
transport: [ TCP ], config: {
streamMuxer: [ Mplex ], peerDiscovery: {
connEncryption: [ NOISE ], mdns: {
peerDiscovery: [ MulticastDNS ] // Run at 1s so we can observe more quickly, default is 10s
}, interval: 1000,
config: { enabled: true
peerDiscovery: { }
mdns: {
interval: 20e3,
enabled: true
} }
} }
} }
})
super(defaultsDeep(_options, defaults))
}
} }
``` ```
To observe it working, spawn two nodes. To observe it working, spawn two nodes.
```JavaScript ```JavaScript
const [node1, node2] = await Promise.all([ parallel([
createNode(), (cb) => createNode(cb),
createNode() (cb) => createNode(cb)
]) ], (err, nodes) => {
if (err) { throw err }
node1.on('peer:discovery', (peer) => console.log('Discovered:', peerId.toB58String())) const node1 = nodes[0]
node2.on('peer:discovery', (peer) => console.log('Discovered:', peerId.toB58String())) const node2 = nodes[1]
await Promise.all([ node1.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
node1.start(), node2.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
node2.start() })
])
``` ```
If you run this example, you will see the other peers being discovered. If you run this example, you will see a continuous stream of each peer discovering each other.
```bash ```bash
> node 2.js > node 2.js
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
``` ```
## 3. Pubsub based Peer Discovery ## 3. Where to find other Peer Discovery Mechanisms
For this example, we need [`libp2p-pubsub-peer-discovery`](https://github.com/libp2p/js-libp2p-pubsub-peer-discovery/), go ahead and `npm install` it. You also need to spin up a set of [`libp2p-relay-servers`](https://github.com/libp2p/js-libp2p-relay-server). These servers act as relay servers and a peer discovery source.
In the context of this example, we will create and run the `libp2p-relay-server` in the same code snippet. You can find the complete solution at [3.js](./3.js).
You can create your libp2p nodes as follows:
```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const { NOISE } = require('@chainsafe/libp2p-noise')
const Gossipsub = require('libp2p-gossipsub')
const Bootstrap = require('libp2p-bootstrap')
const PubsubPeerDiscovery = require('libp2p-pubsub-peer-discovery')
const createNode = async (bootstrapers) => {
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [Mplex],
connEncryption: [NOISE],
pubsub: Gossipsub,
peerDiscovery: [Bootstrap, PubsubPeerDiscovery]
},
config: {
peerDiscovery: {
[PubsubPeerDiscovery.tag]: {
interval: 1000,
enabled: true
},
[Bootstrap.tag]: {
enabled: true,
list: bootstrapers
}
}
}
})
return node
}
```
We will use the `libp2p-relay-server` as bootstrap nodes for the libp2p nodes, so that they establish a connection with the relay after starting. As a result, after they establish a connection with the relay, the pubsub discovery will kick in and the relay will advertise them.
```js
const relay = await createRelayServer({
listenAddresses: ['/ip4/0.0.0.0/tcp/0']
})
console.log(`libp2p relay starting with id: ${relay.peerId.toB58String()}`)
await relay.start()
const relayMultiaddrs = relay.multiaddrs.map((m) => `${m.toString()}/p2p/${relay.peerId.toB58String()}`)
const [node1, node2] = await Promise.all([
createNode(relayMultiaddrs),
createNode(relayMultiaddrs)
])
node1.on('peer:discovery', (peerId) => {
console.log(`Peer ${node1.peerId.toB58String()} discovered: ${peerId.toB58String()}`)
})
node2.on('peer:discovery', (peerId) => {
console.log(`Peer ${node2.peerId.toB58String()} discovered: ${peerId.toB58String()}`)
})
;[node1, node2].forEach((node, index) => console.log(`Node ${index} starting with id: ${node.peerId.toB58String()}`))
await Promise.all([
node1.start(),
node2.start()
])
```
If you run this example, you will see the other peers being discovered.
```bash
> node 3.js
libp2p relay starting with id: QmW6FqVV6RsyoGC5zaeFGW9gSWA3LcBRVZrjkKMruh38Bo
Node 0 starting with id: QmezqDTmEjZ5BfMgVqjSpLY19mVVLTQ9bE9mRpZwtGxL8N
Node 1 starting with id: QmYWeom2odTkm79DzB68NHULqVHDaNDqHhoyqLdcV1fqdv
Peer QmezqDTmEjZ5BfMgVqjSpLY19mVVLTQ9bE9mRpZwtGxL8N discovered: QmW6FqVV6RsyoGC5zaeFGW9gSWA3LcBRVZrjkKMruh38Bo
Peer QmYWeom2odTkm79DzB68NHULqVHDaNDqHhoyqLdcV1fqdv discovered: QmW6FqVV6RsyoGC5zaeFGW9gSWA3LcBRVZrjkKMruh38Bo
Peer QmYWeom2odTkm79DzB68NHULqVHDaNDqHhoyqLdcV1fqdv discovered: QmezqDTmEjZ5BfMgVqjSpLY19mVVLTQ9bE9mRpZwtGxL8N
Peer QmezqDTmEjZ5BfMgVqjSpLY19mVVLTQ9bE9mRpZwtGxL8N discovered: QmYWeom2odTkm79DzB68NHULqVHDaNDqHhoyqLdcV1fqdv
```
Taking into account the output, after the relay and both libp2p nodes start, both libp2p nodes will discover the bootstrap node (relay) and connect with it. After establishing a connection with the relay, they will discover each other.
This is really useful when running libp2p in constrained environments like a browser. You can run a set of `libp2p-relay-server` nodes that will be responsible for both relaying websocket connections between browser nodes and for discovering other browser peers.
## 4. Where to find other Peer Discovery Mechanisms
There are plenty more Peer Discovery Mechanisms out there, you can: 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. - 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. For example [libp2p-kad-dht](https://github.com/libp2p/js-libp2p-kad-dht) can be used for peer discovery. An example of 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). - Any DHT will offer you a discovery capability. You can simple _random-walk_ the routing tables to find other peers to connect to.
- You can create your own Discovery service, a registry, a list, a radio beacon, you name it! - You can create your own Discovery service, a registry, a list, a radio beacon, you name it!

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,53 +6,53 @@
*/ */
const PeerId = require('peer-id') const PeerId = require('peer-id')
const createLibp2p = require('./libp2p') const PeerInfo = require('peer-info')
const pipe = require('it-pipe') const Node = require('./libp2p-bundle')
const pull = require('pull-stream')
const async = require('async')
async function run() { async.parallel([
const [dialerId, listenerId] = await Promise.all([ (cb) => PeerId.createFromJSON(require('./id-d'), cb),
PeerId.createFromJSON(require('./id-d')), (cb) => PeerId.createFromJSON(require('./id-l'), cb)
PeerId.createFromJSON(require('./id-l')) ], (err, ids) => {
]) if (err) { throw err }
// Dialer // Dialer
const dialerNode = await createLibp2p({ const dialerId = ids[0]
addresses: { const dialerPeerInfo = new PeerInfo(dialerId)
listen: ['/ip4/0.0.0.0/tcp/0'] dialerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
}, const dialerNode = new Node({
peerId: dialerId peerInfo: dialerPeerInfo
}) })
// Add peer to Dial (the listener) into the PeerStore // Peer to Dial
const listenerMultiaddr = '/ip4/127.0.0.1/tcp/10333/p2p/' + listenerId.toB58String() 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)
// Start the dialer libp2p node dialerNode.start((err) => {
await dialerNode.start() if (err) { throw err }
console.log('Dialer ready, listening on:') console.log('Dialer ready, listening on:')
dialerNode.multiaddrs.forEach((ma) => console.log(ma.toString() + dialerPeerInfo.multiaddrs.forEach((ma) => console.log(ma.toString() +
'/p2p/' + dialerId.toB58String())) '/p2p/' + dialerId.toB58String()))
// Dial the listener node console.log('Dialing to peer:', listenerMultiaddr.toString())
console.log('Dialing to peer:', listenerMultiaddr) dialerNode.dialProtocol(listenerPeerInfo, '/echo/1.0.0', (err, conn) => {
const { stream } = await dialerNode.dialProtocol(listenerMultiaddr, '/echo/1.0.0') if (err) { throw err }
console.log('nodeA dialed to nodeB on protocol: /echo/1.0.0') console.log('nodeA dialed to nodeB on protocol: /echo/1.0.0')
pipe( pull(
// Source data pull.values(['hey']),
['hey'], conn,
// Write to the stream, and pass its output to the next function pull.collect((err, data) => {
stream, if (err) { throw err }
// Sink function console.log('received echo:', data.toString())
async function (source) { })
// For each chunk of data )
for await (const data of source) { })
// Output the data })
console.log('received echo:', data.toString()) })
}
}
)
}
run()

View File

@ -0,0 +1,77 @@
'use strict'
const TCP = require('libp2p-tcp')
const MulticastDNS = require('libp2p-mdns')
const WS = require('libp2p-websockets')
const Bootstrap = require('libp2p-bootstrap')
const spdy = require('libp2p-spdy')
const KadDHT = require('libp2p-kad-dht')
const mplex = require('libp2p-mplex')
const secio = require('libp2p-secio')
const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../..')
function mapMuxers (list) {
return list.map((pref) => {
if (typeof pref !== 'string') {
return pref
}
switch (pref.trim().toLowerCase()) {
case 'spdy': return spdy
case 'mplex': return mplex
default:
throw new Error(pref + ' muxer not available')
}
})
}
function getMuxers (muxers) {
const muxerPrefs = process.env.LIBP2P_MUXER
if (muxerPrefs && !muxers) {
return mapMuxers(muxerPrefs.split(','))
} else if (muxers) {
return mapMuxers(muxers)
} else {
return [mplex, spdy]
}
}
class Node extends libp2p {
constructor (_options) {
const defaults = {
modules: {
transport: [
TCP,
WS
],
streamMuxer: getMuxers(_options.muxer),
connEncryption: [ secio ],
peerDiscovery: [
MulticastDNS,
Bootstrap
],
dht: KadDHT
},
config: {
peerDiscovery: {
mdns: {
interval: 10000,
enabled: false
},
bootstrap: {
interval: 10000,
enabled: false,
list: _options.bootstrapList
}
},
dht: {
kBucketSize: 20
}
}
}
super(defaultsDeep(_options, defaults))
}
}
module.exports = Node

View File

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

View File

@ -6,36 +6,41 @@
*/ */
const PeerId = require('peer-id') const PeerId = require('peer-id')
const createLibp2p = require('./libp2p') const PeerInfo = require('peer-info')
const pipe = require('it-pipe') const Node = require('./libp2p-bundle')
const pull = require('pull-stream')
const series = require('async/series')
async function run() { let listenerId
const listenerId = await PeerId.createFromJSON(require('./id-l')) let listenerNode
// Listener libp2p node series([
const listenerNode = await createLibp2p({ (cb) => {
addresses: { PeerId.createFromJSON(require('./id-l'), (err, id) => {
listen: ['/ip4/0.0.0.0/tcp/10333'] if (err) { return cb(err) }
}, listenerId = id
peerId: listenerId cb()
}) })
},
(cb) => {
const listenerPeerInfo = new PeerInfo(listenerId)
listenerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
listenerNode = new Node({
peerInfo: listenerPeerInfo
})
// Log a message when we receive a connection listenerNode.on('peer:connect', (peerInfo) => {
listenerNode.connectionManager.on('peer:connect', (connection) => { console.log('received dial to me from:', peerInfo.id.toB58String())
console.log('received dial to me from:', connection.remotePeer.toB58String()) })
})
// Handle incoming connections for the protocol by piping from the stream listenerNode.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
// back to itself (an echo) listenerNode.start(cb)
await listenerNode.handle('/echo/1.0.0', ({ stream }) => pipe(stream.source, stream.sink)) }
], (err) => {
// Start listening if (err) { throw err }
await listenerNode.start()
console.log('Listener ready, listening on:') console.log('Listener ready, listening on:')
listenerNode.multiaddrs.forEach((ma) => { listenerNode.peerInfo.multiaddrs.forEach((ma) => {
console.log(ma.toString() + '/p2p/' + listenerId.toB58String()) console.log(ma.toString() + '/p2p/' + listenerId.toB58String())
}) })
} })
run()

View File

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

View File

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

View File

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

View File

@ -1,4 +0,0 @@
{
"presets": ["@babel/preset-env"],
"plugins": ["syntax-async-functions","transform-regenerator"]
}

View File

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

View File

@ -0,0 +1,32 @@
{
"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": "../../../",
"libp2p-bootstrap": "~0.9.7",
"libp2p-gossipsub": "~0.0.4",
"libp2p-kad-dht": "^0.15.3",
"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"
}
}

View File

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

View File

@ -0,0 +1,94 @@
'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 Gossipsub = require('libp2p-gossipsub')
const libp2p = require('libp2p')
// 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 ({ peerInfo }) {
const wrtcStar = new WebRTCStar({ id: peerInfo.id })
const wsstar = new WebSocketStar({ id: peerInfo.id })
const defaults = {
modules: {
transport: [
wrtcStar,
WebSockets,
wsstar
],
streamMuxer: [
Mplex,
SPDY
],
connEncryption: [
SECIO
],
peerDiscovery: [
wrtcStar.discovery,
wsstar.discovery,
Bootstrap
],
dht: DHT,
pubsub: Gossipsub
},
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
},
pubsub: {
enabled: false
}
},
connectionManager: {
minPeers: 10,
maxPeers: 50
}
}
super({ ...defaults, peerInfo })
}
}
module.exports = Node

View File

@ -0,0 +1,28 @@
'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 webrtcAddr = `/dns4/star-signal.cloud.ipfs.team/tcp/443/wss/p2p-webrtc-star/p2p/${peerIdStr}`
const wsAddr = `/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star`
peerInfo.multiaddrs.add(webrtcAddr)
peerInfo.multiaddrs.add(wsAddr)
const node = new Node({
peerInfo
})
node.idStr = peerIdStr
callback(null, node)
})
}
module.exports = createNode

View File

@ -0,0 +1,57 @@
/* 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/')
node.peerInfo.multiaddrs.toArray().forEach(ma => {
console.log(ma.toString())
})
// NOTE: to stop the node
// node.stop((err) => {})
})
})
})

View File

@ -1,50 +1,22 @@
# libp2p in the browser # libp2p running in the Browser
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. 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.
## Setup # 1. Setting up a simple app that lists connections to other nodes
In order to run the example: Start by installing libp2p's dependencies.
- Install dependencey at the root of the js-libp2p repository (if not already done), ```bash
- then, install the dependencies from same directory as this README: > cd ../../
> npm install
``` > cd examples/libp2p-in-the-browser
npm install
cd ./examples/libp2p-in-the-browser
npm install
``` ```
## Running the examples Then simply go into the folder [1](./1) and execute the following
Start by running the Parcel server:
```bash
> cd 1
> npm install
> npm start
# open your browser in port :9090
``` ```
npm start
```
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.

View File

@ -1,23 +0,0 @@
<!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 type="module" src="./index.js"></script>
</body>
</html>

View File

@ -1,77 +0,0 @@
import 'babel-polyfill'
import Libp2p from 'libp2p'
import Websockets from 'libp2p-websockets'
import WebRTCStar from 'libp2p-webrtc-star'
import { NOISE } from '@chainsafe/libp2p-noise'
import Mplex from 'libp2p-mplex'
import Bootstrap from 'libp2p-bootstrap'
document.addEventListener('DOMContentLoaded', async () => {
// Create our libp2p node
const libp2p = await Libp2p.create({
addresses: {
// Add the signaling server address, along with our PeerId to our multiaddrs list
// libp2p will automatically attempt to dial to the signaling server so that it can
// receive inbound connections from other peers
listen: [
'/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star',
'/dns4/wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star'
]
},
modules: {
transport: [Websockets, WebRTCStar],
connEncryption: [NOISE],
streamMuxer: [Mplex],
peerDiscovery: [Bootstrap]
},
config: {
peerDiscovery: {
// 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: [
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
]
}
}
}
})
// 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
})

View File

@ -1,33 +0,0 @@
{
"name": "libp2p-in-browser",
"version": "1.0.0",
"description": "A libp2p node running in the browser",
"browserslist": [
"last 2 Chrome versions"
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "parcel build index.html",
"start": "parcel index.html"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@babel/preset-env": "^7.13.0",
"libp2p": "../../",
"libp2p-bootstrap": "^0.13.0",
"libp2p-mplex": "^0.10.4",
"@chainsafe/libp2p-noise": "^4.1.0",
"libp2p-webrtc-star": "^0.23.0",
"libp2p-websockets": "^0.16.1"
},
"devDependencies": {
"@babel/cli": "^7.13.10",
"@babel/core": "^7.13.0",
"babel-plugin-syntax-async-functions": "^6.13.0",
"babel-plugin-transform-regenerator": "^6.26.0",
"babel-polyfill": "^6.26.0",
"parcel": "^2.0.1"
}
}

View File

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

View File

@ -1,25 +0,0 @@
{
"name": "libp2p-examples",
"version": "1.0.0",
"description": "Examples of how to use libp2p",
"scripts": {
"test": "node ./test.js",
"test:all": "node ./test-all.js"
},
"license": "MIT",
"dependencies": {
"execa": "^2.1.0",
"fs-extra": "^8.1.0",
"libp2p": "../src",
"libp2p-pubsub-peer-discovery": "^4.0.0",
"libp2p-relay-server": "^0.3.0",
"libp2p-gossipsub": "^0.11.0",
"p-defer": "^3.0.0",
"uint8arrays": "^3.0.0",
"which": "^2.0.1"
},
"devDependencies": {
"https": "^1.0.0",
"playwright": "^1.7.1"
}
}

View File

@ -1,56 +1,77 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
'use strict' 'use strict'
const Libp2p = require('../../') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex') const Mplex = require('libp2p-mplex')
const { NOISE } = require('@chainsafe/libp2p-noise') const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const KadDHT = require('libp2p-kad-dht') const KadDHT = require('libp2p-kad-dht')
const defaultsDeep = require('@nodeutils/defaults-deep')
const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
const delay = require('delay') class MyBundle extends libp2p {
constructor (_options) {
const createNode = async () => { const defaults = {
const node = await Libp2p.create({ modules: {
addresses: { transport: [ TCP ],
listen: ['/ip4/0.0.0.0/tcp/0'] streamMuxer: [ Mplex ],
}, connEncryption: [ SECIO ],
modules: { // we add the DHT module that will enable Peer and Content Routing
transport: [TCP], dht: KadDHT
streamMuxer: [Mplex], },
connEncryption: [NOISE], config: {
dht: KadDHT dht: {
}, enabled: true,
config: { kBucketSize: 20
dht: { }
enabled: true
} }
} }
})
await node.start() super(defaultsDeep(_options, defaults))
return node }
} }
;(async () => { function createNode (callback) {
const [node1, node2, node3] = await Promise.all([ let node
createNode(),
createNode(),
createNode()
])
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs) waterfall([
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs) (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))
}
await Promise.all([ parallel([
node1.dial(node2.peerId), (cb) => createNode(cb),
node2.dial(node3.peerId) (cb) => createNode(cb),
]) (cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
// The DHT routing tables need a moment to populate const node1 = nodes[0]
await delay(100) const node2 = nodes[1]
const node3 = nodes[2]
const peer = await node1.peerRouting.findPeer(node3.peerId) 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 }
console.log('Found it, multiaddrs are:') node1.peerRouting.findPeer(node3.peerInfo.id, (err, peer) => {
peer.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${peer.id.toB58String()}`)) if (err) { throw err }
})();
console.log('Found it, multiaddrs are:')
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
})
})
})

View File

@ -1,65 +1,85 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
'use strict' 'use strict'
const Libp2p = require('../../') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex') const Mplex = require('libp2p-mplex')
const { NOISE } = require('@chainsafe/libp2p-noise') const SECIO = require('libp2p-secio')
const { CID } = require('multiformats/cid') const PeerInfo = require('peer-info')
const CID = require('cids')
const KadDHT = require('libp2p-kad-dht') const KadDHT = require('libp2p-kad-dht')
const defaultsDeep = require('@nodeutils/defaults-deep')
const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
const all = require('it-all') class MyBundle extends libp2p {
const delay = require('delay') constructor (_options) {
const defaults = {
const createNode = async () => { modules: {
const node = await Libp2p.create({ transport: [ TCP ],
addresses: { streamMuxer: [ Mplex ],
listen: ['/ip4/0.0.0.0/tcp/0'] connEncryption: [ SECIO ],
}, // we add the DHT module that will enable Peer and Content Routing
modules: { dht: KadDHT
transport: [TCP], },
streamMuxer: [Mplex], config: {
connEncryption: [NOISE], dht: {
dht: KadDHT enabled: true,
}, kBucketSize: 20
config: { }
dht: {
enabled: true
} }
} }
})
await node.start() super(defaultsDeep(_options, defaults))
return node }
} }
;(async () => { function createNode (callback) {
const [node1, node2, node3] = await Promise.all([ let node
createNode(),
createNode(),
createNode()
])
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs) waterfall([
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs) (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))
}
await Promise.all([ parallel([
node1.dial(node2.peerId), (cb) => createNode(cb),
node2.dial(node3.peerId) (cb) => createNode(cb),
]) (cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
// Wait for onConnect handlers in the DHT const node1 = nodes[0]
await delay(100) const node2 = nodes[1]
const node3 = nodes[2]
const cid = CID.parse('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL') parallel([
await node1.contentRouting.provide(cid) (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 }
console.log('Node %s is providing %s', node1.peerId.toB58String(), cid.toString()) const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
// wait for propagation node1.contentRouting.provide(cid, (err) => {
await delay(300) if (err) { throw err }
const providers = await all(node3.contentRouting.findProviders(cid, { timeout: 3000 })) console.log('Node %s is providing %s', node1.peerInfo.id.toB58String(), cid.toBaseEncodedString())
console.log('Found provider:', providers[0].id.toB58String()) node3.contentRouting.findProviders(cid, 5000, (err, providers) => {
})(); if (err) { throw err }
console.log('Found provider:', providers[0].id.toB58String())
})
})
})
})

View File

@ -10,30 +10,31 @@ 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). 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 config to support Peer Routing and Content Routing. First, let's update our bundle to support Peer Routing and Content Routing.
```JavaScript ```JavaScript
const Libp2p = require('libp2p') class MyBundle extends libp2p {
const KadDHT = require('libp2p-kad-dht') constructor (_options) {
const defaults = {
const node = await Libp2p.create({ modules: {
addresses: { transport: [ TCP ],
listen: ['/ip4/0.0.0.0/tcp/0'] streamMuxer: [ Mplex ],
}, connEncryption: [ SECIO ],
modules: { // we add the DHT module that will enable Peer and Content Routing
transport: [ TCP ], dht: KadDHT
streamMuxer: [ Mplex ], },
connEncryption: [ NOISE ], config: {
// we add the DHT module that will enable Peer and Content Routing dht: {
dht: KadDHT // dht must be enabled
}, enabled: true,
config: { kBucketSize: 20
dht: { }
// dht must be enabled }
enabled: true
} }
super(defaultsDeep(_options, defaults))
} }
}) }
``` ```
Once that is done, we can use the createNode function we developed in the previous example to create 3 nodes. Connect node 1 to node 2 and node 2 to node 3. We will use node 2 as a way to find the whereabouts of node 3 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
@ -43,21 +44,22 @@ const node1 = nodes[0]
const node2 = nodes[1] const node2 = nodes[1]
const node3 = nodes[2] const node3 = nodes[2]
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs) parallel([
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs) (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 }
await Promise.all([ //
node1.dial(node2.peerId), node1.peerRouting.findPeer(node3.peerInfo.id, (err, peer) => {
node2.dial(node3.peerId) if (err) { throw err }
])
// Set up of the cons might take time console.log('Found it, multiaddrs are:')
await delay(100) peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
})
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: You should see the output being something like:
@ -65,8 +67,8 @@ You should see the output being something like:
```Bash ```Bash
> node 1.js > node 1.js
Found it, multiaddrs are: Found it, multiaddrs are:
/ip4/127.0.0.1/tcp/63617 /ip4/127.0.0.1/tcp/63617/p2p/QmWrFXvZr9S4iDqycyoyc2zDdrT1jg9wpdenUTdd1LTar6
/ip4/192.168.86.41/tcp/63617 /ip4/192.168.86.41/tcp/63617/p2p/QmWrFXvZr9S4iDqycyoyc2zDdrT1jg9wpdenUTdd1LTar6
``` ```
You have successfully used Peer Routing to find a peer that you were not directly connected. Now all you have to do is to dial to the multiaddrs you discovered. 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.
@ -80,12 +82,17 @@ 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`. Instead of calling `peerRouting.findPeer`, we will use `contentRouting.provide` and `contentRouting.findProviders`.
```JavaScript ```JavaScript
await node1.contentRouting.provide(cid) node1.contentRouting.provide(cid, (err) => {
console.log('Node %s is providing %s', node1.peerId.toB58String(), cid.toString()) if (err) { throw err }
const provs = await all(node3.contentRouting.findProviders(cid, { timeout: 5000 })) console.log('Node %s is providing %s', node1.peerInfo.id.toB58String(), cid.toBaseEncodedString())
console.log('Found provider:', providers[0].id.toB58String()) node3.contentRouting.findProviders(cid, 5000, (err, providers) => {
if (err) { throw err }
console.log('Found provider:', providers[0].id.toB58String())
})
})
``` ```
The output of your program should look like: The output of your program should look like:

View File

@ -1,36 +0,0 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pWaitFor = require('p-wait-for')
const { toString: uint8ArrayToString } = require('uint8arrays/to-string')
async function test() {
process.stdout.write('1.js\n')
const addrs = []
let foundIt = false
const proc = execa('node', [path.join(__dirname, '1.js')], {
cwd: path.resolve(__dirname),
all: true
})
proc.all.on('data', async (data) => {
process.stdout.write(data)
const line = uint8ArrayToString(data)
// Discovered peer
if (!foundIt && line.includes('Found it, multiaddrs are:')) {
foundIt = true
}
addrs.push(line)
})
await pWaitFor(() => addrs.length === 2)
proc.kill()
}
module.exports = test

View File

@ -1,40 +0,0 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pDefer = require('p-defer')
const { toString: uint8ArrayToString } = require('uint8arrays/to-string')
const providedCopy = 'is providing'
const foundCopy = 'Found provider:'
async function test() {
process.stdout.write('2.js\n')
const providedDefer = pDefer()
const foundDefer = pDefer()
const proc = execa('node', [path.join(__dirname, '2.js')], {
cwd: path.resolve(__dirname),
all: true
})
proc.all.on('data', async (data) => {
process.stdout.write(data)
const line = uint8ArrayToString(data)
if (line.includes(providedCopy)) {
providedDefer.resolve()
} else if (line.includes(foundCopy)) {
foundDefer.resolve()
}
})
await Promise.all([
providedDefer.promise,
foundDefer.promise
])
proc.kill()
}
module.exports = test

View File

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

View File

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

145
examples/pnet-ipfs/index.js Normal file
View File

@ -0,0 +1,145 @@
/* eslint no-console: ["off"] */
'use strict'
const IPFS = require('ipfs')
const assert = require('assert').strict
const writeKey = require('libp2p-pnet').generate
const path = require('path')
const fs = require('fs')
const privateLibp2pBundle = require('./libp2p-bundle')
const { mkdirp } = require('./utils')
// Create two separate repo paths so we can run two nodes and check their output
const repo1 = path.resolve('./tmp', 'repo1', '.ipfs')
const repo2 = path.resolve('./tmp', 'repo2', '.ipfs')
mkdirp(repo1)
mkdirp(repo2)
// Create a buffer and write the swarm key to it
const swarmKey = Buffer.alloc(95)
writeKey(swarmKey)
// This key is for the `TASK` mentioned in the writeFileSync calls below
const otherSwarmKey = Buffer.alloc(95)
writeKey(otherSwarmKey)
// Add the swarm key to both repos
const swarmKey1Path = path.resolve(repo1, 'swarm.key')
const swarmKey2Path = path.resolve(repo2, 'swarm.key')
fs.writeFileSync(swarmKey1Path, swarmKey)
// TASK: switch the commented out line below so we're using a different key, to see the nodes fail to connect
fs.writeFileSync(swarmKey2Path, swarmKey)
// fs.writeFileSync(swarmKey2Path, otherSwarmKey)
// Create the first ipfs node
const node1 = new IPFS({
repo: repo1,
libp2p: privateLibp2pBundle(swarmKey1Path),
config: {
Addresses: {
// Set the swarm address so we dont get port collision on the nodes
Swarm: ['/ip4/0.0.0.0/tcp/9101']
}
}
})
// Create the second ipfs node
const node2 = new IPFS({
repo: repo2,
libp2p: privateLibp2pBundle(swarmKey2Path),
config: {
Addresses: {
// Set the swarm address so we dont get port collision on the nodes
Swarm: ['/ip4/0.0.0.0/tcp/9102']
}
}
})
console.log('auto starting the nodes...')
// `nodesStarted` keeps track of how many of our nodes have started
let nodesStarted = 0
/**
* Calls `connectAndTalk` when both nodes have started
* @returns {void}
*/
const didStartHandler = () => {
if (++nodesStarted === 2) {
// If both nodes are up, start talking
connectAndTalk()
}
}
/**
* Exits the process when all started nodes have stopped
* @returns {void}
*/
const didStopHandler = () => {
if (--nodesStarted < 1) {
console.log('all nodes stopped, exiting.')
process.exit(0)
}
}
/**
* Stops the running nodes
* @param {Error} err An optional error to log to the console
* @returns {void}
*/
const doStop = (err) => {
if (err) {
console.error(err)
}
console.log('Shutting down...')
node1.stop()
node2.stop()
}
/**
* Connects the IPFS nodes and transfers data between them
* @returns {void}
*/
const connectAndTalk = async () => {
console.log('connecting the nodes...')
const node2Id = await node2.id()
const dataToAdd = Buffer.from('Hello, private friend!')
// Connect the nodes
// This will error when different private keys are used
try {
await node1.swarm.connect(node2Id.addresses[0])
} catch (err) {
return doStop(err)
}
console.log('the nodes are connected, let\'s add some data')
// Add some data to node 1
let addedCID
try {
addedCID = await node1.files.add(dataToAdd)
} catch (err) {
return doStop(err)
}
console.log(`added ${addedCID[0].path} to the node1`)
// Retrieve the data from node 2
let cattedData
try {
cattedData = await node2.files.cat(addedCID[0].path)
} catch (err) {
return doStop(err)
}
assert.deepEqual(cattedData.toString(), dataToAdd.toString(), 'Should have equal data')
console.log(`successfully retrieved "${dataToAdd.toString()}" from node2`)
doStop()
}
// Wait for the nodes to boot
node1.once('start', didStartHandler)
node2.once('start', didStartHandler)
// Listen for the nodes stopping so we can cleanup
node1.once('stop', didStopHandler)
node2.once('stop', didStopHandler)

View File

@ -0,0 +1,60 @@
'use strict'
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const fs = require('fs')
const Protector = require('libp2p-pnet')
/**
* Options for the libp2p bundle
* @typedef {Object} libp2pBundle~options
* @property {PeerInfo} peerInfo - The PeerInfo of the IPFS node
* @property {PeerBook} peerBook - The PeerBook of the IPFS node
* @property {Object} config - The config of the IPFS node
* @property {Object} options - The options given to the IPFS node
*/
/**
* privateLibp2pBundle returns a libp2p bundle function that will use the swarm
* key at the given `swarmKeyPath` to create the Protector
*
* @param {string} swarmKeyPath The path to our swarm key
* @returns {libp2pBundle} Returns a libp2pBundle function for use in IPFS creation
*/
const privateLibp2pBundle = (swarmKeyPath) => {
/**
* This is the bundle we will use to create our fully customized libp2p bundle.
*
* @param {libp2pBundle~options} opts The options to use when generating the libp2p node
* @returns {Libp2p} Our new libp2p node
*/
const libp2pBundle = (opts) => {
// Set convenience variables to clearly showcase some of the useful things that are available
const peerInfo = opts.peerInfo
const peerBook = opts.peerBook
// Build and return our libp2p node
return new Libp2p({
peerInfo,
peerBook,
modules: {
transport: [TCP], // We're only using the TCP transport for this example
streamMuxer: [MPLEX], // We're only using mplex muxing
// Let's make sure to use identifying crypto in our pnet since the protector doesn't
// care about node identity, and only the presence of private keys
connEncryption: [SECIO],
// 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(fs.readFileSync(swarmKeyPath))
}
})
}
return libp2pBundle
}
module.exports = privateLibp2pBundle

View File

@ -0,0 +1,21 @@
{
"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": {
"ipfs": "~0.32.3",
"libp2p": "~0.23.1",
"libp2p-mplex": "~0.8.2",
"libp2p-pnet": "../../",
"libp2p-secio": "~0.10.0",
"libp2p-tcp": "~0.13.0"
}
}

View File

@ -1,52 +0,0 @@
/* eslint no-console: ["off"] */
'use strict'
const { generate } = require('libp2p/src/pnet')
const privateLibp2pNode = require('./libp2p-node')
const pipe = require('it-pipe')
// Create a Uint8Array and write the swarm key to it
const swarmKey = new Uint8Array(95)
generate(swarmKey)
// This key is for testing a different key not working
const otherSwarmKey = new Uint8Array(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
)
})()

View File

@ -1,38 +0,0 @@
'use strict'
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('@chainsafe/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 {Uint8Array} 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],
// 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

View File

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

View File

@ -1,80 +1,102 @@
'use strict' 'use strict'
const Libp2p = require('../../') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const PeerInfo = require('peer-info')
const { NOISE } = require('@chainsafe/libp2p-noise') const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
const pull = require('pull-stream')
const defaultsDeep = require('@nodeutils/defaults-deep')
const pipe = require('it-pipe') class MyBundle extends libp2p {
constructor (_options) {
const createNode = async () => { const defaults = {
const node = await Libp2p.create({ modules: {
addresses: { transport: [ TCP ]
listen: ['/ip4/0.0.0.0/tcp/0'] }
},
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
} }
})
await node.start() super(defaultsDeep(_options, defaults))
}
return node
} }
;(async () => { function createNode (callback) {
const [node1, node2] = await Promise.all([ let node
createNode(),
createNode()
])
// Add node's 2 data to the PeerStore waterfall([
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs) (cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => callback(err, node))
}
parallel([
(cb) => createNode(cb),
(cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
const node1 = nodes[0]
const node2 = nodes[1]
// exact matching // exact matching
node2.handle('/your-protocol', ({ stream }) => { node2.handle('/your-protocol', (protocol, conn) => {
pipe( pull(
stream, conn,
async function (source) { pull.map((v) => v.toString()),
for await (const msg of source) { pull.log()
console.log(msg.toString())
}
}
) )
}) })
// multiple protocols // semver matching
/* /*
node2.handle(['/another-protocol/1.0.0', '/another-protocol/2.0.0'], ({ protocol, stream }) => { node2.handle('/another-protocol/1.0.1', (protocol, conn) => {
if (protocol === '/another-protocol/2.0.0') { pull(
// handle backwards compatibility 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)
} }
pipe(
stream,
async function (source) {
for await (const msg of source) {
console.log(msg.toString())
}
}
)
}) })
*/ */
const { stream } = await node1.dialProtocol(node2.peerId, ['/your-protocol']) node1.dialProtocol(node2.peerInfo, '/your-protocol', (err, conn) => {
await pipe( if (err) { throw err }
['my own protocol, wow!'], pull(pull.values(['my own protocol, wow!']), conn)
stream })
)
/* /*
const { stream } = node1.dialProtocol(node2.peerId, ['/another-protocol/1.0.0']) node1.dialProtocol(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
if (err) { throw err }
await pipe( pull(pull.values(['semver me please']), conn)
['my own protocol, wow!'], })
stream
)
*/ */
})();
/*
node1.dialProtocol(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => {
if (err) { throw err }
pull(pull.values(['do I fall into your criteria?']), conn)
})
*/
})

View File

@ -1,64 +1,83 @@
'use strict' 'use strict'
const Libp2p = require('../../') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const SPDY = require('libp2p-spdy')
const { NOISE } = require('@chainsafe/libp2p-noise') 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 pipe = require('it-pipe') class MyBundle extends libp2p {
constructor (_options) {
const createNode = async () => { const defaults = {
const node = await Libp2p.create({ modules: {
addresses: { transport: [ TCP ],
listen: ['/ip4/0.0.0.0/tcp/0'] streamMuxer: [ SPDY ]
}, }
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
} }
})
await node.start() super(defaultsDeep(_options, defaults))
}
return node
} }
;(async () => { function createNode (callback) {
const [node1, node2] = await Promise.all([ let node
createNode(),
createNode()
])
// Add node's 2 data to the PeerStore waterfall([
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs) (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))
}
node2.handle(['/a', '/b'], ({ protocol, stream }) => { parallel([
pipe( (cb) => createNode(cb),
stream, (cb) => createNode(cb)
async function (source) { ], (err, nodes) => {
for await (const msg of source) { if (err) { throw err }
console.log(`from: ${protocol}, msg: ${msg.toString()}`)
} const node1 = nodes[0]
} const node2 = nodes[1]
node2.handle('/a', (protocol, conn) => {
pull(
conn,
pull.map((v) => v.toString()),
pull.log()
) )
}) })
const { stream: stream1 } = await node1.dialProtocol(node2.peerId, ['/a']) node2.handle('/b', (protocol, conn) => {
await pipe( pull(
['protocol (a)'], conn,
stream1 pull.map((v) => v.toString()),
) pull.log()
)
})
const { stream: stream2 } = await node1.dialProtocol(node2.peerId, ['/b']) series([
await pipe( (cb) => node1.dialProtocol(node2.peerInfo, '/a', (err, conn) => {
['protocol (b)'], if (err) { throw err }
stream2 pull(pull.values(['protocol (a)']), conn)
) cb()
}),
const { stream: stream3 } = await node1.dialProtocol(node2.peerId, ['/b']) (cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
await pipe( if (err) { throw err }
['another stream on protocol (b)'], pull(pull.values(['protocol (b)']), conn)
stream3 cb()
) }),
})(); (cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
if (err) { throw err }
pull(pull.values(['another conn on protocol (b)']), conn)
cb()
})
])
})

View File

@ -1,70 +1,88 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
'use strict' 'use strict'
const Libp2p = require('../../') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const SPDY = require('libp2p-spdy')
const { NOISE } = require('@chainsafe/libp2p-noise') 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 pipe = require('it-pipe') class MyBundle extends libp2p {
constructor (_options) {
const createNode = async () => { const defaults = {
const node = await Libp2p.create({ modules: {
addresses: { transport: [ TCP ],
listen: ['/ip4/0.0.0.0/tcp/0'] streamMuxer: [ SPDY ]
}, }
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE]
} }
})
await node.start() super(defaultsDeep(_options, defaults))
}
return node
} }
;(async () => { function createNode (callback) {
const [node1, node2] = await Promise.all([ let node
createNode(),
createNode()
])
// Add node's 2 data to the PeerStore waterfall([
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs) (cb) => PeerInfo.create(cb),
(peerInfo, cb) => {
node1.handle('/node-1', ({ stream }) => { peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
pipe( node = new MyBundle({
stream, peerInfo
async function (source) { })
for await (const msg of source) { node.start(cb)
console.log(msg.toString()) }
} ], (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', ({ stream }) => { node2.handle('/node-2', (protocol, conn) => {
pipe( pull(
stream, conn,
async function (source) { pull.map((v) => v.toString()),
for await (const msg of source) { pull.log()
console.log(msg.toString())
}
}
) )
}) })
const { stream: stream1 } = await node1.dialProtocol(node2.peerId, ['/node-2']) series([
await pipe( (cb) => node1.dialProtocol(node2.peerInfo, '/node-2', (err, conn) => {
['from 1 to 2'], if (err) { throw err }
stream1 pull(pull.values(['from 1 to 2']), conn)
) cb()
}),
const { stream: stream2 } = await node2.dialProtocol(node1.peerId, ['/node-1']) (cb) => node2.dialProtocol(node1.peerInfo, '/node-1', (err, conn) => {
await pipe( if (err) { throw err }
['from 2 to 1'], pull(pull.values(['from 2 to 1']), conn)
stream2 cb()
) })
})(); ], (err) => {
if (err) { throw err }
console.log('Addresses by which both peers are connected')
node1.peerBook
.getAllArray()
.forEach((peer) => console.log('node 1 to node 2:', peer.isConnected().toString()))
node2.peerBook
.getAllArray()
.forEach((peer) => console.log('node 2 to node 1:', peer.isConnected().toString()))
})
})

View File

@ -6,33 +6,23 @@ The feature of agreeing on a protocol over an established connection is what we
# 1. Handle multiple protocols # 1. Handle multiple protocols
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). Let's see _protocol multiplexing_ in action! You will need the following modules for this example: `libp2p`, `libp2p-tcp`, `peer-info`, `async` and `pull-stream`. This example reuses the base left by the [Transports](../transports) example. You can see the complete solution at [1.js](./1.js).
After creating the nodes, we need to tell libp2p which protocols to handle. After creating the nodes, we need to tell libp2p which protocols to handle.
```JavaScript ```JavaScript
const pipe = require('it-pipe')
const { map } = require('streaming-iterables')
const { toBuffer } = require('it-buffer')
// ... // ...
const node1 = nodes[0] const node1 = nodes[0]
const node2 = nodes[1] const node2 = nodes[1]
// Add node's 2 data to the PeerStore // Here we are telling libp2p that is someone dials this node to talk with the `/your-protocol`
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs) // multicodec, the protocol identifier, please call this callback and give it the connection
// 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 // so that incomming data can be handled
node2.handle('/your-protocol', ({ stream }) => { node2.handle('/your-protocol', (protocol, conn) => {
pipe( pull(
stream, conn,
source => (async function () { pull.map((v) => v.toString()),
for await (const msg of source) { pull.log()
console.log(msg.toString())
}
})()
) )
}) })
``` ```
@ -40,54 +30,53 @@ node2.handle('/your-protocol', ({ stream }) => {
After the protocol is _handled_, now we can dial to it. After the protocol is _handled_, now we can dial to it.
```JavaScript ```JavaScript
const { stream } = await node1.dialProtocol(node2.peerId, ['/your-protocol']) node1.dialProtocol(node2.peerInfo, '/your-protocol', (err, conn) => {
if (err) { throw err }
await pipe( pull(pull.values(['my own protocol, wow!']), conn)
['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. 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 ```JavaScript
node2.handle('/another-protocol/1.0.1', ({ stream }) => { node2.handle('/another-protocol/1.0.1', (protocol, conn) => {
pipe( pull(
stream, conn,
async function (source) { pull.map((v) => v.toString()),
for await (const msg of source) { pull.log()
console.log(msg.toString())
}
}
) )
}) })
// ... // ...
const { stream } = await node1.dialProtocol(node2.peerId, ['/another-protocol/1.0.0']) node1.dialProtocol(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
if (err) { throw err }
await pipe( pull(pull.values(['semver me please']), conn)
['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. 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 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 There is still one last feature, you can create your custom match functions.
```JavaScript ```JavaScript
node2.handle(['/another-protocol/1.0.0', '/another-protocol/2.0.0'], ({ protocol, stream }) => { node2.handle('/custom-match-func', (protocol, conn) => {
if (protocol === '/another-protocol/2.0.0') { pull(
// handle backwards compatibility conn,
} pull.map((v) => v.toString()),
pull.log()
pipe(
stream,
async function (source) {
for await (const msg of source) {
console.log(msg.toString())
}
}
) )
}, (myProtocol, requestedProtocol, callback) => {
// This is all custom. I'm checking the base path matches, think of this
// as a HTTP routing table.
if (myProtocol.indexOf(requestedProtocol)) {
callback(null, true)
} else {
callback(null, false)
}
})
// ...
node1.dialProtocol(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => {
if (err) { throw err }
pull(pull.values(['do I fall into your criteria?']), conn)
}) })
``` ```
@ -95,68 +84,77 @@ Try all of this out by executing [1.js](./1.js).
# 2. Reuse existing connection # 2. Reuse existing connection
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. The example above would require a node to create a whole new connection for every time it dials in one of the protocols, this is a waste of resources and also it might be simply not possible (e.g lack of file descriptors, not enough ports being open, etc). What we really want is to dial a connection once and then multiplex several virtual connections (stream) over a single connection, this is where _stream multiplexing_ comes into play.
Stream multiplexing is 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. Stream multiplexing is a old concept, in fact it happens in many of the layers of the [OSI System](https://en.wikipedia.org/wiki/OSI_model), in libp2p we make this feature to our avail by letting the user pick which module for stream multiplexing to use.
Currently, we have [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. Currently, we have two available [libp2p-spdy](https://github.com/libp2p/js-libp2p-spdy) and [libp2p-mplex](https://github.com/libp2p/js-libp2p-mplex) and pluging them in is as easy as adding another transport. Let's revisit our libp2p bundle.
```JavaScript ```JavaScript
const Libp2p = require('libp2p') const SPDY = require('libp2p-spdy')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
//... //...
class MyBundle extends libp2p {
const createNode = () => { constructor (_options) {
return Libp2p.create({ const defaults = {
modules: { modules: {
transport: [ TCP ], transport: [ TCP ],
streamMuxer: [ Mplex ] // Here we are adding the SPDY muxer to our libp2p bundle.
// Thanks to protocol muxing, a libp2p bundle can support multiple Stream Muxers at the same
// time and pick the right one when dialing to a node
streamMuxer: [ SPDY ]
}
} }
})
super(defaultsDeep(_options, defaults))
}
} }
``` ```
With this, we can dial as many times as we want to a peer and always reuse the same established underlying connection. With this, we can dial as many times as we want to a peer and always reuse the same established underlying connection.
```JavaScript ```JavaScript
node2.handle(['/a', '/b'], ({ protocol, stream }) => { node2.handle('/a', (protocol, conn) => {
pipe( pull(
stream, conn,
async function (source) { pull.map((v) => v.toString()),
for await (const msg of source) { pull.log()
console.log(`from: ${protocol}, msg: ${msg.toString()}`)
}
}
) )
}) })
const { stream } = await node1.dialProtocol(node2.peerId, ['/a']) node2.handle('/b', (protocol, conn) => {
await pipe( pull(
['protocol (a)'], conn,
stream pull.map((v) => v.toString()),
) pull.log()
)
})
const { stream: stream2 } = await node1.dialProtocol(node2.peerId, ['/b']) series([
await pipe( (cb) => node1.dialProtocol(node2.peerInfo, '/a', (err, conn) => {
['protocol (b)'], if (err) { throw err }
stream2 pull(pull.values(['protocol (a)']), conn)
) cb()
}),
const { stream: stream3 } = await node1.dialProtocol(node2.peerId, ['/b']) (cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
await pipe( if (err) { throw err }
['another stream on protocol (b)'], pull(pull.values(['protocol (b)']), conn)
stream3 cb()
) }),
(cb) => node1.dialProtocol(node2.peerInfo, '/b', (err, conn) => {
if (err) { throw err }
pull(pull.values(['another conn on protocol (b)']), conn)
cb()
})
])
``` ```
By running [2.js](./2.js) you should see the following result: By running [2.js](./2.js) you should see the following result:
``` ```
> node 2.js > node 2.js
from: /a, msg: protocol (a) protocol (a)
from: /b, msg: protocol (b) protocol (b)
from: /b, msg: another stream on protocol (b) another protocol (b)
``` ```
# 3. Bidirectional connections # 3. Bidirectional connections
@ -170,5 +168,8 @@ You can see this working on example [3.js](./3.js). The result should look like
```Bash ```Bash
> node 3.js > node 3.js
from 1 to 2 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 from 2 to 1
``` ```

View File

@ -1,31 +0,0 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pDefer = require('p-defer')
const { toString: uint8ArrayToString } = require('uint8arrays/to-string')
async function test() {
const messageDefer = pDefer()
process.stdout.write('1.js\n')
const proc = execa('node', [path.join(__dirname, '1.js')], {
cwd: path.resolve(__dirname),
all: true
})
proc.all.on('data', async (data) => {
process.stdout.write(data)
const line = uint8ArrayToString(data)
if (line.includes('my own protocol, wow!')) {
messageDefer.resolve()
}
})
await messageDefer.promise
proc.kill()
}
module.exports = test

View File

@ -1,38 +0,0 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pWaitFor = require('p-wait-for')
const { toString: uint8ArrayToString } = require('uint8arrays/to-string')
const messages = [
'protocol (a)',
'protocol (b)',
'another stream on protocol (b)'
]
async function test() {
process.stdout.write('2.js\n')
let count = 0
const proc = execa('node', [path.join(__dirname, '2.js')], {
cwd: path.resolve(__dirname),
all: true
})
proc.all.on('data', async (data) => {
process.stdout.write(data)
const line = uint8ArrayToString(data)
if (messages.find((m) => line.includes(m))) {
count += 1
}
})
await pWaitFor(() => count === messages.length)
proc.kill()
}
module.exports = test

View File

@ -1,37 +0,0 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pWaitFor = require('p-wait-for')
const { toString: uint8ArrayToString } = require('uint8arrays/to-string')
const messages = [
'from 1 to 2',
'from 2 to 1'
]
async function test() {
process.stdout.write('3.js\n')
let count = 0
const proc = execa('node', [path.join(__dirname, '3.js')], {
cwd: path.resolve(__dirname),
all: true
})
proc.all.on('data', async (data) => {
process.stdout.write(data)
const line = uint8ArrayToString(data)
if (messages.find((m) => line.includes(m))) {
count += 1
}
})
await pWaitFor(() => count === messages.length)
proc.kill()
}
module.exports = test

View File

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

View File

@ -1,56 +1,103 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
'use strict' 'use strict'
const Libp2p = require('../../') const libp2p = require('../../')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex') const Mplex = require('libp2p-mplex')
const { NOISE } = require('@chainsafe/libp2p-noise') const SECIO = require('libp2p-secio')
const PeerInfo = require('peer-info')
const MulticastDNS = require('libp2p-mdns')
const Gossipsub = require('libp2p-gossipsub') const Gossipsub = require('libp2p-gossipsub')
const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string') const defaultsDeep = require('@nodeutils/defaults-deep')
const { toString: uint8ArrayToString } = require('uint8arrays/to-string') const waterfall = require('async/waterfall')
const parallel = require('async/parallel')
const series = require('async/series')
const createNode = async () => { class MyBundle extends libp2p {
const node = await Libp2p.create({ constructor (_options) {
addresses: { const defaults = {
listen: ['/ip4/0.0.0.0/tcp/0'] modules: {
}, transport: [ TCP ],
modules: { streamMuxer: [ Mplex ],
transport: [TCP], connEncryption: [ SECIO ],
streamMuxer: [Mplex], peerDiscovery: [ MulticastDNS ],
connEncryption: [NOISE], pubsub: Gossipsub
pubsub: Gossipsub },
config: {
peerDiscovery: {
mdns: {
interval: 2000,
enabled: true
}
},
pubsub: {
enabled: true,
emitSelf: true
}
}
} }
})
await node.start() super(defaultsDeep(_options, defaults))
return node }
} }
;(async () => { function createNode (callback) {
const topic = 'news' let node
const [node1, node2] = await Promise.all([ waterfall([
createNode(), (cb) => PeerInfo.create(cb),
createNode() (peerInfo, cb) => {
]) peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
node = new MyBundle({
peerInfo
})
node.start(cb)
}
], (err) => callback(err, node))
}
// Add node's 2 data to the PeerStore parallel([
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs) (cb) => createNode(cb),
await node1.dial(node2.peerId) (cb) => createNode(cb)
], (err, nodes) => {
if (err) { throw err }
node1.pubsub.on(topic, (msg) => { const node1 = nodes[0]
console.log(`node1 received: ${uint8ArrayToString(msg.data)}`) const node2 = nodes[1]
node1.once('peer:connect', (peer) => {
console.log('connected to %s', peer.id.toB58String())
series([
// node1 subscribes to "news"
(cb) => node1.pubsub.subscribe(
'news',
(msg) => console.log(`node1 received: ${msg.data.toString()}`),
cb
),
(cb) => setTimeout(cb, 500),
// node2 subscribes to "news"
(cb) => node2.pubsub.subscribe(
'news',
(msg) => console.log(`node2 received: ${msg.data.toString()}`),
cb
),
(cb) => setTimeout(cb, 500),
// node2 publishes "news" every second
(cb) => {
setInterval(() => {
node2.pubsub.publish(
'news',
Buffer.from('Bird bird bird, bird is the word!'),
(err) => {
if (err) { throw err }
}
)
}, 1000)
cb()
},
], (err) => {
if (err) { throw err }
})
}) })
await node1.pubsub.subscribe(topic) })
// Will not receive own published messages by default
node2.pubsub.on(topic, (msg) => {
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`)
})
await node2.pubsub.subscribe(topic)
// node2 publishes "news" every second
setInterval(() => {
node2.pubsub.publish(topic, uint8ArrayFromString('Bird bird bird, bird is the word!'))
}, 1000)
})()

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