Compare commits

..

19 Commits

Author SHA1 Message Date
Jacob Heun
71dcaafcac chore: release version v0.25.2 2019-04-17 15:06:47 +02:00
Jacob Heun
5319e065ec chore: update contributors 2019-04-17 15:06:47 +02:00
Jacob Heun
f3801f0e6c fix: dht config (#359) 2019-04-17 15:04:35 +02:00
Jacob Heun
51cc993876 docs: fix incorrect references for enabling dht discovery (#358) 2019-04-16 15:04:23 +02:00
Jacob Heun
a800c1ad91 chore: release version v0.25.1 2019-04-16 12:40:41 +02:00
Jacob Heun
54c474de98 chore: update contributors 2019-04-16 12:40:41 +02:00
Jacob Heun
f28dffb268 fix: bail when discovering self (#357) 2019-04-16 12:05:22 +02:00
Jacob Heun
c049074cb5 chore: update release template (#355) 2019-04-12 13:05:13 +02:00
Jacob Heun
1bde70f1b5 chore: release version v0.25.0 2019-04-12 11:15:34 +02:00
Jacob Heun
cfa4df6e11 chore: update contributors 2019-04-12 11:15:33 +02:00
Jacob Heun
eb5aa03232 fix: allow switch to be configured (#354) 2019-04-12 11:10:09 +02:00
Jacob Heun
4cb541ddae docs: update examples for latest libp2p rc (#353)
* docs: update chat example readme

* docs: update discovery test for autodial

* docs: fix delegated routing example

* docs: update echo example readme

* docs: fix libp2p in the browser example

* docs: update examples for peer/content routing

* docs: update the pubsub example
2019-04-11 15:52:04 +02:00
Jacob Heun
aa1d9b273a chore: release version v0.25.0-rc.6 2019-04-11 13:54:40 +02:00
Jacob Heun
7313f781fc chore: update contributors 2019-04-11 13:54:39 +02:00
Jacob Heun
313b1eae20 fix: connection emits (#352)
* fix: add connection tracking events

* chore: update dependencies
2019-04-11 13:49:31 +02:00
Jacob Heun
01aa44724e feat: auto dial discovered peers (#349) 2019-04-11 12:44:58 +02:00
Vasco Santos
8b627797e2 chore: add discourse badge (#351) 2019-04-10 11:53:19 +02:00
Jacob Heun
e5f19e860b fix: remove unneeded peerbook puts (#348) 2019-04-05 14:27:35 +02:00
Yusef Napora
5204da73f7 chore: update readme with links to docs & discuss (#347) 2019-04-04 10:49:40 +02:00
33 changed files with 761 additions and 420 deletions

View File

@@ -79,7 +79,7 @@ const after = (done) => {
}
module.exports = {
bundlesize: { maxSize: '215kB' },
bundlesize: { maxSize: '218kB' },
hooks: {
pre: before,
post: after

View File

@@ -1,3 +1,49 @@
<a name="0.25.2"></a>
## [0.25.2](https://github.com/libp2p/js-libp2p/compare/v0.25.1...v0.25.2) (2019-04-17)
### Bug Fixes
* dht config ([#359](https://github.com/libp2p/js-libp2p/issues/359)) ([f3801f0](https://github.com/libp2p/js-libp2p/commit/f3801f0))
<a name="0.25.1"></a>
## [0.25.1](https://github.com/libp2p/js-libp2p/compare/v0.25.0...v0.25.1) (2019-04-16)
### Bug Fixes
* bail when discovering self ([#357](https://github.com/libp2p/js-libp2p/issues/357)) ([f28dffb](https://github.com/libp2p/js-libp2p/commit/f28dffb))
<a name="0.25.0"></a>
# [0.25.0](https://github.com/libp2p/js-libp2p/compare/v0.25.0-rc.6...v0.25.0) (2019-04-12)
### Bug Fixes
* allow switch to be configured ([#354](https://github.com/libp2p/js-libp2p/issues/354)) ([eb5aa03](https://github.com/libp2p/js-libp2p/commit/eb5aa03))
<a name="0.25.0-rc.6"></a>
# [0.25.0-rc.6](https://github.com/libp2p/js-libp2p/compare/v0.25.0-rc.5...v0.25.0-rc.6) (2019-04-11)
### Bug Fixes
* connection emits ([#352](https://github.com/libp2p/js-libp2p/issues/352)) ([313b1ea](https://github.com/libp2p/js-libp2p/commit/313b1ea))
* remove unneeded peerbook puts ([#348](https://github.com/libp2p/js-libp2p/issues/348)) ([e5f19e8](https://github.com/libp2p/js-libp2p/commit/e5f19e8))
### Features
* auto dial discovered peers ([#349](https://github.com/libp2p/js-libp2p/issues/349)) ([01aa447](https://github.com/libp2p/js-libp2p/commit/01aa447))
<a name="0.25.0-rc.5"></a>
# [0.25.0-rc.5](https://github.com/libp2p/js-libp2p/compare/v0.25.0-rc.4...v0.25.0-rc.5) (2019-03-21)

60
PEER_DISCOVERY.md Normal file
View File

@@ -0,0 +1,60 @@
# Peer Discovery and Auto Dial
**Synopsis**:
* All peers discovered are emitted via `peer:discovery` so applications can take any desired action.
* Libp2p defaults to automatically connecting to new peers, when under the [ConnectionManager](https://github.com/libp2p/js-libp2p-connection-manager) low watermark (minimum peers).
* Applications can disable this via the `peerDiscovery.autoDial` config property, and handle connections themselves.
* Applications who have not disabled this should **never** connect on peer discovery. Applications should use the `peer:connect` event if they wish to take a specific action on new peers.
## Scenarios
In any scenario, if a peer is discovered it should be added to the PeerBook. This ensures that even if we don't dial to a node when we discover it, we know about it in the event that it becomes known as a provider for something we need. The scenarios listed below detail what actions the auto dialer will take when peers are discovered.
### 1. Joining the network
The node is new and needs to join the network. It currently has 0 peers.
**Discovery Mechanisms**: [Ambient Discovery](#ambient-discovery)
### Action to take
Connect to discovered peers. This should have some degree of concurrency limiting. While the case should be low, if we immediately discover more peers than our high watermark we should avoid dialing them all.
### 2. Connected to some
The node is connected to other nodes. The current number of connections is less than the desired low watermark.
**Discovery Mechanisms**: [Ambient Discovery](#ambient-discovery) and [Active Discovery](#active-discovery)
### Action to take
Connect to discovered peers. This should have some degree of concurrency limiting. The concurrency may need to be modified to reflect the current number of peers connected. The more peers we have, the lower the concurrency may need to be.
### 3. Connected to enough
**Discovery Mechanisms**: [Ambient Discovery](#ambient-discovery) and [Active Discovery](#active-discovery)
### Action to take
None. If we are connected to enough peers, the low watermark, we should not connect to discovered peers. As other peers discover us, they may connect to us based on their current scenario.
For example, a long running node with adequate peers is on an MDNS network. A new peer joins the network and both become aware of each other. The new peer should be the peer that dials, as it has too few peers. The existing node has no reason to dial the new peer, but should keep a record of it in case it later becomes an important node due to its contents/capabilities.
Avoiding dials above the low watermark also allows for a pool of connections to be reserved for application specific actions, such as connecting to a specific content provider via a DHT query to find that content (ipfs-bitswap).
### 4. Connected to too many
The node has more connections than it wants. The current number of connections is greater than the high watermark.
[WIP Connection Manager v2 spec](https://github.com/libp2p/specs/pull/161)
**Discovery Mechanisms**: [Ambient Discovery](#ambient-discovery) and [Active Discovery](#active-discovery)
### Action to take
None, the `ConnectionManager` will automatically prune connections.
## Discovery Mechanisms
Means of which a libp2p node discovers other peers.
### Active Discovery
Through active use of the libp2p network, a node may discovery peers.
* Content/Peer routing (DHT, delegated, etc) provider and peer queries
* DHT random walk
* Rendezvous servers
### Ambient Discovery
Leveraging known addresses, or network discovery mechanisms, a node may discover peers outside of the bounds of the libp2p network.
* Bootstrap
* MDNS
* proximity based (bluetooth, sound, etc)

View File

@@ -8,6 +8,7 @@
<a href="http://ipn.io"><img src="https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square" /></a>
<a href="http://libp2p.io/"><img src="https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square" /></a>
<a href="http://webchat.freenode.net/?channels=%23libp2p"><img src="https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square" /></a>
<a href="https://discuss.libp2p.io"><img src="https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg" /></a>
<a href="https://waffle.io/libp2p/libp2p"><img src="https://img.shields.io/badge/pm-waffle-yellow.svg?style=flat-square" /></a>
</p>
@@ -64,7 +65,9 @@ libp2p is the product of a long and arduous quest to understand the evolution of
We are in the process of writing better documentation, blog posts, tutorials and a formal specification. Today you can find:
- [libp2p.io](https://libp2p.io)
- [docs.libp2p.io](https://docs.libp2p.io)
- [Specification (WIP)](https://github.com/libp2p/specs)
- [Discussion Forums](https://discuss.libp2p.io)
- Talks
- [`libp2p <3 ethereum` at DEVCON2](https://ethereumfoundation.org/devcon/?session=libp2p) [📼 video](https://www.youtube.com/watch?v=HxueJbeMVG4) [slides](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p-HEART-devp2p-IPFS-PLUS-Ethereum-networking.pdf) [📼 demo-1](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p_demo1-1.mp4) [📼 demo-2](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p_demo2-1.mp4)
- Articles
@@ -157,6 +160,7 @@ class Node extends libp2p {
// libp2p config options (typically found on a config.json)
config: { // The config object is the part of the config that can go into a file, config.json.
peerDiscovery: {
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minPeers)
mdns: { // mdns options
interval: 1000, // ms
enabled: true
@@ -177,7 +181,11 @@ class Node extends libp2p {
dht: {
kBucketSize: 20,
enabled: true,
enabledDiscovery: true // Allows to disable discovery (enabled by default)
randomWalk: {
enabled: true, // Allows to disable discovery (enabled by default)
interval: 300e3,
timeout: 10e3
}
},
// Enable/Disable Experimental features
EXPERIMENTAL: { // Experimental features ("behind a flag")
@@ -302,17 +310,32 @@ Required keys in the `options` object:
> Peer has been discovered.
If `autoDial` is `true`, applications should **not** attempt to connect to the peer
unless they are performing a specific action. See [peer discovery and auto dial](./PEER_DISCOVERY.md) for more information.
- `peer`: instance of [PeerInfo][]
##### `libp2p.on('peer:connect', (peer) => {})`
> We connected to a new peer
> We have a new muxed connection to a peer
- `peer`: instance of [PeerInfo][]
##### `libp2p.on('peer:disconnect', (peer) => {})`
> We disconnected from Peer
> We have closed a connection to a peer
- `peer`: instance of [PeerInfo][]
##### `libp2p.on('connection:start', (peer) => {})`
> We created a new connection to a peer
- `peer`: instance of [PeerInfo][]
##### `libp2p.on('connection:end', (peer) => {})`
> We closed a connection to a peer
- `peer`: instance of [PeerInfo][]
@@ -506,18 +529,6 @@ Some available network protectors:
> npm run test:browser
```
#### Run interop tests
```sh
N/A
```
#### Run benchmark tests
```sh
N/A
```
### Packages
List of packages currently in existence for libp2p

View File

@@ -33,7 +33,7 @@
- [ ] Twitter
- [ ] IRC
- [ ] Reddit
- [ ] [discuss.ipfs.io](https://discuss.ipfs.io/c/announcements)
- [ ] [discuss.libp2p.io](https://discuss.libp2p.io/c/news)
- [ ] Blog post
- [ ] Copy release notes to the [GitHub Release description](https://github.com/libp2p/js-libp2p/releases)
@@ -47,7 +47,7 @@ In alphabetical order, here are all the humans that contributed to the release:
Would you like to contribute to the libp2p project and don't know how? Well, there are a few places you can get started:
- Check the issues with the `help wanted` label at the Ready column in our waffle board - https://waffle.io/libp2p/js-libp2p?label=help%20wanted
- Check the issues with the `help wanted` label in the [libp2p repo](https://github.com/libp2p/js-libp2p/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
- Join an IPFS All Hands, introduce yourself and let us know where you would like to contribute - https://github.com/ipfs/team-mgmt#all-hands-call
- Hack with IPFS and show us what you made! The All Hands call is also the perfect venue for demos, join in and show us what you built
- Join the discussion at http://discuss.ipfs.io/ and help users finding their answers.
@@ -55,4 +55,4 @@ Would you like to contribute to the libp2p project and don't know how? Well, the
# ⁉️ Do you have questions?
The best place to ask your questions about libp2p, how it works and what you can do with it is at [discuss.ipfs.io](http://discuss.ipfs.io). We are also available at the #libp2p channel on Freenode.
The best place to ask your questions about libp2p, how it works and what you can do with it is at [discuss.libp2p.io](https://discuss.libp2p.io). We are also available at the #libp2p channel on Freenode.

View File

@@ -3,8 +3,8 @@
This example creates a simple chat app in your terminal.
## Setup
1. Install the modules, `npm install`.
2. Open 2 terminal windows in the `./src` directory.
1. Install the modules in the libp2p root directory, `npm install`.
2. Open 2 terminal windows in the `./examples/chat/src` directory.
## Running
1. Run the listener in window 1, `node listener.js`

View File

@@ -3,21 +3,27 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"ipfs": "~0.32.2",
"libp2p": "../../",
"ipfs": "~0.34.4",
"libp2p": "github:libp2p/js-libp2p#master",
"libp2p-delegated-content-routing": "~0.2.2",
"libp2p-delegated-peer-routing": "~0.2.2",
"libp2p-kad-dht": "~0.10.4",
"libp2p-mplex": "~0.8.0",
"libp2p-secio": "~0.10.0",
"libp2p-webrtc-star": "~0.15.5",
"libp2p-websocket-star": "~0.8.1",
"libp2p-websockets": "~0.12.0",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-scripts": "1.1.5"
"libp2p-kad-dht": "~0.14.12",
"libp2p-mplex": "~0.8.5",
"libp2p-secio": "~0.11.1",
"libp2p-webrtc-star": "~0.15.8",
"libp2p-websocket-star": "~0.10.2",
"libp2p-websockets": "~0.12.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "2.1.8"
},
"scripts": {
"start": "react-scripts start"
}
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}

View File

@@ -1,11 +1,10 @@
// eslint-disable-next-line
'use strict'
const React = require('react')
import React from 'react'
import Ipfs from 'ipfs'
import libp2pBundle from './libp2p-bundle'
const Component = React.Component
const Ipfs = require('ipfs')
const libp2pBundle = require('./libp2p-bundle')
// require('./App.css')
const BootstrapNode = '/ip4/127.0.0.1/tcp/8081/ws/p2p/QmdoG8DpzYUZMVP5dGmgmigZwR1RE8Cf6SxMPg1SBXJAQ8'
@@ -150,4 +149,4 @@ class App extends Component {
}
}
module.exports = App
export default App

View File

@@ -1,9 +1,8 @@
// eslint-disable-next-line
'use strict'
const React = require('react') // eslint-disable-line no-unused-vars
const ReactDOM = require('react-dom')
const App = require('./App') // eslint-disable-line no-unused-vars
// require('index.css')
import React from 'react' // eslint-disable-line no-unused-vars
import ReactDOM from 'react-dom'
import App from './App' // eslint-disable-line no-unused-vars
ReactDOM.render(<App />, document.getElementById('root'))

View File

@@ -11,7 +11,7 @@ const KadDHT = require('libp2p-kad-dht')
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
const DelegatedContentRouter = require('libp2p-delegated-content-routing')
module.exports = ({peerInfo, peerBook}) => {
export default function Libp2pBundle ({peerInfo, peerBook}) {
const wrtcstar = new WebRTCStar({id: peerInfo.id})
const wsstar = new WebSocketStar({id: peerInfo.id})
const delegatedApiOptions = {
@@ -54,6 +54,7 @@ module.exports = ({peerInfo, peerBook}) => {
},
config: {
peerDiscovery: {
autoDial: false,
webrtcStar: {
enabled: false
},
@@ -62,16 +63,13 @@ module.exports = ({peerInfo, peerBook}) => {
}
},
dht: {
kBucketSize: 20
enabled: false
},
relay: {
enabled: true,
hop: {
enabled: false
}
},
EXPERIMENTAL: {
dht: false
}
}
})

View File

@@ -34,8 +34,9 @@ class MyBundle extends libp2p {
},
config: {
peerDiscovery: {
autoDial: true,
bootstrap: {
interval: 2000,
interval: 20e3,
enabled: true,
list: bootstrapers
}
@@ -62,8 +63,8 @@ waterfall([
if (err) { throw err }
node.on('peer:discovery', (peer) => {
// No need to dial, autoDial is on
console.log('Discovered:', peer.id.toB58String())
node.dial(peer, () => {})
})
node.on('peer:connect', (peer) => {

View File

@@ -23,7 +23,7 @@ class MyBundle extends libp2p {
config: {
peerDiscovery: {
mdns: {
interval: 1000,
interval: 20e3,
enabled: true
}
}

View File

@@ -3,7 +3,7 @@
This example performs a simple echo from the listener to the dialer.
## Setup
1. Install the modules, `npm install`.
1. Install the modules from libp2p root, `npm install`.
2. Open 2 terminal windows in the `./src` directory.
## Running

View File

@@ -17,13 +17,13 @@
},
"dependencies": {
"detect-dom-ready": "^1.0.2",
"libp2p-bootstrap": "~0.9.3",
"libp2p-mplex": "~0.8.0",
"libp2p-secio": "~0.10.0",
"libp2p-spdy": "~0.12.1",
"libp2p-webrtc-star": "~0.15.3",
"libp2p-websocket-star": "~0.8.1",
"libp2p-websockets": "~0.12.0",
"libp2p-bootstrap": "~0.9.7",
"libp2p-mplex": "~0.8.5",
"libp2p-secio": "~0.11.1",
"libp2p-spdy": "~0.13.3",
"libp2p-webrtc-star": "~0.15.8",
"libp2p-websocket-star": "~0.10.2",
"libp2p-websockets": "~0.12.2",
"peer-info": "~0.15.1"
}
}

View File

@@ -7,6 +7,7 @@ const Mplex = require('libp2p-mplex')
const SPDY = require('libp2p-spdy')
const SECIO = require('libp2p-secio')
const Bootstrap = require('libp2p-bootstrap')
const DHT = require('libp2p-kad-dht')
const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../../../../')
@@ -47,10 +48,12 @@ class Node extends libp2p {
wrtcStar.discovery,
wsstar.discovery,
Bootstrap
]
],
dht: DHT
},
config: {
peerDiscovery: {
autoDial: true,
webRTCStar: {
enabled: true
},
@@ -58,7 +61,7 @@ class Node extends libp2p {
enabled: true
},
bootstrap: {
interval: 10000,
interval: 20e3,
enabled: true,
list: bootstrapList
}
@@ -66,16 +69,19 @@ class Node extends libp2p {
relay: {
enabled: true,
hop: {
enabled: true,
enabled: false,
active: false
}
},
dht: {
enabled: false
},
EXPERIMENTAL: {
dht: false,
pubsub: false
}
},
connectionManager: {
minPeers: 10,
maxPeers: 50
}
}

View File

@@ -14,26 +14,8 @@ domReady(() => {
return console.log('Could not create the Node, check if your browser has WebRTC Support', err)
}
let connections = {}
node.on('peer:discovery', (peerInfo) => {
const idStr = peerInfo.id.toB58String()
if (connections[idStr]) {
// If we're already trying to connect to this peer, dont dial again
return
}
console.log('Discovered a peer:', idStr)
connections[idStr] = true
node.dial(peerInfo, (err, conn) => {
if (err) {
// Prevent immediate connection retries from happening
// and include a 10s jitter
const timeToNextDial = 25 * 1000 + (Math.random(0) * 10000).toFixed(0)
console.log('Failed to dial:', idStr)
setTimeout(() => delete connections[idStr], timeToNextDial)
}
})
console.log('Discovered a peer:', peerInfo.id.toB58String())
})
node.on('peer:connect', (peerInfo) => {
@@ -47,15 +29,13 @@ domReady(() => {
node.on('peer:disconnect', (peerInfo) => {
const idStr = peerInfo.id.toB58String()
delete connections[idStr]
console.log('Lost connection to: ' + idStr)
const el = document.getElementById(idStr)
el && el.remove()
})
node.start((err) => {
if (err) {
return console.log('WebRTC not supported')
return console.log(err)
}
const idStr = node.peerInfo.id.toB58String()

View File

@@ -23,10 +23,8 @@ class MyBundle extends libp2p {
},
config: {
dht: {
enabled: true,
kBucketSize: 20
},
EXPERIMENTAL: {
dht: true
}
}
}

View File

@@ -24,10 +24,8 @@ class MyBundle extends libp2p {
},
config: {
dht: {
enabled: true,
kBucketSize: 20
},
EXPERIMENTAL: {
dht: true
}
}
}

View File

@@ -25,11 +25,9 @@ class MyBundle extends libp2p {
},
config: {
dht: {
kBucketSize: 20
},
EXPERIMENTAL: {
// dht must be enabled
dht: true
enabled: true,
kBucketSize: 20
}
}
}

View File

@@ -62,11 +62,8 @@ parallel([
const node1 = nodes[0]
const node2 = nodes[1]
series([
(cb) => node1.once('peer:discovery', (peer) => node1.dial(peer, cb)),
(cb) => setTimeout(cb, 500)
], (err) => {
if (err) { throw err }
node1.once('peer:connect', (peer) => {
console.log('connected to %s', peer.id.toB58String())
// Subscribe to the topic 'news'
node1.pubsub.subscribe('news',

View File

@@ -12,14 +12,11 @@ We've seen many interesting use cases appear with this, here are some highlights
For this example, we will use MulticastDNS for automatic Peer Discovery. This example is based the previous examples found in [Discovery Mechanisms](../discovery-mechanisms). You can find the complete version at [1.js](./1.js).
Using PubSub is super simple, all you have to do is start a libp2p node, PubSub will be enabled by default.
Using PubSub is super simple, all you have to do is start a libp2p node with `EXPERIMENTAL.pubsub` set to true.
```JavaScript
series([
(cb) => node1.once('peer:discovery', (peer) => node1.dial(peer, cb)),
(cb) => setTimeout(cb, 500)
], (err) => {
if (err) { throw err }
node1.once('peer:connect', (peer) => {
console.log('connected to %s', peer.id.toB58String())
// Subscribe to the topic 'news'
node1.pubsub.subscribe('news',
@@ -42,6 +39,7 @@ The output of the program should look like:
```
> node 1.js
connected to QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!

View File

@@ -1,112 +0,0 @@
'use strict'
const createNode = require('../test/utils/create-node')
const introspection = async () => {
let libp2pNode
// Start node
await new Promise((resolve, reject) => {
createNode('/ip4/0.0.0.0/tcp/0', {
config: {
peerDiscovery: {
bootstrap: {
interval: 100,
enabled: true,
list: [
'/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
'/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
'/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
'/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx',
'/ip6/2604:a880:1:20::1f9:9001/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
'/ip6/2604:a880:1:20::203:d001/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
'/ip6/2604:a880:0:1010::23:d001/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
'/ip6/2400:6180:0:d0::151:6001/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
'/ip6/2604:a880:800:10::4a:5001/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
'/ip6/2a03:b0c0:0:1010::23:1001/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
'/ip6/2a03:b0c0:1:d0::e7:1/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
'/ip6/2604:a880:1:20::1d9:6001/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx',
'/dns4/node0.preload.ipfs.io/tcp/443/wss/ipfs/QmZMxNdpMkewiVZLMRxaNxUeZpDUb34pWjZ1kZvsd16Zic',
'/dns4/node1.preload.ipfs.io/tcp/443/wss/ipfs/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6'
]
},
mdns: {
interval: 100,
enabled: true
}
}
}
}, (err, node) => {
if (err) {
reject(err)
}
libp2pNode = node
libp2pNode.start((err) => {
if (err) {
reject(err)
} else {
resolve()
}
})
})
})
libp2pNode.on('peer:discovery', (peerInfo) => {
const noop = () => { }
peerInfo = libp2pNode.peerBook.put(peerInfo)
if (!peerInfo.isConnected()) {
libp2pNode.dial(peerInfo, noop)
}
})
await new Promise(resolve => setTimeout(resolve, 15000))
// Intropsection
const data = libp2pNode.introspection.host()
// console.log('conns', data.subsystems.swarm.conns)
const conns = libp2pNode._switch.connection.connections
const connsPeers = Object.keys(conns)
connsPeers.forEach((key) => {
const conn = conns[key][0]
console.log(conn.muxer.multiplex._list.filter((m) => !!m))
})
const encodedData = libp2pNode.introspection.marshal(data)
const decodedData = libp2pNode.introspection.unmarshal(encodedData)
console.log('dec', decodedData)
// Stop node
await new Promise((resolve, reject) => {
libp2pNode.stop((err) => {
if (err) {
reject(err)
} else {
resolve()
}
})
})
console.log('all done')
process.exit()
}
introspection()
/*
TODO:
- WebSockets transports with empty listen_multiaddrs
*/

View File

@@ -1,6 +1,6 @@
{
"name": "libp2p",
"version": "0.25.0-rc.5",
"version": "0.25.2",
"description": "JavaScript base class for libp2p bundles",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js",
@@ -42,11 +42,10 @@
"debug": "^4.1.1",
"err-code": "^1.1.2",
"fsm-event": "^2.1.0",
"libp2p-connection-manager": "~0.0.2",
"libp2p-introspection": "vasco-santos/js-libp2p-introspection#feat/instrumentation",
"libp2p-connection-manager": "~0.1.0",
"libp2p-floodsub": "~0.15.8",
"libp2p-ping": "~0.8.5",
"libp2p-switch": "libp2p/js-libp2p-switch#feat/instrumentation",
"libp2p-switch": "~0.42.9",
"libp2p-websockets": "~0.12.2",
"mafmt": "^6.0.7",
"multiaddr": "^6.0.6",
@@ -80,7 +79,6 @@
"libp2p-websocket-star-rendezvous": "~0.3.0",
"lodash.times": "^4.3.2",
"nock": "^10.0.6",
"protons": "^1.0.1",
"pull-goodbye": "0.0.2",
"pull-mplex": "~0.1.2",
"pull-serializer": "~0.3.2",
@@ -128,6 +126,7 @@
"Vasco Santos <vasco.santos@ua.pt>",
"Vasco Santos <vasco.santos@moxy.studio>",
"Volker Mische <volker.mische@gmail.com>",
"Yusef Napora <yusef@napora.org>",
"Zane Starr <zcstarr@gmail.com>",
"ebinks <elizabethjbinks@gmail.com>",
"greenkeeperio-bot <support@greenkeeper.io>",

View File

@@ -12,58 +12,73 @@ const transport = s.union([
}),
'function'
])
const modulesSchema = s({
connEncryption: optional(list([s('object|function')])),
// this is hacky to simulate optional because interface doesnt work correctly with it
// change to optional when fixed upstream
connProtector: s.union(['undefined', s.interface({ protect: 'function' })]),
contentRouting: optional(list(['object'])),
dht: optional(s('null|function|object')),
peerDiscovery: optional(list([s('object|function')])),
peerRouting: optional(list(['object'])),
streamMuxer: optional(list([s('object|function')])),
transport: s.intersection([[transport], s.interface({
length (v) {
return v > 0 ? true : 'ERROR_EMPTY'
}
})])
})
const optionsSchema = s(
{
connectionManager: 'object?',
datastore: 'object?',
peerInfo: 'object',
peerBook: 'object?',
modules: s({
connEncryption: optional(list([s('object|function')])),
// this is hacky to simulate optional because interface doesnt work correctly with it
// change to optional when fixed upstream
connProtector: s.union(['undefined', s.interface({ protect: 'function' })]),
contentRouting: optional(list(['object'])),
dht: optional(s('null|function|object')),
peerDiscovery: optional(list([s('object|function')])),
peerRouting: optional(list(['object'])),
streamMuxer: optional(list([s('object|function')])),
transport: s.intersection([[transport], s.interface({
length (v) {
return v > 0 ? true : 'ERROR_EMPTY'
}
})])
}),
config: s({
peerDiscovery: 'object?',
relay: s({
enabled: 'boolean',
hop: optional(s({
enabled: 'boolean',
active: 'boolean'
},
{ enabled: false, active: false }))
}, { enabled: true, hop: {} }),
dht: s({
kBucketSize: 'number',
enabled: 'boolean?',
randomWalk: optional(s({
enabled: 'boolean?', // disabled waiting for https://github.com/libp2p/js-libp2p-kad-dht/issues/86
queriesPerPeriod: 'number?',
interval: 'number?',
timeout: 'number?'
}, { enabled: false, queriesPerPeriod: 1, interval: 30000, timeout: 10000 })),
validators: 'object?',
selectors: 'object?'
}, { enabled: false, kBucketSize: 20, enabledDiscovery: false }),
EXPERIMENTAL: s({
pubsub: 'boolean'
}, { pubsub: false })
}, { relay: {}, dht: {}, EXPERIMENTAL: {} })
},
{ config: {}, modules: {} }
)
const configSchema = s({
peerDiscovery: s('object', {
autoDial: true
}),
relay: s({
enabled: 'boolean',
hop: optional(s({
enabled: 'boolean',
active: 'boolean'
}, {
// HOP defaults
enabled: false,
active: false
}))
}, {
// Relay defaults
enabled: true
}),
// DHT config
dht: s('object?', {
// DHT defaults
enabled: false,
kBucketSize: 20,
randomWalk: {
enabled: false, // disabled waiting for https://github.com/libp2p/js-libp2p-kad-dht/issues/86
queriesPerPeriod: 1,
interval: 300e3,
timeout: 10e3
}
}),
// Experimental config
EXPERIMENTAL: s({
pubsub: 'boolean'
}, {
// Experimental defaults
pubsub: false
})
}, {})
const optionsSchema = s({
switch: 'object?',
connectionManager: s('object', {
minPeers: 25
}),
datastore: 'object?',
peerInfo: 'object',
peerBook: 'object?',
modules: modulesSchema,
config: configSchema
})
module.exports.validate = (opts) => {
const [error, options] = optionsSchema.validate(opts)
@@ -78,5 +93,9 @@ module.exports.validate = (opts) => {
}
}
if (options.config.peerDiscovery.autoDial === undefined) {
options.config.peerDiscovery.autoDial = true
}
return options
}

View File

@@ -7,5 +7,7 @@ exports.messages = {
exports.codes = {
DHT_DISABLED: 'ERR_DHT_DISABLED',
PUBSUB_NOT_STARTED: 'ERR_PUBSUB_NOT_STARTED'
PUBSUB_NOT_STARTED: 'ERR_PUBSUB_NOT_STARTED',
ERR_NODE_NOT_STARTED: 'ERR_NODE_NOT_STARTED',
ERR_DISCOVERED_SELF: 'ERR_DISCOVERED_SELF'
}

View File

@@ -22,14 +22,14 @@ const peerRouting = require('./peer-routing')
const contentRouting = require('./content-routing')
const dht = require('./dht')
const pubsub = require('./pubsub')
const introspection = require('libp2p-introspection')
const getPeerInfo = require('./get-peer-info')
const validateConfig = require('./config').validate
const { codes } = require('./errors')
const notStarted = (action, state) => {
return errCode(
new Error(`libp2p cannot ${action} when not started; state is ${state}`),
'ERR_NODE_NOT_STARTED'
codes.ERR_NODE_NOT_STARTED
)
}
@@ -46,24 +46,23 @@ class Node extends EventEmitter {
super()
// validateConfig will ensure the config is correct,
// and add default values where appropriate
_options = validateConfig(_options)
this._options = validateConfig(_options)
this.datastore = _options.datastore
this.peerInfo = _options.peerInfo
this.peerBook = _options.peerBook || new PeerBook()
this.introspection = introspection(this)
this.datastore = this._options.datastore
this.peerInfo = this._options.peerInfo
this.peerBook = this._options.peerBook || new PeerBook()
this._modules = _options.modules
this._config = _options.config
this._modules = this._options.modules
this._config = this._options.config
this._transport = [] // Transport instances/references
this._discovery = [] // Discovery service instances/references
// create the switch, and listen for errors
this._switch = new Switch(this.peerInfo, this.peerBook, _options.switch)
this._switch = new Switch(this.peerInfo, this.peerBook, this._options.switch)
this._switch.on('error', (...args) => this.emit('error', ...args))
this.stats = this._switch.stats
this.connectionManager = new ConnectionManager(this, _options.connectionManager)
this.connectionManager = new ConnectionManager(this, this._options.connectionManager)
// Attach stream multiplexers
if (this._modules.streamMuxer) {
@@ -80,7 +79,6 @@ class Node extends EventEmitter {
// reuse this muxed connection
this._switch.on('peer-mux-established', (peerInfo) => {
this.emit('peer:connect', peerInfo)
this.peerBook.put(peerInfo)
})
this._switch.on('peer-mux-closed', (peerInfo) => {
@@ -88,6 +86,14 @@ class Node extends EventEmitter {
})
}
// Events for anytime connections are created/removed
this._switch.on('connection:start', (peerInfo) => {
this.emit('connection:start', peerInfo)
})
this._switch.on('connection:end', (peerInfo) => {
this.emit('connection:end', peerInfo)
})
// Attach crypto channels
if (this._modules.connEncryption) {
let cryptos = this._modules.connEncryption
@@ -168,6 +174,16 @@ class Node extends EventEmitter {
log.error(err)
this.emit('error', err)
})
// Once we start, emit and dial any peers we may have already discovered
this.state.on('STARTED', () => {
this.peerBook.getAllArray().forEach((peerInfo) => {
this.emit('peer:discovery', peerInfo)
this._maybeConnect(peerInfo)
})
})
this._peerDiscovered = this._peerDiscovered.bind(this)
}
/**
@@ -246,11 +262,7 @@ class Node extends EventEmitter {
this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { return callback(err) }
this._switch.dial(peerInfo, protocol, (err, conn) => {
if (err) { return callback(err) }
this.peerBook.put(peerInfo)
callback(null, conn)
})
this._switch.dial(peerInfo, protocol, callback)
})
}
@@ -276,12 +288,7 @@ class Node extends EventEmitter {
this._getPeerInfo(peer, (err, peerInfo) => {
if (err) { return callback(err) }
this._switch.dialFSM(peerInfo, protocol, (err, connFSM) => {
if (!err) {
this.peerBook.put(peerInfo)
}
callback(err, connFSM)
})
this._switch.dialFSM(peerInfo, protocol, callback)
})
}
@@ -364,47 +371,21 @@ class Node extends EventEmitter {
this._switch.transport.add(ws.tag || ws.constructor.name, ws)
}
// all transports need to be setup before discover starts
if (this._modules.peerDiscovery) {
each(this._modules.peerDiscovery, (D, _cb) => {
let config = {}
// detect which multiaddrs we don't have a transport for and remove them
const multiaddrs = this.peerInfo.multiaddrs.toArray()
if (D.tag &&
this._config.peerDiscovery &&
this._config.peerDiscovery[D.tag]) {
config = this._config.peerDiscovery[D.tag]
}
// If not configured to be enabled/disabled then enable by default
const enabled = config.enabled == null ? true : config.enabled
// If enabled then start it
if (enabled) {
let d
if (typeof D === 'function') {
d = new D(Object.assign({}, config, { peerInfo: this.peerInfo }))
} else {
d = D
}
d.on('peer', (peerInfo) => {
this.emit('peer:discovery', peerInfo)
})
this._discovery.push(d)
d.start(_cb)
} else {
_cb()
}
}, cb)
} else {
cb()
}
multiaddrs.forEach((multiaddr) => {
if (!multiaddr.toString().match(/\/p2p-circuit($|\/)/) &&
!this._transport.find((transport) => transport.filter(multiaddr).length > 0)) {
this.peerInfo.multiaddrs.delete(multiaddr)
}
})
cb()
},
(cb) => {
if (this._dht) {
this._dht.start(() => {
this._dht.on('peer', (peerInfo) => this.emit('peer:discovery', peerInfo))
this._dht.on('peer', this._peerDiscovered)
cb()
})
} else {
@@ -417,17 +398,13 @@ class Node extends EventEmitter {
}
cb()
},
// Peer Discovery
(cb) => {
// detect which multiaddrs we don't have a transport for and remove them
const multiaddrs = this.peerInfo.multiaddrs.toArray()
multiaddrs.forEach((multiaddr) => {
if (!multiaddr.toString().match(/\/p2p-circuit($|\/)/) &&
!this._transport.find((transport) => transport.filter(multiaddr).length > 0)) {
this.peerInfo.multiaddrs.delete(multiaddr)
}
})
cb()
if (this._modules.peerDiscovery) {
this._setupPeerDiscovery(cb)
} else {
cb()
}
}
], (err) => {
if (err) {
@@ -442,16 +419,17 @@ class Node extends EventEmitter {
_onStopping () {
series([
(cb) => {
if (this._modules.peerDiscovery) {
// stop all discoveries before continuing with shutdown
return parallel(
this._discovery.map((d) => {
return (_cb) => d.stop(() => { _cb() })
}),
cb
)
}
cb()
// stop all discoveries before continuing with shutdown
parallel(
this._discovery.map((d) => {
d.removeListener('peer', this._peerDiscovered)
return (_cb) => d.stop((err) => {
log.error('an error occurred stopping the discovery service', err)
_cb()
})
}),
cb
)
},
(cb) => {
if (this._floodSub) {
@@ -461,6 +439,7 @@ class Node extends EventEmitter {
},
(cb) => {
if (this._dht) {
this._dht.removeListener('peer', this._peerDiscovered)
return this._dht.stop(cb)
}
cb()
@@ -482,6 +461,90 @@ class Node extends EventEmitter {
this.state('done')
})
}
/**
* Handles discovered peers. Each discovered peer will be emitted via
* the `peer:discovery` event. If auto dial is enabled for libp2p
* and the current connection count is under the low watermark, the
* peer will be dialed.
*
* TODO: If `peerBook.put` becomes centralized, https://github.com/libp2p/js-libp2p/issues/345,
* it would be ideal if only new peers were emitted. Currently, with
* other modules adding peers to the `PeerBook` we have no way of knowing
* if a peer is new or not, so it has to be emitted.
*
* @private
* @param {PeerInfo} peerInfo
*/
_peerDiscovered (peerInfo) {
if (peerInfo.id.toB58String() === this.peerInfo.id.toB58String()) {
log.error(new Error(codes.ERR_DISCOVERED_SELF))
return
}
peerInfo = this.peerBook.put(peerInfo)
if (!this.isStarted()) return
this.emit('peer:discovery', peerInfo)
this._maybeConnect(peerInfo)
}
/**
* Will dial to the given `peerInfo` if the current number of
* connected peers is less than the configured `ConnectionManager`
* minPeers.
* @private
* @param {PeerInfo} peerInfo
*/
_maybeConnect (peerInfo) {
// If auto dialing is on, check if we should dial
if (this._config.peerDiscovery.autoDial === true && !peerInfo.isConnected()) {
const minPeers = this._options.connectionManager.minPeers || 0
if (minPeers > Object.keys(this._switch.connection.connections).length) {
log('connecting to discovered peer')
this._switch.dialer.connect(peerInfo, (err) => {
err && log.error('could not connect to discovered peer', err)
})
}
}
}
/**
* Initializes and starts peer discovery services
*
* @private
* @param {function(Error)} callback
*/
_setupPeerDiscovery (callback) {
for (const DiscoveryService of this._modules.peerDiscovery) {
let config = {
enabled: true // on by default
}
if (DiscoveryService.tag &&
this._config.peerDiscovery &&
this._config.peerDiscovery[DiscoveryService.tag]) {
config = { ...config, ...this._config.peerDiscovery[DiscoveryService.tag] }
}
if (config.enabled) {
let discoveryService
if (typeof DiscoveryService === 'function') {
discoveryService = new DiscoveryService(Object.assign({}, config, { peerInfo: this.peerInfo }))
} else {
discoveryService = DiscoveryService
}
discoveryService.on('peer', this._peerDiscovered)
this._discovery.push(discoveryService)
}
}
each(this._discovery, (d, cb) => {
d.start(cb)
}, callback)
}
}
module.exports = Node

View File

@@ -114,10 +114,9 @@ describe('circuit relay', () => {
nodeWS2 = node
cb()
}),
// set up node with TCP and listening on relay1
// set up node with TCP
(cb) => setupNode([
'/ip4/0.0.0.0/tcp/0',
`/p2p/${relayNode1.peerInfo.id.toB58String()}/p2p-circuit`
'/ip4/0.0.0.0/tcp/0'
], {
config: {
relay: {
@@ -128,10 +127,9 @@ describe('circuit relay', () => {
nodeTCP1 = node
cb()
}),
// set up node with TCP and listening on relay2 over TCP transport
// set up node with TCP
(cb) => setupNode([
'/ip4/0.0.0.0/tcp/0',
`/ip4/0.0.0.0/tcp/0/p2p/${relayNode2.peerInfo.id.toB58String()}/p2p-circuit`
'/ip4/0.0.0.0/tcp/0'
], {
config: {
relay: {

View File

@@ -58,6 +58,56 @@ describe('configuration', () => {
}).to.throw('ERROR_EMPTY')
})
it('should add defaults to config', () => {
const options = {
peerInfo,
modules: {
transport: [ WS ],
peerDiscovery: [ Bootstrap ],
dht: DHT
}
}
const expected = {
peerInfo,
connectionManager: {
minPeers: 25
},
modules: {
transport: [ WS ],
peerDiscovery: [ Bootstrap ],
dht: DHT
},
config: {
peerDiscovery: {
autoDial: true
},
EXPERIMENTAL: {
pubsub: false
},
dht: {
kBucketSize: 20,
enabled: false,
randomWalk: {
enabled: false,
queriesPerPeriod: 1,
interval: 300000,
timeout: 10000
}
},
relay: {
enabled: true,
hop: {
active: false,
enabled: false
}
}
}
}
expect(validateConfig(options)).to.deep.equal(expected)
})
it('should add defaults to missing items', () => {
const options = {
peerInfo,
@@ -78,6 +128,9 @@ describe('configuration', () => {
const expected = {
peerInfo,
connectionManager: {
minPeers: 25
},
modules: {
transport: [ WS ],
peerDiscovery: [ Bootstrap ],
@@ -85,6 +138,7 @@ describe('configuration', () => {
},
config: {
peerDiscovery: {
autoDial: true,
bootstrap: {
interval: 1000,
enabled: true
@@ -99,7 +153,7 @@ describe('configuration', () => {
randomWalk: {
enabled: false,
queriesPerPeriod: 1,
interval: 30000,
interval: 300000,
timeout: 10000
}
},
@@ -116,6 +170,33 @@ describe('configuration', () => {
expect(validateConfig(options)).to.deep.equal(expected)
})
it('should allow for configuring the switch', () => {
const options = {
peerInfo,
switch: {
blacklistTTL: 60e3,
blackListAttempts: 5,
maxParallelDials: 100,
maxColdCalls: 50,
dialTimeout: 30e3
},
modules: {
transport: [ WS ],
peerDiscovery: [ ]
}
}
expect(validateConfig(options)).to.deep.include({
switch: {
blacklistTTL: 60e3,
blackListAttempts: 5,
maxParallelDials: 100,
maxColdCalls: 50,
dialTimeout: 30e3
}
})
})
it('should allow for delegated content and peer routing', () => {
const peerRouter = new DelegatedPeerRouter()
const contentRouter = new DelegatedContentRouter(peerInfo)
@@ -161,7 +242,7 @@ describe('configuration', () => {
expect(() => validateConfig(options)).to.throw()
})
it('should add defaults, validators and selectors for dht', () => {
it('should be able to add validators and selectors for dht', () => {
const selectors = {}
const validators = {}
@@ -180,6 +261,9 @@ describe('configuration', () => {
}
const expected = {
peerInfo,
connectionManager: {
minPeers: 25
},
modules: {
transport: [WS],
dht: DHT
@@ -188,6 +272,9 @@ describe('configuration', () => {
EXPERIMENTAL: {
pubsub: false
},
peerDiscovery: {
autoDial: true
},
relay: {
enabled: true,
hop: {
@@ -196,14 +283,6 @@ describe('configuration', () => {
}
},
dht: {
kBucketSize: 20,
enabled: false,
randomWalk: {
enabled: false,
queriesPerPeriod: 1,
interval: 30000,
timeout: 10000
},
selectors,
validators
}
@@ -211,4 +290,43 @@ describe('configuration', () => {
}
expect(validateConfig(options)).to.deep.equal(expected)
})
it('should support new properties for the dht config', () => {
const options = {
peerInfo,
modules: {
transport: [WS],
dht: DHT
},
config: {
dht: {
kBucketSize: 20,
enabled: false,
myNewDHTConfigProperty: true,
randomWalk: {
enabled: false,
queriesPerPeriod: 1,
interval: 300000,
timeout: 10000
}
}
}
}
const expected = {
kBucketSize: 20,
enabled: false,
myNewDHTConfigProperty: true,
randomWalk: {
enabled: false,
queriesPerPeriod: 1,
interval: 300000,
timeout: 10000
}
}
const actual = validateConfig(options).config.dht
expect(actual).to.deep.equal(expected)
})
})

View File

@@ -9,15 +9,13 @@ const signalling = require('libp2p-webrtc-star/src/sig-server')
const parallel = require('async/parallel')
const crypto = require('crypto')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const createNode = require('./utils/create-node')
const echo = require('./utils/echo')
describe('peer discovery', () => {
let nodeA
let nodeB
let nodeC
let port = 24642
let ss
@@ -49,6 +47,15 @@ describe('peer discovery', () => {
nodeB = node
node.handle('/echo/1.0.0', echo)
node.start(cb)
}),
(cb) => createNode([
'/ip4/0.0.0.0/tcp/0',
`/ip4/127.0.0.1/tcp/${port}/ws/p2p-webrtc-star`
], options, (err, node) => {
expect(err).to.not.exist()
nodeC = node
node.handle('/echo/1.0.0', echo)
node.start(cb)
})
], done)
})
@@ -57,15 +64,21 @@ describe('peer discovery', () => {
parallel([
(cb) => nodeA.stop(cb),
(cb) => nodeB.stop(cb),
(cb) => nodeC.stop(cb),
(cb) => ss.stop(cb)
], done)
})
afterEach(() => {
sinon.restore()
})
}
describe('module registration', () => {
it('should enable by default a module passed as an object', (done) => {
const mockDiscovery = {
on: sinon.stub(),
removeListener: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0)
}
@@ -86,6 +99,7 @@ describe('peer discovery', () => {
it('should enable by default a module passed as a function', (done) => {
const mockDiscovery = {
on: sinon.stub(),
removeListener: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0)
}
@@ -108,6 +122,7 @@ describe('peer discovery', () => {
it('should enable module by configutation', (done) => {
const mockDiscovery = {
on: sinon.stub(),
removeListener: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0),
tag: 'mockDiscovery'
@@ -143,6 +158,7 @@ describe('peer discovery', () => {
it('should disable module by configutation', (done) => {
const mockDiscovery = {
on: sinon.stub(),
removeListener: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0),
tag: 'mockDiscovery'
@@ -178,6 +194,7 @@ describe('peer discovery', () => {
it('should register module passed as function', (done) => {
const mockDiscovery = {
on: sinon.stub(),
removeListener: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0)
}
@@ -215,6 +232,7 @@ describe('peer discovery', () => {
it('should register module passed as object', (done) => {
const mockDiscovery = {
on: sinon.stub(),
removeListener: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0),
tag: 'mockDiscovery'
@@ -241,6 +259,33 @@ describe('peer discovery', () => {
})
})
describe('discovery scenarios', () => {
setup({
config: {
dht: {
enabled: false
},
peerDiscovery: {
autoDial: false,
bootstrap: {
enabled: true,
list: []
}
}
}
})
it('should ignore self on discovery', function () {
const discoverySpy = sinon.spy()
nodeA.on('peer:discovery', discoverySpy)
nodeA._discovery[0].emit('peer', nodeA.peerInfo)
expect(discoverySpy.called).to.eql(false)
expect(nodeA.peerBook.getAllArray()).to.have.length(0)
expect()
})
})
describe('MulticastDNS', () => {
setup({
config: {
@@ -248,8 +293,10 @@ describe('peer discovery', () => {
enabled: false
},
peerDiscovery: {
autoDial: true,
mdns: {
enabled: true,
interval: 200, // discover quickly
// use a random tag to prevent CI collision
serviceTag: crypto.randomBytes(10).toString('hex')
}
@@ -257,13 +304,23 @@ describe('peer discovery', () => {
}
})
it('find a peer', function (done) {
this.timeout(15 * 1000)
it('find peers', function (done) {
let expectedPeers = new Set([
nodeB.peerInfo.id.toB58String(),
nodeC.peerInfo.id.toB58String()
])
nodeA.once('peer:discovery', (peerInfo) => {
expect(nodeB.peerInfo.id.toB58String())
.to.eql(peerInfo.id.toB58String())
function finish () {
nodeA.removeAllListeners('peer:discovery')
expect(expectedPeers.size).to.eql(0)
done()
}
nodeA.on('peer:discovery', (peerInfo) => {
expectedPeers.delete(peerInfo.id.toB58String())
if (expectedPeers.size === 0) {
finish()
}
})
})
})
@@ -276,6 +333,7 @@ describe('peer discovery', () => {
enabled: false
},
peerDiscovery: {
autoDial: true,
webRTCStar: {
enabled: true
}
@@ -283,12 +341,24 @@ describe('peer discovery', () => {
}
})
it('find a peer', function (done) {
this.timeout(15 * 1000)
nodeA.once('peer:discovery', (peerInfo) => {
expect(nodeB.peerInfo.id.toB58String())
.to.eql(peerInfo.id.toB58String())
it('find peers', function (done) {
this.timeout(20e3)
let expectedPeers = new Set([
nodeB.peerInfo.id.toB58String(),
nodeC.peerInfo.id.toB58String()
])
function finish () {
nodeA.removeAllListeners('peer:discovery')
expect(expectedPeers.size).to.eql(0)
done()
}
nodeA.on('peer:discovery', (peerInfo) => {
expectedPeers.delete(peerInfo.id.toB58String())
if (expectedPeers.size === 0) {
finish()
}
})
})
})
@@ -300,8 +370,10 @@ describe('peer discovery', () => {
enabled: false
},
peerDiscovery: {
autoDial: true,
mdns: {
enabled: true,
interval: 200, // discovery quickly
// use a random tag to prevent CI collision
serviceTag: crypto.randomBytes(10).toString('hex')
},
@@ -312,12 +384,23 @@ describe('peer discovery', () => {
}
})
it('find a peer', function (done) {
this.timeout(15 * 1000)
nodeA.once('peer:discovery', (peerInfo) => {
expect(nodeB.peerInfo.id.toB58String())
.to.eql(peerInfo.id.toB58String())
it('find peers', function (done) {
let expectedPeers = new Set([
nodeB.peerInfo.id.toB58String(),
nodeC.peerInfo.id.toB58String()
])
function finish () {
nodeA.removeAllListeners('peer:discovery')
expect(expectedPeers.size).to.eql(0)
done()
}
nodeA.on('peer:discovery', (peerInfo) => {
expectedPeers.delete(peerInfo.id.toB58String())
if (expectedPeers.size === 0) {
finish()
}
})
})
})
@@ -326,32 +409,96 @@ describe('peer discovery', () => {
setup({
config: {
peerDiscovery: {
autoDial: true,
mdns: {
enabled: false
},
webRTCStar: {
enabled: false
}
},
dht: {
enabled: true,
kBucketSize: 20,
randomWalk: {
enabled: true,
queriesPerPeriod: 1,
interval: 200, // start the query sooner
timeout: 3000
}
}
}
})
it('find a peer', function (done) {
this.timeout(15 * 1000)
it('find peers through the dht', function (done) {
let expectedPeers = new Set([
nodeB.peerInfo.id.toB58String(),
nodeC.peerInfo.id.toB58String()
])
nodeA.once('peer:discovery', (peerInfo) => {
expect(nodeB.peerInfo.id.toB58String())
.to.eql(peerInfo.id.toB58String())
function finish () {
nodeA.removeAllListeners('peer:discovery')
expect(expectedPeers.size).to.eql(0)
done()
}
nodeA.on('peer:discovery', (peerInfo) => {
expectedPeers.delete(peerInfo.id.toB58String())
if (expectedPeers.size === 0) {
finish()
}
})
// connect two dhts
const publicPeerId = new PeerId(nodeB._dht.peerInfo.id.id, null, nodeB._dht.peerInfo.id.pubKey)
const target = new PeerInfo(publicPeerId)
target.multiaddrs = nodeB._dht.peerInfo.multiaddrs
nodeA._dht.switch.dial(target, (err) => {
// Topology:
// A -> B
// C -> B
nodeA.dial(nodeB.peerInfo, (err) => {
expect(err).to.not.exist()
})
nodeC.dial(nodeB.peerInfo, (err) => {
expect(err).to.not.exist()
})
})
})
describe('auto dial', () => {
setup({
connectionManager: {
minPeers: 1
},
config: {
peerDiscovery: {
autoDial: true,
mdns: {
enabled: false
},
webRTCStar: {
enabled: false
},
bootstrap: {
enabled: true,
list: []
}
},
dht: {
enabled: false
}
}
})
it('should only dial when the peer count is below the low watermark', (done) => {
const bootstrap = nodeA._discovery[0]
sinon.stub(nodeA._switch.dialer, 'connect').callsFake((peerInfo) => {
nodeA._switch.connection.connections[peerInfo.id.toB58String()] = []
})
bootstrap.emit('peer', nodeB.peerInfo)
bootstrap.emit('peer', nodeC.peerInfo)
// Only nodeB should get dialed
expect(nodeA._switch.dialer.connect.callCount).to.eql(1)
expect(nodeA._switch.dialer.connect.getCall(0).args[0]).to.eql(nodeB.peerInfo)
done()
})
})
})

View File

@@ -4,6 +4,7 @@
const chai = require('chai')
chai.use(require('dirty-chai'))
chai.use(require('chai-checkmark'))
const expect = chai.expect
const PeerInfo = require('peer-info')
const PeerId = require('peer-id')
@@ -413,29 +414,19 @@ describe('transports', () => {
it('node1 hangUp node2', (done) => {
node1.hangUp(peer2, (err) => {
expect(err).to.not.exist()
setTimeout(check, 500)
function check () {
const peers = node1.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1)
expect(node1._switch.connection.getAll()).to.have.length(0)
done()
}
const peers = node1.peerBook.getAll()
expect(Object.keys(peers)).to.have.length(1)
expect(node1._switch.connection.getAll()).to.have.length(0)
done()
})
})
it('create a third node and check that discovery works', function (done) {
this.timeout(10 * 1000)
let counter = 0
function check () {
if (++counter === 3) {
expect(node1._switch.connection.getAll()).to.have.length(1)
expect(node2._switch.connection.getAll()).to.have.length(1)
done()
}
}
const expectedPeers = [
node1.peerInfo.id.toB58String(),
node2.peerInfo.id.toB58String()
]
PeerId.create((err, id3) => {
expect(err).to.not.exist()
@@ -444,13 +435,18 @@ describe('transports', () => {
const ma3 = '/ip4/127.0.0.1/tcp/14444/ws/p2p-websocket-star/p2p/' + id3.toB58String()
peer3.multiaddrs.add(ma3)
node1.on('peer:discovery', (peerInfo) => node1.dial(peerInfo, check))
node2.on('peer:discovery', (peerInfo) => node2.dial(peerInfo, check))
// 2 connects and 1 start
expect(3).checks(done)
const node3 = new Node({
peerInfo: peer3
})
node3.start(check)
node3.on('peer:connect', (peerInfo) => {
expect(expectedPeers).to.include(peerInfo.id.toB58String()).mark()
})
node3.start((err) => {
expect(err).to.not.exist().mark()
})
})
})
})

View File

@@ -438,6 +438,7 @@ describe('transports', () => {
},
config: {
peerDiscovery: {
autoDial: false,
[wstar.discovery.tag]: {
enabled: true
}
@@ -452,7 +453,13 @@ describe('transports', () => {
},
(cb) => createNode([
'/ip4/0.0.0.0/tcp/0'
], (err, node) => {
], {
config: {
peerDiscovery: {
autoDial: false
}
}
}, (err, node) => {
expect(err).to.not.exist()
nodeTCP = node
node.handle('/echo/1.0.0', echo)
@@ -460,7 +467,13 @@ describe('transports', () => {
}),
(cb) => createNode([
'/ip4/127.0.0.1/tcp/25022/ws'
], (err, node) => {
], {
config: {
peerDiscovery: {
autoDial: false
}
}
}, (err, node) => {
expect(err).to.not.exist()
nodeWS = node
node.handle('/echo/1.0.0', echo)
@@ -479,6 +492,7 @@ describe('transports', () => {
},
config: {
peerDiscovery: {
autoDial: false,
[wstar.discovery.tag]: {
enabled: true
}

View File

@@ -61,6 +61,7 @@ class Node extends libp2p {
},
config: {
peerDiscovery: {
autoDial: true,
webRTCStar: {
enabled: true
},

View File

@@ -56,6 +56,7 @@ class Node extends libp2p {
},
config: {
peerDiscovery: {
autoDial: true,
mdns: {
interval: 10000,
enabled: false