mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-07-16 09:01:58 +00:00
Compare commits
9 Commits
docs/disco
...
v0.29.4
Author | SHA1 | Date | |
---|---|---|---|
|
48656712ea | ||
|
1a5ae74741 | ||
|
8691465a52 | ||
|
6350a187c7 | ||
|
8e3bb09279 | ||
|
73204958ee | ||
|
e9e4b731a5 | ||
|
d0a9fada32 | ||
|
824a444f56 |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,3 +1,19 @@
|
||||
<a name="0.29.4"></a>
|
||||
## [0.29.4](https://github.com/libp2p/js-libp2p/compare/v0.29.3...v0.29.4) (2020-12-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* dial self ([#826](https://github.com/libp2p/js-libp2p/issues/826)) ([6350a18](https://github.com/libp2p/js-libp2p/commit/6350a18))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* custom and store self agent version + store self protocol version ([#800](https://github.com/libp2p/js-libp2p/issues/800)) ([d0a9fad](https://github.com/libp2p/js-libp2p/commit/d0a9fad))
|
||||
* support custom listener options ([#822](https://github.com/libp2p/js-libp2p/issues/822)) ([8691465](https://github.com/libp2p/js-libp2p/commit/8691465))
|
||||
|
||||
|
||||
|
||||
<a name="0.29.3"></a>
|
||||
## [0.29.3](https://github.com/libp2p/js-libp2p/compare/v0.29.2...v0.29.3) (2020-11-04)
|
||||
|
||||
|
@@ -35,7 +35,7 @@ We've come a long way, but this project is still in Alpha, lots of development i
|
||||
The documentation in the master branch may contain changes from a pre-release.
|
||||
If you are looking for the documentation of the latest release, you can view the latest release on [**npm**](https://www.npmjs.com/package/libp2p), or select the tag in github that matches the version you are looking for.
|
||||
|
||||
**Want to get started?** Check our [GETTING_STARTED.md](./doc/GETTING_STARTED.md) guide, [Discoverability and Connectivity Readme](./DISCOVERABILITY_AND_CONNECTIVITY.md) and [examples folder](/examples).
|
||||
**Want to get started?** Check our [GETTING_STARTED.md](./doc/GETTING_STARTED.md) guide and [examples folder](/examples).
|
||||
|
||||
**Want to update libp2p in your project?** Check our [migrations folder](./doc/migrations).
|
||||
|
||||
|
127
doc/API.md
127
doc/API.md
@@ -37,7 +37,6 @@
|
||||
* [`peerStore.protoBook.add`](#peerstoreprotobookadd)
|
||||
* [`peerStore.protoBook.delete`](#peerstoreprotobookdelete)
|
||||
* [`peerStore.protoBook.get`](#peerstoreprotobookget)
|
||||
* [`peerStore.protoBook.remove`](#peerstoreprotobookremove)
|
||||
* [`peerStore.protoBook.set`](#peerstoreprotobookset)
|
||||
* [`peerStore.delete`](#peerstoredelete)
|
||||
* [`peerStore.get`](#peerstoreget)
|
||||
@@ -93,6 +92,7 @@ Creates an instance of Libp2p.
|
||||
| options.modules | [`Array<object>`](./CONFIGURATION.md#modules) | libp2p [modules](./CONFIGURATION.md#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.host] | `{ agentVersion: string }` | libp2p host options |
|
||||
| [options.connectionManager] | [`object`](./CONFIGURATION.md#configuring-connection-manager) | libp2p Connection Manager [configuration](./CONFIGURATION.md#configuring-connection-manager) |
|
||||
| [options.transportManager] | [`object`](./CONFIGURATION.md#configuring-transport-manager) | libp2p transport manager [configuration](./CONFIGURATION.md#configuring-transport-manager) |
|
||||
| [options.datastore] | `object` | must implement [ipfs/interface-datastore](https://github.com/ipfs/interface-datastore) (in memory datastore will be used if not provided) |
|
||||
@@ -114,12 +114,25 @@ For Libp2p configurations and modules details read the [Configuration Document](
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
|
||||
// specify options
|
||||
const options = {}
|
||||
async function main () {
|
||||
// specify options
|
||||
const options = {
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [NOISE]
|
||||
}
|
||||
}
|
||||
|
||||
// create libp2p
|
||||
const libp2p = await Libp2p.create(options)
|
||||
// create libp2p
|
||||
const libp2p = await Libp2p.create(options)
|
||||
}
|
||||
|
||||
main()
|
||||
```
|
||||
|
||||
Note: The [`PeerId`][peer-id] option is not required and will be generated if it is not provided.
|
||||
@@ -131,12 +144,30 @@ As an alternative, it is possible to create a Libp2p instance with the construct
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
// specify options
|
||||
const options = {}
|
||||
async function main () {
|
||||
const peerId = await PeerId.create();
|
||||
|
||||
// create libp2p
|
||||
const libp2p = new Libp2p(options)
|
||||
// specify options
|
||||
// peerId is required when Libp2p is instantiated via the constructor
|
||||
const options = {
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [NOISE]
|
||||
}
|
||||
}
|
||||
|
||||
// create libp2p
|
||||
const libp2p = new Libp2p(options)
|
||||
}
|
||||
|
||||
main()
|
||||
```
|
||||
|
||||
Required keys in the `options` object:
|
||||
@@ -666,7 +697,7 @@ Queries the DHT for the n values stored for the given key (without sorting).
|
||||
// ...
|
||||
|
||||
const key = '/key'
|
||||
const { from, val } = await libp2p.contentRouting.get(key)
|
||||
const records = await libp2p.contentRouting.getMany(key, 2)
|
||||
```
|
||||
|
||||
### peerRouting.findPeer
|
||||
@@ -844,6 +875,32 @@ Consider using `addressBook.add()` if you're not sure this is what you want to d
|
||||
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.
|
||||
@@ -1066,31 +1123,6 @@ Set known metadata of a given `peerId`.
|
||||
peerStore.metadataBook.set(peerId, 'location', uint8ArrayFromString('Berlin'))
|
||||
```
|
||||
|
||||
### 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.protoBook.delete
|
||||
|
||||
Delete the provided peer from the book.
|
||||
@@ -1147,31 +1179,6 @@ peerStore.protoBook.get(peerId)
|
||||
// [ '/proto/1.0.0', '/proto/1.1.0' ]
|
||||
```
|
||||
|
||||
### peerStore.protoBook.remove
|
||||
|
||||
Remove given `protocols` of a given peer.
|
||||
|
||||
`peerStore.protoBook.remove(peerId, protocols)`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| peerId | [`PeerId`][peer-id] | peerId to set |
|
||||
| protocols | `Array<string>` | protocols to remove |
|
||||
|
||||
#### Returns
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `ProtoBook` | Returns the Proto Book component |
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
peerStore.protoBook.remove(peerId, protocols)
|
||||
```
|
||||
|
||||
### peerStore.protoBook.set
|
||||
|
||||
Set known `protocols` of a given peer.
|
||||
|
@@ -20,7 +20,6 @@
|
||||
- [Customizing DHT](#customizing-dht)
|
||||
- [Setup with Content and Peer Routing](#setup-with-content-and-peer-routing)
|
||||
- [Setup with Relay](#setup-with-relay)
|
||||
- [Setup with Auto Relay](#setup-with-auto-relay)
|
||||
- [Setup with Keychain](#setup-with-keychain)
|
||||
- [Configuring Dialing](#configuring-dialing)
|
||||
- [Configuring Connection Manager](#configuring-connection-manager)
|
||||
@@ -420,37 +419,6 @@ const node = await Libp2p.create({
|
||||
hop: {
|
||||
enabled: true, // Allows you to be a relay for other peers
|
||||
active: true // You will attempt to dial destination peers if you are not connected to them
|
||||
},
|
||||
advertise: {
|
||||
bootDelay: 15 * 60 * 1000, // Delay before HOP relay service is advertised on the network
|
||||
enabled: true, // Allows you to disable the advertise of the Hop service
|
||||
ttl: 30 * 60 * 1000 // Delay Between HOP relay service advertisements on the network
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Setup with Auto Relay
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
},
|
||||
config: {
|
||||
relay: { // Circuit Relay options (this config is part of libp2p core configurations)
|
||||
enabled: true, // Allows you to dial and accept relayed connections. Does not make you a relay.
|
||||
autoRelay: {
|
||||
enabled: true, // Allows you to bind to relays with HOP enabled for improving node dialability
|
||||
maxListeners: 2 // Configure maximum number of HOP relays to use
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -683,6 +651,35 @@ const node = await Libp2p.create({
|
||||
})
|
||||
```
|
||||
|
||||
During Libp2p startup, transport listeners will be created for the configured listen multiaddrs. Some transports support custom listener options and you can set them using the `listenerOptions` in the transport configuration. For example, [libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star) transport listener supports the configuration of its underlying [simple-peer](https://github.com/feross/simple-peer) ice server(STUN/TURN) config as follows:
|
||||
|
||||
```js
|
||||
const transportKey = WebRTCStar.prototype[Symbol.toStringTag]
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [WebRTCStar],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [NOISE]
|
||||
},
|
||||
addresses: {
|
||||
listen: ['/dns4/your-wrtc-star.pub/tcp/443/wss/p2p-webrtc-star'] // your webrtc dns multiaddr
|
||||
},
|
||||
config: {
|
||||
transport: {
|
||||
[transportKey]: {
|
||||
listenerOptions: {
|
||||
config: {
|
||||
iceServers: [
|
||||
{"urls": ["turn:YOUR.TURN.SERVER:3478"], "username": "YOUR.USER", "credential": "YOUR.PASSWORD"},
|
||||
{"urls": ["stun:YOUR.STUN.SERVER:3478"], "username": "", "credential": ""}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Configuration examples
|
||||
|
||||
As libp2p is designed to be a modular networking library, its usage will vary based on individual project needs. We've included links to some existing project configurations for your reference, in case you wish to replicate their configuration:
|
||||
|
@@ -1,99 +0,0 @@
|
||||
# Discoverability and Connectivity
|
||||
|
||||
While different p2p applications have different needs and requirements, they might also run in different environments and have different hardware capabilities. These characteristics will influence how other peers can be discovered, as well as how connections are established and kept open.
|
||||
|
||||
This document contains a set of guidelines to setup libp2p for the most common use cases, in the context of the typical environments where you can run a `js-libp2p` node.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
[Background](#background)
|
||||
- [Discovery](#discovery)
|
||||
- [Connectivity](#connectivity)
|
||||
[Browser](#browser)
|
||||
- [Discovery](#discovery)
|
||||
- [Connectivity](#connectivity)
|
||||
- [Routing](#routing)
|
||||
- [Overview](#overview)
|
||||
[Node](#node)
|
||||
|
||||
## Background
|
||||
|
||||
### Discovery
|
||||
|
||||
Libp2p offers a variety of options to discover other peers on the network. They range from specifying well known peer addresses, to issue queries in a local network or exchanging peer addresses with other previously discovered peers.
|
||||
|
||||
To enable peer addresses exchange, peers need to specify their own announce addresses. Accordingly, announce addresses should be reachable from other peers to be valuable.
|
||||
|
||||
### Connectivity
|
||||
|
||||
A libp2p node cannot keep a unlimited number of connections over time due to hardware and network constraints. As a consequence, a node must keep the most important connections open at any moment. While certain connections will probably be important over longer periods of time, others might only be important for a smaller interval. Accordingly, libp2p needs to keep track of its open connections over time and verify if there are better connections to establish while keeping an healthy number open.
|
||||
|
||||
Well known peers are important for bootstrapping and getting to know other peers in the network. However, they will become less important over time since their main purpose is usually to bootstrap the network and not to provide other services. Moreover, as they will be reached by most of the network, they should be disconnected when they do not provide any more clear value to keep the network healthy.
|
||||
|
||||
Libp2p is able to automatically identify the importance of some connections over time, but the application layer should also flag important connections manually to improve the node's sensing of the network. For instance, libp2p will protect connections that are used in their listening addresses, in order to be reachable by other nodes, as well as connections with relevant peers for core protocols like gossipsub.
|
||||
|
||||
## Browser
|
||||
|
||||
Regarding enabling p2p applications, browsers currently have limitations that have impact on how libp2p should be setup.
|
||||
|
||||
### Discovery
|
||||
|
||||
Taking into account that a web browser does not offer any mDNS-like local discovery method to find peers on the same network and/or on the same web origin, a browser node will need to know other peers' addresses beforehand, so that it can bootstrap its network. These initial nodes should be used as a way to get to know other peers in the network and establish connections with them. Moreover, some of these peers can also advertise the browser peer to other nodes in the network, so that they can connect to it.
|
||||
|
||||
### Connectivity
|
||||
|
||||
Browser nodes do not have the ability to "listen" for incoming connections, nor a permanent address that can be dialed later for quick resume. However, Libp2p provides a set of possibilities to overcome these limitations. These solutions usually rely on other nodes to listen for connections on its behalf, as well as to advertise its information to other peers.
|
||||
|
||||
A browser node should start by establishing a connection with a known machine. As a result of this connection, the browser node will likely be interested to have its addresses announced to other peers in the network. Given that a browser cannot be dialed, the announced addresses of the node will be addresses that rely on this previously connection as the entry point of a dial request. For example, a circuit relay address from a connected peer. Shortly, browser nodes should have auto-relay enabled, so that they can bind to relay nodes that support HOP and become diable via them.
|
||||
|
||||
### Routing
|
||||
|
||||
DHTs are an essential building block of a p2p system to provide a lookup mechanism similar to a key-value hash table.
|
||||
|
||||
As browsers cannot handle large pools of open connections at the same time, as well as establish direct connections to each others, browser nodes cannot participate efficiently in DHTs. Once again, the best way to circumvent this limitation is to rely on more capable nodes in the network to handle DHT queries on their behalf. Browser nodes can rely on delegate nodes or use the DHT in client mode.
|
||||
|
||||
### Overview
|
||||
|
||||
The base connections to have a fully functional libp2p browser node are:
|
||||
- nodes that can listen for incoming connections
|
||||
- Relay nodes, Webrtc-star servers, ...
|
||||
- nodes that can enable peer discovery and service discovery
|
||||
- Webrtc-star servers, Rendezvous servers, DHT server Nodes, ...
|
||||
- closest nodes
|
||||
- nodes that can enable efficient routing
|
||||
- DHT server nodes
|
||||
- nodes from the pubsub topics mesh
|
||||
- application protocol peers (as needed via `MulticodecTopology`)
|
||||
|
||||
While the first three points are important in any context, the last three points depend on the application use case and if the mentioned subsystems are needed.
|
||||
|
||||
TODO: Clearly define what libp2p handles and how it is handled
|
||||
|
||||
- Libp2p will protect connections used in their listening addresses like connections to a `webrtc-star` server or connections to a node acting as a relay through the `AutoRelay`, as well as nodes used for peer and service discovery
|
||||
- Libp2p pubsub routers will protect the most important peer connections
|
||||
- How to control and avoid excess
|
||||
- Libp2p will protect connections to n DHT servers
|
||||
- Libp2p will protect the n (configurable) closest peers on the network and refresh them over time, if needed
|
||||
- Application protocol peers should be protected
|
||||
- TODO: define how
|
||||
|
||||
## Node
|
||||
|
||||
In a Node.js context, there are less limitations that need to be considered regarding discoverability and connectivity compared to browser nodes.
|
||||
|
||||
The most common issue is when Libp2p nodes are behind NATs. While NAT is usually transparent for outgoing connections, listening for incoming connections might require some configuration. While it’s usually possible to manually configure routers, not everyone that wants to run a peer-to-peer application or other network service will have the ability to do so. Moreover, libp2p applications should run everywhere, not just in data centers or on machines with stable public IP addresses.
|
||||
|
||||
The best approach at the moment to circumvent this limitation is to rely on relay to communicate indirectly via an intermediary peer.
|
||||
|
||||
### Overview
|
||||
|
||||
The base connections to have a fully functional libp2p browser node are:
|
||||
- nodes that can listen for incoming connections when behind a NAT
|
||||
- Relay nodes, ...
|
||||
- nodes that can enable service discovery
|
||||
- Rendezvous servers, DHT server Nodes, ...
|
||||
- closest nodes
|
||||
- nodes that can enable efficient routing
|
||||
- DHT server nodes
|
||||
- nodes from the pubsub topics mesh
|
||||
- application protocol peers (as needed via `MulticodecTopology`)
|
@@ -248,9 +248,7 @@ If you want to know more about libp2p peer discovery, you should read the follow
|
||||
|
||||
## What is next
|
||||
|
||||
There are a lot of other concepts within `libp2p`, that are not covered in this guide. For additional configuration options we recommend checking out the [Configuration Readme](./CONFIGURATION.md) and the [examples folder](../examples).
|
||||
For guidelines on how to enable discoverability and connectivity for your node environment and use case, you can check the [Discoverability and Connectivity Readme](./DISCOVERABILITY_AND_CONNECTIVITY.md).
|
||||
If you have any problems getting started, or if anything isn't clear, please let us know by submitting an issue!
|
||||
There are a lot of other concepts within `libp2p`, that are not covered in this guide. For additional configuration options we recommend checking out the [Configuration Readme](./CONFIGURATION.md) and the [examples folder](../examples). If you have any problems getting started, or if anything isn't clear, please let us know by submitting an issue!
|
||||
|
||||
|
||||
[transport]: https://github.com/libp2p/js-interfaces/tree/master/src/transport
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const multiaddr = require('multiaddr')
|
||||
const Node = require('./libp2p-bundle')
|
||||
const createLibp2p = require('./libp2p-bundle')
|
||||
const { stdinToStream, streamToConsole } = require('./stream')
|
||||
|
||||
async function run() {
|
||||
@@ -13,7 +13,7 @@ async function run() {
|
||||
])
|
||||
|
||||
// Create a new libp2p node on localhost with a randomly chosen port
|
||||
const nodeDialer = new Node({
|
||||
const nodeDialer = await createLibp2p({
|
||||
peerId: idDialer,
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
|
@@ -7,21 +7,16 @@ const { NOISE } = require('libp2p-noise')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const libp2p = require('../../..')
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [
|
||||
TCP,
|
||||
WS
|
||||
],
|
||||
streamMuxer: [ mplex ],
|
||||
connEncryption: [ NOISE ]
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
async function createLibp2p(_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [TCP, WS],
|
||||
streamMuxer: [mplex],
|
||||
connEncryption: [NOISE],
|
||||
},
|
||||
}
|
||||
|
||||
return libp2p.create(defaultsDeep(_options, defaults))
|
||||
}
|
||||
|
||||
module.exports = Node
|
||||
module.exports = createLibp2p
|
||||
|
@@ -2,13 +2,13 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const Node = require('./libp2p-bundle.js')
|
||||
const createLibp2p = require('./libp2p-bundle.js')
|
||||
const { stdinToStream, streamToConsole } = require('./stream')
|
||||
|
||||
async function run() {
|
||||
// Create a new libp2p node with the given multi-address
|
||||
const idListener = await PeerId.createFromJSON(require('./peer-id-listener'))
|
||||
const nodeListener = new Node({
|
||||
const nodeListener = await createLibp2p({
|
||||
peerId: idListener,
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/10333']
|
||||
|
@@ -10,14 +10,11 @@ const Bootstrap = require('libp2p-bootstrap')
|
||||
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-nodejs.json
|
||||
const bootstrapers = [
|
||||
'/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
|
||||
'/ip4/104.236.176.52/tcp/4001/p2p/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
|
||||
'/ip4/104.236.179.241/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
|
||||
'/ip4/162.243.248.213/tcp/4001/p2p/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
|
||||
'/ip4/128.199.219.111/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
|
||||
'/ip4/104.236.76.40/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
|
||||
'/ip4/178.62.158.247/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
|
||||
'/ip4/178.62.61.185/tcp/4001/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
|
||||
'/ip4/104.236.151.122/tcp/4001/p2p/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx'
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
|
||||
]
|
||||
|
||||
;(async () => {
|
||||
|
@@ -40,14 +40,11 @@ In this configuration, we use a `bootstrappers` array listing peers to connect _
|
||||
```JavaScript
|
||||
const bootstrapers = [
|
||||
'/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
|
||||
'/ip4/104.236.176.52/tcp/4001/p2p/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
|
||||
'/ip4/104.236.179.241/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
|
||||
'/ip4/162.243.248.213/tcp/4001/p2p/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
|
||||
'/ip4/128.199.219.111/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
|
||||
'/ip4/104.236.76.40/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
|
||||
'/ip4/178.62.158.247/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
|
||||
'/ip4/178.62.61.185/tcp/4001/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
|
||||
'/ip4/104.236.151.122/tcp/4001/p2p/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx'
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
|
||||
]
|
||||
```
|
||||
|
||||
@@ -93,23 +90,17 @@ From running [1.js](./1.js), you should see the following:
|
||||
```bash
|
||||
> node 1.js
|
||||
Discovered: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
|
||||
Discovered: QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z
|
||||
Discovered: QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM
|
||||
Discovered: QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm
|
||||
Discovered: QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu
|
||||
Discovered: QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64
|
||||
Discovered: QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd
|
||||
Discovered: QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3
|
||||
Discovered: QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
Discovered: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN
|
||||
Discovered: QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb
|
||||
Discovered: QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp
|
||||
Discovered: QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa
|
||||
Discovered: QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt
|
||||
Connection established to: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
|
||||
Connection established to: QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z
|
||||
Connection established to: QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM
|
||||
Connection established to: QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm
|
||||
Connection established to: QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu
|
||||
Connection established to: QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64
|
||||
Connection established to: QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd
|
||||
Connection established to: QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3
|
||||
Connection established to: QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
Connection established to: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN
|
||||
Connection established to: QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp
|
||||
Connection established to: QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa
|
||||
Connection established to: QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt
|
||||
Connection established to: QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb
|
||||
```
|
||||
|
||||
## 2. MulticastDNS to find other peers in the network
|
||||
|
@@ -5,9 +5,8 @@
|
||||
* Dialer Node
|
||||
*/
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
const Node = require('./libp2p-bundle')
|
||||
const createLibp2p = require('./libp2p-bundle')
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
async function run() {
|
||||
@@ -17,7 +16,7 @@ async function run() {
|
||||
])
|
||||
|
||||
// Dialer
|
||||
const dialerNode = new Node({
|
||||
const dialerNode = await createLibp2p({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
|
@@ -8,21 +8,16 @@ const { NOISE } = require('libp2p-noise')
|
||||
const defaultsDeep = require('@nodeutils/defaults-deep')
|
||||
const libp2p = require('../../..')
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [
|
||||
TCP,
|
||||
WS
|
||||
],
|
||||
streamMuxer: [ mplex ],
|
||||
connEncryption: [ NOISE ]
|
||||
}
|
||||
}
|
||||
|
||||
super(defaultsDeep(_options, defaults))
|
||||
async function createLibp2p(_options) {
|
||||
const defaults = {
|
||||
modules: {
|
||||
transport: [TCP, WS],
|
||||
streamMuxer: [mplex],
|
||||
connEncryption: [NOISE],
|
||||
},
|
||||
}
|
||||
|
||||
return libp2p.create(defaultsDeep(_options, defaults))
|
||||
}
|
||||
|
||||
module.exports = Node
|
||||
module.exports = createLibp2p
|
||||
|
@@ -6,14 +6,14 @@
|
||||
*/
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const Node = require('./libp2p-bundle')
|
||||
const createLibp2p = require('./libp2p-bundle')
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
async function run() {
|
||||
const listenerId = await PeerId.createFromJSON(require('./id-l'))
|
||||
|
||||
// Listener libp2p node
|
||||
const listenerNode = new Node({
|
||||
const listenerNode = await createLibp2p({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/10333']
|
||||
},
|
||||
|
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "libp2p",
|
||||
"version": "0.29.3",
|
||||
"version": "0.29.4",
|
||||
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
|
||||
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
|
||||
"main": "src/index.js",
|
||||
@@ -45,7 +45,6 @@
|
||||
"aggregate-error": "^3.0.1",
|
||||
"any-signal": "^1.1.0",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"cids": "^1.0.0",
|
||||
"class-is": "^1.1.0",
|
||||
"debug": "^4.1.1",
|
||||
"err-code": "^2.0.0",
|
||||
@@ -61,13 +60,12 @@
|
||||
"it-protocol-buffers": "^0.2.0",
|
||||
"libp2p-crypto": "^0.18.0",
|
||||
"libp2p-interfaces": "^0.5.1",
|
||||
"libp2p-utils": "^0.2.1",
|
||||
"libp2p-utils": "^0.2.0",
|
||||
"mafmt": "^8.0.0",
|
||||
"merge-options": "^2.0.0",
|
||||
"moving-average": "^1.0.0",
|
||||
"multiaddr": "^8.1.0",
|
||||
"multicodec": "^2.0.0",
|
||||
"multihashing-async": "^2.0.1",
|
||||
"multistream-select": "^1.0.0",
|
||||
"mutable-proxy": "^1.0.0",
|
||||
"node-forge": "^0.9.1",
|
||||
@@ -91,6 +89,7 @@
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-bytes": "^0.1.2",
|
||||
"chai-string": "^1.5.0",
|
||||
"cids": "^1.0.0",
|
||||
"delay": "^4.3.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"interop-libp2p": "^0.3.0",
|
||||
@@ -136,6 +135,7 @@
|
||||
"dirkmc <dirkmdev@gmail.com>",
|
||||
"Volker Mische <volker.mische@gmail.com>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"a1300 <matthias-knopp@gmx.net>",
|
||||
"Elven <mon.samuel@qq.com>",
|
||||
"Andrew Nesbitt <andrewnez@gmail.com>",
|
||||
"Giovanni T. Parra <fiatjaf@gmail.com>",
|
||||
@@ -143,8 +143,6 @@
|
||||
"Thomas Eizinger <thomas@eizinger.io>",
|
||||
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
|
||||
"Didrik Nordström <didrik@betamos.se>",
|
||||
"Henrique Dias <hacdias@gmail.com>",
|
||||
"Fei Liu <liu.feiwood@gmail.com>",
|
||||
"Irakli Gozalishvili <rfobic@gmail.com>",
|
||||
"Ethan Lam <elmemphis2000@gmail.com>",
|
||||
"Joel Gustafson <joelg@mit.edu>",
|
||||
@@ -154,9 +152,11 @@
|
||||
"Dmitriy Ryajov <dryajov@gmail.com>",
|
||||
"RasmusErik Voel Jensen <github@solsort.com>",
|
||||
"Diogo Silva <fsdiogo@gmail.com>",
|
||||
"robertkiel <robert.kiel@validitylabs.org>",
|
||||
"Samlior <samlior@foxmail.com>",
|
||||
"Smite Chow <xiaopengyou@live.com>",
|
||||
"Soeren <nikorpoulsen@gmail.com>",
|
||||
"Sönke Hahn <soenkehahn@gmail.com>",
|
||||
"robertkiel <robert.kiel@validitylabs.org>",
|
||||
"Tiago Alves <alvesjtiago@gmail.com>",
|
||||
"Daijiro Wachi <daijiro.wachi@gmail.com>",
|
||||
"Yusef Napora <yusef@napora.org>",
|
||||
@@ -165,9 +165,11 @@
|
||||
"Chris Bratlien <chrisbratlien@gmail.com>",
|
||||
"ebinks <elizabethjbinks@gmail.com>",
|
||||
"Bernd Strehl <bernd.strehl@gmail.com>",
|
||||
"isan_rivkin <isanrivkin@gmail.com>",
|
||||
"Florian-Merle <florian.david.merle@gmail.com>",
|
||||
"Francis Gulotta <wizard@roborooter.com>",
|
||||
"Felipe Martins <felipebrasil93@gmail.com>"
|
||||
"Felipe Martins <felipebrasil93@gmail.com>",
|
||||
"isan_rivkin <isanrivkin@gmail.com>",
|
||||
"Henrique Dias <hacdias@gmail.com>",
|
||||
"Fei Liu <liu.feiwood@gmail.com>"
|
||||
]
|
||||
}
|
||||
|
@@ -1,292 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:auto-relay')
|
||||
log.error = debug('libp2p:auto-relay:error')
|
||||
|
||||
const isPrivate = require('libp2p-utils/src/multiaddr/is-private')
|
||||
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const { relay: multicodec } = require('./multicodec')
|
||||
const { canHop } = require('./circuit/hop')
|
||||
const { namespaceToCid } = require('./utils')
|
||||
const {
|
||||
CIRCUIT_PROTO_CODE,
|
||||
HOP_METADATA_KEY,
|
||||
HOP_METADATA_VALUE,
|
||||
RELAY_RENDEZVOUS_NS
|
||||
} = require('./constants')
|
||||
|
||||
class AutoRelay {
|
||||
/**
|
||||
* Creates an instance of AutoRelay.
|
||||
*
|
||||
* @class
|
||||
* @param {object} props
|
||||
* @param {Libp2p} props.libp2p
|
||||
* @param {number} [props.maxListeners = 1] - maximum number of relays to listen.
|
||||
*/
|
||||
constructor ({ libp2p, maxListeners = 1 }) {
|
||||
this._libp2p = libp2p
|
||||
this._peerId = libp2p.peerId
|
||||
this._peerStore = libp2p.peerStore
|
||||
this._connectionManager = libp2p.connectionManager
|
||||
this._transportManager = libp2p.transportManager
|
||||
|
||||
this.maxListeners = maxListeners
|
||||
|
||||
/**
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
this._listenRelays = new Set()
|
||||
|
||||
this._onProtocolChange = this._onProtocolChange.bind(this)
|
||||
this._onPeerDisconnected = this._onPeerDisconnected.bind(this)
|
||||
|
||||
this._peerStore.on('change:protocols', this._onProtocolChange)
|
||||
this._connectionManager.on('peer:disconnect', this._onPeerDisconnected)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a peer supports the relay protocol.
|
||||
* If the protocol is not supported, check if it was supported before and remove it as a listen relay.
|
||||
* If the protocol is supported, check if the peer supports **HOP** and add it as a listener if
|
||||
* inside the threshold.
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {PeerId} props.peerId
|
||||
* @param {Array<string>} props.protocols
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _onProtocolChange ({ peerId, protocols }) {
|
||||
const id = peerId.toB58String()
|
||||
|
||||
// Check if it has the protocol
|
||||
const hasProtocol = protocols.find(protocol => protocol === multicodec)
|
||||
|
||||
// If no protocol, check if we were keeping the peer before as a listenRelay
|
||||
if (!hasProtocol && this._listenRelays.has(id)) {
|
||||
this._removeListenRelay(id)
|
||||
return
|
||||
} else if (!hasProtocol || this._listenRelays.has(id)) {
|
||||
return
|
||||
}
|
||||
|
||||
// If protocol, check if can hop, store info in the metadataBook and listen on it
|
||||
try {
|
||||
const connection = this._connectionManager.get(peerId)
|
||||
|
||||
// Do not hop on a relayed connection
|
||||
if (connection.remoteAddr.protoCodes().includes(CIRCUIT_PROTO_CODE)) {
|
||||
log(`relayed connection to ${id} will not be used to hop on`)
|
||||
return
|
||||
}
|
||||
|
||||
const supportsHop = await canHop({ connection })
|
||||
|
||||
if (supportsHop) {
|
||||
this._peerStore.metadataBook.set(peerId, HOP_METADATA_KEY, uint8ArrayFromString(HOP_METADATA_VALUE))
|
||||
await this._addListenRelay(connection, id)
|
||||
}
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Peer disconnects.
|
||||
*
|
||||
* @param {Connection} connection - connection to the peer
|
||||
* @returns {void}
|
||||
*/
|
||||
_onPeerDisconnected (connection) {
|
||||
const peerId = connection.remotePeer
|
||||
const id = peerId.toB58String()
|
||||
|
||||
// Not listening on this relay
|
||||
if (!this._listenRelays.has(id)) {
|
||||
return
|
||||
}
|
||||
|
||||
this._removeListenRelay(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to listen on the given relay connection.
|
||||
*
|
||||
* @private
|
||||
* @param {Connection} connection - connection to the peer
|
||||
* @param {string} id - peer identifier string
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _addListenRelay (connection, id) {
|
||||
// Check if already listening on enough relays
|
||||
if (this._listenRelays.size >= this.maxListeners) {
|
||||
return
|
||||
}
|
||||
|
||||
// Create relay listen addr
|
||||
let listenAddr, remoteMultiaddr, remoteAddrs
|
||||
|
||||
try {
|
||||
// Get peer known addresses and sort them per public addresses first
|
||||
remoteAddrs = this._peerStore.addressBook.get(connection.remotePeer)
|
||||
// TODO: This sort should be customizable in the config (dialer addr sort)
|
||||
remoteAddrs.sort(multiaddrsCompareFunction)
|
||||
|
||||
remoteMultiaddr = remoteAddrs.find(a => a.isCertified).multiaddr // Get first announced address certified
|
||||
// TODO: HOP Relays should avoid advertising private addresses!
|
||||
} catch (_) {
|
||||
log.error(`${id} does not have announced certified multiaddrs`)
|
||||
|
||||
// Attempt first if existing
|
||||
if (!remoteAddrs || !remoteAddrs.length) {
|
||||
return
|
||||
}
|
||||
|
||||
remoteMultiaddr = remoteAddrs[0].multiaddr
|
||||
}
|
||||
|
||||
if (!remoteMultiaddr.protoNames().includes('p2p')) {
|
||||
listenAddr = `${remoteMultiaddr.toString()}/p2p/${connection.remotePeer.toB58String()}/p2p-circuit`
|
||||
} else {
|
||||
listenAddr = `${remoteMultiaddr.toString()}/p2p-circuit`
|
||||
}
|
||||
|
||||
// Attempt to listen on relay
|
||||
this._listenRelays.add(id)
|
||||
|
||||
try {
|
||||
await this._transportManager.listen([multiaddr(listenAddr)])
|
||||
// Announce multiaddrs will update on listen success by TransportManager event being triggered
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
this._listenRelays.delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove listen relay.
|
||||
*
|
||||
* @private
|
||||
* @param {string} id - peer identifier string.
|
||||
* @returns {void}
|
||||
*/
|
||||
_removeListenRelay (id) {
|
||||
if (this._listenRelays.delete(id)) {
|
||||
// TODO: this should be responsibility of the connMgr
|
||||
this._listenOnAvailableHopRelays([id])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to listen on available hop relay connections.
|
||||
* The following order will happen while we do not have enough relays.
|
||||
* 1. Check the metadata store for known relays, try to listen on the ones we are already connected.
|
||||
* 2. Dial and try to listen on the peers we know that support hop but are not connected.
|
||||
* 3. Search the network.
|
||||
*
|
||||
* @param {Array<string>} [peersToIgnore]
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _listenOnAvailableHopRelays (peersToIgnore = []) {
|
||||
// TODO: The peer redial issue on disconnect should be handled by connection gating
|
||||
// Check if already listening on enough relays
|
||||
if (this._listenRelays.size >= this.maxListeners) {
|
||||
return
|
||||
}
|
||||
|
||||
const knownHopsToDial = []
|
||||
|
||||
// Check if we have known hop peers to use and attempt to listen on the already connected
|
||||
for (const [id, metadataMap] of this._peerStore.metadataBook.data.entries()) {
|
||||
// Continue to next if listening on this or peer to ignore
|
||||
if (this._listenRelays.has(id) || peersToIgnore.includes(id)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const supportsHop = metadataMap.get(HOP_METADATA_KEY)
|
||||
|
||||
// Continue to next if it does not support Hop
|
||||
if (!supportsHop || uint8ArrayToString(supportsHop) !== HOP_METADATA_VALUE) {
|
||||
continue
|
||||
}
|
||||
|
||||
const peerId = PeerId.createFromCID(id)
|
||||
const connection = this._connectionManager.get(peerId)
|
||||
|
||||
// If not connected, store for possible later use.
|
||||
if (!connection) {
|
||||
knownHopsToDial.push(peerId)
|
||||
continue
|
||||
}
|
||||
|
||||
await this._addListenRelay(connection, id)
|
||||
|
||||
// Check if already listening on enough relays
|
||||
if (this._listenRelays.size >= this.maxListeners) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Try to listen on known peers that are not connected
|
||||
for (const peerId of knownHopsToDial) {
|
||||
const connection = await this._libp2p.dial(peerId)
|
||||
await this._addListenRelay(connection, peerId.toB58String())
|
||||
|
||||
// Check if already listening on enough relays
|
||||
if (this._listenRelays.size >= this.maxListeners) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find relays to hop on the network
|
||||
try {
|
||||
const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS)
|
||||
for await (const provider of this._libp2p.contentRouting.findProviders(cid)) {
|
||||
if (!provider.multiaddrs.length) {
|
||||
continue
|
||||
}
|
||||
const peerId = provider.id
|
||||
|
||||
this._peerStore.addressBook.add(peerId, provider.multiaddrs)
|
||||
const connection = await this._libp2p.dial(peerId)
|
||||
|
||||
await this._addListenRelay(connection, peerId.toB58String())
|
||||
|
||||
// Check if already listening on enough relays
|
||||
if (this._listenRelays.size >= this.maxListeners) {
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare function for array.sort().
|
||||
* This sort aims to move the private adresses to the end of the array.
|
||||
*
|
||||
* @param {Address} a
|
||||
* @param {Address} b
|
||||
* @returns {number}
|
||||
*/
|
||||
function multiaddrsCompareFunction (a, b) {
|
||||
const isAPrivate = isPrivate(a.multiaddr)
|
||||
const isBPrivate = isPrivate(b.multiaddr)
|
||||
|
||||
if (isAPrivate && !isBPrivate) {
|
||||
return 1
|
||||
} else if (!isAPrivate && isBPrivate) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
module.exports = AutoRelay
|
@@ -116,34 +116,6 @@ module.exports.hop = async function hop ({
|
||||
throw errCode(new Error(`HOP request failed with code ${response.code}`), Errors.ERR_HOP_REQUEST_FAILED)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a CAN_HOP request to a relay peer, in order to understand its capabilities.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {Connection} options.connection - Connection to the relay
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
module.exports.canHop = async function canHop ({
|
||||
connection
|
||||
}) {
|
||||
// Create a new stream to the relay
|
||||
const { stream } = await connection.newStream([multicodec.relay])
|
||||
// Send the HOP request
|
||||
const streamHandler = new StreamHandler({ stream })
|
||||
streamHandler.write({
|
||||
type: CircuitPB.Type.CAN_HOP
|
||||
})
|
||||
|
||||
const response = await streamHandler.read()
|
||||
await streamHandler.close()
|
||||
|
||||
if (response.code !== CircuitPB.Status.SUCCESS) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unencoded CAN_HOP response based on the Circuits configuration
|
||||
*
|
||||
|
@@ -1,12 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const minute = 60 * 1000
|
||||
|
||||
module.exports = {
|
||||
ADVERTISE_BOOT_DELAY: 15 * minute, // Delay before HOP relay service is advertised on the network
|
||||
ADVERTISE_TTL: 30 * minute, // Delay Between HOP relay service advertisements on the network
|
||||
CIRCUIT_PROTO_CODE: 290, // Multicodec code
|
||||
HOP_METADATA_KEY: 'hop_relay', // PeerStore metadaBook key for HOP relay service
|
||||
HOP_METADATA_VALUE: 'true', // PeerStore metadaBook value for HOP relay service
|
||||
RELAY_RENDEZVOUS_NS: '/libp2p/relay' // Relay HOP relay service namespace for discovery
|
||||
}
|
@@ -1,91 +1,187 @@
|
||||
'use strict'
|
||||
|
||||
const mafmt = require('mafmt')
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
const withIs = require('class-is')
|
||||
const { CircuitRelay: CircuitPB } = require('./protocol')
|
||||
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:relay')
|
||||
log.error = debug('libp2p:relay:error')
|
||||
const log = debug('libp2p:circuit')
|
||||
log.error = debug('libp2p:circuit:error')
|
||||
const toConnection = require('libp2p-utils/src/stream-to-ma-conn')
|
||||
|
||||
const AutoRelay = require('./auto-relay')
|
||||
const { namespaceToCid } = require('./utils')
|
||||
const {
|
||||
ADVERTISE_BOOT_DELAY,
|
||||
ADVERTISE_TTL,
|
||||
RELAY_RENDEZVOUS_NS
|
||||
} = require('./constants')
|
||||
const { relay: multicodec } = require('./multicodec')
|
||||
const createListener = require('./listener')
|
||||
const { handleCanHop, handleHop, hop } = require('./circuit/hop')
|
||||
const { handleStop } = require('./circuit/stop')
|
||||
const StreamHandler = require('./circuit/stream-handler')
|
||||
|
||||
class Relay {
|
||||
class Circuit {
|
||||
/**
|
||||
* Creates an instance of Relay.
|
||||
* Creates an instance of Circuit.
|
||||
*
|
||||
* @class
|
||||
* @param {Libp2p} libp2p
|
||||
* @param {object} options
|
||||
* @param {Libp2p} options.libp2p
|
||||
* @param {Upgrader} options.upgrader
|
||||
*/
|
||||
constructor (libp2p) {
|
||||
constructor ({ libp2p, upgrader }) {
|
||||
this._dialer = libp2p.dialer
|
||||
this._registrar = libp2p.registrar
|
||||
this._connectionManager = libp2p.connectionManager
|
||||
this._upgrader = upgrader
|
||||
this._options = libp2p._config.relay
|
||||
this._libp2p = libp2p
|
||||
this._options = {
|
||||
advertise: {
|
||||
bootDelay: ADVERTISE_BOOT_DELAY,
|
||||
enabled: true,
|
||||
ttl: ADVERTISE_TTL,
|
||||
...libp2p._config.relay.advertise
|
||||
},
|
||||
...libp2p._config.relay
|
||||
}
|
||||
|
||||
// Create autoRelay if enabled
|
||||
this._autoRelay = this._options.autoRelay.enabled && new AutoRelay({ libp2p, ...this._options.autoRelay })
|
||||
this.peerId = libp2p.peerId
|
||||
this._registrar.handle(multicodec, this._onProtocol.bind(this))
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Relay service.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
start () {
|
||||
// Advertise service if HOP enabled
|
||||
const canHop = this._options.hop.enabled
|
||||
async _onProtocol ({ connection, stream, protocol }) {
|
||||
const streamHandler = new StreamHandler({ stream })
|
||||
const request = await streamHandler.read()
|
||||
const circuit = this
|
||||
let virtualConnection
|
||||
|
||||
if (canHop && this._options.advertise.enabled) {
|
||||
this._timeout = setTimeout(() => {
|
||||
this._advertiseService()
|
||||
}, this._options.advertise.bootDelay)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop Relay service.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
stop () {
|
||||
clearTimeout(this._timeout)
|
||||
}
|
||||
|
||||
/**
|
||||
* Advertise hop relay service in the network.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _advertiseService () {
|
||||
try {
|
||||
const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS)
|
||||
await this._libp2p.contentRouting.provide(cid)
|
||||
} catch (err) {
|
||||
if (err.code === 'NO_ROUTERS_AVAILABLE') {
|
||||
log.error('a content router, such as a DHT, must be provided in order to advertise the relay service', err)
|
||||
// Stop the advertise
|
||||
this.stop()
|
||||
} else {
|
||||
log.error(err)
|
||||
switch (request.type) {
|
||||
case CircuitPB.Type.CAN_HOP: {
|
||||
log('received CAN_HOP request from %s', connection.remotePeer.toB58String())
|
||||
await handleCanHop({ circuit, connection, streamHandler })
|
||||
break
|
||||
}
|
||||
case CircuitPB.Type.HOP: {
|
||||
log('received HOP request from %s', connection.remotePeer.toB58String())
|
||||
virtualConnection = await handleHop({
|
||||
connection,
|
||||
request,
|
||||
streamHandler,
|
||||
circuit
|
||||
})
|
||||
break
|
||||
}
|
||||
case CircuitPB.Type.STOP: {
|
||||
log('received STOP request from %s', connection.remotePeer.toB58String())
|
||||
virtualConnection = await handleStop({
|
||||
connection,
|
||||
request,
|
||||
streamHandler,
|
||||
circuit
|
||||
})
|
||||
break
|
||||
}
|
||||
default: {
|
||||
log('Request of type %s not supported', request.type)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Restart timeout
|
||||
this._timeout = setTimeout(() => {
|
||||
this._advertiseService()
|
||||
}, this._options.advertise.ttl)
|
||||
if (virtualConnection) {
|
||||
const remoteAddr = multiaddr(request.dstPeer.addrs[0])
|
||||
const localAddr = multiaddr(request.srcPeer.addrs[0])
|
||||
const maConn = toConnection({
|
||||
stream: virtualConnection,
|
||||
remoteAddr,
|
||||
localAddr
|
||||
})
|
||||
const type = CircuitPB.Type === CircuitPB.Type.HOP ? 'relay' : 'inbound'
|
||||
log('new %s connection %s', type, maConn.remoteAddr)
|
||||
|
||||
const conn = await this._upgrader.upgradeInbound(maConn)
|
||||
log('%s connection %s upgraded', type, maConn.remoteAddr)
|
||||
this.handler && this.handler(conn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dial a peer over a relay
|
||||
*
|
||||
* @param {multiaddr} ma - the multiaddr of the peer to dial
|
||||
* @param {Object} options - dial options
|
||||
* @param {AbortSignal} [options.signal] - An optional abort signal
|
||||
* @returns {Connection} - the connection
|
||||
*/
|
||||
async dial (ma, options) {
|
||||
// Check the multiaddr to see if it contains a relay and a destination peer
|
||||
const addrs = ma.toString().split('/p2p-circuit')
|
||||
const relayAddr = multiaddr(addrs[0])
|
||||
const destinationAddr = multiaddr(addrs[addrs.length - 1])
|
||||
const relayPeer = PeerId.createFromCID(relayAddr.getPeerId())
|
||||
const destinationPeer = PeerId.createFromCID(destinationAddr.getPeerId())
|
||||
|
||||
let disconnectOnFailure = false
|
||||
let relayConnection = this._connectionManager.get(relayPeer)
|
||||
if (!relayConnection) {
|
||||
relayConnection = await this._dialer.connectToPeer(relayAddr, options)
|
||||
disconnectOnFailure = true
|
||||
}
|
||||
|
||||
try {
|
||||
const virtualConnection = await hop({
|
||||
connection: relayConnection,
|
||||
circuit: this,
|
||||
request: {
|
||||
type: CircuitPB.Type.HOP,
|
||||
srcPeer: {
|
||||
id: this.peerId.toBytes(),
|
||||
addrs: this._libp2p.multiaddrs.map(addr => addr.bytes)
|
||||
},
|
||||
dstPeer: {
|
||||
id: destinationPeer.toBytes(),
|
||||
addrs: [multiaddr(destinationAddr).bytes]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toB58String()}`)
|
||||
const maConn = toConnection({
|
||||
stream: virtualConnection,
|
||||
remoteAddr: ma,
|
||||
localAddr
|
||||
})
|
||||
log('new outbound connection %s', maConn.remoteAddr)
|
||||
|
||||
return this._upgrader.upgradeOutbound(maConn)
|
||||
} catch (err) {
|
||||
log.error('Circuit relay dial failed', err)
|
||||
disconnectOnFailure && await relayConnection.close()
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener
|
||||
*
|
||||
* @param {any} options
|
||||
* @param {Function} handler
|
||||
* @returns {listener}
|
||||
*/
|
||||
createListener (options, handler) {
|
||||
if (typeof options === 'function') {
|
||||
handler = options
|
||||
options = {}
|
||||
}
|
||||
|
||||
// Called on successful HOP and STOP requests
|
||||
this.handler = handler
|
||||
|
||||
return createListener(this, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter check for all Multiaddrs that this transport can dial on
|
||||
*
|
||||
* @param {Array<Multiaddr>} multiaddrs
|
||||
* @returns {Array<Multiaddr>}
|
||||
*/
|
||||
filter (multiaddrs) {
|
||||
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
|
||||
|
||||
return multiaddrs.filter((ma) => {
|
||||
return mafmt.Circuit.matches(ma)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Relay
|
||||
/**
|
||||
* @type {Circuit}
|
||||
*/
|
||||
module.exports = withIs(Circuit, { className: 'Circuit', symbolName: '@libp2p/js-libp2p-circuit/circuit' })
|
||||
|
@@ -8,23 +8,13 @@ const log = debug('libp2p:circuit:listener')
|
||||
log.err = debug('libp2p:circuit:error:listener')
|
||||
|
||||
/**
|
||||
* @param {Libp2p} libp2p
|
||||
* @param {*} circuit
|
||||
* @returns {Listener} a transport listener
|
||||
*/
|
||||
module.exports = (libp2p) => {
|
||||
module.exports = (circuit) => {
|
||||
const listener = new EventEmitter()
|
||||
const listeningAddrs = new Map()
|
||||
|
||||
// Remove listeningAddrs when a peer disconnects
|
||||
libp2p.connectionManager.on('peer:disconnect', (connection) => {
|
||||
const deleted = listeningAddrs.delete(connection.remotePeer.toB58String())
|
||||
|
||||
if (deleted) {
|
||||
// Announce listen addresses change
|
||||
listener.emit('close')
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Add swarm handler and listen for incoming connections
|
||||
*
|
||||
@@ -34,7 +24,7 @@ module.exports = (libp2p) => {
|
||||
listener.listen = async (addr) => {
|
||||
const addrString = String(addr).split('/p2p-circuit').find(a => a !== '')
|
||||
|
||||
const relayConn = await libp2p.dial(multiaddr(addrString))
|
||||
const relayConn = await circuit._dialer.connectToPeer(multiaddr(addrString))
|
||||
const relayedAddr = relayConn.remoteAddr.encapsulate('/p2p-circuit')
|
||||
|
||||
listeningAddrs.set(relayConn.remotePeer.toB58String(), relayedAddr)
|
||||
|
@@ -1,194 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:circuit')
|
||||
log.error = debug('libp2p:circuit:error')
|
||||
|
||||
const mafmt = require('mafmt')
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
const withIs = require('class-is')
|
||||
const { CircuitRelay: CircuitPB } = require('./protocol')
|
||||
|
||||
const toConnection = require('libp2p-utils/src/stream-to-ma-conn')
|
||||
|
||||
const { relay: multicodec } = require('./multicodec')
|
||||
const createListener = require('./listener')
|
||||
const { handleCanHop, handleHop, hop } = require('./circuit/hop')
|
||||
const { handleStop } = require('./circuit/stop')
|
||||
const StreamHandler = require('./circuit/stream-handler')
|
||||
|
||||
class Circuit {
|
||||
/**
|
||||
* Creates an instance of the Circuit Transport.
|
||||
*
|
||||
* @class
|
||||
* @param {object} options
|
||||
* @param {Libp2p} options.libp2p
|
||||
* @param {Upgrader} options.upgrader
|
||||
*/
|
||||
constructor ({ libp2p, upgrader }) {
|
||||
this._dialer = libp2p.dialer
|
||||
this._registrar = libp2p.registrar
|
||||
this._connectionManager = libp2p.connectionManager
|
||||
this._upgrader = upgrader
|
||||
this._options = libp2p._config.relay
|
||||
this._libp2p = libp2p
|
||||
this.peerId = libp2p.peerId
|
||||
|
||||
this._registrar.handle(multicodec, this._onProtocol.bind(this))
|
||||
}
|
||||
|
||||
async _onProtocol ({ connection, stream }) {
|
||||
const streamHandler = new StreamHandler({ stream })
|
||||
const request = await streamHandler.read()
|
||||
|
||||
if (!request) {
|
||||
return
|
||||
}
|
||||
|
||||
const circuit = this
|
||||
let virtualConnection
|
||||
|
||||
switch (request.type) {
|
||||
case CircuitPB.Type.CAN_HOP: {
|
||||
log('received CAN_HOP request from %s', connection.remotePeer.toB58String())
|
||||
await handleCanHop({ circuit, connection, streamHandler })
|
||||
break
|
||||
}
|
||||
case CircuitPB.Type.HOP: {
|
||||
log('received HOP request from %s', connection.remotePeer.toB58String())
|
||||
virtualConnection = await handleHop({
|
||||
connection,
|
||||
request,
|
||||
streamHandler,
|
||||
circuit
|
||||
})
|
||||
break
|
||||
}
|
||||
case CircuitPB.Type.STOP: {
|
||||
log('received STOP request from %s', connection.remotePeer.toB58String())
|
||||
virtualConnection = await handleStop({
|
||||
connection,
|
||||
request,
|
||||
streamHandler,
|
||||
circuit
|
||||
})
|
||||
break
|
||||
}
|
||||
default: {
|
||||
log('Request of type %s not supported', request.type)
|
||||
}
|
||||
}
|
||||
|
||||
if (virtualConnection) {
|
||||
const remoteAddr = multiaddr(request.dstPeer.addrs[0])
|
||||
const localAddr = multiaddr(request.srcPeer.addrs[0])
|
||||
const maConn = toConnection({
|
||||
stream: virtualConnection,
|
||||
remoteAddr,
|
||||
localAddr
|
||||
})
|
||||
const type = CircuitPB.Type === CircuitPB.Type.HOP ? 'relay' : 'inbound'
|
||||
log('new %s connection %s', type, maConn.remoteAddr)
|
||||
|
||||
const conn = await this._upgrader.upgradeInbound(maConn)
|
||||
log('%s connection %s upgraded', type, maConn.remoteAddr)
|
||||
this.handler && this.handler(conn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dial a peer over a relay
|
||||
*
|
||||
* @param {multiaddr} ma - the multiaddr of the peer to dial
|
||||
* @param {Object} options - dial options
|
||||
* @param {AbortSignal} [options.signal] - An optional abort signal
|
||||
* @returns {Connection} - the connection
|
||||
*/
|
||||
async dial (ma, options) {
|
||||
// Check the multiaddr to see if it contains a relay and a destination peer
|
||||
const addrs = ma.toString().split('/p2p-circuit')
|
||||
const relayAddr = multiaddr(addrs[0])
|
||||
const destinationAddr = multiaddr(addrs[addrs.length - 1])
|
||||
const relayPeer = PeerId.createFromCID(relayAddr.getPeerId())
|
||||
const destinationPeer = PeerId.createFromCID(destinationAddr.getPeerId())
|
||||
|
||||
let disconnectOnFailure = false
|
||||
let relayConnection = this._connectionManager.get(relayPeer)
|
||||
if (!relayConnection) {
|
||||
relayConnection = await this._dialer.connectToPeer(relayAddr, options)
|
||||
disconnectOnFailure = true
|
||||
}
|
||||
|
||||
try {
|
||||
const virtualConnection = await hop({
|
||||
connection: relayConnection,
|
||||
circuit: this,
|
||||
request: {
|
||||
type: CircuitPB.Type.HOP,
|
||||
srcPeer: {
|
||||
id: this.peerId.toBytes(),
|
||||
addrs: this._libp2p.multiaddrs.map(addr => addr.bytes)
|
||||
},
|
||||
dstPeer: {
|
||||
id: destinationPeer.toBytes(),
|
||||
addrs: [multiaddr(destinationAddr).bytes]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toB58String()}`)
|
||||
const maConn = toConnection({
|
||||
stream: virtualConnection,
|
||||
remoteAddr: ma,
|
||||
localAddr
|
||||
})
|
||||
log('new outbound connection %s', maConn.remoteAddr)
|
||||
|
||||
return this._upgrader.upgradeOutbound(maConn)
|
||||
} catch (err) {
|
||||
log.error('Circuit relay dial failed', err)
|
||||
disconnectOnFailure && await relayConnection.close()
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener
|
||||
*
|
||||
* @param {any} options
|
||||
* @param {Function} handler
|
||||
* @returns {listener}
|
||||
*/
|
||||
createListener (options, handler) {
|
||||
if (typeof options === 'function') {
|
||||
handler = options
|
||||
options = {}
|
||||
}
|
||||
|
||||
// Called on successful HOP and STOP requests
|
||||
this.handler = handler
|
||||
|
||||
return createListener(this._libp2p, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter check for all Multiaddrs that this transport can dial on
|
||||
*
|
||||
* @param {Array<Multiaddr>} multiaddrs
|
||||
* @returns {Array<Multiaddr>}
|
||||
*/
|
||||
filter (multiaddrs) {
|
||||
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
|
||||
|
||||
return multiaddrs.filter((ma) => {
|
||||
return mafmt.Circuit.matches(ma)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Circuit}
|
||||
*/
|
||||
module.exports = withIs(Circuit, { className: 'Circuit', symbolName: '@libp2p/js-libp2p-circuit/circuit' })
|
@@ -1,17 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const CID = require('cids')
|
||||
const multihashing = require('multihashing-async')
|
||||
|
||||
/**
|
||||
* Convert a namespace string into a cid.
|
||||
*
|
||||
* @param {string} namespace
|
||||
* @returns {Promise<CID>}
|
||||
*/
|
||||
module.exports.namespaceToCid = async (namespace) => {
|
||||
const bytes = new TextEncoder('utf8').encode(namespace)
|
||||
const hash = await multihashing(bytes, 'sha2-256')
|
||||
|
||||
return new CID(hash)
|
||||
}
|
@@ -4,7 +4,7 @@ const mergeOptions = require('merge-options')
|
||||
const { dnsaddrResolver } = require('multiaddr/src/resolvers')
|
||||
|
||||
const Constants = require('./constants')
|
||||
const RelayConstants = require('./circuit/constants')
|
||||
const { AGENT_VERSION } = require('./identify/consts')
|
||||
|
||||
const { FaultTolerance } = require('./transport-manager')
|
||||
|
||||
@@ -28,6 +28,9 @@ const DefaultConfig = {
|
||||
dnsaddr: dnsaddrResolver
|
||||
}
|
||||
},
|
||||
host: {
|
||||
agentVersion: AGENT_VERSION
|
||||
},
|
||||
metrics: {
|
||||
enabled: false
|
||||
},
|
||||
@@ -57,18 +60,9 @@ const DefaultConfig = {
|
||||
},
|
||||
relay: {
|
||||
enabled: true,
|
||||
advertise: {
|
||||
bootDelay: RelayConstants.ADVERTISE_BOOT_DELAY,
|
||||
enabled: false,
|
||||
ttl: RelayConstants.ADVERTISE_TTL
|
||||
},
|
||||
hop: {
|
||||
enabled: false,
|
||||
active: false
|
||||
},
|
||||
autoRelay: {
|
||||
enabled: false,
|
||||
maxListeners: 2
|
||||
}
|
||||
},
|
||||
transport: {}
|
||||
|
@@ -16,6 +16,7 @@ exports.codes = {
|
||||
ERR_NODE_NOT_STARTED: 'ERR_NODE_NOT_STARTED',
|
||||
ERR_ALREADY_ABORTED: 'ERR_ALREADY_ABORTED',
|
||||
ERR_NO_VALID_ADDRESSES: 'ERR_NO_VALID_ADDRESSES',
|
||||
ERR_DIALED_SELF: 'ERR_DIALED_SELF',
|
||||
ERR_DISCOVERED_SELF: 'ERR_DISCOVERED_SELF',
|
||||
ERR_DUPLICATE_TRANSPORT: 'ERR_DUPLICATE_TRANSPORT',
|
||||
ERR_ENCRYPTION_FAILED: 'ERR_ENCRYPTION_FAILED',
|
||||
|
@@ -51,8 +51,9 @@ class IdentifyService {
|
||||
* @class
|
||||
* @param {object} options
|
||||
* @param {Libp2p} options.libp2p
|
||||
* @param {Map<string, handler>} options.protocols - A reference to the protocols we support
|
||||
*/
|
||||
constructor ({ libp2p }) {
|
||||
constructor ({ libp2p, protocols }) {
|
||||
/**
|
||||
* @property {PeerStore}
|
||||
*/
|
||||
@@ -63,6 +64,12 @@ class IdentifyService {
|
||||
*/
|
||||
this.connectionManager = libp2p.connectionManager
|
||||
|
||||
this.connectionManager.on('peer:connect', (connection) => {
|
||||
const peerId = connection.remotePeer
|
||||
|
||||
this.identify(connection, peerId).catch(log.error)
|
||||
})
|
||||
|
||||
/**
|
||||
* @property {PeerId}
|
||||
*/
|
||||
@@ -73,28 +80,19 @@ class IdentifyService {
|
||||
*/
|
||||
this._libp2p = libp2p
|
||||
|
||||
this._protocols = protocols
|
||||
|
||||
this.handleMessage = this.handleMessage.bind(this)
|
||||
|
||||
// When a new connection happens, trigger identify
|
||||
this.connectionManager.on('peer:connect', (connection) => {
|
||||
const peerId = connection.remotePeer
|
||||
// Store self host metadata
|
||||
this._host = {
|
||||
agentVersion: AGENT_VERSION,
|
||||
protocolVersion: PROTOCOL_VERSION,
|
||||
...libp2p._options.host
|
||||
}
|
||||
|
||||
this.identify(connection, peerId).catch(log.error)
|
||||
})
|
||||
|
||||
// When self multiaddrs change, trigger identify-push
|
||||
this.peerStore.on('change:multiaddrs', ({ peerId }) => {
|
||||
if (peerId.toString() === this.peerId.toString()) {
|
||||
this.pushToPeerStore()
|
||||
}
|
||||
})
|
||||
|
||||
// When self protocols change, trigger identify-push
|
||||
this.peerStore.on('change:protocols', ({ peerId }) => {
|
||||
if (peerId.toString() === this.peerId.toString()) {
|
||||
this.pushToPeerStore()
|
||||
}
|
||||
})
|
||||
this.peerStore.metadataBook.set(this.peerId, 'AgentVersion', uint8ArrayFromString(this._host.agentVersion))
|
||||
this.peerStore.metadataBook.set(this.peerId, 'ProtocolVersion', uint8ArrayFromString(this._host.protocolVersion))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,9 +102,9 @@ class IdentifyService {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async push (connections) {
|
||||
const signedPeerRecord = await this.peerStore.addressBook.getRawEnvelope(this.peerId)
|
||||
const signedPeerRecord = await this._getSelfPeerRecord()
|
||||
const listenAddrs = this._libp2p.multiaddrs.map((ma) => ma.bytes)
|
||||
const protocols = this.peerStore.protoBook.get(this.peerId) || []
|
||||
const protocols = Array.from(this._protocols.keys())
|
||||
|
||||
const pushes = connections.map(async connection => {
|
||||
try {
|
||||
@@ -134,17 +132,12 @@ class IdentifyService {
|
||||
/**
|
||||
* Calls `push` for all peers in the `peerStore` that are connected
|
||||
*
|
||||
* @returns {void}
|
||||
* @param {PeerStore} peerStore
|
||||
*/
|
||||
pushToPeerStore () {
|
||||
// Do not try to push if libp2p node is not running
|
||||
if (!this._libp2p.isStarted()) {
|
||||
return
|
||||
}
|
||||
|
||||
pushToPeerStore (peerStore) {
|
||||
const connections = []
|
||||
let connection
|
||||
for (const peer of this.peerStore.peers.values()) {
|
||||
for (const peer of peerStore.peers.values()) {
|
||||
if (peer.protocols.includes(MULTICODEC_IDENTIFY_PUSH) && (connection = this.connectionManager.get(peer.id))) {
|
||||
connections.push(connection)
|
||||
}
|
||||
@@ -260,17 +253,16 @@ class IdentifyService {
|
||||
publicKey = this.peerId.pubKey.bytes
|
||||
}
|
||||
|
||||
const signedPeerRecord = await this.peerStore.addressBook.getRawEnvelope(this.peerId)
|
||||
const protocols = this.peerStore.protoBook.get(this.peerId) || []
|
||||
const signedPeerRecord = await this._getSelfPeerRecord()
|
||||
|
||||
const message = Message.encode({
|
||||
protocolVersion: PROTOCOL_VERSION,
|
||||
agentVersion: AGENT_VERSION,
|
||||
protocolVersion: this._host.protocolVersion,
|
||||
agentVersion: this._host.agentVersion,
|
||||
publicKey,
|
||||
listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.bytes),
|
||||
signedPeerRecord,
|
||||
observedAddr: connection.remoteAddr.bytes,
|
||||
protocols
|
||||
protocols: Array.from(this._protocols.keys())
|
||||
})
|
||||
|
||||
try {
|
||||
@@ -331,6 +323,34 @@ class IdentifyService {
|
||||
// Update the protocols
|
||||
this.peerStore.protoBook.set(id, message.protocols)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self signed peer record raw envelope.
|
||||
*
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
async _getSelfPeerRecord () {
|
||||
const selfSignedPeerRecord = this.peerStore.addressBook.getRawEnvelope(this.peerId)
|
||||
|
||||
// TODO: support invalidation when dynamic multiaddrs are supported
|
||||
if (selfSignedPeerRecord) {
|
||||
return selfSignedPeerRecord
|
||||
}
|
||||
|
||||
try {
|
||||
const peerRecord = new PeerRecord({
|
||||
peerId: this.peerId,
|
||||
multiaddrs: this._libp2p.multiaddrs
|
||||
})
|
||||
const envelope = await Envelope.seal(peerRecord, this.peerId)
|
||||
this.peerStore.addressBook.consumePeerRecord(envelope)
|
||||
|
||||
return this.peerStore.addressBook.getRawEnvelope(this.peerId)
|
||||
} catch (err) {
|
||||
log.error('failed to get self peer record')
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.IdentifyService = IdentifyService
|
||||
|
44
src/index.js
44
src/index.js
@@ -17,8 +17,7 @@ const { codes, messages } = require('./errors')
|
||||
|
||||
const AddressManager = require('./address-manager')
|
||||
const ConnectionManager = require('./connection-manager')
|
||||
const Circuit = require('./circuit/transport')
|
||||
const Relay = require('./circuit')
|
||||
const Circuit = require('./circuit')
|
||||
const Dialer = require('./dialer')
|
||||
const Keychain = require('./keychain')
|
||||
const Metrics = require('./metrics')
|
||||
@@ -147,7 +146,6 @@ class Libp2p extends EventEmitter {
|
||||
|
||||
if (this._config.relay.enabled) {
|
||||
this.transportManager.add(Circuit.prototype[Symbol.toStringTag], Circuit)
|
||||
this.relay = new Relay(this)
|
||||
}
|
||||
|
||||
// Attach stream multiplexers
|
||||
@@ -158,7 +156,10 @@ class Libp2p extends EventEmitter {
|
||||
})
|
||||
|
||||
// Add the identify service since we can multiplex
|
||||
this.identifyService = new IdentifyService({ libp2p: this })
|
||||
this.identifyService = new IdentifyService({
|
||||
libp2p: this,
|
||||
protocols: this.upgrader.protocols
|
||||
})
|
||||
this.handle(Object.values(IDENTIFY_PROTOCOLS), this.identifyService.handleMessage)
|
||||
}
|
||||
|
||||
@@ -241,17 +242,12 @@ class Libp2p extends EventEmitter {
|
||||
* Stop the libp2p node by closing its listeners and open connections
|
||||
*
|
||||
* @async
|
||||
* @returns {void}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async stop () {
|
||||
log('libp2p is stopping')
|
||||
|
||||
try {
|
||||
this._isStarted = false
|
||||
|
||||
// Relay
|
||||
this.relay && this.relay.stop()
|
||||
|
||||
for (const service of this._discovery.values()) {
|
||||
service.removeListener('peer', this._onDiscoveryPeer)
|
||||
}
|
||||
@@ -279,6 +275,7 @@ class Libp2p extends EventEmitter {
|
||||
this.emit('error', err)
|
||||
}
|
||||
}
|
||||
this._isStarted = false
|
||||
log('libp2p has stopped')
|
||||
}
|
||||
|
||||
@@ -338,6 +335,11 @@ class Libp2p extends EventEmitter {
|
||||
*/
|
||||
async dialProtocol (peer, protocols, options) {
|
||||
const { id, multiaddrs } = getPeer(peer)
|
||||
|
||||
if (id.equals(this.peerId)) {
|
||||
throw errCode(new Error('Cannot dial self'), codes.ERR_DIALED_SELF)
|
||||
}
|
||||
|
||||
let connection = this.connectionManager.get(id)
|
||||
|
||||
if (!connection) {
|
||||
@@ -434,8 +436,10 @@ class Libp2p extends EventEmitter {
|
||||
this.upgrader.protocols.set(protocol, handler)
|
||||
})
|
||||
|
||||
// Add new protocols to self protocols in the Protobook
|
||||
this.peerStore.protoBook.add(this.peerId, protocols)
|
||||
// Only push if libp2p is running
|
||||
if (this.isStarted() && this.identifyService) {
|
||||
this.identifyService.pushToPeerStore(this.peerStore)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,14 +454,15 @@ class Libp2p extends EventEmitter {
|
||||
this.upgrader.protocols.delete(protocol)
|
||||
})
|
||||
|
||||
// Remove protocols from self protocols in the Protobook
|
||||
this.peerStore.protoBook.remove(this.peerId, protocols)
|
||||
// Only push if libp2p is running
|
||||
if (this.isStarted() && this.identifyService) {
|
||||
this.identifyService.pushToPeerStore(this.peerStore)
|
||||
}
|
||||
}
|
||||
|
||||
async _onStarting () {
|
||||
// Listen on the provided transports for the provided addresses
|
||||
const addrs = this.addressManager.getListenAddrs()
|
||||
await this.transportManager.listen(addrs)
|
||||
// Listen on the provided transports
|
||||
await this.transportManager.listen()
|
||||
|
||||
// Start PeerStore
|
||||
await this.peerStore.start()
|
||||
@@ -502,9 +507,6 @@ class Libp2p extends EventEmitter {
|
||||
|
||||
// Peer discovery
|
||||
await this._setupPeerDiscovery()
|
||||
|
||||
// Relay
|
||||
this.relay && this.relay.start()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -604,7 +606,7 @@ class Libp2p extends EventEmitter {
|
||||
* instance if one is not provided in options.
|
||||
*
|
||||
* @param {object} options - Libp2p configuration options
|
||||
* @returns {Libp2p}
|
||||
* @returns {Promise<Libp2p>}
|
||||
*/
|
||||
Libp2p.create = async function create (options = {}) {
|
||||
if (options.peerId) {
|
||||
|
@@ -270,7 +270,7 @@ class AddressBook extends Book {
|
||||
*
|
||||
* @override
|
||||
* @param {PeerId} peerId
|
||||
* @returns {Array<Address>|undefined}
|
||||
* @returns {Array<data>}
|
||||
*/
|
||||
get (peerId) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
|
@@ -112,50 +112,13 @@ class ProtoBook extends Book {
|
||||
return this
|
||||
}
|
||||
|
||||
protocols = [...newSet]
|
||||
|
||||
this._setData(peerId, newSet)
|
||||
log(`added provided protocols for ${id}`)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes known protocols of a provided peer.
|
||||
* If the protocols did not exist before, nothing will be done.
|
||||
*
|
||||
* @param {PeerId} peerId
|
||||
* @param {Array<string>} protocols
|
||||
* @returns {ProtoBook}
|
||||
*/
|
||||
remove (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)
|
||||
|
||||
if (recSet) {
|
||||
const newSet = new Set([
|
||||
...recSet
|
||||
].filter((p) => !protocols.includes(p)))
|
||||
|
||||
// Any protocol removed?
|
||||
if (recSet.size === newSet.size) {
|
||||
return this
|
||||
}
|
||||
|
||||
this._setData(peerId, newSet)
|
||||
log(`removed provided protocols for ${id}`)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProtoBook
|
||||
|
@@ -1,21 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const Envelope = require('./envelope')
|
||||
const PeerRecord = require('./peer-record')
|
||||
|
||||
/**
|
||||
* Create (or update if existing) self peer record and store it in the AddressBook.
|
||||
*
|
||||
* @param {libp2p} libp2p
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function updateSelfPeerRecord (libp2p) {
|
||||
const peerRecord = new PeerRecord({
|
||||
peerId: libp2p.peerId,
|
||||
multiaddrs: libp2p.multiaddrs
|
||||
})
|
||||
const envelope = await Envelope.seal(peerRecord, libp2p.peerId)
|
||||
libp2p.peerStore.addressBook.consumePeerRecord(envelope)
|
||||
}
|
||||
|
||||
module.exports.updateSelfPeerRecord = updateSelfPeerRecord
|
@@ -7,8 +7,6 @@ const debug = require('debug')
|
||||
const log = debug('libp2p:transports')
|
||||
log.error = debug('libp2p:transports:error')
|
||||
|
||||
const { updateSelfPeerRecord } = require('./record/utils')
|
||||
|
||||
class TransportManager {
|
||||
/**
|
||||
* @class
|
||||
@@ -22,6 +20,7 @@ class TransportManager {
|
||||
this.upgrader = upgrader
|
||||
this._transports = new Map()
|
||||
this._listeners = new Map()
|
||||
this._listenerOptions = new Map()
|
||||
this.faultTolerance = faultTolerance
|
||||
}
|
||||
|
||||
@@ -49,6 +48,7 @@ class TransportManager {
|
||||
})
|
||||
|
||||
this._transports.set(key, transport)
|
||||
this._listenerOptions.set(key, transportOptions.listenerOptions || {})
|
||||
if (!this._listeners.has(key)) {
|
||||
this._listeners.set(key, [])
|
||||
}
|
||||
@@ -65,8 +65,6 @@ class TransportManager {
|
||||
log('closing listeners for %s', key)
|
||||
while (listeners.length) {
|
||||
const listener = listeners.pop()
|
||||
listener.removeAllListeners('listening')
|
||||
listener.removeAllListeners('close')
|
||||
tasks.push(listener.close())
|
||||
}
|
||||
}
|
||||
@@ -141,10 +139,11 @@ class TransportManager {
|
||||
* Starts listeners for each listen Multiaddr.
|
||||
*
|
||||
* @async
|
||||
* @param {Array<Multiaddr>} addrs - addresses to attempt to listen on
|
||||
*/
|
||||
async listen (addrs) {
|
||||
if (!addrs || addrs.length === 0) {
|
||||
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
|
||||
}
|
||||
@@ -157,13 +156,9 @@ class TransportManager {
|
||||
// For each supported multiaddr, create a listener
|
||||
for (const addr of supportedAddrs) {
|
||||
log('creating listener for %s on %s', key, addr)
|
||||
const listener = transport.createListener({}, this.onConnection)
|
||||
const listener = transport.createListener(this._listenerOptions.get(key), this.onConnection)
|
||||
this._listeners.get(key).push(listener)
|
||||
|
||||
// Track listen/close events
|
||||
listener.on('listening', () => updateSelfPeerRecord(this.libp2p))
|
||||
listener.on('close', () => updateSelfPeerRecord(this.libp2p))
|
||||
|
||||
// We need to attempt to listen on everything
|
||||
tasks.push(listener.listen(addr))
|
||||
}
|
||||
@@ -208,8 +203,6 @@ class TransportManager {
|
||||
if (this._listeners.has(key)) {
|
||||
// Close any running listeners
|
||||
for (const listener of this._listeners.get(key)) {
|
||||
listener.removeAllListeners('listening')
|
||||
listener.removeAllListeners('close')
|
||||
await listener.close()
|
||||
}
|
||||
}
|
||||
|
@@ -18,10 +18,16 @@ const listenMultiaddr = '/ip4/127.0.0.1/tcp/15002/ws'
|
||||
|
||||
describe('Connection Manager', () => {
|
||||
let libp2p
|
||||
let peerIds
|
||||
|
||||
before(async () => {
|
||||
peerIds = await peerUtils.createPeerId({ number: 2 })
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
peerId: peerIds[0],
|
||||
addresses: {
|
||||
listen: [listenMultiaddr]
|
||||
},
|
||||
@@ -33,12 +39,10 @@ describe('Connection Manager', () => {
|
||||
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: peerIds[0], remotePeer: peerIds[1] })
|
||||
const conn2 = await mockConnection({ localPeer: peerIds[0], remotePeer: peerIds[1] })
|
||||
|
||||
const conn1 = await mockConnection({ localPeer, remotePeer })
|
||||
const conn2 = await mockConnection({ localPeer, remotePeer })
|
||||
|
||||
const id = remotePeer.toB58String()
|
||||
const id = peerIds[1].toB58String()
|
||||
|
||||
// Add connection to the connectionManager
|
||||
libp2p.connectionManager.onConnect(conn1)
|
||||
@@ -57,6 +61,7 @@ describe('Connection Manager', () => {
|
||||
it('should add connection on dial and remove on node stop', async () => {
|
||||
const [remoteLibp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
peerId: peerIds[1],
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/15003/ws']
|
||||
},
|
||||
@@ -89,9 +94,16 @@ describe('Connection Manager', () => {
|
||||
})
|
||||
|
||||
describe('libp2p.connections', () => {
|
||||
let peerIds
|
||||
|
||||
before(async () => {
|
||||
peerIds = await peerUtils.createPeerId({ number: 2 })
|
||||
})
|
||||
|
||||
it('libp2p.connections gets the connectionManager conns', async () => {
|
||||
const [libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
peerId: peerIds[0],
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/15003/ws']
|
||||
},
|
||||
@@ -100,6 +112,7 @@ describe('libp2p.connections', () => {
|
||||
})
|
||||
const [remoteLibp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
peerId: peerIds[1],
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/15004/ws']
|
||||
},
|
||||
|
@@ -42,28 +42,21 @@ describe('Dialing (direct, TCP)', () => {
|
||||
let peerStore
|
||||
let remoteAddr
|
||||
|
||||
beforeEach(async () => {
|
||||
const [localPeerId, remotePeerId] = await Promise.all([
|
||||
PeerId.createFromJSON(Peers[0]),
|
||||
PeerId.createFromJSON(Peers[1])
|
||||
before(async () => {
|
||||
const [remotePeerId] = await Promise.all([
|
||||
PeerId.createFromJSON(Peers[0])
|
||||
])
|
||||
|
||||
peerStore = new PeerStore({ peerId: remotePeerId })
|
||||
remoteTM = new TransportManager({
|
||||
libp2p: {
|
||||
addressManager: new AddressManager({ listen: [listenAddr] }),
|
||||
peerId: remotePeerId,
|
||||
peerStore
|
||||
addressManager: new AddressManager({ listen: [listenAddr] })
|
||||
},
|
||||
upgrader: mockUpgrader
|
||||
})
|
||||
remoteTM.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
|
||||
peerStore = new PeerStore({ peerId: remotePeerId })
|
||||
localTM = new TransportManager({
|
||||
libp2p: {
|
||||
peerId: localPeerId,
|
||||
peerStore: new PeerStore({ peerId: localPeerId })
|
||||
},
|
||||
libp2p: {},
|
||||
upgrader: mockUpgrader
|
||||
})
|
||||
localTM.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
@@ -73,7 +66,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
remoteAddr = remoteTM.getAddrs()[0].encapsulate(`/p2p/${remotePeerId.toB58String()}`)
|
||||
})
|
||||
|
||||
afterEach(() => remoteTM.close())
|
||||
after(() => remoteTM.close())
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore()
|
||||
@@ -119,7 +112,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
peerStore
|
||||
})
|
||||
|
||||
peerStore.addressBook.set(peerId, remoteTM.getAddrs())
|
||||
peerStore.addressBook.set(peerId, [remoteAddr])
|
||||
|
||||
const connection = await dialer.connectToPeer(peerId)
|
||||
expect(connection).to.exist()
|
||||
|
@@ -349,6 +349,7 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
const connection = await libp2p.dial(remoteAddr)
|
||||
expect(connection).to.exist()
|
||||
|
||||
sinon.spy(libp2p.peerStore.addressBook, 'consumePeerRecord')
|
||||
sinon.spy(libp2p.peerStore.protoBook, 'set')
|
||||
|
||||
// Wait for onConnection to be called
|
||||
@@ -357,6 +358,8 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
expect(libp2p.identifyService.identify.callCount).to.equal(1)
|
||||
await libp2p.identifyService.identify.firstCall.returnValue
|
||||
|
||||
// Self + New peer
|
||||
expect(libp2p.peerStore.addressBook.consumePeerRecord.callCount).to.equal(2)
|
||||
expect(libp2p.peerStore.protoBook.set.callCount).to.equal(1)
|
||||
})
|
||||
|
||||
@@ -406,5 +409,20 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
|
||||
expect(libp2p.dialer.destroy).to.have.property('callCount', 1)
|
||||
})
|
||||
|
||||
it('should fail to dial self', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
streamMuxer: [Muxer],
|
||||
connEncryption: [Crypto]
|
||||
}
|
||||
})
|
||||
|
||||
await expect(libp2p.dial(peerId))
|
||||
.to.eventually.be.rejected()
|
||||
.and.to.have.property('code', ErrorCodes.ERR_DIALED_SELF)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -72,7 +72,7 @@ describe('Dialing (via relay, TCP)', () => {
|
||||
const tcpAddrs = dstLibp2p.transportManager.getAddrs()
|
||||
sinon.stub(dstLibp2p.addressManager, 'listen').value([multiaddr(`/p2p-circuit${relayAddr}/p2p/${relayIdString}`)])
|
||||
|
||||
await dstLibp2p.transportManager.listen(dstLibp2p.addressManager.getListenAddrs())
|
||||
await dstLibp2p.transportManager.listen()
|
||||
expect(dstLibp2p.transportManager.getAddrs()).to.have.deep.members([...tcpAddrs, dialAddr.decapsulate('p2p')])
|
||||
|
||||
const connection = await srcLibp2p.dial(dialAddr)
|
||||
@@ -157,7 +157,7 @@ describe('Dialing (via relay, TCP)', () => {
|
||||
const tcpAddrs = dstLibp2p.transportManager.getAddrs()
|
||||
sinon.stub(dstLibp2p.addressManager, 'getListenAddrs').returns([multiaddr(`${relayAddr}/p2p-circuit`)])
|
||||
|
||||
await dstLibp2p.transportManager.listen(dstLibp2p.addressManager.getListenAddrs())
|
||||
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
|
@@ -8,6 +8,7 @@ const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const delay = require('delay')
|
||||
const PeerId = require('peer-id')
|
||||
const duplexPair = require('it-pair/duplex')
|
||||
const multiaddr = require('multiaddr')
|
||||
@@ -21,7 +22,6 @@ const Libp2p = require('../../src')
|
||||
const Envelope = require('../../src/record/envelope')
|
||||
const PeerStore = require('../../src/peer-store')
|
||||
const baseOptions = require('../utils/base-options.browser')
|
||||
const { updateSelfPeerRecord } = require('../../src/record/utils')
|
||||
const pkg = require('../../package.json')
|
||||
|
||||
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
|
||||
@@ -29,21 +29,18 @@ const remoteAddr = MULTIADDRS_WEBSOCKETS[0]
|
||||
const listenMaddrs = [multiaddr('/ip4/127.0.0.1/tcp/15002/ws')]
|
||||
|
||||
describe('Identify', () => {
|
||||
let localPeer, localPeerStore
|
||||
let remotePeer, remotePeerStore
|
||||
const protocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH]
|
||||
let localPeer
|
||||
let remotePeer
|
||||
const protocols = new Map([
|
||||
[multicodecs.IDENTIFY, () => {}],
|
||||
[multicodecs.IDENTIFY_PUSH, () => {}]
|
||||
])
|
||||
|
||||
before(async () => {
|
||||
[localPeer, remotePeer] = (await Promise.all([
|
||||
PeerId.createFromJSON(Peers[0]),
|
||||
PeerId.createFromJSON(Peers[1])
|
||||
]))
|
||||
|
||||
localPeerStore = new PeerStore({ peerId: localPeer })
|
||||
localPeerStore.protoBook.set(localPeer, protocols)
|
||||
|
||||
remotePeerStore = new PeerStore({ peerId: remotePeer })
|
||||
remotePeerStore.protoBook.set(remotePeer, protocols)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -55,19 +52,22 @@ describe('Identify', () => {
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: localPeerStore,
|
||||
peerStore: new PeerStore({ peerId: localPeer }),
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
}
|
||||
_options: { host: {} }
|
||||
},
|
||||
protocols
|
||||
})
|
||||
|
||||
const remoteIdentify = new IdentifyService({
|
||||
libp2p: {
|
||||
peerId: remotePeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: remotePeerStore,
|
||||
peerStore: new PeerStore({ peerId: remotePeer }),
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
}
|
||||
_options: { host: {} }
|
||||
},
|
||||
protocols
|
||||
})
|
||||
|
||||
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
|
||||
@@ -80,9 +80,6 @@ describe('Identify', () => {
|
||||
sinon.spy(localIdentify.peerStore.addressBook, 'consumePeerRecord')
|
||||
sinon.spy(localIdentify.peerStore.protoBook, 'set')
|
||||
|
||||
// Transport Manager creates signed peer record
|
||||
await updateSelfPeerRecord(remoteIdentify._libp2p)
|
||||
|
||||
// Run identify
|
||||
await Promise.all([
|
||||
localIdentify.identify(localConnectionMock),
|
||||
@@ -110,20 +107,22 @@ describe('Identify', () => {
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: localPeerStore,
|
||||
peerStore: new PeerStore({ peerId: localPeer }),
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
}
|
||||
_options: { host: {} }
|
||||
},
|
||||
protocols
|
||||
})
|
||||
|
||||
const remoteIdentify = new IdentifyService({
|
||||
libp2p: {
|
||||
peerId: remotePeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: remotePeerStore,
|
||||
peerStore: new PeerStore({ peerId: remotePeer }),
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
}
|
||||
_options: { host: {} }
|
||||
},
|
||||
protocols
|
||||
})
|
||||
|
||||
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
|
||||
@@ -169,17 +168,21 @@ describe('Identify', () => {
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: localPeerStore,
|
||||
multiaddrs: []
|
||||
}
|
||||
peerStore: new PeerStore({ peerId: localPeer }),
|
||||
multiaddrs: [],
|
||||
_options: { host: {} }
|
||||
},
|
||||
protocols
|
||||
})
|
||||
const remoteIdentify = new IdentifyService({
|
||||
libp2p: {
|
||||
peerId: remotePeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: remotePeerStore,
|
||||
multiaddrs: []
|
||||
}
|
||||
peerStore: new PeerStore({ peerId: remotePeer }),
|
||||
multiaddrs: [],
|
||||
_options: { host: {} }
|
||||
},
|
||||
protocols
|
||||
})
|
||||
|
||||
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
|
||||
@@ -204,40 +207,67 @@ describe('Identify', () => {
|
||||
.and.to.have.property('code', Errors.ERR_INVALID_PEER)
|
||||
})
|
||||
|
||||
it('should store host data and protocol version into metadataBook', () => {
|
||||
const agentVersion = 'js-project/1.0.0'
|
||||
const peerStore = new PeerStore({ peerId: localPeer })
|
||||
|
||||
sinon.spy(peerStore.metadataBook, 'set')
|
||||
|
||||
new IdentifyService({ // eslint-disable-line no-new
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore,
|
||||
multiaddrs: listenMaddrs,
|
||||
_options: {
|
||||
host: {
|
||||
agentVersion
|
||||
}
|
||||
}
|
||||
},
|
||||
protocols
|
||||
})
|
||||
|
||||
expect(peerStore.metadataBook.set.callCount).to.eql(2)
|
||||
|
||||
const storedAgentVersion = peerStore.metadataBook.getValue(localPeer, 'AgentVersion')
|
||||
const storedProtocolVersion = peerStore.metadataBook.getValue(localPeer, 'ProtocolVersion')
|
||||
|
||||
expect(agentVersion).to.eql(unit8ArrayToString(storedAgentVersion))
|
||||
expect(storedProtocolVersion).to.exist()
|
||||
})
|
||||
|
||||
describe('push', () => {
|
||||
it('should be able to push identify updates to another peer', async () => {
|
||||
const storedProtocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0']
|
||||
const connectionManager = new EventEmitter()
|
||||
connectionManager.getConnection = () => { }
|
||||
|
||||
const localPeerStore = new PeerStore({ peerId: localPeer })
|
||||
localPeerStore.protoBook.set(localPeer, storedProtocols)
|
||||
|
||||
const localIdentify = new IdentifyService({
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: localPeerStore,
|
||||
peerStore: new PeerStore({ peerId: localPeer }),
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
}
|
||||
_options: { host: {} }
|
||||
},
|
||||
protocols: new Map([
|
||||
[multicodecs.IDENTIFY],
|
||||
[multicodecs.IDENTIFY_PUSH],
|
||||
['/echo/1.0.0']
|
||||
])
|
||||
})
|
||||
|
||||
const remotePeerStore = new PeerStore({ peerId: remotePeer })
|
||||
remotePeerStore.protoBook.set(remotePeer, storedProtocols)
|
||||
|
||||
const remoteIdentify = new IdentifyService({
|
||||
libp2p: {
|
||||
peerId: remotePeer,
|
||||
connectionManager,
|
||||
peerStore: remotePeerStore,
|
||||
peerStore: new PeerStore({ peerId: remotePeer }),
|
||||
multiaddrs: [],
|
||||
isStarted: () => true
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
|
||||
// Setup peer protocols and multiaddrs
|
||||
const localProtocols = new Set(storedProtocols)
|
||||
const localProtocols = new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0'])
|
||||
const localConnectionMock = { newStream: () => { } }
|
||||
const remoteConnectionMock = { remotePeer: localPeer }
|
||||
|
||||
@@ -247,10 +277,6 @@ describe('Identify', () => {
|
||||
sinon.spy(remoteIdentify.peerStore.addressBook, 'consumePeerRecord')
|
||||
sinon.spy(remoteIdentify.peerStore.protoBook, 'set')
|
||||
|
||||
// Transport Manager creates signed peer record
|
||||
await updateSelfPeerRecord(localIdentify._libp2p)
|
||||
await updateSelfPeerRecord(remoteIdentify._libp2p)
|
||||
|
||||
// Run identify
|
||||
await Promise.all([
|
||||
localIdentify.push([localConnectionMock]),
|
||||
@@ -261,7 +287,7 @@ describe('Identify', () => {
|
||||
})
|
||||
])
|
||||
|
||||
expect(remoteIdentify.peerStore.addressBook.consumePeerRecord.callCount).to.equal(2)
|
||||
expect(remoteIdentify.peerStore.addressBook.consumePeerRecord.callCount).to.equal(1)
|
||||
expect(remoteIdentify.peerStore.protoBook.set.callCount).to.equal(1)
|
||||
|
||||
const addresses = localIdentify.peerStore.addressBook.get(localPeer)
|
||||
@@ -276,38 +302,35 @@ describe('Identify', () => {
|
||||
|
||||
// LEGACY
|
||||
it('should be able to push identify updates to another peer with no certified peer records support', async () => {
|
||||
const storedProtocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0']
|
||||
const connectionManager = new EventEmitter()
|
||||
connectionManager.getConnection = () => { }
|
||||
|
||||
const localPeerStore = new PeerStore({ peerId: localPeer })
|
||||
localPeerStore.protoBook.set(localPeer, storedProtocols)
|
||||
|
||||
const localIdentify = new IdentifyService({
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: localPeerStore,
|
||||
peerStore: new PeerStore({ peerId: localPeer }),
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
}
|
||||
_options: { host: {} }
|
||||
},
|
||||
protocols: new Map([
|
||||
[multicodecs.IDENTIFY],
|
||||
[multicodecs.IDENTIFY_PUSH],
|
||||
['/echo/1.0.0']
|
||||
])
|
||||
})
|
||||
|
||||
const remotePeerStore = new PeerStore({ peerId: remotePeer })
|
||||
remotePeerStore.protoBook.set(remotePeer, storedProtocols)
|
||||
|
||||
const remoteIdentify = new IdentifyService({
|
||||
libp2p: {
|
||||
peerId: remotePeer,
|
||||
connectionManager,
|
||||
peerStore: new PeerStore({ peerId: remotePeer }),
|
||||
multiaddrs: [],
|
||||
isStarted: () => true
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
|
||||
// Setup peer protocols and multiaddrs
|
||||
const localProtocols = new Set(storedProtocols)
|
||||
const localProtocols = new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0'])
|
||||
const localConnectionMock = { newStream: () => {} }
|
||||
const remoteConnectionMock = { remotePeer: localPeer }
|
||||
|
||||
@@ -376,8 +399,8 @@ describe('Identify', () => {
|
||||
expect(connection).to.exist()
|
||||
|
||||
// Wait for peer store to be updated
|
||||
// Dialer._createDialTarget (add), Identify (consume)
|
||||
await pWaitFor(() => peerStoreSpyConsumeRecord.callCount === 1 && peerStoreSpyAdd.callCount === 1)
|
||||
// Dialer._createDialTarget (add), Identify (consume), Create self (consume)
|
||||
await pWaitFor(() => peerStoreSpyConsumeRecord.callCount === 2 && peerStoreSpyAdd.callCount === 1)
|
||||
expect(libp2p.identifyService.identify.callCount).to.equal(1)
|
||||
|
||||
// The connection should have no open streams
|
||||
@@ -398,6 +421,8 @@ describe('Identify', () => {
|
||||
|
||||
const connection = await libp2p.dialer.connectToPeer(remoteAddr)
|
||||
expect(connection).to.exist()
|
||||
// Wait for nextTick to trigger the identify call
|
||||
await delay(1)
|
||||
|
||||
// Wait for identify to finish
|
||||
await libp2p.identifyService.identify.firstCall.returnValue
|
||||
@@ -420,38 +445,22 @@ describe('Identify', () => {
|
||||
await pWaitFor(() => connection.streams.length === 0)
|
||||
})
|
||||
|
||||
it('should push multiaddr updates to an already connected peer', async () => {
|
||||
it('should store host data and protocol version into metadataBook', () => {
|
||||
const agentVersion = 'js-project/1.0.0'
|
||||
|
||||
libp2p = new Libp2p({
|
||||
...baseOptions,
|
||||
peerId
|
||||
peerId,
|
||||
host: {
|
||||
agentVersion
|
||||
}
|
||||
})
|
||||
|
||||
await libp2p.start()
|
||||
const storedAgentVersion = libp2p.peerStore.metadataBook.getValue(localPeer, 'AgentVersion')
|
||||
const storedProtocolVersion = libp2p.peerStore.metadataBook.getValue(localPeer, 'ProtocolVersion')
|
||||
|
||||
sinon.spy(libp2p.identifyService, 'identify')
|
||||
sinon.spy(libp2p.identifyService, 'push')
|
||||
|
||||
const connection = await libp2p.dialer.connectToPeer(remoteAddr)
|
||||
expect(connection).to.exist()
|
||||
|
||||
// Wait for identify to finish
|
||||
await libp2p.identifyService.identify.firstCall.returnValue
|
||||
sinon.stub(libp2p, 'isStarted').returns(true)
|
||||
|
||||
libp2p.peerStore.addressBook.add(libp2p.peerId, [multiaddr('/ip4/180.0.0.1/tcp/15001/ws')])
|
||||
|
||||
// Verify the remote peer is notified of change
|
||||
expect(libp2p.identifyService.push.callCount).to.equal(1)
|
||||
for (const call of libp2p.identifyService.push.getCalls()) {
|
||||
const [connections] = call.args
|
||||
expect(connections.length).to.equal(1)
|
||||
expect(connections[0].remotePeer.toB58String()).to.equal(remoteAddr.getPeerId())
|
||||
const results = await call.returnValue
|
||||
expect(results.length).to.equal(1)
|
||||
}
|
||||
|
||||
// Verify the streams close
|
||||
await pWaitFor(() => connection.streams.length === 0)
|
||||
expect(agentVersion).to.eql(unit8ArrayToString(storedAgentVersion))
|
||||
expect(storedProtocolVersion).to.exist()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -5,9 +5,7 @@ const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
|
||||
const sinon = require('sinon')
|
||||
const pDefer = require('p-defer')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
|
||||
const PeerStore = require('../../src/peer-store')
|
||||
|
||||
@@ -226,96 +224,6 @@ describe('protoBook', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('protoBook.remove', () => {
|
||||
let peerStore, pb
|
||||
|
||||
beforeEach(() => {
|
||||
peerStore = new PeerStore({ peerId })
|
||||
pb = peerStore.protoBook
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
peerStore.removeAllListeners()
|
||||
})
|
||||
|
||||
it('throws invalid parameters error if invalid PeerId is provided', () => {
|
||||
expect(() => {
|
||||
pb.remove('invalid peerId')
|
||||
}).to.throw(ERR_INVALID_PARAMETERS)
|
||||
})
|
||||
|
||||
it('throws invalid parameters error if no protocols provided', () => {
|
||||
expect(() => {
|
||||
pb.remove(peerId)
|
||||
}).to.throw(ERR_INVALID_PARAMETERS)
|
||||
})
|
||||
|
||||
it('removes the given protocol and emits change event', async () => {
|
||||
const spy = sinon.spy()
|
||||
|
||||
const supportedProtocols = ['protocol1', 'protocol2']
|
||||
const removedProtocols = ['protocol1']
|
||||
const finalProtocols = supportedProtocols.filter(p => !removedProtocols.includes(p))
|
||||
|
||||
peerStore.on('change:protocols', spy)
|
||||
|
||||
// Replace
|
||||
pb.set(peerId, supportedProtocols)
|
||||
let protocols = pb.get(peerId)
|
||||
expect(protocols).to.have.deep.members(supportedProtocols)
|
||||
|
||||
// Remove
|
||||
pb.remove(peerId, removedProtocols)
|
||||
protocols = pb.get(peerId)
|
||||
expect(protocols).to.have.deep.members(finalProtocols)
|
||||
|
||||
await pWaitFor(() => spy.callCount === 2)
|
||||
|
||||
const [firstCallArgs] = spy.firstCall.args
|
||||
const [secondCallArgs] = spy.secondCall.args
|
||||
expect(arraysAreEqual(firstCallArgs.protocols, supportedProtocols))
|
||||
expect(arraysAreEqual(secondCallArgs.protocols, finalProtocols))
|
||||
})
|
||||
|
||||
it('emits on remove if the content changes', () => {
|
||||
const spy = sinon.spy()
|
||||
|
||||
const supportedProtocols = ['protocol1', 'protocol2']
|
||||
const removedProtocols = ['protocol2']
|
||||
const finalProtocols = supportedProtocols.filter(p => !removedProtocols.includes(p))
|
||||
|
||||
peerStore.on('change:protocols', spy)
|
||||
|
||||
// set
|
||||
pb.set(peerId, supportedProtocols)
|
||||
|
||||
// remove (content already existing)
|
||||
pb.remove(peerId, removedProtocols)
|
||||
const protocols = pb.get(peerId)
|
||||
expect(protocols).to.have.deep.members(finalProtocols)
|
||||
|
||||
return pWaitFor(() => spy.callCount === 2)
|
||||
})
|
||||
|
||||
it('does not emit on remove if the content does not change', () => {
|
||||
const spy = sinon.spy()
|
||||
|
||||
const supportedProtocols = ['protocol1', 'protocol2']
|
||||
const removedProtocols = ['protocol3']
|
||||
|
||||
peerStore.on('change:protocols', spy)
|
||||
|
||||
// set
|
||||
pb.set(peerId, supportedProtocols)
|
||||
|
||||
// remove
|
||||
pb.remove(peerId, removedProtocols)
|
||||
|
||||
// Only one event
|
||||
expect(spy.callCount).to.eql(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('protoBook.get', () => {
|
||||
let peerStore, pb
|
||||
|
||||
|
@@ -1,585 +0,0 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const { expect } = chai
|
||||
|
||||
const delay = require('delay')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
const sinon = require('sinon')
|
||||
const nock = require('nock')
|
||||
|
||||
const ipfsHttpClient = require('ipfs-http-client')
|
||||
const DelegatedContentRouter = require('libp2p-delegated-content-routing')
|
||||
const multiaddr = require('multiaddr')
|
||||
const Libp2p = require('../../src')
|
||||
const { relay: relayMulticodec } = require('../../src/circuit/multicodec')
|
||||
|
||||
const { createPeerId } = require('../utils/creators/peer')
|
||||
const baseOptions = require('../utils/base-options')
|
||||
|
||||
const listenAddr = '/ip4/0.0.0.0/tcp/0'
|
||||
|
||||
describe('auto-relay', () => {
|
||||
describe('basics', () => {
|
||||
let libp2p
|
||||
let relayLibp2p
|
||||
let autoRelay
|
||||
|
||||
beforeEach(async () => {
|
||||
const peerIds = await createPeerId({ number: 2 })
|
||||
// Create 2 nodes, and turn HOP on for the relay
|
||||
;[libp2p, relayLibp2p] = peerIds.map((peerId, index) => {
|
||||
const opts = {
|
||||
...baseOptions,
|
||||
config: {
|
||||
...baseOptions.config,
|
||||
relay: {
|
||||
hop: {
|
||||
enabled: index !== 0
|
||||
},
|
||||
autoRelay: {
|
||||
enabled: true,
|
||||
maxListeners: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Libp2p({
|
||||
...opts,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
connectionManager: {
|
||||
autoDial: false
|
||||
},
|
||||
peerDiscovery: {
|
||||
autoDial: false
|
||||
},
|
||||
peerId
|
||||
})
|
||||
})
|
||||
|
||||
autoRelay = libp2p.relay._autoRelay
|
||||
|
||||
expect(autoRelay.maxListeners).to.eql(1)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
// Start each node
|
||||
return Promise.all([libp2p, relayLibp2p].map(libp2p => libp2p.start()))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// Stop each node
|
||||
return Promise.all([libp2p, relayLibp2p].map(libp2p => libp2p.stop()))
|
||||
})
|
||||
|
||||
it('should ask if node supports hop on protocol change (relay protocol) and add to listen multiaddrs', async () => {
|
||||
// Spy if a connected peer is being added as listen relay
|
||||
sinon.spy(autoRelay, '_addListenRelay')
|
||||
|
||||
const originalMultiaddrsLength = relayLibp2p.multiaddrs.length
|
||||
|
||||
// Discover relay
|
||||
libp2p.peerStore.addressBook.add(relayLibp2p.peerId, relayLibp2p.multiaddrs)
|
||||
await libp2p.dial(relayLibp2p.peerId)
|
||||
|
||||
// Wait for peer added as listen relay
|
||||
await pWaitFor(() => autoRelay._addListenRelay.callCount === 1)
|
||||
expect(autoRelay._listenRelays.size).to.equal(1)
|
||||
|
||||
// Wait for listen multiaddr update
|
||||
await pWaitFor(() => libp2p.multiaddrs.length === originalMultiaddrsLength + 1)
|
||||
expect(libp2p.multiaddrs[originalMultiaddrsLength].getPeerId()).to.eql(relayLibp2p.peerId.toB58String())
|
||||
|
||||
// Peer has relay multicodec
|
||||
const knownProtocols = libp2p.peerStore.protoBook.get(relayLibp2p.peerId)
|
||||
expect(knownProtocols).to.include(relayMulticodec)
|
||||
})
|
||||
})
|
||||
|
||||
describe('flows with 1 listener max', () => {
|
||||
let libp2p
|
||||
let relayLibp2p1
|
||||
let relayLibp2p2
|
||||
let relayLibp2p3
|
||||
let autoRelay1
|
||||
|
||||
beforeEach(async () => {
|
||||
const peerIds = await createPeerId({ number: 4 })
|
||||
// Create 4 nodes, and turn HOP on for the relay
|
||||
;[libp2p, relayLibp2p1, relayLibp2p2, relayLibp2p3] = peerIds.map((peerId, index) => {
|
||||
let opts = baseOptions
|
||||
|
||||
if (index !== 0) {
|
||||
opts = {
|
||||
...baseOptions,
|
||||
config: {
|
||||
...baseOptions.config,
|
||||
relay: {
|
||||
hop: {
|
||||
enabled: true
|
||||
},
|
||||
autoRelay: {
|
||||
enabled: true,
|
||||
maxListeners: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Libp2p({
|
||||
...opts,
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
connectionManager: {
|
||||
autoDial: false
|
||||
},
|
||||
peerDiscovery: {
|
||||
autoDial: false
|
||||
},
|
||||
peerId
|
||||
})
|
||||
})
|
||||
|
||||
autoRelay1 = relayLibp2p1.relay._autoRelay
|
||||
|
||||
expect(autoRelay1.maxListeners).to.eql(1)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
// Start each node
|
||||
return Promise.all([libp2p, relayLibp2p1, relayLibp2p2, relayLibp2p3].map(libp2p => libp2p.start()))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// Stop each node
|
||||
return Promise.all([libp2p, relayLibp2p1, relayLibp2p2, relayLibp2p3].map(libp2p => libp2p.stop()))
|
||||
})
|
||||
|
||||
it('should ask if node supports hop on protocol change (relay protocol) and add to listen multiaddrs', async () => {
|
||||
// Spy if a connected peer is being added as listen relay
|
||||
sinon.spy(autoRelay1, '_addListenRelay')
|
||||
|
||||
// Discover relay
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
|
||||
|
||||
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
|
||||
const originalMultiaddrs2Length = relayLibp2p2.multiaddrs.length
|
||||
|
||||
await relayLibp2p1.dial(relayLibp2p2.peerId)
|
||||
|
||||
// Wait for peer added as listen relay
|
||||
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 1)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
|
||||
// Wait for listen multiaddr update
|
||||
await Promise.all([
|
||||
pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1),
|
||||
pWaitFor(() => relayLibp2p2.multiaddrs.length === originalMultiaddrs2Length + 1)
|
||||
])
|
||||
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
|
||||
|
||||
// Peer has relay multicodec
|
||||
const knownProtocols = relayLibp2p1.peerStore.protoBook.get(relayLibp2p2.peerId)
|
||||
expect(knownProtocols).to.include(relayMulticodec)
|
||||
})
|
||||
|
||||
it('should be able to dial a peer from its relayed address previously added', async () => {
|
||||
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
|
||||
const originalMultiaddrs2Length = relayLibp2p2.multiaddrs.length
|
||||
|
||||
// Discover relay
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
|
||||
|
||||
await relayLibp2p1.dial(relayLibp2p2.peerId)
|
||||
|
||||
// Wait for listen multiaddr update
|
||||
await Promise.all([
|
||||
pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1),
|
||||
pWaitFor(() => relayLibp2p2.multiaddrs.length === originalMultiaddrs2Length + 1)
|
||||
])
|
||||
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
|
||||
|
||||
// Dial from the other through a relay
|
||||
const relayedMultiaddr2 = multiaddr(`${relayLibp2p1.multiaddrs[0]}/p2p/${relayLibp2p1.peerId.toB58String()}/p2p-circuit`)
|
||||
libp2p.peerStore.addressBook.add(relayLibp2p2.peerId, [relayedMultiaddr2])
|
||||
|
||||
await libp2p.dial(relayLibp2p2.peerId)
|
||||
})
|
||||
|
||||
it('should only add maxListeners relayed addresses', async () => {
|
||||
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
|
||||
const originalMultiaddrs2Length = relayLibp2p2.multiaddrs.length
|
||||
|
||||
// Spy if a connected peer is being added as listen relay
|
||||
sinon.spy(autoRelay1, '_addListenRelay')
|
||||
sinon.spy(autoRelay1._listenRelays, 'add')
|
||||
|
||||
// Discover one relay and connect
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
|
||||
await relayLibp2p1.dial(relayLibp2p2.peerId)
|
||||
|
||||
expect(relayLibp2p1.connectionManager.size).to.eql(1)
|
||||
|
||||
// Wait for peer added as listen relay
|
||||
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 1 && autoRelay1._listenRelays.add.callCount === 1)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
|
||||
// Wait for listen multiaddr update
|
||||
await Promise.all([
|
||||
pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1),
|
||||
pWaitFor(() => relayLibp2p2.multiaddrs.length === originalMultiaddrs2Length + 1)
|
||||
])
|
||||
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
|
||||
|
||||
// Relay2 has relay multicodec
|
||||
const knownProtocols2 = relayLibp2p1.peerStore.protoBook.get(relayLibp2p2.peerId)
|
||||
expect(knownProtocols2).to.include(relayMulticodec)
|
||||
|
||||
// Discover an extra relay and connect
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
|
||||
await relayLibp2p1.dial(relayLibp2p3.peerId)
|
||||
|
||||
// Wait to guarantee the dialed peer is not added as a listen relay
|
||||
await delay(300)
|
||||
|
||||
expect(autoRelay1._addListenRelay.callCount).to.equal(2)
|
||||
expect(autoRelay1._listenRelays.add.callCount).to.equal(1)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
expect(relayLibp2p1.connectionManager.size).to.eql(2)
|
||||
|
||||
// Relay2 has relay multicodec
|
||||
const knownProtocols3 = relayLibp2p1.peerStore.protoBook.get(relayLibp2p3.peerId)
|
||||
expect(knownProtocols3).to.include(relayMulticodec)
|
||||
})
|
||||
|
||||
it('should not listen on a relayed address if peer disconnects', async () => {
|
||||
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
|
||||
|
||||
// Spy if identify push is fired on adding/removing listen addr
|
||||
sinon.spy(relayLibp2p1.identifyService, 'pushToPeerStore')
|
||||
|
||||
// Discover one relay and connect
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
|
||||
await relayLibp2p1.dial(relayLibp2p2.peerId)
|
||||
|
||||
// Wait for listenning on the relay
|
||||
await pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
|
||||
|
||||
// Identify push for adding listen relay multiaddr
|
||||
expect(relayLibp2p1.identifyService.pushToPeerStore.callCount).to.equal(1)
|
||||
|
||||
// Disconnect from peer used for relay
|
||||
await relayLibp2p1.hangUp(relayLibp2p2.peerId)
|
||||
|
||||
// Wait for removed listening on the relay
|
||||
await pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(0)
|
||||
|
||||
// Identify push for removing listen relay multiaddr
|
||||
expect(relayLibp2p1.identifyService.pushToPeerStore.callCount).to.equal(2)
|
||||
})
|
||||
|
||||
it('should try to listen on other connected peers relayed address if one used relay disconnects', async () => {
|
||||
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
|
||||
|
||||
// Spy if a connected peer is being added as listen relay
|
||||
sinon.spy(autoRelay1, '_addListenRelay')
|
||||
sinon.spy(relayLibp2p1.transportManager, 'listen')
|
||||
|
||||
// Discover one relay and connect
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
|
||||
await relayLibp2p1.dial(relayLibp2p2.peerId)
|
||||
|
||||
// Discover an extra relay and connect
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
|
||||
await relayLibp2p1.dial(relayLibp2p3.peerId)
|
||||
|
||||
// Wait for both peer to be attempted to added as listen relay
|
||||
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 1)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
expect(relayLibp2p1.connectionManager.size).to.equal(2)
|
||||
|
||||
// Wait for listen multiaddr update
|
||||
await pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1)
|
||||
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
|
||||
|
||||
// Only one will be used for listeninng
|
||||
expect(relayLibp2p1.transportManager.listen.callCount).to.equal(1)
|
||||
|
||||
// Spy if relay from listen map was removed
|
||||
sinon.spy(autoRelay1._listenRelays, 'delete')
|
||||
|
||||
// Disconnect from peer used for relay
|
||||
await relayLibp2p1.hangUp(relayLibp2p2.peerId)
|
||||
expect(autoRelay1._listenRelays.delete.callCount).to.equal(1)
|
||||
expect(autoRelay1._addListenRelay.callCount).to.equal(1)
|
||||
|
||||
// Wait for other peer connected to be added as listen addr
|
||||
await pWaitFor(() => relayLibp2p1.transportManager.listen.callCount === 2)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
expect(relayLibp2p1.connectionManager.size).to.eql(1)
|
||||
|
||||
// Wait for listen multiaddr update
|
||||
await pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1)
|
||||
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p3.peerId.toB58String())
|
||||
})
|
||||
|
||||
it('should try to listen on stored peers relayed address if one used relay disconnects and there are not enough connected', async () => {
|
||||
// Spy if a connected peer is being added as listen relay
|
||||
sinon.spy(autoRelay1, '_addListenRelay')
|
||||
sinon.spy(relayLibp2p1.transportManager, 'listen')
|
||||
|
||||
// Discover one relay and connect
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
|
||||
await relayLibp2p1.dial(relayLibp2p2.peerId)
|
||||
|
||||
// Discover an extra relay and connect to gather its Hop support
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
|
||||
await relayLibp2p1.dial(relayLibp2p3.peerId)
|
||||
|
||||
// Wait for both peer to be attempted to added as listen relay
|
||||
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 2)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
expect(relayLibp2p1.connectionManager.size).to.equal(2)
|
||||
|
||||
// Only one will be used for listeninng
|
||||
expect(relayLibp2p1.transportManager.listen.callCount).to.equal(1)
|
||||
|
||||
// Disconnect not used listen relay
|
||||
await relayLibp2p1.hangUp(relayLibp2p3.peerId)
|
||||
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
expect(relayLibp2p1.connectionManager.size).to.equal(1)
|
||||
|
||||
// Spy on dial
|
||||
sinon.spy(relayLibp2p1, 'dial')
|
||||
|
||||
// Remove peer used as relay from peerStore and disconnect it
|
||||
relayLibp2p1.peerStore.delete(relayLibp2p2.peerId)
|
||||
await relayLibp2p1.hangUp(relayLibp2p2.peerId)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(0)
|
||||
expect(relayLibp2p1.connectionManager.size).to.equal(0)
|
||||
|
||||
// Wait for other peer connected to be added as listen addr
|
||||
await pWaitFor(() => relayLibp2p1.transportManager.listen.callCount === 2)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
expect(relayLibp2p1.connectionManager.size).to.eql(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('flows with 2 max listeners', () => {
|
||||
let relayLibp2p1
|
||||
let relayLibp2p2
|
||||
let relayLibp2p3
|
||||
let autoRelay1
|
||||
let autoRelay2
|
||||
|
||||
beforeEach(async () => {
|
||||
const peerIds = await createPeerId({ number: 3 })
|
||||
// Create 3 nodes, and turn HOP on for the relay
|
||||
;[relayLibp2p1, relayLibp2p2, relayLibp2p3] = peerIds.map((peerId) => {
|
||||
return new Libp2p({
|
||||
...baseOptions,
|
||||
config: {
|
||||
...baseOptions.config,
|
||||
relay: {
|
||||
...baseOptions.config.relay,
|
||||
hop: {
|
||||
enabled: true
|
||||
},
|
||||
autoRelay: {
|
||||
enabled: true,
|
||||
maxListeners: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
connectionManager: {
|
||||
autoDial: false
|
||||
},
|
||||
peerDiscovery: {
|
||||
autoDial: false
|
||||
},
|
||||
peerId
|
||||
})
|
||||
})
|
||||
|
||||
autoRelay1 = relayLibp2p1.relay._autoRelay
|
||||
autoRelay2 = relayLibp2p2.relay._autoRelay
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
// Start each node
|
||||
return Promise.all([relayLibp2p1, relayLibp2p2, relayLibp2p3].map(libp2p => libp2p.start()))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// Stop each node
|
||||
return Promise.all([relayLibp2p1, relayLibp2p2, relayLibp2p3].map(libp2p => libp2p.stop()))
|
||||
})
|
||||
|
||||
it('should not add listener to a already relayed connection', async () => {
|
||||
// Spy if a connected peer is being added as listen relay
|
||||
sinon.spy(autoRelay1, '_addListenRelay')
|
||||
sinon.spy(autoRelay2, '_addListenRelay')
|
||||
|
||||
// Relay 1 discovers Relay 3 and connect
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
|
||||
await relayLibp2p1.dial(relayLibp2p3.peerId)
|
||||
|
||||
// Wait for peer added as listen relay
|
||||
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 1)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
|
||||
// Relay 2 discovers Relay 3 and connect
|
||||
relayLibp2p2.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
|
||||
await relayLibp2p2.dial(relayLibp2p3.peerId)
|
||||
|
||||
// Wait for peer added as listen relay
|
||||
await pWaitFor(() => autoRelay2._addListenRelay.callCount === 1)
|
||||
expect(autoRelay2._listenRelays.size).to.equal(1)
|
||||
|
||||
// Relay 1 discovers Relay 2 relayed multiaddr via Relay 3
|
||||
const ma2RelayedBy3 = relayLibp2p2.multiaddrs[relayLibp2p2.multiaddrs.length - 1]
|
||||
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, [ma2RelayedBy3])
|
||||
await relayLibp2p1.dial(relayLibp2p2.peerId)
|
||||
|
||||
// Peer not added as listen relay
|
||||
expect(autoRelay1._addListenRelay.callCount).to.equal(1)
|
||||
expect(autoRelay1._listenRelays.size).to.equal(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('discovery', () => {
|
||||
let local
|
||||
let remote
|
||||
let relayLibp2p
|
||||
|
||||
beforeEach(async () => {
|
||||
const peerIds = await createPeerId({ number: 3 })
|
||||
|
||||
// Create 2 nodes, and turn HOP on for the relay
|
||||
;[local, remote, relayLibp2p] = peerIds.map((peerId, index) => {
|
||||
const delegate = new DelegatedContentRouter(peerId, ipfsHttpClient({
|
||||
host: '0.0.0.0',
|
||||
protocol: 'http',
|
||||
port: 60197
|
||||
}), [
|
||||
multiaddr('/ip4/0.0.0.0/tcp/60197')
|
||||
])
|
||||
|
||||
const opts = {
|
||||
...baseOptions,
|
||||
config: {
|
||||
...baseOptions.config,
|
||||
relay: {
|
||||
advertise: {
|
||||
bootDelay: 1000,
|
||||
ttl: 1000,
|
||||
enabled: true
|
||||
},
|
||||
hop: {
|
||||
enabled: index === 2
|
||||
},
|
||||
autoRelay: {
|
||||
enabled: true,
|
||||
maxListeners: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Libp2p({
|
||||
...opts,
|
||||
modules: {
|
||||
...opts.modules,
|
||||
contentRouting: [delegate]
|
||||
},
|
||||
addresses: {
|
||||
listen: [listenAddr]
|
||||
},
|
||||
connectionManager: {
|
||||
autoDial: false
|
||||
},
|
||||
peerDiscovery: {
|
||||
autoDial: false
|
||||
},
|
||||
peerId
|
||||
})
|
||||
})
|
||||
|
||||
sinon.spy(relayLibp2p.contentRouting, 'provide')
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
nock('http://0.0.0.0:60197')
|
||||
// mock the refs call
|
||||
.post('/api/v0/refs')
|
||||
.query(true)
|
||||
.reply(200, null, [
|
||||
'Content-Type', 'application/json',
|
||||
'X-Chunked-Output', '1'
|
||||
])
|
||||
|
||||
// Start each node
|
||||
await Promise.all([local, remote, relayLibp2p].map(libp2p => libp2p.start()))
|
||||
|
||||
// Should provide on start
|
||||
await pWaitFor(() => relayLibp2p.contentRouting.provide.callCount === 1)
|
||||
|
||||
const provider = relayLibp2p.peerId.toB58String()
|
||||
const multiaddrs = relayLibp2p.multiaddrs.map((m) => m.toString())
|
||||
|
||||
// Mock findProviders
|
||||
nock('http://0.0.0.0:60197')
|
||||
.post('/api/v0/dht/findprovs')
|
||||
.query(true)
|
||||
.reply(200, `{"Extra":"","ID":"${provider}","Responses":[{"Addrs":${JSON.stringify(multiaddrs)},"ID":"${provider}"}],"Type":4}\n`, [
|
||||
'Content-Type', 'application/json',
|
||||
'X-Chunked-Output', '1'
|
||||
])
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// Stop each node
|
||||
return Promise.all([local, remote, relayLibp2p].map(libp2p => libp2p.stop()))
|
||||
})
|
||||
|
||||
it('should find providers for relay and add it as listen relay', async () => {
|
||||
const originalMultiaddrsLength = local.multiaddrs.length
|
||||
|
||||
// Spy add listen relay
|
||||
sinon.spy(local.relay._autoRelay, '_addListenRelay')
|
||||
// Spy Find Providers
|
||||
sinon.spy(local.contentRouting, 'findProviders')
|
||||
|
||||
// Try to listen on Available hop relays
|
||||
await local.relay._autoRelay._listenOnAvailableHopRelays()
|
||||
|
||||
// Should try to find relay service providers
|
||||
await pWaitFor(() => local.contentRouting.findProviders.callCount === 1)
|
||||
// Wait for peer added as listen relay
|
||||
await pWaitFor(() => local.relay._autoRelay._addListenRelay.callCount === 1)
|
||||
expect(local.relay._autoRelay._listenRelays.size).to.equal(1)
|
||||
await pWaitFor(() => local.multiaddrs.length === originalMultiaddrsLength + 1)
|
||||
|
||||
const relayedAddr = local.multiaddrs[local.multiaddrs.length - 1]
|
||||
remote.peerStore.addressBook.set(local.peerId, [relayedAddr])
|
||||
|
||||
// Dial from remote through the relayed address
|
||||
const conn = await remote.dial(local.peerId)
|
||||
expect(conn).to.exist()
|
||||
})
|
||||
})
|
||||
})
|
@@ -7,13 +7,10 @@ const { expect } = chai
|
||||
|
||||
const AddressManager = require('../../src/address-manager')
|
||||
const TransportManager = require('../../src/transport-manager')
|
||||
const PeerStore = require('../../src/peer-store')
|
||||
const PeerRecord = require('../../src/record/peer-record')
|
||||
const Transport = require('libp2p-tcp')
|
||||
const PeerId = require('peer-id')
|
||||
const multiaddr = require('multiaddr')
|
||||
const mockUpgrader = require('../utils/mockUpgrader')
|
||||
const Peers = require('../fixtures/peers')
|
||||
const sinon = require('sinon')
|
||||
const addrs = [
|
||||
multiaddr('/ip4/127.0.0.1/tcp/0'),
|
||||
multiaddr('/ip4/127.0.0.1/tcp/0')
|
||||
@@ -21,19 +18,11 @@ const addrs = [
|
||||
|
||||
describe('Transport Manager (TCP)', () => {
|
||||
let tm
|
||||
let localPeer
|
||||
|
||||
before(async () => {
|
||||
localPeer = await PeerId.createFromJSON(Peers[0])
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
before(() => {
|
||||
tm = new TransportManager({
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
multiaddrs: addrs,
|
||||
addressManager: new AddressManager({ listen: addrs }),
|
||||
peerStore: new PeerStore({ peerId: localPeer })
|
||||
addressManager: new AddressManager({ listen: addrs })
|
||||
},
|
||||
upgrader: mockUpgrader,
|
||||
onConnection: () => {}
|
||||
@@ -52,39 +41,22 @@ describe('Transport Manager (TCP)', () => {
|
||||
})
|
||||
|
||||
it('should be able to listen', async () => {
|
||||
tm.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
await tm.listen(addrs)
|
||||
tm.add(Transport.prototype[Symbol.toStringTag], Transport, { listenerOptions: { listen: 'carefully' } })
|
||||
const transport = tm._transports.get(Transport.prototype[Symbol.toStringTag])
|
||||
const spyListener = sinon.spy(transport, 'createListener')
|
||||
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
|
||||
expect(tm.getAddrs().length).to.equal(addrs.length)
|
||||
await tm.close()
|
||||
expect(tm._listeners.get(Transport.prototype[Symbol.toStringTag])).to.have.length(0)
|
||||
})
|
||||
|
||||
it('should create self signed peer record on listen', async () => {
|
||||
let signedPeerRecord = await tm.libp2p.peerStore.addressBook.getPeerRecord(localPeer)
|
||||
expect(signedPeerRecord).to.not.exist()
|
||||
|
||||
tm.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
await tm.listen(addrs)
|
||||
|
||||
// Should created Self Peer record on new listen address
|
||||
signedPeerRecord = await tm.libp2p.peerStore.addressBook.getPeerRecord(localPeer)
|
||||
expect(signedPeerRecord).to.exist()
|
||||
|
||||
const record = PeerRecord.createFromProtobuf(signedPeerRecord.payload)
|
||||
expect(record).to.exist()
|
||||
expect(record.multiaddrs.length).to.equal(addrs.length)
|
||||
addrs.forEach((a, i) => {
|
||||
expect(record.multiaddrs[i].equals(a)).to.be.true()
|
||||
})
|
||||
expect(spyListener.firstCall.firstArg).to.deep.equal({ listen: 'carefully' })
|
||||
})
|
||||
|
||||
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()
|
||||
|
@@ -87,7 +87,7 @@ describe('Transport Manager (WebSockets)', () => {
|
||||
it('should fail to listen with no valid address', async () => {
|
||||
tm.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
|
||||
await expect(tm.listen([listenAddr]))
|
||||
await expect(tm.listen())
|
||||
.to.eventually.be.rejected()
|
||||
.and.to.have.property('code', ErrorCodes.ERR_NO_VALID_ADDRESSES)
|
||||
})
|
||||
@@ -125,7 +125,10 @@ describe('libp2p.transportManager', () => {
|
||||
const spy = sinon.spy()
|
||||
const key = spy.prototype[Symbol.toStringTag] = 'TransportSpy'
|
||||
const customOptions = {
|
||||
another: 'value'
|
||||
another: 'value',
|
||||
listenerOptions: {
|
||||
listen: 'carefully'
|
||||
}
|
||||
}
|
||||
libp2p = new Libp2p({
|
||||
peerId,
|
||||
@@ -143,6 +146,7 @@ describe('libp2p.transportManager', () => {
|
||||
expect(libp2p.transportManager).to.exist()
|
||||
// Our transport and circuit relay
|
||||
expect(libp2p.transportManager._transports.size).to.equal(2)
|
||||
expect(libp2p.transportManager._listenerOptions.size).to.equal(2)
|
||||
expect(spy).to.have.property('callCount', 1)
|
||||
expect(spy.getCall(0)).to.have.deep.property('args', [{
|
||||
...customOptions,
|
||||
|
Reference in New Issue
Block a user