Compare commits

...

31 Commits

Author SHA1 Message Date
Jacob Heun
7c2c852fc0 chore: release version v0.26.0-rc.1 2019-07-31 14:35:43 +02:00
Jacob Heun
e8d8aab278 chore: update contributors 2019-07-31 14:35:42 +02:00
Vasco Santos
dd48d268ec chore: promisify pubsub start and stop (#392) 2019-07-31 14:33:00 +02:00
Jacob Heun
99a53592e2 chore: release version v0.26.0-rc.0 2019-07-31 09:47:06 +02:00
Jacob Heun
2a2e7a1012 chore: update contributors 2019-07-31 09:47:06 +02:00
Vasco Santos
791f39a09b feat: integrate gossipsub by default (#365)
BREAKING CHANGE: new configuration for deciding the implementation of pubsub to be used.
In this context, the experimental flags were also removed. See the README for the latest usage.
2019-07-31 09:38:14 +02:00
Jacob Heun
65d52857a5 test(fix): correct findProviders test for missing provider (#391)
* test(fix): correct findProviders test for missing provider

* chore: fix lint
2019-07-30 15:11:24 +02:00
Jacob Heun
48b1b442e9 docs: libp2p in browser example (#390)
* docs: improve browser example

* docs: remove bad ipfs link
2019-07-30 12:38:02 +02:00
Jacob Heun
9554b05c6f fix: make subscribe comply with ipfs interface (#389)
BREAKING CHANGE: The ipfs interface specified that options
should be provided after the handler, not before.
https://github.com/ipfs/interface-js-ipfs-core/blob/v0.109.0/SPEC/PUBSUB.md#pubsubsubscribe

This corrects the order of parameters. See the jsdocs examples
for subscribe to see how it should be used.
2019-07-30 12:36:23 +02:00
Alex Potsides
df6ef45a2d feat: promisify all api methods that accept callbacks (#381)
* feat: promisify all api methods that accept callbacks

This is a stop-gap until the full async/await migration can be
completed.  It means we can refactor tests of other modules that
depend on this module without having to mix async flow control
strategies.

N.b. some methods that were previously callable without callbacks
(e.g. `node.start()`, `node.stop()`, etc) now require callbacks
otherwise a promise is returned which, if rejected, can cause
`unhandledPromiseRejection` events and lead to memory leaks.

* docs: add a global note to the api about promisify

* fix: update the logic for unsubscribe

* test(fix): correct pubsub unsubscribe usage for api change

* test(fix): update content routing tests for latest delegate version
2019-07-29 15:40:40 +02:00
Jacob Heun
b4a70ea476 chore: release version v0.25.5 2019-07-12 13:10:55 +01:00
Jacob Heun
45716da465 chore: update contributors 2019-07-12 13:10:53 +01:00
Vasco Santos
905c911946 fix: peer routing for delegate router (#377)
* fix: peer routing tests

* test: fix mock payload type

Provider results are type 4, not type 1: 6e566d10f4/routing/query.go (L15-L24)
2019-07-12 13:02:03 +01:00
Jacob Heun
10811e9ced chore: update keywords and description (#370)
* chore: update keywords and description

chore: reorganize package.json fields

* test: bump timeouts for peer generation
2019-06-12 14:18:34 +02:00
Jacob Heun
9c2789bc15 chore: release version v0.25.4 2019-06-07 17:10:07 +02:00
Jacob Heun
24be691bc1 chore: update contributors 2019-06-07 17:10:07 +02:00
Jacob Heun
9433c6c398 docs: add createLibp2p to readme (#368)
* chore: update deps
* test(fix): account for wrtcrendezvous now being thenable
2019-06-07 15:50:23 +02:00
Jacob Heun
04faf1806c feat: add createLibp2p to generate a PeerInfo instance (#367)
createLibp2p is a new exported helper function that allows users to create a libp2p instance without worrying about creating a PeerInfo instance first.
2019-06-06 12:21:31 +02:00
Guy Sviry
b06ca1b3c7 feat: pass libp2p as option to transport creation (#363) 2019-05-17 12:11:22 +02:00
Jacob Heun
bde30cac45 chore: remove commitlint from travis
Commit messages should be fixed on PR squash and merge
2019-05-17 10:31:16 +02:00
Jacob Heun
28c054c21e chore: release version v0.25.3 2019-05-07 13:49:03 +02:00
Jacob Heun
c346e8066b chore: update contributors 2019-05-07 13:49:02 +02:00
Jacob Heun
40978a1940 feat: sign pubsub messages (#362)
* fix: forward pubsub publish callback to floodsub

chore: update floodsub version

* test: add random walk delay to config

* chore: update floodsub
2019-05-07 13:45:59 +02:00
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
33 changed files with 731 additions and 350 deletions

View File

@@ -22,13 +22,12 @@ const before = (done) => {
sigServer.start({ sigServer.start({
port: WRTC_RENDEZVOUS_MULTIADDR.nodeAddress().port port: WRTC_RENDEZVOUS_MULTIADDR.nodeAddress().port
// cryptoChallenge: true TODO: needs https://github.com/libp2p/js-libp2p-webrtc-star/issues/128 // cryptoChallenge: true TODO: needs https://github.com/libp2p/js-libp2p-webrtc-star/issues/128
}, (err, server) => { })
if (err) { .then(server => {
return cb(err)
}
wrtcRendezvous = server wrtcRendezvous = server
cb() cb()
}) })
.catch(cb)
}, },
(cb) => { (cb) => {
WebSocketStarRendezvous.start({ WebSocketStarRendezvous.start({
@@ -72,14 +71,16 @@ const before = (done) => {
const after = (done) => { const after = (done) => {
setTimeout(() => setTimeout(() =>
parallel( parallel([
[node, wrtcRendezvous, wsRendezvous].map((s) => (cb) => s.stop(cb)), (cb) => wrtcRendezvous.stop().then(cb).catch(cb),
done), ...[node, wsRendezvous].map((s) => (cb) => s.stop(cb)),
2000) ], done),
2000
)
} }
module.exports = { module.exports = {
bundlesize: { maxSize: '218kB' }, bundlesize: { maxSize: '220kB' },
hooks: { hooks: {
pre: before, pre: before,
post: after post: after

View File

@@ -7,11 +7,12 @@ stages:
node_js: node_js:
- '10' - '10'
- '12'
os: os:
- linux - linux
- osx - osx
script: npx nyc -s npm run test:node -- --bail script: npx nyc -s npm run test:node -- --bail
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
@@ -20,7 +21,6 @@ jobs:
- stage: check - stage: check
script: script:
- npx aegir build --bundlesize - npx aegir build --bundlesize
- npx aegir commitlint --travis
- npx aegir dep-check -- -i wrtc -i electron-webrtc - npx aegir dep-check -- -i wrtc -i electron-webrtc
- npm run lint - npm run lint

View File

@@ -1,3 +1,87 @@
<a name="0.26.0-rc.1"></a>
# [0.26.0-rc.1](https://github.com/libp2p/js-libp2p/compare/v0.26.0-rc.0...v0.26.0-rc.1) (2019-07-31)
<a name="0.26.0-rc.0"></a>
# [0.26.0-rc.0](https://github.com/libp2p/js-libp2p/compare/v0.25.5...v0.26.0-rc.0) (2019-07-31)
### Bug Fixes
* make subscribe comply with ipfs interface ([#389](https://github.com/libp2p/js-libp2p/issues/389)) ([9554b05](https://github.com/libp2p/js-libp2p/commit/9554b05))
### Features
* integrate gossipsub by default ([#365](https://github.com/libp2p/js-libp2p/issues/365)) ([791f39a](https://github.com/libp2p/js-libp2p/commit/791f39a))
* promisify all api methods that accept callbacks ([#381](https://github.com/libp2p/js-libp2p/issues/381)) ([df6ef45](https://github.com/libp2p/js-libp2p/commit/df6ef45))
### BREAKING CHANGES
* new configuration for deciding the implementation of pubsub to be used.
In this context, the experimental flags were also removed. See the README for the latest usage.
* The ipfs interface specified that options
should be provided after the handler, not before.
https://github.com/ipfs/interface-js-ipfs-core/blob/v0.109.0/SPEC/PUBSUB.md#pubsubsubscribe
This corrects the order of parameters. See the jsdocs examples
for subscribe to see how it should be used.
<a name="0.25.5"></a>
## [0.25.5](https://github.com/libp2p/js-libp2p/compare/v0.25.4...v0.25.5) (2019-07-12)
### Bug Fixes
* peer routing for delegate router ([#377](https://github.com/libp2p/js-libp2p/issues/377)) ([905c911](https://github.com/libp2p/js-libp2p/commit/905c911)), closes [/github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L15-L24](https://github.com//github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go/issues/L15-L24)
<a name="0.25.4"></a>
## [0.25.4](https://github.com/libp2p/js-libp2p/compare/v0.25.3...v0.25.4) (2019-06-07)
### Features
* add createLibp2p to generate a PeerInfo instance ([#367](https://github.com/libp2p/js-libp2p/issues/367)) ([04faf18](https://github.com/libp2p/js-libp2p/commit/04faf18))
* pass libp2p as option to transport creation ([#363](https://github.com/libp2p/js-libp2p/issues/363)) ([b06ca1b](https://github.com/libp2p/js-libp2p/commit/b06ca1b))
<a name="0.25.3"></a>
## [0.25.3](https://github.com/libp2p/js-libp2p/compare/v0.25.2...v0.25.3) (2019-05-07)
### Features
* sign pubsub messages ([#362](https://github.com/libp2p/js-libp2p/issues/362)) ([40978a1](https://github.com/libp2p/js-libp2p/commit/40978a1))
<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> <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) # [0.25.0](https://github.com/libp2p/js-libp2p/compare/v0.25.0-rc.6...v0.25.0) (2019-04-12)

View File

@@ -111,7 +111,7 @@ The libp2p module acts as a glue for every libp2p module that you can use to cre
// crypto-channel: secio // crypto-channel: secio
// discovery: multicast-dns // discovery: multicast-dns
const libp2p = require('libp2p') const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets') const WS = require('libp2p-websockets')
const SPDY = require('libp2p-spdy') const SPDY = require('libp2p-spdy')
@@ -119,12 +119,13 @@ const MPLEX = require('libp2p-mplex')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
const MulticastDNS = require('libp2p-mdns') const MulticastDNS = require('libp2p-mdns')
const DHT = require('libp2p-kad-dht') const DHT = require('libp2p-kad-dht')
const GossipSub = require('libp2p-gossipsub')
const defaultsDeep = require('@nodeutils/defaults-deep') const defaultsDeep = require('@nodeutils/defaults-deep')
const Protector = require('libp2p-pnet') const Protector = require('libp2p-pnet')
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing') const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
const DelegatedContentRouter = require('libp2p-delegated-content-routing') const DelegatedContentRouter = require('libp2p-delegated-content-routing')
class Node extends libp2p { class Node extends Libp2p {
constructor (_options) { constructor (_options) {
const peerInfo = _options.peerInfo const peerInfo = _options.peerInfo
const defaults = { const defaults = {
@@ -154,7 +155,8 @@ class Node extends libp2p {
peerDiscovery: [ peerDiscovery: [
MulticastDNS MulticastDNS
], ],
dht: DHT // DHT enables PeerRouting, ContentRouting and DHT itself components dht: DHT, // DHT enables PeerRouting, ContentRouting and DHT itself components
pubsub: GossipSub
}, },
// libp2p config options (typically found on a config.json) // libp2p config options (typically found on a config.json)
@@ -181,11 +183,14 @@ class Node extends libp2p {
dht: { dht: {
kBucketSize: 20, kBucketSize: 20,
enabled: true, 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 pubsub: {
EXPERIMENTAL: { // Experimental features ("behind a flag") enabled: true
pubsub: false
} }
} }
} }
@@ -200,13 +205,33 @@ class Node extends libp2p {
### API ### API
#### Create a Node - `new libp2p.Node(options)` **IMPORTANT NOTE**: All the methods listed in the API section that take a callback are also now Promisified. Libp2p is migrating away from callbacks to async/await, and in a future release (that will be announced in advance), callback support will be removed entirely. You can follow progress of the async/await endeavor at https://github.com/ipfs/js-ipfs/issues/1670.
> Creates an instance of the libp2p.Node. #### Create a Node - `Libp2p.createLibp2p(options, callback)`
> Behaves exactly like `new Libp2p(options)`, but doesn't require a PeerInfo. One will be generated instead
```js
const { createLibp2p } = require('libp2p')
createLibp2p(options, (err, libp2p) => {
if (err) throw err
libp2p.start((err) => {
if (err) throw err
})
})
```
- `options`: Object of libp2p configuration options
- `callback`: Function with signature `function (Error, Libp2p) {}`
#### Create a Node alternative - `new Libp2p(options)`
> Creates an instance of Libp2p with a custom `PeerInfo` provided via `options.peerInfo`.
Required keys in the `options` object: Required keys in the `options` object:
- `peerInfo`: instance of [PeerInfo][] that contains the [PeerId][], Keys and [multiaddrs][multiaddr] of the libp2p Node. - `peerInfo`: instance of [PeerInfo][] that contains the [PeerId][], Keys and [multiaddrs][multiaddr] of the libp2p Node.
- `modules.transport`: An array that must include at least 1 transport, such as `libp2p-tcp`.
#### `libp2p.start(callback)` #### `libp2p.start(callback)`

View File

@@ -33,7 +33,7 @@
- [ ] Twitter - [ ] Twitter
- [ ] IRC - [ ] IRC
- [ ] Reddit - [ ] Reddit
- [ ] [discuss.ipfs.io](https://discuss.ipfs.io/c/announcements) - [ ] [discuss.libp2p.io](https://discuss.libp2p.io/c/news)
- [ ] Blog post - [ ] Blog post
- [ ] Copy release notes to the [GitHub Release description](https://github.com/libp2p/js-libp2p/releases) - [ ] 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: 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 - 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 - 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. - 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? # ⁉️ 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

@@ -17,7 +17,9 @@
}, },
"dependencies": { "dependencies": {
"detect-dom-ready": "^1.0.2", "detect-dom-ready": "^1.0.2",
"libp2p": "../../../",
"libp2p-bootstrap": "~0.9.7", "libp2p-bootstrap": "~0.9.7",
"libp2p-kad-dht": "^0.15.3",
"libp2p-mplex": "~0.8.5", "libp2p-mplex": "~0.8.5",
"libp2p-secio": "~0.11.1", "libp2p-secio": "~0.11.1",
"libp2p-spdy": "~0.13.3", "libp2p-spdy": "~0.13.3",

View File

@@ -8,8 +8,7 @@ const SPDY = require('libp2p-spdy')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
const Bootstrap = require('libp2p-bootstrap') const Bootstrap = require('libp2p-bootstrap')
const DHT = require('libp2p-kad-dht') const DHT = require('libp2p-kad-dht')
const defaultsDeep = require('@nodeutils/defaults-deep') const libp2p = require('libp2p')
const libp2p = require('../../../../')
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-browser.json // Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-browser.json
const bootstrapList = [ const bootstrapList = [
@@ -26,9 +25,9 @@ const bootstrapList = [
] ]
class Node extends libp2p { class Node extends libp2p {
constructor (_options) { constructor ({ peerInfo }) {
const wrtcStar = new WebRTCStar({ id: _options.peerInfo.id }) const wrtcStar = new WebRTCStar({ id: peerInfo.id })
const wsstar = new WebSocketStar({ id: _options.peerInfo.id }) const wsstar = new WebSocketStar({ id: peerInfo.id })
const defaults = { const defaults = {
modules: { modules: {
@@ -86,7 +85,7 @@ class Node extends libp2p {
} }
} }
super(defaultsDeep(_options, defaults)) super({ ...defaults, peerInfo })
} }
} }

View File

@@ -10,9 +10,11 @@ function createNode (callback) {
} }
const peerIdStr = peerInfo.id.toB58String() const peerIdStr = peerInfo.id.toB58String()
const ma = `/dns4/star-signal.cloud.ipfs.team/tcp/443/wss/p2p-webrtc-star/p2p/${peerIdStr}` const webrtcAddr = `/dns4/star-signal.cloud.ipfs.team/tcp/443/wss/p2p-webrtc-star/p2p/${peerIdStr}`
const wsAddr = `/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star`
peerInfo.multiaddrs.add(ma) peerInfo.multiaddrs.add(webrtcAddr)
peerInfo.multiaddrs.add(wsAddr)
const node = new Node({ const node = new Node({
peerInfo peerInfo

View File

@@ -46,6 +46,9 @@ domReady(() => {
myPeerDiv.append(idDiv) myPeerDiv.append(idDiv)
console.log('Node is listening o/') console.log('Node is listening o/')
node.peerInfo.multiaddrs.toArray().forEach(ma => {
console.log(ma.toString())
})
// NOTE: to stop the node // NOTE: to stop the node
// node.stop((err) => {}) // node.stop((err) => {})

View File

@@ -20,5 +20,3 @@ Then simply go into the folder [1](./1) and execute the following
> npm start > npm start
# open your browser in port :9090 # open your browser in port :9090
``` ```
[Version Published on IPFS](http://ipfs.io/ipfs/Qmbc1J7ehw1dNYachbkCWPto4RsnVvqCKNVzmYEod2gXcy)

View File

@@ -1,7 +1,7 @@
{ {
"name": "libp2p", "name": "libp2p",
"version": "0.25.0", "version": "0.26.0-rc.1",
"description": "JavaScript base class for libp2p bundles", "description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>", "leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js", "main": "src/index.js",
"files": [ "files": [
@@ -23,73 +23,82 @@
"url": "https://github.com/libp2p/js-libp2p.git" "url": "https://github.com/libp2p/js-libp2p.git"
}, },
"keywords": [ "keywords": [
"libp2p",
"network",
"p2p",
"peer",
"peer-to-peer",
"IPFS" "IPFS"
], ],
"engines": {
"node": ">=6.0.0",
"npm": ">=3.0.0"
},
"license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/libp2p/js-libp2p/issues" "url": "https://github.com/libp2p/js-libp2p/issues"
}, },
"homepage": "https://github.com/libp2p/js-libp2p", "homepage": "https://libp2p.io",
"license": "MIT",
"browser": { "browser": {
"./test/utils/bundle-nodejs": "./test/utils/bundle-browser" "./test/utils/bundle-nodejs": "./test/utils/bundle-browser"
}, },
"engines": {
"node": ">=10.0.0",
"npm": ">=6.0.0"
},
"dependencies": { "dependencies": {
"async": "^2.6.2", "async": "^2.6.2",
"debug": "^4.1.1", "debug": "^4.1.1",
"err-code": "^1.1.2", "err-code": "^1.1.2",
"fsm-event": "^2.1.0", "fsm-event": "^2.1.0",
"libp2p-connection-manager": "~0.1.0", "libp2p-connection-manager": "^0.1.0",
"libp2p-floodsub": "~0.15.8", "libp2p-ping": "^0.8.5",
"libp2p-ping": "~0.8.5", "libp2p-switch": "^0.42.12",
"libp2p-switch": "~0.42.9", "libp2p-websockets": "^0.12.2",
"libp2p-websockets": "~0.12.2",
"mafmt": "^6.0.7", "mafmt": "^6.0.7",
"multiaddr": "^6.0.6", "multiaddr": "^6.1.0",
"once": "^1.4.0", "once": "^1.4.0",
"peer-book": "~0.9.1", "peer-book": "^0.9.1",
"peer-id": "~0.12.2", "peer-id": "^0.12.2",
"peer-info": "~0.15.1", "peer-info": "^0.15.1",
"superstruct": "~0.6.0" "promisify-es6": "^1.0.3",
"superstruct": "^0.6.0"
}, },
"devDependencies": { "devDependencies": {
"@nodeutils/defaults-deep": "^1.1.0", "@nodeutils/defaults-deep": "^1.1.0",
"aegir": "^18.2.1", "aegir": "^20.0.0",
"chai": "^4.2.0", "chai": "^4.2.0",
"chai-checkmark": "^1.0.1", "chai-checkmark": "^1.0.1",
"cids": "~0.5.8", "cids": "^0.7.1",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"electron-webrtc": "~0.3.0", "electron-webrtc": "^0.3.0",
"interface-datastore": "~0.6.0", "interface-datastore": "^0.6.0",
"libp2p-bootstrap": "~0.9.7", "libp2p-bootstrap": "^0.9.7",
"libp2p-circuit": "~0.3.6", "libp2p-circuit": "^0.3.7",
"libp2p-delegated-content-routing": "~0.2.2", "libp2p-delegated-content-routing": "^0.2.2",
"libp2p-delegated-peer-routing": "~0.2.2", "libp2p-delegated-peer-routing": "^0.2.2",
"libp2p-kad-dht": "~0.14.8", "libp2p-floodsub": "~0.17.0",
"libp2p-mdns": "~0.12.2", "libp2p-gossipsub": "~0.0.4",
"libp2p-mplex": "~0.8.4", "libp2p-kad-dht": "^0.15.3",
"libp2p-secio": "~0.11.1", "libp2p-mdns": "^0.12.3",
"libp2p-spdy": "~0.13.2", "libp2p-mplex": "^0.8.4",
"libp2p-tcp": "~0.13.0", "libp2p-secio": "^0.11.1",
"libp2p-webrtc-star": "~0.15.8", "libp2p-spdy": "^0.13.2",
"libp2p-tcp": "^0.13.0",
"libp2p-webrtc-star": "^0.16.1",
"libp2p-websocket-star": "~0.10.2", "libp2p-websocket-star": "~0.10.2",
"libp2p-websocket-star-rendezvous": "~0.3.0", "libp2p-websocket-star-rendezvous": "~0.3.0",
"lodash.times": "^4.3.2", "lodash.times": "^4.3.2",
"merge-options": "^1.0.1",
"nock": "^10.0.6", "nock": "^10.0.6",
"pull-goodbye": "0.0.2", "pull-goodbye": "0.0.2",
"pull-mplex": "~0.1.2", "pull-mplex": "^0.1.2",
"pull-serializer": "~0.3.2", "pull-serializer": "^0.3.2",
"pull-stream": "^3.6.9", "pull-stream": "^3.6.12",
"sinon": "^7.2.7", "sinon": "^7.2.7",
"wrtc": "~0.3.5" "wrtc": "^0.4.1"
}, },
"contributors": [ "contributors": [
"Aditya Bose <13054902+adbose@users.noreply.github.com>", "Aditya Bose <13054902+adbose@users.noreply.github.com>",
"Alan Shaw <alan.shaw@protocol.ai>", "Alan Shaw <alan.shaw@protocol.ai>",
"Alan Shaw <alan@tableflip.io>", "Alan Shaw <alan@tableflip.io>",
"Alex Potsides <alex@achingbrain.net>",
"Andrew Nesbitt <andrewnez@gmail.com>", "Andrew Nesbitt <andrewnez@gmail.com>",
"Chris Bratlien <chrisbratlien@gmail.com>", "Chris Bratlien <chrisbratlien@gmail.com>",
"Chris Dostert <chrisdostert@users.noreply.github.com>", "Chris Dostert <chrisdostert@users.noreply.github.com>",
@@ -101,6 +110,7 @@
"Florian-Merle <florian.david.merle@gmail.com>", "Florian-Merle <florian.david.merle@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>", "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Giovanni T. Parra <fiatjaf@gmail.com>", "Giovanni T. Parra <fiatjaf@gmail.com>",
"Guy Sviry <32539816+guysv@users.noreply.github.com>",
"Henrique Dias <hacdias@gmail.com>", "Henrique Dias <hacdias@gmail.com>",
"Hugo Dias <mail@hugodias.me>", "Hugo Dias <mail@hugodias.me>",
"Hugo Dias <hugomrdias@gmail.com>", "Hugo Dias <hugomrdias@gmail.com>",
@@ -123,8 +133,8 @@
"Sönke Hahn <soenkehahn@gmail.com>", "Sönke Hahn <soenkehahn@gmail.com>",
"Thomas Eizinger <thomas@eizinger.io>", "Thomas Eizinger <thomas@eizinger.io>",
"Tiago Alves <alvesjtiago@gmail.com>", "Tiago Alves <alvesjtiago@gmail.com>",
"Vasco Santos <vasco.santos@ua.pt>",
"Vasco Santos <vasco.santos@moxy.studio>", "Vasco Santos <vasco.santos@moxy.studio>",
"Vasco Santos <vasco.santos@ua.pt>",
"Volker Mische <volker.mische@gmail.com>", "Volker Mische <volker.mische@gmail.com>",
"Yusef Napora <yusef@napora.org>", "Yusef Napora <yusef@napora.org>",
"Zane Starr <zcstarr@gmail.com>", "Zane Starr <zcstarr@gmail.com>",

View File

@@ -19,6 +19,7 @@ const modulesSchema = s({
connProtector: s.union(['undefined', s.interface({ protect: 'function' })]), connProtector: s.union(['undefined', s.interface({ protect: 'function' })]),
contentRouting: optional(list(['object'])), contentRouting: optional(list(['object'])),
dht: optional(s('null|function|object')), dht: optional(s('null|function|object')),
pubsub: optional(s('null|function|object')),
peerDiscovery: optional(list([s('object|function')])), peerDiscovery: optional(list([s('object|function')])),
peerRouting: optional(list(['object'])), peerRouting: optional(list(['object'])),
streamMuxer: optional(list([s('object|function')])), streamMuxer: optional(list([s('object|function')])),
@@ -48,41 +49,23 @@ const configSchema = s({
enabled: true enabled: true
}), }),
// DHT config // DHT config
dht: s({ dht: s('object?', {
kBucketSize: 'number',
enabled: 'boolean?',
validators: 'object?',
selectors: 'object?',
randomWalk: optional(s({
enabled: 'boolean?',
queriesPerPeriod: 'number?',
interval: 'number?',
timeout: 'number?'
}, {
// random walk defaults
enabled: false, // disabled waiting for https://github.com/libp2p/js-libp2p-kad-dht/issues/86
queriesPerPeriod: 1,
interval: 30000,
timeout: 10000
}))
}, {
// DHT defaults // DHT defaults
enabled: false, enabled: false,
kBucketSize: 20, kBucketSize: 20,
enabledDiscovery: false randomWalk: {
enabled: false, // disabled waiting for https://github.com/libp2p/js-libp2p-kad-dht/issues/86
queriesPerPeriod: 1,
interval: 300e3,
timeout: 10e3
}
}), }),
// Experimental config // Pubsub config
EXPERIMENTAL: s({ pubsub: s('object?', {
pubsub: 'boolean' // DHT defaults
}, { enabled: false
// Experimental defaults
pubsub: false
}) })
}, { }, {})
relay: {},
dht: {},
EXPERIMENTAL: {}
})
const optionsSchema = s({ const optionsSchema = s({
switch: 'object?', switch: 'object?',

View File

@@ -3,6 +3,7 @@
const tryEach = require('async/tryEach') const tryEach = require('async/tryEach')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const errCode = require('err-code') const errCode = require('err-code')
const promisify = require('promisify-es6')
module.exports = (node) => { module.exports = (node) => {
const routers = node._modules.contentRouting || [] const routers = node._modules.contentRouting || []
@@ -24,7 +25,7 @@ module.exports = (node) => {
* @param {function(Error, Result<Array>)} callback * @param {function(Error, Result<Array>)} callback
* @returns {void} * @returns {void}
*/ */
findProviders: (key, options, callback) => { findProviders: promisify((key, options, callback) => {
if (typeof options === 'function') { if (typeof options === 'function') {
callback = options callback = options
options = {} options = {}
@@ -60,7 +61,7 @@ module.exports = (node) => {
results = results || [] results = results || []
callback(null, results) callback(null, results)
}) })
}, }),
/** /**
* Iterates over all content routers in parallel to notify it is * Iterates over all content routers in parallel to notify it is
@@ -70,7 +71,7 @@ module.exports = (node) => {
* @param {function(Error)} callback * @param {function(Error)} callback
* @returns {void} * @returns {void}
*/ */
provide: (key, callback) => { provide: promisify((key, callback) => {
if (!routers.length) { if (!routers.length) {
return callback(errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE')) return callback(errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE'))
} }
@@ -78,6 +79,6 @@ module.exports = (node) => {
parallel(routers.map((router) => { parallel(routers.map((router) => {
return (cb) => router.provide(key, cb) return (cb) => router.provide(key, cb)
}), callback) }), callback)
} })
} }
} }

View File

@@ -2,19 +2,20 @@
const nextTick = require('async/nextTick') const nextTick = require('async/nextTick')
const errCode = require('err-code') const errCode = require('err-code')
const promisify = require('promisify-es6')
const { messages, codes } = require('./errors') const { messages, codes } = require('./errors')
module.exports = (node) => { module.exports = (node) => {
return { return {
put: (key, value, callback) => { put: promisify((key, value, callback) => {
if (!node._dht) { if (!node._dht) {
return nextTick(callback, errCode(new Error(messages.DHT_DISABLED), codes.DHT_DISABLED)) return nextTick(callback, errCode(new Error(messages.DHT_DISABLED), codes.DHT_DISABLED))
} }
node._dht.put(key, value, callback) node._dht.put(key, value, callback)
}, }),
get: (key, options, callback) => { get: promisify((key, options, callback) => {
if (typeof options === 'function') { if (typeof options === 'function') {
callback = options callback = options
options = {} options = {}
@@ -25,8 +26,8 @@ module.exports = (node) => {
} }
node._dht.get(key, options, callback) node._dht.get(key, options, callback)
}, }),
getMany: (key, nVals, options, callback) => { getMany: promisify((key, nVals, options, callback) => {
if (typeof options === 'function') { if (typeof options === 'function') {
callback = options callback = options
options = {} options = {}
@@ -37,6 +38,6 @@ module.exports = (node) => {
} }
node._dht.getMany(key, nVals, options, callback) node._dht.getMany(key, nVals, options, callback)
} })
} }
} }

View File

@@ -7,5 +7,7 @@ exports.messages = {
exports.codes = { exports.codes = {
DHT_DISABLED: 'ERR_DHT_DISABLED', 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

@@ -4,12 +4,13 @@ const PeerId = require('peer-id')
const PeerInfo = require('peer-info') const PeerInfo = require('peer-info')
const multiaddr = require('multiaddr') const multiaddr = require('multiaddr')
const errCode = require('err-code') const errCode = require('err-code')
const promisify = require('promisify-es6')
module.exports = (node) => { module.exports = (node) => {
/* /*
* Helper method to check the data type of peer and convert it to PeerInfo * Helper method to check the data type of peer and convert it to PeerInfo
*/ */
return function (peer, callback) { return promisify(function (peer, callback) {
let p let p
// PeerInfo // PeerInfo
if (PeerInfo.isPeerInfo(peer)) { if (PeerInfo.isPeerInfo(peer)) {
@@ -62,5 +63,5 @@ module.exports = (node) => {
} }
callback(null, p) callback(null, p)
} })
} }

View File

@@ -6,12 +6,15 @@ const debug = require('debug')
const log = debug('libp2p') const log = debug('libp2p')
log.error = debug('libp2p:error') log.error = debug('libp2p:error')
const errCode = require('err-code') const errCode = require('err-code')
const promisify = require('promisify-es6')
const each = require('async/each') const each = require('async/each')
const series = require('async/series') const series = require('async/series')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const nextTick = require('async/nextTick')
const PeerBook = require('peer-book') const PeerBook = require('peer-book')
const PeerInfo = require('peer-info')
const Switch = require('libp2p-switch') const Switch = require('libp2p-switch')
const Ping = require('libp2p-ping') const Ping = require('libp2p-ping')
const WebSockets = require('libp2p-websockets') const WebSockets = require('libp2p-websockets')
@@ -24,23 +27,24 @@ const dht = require('./dht')
const pubsub = require('./pubsub') const pubsub = require('./pubsub')
const getPeerInfo = require('./get-peer-info') const getPeerInfo = require('./get-peer-info')
const validateConfig = require('./config').validate const validateConfig = require('./config').validate
const { codes } = require('./errors')
const notStarted = (action, state) => { const notStarted = (action, state) => {
return errCode( return errCode(
new Error(`libp2p cannot ${action} when not started; state is ${state}`), new Error(`libp2p cannot ${action} when not started; state is ${state}`),
'ERR_NODE_NOT_STARTED' codes.ERR_NODE_NOT_STARTED
) )
} }
/** /**
* @fires Node#error Emitted when an error occurs * @fires Libp2p#error Emitted when an error occurs
* @fires Node#peer:connect Emitted when a peer is connected to this node * @fires Libp2p#peer:connect Emitted when a peer is connected to this node
* @fires Node#peer:disconnect Emitted when a peer disconnects from this node * @fires Libp2p#peer:disconnect Emitted when a peer disconnects from this node
* @fires Node#peer:discovery Emitted when a peer is discovered * @fires Libp2p#peer:discovery Emitted when a peer is discovered
* @fires Node#start Emitted when the node and its services has started * @fires Libp2p#start Emitted when the node and its services has started
* @fires Node#stop Emitted when the node and its services has stopped * @fires Libp2p#stop Emitted when the node and its services has stopped
*/ */
class Node extends EventEmitter { class Libp2p extends EventEmitter {
constructor (_options) { constructor (_options) {
super() super()
// validateConfig will ensure the config is correct, // validateConfig will ensure the config is correct,
@@ -65,7 +69,7 @@ class Node extends EventEmitter {
// Attach stream multiplexers // Attach stream multiplexers
if (this._modules.streamMuxer) { if (this._modules.streamMuxer) {
let muxers = this._modules.streamMuxer const muxers = this._modules.streamMuxer
muxers.forEach((muxer) => this._switch.connection.addStreamMuxer(muxer)) muxers.forEach((muxer) => this._switch.connection.addStreamMuxer(muxer))
// If muxer exists // If muxer exists
@@ -95,7 +99,7 @@ class Node extends EventEmitter {
// Attach crypto channels // Attach crypto channels
if (this._modules.connEncryption) { if (this._modules.connEncryption) {
let cryptos = this._modules.connEncryption const cryptos = this._modules.connEncryption
cryptos.forEach((crypto) => { cryptos.forEach((crypto) => {
this._switch.connection.crypto(crypto.tag, crypto.encrypt) this._switch.connection.crypto(crypto.tag, crypto.encrypt)
}) })
@@ -118,9 +122,9 @@ class Node extends EventEmitter {
}) })
} }
// enable/disable pubsub // start pubsub
if (this._config.EXPERIMENTAL.pubsub) { if (this._modules.pubsub && this._config.pubsub.enabled !== false) {
this.pubsub = pubsub(this) this.pubsub = pubsub(this, this._modules.pubsub)
} }
// Attach remaining APIs // Attach remaining APIs
@@ -183,6 +187,13 @@ class Node extends EventEmitter {
}) })
this._peerDiscovered = this._peerDiscovered.bind(this) this._peerDiscovered = this._peerDiscovered.bind(this)
// promisify all instance methods
;['start', 'stop', 'dial', 'dialProtocol', 'dialFSM', 'hangUp', 'ping'].forEach(method => {
this[method] = promisify(this[method], {
context: this
})
})
} }
/** /**
@@ -344,7 +355,7 @@ class Node extends EventEmitter {
let t let t
if (typeof Transport === 'function') { if (typeof Transport === 'function') {
t = new Transport() t = new Transport({ libp2p: this })
} else { } else {
t = Transport t = Transport
} }
@@ -392,8 +403,8 @@ class Node extends EventEmitter {
} }
}, },
(cb) => { (cb) => {
if (this._floodSub) { if (this.pubsub) {
return this._floodSub.start(cb) return this.pubsub.start(cb)
} }
cb() cb()
}, },
@@ -431,8 +442,8 @@ class Node extends EventEmitter {
) )
}, },
(cb) => { (cb) => {
if (this._floodSub) { if (this.pubsub) {
return this._floodSub.stop(cb) return this.pubsub.stop(cb)
} }
cb() cb()
}, },
@@ -476,6 +487,10 @@ class Node extends EventEmitter {
* @param {PeerInfo} peerInfo * @param {PeerInfo} peerInfo
*/ */
_peerDiscovered (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) peerInfo = this.peerBook.put(peerInfo)
if (!this.isStarted()) return if (!this.isStarted()) return
@@ -542,4 +557,21 @@ class Node extends EventEmitter {
} }
} }
module.exports = Node module.exports = Libp2p
/**
* Like `new Libp2p(options)` except it will create a `PeerInfo`
* instance if one is not provided in options.
* @param {object} options Libp2p configuration options
* @param {function(Error, Libp2p)} callback
* @returns {void}
*/
module.exports.createLibp2p = promisify((options, callback) => {
if (options.peerInfo) {
return nextTick(callback, null, new Libp2p(options))
}
PeerInfo.create((err, peerInfo) => {
if (err) return callback(err)
options.peerInfo = peerInfo
callback(null, new Libp2p(options))
})
})

View File

@@ -2,6 +2,7 @@
const tryEach = require('async/tryEach') const tryEach = require('async/tryEach')
const errCode = require('err-code') const errCode = require('err-code')
const promisify = require('promisify-es6')
module.exports = (node) => { module.exports = (node) => {
const routers = node._modules.peerRouting || [] const routers = node._modules.peerRouting || []
@@ -21,7 +22,7 @@ module.exports = (node) => {
* @param {function(Error, Result<Array>)} callback * @param {function(Error, Result<Array>)} callback
* @returns {void} * @returns {void}
*/ */
findPeer: (id, options, callback) => { findPeer: promisify((id, options, callback) => {
if (typeof options === 'function') { if (typeof options === 'function') {
callback = options callback = options
options = {} options = {}
@@ -47,12 +48,12 @@ module.exports = (node) => {
}) })
tryEach(tasks, (err, results) => { tryEach(tasks, (err, results) => {
if (err && err.code !== 'NOT_FOUND') { if (err) {
return callback(err) return callback(err)
} }
results = results || [] results = results || []
callback(null, results) callback(null, results)
}) })
} })
} }
} }

View File

@@ -2,84 +2,128 @@
const nextTick = require('async/nextTick') const nextTick = require('async/nextTick')
const { messages, codes } = require('./errors') const { messages, codes } = require('./errors')
const FloodSub = require('libp2p-floodsub') const promisify = require('promisify-es6')
const errCode = require('err-code') const errCode = require('err-code')
module.exports = (node) => { module.exports = (node, Pubsub) => {
const floodSub = new FloodSub(node) const pubsub = new Pubsub(node, { emitSelf: true })
node._floodSub = floodSub
return { return {
subscribe: (topic, options, handler, callback) => { /**
* Subscribe the given handler to a pubsub topic
*
* @param {string} topic
* @param {function} handler The handler to subscribe
* @param {object|null} [options]
* @param {function} [callback] An optional callback
*
* @returns {Promise|void} A promise is returned if no callback is provided
*
* @example <caption>Subscribe a handler to a topic</caption>
*
* // `null` must be passed for options until subscribe is no longer using promisify
* const handler = (message) => { }
* await libp2p.subscribe(topic, handler, null)
*
* @example <caption>Use a callback instead of the Promise api</caption>
*
* // `options` may be passed or omitted when supplying a callback
* const handler = (message) => { }
* libp2p.subscribe(topic, handler, callback)
*/
subscribe: promisify((topic, handler, options, callback) => {
if (typeof options === 'function') { if (typeof options === 'function') {
callback = handler callback = options
handler = options
options = {} options = {}
} }
if (!node.isStarted() && !floodSub.started) { if (!node.isStarted() && !pubsub.started) {
return nextTick(callback, errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED)) return nextTick(callback, errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED))
} }
function subscribe (cb) { function subscribe (cb) {
if (floodSub.listenerCount(topic) === 0) { if (pubsub.listenerCount(topic) === 0) {
floodSub.subscribe(topic) pubsub.subscribe(topic)
} }
floodSub.on(topic, handler) pubsub.on(topic, handler)
nextTick(cb) nextTick(cb)
} }
subscribe(callback) subscribe(callback)
}, }),
unsubscribe: (topic, handler, callback) => { /**
if (!node.isStarted() && !floodSub.started) { * Unsubscribes from a pubsub topic
*
* @param {string} topic
* @param {function|null} handler The handler to unsubscribe from
* @param {function} [callback] An optional callback
*
* @returns {Promise|void} A promise is returned if no callback is provided
*
* @example <caption>Unsubscribe a topic for all handlers</caption>
*
* // `null` must be passed until unsubscribe is no longer using promisify
* await libp2p.unsubscribe(topic, null)
*
* @example <caption>Unsubscribe a topic for 1 handler</caption>
*
* await libp2p.unsubscribe(topic, handler)
*
* @example <caption>Use a callback instead of the Promise api</caption>
*
* libp2p.unsubscribe(topic, handler, callback)
*/
unsubscribe: promisify((topic, handler, callback) => {
if (!node.isStarted() && !pubsub.started) {
return nextTick(callback, errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED)) return nextTick(callback, errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED))
} }
if (!handler && !callback) {
floodSub.removeAllListeners(topic) if (!handler) {
pubsub.removeAllListeners(topic)
} else { } else {
floodSub.removeListener(topic, handler) pubsub.removeListener(topic, handler)
} }
if (floodSub.listenerCount(topic) === 0) { if (pubsub.listenerCount(topic) === 0) {
floodSub.unsubscribe(topic) pubsub.unsubscribe(topic)
} }
if (typeof callback === 'function') { if (typeof callback === 'function') {
nextTick(() => callback()) return nextTick(() => callback())
} }
},
publish: (topic, data, callback) => { return Promise.resolve()
if (!node.isStarted() && !floodSub.started) { }),
publish: promisify((topic, data, callback) => {
if (!node.isStarted() && !pubsub.started) {
return nextTick(callback, errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED)) return nextTick(callback, errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED))
} }
if (!Buffer.isBuffer(data)) { try {
return nextTick(callback, errCode(new Error('data must be a Buffer'), 'ERR_DATA_IS_NOT_A_BUFFER')) data = Buffer.from(data)
} catch (err) {
return nextTick(callback, errCode(new Error('data must be convertible to a Buffer'), 'ERR_DATA_IS_NOT_VALID'))
} }
floodSub.publish(topic, data) pubsub.publish(topic, data, callback)
}),
nextTick(() => callback()) ls: promisify((callback) => {
}, if (!node.isStarted() && !pubsub.started) {
ls: (callback) => {
if (!node.isStarted() && !floodSub.started) {
return nextTick(callback, errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED)) return nextTick(callback, errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED))
} }
const subscriptions = Array.from(floodSub.subscriptions) const subscriptions = Array.from(pubsub.subscriptions)
nextTick(() => callback(null, subscriptions)) nextTick(() => callback(null, subscriptions))
}, }),
peers: (topic, callback) => { peers: promisify((topic, callback) => {
if (!node.isStarted() && !floodSub.started) { if (!node.isStarted() && !pubsub.started) {
return nextTick(callback, errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED)) return nextTick(callback, errCode(new Error(messages.NOT_STARTED_YET), codes.PUBSUB_NOT_STARTED))
} }
@@ -88,15 +132,19 @@ module.exports = (node) => {
topic = null topic = null
} }
const peers = Array.from(floodSub.peers.values()) const peers = Array.from(pubsub.peers.values())
.filter((peer) => topic ? peer.topics.has(topic) : true) .filter((peer) => topic ? peer.topics.has(topic) : true)
.map((peer) => peer.info.id.toB58String()) .map((peer) => peer.info.id.toB58String())
nextTick(() => callback(null, peers)) nextTick(() => callback(null, peers))
}, }),
setMaxListeners (n) { setMaxListeners (n) {
return floodSub.setMaxListeners(n) return pubsub.setMaxListeners(n)
} },
start: promisify((cb) => pubsub.start(cb)),
stop: promisify((cb) => pubsub.stop(cb))
} }
} }

View File

@@ -16,7 +16,7 @@ const tryEcho = require('./utils/try-echo')
const echo = require('./utils/echo') const echo = require('./utils/echo')
describe('circuit relay', () => { describe('circuit relay', () => {
let handlerSpies = [] const handlerSpies = []
let relayNode1 let relayNode1
let relayNode2 let relayNode2
let nodeWS1 let nodeWS1

View File

@@ -33,7 +33,7 @@ describe('configuration', () => {
expect(() => { expect(() => {
validateConfig({ validateConfig({
modules: { modules: {
transport: [ WS ] transport: [WS]
} }
}) })
}).to.throw() }).to.throw()
@@ -52,7 +52,7 @@ describe('configuration', () => {
validateConfig({ validateConfig({
peerInfo, peerInfo,
modules: { modules: {
transport: [ ] transport: []
} }
}) })
}).to.throw('ERROR_EMPTY') }).to.throw('ERROR_EMPTY')
@@ -62,8 +62,8 @@ describe('configuration', () => {
const options = { const options = {
peerInfo, peerInfo,
modules: { modules: {
transport: [ WS ], transport: [WS],
peerDiscovery: [ Bootstrap ], peerDiscovery: [Bootstrap],
dht: DHT dht: DHT
} }
} }
@@ -74,16 +74,16 @@ describe('configuration', () => {
minPeers: 25 minPeers: 25
}, },
modules: { modules: {
transport: [ WS ], transport: [WS],
peerDiscovery: [ Bootstrap ], peerDiscovery: [Bootstrap],
dht: DHT dht: DHT
}, },
config: { config: {
peerDiscovery: { peerDiscovery: {
autoDial: true autoDial: true
}, },
EXPERIMENTAL: { pubsub: {
pubsub: false enabled: false
}, },
dht: { dht: {
kBucketSize: 20, kBucketSize: 20,
@@ -91,7 +91,7 @@ describe('configuration', () => {
randomWalk: { randomWalk: {
enabled: false, enabled: false,
queriesPerPeriod: 1, queriesPerPeriod: 1,
interval: 30000, interval: 300000,
timeout: 10000 timeout: 10000
} }
}, },
@@ -112,8 +112,8 @@ describe('configuration', () => {
const options = { const options = {
peerInfo, peerInfo,
modules: { modules: {
transport: [ WS ], transport: [WS],
peerDiscovery: [ Bootstrap ], peerDiscovery: [Bootstrap],
dht: DHT dht: DHT
}, },
config: { config: {
@@ -132,8 +132,8 @@ describe('configuration', () => {
minPeers: 25 minPeers: 25
}, },
modules: { modules: {
transport: [ WS ], transport: [WS],
peerDiscovery: [ Bootstrap ], peerDiscovery: [Bootstrap],
dht: DHT dht: DHT
}, },
config: { config: {
@@ -144,8 +144,8 @@ describe('configuration', () => {
enabled: true enabled: true
} }
}, },
EXPERIMENTAL: { pubsub: {
pubsub: false enabled: false
}, },
dht: { dht: {
kBucketSize: 20, kBucketSize: 20,
@@ -153,7 +153,7 @@ describe('configuration', () => {
randomWalk: { randomWalk: {
enabled: false, enabled: false,
queriesPerPeriod: 1, queriesPerPeriod: 1,
interval: 30000, interval: 300000,
timeout: 10000 timeout: 10000
} }
}, },
@@ -181,8 +181,8 @@ describe('configuration', () => {
dialTimeout: 30e3 dialTimeout: 30e3
}, },
modules: { modules: {
transport: [ WS ], transport: [WS],
peerDiscovery: [ ] peerDiscovery: []
} }
} }
@@ -204,10 +204,10 @@ describe('configuration', () => {
const options = { const options = {
peerInfo, peerInfo,
modules: { modules: {
transport: [ WS ], transport: [WS],
peerDiscovery: [ Bootstrap ], peerDiscovery: [Bootstrap],
peerRouting: [ peerRouter ], peerRouting: [peerRouter],
contentRouting: [ contentRouter ], contentRouting: [contentRouter],
dht: DHT dht: DHT
}, },
config: { config: {
@@ -221,8 +221,8 @@ describe('configuration', () => {
} }
expect(validateConfig(options).modules).to.deep.include({ expect(validateConfig(options).modules).to.deep.include({
peerRouting: [ peerRouter ], peerRouting: [peerRouter],
contentRouting: [ contentRouter ] contentRouting: [contentRouter]
}) })
}) })
@@ -230,7 +230,7 @@ describe('configuration', () => {
const options = { const options = {
peerInfo, peerInfo,
modules: { modules: {
transport: [ WS ] transport: [WS]
}, },
config: { config: {
dht: { dht: {
@@ -242,7 +242,7 @@ describe('configuration', () => {
expect(() => validateConfig(options)).to.throw() 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 selectors = {}
const validators = {} const validators = {}
@@ -269,8 +269,8 @@ describe('configuration', () => {
dht: DHT dht: DHT
}, },
config: { config: {
EXPERIMENTAL: { pubsub: {
pubsub: false enabled: false
}, },
peerDiscovery: { peerDiscovery: {
autoDial: true autoDial: true
@@ -283,14 +283,6 @@ describe('configuration', () => {
} }
}, },
dht: { dht: {
kBucketSize: 20,
enabled: false,
randomWalk: {
enabled: false,
queriesPerPeriod: 1,
interval: 30000,
timeout: 10000
},
selectors, selectors,
validators validators
} }
@@ -298,4 +290,43 @@ describe('configuration', () => {
} }
expect(validateConfig(options)).to.deep.equal(expected) 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

@@ -121,8 +121,9 @@ describe('.contentRouting', () => {
const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSnnnn') const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSnnnn')
nodeE.contentRouting.findProviders(cid, { maxTimeout: 5000 }, (err, providers) => { nodeE.contentRouting.findProviders(cid, { maxTimeout: 5000 }, (err, providers) => {
expect(err).to.not.exist() expect(err).to.exist()
expect(providers).to.have.length(0) expect(err.code).to.eql('ERR_NOT_FOUND')
expect(providers).to.not.exist()
done() done()
}) })
}) })
@@ -150,7 +151,7 @@ describe('.contentRouting', () => {
nodeA = new Node({ nodeA = new Node({
peerInfo, peerInfo,
modules: { modules: {
contentRouting: [ delegate ] contentRouting: [delegate]
}, },
config: { config: {
dht: { dht: {
@@ -185,19 +186,10 @@ describe('.contentRouting', () => {
it('should be able to register as a provider', (done) => { it('should be able to register as a provider', (done) => {
const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB') const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB')
const mockApi = nock('http://0.0.0.0:60197') const mockApi = nock('http://0.0.0.0:60197')
// mock the swarm connect
.post('/api/v0/swarm/connect')
.query({
arg: `/ip4/0.0.0.0/tcp/60194/p2p-circuit/ipfs/${nodeA.peerInfo.id.toB58String()}`,
'stream-channels': true
})
.reply(200, {
Strings: [`connect ${nodeA.peerInfo.id.toB58String()} success`]
}, ['Content-Type', 'application/json'])
// mock the refs call // mock the refs call
.post('/api/v0/refs') .post('/api/v0/refs')
.query({ .query({
recursive: true, recursive: false,
arg: cid.toBaseEncodedString(), arg: cid.toBaseEncodedString(),
'stream-channels': true 'stream-channels': true
}) })
@@ -216,10 +208,11 @@ describe('.contentRouting', () => {
it('should handle errors when registering as a provider', (done) => { it('should handle errors when registering as a provider', (done) => {
const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB') const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB')
const mockApi = nock('http://0.0.0.0:60197') const mockApi = nock('http://0.0.0.0:60197')
// mock the swarm connect // mock the refs call
.post('/api/v0/swarm/connect') .post('/api/v0/refs')
.query({ .query({
arg: `/ip4/0.0.0.0/tcp/60194/p2p-circuit/ipfs/${nodeA.peerInfo.id.toB58String()}`, recursive: false,
arg: cid.toBaseEncodedString(),
'stream-channels': true 'stream-channels': true
}) })
.reply(502, 'Bad Gateway', ['Content-Type', 'application/json']) .reply(502, 'Bad Gateway', ['Content-Type', 'application/json'])
@@ -251,7 +244,7 @@ describe('.contentRouting', () => {
timeout: '1000ms', timeout: '1000ms',
'stream-channels': true 'stream-channels': true
}) })
.reply(200, `{"Extra":"","ID":"QmWKqWXCtRXEeCQTo3FoZ7g4AfnGiauYYiczvNxFCHicbB","Responses":[{"Addrs":["/ip4/0.0.0.0/tcp/0"],"ID":"${provider}"}],"Type":1}\n`, [ .reply(200, `{"Extra":"","ID":"QmWKqWXCtRXEeCQTo3FoZ7g4AfnGiauYYiczvNxFCHicbB","Responses":[{"Addrs":["/ip4/0.0.0.0/tcp/0"],"ID":"${provider}"}],"Type":4}\n`, [
'Content-Type', 'application/json', 'Content-Type', 'application/json',
'X-Chunked-Output', '1' 'X-Chunked-Output', '1'
]) ])
@@ -308,7 +301,7 @@ describe('.contentRouting', () => {
nodeA = new Node({ nodeA = new Node({
peerInfo, peerInfo,
modules: { modules: {
contentRouting: [ delegate ] contentRouting: [delegate]
}, },
config: { config: {
relay: { relay: {

View File

@@ -7,13 +7,20 @@ const expect = chai.expect
const series = require('async/series') const series = require('async/series')
const createNode = require('./utils/create-node') const createNode = require('./utils/create-node')
const sinon = require('sinon') const sinon = require('sinon')
const { createLibp2p } = require('../src')
const WS = require('libp2p-websockets')
const PeerInfo = require('peer-info')
describe('libp2p creation', () => { describe('libp2p creation', () => {
afterEach(() => {
sinon.restore()
})
it('should be able to start and stop successfully', (done) => { it('should be able to start and stop successfully', (done) => {
createNode([], { createNode([], {
config: { config: {
EXPERIMENTAL: { pubsub: {
pubsub: true enabled: true
}, },
dht: { dht: {
enabled: true enabled: true
@@ -22,10 +29,10 @@ describe('libp2p creation', () => {
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
let sw = node._switch const sw = node._switch
let cm = node.connectionManager const cm = node.connectionManager
let dht = node._dht const dht = node._dht
let pub = node._floodSub const pub = node.pubsub
sinon.spy(sw, 'start') sinon.spy(sw, 'start')
sinon.spy(cm, 'start') sinon.spy(cm, 'start')
@@ -70,13 +77,13 @@ describe('libp2p creation', () => {
it('should not create disabled modules', (done) => { it('should not create disabled modules', (done) => {
createNode([], { createNode([], {
config: { config: {
EXPERIMENTAL: { pubsub: {
pubsub: false enabled: false
} }
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(node._floodSub).to.not.exist() expect(node._pubsub).to.not.exist()
done() done()
}) })
}) })
@@ -101,4 +108,36 @@ describe('libp2p creation', () => {
node._switch.emit('error', error) node._switch.emit('error', error)
}) })
}) })
it('createLibp2p should create a peerInfo instance', function (done) {
this.timeout(10e3)
createLibp2p({
modules: {
transport: [WS]
}
}, (err, libp2p) => {
expect(err).to.not.exist()
expect(libp2p).to.exist()
done()
})
})
it('createLibp2p should allow for a provided peerInfo instance', function (done) {
this.timeout(10e3)
PeerInfo.create((err, peerInfo) => {
expect(err).to.not.exist()
sinon.spy(PeerInfo, 'create')
createLibp2p({
peerInfo,
modules: {
transport: [WS]
}
}, (err, libp2p) => {
expect(err).to.not.exist()
expect(libp2p).to.exist()
expect(PeerInfo.create.callCount).to.eql(0)
done()
})
})
})
}) })

View File

@@ -60,9 +60,9 @@ describe('libp2p state machine (fsm)', () => {
node.once('stop', done) node.once('stop', done)
// stop the stopped node // stop the stopped node
node.stop() node.stop(() => {})
}) })
node.start() node.start(() => {})
}) })
it('should callback with an error when it occurs on stop', (done) => { it('should callback with an error when it occurs on stop', (done) => {
@@ -79,7 +79,7 @@ describe('libp2p state machine (fsm)', () => {
expect(2).checks(done) expect(2).checks(done)
sinon.stub(node._switch, 'stop').callsArgWith(0, error) sinon.stub(node._switch, 'stop').callsArgWith(0, error)
node.start() node.start(() => {})
}) })
it('should noop when starting a started node', (done) => { it('should noop when starting a started node', (done) => {
@@ -89,17 +89,17 @@ describe('libp2p state machine (fsm)', () => {
}) })
node.once('start', () => { node.once('start', () => {
node.once('stop', done) node.once('stop', done)
node.stop() node.stop(() => {})
}) })
// start the started node // start the started node
node.start() node.start(() => {})
}) })
node.start() node.start(() => {})
}) })
it('should error on start with no transports', (done) => { it('should error on start with no transports', (done) => {
let transports = node._modules.transport const transports = node._modules.transport
node._modules.transport = null node._modules.transport = null
node.on('stop', () => { node.on('stop', () => {
@@ -115,7 +115,7 @@ describe('libp2p state machine (fsm)', () => {
expect(2).checks(done) expect(2).checks(done)
node.start() node.start(() => {})
}) })
it('should not start if the switch fails to start', (done) => { it('should not start if the switch fails to start', (done) => {
@@ -150,7 +150,7 @@ describe('libp2p state machine (fsm)', () => {
}) })
}) })
node.stop() node.stop(() => {})
}) })
it('should not dial (fsm) when the node is stopped', (done) => { it('should not dial (fsm) when the node is stopped', (done) => {
@@ -162,7 +162,7 @@ describe('libp2p state machine (fsm)', () => {
}) })
}) })
node.stop() node.stop(() => {})
}) })
}) })
}) })

View File

@@ -5,34 +5,24 @@ const chai = require('chai')
chai.use(require('dirty-chai')) chai.use(require('dirty-chai'))
const expect = chai.expect const expect = chai.expect
const sinon = require('sinon') const sinon = require('sinon')
const signalling = require('libp2p-webrtc-star/src/sig-server')
const parallel = require('async/parallel') const parallel = require('async/parallel')
const crypto = require('crypto') const crypto = require('crypto')
const createNode = require('./utils/create-node') const createNode = require('./utils/create-node')
const echo = require('./utils/echo') const echo = require('./utils/echo')
const { WRTC_RENDEZVOUS_MULTIADDR } = require('./utils/constants')
describe('peer discovery', () => { describe('peer discovery', () => {
let nodeA let nodeA
let nodeB let nodeB
let nodeC let nodeC
let port = 24642
let ss
function setup (options) { function setup (options) {
before((done) => { before((done) => {
port++
parallel([ parallel([
(cb) => {
signalling.start({ port: port }, (err, server) => {
expect(err).to.not.exist()
ss = server
cb()
})
},
(cb) => createNode([ (cb) => createNode([
'/ip4/0.0.0.0/tcp/0', '/ip4/0.0.0.0/tcp/0',
`/ip4/127.0.0.1/tcp/${port}/ws/p2p-webrtc-star` `${WRTC_RENDEZVOUS_MULTIADDR.toString()}/p2p-webrtc-star`
], options, (err, node) => { ], options, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeA = node nodeA = node
@@ -41,7 +31,7 @@ describe('peer discovery', () => {
}), }),
(cb) => createNode([ (cb) => createNode([
'/ip4/0.0.0.0/tcp/0', '/ip4/0.0.0.0/tcp/0',
`/ip4/127.0.0.1/tcp/${port}/ws/p2p-webrtc-star` `${WRTC_RENDEZVOUS_MULTIADDR.toString()}/p2p-webrtc-star`
], options, (err, node) => { ], options, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeB = node nodeB = node
@@ -50,7 +40,7 @@ describe('peer discovery', () => {
}), }),
(cb) => createNode([ (cb) => createNode([
'/ip4/0.0.0.0/tcp/0', '/ip4/0.0.0.0/tcp/0',
`/ip4/127.0.0.1/tcp/${port}/ws/p2p-webrtc-star` `${WRTC_RENDEZVOUS_MULTIADDR.toString()}/p2p-webrtc-star`
], options, (err, node) => { ], options, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
nodeC = node nodeC = node
@@ -64,8 +54,7 @@ describe('peer discovery', () => {
parallel([ parallel([
(cb) => nodeA.stop(cb), (cb) => nodeA.stop(cb),
(cb) => nodeB.stop(cb), (cb) => nodeB.stop(cb),
(cb) => nodeC.stop(cb), (cb) => nodeC.stop(cb)
(cb) => ss.stop(cb)
], done) ], done)
}) })
@@ -83,7 +72,7 @@ describe('peer discovery', () => {
stop: sinon.stub().callsArg(0) stop: sinon.stub().callsArg(0)
} }
const options = { modules: { peerDiscovery: [ mockDiscovery ] } } const options = { modules: { peerDiscovery: [mockDiscovery] } }
createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -106,7 +95,7 @@ describe('peer discovery', () => {
const MockDiscovery = sinon.stub().returns(mockDiscovery) const MockDiscovery = sinon.stub().returns(mockDiscovery)
const options = { modules: { peerDiscovery: [ MockDiscovery ] } } const options = { modules: { peerDiscovery: [MockDiscovery] } }
createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -131,7 +120,7 @@ describe('peer discovery', () => {
const enabled = sinon.stub().returns(true) const enabled = sinon.stub().returns(true)
const options = { const options = {
modules: { peerDiscovery: [ mockDiscovery ] }, modules: { peerDiscovery: [mockDiscovery] },
config: { config: {
peerDiscovery: { peerDiscovery: {
mockDiscovery: { mockDiscovery: {
@@ -167,7 +156,7 @@ describe('peer discovery', () => {
const disabled = sinon.stub().returns(false) const disabled = sinon.stub().returns(false)
const options = { const options = {
modules: { peerDiscovery: [ mockDiscovery ] }, modules: { peerDiscovery: [mockDiscovery] },
config: { config: {
peerDiscovery: { peerDiscovery: {
mockDiscovery: { mockDiscovery: {
@@ -203,7 +192,7 @@ describe('peer discovery', () => {
MockDiscovery.tag = 'mockDiscovery' MockDiscovery.tag = 'mockDiscovery'
const options = { const options = {
modules: { peerDiscovery: [ MockDiscovery ] }, modules: { peerDiscovery: [MockDiscovery] },
config: { config: {
peerDiscovery: { peerDiscovery: {
mockDiscovery: { mockDiscovery: {
@@ -239,7 +228,7 @@ describe('peer discovery', () => {
} }
const options = { const options = {
modules: { peerDiscovery: [ mockDiscovery ] }, modules: { peerDiscovery: [mockDiscovery] },
config: { config: {
peerDiscovery: { peerDiscovery: {
mockDiscovery: { enabled: true } mockDiscovery: { enabled: true }
@@ -259,6 +248,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', () => { describe('MulticastDNS', () => {
setup({ setup({
config: { config: {
@@ -278,7 +294,7 @@ describe('peer discovery', () => {
}) })
it('find peers', function (done) { it('find peers', function (done) {
let expectedPeers = new Set([ const expectedPeers = new Set([
nodeB.peerInfo.id.toB58String(), nodeB.peerInfo.id.toB58String(),
nodeC.peerInfo.id.toB58String() nodeC.peerInfo.id.toB58String()
]) ])
@@ -316,7 +332,7 @@ describe('peer discovery', () => {
it('find peers', function (done) { it('find peers', function (done) {
this.timeout(20e3) this.timeout(20e3)
let expectedPeers = new Set([ const expectedPeers = new Set([
nodeB.peerInfo.id.toB58String(), nodeB.peerInfo.id.toB58String(),
nodeC.peerInfo.id.toB58String() nodeC.peerInfo.id.toB58String()
]) ])
@@ -358,7 +374,7 @@ describe('peer discovery', () => {
}) })
it('find peers', function (done) { it('find peers', function (done) {
let expectedPeers = new Set([ const expectedPeers = new Set([
nodeB.peerInfo.id.toB58String(), nodeB.peerInfo.id.toB58String(),
nodeC.peerInfo.id.toB58String() nodeC.peerInfo.id.toB58String()
]) ])
@@ -396,6 +412,7 @@ describe('peer discovery', () => {
randomWalk: { randomWalk: {
enabled: true, enabled: true,
queriesPerPeriod: 1, queriesPerPeriod: 1,
delay: 100,
interval: 200, // start the query sooner interval: 200, // start the query sooner
timeout: 3000 timeout: 3000
} }
@@ -404,7 +421,7 @@ describe('peer discovery', () => {
}) })
it('find peers through the dht', function (done) { it('find peers through the dht', function (done) {
let expectedPeers = new Set([ const expectedPeers = new Set([
nodeB.peerInfo.id.toB58String(), nodeB.peerInfo.id.toB58String(),
nodeC.peerInfo.id.toB58String() nodeC.peerInfo.id.toB58String()
]) ])

View File

@@ -105,7 +105,7 @@ describe('.peerRouting', () => {
}) })
createNode('/ip4/0.0.0.0/tcp/0', { createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
peerRouting: [ delegate ] peerRouting: [delegate]
}, },
config: { config: {
dht: { dht: {
@@ -211,7 +211,7 @@ describe('.peerRouting', () => {
}) })
createNode('/ip4/0.0.0.0/tcp/0', { createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
peerRouting: [ delegate ] peerRouting: [delegate]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()

View File

@@ -24,7 +24,7 @@ describe('private network', () => {
config = { config = {
peerInfo, peerInfo,
modules: { modules: {
transport: [ WS ], transport: [WS],
dht: DHT dht: DHT
} }
} }
@@ -50,14 +50,14 @@ describe('private network', () => {
it('should create a libp2p node with a provided protector', () => { it('should create a libp2p node with a provided protector', () => {
let node let node
let protector = { const protector = {
psk: '123', psk: '123',
tag: '/psk/1.0.0', tag: '/psk/1.0.0',
protect: () => { } protect: () => { }
} }
expect(() => { expect(() => {
let options = defaultsDeep(config, { const options = defaultsDeep(config, {
modules: { modules: {
connProtector: protector connProtector: protector
} }
@@ -71,7 +71,7 @@ describe('private network', () => {
it('should throw an error if the protector does not have a protect method', () => { it('should throw an error if the protector does not have a protect method', () => {
expect(() => { expect(() => {
let options = defaultsDeep(config, { const options = defaultsDeep(config, {
modules: { modules: {
connProtector: { } connProtector: { }
} }

View File

@@ -11,23 +11,31 @@ const parallel = require('async/parallel')
const series = require('async/series') const series = require('async/series')
const _times = require('lodash.times') const _times = require('lodash.times')
const Floodsub = require('libp2p-floodsub')
const mergeOptions = require('merge-options')
const { codes } = require('../src/errors') const { codes } = require('../src/errors')
const createNode = require('./utils/create-node') const createNode = require('./utils/create-node')
function startTwo (callback) { function startTwo (options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}
const tasks = _times(2, () => (cb) => { const tasks = _times(2, () => (cb) => {
createNode('/ip4/0.0.0.0/tcp/0', { createNode('/ip4/0.0.0.0/tcp/0', mergeOptions({
config: { config: {
peerDiscovery: { peerDiscovery: {
mdns: { mdns: {
enabled: false enabled: false
} }
}, },
EXPERIMENTAL: { pubsub: {
pubsub: true enabled: true
} }
} }
}, (err, node) => { }, options), (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
node.start((err) => cb(err, node)) node.start((err) => cb(err, node))
}) })
@@ -47,22 +55,17 @@ function stopTwo (nodes, callback) {
], callback) ], callback)
} }
// There is a vast test suite on PubSub through js-ipfs
// https://github.com/ipfs/interface-ipfs-core/blob/master/js/src/pubsub.js
// and libp2p-floodsub itself
// https://github.com/libp2p/js-libp2p-floodsub/tree/master/test
// TODO: consider if all or some of those should come here
describe('.pubsub', () => { describe('.pubsub', () => {
describe('.pubsub on (default)', (done) => { describe('.pubsub on (default)', () => {
it('start two nodes and send one message, then unsubscribe', (done) => { it('start two nodes and send one message, then unsubscribe', (done) => {
// Check the final series error, and the publish handler // Check the final series error, and the publish handler
expect(2).checks(done) expect(2).checks(done)
let nodes let nodes
const data = Buffer.from('test') const data = 'test'
const handler = (msg) => { const handler = (msg) => {
// verify the data is correct and mark the expect // verify the data is correct and mark the expect
expect(msg.data).to.eql(data).mark() expect(msg.data.toString()).to.eql(data).mark()
} }
series([ series([
@@ -72,15 +75,11 @@ describe('.pubsub', () => {
cb(err) cb(err)
}), }),
// subscribe on the first // subscribe on the first
(cb) => nodes[0].pubsub.subscribe('pubsub', handler, cb), (cb) => nodes[0].pubsub.subscribe('pubsub', handler, null, cb),
// Wait a moment before publishing // Wait a moment before publishing
(cb) => setTimeout(cb, 500), (cb) => setTimeout(cb, 500),
// publish on the second // publish on the second
(cb) => nodes[1].pubsub.publish('pubsub', data, cb), (cb) => nodes[1].pubsub.publish('pubsub', data, cb),
// ls subscripts
(cb) => nodes[1].pubsub.ls(cb),
// get subscribed peers
(cb) => nodes[1].pubsub.peers('pubsub', cb),
// Wait a moment before unsubscribing // Wait a moment before unsubscribing
(cb) => setTimeout(cb, 500), (cb) => setTimeout(cb, 500),
// unsubscribe on the first // unsubscribe on the first
@@ -110,6 +109,85 @@ describe('.pubsub', () => {
cb(err) cb(err)
}), }),
// subscribe on the first // subscribe on the first
(cb) => nodes[0].pubsub.subscribe('pubsub', handler, {}, cb),
// Wait a moment before publishing
(cb) => setTimeout(cb, 500),
// publish on the second
(cb) => nodes[1].pubsub.publish('pubsub', data, cb),
// ls subscripts
(cb) => nodes[1].pubsub.ls(cb),
// get subscribed peers
(cb) => nodes[1].pubsub.peers('pubsub', cb),
// Wait a moment before unsubscribing
(cb) => setTimeout(cb, 500),
// unsubscribe from all
(cb) => nodes[0].pubsub.unsubscribe('pubsub', null, cb),
// Verify unsubscribed
(cb) => {
nodes[0].pubsub.ls((err, topics) => {
expect(topics.length).to.eql(0).mark()
cb(err)
})
},
// Stop both nodes
(cb) => stopTwo(nodes, cb)
], (err) => {
// Verify there was no error, and mark the expect
expect(err).to.not.exist().mark()
})
})
it('publish should fail if data is not a buffer nor a string', (done) => {
createNode('/ip4/0.0.0.0/tcp/0', {
config: {
peerDiscovery: {
mdns: {
enabled: false
}
},
pubsub: {
enabled: true
}
}
}, (err, node) => {
expect(err).to.not.exist()
node.start((err) => {
expect(err).to.not.exist()
node.pubsub.publish('pubsub', 10, (err) => {
expect(err).to.exist()
expect(err.code).to.equal('ERR_DATA_IS_NOT_VALID')
done()
})
})
})
})
})
describe('.pubsub on using floodsub', () => {
it('start two nodes and send one message, then unsubscribe', (done) => {
// Check the final series error, and the publish handler
expect(2).checks(done)
let nodes
const data = Buffer.from('test')
const handler = (msg) => {
// verify the data is correct and mark the expect
expect(msg.data).to.eql(data).mark()
}
series([
// Start the nodes
(cb) => startTwo({
modules: {
pubsub: Floodsub
}
}, (err, _nodes) => {
nodes = _nodes
cb(err)
}),
// subscribe on the first
(cb) => nodes[0].pubsub.subscribe('pubsub', handler, cb), (cb) => nodes[0].pubsub.subscribe('pubsub', handler, cb),
// Wait a moment before publishing // Wait a moment before publishing
(cb) => setTimeout(cb, 500), (cb) => setTimeout(cb, 500),
@@ -118,11 +196,45 @@ describe('.pubsub', () => {
// Wait a moment before unsubscribing // Wait a moment before unsubscribing
(cb) => setTimeout(cb, 500), (cb) => setTimeout(cb, 500),
// unsubscribe on the first // unsubscribe on the first
(cb) => { (cb) => nodes[0].pubsub.unsubscribe('pubsub', handler, cb),
nodes[0].pubsub.unsubscribe('pubsub') // Stop both nodes
// Wait a moment to make sure the ubsubscribe-from-all worked (cb) => stopTwo(nodes, cb)
setTimeout(cb, 500) ], (err) => {
}, // Verify there was no error, and mark the expect
expect(err).to.not.exist().mark()
})
})
it('start two nodes and send one message, then unsubscribe without handler', (done) => {
// Check the final series error, and the publish handler
expect(3).checks(done)
let nodes
const data = Buffer.from('test')
const handler = (msg) => {
// verify the data is correct and mark the expect
expect(msg.data).to.eql(data).mark()
}
series([
// Start the nodes
(cb) => startTwo({
modules: {
pubsub: Floodsub
}
}, (err, _nodes) => {
nodes = _nodes
cb(err)
}),
// subscribe on the first
(cb) => nodes[0].pubsub.subscribe('pubsub', handler, cb),
// Wait a moment before publishing
(cb) => setTimeout(cb, 500),
// publish on the second
(cb) => nodes[1].pubsub.publish('pubsub', data, cb),
// Wait a moment before unsubscribing
(cb) => setTimeout(cb, 500),
// unsubscribe from all
(cb) => nodes[0].pubsub.unsubscribe('pubsub', null, cb),
// Verify unsubscribed // Verify unsubscribed
(cb) => { (cb) => {
nodes[0].pubsub.ls((err, topics) => { nodes[0].pubsub.ls((err, topics) => {
@@ -145,9 +257,12 @@ describe('.pubsub', () => {
enabled: false enabled: false
} }
}, },
EXPERIMENTAL: { pubsub: {
pubsub: true enabled: true
} }
},
modules: {
pubsub: Floodsub
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -155,9 +270,9 @@ describe('.pubsub', () => {
node.start((err) => { node.start((err) => {
expect(err).to.not.exist() expect(err).to.not.exist()
node.pubsub.publish('pubsub', 'datastr', (err) => { node.pubsub.publish('pubsub', 10, (err) => {
expect(err).to.exist() expect(err).to.exist()
expect(err.code).to.equal('ERR_DATA_IS_NOT_A_BUFFER') expect(err.code).to.equal('ERR_DATA_IS_NOT_VALID')
done() done()
}) })
@@ -174,9 +289,6 @@ describe('.pubsub', () => {
mdns: { mdns: {
enabled: false enabled: false
} }
},
EXPERIMENTAL: {
pubsub: false
} }
} }
}, (err, node) => { }, (err, node) => {
@@ -198,8 +310,8 @@ describe('.pubsub', () => {
enabled: false enabled: false
} }
}, },
EXPERIMENTAL: { pubsub: {
pubsub: true enabled: true
} }
} }
}, (err, node) => { }, (err, node) => {

View File

@@ -36,7 +36,7 @@ describe('stream muxing', () => {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ SPDY ] streamMuxer: [SPDY]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -46,7 +46,7 @@ describe('stream muxing', () => {
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ SPDY ] streamMuxer: [SPDY]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -72,7 +72,7 @@ describe('stream muxing', () => {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ Mplex ] streamMuxer: [Mplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -82,7 +82,7 @@ describe('stream muxing', () => {
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ Mplex ] streamMuxer: [Mplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -108,7 +108,7 @@ describe('stream muxing', () => {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ pMplex ] streamMuxer: [pMplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -118,7 +118,7 @@ describe('stream muxing', () => {
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ pMplex ] streamMuxer: [pMplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -146,7 +146,7 @@ describe('stream muxing', () => {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ Mplex ] streamMuxer: [Mplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -156,7 +156,7 @@ describe('stream muxing', () => {
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ SPDY, Mplex ] streamMuxer: [SPDY, Mplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -184,7 +184,7 @@ describe('stream muxing', () => {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ Mplex ] streamMuxer: [Mplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -194,7 +194,7 @@ describe('stream muxing', () => {
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ pMplex ] streamMuxer: [pMplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -222,7 +222,7 @@ describe('stream muxing', () => {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ SPDY, Mplex ] streamMuxer: [SPDY, Mplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -232,7 +232,7 @@ describe('stream muxing', () => {
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ Mplex, SPDY ] streamMuxer: [Mplex, SPDY]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -260,7 +260,7 @@ describe('stream muxing', () => {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ SPDY, pMplex ] streamMuxer: [SPDY, pMplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -270,7 +270,7 @@ describe('stream muxing', () => {
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ pMplex, SPDY ] streamMuxer: [pMplex, SPDY]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -298,7 +298,7 @@ describe('stream muxing', () => {
parallel([ parallel([
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ SPDY ] streamMuxer: [SPDY]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()
@@ -308,7 +308,7 @@ describe('stream muxing', () => {
}), }),
(cb) => createNode('/ip4/0.0.0.0/tcp/0', { (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
modules: { modules: {
streamMuxer: [ Mplex ] streamMuxer: [Mplex]
} }
}, (err, node) => { }, (err, node) => {
expect(err).to.not.exist() expect(err).to.not.exist()

View File

@@ -55,7 +55,7 @@ describe('transports', () => {
const b = new Node({ const b = new Node({
peerInfo: peerInfo, peerInfo: peerInfo,
modules: { modules: {
streamMuxer: [ Mplex ] streamMuxer: [Mplex]
} }
}) })
expect(b._modules.streamMuxer).to.eql([require('pull-mplex')]) expect(b._modules.streamMuxer).to.eql([require('pull-mplex')])

View File

@@ -6,7 +6,6 @@ chai.use(require('dirty-chai'))
const expect = chai.expect const expect = chai.expect
const parallel = require('async/parallel') const parallel = require('async/parallel')
const series = require('async/series') const series = require('async/series')
const signalling = require('libp2p-webrtc-star/src/sig-server')
const rendezvous = require('libp2p-websocket-star-rendezvous') const rendezvous = require('libp2p-websocket-star-rendezvous')
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const WS = require('libp2p-websockets') const WS = require('libp2p-websockets')
@@ -18,6 +17,8 @@ const createNode = require('./utils/create-node.js')
const tryEcho = require('./utils/try-echo') const tryEcho = require('./utils/try-echo')
const echo = require('./utils/echo') const echo = require('./utils/echo')
const { WRTC_RENDEZVOUS_MULTIADDR } = require('./utils/constants')
describe('transports', () => { describe('transports', () => {
describe('TCP only', () => { describe('TCP only', () => {
let nodeA let nodeA
@@ -409,24 +410,17 @@ describe('transports', () => {
let nodeWS let nodeWS
let nodeWebRTCStar let nodeWebRTCStar
let ss
before(function (done) { before(function (done) {
this.timeout(5 * 1000) this.timeout(5 * 1000)
parallel([ parallel([
(cb) => signalling.start({ port: 24642 }, (err, server) => {
expect(err).to.not.exist()
ss = server
cb()
}),
(cb) => { (cb) => {
const wstar = new WRTCStar({ wrtc: wrtc }) const wstar = new WRTCStar({ wrtc: wrtc })
createNode([ createNode([
'/ip4/0.0.0.0/tcp/0', '/ip4/0.0.0.0/tcp/0',
'/ip4/127.0.0.1/tcp/25011/ws', '/ip4/127.0.0.1/tcp/25011/ws',
'/ip4/127.0.0.1/tcp/24642/ws/p2p-webrtc-star' `${WRTC_RENDEZVOUS_MULTIADDR.toString()}/p2p-webrtc-star`
], { ], {
modules: { modules: {
transport: [ transport: [
@@ -479,12 +473,11 @@ describe('transports', () => {
node.handle('/echo/1.0.0', echo) node.handle('/echo/1.0.0', echo)
node.start(cb) node.start(cb)
}), }),
(cb) => { (cb) => {
const wstar = new WRTCStar({ wrtc: wrtc }) const wstar = new WRTCStar({ wrtc: wrtc })
createNode([ createNode([
'/ip4/127.0.0.1/tcp/24642/ws/p2p-webrtc-star' `${WRTC_RENDEZVOUS_MULTIADDR.toString()}/p2p-webrtc-star`
], { ], {
modules: { modules: {
transport: [wstar], transport: [wstar],
@@ -515,8 +508,7 @@ describe('transports', () => {
(cb) => nodeAll.stop(cb), (cb) => nodeAll.stop(cb),
(cb) => nodeTCP.stop(cb), (cb) => nodeTCP.stop(cb),
(cb) => nodeWS.stop(cb), (cb) => nodeWS.stop(cb),
(cb) => nodeWebRTCStar.stop(cb), (cb) => nodeWebRTCStar.stop(cb)
(cb) => ss.stop(cb)
], done) ], done)
}) })

View File

@@ -8,6 +8,7 @@ const SPDY = require('libp2p-spdy')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const PULLMPLEX = require('pull-mplex') const PULLMPLEX = require('pull-mplex')
const KadDHT = require('libp2p-kad-dht') const KadDHT = require('libp2p-kad-dht')
const GossipSub = require('libp2p-gossipsub')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
const defaultsDeep = require('@nodeutils/defaults-deep') const defaultsDeep = require('@nodeutils/defaults-deep')
const libp2p = require('../..') const libp2p = require('../..')
@@ -57,7 +58,8 @@ class Node extends libp2p {
wsStar.discovery, wsStar.discovery,
Bootstrap Bootstrap
], ],
dht: KadDHT dht: KadDHT,
pubsub: GossipSub
}, },
config: { config: {
peerDiscovery: { peerDiscovery: {
@@ -88,8 +90,8 @@ class Node extends libp2p {
}, },
enabled: false enabled: false
}, },
EXPERIMENTAL: { pubsub: {
pubsub: false enabled: false
} }
} }
} }

View File

@@ -6,6 +6,7 @@ const WS = require('libp2p-websockets')
const Bootstrap = require('libp2p-bootstrap') const Bootstrap = require('libp2p-bootstrap')
const SPDY = require('libp2p-spdy') const SPDY = require('libp2p-spdy')
const KadDHT = require('libp2p-kad-dht') const KadDHT = require('libp2p-kad-dht')
const GossipSub = require('libp2p-gossipsub')
const MPLEX = require('libp2p-mplex') const MPLEX = require('libp2p-mplex')
const PULLMPLEX = require('pull-mplex') const PULLMPLEX = require('pull-mplex')
const SECIO = require('libp2p-secio') const SECIO = require('libp2p-secio')
@@ -52,7 +53,8 @@ class Node extends libp2p {
MulticastDNS, MulticastDNS,
Bootstrap Bootstrap
], ],
dht: KadDHT dht: KadDHT,
pubsub: GossipSub
}, },
config: { config: {
peerDiscovery: { peerDiscovery: {
@@ -81,8 +83,8 @@ class Node extends libp2p {
}, },
enabled: true enabled: true
}, },
EXPERIMENTAL: { pubsub: {
pubsub: false enabled: false
} }
} }
} }