Compare commits

...

23 Commits

Author SHA1 Message Date
Jacob Heun
0e18735b8c chore: release version v0.28.10 2020-08-05 19:07:08 +02:00
Jacob Heun
f68ff35625 chore: update contributors 2020-08-05 19:07:07 +02:00
Alex Potsides
8c56ec0d23 fix: allow certain keychain operations without a password (#726)
* fix: allow certain keychain operations without a password

Listing, removing, renaming etc keys do not require a password so
the user should not be required to provide one.

This means we don't have to prompt the user to create a password
when they aren't going to do any operations that require a password.

* fix: make keychain pass optional

* fix: support libp2p creation without keychain pass

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-08-05 19:03:11 +02:00
Vasco Santos
fa5ee873e3 chore: update node and npm badges (#727) 2020-08-05 18:26:50 +02:00
Jacob Heun
51d7ca44c1 feat(keychain): add support for ed25519 and secp keys (#725)
* feat(keychain): add support for ed25519 and secp keys

* chore: bump crypto

* refactor: cleanup keychain usage
2020-08-05 18:19:10 +02:00
Jacob Heun
726a746479 fix(identify): make agentversion dynamic and add it to the peerstore (#724) 2020-08-04 18:39:05 +02:00
Felipe Martins
a331b84f13 docs(example): add pubssub message filter example
moved the filter folder

rename folder

docs(refactor): clean up pubsub validator example
2020-07-31 12:09:34 +02:00
Jacob Heun
78d152dd68 chore: release version v0.28.9 2020-07-27 11:58:53 +02:00
Jacob Heun
7e14aa19b5 chore: update contributors 2020-07-27 11:58:52 +02:00
Vasco Santos
2440c872df fix: ping multiaddr from peer not previously stored in peerstore (#719) 2020-07-27 10:53:23 +02:00
Jacob Heun
6c7e5e5eef chore: release version v0.28.8 2020-07-20 17:23:01 +02:00
Jacob Heun
388df6b6e6 chore: update contributors 2020-07-20 17:23:00 +02:00
Jacob Heun
7dbfe6ab1a chore: update libp2p-crypto (#717)
This includes a patch for ed25519 interop with Go
2020-07-20 17:18:09 +02:00
Vasco Santos
cea59a1fe4 docs: update addressBook get and getMultiaddrsForPeer return values (#716) 2020-07-20 16:17:39 +02:00
Vasco Santos
7da9ad44ab fix: create dial target for peer with no known addrs (#715) 2020-07-20 14:54:03 +02:00
Vasco Santos
3896941128 docs: use tag for webrtc-star discovery config (#713) 2020-07-20 14:51:24 +02:00
Vasco Santos
856b38de67 chore: add migration guide template (#711) 2020-07-16 18:14:02 +02:00
Jacob Heun
798d7b73c1 chore: release version v0.28.7 2020-07-14 19:13:55 +02:00
Jacob Heun
f2d0d8b51d chore: update contributors 2020-07-14 19:13:54 +02:00
Vasco Santos
999c1b7740 fix: retimer reschedule does not work as interval (#710)
* fix: retimer reschedule does not work as interval

* chore: apply suggestions from code review

Co-authored-by: Jacob Heun <jacobheun@gmail.com>

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-07-14 19:11:29 +02:00
Jacob Heun
99ff361a55 chore: release version v0.28.6 2020-07-14 16:13:00 +02:00
Jacob Heun
6115f8b680 chore: update contributors 2020-07-14 16:12:59 +02:00
Vasco Santos
9ccab40fc8 fix: not dial all known peers in parallel on startup (#698)
* fix: not dial all known peers on startup

* feat: connection manager should proactively connect to peers from peerStore

* chore: increase bundle size

* fix: do connMgr proactive dial on an interval

* chore: address review

* chore: use retimer reschedule

* chore: address review

* fix: use minConnections in default config

* chore: minPeers to minConnections everywhere
2020-07-14 16:05:26 +02:00
28 changed files with 758 additions and 101 deletions

View File

@@ -45,7 +45,7 @@ const after = async () => {
}
module.exports = {
bundlesize: { maxSize: '200kB' },
bundlesize: { maxSize: '202kB' },
hooks: {
pre: before,
post: after

View File

@@ -1,3 +1,59 @@
<a name="0.28.10"></a>
## [0.28.10](https://github.com/libp2p/js-libp2p/compare/v0.28.9...v0.28.10) (2020-08-05)
### Bug Fixes
* allow certain keychain operations without a password ([#726](https://github.com/libp2p/js-libp2p/issues/726)) ([8c56ec0](https://github.com/libp2p/js-libp2p/commit/8c56ec0))
* **identify:** make agentversion dynamic and add it to the peerstore ([#724](https://github.com/libp2p/js-libp2p/issues/724)) ([726a746](https://github.com/libp2p/js-libp2p/commit/726a746))
### Features
* **keychain:** add support for ed25519 and secp keys ([#725](https://github.com/libp2p/js-libp2p/issues/725)) ([51d7ca4](https://github.com/libp2p/js-libp2p/commit/51d7ca4))
<a name="0.28.9"></a>
## [0.28.9](https://github.com/libp2p/js-libp2p/compare/v0.28.8...v0.28.9) (2020-07-27)
### Bug Fixes
* ping multiaddr from peer not previously stored in peerstore ([#719](https://github.com/libp2p/js-libp2p/issues/719)) ([2440c87](https://github.com/libp2p/js-libp2p/commit/2440c87))
<a name="0.28.8"></a>
## [0.28.8](https://github.com/libp2p/js-libp2p/compare/v0.28.7...v0.28.8) (2020-07-20)
### Bug Fixes
* create dial target for peer with no known addrs ([#715](https://github.com/libp2p/js-libp2p/issues/715)) ([7da9ad4](https://github.com/libp2p/js-libp2p/commit/7da9ad4))
<a name="0.28.7"></a>
## [0.28.7](https://github.com/libp2p/js-libp2p/compare/v0.28.6...v0.28.7) (2020-07-14)
### Bug Fixes
* retimer reschedule does not work as interval ([#710](https://github.com/libp2p/js-libp2p/issues/710)) ([999c1b7](https://github.com/libp2p/js-libp2p/commit/999c1b7))
<a name="0.28.6"></a>
## [0.28.6](https://github.com/libp2p/js-libp2p/compare/v0.28.5...v0.28.6) (2020-07-14)
### Bug Fixes
* not dial all known peers in parallel on startup ([#698](https://github.com/libp2p/js-libp2p/issues/698)) ([9ccab40](https://github.com/libp2p/js-libp2p/commit/9ccab40))
<a name="0.28.5"></a>
## [0.28.5](https://github.com/libp2p/js-libp2p/compare/v0.28.4...v0.28.5) (2020-07-10)

45
MIGRATION_TEMPLATE.md Normal file
View File

@@ -0,0 +1,45 @@
<!--Specify versions for migration below-->
# Migrating to libp2p@__
A migration guide for refactoring your application code from libp2p v__ to v__.
## Table of Contents
- [API](#api)
- [Module Updates](#module-updates)
## API
<!--Describe breaking APIs with examples for Before and After
Example:
### Peer Discovery
__Describe__
**Before**
```js
```
**After**
```js
```
-->
## Module Updates
With this release you should update the following libp2p modules if you are relying on them:
<!--Specify module versions in JSON for migration below.
It's recommended to check package.json changes for this:
`git diff <release> <prev> -- package.json`
-->
```json
```

View File

@@ -23,8 +23,8 @@
<a href="https://david-dm.org/libp2p/js-libp2p"><img src="https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square" /></a>
<a href="https://github.com/feross/standard"><img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square"></a>
<a href="https://github.com/RichardLitt/standard-readme"><img src="https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square" /></a>
<a href=""><img src="https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square" /></a>
<a href=""><img src="https://img.shields.io/badge/Node.js-%3E%3D6.0.0-orange.svg?style=flat-square" /></a>
<a href=""><img src="https://img.shields.io/badge/npm-%3E%3D6.0.0-orange.svg?style=flat-square" /></a>
<a href=""><img src="https://img.shields.io/badge/Node.js-%3E%3D10.0.0-orange.svg?style=flat-square" /></a>
<br>
</p>

View File

@@ -418,7 +418,7 @@ const latency = await libp2p.ping(otherPeerId)
## multiaddrs
Gets the multiaddrs the libp2p node announces to the network. This computes the advertising multiaddrs
Gets the multiaddrs the libp2p node announces to the network. This computes the advertising multiaddrs
of the peer by joining the multiaddrs that libp2p transports are listening on with the announce multiaddrs
provided in the libp2p config. Configured no announce multiaddrs will be filtered out of the advertised addresses.
@@ -760,7 +760,7 @@ Get the known [`Addresses`][address] of a provided peer.
| Type | Description |
|------|-------------|
| `Array<Address>` | Array of peer's [`Addresses`][address] containing the multiaddr and its metadata |
| `Array<Address>|undefined` | Array of peer's [`Addresses`][address] containing the multiaddr and its metadata if available, otherwise undefined |
#### Example
@@ -797,7 +797,7 @@ Get the known `Multiaddr` of a provided peer. All returned multiaddrs will inclu
| Type | Description |
|------|-------------|
| `Array<Multiaddr>` | Array of peer's multiaddr |
| `Array<Multiaddr>|undefined` | Array of peer's multiaddr if available, otherwise undefined |
#### Example
@@ -1454,7 +1454,7 @@ Create a key in the keychain.
|------|------|-------------|
| name | `string` | The local key name. It cannot already exist. |
| type | `string` | One of the key types; 'rsa' |
| size | `number` | The key size in bits. |
| [size] | `number` | The key size in bits. Must be provided for rsa keys. |
#### Returns
@@ -1801,7 +1801,7 @@ console.log(peerStats.toJSON())
## Events
Once you have a libp2p instance, you can listen to several events it emits, so that you can be notified of relevant network events.
Once you have a libp2p instance, you can listen to several events it emits, so that you can be notified of relevant network events.
### libp2p

View File

@@ -270,7 +270,7 @@ const node = await Libp2p.create({
},
config: {
peerDiscovery: {
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minPeers)
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minConnections)
// The `tag` property will be searched when creating the instance of your Peer Discovery service.
// The associated object, will be passed to the service when it is instantiated.
[MulticastDNS.tag]: {
@@ -304,7 +304,7 @@ const node = await Libp2p.create({
},
config: {
peerDiscovery: {
webRTCStar: {
[WebRTCStar.tag]: {
enabled: true
}
}

View File

@@ -217,7 +217,7 @@ const node = await Libp2p.create({
},
config: {
peerDiscovery: {
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minPeers)
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minConnections)
// The `tag` property will be searched when creating the instance of your Peer Discovery service.
// The associated object, will be passed to the service when it is instantiated.
[Bootstrap.tag]: {

View File

@@ -0,0 +1,82 @@
/* eslint-disable no-console */
'use strict'
const { Buffer } = require('buffer')
const Libp2p = require('../../../')
const TCP = require('libp2p-tcp')
const Mplex = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const SECIO = require('libp2p-secio')
const Gossipsub = require('libp2p-gossipsub')
const createNode = async () => {
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [TCP],
streamMuxer: [Mplex],
connEncryption: [NOISE, SECIO],
pubsub: Gossipsub
}
})
await node.start()
return node
}
(async () => {
const topic = 'fruit'
const [node1, node2, node3] = await Promise.all([
createNode(),
createNode(),
createNode(),
])
// node1 conect to node2 and node2 conect to node3
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
await node1.dial(node2.peerId)
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
await node2.dial(node3.peerId)
//subscribe
await node1.pubsub.subscribe(topic, (msg) => {
console.log(`node1 received: ${msg.data.toString()}`)
})
await node2.pubsub.subscribe(topic, (msg) => {
console.log(`node2 received: ${msg.data.toString()}`)
})
await node3.pubsub.subscribe(topic, (msg) => {
console.log(`node3 received: ${msg.data.toString()}`)
})
const validateFruit = (msgTopic, peer, msg) => {
const fruit = msg.data.toString();
const validFruit = ['banana', 'apple', 'orange']
const valid = validFruit.includes(fruit);
return valid;
}
//validate fruit
node1.pubsub._pubsub.topicValidators.set(topic, validateFruit);
node2.pubsub._pubsub.topicValidators.set(topic, validateFruit);
node3.pubsub._pubsub.topicValidators.set(topic, validateFruit);
// node1 publishes "fruits" every five seconds
var count = 0;
const myFruits = ['banana', 'apple', 'car', 'orange'];
// car is not a fruit !
setInterval(() => {
console.log('############## fruit ' + myFruits[count] + ' ##############')
node1.pubsub.publish(topic, Buffer.from(myFruits[count]))
count++
if (count == myFruits.length) {
count = 0
}
}, 5000)
})()

View File

@@ -0,0 +1,108 @@
# Filter Messages
To prevent undesired data from being propagated on the network, we can apply a filter to Gossipsub. Messages that fail validation in the filter will not be re-shared.
## 1. Setting up a PubSub network with three nodes
First, let's update our libp2p configuration with a pubsub implementation.
```JavaScript
const Libp2p = require('libp2p')
const Gossipsub = require('libp2p-gossipsub')
const node = await Libp2p.create({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
modules: {
transport: [ TCP ],
streamMuxer: [ Mplex ],
connEncryption: [ NOISE, SECIO ],
pubsub: Gossipsub
}
})
```
Then, create three nodes and connect them together. In this example, we will connect the nodes in series. Node 1 connected with node 2 and node 2 connected with node 3.
```JavaScript
const [node1, node2, node3] = await Promise.all([
createNode(),
createNode(),
createNode(),
])
node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
await node1.dial(node2.peerId)
node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
await node2.dial(node3.peerId)
```
Now we' can subscribe to the fruit topic and log incoming messages.
```JavaScript
const topic = 'fruit'
await node1.pubsub.subscribe(topic, (msg) => {
console.log(`node1 received: ${msg.data.toString()}`)
})
await node2.pubsub.subscribe(topic, (msg) => {
console.log(`node2 received: ${msg.data.toString()}`)
})
await node3.pubsub.subscribe(topic, (msg) => {
console.log(`node3 received: ${msg.data.toString()}`)
})
```
Finally, let's define the additional filter in the fruit topic.
```JavaScript
const validateFruit = (msgTopic, peer, msg) => {
const fruit = msg.data.toString();
const validFruit = ['banana', 'apple', 'orange']
const valid = validFruit.includes(fruit);
return valid;
}
node1.pubsub._pubsub.topicValidators.set(topic, validateFruit);
node2.pubsub._pubsub.topicValidators.set(topic, validateFruit);
node3.pubsub._pubsub.topicValidators.set(topic, validateFruit);
```
In this example, node one has an outdated version of the system, or is a malicious node. When it tries to publish fruit, the messages are re-shared and all the nodes share the message. However, when it tries to publish a vehicle the message is not re-shared.
```JavaScript
var count = 0;
const myFruits = ['banana', 'apple', 'car', 'orange'];
setInterval(() => {
console.log('############## fruit ' + myFruits[count] + ' ##############')
node1.pubsub.publish(topic, Buffer.from(myFruits[count]))
count++
if (count == myFruits.length) {
count = 0
}
}, 5000)
```
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
node3 received: orange
```

View File

@@ -1,6 +1,6 @@
{
"name": "libp2p",
"version": "0.28.5",
"version": "0.28.10",
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js",
@@ -58,7 +58,7 @@
"it-length-prefixed": "^3.0.1",
"it-pipe": "^1.1.0",
"it-protocol-buffers": "^0.2.0",
"libp2p-crypto": "^0.17.6",
"libp2p-crypto": "^0.17.9",
"libp2p-interfaces": "^0.3.1",
"libp2p-utils": "^0.1.2",
"mafmt": "^7.0.0",
@@ -126,43 +126,44 @@
"Vasco Santos <vasco.santos@moxy.studio>",
"Alan Shaw <alan@tableflip.io>",
"Cayman <caymannava@gmail.com>",
"Alex Potsides <alex@achingbrain.net>",
"Pedro Teixeira <i@pgte.me>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Alex Potsides <alex@achingbrain.net>",
"Maciej Krüger <mkg20001@gmail.com>",
"Hugo Dias <mail@hugodias.me>",
"Volker Mische <volker.mische@gmail.com>",
"dirkmc <dirkmdev@gmail.com>",
"Volker Mische <volker.mische@gmail.com>",
"Richard Littauer <richard.littauer@gmail.com>",
"Thomas Eizinger <thomas@eizinger.io>",
"Ryan Bell <ryan@piing.net>",
"Giovanni T. Parra <fiatjaf@gmail.com>",
"Andrew Nesbitt <andrewnez@gmail.com>",
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
"Andrew Nesbitt <andrewnez@gmail.com>",
"Elven <mon.samuel@qq.com>",
"Didrik Nordström <didrik.nordstrom@gmail.com>",
"Tiago Alves <alvesjtiago@gmail.com>",
"Yusef Napora <yusef@napora.org>",
"Zane Starr <zcstarr@gmail.com>",
"ebinks <elizabethjbinks@gmail.com>",
"isan_rivkin <isanrivkin@gmail.com>",
"robertkiel <robert.kiel@validitylabs.org>",
"RasmusErik Voel Jensen <github@solsort.com>",
"Bernd Strehl <bernd.strehl@gmail.com>",
"Chris Bratlien <chrisbratlien@gmail.com>",
"Daijiro Wachi <daijiro.wachi@gmail.com>",
"Diogo Silva <fsdiogo@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"Fei Liu <liu.feiwood@gmail.com>",
"Florian-Merle <florian.david.merle@gmail.com>",
"Giovanni T. Parra <fiatjaf@gmail.com>",
"Ryan Bell <ryan@piing.net>",
"Thomas Eizinger <thomas@eizinger.io>",
"Didrik Nordström <didrik@betamos.se>",
"Francis Gulotta <wizard@roborooter.com>",
"Henrique Dias <hacdias@gmail.com>",
"Irakli Gozalishvili <rfobic@gmail.com>",
"Florian-Merle <florian.david.merle@gmail.com>",
"Joel Gustafson <joelg@mit.edu>",
"Julien Bouquillon <contact@revolunet.com>",
"Kevin Kwok <antimatter15@gmail.com>",
"Felipe Martins <felipebrasil93@gmail.com>",
"Nuno Nogueira <nunofmn@gmail.com>",
"Fei Liu <liu.feiwood@gmail.com>",
"RasmusErik Voel Jensen <github@solsort.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"Soeren <nikorpoulsen@gmail.com>",
"Sönke Hahn <soenkehahn@gmail.com>"
"Sönke Hahn <soenkehahn@gmail.com>",
"Tiago Alves <alvesjtiago@gmail.com>",
"Diogo Silva <fsdiogo@gmail.com>",
"Yusef Napora <yusef@napora.org>",
"Zane Starr <zcstarr@gmail.com>",
"Daijiro Wachi <daijiro.wachi@gmail.com>",
"Chris Bratlien <chrisbratlien@gmail.com>",
"ebinks <elizabethjbinks@gmail.com>",
"Bernd Strehl <bernd.strehl@gmail.com>",
"isan_rivkin <isanrivkin@gmail.com>",
"Henrique Dias <hacdias@gmail.com>",
"robertkiel <robert.kiel@validitylabs.org>",
"Irakli Gozalishvili <rfobic@gmail.com>"
]
}

View File

@@ -12,7 +12,7 @@ const DefaultConfig = {
noAnnounce: []
},
connectionManager: {
minPeers: 25
minConnections: 25
},
transportManager: {
faultTolerance: FaultTolerance.FATAL_ALL

View File

@@ -1,9 +1,12 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:connection-manager')
log.error = debug('libp2p:connection-manager:error')
const errcode = require('err-code')
const mergeOptions = require('merge-options')
const LatencyMonitor = require('./latency-monitor')
const debug = require('debug')('libp2p:connection-manager')
const retimer = require('retimer')
const { EventEmitter } = require('events')
@@ -22,6 +25,7 @@ const defaultOptions = {
maxReceivedData: Infinity,
maxEventLoopDelay: Infinity,
pollInterval: 2000,
autoDialInterval: 10000,
movingAverageInterval: 60000,
defaultPeerValue: 1
}
@@ -45,6 +49,8 @@ class ConnectionManager extends EventEmitter {
* @param {Number} options.pollInterval How often, in milliseconds, metrics and latency should be checked. Default=2000
* @param {Number} options.movingAverageInterval How often, in milliseconds, to compute averages. Default=60000
* @param {Number} options.defaultPeerValue The value of the peer. Default=1
* @param {boolean} options.autoDial Should preemptively guarantee connections are above the low watermark. Default=true
* @param {Number} options.autoDialInterval How often, in milliseconds, it should preemptively guarantee connections are above the low watermark. Default=10000
*/
constructor (libp2p, options) {
super()
@@ -57,7 +63,7 @@ class ConnectionManager extends EventEmitter {
throw errcode(new Error('Connection Manager maxConnections must be greater than minConnections'), ERR_INVALID_PARAMETERS)
}
debug('options: %j', this._options)
log('options: %j', this._options)
this._libp2p = libp2p
@@ -73,8 +79,11 @@ class ConnectionManager extends EventEmitter {
*/
this.connections = new Map()
this._started = false
this._timer = null
this._autoDialTimeout = null
this._checkMetrics = this._checkMetrics.bind(this)
this._autoDial = this._autoDial.bind(this)
}
/**
@@ -101,7 +110,11 @@ class ConnectionManager extends EventEmitter {
})
this._onLatencyMeasure = this._onLatencyMeasure.bind(this)
this._latencyMonitor.on('data', this._onLatencyMeasure)
debug('started')
this._started = true
log('started')
this._options.autoDial && this._autoDial()
}
/**
@@ -109,11 +122,13 @@ class ConnectionManager extends EventEmitter {
* @async
*/
async stop () {
this._autoDialTimeout && this._autoDialTimeout.clear()
this._timer && this._timer.clear()
this._latencyMonitor && this._latencyMonitor.removeListener('data', this._onLatencyMeasure)
this._started = false
await this._close()
debug('stopped')
log('stopped')
}
/**
@@ -157,13 +172,13 @@ class ConnectionManager extends EventEmitter {
_checkMetrics () {
const movingAverages = this._libp2p.metrics.global.movingAverages
const received = movingAverages.dataReceived[this._options.movingAverageInterval].movingAverage()
this._checkLimit('maxReceivedData', received)
this._checkMaxLimit('maxReceivedData', received)
const sent = movingAverages.dataSent[this._options.movingAverageInterval].movingAverage()
this._checkLimit('maxSentData', sent)
this._checkMaxLimit('maxSentData', sent)
const total = received + sent
this._checkLimit('maxData', total)
debug('metrics update', total)
this._timer.reschedule(this._options.pollInterval)
this._checkMaxLimit('maxData', total)
log('metrics update', total)
this._timer = retimer(this._checkMetrics, this._options.pollInterval)
}
/**
@@ -188,7 +203,7 @@ class ConnectionManager extends EventEmitter {
this._peerValues.set(peerIdStr, this._options.defaultPeerValue)
}
this._checkLimit('maxConnections', this.size)
this._checkMaxLimit('maxConnections', this.size)
}
/**
@@ -248,7 +263,7 @@ class ConnectionManager extends EventEmitter {
* @param {*} summary The LatencyMonitor summary
*/
_onLatencyMeasure (summary) {
this._checkLimit('maxEventLoopDelay', summary.avgMs)
this._checkMaxLimit('maxEventLoopDelay', summary.avgMs)
}
/**
@@ -257,15 +272,61 @@ class ConnectionManager extends EventEmitter {
* @param {string} name The name of the field to check limits for
* @param {number} value The current value of the field
*/
_checkLimit (name, value) {
_checkMaxLimit (name, value) {
const limit = this._options[name]
debug('checking limit of %s. current value: %d of %d', name, value, limit)
log('checking limit of %s. current value: %d of %d', name, value, limit)
if (value > limit) {
debug('%s: limit exceeded: %s, %d', this._peerId, name, value)
log('%s: limit exceeded: %s, %d', this._peerId, name, value)
this._maybeDisconnectOne()
}
}
/**
* Proactively tries to connect to known peers stored in the PeerStore.
* It will keep the number of connections below the upper limit and sort
* the peers to connect based on wether we know their keys and protocols.
* @async
* @private
*/
async _autoDial () {
const minConnections = this._options.minConnections
// Already has enough connections
if (this.size >= minConnections) {
this._autoDialTimeout = retimer(this._autoDial, this._options.autoDialInterval)
return
}
// Sort peers on wether we know protocols of public keys for them
const peers = Array.from(this._libp2p.peerStore.peers.values())
.sort((a, b) => {
if (b.protocols && b.protocols.length && (!a.protocols || !a.protocols.length)) {
return 1
} else if (b.id.pubKey && !a.id.pubKey) {
return 1
}
return -1
})
for (let i = 0; i < peers.length && this.size < minConnections; i++) {
if (!this.get(peers[i].id)) {
log('connecting to a peerStore stored peer %s', peers[i].id.toB58String())
try {
await this._libp2p.dialer.connectToPeer(peers[i].id)
// Connection Manager was stopped
if (!this._started) {
return
}
} catch (err) {
log.error('could not connect to peerStore stored peer', err)
}
}
}
this._autoDialTimeout = retimer(this._autoDial, this._options.autoDialInterval)
}
/**
* If we have more connections than our maximum, close a connection
* to the lowest valued peer.
@@ -274,12 +335,12 @@ class ConnectionManager extends EventEmitter {
_maybeDisconnectOne () {
if (this._options.minConnections < this.connections.size) {
const peerValues = Array.from(this._peerValues).sort(byPeerValue)
debug('%s: sorted peer values: %j', this._peerId, peerValues)
log('%s: sorted peer values: %j', this._peerId, peerValues)
const disconnectPeer = peerValues[0]
if (disconnectPeer) {
const peerId = disconnectPeer[0]
debug('%s: lowest value peer is %s', this._peerId, peerId)
debug('%s: closing a connection to %j', this._peerId, peerId)
log('%s: lowest value peer is %s', this._peerId, peerId)
log('%s: closing a connection to %j', this._peerId, peerId)
for (const connections of this.connections.values()) {
if (connections[0].remotePeer.toB58String() === peerId) {
connections[0].close()

View File

@@ -112,7 +112,7 @@ class Dialer {
this.peerStore.addressBook.add(id, multiaddrs)
}
let addrs = this.peerStore.addressBook.getMultiaddrsForPeer(id)
let addrs = this.peerStore.addressBook.getMultiaddrsForPeer(id) || []
// If received a multiaddr to dial, it should be the first to use
// But, if we know other multiaddrs for the peer, we should try them too.

View File

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

View File

@@ -175,6 +175,7 @@ class IdentifyService {
// Update peers data in PeerStore
this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr)))
this.peerStore.protoBook.set(id, protocols)
this.peerStore.metadataBook.set(id, 'AgentVersion', Buffer.from(message.agentVersion))
// TODO: Track our observed address so that we can score it
log('received observed address of %s', observedAddr)

View File

@@ -65,7 +65,13 @@ class Libp2p extends EventEmitter {
this._discovery = new Map() // Discovery service instances/references
// Create the Connection Manager
this.connectionManager = new ConnectionManager(this, this._options.connectionManager)
if (this._options.connectionManager.minPeers) { // Remove in 0.29
this._options.connectionManager.minConnections = this._options.connectionManager.minPeers
}
this.connectionManager = new ConnectionManager(this, {
autoDial: this._config.peerDiscovery.autoDial,
...this._options.connectionManager
})
// Create Metrics
if (this._options.metrics.enabled) {
@@ -76,7 +82,7 @@ class Libp2p extends EventEmitter {
}
// Create keychain
if (this._options.keychain && this._options.keychain.pass && this._options.keychain.datastore) {
if (this._options.keychain && this._options.keychain.datastore) {
log('creating keychain')
const keychainOpts = Keychain.generateOptions()
@@ -318,7 +324,7 @@ class Libp2p extends EventEmitter {
* @returns {Promise<Connection|*>}
*/
async dialProtocol (peer, protocols, options) {
const { id, multiaddrs } = getPeer(peer, this.peerStore)
const { id, multiaddrs } = getPeer(peer)
let connection = this.connectionManager.get(id)
if (!connection) {
@@ -390,7 +396,12 @@ class Libp2p extends EventEmitter {
* @returns {Promise<number>}
*/
ping (peer) {
const { id } = getPeer(peer)
const { id, multiaddrs } = getPeer(peer)
// If received multiaddr, ping it
if (multiaddrs) {
return ping(this, multiaddrs[0])
}
return ping(this, id)
}
@@ -460,19 +471,19 @@ class Libp2p extends EventEmitter {
async _onDidStart () {
this._isStarted = true
this.connectionManager.start()
this.peerStore.on('peer', peerId => {
this.emit('peer:discovery', peerId)
this._maybeConnect(peerId)
})
// Once we start, emit and dial any peers we may have already discovered
// Once we start, emit any peers we may have already discovered
// TODO: this should be removed, as we already discovered these peers in the past
for (const peer of this.peerStore.peers.values()) {
this.emit('peer:discovery', peer.id)
this._maybeConnect(peer.id)
}
this.connectionManager.start()
// Peer discovery
await this._setupPeerDiscovery()
}
@@ -496,15 +507,15 @@ class Libp2p extends EventEmitter {
/**
* Will dial to the given `peerId` if the current number of
* connected peers is less than the configured `ConnectionManager`
* minPeers.
* minConnections.
* @private
* @param {PeerId} peerId
*/
async _maybeConnect (peerId) {
// If auto dialing is on and we have no connection to the peer, check if we should dial
if (this._config.peerDiscovery.autoDial === true && !this.connectionManager.get(peerId)) {
const minPeers = this._options.connectionManager.minPeers || 0
if (minPeers > this.connectionManager.size) {
const minConnections = this._options.connectionManager.minConnections || 0
if (minConnections > this.connectionManager.size) {
log('connecting to discovered peer %s', peerId.toB58String())
try {
await this.dialer.connectToPeer(peerId)

View File

@@ -7,6 +7,9 @@ const crypto = require('libp2p-crypto')
const DS = require('interface-datastore')
const CMS = require('./cms')
const errcode = require('err-code')
const { Number } = require('ipfs-utils/src/globalthis')
require('node-forge/lib/sha512')
const keyPrefix = '/pkcs8/'
const infoPrefix = '/info/'
@@ -107,7 +110,7 @@ class Keychain {
this.opts = mergeOptions(defaultOptions, options)
// Enforce NIST SP 800-132
if (!this.opts.passPhrase || this.opts.passPhrase.length < 20) {
if (this.opts.passPhrase && this.opts.passPhrase.length < 20) {
throw new Error('passPhrase must be least 20 characters')
}
if (this.opts.dek.keyLength < NIST.minKeyLength) {
@@ -120,13 +123,13 @@ class Keychain {
throw new Error(`dek.iterationCount must be least ${NIST.minIterationCount}`)
}
// Create the derived encrypting key
const dek = crypto.pbkdf2(
const dek = this.opts.passPhrase ? crypto.pbkdf2(
this.opts.passPhrase,
this.opts.dek.salt,
this.opts.dek.iterationCount,
this.opts.dek.keyLength,
this.opts.dek.hash)
this.opts.dek.hash) : ''
Object.defineProperty(this, '_', { value: () => dek })
}
@@ -171,8 +174,8 @@ 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.
* @returns {KeyInfo}
* @param {int} [size] - The key size in bits. Used for rsa keys only.
* @returns {KeyInfo}
*/
async createKey (name, type, size) {
const self = this
@@ -185,17 +188,13 @@ class Keychain {
return throwDelayed(errcode(new Error(`Invalid key type '${type}'`), 'ERR_INVALID_KEY_TYPE'))
}
if (!Number.isSafeInteger(size)) {
return throwDelayed(errcode(new Error(`Invalid key size '${size}'`), 'ERR_INVALID_KEY_SIZE'))
}
const dsname = DsName(name)
const exists = await self.store.has(dsname)
if (exists) return throwDelayed(errcode(new Error(`Key '${name}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))
switch (type.toLowerCase()) {
case 'rsa':
if (size < 2048) {
if (!Number.isSafeInteger(size) || size < 2048) {
return throwDelayed(errcode(new Error(`Invalid RSA key size ${size}`), 'ERR_INVALID_KEY_SIZE'))
}
break

View File

@@ -128,11 +128,7 @@ class Stats extends EventEmitter {
* @returns {void}
*/
_resetComputeTimeout () {
if (this._timeout) {
this._timeout.reschedule(this._nextTimeout())
} else {
this._timeout = retimer(this._update, this._nextTimeout())
}
this._timeout = retimer(this._update, this._nextTimeout())
}
/**

View File

@@ -168,8 +168,9 @@ class AddressBook extends Book {
/**
* Get the known multiaddrs for a given peer. All returned multiaddrs
* will include the encapsulated `PeerId` of the peer.
* Returns `undefined` if there are no known multiaddrs for the given peer.
* @param {PeerId} peerId
* @returns {Array<Multiaddr>}
* @returns {Array<Multiaddr>|undefined}
*/
getMultiaddrsForPeer (peerId) {
if (!PeerId.isPeerId(peerId)) {

View File

@@ -77,8 +77,9 @@ class Book {
/**
* Get the known data of a provided peer.
* Returns `undefined` if there is no available data for the given peer.
* @param {PeerId} peerId
* @returns {Array<Data>}
* @returns {Array<Data>|undefined}
*/
get (peerId) {
if (!PeerId.isPeerId(peerId)) {

View File

@@ -15,11 +15,11 @@ const { PROTOCOL, PING_LENGTH } = require('./constants')
/**
* Ping a given peer and wait for its response, getting the operation latency.
* @param {Libp2p} node
* @param {PeerId} peer
* @param {PeerId|multiaddr} peer
* @returns {Promise<Number>}
*/
async function ping (node, peer) {
log('dialing %s to %s', PROTOCOL, peer.toB58String())
log('dialing %s to %s', PROTOCOL, peer.toB58String ? peer.toB58String() : peer)
const { stream } = await node.dialProtocol(peer, PROTOCOL)

View File

@@ -7,6 +7,9 @@ chai.use(require('chai-as-promised'))
const { expect } = chai
const sinon = require('sinon')
const delay = require('delay')
const pWaitFor = require('p-wait-for')
const peerUtils = require('../utils/creators/peer')
const mockConnection = require('../utils/mockConnection')
const baseOptions = require('../utils/base-options.browser')
@@ -112,4 +115,148 @@ describe('libp2p.connections', () => {
await libp2p.stop()
await remoteLibp2p.stop()
})
describe('proactive connections', () => {
let nodes = []
beforeEach(async () => {
nodes = await peerUtils.createPeer({
number: 2,
config: {
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
}
}
})
})
afterEach(async () => {
await Promise.all(nodes.map((node) => node.stop()))
sinon.reset()
})
it('should connect to all the peers stored in the PeerStore, if their number is below minConnections', async () => {
const [libp2p] = await peerUtils.createPeer({
fixture: false,
started: false,
config: {
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
connectionManager: {
minConnections: 3
}
}
})
// Populate PeerStore before starting
libp2p.peerStore.addressBook.set(nodes[0].peerId, nodes[0].multiaddrs)
libp2p.peerStore.addressBook.set(nodes[1].peerId, nodes[1].multiaddrs)
await libp2p.start()
// Wait for peers to connect
await pWaitFor(() => libp2p.connectionManager.size === 2)
await libp2p.stop()
})
it('should connect to all the peers stored in the PeerStore until reaching the minConnections', async () => {
const minConnections = 1
const [libp2p] = await peerUtils.createPeer({
fixture: false,
started: false,
config: {
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
connectionManager: {
minConnections
}
}
})
// Populate PeerStore before starting
libp2p.peerStore.addressBook.set(nodes[0].peerId, nodes[0].multiaddrs)
libp2p.peerStore.addressBook.set(nodes[1].peerId, nodes[1].multiaddrs)
await libp2p.start()
// Wait for peer to connect
await pWaitFor(() => libp2p.connectionManager.size === minConnections)
// Wait more time to guarantee no other connection happened
await delay(200)
expect(libp2p.connectionManager.size).to.eql(minConnections)
await libp2p.stop()
})
it('should connect to all the peers stored in the PeerStore until reaching the minConnections sorted', async () => {
const minConnections = 1
const [libp2p] = await peerUtils.createPeer({
fixture: false,
started: false,
config: {
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
connectionManager: {
minConnections
}
}
})
// Populate PeerStore before starting
libp2p.peerStore.addressBook.set(nodes[0].peerId, nodes[0].multiaddrs)
libp2p.peerStore.addressBook.set(nodes[1].peerId, nodes[1].multiaddrs)
libp2p.peerStore.protoBook.set(nodes[1].peerId, ['/protocol-min-conns'])
await libp2p.start()
// Wait for peer to connect
await pWaitFor(() => libp2p.connectionManager.size === minConnections)
// Should have connected to the peer with protocols
expect(libp2p.connectionManager.get(nodes[0].peerId)).to.not.exist()
expect(libp2p.connectionManager.get(nodes[1].peerId)).to.exist()
await libp2p.stop()
})
it('should connect to peers in the PeerStore when a peer disconnected', async () => {
const minConnections = 1
const autoDialInterval = 1000
const [libp2p] = await peerUtils.createPeer({
fixture: false,
config: {
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
connectionManager: {
minConnections,
autoDialInterval
}
}
})
// Populate PeerStore after starting (discovery)
libp2p.peerStore.addressBook.set(nodes[0].peerId, nodes[0].multiaddrs)
// Wait for peer to connect
const conn = await libp2p.dial(nodes[0].peerId)
expect(libp2p.connectionManager.get(nodes[0].peerId)).to.exist()
await conn.close()
// Closed
await pWaitFor(() => libp2p.connectionManager.size === 0)
// Connected
await pWaitFor(() => libp2p.connectionManager.size === 1)
expect(libp2p.connectionManager.get(nodes[0].peerId)).to.exist()
await libp2p.stop()
})
})
})

View File

@@ -58,7 +58,8 @@ describe('Connection Manager', () => {
config: {
modules: baseOptions.modules,
connectionManager: {
maxConnections: max
maxConnections: max,
minConnections: 2
}
},
started: false
@@ -96,7 +97,8 @@ describe('Connection Manager', () => {
config: {
modules: baseOptions.modules,
connectionManager: {
maxConnections: max
maxConnections: max,
minConnections: 0
}
},
started: false

View File

@@ -17,7 +17,7 @@ describe('ping', () => {
beforeEach(async () => {
nodes = await peerUtils.createPeer({
number: 2,
number: 3,
config: baseOptions
})
@@ -25,7 +25,14 @@ describe('ping', () => {
nodes[1].peerStore.addressBook.set(nodes[0].peerId, nodes[0].multiaddrs)
})
it('ping once from peer0 to peer1', async () => {
it('ping once from peer0 to peer1 using a multiaddr', async () => {
const ma = `${nodes[2].multiaddrs[0]}/p2p/${nodes[2].peerId.toB58String()}`
const latency = await nodes[0].ping(ma)
expect(latency).to.be.a('Number')
})
it('ping once from peer0 to peer1 using a peerId', async () => {
const latency = await nodes[0].ping(nodes[1].peerId)
expect(latency).to.be.a('Number')

View File

@@ -96,6 +96,15 @@ describe('Dialing (direct, TCP)', () => {
.and.to.have.nested.property('._errors[0].code', ErrorCodes.ERR_TRANSPORT_UNAVAILABLE)
})
it('should fail to connect if peer has no known addresses', async () => {
const dialer = new Dialer({ transportManager: localTM, peerStore })
const peerId = await PeerId.createFromJSON(Peers[1])
await expect(dialer.connectToPeer(peerId))
.to.eventually.be.rejectedWith(Error)
.and.to.have.nested.property('.code', ErrorCodes.ERR_NO_VALID_ADDRESSES)
})
it('should be able to connect to a given peer id', async () => {
const peerStore = new PeerStore()
const dialer = new Dialer({

View File

@@ -19,6 +19,7 @@ const { IdentifyService, multicodecs } = require('../../src/identify')
const Peers = require('../fixtures/peers')
const Libp2p = require('../../src')
const baseOptions = require('../utils/base-options.browser')
const pkg = require('../../package.json')
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
const remoteAddr = MULTIADDRS_WEBSOCKETS[0]
@@ -53,6 +54,9 @@ describe('Identify', () => {
},
protoBook: {
set: () => { }
},
metadataBook: {
set: () => { }
}
},
multiaddrs: []
@@ -77,6 +81,7 @@ describe('Identify', () => {
sinon.spy(localIdentify.peerStore.addressBook, 'set')
sinon.spy(localIdentify.peerStore.protoBook, 'set')
sinon.spy(localIdentify.peerStore.metadataBook, 'set')
// Run identify
await Promise.all([
@@ -90,6 +95,12 @@ describe('Identify', () => {
expect(localIdentify.peerStore.addressBook.set.callCount).to.equal(1)
expect(localIdentify.peerStore.protoBook.set.callCount).to.equal(1)
const metadataArgs = localIdentify.peerStore.metadataBook.set.firstCall.args
expect(metadataArgs[0].id.bytes).to.equal(remotePeer.bytes)
expect(metadataArgs[1]).to.equal('AgentVersion')
expect(metadataArgs[2].toString()).to.equal(`js-libp2p/${pkg.version}`)
// Validate the remote peer gets updated in the peer store
const call = localIdentify.peerStore.addressBook.set.firstCall
expect(call.args[0].id.bytes).to.equal(remotePeer.bytes)

View File

@@ -2,10 +2,8 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const { expect } = chai
const { chai, expect } = require('aegir/utils/chai')
const fail = expect.fail
chai.use(require('dirty-chai'))
chai.use(require('chai-string'))
const peerUtils = require('../utils/creators/peer')
@@ -40,8 +38,8 @@ describe('keychain', () => {
emptyKeystore = new Keychain(datastore1, { passPhrase: passPhrase })
})
it('needs a pass phrase to encrypt a key', () => {
expect(() => new Keychain(datastore2)).to.throw()
it('can start without a password', () => {
expect(() => new Keychain(datastore2)).to.not.throw()
})
it('needs a NIST SP 800-132 non-weak pass phrase', () => {
@@ -56,12 +54,48 @@ describe('keychain', () => {
expect(Keychain.options).to.exist()
})
it('needs a supported hashing alorithm', () => {
it('supports supported hashing alorithms', () => {
const ok = new Keychain(datastore2, { passPhrase: passPhrase, dek: { hash: 'sha2-256' } })
expect(ok).to.exist()
})
it('does not support unsupported hashing alorithms', () => {
expect(() => new Keychain(datastore2, { passPhrase: passPhrase, dek: { hash: 'my-hash' } })).to.throw()
})
it('can list keys without a password', async () => {
const keychain = new Keychain(datastore2)
expect(await keychain.listKeys()).to.have.lengthOf(0)
})
it('can find a key without a password', async () => {
const keychain = new Keychain(datastore2)
const keychainWithPassword = new Keychain(datastore2, { passPhrase: `hello-${Date.now()}-${Date.now()}` })
const id = `key-${Math.random()}`
await keychainWithPassword.createKey(id, 'rsa', 2048)
await expect(keychain.findKeyById(id)).to.eventually.be.ok()
})
it('can remove a key without a password', async () => {
const keychainWithoutPassword = new Keychain(datastore2)
const keychainWithPassword = new Keychain(datastore2, { passPhrase: `hello-${Date.now()}-${Date.now()}` })
const name = `key-${Math.random()}`
expect(await keychainWithPassword.createKey(name, 'rsa', 2048)).to.have.property('name', name)
expect(await keychainWithoutPassword.findKeyByName(name)).to.have.property('name', name)
await keychainWithoutPassword.removeKey(name)
await expect(keychainWithoutPassword.findKeyByName(name)).to.be.rejectedWith(/does not exist/)
})
it('requires a key to create a password', async () => {
const keychain = new Keychain(datastore2)
await expect(keychain.createKey('derp')).to.be.rejected()
})
it('can generate options', () => {
const options = Keychain.generateOptions()
options.passPhrase = passPhrase
@@ -149,6 +183,70 @@ describe('keychain', () => {
})
})
describe('ed25519 keys', () => {
const keyName = 'my custom key'
it('can be an ed25519 key', async () => {
const keyInfo = await ks.createKey(keyName, 'ed25519')
expect(keyInfo).to.exist()
expect(keyInfo).to.have.property('name', keyName)
expect(keyInfo).to.have.property('id')
})
it('does not overwrite existing key', async () => {
const err = await ks.createKey(keyName, 'ed25519').then(fail, err => err)
expect(err).to.have.property('code', 'ERR_KEY_ALREADY_EXISTS')
})
it('can export/import a key', async () => {
const keyName = 'a new key'
const password = 'my sneaky password'
const keyInfo = await ks.createKey(keyName, 'ed25519')
const exportedKey = await ks.exportKey(keyName, password)
// remove it so we can import it
await ks.removeKey(keyName)
const importedKey = await ks.importKey(keyName, exportedKey, password)
expect(importedKey.id).to.eql(keyInfo.id)
})
it('cannot create the "self" key', async () => {
const err = await ks.createKey('self', 'ed25519').then(fail, err => err)
expect(err).to.exist()
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
})
})
describe('secp256k1 keys', () => {
const keyName = 'my secp256k1 key'
it('can be an secp256k1 key', async () => {
const keyInfo = await ks.createKey(keyName, 'secp256k1')
expect(keyInfo).to.exist()
expect(keyInfo).to.have.property('name', keyName)
expect(keyInfo).to.have.property('id')
})
it('does not overwrite existing key', async () => {
const err = await ks.createKey(keyName, 'secp256k1').then(fail, err => err)
expect(err).to.have.property('code', 'ERR_KEY_ALREADY_EXISTS')
})
it('can export/import a key', async () => {
const keyName = 'a new secp256k1 key'
const password = 'my sneaky password'
const keyInfo = await ks.createKey(keyName, 'secp256k1')
const exportedKey = await ks.exportKey(keyName, password)
// remove it so we can import it
await ks.removeKey(keyName)
const importedKey = await ks.importKey(keyName, exportedKey, password)
expect(importedKey.id).to.eql(keyInfo.id)
})
it('cannot create the "self" key', async () => {
const err = await ks.createKey('self', 'secp256k1').then(fail, err => err)
expect(err).to.exist()
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
})
})
describe('query', () => {
it('finds all existing keys', async () => {
const keys = await ks.listKeys()
@@ -411,7 +509,7 @@ describe('libp2p.keychain', () => {
throw new Error('should throw an error using the keychain if no passphrase provided')
})
it('can be used if a passphrase is provided', async () => {
it('can be used when a passphrase is provided', async () => {
const [libp2p] = await peerUtils.createPeer({
started: false,
config: {
@@ -428,6 +526,22 @@ describe('libp2p.keychain', () => {
expect(kInfo).to.exist()
})
it('does not require a keychain passphrase', async () => {
const [libp2p] = await peerUtils.createPeer({
started: false,
config: {
keychain: {
datastore: new MemoryDatastore()
}
}
})
await libp2p.loadKeychain()
const kInfo = await libp2p.keychain.createKey('keyName', 'ed25519')
expect(kInfo).to.exist()
})
it('can reload keys', async () => {
const datastore = new MemoryDatastore()
const [libp2p] = await peerUtils.createPeer({

View File

@@ -31,10 +31,13 @@ describe('peer discovery', () => {
sinon.reset()
})
it('should dial know peers on startup', async () => {
it('should dial know peers on startup below the minConnections watermark', async () => {
libp2p = new Libp2p({
...baseOptions,
peerId
peerId,
connectionManager: {
minConnections: 2
}
})
libp2p.peerStore.addressBook.set(remotePeerId, [multiaddr('/ip4/165.1.1.1/tcp/80')])