mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-07-24 21:11:58 +00:00
Compare commits
52 Commits
chore/add-
...
v0.30.0
Author | SHA1 | Date | |
---|---|---|---|
|
d19401aa4c | ||
|
24bb8df521 | ||
|
58d4f9a915 | ||
|
239413e331 | ||
|
01d43a7b60 | ||
|
37d66fd88c | ||
|
21e8ced81a | ||
|
9ae1b758e9 | ||
|
408868655c | ||
|
c5f61ac05f | ||
|
5d0ac529e4 | ||
|
bc05083207 | ||
|
169bb806a7 | ||
|
7809e6444e | ||
|
f7e1426b9e | ||
|
7d76ba1367 | ||
|
b538ebdc0a | ||
|
baedf3fe5a | ||
|
4ebcdb085c | ||
|
4448de8432 | ||
|
585ad52b4c | ||
|
e50c6abcf2 | ||
|
49fffda23c | ||
|
689c35ed1c | ||
|
1a13e2c6ca | ||
|
5758db8ea9 | ||
|
ef9d3ca2c6 | ||
|
97e3633f47 | ||
|
e36b67a212 | ||
|
e977039c8a | ||
|
a5337c1797 | ||
|
ee23fb9508 | ||
|
11a46ea71e | ||
|
5c72424e57 | ||
|
0bf0b7cf89 | ||
|
55020056ee | ||
|
bb83cacb5a | ||
|
447d0ed0dd | ||
|
43eda43f06 | ||
|
7b93ece7f2 | ||
|
74bdfd1024 | ||
|
4d1fcdb3d2 | ||
|
caf66ea143 | ||
|
48656712ea | ||
|
1a5ae74741 | ||
|
8691465a52 | ||
|
6350a187c7 | ||
|
8e3bb09279 | ||
|
73204958ee | ||
|
e9e4b731a5 | ||
|
d0a9fada32 | ||
|
824a444f56 |
86
CHANGELOG.md
86
CHANGELOG.md
@@ -1,3 +1,89 @@
|
||||
# [0.30.0](https://github.com/libp2p/js-libp2p/compare/v0.29.4...v0.30.0) (2020-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove test/dialing/utils extra file ([689c35e](https://github.com/libp2p/js-libp2p/commit/689c35ed1c68e514293a9895d496e2e8440454e9))
|
||||
* types from ipfs integration ([#832](https://github.com/libp2p/js-libp2p/issues/832)) ([9ae1b75](https://github.com/libp2p/js-libp2p/commit/9ae1b758e99e3fc9067e26b4eae4c15ccb1ba303))
|
||||
|
||||
|
||||
### chore
|
||||
|
||||
* update pubsub ([#801](https://github.com/libp2p/js-libp2p/issues/801)) ([e50c6ab](https://github.com/libp2p/js-libp2p/commit/e50c6abcf2ebc80ebf2dfadd015ab21a20cffadc))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* auto relay ([#723](https://github.com/libp2p/js-libp2p/issues/723)) ([caf66ea](https://github.com/libp2p/js-libp2p/commit/caf66ea1439f6b75a0c321a16bd5c5d7d6a2bd47))
|
||||
* auto relay network query for new relays ([0bf0b7c](https://github.com/libp2p/js-libp2p/commit/0bf0b7cf8968d55002ac4c559ffb59985feeb092))
|
||||
* custom announce filter ([ef9d3ca](https://github.com/libp2p/js-libp2p/commit/ef9d3ca2c6f35d692d6079e74088c5146d46eebe))
|
||||
* custom dialer addr sorter ([#792](https://github.com/libp2p/js-libp2p/issues/792)) ([585ad52](https://github.com/libp2p/js-libp2p/commit/585ad52b4c71dd7514e99a287e0318b2b837ec48))
|
||||
* discover and connect to closest peers ([#798](https://github.com/libp2p/js-libp2p/issues/798)) ([baedf3f](https://github.com/libp2p/js-libp2p/commit/baedf3fe5ab946e938db1415d1662452cdfc0cc1))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* pubsub signing policy properties were changed according to libp2p-interfaces changes to a single property. The emitSelf option default value was also modified to match the routers value
|
||||
|
||||
|
||||
|
||||
# [0.30.0-rc.2](https://github.com/libp2p/js-libp2p/compare/v0.30.0-rc.1...v0.30.0-rc.2) (2020-12-15)
|
||||
|
||||
|
||||
|
||||
# [0.30.0-rc.1](https://github.com/libp2p/js-libp2p/compare/v0.30.0-rc.0...v0.30.0-rc.1) (2020-12-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* types from ipfs integration ([#832](https://github.com/libp2p/js-libp2p/issues/832)) ([216eb97](https://github.com/libp2p/js-libp2p/commit/216eb9730ef473f73a974c3dbaf306ecdc815c8b))
|
||||
|
||||
|
||||
|
||||
# [0.30.0-rc.0](https://github.com/libp2p/js-libp2p/compare/v0.29.4...v0.30.0-rc.0) (2020-12-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove test/dialing/utils extra file ([3f1dc20](https://github.com/libp2p/js-libp2p/commit/3f1dc20caf1c80078f403deb9174cd06d08567ab))
|
||||
|
||||
|
||||
### chore
|
||||
|
||||
* update pubsub ([#801](https://github.com/libp2p/js-libp2p/issues/801)) ([9205fce](https://github.com/libp2p/js-libp2p/commit/9205fce34d0cd8dd5d32988be34c110fc0a5b6e2))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* auto relay ([#723](https://github.com/libp2p/js-libp2p/issues/723)) ([65ec267](https://github.com/libp2p/js-libp2p/commit/65ec267e7f4826caacd042213c3fbacce589ab5b))
|
||||
* auto relay network query for new relays ([9faf1bf](https://github.com/libp2p/js-libp2p/commit/9faf1bfcf61581acc715b9be78b71dc14501835a))
|
||||
* custom announce filter ([48476d5](https://github.com/libp2p/js-libp2p/commit/48476d504a98b7b51b3e2dc64eab93670fde0c7b))
|
||||
* custom dialer addr sorter ([#792](https://github.com/libp2p/js-libp2p/issues/792)) ([91b15b6](https://github.com/libp2p/js-libp2p/commit/91b15b6790952b4db11264961d9c6f2a96d1fe43))
|
||||
* discover and connect to closest peers ([#798](https://github.com/libp2p/js-libp2p/issues/798)) ([b73106e](https://github.com/libp2p/js-libp2p/commit/b73106eba2d559621f427f7aa788e9b0ef47d135))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* pubsub signing policy properties were changed according to libp2p-interfaces changes to a single property. The emitSelf option default value was also modified to match the routers value
|
||||
|
||||
|
||||
|
||||
<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)
|
||||
|
||||
|
@@ -147,7 +147,6 @@ List of packages currently in existence for libp2p
|
||||
| [`libp2p-websockets`](//github.com/libp2p/js-libp2p-websockets) | [](//github.com/libp2p/js-libp2p-websockets/releases) | [](https://david-dm.org/libp2p/js-libp2p-websockets) | [](https://travis-ci.com/libp2p/js-libp2p-websockets) | [](https://codecov.io/gh/libp2p/js-libp2p-websockets) | [Jacob Heun](mailto:jacobheun@gmail.com) |
|
||||
| **secure channels** |
|
||||
| [`libp2p-noise`](//github.com/NodeFactoryIo/js-libp2p-noise) | [](//github.com/NodeFactoryIo/js-libp2p-noise/releases) | [](https://david-dm.org/NodeFactoryIo/js-libp2p-noise) | [](https://travis-ci.com/NodeFactoryIo/js-libp2p-noise) | [](https://codecov.io/gh/NodeFactoryIo/js-libp2p-noise) | N/A |
|
||||
| [`libp2p-secio`](//github.com/libp2p/js-libp2p-secio) | [](//github.com/libp2p/js-libp2p-secio/releases) | [](https://david-dm.org/libp2p/js-libp2p-secio) | [](https://travis-ci.com/libp2p/js-libp2p-secio) | [](https://codecov.io/gh/libp2p/js-libp2p-secio) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
|
||||
| **stream multiplexers** |
|
||||
| [`libp2p-mplex`](//github.com/libp2p/js-libp2p-mplex) | [](//github.com/libp2p/js-libp2p-mplex/releases) | [](https://david-dm.org/libp2p/js-libp2p-mplex) | [](https://travis-ci.com/libp2p/js-libp2p-mplex) | [](https://codecov.io/gh/libp2p/js-libp2p-mplex) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
|
||||
| **peer discovery** |
|
||||
|
50
doc/API.md
50
doc/API.md
@@ -93,6 +93,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>, announceFilter: (ma: Array<multiaddr>) => Array<multiaddr> }` | 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) |
|
||||
@@ -115,12 +116,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.
|
||||
@@ -132,12 +146,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:
|
||||
@@ -647,7 +679,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
|
||||
|
@@ -445,13 +445,13 @@ const node = await Libp2p.create({
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [SECIO]
|
||||
connEncryption: [NOISE]
|
||||
},
|
||||
config: {
|
||||
relay: { // Circuit Relay options (this config is part of libp2p core configurations)
|
||||
@@ -694,6 +694,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:
|
||||
|
3
doc/production/DELEGATE_NODES.md
Normal file
3
doc/production/DELEGATE_NODES.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Delegate Nodes
|
||||
|
||||
[TODO](https://github.com/libp2p/js-libp2p/pull/718)
|
65
doc/production/README.md
Normal file
65
doc/production/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Production
|
||||
|
||||
Nowadays, you can run JavaScript code in several different environments, some of them with their own particularities. Moreover, you can use `js-libp2p` for a wide range of use cases. Different environments and different use cases mean different configurations and challenges in the network.
|
||||
|
||||
Libp2p nodes can vary from nodes behind an application, to infrastructure nodes that enable the network to operate and to be efficient. In this context, the Libp2p project provides public infrastructure to boost the network, enable nodes connectivity and improve constrained nodes performance. This public infrastructure should be leveraged for learning the concepts and experimenting. When an application on top of libp2p aims to move into production, its own infrastructure should be setup as the public nodes will be intensively used by others and its availability is not guaranteed.
|
||||
|
||||
This guide aims to guide you from using the public infrastructure into setting up your own.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Joining the Network](#joining-the-network)
|
||||
* [Connecting to Nodes with connectivity limitations](#connecting-to-nodes-with-connectivity-limitations)
|
||||
* [`webrtc-star` servers](#webrtc-star-servers)
|
||||
* [Circuit Relay](#circuit-relay)
|
||||
* [Querying the network from the browser](#querying-the-network-from-the-browser)
|
||||
* [Others](#others)
|
||||
* [SSL](#ssl)
|
||||
|
||||
## Joining the Network
|
||||
|
||||
Once a libp2p node stars, it will need to connect to a set of peers in order to establish its overlay network.
|
||||
|
||||
Currently `js-libp2p` is not the best choice for being a bootstrap node. Its DHT needs to be improved, in order to become an effective server to enable other nodes to properly bootstrap their network.
|
||||
|
||||
Setting up a fleet of [`go-libp2p`](https://github.com/libp2p/go-libp2p) nodes is the recommended way to proceed here.
|
||||
|
||||
## Connecting to Nodes with connectivity limitations
|
||||
|
||||
While the libp2p core codebase aims to work in multiple environments, there are some limitations that are not possible to overcome at the time of writing. These limitations include browser nodes, nodes behind NAT, reverse proxies, firewalls, or lack of compatible transports.
|
||||
|
||||
In the browser, libp2p supports two transports: `websockets` and `webrtc-star`. Nowadays, browsers do not support listening for connections, but only to dial known addresses. `webrtc-star` servers can be used to enable libp2p nodes to discover other nodes running on the browser and to help them establish a connection.
|
||||
|
||||
For nodes that cannot be dialed (including browser), circuit relay nodes should be used.
|
||||
|
||||
### `webrtc-star` servers
|
||||
|
||||
Regarding `webRTC` connections, a set of star servers are needed to act as a rendezvous point, where peers can learn about other peers (`peer-discovery`), as well as exchange their SDP offers (signaling data).
|
||||
|
||||
You can read on how to setup your own star servers in [libp2p/js-libp2p-webrtc-star/DEPLOYMENT.md](https://github.com/libp2p/js-libp2p-webrtc-star/blob/master/DEPLOYMENT.md).
|
||||
|
||||
It is worth pointing out that with new discovery protocols on the way, as well as support for distributed signaling, the star servers should be deprecated on the long run.
|
||||
|
||||
### Circuit Relay
|
||||
|
||||
Libp2p nodes acting as circuit relay aim to establish connectivity between libp2p nodes (e.g. IPFS nodes) that wouldn't otherwise be able to establish a direct connection to each other.
|
||||
|
||||
A relay is needed in situations where nodes are behind NAT, reverse proxies, firewalls and/or simply don't support the same transports (e.g. go-libp2p vs. browser-libp2p). The circuit relay protocol exists to overcome those scenarios. Nodes with the `auto-relay` feature enabled can automatically bind themselves on a relay to listen for connections on their behalf.
|
||||
|
||||
You can use [libp2p/js-libp2p-relay-server](https://github.com/libp2p/js-libp2p-relay-server) to setup your own relay server. This also includes an easy to customize Docker setup for a HOP Relay.
|
||||
|
||||
## Querying the network from the browser
|
||||
|
||||
Libp2p nodes in scenarios such as browser environment and constrained devices will not be an efficient node in the libp2p DHT overlay, as a consequence of their known limitations regarding connectivity and performance.
|
||||
|
||||
Aiming to support these type of nodes to find other peers and content in the network, delegate nodes can be setup. With a set of well known IPFS delegate nodes, nodes with limitations in the network can leverage them to perform peer and content routing queries.
|
||||
|
||||
Currently, delegate nodes must be IPFS nodes as the IPFS HTTP API is leveraged by them to make routing queries.
|
||||
|
||||
You can read on how to setup your own set of delegated nodes in [DELEGATE_NODES.md](./DELEGATE_NODES.md).
|
||||
|
||||
## Others
|
||||
|
||||
### SSL
|
||||
|
||||
TODO
|
@@ -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']
|
||||
},
|
||||
|
@@ -43,6 +43,7 @@ const createNode = async () => {
|
||||
})
|
||||
await node1.pubsub.subscribe(topic)
|
||||
|
||||
// Will not receive own published messages by default
|
||||
node2.pubsub.on(topic, (msg) => {
|
||||
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`)
|
||||
})
|
||||
|
@@ -44,7 +44,6 @@ const node2 = nodes[1]
|
||||
|
||||
// Add node's 2 data to the PeerStore
|
||||
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
||||
|
||||
await node1.dial(node2.peerId)
|
||||
|
||||
node1.pubsub.on(topic, (msg) => {
|
||||
@@ -52,6 +51,7 @@ node1.pubsub.on(topic, (msg) => {
|
||||
})
|
||||
await node1.pubsub.subscribe(topic)
|
||||
|
||||
// Will not receive own published messages by default
|
||||
node2.pubsub.on(topic, (msg) => {
|
||||
console.log(`node2 received: ${uint8ArrayToString(msg.data)}`)
|
||||
})
|
||||
@@ -68,25 +68,34 @@ The output of the program should look like:
|
||||
```
|
||||
> node 1.js
|
||||
connected to QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82
|
||||
node2 received: Bird bird bird, bird is the word!
|
||||
node1 received: Bird bird bird, bird is the word!
|
||||
node2 received: Bird bird bird, bird is the word!
|
||||
node1 received: Bird bird bird, bird is the word!
|
||||
```
|
||||
|
||||
You can change the pubsub `emitSelf` option if you don't want the publishing node to receive its own messages.
|
||||
You can change the pubsub `emitSelf` option if you want the publishing node to receive its own messages.
|
||||
|
||||
```JavaScript
|
||||
const defaults = {
|
||||
config: {
|
||||
pubsub: {
|
||||
enabled: true,
|
||||
emitSelf: false
|
||||
emitSelf: true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The output of the program should look like:
|
||||
|
||||
```
|
||||
> node 1.js
|
||||
connected to QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82
|
||||
node1 received: Bird bird bird, bird is the word!
|
||||
node2 received: Bird bird bird, bird is the word!
|
||||
node1 received: Bird bird bird, bird is the word!
|
||||
node2 received: Bird bird bird, bird is the word!
|
||||
```
|
||||
|
||||
## 2. Future work
|
||||
|
||||
libp2p/IPFS PubSub is enabling a whole set of Distributed Real Time applications using CRDT (Conflict-Free Replicated Data Types). It is still going through heavy research (and hacking) and we invite you to join the conversation at [research-CRDT](https://github.com/ipfs/research-CRDT). Here is a list of some of the exciting examples:
|
||||
|
@@ -44,6 +44,7 @@ const createNode = async () => {
|
||||
|
||||
//subscribe
|
||||
node1.pubsub.on(topic, (msg) => {
|
||||
// Will not receive own published messages by default
|
||||
console.log(`node1 received: ${uint8ArrayToString(msg.data)}`)
|
||||
})
|
||||
await node1.pubsub.subscribe(topic)
|
||||
|
@@ -97,15 +97,12 @@ Result
|
||||
```
|
||||
> node 1.js
|
||||
############## fruit banana ##############
|
||||
node1 received: banana
|
||||
node2 received: banana
|
||||
node3 received: banana
|
||||
############## fruit apple ##############
|
||||
node1 received: apple
|
||||
node2 received: apple
|
||||
node3 received: apple
|
||||
############## fruit car ##############
|
||||
node1 received: car
|
||||
############## fruit orange ##############
|
||||
node1 received: orange
|
||||
node2 received: orange
|
||||
|
@@ -23,7 +23,6 @@
|
||||
|
||||
"secure channels",
|
||||
["NodeFactoryIo/js-libp2p-noise", "libp2p-noise"],
|
||||
["libp2p/js-libp2p-secio", "libp2p-secio"],
|
||||
|
||||
"stream multiplexers",
|
||||
["libp2p/js-libp2p-mplex", "libp2p-mplex"],
|
||||
|
17
package.json
17
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "libp2p",
|
||||
"version": "0.29.3",
|
||||
"version": "0.30.0",
|
||||
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
|
||||
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
|
||||
"main": "src/index.js",
|
||||
@@ -70,7 +70,7 @@
|
||||
"it-pipe": "^1.1.0",
|
||||
"it-protocol-buffers": "^0.2.0",
|
||||
"libp2p-crypto": "^0.18.0",
|
||||
"libp2p-interfaces": "libp2p/js-libp2p-interfaces#feat/add-types-with-post-install",
|
||||
"libp2p-interfaces": "^0.8.1",
|
||||
"libp2p-utils": "^0.2.2",
|
||||
"mafmt": "^8.0.0",
|
||||
"merge-options": "^2.0.0",
|
||||
@@ -145,6 +145,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>",
|
||||
@@ -152,8 +153,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>",
|
||||
@@ -163,9 +162,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>",
|
||||
@@ -174,9 +175,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>"
|
||||
]
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ const mergeOptions = require('merge-options')
|
||||
const { dnsaddrResolver } = require('multiaddr/src/resolvers')
|
||||
|
||||
const Constants = require('./constants')
|
||||
const { AGENT_VERSION } = require('./identify/consts')
|
||||
const RelayConstants = require('./circuit/constants')
|
||||
|
||||
const { publicAddressesFirst } = require('libp2p-utils/src/address-sort')
|
||||
@@ -30,6 +31,9 @@ const DefaultConfig = {
|
||||
},
|
||||
addressSorter: publicAddressesFirst
|
||||
},
|
||||
host: {
|
||||
agentVersion: AGENT_VERSION
|
||||
},
|
||||
metrics: {
|
||||
enabled: false
|
||||
},
|
||||
|
@@ -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',
|
||||
|
@@ -48,6 +48,15 @@ class IdentifyService {
|
||||
|
||||
this.handleMessage = this.handleMessage.bind(this)
|
||||
|
||||
// Store self host metadata
|
||||
this._host = {
|
||||
agentVersion: AGENT_VERSION,
|
||||
protocolVersion: PROTOCOL_VERSION,
|
||||
...libp2p._options.host
|
||||
}
|
||||
|
||||
this.peerStore.metadataBook.set(this.peerId, 'AgentVersion', uint8ArrayFromString(this._host.agentVersion))
|
||||
this.peerStore.metadataBook.set(this.peerId, 'ProtocolVersion', uint8ArrayFromString(this._host.protocolVersion))
|
||||
// When a new connection happens, trigger identify
|
||||
this.connectionManager.on('peer:connect', (connection) => {
|
||||
this.identify(connection).catch(log.error)
|
||||
@@ -236,8 +245,8 @@ class IdentifyService {
|
||||
const protocols = this.peerStore.protoBook.get(this.peerId) || []
|
||||
|
||||
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,
|
||||
|
49
src/index.js
49
src/index.js
@@ -80,11 +80,32 @@ const IDENTIFY_PROTOCOLS = IdentifyService.multicodecs
|
||||
* @property {Libp2pConfig} [config]
|
||||
* @property {PeerId} peerId
|
||||
*
|
||||
* @typedef {Object} CreateOptions
|
||||
* @property {PeerId} peerId
|
||||
*
|
||||
* @extends {EventEmitter}
|
||||
* @fires Libp2p#error Emitted when an error occurs
|
||||
* @fires Libp2p#peer:discovery Emitted when a peer is discovered
|
||||
*/
|
||||
class Libp2p extends EventEmitter {
|
||||
/**
|
||||
* Like `new Libp2p(options)` except it will create a `PeerId`
|
||||
* instance if one is not provided in options.
|
||||
*
|
||||
* @param {Libp2pOptions & CreateOptions} options - Libp2p configuration options
|
||||
* @returns {Promise<Libp2p>}
|
||||
*/
|
||||
static async create (options) {
|
||||
if (options.peerId) {
|
||||
return new Libp2p(options)
|
||||
}
|
||||
|
||||
const peerId = await PeerId.create()
|
||||
|
||||
options.peerId = peerId
|
||||
return new Libp2p(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Libp2p node.
|
||||
*
|
||||
@@ -401,6 +422,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) {
|
||||
@@ -656,27 +682,4 @@ class Libp2p extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CreateOptions
|
||||
* @property {PeerId} peerId
|
||||
*/
|
||||
|
||||
/**
|
||||
* Like `new Libp2p(options)` except it will create a `PeerId`
|
||||
* instance if one is not provided in options.
|
||||
*
|
||||
* @param {Libp2pOptions & CreateOptions} options - Libp2p configuration options
|
||||
* @returns {Promise<Libp2p>}
|
||||
*/
|
||||
Libp2p.create = async function create (options) {
|
||||
if (options.peerId) {
|
||||
return new Libp2p(options)
|
||||
}
|
||||
|
||||
const peerId = await PeerId.create()
|
||||
|
||||
options.peerId = peerId
|
||||
return new Libp2p(options)
|
||||
}
|
||||
|
||||
module.exports = Libp2p
|
||||
|
@@ -1,4 +1,3 @@
|
||||
// @ts-nocheck
|
||||
'use strict'
|
||||
|
||||
require('node-forge/lib/pkcs7')
|
||||
@@ -9,6 +8,8 @@ const errcode = require('err-code')
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
const privates = new WeakMap()
|
||||
|
||||
/**
|
||||
* Cryptographic Message Syntax (aka PKCS #7)
|
||||
*
|
||||
@@ -23,13 +24,15 @@ class CMS {
|
||||
* Creates a new instance with a keychain
|
||||
*
|
||||
* @param {import('./index')} keychain - the available keys
|
||||
* @param {string} dek
|
||||
*/
|
||||
constructor (keychain) {
|
||||
constructor (keychain, dek) {
|
||||
if (!keychain) {
|
||||
throw errcode(new Error('keychain is required'), 'ERR_KEYCHAIN_REQUIRED')
|
||||
}
|
||||
|
||||
this.keychain = keychain
|
||||
privates.set(this, { dek })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,7 +51,9 @@ class CMS {
|
||||
|
||||
const key = await this.keychain.findKeyByName(name)
|
||||
const pem = await this.keychain._getPrivateKey(name)
|
||||
const privateKey = forge.pki.decryptRsaPrivateKey(pem, this.keychain._())
|
||||
/** @type {string} */
|
||||
const dek = privates.get(this).dek
|
||||
const privateKey = forge.pki.decryptRsaPrivateKey(pem, dek)
|
||||
const certificate = await certificateForKey(key, privateKey)
|
||||
|
||||
// create a p7 enveloped message
|
||||
@@ -115,8 +120,14 @@ class CMS {
|
||||
}
|
||||
|
||||
const key = await this.keychain.findKeyById(r.keyId)
|
||||
|
||||
if (!key) {
|
||||
throw errcode(new Error('No key available to decrypto'), 'ERR_NO_KEY')
|
||||
}
|
||||
|
||||
const pem = await this.keychain._getPrivateKey(key.name)
|
||||
const privateKey = forge.pki.decryptRsaPrivateKey(pem, this.keychain._())
|
||||
const dek = privates.get(this).dek
|
||||
const privateKey = forge.pki.decryptRsaPrivateKey(pem, dek)
|
||||
cms.decrypt(r.recipient, privateKey)
|
||||
return uint8ArrayFromString(cms.content.getBytes(), 'ascii')
|
||||
}
|
||||
|
@@ -1,11 +1,10 @@
|
||||
// @ts-nocheck
|
||||
/* eslint max-nested-callbacks: ["error", 5] */
|
||||
'use strict'
|
||||
|
||||
const sanitize = require('sanitize-filename')
|
||||
const mergeOptions = require('merge-options')
|
||||
const crypto = require('libp2p-crypto')
|
||||
const DS = require('interface-datastore')
|
||||
const Datastore = require('interface-datastore')
|
||||
const CMS = require('./cms')
|
||||
const errcode = require('err-code')
|
||||
const { Number } = require('ipfs-utils/src/globalthis')
|
||||
@@ -14,8 +13,14 @@ const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
require('node-forge/lib/sha512')
|
||||
|
||||
/**
|
||||
* @typedef {import('peer-id')} PeerId
|
||||
* @typedef {import('interface-datastore/src/key')} Key
|
||||
*/
|
||||
|
||||
const keyPrefix = '/pkcs8/'
|
||||
const infoPrefix = '/info/'
|
||||
const privates = new WeakMap()
|
||||
|
||||
// NIST SP 800-132
|
||||
const NIST = {
|
||||
@@ -46,7 +51,8 @@ function validateKeyName (name) {
|
||||
* This assumes than an error indicates that the keychain is under attack. Delay returning an
|
||||
* error to make brute force attacks harder.
|
||||
*
|
||||
* @param {string | Error} err - The error
|
||||
* @param {string|Error} err - The error
|
||||
* @returns {Promise<never>}
|
||||
* @private
|
||||
*/
|
||||
async function throwDelayed (err) {
|
||||
@@ -62,29 +68,28 @@ async function throwDelayed (err) {
|
||||
* Converts a key name into a datastore name.
|
||||
*
|
||||
* @param {string} name
|
||||
* @returns {DS.Key}
|
||||
* @returns {Key}
|
||||
* @private
|
||||
*/
|
||||
function DsName (name) {
|
||||
return new DS.Key(keyPrefix + name)
|
||||
return new Datastore.Key(keyPrefix + name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a key name into a datastore info name.
|
||||
*
|
||||
* @param {string} name
|
||||
* @returns {DS.Key}
|
||||
* @returns {Key}
|
||||
* @private
|
||||
*/
|
||||
function DsInfoName (name) {
|
||||
return new DS.Key(infoPrefix + name)
|
||||
return new Datastore.Key(infoPrefix + name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about a key.
|
||||
*
|
||||
* @typedef {Object} KeyInfo
|
||||
*
|
||||
* @property {string} id - The universally unique key id.
|
||||
* @property {string} name - The local key name.
|
||||
*/
|
||||
@@ -101,7 +106,7 @@ class Keychain {
|
||||
/**
|
||||
* Creates a new instance of a key chain.
|
||||
*
|
||||
* @param {DS} store - where the key are.
|
||||
* @param {Datastore} store - where the key are.
|
||||
* @param {object} options
|
||||
* @class
|
||||
*/
|
||||
@@ -134,7 +139,7 @@ class Keychain {
|
||||
this.opts.dek.keyLength,
|
||||
this.opts.dek.hash) : ''
|
||||
|
||||
Object.defineProperty(this, '_', { value: () => dek })
|
||||
privates.set(this, { dek })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,13 +153,13 @@ class Keychain {
|
||||
* @returns {CMS}
|
||||
*/
|
||||
get cms () {
|
||||
return new CMS(this)
|
||||
return new CMS(this, privates.get(this).dek)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the options for a keychain. A random salt is produced.
|
||||
*
|
||||
* @returns {object}
|
||||
* @returns {Object}
|
||||
*/
|
||||
static generateOptions () {
|
||||
const options = Object.assign({}, defaultOptions)
|
||||
@@ -167,7 +172,7 @@ class Keychain {
|
||||
* Gets an object that can encrypt/decrypt protected data.
|
||||
* The default options for a keychain.
|
||||
*
|
||||
* @returns {object}
|
||||
* @returns {Object}
|
||||
*/
|
||||
static get options () {
|
||||
return defaultOptions
|
||||
@@ -178,10 +183,10 @@ class Keychain {
|
||||
*
|
||||
* @param {string} name - The local key name; cannot already exist.
|
||||
* @param {string} type - One of the key types; 'rsa'.
|
||||
* @param {int} [size] - The key size in bits. Used for rsa keys only.
|
||||
* @returns {KeyInfo}
|
||||
* @param {number} [size = 2048] - The key size in bits. Used for rsa keys only.
|
||||
* @returns {Promise<KeyInfo>}
|
||||
*/
|
||||
async createKey (name, type, size) {
|
||||
async createKey (name, type, size = 2048) {
|
||||
const self = this
|
||||
|
||||
if (!validateKeyName(name) || name === 'self') {
|
||||
@@ -208,9 +213,12 @@ class Keychain {
|
||||
|
||||
let keyInfo
|
||||
try {
|
||||
// @ts-ignore Differences between several crypto return types need to be fixed in libp2p-crypto
|
||||
const keypair = await crypto.keys.generateKeyPair(type, size)
|
||||
const kid = await keypair.id()
|
||||
const pem = await keypair.export(this._())
|
||||
/** @type {string} */
|
||||
const dek = privates.get(this).dek
|
||||
const pem = await keypair.export(dek)
|
||||
keyInfo = {
|
||||
name: name,
|
||||
id: kid
|
||||
@@ -230,7 +238,7 @@ class Keychain {
|
||||
/**
|
||||
* List all the keys.
|
||||
*
|
||||
* @returns {KeyInfo[]}
|
||||
* @returns {Promise<KeyInfo[]>}
|
||||
*/
|
||||
async listKeys () {
|
||||
const self = this
|
||||
@@ -250,7 +258,7 @@ class Keychain {
|
||||
* Find a key by it's id.
|
||||
*
|
||||
* @param {string} id - The universally unique key identifier.
|
||||
* @returns {KeyInfo}
|
||||
* @returns {Promise<KeyInfo|undefined>}
|
||||
*/
|
||||
async findKeyById (id) {
|
||||
try {
|
||||
@@ -265,7 +273,7 @@ class Keychain {
|
||||
* Find a key by it's name.
|
||||
*
|
||||
* @param {string} name - The local key name.
|
||||
* @returns {KeyInfo}
|
||||
* @returns {Promise<KeyInfo>}
|
||||
*/
|
||||
async findKeyByName (name) {
|
||||
if (!validateKeyName(name)) {
|
||||
@@ -285,7 +293,7 @@ class Keychain {
|
||||
* Remove an existing key.
|
||||
*
|
||||
* @param {string} name - The local key name; must already exist.
|
||||
* @returns {KeyInfo}
|
||||
* @returns {Promise<KeyInfo>}
|
||||
*/
|
||||
async removeKey (name) {
|
||||
const self = this
|
||||
@@ -306,7 +314,7 @@ class Keychain {
|
||||
*
|
||||
* @param {string} oldName - The old local key name; must already exist.
|
||||
* @param {string} newName - The new local key name; must not already exist.
|
||||
* @returns {KeyInfo}
|
||||
* @returns {Promise<KeyInfo>}
|
||||
*/
|
||||
async renameKey (oldName, newName) {
|
||||
const self = this
|
||||
@@ -347,7 +355,7 @@ class Keychain {
|
||||
*
|
||||
* @param {string} name - The local key name; must already exist.
|
||||
* @param {string} password - The password
|
||||
* @returns {string}
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async exportKey (name, password) {
|
||||
if (!validateKeyName(name)) {
|
||||
@@ -361,7 +369,9 @@ class Keychain {
|
||||
try {
|
||||
const res = await this.store.get(dsname)
|
||||
const pem = uint8ArrayToString(res)
|
||||
const privateKey = await crypto.keys.import(pem, this._())
|
||||
/** @type {string} */
|
||||
const dek = privates.get(this).dek
|
||||
const privateKey = await crypto.keys.import(pem, dek)
|
||||
return privateKey.export(password)
|
||||
} catch (err) {
|
||||
return throwDelayed(err)
|
||||
@@ -374,7 +384,7 @@ class Keychain {
|
||||
* @param {string} name - The local key name; must not already exist.
|
||||
* @param {string} pem - The PEM encoded PKCS #8 string
|
||||
* @param {string} password - The password.
|
||||
* @returns {KeyInfo}
|
||||
* @returns {Promise<KeyInfo>}
|
||||
*/
|
||||
async importKey (name, pem, password) {
|
||||
const self = this
|
||||
@@ -398,7 +408,9 @@ class Keychain {
|
||||
let kid
|
||||
try {
|
||||
kid = await privateKey.id()
|
||||
pem = await privateKey.export(this._())
|
||||
/** @type {string} */
|
||||
const dek = privates.get(this).dek
|
||||
pem = await privateKey.export(dek)
|
||||
} catch (err) {
|
||||
return throwDelayed(err)
|
||||
}
|
||||
@@ -415,6 +427,13 @@ class Keychain {
|
||||
return keyInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a peer key
|
||||
*
|
||||
* @param {string} name - The local key name; must not already exist.
|
||||
* @param {PeerId} peer - The PEM encoded PKCS #8 string
|
||||
* @returns {Promise<KeyInfo>}
|
||||
*/
|
||||
async importPeer (name, peer) {
|
||||
const self = this
|
||||
if (!validateKeyName(name)) {
|
||||
@@ -431,7 +450,9 @@ class Keychain {
|
||||
|
||||
try {
|
||||
const kid = await privateKey.id()
|
||||
const pem = await privateKey.export(this._())
|
||||
/** @type {string} */
|
||||
const dek = privates.get(this).dek
|
||||
const pem = await privateKey.export(dek)
|
||||
const keyInfo = {
|
||||
name: name,
|
||||
id: kid
|
||||
@@ -450,8 +471,7 @@ class Keychain {
|
||||
* Gets the private key as PEM encoded PKCS #8 string.
|
||||
*
|
||||
* @param {string} name
|
||||
* @returns {string}
|
||||
* @private
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async _getPrivateKey (name) {
|
||||
if (!validateKeyName(name)) {
|
||||
|
@@ -73,7 +73,7 @@ class PeerRouting {
|
||||
/**
|
||||
* Iterates over all peer routers in series to find the given peer.
|
||||
*
|
||||
* @param {string} id - The id of the peer to find
|
||||
* @param {PeerId} id - The id of the peer to find
|
||||
* @param {object} [options]
|
||||
* @param {number} [options.timeout] - How long the query should run
|
||||
* @returns {Promise<{ id: PeerId, multiaddrs: Multiaddr[] }>}
|
||||
|
@@ -33,11 +33,11 @@ const Envelope = require('../record/envelope')
|
||||
*
|
||||
* @typedef {Object} Entry
|
||||
* @property {Address[]} addresses peer Addresses.
|
||||
* @property {CertifiedRecord} [record] certified peer record.
|
||||
* @property {CertifiedRecord} record certified peer record.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @extends {Book<Entry, Address[], Multiaddr[]>}
|
||||
* @extends {Book}
|
||||
*/
|
||||
class AddressBook extends Book {
|
||||
/**
|
||||
@@ -56,13 +56,12 @@ class AddressBook extends Book {
|
||||
peerStore,
|
||||
eventName: 'change:multiaddrs',
|
||||
eventProperty: 'multiaddrs',
|
||||
eventTransformer: (entry) => {
|
||||
if (!entry || !entry.addresses) {
|
||||
eventTransformer: (data) => {
|
||||
if (!data.addresses) {
|
||||
return []
|
||||
}
|
||||
return entry.addresses.map((address) => address.multiaddr)
|
||||
},
|
||||
getTransformer: (entry) => entry && entry.addresses ? [...entry.addresses] : undefined
|
||||
return data.addresses.map((address) => address.multiaddr)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -264,6 +263,23 @@ class AddressBook extends Book {
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the known data of a provided peer.
|
||||
*
|
||||
* @override
|
||||
* @param {PeerId} peerId
|
||||
* @returns {Address[]|undefined}
|
||||
*/
|
||||
get (peerId) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const entry = this.data.get(peerId.toB58String())
|
||||
|
||||
return entry && entry.addresses ? [...entry.addresses] : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms received multiaddrs into Address.
|
||||
*
|
||||
|
@@ -13,9 +13,6 @@ const passthrough = data => data
|
||||
* @typedef {import('./')} PeerStore
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template Data, GetData, EventData
|
||||
*/
|
||||
class Book {
|
||||
/**
|
||||
* The Book is the skeleton for the PeerStore books.
|
||||
@@ -25,20 +22,18 @@ class Book {
|
||||
* @param {PeerStore} properties.peerStore - PeerStore instance.
|
||||
* @param {string} properties.eventName - Name of the event to emit by the PeerStore.
|
||||
* @param {string} properties.eventProperty - Name of the property to emit by the PeerStore.
|
||||
* @param {(data: Data | undefined) => EventData | undefined} [properties.eventTransformer] - Transformer function of the provided data for being emitted.
|
||||
* @param {(data: Data | undefined) => GetData | undefined} [properties.getTransformer] - Transformer function of the provided data for being returned on get.
|
||||
* @param {(data: any) => any[]} [properties.eventTransformer] - Transformer function of the provided data for being emitted.
|
||||
*/
|
||||
constructor ({ peerStore, eventName, eventProperty, eventTransformer = passthrough, getTransformer = passthrough }) {
|
||||
constructor ({ peerStore, eventName, eventProperty, eventTransformer = passthrough }) {
|
||||
this._ps = peerStore
|
||||
this.eventName = eventName
|
||||
this.eventProperty = eventProperty
|
||||
this.eventTransformer = eventTransformer
|
||||
this.getTransformer = getTransformer
|
||||
|
||||
/**
|
||||
* Map known peers to their data.
|
||||
*
|
||||
* @type {Map<string, Data>}
|
||||
* @type {Map<string, any[]|any>}
|
||||
*/
|
||||
this.data = new Map()
|
||||
}
|
||||
@@ -47,7 +42,7 @@ class Book {
|
||||
* Set known data of a provided peer.
|
||||
*
|
||||
* @param {PeerId} peerId
|
||||
* @param {unknown} data
|
||||
* @param {any[]|any} data
|
||||
*/
|
||||
set (peerId, data) {
|
||||
throw errcode(new Error('set must be implemented by the subclass'), 'ERR_NOT_IMPLEMENTED')
|
||||
@@ -58,7 +53,7 @@ class Book {
|
||||
*
|
||||
* @protected
|
||||
* @param {PeerId} peerId - peerId of the data to store
|
||||
* @param {Data} data - data to store.
|
||||
* @param {any} data - data to store.
|
||||
* @param {Object} [options] - storing options.
|
||||
* @param {boolean} [options.emit = true] - emit the provided data.
|
||||
* @returns {void}
|
||||
@@ -78,7 +73,7 @@ class Book {
|
||||
*
|
||||
* @protected
|
||||
* @param {PeerId} peerId
|
||||
* @param {Data | undefined} [data]
|
||||
* @param {any} [data]
|
||||
*/
|
||||
_emit (peerId, data) {
|
||||
this._ps.emit(this.eventName, {
|
||||
@@ -92,7 +87,7 @@ class Book {
|
||||
* Returns `undefined` if there is no available data for the given peer.
|
||||
*
|
||||
* @param {PeerId} peerId
|
||||
* @returns {GetData | undefined}
|
||||
* @returns {any[]|any|undefined}
|
||||
*/
|
||||
get (peerId) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
@@ -101,7 +96,8 @@ class Book {
|
||||
|
||||
const rec = this.data.get(peerId.toB58String())
|
||||
|
||||
return this.getTransformer(rec)
|
||||
// @ts-ignore
|
||||
return rec ? [...rec] : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +115,7 @@ class Book {
|
||||
return false
|
||||
}
|
||||
|
||||
this._emit(peerId, undefined)
|
||||
this._emit(peerId, [])
|
||||
|
||||
return true
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ const {
|
||||
*/
|
||||
|
||||
/**
|
||||
* @extends {Book<PeerId, PublicKey, PublicKey>}
|
||||
* @extends {Book}
|
||||
*/
|
||||
class KeyBook extends Book {
|
||||
/**
|
||||
@@ -34,8 +34,7 @@ class KeyBook extends Book {
|
||||
peerStore,
|
||||
eventName: 'change:pubkey',
|
||||
eventProperty: 'pubkey',
|
||||
eventTransformer: (data) => data && data.pubKey,
|
||||
getTransformer: (data) => data && data.pubKey
|
||||
eventTransformer: (data) => data.pubKey
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -75,6 +74,23 @@ class KeyBook extends Book {
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Public key of the given PeerId, if stored.
|
||||
*
|
||||
* @override
|
||||
* @param {PeerId} peerId
|
||||
* @returns {PublicKey | undefined}
|
||||
*/
|
||||
get (peerId) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
const rec = this.data.get(peerId.toB58String())
|
||||
|
||||
return rec ? rec.pubKey : undefined
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = KeyBook
|
||||
|
@@ -10,23 +10,17 @@ const uint8ArrayEquals = require('uint8arrays/equals')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const Book = require('./book')
|
||||
|
||||
const {
|
||||
codes: { ERR_INVALID_PARAMETERS }
|
||||
} = require('../errors')
|
||||
|
||||
const eventName = 'change:metadata'
|
||||
const eventProperty = 'metadata'
|
||||
|
||||
/**
|
||||
* @typedef {import('./')} PeerStore
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Map<string, Uint8Array>} Metadata
|
||||
*/
|
||||
|
||||
/**
|
||||
* @extends {Book<Metadata, Metadata, string>}
|
||||
* @extends {Book}
|
||||
*
|
||||
* @fires MetadataBook#change:metadata
|
||||
*/
|
||||
@@ -45,14 +39,14 @@ class MetadataBook extends Book {
|
||||
*/
|
||||
super({
|
||||
peerStore,
|
||||
eventName,
|
||||
eventProperty
|
||||
eventName: 'change:metadata',
|
||||
eventProperty: 'metadata'
|
||||
})
|
||||
|
||||
/**
|
||||
* Map known peers to their known protocols.
|
||||
*
|
||||
* @type {Map<string, Metadata>}
|
||||
* @type {Map<string, Map<string, Uint8Array>>}
|
||||
*/
|
||||
this.data = new Map()
|
||||
}
|
||||
@@ -105,6 +99,20 @@ class MetadataBook extends Book {
|
||||
emit && this._emit(peerId, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the known data of a provided peer.
|
||||
*
|
||||
* @param {PeerId} peerId
|
||||
* @returns {Map<string, Uint8Array>|undefined}
|
||||
*/
|
||||
get (peerId) {
|
||||
if (!PeerId.isPeerId(peerId)) {
|
||||
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
|
||||
return this.data.get(peerId.toB58String())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific metadata value, if it exists
|
||||
*
|
||||
@@ -159,10 +167,7 @@ class MetadataBook extends Book {
|
||||
return false
|
||||
}
|
||||
|
||||
this._ps.emit(eventName, {
|
||||
peerId,
|
||||
[eventProperty]: key
|
||||
})
|
||||
this._emit(peerId, key)
|
||||
|
||||
return true
|
||||
}
|
||||
|
@@ -17,21 +17,8 @@ const {
|
||||
* @typedef {import('./')} PeerStore
|
||||
*/
|
||||
|
||||
// @extends {Book<Entry, Address, Multiaddr>}
|
||||
|
||||
/**
|
||||
* @param {Set<string> | undefined} set
|
||||
* @returns {string[] | undefined}
|
||||
*/
|
||||
const transformSetToArray = (set) => {
|
||||
if (!set) {
|
||||
return undefined
|
||||
}
|
||||
return Array.from(set)
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends {Book<Set<string>, string[], string[]>}
|
||||
* @extends {Book}
|
||||
*
|
||||
* @fires ProtoBook#change:protocols
|
||||
*/
|
||||
@@ -52,8 +39,7 @@ class ProtoBook extends Book {
|
||||
peerStore,
|
||||
eventName: 'change:protocols',
|
||||
eventProperty: 'protocols',
|
||||
eventTransformer: (data) => transformSetToArray(data) || [],
|
||||
getTransformer: (data) => transformSetToArray(data)
|
||||
eventTransformer: (data) => Array.from(data)
|
||||
})
|
||||
|
||||
/**
|
||||
|
@@ -3,9 +3,14 @@
|
||||
const multicodec = require('multicodec')
|
||||
|
||||
// The domain string used for peer records contained in a Envelope.
|
||||
module.exports.ENVELOPE_DOMAIN_PEER_RECORD = multicodec.getName(multicodec.LIBP2P_PEER_RECORD)
|
||||
const domain = multicodec.getName(multicodec.LIBP2P_PEER_RECORD) || 'libp2p-peer-record'
|
||||
|
||||
// The type hint used to identify peer records in a Envelope.
|
||||
// Defined in https://github.com/multiformats/multicodec/blob/master/table.csv
|
||||
// with name "libp2p-peer-record"
|
||||
module.exports.ENVELOPE_PAYLOAD_TYPE_PEER_RECORD = Uint8Array.from([3, 1])
|
||||
const payloadType = Uint8Array.from([3, 1])
|
||||
|
||||
module.exports = {
|
||||
ENVELOPE_DOMAIN_PEER_RECORD: domain,
|
||||
ENVELOPE_PAYLOAD_TYPE_PEER_RECORD: payloadType
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ class TransportManager {
|
||||
/** @type {Map<string, Transport>} */
|
||||
this._transports = new Map()
|
||||
this._listeners = new Map()
|
||||
this._listenerOptions = new Map()
|
||||
this.faultTolerance = faultTolerance
|
||||
}
|
||||
|
||||
@@ -63,6 +64,7 @@ class TransportManager {
|
||||
})
|
||||
|
||||
this._transports.set(key, transport)
|
||||
this._listenerOptions.set(key, transportOptions.listenerOptions || {})
|
||||
if (!this._listeners.has(key)) {
|
||||
this._listeners.set(key, [])
|
||||
}
|
||||
@@ -171,7 +173,7 @@ 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({})
|
||||
const listener = transport.createListener(this._listenerOptions.get(key))
|
||||
this._listeners.get(key).push(listener)
|
||||
|
||||
// Track listen/close events
|
||||
|
@@ -15,10 +15,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]
|
||||
},
|
||||
@@ -30,12 +36,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)
|
||||
@@ -54,6 +58,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']
|
||||
},
|
||||
@@ -86,9 +91,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']
|
||||
},
|
||||
@@ -97,6 +109,7 @@ describe('libp2p.connections', () => {
|
||||
})
|
||||
const [remoteLibp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
peerId: peerIds[1],
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/15004/ws']
|
||||
},
|
||||
|
@@ -487,5 +487,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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -55,7 +55,8 @@ describe('Identify', () => {
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: localPeerStore,
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
isStarted: () => true,
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
const remoteIdentify = new IdentifyService({
|
||||
@@ -64,7 +65,8 @@ describe('Identify', () => {
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: remotePeerStore,
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
isStarted: () => true,
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -110,7 +112,8 @@ describe('Identify', () => {
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: localPeerStore,
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
isStarted: () => true,
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -120,7 +123,8 @@ describe('Identify', () => {
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: remotePeerStore,
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
isStarted: () => true,
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -168,7 +172,8 @@ describe('Identify', () => {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: localPeerStore,
|
||||
multiaddrs: []
|
||||
multiaddrs: [],
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
const remoteIdentify = new IdentifyService({
|
||||
@@ -176,7 +181,8 @@ describe('Identify', () => {
|
||||
peerId: remotePeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: remotePeerStore,
|
||||
multiaddrs: []
|
||||
multiaddrs: [],
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -202,6 +208,36 @@ 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']
|
||||
@@ -217,7 +253,8 @@ describe('Identify', () => {
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: localPeerStore,
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
isStarted: () => true,
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -230,7 +267,8 @@ describe('Identify', () => {
|
||||
connectionManager,
|
||||
peerStore: remotePeerStore,
|
||||
multiaddrs: [],
|
||||
isStarted: () => true
|
||||
isStarted: () => true,
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -287,7 +325,8 @@ describe('Identify', () => {
|
||||
connectionManager: new EventEmitter(),
|
||||
peerStore: localPeerStore,
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true
|
||||
isStarted: () => true,
|
||||
_options: { host: {} }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -300,6 +339,7 @@ describe('Identify', () => {
|
||||
connectionManager,
|
||||
peerStore: new PeerStore({ peerId: remotePeer }),
|
||||
multiaddrs: [],
|
||||
_options: { host: {} },
|
||||
isStarted: () => true
|
||||
}
|
||||
})
|
||||
@@ -418,6 +458,24 @@ describe('Identify', () => {
|
||||
await pWaitFor(() => connection.streams.length === 0)
|
||||
})
|
||||
|
||||
it('should store host data and protocol version into metadataBook', () => {
|
||||
const agentVersion = 'js-project/1.0.0'
|
||||
|
||||
libp2p = new Libp2p({
|
||||
...baseOptions,
|
||||
peerId,
|
||||
host: {
|
||||
agentVersion
|
||||
}
|
||||
})
|
||||
|
||||
const storedAgentVersion = libp2p.peerStore.metadataBook.getValue(localPeer, 'AgentVersion')
|
||||
const storedProtocolVersion = libp2p.peerStore.metadataBook.getValue(localPeer, 'ProtocolVersion')
|
||||
|
||||
expect(agentVersion).to.eql(unit8ArrayToString(storedAgentVersion))
|
||||
expect(storedProtocolVersion).to.exist()
|
||||
})
|
||||
|
||||
it('should push multiaddr updates to an already connected peer', async () => {
|
||||
libp2p = new Libp2p({
|
||||
...baseOptions,
|
||||
|
@@ -11,6 +11,7 @@ const Transport = require('libp2p-tcp')
|
||||
const PeerId = require('peer-id')
|
||||
const multiaddr = require('multiaddr')
|
||||
const mockUpgrader = require('../utils/mockUpgrader')
|
||||
const sinon = require('sinon')
|
||||
const Peers = require('../fixtures/peers')
|
||||
const addrs = [
|
||||
multiaddr('/ip4/127.0.0.1/tcp/0'),
|
||||
@@ -50,7 +51,9 @@ describe('Transport Manager (TCP)', () => {
|
||||
})
|
||||
|
||||
it('should be able to listen', async () => {
|
||||
tm.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
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(addrs)
|
||||
expect(tm._listeners).to.have.key(Transport.prototype[Symbol.toStringTag])
|
||||
expect(tm._listeners.get(Transport.prototype[Symbol.toStringTag])).to.have.length(addrs.length)
|
||||
@@ -59,6 +62,7 @@ describe('Transport Manager (TCP)', () => {
|
||||
expect(tm.getAddrs().length).to.equal(addrs.length)
|
||||
await tm.close()
|
||||
expect(tm._listeners.get(Transport.prototype[Symbol.toStringTag])).to.have.length(0)
|
||||
expect(spyListener.firstCall.firstArg).to.deep.equal({ listen: 'carefully' })
|
||||
})
|
||||
|
||||
it('should create self signed peer record on listen', async () => {
|
||||
|
@@ -123,7 +123,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,
|
||||
@@ -141,6 +144,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