mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-07-23 20:41:57 +00:00
Compare commits
36 Commits
v0.27.5
...
feat/keybo
Author | SHA1 | Date | |
---|---|---|---|
|
14bd946990 | ||
|
7969151458 | ||
|
1c2c4324bf | ||
|
6610ef33f9 | ||
|
1059c87925 | ||
|
b880d5de97 | ||
|
da7ddbb160 | ||
|
7d3d6dec68 | ||
|
8c92e8b3d8 | ||
|
0252eaf960 | ||
|
de05e70820 | ||
|
beb6b8090d | ||
|
52f45d0319 | ||
|
bc9760319c | ||
|
261c99dd18 | ||
|
31d8a929ea | ||
|
67fda7e106 | ||
|
e96f01ae74 | ||
|
ea149cd1fe | ||
|
daf980e2ba | ||
|
ac124644d5 | ||
|
a08a725123 | ||
|
97455957ac | ||
|
5255d87d0a | ||
|
03ce7c3103 | ||
|
ea0621b2f6 | ||
|
9197df309b | ||
|
8dd361338b | ||
|
9b13fe321a | ||
|
1f85e0213a | ||
|
09a604b5e5 | ||
|
ed16b69057 | ||
|
c940f2d384 | ||
|
2620d46f01 | ||
|
e192eb6508 | ||
|
b57de4ed0c |
12
.aegir.js
12
.aegir.js
@@ -4,21 +4,21 @@ const Libp2p = require('./src')
|
||||
const { MULTIADDRS_WEBSOCKETS } = require('./test/fixtures/browser')
|
||||
const Peers = require('./test/fixtures/peers')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const Muxer = require('libp2p-mplex')
|
||||
const Crypto = require('libp2p-secio')
|
||||
const { NOISE: Crypto } = require('libp2p-noise')
|
||||
const pipe = require('it-pipe')
|
||||
let libp2p
|
||||
|
||||
const before = async () => {
|
||||
// Use the last peer
|
||||
const peerId = await PeerId.createFromJSON(Peers[Peers.length - 1])
|
||||
const peerInfo = new PeerInfo(peerId)
|
||||
peerInfo.multiaddrs.add(MULTIADDRS_WEBSOCKETS[0])
|
||||
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
addresses: {
|
||||
listen: [MULTIADDRS_WEBSOCKETS[0]]
|
||||
},
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [WebSockets],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -45,7 +45,7 @@ const after = async () => {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
bundlesize: { maxSize: '220kB' },
|
||||
bundlesize: { maxSize: '185kB' },
|
||||
hooks: {
|
||||
pre: before,
|
||||
post: after
|
||||
|
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,3 +1,24 @@
|
||||
<a name="0.27.7"></a>
|
||||
## [0.27.7](https://github.com/libp2p/js-libp2p/compare/v0.27.6...v0.27.7) (2020-04-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove node global ([#587](https://github.com/libp2p/js-libp2p/issues/587)) ([9b13fe3](https://github.com/libp2p/js-libp2p/commit/9b13fe3))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.6"></a>
|
||||
## [0.27.6](https://github.com/libp2p/js-libp2p/compare/v0.27.5...v0.27.6) (2020-04-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add null check in libp2p.hangUp() ([c940f2d](https://github.com/libp2p/js-libp2p/commit/c940f2d))
|
||||
* make circuit relay listening addresses more forgiving ([#604](https://github.com/libp2p/js-libp2p/issues/604)) ([e192eb6](https://github.com/libp2p/js-libp2p/commit/e192eb6))
|
||||
|
||||
|
||||
|
||||
<a name="0.27.5"></a>
|
||||
## [0.27.5](https://github.com/libp2p/js-libp2p/compare/v0.27.4...v0.27.5) (2020-04-06)
|
||||
|
||||
|
@@ -39,10 +39,6 @@ If you are looking for the documentation of the latest release, you can view the
|
||||
|
||||
[**`Weekly Core Dev Calls`**](https://github.com/ipfs/pm/issues/650)
|
||||
|
||||
## Tech Lead
|
||||
|
||||
[David Dias](https://github.com/diasdavid/)
|
||||
|
||||
## Lead Maintainer
|
||||
|
||||
[Jacob Heun](https://github.com/jacobheun/)
|
||||
@@ -148,15 +144,16 @@ List of packages currently in existence for libp2p
|
||||
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-websockets`](//github.com/libp2p/js-libp2p-websockets) | [](//github.com/libp2p/js-libp2p-websockets/releases) | [](https://david-dm.org/libp2p/js-libp2p-websockets) | [](https://travis-ci.com/libp2p/js-libp2p-websockets) | [](https://codecov.io/gh/libp2p/js-libp2p-websockets) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **secure channels** |
|
||||
| [`libp2p-noise`](//github.com/NodeFactoryIo/js-libp2p-noise) | [](//github.com/NodeFactoryIo/js-libp2p-noise/releases) | [](https://david-dm.org/NodeFactoryIo/js-libp2p-noise) | [](https://travis-ci.com/NodeFactoryIo/js-libp2p-noise) | [](https://codecov.io/gh/NodeFactoryIo/js-libp2p-noise) | N/A |
|
||||
| [`libp2p-secio`](//github.com/libp2p/js-libp2p-secio) | [](//github.com/libp2p/js-libp2p-secio/releases) | [](https://david-dm.org/libp2p/js-libp2p-secio) | [](https://travis-ci.com/libp2p/js-libp2p-secio) | [](https://codecov.io/gh/libp2p/js-libp2p-secio) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
|
||||
| **stream multiplexers** |
|
||||
| [`libp2p-mplex`](//github.com/libp2p/js-libp2p-mplex) | [](//github.com/libp2p/js-libp2p-mplex/releases) | [](https://david-dm.org/libp2p/js-libp2p-mplex) | [](https://travis-ci.com/libp2p/js-libp2p-mplex) | [](https://codecov.io/gh/libp2p/js-libp2p-mplex) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-spdy`](//github.com/libp2p/js-libp2p-spdy) | [](//github.com/libp2p/js-libp2p-spdy/releases) | [](https://david-dm.org/libp2p/js-libp2p-spdy) | [](https://travis-ci.com/libp2p/js-libp2p-spdy) | [](https://codecov.io/gh/libp2p/js-libp2p-spdy) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **peer discovery** |
|
||||
| [`libp2p-bootstrap`](//github.com/libp2p/js-libp2p-bootstrap) | [](//github.com/libp2p/js-libp2p-bootstrap/releases) | [](https://david-dm.org/libp2p/js-libp2p-bootstrap) | [](https://travis-ci.com/libp2p/js-libp2p-bootstrap) | [](https://codecov.io/gh/libp2p/js-libp2p-bootstrap) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [](//github.com/libp2p/js-libp2p-kad-dht/releases) | [](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`libp2p-mdns`](//github.com/libp2p/js-libp2p-mdns) | [](//github.com/libp2p/js-libp2p-mdns/releases) | [](https://david-dm.org/libp2p/js-libp2p-mdns) | [](https://travis-ci.com/libp2p/js-libp2p-mdns) | [](https://codecov.io/gh/libp2p/js-libp2p-mdns) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-webrtc-star`](//github.com/libp2p/js-libp2p-webrtc-star) | [](//github.com/libp2p/js-libp2p-webrtc-star/releases) | [](https://david-dm.org/libp2p/js-libp2p-webrtc-star) | [](https://travis-ci.com/libp2p/js-libp2p-webrtc-star) | [](https://codecov.io/gh/libp2p/js-libp2p-webrtc-star) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| [`discv5`](//github.com/ChainSafe/discv5) | [](//github.com/ChainSafe/discv5/releases) | [](https://david-dm.org/ChainSafe/discv5) | [](https://travis-ci.com/ChainSafe/discv5) | [](https://codecov.io/gh/ChainSafe/discv5) | [Cayman Nava](mailto:caymannava@gmail.com) |
|
||||
| **content routing** |
|
||||
| [`libp2p-delegated-content-routing`](//github.com/libp2p/js-libp2p-delegated-content-routing) | [](//github.com/libp2p/js-libp2p-delegated-content-routing/releases) | [](https://david-dm.org/libp2p/js-libp2p-delegated-content-routing) | [](https://travis-ci.com/libp2p/js-libp2p-delegated-content-routing) | [](https://codecov.io/gh/libp2p/js-libp2p-delegated-content-routing) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [](//github.com/libp2p/js-libp2p-kad-dht/releases) | [](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
|
738
doc/API.md
738
doc/API.md
@@ -11,24 +11,48 @@
|
||||
* [`handle`](#handle)
|
||||
* [`unhandle`](#unhandle)
|
||||
* [`ping`](#ping)
|
||||
* [`peerRouting.findPeer`](#peerroutingfindpeer)
|
||||
* [`multiaddrs`](#multiaddrs)
|
||||
* [`addressManager.getListenAddrs`](#addressmanagergetlistenaddrs)
|
||||
* [`addressmger.getAnnounceAddrs`](#addressmanagergetannounceaddrs)
|
||||
* [`addressManager.getNoAnnounceAddrs`](#addressmanagergetnoannounceaddrs)
|
||||
* [`contentRouting.findProviders`](#contentroutingfindproviders)
|
||||
* [`contentRouting.provide`](#contentroutingprovide)
|
||||
* [`contentRouting.put`](#contentroutingput)
|
||||
* [`contentRouting.get`](#contentroutingget)
|
||||
* [`contentRouting.getMany`](#contentroutinggetmany)
|
||||
* [`peerRouting.findPeer`](#peerroutingfindpeer)
|
||||
* [`peerStore.addressBook.add`](#peerstoreaddressbookadd)
|
||||
* [`peerStore.addressBook.delete`](#peerstoreaddressbookdelete)
|
||||
* [`peerStore.addressBook.get`](#peerstoreaddressbookget)
|
||||
* [`peerStore.addressBook.getMultiaddrsForPeer`](#peerstoreaddressbookgetmultiaddrsforpeer)
|
||||
* [`peerStore.addressBook.set`](#peerstoreaddressbookset)
|
||||
* [`peerStore.keyBook.delete`](#peerstorekeybookdelete)
|
||||
* [`peerStore.keyBook.get`](#peerstorekeybookget)
|
||||
* [`peerStore.keyBook.set`](#peerstorekeybookset)
|
||||
* [`peerStore.protoBook.add`](#peerstoreprotobookadd)
|
||||
* [`peerStore.protoBook.delete`](#peerstoreprotobookdelete)
|
||||
* [`peerStore.protoBook.get`](#peerstoreprotobookget)
|
||||
* [`peerStore.protoBook.set`](#peerstoreprotobookset)
|
||||
* [`peerStore.delete`](#peerstoredelete)
|
||||
* [`peerStore.get`](#peerstoreget)
|
||||
* [`peerStore.peers`](#peerstorepeers)
|
||||
* [`pubsub.getSubscribers`](#pubsubgetsubscribers)
|
||||
* [`pubsub.getTopics`](#pubsubgettopics)
|
||||
* [`pubsub.publish`](#pubsubpublish)
|
||||
* [`pubsub.subscribe`](#pubsubsubscribe)
|
||||
* [`pubsub.unsubscribe`](#pubsubunsubscribe)
|
||||
* [`connectionManager.get`](#connectionmanagerget)
|
||||
* [`connectionManager.setPeerValue`](#connectionmanagersetpeervalue)
|
||||
* [`connectionManager.size`](#connectionmanagersize)
|
||||
* [`metrics.global`](#metricsglobal)
|
||||
* [`metrics.peers`](#metricspeers)
|
||||
* [`metrics.protocols`](#metricsprotocols)
|
||||
* [`metrics.forPeer`](#metricsforpeer)
|
||||
* [`metrics.forProtocol`](#metricsforprotocol)
|
||||
* [Events](#events)
|
||||
* [`libp2p`](#libp2p)
|
||||
* [`libp2p.connectionManager`](#libp2pconnectionmanager)
|
||||
* [`libp2p.peerStore`](#libp2ppeerStore)
|
||||
* [Types](#types)
|
||||
* [`Stats`](#stats)
|
||||
|
||||
@@ -44,14 +68,16 @@ Creates an instance of Libp2p.
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| options | `Object` | libp2p options |
|
||||
| options.modules | `Array<Object>` | libp2p modules to use |
|
||||
| [options.config] | `Object` | libp2p modules configuration and core configuration |
|
||||
| [options.connectionManager] | `Object` | libp2p Connection 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.dialer] | `Object` | libp2p Dialer configuration
|
||||
| [options.metrics] | `Object` | libp2p Metrics configuration
|
||||
| [options.peerInfo] | [`PeerInfo`][peer-info] | peerInfo instance (it will be created if not provided) |
|
||||
| options | `object` | libp2p options |
|
||||
| options.modules | `Array<object>` | 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.config] | `object` | libp2p modules configuration and core configuration |
|
||||
| [options.connectionManager] | `object` | libp2p Connection 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.dialer] | `object` | libp2p Dialer configuration
|
||||
| [options.metrics] | `object` | libp2p Metrics configuration
|
||||
| [options.peerId] | [`PeerId`][peer-id] | peerId instance (it will be created if not provided) |
|
||||
| [options.peerStore] | `object` | libp2p PeerStore configuration |
|
||||
|
||||
For Libp2p configurations and modules details read the [Configuration Document](./CONFIGURATION.md).
|
||||
|
||||
@@ -73,7 +99,7 @@ const options = {}
|
||||
const libp2p = await Libp2p.create(options)
|
||||
```
|
||||
|
||||
Note: The [`PeerInfo`][peer-info] option is not required and will be generated if it is not provided.
|
||||
Note: The [`PeerId`][peer-id] option is not required and will be generated if it is not provided.
|
||||
|
||||
<details><summary>Alternative</summary>
|
||||
As an alternative, it is possible to create a Libp2p instance with the constructor:
|
||||
@@ -92,7 +118,7 @@ const libp2p = new Libp2p(options)
|
||||
|
||||
Required keys in the `options` object:
|
||||
|
||||
- `peerInfo`: instance of [`PeerInfo`][peer-info] that contains the [`PeerId`][peer-id], Keys and [multiaddrs][multiaddr] of the libp2p Node (optional when using `.create`).
|
||||
- `peerId`: instance of [`PeerId`][peer-id] that contains the peer Keys (optional when using `.create`).
|
||||
- `modules.transport`: An array that must include at least 1 compliant transport. See [modules that implement the transport interface](https://github.com/libp2p/js-interfaces/tree/master/src/transport#modules-that-implement-the-interface).
|
||||
|
||||
</details>
|
||||
@@ -149,6 +175,10 @@ const libp2p = await Libp2p.create(options)
|
||||
await libp2p.stop()
|
||||
```
|
||||
|
||||
### addresses
|
||||
|
||||
TODO with `address-manager`.
|
||||
|
||||
### connections
|
||||
|
||||
A Getter that returns a Map of the current Connections libp2p has to other peers.
|
||||
@@ -180,10 +210,12 @@ for (const [peerId, connections] of libp2p.connections) {
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peer | [`PeerInfo`][peer-info]\|[`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | The peer to dial. If a [`Multiaddr`][multiaddr] or its string is provided, it **must** include the peer id |
|
||||
| [options] | `Object` | dial options |
|
||||
| peer | [`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | The peer to dial. |
|
||||
| [options] | `object` | dial options |
|
||||
| [options.signal] | [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) | An `AbortSignal` instance obtained from an [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) that can be used to abort the connection before it completes |
|
||||
|
||||
**Note:** If a [`Multiaddr`][multiaddr] or its string is provided, it **must** include the peer id. Moreover, if a [`PeerId`][peer-id] is given, the peer will need to have known multiaddrs for it in the PeerStore.
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
@@ -194,7 +226,7 @@ for (const [peerId, connections] of libp2p.connections) {
|
||||
|
||||
```js
|
||||
// ...
|
||||
const conn = await libp2p.dial(remotePeerInfo)
|
||||
const conn = await libp2p.dial(remotePeerId)
|
||||
|
||||
// create a new stream within the connection
|
||||
const { stream, protocol } = await conn.newStream(['/echo/1.1.0', '/echo/1.0.0'])
|
||||
@@ -215,11 +247,13 @@ Dials to another peer in the network and selects a protocol to communicate with
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peer | [`PeerInfo`][peer-info]\|[`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | The peer to dial. If a [`Multiaddr`][multiaddr] or its string is provided, it **must** include the peer id |
|
||||
| protocols | `String|Array<String>` | A list of protocols (or single protocol) to negotiate with. Protocols are attempted in order until a match is made. (e.g '/ipfs/bitswap/1.1.0') |
|
||||
| [options] | `Object` | dial options |
|
||||
| peer | [`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | The peer to dial. |
|
||||
| protocols | `string|Array<string>` | A list of protocols (or single protocol) to negotiate with. Protocols are attempted in order until a match is made. (e.g '/ipfs/bitswap/1.1.0') |
|
||||
| [options] | `object` | dial options |
|
||||
| [options.signal] | [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) | An `AbortSignal` instance obtained from an [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) that can be used to abort the connection before it completes |
|
||||
|
||||
**Note:** If a [`Multiaddr`][multiaddr] or its string is provided, it **must** include the peer id. Moreover, if a [`PeerId`][peer-id] is given, the peer will need to have known multiaddrs for it in the PeerStore.
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
@@ -232,7 +266,7 @@ Dials to another peer in the network and selects a protocol to communicate with
|
||||
// ...
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
const { stream, protocol } = await libp2p.dialProtocol(remotePeerInfo, protocols)
|
||||
const { stream, protocol } = await libp2p.dialProtocol(remotePeerId, protocols)
|
||||
|
||||
// Use this new stream like any other duplex stream
|
||||
pipe([1, 2, 3], stream, consume)
|
||||
@@ -248,7 +282,7 @@ Attempts to gracefully close an open connection to the given peer. If the connec
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peer | [`PeerInfo`][peer-info]\|[`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | peer to hang up |
|
||||
| peer | [`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | peer to hang up |
|
||||
|
||||
#### Returns
|
||||
|
||||
@@ -260,7 +294,7 @@ Attempts to gracefully close an open connection to the given peer. If the connec
|
||||
|
||||
```js
|
||||
// ...
|
||||
await libp2p.hangUp(remotePeerInfo)
|
||||
await libp2p.hangUp(remotePeerId)
|
||||
```
|
||||
|
||||
### handle
|
||||
@@ -275,7 +309,7 @@ In the event of a new handler for the same protocol being added, the first one i
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| protocols | `Array<String>|String` | protocols to register |
|
||||
| protocols | `Array<string>|string` | protocols to register |
|
||||
| handler | `function({ connection:*, stream:*, protocol:string })` | handler to call |
|
||||
|
||||
|
||||
@@ -300,7 +334,7 @@ Unregisters all handlers with the given protocols
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| protocols | `Array<String>|String` | protocols to unregister |
|
||||
| protocols | `Array<string>|string` | protocols to unregister |
|
||||
|
||||
#### Example
|
||||
|
||||
@@ -319,7 +353,7 @@ Pings a given peer and get the operation's latency.
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peer | [`PeerInfo`][peer-info]\|[`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | peer to ping |
|
||||
| peer | [`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | peer to ping |
|
||||
|
||||
#### Returns
|
||||
|
||||
@@ -334,31 +368,106 @@ Pings a given peer and get the operation's latency.
|
||||
const latency = await libp2p.ping(otherPeerId)
|
||||
```
|
||||
|
||||
### peerRouting.findPeer
|
||||
## multiaddrs
|
||||
|
||||
Iterates over all peer routers in series to find the given peer. If the DHT is enabled, it will be tried first.
|
||||
Gets the multiaddrs the libp2p node announces to the network. This computes the advertising multiaddrs
|
||||
of the peer by joining the multiaddrs that libp2p transports are listening on with the announce multiaddrs
|
||||
provided in the libp2p config. Configured no announce multiaddrs will be filtered out of the advertised addresses.
|
||||
|
||||
`libp2p.peerRouting.findPeer(peerId, options)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | ID of the peer to find |
|
||||
| options | `Object` | operation options |
|
||||
| options.timeout | `number` | maximum time the query should run |
|
||||
`libp2p.multiaddrs`
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Promise<PeerInfo>` | Peer info of a known peer |
|
||||
| `Array<Multiaddr>` | Advertising multiaddrs |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
// ...
|
||||
const peerInfo = await libp2p.peerRouting.findPeer(peerId, options)
|
||||
const listenMa = libp2p.multiaddrs
|
||||
// [ <Multiaddr 047f00000106f9ba - /ip4/127.0.0.1/tcp/63930> ]
|
||||
```
|
||||
|
||||
### addressManager.getListenAddrs
|
||||
|
||||
Get the multiaddrs that were provided for listening on libp2p transports.
|
||||
|
||||
`libp2p.addressManager.getListenAddrs()`
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Array<Multiaddr>` | Provided listening multiaddrs |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
// ...
|
||||
const listenMa = libp2p.addressManager.getListenAddrs()
|
||||
// [ <Multiaddr 047f00000106f9ba - /ip4/127.0.0.1/tcp/63930> ]
|
||||
```
|
||||
|
||||
### addressManager.getAnnounceAddrs
|
||||
|
||||
Get the multiaddrs that were provided to announce to the network.
|
||||
|
||||
`libp2p.addressManager.getAnnounceAddrs()`
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Array<Multiaddr>` | Provided announce multiaddrs |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
// ...
|
||||
const announceMa = libp2p.addressManager.getAnnounceAddrs()
|
||||
// [ <Multiaddr 047f00000106f9ba - /dns4/peer.io/...> ]
|
||||
```
|
||||
|
||||
### addressManager.getNoAnnounceAddrs
|
||||
|
||||
Get the multiaddrs that were provided to not announce to the network.
|
||||
|
||||
`libp2p.addressManager.getNoAnnounceAddrs()`
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Array<Multiaddr>` | Provided noAnnounce multiaddrs |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
// ...
|
||||
const noAnnounceMa = libp2p.addressManager.getNoAnnounceAddrs()
|
||||
// [ <Multiaddr 047f00000106f9ba - /ip4/127.0.0.1/tcp/63930> ]
|
||||
```
|
||||
|
||||
### transportManager.getAddrs
|
||||
|
||||
Get the multiaddrs that libp2p transports are using to listen on.
|
||||
|
||||
`libp2p.transportManager.getAddrs()`
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Array<Multiaddr>` | listening multiaddrs |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
// ...
|
||||
const listenMa = libp2p.transportManager.getAddrs()
|
||||
// [ <Multiaddr 047f00000106f9ba - /ip4/127.0.0.1/tcp/63930> ]
|
||||
```
|
||||
|
||||
### contentRouting.findProviders
|
||||
@@ -373,7 +482,7 @@ Once a content router succeeds, the iteration will stop. If the DHT is enabled,
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| cid | [`CID`][cid] | cid to find |
|
||||
| options | `Object` | operation options |
|
||||
| options | `object` | operation options |
|
||||
| options.timeout | `number` | maximum time the query should run |
|
||||
| options.maxNumProviders | `number` | maximum number of providers to find |
|
||||
|
||||
@@ -381,14 +490,14 @@ Once a content router succeeds, the iteration will stop. If the DHT is enabled,
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `AsyncIterator<PeerInfo>` | Async iterator for [`PeerInfo`][peer-info] |
|
||||
| `AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }` | Async iterator for peer data |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
// Iterate over the providers found for the given cid
|
||||
for await (const provider of libp2p.contentRouting.findProviders(cid)) {
|
||||
console.log(provider)
|
||||
console.log(provider.id, provider.multiaddrs)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -427,9 +536,9 @@ Writes a value to a key in the DHT.
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| key | `String` | key to add to the dht |
|
||||
| key | `string` | key 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) |
|
||||
|
||||
#### Returns
|
||||
@@ -458,8 +567,8 @@ Queries the DHT for a value stored for a given key.
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| key | `String` | key to get from the dht |
|
||||
| [options] | `Object` | get options |
|
||||
| key | `string` | key to get from the dht |
|
||||
| [options] | `object` | get options |
|
||||
| [options.timeout] | `number` | maximum time the query should run |
|
||||
|
||||
#### Returns
|
||||
@@ -487,9 +596,9 @@ Queries the DHT for the n values stored for the given key (without sorting).
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| key | `String` | key to get from the dht |
|
||||
| key | `string` | key to get from the dht |
|
||||
| nvals | `number` | number of values aimed |
|
||||
| [options] | `Object` | get options |
|
||||
| [options] | `object` | get options |
|
||||
| [options.timeout] | `number` | maximum time the query should run |
|
||||
|
||||
#### Returns
|
||||
@@ -507,6 +616,455 @@ const key = '/key'
|
||||
const { from, val } = await libp2p.contentRouting.get(key)
|
||||
```
|
||||
|
||||
### peerRouting.findPeer
|
||||
|
||||
Iterates over all peer routers in series to find the given peer. If the DHT is enabled, it will be tried first.
|
||||
|
||||
`libp2p.peerRouting.findPeer(peerId, options)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | ID of the peer to find |
|
||||
| options | `object` | operation options |
|
||||
| options.timeout | `number` | maximum time the query should run |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Promise<{ id: PeerId, multiaddrs: Multiaddr[] }>` | Peer data of a known peer |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
// ...
|
||||
const peer = await libp2p.peerRouting.findPeer(peerId, options)
|
||||
```
|
||||
|
||||
### peerStore.addressBook.add
|
||||
|
||||
Adds known `multiaddrs` of a given peer. If the peer is not known, it will be set with the provided multiaddrs.
|
||||
|
||||
`peerStore.addressBook.add(peerId, multiaddrs)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to set |
|
||||
| multiaddrs | |`Array<Multiaddr>` | [`Multiaddrs`][multiaddr] to add |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `AddressBook` | Returns the Address Book component |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.addressBook.add(peerId, multiaddr)
|
||||
```
|
||||
|
||||
### peerStore.addressBook.delete
|
||||
|
||||
Delete the provided peer from the book.
|
||||
|
||||
`peerStore.addressBook.delete(peerId)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to remove |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `boolean` | true if found and removed |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.addressBook.delete(peerId)
|
||||
// false
|
||||
peerStore.addressBook.set(peerId, multiaddr)
|
||||
peerStore.addressBook.delete(peerId)
|
||||
// true
|
||||
```
|
||||
|
||||
### peerStore.addressBook.get
|
||||
|
||||
Get the known [`Addresses`][address] of a provided peer.
|
||||
|
||||
`peerStore.addressBook.get(peerId)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to get |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Array<Address>` | Array of peer's [`Addresses`][address] containing the multiaddr and its metadata |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.addressBook.get(peerId)
|
||||
// undefined
|
||||
peerStore.addressBook.set(peerId, multiaddr)
|
||||
peerStore.addressBook.get(peerId)
|
||||
// [
|
||||
// {
|
||||
// multiaddr: /ip4/140.10.2.1/tcp/8000,
|
||||
// ...
|
||||
// },
|
||||
// {
|
||||
// multiaddr: /ip4/140.10.2.1/ws/8001
|
||||
// ...
|
||||
// },
|
||||
// ]
|
||||
```
|
||||
|
||||
## peerStore.addressBook.getMultiaddrsForPeer
|
||||
|
||||
Get the known `Multiaddr` of a provided peer. All returned multiaddrs will include the encapsulated `PeerId` of the peer.
|
||||
|
||||
`peerStore.addressBook.getMultiaddrsForPeer(peerId)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to get |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Array<Multiaddr>` | Array of peer's multiaddr |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.addressBook.getMultiaddrsForPeer(peerId)
|
||||
// undefined
|
||||
peerStore.addressBook.set(peerId, multiaddr)
|
||||
peerStore.addressBook.getMultiaddrsForPeer(peerId)
|
||||
// [
|
||||
// /ip4/140.10.2.1/tcp/8000/p2p/QmW8rAgaaA6sRydK1k6vonShQME47aDxaFidbtMevWs73t
|
||||
// /ip4/140.10.2.1/ws/8001/p2p/QmW8rAgaaA6sRydK1k6vonShQME47aDxaFidbtMevWs73t
|
||||
// ]
|
||||
```
|
||||
|
||||
### peerStore.addressBook.set
|
||||
|
||||
Set known `multiaddrs` of a given peer.
|
||||
|
||||
`peerStore.addressBook.set(peerId, multiaddrs)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to set |
|
||||
| multiaddrs | |`Array<Multiaddr>` | [`Multiaddrs`][multiaddr] to store |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `AddressBook` | Returns the Address Book component |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.addressBook.add(peerId, multiaddr)
|
||||
```
|
||||
|
||||
### peerStore.protoBook.add
|
||||
|
||||
Add known `protocols` of a given peer.
|
||||
|
||||
`peerStore.protoBook.add(peerId, protocols)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to set |
|
||||
| protocols | `Array<string>` | protocols to add |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `ProtoBook` | Returns the Proto Book component |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.protoBook.add(peerId, protocols)
|
||||
```
|
||||
|
||||
|
||||
### peerStore.keyBook.delete
|
||||
|
||||
Delete the provided peer from the book.
|
||||
|
||||
`peerStore.keyBook.delete(peerId)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to remove |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `boolean` | true if found and removed |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.keyBook.delete(peerId)
|
||||
// false
|
||||
peerStore.keyBook.set(peerId, publicKey)
|
||||
peerStore.keyBook.delete(peerId)
|
||||
// true
|
||||
```
|
||||
|
||||
### peerStore.keyBook.get
|
||||
|
||||
Get the known `PublicKey` of a provided peer.
|
||||
|
||||
`peerStore.keyBook.get(peerId)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to get |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey` | Peer PublicKey |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.keyBook.get(peerId)
|
||||
// undefined
|
||||
peerStore.keyBook.set(peerId, publicKey)
|
||||
peerStore.keyBook.get(peerId)
|
||||
// PublicKey
|
||||
```
|
||||
|
||||
### peerStore.keyBook.set
|
||||
|
||||
Set known `peerId`. This can include its Public Key.
|
||||
|
||||
`peerStore.keyBook.set(peerId, publicKey)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to set |
|
||||
| publicKey | [`RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey`][keys] | peer's public key |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `KeyBook` | Returns the Key Book component |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
const publicKey = peerId.pubKey
|
||||
peerStore.keyBook.set(peerId, publicKey)
|
||||
```
|
||||
|
||||
### peerStore.protoBook.delete
|
||||
|
||||
Delete the provided peer from the book.
|
||||
|
||||
`peerStore.protoBook.delete(peerId)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to remove |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `boolean` | true if found and removed |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.protoBook.delete(peerId)
|
||||
// false
|
||||
peerStore.protoBook.set(peerId, protocols)
|
||||
peerStore.protoBook.delete(peerId)
|
||||
// true
|
||||
```
|
||||
|
||||
### peerStore.protoBook.get
|
||||
|
||||
Get the known `protocols` of a provided peer.
|
||||
|
||||
`peerStore.protoBook.get(peerId)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to get |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Array<string>` | Array of peer's supported protocols |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.protoBook.get(peerId)
|
||||
// undefined
|
||||
peerStore.protoBook.set(peerId, [ '/proto/1.0.0', '/proto/1.1.0' ])
|
||||
peerStore.protoBook.get(peerId)
|
||||
// [ '/proto/1.0.0', '/proto/1.1.0' ]
|
||||
```
|
||||
|
||||
### peerStore.protoBook.set
|
||||
|
||||
Set known `protocols` of a given peer.
|
||||
|
||||
`peerStore.protoBook.set(peerId, protocols)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to set |
|
||||
| protocols | `Array<string>` | protocols to store |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `ProtoBook` | Returns the Proto Book component |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.protoBook.set(peerId, protocols)
|
||||
```
|
||||
|
||||
### peerStore.delete
|
||||
|
||||
Delete the provided peer from every book.
|
||||
|
||||
`peerStore.delete(peerId)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to remove |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `boolean` | true if found and removed |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.delete(peerId)
|
||||
// false
|
||||
peerStore.addressBook.set(peerId, multiaddrs)
|
||||
peerStore.protoBook.set(peerId, protocols)
|
||||
peerStore.delete(peerId)
|
||||
// true
|
||||
peerStore.delete(peerId2)
|
||||
// false
|
||||
peerStore.addressBook.set(peerId2, multiaddrs)
|
||||
peerStore.delete(peerId2)
|
||||
// true
|
||||
```
|
||||
|
||||
### peerStore.get
|
||||
|
||||
Get the stored information of a given peer, namely its [`PeerId`][peer-id], known [`Addresses`][address] and supported protocols.
|
||||
|
||||
`peerStore.get(peerId)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to get |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `{ id: PeerId, addresses: Array<Address>, protocols: Array<string> }` | Peer information of the provided peer |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.get(peerId)
|
||||
// false
|
||||
peerStore.addressBook.set(peerId, multiaddrs)
|
||||
peerStore.protoBook.set(peerId, protocols)
|
||||
peerStore.get(peerId)
|
||||
// {
|
||||
// id: {},
|
||||
// addresses: [...],
|
||||
// protocols: [...]
|
||||
// }
|
||||
```
|
||||
|
||||
### peerStore.peers
|
||||
|
||||
Get all the stored information of every peer.
|
||||
|
||||
`peerStore.peers`
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Map<string, { id: PeerId, addresses: Array<Address>, protocols: Array<string> }>` | Peer data of every peer known |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
for (let [peerIdString, peer] of peerStore.peers.entries()) {
|
||||
// peer { id, addresses, protocols }
|
||||
}
|
||||
```
|
||||
|
||||
### pubsub.getSubscribers
|
||||
|
||||
Gets a list of the peer-ids that are subscribed to one topic.
|
||||
@@ -523,7 +1081,7 @@ Gets a list of the peer-ids that are subscribed to one topic.
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Array<String>` | peer-id subscribed to the topic |
|
||||
| `Array<string>` | peer-id subscribed to the topic |
|
||||
|
||||
#### Example
|
||||
|
||||
@@ -541,7 +1099,7 @@ Gets a list of topics the node is subscribed to.
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Array<String>` | topics the node is subscribed to |
|
||||
| `Array<string>` | topics the node is subscribed to |
|
||||
|
||||
#### Example
|
||||
|
||||
@@ -588,7 +1146,7 @@ Subscribes the given handler to a pubsub topic.
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| 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 |
|
||||
| handler | `function({ from: string, data: Buffer, seqno: Buffer, topicIDs: Array<string>, signature: Buffer, key: Buffer })` | handler for new data on topic |
|
||||
|
||||
#### Returns
|
||||
|
||||
@@ -618,7 +1176,7 @@ Unsubscribes the given handler from a pubsub topic. If no handler is provided, a
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| topic | `string` | topic to unsubscribe |
|
||||
| handler | `function(<Object>)` | handler subscribed |
|
||||
| handler | `function(<object>)` | handler subscribed |
|
||||
|
||||
#### Returns
|
||||
|
||||
@@ -637,6 +1195,28 @@ const handler = (msg) => {
|
||||
libp2p.pubsub.unsubscribe(topic, handler)
|
||||
```
|
||||
|
||||
### connectionManager.get
|
||||
|
||||
Get a connection with a given peer, if it exists.
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | The peer to find |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| [`Connection`][connection] | Connection with the given peer |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
libp2p.connectionManager.get(peerId)
|
||||
```
|
||||
|
||||
### connectionManager.setPeerValue
|
||||
|
||||
Enables users to change the value of certain peers in a range of 0 to 1. Peers with the lowest values will have their Connections pruned first, if any Connection Manager limits are exceeded. See [./CONFIGURATION.md#configuring-connection-manager](./CONFIGURATION.md#configuring-connection-manager) for details on how to configure these limits.
|
||||
@@ -663,6 +1243,17 @@ libp2p.connectionManager.setPeerValue(highPriorityPeerId, 1)
|
||||
libp2p.connectionManager.setPeerValue(lowPriorityPeerId, 0)
|
||||
```
|
||||
|
||||
### connectionManager.size
|
||||
|
||||
Getter for obtaining the current number of open connections.
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
libp2p.connectionManager.size
|
||||
// 10
|
||||
```
|
||||
|
||||
### metrics.global
|
||||
|
||||
A [`Stats`](#stats) object of tracking the global bandwidth of the libp2p node.
|
||||
@@ -714,7 +1305,7 @@ Returns the [`Stats`](#stats) object for a given [`PeerId`][peer-id] if it is be
|
||||
#### Example
|
||||
|
||||
```js
|
||||
const peerStats = libp2p.metrics.forPeer(peerInfo)
|
||||
const peerStats = libp2p.metrics.forPeer(peerId)
|
||||
console.log(peerStats.toJSON())
|
||||
```
|
||||
|
||||
@@ -745,7 +1336,9 @@ console.log(peerStats.toJSON())
|
||||
|
||||
## Events
|
||||
|
||||
Once you have a libp2p instance, you can listen to several events it emits, so that you can be notified of relevant network events.
|
||||
Once you have a libp2p instance, you can listen to several events it emits, so that you can be notified of relevant network events.
|
||||
|
||||
### libp2p
|
||||
|
||||
#### An error has occurred
|
||||
|
||||
@@ -760,23 +1353,47 @@ Once you have a libp2p instance, you can listen to several events it emits, so t
|
||||
If `autoDial` option is `true`, applications should **not** attempt to connect to the peer
|
||||
unless they are performing a specific action. See [peer discovery and auto dial](./PEER_DISCOVERY.md) for more information.
|
||||
|
||||
- `peer`: instance of [`PeerInfo`][peer-info]
|
||||
- `peer`: instance of [`PeerId`][peer-id]
|
||||
|
||||
### libp2p.connectionManager
|
||||
|
||||
#### A new connection to a peer has been opened
|
||||
|
||||
This event will be triggered anytime a new Connection is established to another peer.
|
||||
|
||||
`libp2p.on('peer:connect', (peer) => {})`
|
||||
`libp2p.connectionManager.on('peer:connect', (connection) => {})`
|
||||
|
||||
- `peer`: instance of [`PeerInfo`][peer-info]
|
||||
- `connection`: instance of [`Connection`][connection]
|
||||
|
||||
#### An existing connection to a peer has been closed
|
||||
|
||||
This event will be triggered anytime we are disconnected from another peer, regardless of the circumstances of that disconnection. If we happen to have multiple connections to a peer, this event will **only** be triggered when the last connection is closed.
|
||||
|
||||
`libp2p.on('peer:disconnect', (peer) => {})`
|
||||
`libp2p.connectionManager.on('peer:disconnect', (connection) => {})`
|
||||
|
||||
- `peer`: instance of [`PeerInfo`][peer-info]
|
||||
- `connection`: instance of [`Connection`][connection]
|
||||
|
||||
### libp2p.peerStore
|
||||
|
||||
#### A new peer is added to the peerStore
|
||||
|
||||
`libp2p.peerStore.on('peer', (peerId) => {})`
|
||||
|
||||
- `peerId`: instance of [`PeerId`][peer-id]
|
||||
|
||||
#### Known multiaddrs for a peer change
|
||||
|
||||
`libp2p.peerStore.on('change:multiaddrs', ({ peerId, multiaddrs}) => {})`
|
||||
|
||||
- `peerId`: instance of [`PeerId`][peer-id]
|
||||
- `multiaddrs`: array of known [`multiaddr`][multiaddr] for the peer
|
||||
|
||||
#### Known protocols for a peer change
|
||||
|
||||
`libp2p.peerStore.on('change:protocols', ({ peerId, protocols}) => {})`
|
||||
|
||||
- `peerId`: instance of [`PeerId`][peer-id]
|
||||
- `protocols`: array of known, supported protocols for the peer (string identifiers)
|
||||
|
||||
## Types
|
||||
|
||||
@@ -787,9 +1404,9 @@ This event will be triggered anytime we are disconnected from another peer, rega
|
||||
- `dataReceived<string>`: The stringified value of total incoming data for this stat.
|
||||
- `dataSent<string>`: The stringified value of total outgoing data for this stat.
|
||||
- `movingAverages<object>`: The properties are dependent on the configuration of the moving averages interval. Defaults are listed here.
|
||||
- `['60000']<Number>`: The calculated moving average at a 1 minute interval.
|
||||
- `['300000']<Number>`: The calculated moving average at a 5 minute interval.
|
||||
- `['900000']<Number>`: The calculated moving average at a 15 minute interval.
|
||||
- `['60000']<number>`: The calculated moving average at a 1 minute interval.
|
||||
- `['300000']<number>`: The calculated moving average at a 5 minute interval.
|
||||
- `['900000']<number>`: The calculated moving average at a 15 minute interval.
|
||||
- `snapshot<object>`: A getter that returns a clone of the raw stats.
|
||||
- `dataReceived<BigNumber>`: A [`BigNumber`](https://github.com/MikeMcl/bignumber.js/) of the amount of incoming data
|
||||
- `dataSent<BigNumber>`: A [`BigNumber`](https://github.com/MikeMcl/bignumber.js/) of the amount of outgoing data
|
||||
@@ -798,8 +1415,9 @@ This event will be triggered anytime we are disconnected from another peer, rega
|
||||
- `['300000']<MovingAverage>`: The [MovingAverage](https://www.npmjs.com/package/moving-averages) at a 5 minute interval.
|
||||
- `['900000']<MovingAverage>`: The [MovingAverage](https://www.npmjs.com/package/moving-averages) at a 15 minute interval.
|
||||
|
||||
[address]: https://github.com/libp2p/js-libp2p/tree/master/src/peer-store/address-book.js
|
||||
[cid]: https://github.com/multiformats/js-cid
|
||||
[connection]: https://github.com/libp2p/js-interfaces/tree/master/src/connection
|
||||
[multiaddr]: https://github.com/multiformats/js-multiaddr
|
||||
[peer-id]: https://github.com/libp2p/js-peer-id
|
||||
[peer-info]: https://github.com/libp2p/js-peer-info
|
||||
[keys]: https://github.com/libp2p/js-libp2p-crypto/tree/master/src/keys
|
@@ -94,6 +94,7 @@ If you want to know more about libp2p stream multiplexing, you should read the f
|
||||
|
||||
Some available connection encryption protocols:
|
||||
|
||||
- [NodeFactoryIo/js-libp2p-noise](https://github.com/NodeFactoryIo/js-libp2p-noise)
|
||||
- [libp2p/js-libp2p-secio](https://github.com/libp2p/js-libp2p-secio)
|
||||
|
||||
If none of the available connection encryption mechanisms fulfills your needs, you can create a libp2p compatible one. A libp2p connection encryption protocol just needs to be compliant with the [Crypto Interface](https://github.com/libp2p/js-interfaces/tree/master/src/crypto).
|
||||
@@ -113,6 +114,7 @@ Some available peer discovery modules are:
|
||||
- [js-libp2p-bootstrap](https://github.com/libp2p/js-libp2p-bootstrap)
|
||||
- [js-libp2p-kad-dht](https://github.com/libp2p/js-libp2p-kad-dht)
|
||||
- [js-libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star)
|
||||
- [discv5](https://github.com/chainsafe/discv5)
|
||||
|
||||
**Note**: `peer-discovery` services within transports (such as `js-libp2p-webrtc-star`) are automatically gathered from the `transport`, via it's `discovery` property. As such, they do not need to be added in the discovery modules. However, these transports can also be configured and disabled as the other ones.
|
||||
|
||||
@@ -203,8 +205,12 @@ Moreover, the majority of the modules can be customized via option parameters. T
|
||||
Besides the `modules` and `config`, libp2p allows other internal options and configurations:
|
||||
- `datastore`: an instance of [ipfs/interface-datastore](https://github.com/ipfs/interface-datastore/) modules.
|
||||
- This is used in modules such as the DHT. If it is not provided, `js-libp2p` will use an in memory datastore.
|
||||
- `peerInfo`: a previously created instance of [libp2p/js-peer-info](https://github.com/libp2p/js-peer-info).
|
||||
- `peerId`: the identity of the node, an instance of [libp2p/js-peer-id](https://github.com/libp2p/js-peer-id).
|
||||
- This is particularly useful if you want to reuse the same `peer-id`, as well as for modules like `libp2p-delegated-content-routing`, which need a `peer-id` in their instantiation.
|
||||
- `addresses`: an object containing `listen`, `announce` and `noAnnounce` properties with `Array<string>`:
|
||||
- `listen` addresses will be provided to the libp2p underlying transports for listening on them.
|
||||
- `announce` addresses will be used to compute the advertises that the node should advertise to the network.
|
||||
- `noAnnounce` addresses will be used as a filter to compute the advertises that the node should advertise to the network.
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -500,6 +506,34 @@ const node = await Libp2p.create({
|
||||
})
|
||||
```
|
||||
|
||||
#### Configuring PeerStore
|
||||
|
||||
PeerStore persistence is disabled in libp2p by default. You can enable and configure it as follows. Aside from enabled being `false` by default, it will need an implementation of a [datastore](https://github.com/ipfs/interface-datastore). Take into consideration that using the memory datastore will be ineffective for persistence.
|
||||
|
||||
The threshold number represents the maximum number of "dirty peers" allowed in the PeerStore, i.e. peers that are not updated in the datastore. In this context, browser nodes should use a threshold of 1, since they might not "stop" properly in several scenarios and the PeerStore might end up with unflushed records when the window is closed.
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
const LevelStore = require('datastore-level')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
datastore: new LevelStore('path/to/store'),
|
||||
peerStore: {
|
||||
persistence: true, // Is persistence enabled (default: false)
|
||||
threshold: 5 // Number of dirty peers allowed (default: 5)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Customizing Transports
|
||||
|
||||
Some Transports can be passed additional options when they are created. For example, `libp2p-webrtc-star` accepts an optional, custom `wrtc` implementation. In addition to libp2p passing itself and an `Upgrader` to handle connection upgrading, libp2p will also pass the options, if they are provided, from `config.transport`.
|
||||
@@ -532,7 +566,6 @@ const node = await Libp2p.create({
|
||||
|
||||
As libp2p is designed to be a modular networking library, its usage will vary based on individual project needs. We've included links to some existing project configurations for your reference, in case you wish to replicate their configuration:
|
||||
|
||||
|
||||
- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-nodejs.js) - libp2p configuration used by js-ipfs when running in Node.js
|
||||
- [libp2p-ipfs-browser](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-browser.js) - libp2p configuration used by js-ipfs when running in a Browser (that supports WebRTC)
|
||||
|
||||
|
@@ -69,23 +69,23 @@ If you want to know more about libp2p transports, you should read the following
|
||||
|
||||
Encryption is an important part of communicating on the libp2p network. Every connection must be encrypted to help ensure security for everyone. As such, Connection Encryption (Crypto) is a required component of libp2p.
|
||||
|
||||
There are a growing number of Crypto modules being developed for libp2p. As those are released they will be tracked in the [Connection Encryption section of the configuration readme](./CONFIGURATION.md#connection-encryption). For now, we are going to configure our node to use the `libp2p-secio` module, which is widely supported across the various libp2p implementations.
|
||||
There are a growing number of Crypto modules being developed for libp2p. As those are released they will be tracked in the [Connection Encryption section of the configuration readme](./CONFIGURATION.md#connection-encryption). For now, we are going to configure our node to use the `libp2p-noise` module.
|
||||
|
||||
```sh
|
||||
npm install libp2p-secio
|
||||
npm install libp2p-noise
|
||||
```
|
||||
|
||||
With `libp2p-secio` installed, we can add it to our existing configuration by importing it and adding it to the `modules.connEncryption` array:
|
||||
With `libp2p-noise` installed, we can add it to our existing configuration by importing it and adding it to the `modules.connEncryption` array:
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [WebSockets],
|
||||
connEncryption: [SECIO]
|
||||
connEncryption: [NOISE]
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -143,6 +143,9 @@ const SECIO = require('libp2p-secio')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/8000/ws']
|
||||
},
|
||||
modules: {
|
||||
transport: [WebSockets],
|
||||
connEncryption: [SECIO],
|
||||
@@ -154,6 +157,12 @@ const node = await Libp2p.create({
|
||||
await node.start()
|
||||
console.log('libp2p has started')
|
||||
|
||||
const listenAddrs = node.transportManager.getAddrs()
|
||||
console.log('libp2p is listening on the following addresses: ', listenAddrs)
|
||||
|
||||
const advertiseAddrs = node.multiaddrs
|
||||
console.log('libp2p is advertising the following addresses: ', advertiseAddrs)
|
||||
|
||||
// stop libp2p
|
||||
await node.stop()
|
||||
console.log('libp2p has stopped')
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/* eslint no-console: ["off"] */
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const { generate } = require('libp2p/src/pnet')
|
||||
const privateLibp2pNode = require('./libp2p-node')
|
||||
|
||||
@@ -17,7 +18,7 @@ generate(otherSwarmKey)
|
||||
;(async () => {
|
||||
const node1 = await privateLibp2pNode(swarmKey)
|
||||
|
||||
// TASK: switch the commented out line below so we're using a different key, to see the nodes fail to connect
|
||||
// TASK: switch the commented out line below so we're using a different key, to see the nodes fail to connect
|
||||
const node2 = await privateLibp2pNode(swarmKey)
|
||||
// const node2 = await privateLibp2pNode(otherSwarmKey)
|
||||
|
||||
@@ -47,4 +48,4 @@ generate(otherSwarmKey)
|
||||
['This message is sent on a private network'],
|
||||
stream
|
||||
)
|
||||
})();
|
||||
})()
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const Libp2p = require('../../')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Mplex = require('libp2p-mplex')
|
||||
@@ -31,7 +32,7 @@ const createNode = async () => {
|
||||
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(),
|
||||
createNode(),
|
||||
createNode()
|
||||
])
|
||||
|
||||
await node1.dial(node2.peerInfo)
|
||||
@@ -48,4 +49,4 @@ const createNode = async () => {
|
||||
setInterval(() => {
|
||||
node2.pubsub.publish(topic, Buffer.from('Bird bird bird, bird is the word!'))
|
||||
}, 1000)
|
||||
})();
|
||||
})()
|
||||
|
@@ -22,17 +22,18 @@
|
||||
["libp2p/js-libp2p-websockets", "libp2p-websockets"],
|
||||
|
||||
"secure channels",
|
||||
["NodeFactoryIo/js-libp2p-noise", "libp2p-noise"],
|
||||
["libp2p/js-libp2p-secio", "libp2p-secio"],
|
||||
|
||||
"stream multiplexers",
|
||||
["libp2p/js-libp2p-mplex", "libp2p-mplex"],
|
||||
["libp2p/js-libp2p-spdy", "libp2p-spdy"],
|
||||
|
||||
"peer discovery",
|
||||
["libp2p/js-libp2p-bootstrap", "libp2p-bootstrap"],
|
||||
["libp2p/js-libp2p-kad-dht", "libp2p-kad-dht"],
|
||||
["libp2p/js-libp2p-mdns", "libp2p-mdns"],
|
||||
["libp2p/js-libp2p-webrtc-star", "libp2p-webrtc-star"],
|
||||
["ChainSafe/discv5", "discv5"],
|
||||
|
||||
"content routing",
|
||||
["libp2p/js-libp2p-delegated-content-routing", "libp2p-delegated-content-routing"],
|
||||
|
64
package.json
64
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "libp2p",
|
||||
"version": "0.27.5",
|
||||
"version": "0.27.7",
|
||||
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
|
||||
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
|
||||
"main": "src/index.js",
|
||||
@@ -48,65 +48,68 @@
|
||||
"class-is": "^1.1.0",
|
||||
"debug": "^4.1.1",
|
||||
"err-code": "^2.0.0",
|
||||
"events": "^3.1.0",
|
||||
"hashlru": "^2.3.0",
|
||||
"interface-datastore": "^0.8.3",
|
||||
"ipfs-utils": "^2.2.0",
|
||||
"it-all": "^1.0.1",
|
||||
"it-buffer": "^0.1.1",
|
||||
"it-buffer": "^0.1.2",
|
||||
"it-handshake": "^1.0.1",
|
||||
"it-length-prefixed": "^3.0.0",
|
||||
"it-length-prefixed": "^3.0.1",
|
||||
"it-pipe": "^1.1.0",
|
||||
"it-protocol-buffers": "^0.2.0",
|
||||
"latency-monitor": "~0.2.1",
|
||||
"libp2p-crypto": "^0.17.1",
|
||||
"libp2p-interfaces": "^0.2.3",
|
||||
"libp2p-crypto": "^0.17.6",
|
||||
"libp2p-interfaces": "^0.3.0",
|
||||
"libp2p-utils": "^0.1.2",
|
||||
"mafmt": "^7.0.0",
|
||||
"merge-options": "^2.0.0",
|
||||
"moving-average": "^1.0.0",
|
||||
"multiaddr": "^7.2.1",
|
||||
"multiaddr": "^7.4.3",
|
||||
"multistream-select": "^0.15.0",
|
||||
"mutable-proxy": "^1.0.0",
|
||||
"p-any": "^3.0.0",
|
||||
"p-fifo": "^1.0.0",
|
||||
"p-settle": "^4.0.0",
|
||||
"peer-id": "^0.13.4",
|
||||
"peer-info": "^0.17.0",
|
||||
"p-settle": "^4.0.1",
|
||||
"peer-id": "^0.13.11",
|
||||
"protons": "^1.0.1",
|
||||
"retimer": "^2.0.0",
|
||||
"streaming-iterables": "^4.1.0",
|
||||
"timeout-abort-controller": "^1.0.0",
|
||||
"xsalsa20": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nodeutils/defaults-deep": "^1.1.0",
|
||||
"abortable-iterator": "^3.0.0",
|
||||
"aegir": "^21.3.0",
|
||||
"aegir": "^21.9.0",
|
||||
"chai": "^4.2.0",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-bytes": "^0.1.2",
|
||||
"cids": "^0.8.0",
|
||||
"delay": "^4.3.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"interop-libp2p": "~0.0.1",
|
||||
"interop-libp2p": "libp2p/interop#chore/update-libp2p-daemon-with-peerstore",
|
||||
"ipfs-http-client": "^44.0.0",
|
||||
"it-concat": "^1.0.0",
|
||||
"it-pair": "^1.0.0",
|
||||
"it-pushable": "^1.4.0",
|
||||
"libp2p-bootstrap": "^0.10.3",
|
||||
"libp2p-delegated-content-routing": "^0.4.1",
|
||||
"libp2p-delegated-peer-routing": "^0.4.0",
|
||||
"libp2p-floodsub": "^0.20.0",
|
||||
"libp2p-gossipsub": "^0.2.0",
|
||||
"libp2p-kad-dht": "^0.18.2",
|
||||
"libp2p-mdns": "^0.13.0",
|
||||
"libp2p-mplex": "^0.9.1",
|
||||
"libp2p-secio": "^0.12.1",
|
||||
"libp2p-bootstrap": "^0.11.0",
|
||||
"libp2p-delegated-content-routing": "^0.5.0",
|
||||
"libp2p-delegated-peer-routing": "^0.5.0",
|
||||
"libp2p-floodsub": "^0.21.0",
|
||||
"libp2p-gossipsub": "^0.4.0",
|
||||
"libp2p-kad-dht": "^0.19.1",
|
||||
"libp2p-mdns": "^0.14.1",
|
||||
"libp2p-noise": "^1.1.0",
|
||||
"libp2p-mplex": "^0.9.5",
|
||||
"libp2p-secio": "^0.12.4",
|
||||
"libp2p-tcp": "^0.14.1",
|
||||
"libp2p-webrtc-star": "^0.17.0",
|
||||
"libp2p-webrtc-star": "^0.18.0",
|
||||
"libp2p-websockets": "^0.13.1",
|
||||
"nock": "^12.0.0",
|
||||
"nock": "^12.0.3",
|
||||
"p-defer": "^3.0.0",
|
||||
"p-times": "^2.1.0",
|
||||
"p-times": "^3.0.0",
|
||||
"p-wait-for": "^3.1.0",
|
||||
"sinon": "^9.0.0",
|
||||
"streaming-iterables": "^4.1.0",
|
||||
"wrtc": "^0.4.1"
|
||||
"sinon": "^9.0.2"
|
||||
},
|
||||
"contributors": [
|
||||
"David Dias <daviddias.p@gmail.com>",
|
||||
@@ -117,9 +120,9 @@
|
||||
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||
"Maciej Krüger <mkg20001@gmail.com>",
|
||||
"Alex Potsides <alex@achingbrain.net>",
|
||||
"Hugo Dias <mail@hugodias.me>",
|
||||
"dirkmc <dirkmdev@gmail.com>",
|
||||
"Volker Mische <volker.mische@gmail.com>",
|
||||
"Hugo Dias <mail@hugodias.me>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
|
||||
"Andrew Nesbitt <andrewnez@gmail.com>",
|
||||
@@ -128,7 +131,6 @@
|
||||
"Giovanni T. Parra <fiatjaf@gmail.com>",
|
||||
"Ryan Bell <ryan@piing.net>",
|
||||
"Thomas Eizinger <thomas@eizinger.io>",
|
||||
"Joel Gustafson <joelg@mit.edu>",
|
||||
"Kevin Kwok <antimatter15@gmail.com>",
|
||||
"Henrique Dias <hacdias@gmail.com>",
|
||||
"Nuno Nogueira <nunofmn@gmail.com>",
|
||||
@@ -147,6 +149,8 @@
|
||||
"ebinks <elizabethjbinks@gmail.com>",
|
||||
"Chris Bratlien <chrisbratlien@gmail.com>",
|
||||
"isan_rivkin <isanrivkin@gmail.com>",
|
||||
"Irakli Gozalishvili <rfobic@gmail.com>"
|
||||
"robertkiel <robert.kiel@validitylabs.org>",
|
||||
"Irakli Gozalishvili <rfobic@gmail.com>",
|
||||
"Joel Gustafson <joelg@mit.edu>"
|
||||
]
|
||||
}
|
||||
|
49
src/address-manager/README.md
Normal file
49
src/address-manager/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Address Manager
|
||||
|
||||
The Address manager is responsible for keeping an updated register of the peer's addresses. It includes 3 different types of Addresses: `Listen Addresses`, `Announce Addresses` and `No Announce Addresses`.
|
||||
|
||||
These Addresses should be specified in your libp2p [configuration](../../doc/CONFIGURATION.md) when you create your node.
|
||||
|
||||
## Listen Addresses
|
||||
|
||||
A libp2p node should have a set of listen addresses, which will be used by libp2p underlying transports to listen for dials from other nodes in the network.
|
||||
|
||||
Before a libp2p node starts, its configured listen addresses will be passed to the AddressManager, so that during startup the libp2p transports can use them to listen for connections. Accordingly, listen addresses should be specified through the libp2p configuration, in order to have the `AddressManager` created with them.
|
||||
|
||||
It is important pointing out that libp2p accepts ephemeral listening addresses. In this context, the provided listen addresses might not be exactly the same as the ones used by the transports. For example TCP may replace `/ip4/0.0.0.0/tcp/0` with something like `/ip4/127.0.0.1/tcp/8989`. As a consequence, libp2p should take into account this when determining its advertised addresses.
|
||||
|
||||
## Announce Addresses
|
||||
|
||||
In some scenarios, a libp2p node will need to announce addresses that it is not listening on. In other words, Announce Addresses are an amendment to the Listen Addresses that aim to enable other nodes to achieve connectivity to this node.
|
||||
|
||||
Scenarios for Announce Addresses include:
|
||||
- when you setup a libp2p node in your private network at home, but you need to announce your public IP Address to the outside world;
|
||||
- when you want to announce a DNS address, which maps to your public IP Address.
|
||||
|
||||
## No Announce Addresses
|
||||
|
||||
While we need to add Announce Addresses to enable peers' connectivity, we should also avoid announcing addresses that will not be reachable. No Announce Addresses should be specified so that they are filtered from the advertised multiaddrs.
|
||||
|
||||
As stated in the Listen Addresses section, Listen Addresses might be modified by libp2p transports after the successfully bind to those addresses. Libp2p should also take these changes into account so that they can be matched when No Announce Addresses are being filtered out of the advertised multiaddrs.
|
||||
|
||||
## Implementation
|
||||
|
||||
When a libp2p node is created, the Address Manager will be populated from the provided addresses through the libp2p configuration. Once the node is started, the Transport Manager component will gather the listen addresses from the Address Manager, so that the libp2p transports can attempt to bind to them.
|
||||
|
||||
Libp2p will use the the Address Manager as the source of truth when advertising the peers addresses. After all transports are ready, other libp2p components/subsystems will kickoff, namely the Identify Service and the DHT. Both of them will announce the node addresses to the other peers in the network. The announce and noAnnounce addresses will have an important role here and will be gathered by libp2p to compute its current addresses to advertise everytime it is needed.
|
||||
|
||||
## Future Considerations
|
||||
|
||||
### Dynamic address modifications
|
||||
|
||||
In a future iteration, we can enable these addresses to be modified in runtime. For this, the Address Manager should be responsible for notifying interested subsystems of these changes, through an Event Emitter.
|
||||
|
||||
#### Modify Listen Addresses
|
||||
|
||||
While adding new addresses to listen on runtime should be trivial, removing a listen address might have bad implications for the node, since all the connections using that listen address will be closed. However, libp2p should provide a mechanism for both adding and removing listen addresses in the future.
|
||||
|
||||
Every time a new listen address is added, the Address Manager should emit an event with the new multiaddrs to listen. The Transport Manager should listen to this events and act accordingly.
|
||||
|
||||
#### Modify Announce Addresses
|
||||
|
||||
When the announce addresses are modified, the Address Manager should emit an event so that other subsystems can act accordingly. For example, libp2p identify service should use the libp2p push protocol to inform other peers about these changes.
|
55
src/address-manager/index.js
Normal file
55
src/address-manager/index.js
Normal file
@@ -0,0 +1,55 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:addresses')
|
||||
log.error = debug('libp2p:addresses:error')
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
|
||||
/**
|
||||
* Responsible for managing this peers addresses.
|
||||
* Peers can specify their listen, announce and noAnnounce addresses.
|
||||
* The listen addresses will be used by the libp2p transports to listen for new connections,
|
||||
* while the announce an noAnnounce addresses will be combined with the listen addresses for
|
||||
* address adverstising to other peers in the network.
|
||||
*/
|
||||
class AddressManager {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {object} [options]
|
||||
* @param {Array<string>} [options.listen = []] list of multiaddrs string representation to listen.
|
||||
* @param {Array<string>} [options.announce = []] list of multiaddrs string representation to announce.
|
||||
* @param {Array<string>} [options.noAnnounce = []] list of multiaddrs string representation to not announce.
|
||||
*/
|
||||
constructor ({ listen = [], announce = [], noAnnounce = [] } = {}) {
|
||||
this.listen = new Set(listen)
|
||||
this.announce = new Set(announce)
|
||||
this.noAnnounce = new Set(noAnnounce)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peer listen multiaddrs.
|
||||
* @return {Array<Multiaddr>}
|
||||
*/
|
||||
getListenAddrs () {
|
||||
return Array.from(this.listen).map((a) => multiaddr(a))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peer announcing multiaddrs.
|
||||
* @return {Array<Multiaddr>}
|
||||
*/
|
||||
getAnnounceAddrs () {
|
||||
return Array.from(this.announce).map((a) => multiaddr(a))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peer noAnnouncing multiaddrs.
|
||||
* @return {Array<Multiaddr>}
|
||||
*/
|
||||
getNoAnnounceAddrs () {
|
||||
return Array.from(this.noAnnounce).map((a) => multiaddr(a))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AddressManager
|
@@ -1,7 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')
|
||||
const PeerInfo = require('peer-info')
|
||||
const log = debug('libp2p:circuit:hop')
|
||||
log.error = debug('libp2p:circuit:hop:error')
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const { validateAddrs } = require('./utils')
|
||||
const StreamHandler = require('./stream-handler')
|
||||
@@ -14,9 +16,6 @@ const { stop } = require('./stop')
|
||||
|
||||
const multicodec = require('./../multicodec')
|
||||
|
||||
const log = debug('libp2p:circuit:hop')
|
||||
log.error = debug('libp2p:circuit:hop:error')
|
||||
|
||||
module.exports.handleHop = async function handleHop ({
|
||||
connection,
|
||||
request,
|
||||
@@ -42,7 +41,7 @@ module.exports.handleHop = async function handleHop ({
|
||||
// Get the connection to the destination (stop) peer
|
||||
const destinationPeer = new PeerId(request.dstPeer.id)
|
||||
|
||||
const destinationConnection = circuit._registrar.getConnection(new PeerInfo(destinationPeer))
|
||||
const destinationConnection = circuit._connectionManager.get(destinationPeer)
|
||||
if (!destinationConnection && !circuit._options.hop.active) {
|
||||
log('HOP request received but we are not connected to the destination peer')
|
||||
return streamHandler.end({
|
||||
|
@@ -3,7 +3,6 @@
|
||||
const mafmt = require('mafmt')
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const withIs = require('class-is')
|
||||
const { CircuitRelay: CircuitPB } = require('./protocol')
|
||||
|
||||
@@ -30,9 +29,11 @@ class Circuit {
|
||||
constructor ({ libp2p, upgrader }) {
|
||||
this._dialer = libp2p.dialer
|
||||
this._registrar = libp2p.registrar
|
||||
this._connectionManager = libp2p.connectionManager
|
||||
this._upgrader = upgrader
|
||||
this._options = libp2p._config.relay
|
||||
this.peerInfo = libp2p.peerInfo
|
||||
this._libp2p = libp2p
|
||||
this.peerId = libp2p.peerId
|
||||
this._registrar.handle(multicodec, this._onProtocol.bind(this))
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ class Circuit {
|
||||
const destinationPeer = PeerId.createFromCID(destinationAddr.getPeerId())
|
||||
|
||||
let disconnectOnFailure = false
|
||||
let relayConnection = this._registrar.getConnection(new PeerInfo(relayPeer))
|
||||
let relayConnection = this._connectionManager.get(relayPeer)
|
||||
if (!relayConnection) {
|
||||
relayConnection = await this._dialer.connectToPeer(relayAddr, options)
|
||||
disconnectOnFailure = true
|
||||
@@ -120,8 +121,8 @@ class Circuit {
|
||||
request: {
|
||||
type: CircuitPB.Type.HOP,
|
||||
srcPeer: {
|
||||
id: this.peerInfo.id.toBytes(),
|
||||
addrs: this.peerInfo.multiaddrs.toArray().map(addr => addr.buffer)
|
||||
id: this.peerId.toBytes(),
|
||||
addrs: this._libp2p.multiaddrs.map(addr => addr.buffer)
|
||||
},
|
||||
dstPeer: {
|
||||
id: destinationPeer.toBytes(),
|
||||
@@ -130,7 +131,7 @@ class Circuit {
|
||||
}
|
||||
})
|
||||
|
||||
const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerInfo.id.toB58String()}`)
|
||||
const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toB58String()}`)
|
||||
const maConn = toConnection({
|
||||
stream: virtualConnection,
|
||||
remoteAddr: ma,
|
||||
|
@@ -22,7 +22,7 @@ module.exports = (circuit) => {
|
||||
* @return {void}
|
||||
*/
|
||||
listener.listen = async (addr) => {
|
||||
const [addrString] = String(addr).split('/p2p-circuit').slice(-1)
|
||||
const addrString = String(addr).split('/p2p-circuit').find(a => a !== '')
|
||||
|
||||
const relayConn = await circuit._dialer.connectToPeer(multiaddr(addrString))
|
||||
const relayedAddr = relayConn.remoteAddr.encapsulate('/p2p-circuit')
|
||||
|
@@ -4,6 +4,11 @@ const mergeOptions = require('merge-options')
|
||||
const Constants = require('./constants')
|
||||
|
||||
const DefaultConfig = {
|
||||
addresses: {
|
||||
listen: [],
|
||||
announce: [],
|
||||
noAnnounce: []
|
||||
},
|
||||
connectionManager: {
|
||||
minPeers: 25
|
||||
},
|
||||
@@ -15,6 +20,10 @@ const DefaultConfig = {
|
||||
metrics: {
|
||||
enabled: false
|
||||
},
|
||||
peerStore: {
|
||||
persistence: false,
|
||||
threshold: 5
|
||||
},
|
||||
config: {
|
||||
dht: {
|
||||
enabled: false,
|
||||
|
@@ -2,10 +2,14 @@
|
||||
|
||||
const errcode = require('err-code')
|
||||
const mergeOptions = require('merge-options')
|
||||
const LatencyMonitor = require('latency-monitor').default
|
||||
const LatencyMonitor = require('./latency-monitor')
|
||||
const debug = require('debug')('libp2p:connection-manager')
|
||||
const retimer = require('retimer')
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const {
|
||||
ERR_INVALID_PARAMETERS
|
||||
} = require('../errors')
|
||||
@@ -22,7 +26,12 @@ const defaultOptions = {
|
||||
defaultPeerValue: 1
|
||||
}
|
||||
|
||||
class ConnectionManager {
|
||||
/**
|
||||
* Responsible for managing known connections.
|
||||
* @fires ConnectionManager#peer:connect Emitted when a new peer is connected.
|
||||
* @fires ConnectionManager#peer:disconnect Emitted when a peer is disconnected.
|
||||
*/
|
||||
class ConnectionManager extends EventEmitter {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Libp2p} libp2p
|
||||
@@ -38,9 +47,11 @@ class ConnectionManager {
|
||||
* @param {Number} options.defaultPeerValue The value of the peer. Default=1
|
||||
*/
|
||||
constructor (libp2p, options) {
|
||||
super()
|
||||
|
||||
this._libp2p = libp2p
|
||||
this._registrar = libp2p.registrar
|
||||
this._peerId = libp2p.peerInfo.id.toB58String()
|
||||
this._peerId = libp2p.peerId.toB58String()
|
||||
|
||||
this._options = mergeOptions.call({ ignoreUndefined: true }, defaultOptions, options)
|
||||
if (this._options.maxConnections < this._options.minConnections) {
|
||||
throw errcode(new Error('Connection Manager maxConnections must be greater than minConnections'), ERR_INVALID_PARAMETERS)
|
||||
@@ -48,20 +59,38 @@ class ConnectionManager {
|
||||
|
||||
debug('options: %j', this._options)
|
||||
|
||||
this._metrics = libp2p.metrics
|
||||
this._libp2p = libp2p
|
||||
|
||||
/**
|
||||
* Map of peer identifiers to their peer value for pruning connections.
|
||||
* @type {Map<string, number>}
|
||||
*/
|
||||
this._peerValues = new Map()
|
||||
this._connections = new Map()
|
||||
|
||||
/**
|
||||
* Map of connections per peer
|
||||
* @type {Map<string, Array<conn>>}
|
||||
*/
|
||||
this.connections = new Map()
|
||||
|
||||
this._timer = null
|
||||
this._checkMetrics = this._checkMetrics.bind(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current number of open connections.
|
||||
*/
|
||||
get size () {
|
||||
return Array.from(this.connections.values())
|
||||
.reduce((accumulator, value) => accumulator + value.length, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Connection Manager. If Metrics are not enabled on libp2p
|
||||
* only event loop and connection limits will be monitored.
|
||||
*/
|
||||
start () {
|
||||
if (this._metrics) {
|
||||
if (this._libp2p.metrics) {
|
||||
this._timer = this._timer || retimer(this._checkMetrics, this._options.pollInterval)
|
||||
}
|
||||
|
||||
@@ -77,13 +106,33 @@ class ConnectionManager {
|
||||
|
||||
/**
|
||||
* Stops the Connection Manager
|
||||
* @async
|
||||
*/
|
||||
stop () {
|
||||
async stop () {
|
||||
this._timer && this._timer.clear()
|
||||
this._latencyMonitor && this._latencyMonitor.removeListener('data', this._onLatencyMeasure)
|
||||
|
||||
await this._close()
|
||||
debug('stopped')
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the connections
|
||||
* @async
|
||||
*/
|
||||
async _close () {
|
||||
// Close all connections we're tracking
|
||||
const tasks = []
|
||||
for (const connectionList of this.connections.values()) {
|
||||
for (const connection of connectionList) {
|
||||
tasks.push(connection.close())
|
||||
}
|
||||
}
|
||||
|
||||
await tasks
|
||||
this.connections.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the given peer. Peers with lower values
|
||||
* will be disconnected first.
|
||||
@@ -106,7 +155,7 @@ class ConnectionManager {
|
||||
* @private
|
||||
*/
|
||||
_checkMetrics () {
|
||||
const movingAverages = this._metrics.global.movingAverages
|
||||
const movingAverages = this._libp2p.metrics.global.movingAverages
|
||||
const received = movingAverages.dataReceived[this._options.movingAverageInterval].movingAverage()
|
||||
this._checkLimit('maxReceivedData', received)
|
||||
const sent = movingAverages.dataSent[this._options.movingAverageInterval].movingAverage()
|
||||
@@ -122,12 +171,25 @@ class ConnectionManager {
|
||||
* @param {Connection} connection
|
||||
*/
|
||||
onConnect (connection) {
|
||||
const peerId = connection.remotePeer.toB58String()
|
||||
this._connections.set(connection.id, connection)
|
||||
if (!this._peerValues.has(peerId)) {
|
||||
this._peerValues.set(peerId, this._options.defaultPeerValue)
|
||||
const peerId = connection.remotePeer
|
||||
const peerIdStr = peerId.toB58String()
|
||||
const storedConn = this.connections.get(peerIdStr)
|
||||
|
||||
if (storedConn) {
|
||||
storedConn.push(connection)
|
||||
} else {
|
||||
this.connections.set(peerIdStr, [connection])
|
||||
this.emit('peer:connect', connection)
|
||||
}
|
||||
this._checkLimit('maxConnections', this._connections.size)
|
||||
|
||||
this._libp2p.peerStore.addressBook.add(peerId, [connection.remoteAddr])
|
||||
this._libp2p.peerStore.keyBook.set(peerId, peerId.pubKey)
|
||||
|
||||
if (!this._peerValues.has(peerIdStr)) {
|
||||
this._peerValues.set(peerIdStr, this._options.defaultPeerValue)
|
||||
}
|
||||
|
||||
this._checkLimit('maxConnections', this.size)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,8 +197,37 @@ class ConnectionManager {
|
||||
* @param {Connection} connection
|
||||
*/
|
||||
onDisconnect (connection) {
|
||||
this._connections.delete(connection.id)
|
||||
this._peerValues.delete(connection.remotePeer.toB58String())
|
||||
const peerId = connection.remotePeer.toB58String()
|
||||
let storedConn = this.connections.get(peerId)
|
||||
|
||||
if (storedConn && storedConn.length > 1) {
|
||||
storedConn = storedConn.filter((conn) => conn.id !== connection.id)
|
||||
this.connections.set(peerId, storedConn)
|
||||
} else if (storedConn) {
|
||||
this.connections.delete(peerId)
|
||||
this._peerValues.delete(connection.remotePeer.toB58String())
|
||||
this.emit('peer:disconnect', connection)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a connection with a peer.
|
||||
* @param {PeerId} peerId
|
||||
* @returns {Connection}
|
||||
*/
|
||||
get (peerId) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const id = peerId.toB58String()
|
||||
const connections = this.connections.get(id)
|
||||
|
||||
// Return the first, open connection
|
||||
if (connections) {
|
||||
return connections.find(connection => connection.stat.status === 'open')
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +260,7 @@ class ConnectionManager {
|
||||
* @private
|
||||
*/
|
||||
_maybeDisconnectOne () {
|
||||
if (this._options.minConnections < this._connections.size) {
|
||||
if (this._options.minConnections < this.connections.size) {
|
||||
const peerValues = Array.from(this._peerValues).sort(byPeerValue)
|
||||
debug('%s: sorted peer values: %j', this._peerId, peerValues)
|
||||
const disconnectPeer = peerValues[0]
|
||||
@@ -177,9 +268,9 @@ class ConnectionManager {
|
||||
const peerId = disconnectPeer[0]
|
||||
debug('%s: lowest value peer is %s', this._peerId, peerId)
|
||||
debug('%s: closing a connection to %j', this._peerId, peerId)
|
||||
for (const connection of this._connections.values()) {
|
||||
if (connection.remotePeer.toB58String() === peerId) {
|
||||
connection.close()
|
||||
for (const connections of this.connections.values()) {
|
||||
if (connections[0].remotePeer.toB58String() === peerId) {
|
||||
connections[0].close()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
246
src/connection-manager/latency-monitor.js
Normal file
246
src/connection-manager/latency-monitor.js
Normal file
@@ -0,0 +1,246 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* This code is based on `latency-monitor` (https://github.com/mlucool/latency-monitor) by `mlucool` (https://github.com/mlucool), available under Apache License 2.0 (https://github.com/mlucool/latency-monitor/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
/* global window */
|
||||
const globalThis = require('ipfs-utils/src/globalthis')
|
||||
const EventEmitter = require('events')
|
||||
const VisibilityChangeEmitter = require('./visibility-change-emitter')
|
||||
const debug = require('debug')('latency-monitor:LatencyMonitor')
|
||||
|
||||
/**
|
||||
* @typedef {Object} SummaryObject
|
||||
* @property {Number} events How many events were called
|
||||
* @property {Number} minMS What was the min time for a cb to be called
|
||||
* @property {Number} maxMS What was the max time for a cb to be called
|
||||
* @property {Number} avgMs What was the average time for a cb to be called
|
||||
* @property {Number} lengthMs How long this interval was in ms
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class to monitor latency of any async function which works in a browser or node. This works by periodically calling
|
||||
* the asyncTestFn and timing how long it takes the callback to be called. It can also periodically emit stats about this.
|
||||
* This can be disabled and stats can be pulled via setting dataEmitIntervalMs = 0.
|
||||
*
|
||||
* The default implementation is an event loop latency monitor. This works by firing periodic events into the event loop
|
||||
* and timing how long it takes to get back.
|
||||
*
|
||||
* @example
|
||||
* const monitor = new LatencyMonitor();
|
||||
* monitor.on('data', (summary) => console.log('Event Loop Latency: %O', summary));
|
||||
*
|
||||
* @example
|
||||
* const monitor = new LatencyMonitor({latencyCheckIntervalMs: 1000, dataEmitIntervalMs: 60000, asyncTestFn:ping});
|
||||
* monitor.on('data', (summary) => console.log('Ping Pong Latency: %O', summary));
|
||||
*/
|
||||
class LatencyMonitor extends EventEmitter {
|
||||
/**
|
||||
* @param {Number} [latencyCheckIntervalMs=500] How often to add a latency check event (ms)
|
||||
* @param {Number} [dataEmitIntervalMs=5000] How often to summarize latency check events. null or 0 disables event firing
|
||||
* @param {function} [asyncTestFn] What cb-style async function to use
|
||||
* @param {Number} [latencyRandomPercentage=5] What percent (+/-) of latencyCheckIntervalMs should we randomly use? This helps avoid alignment to other events.
|
||||
*/
|
||||
constructor ({ latencyCheckIntervalMs, dataEmitIntervalMs, asyncTestFn, latencyRandomPercentage } = {}) {
|
||||
super()
|
||||
const that = this
|
||||
|
||||
// 0 isn't valid here, so its ok to use ||
|
||||
that.latencyCheckIntervalMs = latencyCheckIntervalMs || 500 // 0.5s
|
||||
that.latencyRandomPercentage = latencyRandomPercentage || 10
|
||||
that._latecyCheckMultiply = 2 * (that.latencyRandomPercentage / 100.0) * that.latencyCheckIntervalMs
|
||||
that._latecyCheckSubtract = that._latecyCheckMultiply / 2
|
||||
|
||||
that.dataEmitIntervalMs = (dataEmitIntervalMs === null || dataEmitIntervalMs === 0) ? undefined
|
||||
: dataEmitIntervalMs || 5 * 1000 // 5s
|
||||
debug('latencyCheckIntervalMs: %s dataEmitIntervalMs: %s',
|
||||
that.latencyCheckIntervalMs, that.dataEmitIntervalMs)
|
||||
if (that.dataEmitIntervalMs) {
|
||||
debug('Expecting ~%s events per summary', that.latencyCheckIntervalMs / that.dataEmitIntervalMs)
|
||||
} else {
|
||||
debug('Not emitting summaries')
|
||||
}
|
||||
|
||||
that.asyncTestFn = asyncTestFn // If there is no asyncFn, we measure latency
|
||||
|
||||
// If process: use high resolution timer
|
||||
if (globalThis.process && globalThis.process.hrtime) {
|
||||
debug('Using process.hrtime for timing')
|
||||
that.now = globalThis.process.hrtime
|
||||
that.getDeltaMS = (startTime) => {
|
||||
const hrtime = that.now(startTime)
|
||||
return (hrtime[0] * 1000) + (hrtime[1] / 1000000)
|
||||
}
|
||||
// Let's try for a timer that only monotonically increases
|
||||
} else if (typeof window !== 'undefined' && window.performance && window.performance.now) {
|
||||
debug('Using performance.now for timing')
|
||||
that.now = window.performance.now.bind(window.performance)
|
||||
that.getDeltaMS = (startTime) => Math.round(that.now() - startTime)
|
||||
} else {
|
||||
debug('Using Date.now for timing')
|
||||
that.now = Date.now
|
||||
that.getDeltaMS = (startTime) => that.now() - startTime
|
||||
}
|
||||
|
||||
that._latencyData = that._initLatencyData()
|
||||
|
||||
// We check for isBrowser because of browsers set max rates of timeouts when a page is hidden,
|
||||
// so we fall back to another library
|
||||
// See: http://stackoverflow.com/questions/6032429/chrome-timeouts-interval-suspended-in-background-tabs
|
||||
if (isBrowser()) {
|
||||
that._visibilityChangeEmitter = new VisibilityChangeEmitter()
|
||||
that._visibilityChangeEmitter.on('visibilityChange', (pageInFocus) => {
|
||||
if (pageInFocus) {
|
||||
that._startTimers()
|
||||
} else {
|
||||
that._emitSummary()
|
||||
that._stopTimers()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (!that._visibilityChangeEmitter || that._visibilityChangeEmitter.isVisible()) {
|
||||
that._startTimers()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start internal timers
|
||||
* @private
|
||||
*/
|
||||
_startTimers () {
|
||||
// Timer already started, ignore this
|
||||
if (this._checkLatencyID) {
|
||||
return
|
||||
}
|
||||
this._checkLatency()
|
||||
if (this.dataEmitIntervalMs) {
|
||||
this._emitIntervalID = setInterval(() => this._emitSummary(), this.dataEmitIntervalMs)
|
||||
if (typeof this._emitIntervalID.unref === 'function') {
|
||||
this._emitIntervalID.unref() // Doesn't block exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop internal timers
|
||||
* @private
|
||||
*/
|
||||
_stopTimers () {
|
||||
if (this._checkLatencyID) {
|
||||
clearTimeout(this._checkLatencyID)
|
||||
this._checkLatencyID = undefined
|
||||
}
|
||||
if (this._emitIntervalID) {
|
||||
clearInterval(this._emitIntervalID)
|
||||
this._emitIntervalID = undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit summary only if there were events. It might not have any events if it was forced via a page hidden/show
|
||||
* @private
|
||||
*/
|
||||
_emitSummary () {
|
||||
const summary = this.getSummary()
|
||||
if (summary.events > 0) {
|
||||
this.emit('data', summary)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this function will end the collection period. If a timing event was already fired and somewhere in the queue,
|
||||
* it will not count for this time period
|
||||
* @returns {SummaryObject}
|
||||
*/
|
||||
getSummary () {
|
||||
// We might want to adjust for the number of expected events
|
||||
// Example: first 1 event it comes back, then such a long blocker that the next emit check comes
|
||||
// Then this fires - looks like no latency!!
|
||||
const latency = {
|
||||
events: this._latencyData.events,
|
||||
minMs: this._latencyData.minMs,
|
||||
maxMs: this._latencyData.maxMs,
|
||||
avgMs: this._latencyData.events ? this._latencyData.totalMs / this._latencyData.events
|
||||
: Number.POSITIVE_INFINITY,
|
||||
lengthMs: this.getDeltaMS(this._latencyData.startTime)
|
||||
}
|
||||
this._latencyData = this._initLatencyData() // Clear
|
||||
|
||||
debug('Summary: %O', latency)
|
||||
return latency
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly calls an async fn every roughly latencyCheckIntervalMs (plus some randomness). If no async fn is found,
|
||||
* it will simply report on event loop latency.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_checkLatency () {
|
||||
const that = this
|
||||
// Randomness is needed to avoid alignment by accident to regular things in the event loop
|
||||
const randomness = (Math.random() * that._latecyCheckMultiply) - that._latecyCheckSubtract
|
||||
|
||||
// We use this to ensure that in case some overlap somehow, we don't take the wrong startTime/offset
|
||||
const localData = {
|
||||
deltaOffset: Math.ceil(that.latencyCheckIntervalMs + randomness),
|
||||
startTime: that.now()
|
||||
}
|
||||
|
||||
const cb = () => {
|
||||
// We are already stopped, ignore this datapoint
|
||||
if (!this._checkLatencyID) {
|
||||
return
|
||||
}
|
||||
const deltaMS = that.getDeltaMS(localData.startTime) - localData.deltaOffset
|
||||
that._checkLatency() // Start again ASAP
|
||||
|
||||
// Add the data point. If this gets complex, refactor it
|
||||
that._latencyData.events++
|
||||
that._latencyData.minMs = Math.min(that._latencyData.minMs, deltaMS)
|
||||
that._latencyData.maxMs = Math.max(that._latencyData.maxMs, deltaMS)
|
||||
that._latencyData.totalMs += deltaMS
|
||||
debug('MS: %s Data: %O', deltaMS, that._latencyData)
|
||||
}
|
||||
debug('localData: %O', localData)
|
||||
|
||||
this._checkLatencyID = setTimeout(() => {
|
||||
// This gets rid of including event loop
|
||||
if (that.asyncTestFn) {
|
||||
// Clear timing related things
|
||||
localData.deltaOffset = 0
|
||||
localData.startTime = that.now()
|
||||
that.asyncTestFn(cb)
|
||||
} else {
|
||||
// setTimeout is not more accurate than 1ms, so this will ensure positive numbers. Add 1 to emitted data to remove.
|
||||
// This is not the best, but for now it'll be just fine. This isn't meant to be sub ms accurate.
|
||||
localData.deltaOffset -= 1
|
||||
// If there is no function to test, we mean check latency which is a special case that is really cb => cb()
|
||||
// We avoid that for the few extra function all overheads. Also, we want to keep the timers different
|
||||
cb()
|
||||
}
|
||||
}, localData.deltaOffset)
|
||||
|
||||
if (typeof this._checkLatencyID.unref === 'function') {
|
||||
this._checkLatencyID.unref() // Doesn't block exit
|
||||
}
|
||||
}
|
||||
|
||||
_initLatencyData () {
|
||||
return {
|
||||
startTime: this.now(),
|
||||
minMs: Number.POSITIVE_INFINITY,
|
||||
maxMs: Number.NEGATIVE_INFINITY,
|
||||
events: 0,
|
||||
totalMs: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isBrowser () {
|
||||
return typeof window !== 'undefined'
|
||||
}
|
||||
|
||||
module.exports = LatencyMonitor
|
118
src/connection-manager/visibility-change-emitter.js
Normal file
118
src/connection-manager/visibility-change-emitter.js
Normal file
@@ -0,0 +1,118 @@
|
||||
/* global document */
|
||||
|
||||
/**
|
||||
* This code is based on `latency-monitor` (https://github.com/mlucool/latency-monitor) by `mlucool` (https://github.com/mlucool), available under Apache License 2.0 (https://github.com/mlucool/latency-monitor/blob/master/LICENSE)
|
||||
*/
|
||||
'use strict'
|
||||
const EventEmitter = require('events')
|
||||
|
||||
const debug = require('debug')('latency-monitor:VisibilityChangeEmitter')
|
||||
|
||||
/**
|
||||
* Listen to page visibility change events (i.e. when the page is focused / blurred) by an event emitter.
|
||||
*
|
||||
* Warning: This does not work on all browsers, but should work on all modern browsers
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* const myVisibilityEmitter = new VisibilityChangeEmitter();
|
||||
*
|
||||
* myVisibilityEmitter.on('visibilityChange', (pageInFocus) => {
|
||||
* if ( pageInFocus ){
|
||||
* // Page is in focus
|
||||
* console.log('In focus');
|
||||
* }
|
||||
* else {
|
||||
* // Page is blurred
|
||||
* console.log('Out of focus');
|
||||
* }
|
||||
* });
|
||||
* // To access the visibility state directly, call:
|
||||
* console.log('Am I focused now? ' + myVisibilityEmitter.isVisible());
|
||||
*
|
||||
* @class VisibilityChangeEmitter
|
||||
*/
|
||||
module.exports = class VisibilityChangeEmitter extends EventEmitter {
|
||||
/**
|
||||
* Creates a VisibilityChangeEmitter
|
||||
*/
|
||||
constructor () {
|
||||
super()
|
||||
if (typeof document === 'undefined') {
|
||||
debug('This is not a browser, no "document" found. Stopping.')
|
||||
return
|
||||
}
|
||||
this._initializeVisibilityVarNames()
|
||||
this._addVisibilityChangeListener()
|
||||
}
|
||||
|
||||
/**
|
||||
* document.hidden and document.visibilityChange are the two variables we need to check for;
|
||||
* Since these variables are named differently in different browsers, this function sets
|
||||
* the appropriate name based on the browser being used. Once executed, tha actual names of
|
||||
* document.hidden and document.visibilityChange are found in this._hidden and this._visibilityChange
|
||||
* respectively
|
||||
* @private
|
||||
*/
|
||||
_initializeVisibilityVarNames () {
|
||||
let hidden
|
||||
let visibilityChange
|
||||
if (typeof document.hidden !== 'undefined') { // Opera 12.10 and Firefox 18 and later support
|
||||
hidden = 'hidden'
|
||||
visibilityChange = 'visibilitychange'
|
||||
} else if (typeof document.mozHidden !== 'undefined') {
|
||||
hidden = 'mozHidden'
|
||||
visibilityChange = 'mozvisibilitychange'
|
||||
} else if (typeof document.msHidden !== 'undefined') {
|
||||
hidden = 'msHidden'
|
||||
visibilityChange = 'msvisibilitychange'
|
||||
} else if (typeof document.webkitHidden !== 'undefined') {
|
||||
hidden = 'webkitHidden'
|
||||
visibilityChange = 'webkitvisibilitychange'
|
||||
}
|
||||
this._hidden = hidden
|
||||
this._visibilityChange = 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)
|
||||
* @private
|
||||
*/
|
||||
_addVisibilityChangeListener () {
|
||||
if (typeof document.addEventListener === 'undefined' ||
|
||||
typeof document[this._hidden] === 'undefined') {
|
||||
debug('Checking page visibility requires a browser that supports the Page Visibility API.')
|
||||
} else {
|
||||
// Handle page visibility change
|
||||
document.addEventListener(this._visibilityChange, this._handleVisibilityChange.bind(this), false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The function returns ```true``` if the page is visible or ```false``` if the page is not visible and
|
||||
* ```undefined``` if the page visibility API is not supported by the browser.
|
||||
* @returns {Boolean|void} whether the page is now visible or not (undefined is unknown)
|
||||
*/
|
||||
isVisible () {
|
||||
if (this._hidden === undefined || document[this._hidden] === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return !document[this._hidden]
|
||||
}
|
||||
|
||||
/**
|
||||
* The function that is called when document.visibilityChange has changed
|
||||
* It emits an event called visibilityChange and sends the value of document.hidden as a
|
||||
* parameter
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_handleVisibilityChange () {
|
||||
const visible = !document[this._hidden]
|
||||
debug(visible ? 'Page Visible' : 'Page Hidden')
|
||||
// Emit the event
|
||||
this.emit('visibilityChange', visible)
|
||||
}
|
||||
}
|
@@ -24,7 +24,7 @@ module.exports = (node) => {
|
||||
* @param {object} [options]
|
||||
* @param {number} [options.timeout] How long the query should run
|
||||
* @param {number} [options.maxNumProviders] - maximum number of providers to find
|
||||
* @returns {AsyncIterable<PeerInfo>}
|
||||
* @returns {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>}
|
||||
*/
|
||||
async * findProviders (key, options) {
|
||||
if (!routers.length) {
|
||||
@@ -42,8 +42,8 @@ module.exports = (node) => {
|
||||
})
|
||||
)
|
||||
|
||||
for (const pInfo of result) {
|
||||
yield pInfo
|
||||
for (const peer of result) {
|
||||
yield peer
|
||||
}
|
||||
},
|
||||
|
||||
|
@@ -4,12 +4,12 @@ const multiaddr = require('multiaddr')
|
||||
const errCode = require('err-code')
|
||||
const TimeoutController = require('timeout-abort-controller')
|
||||
const anySignal = require('any-signal')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:dialer')
|
||||
log.error = debug('libp2p:dialer:error')
|
||||
|
||||
const { DialRequest } = require('./dial-request')
|
||||
const getPeer = require('../get-peer')
|
||||
|
||||
const { codes } = require('../errors')
|
||||
const {
|
||||
@@ -58,18 +58,19 @@ class Dialer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to a given `PeerId` or `Multiaddr` by dialing all of its known addresses.
|
||||
* Connects to a given `peer` by dialing all of its known addresses.
|
||||
* The dial to the first address that is successfully able to upgrade a connection
|
||||
* will be used.
|
||||
*
|
||||
* @param {PeerInfo|Multiaddr} peer The peer to dial
|
||||
* @param {PeerId|Multiaddr|string} peer The peer to dial
|
||||
* @param {object} [options]
|
||||
* @param {AbortSignal} [options.signal] An AbortController signal
|
||||
* @returns {Promise<Connection>}
|
||||
*/
|
||||
async connectToPeer (peer, options = {}) {
|
||||
const dialTarget = this._createDialTarget(peer)
|
||||
if (dialTarget.addrs.length === 0) {
|
||||
|
||||
if (!dialTarget.addrs.length) {
|
||||
throw errCode(new Error('The dial request has no addresses'), codes.ERR_NO_VALID_ADDRESSES)
|
||||
}
|
||||
const pendingDial = this._pendingDials.get(dialTarget.id) || this._createPendingDial(dialTarget, options)
|
||||
@@ -99,21 +100,29 @@ class Dialer {
|
||||
/**
|
||||
* Creates a DialTarget. The DialTarget is used to create and track
|
||||
* the DialRequest to a given peer.
|
||||
* If a multiaddr is received it should be the first address attempted.
|
||||
* @private
|
||||
* @param {PeerInfo|Multiaddr} peer A PeerId or Multiaddr
|
||||
* @param {PeerId|Multiaddr|string} peer A PeerId or Multiaddr
|
||||
* @returns {DialTarget}
|
||||
*/
|
||||
_createDialTarget (peer) {
|
||||
const dialable = Dialer.getDialable(peer)
|
||||
if (multiaddr.isMultiaddr(dialable)) {
|
||||
return {
|
||||
id: dialable.toString(),
|
||||
addrs: [dialable]
|
||||
}
|
||||
const { id, multiaddrs } = getPeer(peer)
|
||||
|
||||
if (multiaddrs) {
|
||||
this.peerStore.addressBook.add(id, multiaddrs)
|
||||
}
|
||||
const addrs = this.peerStore.multiaddrsForPeer(dialable)
|
||||
|
||||
let addrs = this.peerStore.addressBook.getMultiaddrsForPeer(id)
|
||||
|
||||
// If received a multiaddr to dial, it should be the first to use
|
||||
// But, if we know other multiaddrs for the peer, we should try them too.
|
||||
if (multiaddr.isMultiaddr(peer)) {
|
||||
addrs = addrs.filter((addr) => !peer.equals(addr))
|
||||
addrs.unshift(peer)
|
||||
}
|
||||
|
||||
return {
|
||||
id: dialable.id.toB58String(),
|
||||
id: id.toB58String(),
|
||||
addrs
|
||||
}
|
||||
}
|
||||
@@ -178,36 +187,6 @@ class Dialer {
|
||||
log('token %d released', token)
|
||||
this.tokens.push(token)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given `peer` into a `PeerInfo` or `Multiaddr`.
|
||||
* @static
|
||||
* @param {PeerInfo|PeerId|Multiaddr|string} peer
|
||||
* @returns {PeerInfo|Multiaddr}
|
||||
*/
|
||||
static getDialable (peer) {
|
||||
if (PeerInfo.isPeerInfo(peer)) return peer
|
||||
if (typeof peer === 'string') {
|
||||
peer = multiaddr(peer)
|
||||
}
|
||||
|
||||
let addr
|
||||
if (multiaddr.isMultiaddr(peer)) {
|
||||
addr = peer
|
||||
try {
|
||||
peer = PeerId.createFromCID(peer.getPeerId())
|
||||
} catch (err) {
|
||||
throw errCode(new Error('The multiaddr did not contain a valid peer id'), codes.ERR_INVALID_PEER)
|
||||
}
|
||||
}
|
||||
|
||||
if (PeerId.isPeerId(peer)) {
|
||||
peer = new PeerInfo(peer)
|
||||
}
|
||||
|
||||
addr && peer.multiaddrs.add(addr)
|
||||
return peer
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Dialer
|
||||
|
@@ -26,5 +26,6 @@ exports.codes = {
|
||||
ERR_TIMEOUT: 'ERR_TIMEOUT',
|
||||
ERR_TRANSPORT_UNAVAILABLE: 'ERR_TRANSPORT_UNAVAILABLE',
|
||||
ERR_TRANSPORT_DIAL_FAILED: 'ERR_TRANSPORT_DIAL_FAILED',
|
||||
ERR_UNSUPPORTED_PROTOCOL: 'ERR_UNSUPPORTED_PROTOCOL'
|
||||
ERR_UNSUPPORTED_PROTOCOL: 'ERR_UNSUPPORTED_PROTOCOL',
|
||||
ERR_INVALID_MULTIADDR: 'ERR_INVALID_MULTIADDR'
|
||||
}
|
||||
|
@@ -1,73 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const multiaddr = require('multiaddr')
|
||||
const errCode = require('err-code')
|
||||
|
||||
/**
|
||||
* Converts the given `peer` to a `PeerInfo` instance.
|
||||
* The `PeerStore` will be checked for the resulting peer, and
|
||||
* the peer will be updated in the `PeerStore`.
|
||||
*
|
||||
* @param {PeerInfo|PeerId|Multiaddr|string} peer
|
||||
* @param {PeerStore} peerStore
|
||||
* @returns {PeerInfo}
|
||||
*/
|
||||
function getPeerInfo (peer, peerStore) {
|
||||
if (typeof peer === 'string') {
|
||||
peer = multiaddr(peer)
|
||||
}
|
||||
|
||||
let addr
|
||||
if (multiaddr.isMultiaddr(peer)) {
|
||||
addr = peer
|
||||
try {
|
||||
peer = PeerId.createFromB58String(peer.getPeerId())
|
||||
} catch (err) {
|
||||
throw errCode(
|
||||
new Error(`${peer} is not a valid peer type`),
|
||||
'ERR_INVALID_MULTIADDR'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (PeerId.isPeerId(peer)) {
|
||||
peer = new PeerInfo(peer)
|
||||
}
|
||||
|
||||
addr && peer.multiaddrs.add(addr)
|
||||
|
||||
return peerStore ? peerStore.put(peer) : peer
|
||||
}
|
||||
|
||||
/**
|
||||
* If `getPeerInfo` does not return a peer with multiaddrs,
|
||||
* the `libp2p` PeerRouter will be used to attempt to find the peer.
|
||||
*
|
||||
* @async
|
||||
* @param {PeerInfo|PeerId|Multiaddr|string} peer
|
||||
* @param {Libp2p} libp2p
|
||||
* @returns {Promise<PeerInfo>}
|
||||
*/
|
||||
function getPeerInfoRemote (peer, libp2p) {
|
||||
let peerInfo
|
||||
|
||||
try {
|
||||
peerInfo = getPeerInfo(peer, libp2p.peerStore)
|
||||
} catch (err) {
|
||||
throw errCode(err, 'ERR_INVALID_PEER_TYPE')
|
||||
}
|
||||
|
||||
// If we don't have an address for the peer, attempt to find it
|
||||
if (peerInfo.multiaddrs.size < 1) {
|
||||
return libp2p.peerRouting.findPeer(peerInfo.id)
|
||||
}
|
||||
|
||||
return peerInfo
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getPeerInfoRemote,
|
||||
getPeerInfo
|
||||
}
|
40
src/get-peer.js
Normal file
40
src/get-peer.js
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict'
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const multiaddr = require('multiaddr')
|
||||
const errCode = require('err-code')
|
||||
|
||||
const { codes } = require('./errors')
|
||||
|
||||
/**
|
||||
* Converts the given `peer` to a `Peer` object.
|
||||
* If a multiaddr is received, the addressBook is updated.
|
||||
* @param {PeerId|Multiaddr|string} peer
|
||||
* @param {PeerStore} peerStore
|
||||
* @returns {{ id: PeerId, multiaddrs: Array<Multiaddr> }}
|
||||
*/
|
||||
function getPeer (peer) {
|
||||
if (typeof peer === 'string') {
|
||||
peer = multiaddr(peer)
|
||||
}
|
||||
|
||||
let addr
|
||||
if (multiaddr.isMultiaddr(peer)) {
|
||||
addr = peer
|
||||
try {
|
||||
peer = PeerId.createFromB58String(peer.getPeerId())
|
||||
} catch (err) {
|
||||
throw errCode(
|
||||
new Error(`${peer} is not a valid peer type`),
|
||||
codes.ERR_INVALID_MULTIADDR
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: peer,
|
||||
multiaddrs: addr ? [addr] : undefined
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = getPeer
|
@@ -1,12 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const debug = require('debug')
|
||||
const pb = require('it-protocol-buffers')
|
||||
const lp = require('it-length-prefixed')
|
||||
const pipe = require('it-pipe')
|
||||
const { collect, take, consume } = require('streaming-iterables')
|
||||
|
||||
const PeerInfo = require('peer-info')
|
||||
const PeerId = require('peer-id')
|
||||
const multiaddr = require('multiaddr')
|
||||
const { toBuffer } = require('it-buffer')
|
||||
@@ -27,39 +27,6 @@ const errCode = require('err-code')
|
||||
const { codes } = require('../errors')
|
||||
|
||||
class IdentifyService {
|
||||
/**
|
||||
* Replaces the multiaddrs on the given `peerInfo`,
|
||||
* with the provided `multiaddrs`
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @param {Array<Multiaddr>|Array<Buffer>} multiaddrs
|
||||
*/
|
||||
static updatePeerAddresses (peerInfo, multiaddrs) {
|
||||
if (multiaddrs && multiaddrs.length > 0) {
|
||||
peerInfo.multiaddrs.clear()
|
||||
multiaddrs.forEach(ma => {
|
||||
try {
|
||||
peerInfo.multiaddrs.add(ma)
|
||||
} catch (err) {
|
||||
log.error('could not add multiaddr', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the protocols on the given `peerInfo`,
|
||||
* with the provided `protocols`
|
||||
* @static
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @param {Array<string>} protocols
|
||||
*/
|
||||
static updatePeerProtocols (peerInfo, protocols) {
|
||||
if (protocols && protocols.length > 0) {
|
||||
peerInfo.protocols.clear()
|
||||
protocols.forEach(proto => peerInfo.protocols.add(proto))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the `addr` and converts it to a Multiaddr if possible
|
||||
* @param {Buffer|String} addr
|
||||
@@ -79,21 +46,37 @@ class IdentifyService {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {object} options
|
||||
* @param {Registrar} options.registrar
|
||||
* @param {Libp2p} options.libp2p
|
||||
* @param {Map<string, handler>} options.protocols A reference to the protocols we support
|
||||
* @param {PeerInfo} options.peerInfo The peer running the identify service
|
||||
*/
|
||||
constructor (options) {
|
||||
constructor ({ libp2p, protocols }) {
|
||||
/**
|
||||
* @property {Registrar}
|
||||
* @property {PeerStore}
|
||||
*/
|
||||
this.registrar = options.registrar
|
||||
/**
|
||||
* @property {PeerInfo}
|
||||
*/
|
||||
this.peerInfo = options.peerInfo
|
||||
this.peerStore = libp2p.peerStore
|
||||
|
||||
this._protocols = options.protocols
|
||||
/**
|
||||
* @property {ConnectionManager}
|
||||
*/
|
||||
this.connectionManager = libp2p.connectionManager
|
||||
|
||||
this.connectionManager.on('peer:connect', (connection) => {
|
||||
const peerId = connection.remotePeer
|
||||
|
||||
this.identify(connection, peerId).catch(log.error)
|
||||
})
|
||||
|
||||
/**
|
||||
* @property {PeerId}
|
||||
*/
|
||||
this.peerId = libp2p.peerId
|
||||
|
||||
/**
|
||||
* @property {AddressManager}
|
||||
*/
|
||||
this._libp2p = libp2p
|
||||
|
||||
this._protocols = protocols
|
||||
|
||||
this.handleMessage = this.handleMessage.bind(this)
|
||||
}
|
||||
@@ -110,7 +93,7 @@ class IdentifyService {
|
||||
|
||||
await pipe(
|
||||
[{
|
||||
listenAddrs: this.peerInfo.multiaddrs.toArray().map((ma) => ma.buffer),
|
||||
listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer),
|
||||
protocols: Array.from(this._protocols.keys())
|
||||
}],
|
||||
pb.encode(Message),
|
||||
@@ -134,7 +117,7 @@ class IdentifyService {
|
||||
const connections = []
|
||||
let connection
|
||||
for (const peer of peerStore.peers.values()) {
|
||||
if (peer.protocols.has(MULTICODEC_IDENTIFY_PUSH) && (connection = this.registrar.getConnection(peer))) {
|
||||
if (peer.protocols.includes(MULTICODEC_IDENTIFY_PUSH) && (connection = this.connectionManager.get(peer.id))) {
|
||||
connections.push(connection)
|
||||
}
|
||||
}
|
||||
@@ -181,7 +164,7 @@ class IdentifyService {
|
||||
} = message
|
||||
|
||||
const id = await PeerId.createFromPubKey(publicKey)
|
||||
const peerInfo = new PeerInfo(id)
|
||||
|
||||
if (connection.remotePeer.toB58String() !== id.toB58String()) {
|
||||
throw errCode(new Error('identified peer does not match the expected peer'), codes.ERR_INVALID_PEER)
|
||||
}
|
||||
@@ -189,11 +172,10 @@ class IdentifyService {
|
||||
// Get the observedAddr if there is one
|
||||
observedAddr = IdentifyService.getCleanMultiaddr(observedAddr)
|
||||
|
||||
// Copy the listenAddrs and protocols
|
||||
IdentifyService.updatePeerAddresses(peerInfo, listenAddrs)
|
||||
IdentifyService.updatePeerProtocols(peerInfo, protocols)
|
||||
// Update peers data in PeerStore
|
||||
this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr)))
|
||||
this.peerStore.protoBook.set(id, protocols)
|
||||
|
||||
this.registrar.peerStore.replace(peerInfo)
|
||||
// TODO: Track our observed address so that we can score it
|
||||
log('received observed address of %s', observedAddr)
|
||||
}
|
||||
@@ -228,15 +210,15 @@ class IdentifyService {
|
||||
*/
|
||||
_handleIdentify ({ connection, stream }) {
|
||||
let publicKey = Buffer.alloc(0)
|
||||
if (this.peerInfo.id.pubKey) {
|
||||
publicKey = this.peerInfo.id.pubKey.bytes
|
||||
if (this.peerId.pubKey) {
|
||||
publicKey = this.peerId.pubKey.bytes
|
||||
}
|
||||
|
||||
const message = Message.encode({
|
||||
protocolVersion: PROTOCOL_VERSION,
|
||||
agentVersion: AGENT_VERSION,
|
||||
publicKey,
|
||||
listenAddrs: this.peerInfo.multiaddrs.toArray().map((ma) => ma.buffer),
|
||||
listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer),
|
||||
observedAddr: connection.remoteAddr.buffer,
|
||||
protocols: Array.from(this._protocols.keys())
|
||||
})
|
||||
@@ -273,20 +255,16 @@ class IdentifyService {
|
||||
return log.error('received invalid message', err)
|
||||
}
|
||||
|
||||
// Update the listen addresses
|
||||
const peerInfo = new PeerInfo(connection.remotePeer)
|
||||
|
||||
// Update peers data in PeerStore
|
||||
const id = connection.remotePeer
|
||||
try {
|
||||
IdentifyService.updatePeerAddresses(peerInfo, message.listenAddrs)
|
||||
this.peerStore.addressBook.set(id, message.listenAddrs.map((addr) => multiaddr(addr)))
|
||||
} catch (err) {
|
||||
return log.error('received invalid listen addrs', err)
|
||||
}
|
||||
|
||||
// Update the protocols
|
||||
IdentifyService.updatePeerProtocols(peerInfo, message.protocols)
|
||||
|
||||
// Update the peer in the PeerStore
|
||||
this.registrar.peerStore.replace(peerInfo)
|
||||
this.peerStore.protoBook.set(id, message.protocols)
|
||||
}
|
||||
}
|
||||
|
||||
|
233
src/index.js
233
src/index.js
@@ -2,18 +2,20 @@
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const debug = require('debug')
|
||||
const globalThis = require('ipfs-utils/src/globalthis')
|
||||
const log = debug('libp2p')
|
||||
log.error = debug('libp2p:error')
|
||||
|
||||
const PeerInfo = require('peer-info')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const peerRouting = require('./peer-routing')
|
||||
const contentRouting = require('./content-routing')
|
||||
const pubsub = require('./pubsub')
|
||||
const { getPeerInfo } = require('./get-peer-info')
|
||||
const getPeer = require('./get-peer')
|
||||
const { validate: validateConfig } = require('./config')
|
||||
const { codes } = require('./errors')
|
||||
|
||||
const AddressManager = require('./address-manager')
|
||||
const ConnectionManager = require('./connection-manager')
|
||||
const Circuit = require('./circuit')
|
||||
const Dialer = require('./dialer')
|
||||
@@ -21,6 +23,7 @@ const Metrics = require('./metrics')
|
||||
const TransportManager = require('./transport-manager')
|
||||
const Upgrader = require('./upgrader')
|
||||
const PeerStore = require('./peer-store')
|
||||
const PersistentPeerStore = require('./peer-store/persistent')
|
||||
const Registrar = require('./registrar')
|
||||
const ping = require('./ping')
|
||||
const {
|
||||
@@ -41,62 +44,59 @@ class Libp2p extends EventEmitter {
|
||||
// and add default values where appropriate
|
||||
this._options = validateConfig(_options)
|
||||
|
||||
this.peerId = this._options.peerId
|
||||
this.datastore = this._options.datastore
|
||||
this.peerInfo = this._options.peerInfo
|
||||
this.peerStore = new PeerStore()
|
||||
|
||||
this.peerStore = (this.datastore && this._options.peerStore.persistence)
|
||||
? new PersistentPeerStore({
|
||||
datastore: this.datastore,
|
||||
...this._options.peerStore
|
||||
})
|
||||
: new PeerStore()
|
||||
|
||||
// Addresses {listen, announce, noAnnounce}
|
||||
this.addresses = this._options.addresses
|
||||
this.addressManager = new AddressManager(this._options.addresses)
|
||||
|
||||
this._modules = this._options.modules
|
||||
this._config = this._options.config
|
||||
this._transport = [] // Transport instances/references
|
||||
this._discovery = new Map() // Discovery service instances/references
|
||||
|
||||
// Create the Connection Manager
|
||||
this.connectionManager = new ConnectionManager(this, this._options.connectionManager)
|
||||
|
||||
// Create Metrics
|
||||
if (this._options.metrics.enabled) {
|
||||
this.metrics = new Metrics(this._options.metrics)
|
||||
this.metrics = new Metrics({
|
||||
...this._options.metrics,
|
||||
connectionManager: this.connectionManager
|
||||
})
|
||||
}
|
||||
|
||||
// Setup the Upgrader
|
||||
this.upgrader = new Upgrader({
|
||||
localPeer: this.peerInfo.id,
|
||||
localPeer: this.peerId,
|
||||
metrics: this.metrics,
|
||||
onConnection: (connection) => {
|
||||
const peerInfo = this.peerStore.put(new PeerInfo(connection.remotePeer), { silent: true })
|
||||
this.registrar.onConnect(peerInfo, connection)
|
||||
this.connectionManager.onConnect(connection)
|
||||
this.emit('peer:connect', peerInfo)
|
||||
|
||||
// Run identify for every connection
|
||||
if (this.identifyService) {
|
||||
this.identifyService.identify(connection, connection.remotePeer)
|
||||
.catch(log.error)
|
||||
}
|
||||
},
|
||||
onConnectionEnd: (connection) => {
|
||||
const peerInfo = Dialer.getDialable(connection.remotePeer)
|
||||
this.registrar.onDisconnect(peerInfo, connection)
|
||||
this.connectionManager.onDisconnect(connection)
|
||||
|
||||
// If there are no connections to the peer, disconnect
|
||||
if (!this.registrar.getConnection(peerInfo)) {
|
||||
this.emit('peer:disconnect', peerInfo)
|
||||
this.metrics && this.metrics.onPeerDisconnected(peerInfo.id)
|
||||
}
|
||||
}
|
||||
onConnection: (connection) => this.connectionManager.onConnect(connection),
|
||||
onConnectionEnd: (connection) => this.connectionManager.onDisconnect(connection)
|
||||
})
|
||||
|
||||
// Create the Registrar
|
||||
this.registrar = new Registrar({ peerStore: this.peerStore })
|
||||
this.handle = this.handle.bind(this)
|
||||
this.registrar.handle = this.handle
|
||||
|
||||
// Create the Connection Manager
|
||||
this.connectionManager = new ConnectionManager(this, this._options.connectionManager)
|
||||
|
||||
// Setup the transport manager
|
||||
this.transportManager = new TransportManager({
|
||||
libp2p: this,
|
||||
upgrader: this.upgrader
|
||||
})
|
||||
|
||||
// Create the Registrar
|
||||
this.registrar = new Registrar({
|
||||
peerStore: this.peerStore,
|
||||
connectionManager: this.connectionManager
|
||||
})
|
||||
|
||||
this.handle = this.handle.bind(this)
|
||||
this.registrar.handle = this.handle
|
||||
|
||||
// Attach crypto channels
|
||||
if (this._modules.connEncryption) {
|
||||
const cryptos = this._modules.connEncryption
|
||||
@@ -132,8 +132,7 @@ class Libp2p extends EventEmitter {
|
||||
|
||||
// Add the identify service since we can multiplex
|
||||
this.identifyService = new IdentifyService({
|
||||
registrar: this.registrar,
|
||||
peerInfo: this.peerInfo,
|
||||
libp2p: this,
|
||||
protocols: this.upgrader.protocols
|
||||
})
|
||||
this.handle(Object.values(IDENTIFY_PROTOCOLS), this.identifyService.handleMessage)
|
||||
@@ -142,7 +141,7 @@ class Libp2p extends EventEmitter {
|
||||
// Attach private network protector
|
||||
if (this._modules.connProtector) {
|
||||
this.upgrader.protector = this._modules.connProtector
|
||||
} else if (process.env.LIBP2P_FORCE_PNET) {
|
||||
} else if (globalThis.process !== undefined && globalThis.process.env && globalThis.process.env.LIBP2P_FORCE_PNET) {
|
||||
throw new Error('Private network is enforced, but no protector was provided')
|
||||
}
|
||||
|
||||
@@ -151,7 +150,7 @@ class Libp2p extends EventEmitter {
|
||||
const DHT = this._modules.dht
|
||||
this._dht = new DHT({
|
||||
dialer: this.dialer,
|
||||
peerInfo: this.peerInfo,
|
||||
peerId: this.peerId,
|
||||
peerStore: this.peerStore,
|
||||
registrar: this.registrar,
|
||||
datastore: this.datastore,
|
||||
@@ -197,6 +196,7 @@ class Libp2p extends EventEmitter {
|
||||
*/
|
||||
async start () {
|
||||
log('libp2p is starting')
|
||||
|
||||
try {
|
||||
await this._onStarting()
|
||||
await this._onDidStart()
|
||||
@@ -224,7 +224,10 @@ class Libp2p extends EventEmitter {
|
||||
|
||||
await Promise.all(Array.from(this._discovery.values(), s => s.stop()))
|
||||
|
||||
this.connectionManager.stop()
|
||||
this._discovery = new Map()
|
||||
|
||||
await this.peerStore.stop()
|
||||
await this.connectionManager.stop()
|
||||
|
||||
await Promise.all([
|
||||
this.pubsub && this.pubsub.stop(),
|
||||
@@ -233,7 +236,6 @@ class Libp2p extends EventEmitter {
|
||||
])
|
||||
|
||||
await this.transportManager.close()
|
||||
await this.registrar.close()
|
||||
|
||||
ping.unmount(this)
|
||||
this.dialer.destroy()
|
||||
@@ -257,14 +259,13 @@ class Libp2p extends EventEmitter {
|
||||
* @returns {Map<string, Connection[]>}
|
||||
*/
|
||||
get connections () {
|
||||
return this.registrar.connections
|
||||
return this.connectionManager.connections
|
||||
}
|
||||
|
||||
/**
|
||||
* Dials to the provided peer. If successful, the `PeerInfo` of the
|
||||
* Dials to the provided peer. If successful, the known `Peer` data of the
|
||||
* peer will be added to the nodes `peerStore`
|
||||
*
|
||||
* @param {PeerInfo|PeerId|Multiaddr|string} peer The peer to dial
|
||||
* @param {PeerId|Multiaddr|string} peer The peer to dial
|
||||
* @param {object} options
|
||||
* @param {AbortSignal} [options.signal]
|
||||
* @returns {Promise<Connection>}
|
||||
@@ -275,26 +276,23 @@ class Libp2p extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Dials to the provided peer and handshakes with the given protocol.
|
||||
* If successful, the `PeerInfo` of the peer will be added to the nodes `peerStore`,
|
||||
* and the `Connection` will be sent in the callback
|
||||
*
|
||||
* If successful, the known `Peer` data of the peer will be added to the nodes `peerStore`,
|
||||
* and the `Connection` will be returned
|
||||
* @async
|
||||
* @param {PeerInfo|PeerId|Multiaddr|string} peer The peer to dial
|
||||
* @param {PeerId|Multiaddr|string} peer The peer to dial
|
||||
* @param {string[]|string} protocols
|
||||
* @param {object} options
|
||||
* @param {AbortSignal} [options.signal]
|
||||
* @returns {Promise<Connection|*>}
|
||||
*/
|
||||
async dialProtocol (peer, protocols, options) {
|
||||
const dialable = Dialer.getDialable(peer)
|
||||
let connection
|
||||
if (PeerInfo.isPeerInfo(dialable)) {
|
||||
this.peerStore.put(dialable, { silent: true })
|
||||
connection = this.registrar.getConnection(dialable)
|
||||
}
|
||||
const { id, multiaddrs } = getPeer(peer, this.peerStore)
|
||||
let connection = this.connectionManager.get(id)
|
||||
|
||||
if (!connection) {
|
||||
connection = await this.dialer.connectToPeer(dialable, options)
|
||||
connection = await this.dialer.connectToPeer(peer, options)
|
||||
} else if (multiaddrs) {
|
||||
this.peerStore.addressBook.add(id, multiaddrs)
|
||||
}
|
||||
|
||||
// If a protocol was provided, create a new stream
|
||||
@@ -305,30 +303,64 @@ class Libp2p extends EventEmitter {
|
||||
return connection
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peer advertising multiaddrs by concating the addresses used
|
||||
* by transports to listen with the announce addresses.
|
||||
* Duplicated addresses and noAnnounce addresses are filtered out.
|
||||
* @return {Array<Multiaddr>}
|
||||
*/
|
||||
get multiaddrs () {
|
||||
// Filter noAnnounce multiaddrs
|
||||
const filterMa = this.addressManager.getNoAnnounceAddrs()
|
||||
|
||||
// Create advertising list
|
||||
return this.transportManager.getAddrs()
|
||||
.concat(this.addressManager.getAnnounceAddrs())
|
||||
.filter((ma, index, array) => {
|
||||
// Filter out if repeated
|
||||
if (array.findIndex((otherMa) => otherMa.equals(ma)) !== index) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Filter out if in noAnnounceMultiaddrs
|
||||
if (filterMa.find((fm) => fm.equals(ma))) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects all connections to the given `peer`
|
||||
*
|
||||
* @param {PeerInfo|PeerId|multiaddr|string} peer the peer to close connections to
|
||||
* @param {PeerId|multiaddr|string} peer the peer to close connections to
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
hangUp (peer) {
|
||||
const peerInfo = getPeerInfo(peer, this.peerStore)
|
||||
return Promise.all(
|
||||
this.registrar.connections.get(peerInfo.id.toB58String()).map(connection => {
|
||||
async hangUp (peer) {
|
||||
const { id } = getPeer(peer)
|
||||
|
||||
const connections = this.connectionManager.connections.get(id.toB58String())
|
||||
|
||||
if (!connections) {
|
||||
return
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
connections.map(connection => {
|
||||
return connection.close()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings the given peer
|
||||
* @param {PeerInfo|PeerId|Multiaddr|string} peer The peer to ping
|
||||
* Pings the given peer in order to obtain the operation latency.
|
||||
* @param {PeerId|Multiaddr|string} peer The peer to ping
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
async ping (peer) {
|
||||
const peerInfo = await getPeerInfo(peer, this.peerStore)
|
||||
ping (peer) {
|
||||
const { id } = getPeer(peer)
|
||||
|
||||
return ping(this, peerInfo)
|
||||
return ping(this, id)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -366,17 +398,11 @@ class Libp2p extends EventEmitter {
|
||||
}
|
||||
|
||||
async _onStarting () {
|
||||
// Listen on the addresses supplied in the peerInfo
|
||||
const multiaddrs = this.peerInfo.multiaddrs.toArray()
|
||||
// Listen on the provided transports
|
||||
await this.transportManager.listen()
|
||||
|
||||
await this.transportManager.listen(multiaddrs)
|
||||
|
||||
// The addresses may change once the listener starts
|
||||
// eg /ip4/0.0.0.0/tcp/0 => /ip4/192.168.1.0/tcp/58751
|
||||
this.peerInfo.multiaddrs.clear()
|
||||
for (const ma of this.transportManager.getAddrs()) {
|
||||
this.peerInfo.multiaddrs.add(ma)
|
||||
}
|
||||
// Start PeerStore
|
||||
await this.peerStore.start()
|
||||
|
||||
if (this._config.pubsub.enabled) {
|
||||
this.pubsub && this.pubsub.start()
|
||||
@@ -404,18 +430,18 @@ class Libp2p extends EventEmitter {
|
||||
|
||||
this.connectionManager.start()
|
||||
|
||||
this.peerStore.on('peer', peerInfo => {
|
||||
this.emit('peer:discovery', peerInfo)
|
||||
this._maybeConnect(peerInfo)
|
||||
this.peerStore.on('peer', peerId => {
|
||||
this.emit('peer:discovery', peerId)
|
||||
this._maybeConnect(peerId)
|
||||
})
|
||||
|
||||
// Peer discovery
|
||||
await this._setupPeerDiscovery()
|
||||
|
||||
// Once we start, emit and dial any peers we may have already discovered
|
||||
for (const peerInfo of this.peerStore.peers.values()) {
|
||||
this.emit('peer:discovery', peerInfo)
|
||||
this._maybeConnect(peerInfo)
|
||||
for (const peer of this.peerStore.peers.values()) {
|
||||
this.emit('peer:discovery', peer.id)
|
||||
this._maybeConnect(peer.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,31 +449,33 @@ class Libp2p extends EventEmitter {
|
||||
* Called whenever peer discovery services emit `peer` events.
|
||||
* Known peers may be emitted.
|
||||
* @private
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @param {PeerDara} peer
|
||||
*/
|
||||
_onDiscoveryPeer (peerInfo) {
|
||||
if (peerInfo.id.toB58String() === this.peerInfo.id.toB58String()) {
|
||||
_onDiscoveryPeer (peer) {
|
||||
if (peer.id.toB58String() === this.peerId.toB58String()) {
|
||||
log.error(new Error(codes.ERR_DISCOVERED_SELF))
|
||||
return
|
||||
}
|
||||
this.peerStore.put(peerInfo)
|
||||
|
||||
peer.multiaddrs && this.peerStore.addressBook.add(peer.id, peer.multiaddrs)
|
||||
peer.protocols && this.peerStore.protoBook.set(peer.id, peer.protocols)
|
||||
}
|
||||
|
||||
/**
|
||||
* Will dial to the given `peerInfo` if the current number of
|
||||
* Will dial to the given `peerId` if the current number of
|
||||
* connected peers is less than the configured `ConnectionManager`
|
||||
* minPeers.
|
||||
* @private
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @param {PeerId} peerId
|
||||
*/
|
||||
async _maybeConnect (peerInfo) {
|
||||
async _maybeConnect (peerId) {
|
||||
// If auto dialing is on and we have no connection to the peer, check if we should dial
|
||||
if (this._config.peerDiscovery.autoDial === true && !this.registrar.getConnection(peerInfo)) {
|
||||
if (this._config.peerDiscovery.autoDial === true && !this.connectionManager.get(peerId)) {
|
||||
const minPeers = this._options.connectionManager.minPeers || 0
|
||||
if (minPeers > this.connectionManager._connections.size) {
|
||||
log('connecting to discovered peer %s', peerInfo.id.toB58String())
|
||||
if (minPeers > this.connectionManager.size) {
|
||||
log('connecting to discovered peer %s', peerId.toB58String())
|
||||
try {
|
||||
await this.dialer.connectToPeer(peerInfo)
|
||||
await this.dialer.connectToPeer(peerId)
|
||||
} catch (err) {
|
||||
log.error('could not connect to discovered peer', err)
|
||||
}
|
||||
@@ -478,7 +506,10 @@ class Libp2p extends EventEmitter {
|
||||
let discoveryService
|
||||
|
||||
if (typeof DiscoveryService === 'function') {
|
||||
discoveryService = new DiscoveryService(Object.assign({}, config, { peerInfo: this.peerInfo, libp2p: this }))
|
||||
discoveryService = new DiscoveryService(Object.assign({}, config, {
|
||||
peerId: this.peerId,
|
||||
libp2p: this
|
||||
}))
|
||||
} else {
|
||||
discoveryService = DiscoveryService
|
||||
}
|
||||
@@ -505,19 +536,19 @@ class Libp2p extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `new Libp2p(options)` except it will create a `PeerInfo`
|
||||
* Like `new Libp2p(options)` except it will create a `PeerId`
|
||||
* instance if one is not provided in options.
|
||||
* @param {object} options Libp2p configuration options
|
||||
* @returns {Libp2p}
|
||||
*/
|
||||
Libp2p.create = async function create (options = {}) {
|
||||
if (options.peerInfo) {
|
||||
if (options.peerId) {
|
||||
return new Libp2p(options)
|
||||
}
|
||||
|
||||
const peerInfo = await PeerInfo.create()
|
||||
const peerId = await PeerId.create()
|
||||
|
||||
options.peerInfo = peerInfo
|
||||
options.peerId = peerId
|
||||
return new Libp2p(options)
|
||||
}
|
||||
|
||||
|
@@ -21,6 +21,7 @@ class Metrics {
|
||||
/**
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {ConnectionManager} options.connectionManager
|
||||
* @param {number} options.computeThrottleMaxQueueSize
|
||||
* @param {number} options.computeThrottleTimeout
|
||||
* @param {Array<number>} options.movingAverageIntervals
|
||||
@@ -34,6 +35,10 @@ class Metrics {
|
||||
this._oldPeers = oldPeerLRU(this._options.maxOldPeersRetention)
|
||||
this._running = false
|
||||
this._onMessage = this._onMessage.bind(this)
|
||||
this._connectionManager = options.connectionManager
|
||||
this._connectionManager.on('peer:disconnect', (connection) => {
|
||||
this.onPeerDisconnected(connection.remotePeer)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -18,7 +18,7 @@ module.exports = (node) => {
|
||||
* @param {String} id The id of the peer to find
|
||||
* @param {object} [options]
|
||||
* @param {number} [options.timeout] How long the query should run
|
||||
* @returns {Promise<PeerInfo>}
|
||||
* @returns {Promise<{ id: PeerId, multiaddrs: Multiaddr[] }>}
|
||||
*/
|
||||
findPeer: async (id, options) => { // eslint-disable-line require-await
|
||||
if (!routers.length) {
|
||||
|
@@ -1,3 +1,134 @@
|
||||
# Peerstore
|
||||
# PeerStore
|
||||
|
||||
WIP
|
||||
Libp2p's PeerStore is responsible for keeping an updated register with the relevant information of the known peers. It should be the single source of truth for all peer data, where a subsystem can learn about peers' data and where someone can listen for updates. The PeerStore comprises four main components: `addressBook`, `keyBook`, `protocolBook` and `metadataBook`.
|
||||
|
||||
The PeerStore manages the high level operations on its inner books. Moreover, the PeerStore should be responsible for notifying interested parties of relevant events, through its Event Emitter.
|
||||
|
||||
## Data gathering
|
||||
|
||||
Several libp2p subsystems will perform operations, which will gather relevant information about peers. Some operations might not have this as an end goal, but can also gather important data.
|
||||
|
||||
In a libp2p node's life, it will discover peers through its discovery protocols. In a typical discovery protocol, addresses of the peer are discovered along with its peer id. Once this happens, the PeerStore should collect this information for future (or immediate) usage by other subsystems. When the information is stored, the PeerStore should inform interested parties of the peer discovered (`peer` event).
|
||||
|
||||
Taking into account a different scenario, a peer might perform/receive a dial request to/from a unkwown peer. In such a scenario, the PeerStore must store the peer's multiaddr once a connection is established.
|
||||
|
||||
When a connection is being upgraded, more precisely after its encryption, or even in a discovery protocol, a libp2p node can get to know other parties public keys. In this scenario, libp2p will add the peer's public key to its `KeyBook`.
|
||||
|
||||
After a connection is established with a peer, the Identify protocol will run automatically. A stream is created and peers exchange their information (Multiaddrs, running protocols and their public key). Once this information is obtained, it should be added to the PeerStore. In this specific case, as we are speaking to the source of truth, we should ensure the PeerStore is prioritizing these records. If the recorded `multiaddrs` or `protocols` have changed, interested parties must be informed via the `change:multiaddrs` or `change:protocols` events respectively.
|
||||
|
||||
In the background, the Identify Service is also waiting for protocol change notifications of peers via the IdentifyPush protocol. Peers may leverage the `identify-push` message to communicate protocol changes to all connected peers, so that their PeerStore can be updated with the updated protocols. As the `identify-push` also sends complete and updated information, the data in the PeerStore can be replaced.
|
||||
|
||||
(To consider: Should we not replace until we get to multiaddr confidence? we might loose true information as we will talk with older nodes on the network.)
|
||||
|
||||
While it is currently not supported in js-libp2p, future iterations may also support the [IdentifyDelta protocol](https://github.com/libp2p/specs/pull/176).
|
||||
|
||||
It is also possible to gather relevant information for peers from other protocols / subsystems. For instance, in `DHT` operations, nodes can exchange peer data as part of the `DHT` operation. In this case, we can learn additional information about a peer we already know. In this scenario the PeerStore should not replace the existing data it has, just add it.
|
||||
|
||||
## Data Consumption
|
||||
|
||||
When the PeerStore data is updated, this information might be important for different parties.
|
||||
|
||||
Every time a peer needs to dial another peer, it is essential that it knows the multiaddrs used by the peer, in order to perform a successful dial to it. The same is true for pinging a peer. While the `AddressBook` is going to keep its data updated, it will also emit `change:multiaddrs` events so that subsystems/users interested in knowing these changes can be notified instead of polling the `AddressBook`.
|
||||
|
||||
Everytime a peer starts/stops supporting a protocol, libp2p subsystems or users might need to act accordingly. `js-libp2p` registrar orchestrates known peers, established connections and protocol topologies. This way, once a protocol is supported for a peer, the topology of that protocol should be informed that a new peer may be used and the subsystem can decide if it should open a new stream with that peer or not. For these situations, the `ProtoBook` will emit `change:protocols` events whenever supported protocols of a peer change.
|
||||
|
||||
## PeerStore implementation
|
||||
|
||||
The PeerStore wraps four main components: `addressBook`, `keyBook`, `protocolBook` and `metadataBook`. Moreover, it provides a high level API for those components, as well as data events.
|
||||
|
||||
### Components
|
||||
|
||||
#### Address Book
|
||||
|
||||
The `addressBook` keeps the known multiaddrs of a peer. The multiaddrs of each peer may change over time and the Address Book must account for this.
|
||||
|
||||
`Map<string, Address>`
|
||||
|
||||
A `peerId.toB58String()` identifier mapping to a `Address` object, which should have the following structure:
|
||||
|
||||
```js
|
||||
{
|
||||
multiaddr: <Multiaddr>
|
||||
}
|
||||
```
|
||||
|
||||
#### Key Book
|
||||
|
||||
The `keyBook` tracks the public keys of the peers by keeping their [`PeerId`][peer-id].
|
||||
|
||||
`Map<string, PeerId`
|
||||
|
||||
A `peerId.toB58String()` identifier mapping to a `PeerId` of the peer. This instance contains the peer public key.
|
||||
|
||||
#### Protocol Book
|
||||
|
||||
The `protoBook` holds the identifiers of the protocols supported by each peer. The protocols supported by each peer are dynamic and will change over time.
|
||||
|
||||
`Map<string, Set<string>>`
|
||||
|
||||
A `peerId.toB58String()` identifier mapping to a `Set` of protocol identifier strings.
|
||||
|
||||
#### Metadata Book
|
||||
|
||||
**Not Yet Implemented**
|
||||
|
||||
### API
|
||||
|
||||
For the complete API documentation, you should check the [API.md](../../doc/API.md).
|
||||
|
||||
Access to its underlying books:
|
||||
|
||||
- `peerStore.addressBook.*`
|
||||
- `peerStore.keyBook.*`
|
||||
- `peerStore.protoBook.*`
|
||||
|
||||
### Events
|
||||
|
||||
- `peer` - emitted when a new peer is added.
|
||||
- `change:multiaadrs` - emitted when a known peer has a different set of multiaddrs.
|
||||
- `change:protocols` - emitted when a known peer supports a different set of protocols.
|
||||
|
||||
## Data Persistence
|
||||
|
||||
The data stored in the PeerStore can be persisted if configured appropriately. Keeping a record of the peers already discovered by the peer, as well as their known data aims to improve the efficiency of peers joining the network after being offline.
|
||||
|
||||
The libp2p node will need to receive a [datastore](https://github.com/ipfs/interface-datastore), in order to persist this data across restarts. A [datastore](https://github.com/ipfs/interface-datastore) stores its data in a key-value fashion. As a result, we need coherent keys so that we do not overwrite data.
|
||||
|
||||
The PeerStore should not continuously update the datastore whenever data is changed. Instead, it should only store new data after reaching a certain threshold of "dirty" peers, as well as when the node is stopped, in order to batch writes to the datastore.
|
||||
|
||||
The peer id will be appended to the datastore key for each data namespace. The namespaces were defined as follows:
|
||||
|
||||
**AddressBook**
|
||||
|
||||
All the known peer addresses are stored with a key pattern as follows:
|
||||
|
||||
`/peers/addrs/<b32 peer id no padding>`
|
||||
|
||||
**ProtoBook**
|
||||
|
||||
All the known peer protocols are stored with a key pattern as follows:
|
||||
|
||||
`/peers/protos/<b32 peer id no padding>`
|
||||
|
||||
**KeyBook**
|
||||
|
||||
All public keys are stored under the following pattern:
|
||||
|
||||
` /peers/keys/<b32 peer id no padding>`
|
||||
|
||||
**MetadataBook**
|
||||
|
||||
_NOT_YET_IMPLEMENTED_
|
||||
|
||||
Metadata is stored under the following key pattern:
|
||||
|
||||
`/peers/metadata/<b32 peer id no padding>/<key>`
|
||||
|
||||
## Future Considerations
|
||||
|
||||
- If multiaddr TTLs are added, the PeerStore may schedule jobs to delete all addresses that exceed the TTL to prevent AddressBook bloating
|
||||
- Further API methods will probably need to be added in the context of multiaddr validity and confidence.
|
||||
- When improving libp2p configuration for specific runtimes, we should take into account the PeerStore recommended datastore.
|
||||
- When improving libp2p configuration, we should think about a possible way of allowing the configuration of Bootstrap to be influenced by the persisted peers, as a way to decrease the load on Bootstrap nodes.
|
||||
|
||||
[peer-id]: https://github.com/libp2p/js-peer-id
|
||||
|
197
src/peer-store/address-book.js
Normal file
197
src/peer-store/address-book.js
Normal file
@@ -0,0 +1,197 @@
|
||||
'use strict'
|
||||
|
||||
const errcode = require('err-code')
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:peer-store:address-book')
|
||||
log.error = debug('libp2p:peer-store:address-book:error')
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const Book = require('./book')
|
||||
|
||||
const {
|
||||
codes: { ERR_INVALID_PARAMETERS }
|
||||
} = require('../errors')
|
||||
|
||||
/**
|
||||
* The AddressBook is responsible for keeping the known multiaddrs
|
||||
* of a peer.
|
||||
*/
|
||||
class AddressBook extends Book {
|
||||
/**
|
||||
* Address object
|
||||
* @typedef {Object} Address
|
||||
* @property {Multiaddr} multiaddr peer multiaddr.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {PeerStore} peerStore
|
||||
*/
|
||||
constructor (peerStore) {
|
||||
/**
|
||||
* PeerStore Event emitter, used by the AddressBook to emit:
|
||||
* "peer" - emitted when a peer is discovered by the node.
|
||||
* "change:multiaddrs" - emitted when the known multiaddrs of a peer change.
|
||||
*/
|
||||
super({
|
||||
peerStore,
|
||||
eventName: 'change:multiaddrs',
|
||||
eventProperty: 'multiaddrs',
|
||||
eventTransformer: (data) => data.map((address) => address.multiaddr)
|
||||
})
|
||||
|
||||
/**
|
||||
* Map known peers to their known Addresses.
|
||||
* @type {Map<string, Array<Address>>}
|
||||
*/
|
||||
this.data = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set known multiaddrs of a provided peer.
|
||||
* @override
|
||||
* @param {PeerId} peerId
|
||||
* @param {Array<Multiaddr>} multiaddrs
|
||||
* @returns {AddressBook}
|
||||
*/
|
||||
set (peerId, multiaddrs) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
log.error('peerId must be an instance of peer-id to store data')
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const addresses = this._toAddresses(multiaddrs)
|
||||
const id = peerId.toB58String()
|
||||
const rec = this.data.get(id)
|
||||
|
||||
// Not replace multiaddrs
|
||||
if (!addresses.length) {
|
||||
return this
|
||||
}
|
||||
|
||||
// Already knows the peer
|
||||
if (rec && rec.length === addresses.length) {
|
||||
const intersection = rec.filter((mi) => addresses.some((newMi) => mi.multiaddr.equals(newMi.multiaddr)))
|
||||
|
||||
// Are new addresses equal to the old ones?
|
||||
// If yes, no changes needed!
|
||||
if (intersection.length === rec.length) {
|
||||
log(`the addresses provided to store are equal to the already stored for ${id}`)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
this._setData(peerId, addresses)
|
||||
log(`stored provided multiaddrs for ${id}`)
|
||||
|
||||
// Notify the existance of a new peer
|
||||
if (!rec) {
|
||||
this._ps.emit('peer', peerId)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Add known addresses of a provided peer.
|
||||
* If the peer is not known, it is set with the given addresses.
|
||||
* @override
|
||||
* @param {PeerId} peerId
|
||||
* @param {Array<Multiaddr>} multiaddrs
|
||||
* @returns {AddressBook}
|
||||
*/
|
||||
add (peerId, multiaddrs) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
log.error('peerId must be an instance of peer-id to store data')
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const addresses = this._toAddresses(multiaddrs)
|
||||
const id = peerId.toB58String()
|
||||
const rec = this.data.get(id)
|
||||
|
||||
// Add recorded uniquely to the new array (Union)
|
||||
rec && rec.forEach((mi) => {
|
||||
if (!addresses.find(r => r.multiaddr.equals(mi.multiaddr))) {
|
||||
addresses.push(mi)
|
||||
}
|
||||
})
|
||||
|
||||
// If the recorded length is equal to the new after the unique union
|
||||
// The content is the same, no need to update.
|
||||
if (rec && rec.length === addresses.length) {
|
||||
log(`the addresses provided to store are already stored for ${id}`)
|
||||
return this
|
||||
}
|
||||
|
||||
this._setData(peerId, addresses)
|
||||
|
||||
log(`added provided multiaddrs for ${id}`)
|
||||
|
||||
// Notify the existance of a new peer
|
||||
if (!rec) {
|
||||
this._ps.emit('peer', peerId)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms received multiaddrs into Address.
|
||||
* @private
|
||||
* @param {Array<Multiaddr>} multiaddrs
|
||||
* @returns {Array<Address>}
|
||||
*/
|
||||
_toAddresses (multiaddrs) {
|
||||
if (!multiaddrs) {
|
||||
log.error('multiaddrs must be provided to store data')
|
||||
throw errcode(new Error('multiaddrs must be provided'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
// create Address for each address
|
||||
const addresses = []
|
||||
multiaddrs.forEach((addr) => {
|
||||
if (!multiaddr.isMultiaddr(addr)) {
|
||||
log.error(`multiaddr ${addr} must be an instance of multiaddr`)
|
||||
throw errcode(new Error(`multiaddr ${addr} must be an instance of multiaddr`), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
addresses.push({
|
||||
multiaddr: addr
|
||||
})
|
||||
})
|
||||
|
||||
return addresses
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the known multiaddrs for a given peer. All returned multiaddrs
|
||||
* will include the encapsulated `PeerId` of the peer.
|
||||
* @param {PeerId} peerId
|
||||
* @returns {Array<Multiaddr>}
|
||||
*/
|
||||
getMultiaddrsForPeer (peerId) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const record = this.data.get(peerId.toB58String())
|
||||
|
||||
if (!record) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return record.map((address) => {
|
||||
const multiaddr = address.multiaddr
|
||||
|
||||
const idString = multiaddr.getPeerId()
|
||||
if (idString && idString === peerId.toB58String()) return multiaddr
|
||||
|
||||
return multiaddr.encapsulate(`/p2p/${peerId.toB58String()}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AddressBook
|
113
src/peer-store/book.js
Normal file
113
src/peer-store/book.js
Normal file
@@ -0,0 +1,113 @@
|
||||
'use strict'
|
||||
|
||||
const errcode = require('err-code')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const {
|
||||
codes: { ERR_INVALID_PARAMETERS }
|
||||
} = require('../errors')
|
||||
|
||||
const passthrough = data => data
|
||||
|
||||
/**
|
||||
* The Book is the skeleton for the PeerStore books.
|
||||
*/
|
||||
class Book {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Object} properties
|
||||
* @param {PeerStore} properties.peerStore PeerStore instance.
|
||||
* @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 {function} [properties.eventTransformer] Transformer function of the provided data for being emitted.
|
||||
*/
|
||||
constructor ({ peerStore, eventName, eventProperty, eventTransformer = passthrough }) {
|
||||
this._ps = peerStore
|
||||
this.eventName = eventName
|
||||
this.eventProperty = eventProperty
|
||||
this.eventTransformer = eventTransformer
|
||||
|
||||
/**
|
||||
* Map known peers to their data.
|
||||
* @type {Map<string, Array<Data>}
|
||||
*/
|
||||
this.data = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set known data of a provided peer.
|
||||
* @param {PeerId} peerId
|
||||
* @param {Array<Data>|Data} data
|
||||
*/
|
||||
set (peerId, data) {
|
||||
throw errcode(new Error('set must be implemented by the subclass'), 'ERR_NOT_IMPLEMENTED')
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data into the datastructure, persistence and emit it using the provided transformers.
|
||||
* @private
|
||||
* @param {PeerId} peerId peerId of the data to store
|
||||
* @param {*} data data to store.
|
||||
* @param {Object} [options] storing options.
|
||||
* @param {boolean} [options.emit = true] emit the provided data.
|
||||
* @return {void}
|
||||
*/
|
||||
_setData (peerId, data, { emit = true } = {}) {
|
||||
const b58key = peerId.toB58String()
|
||||
|
||||
// Store data in memory
|
||||
this.data.set(b58key, data)
|
||||
|
||||
// Emit event
|
||||
emit && this._emit(peerId, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit data.
|
||||
* @private
|
||||
* @param {PeerId} peerId
|
||||
* @param {*} data
|
||||
*/
|
||||
_emit (peerId, data) {
|
||||
this._ps.emit(this.eventName, {
|
||||
peerId,
|
||||
[this.eventProperty]: this.eventTransformer(data)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the known data of a provided peer.
|
||||
* @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 rec = this.data.get(peerId.toB58String())
|
||||
|
||||
return rec ? [...rec] : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the provided peer from the book.
|
||||
* @param {PeerId} peerId
|
||||
* @returns {boolean}
|
||||
*/
|
||||
delete (peerId) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
if (!this.data.delete(peerId.toB58String())) {
|
||||
return false
|
||||
}
|
||||
|
||||
this._emit(peerId, [])
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Book
|
@@ -6,247 +6,133 @@ const log = debug('libp2p:peer-store')
|
||||
log.error = debug('libp2p:peer-store:error')
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
|
||||
const AddressBook = require('./address-book')
|
||||
const KeyBook = require('./key-book')
|
||||
const ProtoBook = require('./proto-book')
|
||||
|
||||
const {
|
||||
ERR_INVALID_PARAMETERS
|
||||
} = require('../errors')
|
||||
|
||||
/**
|
||||
* Responsible for managing known peers, as well as their addresses and metadata
|
||||
* @fires PeerStore#peer Emitted when a peer is connected to this node
|
||||
* @fires PeerStore#change:protocols
|
||||
* @fires PeerStore#change:multiaddrs
|
||||
* 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#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.
|
||||
*/
|
||||
class PeerStore extends EventEmitter {
|
||||
/**
|
||||
* Peer object
|
||||
* @typedef {Object} Peer
|
||||
* @property {PeerId} id peer's peer-id instance.
|
||||
* @property {Array<Address>} addresses peer's addresses containing its multiaddrs and metadata.
|
||||
* @property {Array<string>} protocols peer's supported protocols.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
constructor () {
|
||||
super()
|
||||
|
||||
/**
|
||||
* Map of peers
|
||||
*
|
||||
* @type {Map<string, PeerInfo>}
|
||||
* AddressBook containing a map of peerIdStr to Address.
|
||||
*/
|
||||
this.peers = new Map()
|
||||
this.addressBook = new AddressBook(this)
|
||||
|
||||
// TODO: Track ourselves. We should split `peerInfo` up into its pieces so we get better
|
||||
// control and observability. This will be the initial step for removing PeerInfo
|
||||
// https://github.com/libp2p/go-libp2p-core/blob/master/peerstore/peerstore.go
|
||||
// this.addressBook = new Map()
|
||||
// this.protoBook = new Map()
|
||||
/**
|
||||
* KeyBook containing a map of peerIdStr to their PeerId with public keys.
|
||||
*/
|
||||
this.keyBook = new KeyBook(this)
|
||||
|
||||
/**
|
||||
* ProtoBook containing a map of peerIdStr to supported protocols.
|
||||
*/
|
||||
this.protoBook = new ProtoBook(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the peerInfo of a new peer.
|
||||
* If already exist, its info is updated. If `silent` is set to
|
||||
* true, no 'peer' event will be emitted. This can be useful if you
|
||||
* are already in the process of dialing the peer. The peer is technically
|
||||
* known, but may not have been added to the PeerStore yet.
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @param {object} [options]
|
||||
* @param {boolean} [options.silent] (Default=false)
|
||||
* @return {PeerInfo}
|
||||
* Start the PeerStore.
|
||||
*/
|
||||
put (peerInfo, options = { silent: false }) {
|
||||
if (!PeerInfo.isPeerInfo(peerInfo)) {
|
||||
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
let peer
|
||||
// Already know the peer?
|
||||
if (this.has(peerInfo.id)) {
|
||||
peer = this.update(peerInfo)
|
||||
} else {
|
||||
peer = this.add(peerInfo)
|
||||
|
||||
// Emit the peer if silent = false
|
||||
!options.silent && this.emit('peer', peerInfo)
|
||||
}
|
||||
return peer
|
||||
}
|
||||
start () {}
|
||||
|
||||
/**
|
||||
* Add a new peer to the store.
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @return {PeerInfo}
|
||||
* Stop the PeerStore.
|
||||
*/
|
||||
add (peerInfo) {
|
||||
if (!PeerInfo.isPeerInfo(peerInfo)) {
|
||||
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
// Create new instance and add values to it
|
||||
const newPeerInfo = new PeerInfo(peerInfo.id)
|
||||
|
||||
peerInfo.multiaddrs.forEach((ma) => newPeerInfo.multiaddrs.add(ma))
|
||||
peerInfo.protocols.forEach((p) => newPeerInfo.protocols.add(p))
|
||||
|
||||
const connectedMa = peerInfo.isConnected()
|
||||
connectedMa && newPeerInfo.connect(connectedMa)
|
||||
|
||||
const peerProxy = new Proxy(newPeerInfo, {
|
||||
set: (obj, prop, value) => {
|
||||
if (prop === 'multiaddrs') {
|
||||
this.emit('change:multiaddrs', {
|
||||
peerInfo: obj,
|
||||
multiaddrs: value.toArray()
|
||||
})
|
||||
} else if (prop === 'protocols') {
|
||||
this.emit('change:protocols', {
|
||||
peerInfo: obj,
|
||||
protocols: Array.from(value)
|
||||
})
|
||||
}
|
||||
return Reflect.set(...arguments)
|
||||
}
|
||||
})
|
||||
|
||||
this.peers.set(peerInfo.id.toB58String(), peerProxy)
|
||||
return peerProxy
|
||||
}
|
||||
stop () {}
|
||||
|
||||
/**
|
||||
* Updates an already known peer.
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @return {PeerInfo}
|
||||
* Get all the stored information of every peer.
|
||||
* @returns {Map<string, Peer>}
|
||||
*/
|
||||
update (peerInfo) {
|
||||
if (!PeerInfo.isPeerInfo(peerInfo)) {
|
||||
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
get peers () {
|
||||
const peersData = new Map()
|
||||
|
||||
const id = peerInfo.id.toB58String()
|
||||
const recorded = this.peers.get(id)
|
||||
|
||||
// pass active connection state
|
||||
const ma = peerInfo.isConnected()
|
||||
if (ma) {
|
||||
recorded.connect(ma)
|
||||
}
|
||||
|
||||
// Verify new multiaddrs
|
||||
// TODO: better track added and removed multiaddrs
|
||||
const multiaddrsIntersection = [
|
||||
...recorded.multiaddrs.toArray()
|
||||
].filter((m) => peerInfo.multiaddrs.has(m))
|
||||
|
||||
if (multiaddrsIntersection.length !== peerInfo.multiaddrs.size ||
|
||||
multiaddrsIntersection.length !== recorded.multiaddrs.size) {
|
||||
for (const ma of peerInfo.multiaddrs.toArray()) {
|
||||
recorded.multiaddrs.add(ma)
|
||||
}
|
||||
|
||||
this.emit('change:multiaddrs', {
|
||||
peerInfo: recorded,
|
||||
multiaddrs: recorded.multiaddrs.toArray()
|
||||
// AddressBook
|
||||
for (const [idStr, addresses] of this.addressBook.data.entries()) {
|
||||
const id = this.keyBook.data.get(idStr) || PeerId.createFromCID(idStr)
|
||||
peersData.set(idStr, {
|
||||
id,
|
||||
addresses,
|
||||
protocols: this.protoBook.get(id) || []
|
||||
})
|
||||
}
|
||||
|
||||
// Update protocols
|
||||
// TODO: better track added and removed protocols
|
||||
const protocolsIntersection = new Set(
|
||||
[...recorded.protocols].filter((p) => peerInfo.protocols.has(p))
|
||||
)
|
||||
// ProtoBook
|
||||
for (const [idStr, protocols] of this.protoBook.data.entries()) {
|
||||
const pData = peersData.get(idStr)
|
||||
const id = this.keyBook.data.get(idStr) || PeerId.createFromCID(idStr)
|
||||
|
||||
if (protocolsIntersection.size !== peerInfo.protocols.size ||
|
||||
protocolsIntersection.size !== recorded.protocols.size) {
|
||||
for (const protocol of peerInfo.protocols) {
|
||||
recorded.protocols.add(protocol)
|
||||
if (!pData) {
|
||||
peersData.set(idStr, {
|
||||
id,
|
||||
addresses: [],
|
||||
protocols: Array.from(protocols)
|
||||
})
|
||||
}
|
||||
|
||||
this.emit('change:protocols', {
|
||||
peerInfo: recorded,
|
||||
protocols: Array.from(recorded.protocols)
|
||||
})
|
||||
}
|
||||
|
||||
// Add the public key if missing
|
||||
if (!recorded.id.pubKey && peerInfo.id.pubKey) {
|
||||
recorded.id.pubKey = peerInfo.id.pubKey
|
||||
}
|
||||
|
||||
return recorded
|
||||
return peersData
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the info to the given id.
|
||||
* @param {PeerId|string} peerId b58str id
|
||||
* @returns {PeerInfo}
|
||||
*/
|
||||
get (peerId) {
|
||||
// TODO: deprecate this and just accept `PeerId` instances
|
||||
if (PeerId.isPeerId(peerId)) {
|
||||
peerId = peerId.toB58String()
|
||||
}
|
||||
|
||||
return this.peers.get(peerId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the info to the given id.
|
||||
* @param {PeerId|string} peerId b58str id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has (peerId) {
|
||||
// TODO: deprecate this and just accept `PeerId` instances
|
||||
if (PeerId.isPeerId(peerId)) {
|
||||
peerId = peerId.toB58String()
|
||||
}
|
||||
|
||||
return this.peers.has(peerId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the Peer with the matching `peerId` from the PeerStore
|
||||
* @param {PeerId|string} peerId b58str id
|
||||
* Delete the information of the given peer in every book.
|
||||
* @param {PeerId} peerId
|
||||
* @returns {boolean} true if found and removed
|
||||
*/
|
||||
remove (peerId) {
|
||||
// TODO: deprecate this and just accept `PeerId` instances
|
||||
if (PeerId.isPeerId(peerId)) {
|
||||
peerId = peerId.toB58String()
|
||||
}
|
||||
delete (peerId) {
|
||||
const addressesDeleted = this.addressBook.delete(peerId)
|
||||
const keyDeleted = this.keyBook.delete(peerId)
|
||||
const protocolsDeleted = this.protoBook.delete(peerId)
|
||||
|
||||
return this.peers.delete(peerId)
|
||||
return addressesDeleted || keyDeleted || protocolsDeleted
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely replaces the existing peers metadata with the given `peerInfo`
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @returns {void}
|
||||
* Get the stored information of a given peer.
|
||||
* @param {PeerId} peerId
|
||||
* @returns {Peer}
|
||||
*/
|
||||
replace (peerInfo) {
|
||||
if (!PeerInfo.isPeerInfo(peerInfo)) {
|
||||
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
|
||||
get (peerId) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
this.remove(peerInfo.id.toB58String())
|
||||
this.add(peerInfo)
|
||||
const id = this.keyBook.data.get(peerId.toB58String())
|
||||
const addresses = this.addressBook.get(peerId)
|
||||
const protocols = this.protoBook.get(peerId)
|
||||
|
||||
// This should be cleaned up in PeerStore v2
|
||||
this.emit('change:multiaddrs', {
|
||||
peerInfo,
|
||||
multiaddrs: peerInfo.multiaddrs.toArray()
|
||||
})
|
||||
this.emit('change:protocols', {
|
||||
peerInfo,
|
||||
protocols: Array.from(peerInfo.protocols)
|
||||
})
|
||||
}
|
||||
if (!addresses && !protocols) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the known multiaddrs for a given `PeerInfo`. All returned multiaddrs
|
||||
* will include the encapsulated `PeerId` of the peer.
|
||||
* @param {PeerInfo} peer
|
||||
* @returns {Array<Multiaddr>}
|
||||
*/
|
||||
multiaddrsForPeer (peer) {
|
||||
return this.put(peer, true).multiaddrs.toArray().map(addr => {
|
||||
const idString = addr.getPeerId()
|
||||
if (idString && idString === peer.id.toB58String()) return addr
|
||||
return addr.encapsulate(`/p2p/${peer.id.toB58String()}`)
|
||||
})
|
||||
return {
|
||||
id: id || peerId,
|
||||
addresses: addresses || [],
|
||||
protocols: protocols || []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
85
src/peer-store/key-book.js
Normal file
85
src/peer-store/key-book.js
Normal file
@@ -0,0 +1,85 @@
|
||||
'use strict'
|
||||
|
||||
const errcode = require('err-code')
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:peer-store:key-book')
|
||||
log.error = debug('libp2p:peer-store:key-book:error')
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const Book = require('./book')
|
||||
|
||||
const {
|
||||
codes: { ERR_INVALID_PARAMETERS }
|
||||
} = require('../errors')
|
||||
|
||||
/**
|
||||
* The KeyBook is responsible for keeping the known public keys of a peer.
|
||||
*/
|
||||
class KeyBook extends Book {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {PeerStore} peerStore
|
||||
*/
|
||||
constructor (peerStore) {
|
||||
super({
|
||||
peerStore,
|
||||
eventName: 'change:pubkey',
|
||||
eventProperty: 'pubkey',
|
||||
eventTransformer: (data) => data.pubKey
|
||||
})
|
||||
|
||||
/**
|
||||
* Map known peers to their known Public Key.
|
||||
* @type {Map<string, PeerId>}
|
||||
*/
|
||||
this.data = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Peer public key.
|
||||
* @override
|
||||
* @param {PeerId} peerId
|
||||
* @param {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey} publicKey
|
||||
* @return {KeyBook}
|
||||
*/
|
||||
set (peerId, publicKey) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
log.error('peerId must be an instance of peer-id to store data')
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const id = peerId.toB58String()
|
||||
const recPeerId = this.data.get(id)
|
||||
|
||||
// If no record available, and this is valid
|
||||
if (!recPeerId && publicKey) {
|
||||
// This might be unecessary, but we want to store the PeerId
|
||||
// to avoid an async operation when reconstructing the PeerId
|
||||
peerId.pubKey = publicKey
|
||||
|
||||
this._setData(peerId, peerId)
|
||||
log(`stored provided public key for ${id}`)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Public key of the given PeerId, if stored.
|
||||
* @override
|
||||
* @param {PeerId} peerId
|
||||
* @return {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey}
|
||||
*/
|
||||
get (peerId) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const rec = this.data.get(peerId.toB58String())
|
||||
|
||||
return rec ? rec.pubKey : undefined
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = KeyBook
|
12
src/peer-store/persistent/consts.js
Normal file
12
src/peer-store/persistent/consts.js
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
module.exports.NAMESPACE_COMMON = '/peers/'
|
||||
|
||||
// /peers/protos/<b32 peer id no padding>
|
||||
module.exports.NAMESPACE_ADDRESS = '/peers/addrs/'
|
||||
|
||||
// /peers/keys/<b32 peer id no padding>
|
||||
module.exports.NAMESPACE_KEYS = '/peers/keys/'
|
||||
|
||||
// /peers/addrs/<b32 peer id no padding>
|
||||
module.exports.NAMESPACE_PROTOCOL = '/peers/protos/'
|
264
src/peer-store/persistent/index.js
Normal file
264
src/peer-store/persistent/index.js
Normal file
@@ -0,0 +1,264 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:persistent-peer-store')
|
||||
log.error = debug('libp2p:persistent-peer-store:error')
|
||||
|
||||
const { Key } = require('interface-datastore')
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const PeerStore = require('..')
|
||||
|
||||
const {
|
||||
NAMESPACE_ADDRESS,
|
||||
NAMESPACE_COMMON,
|
||||
NAMESPACE_KEYS,
|
||||
NAMESPACE_PROTOCOL
|
||||
} = require('./consts')
|
||||
|
||||
const Addresses = require('./pb/address-book.proto')
|
||||
const Protocols = require('./pb/proto-book.proto')
|
||||
|
||||
/**
|
||||
* Responsible for managing the persistence of data in the PeerStore.
|
||||
*/
|
||||
class PersistentPeerStore extends PeerStore {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Object} properties
|
||||
* @param {Datastore} properties.datastore Datastore to persist data.
|
||||
* @param {number} [properties.threshold = 5] Number of dirty peers allowed before commit data.
|
||||
*/
|
||||
constructor ({ datastore, threshold = 5 }) {
|
||||
super()
|
||||
|
||||
/**
|
||||
* Backend datastore used to persist data.
|
||||
*/
|
||||
this._datastore = datastore
|
||||
|
||||
/**
|
||||
* Peers modified after the latest data persisted.
|
||||
*/
|
||||
this._dirtyPeers = new Set()
|
||||
|
||||
this.threshold = threshold
|
||||
this._addDirtyPeer = this._addDirtyPeer.bind(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Persistent PeerStore.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async start () {
|
||||
log('PeerStore is starting')
|
||||
|
||||
// Handlers for dirty peers
|
||||
this.on('change:protocols', this._addDirtyPeer)
|
||||
this.on('change:multiaddrs', this._addDirtyPeer)
|
||||
this.on('change:pubkey', this._addDirtyPeer)
|
||||
|
||||
// Load data
|
||||
for await (const entry of this._datastore.query({ prefix: NAMESPACE_COMMON })) {
|
||||
await this._processDatastoreEntry(entry)
|
||||
}
|
||||
|
||||
log('PeerStore started')
|
||||
}
|
||||
|
||||
async stop () {
|
||||
log('PeerStore is stopping')
|
||||
this.removeAllListeners()
|
||||
await this._commitData()
|
||||
log('PeerStore stopped')
|
||||
}
|
||||
|
||||
/**
|
||||
* Add modified peer to the dirty set
|
||||
* @private
|
||||
* @param {Object} params
|
||||
* @param {PeerId} params.peerId
|
||||
*/
|
||||
_addDirtyPeer ({ peerId }) {
|
||||
const peerIdstr = peerId.toB58String()
|
||||
|
||||
log('add dirty peer', peerIdstr)
|
||||
this._dirtyPeers.add(peerIdstr)
|
||||
|
||||
if (this._dirtyPeers.size >= this.threshold) {
|
||||
// Commit current data
|
||||
this._commitData()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the peers current data to a datastore batch and commit it.
|
||||
* @private
|
||||
* @param {Array<string>} peers
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async _commitData () {
|
||||
const commitPeers = Array.from(this._dirtyPeers)
|
||||
|
||||
if (!commitPeers.length) {
|
||||
return
|
||||
}
|
||||
|
||||
// Clear Dirty Peers set
|
||||
this._dirtyPeers.clear()
|
||||
|
||||
log('create batch commit')
|
||||
const batch = this._datastore.batch()
|
||||
for (const peerIdStr of commitPeers) {
|
||||
// PeerId
|
||||
const peerId = this.keyBook.data.get(peerIdStr) || PeerId.createFromCID(peerIdStr)
|
||||
|
||||
// Address Book
|
||||
this._batchAddressBook(peerId, batch)
|
||||
|
||||
// Key Book
|
||||
this._batchKeyBook(peerId, batch)
|
||||
|
||||
// Proto Book
|
||||
this._batchProtoBook(peerId, batch)
|
||||
}
|
||||
|
||||
await batch.commit()
|
||||
log('batch committed')
|
||||
}
|
||||
|
||||
/**
|
||||
* Add address book data of the peer to the batch.
|
||||
* @private
|
||||
* @param {PeerId} peerId
|
||||
* @param {Object} batch
|
||||
*/
|
||||
_batchAddressBook (peerId, batch) {
|
||||
const b32key = peerId.toString()
|
||||
const key = new Key(`${NAMESPACE_ADDRESS}${b32key}`)
|
||||
|
||||
const addresses = this.addressBook.get(peerId)
|
||||
|
||||
try {
|
||||
// Deleted from the book
|
||||
if (!addresses) {
|
||||
batch.delete(key)
|
||||
return
|
||||
}
|
||||
|
||||
const encodedData = Addresses.encode({
|
||||
addrs: addresses.map((address) => ({
|
||||
multiaddr: address.multiaddr.buffer
|
||||
}))
|
||||
})
|
||||
|
||||
batch.put(key, encodedData)
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Key book data of the peer to the batch.
|
||||
* @private
|
||||
* @param {PeerId} peerId
|
||||
* @param {Object} batch
|
||||
*/
|
||||
_batchKeyBook (peerId, batch) {
|
||||
const b32key = peerId.toString()
|
||||
const key = new Key(`${NAMESPACE_KEYS}${b32key}`)
|
||||
|
||||
try {
|
||||
// Deleted from the book
|
||||
if (!peerId.pubKey) {
|
||||
batch.delete(key)
|
||||
return
|
||||
}
|
||||
|
||||
const encodedData = peerId.marshalPubKey()
|
||||
|
||||
batch.put(key, encodedData)
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add proto book data of the peer to the batch.
|
||||
* @private
|
||||
* @param {PeerId} peerId
|
||||
* @param {Object} batch
|
||||
*/
|
||||
_batchProtoBook (peerId, batch) {
|
||||
const b32key = peerId.toString()
|
||||
const key = new Key(`${NAMESPACE_PROTOCOL}${b32key}`)
|
||||
|
||||
const protocols = this.protoBook.get(peerId)
|
||||
|
||||
try {
|
||||
// Deleted from the book
|
||||
if (!protocols) {
|
||||
batch.delete(key)
|
||||
return
|
||||
}
|
||||
|
||||
const encodedData = Protocols.encode({ protocols })
|
||||
|
||||
batch.put(key, encodedData)
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process datastore entry and add its data to the correct book.
|
||||
* @private
|
||||
* @param {Object} params
|
||||
* @param {Key} params.key datastore key
|
||||
* @param {Buffer} params.value datastore value stored
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async _processDatastoreEntry ({ key, value }) {
|
||||
try {
|
||||
const keyParts = key.toString().split('/')
|
||||
const peerId = PeerId.createFromCID(keyParts[3])
|
||||
|
||||
let decoded
|
||||
switch (keyParts[2]) {
|
||||
case 'addrs':
|
||||
decoded = Addresses.decode(value)
|
||||
|
||||
this.addressBook._setData(
|
||||
peerId,
|
||||
decoded.addrs.map((address) => ({
|
||||
multiaddr: multiaddr(address.multiaddr)
|
||||
})),
|
||||
{ emit: false })
|
||||
break
|
||||
case 'keys':
|
||||
decoded = await PeerId.createFromPubKey(value)
|
||||
|
||||
this.keyBook._setData(
|
||||
decoded,
|
||||
decoded,
|
||||
{ emit: false })
|
||||
break
|
||||
case 'protos':
|
||||
decoded = Protocols.decode(value)
|
||||
|
||||
this.protoBook._setData(
|
||||
peerId,
|
||||
new Set(decoded.protocols),
|
||||
{ emit: false })
|
||||
break
|
||||
default:
|
||||
log('invalid data persisted for: ', key.toString())
|
||||
}
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PersistentPeerStore
|
15
src/peer-store/persistent/pb/address-book.proto.js
Normal file
15
src/peer-store/persistent/pb/address-book.proto.js
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
const protons = require('protons')
|
||||
|
||||
const message = `
|
||||
message Addresses {
|
||||
message Address {
|
||||
required bytes multiaddr = 1;
|
||||
}
|
||||
|
||||
repeated Address addrs = 1;
|
||||
}
|
||||
`
|
||||
|
||||
module.exports = protons(message).Addresses
|
12
src/peer-store/persistent/pb/proto-book.proto.js
Normal file
12
src/peer-store/persistent/pb/proto-book.proto.js
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
const protons = require('protons')
|
||||
|
||||
/* eslint-disable no-tabs */
|
||||
const message = `
|
||||
message Protocols {
|
||||
repeated string protocols = 1;
|
||||
}
|
||||
`
|
||||
|
||||
module.exports = protons(message).Protocols
|
121
src/peer-store/proto-book.js
Normal file
121
src/peer-store/proto-book.js
Normal file
@@ -0,0 +1,121 @@
|
||||
'use strict'
|
||||
|
||||
const errcode = require('err-code')
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:peer-store:proto-book')
|
||||
log.error = debug('libp2p:peer-store:proto-book:error')
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const Book = require('./book')
|
||||
|
||||
const {
|
||||
codes: { ERR_INVALID_PARAMETERS }
|
||||
} = require('../errors')
|
||||
|
||||
/**
|
||||
* The ProtoBook is responsible for keeping the known supported
|
||||
* protocols of a peer.
|
||||
* @fires ProtoBook#change:protocols
|
||||
*/
|
||||
class ProtoBook extends Book {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {PeerStore} peerStore
|
||||
*/
|
||||
constructor (peerStore) {
|
||||
/**
|
||||
* PeerStore Event emitter, used by the ProtoBook to emit:
|
||||
* "change:protocols" - emitted when the known protocols of a peer change.
|
||||
*/
|
||||
super({
|
||||
peerStore,
|
||||
eventName: 'change:protocols',
|
||||
eventProperty: 'protocols',
|
||||
eventTransformer: (data) => Array.from(data)
|
||||
})
|
||||
|
||||
/**
|
||||
* Map known peers to their known protocols.
|
||||
* @type {Map<string, Set<string>>}
|
||||
*/
|
||||
this.data = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set known protocols of a provided peer.
|
||||
* If the peer was not known before, it will be added.
|
||||
* @override
|
||||
* @param {PeerId} peerId
|
||||
* @param {Array<string>} protocols
|
||||
* @returns {ProtoBook}
|
||||
*/
|
||||
set (peerId, protocols) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
log.error('peerId must be an instance of peer-id to store data')
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
if (!protocols) {
|
||||
log.error('protocols must be provided to store data')
|
||||
throw errcode(new Error('protocols must be provided'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const id = peerId.toB58String()
|
||||
const recSet = this.data.get(id)
|
||||
const newSet = new Set(protocols)
|
||||
|
||||
const isSetEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value))
|
||||
|
||||
// Already knows the peer and the recorded protocols are the same?
|
||||
// If yes, no changes needed!
|
||||
if (recSet && isSetEqual(recSet, newSet)) {
|
||||
log(`the protocols provided to store are equal to the already stored for ${id}`)
|
||||
return this
|
||||
}
|
||||
|
||||
this._setData(peerId, newSet)
|
||||
log(`stored provided protocols for ${id}`)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds known protocols of a provided peer.
|
||||
* If the peer was not known before, it will be added.
|
||||
* @override
|
||||
* @param {PeerId} peerId
|
||||
* @param {Array<string>} protocols
|
||||
* @returns {ProtoBook}
|
||||
*/
|
||||
add (peerId, protocols) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
log.error('peerId must be an instance of peer-id to store data')
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
if (!protocols) {
|
||||
log.error('protocols must be provided to store data')
|
||||
throw errcode(new Error('protocols must be provided'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const id = peerId.toB58String()
|
||||
const recSet = this.data.get(id) || new Set()
|
||||
const newSet = new Set([...recSet, ...protocols]) // Set Union
|
||||
|
||||
// Any new protocol added?
|
||||
if (recSet.size === newSet.size) {
|
||||
log(`the protocols provided to store are already stored for ${id}`)
|
||||
return this
|
||||
}
|
||||
|
||||
protocols = [...newSet]
|
||||
|
||||
this._setData(peerId, newSet)
|
||||
log(`added provided protocols for ${id}`)
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProtoBook
|
@@ -15,11 +15,11 @@ const { PROTOCOL, PING_LENGTH } = require('./constants')
|
||||
/**
|
||||
* Ping a given peer and wait for its response, getting the operation latency.
|
||||
* @param {Libp2p} node
|
||||
* @param {PeerInfo} peer
|
||||
* @param {PeerId} peer
|
||||
* @returns {Promise<Number>}
|
||||
*/
|
||||
async function ping (node, peer) {
|
||||
log('dialing %s to %s', PROTOCOL, peer.id.toB58String())
|
||||
log('dialing %s to %s', PROTOCOL, peer.toB58String())
|
||||
|
||||
const { stream } = await node.dialProtocol(peer, PROTOCOL)
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const debug = require('debug')
|
||||
const Errors = require('./errors')
|
||||
const xsalsa20 = require('xsalsa20')
|
||||
|
@@ -1,6 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('crypto')
|
||||
const crypto = require('libp2p-crypto')
|
||||
const KEY_LENGTH = 32
|
||||
|
||||
/**
|
||||
|
@@ -1,10 +1,11 @@
|
||||
'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.peerInfo, node.registrar, config)
|
||||
const pubsub = new Pubsub(node.peerId, node.registrar, config)
|
||||
|
||||
return {
|
||||
/**
|
||||
|
114
src/registrar.js
114
src/registrar.js
@@ -9,8 +9,6 @@ const {
|
||||
ERR_INVALID_PARAMETERS
|
||||
} = require('./errors')
|
||||
const Topology = require('libp2p-interfaces/src/topology')
|
||||
const { Connection } = require('libp2p-interfaces/src/connection')
|
||||
const PeerInfo = require('peer-info')
|
||||
|
||||
/**
|
||||
* Responsible for notifying registered protocols of events in the network.
|
||||
@@ -19,17 +17,14 @@ class Registrar {
|
||||
/**
|
||||
* @param {Object} props
|
||||
* @param {PeerStore} props.peerStore
|
||||
* @param {connectionManager} props.connectionManager
|
||||
* @constructor
|
||||
*/
|
||||
constructor ({ peerStore }) {
|
||||
constructor ({ peerStore, connectionManager }) {
|
||||
// Used on topology to listen for protocol changes
|
||||
this.peerStore = peerStore
|
||||
|
||||
/**
|
||||
* Map of connections per peer
|
||||
* TODO: this should be handled by connectionManager
|
||||
* @type {Map<string, Array<conn>>}
|
||||
*/
|
||||
this.connections = new Map()
|
||||
this.connectionManager = connectionManager
|
||||
|
||||
/**
|
||||
* Map of topologies
|
||||
@@ -39,6 +34,9 @@ class Registrar {
|
||||
this.topologies = new Map()
|
||||
|
||||
this._handle = undefined
|
||||
|
||||
this._onDisconnect = this._onDisconnect.bind(this)
|
||||
this.connectionManager.on('peer:disconnect', this._onDisconnect)
|
||||
}
|
||||
|
||||
get handle () {
|
||||
@@ -49,93 +47,13 @@ class Registrar {
|
||||
this._handle = handle
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the registrar
|
||||
* @async
|
||||
*/
|
||||
async close () {
|
||||
// Close all connections we're tracking
|
||||
const tasks = []
|
||||
for (const connectionList of this.connections.values()) {
|
||||
for (const connection of connectionList) {
|
||||
tasks.push(connection.close())
|
||||
}
|
||||
}
|
||||
|
||||
await tasks
|
||||
this.connections.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new connected peer to the record
|
||||
* TODO: this should live in the ConnectionManager
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @param {Connection} conn
|
||||
* @returns {void}
|
||||
*/
|
||||
onConnect (peerInfo, conn) {
|
||||
if (!PeerInfo.isPeerInfo(peerInfo)) {
|
||||
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
if (!Connection.isConnection(conn)) {
|
||||
throw errcode(new Error('conn must be an instance of interface-connection'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const id = peerInfo.id.toB58String()
|
||||
const storedConn = this.connections.get(id)
|
||||
|
||||
if (storedConn) {
|
||||
storedConn.push(conn)
|
||||
} else {
|
||||
this.connections.set(id, [conn])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a disconnected peer from the record
|
||||
* TODO: this should live in the ConnectionManager
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @param {Connection} connection
|
||||
* @param {Error} [error]
|
||||
* @returns {void}
|
||||
*/
|
||||
onDisconnect (peerInfo, connection, error) {
|
||||
if (!PeerInfo.isPeerInfo(peerInfo)) {
|
||||
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const id = peerInfo.id.toB58String()
|
||||
let storedConn = this.connections.get(id)
|
||||
|
||||
if (storedConn && storedConn.length > 1) {
|
||||
storedConn = storedConn.filter((conn) => conn.id !== connection.id)
|
||||
this.connections.set(id, storedConn)
|
||||
} else if (storedConn) {
|
||||
for (const [, topology] of this.topologies) {
|
||||
topology.disconnect(peerInfo, error)
|
||||
}
|
||||
|
||||
this.connections.delete(peerInfo.id.toB58String())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a connection with a peer.
|
||||
* @param {PeerInfo} peerInfo
|
||||
* @param {PeerId} peerId
|
||||
* @returns {Connection}
|
||||
*/
|
||||
getConnection (peerInfo) {
|
||||
if (!PeerInfo.isPeerInfo(peerInfo)) {
|
||||
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const connections = this.connections.get(peerInfo.id.toB58String())
|
||||
// Return the first, open connection
|
||||
if (connections) {
|
||||
return connections.find(connection => connection.stat.status === 'open')
|
||||
}
|
||||
return null
|
||||
getConnection (peerId) {
|
||||
return this.connectionManager.get(peerId)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,6 +85,18 @@ class Registrar {
|
||||
unregister (id) {
|
||||
return this.topologies.delete(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a disconnected peer from the record
|
||||
* @param {Connection} connection
|
||||
* @param {Error} [error]
|
||||
* @returns {void}
|
||||
*/
|
||||
_onDisconnect (connection, error) {
|
||||
for (const [, topology] of this.topologies) {
|
||||
topology.disconnect(connection.remotePeer, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Registrar
|
||||
|
@@ -127,11 +127,12 @@ class TransportManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts listeners for each given Multiaddr.
|
||||
* Starts listeners for each listen Multiaddr.
|
||||
* @async
|
||||
* @param {Multiaddr[]} addrs
|
||||
*/
|
||||
async listen (addrs) {
|
||||
async listen () {
|
||||
const addrs = this.libp2p.addressManager.getListenAddrs()
|
||||
|
||||
if (addrs.length === 0) {
|
||||
log('no addresses were provided for listening, this node is dial only')
|
||||
return
|
||||
|
@@ -317,7 +317,7 @@ class Upgrader {
|
||||
* Attempts to encrypt the incoming `connection` with the provided `cryptos`.
|
||||
* @private
|
||||
* @async
|
||||
* @param {PeerId} localPeer The initiators PeerInfo
|
||||
* @param {PeerId} localPeer The initiators PeerId
|
||||
* @param {*} connection
|
||||
* @param {Map<string, Crypto>} cryptos
|
||||
* @returns {CryptoResult} An encrypted connection, remote peer `PeerId` and the protocol of the `Crypto` used
|
||||
@@ -346,7 +346,7 @@ class Upgrader {
|
||||
* The first `Crypto` module to succeed will be used
|
||||
* @private
|
||||
* @async
|
||||
* @param {PeerId} localPeer The initiators PeerInfo
|
||||
* @param {PeerId} localPeer The initiators PeerId
|
||||
* @param {*} connection
|
||||
* @param {PeerId} remotePeerId
|
||||
* @param {Map<string, Crypto>} cryptos
|
||||
|
93
test/addresses/address-manager.spec.js
Normal file
93
test/addresses/address-manager.spec.js
Normal file
@@ -0,0 +1,93 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
chai.use(require('chai-as-promised'))
|
||||
const { expect } = chai
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
|
||||
const AddressManager = require('../../src/address-manager')
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
|
||||
const listenAddresses = ['/ip4/127.0.0.1/tcp/15006/ws', '/ip4/127.0.0.1/tcp/15008/ws']
|
||||
const announceAddreses = ['/dns4/peer.io']
|
||||
|
||||
describe('Address Manager', () => {
|
||||
it('should not need any addresses', () => {
|
||||
const am = new AddressManager()
|
||||
|
||||
expect(am.listen.size).to.equal(0)
|
||||
expect(am.announce.size).to.equal(0)
|
||||
expect(am.noAnnounce.size).to.equal(0)
|
||||
})
|
||||
|
||||
it('should return listen multiaddrs on get', () => {
|
||||
const am = new AddressManager({
|
||||
listen: listenAddresses
|
||||
})
|
||||
|
||||
expect(am.listen.size).to.equal(listenAddresses.length)
|
||||
expect(am.announce.size).to.equal(0)
|
||||
expect(am.noAnnounce.size).to.equal(0)
|
||||
|
||||
const listenMultiaddrs = am.getListenAddrs()
|
||||
expect(listenMultiaddrs.length).to.equal(2)
|
||||
expect(listenMultiaddrs[0].equals(multiaddr(listenAddresses[0]))).to.equal(true)
|
||||
expect(listenMultiaddrs[1].equals(multiaddr(listenAddresses[1]))).to.equal(true)
|
||||
})
|
||||
|
||||
it('should return announce multiaddrs on get', () => {
|
||||
const am = new AddressManager({
|
||||
listen: listenAddresses,
|
||||
announce: announceAddreses
|
||||
})
|
||||
|
||||
expect(am.listen.size).to.equal(listenAddresses.length)
|
||||
expect(am.announce.size).to.equal(announceAddreses.length)
|
||||
expect(am.noAnnounce.size).to.equal(0)
|
||||
|
||||
const announceMultiaddrs = am.getAnnounceAddrs()
|
||||
expect(announceMultiaddrs.length).to.equal(1)
|
||||
expect(announceMultiaddrs[0].equals(multiaddr(announceAddreses[0]))).to.equal(true)
|
||||
})
|
||||
|
||||
it('should return noAnnounce multiaddrs on get', () => {
|
||||
const am = new AddressManager({
|
||||
listen: listenAddresses,
|
||||
noAnnounce: listenAddresses
|
||||
})
|
||||
|
||||
expect(am.listen.size).to.equal(listenAddresses.length)
|
||||
expect(am.announce.size).to.equal(0)
|
||||
expect(am.noAnnounce.size).to.equal(listenAddresses.length)
|
||||
|
||||
const noAnnounceMultiaddrs = am.getNoAnnounceAddrs()
|
||||
expect(noAnnounceMultiaddrs.length).to.equal(2)
|
||||
expect(noAnnounceMultiaddrs[0].equals(multiaddr(listenAddresses[0]))).to.equal(true)
|
||||
expect(noAnnounceMultiaddrs[1].equals(multiaddr(listenAddresses[1]))).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('libp2p.addressManager', () => {
|
||||
let libp2p
|
||||
afterEach(() => libp2p && libp2p.stop())
|
||||
|
||||
it('should populate the AddressManager from the config', async () => {
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
started: false,
|
||||
config: {
|
||||
addresses: {
|
||||
listen: listenAddresses,
|
||||
announce: announceAddreses,
|
||||
noAnnounce: listenAddresses
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
expect(libp2p.addressManager.listen.size).to.equal(listenAddresses.length)
|
||||
expect(libp2p.addressManager.announce.size).to.equal(announceAddreses.length)
|
||||
expect(libp2p.addressManager.noAnnounce.size).to.equal(listenAddresses.length)
|
||||
})
|
||||
})
|
126
test/addresses/addresses.node.js
Normal file
126
test/addresses/addresses.node.js
Normal file
@@ -0,0 +1,126 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
chai.use(require('chai-as-promised'))
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const { AddressesOptions } = require('./utils')
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
|
||||
const listenAddresses = ['/ip4/127.0.0.1/tcp/0', '/ip4/127.0.0.1/tcp/8000/ws']
|
||||
const announceAddreses = ['/dns4/peer.io']
|
||||
|
||||
describe('libp2p.multiaddrs', () => {
|
||||
let libp2p
|
||||
|
||||
afterEach(() => libp2p && libp2p.stop())
|
||||
|
||||
it('should keep listen addresses after start, even if changed', async () => {
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
started: false,
|
||||
config: {
|
||||
...AddressesOptions,
|
||||
addresses: {
|
||||
listen: listenAddresses,
|
||||
announce: announceAddreses
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let listenAddrs = libp2p.addressManager.listen
|
||||
expect(listenAddrs.size).to.equal(listenAddresses.length)
|
||||
expect(listenAddrs.has(listenAddresses[0])).to.equal(true)
|
||||
expect(listenAddrs.has(listenAddresses[1])).to.equal(true)
|
||||
|
||||
// Should not replace listen addresses after transport listen
|
||||
// Only transportManager has visibility of the port used
|
||||
await libp2p.start()
|
||||
|
||||
listenAddrs = libp2p.addressManager.listen
|
||||
expect(listenAddrs.size).to.equal(listenAddresses.length)
|
||||
expect(listenAddrs.has(listenAddresses[0])).to.equal(true)
|
||||
expect(listenAddrs.has(listenAddresses[1])).to.equal(true)
|
||||
})
|
||||
|
||||
it('should advertise all addresses if noAnnounce addresses are not provided, but with correct ports', async () => {
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
...AddressesOptions,
|
||||
addresses: {
|
||||
listen: listenAddresses,
|
||||
announce: announceAddreses
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const tmListen = libp2p.transportManager.getAddrs().map((ma) => ma.toString())
|
||||
|
||||
const spyAnnounce = sinon.spy(libp2p.addressManager, 'getAnnounceAddrs')
|
||||
const spyNoAnnounce = sinon.spy(libp2p.addressManager, 'getNoAnnounceAddrs')
|
||||
const spyListen = sinon.spy(libp2p.addressManager, 'getListenAddrs')
|
||||
const spyTranspMgr = sinon.spy(libp2p.transportManager, 'getAddrs')
|
||||
|
||||
const advertiseMultiaddrs = libp2p.multiaddrs.map((ma) => ma.toString())
|
||||
|
||||
expect(spyAnnounce).to.have.property('callCount', 1)
|
||||
expect(spyNoAnnounce).to.have.property('callCount', 1)
|
||||
expect(spyListen).to.have.property('callCount', 0) // Listen addr should not be used
|
||||
expect(spyTranspMgr).to.have.property('callCount', 1)
|
||||
|
||||
// Announce 2 listen (transport) + 1 announce
|
||||
expect(advertiseMultiaddrs.length).to.equal(3)
|
||||
tmListen.forEach((m) => {
|
||||
expect(advertiseMultiaddrs).to.include(m)
|
||||
})
|
||||
announceAddreses.forEach((m) => {
|
||||
expect(advertiseMultiaddrs).to.include(m)
|
||||
})
|
||||
expect(advertiseMultiaddrs).to.not.include(listenAddresses[0]) // Random Port switch
|
||||
})
|
||||
|
||||
it('should remove replicated addresses', async () => {
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
...AddressesOptions,
|
||||
addresses: {
|
||||
listen: listenAddresses,
|
||||
announce: [listenAddresses[1]]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const advertiseMultiaddrs = libp2p.multiaddrs.map((ma) => ma.toString())
|
||||
|
||||
// Announce 2 listen (transport), ignoring duplicated in announce
|
||||
expect(advertiseMultiaddrs.length).to.equal(2)
|
||||
})
|
||||
|
||||
it('should not advertise noAnnounce addresses', async () => {
|
||||
const noAnnounce = [listenAddresses[1]]
|
||||
;[libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
...AddressesOptions,
|
||||
addresses: {
|
||||
listen: listenAddresses,
|
||||
announce: announceAddreses,
|
||||
noAnnounce
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const advertiseMultiaddrs = libp2p.multiaddrs.map((ma) => ma.toString())
|
||||
|
||||
// Announce 1 listen (transport) not in the noAnnounce and the announce
|
||||
expect(advertiseMultiaddrs.length).to.equal(2)
|
||||
|
||||
announceAddreses.forEach((m) => {
|
||||
expect(advertiseMultiaddrs).to.include(m)
|
||||
})
|
||||
noAnnounce.forEach((m) => {
|
||||
expect(advertiseMultiaddrs).to.not.include(m)
|
||||
})
|
||||
})
|
||||
})
|
16
test/addresses/utils.js
Normal file
16
test/addresses/utils.js
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict'
|
||||
|
||||
const Transport1 = require('libp2p-tcp')
|
||||
const Transport2 = require('libp2p-websockets')
|
||||
const mergeOptions = require('merge-options')
|
||||
const baseOptions = require('../utils/base-options')
|
||||
|
||||
module.exports.baseOptions = baseOptions
|
||||
|
||||
const AddressesOptions = mergeOptions(baseOptions, {
|
||||
modules: {
|
||||
transport: [Transport1, Transport2]
|
||||
}
|
||||
})
|
||||
|
||||
module.exports.AddressesOptions = AddressesOptions
|
115
test/connection-manager/index.node.js
Normal file
115
test/connection-manager/index.node.js
Normal file
@@ -0,0 +1,115 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
chai.use(require('chai-as-promised'))
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
const mockConnection = require('../utils/mockConnection')
|
||||
const baseOptions = require('../utils/base-options.browser')
|
||||
|
||||
const listenMultiaddr = '/ip4/127.0.0.1/tcp/15002/ws'
|
||||
|
||||
describe('Connection Manager', () => {
|
||||
let libp2p
|
||||
|
||||
beforeEach(async () => {
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
addresses: {
|
||||
listen: [listenMultiaddr]
|
||||
},
|
||||
modules: baseOptions.modules
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => libp2p.stop())
|
||||
|
||||
it('should filter connections on disconnect, removing the closed one', async () => {
|
||||
const [localPeer, remotePeer] = await peerUtils.createPeerId({ number: 2 })
|
||||
|
||||
const conn1 = await mockConnection({ localPeer, remotePeer })
|
||||
const conn2 = await mockConnection({ localPeer, remotePeer })
|
||||
|
||||
const id = remotePeer.toB58String()
|
||||
|
||||
// Add connection to the connectionManager
|
||||
libp2p.connectionManager.onConnect(conn1)
|
||||
libp2p.connectionManager.onConnect(conn2)
|
||||
|
||||
expect(libp2p.connectionManager.connections.get(id).length).to.eql(2)
|
||||
|
||||
conn2._stat.status = 'closed'
|
||||
libp2p.connectionManager.onDisconnect(conn2)
|
||||
|
||||
const peerConnections = libp2p.connectionManager.connections.get(id)
|
||||
expect(peerConnections.length).to.eql(1)
|
||||
expect(peerConnections[0]._stat.status).to.eql('open')
|
||||
})
|
||||
|
||||
it('should add connection on dial and remove on node stop', async () => {
|
||||
const [remoteLibp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/15003/ws']
|
||||
},
|
||||
modules: baseOptions.modules
|
||||
}
|
||||
})
|
||||
|
||||
// Spy on emit for easy verification
|
||||
sinon.spy(libp2p.connectionManager, 'emit')
|
||||
sinon.spy(remoteLibp2p.connectionManager, 'emit')
|
||||
|
||||
libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.multiaddrs)
|
||||
await libp2p.dial(remoteLibp2p.peerId)
|
||||
|
||||
// check connect event
|
||||
expect(libp2p.connectionManager.emit.callCount).to.equal(1)
|
||||
const [event, connection] = libp2p.connectionManager.emit.getCall(0).args
|
||||
expect(event).to.equal('peer:connect')
|
||||
expect(connection.remotePeer.isEqual(remoteLibp2p.peerId)).to.equal(true)
|
||||
|
||||
const libp2pConn = libp2p.connectionManager.get(remoteLibp2p.peerId)
|
||||
expect(libp2pConn).to.exist()
|
||||
|
||||
const remoteConn = remoteLibp2p.connectionManager.get(libp2p.peerId)
|
||||
expect(remoteConn).to.exist()
|
||||
|
||||
await remoteLibp2p.stop()
|
||||
expect(remoteLibp2p.connectionManager.size).to.eql(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('libp2p.connections', () => {
|
||||
it('libp2p.connections gets the connectionManager conns', async () => {
|
||||
const [libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/15003/ws']
|
||||
},
|
||||
modules: baseOptions.modules
|
||||
}
|
||||
})
|
||||
const [remoteLibp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/15004/ws']
|
||||
},
|
||||
modules: baseOptions.modules
|
||||
}
|
||||
})
|
||||
|
||||
libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.multiaddrs)
|
||||
await libp2p.dial(remoteLibp2p.peerId)
|
||||
|
||||
expect(libp2p.connections.size).to.eql(1)
|
||||
|
||||
await libp2p.stop()
|
||||
await remoteLibp2p.stop()
|
||||
})
|
||||
})
|
@@ -7,7 +7,7 @@ chai.use(require('chai-as-promised'))
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const { createPeer } = require('../utils/creators/peer')
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
const mockConnection = require('../utils/mockConnection')
|
||||
const baseOptions = require('../utils/base-options.browser')
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('Connection Manager', () => {
|
||||
})
|
||||
|
||||
it('should be able to create without metrics', async () => {
|
||||
[libp2p] = await createPeer({
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
modules: baseOptions.modules
|
||||
},
|
||||
@@ -35,7 +35,7 @@ describe('Connection Manager', () => {
|
||||
})
|
||||
|
||||
it('should be able to create with metrics', async () => {
|
||||
[libp2p] = await createPeer({
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
modules: baseOptions.modules,
|
||||
metrics: {
|
||||
@@ -49,12 +49,12 @@ describe('Connection Manager', () => {
|
||||
|
||||
await libp2p.start()
|
||||
expect(spy).to.have.property('callCount', 1)
|
||||
expect(libp2p.connectionManager._metrics).to.exist()
|
||||
expect(libp2p.connectionManager._libp2p.metrics).to.exist()
|
||||
})
|
||||
|
||||
it('should close lowest value peer connection when the maximum has been reached', async () => {
|
||||
const max = 5
|
||||
;[libp2p] = await createPeer({
|
||||
;[libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
modules: baseOptions.modules,
|
||||
connectionManager: {
|
||||
@@ -92,7 +92,7 @@ describe('Connection Manager', () => {
|
||||
|
||||
it('should close connection when the maximum has been reached even without peer values', async () => {
|
||||
const max = 5
|
||||
;[libp2p] = await createPeer({
|
||||
;[libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
modules: baseOptions.modules,
|
||||
connectionManager: {
|
||||
@@ -110,7 +110,7 @@ describe('Connection Manager', () => {
|
||||
const spy = sinon.spy()
|
||||
await Promise.all([...new Array(max + 1)].map(async () => {
|
||||
const connection = await mockConnection()
|
||||
sinon.stub(connection, 'close').callsFake(() => spy())
|
||||
sinon.stub(connection, 'close').callsFake(() => spy()) // eslint-disable-line
|
||||
libp2p.connectionManager.onConnect(connection)
|
||||
}))
|
||||
|
||||
@@ -119,7 +119,7 @@ describe('Connection Manager', () => {
|
||||
})
|
||||
|
||||
it('should fail if the connection manager has mismatched connection limit options', async () => {
|
||||
await expect(createPeer({
|
||||
await expect(peerUtils.createPeer({
|
||||
config: {
|
||||
modules: baseOptions.modules,
|
||||
connectionManager: {
|
||||
|
@@ -56,7 +56,7 @@ describe('content-routing', () => {
|
||||
|
||||
// Ring dial
|
||||
await Promise.all(
|
||||
nodes.map((peer, i) => peer.dial(nodes[(i + 1) % number].peerInfo))
|
||||
nodes.map((peer, i) => peer.dial(nodes[(i + 1) % number].peerId))
|
||||
)
|
||||
})
|
||||
|
||||
@@ -96,9 +96,9 @@ describe('content-routing', () => {
|
||||
let delegate
|
||||
|
||||
beforeEach(async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo({ fixture: false })
|
||||
const [peerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
|
||||
delegate = new DelegatedContentRouter(peerInfo.id, {
|
||||
delegate = new DelegatedContentRouter(peerId, {
|
||||
host: '0.0.0.0',
|
||||
protocol: 'http',
|
||||
port: 60197
|
||||
@@ -155,10 +155,7 @@ describe('content-routing', () => {
|
||||
const mockApi = nock('http://0.0.0.0:60197')
|
||||
// mock the refs call
|
||||
.post('/api/v0/refs')
|
||||
.query({
|
||||
recursive: false,
|
||||
arg: cid.toBaseEncodedString()
|
||||
})
|
||||
.query(true)
|
||||
.reply(200, null, [
|
||||
'Content-Type', 'application/json',
|
||||
'X-Chunked-Output', '1'
|
||||
@@ -174,10 +171,7 @@ describe('content-routing', () => {
|
||||
const mockApi = nock('http://0.0.0.0:60197')
|
||||
// mock the refs call
|
||||
.post('/api/v0/refs')
|
||||
.query({
|
||||
recursive: false,
|
||||
arg: cid.toBaseEncodedString()
|
||||
})
|
||||
.query(true)
|
||||
.reply(502, 'Bad Gateway', ['Content-Type', 'application/json'])
|
||||
|
||||
await expect(node.contentRouting.provide(cid))
|
||||
@@ -192,9 +186,7 @@ describe('content-routing', () => {
|
||||
|
||||
const mockApi = nock('http://0.0.0.0:60197')
|
||||
.post('/api/v0/dht/findprovs')
|
||||
.query({
|
||||
arg: cid.toBaseEncodedString()
|
||||
})
|
||||
.query(true)
|
||||
.reply(200, `{"Extra":"","ID":"QmWKqWXCtRXEeCQTo3FoZ7g4AfnGiauYYiczvNxFCHicbB","Responses":[{"Addrs":["/ip4/0.0.0.0/tcp/0"],"ID":"${provider}"}],"Type":4}\n`, [
|
||||
'Content-Type', 'application/json',
|
||||
'X-Chunked-Output', '1'
|
||||
@@ -214,9 +206,7 @@ describe('content-routing', () => {
|
||||
const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB')
|
||||
const mockApi = nock('http://0.0.0.0:60197')
|
||||
.post('/api/v0/dht/findprovs')
|
||||
.query({
|
||||
arg: cid.toBaseEncodedString()
|
||||
})
|
||||
.query(true)
|
||||
.reply(502, 'Bad Gateway', [
|
||||
'X-Chunked-Output', '1'
|
||||
])
|
||||
@@ -237,9 +227,9 @@ describe('content-routing', () => {
|
||||
let delegate
|
||||
|
||||
beforeEach(async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo({ fixture: false })
|
||||
const [peerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
|
||||
delegate = new DelegatedContentRouter(peerInfo.id, {
|
||||
delegate = new DelegatedContentRouter(peerId, {
|
||||
host: '0.0.0.0',
|
||||
protocol: 'http',
|
||||
port: 60197
|
||||
|
@@ -6,13 +6,12 @@ chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
|
||||
const mergeOptions = require('merge-options')
|
||||
const multiaddr = require('multiaddr')
|
||||
|
||||
const { create } = require('../../../src')
|
||||
const { baseOptions, subsystemOptions } = require('./utils')
|
||||
const peerUtils = require('../../utils/creators/peer')
|
||||
|
||||
const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
const listenAddr = '/ip4/127.0.0.1/tcp/0'
|
||||
|
||||
describe('DHT subsystem is configurable', () => {
|
||||
let libp2p
|
||||
@@ -32,11 +31,13 @@ describe('DHT subsystem is configurable', () => {
|
||||
})
|
||||
|
||||
it('should start and stop by default once libp2p starts', async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo(1)
|
||||
peerInfo.multiaddrs.add(listenAddr)
|
||||
const [peerId] = await peerUtils.createPeerId(1)
|
||||
|
||||
const customOptions = mergeOptions(subsystemOptions, {
|
||||
peerInfo
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
}
|
||||
})
|
||||
|
||||
libp2p = await create(customOptions)
|
||||
@@ -50,11 +51,13 @@ describe('DHT subsystem is configurable', () => {
|
||||
})
|
||||
|
||||
it('should not start if disabled once libp2p starts', async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo(1)
|
||||
peerInfo.multiaddrs.add(listenAddr)
|
||||
const [peerId] = await peerUtils.createPeerId(1)
|
||||
|
||||
const customOptions = mergeOptions(subsystemOptions, {
|
||||
peerInfo,
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
config: {
|
||||
dht: {
|
||||
enabled: false
|
||||
@@ -70,11 +73,13 @@ describe('DHT subsystem is configurable', () => {
|
||||
})
|
||||
|
||||
it('should allow a manual start', async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo(1)
|
||||
peerInfo.multiaddrs.add(listenAddr)
|
||||
const [peerId] = await peerUtils.createPeerId(1)
|
||||
|
||||
const customOptions = mergeOptions(subsystemOptions, {
|
||||
peerInfo,
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
config: {
|
||||
dht: {
|
||||
enabled: false
|
||||
|
@@ -1,13 +1,14 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
const mergeOptions = require('merge-options')
|
||||
const multiaddr = require('multiaddr')
|
||||
|
||||
const { create } = require('../../../src')
|
||||
const { subsystemOptions, subsystemMulticodecs } = require('./utils')
|
||||
@@ -17,25 +18,28 @@ const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/8000')
|
||||
const remoteListenAddr = multiaddr('/ip4/127.0.0.1/tcp/8001')
|
||||
|
||||
describe('DHT subsystem operates correctly', () => {
|
||||
let peerInfo, remotePeerInfo
|
||||
let peerId, remotePeerId
|
||||
let libp2p, remoteLibp2p
|
||||
let remAddr
|
||||
|
||||
beforeEach(async () => {
|
||||
[peerInfo, remotePeerInfo] = await peerUtils.createPeerInfo({ number: 2 })
|
||||
|
||||
peerInfo.multiaddrs.add(listenAddr)
|
||||
remotePeerInfo.multiaddrs.add(remoteListenAddr)
|
||||
[peerId, remotePeerId] = await peerUtils.createPeerId({ number: 2 })
|
||||
})
|
||||
|
||||
describe('dht started before connect', () => {
|
||||
beforeEach(async () => {
|
||||
libp2p = await create(mergeOptions(subsystemOptions, {
|
||||
peerInfo
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
}
|
||||
}))
|
||||
|
||||
remoteLibp2p = await create(mergeOptions(subsystemOptions, {
|
||||
peerInfo: remotePeerInfo
|
||||
peerId: remotePeerId,
|
||||
addresses: {
|
||||
listen: [remoteListenAddr]
|
||||
}
|
||||
}))
|
||||
|
||||
await Promise.all([
|
||||
@@ -43,7 +47,8 @@ describe('DHT subsystem operates correctly', () => {
|
||||
remoteLibp2p.start()
|
||||
])
|
||||
|
||||
remAddr = libp2p.peerStore.multiaddrsForPeer(remotePeerInfo)[0]
|
||||
libp2p.peerStore.addressBook.set(remotePeerId, [remoteListenAddr])
|
||||
remAddr = libp2p.peerStore.addressBook.getMultiaddrsForPeer(remotePeerId)[0]
|
||||
})
|
||||
|
||||
afterEach(() => Promise.all([
|
||||
@@ -67,15 +72,14 @@ describe('DHT subsystem operates correctly', () => {
|
||||
const value = Buffer.from('world')
|
||||
|
||||
await libp2p.dialProtocol(remAddr, subsystemMulticodecs)
|
||||
|
||||
await Promise.all([
|
||||
pWaitFor(() => libp2p._dht.routingTable.size === 1),
|
||||
pWaitFor(() => remoteLibp2p._dht.routingTable.size === 1)
|
||||
])
|
||||
|
||||
await libp2p.contentRouting.put(key, value)
|
||||
|
||||
const fetchedValue = await remoteLibp2p.contentRouting.get(key)
|
||||
|
||||
expect(fetchedValue).to.eql(value)
|
||||
})
|
||||
})
|
||||
@@ -83,11 +87,17 @@ describe('DHT subsystem operates correctly', () => {
|
||||
describe('dht started after connect', () => {
|
||||
beforeEach(async () => {
|
||||
libp2p = await create(mergeOptions(subsystemOptions, {
|
||||
peerInfo
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
}
|
||||
}))
|
||||
|
||||
remoteLibp2p = await create(mergeOptions(subsystemOptions, {
|
||||
peerInfo: remotePeerInfo,
|
||||
peerId: remotePeerId,
|
||||
addresses: {
|
||||
listen: [remoteListenAddr]
|
||||
},
|
||||
config: {
|
||||
dht: {
|
||||
enabled: false
|
||||
@@ -98,7 +108,8 @@ describe('DHT subsystem operates correctly', () => {
|
||||
await libp2p.start()
|
||||
await remoteLibp2p.start()
|
||||
|
||||
remAddr = libp2p.peerStore.multiaddrsForPeer(remotePeerInfo)[0]
|
||||
libp2p.peerStore.addressBook.set(remotePeerId, [remoteListenAddr])
|
||||
remAddr = libp2p.peerStore.addressBook.getMultiaddrsForPeer(remotePeerId)[0]
|
||||
})
|
||||
|
||||
afterEach(() => Promise.all([
|
||||
|
@@ -5,21 +5,19 @@ const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
const Transport = require('libp2p-tcp')
|
||||
|
||||
const { create } = require('../../src')
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
|
||||
const listenAddr = multiaddr('/ip4/0.0.0.0/tcp/0')
|
||||
const listenAddr = '/ip4/0.0.0.0/tcp/0'
|
||||
|
||||
describe('Listening', () => {
|
||||
let peerInfo
|
||||
let peerId
|
||||
let libp2p
|
||||
|
||||
before(async () => {
|
||||
[peerInfo] = await peerUtils.createPeerInfo()
|
||||
peerInfo.multiaddrs.add(listenAddr)
|
||||
[peerId] = await peerUtils.createPeerId()
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
@@ -28,7 +26,10 @@ describe('Listening', () => {
|
||||
|
||||
it('should replace wildcard host and port with actual host and port on startup', async () => {
|
||||
libp2p = await create({
|
||||
peerInfo,
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
modules: {
|
||||
transport: [Transport]
|
||||
}
|
||||
@@ -36,7 +37,7 @@ describe('Listening', () => {
|
||||
|
||||
await libp2p.start()
|
||||
|
||||
const addrs = libp2p.peerInfo.multiaddrs.toArray()
|
||||
const addrs = libp2p.transportManager.getAddrs()
|
||||
|
||||
// Should get something like:
|
||||
// /ip4/127.0.0.1/tcp/50866
|
||||
|
@@ -20,16 +20,19 @@ describe('ping', () => {
|
||||
number: 2,
|
||||
config: baseOptions
|
||||
})
|
||||
|
||||
nodes[0].peerStore.addressBook.set(nodes[1].peerId, nodes[1].multiaddrs)
|
||||
nodes[1].peerStore.addressBook.set(nodes[0].peerId, nodes[0].multiaddrs)
|
||||
})
|
||||
|
||||
it('ping once from peer0 to peer1', async () => {
|
||||
const latency = await nodes[0].ping(nodes[1].peerInfo)
|
||||
const latency = await nodes[0].ping(nodes[1].peerId)
|
||||
|
||||
expect(latency).to.be.a('Number')
|
||||
})
|
||||
|
||||
it('ping several times for getting an average', async () => {
|
||||
const latencies = await pTimes(5, () => nodes[1].ping(nodes[0].peerInfo))
|
||||
const latencies = await pTimes(5, () => nodes[1].ping(nodes[0].peerId))
|
||||
|
||||
const averageLatency = latencies.reduce((p, c) => p + c, 0) / latencies.length
|
||||
expect(averageLatency).to.be.a('Number')
|
||||
@@ -66,7 +69,7 @@ describe('ping', () => {
|
||||
)
|
||||
})
|
||||
|
||||
const latency = await nodes[0].ping(nodes[1].peerInfo)
|
||||
const latency = await nodes[0].ping(nodes[1].peerId)
|
||||
|
||||
expect(latency).to.be.a('Number')
|
||||
})
|
||||
|
@@ -1,6 +1,7 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
chai.use(require('chai-as-promised'))
|
||||
@@ -8,10 +9,9 @@ const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
const Transport = require('libp2p-tcp')
|
||||
const Muxer = require('libp2p-mplex')
|
||||
const Crypto = require('libp2p-secio')
|
||||
const { NOISE: Crypto } = require('libp2p-noise')
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const delay = require('delay')
|
||||
const pDefer = require('p-defer')
|
||||
const pSettle = require('p-settle')
|
||||
@@ -22,6 +22,7 @@ const { AbortError } = require('libp2p-interfaces/src/transport/errors')
|
||||
|
||||
const Libp2p = require('../../src')
|
||||
const Dialer = require('../../src/dialer')
|
||||
const AddressManager = require('../../src/address-manager')
|
||||
const PeerStore = require('../../src/peer-store')
|
||||
const TransportManager = require('../../src/transport-manager')
|
||||
const { codes: ErrorCodes } = require('../../src/errors')
|
||||
@@ -31,7 +32,7 @@ const swarmKeyBuffer = Buffer.from(require('../fixtures/swarm.key'))
|
||||
const mockUpgrader = require('../utils/mockUpgrader')
|
||||
const createMockConnection = require('../utils/mockConnection')
|
||||
const Peers = require('../fixtures/peers')
|
||||
const { createPeerInfo } = require('../utils/creators/peer')
|
||||
const { createPeerId } = require('../utils/creators/peer')
|
||||
|
||||
const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
const unsupportedAddr = multiaddr('/ip4/127.0.0.1/tcp/9999/ws/p2p/QmckxVrJw1Yo8LqvmDJNUmdAsKtSbiKWmrXJFyKmUraBoN')
|
||||
@@ -47,7 +48,9 @@ describe('Dialing (direct, TCP)', () => {
|
||||
PeerId.createFromJSON(Peers[0])
|
||||
])
|
||||
remoteTM = new TransportManager({
|
||||
libp2p: {},
|
||||
libp2p: {
|
||||
addressManager: new AddressManager({ listen: [listenAddr] })
|
||||
},
|
||||
upgrader: mockUpgrader
|
||||
})
|
||||
remoteTM.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
@@ -80,9 +83,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
|
||||
it('should be able to connect to a remote node via its stringified multiaddr', async () => {
|
||||
const dialer = new Dialer({ transportManager: localTM, peerStore })
|
||||
|
||||
const dialable = Dialer.getDialable(remoteAddr.toString())
|
||||
const connection = await dialer.connectToPeer(dialable)
|
||||
const connection = await dialer.connectToPeer(remoteAddr.toString())
|
||||
expect(connection).to.exist()
|
||||
await connection.close()
|
||||
})
|
||||
@@ -95,21 +96,6 @@ describe('Dialing (direct, TCP)', () => {
|
||||
.and.to.have.nested.property('._errors[0].code', ErrorCodes.ERR_TRANSPORT_UNAVAILABLE)
|
||||
})
|
||||
|
||||
it('should be able to connect to a given peer info', async () => {
|
||||
const dialer = new Dialer({
|
||||
transportManager: localTM,
|
||||
peerStore: {
|
||||
multiaddrsForPeer: () => [remoteAddr]
|
||||
}
|
||||
})
|
||||
const peerId = await PeerId.createFromJSON(Peers[0])
|
||||
const peerInfo = new PeerInfo(peerId)
|
||||
|
||||
const connection = await dialer.connectToPeer(peerInfo)
|
||||
expect(connection).to.exist()
|
||||
await connection.close()
|
||||
})
|
||||
|
||||
it('should be able to connect to a given peer id', async () => {
|
||||
const peerStore = new PeerStore()
|
||||
const dialer = new Dialer({
|
||||
@@ -118,11 +104,9 @@ describe('Dialing (direct, TCP)', () => {
|
||||
})
|
||||
|
||||
const peerId = await PeerId.createFromJSON(Peers[0])
|
||||
const peerInfo = new PeerInfo(peerId)
|
||||
peerInfo.multiaddrs.add(remoteAddr)
|
||||
peerStore.put(peerInfo)
|
||||
peerStore.addressBook.set(peerId, [remoteAddr])
|
||||
|
||||
const connection = await dialer.connectToPeer(peerInfo)
|
||||
const connection = await dialer.connectToPeer(peerId)
|
||||
expect(connection).to.exist()
|
||||
await connection.close()
|
||||
})
|
||||
@@ -131,13 +115,15 @@ describe('Dialing (direct, TCP)', () => {
|
||||
const dialer = new Dialer({
|
||||
transportManager: localTM,
|
||||
peerStore: {
|
||||
multiaddrsForPeer: () => [unsupportedAddr]
|
||||
addressBook: {
|
||||
add: () => {},
|
||||
getMultiaddrsForPeer: () => [unsupportedAddr]
|
||||
}
|
||||
}
|
||||
})
|
||||
const peerId = await PeerId.createFromJSON(Peers[0])
|
||||
const peerInfo = new PeerInfo(peerId)
|
||||
|
||||
await expect(dialer.connectToPeer(peerInfo))
|
||||
await expect(dialer.connectToPeer(peerId))
|
||||
.to.eventually.be.rejectedWith(AggregateError)
|
||||
.and.to.have.nested.property('._errors[0].code', ErrorCodes.ERR_TRANSPORT_UNAVAILABLE)
|
||||
})
|
||||
@@ -172,7 +158,10 @@ describe('Dialing (direct, TCP)', () => {
|
||||
transportManager: localTM,
|
||||
concurrency: 2,
|
||||
peerStore: {
|
||||
multiaddrsForPeer: () => addrs
|
||||
addressBook: {
|
||||
add: () => {},
|
||||
getMultiaddrsForPeer: () => addrs
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -181,10 +170,10 @@ describe('Dialing (direct, TCP)', () => {
|
||||
const deferredDial = pDefer()
|
||||
sinon.stub(localTM, 'dial').callsFake(() => deferredDial.promise)
|
||||
|
||||
const [peerInfo] = await createPeerInfo()
|
||||
const [peerId] = await createPeerId()
|
||||
|
||||
// Perform 3 multiaddr dials
|
||||
dialer.connectToPeer(peerInfo)
|
||||
dialer.connectToPeer(peerId)
|
||||
|
||||
// Let the call stack run
|
||||
await delay(0)
|
||||
@@ -203,30 +192,28 @@ describe('Dialing (direct, TCP)', () => {
|
||||
})
|
||||
|
||||
describe('libp2p.dialer', () => {
|
||||
let peerInfo
|
||||
let remotePeerInfo
|
||||
let peerId, remotePeerId
|
||||
let libp2p
|
||||
let remoteLibp2p
|
||||
let remoteAddr
|
||||
|
||||
before(async () => {
|
||||
const [peerId, remotePeerId] = await Promise.all([
|
||||
[peerId, remotePeerId] = await Promise.all([
|
||||
PeerId.createFromJSON(Peers[0]),
|
||||
PeerId.createFromJSON(Peers[1])
|
||||
])
|
||||
|
||||
peerInfo = new PeerInfo(peerId)
|
||||
remotePeerInfo = new PeerInfo(remotePeerId)
|
||||
|
||||
remoteLibp2p = new Libp2p({
|
||||
peerInfo: remotePeerInfo,
|
||||
peerId: remotePeerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
connEncryption: [Crypto]
|
||||
}
|
||||
})
|
||||
remoteLibp2p.peerInfo.multiaddrs.add(listenAddr)
|
||||
remoteLibp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
|
||||
|
||||
await remoteLibp2p.start()
|
||||
@@ -243,7 +230,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
|
||||
it('should fail if no peer id is provided', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -256,7 +243,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
try {
|
||||
await libp2p.dial(remoteLibp2p.transportManager.getAddrs()[0])
|
||||
} catch (err) {
|
||||
expect(err).to.have.property('code', ErrorCodes.ERR_INVALID_PEER)
|
||||
expect(err).to.have.property('code', ErrorCodes.ERR_INVALID_MULTIADDR)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -265,7 +252,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
|
||||
it('should use the dialer for connecting to a multiaddr', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -286,7 +273,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
|
||||
it('should use the dialer for connecting to a peer', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -295,8 +282,9 @@ describe('Dialing (direct, TCP)', () => {
|
||||
})
|
||||
|
||||
sinon.spy(libp2p.dialer, 'connectToPeer')
|
||||
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.multiaddrs)
|
||||
|
||||
const connection = await libp2p.dial(remotePeerInfo)
|
||||
const connection = await libp2p.dial(remotePeerId)
|
||||
expect(connection).to.exist()
|
||||
const { stream, protocol } = await connection.newStream('/echo/1.0.0')
|
||||
expect(stream).to.exist()
|
||||
@@ -307,7 +295,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
|
||||
it('should be able to use hangup to close connections', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -324,7 +312,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
|
||||
it('should be able to use hangup by address string to close connections', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -342,7 +330,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
it('should use the protectors when provided for connecting', async () => {
|
||||
const protector = new Protector(swarmKeyBuffer)
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -365,7 +353,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
|
||||
it('should coalesce parallel dials to the same peer (id in multiaddr)', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -374,9 +362,11 @@ describe('Dialing (direct, TCP)', () => {
|
||||
})
|
||||
const dials = 10
|
||||
|
||||
const fullAddress = remoteAddr.encapsulate(`/p2p/${remoteLibp2p.peerInfo.id.toB58String()}`)
|
||||
const fullAddress = remoteAddr.encapsulate(`/p2p/${remoteLibp2p.peerId.toB58String()}`)
|
||||
|
||||
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.multiaddrs)
|
||||
const dialResults = await Promise.all([...new Array(dials)].map((_, index) => {
|
||||
if (index % 2 === 0) return libp2p.dial(remoteLibp2p.peerInfo)
|
||||
if (index % 2 === 0) return libp2p.dial(remoteLibp2p.peerId)
|
||||
return libp2p.dial(fullAddress)
|
||||
}))
|
||||
|
||||
@@ -387,13 +377,13 @@ describe('Dialing (direct, TCP)', () => {
|
||||
}
|
||||
|
||||
// 1 connection, because we know the peer in the multiaddr
|
||||
expect(libp2p.connectionManager._connections.size).to.equal(1)
|
||||
expect(remoteLibp2p.connectionManager._connections.size).to.equal(1)
|
||||
expect(libp2p.connectionManager.size).to.equal(1)
|
||||
expect(remoteLibp2p.connectionManager.size).to.equal(1)
|
||||
})
|
||||
|
||||
it('should coalesce parallel dials to the same error on failure', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -404,8 +394,9 @@ describe('Dialing (direct, TCP)', () => {
|
||||
const error = new Error('Boom')
|
||||
sinon.stub(libp2p.transportManager, 'dial').callsFake(() => Promise.reject(error))
|
||||
|
||||
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.multiaddrs)
|
||||
const dialResults = await pSettle([...new Array(dials)].map((_, index) => {
|
||||
if (index % 2 === 0) return libp2p.dial(remoteLibp2p.peerInfo)
|
||||
if (index % 2 === 0) return libp2p.dial(remoteLibp2p.peerId)
|
||||
return libp2p.dial(remoteAddr)
|
||||
}))
|
||||
|
||||
@@ -421,8 +412,8 @@ describe('Dialing (direct, TCP)', () => {
|
||||
}
|
||||
|
||||
// 1 connection, because we know the peer in the multiaddr
|
||||
expect(libp2p.connectionManager._connections.size).to.equal(0)
|
||||
expect(remoteLibp2p.connectionManager._connections.size).to.equal(0)
|
||||
expect(libp2p.connectionManager.size).to.equal(0)
|
||||
expect(remoteLibp2p.connectionManager.size).to.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -11,10 +11,9 @@ const pWaitFor = require('p-wait-for')
|
||||
const delay = require('delay')
|
||||
const Transport = require('libp2p-websockets')
|
||||
const Muxer = require('libp2p-mplex')
|
||||
const Crypto = require('libp2p-secio')
|
||||
const { NOISE: Crypto } = require('libp2p-noise')
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const AggregateError = require('aggregate-error')
|
||||
const { AbortError } = require('libp2p-interfaces/src/transport/errors')
|
||||
|
||||
@@ -87,7 +86,10 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
const dialer = new Dialer({
|
||||
transportManager: localTM,
|
||||
peerStore: {
|
||||
multiaddrsForPeer: () => [remoteAddr]
|
||||
addressBook: {
|
||||
add: () => {},
|
||||
getMultiaddrsForPeer: () => [remoteAddr]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -100,7 +102,10 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
const dialer = new Dialer({
|
||||
transportManager: localTM,
|
||||
peerStore: {
|
||||
multiaddrsForPeer: () => [remoteAddr]
|
||||
addressBook: {
|
||||
add: () => {},
|
||||
getMultiaddrsForPeer: () => [remoteAddr]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -121,7 +126,10 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
const dialer = new Dialer({
|
||||
transportManager: localTM,
|
||||
peerStore: {
|
||||
multiaddrsForPeer: () => [remoteAddr]
|
||||
addressBook: {
|
||||
add: () => {},
|
||||
getMultiaddrsForPeer: () => [remoteAddr]
|
||||
}
|
||||
}
|
||||
})
|
||||
const peerId = await PeerId.createFromJSON(Peers[0])
|
||||
@@ -135,7 +143,10 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
const dialer = new Dialer({
|
||||
transportManager: localTM,
|
||||
peerStore: {
|
||||
multiaddrsForPeer: () => [unsupportedAddr]
|
||||
addressBook: {
|
||||
set: () => {},
|
||||
getMultiaddrsForPeer: () => [unsupportedAddr]
|
||||
}
|
||||
}
|
||||
})
|
||||
const peerId = await PeerId.createFromJSON(Peers[0])
|
||||
@@ -150,7 +161,10 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
transportManager: localTM,
|
||||
timeout: 50,
|
||||
peerStore: {
|
||||
multiaddrsForPeer: () => [remoteAddr]
|
||||
addressBook: {
|
||||
add: () => {},
|
||||
getMultiaddrsForPeer: () => [remoteAddr]
|
||||
}
|
||||
}
|
||||
})
|
||||
sinon.stub(localTM, 'dial').callsFake(async (addr, options) => {
|
||||
@@ -172,7 +186,10 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
transportManager: localTM,
|
||||
concurrency: 2,
|
||||
peerStore: {
|
||||
multiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
|
||||
addressBook: {
|
||||
set: () => {},
|
||||
getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -208,7 +225,10 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
transportManager: localTM,
|
||||
concurrency: 2,
|
||||
peerStore: {
|
||||
multiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
|
||||
addressBook: {
|
||||
set: () => {},
|
||||
getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -246,13 +266,12 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
})
|
||||
|
||||
describe('libp2p.dialer', () => {
|
||||
let peerInfo
|
||||
let peerId
|
||||
let libp2p
|
||||
let remoteLibp2p
|
||||
|
||||
before(async () => {
|
||||
const peerId = await PeerId.createFromJSON(Peers[0])
|
||||
peerInfo = new PeerInfo(peerId)
|
||||
peerId = await PeerId.createFromJSON(Peers[0])
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -267,7 +286,7 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
|
||||
it('should create a dialer', () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -285,7 +304,7 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
|
||||
it('should be able to override dialer options', async () => {
|
||||
const config = {
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -307,7 +326,7 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
|
||||
it('should use the dialer for connecting', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -316,7 +335,7 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
})
|
||||
|
||||
sinon.spy(libp2p.dialer, 'connectToPeer')
|
||||
sinon.spy(libp2p.peerStore, 'put')
|
||||
sinon.spy(libp2p.peerStore.addressBook, 'add')
|
||||
|
||||
const connection = await libp2p.dial(remoteAddr)
|
||||
expect(connection).to.exist()
|
||||
@@ -325,12 +344,12 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
expect(protocol).to.equal('/echo/1.0.0')
|
||||
await connection.close()
|
||||
expect(libp2p.dialer.connectToPeer.callCount).to.equal(1)
|
||||
expect(libp2p.peerStore.put.callCount).to.be.at.least(1)
|
||||
expect(libp2p.peerStore.addressBook.add.callCount).to.be.at.least(1)
|
||||
})
|
||||
|
||||
it('should run identify automatically after connecting', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -339,24 +358,27 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
})
|
||||
|
||||
sinon.spy(libp2p.identifyService, 'identify')
|
||||
sinon.spy(libp2p.peerStore, 'replace')
|
||||
sinon.spy(libp2p.upgrader, 'onConnection')
|
||||
|
||||
const connection = await libp2p.dial(remoteAddr)
|
||||
expect(connection).to.exist()
|
||||
|
||||
sinon.spy(libp2p.peerStore.addressBook, 'set')
|
||||
sinon.spy(libp2p.peerStore.protoBook, 'set')
|
||||
|
||||
// Wait for onConnection to be called
|
||||
await pWaitFor(() => libp2p.upgrader.onConnection.callCount === 1)
|
||||
|
||||
expect(libp2p.identifyService.identify.callCount).to.equal(1)
|
||||
await libp2p.identifyService.identify.firstCall.returnValue
|
||||
|
||||
expect(libp2p.peerStore.replace.callCount).to.equal(1)
|
||||
expect(libp2p.peerStore.addressBook.set.callCount).to.equal(1)
|
||||
expect(libp2p.peerStore.protoBook.set.callCount).to.equal(1)
|
||||
})
|
||||
|
||||
it('should be able to use hangup to close connections', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -371,9 +393,22 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
expect(connection.stat.timeline.close).to.exist()
|
||||
})
|
||||
|
||||
it('should be able to use hangup when no connection exists', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
connEncryption: [Crypto]
|
||||
}
|
||||
})
|
||||
|
||||
await libp2p.hangUp(remoteAddr)
|
||||
})
|
||||
|
||||
it('should abort pending dials on stop', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
|
@@ -1,6 +1,7 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
chai.use(require('chai-as-promised'))
|
||||
@@ -11,25 +12,32 @@ const multiaddr = require('multiaddr')
|
||||
const { collect } = require('streaming-iterables')
|
||||
const pipe = require('it-pipe')
|
||||
const AggregateError = require('aggregate-error')
|
||||
const { createPeerInfo } = require('../utils/creators/peer')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const { createPeerId } = require('../utils/creators/peer')
|
||||
const baseOptions = require('../utils/base-options')
|
||||
const Libp2p = require('../../src')
|
||||
const { codes: Errors } = require('../../src/errors')
|
||||
|
||||
const listenAddr = '/ip4/0.0.0.0/tcp/0'
|
||||
|
||||
describe('Dialing (via relay, TCP)', () => {
|
||||
let srcLibp2p
|
||||
let relayLibp2p
|
||||
let dstLibp2p
|
||||
|
||||
before(async () => {
|
||||
const peerInfos = await createPeerInfo({ number: 3 })
|
||||
beforeEach(async () => {
|
||||
const peerIds = await createPeerId({ number: 3 })
|
||||
// Create 3 nodes, and turn HOP on for the relay
|
||||
;[srcLibp2p, relayLibp2p, dstLibp2p] = peerInfos.map((peerInfo, index) => {
|
||||
;[srcLibp2p, relayLibp2p, dstLibp2p] = peerIds.map((peerId, index) => {
|
||||
const opts = baseOptions
|
||||
index === 1 && (opts.config.relay.hop.enabled = true)
|
||||
return new Libp2p({
|
||||
...opts,
|
||||
peerInfo
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
peerId
|
||||
})
|
||||
})
|
||||
|
||||
@@ -38,12 +46,7 @@ describe('Dialing (via relay, TCP)', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
// Start each node
|
||||
return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(libp2p => {
|
||||
// Reset multiaddrs and start
|
||||
libp2p.peerInfo.multiaddrs.clear()
|
||||
libp2p.peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
return libp2p.start()
|
||||
}))
|
||||
return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(libp2p => libp2p.start()))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -51,34 +54,37 @@ describe('Dialing (via relay, TCP)', () => {
|
||||
return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(async libp2p => {
|
||||
await libp2p.stop()
|
||||
// Clear the peer stores
|
||||
for (const peerId of libp2p.peerStore.peers.keys()) {
|
||||
libp2p.peerStore.remove(peerId)
|
||||
for (const peerIdStr of libp2p.peerStore.peers.keys()) {
|
||||
const peerId = PeerId.createFromCID(peerIdStr)
|
||||
libp2p.peerStore.delete(peerId)
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
it('should be able to connect to a peer over a relay with active connections', async () => {
|
||||
const relayAddr = relayLibp2p.transportManager.getAddrs()[0]
|
||||
const relayIdString = relayLibp2p.peerInfo.id.toB58String()
|
||||
const relayIdString = relayLibp2p.peerId.toB58String()
|
||||
|
||||
const dialAddr = relayAddr
|
||||
.encapsulate(`/p2p/${relayIdString}`)
|
||||
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerInfo.id.toB58String()}`)
|
||||
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`)
|
||||
|
||||
const tcpAddrs = dstLibp2p.transportManager.getAddrs()
|
||||
await dstLibp2p.transportManager.listen([multiaddr(`/p2p-circuit${relayAddr}/p2p/${relayIdString}`)])
|
||||
sinon.stub(dstLibp2p.addressManager, 'listen').value([multiaddr(`/p2p-circuit${relayAddr}/p2p/${relayIdString}`)])
|
||||
|
||||
await dstLibp2p.transportManager.listen()
|
||||
expect(dstLibp2p.transportManager.getAddrs()).to.have.deep.members([...tcpAddrs, dialAddr.decapsulate('p2p')])
|
||||
|
||||
const connection = await srcLibp2p.dial(dialAddr)
|
||||
expect(connection).to.exist()
|
||||
expect(connection.remotePeer.toBytes()).to.eql(dstLibp2p.peerInfo.id.toBytes())
|
||||
expect(connection.localPeer.toBytes()).to.eql(srcLibp2p.peerInfo.id.toBytes())
|
||||
expect(connection.remotePeer.toBytes()).to.eql(dstLibp2p.peerId.toBytes())
|
||||
expect(connection.localPeer.toBytes()).to.eql(srcLibp2p.peerId.toBytes())
|
||||
expect(connection.remoteAddr).to.eql(dialAddr)
|
||||
expect(connection.localAddr).to.eql(
|
||||
relayAddr // the relay address
|
||||
.encapsulate(`/p2p/${relayIdString}`) // with its peer id
|
||||
.encapsulate('/p2p-circuit') // the local peer is connected over the relay
|
||||
.encapsulate(`/p2p/${srcLibp2p.peerInfo.id.toB58String()}`) // and the local peer id
|
||||
.encapsulate(`/p2p/${srcLibp2p.peerId.toB58String()}`) // and the local peer id
|
||||
)
|
||||
|
||||
const { stream: echoStream } = await connection.newStream('/echo/1.0.0')
|
||||
@@ -94,11 +100,11 @@ describe('Dialing (via relay, TCP)', () => {
|
||||
|
||||
it('should fail to connect to a peer over a relay with inactive connections', async () => {
|
||||
const relayAddr = relayLibp2p.transportManager.getAddrs()[0]
|
||||
const relayIdString = relayLibp2p.peerInfo.id.toB58String()
|
||||
const relayIdString = relayLibp2p.peerId.toB58String()
|
||||
|
||||
const dialAddr = relayAddr
|
||||
.encapsulate(`/p2p/${relayIdString}`)
|
||||
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerInfo.id.toB58String()}`)
|
||||
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`)
|
||||
|
||||
await expect(srcLibp2p.dial(dialAddr))
|
||||
.to.eventually.be.rejectedWith(AggregateError)
|
||||
@@ -107,27 +113,27 @@ describe('Dialing (via relay, TCP)', () => {
|
||||
|
||||
it('should not stay connected to a relay when not already connected and HOP fails', async () => {
|
||||
const relayAddr = relayLibp2p.transportManager.getAddrs()[0]
|
||||
const relayIdString = relayLibp2p.peerInfo.id.toB58String()
|
||||
const relayIdString = relayLibp2p.peerId.toB58String()
|
||||
|
||||
const dialAddr = relayAddr
|
||||
.encapsulate(`/p2p/${relayIdString}`)
|
||||
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerInfo.id.toB58String()}`)
|
||||
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`)
|
||||
|
||||
await expect(srcLibp2p.dial(dialAddr))
|
||||
.to.eventually.be.rejectedWith(AggregateError)
|
||||
.and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED)
|
||||
|
||||
// We should not be connected to the relay, because we weren't before the dial
|
||||
const srcToRelayConn = srcLibp2p.registrar.getConnection(relayLibp2p.peerInfo)
|
||||
const srcToRelayConn = srcLibp2p.connectionManager.get(relayLibp2p.peerId)
|
||||
expect(srcToRelayConn).to.not.exist()
|
||||
})
|
||||
|
||||
it('dialer should stay connected to an already connected relay on hop failure', async () => {
|
||||
const relayIdString = relayLibp2p.peerInfo.id.toB58String()
|
||||
const relayIdString = relayLibp2p.peerId.toB58String()
|
||||
const relayAddr = relayLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${relayIdString}`)
|
||||
|
||||
const dialAddr = relayAddr
|
||||
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerInfo.id.toB58String()}`)
|
||||
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`)
|
||||
|
||||
await srcLibp2p.dial(relayAddr)
|
||||
|
||||
@@ -135,25 +141,27 @@ describe('Dialing (via relay, TCP)', () => {
|
||||
.to.eventually.be.rejectedWith(AggregateError)
|
||||
.and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED)
|
||||
|
||||
const srcToRelayConn = srcLibp2p.registrar.getConnection(relayLibp2p.peerInfo)
|
||||
const srcToRelayConn = srcLibp2p.connectionManager.get(relayLibp2p.peerId)
|
||||
expect(srcToRelayConn).to.exist()
|
||||
expect(srcToRelayConn.stat.status).to.equal('open')
|
||||
})
|
||||
|
||||
it('destination peer should stay connected to an already connected relay on hop failure', async () => {
|
||||
const relayIdString = relayLibp2p.peerInfo.id.toB58String()
|
||||
const relayIdString = relayLibp2p.peerId.toB58String()
|
||||
const relayAddr = relayLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${relayIdString}`)
|
||||
|
||||
const dialAddr = relayAddr
|
||||
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerInfo.id.toB58String()}`)
|
||||
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`)
|
||||
|
||||
// Connect the destination peer and the relay
|
||||
const tcpAddrs = dstLibp2p.transportManager.getAddrs()
|
||||
await dstLibp2p.transportManager.listen([multiaddr(`/p2p-circuit${relayAddr}`)])
|
||||
sinon.stub(dstLibp2p.addressManager, 'getListenAddrs').returns([multiaddr(`${relayAddr}/p2p-circuit`)])
|
||||
|
||||
await dstLibp2p.transportManager.listen()
|
||||
expect(dstLibp2p.transportManager.getAddrs()).to.have.deep.members([...tcpAddrs, dialAddr.decapsulate('p2p')])
|
||||
|
||||
// Tamper with the our multiaddrs for the circuit message
|
||||
sinon.stub(srcLibp2p.peerInfo.multiaddrs, 'toArray').returns([{
|
||||
sinon.stub(srcLibp2p, 'multiaddrs').value([{
|
||||
buffer: Buffer.from('an invalid multiaddr')
|
||||
}])
|
||||
|
||||
@@ -161,7 +169,7 @@ describe('Dialing (via relay, TCP)', () => {
|
||||
.to.eventually.be.rejectedWith(AggregateError)
|
||||
.and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED)
|
||||
|
||||
const dstToRelayConn = dstLibp2p.registrar.getConnection(relayLibp2p.peerInfo)
|
||||
const dstToRelayConn = dstLibp2p.connectionManager.get(relayLibp2p.peerId)
|
||||
expect(dstToRelayConn).to.exist()
|
||||
expect(dstToRelayConn.stat.status).to.equal('open')
|
||||
})
|
||||
|
@@ -7,9 +7,9 @@ chai.use(require('chai-as-promised'))
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const delay = require('delay')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const duplexPair = require('it-pair/duplex')
|
||||
const multiaddr = require('multiaddr')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
@@ -35,7 +35,7 @@ describe('Identify', () => {
|
||||
[localPeer, remotePeer] = (await Promise.all([
|
||||
PeerId.createFromJSON(Peers[0]),
|
||||
PeerId.createFromJSON(Peers[1])
|
||||
])).map(id => new PeerInfo(id))
|
||||
]))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -44,27 +44,39 @@ describe('Identify', () => {
|
||||
|
||||
it('should be able to identify another peer', async () => {
|
||||
const localIdentify = new IdentifyService({
|
||||
peerInfo: localPeer,
|
||||
protocols,
|
||||
registrar: {
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: {
|
||||
replace: () => {}
|
||||
}
|
||||
}
|
||||
addressBook: {
|
||||
set: () => { }
|
||||
},
|
||||
protoBook: {
|
||||
set: () => { }
|
||||
}
|
||||
},
|
||||
multiaddrs: []
|
||||
},
|
||||
protocols
|
||||
})
|
||||
const remoteIdentify = new IdentifyService({
|
||||
peerInfo: remotePeer,
|
||||
libp2p: {
|
||||
peerId: remotePeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
multiaddrs: []
|
||||
},
|
||||
protocols
|
||||
})
|
||||
|
||||
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
|
||||
const localConnectionMock = { newStream: () => {}, remotePeer: remotePeer.id }
|
||||
const localConnectionMock = { newStream: () => {}, remotePeer }
|
||||
const remoteConnectionMock = { remoteAddr: observedAddr }
|
||||
|
||||
const [local, remote] = duplexPair()
|
||||
sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY })
|
||||
|
||||
sinon.spy(localIdentify.registrar.peerStore, 'replace')
|
||||
sinon.spy(localIdentify.peerStore.addressBook, 'set')
|
||||
sinon.spy(localIdentify.peerStore.protoBook, 'set')
|
||||
|
||||
// Run identify
|
||||
await Promise.all([
|
||||
@@ -76,29 +88,41 @@ describe('Identify', () => {
|
||||
})
|
||||
])
|
||||
|
||||
expect(localIdentify.registrar.peerStore.replace.callCount).to.equal(1)
|
||||
expect(localIdentify.peerStore.addressBook.set.callCount).to.equal(1)
|
||||
expect(localIdentify.peerStore.protoBook.set.callCount).to.equal(1)
|
||||
// Validate the remote peer gets updated in the peer store
|
||||
const call = localIdentify.registrar.peerStore.replace.firstCall
|
||||
expect(call.args[0].id.bytes).to.equal(remotePeer.id.bytes)
|
||||
const call = localIdentify.peerStore.addressBook.set.firstCall
|
||||
expect(call.args[0].id.bytes).to.equal(remotePeer.bytes)
|
||||
})
|
||||
|
||||
it('should throw if identified peer is the wrong peer', async () => {
|
||||
const localIdentify = new IdentifyService({
|
||||
peerInfo: localPeer,
|
||||
protocols,
|
||||
registrar: {
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: {
|
||||
replace: () => {}
|
||||
}
|
||||
}
|
||||
addressBook: {
|
||||
set: () => { }
|
||||
},
|
||||
protoBook: {
|
||||
set: () => { }
|
||||
}
|
||||
},
|
||||
multiaddrs: []
|
||||
},
|
||||
protocols
|
||||
})
|
||||
const remoteIdentify = new IdentifyService({
|
||||
peerInfo: remotePeer,
|
||||
libp2p: {
|
||||
peerId: remotePeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
multiaddrs: []
|
||||
},
|
||||
protocols
|
||||
})
|
||||
|
||||
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
|
||||
const localConnectionMock = { newStream: () => {}, remotePeer: localPeer.id }
|
||||
const localConnectionMock = { newStream: () => {}, remotePeer: localPeer }
|
||||
const remoteConnectionMock = { remoteAddr: observedAddr }
|
||||
|
||||
const [local, remote] = duplexPair()
|
||||
@@ -106,7 +130,7 @@ describe('Identify', () => {
|
||||
|
||||
// Run identify
|
||||
const identifyPromise = Promise.all([
|
||||
localIdentify.identify(localConnectionMock, localPeer.id),
|
||||
localIdentify.identify(localConnectionMock, localPeer),
|
||||
remoteIdentify.handleMessage({
|
||||
connection: remoteConnectionMock,
|
||||
stream: remote,
|
||||
@@ -121,9 +145,16 @@ describe('Identify', () => {
|
||||
|
||||
describe('push', () => {
|
||||
it('should be able to push identify updates to another peer', async () => {
|
||||
const listeningAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
|
||||
const connectionManager = new EventEmitter()
|
||||
connectionManager.getConnection = () => {}
|
||||
|
||||
const localIdentify = new IdentifyService({
|
||||
peerInfo: localPeer,
|
||||
registrar: { getConnection: () => {} },
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
multiaddrs: [listeningAddr]
|
||||
},
|
||||
protocols: new Map([
|
||||
[multicodecs.IDENTIFY],
|
||||
[multicodecs.IDENTIFY_PUSH],
|
||||
@@ -131,30 +162,31 @@ describe('Identify', () => {
|
||||
])
|
||||
})
|
||||
const remoteIdentify = new IdentifyService({
|
||||
peerInfo: remotePeer,
|
||||
registrar: {
|
||||
libp2p: {
|
||||
peerId: remotePeer,
|
||||
connectionManager,
|
||||
peerStore: {
|
||||
replace: () => {}
|
||||
}
|
||||
addressBook: {
|
||||
set: () => { }
|
||||
},
|
||||
protoBook: {
|
||||
set: () => { }
|
||||
}
|
||||
},
|
||||
multiaddrs: []
|
||||
}
|
||||
})
|
||||
|
||||
// Setup peer protocols and multiaddrs
|
||||
const localProtocols = new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0'])
|
||||
const listeningAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
|
||||
sinon.stub(localPeer.multiaddrs, 'toArray').returns([listeningAddr])
|
||||
sinon.stub(localPeer, 'protocols').value(localProtocols)
|
||||
sinon.stub(remotePeer, 'protocols').value(new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH]))
|
||||
|
||||
const localConnectionMock = { newStream: () => {} }
|
||||
const remoteConnectionMock = { remotePeer: localPeer.id }
|
||||
const remoteConnectionMock = { remotePeer: localPeer }
|
||||
|
||||
const [local, remote] = duplexPair()
|
||||
sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH })
|
||||
|
||||
sinon.spy(IdentifyService, 'updatePeerAddresses')
|
||||
sinon.spy(IdentifyService, 'updatePeerProtocols')
|
||||
sinon.spy(remoteIdentify.registrar.peerStore, 'replace')
|
||||
sinon.spy(remoteIdentify.peerStore.addressBook, 'set')
|
||||
sinon.spy(remoteIdentify.peerStore.protoBook, 'set')
|
||||
|
||||
// Run identify
|
||||
await Promise.all([
|
||||
@@ -166,25 +198,24 @@ describe('Identify', () => {
|
||||
})
|
||||
])
|
||||
|
||||
expect(IdentifyService.updatePeerAddresses.callCount).to.equal(1)
|
||||
expect(IdentifyService.updatePeerProtocols.callCount).to.equal(1)
|
||||
|
||||
expect(remoteIdentify.registrar.peerStore.replace.callCount).to.equal(1)
|
||||
const [peerInfo] = remoteIdentify.registrar.peerStore.replace.firstCall.args
|
||||
expect(peerInfo.id.bytes).to.eql(localPeer.id.bytes)
|
||||
expect(peerInfo.multiaddrs.toArray()).to.eql([listeningAddr])
|
||||
expect(peerInfo.protocols).to.eql(localProtocols)
|
||||
expect(remoteIdentify.peerStore.addressBook.set.callCount).to.equal(1)
|
||||
expect(remoteIdentify.peerStore.protoBook.set.callCount).to.equal(1)
|
||||
const [peerId, multiaddrs] = remoteIdentify.peerStore.addressBook.set.firstCall.args
|
||||
expect(peerId.bytes).to.eql(localPeer.bytes)
|
||||
expect(multiaddrs).to.eql([listeningAddr])
|
||||
const [peerId2, protocols] = remoteIdentify.peerStore.protoBook.set.firstCall.args
|
||||
expect(peerId2.bytes).to.eql(localPeer.bytes)
|
||||
expect(protocols).to.eql(Array.from(localProtocols))
|
||||
})
|
||||
})
|
||||
|
||||
describe('libp2p.dialer.identifyService', () => {
|
||||
let peerInfo
|
||||
let peerId
|
||||
let libp2p
|
||||
let remoteLibp2p
|
||||
|
||||
before(async () => {
|
||||
const peerId = await PeerId.createFromJSON(Peers[0])
|
||||
peerInfo = new PeerInfo(peerId)
|
||||
peerId = await PeerId.createFromJSON(Peers[0])
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -200,17 +231,19 @@ describe('Identify', () => {
|
||||
it('should run identify automatically after connecting', async () => {
|
||||
libp2p = new Libp2p({
|
||||
...baseOptions,
|
||||
peerInfo
|
||||
peerId
|
||||
})
|
||||
|
||||
sinon.spy(libp2p.identifyService, 'identify')
|
||||
const peerStoreSpy = sinon.spy(libp2p.peerStore, 'replace')
|
||||
const peerStoreSpySet = sinon.spy(libp2p.peerStore.addressBook, 'set')
|
||||
const peerStoreSpyAdd = sinon.spy(libp2p.peerStore.addressBook, 'add')
|
||||
|
||||
const connection = await libp2p.dialer.connectToPeer(remoteAddr)
|
||||
expect(connection).to.exist()
|
||||
|
||||
// Wait for peer store to be updated
|
||||
await pWaitFor(() => peerStoreSpy.callCount === 1)
|
||||
// Dialer._createDialTarget (add), Connected (add), Identify (replace)
|
||||
await pWaitFor(() => peerStoreSpySet.callCount === 1 && peerStoreSpyAdd.callCount === 2)
|
||||
expect(libp2p.identifyService.identify.callCount).to.equal(1)
|
||||
|
||||
// The connection should have no open streams
|
||||
@@ -221,12 +254,11 @@ describe('Identify', () => {
|
||||
it('should push protocol updates to an already connected peer', async () => {
|
||||
libp2p = new Libp2p({
|
||||
...baseOptions,
|
||||
peerInfo
|
||||
peerId
|
||||
})
|
||||
|
||||
sinon.spy(libp2p.identifyService, 'identify')
|
||||
sinon.spy(libp2p.identifyService, 'push')
|
||||
sinon.spy(libp2p.peerStore, 'update')
|
||||
|
||||
const connection = await libp2p.dialer.connectToPeer(remoteAddr)
|
||||
expect(connection).to.exist()
|
||||
|
@@ -1,6 +1,7 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
|
@@ -74,7 +74,7 @@ describe('libp2p.metrics', () => {
|
||||
|
||||
remoteLibp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
|
||||
|
||||
const connection = await libp2p.dial(remoteLibp2p.peerInfo)
|
||||
const connection = await libp2p.dial(remoteLibp2p.peerId)
|
||||
const { stream } = await connection.newStream('/echo/1.0.0')
|
||||
|
||||
const bytes = randomBytes(512)
|
||||
@@ -109,6 +109,11 @@ describe('libp2p.metrics', () => {
|
||||
enabled: true,
|
||||
computeThrottleMaxQueueSize: 1, // compute after every message
|
||||
movingAverageIntervals: [10]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
autoDial: false
|
||||
}
|
||||
}
|
||||
}
|
||||
let remoteLibp2p
|
||||
@@ -116,7 +121,7 @@ describe('libp2p.metrics', () => {
|
||||
|
||||
remoteLibp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
|
||||
|
||||
const connection = await libp2p.dial(remoteLibp2p.peerInfo)
|
||||
const connection = await libp2p.dial(remoteLibp2p.peerId)
|
||||
const { stream } = await connection.newStream('/echo/1.0.0')
|
||||
|
||||
const bytes = randomBytes(512)
|
||||
|
@@ -7,6 +7,8 @@ chai.use(require('chai-as-promised'))
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
|
||||
const { randomBytes } = require('libp2p-crypto')
|
||||
const duplexPair = require('it-pair/duplex')
|
||||
const pipe = require('it-pipe')
|
||||
@@ -35,7 +37,8 @@ describe('Metrics', () => {
|
||||
const [local, remote] = duplexPair()
|
||||
const metrics = new Metrics({
|
||||
computeThrottleMaxQueueSize: 1, // compute after every message
|
||||
movingAverageIntervals: [10, 100, 1000]
|
||||
movingAverageIntervals: [10, 100, 1000],
|
||||
connectionManager: new EventEmitter()
|
||||
})
|
||||
|
||||
metrics.trackStream({
|
||||
@@ -70,7 +73,8 @@ describe('Metrics', () => {
|
||||
const [local, remote] = duplexPair()
|
||||
const metrics = new Metrics({
|
||||
computeThrottleMaxQueueSize: 1, // compute after every message
|
||||
movingAverageIntervals: [10, 100, 1000]
|
||||
movingAverageIntervals: [10, 100, 1000],
|
||||
connectionManager: new EventEmitter()
|
||||
})
|
||||
|
||||
metrics.trackStream({
|
||||
@@ -118,7 +122,8 @@ describe('Metrics', () => {
|
||||
const [local2, remote2] = duplexPair()
|
||||
const metrics = new Metrics({
|
||||
computeThrottleMaxQueueSize: 1, // compute after every message
|
||||
movingAverageIntervals: [10, 100, 1000]
|
||||
movingAverageIntervals: [10, 100, 1000],
|
||||
connectionManager: new EventEmitter()
|
||||
})
|
||||
const protocol = '/echo/1.0.0'
|
||||
metrics.start()
|
||||
@@ -173,7 +178,8 @@ describe('Metrics', () => {
|
||||
const [local, remote] = duplexPair()
|
||||
const metrics = new Metrics({
|
||||
computeThrottleMaxQueueSize: 1, // compute after every message
|
||||
movingAverageIntervals: [10, 100, 1000]
|
||||
movingAverageIntervals: [10, 100, 1000],
|
||||
connectionManager: new EventEmitter()
|
||||
})
|
||||
metrics.start()
|
||||
|
||||
@@ -228,7 +234,8 @@ describe('Metrics', () => {
|
||||
}))
|
||||
|
||||
const metrics = new Metrics({
|
||||
maxOldPeersRetention: 5 // Only keep track of 5
|
||||
maxOldPeersRetention: 5, // Only keep track of 5
|
||||
connectionManager: new EventEmitter()
|
||||
})
|
||||
|
||||
// Clone so trackedPeers isn't modified
|
||||
|
@@ -3,7 +3,8 @@
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
const defer = require('p-defer')
|
||||
const mergeOptions = require('merge-options')
|
||||
|
||||
@@ -15,34 +16,50 @@ const multiaddr = require('multiaddr')
|
||||
|
||||
const Libp2p = require('../../src')
|
||||
const baseOptions = require('../utils/base-options')
|
||||
const { createPeerInfo } = require('../utils/creators/peer')
|
||||
const { createPeerId } = require('../utils/creators/peer')
|
||||
|
||||
const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
|
||||
describe('peer discovery scenarios', () => {
|
||||
let peerInfo, remotePeerInfo1, remotePeerInfo2
|
||||
let peerId, remotePeerId1, remotePeerId2
|
||||
let libp2p
|
||||
|
||||
before(async () => {
|
||||
[peerInfo, remotePeerInfo1, remotePeerInfo2] = await createPeerInfo({ number: 3 })
|
||||
|
||||
peerInfo.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
|
||||
remotePeerInfo1.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
|
||||
remotePeerInfo2.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
|
||||
[peerId, remotePeerId1, remotePeerId2] = await createPeerId({ number: 3 })
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
libp2p && await libp2p.stop()
|
||||
})
|
||||
it('should ignore self on discovery', async () => {
|
||||
libp2p = new Libp2p(mergeOptions(baseOptions, {
|
||||
peerId,
|
||||
modules: {
|
||||
peerDiscovery: [MulticastDNS]
|
||||
}
|
||||
}))
|
||||
|
||||
await libp2p.start()
|
||||
const discoverySpy = sinon.spy()
|
||||
libp2p.on('peer:discovery', discoverySpy)
|
||||
libp2p._discovery.get('mdns').emit('peer', { id: libp2p.peerId })
|
||||
|
||||
expect(discoverySpy.called).to.eql(false)
|
||||
})
|
||||
|
||||
it('bootstrap should discover all peers in the list', async () => {
|
||||
const deferred = defer()
|
||||
|
||||
const bootstrappers = [
|
||||
...remotePeerInfo1.multiaddrs.toArray().map((ma) => `${ma}/p2p/${remotePeerInfo1.id.toB58String()}`),
|
||||
...remotePeerInfo2.multiaddrs.toArray().map((ma) => `${ma}/p2p/${remotePeerInfo2.id.toB58String()}`)
|
||||
`${listenAddr}/p2p/${remotePeerId1.toB58String()}`,
|
||||
`${listenAddr}/p2p/${remotePeerId2.toB58String()}`
|
||||
]
|
||||
|
||||
libp2p = new Libp2p(mergeOptions(baseOptions, {
|
||||
peerInfo,
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
modules: {
|
||||
peerDiscovery: [Bootstrap]
|
||||
},
|
||||
@@ -58,12 +75,12 @@ describe('peer discovery scenarios', () => {
|
||||
}))
|
||||
|
||||
const expectedPeers = new Set([
|
||||
remotePeerInfo1.id.toB58String(),
|
||||
remotePeerInfo2.id.toB58String()
|
||||
remotePeerId1.toB58String(),
|
||||
remotePeerId2.toB58String()
|
||||
])
|
||||
|
||||
libp2p.on('peer:discovery', (peerInfo) => {
|
||||
expectedPeers.delete(peerInfo.id.toB58String())
|
||||
libp2p.on('peer:discovery', (peerId) => {
|
||||
expectedPeers.delete(peerId.toB58String())
|
||||
if (expectedPeers.size === 0) {
|
||||
libp2p.removeAllListeners('peer:discovery')
|
||||
deferred.resolve()
|
||||
@@ -78,8 +95,11 @@ describe('peer discovery scenarios', () => {
|
||||
it('MulticastDNS should discover all peers on the local network', async () => {
|
||||
const deferred = defer()
|
||||
|
||||
const getConfig = (peerInfo) => mergeOptions(baseOptions, {
|
||||
peerInfo,
|
||||
const getConfig = (peerId) => mergeOptions(baseOptions, {
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
modules: {
|
||||
peerDiscovery: [MulticastDNS]
|
||||
},
|
||||
@@ -96,17 +116,17 @@ describe('peer discovery scenarios', () => {
|
||||
}
|
||||
})
|
||||
|
||||
libp2p = new Libp2p(getConfig(peerInfo))
|
||||
const remoteLibp2p1 = new Libp2p(getConfig(remotePeerInfo1))
|
||||
const remoteLibp2p2 = new Libp2p(getConfig(remotePeerInfo2))
|
||||
libp2p = new Libp2p(getConfig(peerId))
|
||||
const remoteLibp2p1 = new Libp2p(getConfig(remotePeerId1))
|
||||
const remoteLibp2p2 = new Libp2p(getConfig(remotePeerId2))
|
||||
|
||||
const expectedPeers = new Set([
|
||||
remotePeerInfo1.id.toB58String(),
|
||||
remotePeerInfo2.id.toB58String()
|
||||
remotePeerId1.toB58String(),
|
||||
remotePeerId2.toB58String()
|
||||
])
|
||||
|
||||
libp2p.on('peer:discovery', (peerInfo) => {
|
||||
expectedPeers.delete(peerInfo.id.toB58String())
|
||||
libp2p.on('peer:discovery', (peerId) => {
|
||||
expectedPeers.delete(peerId.toB58String())
|
||||
if (expectedPeers.size === 0) {
|
||||
libp2p.removeAllListeners('peer:discovery')
|
||||
deferred.resolve()
|
||||
@@ -128,8 +148,11 @@ describe('peer discovery scenarios', () => {
|
||||
it('kad-dht should discover other peers', async () => {
|
||||
const deferred = defer()
|
||||
|
||||
const getConfig = (peerInfo) => mergeOptions(baseOptions, {
|
||||
peerInfo,
|
||||
const getConfig = (peerId) => mergeOptions(baseOptions, {
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
modules: {
|
||||
dht: KadDht
|
||||
},
|
||||
@@ -149,16 +172,16 @@ describe('peer discovery scenarios', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const localConfig = getConfig(peerInfo)
|
||||
const localConfig = getConfig(peerId)
|
||||
// Only run random walk on our local node
|
||||
localConfig.config.dht.randomWalk.enabled = true
|
||||
libp2p = new Libp2p(localConfig)
|
||||
|
||||
const remoteLibp2p1 = new Libp2p(getConfig(remotePeerInfo1))
|
||||
const remoteLibp2p2 = new Libp2p(getConfig(remotePeerInfo2))
|
||||
const remoteLibp2p1 = new Libp2p(getConfig(remotePeerId1))
|
||||
const remoteLibp2p2 = new Libp2p(getConfig(remotePeerId2))
|
||||
|
||||
libp2p.on('peer:discovery', (peerInfo) => {
|
||||
if (peerInfo.id.toB58String() === remotePeerInfo2.id.toB58String()) {
|
||||
libp2p.on('peer:discovery', (peerId) => {
|
||||
if (peerId.toB58String() === remotePeerId1.toB58String()) {
|
||||
libp2p.removeAllListeners('peer:discovery')
|
||||
deferred.resolve()
|
||||
}
|
||||
@@ -170,12 +193,15 @@ describe('peer discovery scenarios', () => {
|
||||
remoteLibp2p2.start()
|
||||
])
|
||||
|
||||
libp2p.peerStore.addressBook.set(remotePeerId1, remoteLibp2p1.multiaddrs)
|
||||
remoteLibp2p2.peerStore.addressBook.set(remotePeerId1, remoteLibp2p1.multiaddrs)
|
||||
|
||||
// Topology:
|
||||
// A -> B
|
||||
// C -> B
|
||||
await Promise.all([
|
||||
libp2p.dial(remotePeerInfo1),
|
||||
remoteLibp2p2.dial(remotePeerInfo1)
|
||||
libp2p.dial(remotePeerId1),
|
||||
remoteLibp2p2.dial(remotePeerId1)
|
||||
])
|
||||
|
||||
await deferred.promise
|
||||
|
@@ -9,21 +9,21 @@ const sinon = require('sinon')
|
||||
const defer = require('p-defer')
|
||||
const mergeOptions = require('merge-options')
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
const WebRTCStar = require('libp2p-webrtc-star')
|
||||
|
||||
const Libp2p = require('../../src')
|
||||
const baseOptions = require('../utils/base-options.browser')
|
||||
const { createPeerInfo } = require('../utils/creators/peer')
|
||||
const { EventEmitter } = require('events')
|
||||
const { createPeerId } = require('../utils/creators/peer')
|
||||
|
||||
describe('peer discovery', () => {
|
||||
describe('basic functions', () => {
|
||||
let peerInfo
|
||||
let remotePeerInfo
|
||||
let peerId
|
||||
let remotePeerId
|
||||
let libp2p
|
||||
|
||||
before(async () => {
|
||||
[peerInfo, remotePeerInfo] = await createPeerInfo({ number: 2 })
|
||||
[peerId, remotePeerId] = await createPeerId({ number: 2 })
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -34,12 +34,14 @@ describe('peer discovery', () => {
|
||||
it('should dial know peers on startup', async () => {
|
||||
libp2p = new Libp2p({
|
||||
...baseOptions,
|
||||
peerInfo
|
||||
peerId
|
||||
})
|
||||
libp2p.peerStore.add(remotePeerInfo)
|
||||
|
||||
libp2p.peerStore.addressBook.set(remotePeerId, [multiaddr('/ip4/165.1.1.1/tcp/80')])
|
||||
|
||||
const deferred = defer()
|
||||
sinon.stub(libp2p.dialer, 'connectToPeer').callsFake((remotePeerInfo) => {
|
||||
expect(remotePeerInfo).to.equal(remotePeerInfo)
|
||||
sinon.stub(libp2p.dialer, 'connectToPeer').callsFake((remotePeerId) => {
|
||||
expect(remotePeerId).to.equal(remotePeerId)
|
||||
deferred.resolve()
|
||||
})
|
||||
const spy = sinon.spy()
|
||||
@@ -47,28 +49,9 @@ describe('peer discovery', () => {
|
||||
|
||||
libp2p.start()
|
||||
await deferred.promise
|
||||
expect(spy.getCall(0).args).to.eql([remotePeerInfo])
|
||||
})
|
||||
|
||||
it('should ignore self on discovery', async () => {
|
||||
const mockDiscovery = new EventEmitter()
|
||||
mockDiscovery.tag = 'mock'
|
||||
mockDiscovery.start = () => {}
|
||||
mockDiscovery.stop = () => {}
|
||||
|
||||
libp2p = new Libp2p(mergeOptions(baseOptions, {
|
||||
peerInfo,
|
||||
modules: {
|
||||
peerDiscovery: [mockDiscovery]
|
||||
}
|
||||
}))
|
||||
|
||||
await libp2p.start()
|
||||
const discoverySpy = sinon.spy()
|
||||
libp2p.on('peer:discovery', discoverySpy)
|
||||
libp2p._discovery.get('mock').emit('peer', libp2p.peerInfo)
|
||||
|
||||
expect(discoverySpy.called).to.eql(false)
|
||||
expect(spy.calledOnce).to.eql(true)
|
||||
expect(spy.getCall(0).args[0].toString()).to.eql(remotePeerId.toString())
|
||||
})
|
||||
|
||||
it('should stop discovery on libp2p start/stop', async () => {
|
||||
@@ -83,7 +66,7 @@ describe('peer discovery', () => {
|
||||
const stopSpy = sinon.spy(mockDiscovery, 'stop')
|
||||
|
||||
libp2p = new Libp2p(mergeOptions(baseOptions, {
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
peerDiscovery: [mockDiscovery]
|
||||
}
|
||||
@@ -99,15 +82,15 @@ describe('peer discovery', () => {
|
||||
})
|
||||
|
||||
describe('discovery modules from transports', () => {
|
||||
let peerInfo, libp2p
|
||||
let peerId, libp2p
|
||||
|
||||
before(async () => {
|
||||
[peerInfo] = await createPeerInfo()
|
||||
[peerId] = await createPeerId()
|
||||
})
|
||||
|
||||
it('should add discovery module if present in transports and enabled', async () => {
|
||||
libp2p = new Libp2p(mergeOptions(baseOptions, {
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [WebRTCStar]
|
||||
},
|
||||
@@ -128,7 +111,7 @@ describe('peer discovery', () => {
|
||||
|
||||
it('should not add discovery module if present in transports but disabled', async () => {
|
||||
libp2p = new Libp2p(mergeOptions(baseOptions, {
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [WebRTCStar]
|
||||
},
|
||||
|
@@ -44,7 +44,7 @@ describe('peer-routing', () => {
|
||||
|
||||
// Ring dial
|
||||
await Promise.all(
|
||||
nodes.map((peer, i) => peer.dial(nodes[(i + 1) % number].peerInfo))
|
||||
nodes.map((peer, i) => peer.dial(nodes[(i + 1) % number].peerId))
|
||||
)
|
||||
})
|
||||
|
||||
@@ -59,7 +59,7 @@ describe('peer-routing', () => {
|
||||
|
||||
sinon.stub(nodes[0]._dht, 'findPeer').callsFake(() => {
|
||||
deferred.resolve()
|
||||
return nodes[1].peerInfo
|
||||
return nodes[1].peerId
|
||||
})
|
||||
|
||||
nodes[0].peerRouting.findPeer()
|
||||
@@ -104,7 +104,7 @@ describe('peer-routing', () => {
|
||||
|
||||
sinon.stub(delegate, 'findPeer').callsFake(() => {
|
||||
deferred.resolve()
|
||||
return 'fake peer-info'
|
||||
return 'fake peer-id'
|
||||
})
|
||||
|
||||
await node.peerRouting.findPeer()
|
||||
@@ -115,17 +115,15 @@ describe('peer-routing', () => {
|
||||
const peerKey = 'QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL'
|
||||
const mockApi = nock('http://0.0.0.0:60197')
|
||||
.post('/api/v0/dht/findpeer')
|
||||
.query({
|
||||
arg: peerKey
|
||||
})
|
||||
.query(true)
|
||||
.reply(200, `{"Extra":"","ID":"some other id","Responses":null,"Type":0}\n{"Extra":"","ID":"","Responses":[{"Addrs":["/ip4/127.0.0.1/tcp/4001"],"ID":"${peerKey}"}],"Type":2}\n`, [
|
||||
'Content-Type', 'application/json',
|
||||
'X-Chunked-Output', '1'
|
||||
])
|
||||
|
||||
const peerInfo = await node.peerRouting.findPeer(peerKey)
|
||||
const peer = await node.peerRouting.findPeer(peerKey)
|
||||
|
||||
expect(peerInfo.id.toB58String()).to.equal(peerKey)
|
||||
expect(peer.id).to.equal(peerKey)
|
||||
expect(mockApi.isDone()).to.equal(true)
|
||||
})
|
||||
|
||||
@@ -133,9 +131,7 @@ describe('peer-routing', () => {
|
||||
const peerKey = 'key of a peer not on the network'
|
||||
const mockApi = nock('http://0.0.0.0:60197')
|
||||
.post('/api/v0/dht/findpeer')
|
||||
.query({
|
||||
arg: peerKey
|
||||
})
|
||||
.query(true)
|
||||
.reply(200, '{"Extra":"","ID":"some other id","Responses":null,"Type":6}\n{"Extra":"","ID":"yet another id","Responses":null,"Type":0}\n{"Extra":"routing:not found","ID":"","Responses":null,"Type":3}\n', [
|
||||
'Content-Type', 'application/json',
|
||||
'X-Chunked-Output', '1'
|
||||
@@ -151,9 +147,7 @@ describe('peer-routing', () => {
|
||||
const peerKey = 'key of a peer not on the network'
|
||||
const mockApi = nock('http://0.0.0.0:60197')
|
||||
.post('/api/v0/dht/findpeer')
|
||||
.query({
|
||||
arg: peerKey
|
||||
})
|
||||
.query(true)
|
||||
.reply(502)
|
||||
|
||||
await expect(node.peerRouting.findPeer(peerKey))
|
||||
@@ -194,7 +188,7 @@ describe('peer-routing', () => {
|
||||
|
||||
sinon.stub(node._dht, 'findPeer').callsFake(() => {
|
||||
dhtDeferred.resolve()
|
||||
return node.peerInfo
|
||||
return { id: node.peerId }
|
||||
})
|
||||
sinon.stub(delegate, 'findPeer').callsFake(() => {
|
||||
throw new Error('the delegate should not have been called')
|
||||
|
400
test/peer-store/address-book.spec.js
Normal file
400
test/peer-store/address-book.spec.js
Normal file
@@ -0,0 +1,400 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
|
||||
const pDefer = require('p-defer')
|
||||
const multiaddr = require('multiaddr')
|
||||
|
||||
const PeerStore = require('../../src/peer-store')
|
||||
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
const {
|
||||
codes: { ERR_INVALID_PARAMETERS }
|
||||
} = require('../../src/errors')
|
||||
|
||||
const addr1 = multiaddr('/ip4/127.0.0.1/tcp/8000')
|
||||
const addr2 = multiaddr('/ip4/127.0.0.1/tcp/8001')
|
||||
const addr3 = multiaddr('/ip4/127.0.0.1/tcp/8002')
|
||||
|
||||
const arraysAreEqual = (a, b) => a.length === b.length && a.sort().every((item, index) => b[index] === item)
|
||||
|
||||
describe('addressBook', () => {
|
||||
let peerId
|
||||
|
||||
before(async () => {
|
||||
[peerId] = await peerUtils.createPeerId()
|
||||
})
|
||||
|
||||
describe('addressBook.set', () => {
|
||||
let peerStore, ab
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
ab = peerStore.addressBook
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
peerStore.removeAllListeners()
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid PeerId is provided', () => {
|
||||
try {
|
||||
ab.set('invalid peerId')
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('invalid peerId should throw error')
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if no addresses provided', () => {
|
||||
try {
|
||||
ab.set(peerId)
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('no addresses should throw error')
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid multiaddrs are provided', () => {
|
||||
try {
|
||||
ab.set(peerId, ['invalid multiaddr'])
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('invalid multiaddrs should throw error')
|
||||
})
|
||||
|
||||
it('replaces the stored content by default and emit change event', () => {
|
||||
const defer = pDefer()
|
||||
const supportedMultiaddrs = [addr1, addr2]
|
||||
|
||||
peerStore.once('change:multiaddrs', ({ peerId, multiaddrs }) => {
|
||||
expect(peerId).to.exist()
|
||||
expect(multiaddrs).to.eql(supportedMultiaddrs)
|
||||
defer.resolve()
|
||||
})
|
||||
|
||||
ab.set(peerId, supportedMultiaddrs)
|
||||
const addresses = ab.get(peerId)
|
||||
const multiaddrs = addresses.map((mi) => mi.multiaddr)
|
||||
expect(multiaddrs).to.have.deep.members(supportedMultiaddrs)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
|
||||
it('emits on set if not storing the exact same content', async () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedMultiaddrsA = [addr1, addr2]
|
||||
const supportedMultiaddrsB = [addr2]
|
||||
|
||||
let changeCounter = 0
|
||||
peerStore.on('change:multiaddrs', () => {
|
||||
changeCounter++
|
||||
if (changeCounter > 1) {
|
||||
defer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// set 1
|
||||
ab.set(peerId, supportedMultiaddrsA)
|
||||
|
||||
// set 2 (same content)
|
||||
ab.set(peerId, supportedMultiaddrsB)
|
||||
const addresses = ab.get(peerId)
|
||||
const multiaddrs = addresses.map((mi) => mi.multiaddr)
|
||||
expect(multiaddrs).to.have.deep.members(supportedMultiaddrsB)
|
||||
|
||||
await defer.promise
|
||||
})
|
||||
|
||||
it('does not emit on set if it is storing the exact same content', async () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedMultiaddrs = [addr1, addr2]
|
||||
|
||||
let changeCounter = 0
|
||||
peerStore.on('change:multiaddrs', () => {
|
||||
changeCounter++
|
||||
if (changeCounter > 1) {
|
||||
defer.reject()
|
||||
}
|
||||
})
|
||||
|
||||
// set 1
|
||||
ab.set(peerId, supportedMultiaddrs)
|
||||
|
||||
// set 2 (same content)
|
||||
ab.set(peerId, supportedMultiaddrs)
|
||||
|
||||
// Wait 50ms for incorrect second event
|
||||
setTimeout(() => {
|
||||
defer.resolve()
|
||||
}, 50)
|
||||
|
||||
await defer.promise
|
||||
})
|
||||
})
|
||||
|
||||
describe('addressBook.add', () => {
|
||||
let peerStore, ab
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
ab = peerStore.addressBook
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
peerStore.removeAllListeners()
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid PeerId is provided', () => {
|
||||
try {
|
||||
ab.add('invalid peerId')
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('invalid peerId should throw error')
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if no addresses provided', () => {
|
||||
try {
|
||||
ab.add(peerId)
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('no addresses provided should throw error')
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid multiaddrs are provided', () => {
|
||||
try {
|
||||
ab.add(peerId, ['invalid multiaddr'])
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('invalid multiaddr should throw error')
|
||||
})
|
||||
|
||||
it('adds the new content and emits change event', () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedMultiaddrsA = [addr1, addr2]
|
||||
const supportedMultiaddrsB = [addr3]
|
||||
const finalMultiaddrs = supportedMultiaddrsA.concat(supportedMultiaddrsB)
|
||||
|
||||
let changeTrigger = 2
|
||||
peerStore.on('change:multiaddrs', ({ multiaddrs }) => {
|
||||
changeTrigger--
|
||||
if (changeTrigger === 0 && arraysAreEqual(multiaddrs, finalMultiaddrs)) {
|
||||
defer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// Replace
|
||||
ab.set(peerId, supportedMultiaddrsA)
|
||||
let addresses = ab.get(peerId)
|
||||
let multiaddrs = addresses.map((mi) => mi.multiaddr)
|
||||
expect(multiaddrs).to.have.deep.members(supportedMultiaddrsA)
|
||||
|
||||
// Add
|
||||
ab.add(peerId, supportedMultiaddrsB)
|
||||
addresses = ab.get(peerId)
|
||||
multiaddrs = addresses.map((mi) => mi.multiaddr)
|
||||
expect(multiaddrs).to.have.deep.members(finalMultiaddrs)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
|
||||
it('emits on add if the content to add not exists', async () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedMultiaddrsA = [addr1]
|
||||
const supportedMultiaddrsB = [addr2]
|
||||
const finalMultiaddrs = supportedMultiaddrsA.concat(supportedMultiaddrsB)
|
||||
|
||||
let changeCounter = 0
|
||||
peerStore.on('change:multiaddrs', () => {
|
||||
changeCounter++
|
||||
if (changeCounter > 1) {
|
||||
defer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// set 1
|
||||
ab.set(peerId, supportedMultiaddrsA)
|
||||
|
||||
// set 2 (content already existing)
|
||||
ab.add(peerId, supportedMultiaddrsB)
|
||||
const addresses = ab.get(peerId)
|
||||
const multiaddrs = addresses.map((mi) => mi.multiaddr)
|
||||
expect(multiaddrs).to.have.deep.members(finalMultiaddrs)
|
||||
|
||||
await defer.promise
|
||||
})
|
||||
|
||||
it('does not emit on add if the content to add already exists', async () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedMultiaddrsA = [addr1, addr2]
|
||||
const supportedMultiaddrsB = [addr2]
|
||||
|
||||
let changeCounter = 0
|
||||
peerStore.on('change:multiaddrs', () => {
|
||||
changeCounter++
|
||||
if (changeCounter > 1) {
|
||||
defer.reject()
|
||||
}
|
||||
})
|
||||
|
||||
// set 1
|
||||
ab.set(peerId, supportedMultiaddrsA)
|
||||
|
||||
// set 2 (content already existing)
|
||||
ab.add(peerId, supportedMultiaddrsB)
|
||||
|
||||
// Wait 50ms for incorrect second event
|
||||
setTimeout(() => {
|
||||
defer.resolve()
|
||||
}, 50)
|
||||
|
||||
await defer.promise
|
||||
})
|
||||
})
|
||||
|
||||
describe('addressBook.get', () => {
|
||||
let peerStore, ab
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
ab = peerStore.addressBook
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid PeerId is provided', () => {
|
||||
try {
|
||||
ab.get('invalid peerId')
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('invalid peerId should throw error')
|
||||
})
|
||||
|
||||
it('returns undefined if no multiaddrs are known for the provided peer', () => {
|
||||
const addresses = ab.get(peerId)
|
||||
|
||||
expect(addresses).to.not.exist()
|
||||
})
|
||||
|
||||
it('returns the multiaddrs stored', () => {
|
||||
const supportedMultiaddrs = [addr1, addr2]
|
||||
|
||||
ab.set(peerId, supportedMultiaddrs)
|
||||
|
||||
const addresses = ab.get(peerId)
|
||||
const multiaddrs = addresses.map((mi) => mi.multiaddr)
|
||||
expect(multiaddrs).to.have.deep.members(supportedMultiaddrs)
|
||||
})
|
||||
})
|
||||
|
||||
describe('addressBook.getMultiaddrsForPeer', () => {
|
||||
let peerStore, ab
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
ab = peerStore.addressBook
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid PeerId is provided', () => {
|
||||
try {
|
||||
ab.getMultiaddrsForPeer('invalid peerId')
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('invalid peerId should throw error')
|
||||
})
|
||||
|
||||
it('returns undefined if no multiaddrs are known for the provided peer', () => {
|
||||
const addresses = ab.getMultiaddrsForPeer(peerId)
|
||||
|
||||
expect(addresses).to.not.exist()
|
||||
})
|
||||
|
||||
it('returns the multiaddrs stored', () => {
|
||||
const supportedMultiaddrs = [addr1, addr2]
|
||||
|
||||
ab.set(peerId, supportedMultiaddrs)
|
||||
|
||||
const multiaddrs = ab.getMultiaddrsForPeer(peerId)
|
||||
multiaddrs.forEach((m) => {
|
||||
expect(m.getPeerId()).to.equal(peerId.toB58String())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('addressBook.delete', () => {
|
||||
let peerStore, ab
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
ab = peerStore.addressBook
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid PeerId is provided', () => {
|
||||
try {
|
||||
ab.delete('invalid peerId')
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('invalid peerId should throw error')
|
||||
})
|
||||
|
||||
it('returns false if no records exist for the peer and no event is emitted', () => {
|
||||
const defer = pDefer()
|
||||
|
||||
peerStore.on('change:multiaddrs', () => {
|
||||
defer.reject()
|
||||
})
|
||||
|
||||
const deleted = ab.delete(peerId)
|
||||
|
||||
expect(deleted).to.equal(false)
|
||||
|
||||
// Wait 50ms for incorrect invalid event
|
||||
setTimeout(() => {
|
||||
defer.resolve()
|
||||
}, 50)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
|
||||
it('returns true if the record exists and an event is emitted', () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedMultiaddrs = [addr1, addr2]
|
||||
ab.set(peerId, supportedMultiaddrs)
|
||||
|
||||
// Listen after set
|
||||
peerStore.on('change:multiaddrs', ({ multiaddrs }) => {
|
||||
expect(multiaddrs.length).to.eql(0)
|
||||
defer.resolve()
|
||||
})
|
||||
|
||||
const deleted = ab.delete(peerId)
|
||||
|
||||
expect(deleted).to.equal(true)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
})
|
||||
})
|
64
test/peer-store/key-book.spec.js
Normal file
64
test/peer-store/key-book.spec.js
Normal file
@@ -0,0 +1,64 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
chai.use(require('chai-bytes'))
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const PeerStore = require('../../src/peer-store')
|
||||
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
const {
|
||||
codes: { ERR_INVALID_PARAMETERS }
|
||||
} = require('../../src/errors')
|
||||
|
||||
describe('keyBook', () => {
|
||||
let peerId, peerStore, kb
|
||||
|
||||
beforeEach(async () => {
|
||||
[peerId] = await peerUtils.createPeerId()
|
||||
peerStore = new PeerStore()
|
||||
kb = peerStore.keyBook
|
||||
})
|
||||
|
||||
it('throws invalid parameters error if invalid PeerId is provided in set', () => {
|
||||
try {
|
||||
kb.set('invalid peerId')
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('invalid peerId should throw error')
|
||||
})
|
||||
|
||||
it('throws invalid parameters error if invalid PeerId is provided in get', () => {
|
||||
try {
|
||||
kb.get('invalid peerId')
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||
return
|
||||
}
|
||||
throw new Error('invalid peerId should throw error')
|
||||
})
|
||||
|
||||
it('stores the peerId in the book and returns the public key', () => {
|
||||
// Set PeerId
|
||||
kb.set(peerId, peerId.pubKey)
|
||||
|
||||
// Get public key
|
||||
const pubKey = kb.get(peerId)
|
||||
expect(peerId.pubKey.bytes).to.equalBytes(pubKey.bytes)
|
||||
})
|
||||
|
||||
it('should not store if already stored', () => {
|
||||
const spy = sinon.spy(kb, '_setData')
|
||||
|
||||
// Set PeerId
|
||||
kb.set(peerId, peerId.pubKey)
|
||||
kb.set(peerId, peerId.pubKey)
|
||||
|
||||
expect(spy).to.have.property('callCount', 1)
|
||||
})
|
||||
})
|
53
test/peer-store/peer-store.node.js
Normal file
53
test/peer-store/peer-store.node.js
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
chai.use(require('chai-bytes'))
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const baseOptions = require('../utils/base-options')
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
|
||||
describe('libp2p.peerStore', () => {
|
||||
let libp2p, remoteLibp2p
|
||||
|
||||
beforeEach(async () => {
|
||||
[libp2p, remoteLibp2p] = await peerUtils.createPeer({
|
||||
number: 2,
|
||||
populateAddressBooks: false,
|
||||
config: {
|
||||
...baseOptions
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('adds peer address to AddressBook and keys to the keybook when establishing connection', async () => {
|
||||
const idStr = libp2p.peerId.toB58String()
|
||||
const remoteIdStr = remoteLibp2p.peerId.toB58String()
|
||||
|
||||
const spyAddressBook = sinon.spy(libp2p.peerStore.addressBook, 'add')
|
||||
const spyKeyBook = sinon.spy(libp2p.peerStore.keyBook, 'set')
|
||||
|
||||
const remoteMultiaddr = `${remoteLibp2p.multiaddrs[0]}/p2p/${remoteIdStr}`
|
||||
const conn = await libp2p.dial(remoteMultiaddr)
|
||||
|
||||
expect(conn).to.exist()
|
||||
expect(spyAddressBook).to.have.property('called', true)
|
||||
expect(spyKeyBook).to.have.property('called', true)
|
||||
|
||||
const localPeers = libp2p.peerStore.peers
|
||||
expect(localPeers.size).to.equal(1)
|
||||
|
||||
// TODO: needs https://github.com/NodeFactoryIo/js-libp2p-noise/issues/58
|
||||
// const publicKeyInLocalPeer = localPeers.get(remoteIdStr).id.pubKey
|
||||
// expect(publicKeyInLocalPeer.bytes).to.equalBytes(remoteLibp2p.peerId.pubKey.bytes)
|
||||
|
||||
const remotePeers = remoteLibp2p.peerStore.peers
|
||||
expect(remotePeers.size).to.equal(1)
|
||||
const publicKeyInRemotePeer = remotePeers.get(idStr).id.pubKey
|
||||
expect(publicKeyInRemotePeer).to.exist()
|
||||
expect(publicKeyInRemotePeer.bytes).to.equalBytes(libp2p.peerId.pubKey.bytes)
|
||||
})
|
||||
})
|
@@ -4,185 +4,158 @@
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const pDefer = require('p-defer')
|
||||
|
||||
const PeerStore = require('../../src/peer-store')
|
||||
const multiaddr = require('multiaddr')
|
||||
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
|
||||
const addr = multiaddr('/ip4/127.0.0.1/tcp/8000')
|
||||
const addr1 = multiaddr('/ip4/127.0.0.1/tcp/8000')
|
||||
const addr2 = multiaddr('/ip4/127.0.0.1/tcp/8001')
|
||||
const addr3 = multiaddr('/ip4/127.0.0.1/tcp/8002')
|
||||
const addr4 = multiaddr('/ip4/127.0.0.1/tcp/8003')
|
||||
|
||||
const proto1 = '/protocol1'
|
||||
const proto2 = '/protocol2'
|
||||
const proto3 = '/protocol3'
|
||||
|
||||
describe('peer-store', () => {
|
||||
let peerStore
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
let peerIds
|
||||
before(async () => {
|
||||
peerIds = await peerUtils.createPeerId({
|
||||
number: 4
|
||||
})
|
||||
})
|
||||
|
||||
it('should add a new peer and emit it when it does not exist', async () => {
|
||||
const defer = pDefer()
|
||||
describe('empty books', () => {
|
||||
let peerStore
|
||||
|
||||
sinon.spy(peerStore, 'put')
|
||||
sinon.spy(peerStore, 'add')
|
||||
sinon.spy(peerStore, 'update')
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
})
|
||||
|
||||
const [peerInfo] = await peerUtils.createPeerInfo()
|
||||
it('has an empty map of peers', () => {
|
||||
const peers = peerStore.peers
|
||||
expect(peers.size).to.equal(0)
|
||||
})
|
||||
|
||||
peerStore.on('peer', (peer) => {
|
||||
it('returns false on trying to delete a non existant peerId', () => {
|
||||
const deleted = peerStore.delete(peerIds[0])
|
||||
expect(deleted).to.equal(false)
|
||||
})
|
||||
|
||||
it('returns undefined on trying to find a non existant peerId', () => {
|
||||
const peer = peerStore.get(peerIds[0])
|
||||
expect(peer).to.not.exist()
|
||||
})
|
||||
|
||||
it('sets the peer\'s public key to the KeyBook', () => {
|
||||
peerStore.keyBook.set(peerIds[0], peerIds[0].pubKey)
|
||||
|
||||
const pubKey = peerStore.keyBook.get(peerIds[0])
|
||||
expect(pubKey).to.exist()
|
||||
})
|
||||
})
|
||||
|
||||
describe('previously populated books', () => {
|
||||
let peerStore
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
|
||||
// Add peer0 with { addr1, addr2 } and { proto1 }
|
||||
peerStore.addressBook.set(peerIds[0], [addr1, addr2])
|
||||
peerStore.protoBook.set(peerIds[0], [proto1])
|
||||
|
||||
// Add peer1 with { addr3 } and { proto2, proto3 }
|
||||
peerStore.addressBook.set(peerIds[1], [addr3])
|
||||
peerStore.protoBook.set(peerIds[1], [proto2, proto3])
|
||||
|
||||
// Add peer2 with { addr4 }
|
||||
peerStore.addressBook.set(peerIds[2], [addr4])
|
||||
|
||||
// Add peer3 with { addr4 } and { proto2 }
|
||||
peerStore.addressBook.set(peerIds[3], [addr4])
|
||||
peerStore.protoBook.set(peerIds[3], [proto2])
|
||||
})
|
||||
|
||||
it('has peers', () => {
|
||||
const peers = peerStore.peers
|
||||
|
||||
expect(peers.size).to.equal(4)
|
||||
expect(Array.from(peers.keys())).to.have.members([
|
||||
peerIds[0].toB58String(),
|
||||
peerIds[1].toB58String(),
|
||||
peerIds[2].toB58String(),
|
||||
peerIds[3].toB58String()
|
||||
])
|
||||
})
|
||||
|
||||
it('returns true on deleting a stored peer', () => {
|
||||
const deleted = peerStore.delete(peerIds[0])
|
||||
expect(deleted).to.equal(true)
|
||||
|
||||
const peers = peerStore.peers
|
||||
expect(peers.size).to.equal(3)
|
||||
expect(Array.from(peers.keys())).to.not.have.members([peerIds[0].toB58String()])
|
||||
})
|
||||
|
||||
it('returns true on deleting a stored peer which is only on one book', () => {
|
||||
const deleted = peerStore.delete(peerIds[2])
|
||||
expect(deleted).to.equal(true)
|
||||
|
||||
const peers = peerStore.peers
|
||||
expect(peers.size).to.equal(3)
|
||||
})
|
||||
|
||||
it('gets the stored information of a peer in all its books', () => {
|
||||
const peer = peerStore.get(peerIds[0])
|
||||
expect(peer).to.exist()
|
||||
defer.resolve()
|
||||
})
|
||||
peerStore.put(peerInfo)
|
||||
expect(peer.protocols).to.have.members([proto1])
|
||||
|
||||
// Wait for peerStore to emit the peer
|
||||
await defer.promise
|
||||
const peerMultiaddrs = peer.addresses.map((mi) => mi.multiaddr)
|
||||
expect(peerMultiaddrs).to.have.members([addr1, addr2])
|
||||
|
||||
expect(peerStore.put.callCount).to.equal(1)
|
||||
expect(peerStore.add.callCount).to.equal(1)
|
||||
expect(peerStore.update.callCount).to.equal(0)
|
||||
})
|
||||
|
||||
it('should update peer when it is already in the store', async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo()
|
||||
|
||||
// Put the peer in the store
|
||||
peerStore.put(peerInfo)
|
||||
|
||||
sinon.spy(peerStore, 'add')
|
||||
sinon.spy(peerStore, 'update')
|
||||
|
||||
// When updating, peer event must not be emitted
|
||||
peerStore.on('peer', () => {
|
||||
throw new Error('should not emit twice')
|
||||
})
|
||||
// If no multiaddrs change, the event should not be emitted
|
||||
peerStore.on('change:multiaddrs', () => {
|
||||
throw new Error('should not emit change:multiaddrs')
|
||||
})
|
||||
// If no protocols change, the event should not be emitted
|
||||
peerStore.on('change:protocols', () => {
|
||||
throw new Error('should not emit change:protocols')
|
||||
expect(peer.id).to.exist()
|
||||
})
|
||||
|
||||
peerStore.put(peerInfo)
|
||||
it('gets the stored information of a peer that is not present in all its books', () => {
|
||||
const peers = peerStore.get(peerIds[2])
|
||||
expect(peers).to.exist()
|
||||
expect(peers.protocols.length).to.eql(0)
|
||||
|
||||
expect(peerStore.add.callCount).to.equal(0)
|
||||
expect(peerStore.update.callCount).to.equal(1)
|
||||
})
|
||||
|
||||
it('should emit the "change:multiaddrs" event when a peer has new multiaddrs', async () => {
|
||||
const defer = pDefer()
|
||||
const [createdPeerInfo] = await peerUtils.createPeerInfo()
|
||||
|
||||
// Put the peer in the store
|
||||
peerStore.put(createdPeerInfo)
|
||||
|
||||
// When updating, "change:multiaddrs" event must not be emitted
|
||||
peerStore.on('change:multiaddrs', ({ peerInfo, multiaddrs }) => {
|
||||
expect(peerInfo).to.exist()
|
||||
expect(peerInfo.id).to.eql(createdPeerInfo.id)
|
||||
expect(peerInfo.protocols).to.eql(createdPeerInfo.protocols)
|
||||
expect(multiaddrs).to.exist()
|
||||
expect(multiaddrs).to.eql(createdPeerInfo.multiaddrs.toArray())
|
||||
defer.resolve()
|
||||
})
|
||||
// If no protocols change, the event should not be emitted
|
||||
peerStore.on('change:protocols', () => {
|
||||
throw new Error('should not emit change:protocols')
|
||||
const peerMultiaddrs = peers.addresses.map((mi) => mi.multiaddr)
|
||||
expect(peerMultiaddrs).to.have.members([addr4])
|
||||
})
|
||||
|
||||
createdPeerInfo.multiaddrs.add(addr)
|
||||
peerStore.put(createdPeerInfo)
|
||||
it('can find all the peers supporting a protocol', () => {
|
||||
const peerSupporting2 = []
|
||||
|
||||
// Wait for peerStore to emit the event
|
||||
await defer.promise
|
||||
})
|
||||
for (const [, peer] of peerStore.peers.entries()) {
|
||||
if (peer.protocols.includes(proto2)) {
|
||||
peerSupporting2.push(peer)
|
||||
}
|
||||
}
|
||||
|
||||
it('should emit the "change:protocols" event when a peer has new protocols', async () => {
|
||||
const defer = pDefer()
|
||||
const [createdPeerInfo] = await peerUtils.createPeerInfo()
|
||||
|
||||
// Put the peer in the store
|
||||
peerStore.put(createdPeerInfo)
|
||||
|
||||
// If no multiaddrs change, the event should not be emitted
|
||||
peerStore.on('change:multiaddrs', () => {
|
||||
throw new Error('should not emit change:multiaddrs')
|
||||
})
|
||||
// When updating, "change:protocols" event must be emitted
|
||||
peerStore.on('change:protocols', ({ peerInfo, protocols }) => {
|
||||
expect(peerInfo).to.exist()
|
||||
expect(peerInfo.id).to.eql(createdPeerInfo.id)
|
||||
expect(peerInfo.multiaddrs).to.eql(createdPeerInfo.multiaddrs)
|
||||
expect(protocols).to.exist()
|
||||
expect(protocols).to.eql(Array.from(createdPeerInfo.protocols))
|
||||
defer.resolve()
|
||||
expect(peerSupporting2.length).to.eql(2)
|
||||
expect(peerSupporting2[0].id.toB58String()).to.eql(peerIds[1].toB58String())
|
||||
expect(peerSupporting2[1].id.toB58String()).to.eql(peerIds[3].toB58String())
|
||||
})
|
||||
|
||||
createdPeerInfo.protocols.add('/new-protocol/1.0.0')
|
||||
peerStore.put(createdPeerInfo)
|
||||
it('can find all the peers listening on a given address', () => {
|
||||
const peerListenint4 = []
|
||||
|
||||
// Wait for peerStore to emit the event
|
||||
await defer.promise
|
||||
})
|
||||
for (const [, peer] of peerStore.peers.entries()) {
|
||||
const multiaddrs = peer.addresses.map((mi) => mi.multiaddr)
|
||||
|
||||
it('should be able to retrieve a peer from store through its b58str id', async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo()
|
||||
const id = peerInfo.id
|
||||
if (multiaddrs.includes(addr4)) {
|
||||
peerListenint4.push(peer)
|
||||
}
|
||||
}
|
||||
|
||||
let retrievedPeer = peerStore.get(id)
|
||||
expect(retrievedPeer).to.not.exist()
|
||||
|
||||
// Put the peer in the store
|
||||
peerStore.put(peerInfo)
|
||||
|
||||
retrievedPeer = peerStore.get(id)
|
||||
expect(retrievedPeer).to.exist()
|
||||
expect(retrievedPeer.id).to.equal(peerInfo.id)
|
||||
expect(retrievedPeer.multiaddrs).to.eql(peerInfo.multiaddrs)
|
||||
expect(retrievedPeer.protocols).to.eql(peerInfo.protocols)
|
||||
})
|
||||
|
||||
it('should be able to remove a peer from store through its b58str id', async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo()
|
||||
const id = peerInfo.id
|
||||
|
||||
let removed = peerStore.remove(id)
|
||||
expect(removed).to.eql(false)
|
||||
|
||||
// Put the peer in the store
|
||||
peerStore.put(peerInfo)
|
||||
expect(peerStore.peers.size).to.equal(1)
|
||||
|
||||
removed = peerStore.remove(id)
|
||||
expect(removed).to.eql(true)
|
||||
expect(peerStore.peers.size).to.equal(0)
|
||||
})
|
||||
|
||||
it('should be able to get the multiaddrs for a peer', async () => {
|
||||
const [peerInfo, relayInfo] = await peerUtils.createPeerInfo({ number: 2 })
|
||||
const id = peerInfo.id
|
||||
const ma1 = multiaddr('/ip4/127.0.0.1/tcp/4001')
|
||||
const ma2 = multiaddr('/ip4/127.0.0.1/tcp/4002/ws')
|
||||
const ma3 = multiaddr(`/ip4/127.0.0.1/tcp/4003/ws/p2p/${relayInfo.id.toB58String()}/p2p-circuit`)
|
||||
|
||||
peerInfo.multiaddrs.add(ma1)
|
||||
peerInfo.multiaddrs.add(ma2)
|
||||
peerInfo.multiaddrs.add(ma3)
|
||||
|
||||
const multiaddrs = peerStore.multiaddrsForPeer(peerInfo)
|
||||
const expectedAddrs = [
|
||||
ma1.encapsulate(`/p2p/${id.toB58String()}`),
|
||||
ma2.encapsulate(`/p2p/${id.toB58String()}`),
|
||||
ma3.encapsulate(`/p2p/${id.toB58String()}`)
|
||||
]
|
||||
|
||||
expect(multiaddrs).to.eql(expectedAddrs)
|
||||
expect(peerListenint4.length).to.eql(2)
|
||||
expect(peerListenint4[0].id.toB58String()).to.eql(peerIds[2].toB58String())
|
||||
expect(peerListenint4[1].id.toB58String()).to.eql(peerIds[3].toB58String())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('peer-store on discovery', () => {
|
||||
// TODO: implement with discovery
|
||||
})
|
||||
|
380
test/peer-store/persisted-peer-store.spec.js
Normal file
380
test/peer-store/persisted-peer-store.spec.js
Normal file
@@ -0,0 +1,380 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const PeerStore = require('../../src/peer-store/persistent')
|
||||
const multiaddr = require('multiaddr')
|
||||
const { MemoryDatastore } = require('interface-datastore')
|
||||
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
|
||||
describe('Persisted PeerStore', () => {
|
||||
let datastore, peerStore
|
||||
|
||||
describe('start and stop flows', () => {
|
||||
beforeEach(() => {
|
||||
datastore = new MemoryDatastore()
|
||||
peerStore = new PeerStore({ datastore })
|
||||
})
|
||||
|
||||
afterEach(() => peerStore.stop())
|
||||
|
||||
it('should try to load content from an empty datastore on start', async () => {
|
||||
const spyQuery = sinon.spy(datastore, 'query')
|
||||
const spyProcessEntry = sinon.spy(peerStore, '_processDatastoreEntry')
|
||||
|
||||
await peerStore.start()
|
||||
expect(spyQuery).to.have.property('callCount', 1)
|
||||
expect(spyProcessEntry).to.have.property('callCount', 0)
|
||||
|
||||
// No data to populate
|
||||
expect(peerStore.peers.size).to.eq(0)
|
||||
})
|
||||
|
||||
it('should try to commit data on stop but should not add to batch if not exists', async () => {
|
||||
const spyDs = sinon.spy(peerStore, '_commitData')
|
||||
const spyBatch = sinon.spy(datastore, 'batch')
|
||||
|
||||
await peerStore.start()
|
||||
expect(spyDs).to.have.property('callCount', 0)
|
||||
|
||||
await peerStore.stop()
|
||||
expect(spyBatch).to.have.property('callCount', 0)
|
||||
expect(spyDs).to.have.property('callCount', 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('simple setup with content stored per change (threshold 1)', () => {
|
||||
beforeEach(() => {
|
||||
datastore = new MemoryDatastore()
|
||||
peerStore = new PeerStore({ datastore, threshold: 1 })
|
||||
})
|
||||
|
||||
afterEach(() => peerStore.stop())
|
||||
|
||||
it('should store peerStore content on datastore after peer marked as dirty (threshold 1)', async () => {
|
||||
const [peer] = await peerUtils.createPeerId({ number: 2 })
|
||||
const multiaddrs = [multiaddr('/ip4/156.10.1.22/tcp/1000')]
|
||||
const protocols = ['/ping/1.0.0']
|
||||
const spyDirty = sinon.spy(peerStore, '_addDirtyPeer')
|
||||
const spyDs = sinon.spy(datastore, 'batch')
|
||||
|
||||
await peerStore.start()
|
||||
|
||||
// AddressBook
|
||||
peerStore.addressBook.set(peer, multiaddrs)
|
||||
|
||||
expect(spyDirty).to.have.property('callCount', 1) // Address
|
||||
expect(spyDs).to.have.property('callCount', 1)
|
||||
|
||||
// ProtoBook
|
||||
peerStore.protoBook.set(peer, protocols)
|
||||
|
||||
expect(spyDirty).to.have.property('callCount', 2) // Protocol
|
||||
expect(spyDs).to.have.property('callCount', 2)
|
||||
|
||||
// Should have three peer records stored in the datastore
|
||||
const queryParams = {
|
||||
prefix: '/peers/'
|
||||
}
|
||||
|
||||
let count = 0
|
||||
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
|
||||
count++
|
||||
}
|
||||
expect(count).to.equal(2)
|
||||
|
||||
// Validate data
|
||||
const storedPeer = peerStore.get(peer)
|
||||
expect(storedPeer.id.toB58String()).to.eql(peer.toB58String())
|
||||
expect(storedPeer.protocols).to.have.members(protocols)
|
||||
expect(storedPeer.addresses.map((a) => a.multiaddr.toString())).to.have.members([multiaddrs[0].toString()])
|
||||
})
|
||||
|
||||
it('should load content to the peerStore when restart but not put in datastore again', async () => {
|
||||
const spyDs = sinon.spy(datastore, 'batch')
|
||||
const peers = await peerUtils.createPeerId({ number: 2 })
|
||||
const multiaddrs = [
|
||||
multiaddr('/ip4/156.10.1.22/tcp/1000'),
|
||||
multiaddr('/ip4/156.10.1.23/tcp/1000')
|
||||
]
|
||||
const protocols = ['/ping/1.0.0']
|
||||
|
||||
await peerStore.start()
|
||||
|
||||
// AddressBook
|
||||
peerStore.addressBook.set(peers[0], [multiaddrs[0]])
|
||||
peerStore.addressBook.set(peers[1], [multiaddrs[1]])
|
||||
|
||||
// KeyBook
|
||||
peerStore.keyBook.set(peers[0], peers[0].pubKey)
|
||||
peerStore.keyBook.set(peers[1], peers[1].pubKey)
|
||||
|
||||
// ProtoBook
|
||||
peerStore.protoBook.set(peers[0], protocols)
|
||||
peerStore.protoBook.set(peers[1], protocols)
|
||||
|
||||
expect(spyDs).to.have.property('callCount', 6) // 2 AddressBook + 2 KeyBook + 2 ProtoBook
|
||||
expect(peerStore.peers.size).to.equal(2)
|
||||
|
||||
await peerStore.stop()
|
||||
peerStore.keyBook.data.clear()
|
||||
peerStore.addressBook.data.clear()
|
||||
peerStore.protoBook.data.clear()
|
||||
|
||||
// Load on restart
|
||||
const spy = sinon.spy(peerStore, '_processDatastoreEntry')
|
||||
|
||||
await peerStore.start()
|
||||
|
||||
expect(spy).to.have.property('callCount', 6)
|
||||
expect(spyDs).to.have.property('callCount', 6)
|
||||
|
||||
expect(peerStore.peers.size).to.equal(2)
|
||||
expect(peerStore.addressBook.data.size).to.equal(2)
|
||||
expect(peerStore.keyBook.data.size).to.equal(2)
|
||||
expect(peerStore.protoBook.data.size).to.equal(2)
|
||||
})
|
||||
|
||||
it('should delete content from the datastore on delete', async () => {
|
||||
const [peer] = await peerUtils.createPeerId()
|
||||
const multiaddrs = [multiaddr('/ip4/156.10.1.22/tcp/1000')]
|
||||
const protocols = ['/ping/1.0.0']
|
||||
|
||||
await peerStore.start()
|
||||
|
||||
// AddressBook
|
||||
peerStore.addressBook.set(peer, multiaddrs)
|
||||
// ProtoBook
|
||||
peerStore.protoBook.set(peer, protocols)
|
||||
|
||||
const spyDs = sinon.spy(datastore, 'batch')
|
||||
const spyAddressBook = sinon.spy(peerStore.addressBook, 'delete')
|
||||
const spyKeyBook = sinon.spy(peerStore.keyBook, 'delete')
|
||||
const spyProtoBook = sinon.spy(peerStore.protoBook, 'delete')
|
||||
|
||||
// Delete from PeerStore
|
||||
peerStore.delete(peer)
|
||||
await peerStore.stop()
|
||||
|
||||
expect(spyAddressBook).to.have.property('callCount', 1)
|
||||
expect(spyKeyBook).to.have.property('callCount', 1)
|
||||
expect(spyProtoBook).to.have.property('callCount', 1)
|
||||
expect(spyDs).to.have.property('callCount', 2)
|
||||
|
||||
// Should have zero peer records stored in the datastore
|
||||
const queryParams = {
|
||||
prefix: '/peers/'
|
||||
}
|
||||
|
||||
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
|
||||
throw new Error('Datastore should be empty')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('setup with content not stored per change (threshold 2)', () => {
|
||||
beforeEach(() => {
|
||||
datastore = new MemoryDatastore()
|
||||
peerStore = new PeerStore({ datastore, threshold: 2 })
|
||||
})
|
||||
|
||||
afterEach(() => peerStore.stop())
|
||||
|
||||
it('should not commit until threshold is reached', async () => {
|
||||
const spyDirty = sinon.spy(peerStore, '_addDirtyPeer')
|
||||
const spyDs = sinon.spy(datastore, 'batch')
|
||||
|
||||
const peers = await peerUtils.createPeerId({ number: 2 })
|
||||
|
||||
const multiaddrs = [multiaddr('/ip4/156.10.1.22/tcp/1000')]
|
||||
const protocols = ['/ping/1.0.0']
|
||||
|
||||
await peerStore.start()
|
||||
|
||||
expect(spyDirty).to.have.property('callCount', 0)
|
||||
expect(spyDs).to.have.property('callCount', 0)
|
||||
|
||||
// Add Peer0 data in multiple books
|
||||
peerStore.addressBook.set(peers[0], multiaddrs)
|
||||
peerStore.protoBook.set(peers[0], protocols)
|
||||
|
||||
// Remove data from the same Peer
|
||||
peerStore.addressBook.delete(peers[0])
|
||||
|
||||
expect(spyDirty).to.have.property('callCount', 3) // 2 AddrBook ops, 1 ProtoBook op
|
||||
expect(peerStore._dirtyPeers.size).to.equal(1)
|
||||
expect(spyDs).to.have.property('callCount', 0)
|
||||
|
||||
const queryParams = {
|
||||
prefix: '/peers/'
|
||||
}
|
||||
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
|
||||
throw new Error('Datastore should be empty')
|
||||
}
|
||||
|
||||
// Add data for second book
|
||||
peerStore.addressBook.set(peers[1], multiaddrs)
|
||||
|
||||
expect(spyDirty).to.have.property('callCount', 4)
|
||||
expect(spyDs).to.have.property('callCount', 1)
|
||||
|
||||
// Should have two peer records stored in the datastore
|
||||
let count = 0
|
||||
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
|
||||
count++
|
||||
}
|
||||
expect(count).to.equal(2)
|
||||
expect(peerStore.peers.size).to.equal(2)
|
||||
})
|
||||
|
||||
it('should commit on stop if threshold was not reached', async () => {
|
||||
const spyDirty = sinon.spy(peerStore, '_addDirtyPeer')
|
||||
const spyDs = sinon.spy(datastore, 'batch')
|
||||
|
||||
const protocols = ['/ping/1.0.0']
|
||||
const [peer] = await peerUtils.createPeerId()
|
||||
|
||||
await peerStore.start()
|
||||
|
||||
// Add Peer data in a booka
|
||||
peerStore.protoBook.set(peer, protocols)
|
||||
|
||||
expect(spyDs).to.have.property('callCount', 0)
|
||||
expect(spyDirty).to.have.property('callCount', 1) // ProtoBook
|
||||
expect(peerStore._dirtyPeers.size).to.equal(1)
|
||||
|
||||
const queryParams = {
|
||||
prefix: '/peers/'
|
||||
}
|
||||
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
|
||||
throw new Error('Datastore should be empty')
|
||||
}
|
||||
|
||||
await peerStore.stop()
|
||||
|
||||
expect(spyDirty).to.have.property('callCount', 1)
|
||||
expect(spyDs).to.have.property('callCount', 1)
|
||||
expect(peerStore._dirtyPeers.size).to.equal(0) // Reset
|
||||
|
||||
// Should have one peer record stored in the datastore
|
||||
let count = 0
|
||||
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
|
||||
count++
|
||||
}
|
||||
expect(count).to.equal(1)
|
||||
expect(peerStore.peers.size).to.equal(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('libp2p.peerStore (Persisted)', () => {
|
||||
describe('disabled by default', () => {
|
||||
let libp2p
|
||||
|
||||
before(async () => {
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
started: false
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => libp2p.stop())
|
||||
|
||||
it('should not have have persistence capabilities', async () => {
|
||||
await libp2p.start()
|
||||
expect(libp2p.peerStore._dirtyPeers).to.not.exist()
|
||||
expect(libp2p.peerStore.threshold).to.not.exist()
|
||||
})
|
||||
})
|
||||
|
||||
describe('enabled', () => {
|
||||
let libp2p
|
||||
let memoryDatastore
|
||||
|
||||
beforeEach(async () => {
|
||||
memoryDatastore = new MemoryDatastore()
|
||||
;[libp2p] = await peerUtils.createPeer({
|
||||
started: false,
|
||||
config: {
|
||||
datastore: memoryDatastore,
|
||||
peerStore: {
|
||||
persistence: true,
|
||||
threshold: 2 // trigger on second peer changed
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => libp2p.stop())
|
||||
|
||||
it('should start on libp2p start and load content', async () => {
|
||||
const spyPeerStore = sinon.spy(libp2p.peerStore, 'start')
|
||||
const spyDs = sinon.spy(memoryDatastore, 'query')
|
||||
|
||||
await libp2p.start()
|
||||
expect(spyPeerStore).to.have.property('callCount', 1)
|
||||
expect(spyDs).to.have.property('callCount', 1)
|
||||
})
|
||||
|
||||
it('should load content to the peerStore when a new node is started with the same datastore', async () => {
|
||||
const peers = await peerUtils.createPeerId({ number: 2 })
|
||||
const multiaddrs = [
|
||||
multiaddr('/ip4/156.10.1.22/tcp/1000'),
|
||||
multiaddr('/ip4/156.10.1.23/tcp/1000')
|
||||
]
|
||||
const protocols = ['/ping/1.0.0']
|
||||
|
||||
await libp2p.start()
|
||||
|
||||
// AddressBook
|
||||
libp2p.peerStore.addressBook.set(peers[0], [multiaddrs[0]])
|
||||
libp2p.peerStore.addressBook.set(peers[1], [multiaddrs[1]])
|
||||
|
||||
// ProtoBook
|
||||
libp2p.peerStore.protoBook.set(peers[0], protocols)
|
||||
libp2p.peerStore.protoBook.set(peers[1], protocols)
|
||||
|
||||
expect(libp2p.peerStore.peers.size).to.equal(2)
|
||||
|
||||
await libp2p.stop()
|
||||
|
||||
// Use a new node with the previously populated datastore
|
||||
const [newNode] = await peerUtils.createPeer({
|
||||
started: false,
|
||||
config: {
|
||||
datastore: memoryDatastore,
|
||||
peerStore: {
|
||||
persistence: true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
expect(newNode.peerStore.peers.size).to.equal(0)
|
||||
|
||||
const spy = sinon.spy(newNode.peerStore, '_processDatastoreEntry')
|
||||
|
||||
await newNode.start()
|
||||
|
||||
expect(spy).to.have.property('callCount', 4) // 4 datastore entries
|
||||
|
||||
expect(newNode.peerStore.peers.size).to.equal(2)
|
||||
|
||||
// Validate data
|
||||
const peer0 = newNode.peerStore.get(peers[0])
|
||||
expect(peer0.id.toB58String()).to.eql(peers[0].toB58String())
|
||||
expect(peer0.protocols).to.have.members(protocols)
|
||||
expect(peer0.addresses.map((a) => a.multiaddr.toString())).to.have.members([multiaddrs[0].toString()])
|
||||
|
||||
const peer1 = newNode.peerStore.get(peers[1])
|
||||
expect(peer1.id.toB58String()).to.eql(peers[1].toB58String())
|
||||
expect(peer1.protocols).to.have.members(protocols)
|
||||
expect(peer1.addresses.map((a) => a.multiaddr.toString())).to.have.members([multiaddrs[1].toString()])
|
||||
|
||||
await newNode.stop()
|
||||
})
|
||||
})
|
||||
})
|
309
test/peer-store/proto-book.spec.js
Normal file
309
test/peer-store/proto-book.spec.js
Normal file
@@ -0,0 +1,309 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
|
||||
const pDefer = require('p-defer')
|
||||
|
||||
const PeerStore = require('../../src/peer-store')
|
||||
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
const {
|
||||
ERR_INVALID_PARAMETERS
|
||||
} = require('../../src/errors')
|
||||
|
||||
const arraysAreEqual = (a, b) => a.length === b.length && a.sort().every((item, index) => b[index] === item)
|
||||
|
||||
describe('protoBook', () => {
|
||||
let peerId
|
||||
|
||||
before(async () => {
|
||||
[peerId] = await peerUtils.createPeerId()
|
||||
})
|
||||
|
||||
describe('protoBook.set', () => {
|
||||
let peerStore, pb
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
pb = peerStore.protoBook
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
peerStore.removeAllListeners()
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid PeerId is provided', () => {
|
||||
expect(() => {
|
||||
pb.set('invalid peerId')
|
||||
}).to.throw(ERR_INVALID_PARAMETERS)
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if no protocols provided', () => {
|
||||
expect(() => {
|
||||
pb.set(peerId)
|
||||
}).to.throw(ERR_INVALID_PARAMETERS)
|
||||
})
|
||||
|
||||
it('replaces the stored content by default and emit change event', () => {
|
||||
const defer = pDefer()
|
||||
const supportedProtocols = ['protocol1', 'protocol2']
|
||||
|
||||
peerStore.once('change:protocols', ({ peerId, protocols }) => {
|
||||
expect(peerId).to.exist()
|
||||
expect(protocols).to.have.deep.members(supportedProtocols)
|
||||
defer.resolve()
|
||||
})
|
||||
|
||||
pb.set(peerId, supportedProtocols)
|
||||
const protocols = pb.get(peerId)
|
||||
expect(protocols).to.have.deep.members(supportedProtocols)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
|
||||
it('emits on set if not storing the exact same content', () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedProtocolsA = ['protocol1', 'protocol2']
|
||||
const supportedProtocolsB = ['protocol2']
|
||||
|
||||
let changeCounter = 0
|
||||
peerStore.on('change:protocols', () => {
|
||||
changeCounter++
|
||||
if (changeCounter > 1) {
|
||||
defer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// set 1
|
||||
pb.set(peerId, supportedProtocolsA)
|
||||
|
||||
// set 2 (same content)
|
||||
pb.set(peerId, supportedProtocolsB)
|
||||
const protocols = pb.get(peerId)
|
||||
expect(protocols).to.have.deep.members(supportedProtocolsB)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
|
||||
it('does not emit on set if it is storing the exact same content', () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedProtocols = ['protocol1', 'protocol2']
|
||||
|
||||
let changeCounter = 0
|
||||
peerStore.on('change:protocols', () => {
|
||||
changeCounter++
|
||||
if (changeCounter > 1) {
|
||||
defer.reject()
|
||||
}
|
||||
})
|
||||
|
||||
// set 1
|
||||
pb.set(peerId, supportedProtocols)
|
||||
|
||||
// set 2 (same content)
|
||||
pb.set(peerId, supportedProtocols)
|
||||
|
||||
// Wait 50ms for incorrect second event
|
||||
setTimeout(() => {
|
||||
defer.resolve()
|
||||
}, 50)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
})
|
||||
|
||||
describe('protoBook.add', () => {
|
||||
let peerStore, pb
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
pb = peerStore.protoBook
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
peerStore.removeAllListeners()
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid PeerId is provided', () => {
|
||||
expect(() => {
|
||||
pb.add('invalid peerId')
|
||||
}).to.throw(ERR_INVALID_PARAMETERS)
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if no protocols provided', () => {
|
||||
expect(() => {
|
||||
pb.add(peerId)
|
||||
}).to.throw(ERR_INVALID_PARAMETERS)
|
||||
})
|
||||
|
||||
it('adds the new content and emits change event', () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedProtocolsA = ['protocol1', 'protocol2']
|
||||
const supportedProtocolsB = ['protocol3']
|
||||
const finalProtocols = supportedProtocolsA.concat(supportedProtocolsB)
|
||||
|
||||
let changeTrigger = 2
|
||||
peerStore.on('change:protocols', ({ protocols }) => {
|
||||
changeTrigger--
|
||||
if (changeTrigger === 0 && arraysAreEqual(protocols, finalProtocols)) {
|
||||
defer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// Replace
|
||||
pb.set(peerId, supportedProtocolsA)
|
||||
let protocols = pb.get(peerId)
|
||||
expect(protocols).to.have.deep.members(supportedProtocolsA)
|
||||
|
||||
// Add
|
||||
pb.add(peerId, supportedProtocolsB)
|
||||
protocols = pb.get(peerId)
|
||||
expect(protocols).to.have.deep.members(finalProtocols)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
|
||||
it('emits on add if the content to add not exists', () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedProtocolsA = ['protocol1']
|
||||
const supportedProtocolsB = ['protocol2']
|
||||
const finalProtocols = supportedProtocolsA.concat(supportedProtocolsB)
|
||||
|
||||
let changeCounter = 0
|
||||
peerStore.on('change:protocols', () => {
|
||||
changeCounter++
|
||||
if (changeCounter > 1) {
|
||||
defer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// set 1
|
||||
pb.set(peerId, supportedProtocolsA)
|
||||
|
||||
// set 2 (content already existing)
|
||||
pb.add(peerId, supportedProtocolsB)
|
||||
const protocols = pb.get(peerId)
|
||||
expect(protocols).to.have.deep.members(finalProtocols)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
|
||||
it('does not emit on add if the content to add already exists', () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedProtocolsA = ['protocol1', 'protocol2']
|
||||
const supportedProtocolsB = ['protocol2']
|
||||
|
||||
let changeCounter = 0
|
||||
peerStore.on('change:protocols', () => {
|
||||
changeCounter++
|
||||
if (changeCounter > 1) {
|
||||
defer.reject()
|
||||
}
|
||||
})
|
||||
|
||||
// set 1
|
||||
pb.set(peerId, supportedProtocolsA)
|
||||
|
||||
// set 2 (content already existing)
|
||||
pb.add(peerId, supportedProtocolsB)
|
||||
|
||||
// Wait 50ms for incorrect second event
|
||||
setTimeout(() => {
|
||||
defer.resolve()
|
||||
}, 50)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
})
|
||||
|
||||
describe('protoBook.get', () => {
|
||||
let peerStore, pb
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
pb = peerStore.protoBook
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid PeerId is provided', () => {
|
||||
expect(() => {
|
||||
pb.get('invalid peerId')
|
||||
}).to.throw(ERR_INVALID_PARAMETERS)
|
||||
})
|
||||
|
||||
it('returns undefined if no protocols are known for the provided peer', () => {
|
||||
const protocols = pb.get(peerId)
|
||||
|
||||
expect(protocols).to.not.exist()
|
||||
})
|
||||
|
||||
it('returns the protocols stored', () => {
|
||||
const supportedProtocols = ['protocol1', 'protocol2']
|
||||
|
||||
pb.set(peerId, supportedProtocols)
|
||||
|
||||
const protocols = pb.get(peerId)
|
||||
expect(protocols).to.have.deep.members(supportedProtocols)
|
||||
})
|
||||
})
|
||||
|
||||
describe('protoBook.delete', () => {
|
||||
let peerStore, pb
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
pb = peerStore.protoBook
|
||||
})
|
||||
|
||||
it('throwns invalid parameters error if invalid PeerId is provided', () => {
|
||||
expect(() => {
|
||||
pb.delete('invalid peerId')
|
||||
}).to.throw(ERR_INVALID_PARAMETERS)
|
||||
})
|
||||
|
||||
it('returns false if no records exist for the peer and no event is emitted', () => {
|
||||
const defer = pDefer()
|
||||
|
||||
peerStore.on('change:protocols', () => {
|
||||
defer.reject()
|
||||
})
|
||||
|
||||
const deleted = pb.delete(peerId)
|
||||
|
||||
expect(deleted).to.equal(false)
|
||||
|
||||
// Wait 50ms for incorrect invalid event
|
||||
setTimeout(() => {
|
||||
defer.resolve()
|
||||
}, 50)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
|
||||
it('returns true if the record exists and an event is emitted', () => {
|
||||
const defer = pDefer()
|
||||
|
||||
const supportedProtocols = ['protocol1', 'protocol2']
|
||||
pb.set(peerId, supportedProtocols)
|
||||
|
||||
// Listen after set
|
||||
peerStore.on('change:protocols', ({ protocols }) => {
|
||||
expect(protocols.length).to.eql(0)
|
||||
defer.resolve()
|
||||
})
|
||||
|
||||
const deleted = pb.delete(peerId)
|
||||
|
||||
expect(deleted).to.equal(true)
|
||||
|
||||
return defer.promise
|
||||
})
|
||||
})
|
||||
})
|
@@ -1,6 +1,7 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
chai.use(dirtyChai)
|
||||
|
@@ -32,11 +32,13 @@ describe('Pubsub subsystem is configurable', () => {
|
||||
})
|
||||
|
||||
it('should start and stop by default once libp2p starts', async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo()
|
||||
peerInfo.multiaddrs.add(listenAddr)
|
||||
const [peerId] = await peerUtils.createPeerId()
|
||||
|
||||
const customOptions = mergeOptions(subsystemOptions, {
|
||||
peerInfo
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
}
|
||||
})
|
||||
|
||||
libp2p = await create(customOptions)
|
||||
@@ -50,11 +52,13 @@ describe('Pubsub subsystem is configurable', () => {
|
||||
})
|
||||
|
||||
it('should not start if disabled once libp2p starts', async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo()
|
||||
peerInfo.multiaddrs.add(listenAddr)
|
||||
const [peerId] = await peerUtils.createPeerId()
|
||||
|
||||
const customOptions = mergeOptions(subsystemOptions, {
|
||||
peerInfo,
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
config: {
|
||||
pubsub: {
|
||||
enabled: false
|
||||
@@ -70,11 +74,13 @@ describe('Pubsub subsystem is configurable', () => {
|
||||
})
|
||||
|
||||
it('should allow a manual start', async () => {
|
||||
const [peerInfo] = await peerUtils.createPeerInfo()
|
||||
peerInfo.multiaddrs.add(listenAddr)
|
||||
const [peerId] = await peerUtils.createPeerId()
|
||||
|
||||
const customOptions = mergeOptions(subsystemOptions, {
|
||||
peerInfo,
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
config: {
|
||||
pubsub: {
|
||||
enabled: false
|
||||
|
@@ -24,14 +24,11 @@ const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
const remoteListenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
|
||||
describe('Pubsub subsystem is able to use different implementations', () => {
|
||||
let peerInfo, remotePeerInfo
|
||||
let peerId, remotePeerId
|
||||
let libp2p, remoteLibp2p
|
||||
|
||||
beforeEach(async () => {
|
||||
[peerInfo, remotePeerInfo] = await peerUtils.createPeerInfo({ number: 2 })
|
||||
|
||||
peerInfo.multiaddrs.add(listenAddr)
|
||||
remotePeerInfo.multiaddrs.add(remoteListenAddr)
|
||||
[peerId, remotePeerId] = await peerUtils.createPeerId({ number: 2 })
|
||||
})
|
||||
|
||||
afterEach(() => Promise.all([
|
||||
@@ -53,14 +50,20 @@ describe('Pubsub subsystem is able to use different implementations', () => {
|
||||
const data = 'hey!'
|
||||
|
||||
libp2p = await create(mergeOptions(baseOptions, {
|
||||
peerInfo,
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
modules: {
|
||||
pubsub: pubsub
|
||||
}
|
||||
}))
|
||||
|
||||
remoteLibp2p = await create(mergeOptions(baseOptions, {
|
||||
peerInfo: remotePeerInfo,
|
||||
peerId: remotePeerId,
|
||||
addresses: {
|
||||
listen: [remoteListenAddr]
|
||||
},
|
||||
modules: {
|
||||
pubsub: pubsub
|
||||
}
|
||||
@@ -71,9 +74,10 @@ describe('Pubsub subsystem is able to use different implementations', () => {
|
||||
remoteLibp2p.start()
|
||||
])
|
||||
|
||||
const libp2pId = libp2p.peerInfo.id.toB58String()
|
||||
const libp2pId = libp2p.peerId.toB58String()
|
||||
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.multiaddrs)
|
||||
|
||||
const connection = await libp2p.dialProtocol(remotePeerInfo, multicodec)
|
||||
const connection = await libp2p.dialProtocol(remotePeerId, multicodec)
|
||||
expect(connection).to.exist()
|
||||
|
||||
libp2p.pubsub.subscribe(topic, (msg) => {
|
||||
|
@@ -19,30 +19,35 @@ const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
const remoteListenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
|
||||
describe('Pubsub subsystem operates correctly', () => {
|
||||
let peerInfo, remotePeerInfo
|
||||
let peerId, remotePeerId
|
||||
let libp2p, remoteLibp2p
|
||||
|
||||
beforeEach(async () => {
|
||||
[peerInfo, remotePeerInfo] = await peerUtils.createPeerInfo({ number: 2 })
|
||||
|
||||
peerInfo.multiaddrs.add(listenAddr)
|
||||
remotePeerInfo.multiaddrs.add(remoteListenAddr)
|
||||
[peerId, remotePeerId] = await peerUtils.createPeerId({ number: 2 })
|
||||
})
|
||||
|
||||
describe('pubsub started before connect', () => {
|
||||
beforeEach(async () => {
|
||||
libp2p = await create(mergeOptions(subsystemOptions, {
|
||||
peerInfo
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
}
|
||||
}))
|
||||
|
||||
remoteLibp2p = await create(mergeOptions(subsystemOptions, {
|
||||
peerInfo: remotePeerInfo
|
||||
peerId: remotePeerId,
|
||||
addresses: {
|
||||
listen: [remoteListenAddr]
|
||||
}
|
||||
}))
|
||||
|
||||
await Promise.all([
|
||||
libp2p.start(),
|
||||
remoteLibp2p.start()
|
||||
])
|
||||
|
||||
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.multiaddrs)
|
||||
})
|
||||
|
||||
afterEach(() => Promise.all([
|
||||
@@ -55,7 +60,7 @@ describe('Pubsub subsystem operates correctly', () => {
|
||||
})
|
||||
|
||||
it('should get notified of connected peers on dial', async () => {
|
||||
const connection = await libp2p.dialProtocol(remotePeerInfo, subsystemMulticodecs)
|
||||
const connection = await libp2p.dialProtocol(remotePeerId, subsystemMulticodecs)
|
||||
|
||||
expect(connection).to.exist()
|
||||
|
||||
@@ -69,9 +74,9 @@ describe('Pubsub subsystem operates correctly', () => {
|
||||
const defer = pDefer()
|
||||
const topic = 'test-topic'
|
||||
const data = 'hey!'
|
||||
const libp2pId = libp2p.peerInfo.id.toB58String()
|
||||
const libp2pId = libp2p.peerId.toB58String()
|
||||
|
||||
await libp2p.dialProtocol(remotePeerInfo, subsystemMulticodecs)
|
||||
await libp2p.dialProtocol(remotePeerId, subsystemMulticodecs)
|
||||
|
||||
let subscribedTopics = libp2p.pubsub.getTopics()
|
||||
expect(subscribedTopics).to.not.include(topic)
|
||||
@@ -98,11 +103,17 @@ describe('Pubsub subsystem operates correctly', () => {
|
||||
describe('pubsub started after connect', () => {
|
||||
beforeEach(async () => {
|
||||
libp2p = await create(mergeOptions(subsystemOptions, {
|
||||
peerInfo
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
}
|
||||
}))
|
||||
|
||||
remoteLibp2p = await create(mergeOptions(subsystemOptions, {
|
||||
peerInfo: remotePeerInfo,
|
||||
peerId: remotePeerId,
|
||||
addresses: {
|
||||
listen: [remoteListenAddr]
|
||||
},
|
||||
config: {
|
||||
pubsub: {
|
||||
enabled: false
|
||||
@@ -112,6 +123,8 @@ describe('Pubsub subsystem operates correctly', () => {
|
||||
|
||||
await libp2p.start()
|
||||
await remoteLibp2p.start()
|
||||
|
||||
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.multiaddrs)
|
||||
})
|
||||
|
||||
afterEach(() => Promise.all([
|
||||
@@ -124,7 +137,7 @@ describe('Pubsub subsystem operates correctly', () => {
|
||||
})
|
||||
|
||||
it('should get notified of connected peers after starting', async () => {
|
||||
const connection = await libp2p.dial(remotePeerInfo)
|
||||
const connection = await libp2p.dial(remotePeerId)
|
||||
|
||||
expect(connection).to.exist()
|
||||
expect(libp2p.pubsub._pubsub.peers.size).to.be.eql(0)
|
||||
@@ -141,11 +154,11 @@ describe('Pubsub subsystem operates correctly', () => {
|
||||
it('should receive pubsub messages', async function () {
|
||||
this.timeout(10e3)
|
||||
const defer = pDefer()
|
||||
const libp2pId = libp2p.peerInfo.id.toB58String()
|
||||
const libp2pId = libp2p.peerId.toB58String()
|
||||
const topic = 'test-topic'
|
||||
const data = 'hey!'
|
||||
|
||||
await libp2p.dial(remotePeerInfo)
|
||||
await libp2p.dial(remotePeerId)
|
||||
|
||||
remoteLibp2p.pubsub.start()
|
||||
|
||||
|
@@ -1,72 +0,0 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const mergeOptions = require('merge-options')
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
const Libp2p = require('../../src')
|
||||
|
||||
const baseOptions = require('../utils/base-options')
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
|
||||
describe('registrar on dial', () => {
|
||||
let peerInfo
|
||||
let remotePeerInfo
|
||||
let libp2p
|
||||
let remoteLibp2p
|
||||
let remoteAddr
|
||||
|
||||
before(async () => {
|
||||
[peerInfo, remotePeerInfo] = await peerUtils.createPeerInfo({ number: 2 })
|
||||
remoteLibp2p = new Libp2p(mergeOptions(baseOptions, {
|
||||
peerInfo: remotePeerInfo
|
||||
}))
|
||||
|
||||
await remoteLibp2p.transportManager.listen([listenAddr])
|
||||
remoteAddr = remoteLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${remotePeerInfo.id.toB58String()}`)
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
sinon.restore()
|
||||
await remoteLibp2p.stop()
|
||||
libp2p && await libp2p.stop()
|
||||
})
|
||||
|
||||
it('should inform registrar of a new connection', async () => {
|
||||
libp2p = new Libp2p(mergeOptions(baseOptions, {
|
||||
peerInfo
|
||||
}))
|
||||
|
||||
sinon.spy(remoteLibp2p.registrar, 'onConnect')
|
||||
|
||||
await libp2p.dial(remoteAddr)
|
||||
expect(remoteLibp2p.registrar.onConnect.callCount).to.equal(1)
|
||||
|
||||
const libp2pConn = libp2p.registrar.getConnection(remotePeerInfo)
|
||||
expect(libp2pConn).to.exist()
|
||||
|
||||
const remoteConn = remoteLibp2p.registrar.getConnection(peerInfo)
|
||||
expect(remoteConn).to.exist()
|
||||
})
|
||||
|
||||
it('should be closed on libp2p stop', async () => {
|
||||
libp2p = new Libp2p(mergeOptions(baseOptions, {
|
||||
peerInfo
|
||||
}))
|
||||
|
||||
await libp2p.dial(remoteAddr)
|
||||
expect(libp2p.connections.size).to.equal(1)
|
||||
|
||||
sinon.spy(libp2p.registrar, 'close')
|
||||
|
||||
await libp2p.stop()
|
||||
expect(libp2p.registrar.close.callCount).to.equal(1)
|
||||
expect(libp2p.connections.size).to.equal(0)
|
||||
})
|
||||
})
|
@@ -6,22 +6,26 @@ chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
const pDefer = require('p-defer')
|
||||
|
||||
const PeerInfo = require('peer-info')
|
||||
const { EventEmitter } = require('events')
|
||||
|
||||
const Topology = require('libp2p-interfaces/src/topology/multicodec-topology')
|
||||
const PeerStore = require('../../src/peer-store')
|
||||
const Registrar = require('../../src/registrar')
|
||||
const { createMockConnection } = require('./utils')
|
||||
|
||||
const createMockConnection = require('../utils/mockConnection')
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
const baseOptions = require('../utils/base-options.browser')
|
||||
|
||||
const multicodec = '/test/1.0.0'
|
||||
|
||||
describe('registrar', () => {
|
||||
let peerStore, registrar
|
||||
let peerStore
|
||||
let registrar
|
||||
|
||||
describe('errors', () => {
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
registrar = new Registrar({ peerStore })
|
||||
registrar = new Registrar({ peerStore, connectionManager: new EventEmitter() })
|
||||
})
|
||||
|
||||
it('should fail to register a protocol if no multicodec is provided', () => {
|
||||
@@ -37,11 +41,19 @@ describe('registrar', () => {
|
||||
})
|
||||
|
||||
describe('registration', () => {
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore()
|
||||
registrar = new Registrar({ peerStore })
|
||||
let libp2p
|
||||
|
||||
beforeEach(async () => {
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
modules: baseOptions.modules
|
||||
},
|
||||
started: false
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => libp2p.stop())
|
||||
|
||||
it('should be able to register a protocol', () => {
|
||||
const topologyProps = new Topology({
|
||||
multicodecs: multicodec,
|
||||
@@ -51,7 +63,7 @@ describe('registrar', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const identifier = registrar.register(topologyProps)
|
||||
const identifier = libp2p.registrar.register(topologyProps)
|
||||
|
||||
expect(identifier).to.exist()
|
||||
})
|
||||
@@ -65,14 +77,14 @@ describe('registrar', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const identifier = registrar.register(topologyProps)
|
||||
const success = registrar.unregister(identifier)
|
||||
const identifier = libp2p.registrar.register(topologyProps)
|
||||
const success = libp2p.registrar.unregister(identifier)
|
||||
|
||||
expect(success).to.eql(true)
|
||||
})
|
||||
|
||||
it('should fail to unregister if no register was made', () => {
|
||||
const success = registrar.unregister('bad-identifier')
|
||||
const success = libp2p.registrar.unregister('bad-identifier')
|
||||
|
||||
expect(success).to.eql(false)
|
||||
})
|
||||
@@ -83,27 +95,25 @@ describe('registrar', () => {
|
||||
|
||||
// Setup connections before registrar
|
||||
const conn = await createMockConnection()
|
||||
const remotePeerInfo = await PeerInfo.create(conn.remotePeer)
|
||||
const remotePeerId = conn.remotePeer
|
||||
|
||||
// Add protocol to peer
|
||||
remotePeerInfo.protocols.add(multicodec)
|
||||
// Add connected peer with protocol to peerStore and registrar
|
||||
libp2p.peerStore.protoBook.add(remotePeerId, [multicodec])
|
||||
|
||||
// Add connected peer to peerStore and registrar
|
||||
peerStore.put(remotePeerInfo)
|
||||
registrar.onConnect(remotePeerInfo, conn)
|
||||
expect(registrar.connections.size).to.eql(1)
|
||||
libp2p.connectionManager.onConnect(conn)
|
||||
expect(libp2p.connectionManager.size).to.eql(1)
|
||||
|
||||
const topologyProps = new Topology({
|
||||
multicodecs: multicodec,
|
||||
handlers: {
|
||||
onConnect: (peerInfo, connection) => {
|
||||
expect(peerInfo.id.toB58String()).to.eql(remotePeerInfo.id.toB58String())
|
||||
onConnect: (peerId, connection) => {
|
||||
expect(peerId.toB58String()).to.eql(remotePeerId.toB58String())
|
||||
expect(connection.id).to.eql(conn.id)
|
||||
|
||||
onConnectDefer.resolve()
|
||||
},
|
||||
onDisconnect: (peerInfo) => {
|
||||
expect(peerInfo.id.toB58String()).to.eql(remotePeerInfo.id.toB58String())
|
||||
onDisconnect: (peerId) => {
|
||||
expect(peerId.toB58String()).to.eql(remotePeerId.toB58String())
|
||||
|
||||
onDisconnectDefer.resolve()
|
||||
}
|
||||
@@ -111,14 +121,16 @@ describe('registrar', () => {
|
||||
})
|
||||
|
||||
// Register protocol
|
||||
const identifier = registrar.register(topologyProps)
|
||||
const topology = registrar.topologies.get(identifier)
|
||||
const identifier = libp2p.registrar.register(topologyProps)
|
||||
const topology = libp2p.registrar.topologies.get(identifier)
|
||||
|
||||
// Topology created
|
||||
expect(topology).to.exist()
|
||||
|
||||
registrar.onDisconnect(remotePeerInfo)
|
||||
expect(registrar.connections.size).to.eql(0)
|
||||
await conn.close()
|
||||
|
||||
libp2p.connectionManager.onDisconnect(conn)
|
||||
expect(libp2p.connectionManager.size).to.eql(0)
|
||||
|
||||
// Wait for handlers to be called
|
||||
return Promise.all([
|
||||
@@ -144,71 +156,30 @@ describe('registrar', () => {
|
||||
})
|
||||
|
||||
// Register protocol
|
||||
const identifier = registrar.register(topologyProps)
|
||||
const topology = registrar.topologies.get(identifier)
|
||||
const identifier = libp2p.registrar.register(topologyProps)
|
||||
const topology = libp2p.registrar.topologies.get(identifier)
|
||||
|
||||
// Topology created
|
||||
expect(topology).to.exist()
|
||||
expect(registrar.connections.size).to.eql(0)
|
||||
expect(libp2p.connectionManager.size).to.eql(0)
|
||||
|
||||
// Setup connections before registrar
|
||||
const conn = await createMockConnection()
|
||||
const peerInfo = await PeerInfo.create(conn.remotePeer)
|
||||
const remotePeerId = conn.remotePeer
|
||||
|
||||
// Add connected peer to peerStore and registrar
|
||||
peerStore.put(peerInfo)
|
||||
registrar.onConnect(peerInfo, conn)
|
||||
libp2p.peerStore.protoBook.set(remotePeerId, [])
|
||||
libp2p.connectionManager.onConnect(conn)
|
||||
|
||||
// Add protocol to peer and update it
|
||||
peerInfo.protocols.add(multicodec)
|
||||
peerStore.put(peerInfo)
|
||||
libp2p.peerStore.protoBook.add(remotePeerId, [multicodec])
|
||||
|
||||
await onConnectDefer.promise
|
||||
|
||||
// Remove protocol to peer and update it
|
||||
peerInfo.protocols.delete(multicodec)
|
||||
peerStore.replace(peerInfo)
|
||||
libp2p.peerStore.protoBook.set(remotePeerId, [])
|
||||
|
||||
await onDisconnectDefer.promise
|
||||
})
|
||||
|
||||
it('should filter connections on disconnect, removing the closed one', async () => {
|
||||
const onDisconnectDefer = pDefer()
|
||||
|
||||
const topologyProps = new Topology({
|
||||
multicodecs: multicodec,
|
||||
handlers: {
|
||||
onConnect: () => {},
|
||||
onDisconnect: () => {
|
||||
onDisconnectDefer.resolve()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Register protocol
|
||||
registrar.register(topologyProps)
|
||||
|
||||
// Setup connections before registrar
|
||||
const [localPeer, remotePeer] = await peerUtils.createPeerInfo({ number: 2 })
|
||||
|
||||
const conn1 = await createMockConnection({ localPeer: localPeer.id, remotePeer: remotePeer.id })
|
||||
const conn2 = await createMockConnection({ localPeer: localPeer.id, remotePeer: remotePeer.id })
|
||||
const peerInfo = await PeerInfo.create(remotePeer.id)
|
||||
const id = peerInfo.id.toB58String()
|
||||
|
||||
// Add connection to registrar
|
||||
peerStore.put(peerInfo)
|
||||
registrar.onConnect(peerInfo, conn1)
|
||||
registrar.onConnect(peerInfo, conn2)
|
||||
|
||||
expect(registrar.connections.get(id).length).to.eql(2)
|
||||
|
||||
conn2._stat.status = 'closed'
|
||||
registrar.onDisconnect(peerInfo, conn2)
|
||||
|
||||
const peerConnections = registrar.connections.get(id)
|
||||
expect(peerConnections.length).to.eql(1)
|
||||
expect(peerConnections[0]._stat.status).to.eql('open')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -1,51 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const { Connection } = require('libp2p-interfaces/src/connection')
|
||||
const multiaddr = require('multiaddr')
|
||||
|
||||
const pair = require('it-pair')
|
||||
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
|
||||
module.exports.createMockConnection = async (properties = {}) => {
|
||||
const localAddr = multiaddr('/ip4/127.0.0.1/tcp/8080')
|
||||
const remoteAddr = multiaddr('/ip4/127.0.0.1/tcp/8081')
|
||||
|
||||
const [localPeer, remotePeer] = await peerUtils.createPeerInfo({ number: 2 })
|
||||
const openStreams = []
|
||||
let streamId = 0
|
||||
|
||||
return new Connection({
|
||||
localPeer: localPeer.id,
|
||||
remotePeer: remotePeer.id,
|
||||
localAddr,
|
||||
remoteAddr,
|
||||
stat: {
|
||||
timeline: {
|
||||
open: Date.now() - 10,
|
||||
upgraded: Date.now()
|
||||
},
|
||||
direction: 'outbound',
|
||||
encryption: '/secio/1.0.0',
|
||||
multiplexer: '/mplex/6.7.0',
|
||||
status: 'open'
|
||||
},
|
||||
newStream: (protocols) => {
|
||||
const id = streamId++
|
||||
const stream = pair()
|
||||
|
||||
stream.close = () => stream.sink([])
|
||||
stream.id = id
|
||||
|
||||
openStreams.push(stream)
|
||||
|
||||
return {
|
||||
stream,
|
||||
protocol: protocols[0]
|
||||
}
|
||||
},
|
||||
close: () => { },
|
||||
getStreams: () => openStreams,
|
||||
...properties
|
||||
})
|
||||
}
|
@@ -4,6 +4,8 @@
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
|
||||
const AddressManager = require('../../src/address-manager')
|
||||
const TransportManager = require('../../src/transport-manager')
|
||||
const Transport = require('libp2p-tcp')
|
||||
const multiaddr = require('multiaddr')
|
||||
@@ -18,7 +20,9 @@ describe('Transport Manager (TCP)', () => {
|
||||
|
||||
before(() => {
|
||||
tm = new TransportManager({
|
||||
libp2p: {},
|
||||
libp2p: {
|
||||
addressManager: new AddressManager({ listen: addrs })
|
||||
},
|
||||
upgrader: mockUpgrader,
|
||||
onConnection: () => {}
|
||||
})
|
||||
@@ -37,7 +41,7 @@ describe('Transport Manager (TCP)', () => {
|
||||
|
||||
it('should be able to listen', async () => {
|
||||
tm.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
await tm.listen(addrs)
|
||||
await tm.listen()
|
||||
expect(tm._listeners).to.have.key(Transport.prototype[Symbol.toStringTag])
|
||||
expect(tm._listeners.get(Transport.prototype[Symbol.toStringTag])).to.have.length(addrs.length)
|
||||
// Ephemeral ip addresses may result in multiple listeners
|
||||
@@ -48,7 +52,7 @@ describe('Transport Manager (TCP)', () => {
|
||||
|
||||
it('should be able to dial', async () => {
|
||||
tm.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
await tm.listen(addrs)
|
||||
await tm.listen()
|
||||
const addr = tm.getAddrs().shift()
|
||||
const connection = await tm.dial(addr)
|
||||
expect(connection).to.exist()
|
||||
|
@@ -9,6 +9,7 @@ const sinon = require('sinon')
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
const Transport = require('libp2p-websockets')
|
||||
const AddressManager = require('../../src/address-manager')
|
||||
const TransportManager = require('../../src/transport-manager')
|
||||
const mockUpgrader = require('../utils/mockUpgrader')
|
||||
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
|
||||
@@ -16,14 +17,17 @@ const { codes: ErrorCodes } = require('../../src/errors')
|
||||
const Libp2p = require('../../src')
|
||||
const Peers = require('../fixtures/peers')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
|
||||
const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
|
||||
describe('Transport Manager (WebSockets)', () => {
|
||||
let tm
|
||||
|
||||
before(() => {
|
||||
tm = new TransportManager({
|
||||
libp2p: {},
|
||||
libp2p: {
|
||||
addressManager: new AddressManager({ listen: [listenAddr] })
|
||||
},
|
||||
upgrader: mockUpgrader,
|
||||
onConnection: () => {}
|
||||
})
|
||||
@@ -79,21 +83,19 @@ describe('Transport Manager (WebSockets)', () => {
|
||||
|
||||
it('should fail to listen with no valid address', async () => {
|
||||
tm.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
const addrs = [multiaddr('/ip4/127.0.0.1/tcp/0')]
|
||||
|
||||
await expect(tm.listen(addrs))
|
||||
await expect(tm.listen())
|
||||
.to.eventually.be.rejected()
|
||||
.and.to.have.property('code', ErrorCodes.ERR_NO_VALID_ADDRESSES)
|
||||
})
|
||||
})
|
||||
|
||||
describe('libp2p.transportManager', () => {
|
||||
let peerInfo
|
||||
let peerId
|
||||
let libp2p
|
||||
|
||||
before(async () => {
|
||||
const peerId = await PeerId.createFromJSON(Peers[0])
|
||||
peerInfo = new PeerInfo(peerId)
|
||||
peerId = await PeerId.createFromJSON(Peers[0])
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -104,7 +106,7 @@ describe('libp2p.transportManager', () => {
|
||||
|
||||
it('should create a TransportManager', () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport]
|
||||
}
|
||||
@@ -122,7 +124,7 @@ describe('libp2p.transportManager', () => {
|
||||
another: 'value'
|
||||
}
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [spy]
|
||||
},
|
||||
@@ -146,7 +148,7 @@ describe('libp2p.transportManager', () => {
|
||||
|
||||
it('starting and stopping libp2p should start and stop TransportManager', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo,
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport]
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
@@ -8,12 +9,11 @@ const sinon = require('sinon')
|
||||
const Muxer = require('libp2p-mplex')
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const pipe = require('it-pipe')
|
||||
const { collect } = require('streaming-iterables')
|
||||
const pSettle = require('p-settle')
|
||||
const Transport = require('libp2p-websockets')
|
||||
const Crypto = require('libp2p-secio')
|
||||
const { NOISE: Crypto } = require('libp2p-noise')
|
||||
const Protector = require('../../src/pnet')
|
||||
const swarmKeyBuffer = Buffer.from(require('../fixtures/swarm.key'))
|
||||
|
||||
@@ -347,11 +347,10 @@ describe('libp2p.upgrader', () => {
|
||||
let libp2p
|
||||
|
||||
before(async () => {
|
||||
const ids = await Promise.all([
|
||||
peers = await Promise.all([
|
||||
PeerId.createFromJSON(Peers[0]),
|
||||
PeerId.createFromJSON(Peers[1])
|
||||
])
|
||||
peers = ids.map(peerId => new PeerInfo(peerId))
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -363,7 +362,7 @@ describe('libp2p.upgrader', () => {
|
||||
it('should create an Upgrader', () => {
|
||||
const protector = new Protector(swarmKeyBuffer)
|
||||
libp2p = new Libp2p({
|
||||
peerInfo: peers[0],
|
||||
peerId: peers[0],
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -382,7 +381,7 @@ describe('libp2p.upgrader', () => {
|
||||
|
||||
it('should be able to register and unregister a handler', () => {
|
||||
libp2p = new Libp2p({
|
||||
peerInfo: peers[0],
|
||||
peerId: peers[0],
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -405,7 +404,7 @@ describe('libp2p.upgrader', () => {
|
||||
it('should emit connect and disconnect events', async () => {
|
||||
const remotePeer = peers[1]
|
||||
libp2p = new Libp2p({
|
||||
peerInfo: peers[0],
|
||||
peerId: peers[0],
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
@@ -414,31 +413,32 @@ describe('libp2p.upgrader', () => {
|
||||
})
|
||||
|
||||
const remoteUpgrader = new Upgrader({
|
||||
localPeer: remotePeer.id,
|
||||
localPeer: remotePeer,
|
||||
muxers: new Map([[Muxer.multicodec, Muxer]]),
|
||||
cryptos: new Map([[Crypto.protocol, Crypto]])
|
||||
})
|
||||
|
||||
const { inbound, outbound } = mockMultiaddrConnPair({ addrs, remotePeer: remotePeer.id })
|
||||
const { inbound, outbound } = mockMultiaddrConnPair({ addrs, remotePeer })
|
||||
|
||||
// Spy on emit for easy verification
|
||||
sinon.spy(libp2p, 'emit')
|
||||
sinon.spy(libp2p.connectionManager, 'emit')
|
||||
|
||||
// Upgrade and check the connect event
|
||||
const connections = await Promise.all([
|
||||
libp2p.upgrader.upgradeOutbound(outbound),
|
||||
remoteUpgrader.upgradeInbound(inbound)
|
||||
])
|
||||
expect(libp2p.emit.callCount).to.equal(1)
|
||||
let [event, peerInfo] = libp2p.emit.getCall(0).args
|
||||
expect(libp2p.connectionManager.emit.callCount).to.equal(1)
|
||||
|
||||
let [event, connection] = libp2p.connectionManager.emit.getCall(0).args
|
||||
expect(event).to.equal('peer:connect')
|
||||
expect(peerInfo.id.isEqual(remotePeer.id)).to.equal(true)
|
||||
expect(connection.remotePeer.isEqual(remotePeer)).to.equal(true)
|
||||
|
||||
// Close and check the disconnect event
|
||||
await Promise.all(connections.map(conn => conn.close()))
|
||||
expect(libp2p.emit.callCount).to.equal(2)
|
||||
;([event, peerInfo] = libp2p.emit.getCall(1).args)
|
||||
expect(libp2p.connectionManager.emit.callCount).to.equal(2)
|
||||
;([event, connection] = libp2p.connectionManager.emit.getCall(1).args)
|
||||
expect(event).to.equal('peer:disconnect')
|
||||
expect(peerInfo.id.isEqual(remotePeer.id)).to.equal(true)
|
||||
expect(connection.remotePeer.isEqual(remotePeer)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
const Transport = require('libp2p-websockets')
|
||||
const Muxer = require('libp2p-mplex')
|
||||
const Crypto = require('libp2p-secio')
|
||||
const { NOISE: Crypto } = require('libp2p-noise')
|
||||
|
||||
module.exports = {
|
||||
modules: {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
const Transport = require('libp2p-tcp')
|
||||
const Muxer = require('libp2p-mplex')
|
||||
const Crypto = require('libp2p-secio')
|
||||
const { NOISE: Crypto } = require('libp2p-noise')
|
||||
|
||||
module.exports = {
|
||||
modules: {
|
||||
|
@@ -4,7 +4,6 @@ const pTimes = require('p-times')
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
|
||||
const Libp2p = require('../../../src')
|
||||
const Peers = require('../../fixtures/peers')
|
||||
@@ -19,37 +18,37 @@ const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
* @param {number} [properties.number] number of peers (default: 1).
|
||||
* @param {boolean} [properties.fixture] use fixture for peer-id generation (default: true)
|
||||
* @param {boolean} [properties.started] nodes should start (default: true)
|
||||
* @param {boolean} [properties.populateAddressBooks] nodes addressBooks should be populated with other peers (default: true)
|
||||
* @return {Promise<Array<Libp2p>>}
|
||||
*/
|
||||
async function createPeer ({ number = 1, fixture = true, started = true, config = defaultOptions } = {}) {
|
||||
const peerInfos = await createPeerInfo({ number, fixture })
|
||||
async function createPeer ({ number = 1, fixture = true, started = true, populateAddressBooks = true, config = {} } = {}) {
|
||||
const peerIds = await createPeerId({ number, fixture })
|
||||
|
||||
const addresses = started ? { listen: [listenAddr] } : {}
|
||||
const peers = await pTimes(number, (i) => Libp2p.create({
|
||||
peerInfo: peerInfos[i],
|
||||
peerId: peerIds[i],
|
||||
addresses,
|
||||
...defaultOptions,
|
||||
...config
|
||||
}))
|
||||
|
||||
if (started) {
|
||||
await Promise.all(peers.map((p) => {
|
||||
p.peerInfo.multiaddrs.add(listenAddr)
|
||||
return p.start()
|
||||
}))
|
||||
await Promise.all(peers.map((p) => p.start()))
|
||||
|
||||
populateAddressBooks && _populateAddressBooks(peers)
|
||||
}
|
||||
|
||||
return peers
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Peer-ids.
|
||||
* @param {Object} [properties]
|
||||
* @param {number} [properties.number] number of peers (default: 1).
|
||||
* @param {boolean} [properties.fixture] use fixture for peer-id generation (default: true)
|
||||
* @return {Promise<Array<PeerInfo>>}
|
||||
*/
|
||||
async function createPeerInfo ({ number = 1, fixture = true } = {}) {
|
||||
const peerIds = await createPeerId({ number, fixture })
|
||||
|
||||
return pTimes(number, (i) => PeerInfo.create(peerIds[i]))
|
||||
function _populateAddressBooks (peers) {
|
||||
for (let i = 0; i < peers.length; i++) {
|
||||
for (let j = 0; j < peers.length; j++) {
|
||||
if (i !== j) {
|
||||
peers[i].peerStore.addressBook.set(peers[j].peerId, peers[j].multiaddrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,5 +66,4 @@ function createPeerId ({ number = 1, fixture = true } = {}) {
|
||||
}
|
||||
|
||||
module.exports.createPeer = createPeer
|
||||
module.exports.createPeerInfo = createPeerInfo
|
||||
module.exports.createPeerId = createPeerId
|
||||
|
@@ -16,13 +16,13 @@ module.exports = async (properties = {}) => {
|
||||
const localAddr = multiaddr('/ip4/127.0.0.1/tcp/8080')
|
||||
const remoteAddr = multiaddr('/ip4/127.0.0.1/tcp/8081')
|
||||
|
||||
const [localPeer, remotePeer] = await peerUtils.createPeerInfo({ number: 2 })
|
||||
const [localPeer, remotePeer] = await peerUtils.createPeerId({ number: 2 })
|
||||
const openStreams = []
|
||||
let streamId = 0
|
||||
|
||||
return new Connection({
|
||||
localPeer: localPeer.id,
|
||||
remotePeer: remotePeer.id,
|
||||
localPeer: localPeer,
|
||||
remotePeer: remotePeer,
|
||||
localAddr,
|
||||
remoteAddr,
|
||||
stat: {
|
||||
@@ -31,7 +31,7 @@ module.exports = async (properties = {}) => {
|
||||
upgraded: Date.now()
|
||||
},
|
||||
direction: 'outbound',
|
||||
encryption: '/secio/1.0.0',
|
||||
encryption: '/noise',
|
||||
multiplexer: '/mplex/6.7.0'
|
||||
},
|
||||
newStream: (protocols) => {
|
||||
|
Reference in New Issue
Block a user