Compare commits

..

3 Commits

Author SHA1 Message Date
5e5cf2d31a refactor: cleanup keychain usage 2020-08-05 18:13:13 +02:00
a3ea2f4550 chore: bump crypto 2020-08-05 17:21:03 +02:00
a71fe96aaf feat(keychain): add support for ed25519 and secp keys 2020-08-04 16:32:06 +02:00
134 changed files with 1044 additions and 3818 deletions

View File

@ -45,15 +45,9 @@ const after = async () => {
} }
module.exports = { module.exports = {
bundlesize: { maxSize: '225kB' }, bundlesize: { maxSize: '202kB' },
hooks: { hooks: {
pre: before, pre: before,
post: after post: after
},
webpack: {
node: {
// needed by bcrypto
Buffer: true
}
} }
} }

View File

@ -6,8 +6,8 @@ stages:
- cov - cov
node_js: node_js:
- 'lts/*' - '10'
- '14' - '12'
os: os:
- linux - linux

View File

@ -1,224 +1,3 @@
<a name="0.29.3"></a>
## [0.29.3](https://github.com/libp2p/js-libp2p/compare/v0.29.2...v0.29.3) (2020-11-04)
### Features
* resolve multiaddrs before dial ([#782](https://github.com/libp2p/js-libp2p/issues/782)) ([093c0ea](https://github.com/libp2p/js-libp2p/commit/093c0ea))
<a name="0.29.2"></a>
## [0.29.2](https://github.com/libp2p/js-libp2p/compare/v0.29.1...v0.29.2) (2020-10-23)
### Bug Fixes
* cleanup open streams on conn close ([#791](https://github.com/libp2p/js-libp2p/issues/791)) ([06f26e5](https://github.com/libp2p/js-libp2p/commit/06f26e5))
<a name="0.29.1"></a>
## [0.29.1](https://github.com/libp2p/js-libp2p/compare/v0.29.0...v0.29.1) (2020-10-22)
### Bug Fixes
* catch error in upgrader close call ([e04224a](https://github.com/libp2p/js-libp2p/commit/e04224a))
* ensure streams are closed on connection close ([4c6be91](https://github.com/libp2p/js-libp2p/commit/4c6be91))
* flakey identify test firefox ([#774](https://github.com/libp2p/js-libp2p/issues/774)) ([60d437f](https://github.com/libp2p/js-libp2p/commit/60d437f))
<a name="0.29.0"></a>
# [0.29.0](https://github.com/libp2p/js-libp2p/compare/v0.28.10...v0.29.0) (2020-08-27)
### Bug Fixes
* do not return self on peerstore.peers ([15613cc](https://github.com/libp2p/js-libp2p/commit/15613cc))
* peer record interop with go ([#739](https://github.com/libp2p/js-libp2p/issues/739)) ([93dda74](https://github.com/libp2p/js-libp2p/commit/93dda74))
* replace node buffers with uint8arrays ([#730](https://github.com/libp2p/js-libp2p/issues/730)) ([1e86971](https://github.com/libp2p/js-libp2p/commit/1e86971))
* revert new identify protocol versions ([3158366](https://github.com/libp2p/js-libp2p/commit/3158366))
* signature compliant with spec ([4ab125e](https://github.com/libp2p/js-libp2p/commit/4ab125e))
### Chores
* update travis to use node lts and stable ([098f3d1](https://github.com/libp2p/js-libp2p/commit/098f3d1))
### Features
* cerified addressbook ([8f2e690](https://github.com/libp2p/js-libp2p/commit/8f2e690))
* create self peer record in identify ([8a97dde](https://github.com/libp2p/js-libp2p/commit/8a97dde))
* exchange signed peer records in identify ([e50f0ee](https://github.com/libp2p/js-libp2p/commit/e50f0ee))
* gossipsub 1.1 ([#733](https://github.com/libp2p/js-libp2p/issues/733)) ([55c9bfa](https://github.com/libp2p/js-libp2p/commit/55c9bfa))
* signed peer records record manager ([3e5d450](https://github.com/libp2p/js-libp2p/commit/3e5d450))
### Reverts
* reapply "fix: throw if no conn encryption module provided ([#665](https://github.com/libp2p/js-libp2p/issues/665))" ([689f90a](https://github.com/libp2p/js-libp2p/commit/689f90a))
### BREAKING CHANGES
* pubsub implementation is now directly exposed and its API was updated according to the new pubsub interface in js-libp2p-interfaces repo
* chore: use gossipsub branch with src added
* fix: add pubsub handlers adapter
* chore: fix deps
* chore: update pubsub docs and examples
* chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* chore: use new floodsub
* chore: change validator doc set
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* chore: add new gossipsub src
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* - All deps used by this module now use Uint8Arrays in place of node Buffers
* chore: browser fixes
* chore: remove .only
* chore: stringify uint8array before parsing
* chore: update interop suite
* chore: remove ts from build command
* chore: update deps
* fix: update records to use uint8array
* chore: fix lint
* chore: update deps
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* this drops testing support in node 10.
<a name="0.29.0-rc.1"></a>
# [0.29.0-rc.1](https://github.com/libp2p/js-libp2p/compare/v0.29.0-rc.0...v0.29.0-rc.1) (2020-08-27)
### Bug Fixes
* peer record interop with go ([#739](https://github.com/libp2p/js-libp2p/issues/739)) ([c4c7ef9](https://github.com/libp2p/js-libp2p/commit/c4c7ef9))
<a name="0.29.0-rc.0"></a>
# [0.29.0-rc.0](https://github.com/libp2p/js-libp2p/compare/v0.28.10...v0.29.0-rc.0) (2020-08-25)
### Bug Fixes
* do not return self on peerstore.peers ([e1b8edc](https://github.com/libp2p/js-libp2p/commit/e1b8edc))
* replace node buffers with uint8arrays ([#730](https://github.com/libp2p/js-libp2p/issues/730)) ([507f8c4](https://github.com/libp2p/js-libp2p/commit/507f8c4))
* revert new identify protocol versions ([a798c65](https://github.com/libp2p/js-libp2p/commit/a798c65))
* signature compliant with spec ([97b5d2a](https://github.com/libp2p/js-libp2p/commit/97b5d2a))
### Chores
* update travis to use node lts and stable ([c272288](https://github.com/libp2p/js-libp2p/commit/c272288))
### Features
* cerified addressbook ([e0ed258](https://github.com/libp2p/js-libp2p/commit/e0ed258))
* create self peer record in identify ([83922a7](https://github.com/libp2p/js-libp2p/commit/83922a7))
* exchange signed peer records in identify ([f835457](https://github.com/libp2p/js-libp2p/commit/f835457))
* gossipsub 1.1 ([#733](https://github.com/libp2p/js-libp2p/issues/733)) ([e14ce40](https://github.com/libp2p/js-libp2p/commit/e14ce40))
* signed peer records record manager ([f95edf1](https://github.com/libp2p/js-libp2p/commit/f95edf1))
### Reverts
* reapply "fix: throw if no conn encryption module provided ([#665](https://github.com/libp2p/js-libp2p/issues/665))" ([ad7f02e](https://github.com/libp2p/js-libp2p/commit/ad7f02e))
### BREAKING CHANGES
* pubsub implementation is now directly exposed and its API was updated according to the new pubsub interface in js-libp2p-interfaces repo
* chore: use gossipsub branch with src added
* fix: add pubsub handlers adapter
* chore: fix deps
* chore: update pubsub docs and examples
* chore: apply suggestions from code review
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* chore: use new floodsub
* chore: change validator doc set
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* chore: add new gossipsub src
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* - All deps used by this module now use Uint8Arrays in place of node Buffers
* chore: browser fixes
* chore: remove .only
* chore: stringify uint8array before parsing
* chore: update interop suite
* chore: remove ts from build command
* chore: update deps
* fix: update records to use uint8array
* chore: fix lint
* chore: update deps
Co-authored-by: Jacob Heun <jacobheun@gmail.com>
* this drops testing support in node 10.
<a name="0.28.10"></a>
## [0.28.10](https://github.com/libp2p/js-libp2p/compare/v0.28.9...v0.28.10) (2020-08-05)
### Bug Fixes
* allow certain keychain operations without a password ([#726](https://github.com/libp2p/js-libp2p/issues/726)) ([8c56ec0](https://github.com/libp2p/js-libp2p/commit/8c56ec0))
* **identify:** make agentversion dynamic and add it to the peerstore ([#724](https://github.com/libp2p/js-libp2p/issues/724)) ([726a746](https://github.com/libp2p/js-libp2p/commit/726a746))
### Features
* **keychain:** add support for ed25519 and secp keys ([#725](https://github.com/libp2p/js-libp2p/issues/725)) ([51d7ca4](https://github.com/libp2p/js-libp2p/commit/51d7ca4))
<a name="0.28.9"></a> <a name="0.28.9"></a>
## [0.28.9](https://github.com/libp2p/js-libp2p/compare/v0.28.8...v0.28.9) (2020-07-27) ## [0.28.9](https://github.com/libp2p/js-libp2p/compare/v0.28.8...v0.28.9) (2020-07-27)

View File

@ -23,8 +23,8 @@
<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>
@ -168,6 +168,7 @@ List of packages currently in existence for libp2p
| **data types** | | **data types** |
| [`peer-id`](//github.com/libp2p/js-peer-id) | [![npm](https://img.shields.io/npm/v/peer-id.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-peer-id/releases) | [![Deps](https://david-dm.org/libp2p/js-peer-id.svg?style=flat-square)](https://david-dm.org/libp2p/js-peer-id) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-peer-id/master)](https://travis-ci.com/libp2p/js-peer-id) | [![codecov](https://codecov.io/gh/libp2p/js-peer-id/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-peer-id) | [Vasco Santos](mailto:santos.vasco10@gmail.com) | | [`peer-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) |
| **pubsub** | | **pubsub** |
| [`libp2p-pubsub`](//github.com/libp2p/js-libp2p-pubsub) | [![npm](https://img.shields.io/npm/v/libp2p-pubsub.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-pubsub/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-pubsub.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-pubsub) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-pubsub/master)](https://travis-ci.com/libp2p/js-libp2p-pubsub) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-pubsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-pubsub) | [Vasco Santos](mailto:santos.vasco10@gmail.com) |
| [`libp2p-floodsub`](//github.com/libp2p/js-libp2p-floodsub) | [![npm](https://img.shields.io/npm/v/libp2p-floodsub.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-floodsub/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-floodsub.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-floodsub) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-floodsub/master)](https://travis-ci.com/libp2p/js-libp2p-floodsub) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-floodsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-floodsub) | [Vasco Santos](mailto:vasco.santos@moxy.studio) | | [`libp2p-floodsub`](//github.com/libp2p/js-libp2p-floodsub) | [![npm](https://img.shields.io/npm/v/libp2p-floodsub.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-floodsub/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-floodsub.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-floodsub) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-floodsub/master)](https://travis-ci.com/libp2p/js-libp2p-floodsub) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-floodsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-floodsub) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| [`libp2p-gossipsub`](//github.com/ChainSafe/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-gossipsub`](//github.com/ChainSafe/js-libp2p-gossipsub) | [![npm](https://img.shields.io/npm/v/libp2p-gossipsub.svg?maxAge=86400&style=flat-square)](//github.com/ChainSafe/js-libp2p-gossipsub/releases) | [![Deps](https://david-dm.org/ChainSafe/js-libp2p-gossipsub.svg?style=flat-square)](https://david-dm.org/ChainSafe/js-libp2p-gossipsub) | [![Travis CI](https://flat.badgen.net/travis/ChainSafe/js-libp2p-gossipsub/master)](https://travis-ci.com/ChainSafe/js-libp2p-gossipsub) | [![codecov](https://codecov.io/gh/ChainSafe/js-libp2p-gossipsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ChainSafe/js-libp2p-gossipsub) | [Cayman Nava](mailto:caymannava@gmail.com) |
| **extensions** | | **extensions** |

View File

@ -26,7 +26,6 @@
- Documentation - Documentation
- [ ] Ensure that README.md is up to date - [ ] Ensure that README.md is up to date
- [ ] Ensure that all the examples run - [ ] Ensure that all the examples run
- [ ] Ensure [libp2p/js-libp2p-examples](https://github.com/libp2p/js-libp2p-examples) is updated
- [ ] Ensure that [libp2p/docs](https://github.com/libp2p/docs) is updated - [ ] Ensure that [libp2p/docs](https://github.com/libp2p/docs) is updated
- Communication - Communication
- [ ] Create the release issue - [ ] Create the release issue

View File

@ -2,7 +2,7 @@
* [Static Functions](#static-functions) * [Static Functions](#static-functions)
* [`create`](#create) * [`create`](#create)
* [Instance Methods](#libp2p-instance-methods) * [Instance Methods](#instance-methods)
* [`start`](#start) * [`start`](#start)
* [`stop`](#stop) * [`stop`](#stop)
* [`dial`](#dial) * [`dial`](#dial)
@ -46,10 +46,6 @@
* [`pubsub.publish`](#pubsubpublish) * [`pubsub.publish`](#pubsubpublish)
* [`pubsub.subscribe`](#pubsubsubscribe) * [`pubsub.subscribe`](#pubsubsubscribe)
* [`pubsub.unsubscribe`](#pubsubunsubscribe) * [`pubsub.unsubscribe`](#pubsubunsubscribe)
* [`pubsub.on`](#pubsubon)
* [`pubsub.removeListener`](#pubsubremovelistener)
* [`pubsub.topicValidators.set`](#pubsubtopicvalidatorsset)
* [`pubsub.topicValidators.delete`](#pubsubtopicvalidatorsdelete)
* [`connectionManager.get`](#connectionmanagerget) * [`connectionManager.get`](#connectionmanagerget)
* [`connectionManager.setPeerValue`](#connectionmanagersetpeervalue) * [`connectionManager.setPeerValue`](#connectionmanagersetpeervalue)
* [`connectionManager.size`](#connectionmanagersize) * [`connectionManager.size`](#connectionmanagersize)
@ -89,17 +85,17 @@ Creates an instance of Libp2p.
| Name | Type | Description | | Name | Type | Description |
|------|------|-------------| |------|------|-------------|
| options | `object` | libp2p options | | options | `object` | libp2p options |
| options.modules | [`Array<object>`](./CONFIGURATION.md#modules) | libp2p [modules](./CONFIGURATION.md#modules) to use | | options.modules | [`Array<object>`](./CONFIGURATION.md#modules) | libp2p modules to use |
| [options.addresses] | `{ listen: Array<string>, announce: Array<string>, noAnnounce: Array<string> }` | Addresses for transport listening and to advertise to the network | | [options.addresses] | `{ listen: Array<string>, announce: Array<string>, noAnnounce: Array<string> }` | Addresses for transport listening and to advertise to the network |
| [options.config] | `object` | libp2p modules configuration and core configuration | | [options.config] | `object` | libp2p modules configuration and core configuration |
| [options.connectionManager] | [`object`](./CONFIGURATION.md#configuring-connection-manager) | libp2p Connection Manager [configuration](./CONFIGURATION.md#configuring-connection-manager) | | [options.connectionManager] | [`object`](./CONFIGURATION.md#configuring-connection-manager) | libp2p Connection Manager configuration |
| [options.transportManager] | [`object`](./CONFIGURATION.md#configuring-transport-manager) | libp2p transport manager [configuration](./CONFIGURATION.md#configuring-transport-manager) | | [options.transportManager] | [`object`](./CONFIGURATION.md#configuring-transport-manager) | libp2p transport manager configuration |
| [options.datastore] | `object` | must implement [ipfs/interface-datastore](https://github.com/ipfs/interface-datastore) (in memory datastore will be used if not provided) | | [options.datastore] | `object` | must implement [ipfs/interface-datastore](https://github.com/ipfs/interface-datastore) (in memory datastore will be used if not provided) |
| [options.dialer] | [`object`](./CONFIGURATION.md#configuring-dialing) | libp2p Dialer [configuration](./CONFIGURATION.md#configuring-dialing) | [options.dialer] | [`object`](./CONFIGURATION.md#configuring-dialing) | libp2p Dialer configuration
| [options.keychain] | [`object`](./CONFIGURATION.md#setup-with-keychain) | keychain [configuration](./CONFIGURATION.md#setup-with-keychain) | | [options.keychain] | [`object`](./CONFIGURATION.md#setup-with-keychain) | keychain configuration |
| [options.metrics] | [`object`](./CONFIGURATION.md#configuring-metrics) | libp2p Metrics [configuration](./CONFIGURATION.md#configuring-metrics) | | [options.metrics] | [`object`](./CONFIGURATION.md#configuring-metrics) | libp2p Metrics configuration
| [options.peerId] | [`PeerId`][peer-id] | peerId instance (it will be created if not provided) | | [options.peerId] | [`PeerId`][peer-id] | peerId instance (it will be created if not provided) |
| [options.peerStore] | [`object`](./CONFIGURATION.md#configuring-peerstore) | libp2p PeerStore [configuration](./CONFIGURATION.md#configuring-peerstore) | | [options.peerStore] | [`object`](./CONFIGURATION.md#configuring-peerstore) | libp2p PeerStore configuration |
For Libp2p configurations and modules details read the [Configuration Document](./CONFIGURATION.md). For Libp2p configurations and modules details read the [Configuration Document](./CONFIGURATION.md).
@ -310,7 +306,7 @@ Dials to another peer in the network and selects a protocol to communicate with
| Type | Description | | Type | Description |
|------|-------------| |------|-------------|
| `Promise<{ stream:*, protocol:string }>` | Promise resolves with a [duplex stream](https://github.com/libp2p/js-libp2p/blob/master/doc/STREAMING_ITERABLES.md#duplex) and the protocol used | | `Promise<{ stream:*, protocol:string }>` | Promise resolves with a [duplex stream](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#duplex-it) and the protocol used |
#### Example #### Example
@ -589,7 +585,7 @@ Writes a value to a key in the DHT.
| Name | Type | Description | | Name | Type | Description |
|------|------|-------------| |------|------|-------------|
| key | `string` | key to add to the dht | | key | `string` | key to add to the dht |
| value | `Uint8Array` | value to add to the dht | | value | `Buffer` | value to add to the dht |
| [options] | `object` | put options | | [options] | `object` | put options |
| [options.minPeers] | `number` | minimum number of peers required to successfully put (default: closestPeers.length) | | [options.minPeers] | `number` | minimum number of peers required to successfully put (default: closestPeers.length) |
@ -604,7 +600,7 @@ Writes a value to a key in the DHT.
```js ```js
// ... // ...
const key = '/key' const key = '/key'
const value = uint8ArrayFromString('oh hello there') const value = Buffer.from('oh hello there')
await libp2p.contentRouting.put(key, value) await libp2p.contentRouting.put(key, value)
``` ```
@ -627,7 +623,7 @@ Queries the DHT for a value stored for a given key.
| Type | Description | | Type | Description |
|------|-------------| |------|-------------|
| `Promise<Uint8Array>` | Value obtained from the DHT | | `Promise<Buffer>` | Value obtained from the DHT |
#### Example #### Example
@ -657,7 +653,7 @@ Queries the DHT for the n values stored for the given key (without sorting).
| Type | Description | | Type | Description |
|------|-------------| |------|-------------|
| `Promise<Array<{from: PeerId, val: Uint8Array}>>` | Array of records obtained from the DHT | | `Promise<Array<{from: PeerId, val: Buffer}>>` | Array of records obtained from the DHT |
#### Example #### Example
@ -818,9 +814,7 @@ peerStore.addressBook.getMultiaddrsForPeer(peerId)
### peerStore.addressBook.set ### peerStore.addressBook.set
Set known `multiaddrs` of a given peer. This will replace previously stored multiaddrs, if available. Set known `multiaddrs` of a given peer.
Replacing stored multiaddrs might result in losing obtained certified addresses, which is not desirable.
Consider using `addressBook.add()` if you're not sure this is what you want to do.
`peerStore.addressBook.set(peerId, multiaddrs)` `peerStore.addressBook.set(peerId, multiaddrs)`
@ -974,7 +968,7 @@ Delete the provided peer from the book.
```js ```js
peerStore.metadataBook.delete(peerId) peerStore.metadataBook.delete(peerId)
// false // false
peerStore.metadataBook.set(peerId, 'nickname', uint8ArrayFromString('homePeer')) peerStore.metadataBook.set(peerId, 'nickname', Buffer.from('homePeer'))
peerStore.metadataBook.delete(peerId) peerStore.metadataBook.delete(peerId)
// true // true
``` ```
@ -1003,7 +997,7 @@ Deletes the provided peer metadata key-value pair from the book.
```js ```js
peerStore.metadataBook.deleteValue(peerId, 'location') peerStore.metadataBook.deleteValue(peerId, 'location')
// false // false
peerStore.metadataBook.set(peerId, 'location', uint8ArrayFromString('Berlin')) peerStore.metadataBook.set(peerId, 'location', Buffer.from('Berlin'))
peerStore.metadataBook.deleteValue(peerId, 'location') peerStore.metadataBook.deleteValue(peerId, 'location')
// true // true
``` ```
@ -1024,14 +1018,14 @@ Get the known metadata of a provided peer.
| Type | Description | | Type | Description |
|------|-------------| |------|-------------|
| `Map<string, Uint8Array>` | Peer Metadata | | `Map<string, Buffer>` | Peer Metadata |
#### Example #### Example
```js ```js
peerStore.metadataBook.get(peerId) peerStore.metadataBook.get(peerId)
// undefined // undefined
peerStore.metadataBook.set(peerId, 'location', uint8ArrayFromString('Berlin')) peerStore.metadataBook.set(peerId, 'location', Buffer.from('Berlin'))
peerStore.metadataBook.get(peerId) peerStore.metadataBook.get(peerId)
// Metadata Map // Metadata Map
``` ```
@ -1053,14 +1047,14 @@ Get specific metadata of a provided peer.
| Type | Description | | Type | Description |
|------|-------------| |------|-------------|
| `Map<string, Uint8Array>` | Peer Metadata | | `Map<string, Buffer>` | Peer Metadata |
#### Example #### Example
```js ```js
peerStore.metadataBook.getValue(peerId, 'location') peerStore.metadataBook.getValue(peerId, 'location')
// undefined // undefined
peerStore.metadataBook.set(peerId, 'location', uint8ArrayFromString('Berlin')) peerStore.metadataBook.set(peerId, 'location', Buffer.from('Berlin'))
peerStore.metadataBook.getValue(peerId, 'location') peerStore.metadataBook.getValue(peerId, 'location')
// Metadata Map // Metadata Map
``` ```
@ -1077,7 +1071,7 @@ Set known metadata of a given `peerId`.
|------|------|-------------| |------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to set | | peerId | [`PeerId`][peer-id] | peerId to set |
| key | `string` | key of the metadata value to store | | key | `string` | key of the metadata value to store |
| value | `Uint8Array` | metadata value to store | | value | `Buffer` | metadata value to store |
#### Returns #### Returns
@ -1088,7 +1082,7 @@ Set known metadata of a given `peerId`.
#### Example #### Example
```js ```js
peerStore.metadataBook.set(peerId, 'location', uint8ArrayFromString('Berlin')) peerStore.metadataBook.set(peerId, 'location', Buffer.from('Berlin'))
``` ```
### peerStore.protoBook.delete ### peerStore.protoBook.delete
@ -1222,7 +1216,7 @@ Get the stored information of a given peer, namely its [`PeerId`][peer-id], know
| Type | Description | | Type | Description |
|------|-------------| |------|-------------|
| `{ id: PeerId, addresses: Array<Address>, metadata: Map<string, Buffer>}, protocols: Array<string> }` | Peer information of the provided peer | | `{ id: PeerId, addresses: Array<Address>, protocols: Array<string> }` | Peer information of the provided peer |
#### Example #### Example
@ -1249,13 +1243,13 @@ Get all the stored information of every peer.
| Type | Description | | Type | Description |
|------|-------------| |------|-------------|
| `Map<string, { id: PeerId, addresses: Array<Address>, metadata: Map<string, Buffer>}, protocols: Array<string> }>` | Peer data of every peer known | | `Map<string, { id: PeerId, addresses: Array<Address>, protocols: Array<string> }>` | Peer data of every peer known |
#### Example #### Example
```js ```js
for (let [peerIdString, peer] of peerStore.peers.entries()) { for (let [peerIdString, peer] of peerStore.peers.entries()) {
// peer { id, addresses, metadata, protocols } // peer { id, addresses, protocols }
} }
``` ```
@ -1312,7 +1306,7 @@ Publishes messages to the given topics.
| Name | Type | Description | | Name | Type | Description |
|------|------|-------------| |------|------|-------------|
| topic | `string` | topic to publish | | topic | `string` | topic to publish |
| data | `Uint8Array` | data to publish | | data | `Buffer` | data to publish |
#### Returns #### Returns
@ -1324,22 +1318,23 @@ Publishes messages to the given topics.
```js ```js
const topic = 'topic' const topic = 'topic'
const data = uint8ArrayFromString('data') const data = Buffer.from('data')
await libp2p.pubsub.publish(topic, data) await libp2p.pubsub.publish(topic, data)
``` ```
### pubsub.subscribe ### pubsub.subscribe
Subscribes to a pubsub topic. Subscribes the given handler to a pubsub topic.
`libp2p.pubsub.subscribe(topic)` `libp2p.pubsub.subscribe(topic, handler)`
#### Parameters #### Parameters
| Name | Type | Description | | Name | Type | Description |
|------|------|-------------| |------|------|-------------|
| topic | `string` | topic to subscribe | | topic | `string` | topic to subscribe |
| handler | `function({ from: string, data: Buffer, seqno: Buffer, topicIDs: Array<string>, signature: Buffer, key: Buffer })` | handler for new data on topic |
#### Returns #### Returns
@ -1355,21 +1350,21 @@ const handler = (msg) => {
// msg.data - pubsub data received // msg.data - pubsub data received
} }
libp2p.pubsub.on(topic, handler) libp2p.pubsub.subscribe(topic, handler)
libp2p.pubsub.subscribe(topic)
``` ```
### pubsub.unsubscribe ### pubsub.unsubscribe
Unsubscribes from a pubsub topic. Unsubscribes the given handler from a pubsub topic. If no handler is provided, all handlers for the topic are removed.
`libp2p.pubsub.unsubscribe(topic)` `libp2p.pubsub.unsubscribe(topic, handler)`
#### Parameters #### Parameters
| Name | Type | Description | | Name | Type | Description |
|------|------|-------------| |------|------|-------------|
| topic | `string` | topic to unsubscribe | | topic | `string` | topic to unsubscribe |
| handler | `function(<object>)` | handler subscribed |
#### Returns #### Returns
@ -1385,129 +1380,7 @@ const handler = (msg) => {
// msg.data - pubsub data received // msg.data - pubsub data received
} }
libp2p.pubsub.removeListener(topic handler) libp2p.pubsub.unsubscribe(topic, handler)
libp2p.pubsub.unsubscribe(topic)
```
## pubsub.on
A Pubsub router is an [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) and uses its events for pubsub message handlers.
`libp2p.pubsub.on(topic, handler)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| topic | `string` | topic to listen |
| handler | `function({ from: string, data: Uint8Array, seqno: Uint8Array, topicIDs: Array<string>, signature: Uint8Array, key: Uint8Array })` | handler for new data on topic |
#### Returns
| Type | Description |
|------|-------------|
| `void` | |
#### Example
```js
const topic = 'topic'
const handler = (msg) => {
// msg.data - pubsub data received
}
libp2p.pubsub.on(topic, handler)
libp2p.pubsub.subscribe(topic)
```
## pubsub.removeListener
A Pubsub router is an [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) and uses its events for pubsub message handlers.
`libp2p.pubsub.removeListener(topic, handler)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| topic | `string` | topic to remove listener |
| handler | `function({ from: string, data: Uint8Array, seqno: Uint8Array, topicIDs: Array<string>, signature: Uint8Array, key: Uint8Array })` | handler for new data on topic |
#### Returns
| Type | Description |
|------|-------------|
| `void` | |
#### Example
```js
const topic = 'topic'
const handler = (msg) => {
// msg.data - pubsub data received
}
libp2p.pubsub.removeListener(topic handler)
libp2p.pubsub.unsubscribe(topic)
```
## pubsub.topicValidators.set
Pubsub routers support message validators per topic, which will validate the message before its propagations. Set is used to specify a validator for a topic.
`libp2p.pubsub.topicValidators.set(topic, validator)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| topic | `string` | topic to bind a validator |
| handler | `function({ topic: string, msg: RPC })` | validator for new data on topic |
#### Returns
| Type | Description |
|------|-------------|
| `Map<string, function(string, RPC)>` | The `Map` object |
#### Example
```js
const topic = 'topic'
const validateMessage = (msgTopic, msg) => {
const input = uint8ArrayToString(msg.data)
const validInputs = ['a', 'b', 'c']
if (!validInputs.includes(input)) {
throw new Error('no valid input received')
}
}
libp2p.pubsub.topicValidators.set(topic, validateMessage)
```
## pubsub.topicValidators.delete
Pubsub routers support message validators per topic, which will validate the message before its propagations. Delete is used to remove a validator for a topic.
`libp2p.pubsub.topicValidators.delete(topic)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| topic | `string` | topic to remove a validator |
#### Returns
| Type | Description |
|------|-------------|
| `boolean` | `true` if an element in the Map object existed and has been removed, or `false` if the element does not exist. |
#### Example
```js
const topic = 'topic'
libp2p.pubsub.topicValidators.delete(topic)
``` ```
### connectionManager.get ### connectionManager.get
@ -1804,19 +1677,19 @@ Encrypt protected data using the Cryptographic Message Syntax (CMS).
| Name | Type | Description | | Name | Type | Description |
|------|------|-------------| |------|------|-------------|
| name | `string` | The local key name. | | name | `string` | The local key name. |
| data | `Uint8Array` | The data to encrypt. | | data | `Buffer` | The data to encrypt. |
#### Returns #### Returns
| Type | Description | | Type | Description |
|------|-------------| |------|-------------|
| `Promise<Uint8Array>` | Encrypted data as a PKCS #7 message in DER. | | `Promise<Buffer>` | Encrypted data as a PKCS #7 message in DER. |
#### Example #### Example
```js ```js
const keyInfo = await libp2p.keychain.createKey('keyTest', 'rsa', 4096) const keyInfo = await libp2p.keychain.createKey('keyTest', 'rsa', 4096)
const enc = await libp2p.keychain.cms.encrypt('keyTest', uint8ArrayFromString('data')) const enc = await libp2p.keychain.cms.encrypt('keyTest', Buffer.from('data'))
``` ```
### keychain.cms.decrypt ### keychain.cms.decrypt
@ -1836,13 +1709,13 @@ The keychain must contain one of the keys used to encrypt the data. If none of
| Type | Description | | Type | Description |
|------|-------------| |------|-------------|
| `Promise<Uint8Array>` | Decrypted data. | | `Promise<Buffer>` | Decrypted data. |
#### Example #### Example
```js ```js
const keyInfo = await libp2p.keychain.createKey('keyTest', 'rsa', 4096) const keyInfo = await libp2p.keychain.createKey('keyTest', 'rsa', 4096)
const enc = await libp2p.keychain.cms.encrypt('keyTest', uint8ArrayFromString('data')) const enc = await libp2p.keychain.cms.encrypt('keyTest', Buffer.from('data'))
const decData = await libp2p.keychain.cms.decrypt(enc) const decData = await libp2p.keychain.cms.decrypt(enc)
``` ```

View File

@ -52,7 +52,7 @@ The libp2p ecosystem contains at least one module for each of these subsystems.
After selecting the modules to use, it is also possible to configure each one according to your needs. 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. Bear in mind that only a **transport** and **connection encryption** are required, while all the other subsystems are optional.
### Transport ### Transport
@ -98,7 +98,7 @@ If you want to know more about libp2p stream multiplexing, you should read the f
Some available connection encryption protocols: Some available connection encryption protocols:
- [NodeFactoryIo/js-libp2p-noise](https://github.com/NodeFactoryIo/js-libp2p-noise) - [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) - [libp2p/js-libp2p-secio](https://github.com/libp2p/js-libp2p-secio)
If none of the available connection encryption mechanisms fulfills your needs, you can create a libp2p compatible one. A libp2p connection encryption protocol just needs to be compliant with the [Crypto Interface](https://github.com/libp2p/js-interfaces/tree/master/src/crypto). If 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).
@ -223,7 +223,7 @@ Besides the `modules` and `config`, libp2p allows other internal options and con
// Creating a libp2p node with: // Creating a libp2p node with:
// transport: websockets + tcp // transport: websockets + tcp
// stream-muxing: mplex // stream-muxing: mplex
// crypto-channel: noise // crypto-channel: secio
// discovery: multicast-dns // discovery: multicast-dns
// dht: kad-dht // dht: kad-dht
// pubsub: gossipsub // pubsub: gossipsub
@ -232,7 +232,7 @@ const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets') const WS = require('libp2p-websockets')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const MulticastDNS = require('libp2p-mdns') const MulticastDNS = require('libp2p-mdns')
const DHT = require('libp2p-kad-dht') const DHT = require('libp2p-kad-dht')
const GossipSub = require('libp2p-gossipsub') const GossipSub = require('libp2p-gossipsub')
@ -244,7 +244,7 @@ const node = await Libp2p.create({
new WS() // It can take instances too! new WS() // It can take instances too!
], ],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE], connEncryption: [SECIO],
peerDiscovery: [MulticastDNS], peerDiscovery: [MulticastDNS],
dht: DHT, dht: DHT,
pubsub: GossipSub pubsub: GossipSub
@ -258,14 +258,14 @@ const node = await Libp2p.create({
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const MulticastDNS = require('libp2p-mdns') const MulticastDNS = require('libp2p-mdns')
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE], connEncryption: [SECIO],
peerDiscovery: [MulticastDNS] peerDiscovery: [MulticastDNS]
}, },
config: { config: {
@ -291,7 +291,7 @@ const Libp2p = require('libp2p')
const WS = require('libp2p-websockets') const WS = require('libp2p-websockets')
const WebRTCStar = require('libp2p-webrtc-star') const WebRTCStar = require('libp2p-webrtc-star')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
@ -300,7 +300,7 @@ const node = await Libp2p.create({
WebRTCStar WebRTCStar
], ],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE], connEncryption: [SECIO],
}, },
config: { config: {
peerDiscovery: { peerDiscovery: {
@ -318,14 +318,14 @@ const node = await Libp2p.create({
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const GossipSub = require('libp2p-gossipsub') const GossipSub = require('libp2p-gossipsub')
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE], connEncryption: [SECIO],
pubsub: GossipSub pubsub: GossipSub
}, },
config: { config: {
@ -345,14 +345,14 @@ const node = await Libp2p.create({
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const DHT = require('libp2p-kad-dht') const DHT = require('libp2p-kad-dht')
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE], connEncryption: [SECIO],
dht: DHT dht: DHT
}, },
config: { config: {
@ -375,7 +375,7 @@ const node = await Libp2p.create({
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing') const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
const DelegatedContentRouter = require('libp2p-delegated-content-routing') const DelegatedContentRouter = require('libp2p-delegated-content-routing')
const PeerId = require('peer-id') const PeerId = require('peer-id')
@ -387,7 +387,7 @@ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE], connEncryption: [SECIO],
contentRouting: [ contentRouting: [
new DelegatedContentRouter(peerId) new DelegatedContentRouter(peerId)
], ],
@ -405,13 +405,13 @@ const node = await Libp2p.create({
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [SECIO]
}, },
config: { config: {
relay: { // Circuit Relay options (this config is part of libp2p core configurations) relay: { // Circuit Relay options (this config is part of libp2p core configurations)
@ -438,14 +438,14 @@ Libp2p allows you to setup a secure keychain to manage your keys. The keychain c
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const LevelStore = require('datastore-level') const LevelStore = require('datastore-level')
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [SECIO]
}, },
keychain: { keychain: {
pass: 'notsafepassword123456789', pass: 'notsafepassword123456789',
@ -465,7 +465,6 @@ Dialing in libp2p can be configured to limit the rate of dialing, and how long d
| maxParallelDials | `number` | How many multiaddrs we can dial in parallel. | | maxParallelDials | `number` | How many multiaddrs we can dial in parallel. |
| maxDialsPerPeer | `number` | How many multiaddrs we can dial per peer, in parallel. | | maxDialsPerPeer | `number` | How many multiaddrs we can dial per peer, in parallel. |
| dialTimeout | `number` | Second dial timeout per peer in ms. | | 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 |
The below configuration example shows how the dialer should be configured, with the current defaults: The below configuration example shows how the dialer should be configured, with the current defaults:
@ -473,23 +472,18 @@ The below configuration example shows how the dialer should be configured, with
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const { dnsaddrResolver } = require('multiaddr/src/resolvers')
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [SECIO]
}, },
dialer: { dialer: {
maxParallelDials: 100, maxParallelDials: 100,
maxDialsPerPeer: 4, maxDialsPerPeer: 4,
dialTimeout: 30e3, dialTimeout: 30e3
resolvers: {
dnsaddr: dnsaddrResolver
}
} }
``` ```
@ -501,13 +495,13 @@ The Connection Manager prunes Connections in libp2p whenever certain limits are
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [SECIO]
}, },
connectionManager: { connectionManager: {
maxConnections: Infinity, maxConnections: Infinity,
@ -532,7 +526,7 @@ The Transport Manager is responsible for managing the libp2p transports life cyc
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const { FaultTolerance } = require('libp2p/src/transport-manager')} const { FaultTolerance } = require('libp2p/src/transport-manager')}
@ -540,7 +534,7 @@ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [SECIO]
}, },
transportManager: { transportManager: {
faultTolerance: FaultTolerance.NO_FATAL faultTolerance: FaultTolerance.NO_FATAL
@ -566,13 +560,13 @@ The below configuration example shows how the metrics should be configured. Asid
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [SECIO]
}, },
metrics: { metrics: {
enabled: true, enabled: true,
@ -605,7 +599,7 @@ The below configuration example shows how the PeerStore should be configured. As
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const LevelStore = require('datastore-level') const LevelStore = require('datastore-level')
@ -613,7 +607,7 @@ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [SECIO]
}, },
datastore: new LevelStore('path/to/store'), datastore: new LevelStore('path/to/store'),
peerStore: { peerStore: {
@ -631,7 +625,7 @@ Some Transports can be passed additional options when they are created. For exam
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const WebRTCStar = require('libp2p-webrtc-star') const WebRTCStar = require('libp2p-webrtc-star')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const wrtc = require('wrtc') const wrtc = require('wrtc')
const transportKey = WebRTCStar.prototype[Symbol.toStringTag] const transportKey = WebRTCStar.prototype[Symbol.toStringTag]
@ -639,7 +633,7 @@ const node = await Libp2p.create({
modules: { modules: {
transport: [WebRTCStar], transport: [WebRTCStar],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [SECIO]
}, },
config: { config: {
transport: { transport: {

View File

@ -112,13 +112,13 @@ npm install libp2p-mplex
```js ```js
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const WebSockets = require('libp2p-websockets') const WebSockets = require('libp2p-websockets')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
transport: [WebSockets], transport: [WebSockets],
connEncryption: [NOISE], connEncryption: [SECIO],
streamMuxer: [MPLEX] streamMuxer: [MPLEX]
} }
}) })
@ -139,7 +139,7 @@ Now that you have configured a [**Transport**][transport], [**Crypto**][crypto]
```js ```js
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const WebSockets = require('libp2p-websockets') const WebSockets = require('libp2p-websockets')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const node = await Libp2p.create({ const node = await Libp2p.create({
@ -148,7 +148,7 @@ const node = await Libp2p.create({
}, },
modules: { modules: {
transport: [WebSockets], transport: [WebSockets],
connEncryption: [NOISE], connEncryption: [SECIO],
streamMuxer: [MPLEX] streamMuxer: [MPLEX]
} }
}) })
@ -197,21 +197,21 @@ We can provide specific configurations for each protocol within a `config.peerDi
```js ```js
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const WebSockets = require('libp2p-websockets') const WebSockets = require('libp2p-websockets')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const Bootstrap = require('libp2p-bootstrap') const Bootstrap = require('libp2p-bootstrap')
// Known peers addresses // Known peers addresses
const bootstrapMultiaddrs = [ const bootstrapMultiaddrs = [
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', '/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN' '/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3'
] ]
const node = await Libp2p.create({ const node = await Libp2p.create({
modules: { modules: {
transport: [WebSockets], transport: [WebSockets],
connEncryption: [NOISE], connEncryption: [SECIO],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
peerDiscovery: [Bootstrap] peerDiscovery: [Bootstrap]
}, },
@ -232,9 +232,9 @@ node.on('peer:discovery', (peer) => {
console.log('Discovered %s', peer.id.toB58String()) // Log discovered peer console.log('Discovered %s', peer.id.toB58String()) // Log discovered peer
}) })
node.connectionManager.on('peer:connect', (connection) => { node.on('peer:connect', (peer) => {
console.log('Connected to %s', connection.remotePeer.toB58String()) // Log connected peer console.log('Connected to %s', peer.id.toB58String()) // Log connected peer
}) })
// start libp2p // start libp2p
await node.start() await node.start()

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,7 +0,0 @@
# Circuit Relay
> Circuit Switching for libp2p, also known as TURN or Relay in Networking literature.
If you are looking for information about libp2p's circuit relay, you should read its [spec](https://github.com/libp2p/specs/tree/master/relay).

View File

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

View File

@ -1,38 +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
* [Production](#production)
* [Star servers](#star-servers)
* [Delegate nodes](#delegate-nodes)
* [Circuit Relay](#circuit-relay)
## `webrtc-star` servers
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. Regarding `webRTC`, at the time of writing 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 set of delegated nodes 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.
## Delegate nodes
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 libp2p delegate nodes, nodes with limitations in the network can leverage them to perform peer and content routing calls.
You can read on how to setup your own set of delegated nodes in [DELEGATE_NODES.md](./DELEGATE_NODES.md).
## 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 read on how to setup your own set of delegated nodes in [CIRCUIT_RELAY.md](./CIRCUIT_RELAY.md).

View File

@ -3,6 +3,7 @@
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets') const WS = require('libp2p-websockets')
const mplex = require('libp2p-mplex') const mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const defaultsDeep = require('@nodeutils/defaults-deep') const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../..') const libp2p = require('../../..')
@ -16,7 +17,7 @@ class Node extends libp2p {
WS WS
], ],
streamMuxer: [ mplex ], streamMuxer: [ mplex ],
connEncryption: [ NOISE ] connEncryption: [ NOISE, SECIO ]
} }
} }

View File

@ -1,6 +1,7 @@
'use strict' 'use strict'
/* eslint-disable no-console */ /* eslint-disable no-console */
const multaddr = require('multiaddr')
const PeerId = require('peer-id') const PeerId = require('peer-id')
const Node = require('./libp2p-bundle.js') const Node = require('./libp2p-bundle.js')
const { stdinToStream, streamToConsole } = require('./stream') const { stdinToStream, streamToConsole } = require('./stream')

View File

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

View File

@ -4,6 +4,7 @@
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 SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const Bootstrap = require('libp2p-bootstrap') const Bootstrap = require('libp2p-bootstrap')
@ -28,7 +29,7 @@ const bootstrapers = [
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [Mplex], streamMuxer: [Mplex],
connEncryption: [NOISE], connEncryption: [NOISE, SECIO],
peerDiscovery: [Bootstrap] peerDiscovery: [Bootstrap]
}, },
config: { config: {

View File

@ -4,6 +4,7 @@
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 SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const MulticastDNS = require('libp2p-mdns') const MulticastDNS = require('libp2p-mdns')
@ -15,12 +16,12 @@ const createNode = async () => {
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [Mplex], streamMuxer: [Mplex],
connEncryption: [NOISE], connEncryption: [NOISE, SECIO],
peerDiscovery: [MulticastDNS] peerDiscovery: [MulticastDNS]
}, },
config: { config: {
peerDiscovery: { peerDiscovery: {
[MulticastDNS.tag]: { mdns: {
interval: 20e3, interval: 20e3,
enabled: true enabled: true
} }

View File

@ -8,7 +8,7 @@ These mechanisms save configuration and enable a node to operate without any exp
## 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 node.
@ -20,7 +20,7 @@ const node = Libp2p.create({
modules: { modules: {
transport: [ TCP ], transport: [ TCP ],
streamMuxer: [ Mplex ], streamMuxer: [ Mplex ],
connEncryption: [ NOISE ], connEncryption: [ NOISE, SECIO ],
peerDiscovery: [ Bootstrap ] peerDiscovery: [ Bootstrap ]
}, },
config: { config: {
@ -62,7 +62,7 @@ const node = await Libp2p.create({
modules: { modules: {
transport: [ TCP ], transport: [ TCP ],
streamMuxer: [ Mplex ], streamMuxer: [ Mplex ],
connEncryption: [ NOISE ], connEncryption: [ NOISE, SECIO ],
peerDiscovery: [ Bootstrap ] peerDiscovery: [ Bootstrap ]
}, },
config: { config: {
@ -130,7 +130,7 @@ const createNode = () => {
modules: { modules: {
transport: [ TCP ], transport: [ TCP ],
streamMuxer: [ Mplex ], streamMuxer: [ Mplex ],
connEncryption: [ NOISE ], connEncryption: [ NOISE, SECIO ],
peerDiscovery: [ MulticastDNS ] peerDiscovery: [ MulticastDNS ]
}, },
config: { config: {

View File

@ -3,6 +3,7 @@
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets') const WS = require('libp2p-websockets')
const mplex = require('libp2p-mplex') const mplex = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const defaultsDeep = require('@nodeutils/defaults-deep') const defaultsDeep = require('@nodeutils/defaults-deep')
@ -17,7 +18,7 @@ class Node extends libp2p {
WS WS
], ],
streamMuxer: [ mplex ], streamMuxer: [ mplex ],
connEncryption: [ NOISE ] connEncryption: [ NOISE, SECIO ]
} }
} }

View File

@ -4,6 +4,7 @@ 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('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const pipe = require('it-pipe') const pipe = require('it-pipe')
@ -15,7 +16,7 @@ const createNode = async () => {
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [Mplex], streamMuxer: [Mplex],
connEncryption: [NOISE] connEncryption: [NOISE, SECIO]
} }
}) })

View File

@ -8,24 +8,31 @@ A byproduct of having these encrypted communications modules is that we can auth
# 1. Set up encrypted communications # 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 `libp2p-noise` module to complete it, go ahead and `npm install libp2p-noise`. We will build this example on top of example for [Protocol and Stream Multiplexing](../protocol-and-stream-multiplexing). You will need the modules `libp2p-secio`<sup>*</sup> and `libp2p-noise` to complete it, go ahead and `npm install libp2p-secio libp2p-noise`.
To add them to your libp2p configuration, all you have to do is: To add them to your libp2p configuration, all you have to do is:
```JavaScript ```JavaScript
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const createNode = () => { const createNode = () => {
return Libp2p.create({ return Libp2p.create({
modules: { modules: {
transport: [ TCP ], transport: [ TCP ],
streamMuxer: [ Mplex ], streamMuxer: [ Mplex ],
// Attach noise as the crypto channel to use // Attach secio as the crypto channel to use
connEncryption: [ NOISE ] connEncryption: [ NOISE, SECIO ]
} }
}) })
} }
``` ```
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. And that's it, from now on, all your libp2p communications are encrypted. Try running the example [1.js](./1.js) to see it working.
_<sup>*</sup> SECIO is the crypto channel developed for IPFS, it is a TLS 1.3 like crypto channel that established an encrypted communication channel between two peers._
If you want to want to learn more about how SECIO works, you can read the [great write up done by Dominic Tarr](https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel).
Important note: SECIO hasn't been audited and so, we do not recommend to trust its security. We intent to move to TLS 1.3 once the specification is finalized and an implementation exists that we can use.

View File

@ -3,8 +3,9 @@ import Libp2p from 'libp2p'
import Websockets from 'libp2p-websockets' import Websockets from 'libp2p-websockets'
import WebRTCStar from 'libp2p-webrtc-star' import WebRTCStar from 'libp2p-webrtc-star'
import { NOISE } from 'libp2p-noise' import { NOISE } from 'libp2p-noise'
import Secio from 'libp2p-secio'
import Mplex from 'libp2p-mplex' import Mplex from 'libp2p-mplex'
import Bootstrap from 'libp2p-bootstrap' import Boostrap from 'libp2p-bootstrap'
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
// Create our libp2p node // Create our libp2p node
@ -20,22 +21,21 @@ document.addEventListener('DOMContentLoaded', async () => {
}, },
modules: { modules: {
transport: [Websockets, WebRTCStar], transport: [Websockets, WebRTCStar],
connEncryption: [NOISE], connEncryption: [NOISE, Secio],
streamMuxer: [Mplex], streamMuxer: [Mplex],
peerDiscovery: [Bootstrap] peerDiscovery: [Boostrap]
}, },
config: { config: {
peerDiscovery: { peerDiscovery: {
// The `tag` property will be searched when creating the instance of your Peer Discovery service. bootstrap: {
// The associated object, will be passed to the service when it is instantiated.
[Bootstrap.tag]: {
enabled: true, enabled: true,
list: [ list: [
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN', '/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', '/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp', '/dns4/sfo-3.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa', '/dns4/sgp-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt' '/dns4/nyc-1.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/dns4/nyc-2.bootstrap.libp2p.io/tcp/443/wss/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64'
] ]
} }
} }

View File

@ -16,11 +16,12 @@
"dependencies": { "dependencies": {
"@babel/preset-env": "^7.8.3", "@babel/preset-env": "^7.8.3",
"libp2p": "../../", "libp2p": "../../",
"libp2p-bootstrap": "^0.12.1", "libp2p-bootstrap": "^0.11",
"libp2p-mplex": "^0.10.0", "libp2p-mplex": "^0.9.3",
"libp2p-noise": "^2.0.0", "libp2p-noise": "^1.1.0",
"libp2p-webrtc-star": "^0.20.0", "libp2p-secio": "^0.12.2",
"libp2p-websockets": "^0.14.0" "libp2p-webrtc-star": "^0.18.0",
"libp2p-websockets": "^0.13.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.8.3", "@babel/cli": "^7.8.3",

View File

@ -5,6 +5,7 @@ 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('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const KadDHT = require('libp2p-kad-dht') const KadDHT = require('libp2p-kad-dht')
const delay = require('delay') const delay = require('delay')
@ -17,7 +18,7 @@ const createNode = async () => {
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [Mplex], streamMuxer: [Mplex],
connEncryption: [NOISE], connEncryption: [NOISE, SECIO],
dht: KadDHT dht: KadDHT
}, },
config: { config: {

View File

@ -4,6 +4,7 @@
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 SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const CID = require('cids') const CID = require('cids')
const KadDHT = require('libp2p-kad-dht') const KadDHT = require('libp2p-kad-dht')
@ -19,7 +20,7 @@ const createNode = async () => {
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [Mplex], streamMuxer: [Mplex],
connEncryption: [NOISE], connEncryption: [NOISE, SECIO],
dht: KadDHT dht: KadDHT
}, },
config: { config: {

View File

@ -23,7 +23,7 @@ const node = await Libp2p.create({
modules: { modules: {
transport: [ TCP ], transport: [ TCP ],
streamMuxer: [ Mplex ], streamMuxer: [ Mplex ],
connEncryption: [ NOISE ], connEncryption: [ NOISE, SECIO ],
// we add the DHT module that will enable Peer and Content Routing // we add the DHT module that will enable Peer and Content Routing
dht: KadDHT dht: KadDHT
}, },

View File

@ -2,7 +2,11 @@
This example shows how to set up a private network of libp2p nodes. This example shows how to set up a private network of libp2p 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.

View File

@ -1,17 +1,18 @@
/* eslint no-console: ["off"] */ /* eslint no-console: ["off"] */
'use strict' 'use strict'
const { Buffer } = require('buffer')
const { generate } = require('libp2p/src/pnet') const { generate } = require('libp2p/src/pnet')
const privateLibp2pNode = require('./libp2p-node') const privateLibp2pNode = require('./libp2p-node')
const pipe = require('it-pipe') const pipe = require('it-pipe')
// Create a Uint8Array and write the swarm key to it // Create a buffer and write the swarm key to it
const swarmKey = new Uint8Array(95) const swarmKey = Buffer.alloc(95)
generate(swarmKey) generate(swarmKey)
// This key is for testing a different key not working // This key is for testing a different key not working
const otherSwarmKey = new Uint8Array(95) const otherSwarmKey = Buffer.alloc(95)
generate(otherSwarmKey) generate(otherSwarmKey)
;(async () => { ;(async () => {

View File

@ -3,6 +3,7 @@
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const Protector = require('libp2p/src/pnet') const Protector = require('libp2p/src/pnet')
@ -10,7 +11,7 @@ const Protector = require('libp2p/src/pnet')
* privateLibp2pNode returns a libp2p node function that will use the swarm * privateLibp2pNode returns a libp2p node function that will use the swarm
* key with the given `swarmKey` to create the Protector * key with the given `swarmKey` to create the Protector
* *
* @param {Uint8Array} swarmKey * @param {Buffer} swarmKey
* @returns {Promise<libp2p>} Returns a libp2pNode function for use in IPFS creation * @returns {Promise<libp2p>} Returns a libp2pNode function for use in IPFS creation
*/ */
const privateLibp2pNode = async (swarmKey) => { const privateLibp2pNode = async (swarmKey) => {
@ -23,7 +24,7 @@ const privateLibp2pNode = async (swarmKey) => {
streamMuxer: [MPLEX], // We're only using mplex muxing streamMuxer: [MPLEX], // We're only using mplex muxing
// Let's make sure to use identifying crypto in our pnet since the protector doesn't // 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 // care about node identity, and only the presence of private keys
connEncryption: [NOISE], connEncryption: [NOISE, SECIO],
// Leave peer discovery empty, we don't want to find peers. We could omit the property, but it's // 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. // being left in for explicit readability.
// We should explicitly dial pnet peers, or use a custom discovery service for finding nodes in our pnet // We should explicitly dial pnet peers, or use a custom discovery service for finding nodes in our pnet

View File

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

View File

@ -4,6 +4,7 @@ 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('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const pipe = require('it-pipe') const pipe = require('it-pipe')
@ -15,7 +16,7 @@ const createNode = async () => {
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [NOISE, SECIO]
} }
}) })

View File

@ -4,6 +4,7 @@ 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('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const pipe = require('it-pipe') const pipe = require('it-pipe')
@ -15,7 +16,7 @@ const createNode = async () => {
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [NOISE, SECIO]
} }
}) })

View File

@ -5,6 +5,7 @@ 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('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const pipe = require('it-pipe') const pipe = require('it-pipe')
@ -16,7 +17,7 @@ const createNode = async () => {
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [NOISE, SECIO]
} }
}) })

View File

@ -1,13 +1,13 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
'use strict' 'use strict'
const { Buffer } = require('buffer')
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('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const Gossipsub = require('libp2p-gossipsub') const Gossipsub = require('libp2p-gossipsub')
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayToString = require('uint8arrays/to-string')
const createNode = async () => { const createNode = async () => {
const node = await Libp2p.create({ const node = await Libp2p.create({
@ -17,7 +17,7 @@ const createNode = async () => {
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [Mplex], streamMuxer: [Mplex],
connEncryption: [NOISE], connEncryption: [NOISE, SECIO],
pubsub: Gossipsub pubsub: Gossipsub
} }
}) })
@ -38,18 +38,16 @@ const createNode = async () => {
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs) node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
await node1.dial(node2.peerId) await node1.dial(node2.peerId)
node1.pubsub.on(topic, (msg) => { await node1.pubsub.subscribe(topic, (msg) => {
console.log(`node1 received: ${uint8ArrayToString(msg.data)}`) console.log(`node1 received: ${msg.data.toString()}`)
}) })
await node1.pubsub.subscribe(topic)
node2.pubsub.on(topic, (msg) => { await node2.pubsub.subscribe(topic, (msg) => {
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`) console.log(`node2 received: ${msg.data.toString()}`)
}) })
await node2.pubsub.subscribe(topic)
// node2 publishes "news" every second // node2 publishes "news" every second
setInterval(() => { setInterval(() => {
node2.pubsub.publish(topic, uint8ArrayFromString('Bird bird bird, bird is the word!')) node2.pubsub.publish(topic, Buffer.from('Bird bird bird, bird is the word!'))
}, 1000) }, 1000)
})() })()

View File

@ -27,7 +27,7 @@ const node = await Libp2p.create({
modules: { modules: {
transport: [ TCP ], transport: [ TCP ],
streamMuxer: [ Mplex ], streamMuxer: [ Mplex ],
connEncryption: [ NOISE ], connEncryption: [ NOISE, SECIO ],
// we add the Pubsub module we want // we add the Pubsub module we want
pubsub: Gossipsub pubsub: Gossipsub
} }
@ -47,19 +47,17 @@ node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
await node1.dial(node2.peerId) await node1.dial(node2.peerId)
node1.pubsub.on(topic, (msg) => { await node1.pubsub.subscribe(topic, (msg) => {
console.log(`node1 received: ${uint8ArrayToString(msg.data)}`) console.log(`node1 received: ${msg.data.toString()}`)
}) })
await node1.pubsub.subscribe(topic)
node2.pubsub.on(topic, (msg) => { await node2.pubsub.subscribe(topic, (msg) => {
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`) console.log(`node2 received: ${msg.data.toString()}`)
}) })
await node2.pubsub.subscribe(topic)
// node2 publishes "news" every second // node2 publishes "news" every second
setInterval(() => { setInterval(() => {
node2.pubsub.publish(topic, uint8ArrayFromString('Bird bird bird, bird is the word!')) node2.pubsub.publish(topic, Buffer.from('Bird bird bird, bird is the word!'))
}, 1000) }, 1000)
``` ```

View File

@ -1,13 +1,13 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
'use strict' 'use strict'
const { Buffer } = require('buffer')
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('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const Gossipsub = require('libp2p-gossipsub') const Gossipsub = require('libp2p-gossipsub')
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayToString = require('uint8arrays/to-string')
const createNode = async () => { const createNode = async () => {
const node = await Libp2p.create({ const node = await Libp2p.create({
@ -17,7 +17,7 @@ const createNode = async () => {
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [Mplex], streamMuxer: [Mplex],
connEncryption: [NOISE], connEncryption: [NOISE, SECIO],
pubsub: Gossipsub pubsub: Gossipsub
} }
}) })
@ -43,34 +43,29 @@ const createNode = async () => {
await node2.dial(node3.peerId) await node2.dial(node3.peerId)
//subscribe //subscribe
node1.pubsub.on(topic, (msg) => { await node1.pubsub.subscribe(topic, (msg) => {
console.log(`node1 received: ${uint8ArrayToString(msg.data)}`) console.log(`node1 received: ${msg.data.toString()}`)
}) })
await node1.pubsub.subscribe(topic)
node2.pubsub.on(topic, (msg) => { await node2.pubsub.subscribe(topic, (msg) => {
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`) console.log(`node2 received: ${msg.data.toString()}`)
}) })
await node2.pubsub.subscribe(topic)
node3.pubsub.on(topic, (msg) => { await node3.pubsub.subscribe(topic, (msg) => {
console.log(`node3 received: ${uint8ArrayToString(msg.data)}`) console.log(`node3 received: ${msg.data.toString()}`)
}) })
await node3.pubsub.subscribe(topic)
const validateFruit = (msgTopic, msg) => { const validateFruit = (msgTopic, peer, msg) => {
const fruit = uint8ArrayToString(msg.data) const fruit = msg.data.toString();
const validFruit = ['banana', 'apple', 'orange'] const validFruit = ['banana', 'apple', 'orange']
const valid = validFruit.includes(fruit);
if (!validFruit.includes(fruit)) { return valid;
throw new Error('no valid fruit received')
}
} }
//validate fruit //validate fruit
node1.pubsub.topicValidators.set(topic, validateFruit) node1.pubsub._pubsub.topicValidators.set(topic, validateFruit);
node2.pubsub.topicValidators.set(topic, validateFruit) node2.pubsub._pubsub.topicValidators.set(topic, validateFruit);
node3.pubsub.topicValidators.set(topic, validateFruit) node3.pubsub._pubsub.topicValidators.set(topic, validateFruit);
// node1 publishes "fruits" every five seconds // node1 publishes "fruits" every five seconds
var count = 0; var count = 0;
@ -78,7 +73,7 @@ const createNode = async () => {
// car is not a fruit ! // car is not a fruit !
setInterval(() => { setInterval(() => {
console.log('############## fruit ' + myFruits[count] + ' ##############') console.log('############## fruit ' + myFruits[count] + ' ##############')
node1.pubsub.publish(topic, uint8ArrayFromString(myFruits[count])) node1.pubsub.publish(topic, Buffer.from(myFruits[count]))
count++ count++
if (count == myFruits.length) { if (count == myFruits.length) {
count = 0 count = 0

View File

@ -17,7 +17,7 @@ const node = await Libp2p.create({
modules: { modules: {
transport: [ TCP ], transport: [ TCP ],
streamMuxer: [ Mplex ], streamMuxer: [ Mplex ],
connEncryption: [ NOISE ], connEncryption: [ NOISE, SECIO ],
pubsub: Gossipsub pubsub: Gossipsub
} }
}) })
@ -44,36 +44,31 @@ Now we' can subscribe to the fruit topic and log incoming messages.
```JavaScript ```JavaScript
const topic = 'fruit' const topic = 'fruit'
node1.pubsub.on(topic, (msg) => { await node1.pubsub.subscribe(topic, (msg) => {
console.log(`node1 received: ${uint8ArrayToString(msg.data)}`) console.log(`node1 received: ${msg.data.toString()}`)
}) })
await node1.pubsub.subscribe(topic)
node2.pubsub.on(topic, (msg) => { await node2.pubsub.subscribe(topic, (msg) => {
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`) console.log(`node2 received: ${msg.data.toString()}`)
}) })
await node2.pubsub.subscribe(topic)
node3.pubsub.on(topic, (msg) => { await node3.pubsub.subscribe(topic, (msg) => {
console.log(`node3 received: ${uint8ArrayToString(msg.data)}`) console.log(`node3 received: ${msg.data.toString()}`)
}) })
await node3.pubsub.subscribe(topic)
``` ```
Finally, let's define the additional filter in the fruit topic. Finally, let's define the additional filter in the fruit topic.
```JavaScript ```JavaScript
const validateFruit = (msgTopic, msg) => { const validateFruit = (msgTopic, peer, msg) => {
const fruit = uint8ArrayToString(msg.data) const fruit = msg.data.toString();
const validFruit = ['banana', 'apple', 'orange'] const validFruit = ['banana', 'apple', 'orange']
const valid = validFruit.includes(fruit);
if (!validFruit.includes(fruit)) { return valid;
throw new Error('no valid fruit received')
}
} }
node1.pubsub.topicValidators.set(topic, validateFruit) node1.pubsub._pubsub.topicValidators.set(topic, validateFruit);
node2.pubsub.topicValidators.set(topic, validateFruit) node2.pubsub._pubsub.topicValidators.set(topic, validateFruit);
node3.pubsub.topicValidators.set(topic, validateFruit) node3.pubsub._pubsub.topicValidators.set(topic, validateFruit);
``` ```
In this example, node one has an outdated version of the system, or is a malicious node. When it tries to publish fruit, the messages are re-shared and all the nodes share the message. However, when it tries to publish a vehicle the message is not re-shared. In this example, node one has an outdated version of the system, or is a malicious node. When it tries to publish fruit, the messages are re-shared and all the nodes share the message. However, when it tries to publish a vehicle the message is not re-shared.
@ -84,7 +79,7 @@ const myFruits = ['banana', 'apple', 'car', 'orange'];
setInterval(() => { setInterval(() => {
console.log('############## fruit ' + myFruits[count] + ' ##############') console.log('############## fruit ' + myFruits[count] + ' ##############')
node1.pubsub.publish(topic, new TextEncoder().encode(myFruits[count])) node1.pubsub.publish(topic, Buffer.from(myFruits[count]))
count++ count++
if (count == myFruits.length) { if (count == myFruits.length) {
count = 0 count = 0

View File

@ -4,17 +4,18 @@
const Libp2p = require('../..') const Libp2p = require('../..')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const createNode = async () => { const createNode = async () => {
const node = await Libp2p.create({ const node = await Libp2p.create({
addresses: { addresses: {
// To signal the addresses we want to be available, we use // To signall the addresses we want to be available, we use
// the multiaddr format, a self describable address // the multiaddr format, a self describable address
listen: ['/ip4/0.0.0.0/tcp/0'] listen: ['/ip4/0.0.0.0/tcp/0']
}, },
modules: { modules: {
transport: [TCP], transport: [TCP],
connEncryption: [NOISE] connEncryption: [NOISE, SECIO]
} }
}) })

View File

@ -4,6 +4,7 @@
const Libp2p = require('../..') const Libp2p = require('../..')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const pipe = require('it-pipe') const pipe = require('it-pipe')
@ -12,13 +13,13 @@ const concat = require('it-concat')
const createNode = async () => { const createNode = async () => {
const node = await Libp2p.create({ const node = await Libp2p.create({
addresses: { addresses: {
// To signal the addresses we want to be available, we use // To signall the addresses we want to be available, we use
// the multiaddr format, a self describable address // the multiaddr format, a self describable address
listen: ['/ip4/0.0.0.0/tcp/0'] listen: ['/ip4/0.0.0.0/tcp/0']
}, },
modules: { modules: {
transport: [TCP], transport: [TCP],
connEncryption: [NOISE], connEncryption: [NOISE, SECIO],
streamMuxer: [MPLEX] streamMuxer: [MPLEX]
} }
}) })

View File

@ -5,6 +5,7 @@ const Libp2p = require('../..')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const WebSockets = require('libp2p-websockets') const WebSockets = require('libp2p-websockets')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const pipe = require('it-pipe') const pipe = require('it-pipe')
@ -16,11 +17,11 @@ const createNode = async (transports, addresses = []) => {
const node = await Libp2p.create({ const node = await Libp2p.create({
addresses: { addresses: {
listen: addresses listen: addresses.map((a) => a)
}, },
modules: { modules: {
transport: transports, transport: transports,
connEncryption: [NOISE], connEncryption: [NOISE, SECIO],
streamMuxer: [MPLEX] streamMuxer: [MPLEX]
} }
}) })

View File

@ -13,10 +13,10 @@ When using libp2p, you need properly configure it, that is, pick your set of mod
You will need 4 dependencies total, so go ahead and install all of them with: You will need 4 dependencies total, so go ahead and install all of them with:
```bash ```bash
> npm install libp2p libp2p-tcp libp2p-noise > npm install libp2p libp2p-tcp libp2p-secio peer-info
``` ```
Then, in your favorite text editor create a file with the `.js` extension. I've called mine `1.js`. Then, on your favorite text editor create a file with the `.js` extension. I've called mine `1.js`.
First thing is to create our own libp2p node! Insert: First thing is to create our own libp2p node! Insert:
@ -26,17 +26,18 @@ First thing is to create our own libp2p node! Insert:
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const { NOISE } = require('libp2p-noise') const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const createNode = async () => { const createNode = async () => {
const node = await Libp2p.create({ const node = await Libp2p.create({
addresses: { addresses: {
// To signal the addresses we want to be available, we use // To signall the addresses we want to be available, we use
// the multiaddr format, a self describable address // the multiaddr format, a self describable address
listen: ['/ip4/0.0.0.0/tcp/0'] listen: ['/ip4/0.0.0.0/tcp/0']
}, },
modules: { modules: {
transport: [ TCP ], transport: [ TCP ],
connEncryption: [ NOISE ] connEncryption: [ NOISE, SECIO ]
} }
}) })
@ -77,41 +78,20 @@ That `QmW2cKTakTYqbQkUzBTEGXgWYFj1YEPeUndE1YWs6CBzDQ` is the PeerId that was cre
Now that we have our `createNode` function, let's create two nodes and make them dial to each other! You can find the complete solution at [2.js](./2.js). Now that we have our `createNode` function, let's create two nodes and make them dial to each other! You can find the complete solution at [2.js](./2.js).
For this step, we will need some more dependencies. For this step, we will need one more dependency.
```bash ```bash
> npm install it-pipe it-concat libp2p-mplex > npm install it-pipe it-buffer
``` ```
And we also need to import the modules on our .js file: And we also need to import the module on our .js file:
```js ```js
const pipe = require('it-pipe') const pipe = require('it-pipe')
const concat = require('it-concat') const { toBuffer } = require('it-buffer')
const MPLEX = require('libp2p-mplex')
``` ```
We are going to reuse the `createNode` function from step 1, but this time add a stream multiplexer from `libp2p-mplex`. We are going to reuse the `createNode` function from step 1, but this time to make things simpler, we will create another function to print the addrs to avoid duplicating code.
```js
const createNode = async () => {
const node = await Libp2p.create({
addresses: {
// To signal the addresses we want to be available, we use
// the multiaddr format, a self describable address
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
connEncryption: [NOISE],
streamMuxer: [MPLEX] // <--- Add this line
}
})
await node.start()
return node
}
```
We will also make things simpler by creating another function to print the multiaddresses to avoid duplicating code.
```JavaScript ```JavaScript
function printAddrs (node, number) { function printAddrs (node, number) {
@ -120,7 +100,7 @@ function printAddrs (node, number) {
} }
``` ```
Then add, Then,
```js ```js
;(async () => { ;(async () => {
@ -132,15 +112,18 @@ Then add,
printAddrs(node1, '1') printAddrs(node1, '1')
printAddrs(node2, '2') printAddrs(node2, '2')
node2.handle('/print', async ({ stream }) => { node2.handle('/print', ({ stream }) => {
const result = await pipe( pipe(
stream, stream,
concat async function (source) {
for await (const msg of source) {
console.log(msg.toString())
}
}
) )
console.log(result.toString())
}) })
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs) node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
const { stream } = await node1.dialProtocol(node2.peerId, '/print') const { stream } = await node1.dialProtocol(node2.peerId, '/print')
await pipe( await pipe(
@ -149,9 +132,8 @@ Then add,
) )
})(); })();
``` ```
For more information refer to the [docs](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md).
The result should look like: The result should be look like:
```bash ```bash
> node 2.js > node 2.js
@ -166,33 +148,33 @@ Hello p2p world!
## 3. Using multiple transports ## 3. Using multiple transports
Next, we want nodes to have multiple transports available to increase their chances of having a common transport in the network to communicate over. A simple scenario is a node running in the browser only having access to HTTP, WebSockets and WebRTC since the browser doesn't let you open any other kind of transport. For this node to dial to some other node, that other node needs to share a common transport. Next, we want to be available in multiple transports to increase our chances of having common transports in the network. A simple scenario, a node running in the browser only has access to HTTP, WebSockets and WebRTC since the browser doesn't let you open any other kind of transport, for this node to dial to some other node, that other node needs to share a common transport.
What we are going to do in this step is to create 3 nodes: one with TCP, another with TCP+WebSockets and another one with just WebSockets. The full solution can be found on [3.js](./3.js). What we are going to do in this step is to create 3 nodes, one with TCP, another with TCP+WebSockets and another one with just WebSockets. The full solution can be found on [3.js](./3.js).
In this example, we will need to also install `libp2p-websockets`: In this example, we will need to also install `libp2p-websockets`, go ahead and install:
```bash ```bash
> npm install libp2p-websockets > npm install libp2p-websockets
``` ```
We want to create 3 nodes: one with TCP, one with TCP+WebSockets and one with just WebSockets. We need to update our `createNode` function to accept WebSocket connections as well. Moreover, let's upgrade our function to enable us to pick the addresses over which a node will start a listener: We want to create 3 nodes, one with TCP, one with TCP+WebSockets and one with just WebSockets. We need to update our `createNode` function to contemplate WebSockets as well. Moreover, let's upgrade our function to enable us to pick the addrs in which a node will start a listener:
```JavaScript ```JavaScript
// ... // ...
const createNode = async (transports, addresses = []) => { const createNode = async (transports, multiaddrs = []) => {
if (!Array.isArray(addresses)) { if (!Array.isArray(multiaddrs)) {
addresses = [addresses] multiaddrs = [multiaddrs]
} }
const node = await Libp2p.create({ const node = await Libp2p.create({
addresses: { addresses: {
listen: addresses listen: multiaddrs.map((a) => multiaddr(a))
}, },
modules: { modules: {
transport: transports, transport: transports,
connEncryption: [NOISE], connEncryption: [SECIO],
streamMuxer: [MPLEX] streamMuxer: [MPLEX]
} }
}) })
@ -250,7 +232,7 @@ try {
} }
``` ```
`print` is a function that prints each piece of data from a stream onto a new line but factored into its own function to save lines: `print` is a function created using the code from 2.js, but factored into its own function to save lines, here it is:
```JavaScript ```JavaScript
function print ({ stream }) { function print ({ stream }) {
@ -265,7 +247,7 @@ function print ({ stream }) {
} }
``` ```
If everything was set correctly, you now should see something similar to the following after running the script: If everything was set correctly, you now should see the following after you run the script:
```Bash ```Bash
> node 3.js > node 3.js
@ -284,13 +266,13 @@ node 3 failed to dial to node 1 with:
Error: No transport available for address /ip4/127.0.0.1/tcp/51482 Error: No transport available for address /ip4/127.0.0.1/tcp/51482
``` ```
As expected, we created 3 nodes: node 1 with TCP, node 2 with TCP+WebSockets and node 3 with just WebSockets. node 1 -> node 2 and node 2 -> node 3 managed to dial correctly because they shared a common transport; however, node 3 -> node 1 failed because they didn't share any. As expected, we created 3 nodes, node 1 with TCP, node 2 with TCP+WebSockets and node 3 with just WebSockets. node 1 -> node 2 and node 2 -> node 3 managed to dial correctly because they shared a common transport, however, node 3 -> node 1 failed because they didn't share any.
## 4. How to create a new libp2p transport ## 4. How to create a new libp2p transport
Today there are already several transports available and plenty to come. You can find these at [interface-transport implementations](https://github.com/libp2p/js-interfaces/tree/master/src/transport#modules-that-implement-the-interface) list. Today there are already several transports available and plenty to come, you can find these at [interface-transport implementations](https://github.com/libp2p/js-interfaces/tree/master/src/transport#modules-that-implement-the-interface) list.
Adding more transports is done through the same way as you added TCP and WebSockets. Some transports might offer extra functionalities, but as far as libp2p is concerned, if it follows the interface defined in the [spec](https://github.com/libp2p/js-interfaces/tree/master/src/transport#api) it will be able to use it. Adding more transports is done through the same way as you added TCP and WebSockets. Some transports might offer extra functionalities, but as far as libp2p is concerned, if it follows the interface defined at the [spec](https://github.com/libp2p/js-interfaces/tree/master/src/transport#api) it will be able to use it.
If you decide to implement a transport yourself, please consider adding to the list so that others can use it as well. If you decide to implement a transport yourself, please consider adding to the list so that others can use it as well.

View File

@ -51,6 +51,7 @@
["libp2p/js-peer-id", "peer-id"], ["libp2p/js-peer-id", "peer-id"],
"pubsub", "pubsub",
["libp2p/js-libp2p-pubsub", "libp2p-pubsub"],
["libp2p/js-libp2p-floodsub", "libp2p-floodsub"], ["libp2p/js-libp2p-floodsub", "libp2p-floodsub"],
["ChainSafe/js-libp2p-gossipsub", "libp2p-gossipsub"], ["ChainSafe/js-libp2p-gossipsub", "libp2p-gossipsub"],

View File

@ -1,6 +1,6 @@
{ {
"name": "libp2p", "name": "libp2p",
"version": "0.29.3", "version": "0.28.9",
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack", "description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>", "leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js", "main": "src/index.js",
@ -37,7 +37,7 @@
"homepage": "https://libp2p.io", "homepage": "https://libp2p.io",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=12.0.0", "node": ">=10.0.0",
"npm": ">=6.0.0" "npm": ">=6.0.0"
}, },
"dependencies": { "dependencies": {
@ -50,7 +50,7 @@
"err-code": "^2.0.0", "err-code": "^2.0.0",
"events": "^3.1.0", "events": "^3.1.0",
"hashlru": "^2.3.0", "hashlru": "^2.3.0",
"interface-datastore": "^2.0.0", "interface-datastore": "^1.0.4",
"ipfs-utils": "^2.2.0", "ipfs-utils": "^2.2.0",
"it-all": "^1.0.1", "it-all": "^1.0.1",
"it-buffer": "^0.1.2", "it-buffer": "^0.1.2",
@ -58,115 +58,111 @@
"it-length-prefixed": "^3.0.1", "it-length-prefixed": "^3.0.1",
"it-pipe": "^1.1.0", "it-pipe": "^1.1.0",
"it-protocol-buffers": "^0.2.0", "it-protocol-buffers": "^0.2.0",
"libp2p-crypto": "^0.18.0", "libp2p-crypto": "^0.17.9",
"libp2p-interfaces": "^0.5.1", "libp2p-interfaces": "^0.3.1",
"libp2p-utils": "^0.2.0", "libp2p-utils": "^0.1.2",
"mafmt": "^8.0.0", "mafmt": "^7.0.0",
"merge-options": "^2.0.0", "merge-options": "^2.0.0",
"moving-average": "^1.0.0", "moving-average": "^1.0.0",
"multiaddr": "^8.1.0", "multiaddr": "^7.4.3",
"multicodec": "^2.0.0", "multistream-select": "^0.15.0",
"multistream-select": "^1.0.0",
"mutable-proxy": "^1.0.0", "mutable-proxy": "^1.0.0",
"node-forge": "^0.9.1", "node-forge": "^0.9.1",
"p-any": "^3.0.0", "p-any": "^3.0.0",
"p-fifo": "^1.0.0", "p-fifo": "^1.0.0",
"p-settle": "^4.0.1", "p-settle": "^4.0.1",
"peer-id": "^0.14.2", "peer-id": "^0.13.11",
"protons": "^2.0.0", "protons": "^1.0.1",
"retimer": "^2.0.0", "retimer": "^2.0.0",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"streaming-iterables": "^5.0.2", "streaming-iterables": "^4.1.0",
"timeout-abort-controller": "^1.1.1", "timeout-abort-controller": "^1.0.0",
"varint": "^5.0.0",
"xsalsa20": "^1.0.2" "xsalsa20": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@nodeutils/defaults-deep": "^1.1.0", "@nodeutils/defaults-deep": "^1.1.0",
"abortable-iterator": "^3.0.0", "abortable-iterator": "^3.0.0",
"aegir": "^27.0.0", "aegir": "^22.0.0",
"chai": "^4.2.0", "chai": "^4.2.0",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"chai-bytes": "^0.1.2", "chai-bytes": "^0.1.2",
"chai-string": "^1.5.0", "chai-string": "^1.5.0",
"cids": "^1.0.0", "cids": "^0.8.0",
"datastore-fs": "^1.1.0",
"datastore-level": "^1.1.0",
"delay": "^4.3.0", "delay": "^4.3.0",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"interop-libp2p": "^0.3.0", "interop-libp2p": "^0.1.0",
"ipfs-http-client": "^47.0.1", "ipfs-http-client": "^44.0.0",
"it-concat": "^1.0.0", "it-concat": "^1.0.0",
"it-pair": "^1.0.0", "it-pair": "^1.0.0",
"it-pushable": "^1.4.0", "it-pushable": "^1.4.0",
"libp2p": ".", "level": "^6.0.1",
"libp2p-bootstrap": "^0.12.0", "libp2p-bootstrap": "^0.11.0",
"libp2p-delegated-content-routing": "^0.7.0", "libp2p-delegated-content-routing": "^0.5.0",
"libp2p-delegated-peer-routing": "^0.7.0", "libp2p-delegated-peer-routing": "^0.5.0",
"libp2p-floodsub": "^0.23.0", "libp2p-floodsub": "^0.21.0",
"libp2p-gossipsub": "^0.6.0", "libp2p-gossipsub": "^0.4.6",
"libp2p-kad-dht": "^0.20.0", "libp2p-kad-dht": "^0.19.1",
"libp2p-mdns": "^0.15.0", "libp2p-mdns": "^0.14.1",
"libp2p-mplex": "^0.10.1", "libp2p-mplex": "^0.9.5",
"libp2p-noise": "^2.0.0", "libp2p-noise": "^1.1.1",
"libp2p-secio": "^0.13.1", "libp2p-secio": "^0.12.4",
"libp2p-tcp": "^0.15.1", "libp2p-tcp": "^0.14.1",
"libp2p-webrtc-star": "^0.20.0", "libp2p-webrtc-star": "^0.18.0",
"libp2p-websockets": "^0.14.0", "libp2p-websockets": "^0.13.1",
"multihashes": "^3.0.1", "multihashes": "^0.4.19",
"nock": "^13.0.3", "nock": "^12.0.3",
"p-defer": "^3.0.0", "p-defer": "^3.0.0",
"p-times": "^3.0.0", "p-times": "^3.0.0",
"p-wait-for": "^3.1.0", "p-wait-for": "^3.1.0",
"promisify-es6": "^1.0.3", "promisify-es6": "^1.0.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sinon": "^9.0.2", "sinon": "^9.0.2"
"uint8arrays": "^1.1.0"
}, },
"contributors": [ "contributors": [
"David Dias <daviddias.p@gmail.com>", "David Dias <daviddias.p@gmail.com>",
"Jacob Heun <jacobheun@gmail.com>", "Jacob Heun <jacobheun@gmail.com>",
"Vasco Santos <vasco.santos@moxy.studio>", "Vasco Santos <vasco.santos@moxy.studio>",
"Alan Shaw <alan@tableflip.io>", "Alan Shaw <alan@tableflip.io>",
"Alex Potsides <alex@achingbrain.net>",
"Cayman <caymannava@gmail.com>", "Cayman <caymannava@gmail.com>",
"Pedro Teixeira <i@pgte.me>", "Pedro Teixeira <i@pgte.me>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>", "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Alex Potsides <alex@achingbrain.net>",
"Maciej Krüger <mkg20001@gmail.com>", "Maciej Krüger <mkg20001@gmail.com>",
"Hugo Dias <mail@hugodias.me>", "Hugo Dias <mail@hugodias.me>",
"dirkmc <dirkmdev@gmail.com>",
"Volker Mische <volker.mische@gmail.com>", "Volker Mische <volker.mische@gmail.com>",
"dirkmc <dirkmdev@gmail.com>",
"Richard Littauer <richard.littauer@gmail.com>", "Richard Littauer <richard.littauer@gmail.com>",
"Elven <mon.samuel@qq.com>",
"Andrew Nesbitt <andrewnez@gmail.com>",
"Giovanni T. Parra <fiatjaf@gmail.com>",
"Ryan Bell <ryan@piing.net>",
"Thomas Eizinger <thomas@eizinger.io>", "Thomas Eizinger <thomas@eizinger.io>",
"Ryan Bell <ryan@piing.net>",
"Giovanni T. Parra <fiatjaf@gmail.com>",
"Andrew Nesbitt <andrewnez@gmail.com>",
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>", "ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
"Didrik Nordström <didrik@betamos.se>", "Elven <mon.samuel@qq.com>",
"Henrique Dias <hacdias@gmail.com>", "Didrik Nordström <didrik.nordstrom@gmail.com>",
"Tiago Alves <alvesjtiago@gmail.com>",
"Yusef Napora <yusef@napora.org>",
"Zane Starr <zcstarr@gmail.com>",
"ebinks <elizabethjbinks@gmail.com>",
"isan_rivkin <isanrivkin@gmail.com>",
"robertkiel <robert.kiel@validitylabs.org>",
"RasmusErik Voel Jensen <github@solsort.com>",
"Bernd Strehl <bernd.strehl@gmail.com>",
"Chris Bratlien <chrisbratlien@gmail.com>",
"Daijiro Wachi <daijiro.wachi@gmail.com>",
"Diogo Silva <fsdiogo@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"Fei Liu <liu.feiwood@gmail.com>", "Fei Liu <liu.feiwood@gmail.com>",
"Florian-Merle <florian.david.merle@gmail.com>",
"Francis Gulotta <wizard@roborooter.com>",
"Henrique Dias <hacdias@gmail.com>",
"Irakli Gozalishvili <rfobic@gmail.com>", "Irakli Gozalishvili <rfobic@gmail.com>",
"Ethan Lam <elmemphis2000@gmail.com>",
"Joel Gustafson <joelg@mit.edu>", "Joel Gustafson <joelg@mit.edu>",
"Julien Bouquillon <contact@revolunet.com>", "Julien Bouquillon <contact@revolunet.com>",
"Kevin Kwok <antimatter15@gmail.com>", "Kevin Kwok <antimatter15@gmail.com>",
"Nuno Nogueira <nunofmn@gmail.com>", "Nuno Nogueira <nunofmn@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"RasmusErik Voel Jensen <github@solsort.com>",
"Diogo Silva <fsdiogo@gmail.com>",
"robertkiel <robert.kiel@validitylabs.org>",
"Soeren <nikorpoulsen@gmail.com>", "Soeren <nikorpoulsen@gmail.com>",
"Sönke Hahn <soenkehahn@gmail.com>", "Sönke Hahn <soenkehahn@gmail.com>"
"Tiago Alves <alvesjtiago@gmail.com>",
"Daijiro Wachi <daijiro.wachi@gmail.com>",
"Yusef Napora <yusef@napora.org>",
"Zane Starr <zcstarr@gmail.com>",
"Cindy Wu <ciindy.wu@gmail.com>",
"Chris Bratlien <chrisbratlien@gmail.com>",
"ebinks <elizabethjbinks@gmail.com>",
"Bernd Strehl <bernd.strehl@gmail.com>",
"isan_rivkin <isanrivkin@gmail.com>",
"Florian-Merle <florian.david.merle@gmail.com>",
"Francis Gulotta <wizard@roborooter.com>",
"Felipe Martins <felipebrasil93@gmail.com>"
] ]
} }

View File

@ -15,11 +15,11 @@ const multiaddr = require('multiaddr')
*/ */
class AddressManager { class AddressManager {
/** /**
* @class * @constructor
* @param {object} [options] * @param {object} [options]
* @param {Array<string>} [options.listen = []] - list of multiaddrs string representation to listen. * @param {Array<string>} [options.listen = []] list of multiaddrs string representation to listen.
* @param {Array<string>} [options.announce = []] - list of multiaddrs string representation to announce. * @param {Array<string>} [options.announce = []] list of multiaddrs string representation to announce.
* @param {Array<string>} [options.noAnnounce = []] - list of multiaddrs string representation to not announce. * @param {Array<string>} [options.noAnnounce = []] list of multiaddrs string representation to not announce.
*/ */
constructor ({ listen = [], announce = [], noAnnounce = [] } = {}) { constructor ({ listen = [], announce = [], noAnnounce = [] } = {}) {
this.listen = new Set(listen) this.listen = new Set(listen)
@ -29,8 +29,7 @@ class AddressManager {
/** /**
* Get peer listen multiaddrs. * Get peer listen multiaddrs.
* * @return {Array<Multiaddr>}
* @returns {Array<Multiaddr>}
*/ */
getListenAddrs () { getListenAddrs () {
return Array.from(this.listen).map((a) => multiaddr(a)) return Array.from(this.listen).map((a) => multiaddr(a))
@ -38,8 +37,7 @@ class AddressManager {
/** /**
* Get peer announcing multiaddrs. * Get peer announcing multiaddrs.
* * @return {Array<Multiaddr>}
* @returns {Array<Multiaddr>}
*/ */
getAnnounceAddrs () { getAnnounceAddrs () {
return Array.from(this.announce).map((a) => multiaddr(a)) return Array.from(this.announce).map((a) => multiaddr(a))
@ -47,8 +45,7 @@ class AddressManager {
/** /**
* Get peer noAnnouncing multiaddrs. * Get peer noAnnouncing multiaddrs.
* * @return {Array<Multiaddr>}
* @returns {Array<Multiaddr>}
*/ */
getNoAnnounceAddrs () { getNoAnnounceAddrs () {
return Array.from(this.noAnnounce).map((a) => multiaddr(a)) return Array.from(this.noAnnounce).map((a) => multiaddr(a))

View File

@ -41,7 +41,7 @@ const multiaddr = require('multiaddr')
const Libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise') const SECIO = require('libp2p-secio')
const relayAddr = ... const relayAddr = ...
@ -52,7 +52,7 @@ const node = await Libp2p.create({
modules: { modules: {
transport: [TCP], transport: [TCP],
streamMuxer: [MPLEX], streamMuxer: [MPLEX],
connEncryption: [NOISE] connEncryption: [SECIO]
}, },
config: { config: {
relay: { // Circuit Relay options (this config is part of libp2p core configurations) relay: { // Circuit Relay options (this config is part of libp2p core configurations)

View File

@ -90,8 +90,9 @@ module.exports.handleHop = async function handleHop ({
* peer. A new, virtual, connection will be created between the two via the relay. * peer. A new, virtual, connection will be created between the two via the relay.
* *
* @param {object} options * @param {object} options
* @param {Connection} options.connection - Connection to the relay * @param {Connection} options.connection Connection to the relay
* @param {*} options.request * @param {*} options.request
* @param {Circuit} options.circuit
* @returns {Promise<Connection>} * @returns {Promise<Connection>}
*/ */
module.exports.hop = async function hop ({ module.exports.hop = async function hop ({
@ -118,11 +119,6 @@ module.exports.hop = async function hop ({
/** /**
* Creates an unencoded CAN_HOP response based on the Circuits configuration * Creates an unencoded CAN_HOP response based on the Circuits configuration
*
* @param {Object} options
* @param {Connection} options.connection
* @param {StreamHandler} options.streamHandler
* @param {Circuit} options.circuit
* @private * @private
*/ */
module.exports.handleCanHop = function handleCanHop ({ module.exports.handleCanHop = function handleCanHop ({

View File

@ -15,7 +15,7 @@ log.error = debug('libp2p:circuit:stop:error')
* @private * @private
* @param {*} options * @param {*} options
* @param {Connection} options.connection * @param {Connection} options.connection
* @param {*} options.request - The CircuitRelay protobuf request (unencoded) * @param {*} options.request The CircuitRelay protobuf request (unencoded)
* @param {StreamHandler} options.streamHandler * @param {StreamHandler} options.streamHandler
* @returns {Promise<*>} Resolves a duplex iterable * @returns {Promise<*>} Resolves a duplex iterable
*/ */
@ -42,11 +42,10 @@ module.exports.handleStop = function handleStop ({
/** /**
* Creates a STOP request * Creates a STOP request
*
* @private * @private
* @param {*} options * @param {*} options
* @param {Connection} options.connection * @param {Connection} options.connection
* @param {*} options.request - The CircuitRelay protobuf request (unencoded) * @param {*} options.request The CircuitRelay protobuf request (unencoded)
* @returns {Promise<*>} Resolves a duplex iterable * @returns {Promise<*>} Resolves a duplex iterable
*/ */
module.exports.stop = async function stop ({ module.exports.stop = async function stop ({

View File

@ -14,7 +14,7 @@ class StreamHandler {
* *
* @param {object} options * @param {object} options
* @param {*} options.stream - A duplex iterable * @param {*} options.stream - A duplex iterable
* @param {number} options.maxLength - max bytes length of message * @param {Number} options.maxLength - max bytes length of message
*/ */
constructor ({ stream, maxLength = 4096 }) { constructor ({ stream, maxLength = 4096 }) {
this.stream = stream this.stream = stream
@ -25,7 +25,6 @@ class StreamHandler {
/** /**
* Read and decode message * Read and decode message
*
* @async * @async
* @returns {void} * @returns {void}
*/ */
@ -45,7 +44,7 @@ class StreamHandler {
/** /**
* Encode and write array of buffers * Encode and write array of buffers
* *
* @param {*} msg - An unencoded CircuitRelay protobuf message * @param {*} msg An unencoded CircuitRelay protobuf message
*/ */
write (msg) { write (msg) {
log('write message type %s', msg.type) log('write message type %s', msg.type)
@ -55,7 +54,7 @@ class StreamHandler {
/** /**
* Return the handshake rest stream and invalidate handler * Return the handshake rest stream and invalidate handler
* *
* @returns {*} A duplex iterable * @return {*} A duplex iterable
*/ */
rest () { rest () {
this.shake.rest() this.shake.rest()

View File

@ -19,7 +19,7 @@ function writeResponse (streamHandler, status) {
/** /**
* Validate incomming HOP/STOP message * Validate incomming HOP/STOP message
* *
* @param {*} msg - A CircuitRelay unencoded protobuf message * @param {*} msg A CircuitRelay unencoded protobuf message
* @param {StreamHandler} streamHandler * @param {StreamHandler} streamHandler
*/ */
function validateAddrs (msg, streamHandler) { function validateAddrs (msg, streamHandler) {

View File

@ -21,7 +21,7 @@ class Circuit {
/** /**
* Creates an instance of Circuit. * Creates an instance of Circuit.
* *
* @class * @constructor
* @param {object} options * @param {object} options
* @param {Libp2p} options.libp2p * @param {Libp2p} options.libp2p
* @param {Upgrader} options.upgrader * @param {Upgrader} options.upgrader
@ -122,11 +122,11 @@ class Circuit {
type: CircuitPB.Type.HOP, type: CircuitPB.Type.HOP,
srcPeer: { srcPeer: {
id: this.peerId.toBytes(), id: this.peerId.toBytes(),
addrs: this._libp2p.multiaddrs.map(addr => addr.bytes) addrs: this._libp2p.multiaddrs.map(addr => addr.buffer)
}, },
dstPeer: { dstPeer: {
id: destinationPeer.toBytes(), id: destinationPeer.toBytes(),
addrs: [multiaddr(destinationAddr).bytes] addrs: [multiaddr(destinationAddr).buffer]
} }
} }
}) })
@ -152,7 +152,7 @@ class Circuit {
* *
* @param {any} options * @param {any} options
* @param {Function} handler * @param {Function} handler
* @returns {listener} * @return {listener}
*/ */
createListener (options, handler) { createListener (options, handler) {
if (typeof options === 'function') { if (typeof options === 'function') {

View File

@ -19,7 +19,7 @@ module.exports = (circuit) => {
* Add swarm handler and listen for incoming connections * Add swarm handler and listen for incoming connections
* *
* @param {Multiaddr} addr * @param {Multiaddr} addr
* @returns {void} * @return {void}
*/ */
listener.listen = async (addr) => { listener.listen = async (addr) => {
const addrString = String(addr).split('/p2p-circuit').find(a => a !== '') const addrString = String(addr).split('/p2p-circuit').find(a => a !== '')
@ -34,7 +34,7 @@ module.exports = (circuit) => {
/** /**
* TODO: Remove the peers from our topology * TODO: Remove the peers from our topology
* *
* @returns {void} * @return {void}
*/ */
listener.close = () => {} listener.close = () => {}
@ -52,7 +52,7 @@ module.exports = (circuit) => {
* the encapsulated transport address. This is useful when for example, a peer should only * the encapsulated transport address. This is useful when for example, a peer should only
* be dialed over TCP rather than any other transport * be dialed over TCP rather than any other transport
* *
* @returns {Multiaddr[]} * @return {Multiaddr[]}
*/ */
listener.getAddrs = () => { listener.getAddrs = () => {
const addrs = [] const addrs = []

View File

@ -1,8 +1,6 @@
'use strict' 'use strict'
const mergeOptions = require('merge-options') const mergeOptions = require('merge-options')
const { dnsaddrResolver } = require('multiaddr/src/resolvers')
const Constants = require('./constants') const Constants = require('./constants')
const { FaultTolerance } = require('./transport-manager') const { FaultTolerance } = require('./transport-manager')
@ -22,10 +20,7 @@ const DefaultConfig = {
dialer: { dialer: {
maxParallelDials: Constants.MAX_PARALLEL_DIALS, maxParallelDials: Constants.MAX_PARALLEL_DIALS,
maxDialsPerPeer: Constants.MAX_PER_PEER_DIALS, maxDialsPerPeer: Constants.MAX_PER_PEER_DIALS,
dialTimeout: Constants.DIAL_TIMEOUT, dialTimeout: Constants.DIAL_TIMEOUT
resolvers: {
dnsaddr: dnsaddrResolver
}
}, },
metrics: { metrics: {
enabled: false enabled: false

View File

@ -32,26 +32,25 @@ const defaultOptions = {
/** /**
* Responsible for managing known connections. * Responsible for managing known connections.
*
* @fires ConnectionManager#peer:connect Emitted when a new peer is connected. * @fires ConnectionManager#peer:connect Emitted when a new peer is connected.
* @fires ConnectionManager#peer:disconnect Emitted when a peer is disconnected. * @fires ConnectionManager#peer:disconnect Emitted when a peer is disconnected.
*/ */
class ConnectionManager extends EventEmitter { class ConnectionManager extends EventEmitter {
/** /**
* @class * @constructor
* @param {Libp2p} libp2p * @param {Libp2p} libp2p
* @param {object} options * @param {object} options
* @param {number} options.maxConnections - The maximum number of connections allowed. Default=Infinity * @param {Number} options.maxConnections The maximum number of connections allowed. Default=Infinity
* @param {number} options.minConnections - The minimum number of connections to avoid pruning. Default=0 * @param {Number} options.minConnections The minimum number of connections to avoid pruning. Default=0
* @param {number} options.maxData - The max data (in and out), per average interval to allow. Default=Infinity * @param {Number} options.maxData The max data (in and out), per average interval to allow. Default=Infinity
* @param {number} options.maxSentData - The max outgoing data, per average interval to allow. Default=Infinity * @param {Number} options.maxSentData The max outgoing data, per average interval to allow. Default=Infinity
* @param {number} options.maxReceivedData - The max incoming data, per average interval to allow.. Default=Infinity * @param {Number} options.maxReceivedData The max incoming data, per average interval to allow.. Default=Infinity
* @param {number} options.maxEventLoopDelay - The upper limit the event loop can take to run. Default=Infinity * @param {Number} options.maxEventLoopDelay The upper limit the event loop can take to run. Default=Infinity
* @param {number} options.pollInterval - How often, in milliseconds, metrics and latency should be checked. Default=2000 * @param {Number} options.pollInterval How often, in milliseconds, metrics and latency should be checked. Default=2000
* @param {number} options.movingAverageInterval - How often, in milliseconds, to compute averages. Default=60000 * @param {Number} options.movingAverageInterval How often, in milliseconds, to compute averages. Default=60000
* @param {number} options.defaultPeerValue - The value of the peer. Default=1 * @param {Number} options.defaultPeerValue The value of the peer. Default=1
* @param {boolean} options.autoDial - Should preemptively guarantee connections are above the low watermark. Default=true * @param {boolean} options.autoDial Should preemptively guarantee connections are above the low watermark. Default=true
* @param {number} options.autoDialInterval - How often, in milliseconds, it should preemptively guarantee connections are above the low watermark. Default=10000 * @param {Number} options.autoDialInterval How often, in milliseconds, it should preemptively guarantee connections are above the low watermark. Default=10000
*/ */
constructor (libp2p, options) { constructor (libp2p, options) {
super() super()
@ -70,14 +69,12 @@ class ConnectionManager extends EventEmitter {
/** /**
* Map of peer identifiers to their peer value for pruning connections. * Map of peer identifiers to their peer value for pruning connections.
*
* @type {Map<string, number>} * @type {Map<string, number>}
*/ */
this._peerValues = new Map() this._peerValues = new Map()
/** /**
* Map of connections per peer * Map of connections per peer
*
* @type {Map<string, Array<conn>>} * @type {Map<string, Array<conn>>}
*/ */
this.connections = new Map() this.connections = new Map()
@ -122,7 +119,6 @@ class ConnectionManager extends EventEmitter {
/** /**
* Stops the Connection Manager * Stops the Connection Manager
*
* @async * @async
*/ */
async stop () { async stop () {
@ -137,7 +133,6 @@ class ConnectionManager extends EventEmitter {
/** /**
* Cleans up the connections * Cleans up the connections
*
* @async * @async
*/ */
async _close () { async _close () {
@ -156,9 +151,8 @@ class ConnectionManager extends EventEmitter {
/** /**
* Sets the value of the given peer. Peers with lower values * Sets the value of the given peer. Peers with lower values
* will be disconnected first. * will be disconnected first.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {number} value - A number between 0 and 1 * @param {number} value A number between 0 and 1
*/ */
setPeerValue (peerId, value) { setPeerValue (peerId, value) {
if (value < 0 || value > 1) { if (value < 0 || value > 1) {
@ -173,7 +167,6 @@ class ConnectionManager extends EventEmitter {
/** /**
* Checks the libp2p metrics to determine if any values have exceeded * Checks the libp2p metrics to determine if any values have exceeded
* the configured maximums. * the configured maximums.
*
* @private * @private
*/ */
_checkMetrics () { _checkMetrics () {
@ -190,7 +183,6 @@ class ConnectionManager extends EventEmitter {
/** /**
* Tracks the incoming connection and check the connection limit * Tracks the incoming connection and check the connection limit
*
* @param {Connection} connection * @param {Connection} connection
*/ */
onConnect (connection) { onConnect (connection) {
@ -216,7 +208,6 @@ class ConnectionManager extends EventEmitter {
/** /**
* Removes the connection from tracking * Removes the connection from tracking
*
* @param {Connection} connection * @param {Connection} connection
*/ */
onDisconnect (connection) { onDisconnect (connection) {
@ -235,7 +226,6 @@ class ConnectionManager extends EventEmitter {
/** /**
* Get a connection with a peer. * Get a connection with a peer.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {Connection} * @returns {Connection}
*/ */
@ -249,7 +239,6 @@ class ConnectionManager extends EventEmitter {
/** /**
* Get all open connections with a peer. * Get all open connections with a peer.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {Array<Connection>} * @returns {Array<Connection>}
*/ */
@ -270,9 +259,8 @@ class ConnectionManager extends EventEmitter {
/** /**
* If the event loop is slow, maybe close a connection * If the event loop is slow, maybe close a connection
*
* @private * @private
* @param {*} summary - The LatencyMonitor summary * @param {*} summary The LatencyMonitor summary
*/ */
_onLatencyMeasure (summary) { _onLatencyMeasure (summary) {
this._checkMaxLimit('maxEventLoopDelay', summary.avgMs) this._checkMaxLimit('maxEventLoopDelay', summary.avgMs)
@ -280,10 +268,9 @@ class ConnectionManager extends EventEmitter {
/** /**
* If the `value` of `name` has exceeded its limit, maybe close a connection * If the `value` of `name` has exceeded its limit, maybe close a connection
*
* @private * @private
* @param {string} name - The name of the field to check limits for * @param {string} name The name of the field to check limits for
* @param {number} value - The current value of the field * @param {number} value The current value of the field
*/ */
_checkMaxLimit (name, value) { _checkMaxLimit (name, value) {
const limit = this._options[name] const limit = this._options[name]
@ -298,7 +285,6 @@ class ConnectionManager extends EventEmitter {
* Proactively tries to connect to known peers stored in the PeerStore. * Proactively tries to connect to known peers stored in the PeerStore.
* It will keep the number of connections below the upper limit and sort * It will keep the number of connections below the upper limit and sort
* the peers to connect based on wether we know their keys and protocols. * the peers to connect based on wether we know their keys and protocols.
*
* @async * @async
* @private * @private
*/ */
@ -344,7 +330,6 @@ class ConnectionManager extends EventEmitter {
/** /**
* If we have more connections than our maximum, close a connection * If we have more connections than our maximum, close a connection
* to the lowest valued peer. * to the lowest valued peer.
*
* @private * @private
*/ */
_maybeDisconnectOne () { _maybeDisconnectOne () {

View File

@ -12,11 +12,11 @@ const debug = require('debug')('latency-monitor:LatencyMonitor')
/** /**
* @typedef {Object} SummaryObject * @typedef {Object} SummaryObject
* @property {number} events How many events were called * @property {Number} events How many events were called
* @property {number} minMS What was the min time for a cb to be called * @property {Number} minMS What was the min time for a cb to be called
* @property {number} maxMS What was the max time for a cb to be called * @property {Number} maxMS What was the max time for a cb to be called
* @property {number} avgMs What was the average time for a cb to be called * @property {Number} avgMs What was the average time for a cb to be called
* @property {number} lengthMs How long this interval was in ms * @property {Number} lengthMs How long this interval was in ms
*/ */
/** /**
@ -37,11 +37,10 @@ const debug = require('debug')('latency-monitor:LatencyMonitor')
*/ */
class LatencyMonitor extends EventEmitter { class LatencyMonitor extends EventEmitter {
/** /**
* @param {object} [options] * @param {Number} [latencyCheckIntervalMs=500] How often to add a latency check event (ms)
* @param {number} [options.latencyCheckIntervalMs=500] - How often to add a latency check event (ms) * @param {Number} [dataEmitIntervalMs=5000] How often to summarize latency check events. null or 0 disables event firing
* @param {number} [options.dataEmitIntervalMs=5000] - How often to summarize latency check events. null or 0 disables event firing * @param {function} [asyncTestFn] What cb-style async function to use
* @param {Function} [options.asyncTestFn] - What cb-style async function to use * @param {Number} [latencyRandomPercentage=5] What percent (+/-) of latencyCheckIntervalMs should we randomly use? This helps avoid alignment to other events.
* @param {number} [options.latencyRandomPercentage=5] - What percent (+/-) of latencyCheckIntervalMs should we randomly use? This helps avoid alignment to other events.
*/ */
constructor ({ latencyCheckIntervalMs, dataEmitIntervalMs, asyncTestFn, latencyRandomPercentage } = {}) { constructor ({ latencyCheckIntervalMs, dataEmitIntervalMs, asyncTestFn, latencyRandomPercentage } = {}) {
super() super()
@ -108,7 +107,6 @@ class LatencyMonitor extends EventEmitter {
/** /**
* Start internal timers * Start internal timers
*
* @private * @private
*/ */
_startTimers () { _startTimers () {
@ -127,7 +125,6 @@ class LatencyMonitor extends EventEmitter {
/** /**
* Stop internal timers * Stop internal timers
*
* @private * @private
*/ */
_stopTimers () { _stopTimers () {
@ -143,7 +140,6 @@ class LatencyMonitor extends EventEmitter {
/** /**
* Emit summary only if there were events. It might not have any events if it was forced via a page hidden/show * Emit summary only if there were events. It might not have any events if it was forced via a page hidden/show
*
* @private * @private
*/ */
_emitSummary () { _emitSummary () {
@ -156,7 +152,6 @@ class LatencyMonitor extends EventEmitter {
/** /**
* Calling this function will end the collection period. If a timing event was already fired and somewhere in the queue, * Calling this function will end the collection period. If a timing event was already fired and somewhere in the queue,
* it will not count for this time period * it will not count for this time period
*
* @returns {SummaryObject} * @returns {SummaryObject}
*/ */
getSummary () { getSummary () {

View File

@ -52,7 +52,6 @@ module.exports = class VisibilityChangeEmitter extends EventEmitter {
* the appropriate name based on the browser being used. Once executed, tha actual names of * the appropriate name based on the browser being used. Once executed, tha actual names of
* document.hidden and document.visibilityChange are found in this._hidden and this._visibilityChange * document.hidden and document.visibilityChange are found in this._hidden and this._visibilityChange
* respectively * respectively
*
* @private * @private
*/ */
_initializeVisibilityVarNames () { _initializeVisibilityVarNames () {
@ -78,7 +77,6 @@ module.exports = class VisibilityChangeEmitter extends EventEmitter {
/** /**
* Adds an event listener on the document that listens to changes in document.visibilityChange * Adds an event listener on the document that listens to changes in document.visibilityChange
* (or whatever name by which the visibilityChange variable is known in the browser) * (or whatever name by which the visibilityChange variable is known in the browser)
*
* @private * @private
*/ */
_addVisibilityChangeListener () { _addVisibilityChangeListener () {
@ -94,8 +92,7 @@ module.exports = class VisibilityChangeEmitter extends EventEmitter {
/** /**
* The function returns ```true``` if the page is visible or ```false``` if the page is not visible and * The function returns ```true``` if the page is visible or ```false``` if the page is not visible and
* ```undefined``` if the page visibility API is not supported by the browser. * ```undefined``` if the page visibility API is not supported by the browser.
* * @returns {Boolean|void} whether the page is now visible or not (undefined is unknown)
* @returns {boolean | void} whether the page is now visible or not (undefined is unknown)
*/ */
isVisible () { isVisible () {
if (this._hidden === undefined || document[this._hidden] === undefined) { if (this._hidden === undefined || document[this._hidden] === undefined) {

View File

@ -20,9 +20,9 @@ module.exports = (node) => {
* Iterates over all content routers in series to find providers of the given key. * Iterates over all content routers in series to find providers of the given key.
* Once a content router succeeds, iteration will stop. * Once a content router succeeds, iteration will stop.
* *
* @param {CID} key - The CID key of the content to find * @param {CID} key The CID key of the content to find
* @param {object} [options] * @param {object} [options]
* @param {number} [options.timeout] - How long the query should run * @param {number} [options.timeout] How long the query should run
* @param {number} [options.maxNumProviders] - maximum number of providers to find * @param {number} [options.maxNumProviders] - maximum number of providers to find
* @returns {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>} * @returns {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>}
*/ */
@ -51,7 +51,7 @@ module.exports = (node) => {
* Iterates over all content routers in parallel to notify it is * Iterates over all content routers in parallel to notify it is
* a provider of the given key. * a provider of the given key.
* *
* @param {CID} key - The CID key of the content to find * @param {CID} key The CID key of the content to find
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async provide (key) { // eslint-disable-line require-await async provide (key) { // eslint-disable-line require-await
@ -64,9 +64,8 @@ module.exports = (node) => {
/** /**
* Store the given key/value pair in the DHT. * Store the given key/value pair in the DHT.
* * @param {Buffer} key
* @param {Uint8Array} key * @param {Buffer} value
* @param {Uint8Array} value
* @param {Object} [options] - put options * @param {Object} [options] - put options
* @param {number} [options.minPeers] - minimum number of peers required to successfully put * @param {number} [options.minPeers] - minimum number of peers required to successfully put
* @returns {Promise<void>} * @returns {Promise<void>}
@ -82,11 +81,10 @@ module.exports = (node) => {
/** /**
* Get the value to the given key. * Get the value to the given key.
* Times out after 1 minute by default. * Times out after 1 minute by default.
* * @param {Buffer} key
* @param {Uint8Array} key
* @param {Object} [options] - get options * @param {Object} [options] - get options
* @param {number} [options.timeout] - optional timeout (default: 60000) * @param {number} [options.timeout] - optional timeout (default: 60000)
* @returns {Promise<{from: PeerId, val: Uint8Array}>} * @returns {Promise<{from: PeerId, val: Buffer}>}
*/ */
async get (key, options) { // eslint-disable-line require-await async get (key, options) { // eslint-disable-line require-await
if (!node.isStarted() || !dht.isStarted) { if (!node.isStarted() || !dht.isStarted) {
@ -98,12 +96,11 @@ module.exports = (node) => {
/** /**
* Get the `n` values to the given key without sorting. * Get the `n` values to the given key without sorting.
* * @param {Buffer} key
* @param {Uint8Array} key
* @param {number} nVals * @param {number} nVals
* @param {Object} [options] - get options * @param {Object} [options] - get options
* @param {number} [options.timeout] - optional timeout (default: 60000) * @param {number} [options.timeout] - optional timeout (default: 60000)
* @returns {Promise<Array<{from: PeerId, val: Uint8Array}>>} * @returns {Promise<Array<{from: PeerId, val: Buffer}>>}
*/ */
async getMany (key, nVals, options) { // eslint-disable-line require-await async getMany (key, nVals, options) { // eslint-disable-line require-await
if (!node.isStarted() || !dht.isStarted) { if (!node.isStarted() || !dht.isStarted) {

View File

@ -16,7 +16,6 @@ class DialRequest {
* from `dialer.getTokens`. Once a DialRequest is created, it can be * from `dialer.getTokens`. Once a DialRequest is created, it can be
* started using `DialRequest.run(options)`. Once a single dial has succeeded, * started using `DialRequest.run(options)`. Once a single dial has succeeded,
* all other dials in the request will be cancelled. * all other dials in the request will be cancelled.
*
* @param {object} options * @param {object} options
* @param {Multiaddr[]} options.addrs * @param {Multiaddr[]} options.addrs
* @param {function(Multiaddr):Promise<Connection>} options.dialAction * @param {function(Multiaddr):Promise<Connection>} options.dialAction
@ -35,7 +34,7 @@ class DialRequest {
/** /**
* @async * @async
* @param {object} options * @param {object} options
* @param {AbortSignal} options.signal - An AbortController signal * @param {AbortSignal} options.signal An AbortController signal
* @returns {Connection} * @returns {Connection}
*/ */
async run (options) { async run (options) {

View File

@ -20,22 +20,19 @@ const {
class Dialer { class Dialer {
/** /**
* @class * @constructor
* @param {object} options * @param {object} options
* @param {TransportManager} options.transportManager * @param {TransportManager} options.transportManager
* @param {Peerstore} options.peerStore * @param {Peerstore} peerStore
* @param {number} [options.concurrency = MAX_PARALLEL_DIALS] - Number of max concurrent dials. * @param {number} options.concurrency Number of max concurrent dials. Defaults to `MAX_PARALLEL_DIALS`
* @param {number} [options.perPeerLimit = MAX_PER_PEER_DIALS] - Number of max concurrent dials per peer. * @param {number} options.timeout How long a dial attempt is allowed to take. Defaults to `DIAL_TIMEOUT`
* @param {number} [options.timeout = DIAL_TIMEOUT] - How long a dial attempt is allowed to take.
* @param {object} [options.resolvers = {}] - multiaddr resolvers to use when dialing
*/ */
constructor ({ constructor ({
transportManager, transportManager,
peerStore, peerStore,
concurrency = MAX_PARALLEL_DIALS, concurrency = MAX_PARALLEL_DIALS,
timeout = DIAL_TIMEOUT, timeout = DIAL_TIMEOUT,
perPeerLimit = MAX_PER_PEER_DIALS, perPeerLimit = MAX_PER_PEER_DIALS
resolvers = {}
}) { }) {
this.transportManager = transportManager this.transportManager = transportManager
this.peerStore = peerStore this.peerStore = peerStore
@ -44,10 +41,6 @@ class Dialer {
this.perPeerLimit = perPeerLimit this.perPeerLimit = perPeerLimit
this.tokens = [...new Array(concurrency)].map((_, index) => index) this.tokens = [...new Array(concurrency)].map((_, index) => index)
this._pendingDials = new Map() this._pendingDials = new Map()
for (const [key, value] of Object.entries(resolvers)) {
multiaddr.resolvers.set(key, value)
}
} }
/** /**
@ -69,13 +62,13 @@ class Dialer {
* The dial to the first address that is successfully able to upgrade a connection * The dial to the first address that is successfully able to upgrade a connection
* will be used. * will be used.
* *
* @param {PeerId|Multiaddr|string} peer - The peer to dial * @param {PeerId|Multiaddr|string} peer The peer to dial
* @param {object} [options] * @param {object} [options]
* @param {AbortSignal} [options.signal] - An AbortController signal * @param {AbortSignal} [options.signal] An AbortController signal
* @returns {Promise<Connection>} * @returns {Promise<Connection>}
*/ */
async connectToPeer (peer, options = {}) { async connectToPeer (peer, options = {}) {
const dialTarget = await this._createDialTarget(peer) const dialTarget = this._createDialTarget(peer)
if (!dialTarget.addrs.length) { if (!dialTarget.addrs.length) {
throw errCode(new Error('The dial request has no addresses'), codes.ERR_NO_VALID_ADDRESSES) throw errCode(new Error('The dial request has no addresses'), codes.ERR_NO_VALID_ADDRESSES)
@ -108,31 +101,24 @@ class Dialer {
* Creates a DialTarget. The DialTarget is used to create and track * Creates a DialTarget. The DialTarget is used to create and track
* the DialRequest to a given peer. * the DialRequest to a given peer.
* If a multiaddr is received it should be the first address attempted. * If a multiaddr is received it should be the first address attempted.
*
* @private * @private
* @param {PeerId|Multiaddr|string} peer - A PeerId or Multiaddr * @param {PeerId|Multiaddr|string} peer A PeerId or Multiaddr
* @returns {Promise<DialTarget>} * @returns {DialTarget}
*/ */
async _createDialTarget (peer) { _createDialTarget (peer) {
const { id, multiaddrs } = getPeer(peer) const { id, multiaddrs } = getPeer(peer)
if (multiaddrs) { if (multiaddrs) {
this.peerStore.addressBook.add(id, multiaddrs) this.peerStore.addressBook.add(id, multiaddrs)
} }
let knownAddrs = this.peerStore.addressBook.getMultiaddrsForPeer(id) || [] let addrs = this.peerStore.addressBook.getMultiaddrsForPeer(id) || []
// If received a multiaddr to dial, it should be the first to use // If received a multiaddr to dial, it should be the first to use
// But, if we know other multiaddrs for the peer, we should try them too. // But, if we know other multiaddrs for the peer, we should try them too.
if (multiaddr.isMultiaddr(peer)) { if (multiaddr.isMultiaddr(peer)) {
knownAddrs = knownAddrs.filter((addr) => !peer.equals(addr)) addrs = addrs.filter((addr) => !peer.equals(addr))
knownAddrs.unshift(peer) addrs.unshift(peer)
}
const addrs = []
for (const a of knownAddrs) {
const resolvedAddrs = await this._resolve(a)
resolvedAddrs.forEach(ra => addrs.push(ra))
} }
return { return {
@ -151,11 +137,10 @@ class Dialer {
/** /**
* Creates a PendingDial that wraps the underlying DialRequest * Creates a PendingDial that wraps the underlying DialRequest
*
* @private * @private
* @param {DialTarget} dialTarget * @param {DialTarget} dialTarget
* @param {object} [options] * @param {object} [options]
* @param {AbortSignal} [options.signal] - An AbortController signal * @param {AbortSignal} [options.signal] An AbortController signal
* @returns {PendingDial} * @returns {PendingDial}
*/ */
_createPendingDial (dialTarget, options) { _createPendingDial (dialTarget, options) {
@ -202,52 +187,6 @@ class Dialer {
log('token %d released', token) log('token %d released', token)
this.tokens.push(token) this.tokens.push(token)
} }
/**
* Resolve multiaddr recursively.
*
* @param {Multiaddr} ma
* @returns {Promise<Array<Multiaddr>>}
*/
async _resolve (ma) {
// TODO: recursive logic should live in multiaddr once dns4/dns6 support is in place
// Now only supporting resolve for dnsaddr
const resolvableProto = ma.protoNames().includes('dnsaddr')
// Multiaddr is not resolvable? End recursion!
if (!resolvableProto) {
return [ma]
}
const resolvedMultiaddrs = await this._resolveRecord(ma)
const recursiveMultiaddrs = await Promise.all(resolvedMultiaddrs.map((nm) => {
return this._resolve(nm)
}))
return recursiveMultiaddrs.flat().reduce((array, newM) => {
if (!array.find(m => m.equals(newM))) {
array.push(newM)
}
return array
}, []) // Unique addresses
}
/**
* Resolve a given multiaddr. If this fails, an empty array will be returned
*
* @param {Multiaddr} ma
* @returns {Promise<Array<Multiaddr>>}
*/
async _resolveRecord (ma) {
try {
ma = multiaddr(ma.toString()) // Use current multiaddr module
const multiaddrs = await ma.resolve()
return multiaddrs
} catch (_) {
log.error(`multiaddr ${ma} could not be resolved`)
return []
}
}
} }
module.exports = Dialer module.exports = Dialer

View File

@ -2,15 +2,13 @@
exports.messages = { exports.messages = {
NOT_STARTED_YET: 'The libp2p node is not started yet', NOT_STARTED_YET: 'The libp2p node is not started yet',
DHT_DISABLED: 'DHT is not available', DHT_DISABLED: 'DHT is not available'
CONN_ENCRYPTION_REQUIRED: 'At least one connection encryption module is required'
} }
exports.codes = { exports.codes = {
DHT_DISABLED: 'ERR_DHT_DISABLED', DHT_DISABLED: 'ERR_DHT_DISABLED',
PUBSUB_NOT_STARTED: 'ERR_PUBSUB_NOT_STARTED', PUBSUB_NOT_STARTED: 'ERR_PUBSUB_NOT_STARTED',
DHT_NOT_STARTED: 'ERR_DHT_NOT_STARTED', DHT_NOT_STARTED: 'ERR_DHT_NOT_STARTED',
CONN_ENCRYPTION_REQUIRED: 'ERR_CONN_ENCRYPTION_REQUIRED',
ERR_CONNECTION_ENDED: 'ERR_CONNECTION_ENDED', ERR_CONNECTION_ENDED: 'ERR_CONNECTION_ENDED',
ERR_CONNECTION_FAILED: 'ERR_CONNECTION_FAILED', ERR_CONNECTION_FAILED: 'ERR_CONNECTION_FAILED',
ERR_NODE_NOT_STARTED: 'ERR_NODE_NOT_STARTED', ERR_NODE_NOT_STARTED: 'ERR_NODE_NOT_STARTED',
@ -29,6 +27,5 @@ exports.codes = {
ERR_TRANSPORT_UNAVAILABLE: 'ERR_TRANSPORT_UNAVAILABLE', ERR_TRANSPORT_UNAVAILABLE: 'ERR_TRANSPORT_UNAVAILABLE',
ERR_TRANSPORT_DIAL_FAILED: 'ERR_TRANSPORT_DIAL_FAILED', ERR_TRANSPORT_DIAL_FAILED: 'ERR_TRANSPORT_DIAL_FAILED',
ERR_UNSUPPORTED_PROTOCOL: 'ERR_UNSUPPORTED_PROTOCOL', ERR_UNSUPPORTED_PROTOCOL: 'ERR_UNSUPPORTED_PROTOCOL',
ERR_INVALID_MULTIADDR: 'ERR_INVALID_MULTIADDR', ERR_INVALID_MULTIADDR: 'ERR_INVALID_MULTIADDR'
ERR_SIGNATURE_NOT_VALID: 'ERR_SIGNATURE_NOT_VALID'
} }

View File

@ -9,8 +9,8 @@ const { codes } = require('./errors')
/** /**
* Converts the given `peer` to a `Peer` object. * Converts the given `peer` to a `Peer` object.
* If a multiaddr is received, the addressBook is updated. * If a multiaddr is received, the addressBook is updated.
*
* @param {PeerId|Multiaddr|string} peer * @param {PeerId|Multiaddr|string} peer
* @param {PeerStore} peerStore
* @returns {{ id: PeerId, multiaddrs: Array<Multiaddr> }} * @returns {{ id: PeerId, multiaddrs: Array<Multiaddr> }}
*/ */
function getPeer (peer) { function getPeer (peer) {

View File

@ -1,8 +1,6 @@
'use strict' 'use strict'
const libp2pVersion = require('../../package.json').version
module.exports.PROTOCOL_VERSION = 'ipfs/0.1.0' module.exports.PROTOCOL_VERSION = 'ipfs/0.1.0'
module.exports.AGENT_VERSION = `js-libp2p/${libp2pVersion}` module.exports.AGENT_VERSION = 'js-libp2p/0.1.0'
module.exports.MULTICODEC_IDENTIFY = '/ipfs/id/1.0.0' module.exports.MULTICODEC_IDENTIFY = '/ipfs/id/1.0.0'
module.exports.MULTICODEC_IDENTIFY_PUSH = '/ipfs/id/push/1.0.0' module.exports.MULTICODEC_IDENTIFY_PUSH = '/ipfs/id/push/1.0.0'

View File

@ -1,15 +1,11 @@
'use strict' 'use strict'
const { Buffer } = require('buffer')
const debug = require('debug') const debug = require('debug')
const log = debug('libp2p:identify')
log.error = debug('libp2p:identify:error')
const errCode = require('err-code')
const pb = require('it-protocol-buffers') const pb = require('it-protocol-buffers')
const lp = require('it-length-prefixed') const lp = require('it-length-prefixed')
const pipe = require('it-pipe') const pipe = require('it-pipe')
const { collect, take, consume } = require('streaming-iterables') const { collect, take, consume } = require('streaming-iterables')
const uint8ArrayFromString = require('uint8arrays/from-string')
const PeerId = require('peer-id') const PeerId = require('peer-id')
const multiaddr = require('multiaddr') const multiaddr = require('multiaddr')
@ -17,8 +13,8 @@ const { toBuffer } = require('it-buffer')
const Message = require('./message') const Message = require('./message')
const Envelope = require('../record/envelope') const log = debug('libp2p:identify')
const PeerRecord = require('../record/peer-record') log.error = debug('libp2p:identify:error')
const { const {
MULTICODEC_IDENTIFY, MULTICODEC_IDENTIFY,
@ -27,13 +23,13 @@ const {
PROTOCOL_VERSION PROTOCOL_VERSION
} = require('./consts') } = require('./consts')
const errCode = require('err-code')
const { codes } = require('../errors') const { codes } = require('../errors')
class IdentifyService { class IdentifyService {
/** /**
* Takes the `addr` and converts it to a Multiaddr if possible * Takes the `addr` and converts it to a Multiaddr if possible
* * @param {Buffer|String} addr
* @param {Uint8Array | string} addr
* @returns {Multiaddr|null} * @returns {Multiaddr|null}
*/ */
static getCleanMultiaddr (addr) { static getCleanMultiaddr (addr) {
@ -48,10 +44,10 @@ class IdentifyService {
} }
/** /**
* @class * @constructor
* @param {object} options * @param {object} options
* @param {Libp2p} options.libp2p * @param {Libp2p} options.libp2p
* @param {Map<string, handler>} options.protocols - A reference to the protocols we support * @param {Map<string, handler>} options.protocols A reference to the protocols we support
*/ */
constructor ({ libp2p, protocols }) { constructor ({ libp2p, protocols }) {
/** /**
@ -87,24 +83,18 @@ class IdentifyService {
/** /**
* Send an Identify Push update to the list of connections * Send an Identify Push update to the list of connections
*
* @param {Array<Connection>} connections * @param {Array<Connection>} connections
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async push (connections) { push (connections) {
const signedPeerRecord = await this._getSelfPeerRecord()
const listenAddrs = this._libp2p.multiaddrs.map((ma) => ma.bytes)
const protocols = Array.from(this._protocols.keys())
const pushes = connections.map(async connection => { const pushes = connections.map(async connection => {
try { try {
const { stream } = await connection.newStream(MULTICODEC_IDENTIFY_PUSH) const { stream } = await connection.newStream(MULTICODEC_IDENTIFY_PUSH)
await pipe( await pipe(
[{ [{
listenAddrs, listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer),
signedPeerRecord, protocols: Array.from(this._protocols.keys())
protocols
}], }],
pb.encode(Message), pb.encode(Message),
stream, stream,
@ -121,7 +111,6 @@ class IdentifyService {
/** /**
* Calls `push` for all peers in the `peerStore` that are connected * Calls `push` for all peers in the `peerStore` that are connected
*
* @param {PeerStore} peerStore * @param {PeerStore} peerStore
*/ */
pushToPeerStore (peerStore) { pushToPeerStore (peerStore) {
@ -171,8 +160,7 @@ class IdentifyService {
publicKey, publicKey,
listenAddrs, listenAddrs,
protocols, protocols,
observedAddr, observedAddr
signedPeerRecord
} = message } = message
const id = await PeerId.createFromPubKey(publicKey) const id = await PeerId.createFromPubKey(publicKey)
@ -184,25 +172,9 @@ class IdentifyService {
// Get the observedAddr if there is one // Get the observedAddr if there is one
observedAddr = IdentifyService.getCleanMultiaddr(observedAddr) observedAddr = IdentifyService.getCleanMultiaddr(observedAddr)
try { // Update peers data in PeerStore
const envelope = await Envelope.openAndCertify(signedPeerRecord, PeerRecord.DOMAIN)
if (this.peerStore.addressBook.consumePeerRecord(envelope)) {
this.peerStore.protoBook.set(id, protocols)
return
}
} catch (err) {
log('received invalid envelope, discard it and fallback to listenAddrs is available', err)
}
// LEGACY: Update peers data in PeerStore
try {
this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr))) this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr)))
} catch (err) {
log.error('received invalid addrs', err)
}
this.peerStore.protoBook.set(id, protocols) this.peerStore.protoBook.set(id, protocols)
this.peerStore.metadataBook.set(id, 'AgentVersion', uint8ArrayFromString(message.agentVersion))
// TODO: Track our observed address so that we can score it // TODO: Track our observed address so that we can score it
log('received observed address of %s', observedAddr) log('received observed address of %s', observedAddr)
@ -212,7 +184,7 @@ class IdentifyService {
* A handler to register with Libp2p to process identify messages. * A handler to register with Libp2p to process identify messages.
* *
* @param {object} options * @param {object} options
* @param {string} options.protocol * @param {String} options.protocol
* @param {*} options.stream * @param {*} options.stream
* @param {Connection} options.connection * @param {Connection} options.connection
* @returns {Promise<void>} * @returns {Promise<void>}
@ -229,29 +201,25 @@ class IdentifyService {
} }
/** /**
* Sends the `Identify` response with the Signed Peer Record * Sends the `Identify` response to the requesting peer over the
* to the requesting peer over the given `connection` * given `connection`
*
* @private * @private
* @param {object} options * @param {object} options
* @param {*} options.stream * @param {*} options.stream
* @param {Connection} options.connection * @param {Connection} options.connection
*/ */
async _handleIdentify ({ connection, stream }) { async _handleIdentify ({ connection, stream }) {
let publicKey = new Uint8Array(0) let publicKey = Buffer.alloc(0)
if (this.peerId.pubKey) { if (this.peerId.pubKey) {
publicKey = this.peerId.pubKey.bytes publicKey = this.peerId.pubKey.bytes
} }
const signedPeerRecord = await this._getSelfPeerRecord()
const message = Message.encode({ const message = Message.encode({
protocolVersion: PROTOCOL_VERSION, protocolVersion: PROTOCOL_VERSION,
agentVersion: AGENT_VERSION, agentVersion: AGENT_VERSION,
publicKey, publicKey,
listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.bytes), listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer),
signedPeerRecord, observedAddr: connection.remoteAddr.buffer,
observedAddr: connection.remoteAddr.bytes,
protocols: Array.from(this._protocols.keys()) protocols: Array.from(this._protocols.keys())
}) })
@ -269,7 +237,6 @@ class IdentifyService {
/** /**
* Reads the Identify Push message from the given `connection` * Reads the Identify Push message from the given `connection`
*
* @private * @private
* @param {object} options * @param {object} options
* @param {*} options.stream * @param {*} options.stream
@ -291,62 +258,22 @@ class IdentifyService {
return log.error('received invalid message', err) return log.error('received invalid message', err)
} }
// Update peers data in PeerStore
const id = connection.remotePeer const id = connection.remotePeer
try {
const envelope = await Envelope.openAndCertify(message.signedPeerRecord, PeerRecord.DOMAIN)
if (this.peerStore.addressBook.consumePeerRecord(envelope)) {
this.peerStore.protoBook.set(id, message.protocols)
return
}
} catch (err) {
log('received invalid envelope, discard it and fallback to listenAddrs is available', err)
}
// LEGACY: Update peers data in PeerStore
try { try {
this.peerStore.addressBook.set(id, message.listenAddrs.map((addr) => multiaddr(addr))) this.peerStore.addressBook.set(id, message.listenAddrs.map((addr) => multiaddr(addr)))
} catch (err) { } catch (err) {
log.error('received invalid addrs', err) return log.error('received invalid listen addrs', err)
} }
// Update the protocols // Update the protocols
this.peerStore.protoBook.set(id, message.protocols) this.peerStore.protoBook.set(id, message.protocols)
} }
/**
* Get self signed peer record raw envelope.
*
* @returns {Uint8Array}
*/
async _getSelfPeerRecord () {
const selfSignedPeerRecord = this.peerStore.addressBook.getRawEnvelope(this.peerId)
// TODO: support invalidation when dynamic multiaddrs are supported
if (selfSignedPeerRecord) {
return selfSignedPeerRecord
}
try {
const peerRecord = new PeerRecord({
peerId: this.peerId,
multiaddrs: this._libp2p.multiaddrs
})
const envelope = await Envelope.seal(peerRecord, this.peerId)
this.peerStore.addressBook.consumePeerRecord(envelope)
return this.peerStore.addressBook.getRawEnvelope(this.peerId)
} catch (err) {
log.error('failed to get self peer record')
}
return null
}
} }
module.exports.IdentifyService = IdentifyService module.exports.IdentifyService = IdentifyService
/** /**
* The protocols the IdentifyService supports * The protocols the IdentifyService supports
*
* @property multicodecs * @property multicodecs
*/ */
module.exports.multicodecs = { module.exports.multicodecs = {

View File

@ -24,11 +24,6 @@ message Identify {
optional bytes observedAddr = 4; optional bytes observedAddr = 4;
repeated string protocols = 3; repeated string protocols = 3;
// signedPeerRecord contains a serialized SignedEnvelope containing a PeerRecord,
// signed by the sending node. It contains the same addresses as the listenAddrs field, but
// in a form that lets us share authenticated addrs with other peers.
optional bytes signedPeerRecord = 8;
} }
` `

View File

@ -6,14 +6,14 @@ const globalThis = require('ipfs-utils/src/globalthis')
const log = debug('libp2p') const log = debug('libp2p')
log.error = debug('libp2p:error') log.error = debug('libp2p:error')
const errCode = require('err-code')
const PeerId = require('peer-id') const PeerId = require('peer-id')
const peerRouting = require('./peer-routing') const peerRouting = require('./peer-routing')
const contentRouting = require('./content-routing') const contentRouting = require('./content-routing')
const pubsub = require('./pubsub')
const getPeer = require('./get-peer') const getPeer = require('./get-peer')
const { validate: validateConfig } = require('./config') const { validate: validateConfig } = require('./config')
const { codes, messages } = require('./errors') const { codes } = require('./errors')
const AddressManager = require('./address-manager') const AddressManager = require('./address-manager')
const ConnectionManager = require('./connection-manager') const ConnectionManager = require('./connection-manager')
@ -24,7 +24,6 @@ const Metrics = require('./metrics')
const TransportManager = require('./transport-manager') const TransportManager = require('./transport-manager')
const Upgrader = require('./upgrader') const Upgrader = require('./upgrader')
const PeerStore = require('./peer-store') const PeerStore = require('./peer-store')
const PubsubAdapter = require('./pubsub-adapter')
const PersistentPeerStore = require('./peer-store/persistent') const PersistentPeerStore = require('./peer-store/persistent')
const Registrar = require('./registrar') const Registrar = require('./registrar')
const ping = require('./ping') const ping = require('./ping')
@ -35,6 +34,8 @@ const {
/** /**
* @fires Libp2p#error Emitted when an error occurs * @fires Libp2p#error Emitted when an error occurs
* @fires Libp2p#peer:connect Emitted when a peer is connected to this node
* @fires Libp2p#peer:disconnect Emitted when a peer disconnects from this node
* @fires Libp2p#peer:discovery Emitted when a peer is discovered * @fires Libp2p#peer:discovery Emitted when a peer is discovered
*/ */
class Libp2p extends EventEmitter { class Libp2p extends EventEmitter {
@ -49,11 +50,10 @@ class Libp2p extends EventEmitter {
this.peerStore = (this.datastore && this._options.peerStore.persistence) this.peerStore = (this.datastore && this._options.peerStore.persistence)
? new PersistentPeerStore({ ? new PersistentPeerStore({
peerId: this.peerId,
datastore: this.datastore, datastore: this.datastore,
...this._options.peerStore ...this._options.peerStore
}) })
: new PeerStore({ peerId: this.peerId }) : new PeerStore()
// Addresses {listen, announce, noAnnounce} // Addresses {listen, announce, noAnnounce}
this.addresses = this._options.addresses this.addresses = this._options.addresses
@ -82,7 +82,7 @@ class Libp2p extends EventEmitter {
} }
// Create keychain // Create keychain
if (this._options.keychain && this._options.keychain.datastore) { if (this._options.keychain && this._options.keychain.pass && this._options.keychain.datastore) {
log('creating keychain') log('creating keychain')
const keychainOpts = Keychain.generateOptions() const keychainOpts = Keychain.generateOptions()
@ -121,21 +121,19 @@ class Libp2p extends EventEmitter {
this.registrar.handle = this.handle this.registrar.handle = this.handle
// Attach crypto channels // Attach crypto channels
if (!this._modules.connEncryption || !this._modules.connEncryption.length) { if (this._modules.connEncryption) {
throw errCode(new Error(messages.CONN_ENCRYPTION_REQUIRED), codes.CONN_ENCRYPTION_REQUIRED)
}
const cryptos = this._modules.connEncryption const cryptos = this._modules.connEncryption
cryptos.forEach((crypto) => { cryptos.forEach((crypto) => {
this.upgrader.cryptos.set(crypto.protocol, crypto) this.upgrader.cryptos.set(crypto.protocol, crypto)
}) })
}
this.dialer = new Dialer({ this.dialer = new Dialer({
transportManager: this.transportManager, transportManager: this.transportManager,
peerStore: this.peerStore, peerStore: this.peerStore,
concurrency: this._options.dialer.maxParallelDials, concurrency: this._options.dialer.maxParallelDials,
perPeerLimit: this._options.dialer.maxDialsPerPeer, perPeerLimit: this._options.dialer.maxDialsPerPeer,
timeout: this._options.dialer.dialTimeout, timeout: this._options.dialer.dialTimeout
resolvers: this._options.dialer.resolvers
}) })
this._modules.transport.forEach((Transport) => { this._modules.transport.forEach((Transport) => {
@ -184,11 +182,9 @@ class Libp2p extends EventEmitter {
}) })
} }
// Create pubsub if provided // start pubsub
if (this._modules.pubsub) { if (this._modules.pubsub) {
const Pubsub = this._modules.pubsub this.pubsub = pubsub(this, this._modules.pubsub, this._config.pubsub)
// using pubsub adapter with *DEPRECATED* handlers functionality
this.pubsub = PubsubAdapter(Pubsub, this, this._config.pubsub)
} }
// Attach remaining APIs // Attach remaining APIs
@ -205,7 +201,6 @@ class Libp2p extends EventEmitter {
/** /**
* Overrides EventEmitter.emit to conditionally emit errors * Overrides EventEmitter.emit to conditionally emit errors
* if there is a handler. If not, errors will be logged. * if there is a handler. If not, errors will be logged.
*
* @param {string} eventName * @param {string} eventName
* @param {...any} args * @param {...any} args
* @returns {void} * @returns {void}
@ -240,7 +235,6 @@ class Libp2p extends EventEmitter {
/** /**
* Stop the libp2p node by closing its listeners and open connections * Stop the libp2p node by closing its listeners and open connections
*
* @async * @async
* @returns {void} * @returns {void}
*/ */
@ -282,7 +276,6 @@ class Libp2p extends EventEmitter {
/** /**
* Load keychain keys from the datastore. * Load keychain keys from the datastore.
* Imports the private key as 'self', if needed. * Imports the private key as 'self', if needed.
*
* @async * @async
* @returns {void} * @returns {void}
*/ */
@ -301,7 +294,6 @@ class Libp2p extends EventEmitter {
/** /**
* Gets a Map of the current connections. The keys are the stringified * Gets a Map of the current connections. The keys are the stringified
* `PeerId` of the peer. The value is an array of Connections to that peer. * `PeerId` of the peer. The value is an array of Connections to that peer.
*
* @returns {Map<string, Connection[]>} * @returns {Map<string, Connection[]>}
*/ */
get connections () { get connections () {
@ -311,8 +303,7 @@ class Libp2p extends EventEmitter {
/** /**
* Dials to the provided peer. If successful, the known metadata of the * Dials to the provided peer. If successful, the known metadata of the
* peer will be added to the nodes `peerStore` * peer will be added to the nodes `peerStore`
* * @param {PeerId|Multiaddr|string} peer The peer to dial
* @param {PeerId|Multiaddr|string} peer - The peer to dial
* @param {object} options * @param {object} options
* @param {AbortSignal} [options.signal] * @param {AbortSignal} [options.signal]
* @returns {Promise<Connection>} * @returns {Promise<Connection>}
@ -325,9 +316,8 @@ class Libp2p extends EventEmitter {
* Dials to the provided peer and handshakes with the given protocol. * Dials to the provided peer and handshakes with the given protocol.
* If successful, the known metadata of the peer will be added to the nodes `peerStore`, * If successful, the known metadata of the peer will be added to the nodes `peerStore`,
* and the `Connection` will be returned * and the `Connection` will be returned
*
* @async * @async
* @param {PeerId|Multiaddr|string} peer - The peer to dial * @param {PeerId|Multiaddr|string} peer The peer to dial
* @param {string[]|string} protocols * @param {string[]|string} protocols
* @param {object} options * @param {object} options
* @param {AbortSignal} [options.signal] * @param {AbortSignal} [options.signal]
@ -355,8 +345,7 @@ class Libp2p extends EventEmitter {
* Get peer advertising multiaddrs by concating the addresses used * Get peer advertising multiaddrs by concating the addresses used
* by transports to listen with the announce addresses. * by transports to listen with the announce addresses.
* Duplicated addresses and noAnnounce addresses are filtered out. * Duplicated addresses and noAnnounce addresses are filtered out.
* * @return {Array<Multiaddr>}
* @returns {Array<Multiaddr>}
*/ */
get multiaddrs () { get multiaddrs () {
// Filter noAnnounce multiaddrs // Filter noAnnounce multiaddrs
@ -382,8 +371,7 @@ class Libp2p extends EventEmitter {
/** /**
* Disconnects all connections to the given `peer` * Disconnects all connections to the given `peer`
* * @param {PeerId|multiaddr|string} peer the peer to close connections to
* @param {PeerId|multiaddr|string} peer - the peer to close connections to
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async hangUp (peer) { async hangUp (peer) {
@ -404,8 +392,7 @@ class Libp2p extends EventEmitter {
/** /**
* Pings the given peer in order to obtain the operation latency. * Pings the given peer in order to obtain the operation latency.
* * @param {PeerId|Multiaddr|string} peer The peer to ping
* @param {PeerId|Multiaddr|string} peer - The peer to ping
* @returns {Promise<number>} * @returns {Promise<number>}
*/ */
ping (peer) { ping (peer) {
@ -421,7 +408,6 @@ class Libp2p extends EventEmitter {
/** /**
* Registers the `handler` for each protocol * Registers the `handler` for each protocol
*
* @param {string[]|string} protocols * @param {string[]|string} protocols
* @param {function({ connection:*, stream:*, protocol:string })} handler * @param {function({ connection:*, stream:*, protocol:string })} handler
*/ */
@ -440,7 +426,6 @@ class Libp2p extends EventEmitter {
/** /**
* Removes the handler for each protocol. The protocol * Removes the handler for each protocol. The protocol
* will no longer be supported on streams. * will no longer be supported on streams.
*
* @param {string[]|string} protocols * @param {string[]|string} protocols
*/ */
unhandle (protocols) { unhandle (protocols) {
@ -481,7 +466,6 @@ class Libp2p extends EventEmitter {
/** /**
* Called when libp2p has started and before it returns * Called when libp2p has started and before it returns
*
* @private * @private
*/ */
async _onDidStart () { async _onDidStart () {
@ -507,7 +491,6 @@ class Libp2p extends EventEmitter {
/** /**
* Called whenever peer discovery services emit `peer` events. * Called whenever peer discovery services emit `peer` events.
* Known peers may be emitted. * Known peers may be emitted.
*
* @private * @private
* @param {{ id: PeerId, multiaddrs: Array<Multiaddr>, protocols: Array<string> }} peer * @param {{ id: PeerId, multiaddrs: Array<Multiaddr>, protocols: Array<string> }} peer
*/ */
@ -525,7 +508,6 @@ class Libp2p extends EventEmitter {
* Will dial to the given `peerId` if the current number of * Will dial to the given `peerId` if the current number of
* connected peers is less than the configured `ConnectionManager` * connected peers is less than the configured `ConnectionManager`
* minConnections. * minConnections.
*
* @private * @private
* @param {PeerId} peerId * @param {PeerId} peerId
*/ */
@ -599,8 +581,7 @@ class Libp2p extends EventEmitter {
/** /**
* Like `new Libp2p(options)` except it will create a `PeerId` * Like `new Libp2p(options)` except it will create a `PeerId`
* instance if one is not provided in options. * instance if one is not provided in options.
* * @param {object} options Libp2p configuration options
* @param {object} options - Libp2p configuration options
* @returns {Libp2p} * @returns {Libp2p}
*/ */
Libp2p.create = async function create (options = {}) { Libp2p.create = async function create (options = {}) {

View File

@ -43,7 +43,7 @@ async function encrypt (localId, conn, remoteId) {
throw new InvalidCryptoExchangeError('Remote did not provide its public key') throw new InvalidCryptoExchangeError('Remote did not provide its public key')
} }
if (remoteId && !peerId.equals(remoteId)) { if (remoteId && !peerId.isEqual(remoteId)) {
throw new UnexpectedPeerError() throw new UnexpectedPeerError()
} }

View File

@ -5,8 +5,6 @@ require('node-forge/lib/pbe')
const forge = require('node-forge/lib/forge') const forge = require('node-forge/lib/forge')
const { certificateForKey, findAsync } = require('./util') const { certificateForKey, findAsync } = require('./util')
const errcode = require('err-code') const errcode = require('err-code')
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayToString = require('uint8arrays/to-string')
/** /**
* Cryptographic Message Syntax (aka PKCS #7) * Cryptographic Message Syntax (aka PKCS #7)
@ -34,15 +32,15 @@ class CMS {
/** /**
* Creates some protected data. * Creates some protected data.
* *
* The output Uint8Array contains the PKCS #7 message in DER. * The output Buffer contains the PKCS #7 message in DER.
* *
* @param {string} name - The local key name. * @param {string} name - The local key name.
* @param {Uint8Array} plain - The data to encrypt. * @param {Buffer} plain - The data to encrypt.
* @returns {undefined} * @returns {undefined}
*/ */
async encrypt (name, plain) { async encrypt (name, plain) {
if (!(plain instanceof Uint8Array)) { if (!Buffer.isBuffer(plain)) {
throw errcode(new Error('Plain data must be a Uint8Array'), 'ERR_INVALID_PARAMS') throw errcode(new Error('Plain data must be a Buffer'), 'ERR_INVALID_PARAMS')
} }
const key = await this.keychain.findKeyByName(name) const key = await this.keychain.findKeyByName(name)
@ -58,7 +56,7 @@ class CMS {
// convert message to DER // convert message to DER
const der = forge.asn1.toDer(p7.toAsn1()).getBytes() const der = forge.asn1.toDer(p7.toAsn1()).getBytes()
return uint8ArrayFromString(der, 'ascii') return Buffer.from(der, 'binary')
} }
/** /**
@ -67,17 +65,17 @@ class CMS {
* The keychain must contain one of the keys used to encrypt the data. If none of the keys * The keychain must contain one of the keys used to encrypt the data. If none of the keys
* exists, an Error is returned with the property 'missingKeys'. It is array of key ids. * exists, an Error is returned with the property 'missingKeys'. It is array of key ids.
* *
* @param {Uint8Array} cmsData - The CMS encrypted data to decrypt. * @param {Buffer} cmsData - The CMS encrypted data to decrypt.
* @returns {undefined} * @returns {undefined}
*/ */
async decrypt (cmsData) { async decrypt (cmsData) {
if (!(cmsData instanceof Uint8Array)) { if (!Buffer.isBuffer(cmsData)) {
throw errcode(new Error('CMS data is required'), 'ERR_INVALID_PARAMS') throw errcode(new Error('CMS data is required'), 'ERR_INVALID_PARAMS')
} }
let cms let cms
try { try {
const buf = forge.util.createBuffer(uint8ArrayToString(cmsData, 'ascii')) const buf = forge.util.createBuffer(cmsData.toString('binary'))
const obj = forge.asn1.fromDer(buf) const obj = forge.asn1.fromDer(buf)
cms = forge.pkcs7.messageFromAsn1(obj) cms = forge.pkcs7.messageFromAsn1(obj)
} catch (err) { } catch (err) {
@ -117,7 +115,7 @@ class CMS {
const pem = await this.keychain._getPrivateKey(key.name) const pem = await this.keychain._getPrivateKey(key.name)
const privateKey = forge.pki.decryptRsaPrivateKey(pem, this.keychain._()) const privateKey = forge.pki.decryptRsaPrivateKey(pem, this.keychain._())
cms.decrypt(r.recipient, privateKey) cms.decrypt(r.recipient, privateKey)
return uint8ArrayFromString(cms.content.getBytes(), 'ascii') return Buffer.from(cms.content.getBytes(), 'binary')
} }
} }

View File

@ -8,8 +8,6 @@ const DS = require('interface-datastore')
const CMS = require('./cms') const CMS = require('./cms')
const errcode = require('err-code') const errcode = require('err-code')
const { Number } = require('ipfs-utils/src/globalthis') const { Number } = require('ipfs-utils/src/globalthis')
const uint8ArrayToString = require('uint8arrays/to-string')
const uint8ArrayFromString = require('uint8arrays/from-string')
require('node-forge/lib/sha512') require('node-forge/lib/sha512')
@ -112,7 +110,7 @@ class Keychain {
this.opts = mergeOptions(defaultOptions, options) this.opts = mergeOptions(defaultOptions, options)
// Enforce NIST SP 800-132 // Enforce NIST SP 800-132
if (this.opts.passPhrase && this.opts.passPhrase.length < 20) { if (!this.opts.passPhrase || this.opts.passPhrase.length < 20) {
throw new Error('passPhrase must be least 20 characters') throw new Error('passPhrase must be least 20 characters')
} }
if (this.opts.dek.keyLength < NIST.minKeyLength) { if (this.opts.dek.keyLength < NIST.minKeyLength) {
@ -125,13 +123,13 @@ class Keychain {
throw new Error(`dek.iterationCount must be least ${NIST.minIterationCount}`) throw new Error(`dek.iterationCount must be least ${NIST.minIterationCount}`)
} }
const dek = this.opts.passPhrase ? crypto.pbkdf2( // Create the derived encrypting key
const dek = crypto.pbkdf2(
this.opts.passPhrase, this.opts.passPhrase,
this.opts.dek.salt, this.opts.dek.salt,
this.opts.dek.iterationCount, this.opts.dek.iterationCount,
this.opts.dek.keyLength, this.opts.dek.keyLength,
this.opts.dek.hash) : '' this.opts.dek.hash)
Object.defineProperty(this, '_', { value: () => dek }) Object.defineProperty(this, '_', { value: () => dek })
} }
@ -157,7 +155,7 @@ class Keychain {
static generateOptions () { static generateOptions () {
const options = Object.assign({}, defaultOptions) const options = Object.assign({}, defaultOptions)
const saltLength = Math.ceil(NIST.minSaltLength / 3) * 3 // no base64 padding const saltLength = Math.ceil(NIST.minSaltLength / 3) * 3 // no base64 padding
options.dek.salt = uint8ArrayToString(crypto.randomBytes(saltLength), 'base64') options.dek.salt = crypto.randomBytes(saltLength).toString('base64')
return options return options
} }
@ -214,8 +212,8 @@ class Keychain {
id: kid id: kid
} }
const batch = self.store.batch() const batch = self.store.batch()
batch.put(dsname, uint8ArrayFromString(pem)) batch.put(dsname, pem)
batch.put(DsInfoName(name), uint8ArrayFromString(JSON.stringify(keyInfo))) batch.put(DsInfoName(name), JSON.stringify(keyInfo))
await batch.commit() await batch.commit()
} catch (err) { } catch (err) {
@ -238,7 +236,7 @@ class Keychain {
const info = [] const info = []
for await (const value of self.store.query(query)) { for await (const value of self.store.query(query)) {
info.push(JSON.parse(uint8ArrayToString(value.value))) info.push(JSON.parse(value.value))
} }
return info return info
@ -273,7 +271,7 @@ class Keychain {
const dsname = DsInfoName(name) const dsname = DsInfoName(name)
try { try {
const res = await this.store.get(dsname) const res = await this.store.get(dsname)
return JSON.parse(uint8ArrayToString(res)) return JSON.parse(res.toString())
} catch (err) { } catch (err) {
return throwDelayed(errcode(new Error(`Key '${name}' does not exist. ${err.message}`), 'ERR_KEY_NOT_FOUND')) return throwDelayed(errcode(new Error(`Key '${name}' does not exist. ${err.message}`), 'ERR_KEY_NOT_FOUND'))
} }
@ -323,14 +321,15 @@ class Keychain {
if (exists) return throwDelayed(errcode(new Error(`Key '${newName}' already exists`), 'ERR_KEY_ALREADY_EXISTS')) if (exists) return throwDelayed(errcode(new Error(`Key '${newName}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))
try { try {
const pem = await self.store.get(oldDsname) let res = await this.store.get(oldDsname)
const res = await self.store.get(oldInfoName) const pem = res.toString()
res = await self.store.get(oldInfoName)
const keyInfo = JSON.parse(uint8ArrayToString(res)) const keyInfo = JSON.parse(res.toString())
keyInfo.name = newName keyInfo.name = newName
const batch = self.store.batch() const batch = self.store.batch()
batch.put(newDsname, pem) batch.put(newDsname, pem)
batch.put(newInfoName, uint8ArrayFromString(JSON.stringify(keyInfo))) batch.put(newInfoName, JSON.stringify(keyInfo))
batch.delete(oldDsname) batch.delete(oldDsname)
batch.delete(oldInfoName) batch.delete(oldInfoName)
await batch.commit() await batch.commit()
@ -358,7 +357,7 @@ class Keychain {
const dsname = DsName(name) const dsname = DsName(name)
try { try {
const res = await this.store.get(dsname) const res = await this.store.get(dsname)
const pem = uint8ArrayToString(res) const pem = res.toString()
const privateKey = await crypto.keys.import(pem, this._()) const privateKey = await crypto.keys.import(pem, this._())
return privateKey.export(password) return privateKey.export(password)
} catch (err) { } catch (err) {
@ -406,8 +405,8 @@ class Keychain {
id: kid id: kid
} }
const batch = self.store.batch() const batch = self.store.batch()
batch.put(dsname, uint8ArrayFromString(pem)) batch.put(dsname, pem)
batch.put(DsInfoName(name), uint8ArrayFromString(JSON.stringify(keyInfo))) batch.put(DsInfoName(name), JSON.stringify(keyInfo))
await batch.commit() await batch.commit()
return keyInfo return keyInfo
@ -435,8 +434,8 @@ class Keychain {
id: kid id: kid
} }
const batch = self.store.batch() const batch = self.store.batch()
batch.put(dsname, uint8ArrayFromString(pem)) batch.put(dsname, pem)
batch.put(DsInfoName(name), uint8ArrayFromString(JSON.stringify(keyInfo))) batch.put(DsInfoName(name), JSON.stringify(keyInfo))
await batch.commit() await batch.commit()
return keyInfo return keyInfo
} catch (err) { } catch (err) {
@ -459,7 +458,7 @@ class Keychain {
try { try {
const dsname = DsName(name) const dsname = DsName(name)
const res = await this.store.get(dsname) const res = await this.store.get(dsname)
return uint8ArrayToString(res) return res.toString()
} catch (err) { } catch (err) {
return throwDelayed(errcode(new Error(`Key '${name}' does not exist. ${err.message}`), 'ERR_KEY_NOT_FOUND')) return throwDelayed(errcode(new Error(`Key '${name}' does not exist. ${err.message}`), 'ERR_KEY_NOT_FOUND'))
} }

View File

@ -8,13 +8,13 @@ exports = module.exports
/** /**
* Gets a self-signed X.509 certificate for the key. * Gets a self-signed X.509 certificate for the key.
* *
* The output Uint8Array contains the PKCS #7 message in DER. * The output Buffer contains the PKCS #7 message in DER.
* *
* TODO: move to libp2p-crypto package * TODO: move to libp2p-crypto package
* *
* @param {KeyInfo} key - The id and name of the key * @param {KeyInfo} key - The id and name of the key
* @param {RsaPrivateKey} privateKey - The naked key * @param {RsaPrivateKey} privateKey - The naked key
* @returns {Uint8Array} * @returns {undefined}
*/ */
exports.certificateForKey = (key, privateKey) => { exports.certificateForKey = (key, privateKey) => {
const publicKey = pki.setRsaPublicKey(privateKey.n, privateKey.e) const publicKey = pki.setRsaPublicKey(privateKey.n, privateKey.e)
@ -77,7 +77,7 @@ exports.certificateForKey = (key, privateKey) => {
* resolve to either `true` or `false`. * resolve to either `true` or `false`.
* *
* @param {Array} array * @param {Array} array
* @param {function(*)} asyncCompare - An async function that returns a boolean * @param {function(*)} asyncCompare An async function that returns a boolean
*/ */
async function findAsync (array, asyncCompare) { async function findAsync (array, asyncCompare) {
const promises = array.map(asyncCompare) const promises = array.map(asyncCompare)

View File

@ -66,7 +66,6 @@ class Metrics {
/** /**
* Gets the global `Stats` object * Gets the global `Stats` object
*
* @returns {Stats} * @returns {Stats}
*/ */
get global () { get global () {
@ -75,7 +74,6 @@ class Metrics {
/** /**
* Returns a list of `PeerId` strings currently being tracked * Returns a list of `PeerId` strings currently being tracked
*
* @returns {Array<string>} * @returns {Array<string>}
*/ */
get peers () { get peers () {
@ -85,7 +83,6 @@ class Metrics {
/** /**
* Returns the `Stats` object for the given `PeerId` whether it * Returns the `Stats` object for the given `PeerId` whether it
* is a live peer, or in the disconnected peer LRU cache. * is a live peer, or in the disconnected peer LRU cache.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {Stats} * @returns {Stats}
*/ */
@ -96,7 +93,6 @@ class Metrics {
/** /**
* Returns a list of all protocol strings currently being tracked. * Returns a list of all protocol strings currently being tracked.
*
* @returns {Array<string>} * @returns {Array<string>}
*/ */
get protocols () { get protocols () {
@ -105,7 +101,6 @@ class Metrics {
/** /**
* Returns the `Stats` object for the given `protocol`. * Returns the `Stats` object for the given `protocol`.
*
* @param {string} protocol * @param {string} protocol
* @returns {Stats} * @returns {Stats}
*/ */
@ -117,7 +112,6 @@ class Metrics {
* Should be called when all connections to a given peer * Should be called when all connections to a given peer
* have closed. The `Stats` collection for the peer will * have closed. The `Stats` collection for the peer will
* be stopped and moved to an LRU for temporary retention. * be stopped and moved to an LRU for temporary retention.
*
* @param {PeerId} peerId * @param {PeerId} peerId
*/ */
onPeerDisconnected (peerId) { onPeerDisconnected (peerId) {
@ -137,10 +131,10 @@ class Metrics {
* *
* @private * @private
* @param {object} params * @param {object} params
* @param {PeerId} params.remotePeer - Remote peer * @param {PeerId} params.remotePeer Remote peer
* @param {string} [params.protocol] - Protocol string the stream is running * @param {string} [params.protocol] Protocol string the stream is running
* @param {string} params.direction - One of ['in','out'] * @param {string} params.direction One of ['in','out']
* @param {number} params.dataLength - Size of the message * @param {number} params.dataLength Size of the message
* @returns {void} * @returns {void}
*/ */
_onMessage ({ remotePeer, protocol, direction, dataLength }) { _onMessage ({ remotePeer, protocol, direction, dataLength }) {
@ -173,8 +167,7 @@ class Metrics {
* Replaces the `PeerId` string with the given `peerId`. * Replaces the `PeerId` string with the given `peerId`.
* If stats are already being tracked for the given `peerId`, the * If stats are already being tracked for the given `peerId`, the
* placeholder stats will be merged with the existing stats. * placeholder stats will be merged with the existing stats.
* * @param {PeerId} placeholder A peerId string
* @param {PeerId} placeholder - A peerId string
* @param {PeerId} peerId * @param {PeerId} peerId
*/ */
updatePlaceholder (placeholder, peerId) { updatePlaceholder (placeholder, peerId) {
@ -205,9 +198,9 @@ class Metrics {
* with the placeholder string returned from here, and the known `PeerId`. * with the placeholder string returned from here, and the known `PeerId`.
* *
* @param {Object} options * @param {Object} options
* @param {{ sink: function(*), source: function() }} options.stream - A duplex iterable stream * @param {{ sink: function(*), source: function() }} options.stream A duplex iterable stream
* @param {PeerId} [options.remotePeer] - The id of the remote peer that's connected * @param {PeerId} [options.peerId] The id of the remote peer that's connected
* @param {string} [options.protocol] - The protocol the stream is running * @param {string} [options.protocol] The protocol the stream is running
* @returns {string} The peerId string or placeholder string * @returns {string} The peerId string or placeholder string
*/ */
trackStream ({ stream, remotePeer, protocol }) { trackStream ({ stream, remotePeer, protocol }) {
@ -240,7 +233,6 @@ class Metrics {
/** /**
* Merges `other` into `target`. `target` will be modified * Merges `other` into `target`. `target` will be modified
* and returned. * and returned.
*
* @param {Stats} target * @param {Stats} target
* @param {Stats} other * @param {Stats} other
* @returns {Stats} * @returns {Stats}

View File

@ -5,7 +5,7 @@ const LRU = require('hashlru')
/** /**
* Creates and returns a Least Recently Used Cache * Creates and returns a Least Recently Used Cache
* *
* @param {number} maxSize * @param {Number} maxSize
* @returns {LRUCache} * @returns {LRUCache}
*/ */
module.exports = (maxSize) => { module.exports = (maxSize) => {

View File

@ -196,8 +196,8 @@ class Stats extends EventEmitter {
* *
* @private * @private
* @param {string} key * @param {string} key
* @param {number} timeDiffMS - Time in milliseconds * @param {number} timeDiffMS Time in milliseconds
* @param {Timestamp} latestTime - Time in ticks * @param {Timestamp} latestTime Time in ticks
* @returns {void} * @returns {void}
*/ */
_updateFrequencyFor (key, timeDiffMS, latestTime) { _updateFrequencyFor (key, timeDiffMS, latestTime) {

View File

@ -15,9 +15,9 @@ module.exports = (node) => {
/** /**
* Iterates over all peer routers in series to find the given peer. * Iterates over all peer routers in series to find the given peer.
* *
* @param {string} id - The id of the peer to find * @param {String} id The id of the peer to find
* @param {object} [options] * @param {object} [options]
* @param {number} [options.timeout] - How long the query should run * @param {number} [options.timeout] How long the query should run
* @returns {Promise<{ id: PeerId, multiaddrs: Multiaddr[] }>} * @returns {Promise<{ id: PeerId, multiaddrs: Multiaddr[] }>}
*/ */
findPeer: async (id, options) => { // eslint-disable-line require-await findPeer: async (id, options) => { // eslint-disable-line require-await

View File

@ -75,9 +75,9 @@ A `peerId.toB58String()` identifier mapping to a `Set` of protocol identifier st
#### Metadata Book #### Metadata Book
The `metadataBook` keeps track of the known metadata of a peer. Its metadata is stored in a key value fashion, where a key identifier (`string`) represents a metadata value (`Uint8Array`). The `metadataBook` keeps track of the known metadata of a peer. Its metadata is stored in a key value fashion, where a key identifier (`string`) represents a metadata value (`Buffer`).
`Map<string, Map<string, Uint8Array>>` `Map<string, Map<string, Buffer>>`
A `peerId.toB58String()` identifier mapping to the peer metadata Map. A `peerId.toB58String()` identifier mapping to the peer metadata Map.

View File

@ -9,12 +9,10 @@ const multiaddr = require('multiaddr')
const PeerId = require('peer-id') const PeerId = require('peer-id')
const Book = require('./book') const Book = require('./book')
const PeerRecord = require('../record/peer-record')
const { const {
codes: { ERR_INVALID_PARAMETERS } codes: { ERR_INVALID_PARAMETERS }
} = require('../errors') } = require('../errors')
const Envelope = require('../record/envelope')
/** /**
* The AddressBook is responsible for keeping the known multiaddrs * The AddressBook is responsible for keeping the known multiaddrs
@ -23,30 +21,12 @@ const Envelope = require('../record/envelope')
class AddressBook extends Book { class AddressBook extends Book {
/** /**
* Address object * Address object
*
* @typedef {Object} Address * @typedef {Object} Address
* @property {Multiaddr} multiaddr peer multiaddr. * @property {Multiaddr} multiaddr peer multiaddr.
* @property {boolean} isCertified obtained from a signed peer record.
*/ */
/** /**
* CertifiedRecord object * @constructor
*
* @typedef {Object} CertifiedRecord
* @property {Uint8Array} raw raw envelope.
* @property {number} seqNumber seq counter.
*/
/**
* Entry object for the addressBook
*
* @typedef {Object} Entry
* @property {Array<Address>} addresses peer Addresses.
* @property {CertifiedRecord} record certified peer record.
*/
/**
* @class
* @param {PeerStore} peerStore * @param {PeerStore} peerStore
*/ */
constructor (peerStore) { constructor (peerStore) {
@ -59,116 +39,18 @@ class AddressBook extends Book {
peerStore, peerStore,
eventName: 'change:multiaddrs', eventName: 'change:multiaddrs',
eventProperty: 'multiaddrs', eventProperty: 'multiaddrs',
eventTransformer: (data) => { eventTransformer: (data) => data.map((address) => address.multiaddr)
if (!data.addresses) {
return []
}
return data.addresses.map((address) => address.multiaddr)
}
}) })
/** /**
* Map known peers to their known Address Entries. * Map known peers to their known Addresses.
* * @type {Map<string, Array<Address>>}
* @type {Map<string, Array<Entry>>}
*/ */
this.data = new Map() this.data = new Map()
} }
/**
* ConsumePeerRecord adds addresses from a signed peer record contained in a record envelope.
* This will return a boolean that indicates if the record was successfully processed and added
* into the AddressBook.
*
* @param {Envelope} envelope
* @returns {boolean}
*/
consumePeerRecord (envelope) {
let peerRecord
try {
peerRecord = PeerRecord.createFromProtobuf(envelope.payload)
} catch (err) {
log.error('invalid peer record received')
return false
}
// Verify peerId
if (!peerRecord.peerId.equals(envelope.peerId)) {
log('signing key does not match PeerId in the PeerRecord')
return false
}
// ensure the record has multiaddrs
if (!peerRecord.multiaddrs || !peerRecord.multiaddrs.length) {
return false
}
const peerId = peerRecord.peerId
const id = peerId.toB58String()
const entry = this.data.get(id) || {}
const storedRecord = entry.record
// ensure seq is greater than, or equal to, the last received
if (storedRecord && storedRecord.seqNumber >= peerRecord.seqNumber) {
return false
}
const addresses = this._toAddresses(peerRecord.multiaddrs, true)
// Replace unsigned addresses by the new ones from the record
// TODO: Once we have ttls for the addresses, we should merge these in.
this._setData(peerId, {
addresses,
record: {
raw: envelope.marshal(),
seqNumber: peerRecord.seqNumber
}
})
log(`stored provided peer record for ${id}`)
return true
}
/**
* Get the raw Envelope for a peer. Returns
* undefined if no Envelope is found.
*
* @param {PeerId} peerId
* @returns {Uint8Array|undefined}
*/
getRawEnvelope (peerId) {
const entry = this.data.get(peerId.toB58String())
if (!entry || !entry.record || !entry.record.raw) {
return undefined
}
return entry.record.raw
}
/**
* Get an Envelope containing a PeerRecord for the given peer.
* Returns undefined if no record exists.
*
* @param {PeerId} peerId
* @returns {Promise<Envelope|void>}
*/
getPeerRecord (peerId) {
const raw = this.getRawEnvelope(peerId)
if (!raw) {
return undefined
}
return Envelope.createFromProtobuf(raw)
}
/** /**
* Set known multiaddrs of a provided peer. * Set known multiaddrs of a provided peer.
* This will replace previously stored multiaddrs, if available.
* Replacing stored multiaddrs might result in losing obtained certified addresses.
* If you are not sure, it's recommended to use `add` instead.
*
* @override * @override
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {Array<Multiaddr>} multiaddrs * @param {Array<Multiaddr>} multiaddrs
@ -182,8 +64,7 @@ class AddressBook extends Book {
const addresses = this._toAddresses(multiaddrs) const addresses = this._toAddresses(multiaddrs)
const id = peerId.toB58String() const id = peerId.toB58String()
const entry = this.data.get(id) || {} const rec = this.data.get(id)
const rec = entry.addresses
// Not replace multiaddrs // Not replace multiaddrs
if (!addresses.length) { if (!addresses.length) {
@ -192,7 +73,7 @@ class AddressBook extends Book {
// Already knows the peer // Already knows the peer
if (rec && rec.length === addresses.length) { if (rec && rec.length === addresses.length) {
const intersection = rec.filter((addr) => addresses.some((newAddr) => addr.multiaddr.equals(newAddr.multiaddr))) const intersection = rec.filter((mi) => addresses.some((newMi) => mi.multiaddr.equals(newMi.multiaddr)))
// Are new addresses equal to the old ones? // Are new addresses equal to the old ones?
// If yes, no changes needed! // If yes, no changes needed!
@ -202,10 +83,7 @@ class AddressBook extends Book {
} }
} }
this._setData(peerId, { this._setData(peerId, addresses)
addresses,
record: entry.record
})
log(`stored provided multiaddrs for ${id}`) log(`stored provided multiaddrs for ${id}`)
// Notify the existance of a new peer // Notify the existance of a new peer
@ -219,7 +97,6 @@ class AddressBook extends Book {
/** /**
* Add known addresses of a provided peer. * Add known addresses of a provided peer.
* If the peer is not known, it is set with the given addresses. * If the peer is not known, it is set with the given addresses.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {Array<Multiaddr>} multiaddrs * @param {Array<Multiaddr>} multiaddrs
* @returns {AddressBook} * @returns {AddressBook}
@ -232,14 +109,12 @@ class AddressBook extends Book {
const addresses = this._toAddresses(multiaddrs) const addresses = this._toAddresses(multiaddrs)
const id = peerId.toB58String() const id = peerId.toB58String()
const rec = this.data.get(id)
const entry = this.data.get(id) || {}
const rec = entry.addresses || []
// Add recorded uniquely to the new array (Union) // Add recorded uniquely to the new array (Union)
rec.forEach((addr) => { rec && rec.forEach((mi) => {
if (!addresses.find(r => r.multiaddr.equals(addr.multiaddr))) { if (!addresses.find(r => r.multiaddr.equals(mi.multiaddr))) {
addresses.push(addr) addresses.push(mi)
} }
}) })
@ -250,47 +125,25 @@ class AddressBook extends Book {
return this return this
} }
this._setData(peerId, { this._setData(peerId, addresses)
addresses,
record: entry.record
})
log(`added provided multiaddrs for ${id}`) log(`added provided multiaddrs for ${id}`)
// Notify the existance of a new peer // Notify the existance of a new peer
if (!entry.addresses) { if (!rec) {
this._ps.emit('peer', peerId) this._ps.emit('peer', peerId)
} }
return this return this
} }
/**
* Get the known data of a provided peer.
*
* @override
* @param {PeerId} peerId
* @returns {Array<data>}
*/
get (peerId) {
if (!PeerId.isPeerId(peerId)) {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
const entry = this.data.get(peerId.toB58String())
return entry && entry.addresses ? [...entry.addresses] : undefined
}
/** /**
* Transforms received multiaddrs into Address. * Transforms received multiaddrs into Address.
*
* @private * @private
* @param {Array<Multiaddr>} multiaddrs * @param {Array<Multiaddr>} multiaddrs
* @param {boolean} [isCertified]
* @returns {Array<Address>} * @returns {Array<Address>}
*/ */
_toAddresses (multiaddrs, isCertified = false) { _toAddresses (multiaddrs) {
if (!multiaddrs) { if (!multiaddrs) {
log.error('multiaddrs must be provided to store data') log.error('multiaddrs must be provided to store data')
throw errcode(new Error('multiaddrs must be provided'), ERR_INVALID_PARAMETERS) throw errcode(new Error('multiaddrs must be provided'), ERR_INVALID_PARAMETERS)
@ -305,8 +158,7 @@ class AddressBook extends Book {
} }
addresses.push({ addresses.push({
multiaddr: addr, multiaddr: addr
isCertified
}) })
}) })
@ -317,7 +169,6 @@ class AddressBook extends Book {
* Get the known multiaddrs for a given peer. All returned multiaddrs * Get the known multiaddrs for a given peer. All returned multiaddrs
* will include the encapsulated `PeerId` of the peer. * will include the encapsulated `PeerId` of the peer.
* Returns `undefined` if there are no known multiaddrs for the given peer. * Returns `undefined` if there are no known multiaddrs for the given peer.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {Array<Multiaddr>|undefined} * @returns {Array<Multiaddr>|undefined}
*/ */
@ -326,13 +177,13 @@ class AddressBook extends Book {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS) throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
} }
const entry = this.data.get(peerId.toB58String()) const record = this.data.get(peerId.toB58String())
if (!entry || !entry.addresses) { if (!record) {
return undefined return undefined
} }
return entry.addresses.map((address) => { return record.map((address) => {
const multiaddr = address.multiaddr const multiaddr = address.multiaddr
const idString = multiaddr.getPeerId() const idString = multiaddr.getPeerId()

View File

@ -14,12 +14,12 @@ const passthrough = data => data
*/ */
class Book { class Book {
/** /**
* @class * @constructor
* @param {Object} properties * @param {Object} properties
* @param {PeerStore} properties.peerStore - PeerStore instance. * @param {PeerStore} properties.peerStore PeerStore instance.
* @param {string} properties.eventName - Name of the event to emit by the PeerStore. * @param {string} properties.eventName Name of the event to emit by the PeerStore.
* @param {string} properties.eventProperty - Name of the property to emit by the PeerStore. * @param {string} properties.eventProperty Name of the property to emit by the PeerStore.
* @param {Function} [properties.eventTransformer] - Transformer function of the provided data for being emitted. * @param {function} [properties.eventTransformer] Transformer function of the provided data for being emitted.
*/ */
constructor ({ peerStore, eventName, eventProperty, eventTransformer = passthrough }) { constructor ({ peerStore, eventName, eventProperty, eventTransformer = passthrough }) {
this._ps = peerStore this._ps = peerStore
@ -29,7 +29,6 @@ class Book {
/** /**
* Map known peers to their data. * Map known peers to their data.
*
* @type {Map<string, Array<Data>} * @type {Map<string, Array<Data>}
*/ */
this.data = new Map() this.data = new Map()
@ -37,7 +36,6 @@ class Book {
/** /**
* Set known data of a provided peer. * Set known data of a provided peer.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {Array<Data>|Data} data * @param {Array<Data>|Data} data
*/ */
@ -47,13 +45,12 @@ class Book {
/** /**
* Set data into the datastructure, persistence and emit it using the provided transformers. * Set data into the datastructure, persistence and emit it using the provided transformers.
*
* @private * @private
* @param {PeerId} peerId - peerId of the data to store * @param {PeerId} peerId peerId of the data to store
* @param {*} data - data to store. * @param {*} data data to store.
* @param {Object} [options] - storing options. * @param {Object} [options] storing options.
* @param {boolean} [options.emit = true] - emit the provided data. * @param {boolean} [options.emit = true] emit the provided data.
* @returns {void} * @return {void}
*/ */
_setData (peerId, data, { emit = true } = {}) { _setData (peerId, data, { emit = true } = {}) {
const b58key = peerId.toB58String() const b58key = peerId.toB58String()
@ -67,7 +64,6 @@ class Book {
/** /**
* Emit data. * Emit data.
*
* @private * @private
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {*} data * @param {*} data
@ -82,7 +78,6 @@ class Book {
/** /**
* Get the known data of a provided peer. * Get the known data of a provided peer.
* Returns `undefined` if there is no available data for the given peer. * Returns `undefined` if there is no available data for the given peer.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {Array<Data>|undefined} * @returns {Array<Data>|undefined}
*/ */
@ -98,7 +93,6 @@ class Book {
/** /**
* Deletes the provided peer from the book. * Deletes the provided peer from the book.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {boolean} * @returns {boolean}
*/ */

View File

@ -19,7 +19,6 @@ const {
/** /**
* Responsible for managing known peers, as well as their addresses, protocols and metadata. * Responsible for managing known peers, as well as their addresses, protocols and metadata.
*
* @fires PeerStore#peer Emitted when a new peer is added. * @fires PeerStore#peer Emitted when a new peer is added.
* @fires PeerStore#change:protocols Emitted when a known peer supports a different set of protocols. * @fires PeerStore#change:protocols Emitted when a known peer supports a different set of protocols.
* @fires PeerStore#change:multiaddrs Emitted when a known peer has a different set of multiaddrs. * @fires PeerStore#change:multiaddrs Emitted when a known peer has a different set of multiaddrs.
@ -29,24 +28,18 @@ const {
class PeerStore extends EventEmitter { class PeerStore extends EventEmitter {
/** /**
* Peer object * Peer object
*
* @typedef {Object} Peer * @typedef {Object} Peer
* @property {PeerId} id peer's peer-id instance. * @property {PeerId} id peer's peer-id instance.
* @property {Array<Address>} addresses peer's addresses containing its multiaddrs and metadata. * @property {Array<Address>} addresses peer's addresses containing its multiaddrs and metadata.
* @property {Array<string>} protocols peer's supported protocols. * @property {Array<string>} protocols peer's supported protocols.
* @property {Map<string, Buffer>} metadata peer's metadata map.
*/ */
/** /**
* @param {object} options * @constructor
* @param {PeerId} options.peerId
* @class
*/ */
constructor ({ peerId }) { constructor () {
super() super()
this._peerId = peerId
/** /**
* AddressBook containing a map of peerIdStr to Address. * AddressBook containing a map of peerIdStr to Address.
*/ */
@ -79,8 +72,7 @@ class PeerStore extends EventEmitter {
stop () {} stop () {}
/** /**
* Get all the stored information of every peer known. * Get all the stored information of every peer.
*
* @returns {Map<string, Peer>} * @returns {Map<string, Peer>}
*/ */
get peers () { get peers () {
@ -91,9 +83,6 @@ class PeerStore extends EventEmitter {
...this.metadataBook.data.keys() ...this.metadataBook.data.keys()
]) ])
// Remove self peer if present
this._peerId && storedPeers.delete(this._peerId.toB58String())
const peersData = new Map() const peersData = new Map()
storedPeers.forEach((idStr) => { storedPeers.forEach((idStr) => {
peersData.set(idStr, this.get(PeerId.createFromCID(idStr))) peersData.set(idStr, this.get(PeerId.createFromCID(idStr)))
@ -104,7 +93,6 @@ class PeerStore extends EventEmitter {
/** /**
* Delete the information of the given peer in every book. * Delete the information of the given peer in every book.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {boolean} true if found and removed * @returns {boolean} true if found and removed
*/ */
@ -119,7 +107,6 @@ class PeerStore extends EventEmitter {
/** /**
* Get the stored information of a given peer. * Get the stored information of a given peer.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {Peer} * @returns {Peer}
*/ */

View File

@ -18,7 +18,7 @@ const {
*/ */
class KeyBook extends Book { class KeyBook extends Book {
/** /**
* @class * @constructor
* @param {PeerStore} peerStore * @param {PeerStore} peerStore
*/ */
constructor (peerStore) { constructor (peerStore) {
@ -31,7 +31,6 @@ class KeyBook extends Book {
/** /**
* Map known peers to their known Public Key. * Map known peers to their known Public Key.
*
* @type {Map<string, PeerId>} * @type {Map<string, PeerId>}
*/ */
this.data = new Map() this.data = new Map()
@ -39,11 +38,10 @@ class KeyBook extends Book {
/** /**
* Set the Peer public key. * Set the Peer public key.
*
* @override * @override
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey} publicKey * @param {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey} publicKey
* @returns {KeyBook} * @return {KeyBook}
*/ */
set (peerId, publicKey) { set (peerId, publicKey) {
if (!PeerId.isPeerId(peerId)) { if (!PeerId.isPeerId(peerId)) {
@ -69,10 +67,9 @@ class KeyBook extends Book {
/** /**
* Get Public key of the given PeerId, if stored. * Get Public key of the given PeerId, if stored.
*
* @override * @override
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey} * @return {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey}
*/ */
get (peerId) { get (peerId) {
if (!PeerId.isPeerId(peerId)) { if (!PeerId.isPeerId(peerId)) {

View File

@ -4,7 +4,8 @@ const errcode = require('err-code')
const debug = require('debug') const debug = require('debug')
const log = debug('libp2p:peer-store:proto-book') const log = debug('libp2p:peer-store:proto-book')
log.error = debug('libp2p:peer-store:proto-book:error') log.error = debug('libp2p:peer-store:proto-book:error')
const uint8ArrayEquals = require('uint8arrays/equals')
const { Buffer } = require('buffer')
const PeerId = require('peer-id') const PeerId = require('peer-id')
@ -17,12 +18,11 @@ const {
/** /**
* The MetadataBook is responsible for keeping the known supported * The MetadataBook is responsible for keeping the known supported
* protocols of a peer. * protocols of a peer.
*
* @fires MetadataBook#change:metadata * @fires MetadataBook#change:metadata
*/ */
class MetadataBook extends Book { class MetadataBook extends Book {
/** /**
* @class * @constructor
* @param {PeerStore} peerStore * @param {PeerStore} peerStore
*/ */
constructor (peerStore) { constructor (peerStore) {
@ -38,19 +38,17 @@ class MetadataBook extends Book {
/** /**
* Map known peers to their known protocols. * Map known peers to their known protocols.
* * @type {Map<string, Map<string, Buffer>>}
* @type {Map<string, Map<string, Uint8Array>>}
*/ */
this.data = new Map() this.data = new Map()
} }
/** /**
* Set metadata key and value of a provided peer. * Set metadata key and value of a provided peer.
*
* @override * @override
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {string} key - metadata key * @param {string} key metadata key
* @param {Uint8Array} value - metadata value * @param {Buffer} value metadata value
* @returns {ProtoBook} * @returns {ProtoBook}
*/ */
set (peerId, key, value) { set (peerId, key, value) {
@ -59,7 +57,7 @@ class MetadataBook extends Book {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS) throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
} }
if (typeof key !== 'string' || !(value instanceof Uint8Array)) { if (typeof key !== 'string' || !Buffer.isBuffer(value)) {
log.error('valid key and value must be provided to store data') log.error('valid key and value must be provided to store data')
throw errcode(new Error('valid key and value must be provided'), ERR_INVALID_PARAMETERS) throw errcode(new Error('valid key and value must be provided'), ERR_INVALID_PARAMETERS)
} }
@ -71,7 +69,6 @@ class MetadataBook extends Book {
/** /**
* Set data into the datastructure * Set data into the datastructure
*
* @override * @override
*/ */
_setValue (peerId, key, value, { emit = true } = {}) { _setValue (peerId, key, value, { emit = true } = {}) {
@ -80,7 +77,7 @@ class MetadataBook extends Book {
const recMap = rec.get(key) const recMap = rec.get(key)
// Already exists and is equal // Already exists and is equal
if (recMap && uint8ArrayEquals(value, recMap)) { if (recMap && value.equals(recMap)) {
log(`the metadata provided to store is equal to the already stored for ${id} on ${key}`) log(`the metadata provided to store is equal to the already stored for ${id} on ${key}`)
return return
} }
@ -93,9 +90,8 @@ class MetadataBook extends Book {
/** /**
* Get the known data of a provided peer. * Get the known data of a provided peer.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {Map<string, Uint8Array>} * @returns {Map<string, Buffer>}
*/ */
get (peerId) { get (peerId) {
if (!PeerId.isPeerId(peerId)) { if (!PeerId.isPeerId(peerId)) {
@ -107,10 +103,9 @@ class MetadataBook extends Book {
/** /**
* Get specific metadata value, if it exists * Get specific metadata value, if it exists
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {string} key * @param {string} key
* @returns {Uint8Array} * @returns {Buffer}
*/ */
getValue (peerId, key) { getValue (peerId, key) {
if (!PeerId.isPeerId(peerId)) { if (!PeerId.isPeerId(peerId)) {
@ -123,7 +118,6 @@ class MetadataBook extends Book {
/** /**
* Deletes the provided peer from the book. * Deletes the provided peer from the book.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {boolean} * @returns {boolean}
*/ */
@ -143,7 +137,6 @@ class MetadataBook extends Book {
/** /**
* Deletes the provided peer metadata key from the book. * Deletes the provided peer metadata key from the book.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {string} key * @param {string} key
* @returns {boolean} * @returns {boolean}

View File

@ -26,14 +26,13 @@ const Protocols = require('./pb/proto-book.proto')
*/ */
class PersistentPeerStore extends PeerStore { class PersistentPeerStore extends PeerStore {
/** /**
* @class * @constructor
* @param {Object} properties * @param {Object} properties
* @param {PeerId} properties.peerId * @param {Datastore} properties.datastore Datastore to persist data.
* @param {Datastore} properties.datastore - Datastore to persist data. * @param {number} [properties.threshold = 5] Number of dirty peers allowed before commit data.
* @param {number} [properties.threshold = 5] - Number of dirty peers allowed before commit data.
*/ */
constructor ({ peerId, datastore, threshold = 5 }) { constructor ({ datastore, threshold = 5 }) {
super({ peerId }) super()
/** /**
* Backend datastore used to persist data. * Backend datastore used to persist data.
@ -47,7 +46,6 @@ class PersistentPeerStore extends PeerStore {
/** /**
* Peers metadata changed mapping peer identifers to metadata changed. * Peers metadata changed mapping peer identifers to metadata changed.
*
* @type {Map<string, Set<string>>} * @type {Map<string, Set<string>>}
*/ */
this._dirtyMetadata = new Map() this._dirtyMetadata = new Map()
@ -58,8 +56,7 @@ class PersistentPeerStore extends PeerStore {
/** /**
* Start Persistent PeerStore. * Start Persistent PeerStore.
* * @return {Promise<void>}
* @returns {Promise<void>}
*/ */
async start () { async start () {
log('PeerStore is starting') log('PeerStore is starting')
@ -67,7 +64,7 @@ class PersistentPeerStore extends PeerStore {
// Handlers for dirty peers // Handlers for dirty peers
this.on('change:protocols', this._addDirtyPeer) this.on('change:protocols', this._addDirtyPeer)
this.on('change:multiaddrs', this._addDirtyPeer) this.on('change:multiaddrs', this._addDirtyPeer)
this.on('change:pubkey', this._addDirtyPeerKey) this.on('change:pubkey', this._addDirtyPeer)
this.on('change:metadata', this._addDirtyPeerMetadata) this.on('change:metadata', this._addDirtyPeerMetadata)
// Load data // Load data
@ -78,11 +75,6 @@ class PersistentPeerStore extends PeerStore {
log('PeerStore started') log('PeerStore started')
} }
/**
* Stop Persistent PeerStore.
*
* @returns {Promise<void>}
*/
async stop () { async stop () {
log('PeerStore is stopping') log('PeerStore is stopping')
this.removeAllListeners() this.removeAllListeners()
@ -92,7 +84,6 @@ class PersistentPeerStore extends PeerStore {
/** /**
* Add modified peer to the dirty set * Add modified peer to the dirty set
*
* @private * @private
* @param {Object} params * @param {Object} params
* @param {PeerId} params.peerId * @param {PeerId} params.peerId
@ -111,35 +102,8 @@ class PersistentPeerStore extends PeerStore {
} }
} }
/**
* Add modified peer key to the dirty set
*
* @private
* @param {Object} params
* @param {PeerId} params.peerId
*/
_addDirtyPeerKey ({ peerId }) {
// Not add if inline key available
if (peerId.hasInlinePublicKey()) {
return
}
const peerIdstr = peerId.toB58String()
log('add dirty peer key', peerIdstr)
this._dirtyPeers.add(peerIdstr)
if (this._dirtyPeers.size >= this.threshold) {
// Commit current data
this._commitData().catch(err => {
log.error('error committing data', err)
})
}
}
/** /**
* Add modified metadata peer to the set. * Add modified metadata peer to the set.
*
* @private * @private
* @param {Object} params * @param {Object} params
* @param {PeerId} params.peerId * @param {PeerId} params.peerId
@ -166,9 +130,9 @@ class PersistentPeerStore extends PeerStore {
/** /**
* Add all the peers current data to a datastore batch and commit it. * Add all the peers current data to a datastore batch and commit it.
*
* @private * @private
* @returns {Promise<void>} * @param {Array<string>} peers
* @return {Promise<void>}
*/ */
async _commitData () { async _commitData () {
const commitPeers = Array.from(this._dirtyPeers) const commitPeers = Array.from(this._dirtyPeers)
@ -190,7 +154,7 @@ class PersistentPeerStore extends PeerStore {
this._batchAddressBook(peerId, batch) this._batchAddressBook(peerId, batch)
// Key Book // Key Book
!peerId.hasInlinePublicKey() && this._batchKeyBook(peerId, batch) this._batchKeyBook(peerId, batch)
// Metadata Book // Metadata Book
this._batchMetadataBook(peerId, batch) this._batchMetadataBook(peerId, batch)
@ -205,7 +169,6 @@ class PersistentPeerStore extends PeerStore {
/** /**
* Add address book data of the peer to the batch. * Add address book data of the peer to the batch.
*
* @private * @private
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {Object} batch * @param {Object} batch
@ -214,24 +177,19 @@ class PersistentPeerStore extends PeerStore {
const b32key = peerId.toString() const b32key = peerId.toString()
const key = new Key(`${NAMESPACE_ADDRESS}${b32key}`) const key = new Key(`${NAMESPACE_ADDRESS}${b32key}`)
const entry = this.addressBook.data.get(peerId.toB58String()) const addresses = this.addressBook.get(peerId)
try { try {
// Deleted from the book // Deleted from the book
if (!entry) { if (!addresses) {
batch.delete(key) batch.delete(key)
return return
} }
const encodedData = Addresses.encode({ const encodedData = Addresses.encode({
addrs: entry.addresses.map((address) => ({ addrs: addresses.map((address) => ({
multiaddr: address.multiaddr.bytes, multiaddr: address.multiaddr.buffer
isCertified: address.isCertified }))
})),
certified_record: entry.record ? {
seq: entry.record.seqNumber,
raw: entry.record.raw
} : undefined
}) })
batch.put(key, encodedData) batch.put(key, encodedData)
@ -242,7 +200,6 @@ class PersistentPeerStore extends PeerStore {
/** /**
* Add Key book data of the peer to the batch. * Add Key book data of the peer to the batch.
*
* @private * @private
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {Object} batch * @param {Object} batch
@ -268,7 +225,6 @@ class PersistentPeerStore extends PeerStore {
/** /**
* Add metadata book data of the peer to the batch. * Add metadata book data of the peer to the batch.
*
* @private * @private
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {Object} batch * @param {Object} batch
@ -295,7 +251,6 @@ class PersistentPeerStore extends PeerStore {
/** /**
* Add proto book data of the peer to the batch. * Add proto book data of the peer to the batch.
*
* @private * @private
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {Object} batch * @param {Object} batch
@ -323,12 +278,11 @@ class PersistentPeerStore extends PeerStore {
/** /**
* Process datastore entry and add its data to the correct book. * Process datastore entry and add its data to the correct book.
*
* @private * @private
* @param {Object} params * @param {Object} params
* @param {Key} params.key - datastore key * @param {Key} params.key datastore key
* @param {Uint8Array} params.value - datastore value stored * @param {Buffer} params.value datastore value stored
* @returns {Promise<void>} * @return {Promise<void>}
*/ */
async _processDatastoreEntry ({ key, value }) { async _processDatastoreEntry ({ key, value }) {
try { try {
@ -342,16 +296,9 @@ class PersistentPeerStore extends PeerStore {
this.addressBook._setData( this.addressBook._setData(
peerId, peerId,
{ decoded.addrs.map((address) => ({
addresses: decoded.addrs.map((address) => ({ multiaddr: multiaddr(address.multiaddr)
multiaddr: multiaddr(address.multiaddr),
isCertified: Boolean(address.isCertified)
})), })),
record: decoded.certified_record ? {
raw: decoded.certified_record.raw,
seqNumber: decoded.certified_record.seq
} : undefined
},
{ emit: false }) { emit: false })
break break
case 'keys': case 'keys':

View File

@ -4,29 +4,11 @@ const protons = require('protons')
const message = ` const message = `
message Addresses { message Addresses {
// Address represents a single multiaddr.
message Address { message Address {
required bytes multiaddr = 1; required bytes multiaddr = 1;
// Flag to indicate if the address comes from a certified source.
optional bool isCertified = 2;
} }
// CertifiedRecord contains a serialized signed PeerRecord used to
// populate the signedAddrs list.
message CertifiedRecord {
// The Seq counter from the signed PeerRecord envelope
uint64 seq = 1;
// The serialized bytes of the SignedEnvelope containing the PeerRecord.
bytes raw = 2;
}
// The known multiaddrs.
repeated Address addrs = 1; repeated Address addrs = 1;
// The most recently received signed PeerRecord.
CertifiedRecord certified_record = 2;
} }
` `

View File

@ -16,12 +16,11 @@ const {
/** /**
* The ProtoBook is responsible for keeping the known supported * The ProtoBook is responsible for keeping the known supported
* protocols of a peer. * protocols of a peer.
*
* @fires ProtoBook#change:protocols * @fires ProtoBook#change:protocols
*/ */
class ProtoBook extends Book { class ProtoBook extends Book {
/** /**
* @class * @constructor
* @param {PeerStore} peerStore * @param {PeerStore} peerStore
*/ */
constructor (peerStore) { constructor (peerStore) {
@ -38,7 +37,6 @@ class ProtoBook extends Book {
/** /**
* Map known peers to their known protocols. * Map known peers to their known protocols.
*
* @type {Map<string, Set<string>>} * @type {Map<string, Set<string>>}
*/ */
this.data = new Map() this.data = new Map()
@ -47,7 +45,6 @@ class ProtoBook extends Book {
/** /**
* Set known protocols of a provided peer. * Set known protocols of a provided peer.
* If the peer was not known before, it will be added. * If the peer was not known before, it will be added.
*
* @override * @override
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {Array<string>} protocols * @param {Array<string>} protocols
@ -86,7 +83,6 @@ class ProtoBook extends Book {
/** /**
* Adds known protocols of a provided peer. * Adds known protocols of a provided peer.
* If the peer was not known before, it will be added. * If the peer was not known before, it will be added.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @param {Array<string>} protocols * @param {Array<string>} protocols
* @returns {ProtoBook} * @returns {ProtoBook}

View File

@ -14,10 +14,9 @@ const { PROTOCOL, PING_LENGTH } = require('./constants')
/** /**
* Ping a given peer and wait for its response, getting the operation latency. * Ping a given peer and wait for its response, getting the operation latency.
*
* @param {Libp2p} node * @param {Libp2p} node
* @param {PeerId|multiaddr} peer * @param {PeerId|multiaddr} peer
* @returns {Promise<number>} * @returns {Promise<Number>}
*/ */
async function ping (node, peer) { async function ping (node, peer) {
log('dialing %s to %s', PROTOCOL, peer.toB58String ? peer.toB58String() : peer) log('dialing %s to %s', PROTOCOL, peer.toB58String ? peer.toB58String() : peer)
@ -45,7 +44,6 @@ async function ping (node, peer) {
/** /**
* Subscribe ping protocol handler. * Subscribe ping protocol handler.
*
* @param {Libp2p} node * @param {Libp2p} node
*/ */
function mount (node) { function mount (node) {
@ -54,7 +52,6 @@ function mount (node) {
/** /**
* Unsubscribe ping protocol handler. * Unsubscribe ping protocol handler.
*
* @param {Libp2p} node * @param {Libp2p} node
*/ */
function unmount (node) { function unmount (node) {

View File

@ -64,7 +64,7 @@ node -e "require('libp2p/src/pnet').generate(process.stdout)" > swarm.key
```js ```js
const writeKey = require('libp2p/src/pnet').generate const writeKey = require('libp2p/src/pnet').generate
const swarmKey = new Uint8Array(95) const swarmKey = Buffer.alloc(95)
writeKey(swarmKey) writeKey(swarmKey)
fs.writeFileSync('swarm.key', swarmKey) fs.writeFileSync('swarm.key', swarmKey)
``` ```

View File

@ -1,11 +1,10 @@
'use strict' 'use strict'
const { Buffer } = require('buffer')
const debug = require('debug') const debug = require('debug')
const Errors = require('./errors') const Errors = require('./errors')
const xsalsa20 = require('xsalsa20') const xsalsa20 = require('xsalsa20')
const KEY_LENGTH = require('./key-generator').KEY_LENGTH const KEY_LENGTH = require('./key-generator').KEY_LENGTH
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayToString = require('uint8arrays/to-string')
const log = debug('libp2p:pnet') const log = debug('libp2p:pnet')
log.trace = debug('libp2p:pnet:trace') log.trace = debug('libp2p:pnet:trace')
@ -14,15 +13,15 @@ log.error = debug('libp2p:pnet:err')
/** /**
* Creates a stream iterable to encrypt messages in a private network * Creates a stream iterable to encrypt messages in a private network
* *
* @param {Uint8Array} nonce - The nonce to use in encryption * @param {Buffer} nonce The nonce to use in encryption
* @param {Uint8Array} psk - The private shared key to use in encryption * @param {Buffer} psk The private shared key to use in encryption
* @returns {*} a through iterable * @returns {*} a through iterable
*/ */
module.exports.createBoxStream = (nonce, psk) => { module.exports.createBoxStream = (nonce, psk) => {
const xor = xsalsa20(nonce, psk) const xor = xsalsa20(nonce, psk)
return (source) => (async function * () { return (source) => (async function * () {
for await (const chunk of source) { for await (const chunk of source) {
yield Uint8Array.from(xor.update(chunk.slice())) yield Buffer.from(xor.update(chunk.slice()))
} }
})() })()
} }
@ -30,8 +29,8 @@ module.exports.createBoxStream = (nonce, psk) => {
/** /**
* Creates a stream iterable to decrypt messages in a private network * Creates a stream iterable to decrypt messages in a private network
* *
* @param {Uint8Array} nonce - The nonce of the remote peer * @param {Buffer} nonce The nonce of the remote peer
* @param {Uint8Array} psk - The private shared key to use in decryption * @param {Buffer} psk The private shared key to use in decryption
* @returns {*} a through iterable * @returns {*} a through iterable
*/ */
module.exports.createUnboxStream = (nonce, psk) => { module.exports.createUnboxStream = (nonce, psk) => {
@ -40,15 +39,15 @@ module.exports.createUnboxStream = (nonce, psk) => {
log.trace('Decryption enabled') log.trace('Decryption enabled')
for await (const chunk of source) { for await (const chunk of source) {
yield Uint8Array.from(xor.update(chunk.slice())) yield Buffer.from(xor.update(chunk.slice()))
} }
})() })()
} }
/** /**
* Decode the version 1 psk from the given Uint8Array * Decode the version 1 psk from the given Buffer
* *
* @param {Uint8Array} pskBuffer * @param {Buffer} pskBuffer
* @throws {INVALID_PSK} * @throws {INVALID_PSK}
* @returns {Object} The PSK metadata (tag, codecName, psk) * @returns {Object} The PSK metadata (tag, codecName, psk)
*/ */
@ -59,10 +58,10 @@ module.exports.decodeV1PSK = (pskBuffer) => {
// from the buffer line by line to evaluate the next line // from the buffer line by line to evaluate the next line
// programmatically instead of making assumptions about the // programmatically instead of making assumptions about the
// encodings of each line. // encodings of each line.
const metadata = uint8ArrayToString(pskBuffer).split(/(?:\r\n|\r|\n)/g) const metadata = pskBuffer.toString().split(/(?:\r\n|\r|\n)/g)
const pskTag = metadata.shift() const pskTag = metadata.shift()
const codec = metadata.shift() const codec = metadata.shift()
const psk = uint8ArrayFromString(metadata.shift(), 'base16') const psk = Buffer.from(metadata.shift(), 'hex')
if (psk.byteLength !== KEY_LENGTH) { if (psk.byteLength !== KEY_LENGTH) {
throw new Error(Errors.INVALID_PSK) throw new Error(Errors.INVALID_PSK)

View File

@ -25,8 +25,8 @@ log.error = debug('libp2p:pnet:err')
*/ */
class Protector { class Protector {
/** /**
* @param {Uint8Array} keyBuffer - The private shared key buffer * @param {Buffer} keyBuffer The private shared key buffer
* @class * @constructor
*/ */
constructor (keyBuffer) { constructor (keyBuffer) {
const decodedPSK = decodeV1PSK(keyBuffer) const decodedPSK = decodeV1PSK(keyBuffer)
@ -39,7 +39,7 @@ class Protector {
* between its two peers from the PSK the Protector instance was * between its two peers from the PSK the Protector instance was
* created with. * created with.
* *
* @param {Connection} connection - The connection to protect * @param {Connection} connection The connection to protect
* @returns {*} A protected duplex iterable * @returns {*} A protected duplex iterable
*/ */
async protect (connection) { async protect (connection) {

View File

@ -2,20 +2,15 @@
const crypto = require('libp2p-crypto') const crypto = require('libp2p-crypto')
const KEY_LENGTH = 32 const KEY_LENGTH = 32
const uint8ArrayToString = require('uint8arrays/to-string')
const uint8ArrayFromString = require('uint8arrays/from-string')
/** /**
* Generates a PSK that can be used in a libp2p-pnet private network * Generates a PSK that can be used in a libp2p-pnet private network
* * @param {Writer} writer An object containing a `write` method
* @param {Uint8Array} bytes - An object to write the psk into
* @returns {void} * @returns {void}
*/ */
function generate (bytes) { function generate (writer) {
const psk = uint8ArrayToString(crypto.randomBytes(KEY_LENGTH), 'base16') const psk = crypto.randomBytes(KEY_LENGTH).toString('hex')
const key = uint8ArrayFromString('/key/swarm/psk/1.0.0/\n/base16/\n' + psk) writer.write('/key/swarm/psk/1.0.0/\n/base16/\n' + psk)
bytes.set(key)
} }
module.exports = generate module.exports = generate

View File

@ -1,42 +0,0 @@
'use strict'
// Pubsub adapter to keep API with handlers while not removed.
module.exports = (PubsubRouter, libp2p, options) => {
class Pubsub extends PubsubRouter {
/**
* Subscribes to a given topic.
*
* @override
* @param {string} topic
* @param {function(msg: InMessage)} [handler]
* @returns {void}
*/
subscribe (topic, handler) {
// Bind provided handler
handler && this.on(topic, handler)
super.subscribe(topic)
}
/**
* Unsubscribe from the given topic.
*
* @override
* @param {string} topic
* @param {function(msg: InMessage)} [handler]
* @returns {void}
*/
unsubscribe (topic, handler) {
if (!handler) {
this.removeAllListeners(topic)
} else {
this.removeListener(topic, handler)
}
if (this.listenerCount(topic) === 0) {
super.unsubscribe(topic)
}
}
}
return new Pubsub(libp2p, options)
}

105
src/pubsub.js Normal file
View File

@ -0,0 +1,105 @@
'use strict'
const { Buffer } = require('buffer')
const errCode = require('err-code')
const { messages, codes } = require('./errors')
module.exports = (node, Pubsub, config) => {
const pubsub = new Pubsub(node.peerId, node.registrar, config)
return {
/**
* Subscribe the given handler to a pubsub topic
* @param {string} topic
* @param {function} handler The handler to subscribe
* @returns {void}
*/
subscribe: (topic, handler) => {
if (!node.isStarted() && !pubsub.started) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED)
}
if (pubsub.listenerCount(topic) === 0) {
pubsub.subscribe(topic)
}
pubsub.on(topic, handler)
},
/**
* Unsubscribes from a pubsub topic
* @param {string} topic
* @param {function} [handler] The handler to unsubscribe from
*/
unsubscribe: (topic, handler) => {
if (!node.isStarted() && !pubsub.started) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED)
}
if (!handler) {
pubsub.removeAllListeners(topic)
} else {
pubsub.removeListener(topic, handler)
}
if (pubsub.listenerCount(topic) === 0) {
pubsub.unsubscribe(topic)
}
},
/**
* Publish messages to the given topics.
* @param {Array<string>|string} topic
* @param {Buffer} data
* @returns {Promise<void>}
*/
publish: (topic, data) => {
if (!node.isStarted() && !pubsub.started) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED)
}
try {
data = Buffer.from(data)
} catch (err) {
throw errCode(new Error('data must be convertible to a Buffer'), 'ERR_DATA_IS_NOT_VALID')
}
return pubsub.publish(topic, data)
},
/**
* Get a list of topics the node is subscribed to.
* @returns {Array<String>} topics
*/
getTopics: () => {
if (!node.isStarted() && !pubsub.started) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED)
}
return pubsub.getTopics()
},
/**
* Get a list of the peer-ids that are subscribed to one topic.
* @param {string} topic
* @returns {Array<string>}
*/
getSubscribers: (topic) => {
if (!node.isStarted() && !pubsub.started) {
throw errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED)
}
return pubsub.getSubscribers(topic)
},
setMaxListeners (n) {
return pubsub.setMaxListeners(n)
},
_pubsub: pubsub,
start: () => pubsub.start(),
stop: () => pubsub.stop()
}
}

View File

@ -1,130 +0,0 @@
# Libp2p Records
Libp2p nodes need to store data in a public location (e.g. a DHT), or rely on potentially untrustworthy intermediaries to relay information over its lifetime. Accordingly, libp2p nodes need to be able to verify that the data came from a specific peer and that it hasn't been tampered with.
## Envelope
Libp2p provides an all-purpose data container called **envelope**. It was created to enable the distribution of verifiable records, which we can prove originated from the addressed peer itself. The envelope includes a signature of the data, so that its authenticity is verified.
This envelope stores a marshaled record implementing the [interface-record](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/record). These Records are designed to be serialized to bytes and placed inside of the envelopes before being shared with other peers.
You can read further about the envelope in [libp2p/specs#217](https://github.com/libp2p/specs/pull/217).
### Usage
- create an envelope with an instance of an [interface-record](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/record) implementation and prepare it for being exchanged:
```js
// interface-record implementation example with the "libp2p-example" namespace
const Record = require('libp2p-interfaces/src/record')
const fromString = require('uint8arrays/from-string')
class ExampleRecord extends Record {
constructor () {
super ('libp2p-example', fromString('0302', 'hex'))
}
marshal () {}
equals (other) {}
}
ExampleRecord.createFromProtobuf = () => {}
```
```js
const Envelope = require('libp2p/src/record/envelop')
const ExampleRecord = require('./example-record')
const rec = new ExampleRecord()
const e = await Envelope.seal(rec, peerId)
const wireData = e.marshal()
```
- consume a received envelope (`wireData`) and transform it back to a record:
```js
const Envelope = require('libp2p/src/record/envelop')
const ExampleRecord = require('./example-record')
const domain = 'libp2p-example'
let e
try {
e = await Envelope.openAndCertify(wireData, domain)
} catch (err) {}
const rec = ExampleRecord.createFromProtobuf(e.payload)
```
## Peer Record
All libp2p nodes keep a `PeerStore`, that among other information stores a set of known addresses for each peer, which can come from a variety of sources.
Libp2p peer records were created to enable the distribution of verifiable address records, which we can prove originated from the addressed peer itself. With such guarantees, libp2p is able to prioritize addresses based on their authenticity, with the most strict strategy being to only dial certified addresses (no strategies have been implemented at the time of writing).
A peer record contains the peers' publicly reachable listen addresses, and may be extended in the future to contain additional metadata relevant to routing. It also contains a `seqNumber` field, a timestamp per the spec, so that we can verify the most recent record.
You can read further about the Peer Record in [libp2p/specs#217](https://github.com/libp2p/specs/pull/217).
### Usage
- create a new Peer Record
```js
const PeerRecord = require('libp2p/src/record/peer-record')
const pr = new PeerRecord({
peerId: node.peerId,
multiaddrs: node.multiaddrs
})
```
- create a Peer Record from a protobuf
```js
const PeerRecord = require('libp2p/src/record/peer-record')
const pr = PeerRecord.createFromProtobuf(data)
```
### Libp2p Flows
#### Self Record
Once a libp2p node has started and is listening on a set of multiaddrs, its own peer record can be created.
The identify service is responsible for creating the self record when the identify protocol kicks in for the first time. This record will be stored for future needs of the identify protocol when connecting with other peers.
#### Self record Updates
**_NOT_YET_IMPLEMENTED_**
While creating peer records is fairly trivial, addresses are not static and might be modified at arbitrary times. This can happen via an Address Manager API, or even through AutoRelay/AutoNAT.
When a libp2p node changes its listen addresses, the identify service will be informed. Once that happens, the identify service creates a new self record and stores it. With the new record, the identify push/delta protocol will be used to communicate this change to the connected peers.
#### Subsystem receiving a record
Considering that a node can discover other peers' addresses from a variety of sources, Libp2p Peerstore can differentiate the addresses that were obtained through a signed peer record.
Once a record is received and its signature properly validated, its envelope is stored in the AddressBook in its byte representation. The `seqNumber` remains unmarshalled so that we can quickly compare it against incoming records to determine the most recent record.
The AddressBook Addresses will be updated with the content of the envelope with a certified property. This allows other subsystems to identify the known certified addresses of a peer.
#### Subsystem providing a record
Libp2p subsystems that exchange other peers information will provide the envelope that they received by those peers. As a result, other peers can verify if the envelope was really created by the addressed peer.
When a subsystem wants to provide a record, it will get it from the AddressBook, if it exists. Other subsystems are also able to provide the self record, since it is also stored in the AddressBook.
### Future Work
- Persistence only considering certified addresses?
- Peers may not know their own addresses. It's often impossible to automatically infer one's own public address, and peers may need to rely on third party peers to inform them of their observed public addresses.
- A peer may inadvertently or maliciously sign an address that they do not control. In other words, a signature isn't a guarantee that a given address is valid.
- Some addresses may be ambiguous. For example, addresses on a private subnet are valid within that subnet but are useless on the public internet.
- Once all these pieces are in place, we will also need a way to prioritize addresses based on their authenticity, that is, the dialer can prioritize self-certified addresses over addresses from an unknown origin.
- Modular dialer? (taken from go PR notes)
- With the modular dialer, users should easily be able to configure precedence. With dialer v1, anything we do to prioritise dials is gonna be spaghetti and adhoc. With the modular dialer, youd be able to specify the order of dials when instantiating the pipeline.
- Multiple parallel dials. We already have the issue where new addresses aren't added to existing dials.

View File

@ -1,25 +0,0 @@
'use strict'
const protons = require('protons')
const message = `
message Envelope {
// public_key is the public key of the keypair the enclosed payload was
// signed with.
bytes public_key = 1;
// payload_type encodes the type of payload, so that it can be deserialized
// deterministically.
bytes payload_type = 2;
// payload is the actual payload carried inside this envelope.
bytes payload = 3;
// signature is the signature produced by the private key corresponding to
// the enclosed public key, over the payload, prefixing a domain string for
// additional security.
bytes signature = 5;
}
`
module.exports = protons(message).Envelope

View File

@ -1,182 +0,0 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:envelope')
log.error = debug('libp2p:envelope:error')
const errCode = require('err-code')
const uint8arraysConcat = require('uint8arrays/concat')
const uint8arraysFromString = require('uint8arrays/from-string')
const cryptoKeys = require('libp2p-crypto/src/keys')
const PeerId = require('peer-id')
const varint = require('varint')
const uint8arraysEquals = require('uint8arrays/equals')
const { codes } = require('../../errors')
const Protobuf = require('./envelope.proto')
/**
* The Envelope is responsible for keeping an arbitrary signed record
* by a libp2p peer.
*/
class Envelope {
/**
* @class
* @param {object} params
* @param {PeerId} params.peerId
* @param {Uint8Array} params.payloadType
* @param {Uint8Array} params.payload - marshaled record
* @param {Uint8Array} params.signature - signature of the domain string :: type hint :: payload.
*/
constructor ({ peerId, payloadType, payload, signature }) {
this.peerId = peerId
this.payloadType = payloadType
this.payload = payload
this.signature = signature
// Cache
this._marshal = undefined
}
/**
* Marshal the envelope content.
*
* @returns {Uint8Array}
*/
marshal () {
if (this._marshal) {
return this._marshal
}
const publicKey = cryptoKeys.marshalPublicKey(this.peerId.pubKey)
this._marshal = Protobuf.encode({
public_key: publicKey,
payload_type: this.payloadType,
payload: this.payload,
signature: this.signature
})
return this._marshal
}
/**
* Verifies if the other Envelope is identical to this one.
*
* @param {Envelope} other
* @returns {boolean}
*/
equals (other) {
return uint8arraysEquals(this.peerId.pubKey.bytes, other.peerId.pubKey.bytes) &&
uint8arraysEquals(this.payloadType, other.payloadType) &&
uint8arraysEquals(this.payload, other.payload) &&
uint8arraysEquals(this.signature, other.signature)
}
/**
* Validate envelope data signature for the given domain.
*
* @param {string} domain
* @returns {Promise<boolean>}
*/
validate (domain) {
const signData = formatSignaturePayload(domain, this.payloadType, this.payload)
return this.peerId.pubKey.verify(signData, this.signature)
}
}
/**
* Helper function that prepares a Uint8Array to sign or verify a signature.
*
* @param {string} domain
* @param {Uint8Array} payloadType
* @param {Uint8Array} payload
* @returns {Uint8Array}
*/
const formatSignaturePayload = (domain, payloadType, payload) => {
// When signing, a peer will prepare a Uint8Array by concatenating the following:
// - The length of the domain separation string string in bytes
// - The domain separation string, encoded as UTF-8
// - The length of the payload_type field in bytes
// - The value of the payload_type field
// - The length of the payload field in bytes
// - The value of the payload field
domain = uint8arraysFromString(domain)
const domainLength = varint.encode(domain.byteLength)
const payloadTypeLength = varint.encode(payloadType.length)
const payloadLength = varint.encode(payload.length)
return uint8arraysConcat([
new Uint8Array(domainLength),
domain,
new Uint8Array(payloadTypeLength),
payloadType,
new Uint8Array(payloadLength),
payload
])
}
/**
* Unmarshal a serialized Envelope protobuf message.
*
* @param {Uint8Array} data
* @returns {Promise<Envelope>}
*/
Envelope.createFromProtobuf = async (data) => {
const envelopeData = Protobuf.decode(data)
const peerId = await PeerId.createFromPubKey(envelopeData.public_key)
return new Envelope({
peerId,
payloadType: envelopeData.payload_type,
payload: envelopeData.payload,
signature: envelopeData.signature
})
}
/**
* Seal marshals the given Record, places the marshaled bytes inside an Envelope
* and signs it with the given peerId's private key.
*
* @async
* @param {Record} record
* @param {PeerId} peerId
* @returns {Envelope}
*/
Envelope.seal = async (record, peerId) => {
const domain = record.domain
const payloadType = record.codec
const payload = record.marshal()
const signData = formatSignaturePayload(domain, payloadType, payload)
const signature = await peerId.privKey.sign(signData)
return new Envelope({
peerId,
payloadType,
payload,
signature
})
}
/**
* Open and certify a given marshalled envelope.
* Data is unmarshalled and the signature validated for the given domain.
*
* @param {Uint8Array} data
* @param {string} domain
* @returns {Envelope}
*/
Envelope.openAndCertify = async (data, domain) => {
const envelope = await Envelope.createFromProtobuf(data)
const valid = await envelope.validate(domain)
if (!valid) {
throw errCode(new Error('envelope signature is not valid for the given domain'), codes.ERR_SIGNATURE_NOT_VALID)
}
return envelope
}
module.exports = Envelope

View File

@ -1,11 +0,0 @@
'use strict'
const multicodec = require('multicodec')
// The domain string used for peer records contained in a Envelope.
module.exports.ENVELOPE_DOMAIN_PEER_RECORD = multicodec.getName(multicodec.LIBP2P_PEER_RECORD)
// The type hint used to identify peer records in a Envelope.
// Defined in https://github.com/multiformats/multicodec/blob/master/table.csv
// with name "libp2p-peer-record"
module.exports.ENVELOPE_PAYLOAD_TYPE_PEER_RECORD = Uint8Array.from([3, 1])

View File

@ -1,103 +0,0 @@
'use strict'
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const Record = require('libp2p-interfaces/src/record')
const arrayEquals = require('libp2p-utils/src/array-equals')
const Protobuf = require('./peer-record.proto')
const {
ENVELOPE_DOMAIN_PEER_RECORD,
ENVELOPE_PAYLOAD_TYPE_PEER_RECORD
} = require('./consts')
/**
* The PeerRecord is used for distributing peer routing records across the network.
* It contains the peer's reachable listen addresses.
*/
class PeerRecord extends Record {
/**
* @class
* @param {object} params
* @param {PeerId} params.peerId
* @param {Array<multiaddr>} params.multiaddrs - addresses of the associated peer.
* @param {number} [params.seqNumber] - monotonically-increasing sequence counter that's used to order PeerRecords in time.
*/
constructor ({ peerId, multiaddrs = [], seqNumber = Date.now() }) {
super(ENVELOPE_DOMAIN_PEER_RECORD, ENVELOPE_PAYLOAD_TYPE_PEER_RECORD)
this.peerId = peerId
this.multiaddrs = multiaddrs
this.seqNumber = seqNumber
// Cache
this._marshal = undefined
}
/**
* Marshal a record to be used in an envelope.
*
* @returns {Uint8Array}
*/
marshal () {
if (this._marshal) {
return this._marshal
}
this._marshal = Protobuf.encode({
peer_id: this.peerId.toBytes(),
seq: this.seqNumber,
addresses: this.multiaddrs.map((m) => ({
multiaddr: m.bytes
}))
})
return this._marshal
}
/**
* Returns true if `this` record equals the `other`.
*
* @param {Record} other
* @returns {boolean}
*/
equals (other) {
// Validate PeerId
if (!this.peerId.equals(other.peerId)) {
return false
}
// Validate seqNumber
if (this.seqNumber !== other.seqNumber) {
return false
}
// Validate multiaddrs
if (!arrayEquals(this.multiaddrs, other.multiaddrs)) {
return false
}
return true
}
}
/**
* Unmarshal Peer Record Protobuf.
*
* @param {Uint8Array} buf - marshaled peer record.
* @returns {PeerRecord}
*/
PeerRecord.createFromProtobuf = (buf) => {
// Decode
const peerRecord = Protobuf.decode(buf)
const peerId = PeerId.createFromBytes(peerRecord.peer_id)
const multiaddrs = (peerRecord.addresses || []).map((a) => multiaddr(a.multiaddr))
const seqNumber = peerRecord.seq
return new PeerRecord({ peerId, multiaddrs, seqNumber })
}
PeerRecord.DOMAIN = ENVELOPE_DOMAIN_PEER_RECORD
module.exports = PeerRecord

View File

@ -1,29 +0,0 @@
'use strict'
const protons = require('protons')
// PeerRecord messages contain information that is useful to share with other peers.
// Currently, a PeerRecord contains the public listen addresses for a peer, but this
// is expected to expand to include other information in the future.
// PeerRecords are designed to be serialized to bytes and placed inside of
// SignedEnvelopes before sharing with other peers.
const message = `
message PeerRecord {
// AddressInfo is a wrapper around a binary multiaddr. It is defined as a
// separate message to allow us to add per-address metadata in the future.
message AddressInfo {
bytes multiaddr = 1;
}
// peer_id contains a libp2p peer id in its binary representation.
bytes peer_id = 1;
// seq contains a monotonically-increasing sequence counter to order PeerRecords in time.
uint64 seq = 2;
// addresses is a list of public listen addresses for the peer.
repeated AddressInfo addresses = 3;
}
`
module.exports = protons(message).PeerRecord

View File

@ -18,7 +18,7 @@ class Registrar {
* @param {Object} props * @param {Object} props
* @param {PeerStore} props.peerStore * @param {PeerStore} props.peerStore
* @param {connectionManager} props.connectionManager * @param {connectionManager} props.connectionManager
* @class * @constructor
*/ */
constructor ({ peerStore, connectionManager }) { constructor ({ peerStore, connectionManager }) {
// Used on topology to listen for protocol changes // Used on topology to listen for protocol changes
@ -49,7 +49,6 @@ class Registrar {
/** /**
* Get a connection with a peer. * Get a connection with a peer.
*
* @param {PeerId} peerId * @param {PeerId} peerId
* @returns {Connection} * @returns {Connection}
*/ */
@ -59,9 +58,8 @@ class Registrar {
/** /**
* Register handlers for a set of multicodecs given * Register handlers for a set of multicodecs given
* * @param {Topology} topology protocol topology
* @param {Topology} topology - protocol topology * @return {string} registrar identifier
* @returns {string} registrar identifier
*/ */
register (topology) { register (topology) {
if (!Topology.isTopology(topology)) { if (!Topology.isTopology(topology)) {
@ -81,9 +79,8 @@ class Registrar {
/** /**
* Unregister topology. * Unregister topology.
* * @param {string} id registrar identifier
* @param {string} id - registrar identifier * @return {boolean} unregistered successfully
* @returns {boolean} unregistered successfully
*/ */
unregister (id) { unregister (id) {
return this.topologies.delete(id) return this.topologies.delete(id)
@ -91,7 +88,6 @@ class Registrar {
/** /**
* Remove a disconnected peer from the record * Remove a disconnected peer from the record
*
* @param {Connection} connection * @param {Connection} connection
* @param {Error} [error] * @param {Error} [error]
* @returns {void} * @returns {void}

View File

@ -9,11 +9,11 @@ log.error = debug('libp2p:transports:error')
class TransportManager { class TransportManager {
/** /**
* @class * @constructor
* @param {object} options * @param {object} options
* @param {Libp2p} options.libp2p - The Libp2p instance. It will be passed to the transports. * @param {Libp2p} options.libp2p The Libp2p instance. It will be passed to the transports.
* @param {Upgrader} options.upgrader - The upgrader to provide to the transports * @param {Upgrader} options.upgrader The upgrader to provide to the transports
* @param {boolean} [options.faultTolerance = FAULT_TOLERANCE.FATAL_ALL] - Address listen error tolerance. * @param {boolean} [options.faultTolerance = FAULT_TOLERANCE.FATAL_ALL] Address listen error tolerance.
*/ */
constructor ({ libp2p, upgrader, faultTolerance = FAULT_TOLERANCE.FATAL_ALL }) { constructor ({ libp2p, upgrader, faultTolerance = FAULT_TOLERANCE.FATAL_ALL }) {
this.libp2p = libp2p this.libp2p = libp2p
@ -26,9 +26,9 @@ class TransportManager {
/** /**
* Adds a `Transport` to the manager * Adds a `Transport` to the manager
* *
* @param {string} key * @param {String} key
* @param {Transport} Transport * @param {Transport} Transport
* @param {*} transportOptions - Additional options to pass to the transport * @param {*} transportOptions Additional options to pass to the transport
* @returns {void} * @returns {void}
*/ */
add (key, Transport, transportOptions = {}) { add (key, Transport, transportOptions = {}) {
@ -54,7 +54,6 @@ class TransportManager {
/** /**
* Stops all listeners * Stops all listeners
*
* @async * @async
*/ */
async close () { async close () {
@ -76,7 +75,6 @@ class TransportManager {
/** /**
* Dials the given Multiaddr over it's supported transport * Dials the given Multiaddr over it's supported transport
*
* @param {Multiaddr} ma * @param {Multiaddr} ma
* @param {*} options * @param {*} options
* @returns {Promise<Connection>} * @returns {Promise<Connection>}
@ -97,7 +95,6 @@ class TransportManager {
/** /**
* Returns all Multiaddr's the listeners are using * Returns all Multiaddr's the listeners are using
*
* @returns {Multiaddr[]} * @returns {Multiaddr[]}
*/ */
getAddrs () { getAddrs () {
@ -112,7 +109,6 @@ class TransportManager {
/** /**
* Returns all the transports instances. * Returns all the transports instances.
*
* @returns {Iterator<Transport>} * @returns {Iterator<Transport>}
*/ */
getTransports () { getTransports () {
@ -121,7 +117,6 @@ class TransportManager {
/** /**
* Finds a transport that matches the given Multiaddr * Finds a transport that matches the given Multiaddr
*
* @param {Multiaddr} ma * @param {Multiaddr} ma
* @returns {Transport|null} * @returns {Transport|null}
*/ */
@ -135,7 +130,6 @@ class TransportManager {
/** /**
* Starts listeners for each listen Multiaddr. * Starts listeners for each listen Multiaddr.
*
* @async * @async
*/ */
async listen () { async listen () {
@ -212,7 +206,6 @@ class TransportManager {
/** /**
* Removes all transports from the manager. * Removes all transports from the manager.
* If any listeners are running, they will be closed. * If any listeners are running, they will be closed.
*
* @async * @async
*/ */
async removeAll () { async removeAll () {
@ -229,7 +222,6 @@ class TransportManager {
* Enum Transport Manager Fault Tolerance values. * Enum Transport Manager Fault Tolerance values.
* FATAL_ALL should be used for failing in any listen circumstance. * FATAL_ALL should be used for failing in any listen circumstance.
* NO_FATAL should be used for not failing when not listening. * NO_FATAL should be used for not failing when not listening.
*
* @readonly * @readonly
* @enum {number} * @enum {number}
*/ */

View File

@ -5,7 +5,6 @@ const log = debug('libp2p:upgrader')
log.error = debug('libp2p:upgrader:error') log.error = debug('libp2p:upgrader:error')
const Multistream = require('multistream-select') const Multistream = require('multistream-select')
const { Connection } = require('libp2p-interfaces/src/connection') const { Connection } = require('libp2p-interfaces/src/connection')
const ConnectionStatus = require('libp2p-interfaces/src/connection/status')
const PeerId = require('peer-id') const PeerId = require('peer-id')
const pipe = require('it-pipe') const pipe = require('it-pipe')
const errCode = require('err-code') const errCode = require('err-code')
@ -15,7 +14,7 @@ const { codes } = require('./errors')
/** /**
* @typedef MultiaddrConnection * @typedef MultiaddrConnection
* @property {Function} sink * @property {function} sink
* @property {AsyncIterator} source * @property {AsyncIterator} source
* @property {*} conn * @property {*} conn
* @property {Multiaddr} remoteAddr * @property {Multiaddr} remoteAddr
@ -35,7 +34,7 @@ class Upgrader {
* @param {Metrics} options.metrics * @param {Metrics} options.metrics
* @param {Map<string, Crypto>} options.cryptos * @param {Map<string, Crypto>} options.cryptos
* @param {Map<string, Muxer>} options.muxers * @param {Map<string, Muxer>} options.muxers
* @param {function(Connection)} options.onConnection - Called when a connection is upgraded * @param {function(Connection)} options.onConnection Called when a connection is upgraded
* @param {function(Connection)} options.onConnectionEnd * @param {function(Connection)} options.onConnectionEnd
*/ */
constructor ({ constructor ({
@ -58,7 +57,6 @@ class Upgrader {
/** /**
* Upgrades an inbound connection * Upgrades an inbound connection
*
* @async * @async
* @param {MultiaddrConnection} maConn * @param {MultiaddrConnection} maConn
* @returns {Promise<Connection>} * @returns {Promise<Connection>}
@ -126,7 +124,6 @@ class Upgrader {
/** /**
* Upgrades an outbound connection * Upgrades an outbound connection
*
* @async * @async
* @param {MultiaddrConnection} maConn * @param {MultiaddrConnection} maConn
* @returns {Promise<Connection>} * @returns {Promise<Connection>}
@ -201,15 +198,14 @@ class Upgrader {
/** /**
* A convenience method for generating a new `Connection` * A convenience method for generating a new `Connection`
*
* @private * @private
* @param {object} options * @param {object} options
* @param {string} options.cryptoProtocol - The crypto protocol that was negotiated * @param {string} cryptoProtocol The crypto protocol that was negotiated
* @param {string} options.direction - One of ['inbound', 'outbound'] * @param {string} direction One of ['inbound', 'outbound']
* @param {MultiaddrConnection} options.maConn - The transport layer connection * @param {MultiaddrConnection} maConn The transport layer connection
* @param {*} options.upgradedConn - A duplex connection returned from multiplexer and/or crypto selection * @param {*} upgradedConn A duplex connection returned from multiplexer and/or crypto selection
* @param {Muxer} options.Muxer - The muxer to be used for muxing * @param {Muxer} Muxer The muxer to be used for muxing
* @param {PeerId} options.remotePeer - The peer the connection is with * @param {PeerId} remotePeer The peer the connection is with
* @returns {Connection} * @returns {Connection}
*/ */
_createConnection ({ _createConnection ({
@ -269,19 +265,9 @@ class Upgrader {
maConn.timeline = new Proxy(_timeline, { maConn.timeline = new Proxy(_timeline, {
set: (...args) => { set: (...args) => {
if (connection && args[1] === 'close' && args[2] && !_timeline.close) { if (connection && args[1] === 'close' && args[2] && !_timeline.close) {
// Wait for close to finish before notifying of the closure connection.stat.status = 'closed'
(async () => {
try {
if (connection.stat.status === ConnectionStatus.OPEN) {
await connection.close()
}
} catch (err) {
log.error(err)
} finally {
this.onConnectionEnd(connection) this.onConnectionEnd(connection)
} }
})()
}
return Reflect.set(...args) return Reflect.set(...args)
} }
@ -306,13 +292,7 @@ class Upgrader {
}, },
newStream: newStream || errConnectionNotMultiplexed, newStream: newStream || errConnectionNotMultiplexed,
getStreams: () => muxer ? muxer.streams : errConnectionNotMultiplexed, getStreams: () => muxer ? muxer.streams : errConnectionNotMultiplexed,
close: async (err) => { close: err => maConn.close(err)
await maConn.close(err)
// Ensure remaining streams are aborted
if (muxer) {
muxer.streams.map(stream => stream.abort(err))
}
}
}) })
this.onConnection(connection) this.onConnection(connection)
@ -322,10 +302,9 @@ class Upgrader {
/** /**
* Routes incoming streams to the correct handler * Routes incoming streams to the correct handler
*
* @private * @private
* @param {object} options * @param {object} options
* @param {Connection} options.connection - The connection the stream belongs to * @param {Connection} options.connection The connection the stream belongs to
* @param {Stream} options.stream * @param {Stream} options.stream
* @param {string} options.protocol * @param {string} options.protocol
*/ */
@ -336,10 +315,9 @@ class Upgrader {
/** /**
* Attempts to encrypt the incoming `connection` with the provided `cryptos`. * Attempts to encrypt the incoming `connection` with the provided `cryptos`.
*
* @private * @private
* @async * @async
* @param {PeerId} localPeer - The initiators PeerId * @param {PeerId} localPeer The initiators PeerId
* @param {*} connection * @param {*} connection
* @param {Map<string, Crypto>} cryptos * @param {Map<string, Crypto>} cryptos
* @returns {CryptoResult} An encrypted connection, remote peer `PeerId` and the protocol of the `Crypto` used * @returns {CryptoResult} An encrypted connection, remote peer `PeerId` and the protocol of the `Crypto` used
@ -366,10 +344,9 @@ class Upgrader {
/** /**
* Attempts to encrypt the given `connection` with the provided `cryptos`. * Attempts to encrypt the given `connection` with the provided `cryptos`.
* The first `Crypto` module to succeed will be used * The first `Crypto` module to succeed will be used
*
* @private * @private
* @async * @async
* @param {PeerId} localPeer - The initiators PeerId * @param {PeerId} localPeer The initiators PeerId
* @param {*} connection * @param {*} connection
* @param {PeerId} remotePeerId * @param {PeerId} remotePeerId
* @param {Map<string, Crypto>} cryptos * @param {Map<string, Crypto>} cryptos
@ -397,11 +374,10 @@ class Upgrader {
/** /**
* Selects one of the given muxers via multistream-select. That * Selects one of the given muxers via multistream-select. That
* muxer will be used for all future streams on the connection. * muxer will be used for all future streams on the connection.
*
* @private * @private
* @async * @async
* @param {*} connection - A basic duplex connection to multiplex * @param {*} connection A basic duplex connection to multiplex
* @param {Map<string, Muxer>} muxers - The muxers to attempt multiplexing with * @param {Map<string, Muxer>} muxers The muxers to attempt multiplexing with
* @returns {*} A muxed connection * @returns {*} A muxed connection
*/ */
async _multiplexOutbound (connection, muxers) { async _multiplexOutbound (connection, muxers) {
@ -421,11 +397,10 @@ class Upgrader {
/** /**
* Registers support for one of the given muxers via multistream-select. The * Registers support for one of the given muxers via multistream-select. The
* selected muxer will be used for all future streams on the connection. * selected muxer will be used for all future streams on the connection.
*
* @private * @private
* @async * @async
* @param {*} connection - A basic duplex connection to multiplex * @param {*} connection A basic duplex connection to multiplex
* @param {Map<string, Muxer>} muxers - The muxers to attempt multiplexing with * @param {Map<string, Muxer>} muxers The muxers to attempt multiplexing with
* @returns {*} A muxed connection * @returns {*} A muxed connection
*/ */
async _multiplexInbound (connection, muxers) { async _multiplexInbound (connection, muxers) {

View File

@ -75,7 +75,7 @@ describe('Connection Manager', () => {
expect(libp2p.connectionManager.emit.callCount).to.equal(1) expect(libp2p.connectionManager.emit.callCount).to.equal(1)
const [event, connection] = libp2p.connectionManager.emit.getCall(0).args const [event, connection] = libp2p.connectionManager.emit.getCall(0).args
expect(event).to.equal('peer:connect') expect(event).to.equal('peer:connect')
expect(connection.remotePeer.equals(remoteLibp2p.peerId)).to.equal(true) expect(connection.remotePeer.isEqual(remoteLibp2p.peerId)).to.equal(true)
const libp2pConn = libp2p.connectionManager.get(remoteLibp2p.peerId) const libp2pConn = libp2p.connectionManager.get(remoteLibp2p.peerId)
expect(libp2pConn).to.exist() expect(libp2pConn).to.exist()

View File

@ -3,7 +3,6 @@
const chai = require('chai') const chai = require('chai')
chai.use(require('dirty-chai')) chai.use(require('dirty-chai'))
chai.use(require('chai-as-promised'))
const { expect } = chai const { expect } = chai
const nock = require('nock') const nock = require('nock')
const sinon = require('sinon') const sinon = require('sinon')
@ -12,7 +11,6 @@ const pDefer = require('p-defer')
const mergeOptions = require('merge-options') const mergeOptions = require('merge-options')
const CID = require('cids') const CID = require('cids')
const ipfsHttpClient = require('ipfs-http-client')
const DelegatedContentRouter = require('libp2p-delegated-content-routing') const DelegatedContentRouter = require('libp2p-delegated-content-routing')
const multiaddr = require('multiaddr') const multiaddr = require('multiaddr')
@ -98,13 +96,13 @@ describe('content-routing', () => {
let delegate let delegate
beforeEach(async () => { beforeEach(async () => {
const [peerId] = await peerUtils.createPeerId({ fixture: true }) const [peerId] = await peerUtils.createPeerId({ fixture: false })
delegate = new DelegatedContentRouter(peerId, ipfsHttpClient({ delegate = new DelegatedContentRouter(peerId, {
host: '0.0.0.0', host: '0.0.0.0',
protocol: 'http', protocol: 'http',
port: 60197 port: 60197
}), [ }, [
multiaddr('/ip4/0.0.0.0/tcp/60197') multiaddr('/ip4/0.0.0.0/tcp/60197')
]) ])
@ -229,13 +227,13 @@ describe('content-routing', () => {
let delegate let delegate
beforeEach(async () => { beforeEach(async () => {
const [peerId] = await peerUtils.createPeerId({ fixture: true }) const [peerId] = await peerUtils.createPeerId({ fixture: false })
delegate = new DelegatedContentRouter(peerId, ipfsHttpClient({ delegate = new DelegatedContentRouter(peerId, {
host: '0.0.0.0', host: '0.0.0.0',
protocol: 'http', protocol: 'http',
port: 60197 port: 60197
}), [ }, [
multiaddr('/ip4/0.0.0.0/tcp/60197') multiaddr('/ip4/0.0.0.0/tcp/60197')
]) ])

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