mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-07-08 13:21:34 +00:00
Compare commits
104 Commits
Author | SHA1 | Date | |
---|---|---|---|
c7f20383d9 | |||
a86a95c19f | |||
1b7360fcfa | |||
67ca37c356 | |||
0870068ebc | |||
8563395d32 | |||
ef3cae5b5a | |||
f602cd9f79 | |||
ec956c1d42 | |||
c906f698e4 | |||
95f029eb7a | |||
38a68a01b7 | |||
c08aa517a0 | |||
8eac0f806e | |||
9fd092fddf | |||
87ddc25bee | |||
efb2b67f19 | |||
7c0b4daf49 | |||
aeb3ae1301 | |||
97df76f9c5 | |||
3ad696445d | |||
30bec8d53b | |||
db2b5300b4 | |||
df4d60df88 | |||
29cc0afc64 | |||
2504cbeb26 | |||
468cc421fa | |||
0b991e19d1 | |||
2a81fc75a8 | |||
e04c249d4d | |||
4a995613ae | |||
066c4976bb | |||
429cf9c361 | |||
01c20f8ab9 | |||
c8adce3906 | |||
6e3c5427de | |||
3e140d2c47 | |||
f0858c20bd | |||
ef9c2a6296 | |||
090d75d383 | |||
04a4ffcc36 | |||
7f7278fe82 | |||
2c03222136 | |||
65857838f9 | |||
cad173e3bd | |||
19ce266f1b | |||
a2a85eb8d6 | |||
b674bccfc2 | |||
4ae8e43b8b | |||
6b059a0ba8 | |||
363259f832 | |||
56dbd9e7c2 | |||
314b45897f | |||
0a6f1faf82 | |||
5a638794c5 | |||
d3b0f380bb | |||
c1ccc1d5fb | |||
49484f734c | |||
43345cee6d | |||
06eb7a19f3 | |||
76ef5fdf50 | |||
c8f4eaf982 | |||
abe5c8c171 | |||
a01221f89c | |||
4224c1fe61 | |||
019cd4715d | |||
f877609a58 | |||
4a7eb34056 | |||
fb0c64f9d4 | |||
7ef1c0a598 | |||
c23f147bda | |||
c1a471793d | |||
39145e9a91 | |||
74b6f5f793 | |||
a4e5247ca2 | |||
0349119536 | |||
5eb97a66df | |||
8da166e2a9 | |||
f583b1b2cd | |||
5f5ee98a50 | |||
7df0074667 | |||
b14a2ff717 | |||
7b78736e24 | |||
2b02be08f7 | |||
32cb59fa18 | |||
53cce8d8f6 | |||
b6e41922a7 | |||
195673c2d3 | |||
647d10fe10 | |||
73f2f6d050 | |||
2d40f3ad6a | |||
13452c959e | |||
547c4dbd33 | |||
606fa737b8 | |||
e3870f3f8a | |||
45b0f61824 | |||
0263b899e3 | |||
fb92e672ee | |||
2e326e1619 | |||
e4e9761c99 | |||
a5b7de1b6b | |||
9a7a381f85 | |||
1f00855e6e | |||
8aa932a491 |
71
.aegir.js
Normal file
71
.aegir.js
Normal file
@ -0,0 +1,71 @@
|
||||
'use strict'
|
||||
|
||||
const PeerInfo = require('peer-info')
|
||||
const PeerId = require('peer-id')
|
||||
const pull = require('pull-stream')
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
const rawPeer = require('./test/fixtures/test-peer.json')
|
||||
const Node = require('./test/utils/bundle.node.js')
|
||||
const sigServer = require('libp2p-webrtc-star/src/sig-server')
|
||||
const WebSocketStarRendezvous = require('libp2p-websocket-star-rendezvous')
|
||||
|
||||
let wrtcRendezvous
|
||||
let wsRendezvous
|
||||
let node
|
||||
|
||||
const before = (done) => {
|
||||
parallel([
|
||||
(cb) => {
|
||||
sigServer.start({
|
||||
port: 15555
|
||||
// cryptoChallenge: true TODO: needs https://github.com/libp2p/js-libp2p-webrtc-star/issues/128
|
||||
}, (err, server) => {
|
||||
if (err) { return cb(err) }
|
||||
wrtcRendezvous = server
|
||||
cb()
|
||||
})
|
||||
},
|
||||
(cb) => {
|
||||
WebSocketStarRendezvous.start({
|
||||
port: 14444,
|
||||
refreshPeerListIntervalMS: 1000,
|
||||
strictMultiaddr: false,
|
||||
cryptoChallenge: true
|
||||
}, (err, _server) => {
|
||||
if (err) { return cb(err) }
|
||||
wsRendezvous = _server
|
||||
cb()
|
||||
})
|
||||
},
|
||||
(cb) => {
|
||||
PeerId.createFromJSON(rawPeer, (err, peerId) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
const peer = new PeerInfo(peerId)
|
||||
|
||||
peer.multiaddrs.add('/ip4/127.0.0.1/tcp/9200/ws')
|
||||
|
||||
node = new Node(peer)
|
||||
node.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
|
||||
node.start(cb)
|
||||
})
|
||||
}
|
||||
], done)
|
||||
}
|
||||
|
||||
const after = (done) => {
|
||||
setTimeout(() => parallel(
|
||||
[node, wrtcRendezvous, wsRendezvous].map((s) => {
|
||||
return (cb) => s.stop(cb)
|
||||
})
|
||||
, done), 2000)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hooks: {
|
||||
pre: before,
|
||||
post: after
|
||||
}
|
||||
}
|
26
.gitignore
vendored
26
.gitignore
vendored
@ -1,7 +1,14 @@
|
||||
docs
|
||||
**/node_modules/
|
||||
**/*.log
|
||||
test/repo-tests*
|
||||
**/bundle.js
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
coverage
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
@ -20,16 +27,17 @@ coverage
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
build
|
||||
|
||||
# Dependency directory
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
lib
|
||||
dist
|
||||
test/test-data/go-ipfs-repo/LOCK
|
||||
test/test-data/go-ipfs-repo/LOG
|
||||
test/test-data/go-ipfs-repo/LOG.old
|
||||
|
||||
# while testing npm5
|
||||
package-lock.json
|
||||
|
20
.travis.yml
20
.travis.yml
@ -1,23 +1,19 @@
|
||||
# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
|
||||
sudo: false
|
||||
language: node_js
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- node_js: 4
|
||||
env: CXX=g++-4.8
|
||||
- node_js: 6
|
||||
env:
|
||||
- SAUCE=true
|
||||
- CXX=g++-4.8
|
||||
- node_js: stable
|
||||
env: CXX=g++-4.8
|
||||
|
||||
# Make sure we have new NPM.
|
||||
before_install:
|
||||
- npm install -g npm
|
||||
- node_js: 8
|
||||
env: CXX=g++-4.8
|
||||
# - node_js: stable
|
||||
# env: CXX=g++-4.8
|
||||
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
- npm run test
|
||||
- npm run coverage
|
||||
|
||||
before_script:
|
||||
@ -33,4 +29,4 @@ addons:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
- g++-4.8
|
||||
|
120
CHANGELOG.md
120
CHANGELOG.md
@ -1,3 +1,123 @@
|
||||
<a name="0.14.0"></a>
|
||||
# [0.14.0](https://github.com/libp2p/js-libp2p/compare/v0.13.3...v0.14.0) (2017-12-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove innactive multiaddrs ([#131](https://github.com/libp2p/js-libp2p/issues/131)) ([1b7360f](https://github.com/libp2p/js-libp2p/commit/1b7360f))
|
||||
|
||||
|
||||
|
||||
<a name="0.13.3"></a>
|
||||
## [0.13.3](https://github.com/libp2p/js-libp2p/compare/v0.13.2...v0.13.3) (2017-12-01)
|
||||
|
||||
|
||||
|
||||
<a name="0.13.2"></a>
|
||||
## [0.13.2](https://github.com/libp2p/js-libp2p/compare/v0.13.1...v0.13.2) (2017-11-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Bring libp2p-websocket-star to the Transports family! 🌟 ([#122](https://github.com/libp2p/js-libp2p/issues/122)) ([95f029e](https://github.com/libp2p/js-libp2p/commit/95f029e))
|
||||
|
||||
|
||||
|
||||
<a name="0.13.1"></a>
|
||||
## [0.13.1](https://github.com/libp2p/js-libp2p/compare/v0.13.0...v0.13.1) (2017-11-12)
|
||||
|
||||
|
||||
|
||||
<a name="0.13.0"></a>
|
||||
# [0.13.0](https://github.com/libp2p/js-libp2p/compare/v0.12.4...v0.13.0) (2017-10-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* enable and test Circuit Relay ([29cc0af](https://github.com/libp2p/js-libp2p/commit/29cc0af))
|
||||
|
||||
|
||||
|
||||
<a name="0.12.4"></a>
|
||||
## [0.12.4](https://github.com/libp2p/js-libp2p/compare/v0.12.3...v0.12.4) (2017-09-07)
|
||||
|
||||
|
||||
|
||||
<a name="0.12.3"></a>
|
||||
## [0.12.3](https://github.com/libp2p/js-libp2p/compare/v0.12.2...v0.12.3) (2017-09-07)
|
||||
|
||||
|
||||
|
||||
<a name="0.12.2"></a>
|
||||
## [0.12.2](https://github.com/libp2p/js-libp2p/compare/v0.12.0...v0.12.2) (2017-09-07)
|
||||
|
||||
|
||||
|
||||
<a name="0.12.1"></a>
|
||||
## [0.12.1](https://github.com/libp2p/js-libp2p/compare/v0.12.0...v0.12.1) (2017-09-07)
|
||||
|
||||
|
||||
|
||||
<a name="0.12.0"></a>
|
||||
# [0.12.0](https://github.com/libp2p/js-libp2p/compare/v0.11.0...v0.12.0) (2017-09-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* p2p addrs situation ([#119](https://github.com/libp2p/js-libp2p/issues/119)) ([cad173e](https://github.com/libp2p/js-libp2p/commit/cad173e))
|
||||
|
||||
|
||||
|
||||
<a name="0.11.0"></a>
|
||||
# [0.11.0](https://github.com/libp2p/js-libp2p/compare/v0.10.2...v0.11.0) (2017-07-22)
|
||||
|
||||
|
||||
|
||||
<a name="0.10.2"></a>
|
||||
## [0.10.2](https://github.com/libp2p/js-libp2p/compare/v0.10.1...v0.10.2) (2017-07-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* circle ci, thanks victor! ([4224c1f](https://github.com/libp2p/js-libp2p/commit/4224c1f))
|
||||
|
||||
|
||||
|
||||
<a name="0.10.1"></a>
|
||||
## [0.10.1](https://github.com/libp2p/js-libp2p/compare/v0.10.0...v0.10.1) (2017-07-10)
|
||||
|
||||
|
||||
|
||||
<a name="0.10.0"></a>
|
||||
# [0.10.0](https://github.com/libp2p/js-libp2p/compare/v0.9.1...v0.10.0) (2017-07-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* added missing dep async ([45b0f61](https://github.com/libp2p/js-libp2p/commit/45b0f61))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* state events and query changes ([#100](https://github.com/libp2p/js-libp2p/issues/100)) ([73f2f6d](https://github.com/libp2p/js-libp2p/commit/73f2f6d))
|
||||
|
||||
|
||||
|
||||
<a name="0.9.1"></a>
|
||||
## [0.9.1](https://github.com/libp2p/js-libp2p/compare/v0.9.0...v0.9.1) (2017-04-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* do not use assert in async funcs ([#88](https://github.com/libp2p/js-libp2p/issues/88)) ([2e326e1](https://github.com/libp2p/js-libp2p/commit/2e326e1))
|
||||
|
||||
|
||||
|
||||
<a name="0.9.0"></a>
|
||||
# [0.9.0](https://github.com/libp2p/js-libp2p/compare/v0.8.0...v0.9.0) (2017-04-06)
|
||||
|
||||
|
||||
|
||||
<a name="0.8.0"></a>
|
||||
# [0.8.0](https://github.com/libp2p/js-libp2p/compare/v0.7.0...v0.8.0) (2017-03-31)
|
||||
|
||||
|
3
CODE_OF_CONDUCT.md
Normal file
3
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Contributor Code of Conduct
|
||||
|
||||
The `js-libp2p` project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md)
|
9
CONTRIBUTING.md
Normal file
9
CONTRIBUTING.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Contributing guidelines
|
||||
|
||||
libp2p as a project, including js-libp2p and all of its modules, follows the [standard IPFS Community contributing guidelines](https://github.com/ipfs/community/blob/master/contribution-guidelines.md).
|
||||
|
||||
We also adhere to the [IPFS JavaScript Community contributing guidelines](https://github.com/ipfs/community/blob/master/js-project-guidelines.md) which provide additional information of how to collaborate and contribute in the JavaScript implementation of libp2p.
|
||||
|
||||
We appreciate your time and attention for going over these. Please open an issue on [ipfs/community](https://github.com/ipfs/community) if you have any question.
|
||||
|
||||
Thank you.
|
43
ISSUE_TEMPLATE.md
Normal file
43
ISSUE_TEMPLATE.md
Normal file
@ -0,0 +1,43 @@
|
||||
<!--
|
||||
Thank you for reporting an issue.
|
||||
|
||||
This issue tracker is for bugs and issues found within the JavaScript implementation of libp2p.
|
||||
If you require more general support please file an issue on our discuss forum. https://discuss.ipfs.io/
|
||||
|
||||
Please fill in as much of the template below as you're able.
|
||||
|
||||
Version: package.json version or the commit you have installed.
|
||||
Platform: output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows). If using in a Browser, please share the browser version as well.
|
||||
Subsystem: if known, please specify affected core module name (e.g Transports, SECIO, etc).
|
||||
|
||||
If possible, please provide code that demonstrates the problem, keeping it as
|
||||
simple and free of external dependencies as you are able.
|
||||
-->
|
||||
|
||||
- **Version**:
|
||||
- **Platform**:
|
||||
- **Subsystem**:
|
||||
|
||||
<!-- Bug, Feature, Question, Enhancement, Etc -->
|
||||
#### Type:
|
||||
|
||||
<!--
|
||||
One of following:
|
||||
Critical - System crash, application panic.
|
||||
High - The main functionality of the application does not work, API breakage, repo format breakage, etc.
|
||||
Medium - A non-essential functionality does not work, performance issues, etc.
|
||||
Low - An optional functionality does not work.
|
||||
Very Low - Translation or documentation mistake. Something that won't give anyone a bad day.
|
||||
-->
|
||||
#### Severity:
|
||||
|
||||
#### Description:
|
||||
|
||||
#### Steps to reproduce the error:
|
||||
|
||||
<!--
|
||||
This is for you! Please read, and then delete this text before posting it.
|
||||
The js-ipfs issues are only for bug reports and directly actionable features.
|
||||
|
||||
Read https://github.com/ipfs/community/blob/master/contributing.md#reporting-issues if your issue doesn't fit either of those categories.
|
||||
-->
|
140
README.md
140
README.md
@ -1,13 +1,36 @@
|
||||

|
||||
<h1 align="center">
|
||||
<a href="libp2p.io"><img width="250" src="https://github.com/libp2p/libp2p/blob/master/logo/alternates/libp2p-logo-alt-2.png?raw=true" alt="libp2p hex logo" /></a>
|
||||
</h1>
|
||||
|
||||
[](http://ipn.io)
|
||||
[](http://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://travis-ci.org/libp2p/js-libp2p)
|
||||

|
||||
[](https://david-dm.org/libp2p/js-libp2p)
|
||||
[](https://github.com/feross/standard)
|
||||
<h3 align="center">The JavaScript implementation of the libp2p Networking Stack.</h3>
|
||||
|
||||
> libp2p is the networking stack of IPFS, a modular networking library to solve P2P application needs.
|
||||
<p align="center">
|
||||
<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-blue.svg?style=flat-square" /></a>
|
||||
<a href="http://webchat.freenode.net/?channels=%23ipfs"><img src="https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square" /></a>
|
||||
<a href="https://waffle.io/libp2p/libp2p"><img src="https://img.shields.io/badge/pm-waffle-blue.svg?style=flat-square" /></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/libp2p/js-libp2p"><img src="https://travis-ci.org/libp2p/js-libp2p.svg?branch=master" /></a>
|
||||
<a href="https://circleci.com/gh/libp2p/js-libp2p"><img src="https://circleci.com/gh/libp2p/js-libp2p.svg?style=svg" /></a>
|
||||
<a href="https://coveralls.io/github/libp2p/js-libp2p?branch=master"><img src="https://coveralls.io/repos/github/libp2p/js-libp2p/badge.svg?branch=master"></a>
|
||||
<br>
|
||||
<a href="https://david-dm.org/libp2p/js-libp2p"><img src="https://david-dm.org/libp2p/js-libp2p.svg?style=flat-square" /></a>
|
||||
<a href="https://github.com/feross/standard"><img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/RichardLitt/standard-readme"><img src="https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square" /></a>
|
||||
<a href=""><img src="https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square" /></a>
|
||||
<a href=""><img src="https://img.shields.io/badge/Node.js-%3E%3D6.0.0-orange.svg?style=flat-square" /></a>
|
||||
<br>
|
||||
</p>
|
||||
|
||||
### Project status
|
||||
|
||||
We've come a long way, but this project is still in Alpha, lots of development is happening, API might change, beware of the Dragons 🐉..
|
||||
|
||||
**Want to get started?** Check our [examples folder](/examples). You can check the development status at the [Waffle Board](https://waffle.io/libp2p/js-libp2p).
|
||||
|
||||
[](https://waffle.io/libp2p/js-libp2p/metrics/throughput)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@ -15,6 +38,7 @@
|
||||
- [Bundles](#bundles)
|
||||
- [Usage](#usage)
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [API](#api)
|
||||
- [Development](#development)
|
||||
- [Tests](#tests)
|
||||
@ -24,14 +48,14 @@
|
||||
|
||||
## Background
|
||||
|
||||
libp2p is the product of a long and arduous quest to understand the evolution of the Internet networking stack. In order to build P2P applications, dev have long had to made custom ad-hoc solutions to fit their needs, sometimes making some hard assumptions about their runtimes and the state of the network at the time of their development. Today, looking back more than 20 years, we see a clear pattern in the types of mechanisms built around the Internet Protocol, IP, which can be found throughout many layers of the OSI layer system, libp2p distils these mechanisms into flat categories and defines clear interfaces that once exposed, enable other protocols and applications to use and swap them, enabling upgradability and adaptability for the runtime, without breaking the API.
|
||||
libp2p is the product of a long and arduous quest to understand the evolution of the Internet networking stack. In order to build P2P applications, devs have long had to made custom ad-hoc solutions to fit their needs, sometimes making some hard assumptions about their runtimes and the state of the network at the time of their development. Today, looking back more than 20 years, we see a clear pattern in the types of mechanisms built around the Internet Protocol, IP, which can be found throughout many layers of the OSI layer system, libp2p distils these mechanisms into flat categories and defines clear interfaces that once exposed, enable other protocols and applications to use and swap them, enabling upgradability and adaptability for the runtime, without breaking the API.
|
||||
|
||||
We are in the process of writting better documentation, blog posts, tutorials and a formal specification. Today you can find:
|
||||
We are in the process of writing better documentation, blog posts, tutorials and a formal specification. Today you can find:
|
||||
|
||||
- [libp2p.io - The libp2p Website (WIP)](https://github.com/libp2p/website)
|
||||
- [libp2p.io](https://libp2p.io)
|
||||
- [Specification (WIP)](https://github.com/libp2p/specs)
|
||||
- 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)
|
||||
- [`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
|
||||
- [The overview of libp2p](https://github.com/libp2p/libp2p#description)
|
||||
|
||||
@ -41,8 +65,8 @@ To sum up, libp2p is a "network stack" -- a protocol suite -- that cleanly separ
|
||||
|
||||
With its modular nature, libp2p can be found being used in different projects with different sets of features, while preserving the same top level API. `js-libp2p` is only a skeleton and should not be installed directly, if you are looking for a prebundled libp2p stack, please check:
|
||||
|
||||
- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-libp2p-ipfs-nodejs) - The libp2p build used by js-ipfs when run in Node.js
|
||||
- [libp2p-ipfs-browser](https://github.com/ipfs/js-libp2p-ipfs-browser) - The libp2p build used by js-ipfs when run in a Browser (that supports WebRTC)
|
||||
- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-nodejs.js) - The libp2p build used by js-ipfs when run in Node.js
|
||||
- [libp2p-ipfs-browser](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-browser.js) - The libp2p build used by js-ipfs when run in a Browser (that supports WebRTC)
|
||||
|
||||
If you have developed a libp2p bundle, please consider submitting it to this list so that it can be found easily by the users of libp2p.
|
||||
|
||||
@ -56,6 +80,10 @@ npm install --save libp2p
|
||||
|
||||
## Usage
|
||||
|
||||
### [Tutorials and Examples](/examples)
|
||||
|
||||
You can find multiple examples on the [examples folder](/examples) that will guide you through using libp2p for several scenarios.
|
||||
|
||||
### Extending libp2p skeleton
|
||||
|
||||
libp2p becomes very simple and basically acts as a glue for every module that compose this library. Since it can be highly customized, it requires some setup. What we recommend is to have a libp2p build for the system you are developing taking into account in your needs (e.g. for a browser working version of libp2p that acts as the network layer of IPFS, we have a built and minified version that browsers can require).
|
||||
@ -75,6 +103,7 @@ const WS = require('libp2p-websockets')
|
||||
const spdy = require('libp2p-spdy')
|
||||
const secio = require('libp2p-secio')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const DHT = require('libp2p-kad-dht')
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (peerInfo, peerBook, options) {
|
||||
@ -94,8 +123,10 @@ class Node extends libp2p {
|
||||
]
|
||||
},
|
||||
discovery: [
|
||||
new MulticastDNS(peerInfo, 'your-identifier')
|
||||
]
|
||||
new MulticastDNS(peerInfo)
|
||||
],
|
||||
// DHT is passed as its own enabling PeerRouting, ContentRouting and DHT itself components
|
||||
dht: DHT
|
||||
}
|
||||
|
||||
super(modules, peerInfo, peerBook, options)
|
||||
@ -144,6 +175,22 @@ class Node extends libp2p {
|
||||
|
||||
`callback` is a function with the following `function (err) {}` signature, where `err` is an Error in case stopping the node fails.
|
||||
|
||||
#### `libp2p.peerRouting.findPeer(id, callback)`
|
||||
|
||||
> Looks up for multiaddrs of a peer in the DHT
|
||||
|
||||
- `id`: instance of [PeerId][]
|
||||
|
||||
#### `libp2p.contentRouting.findProviders(key, timeout, callback)`
|
||||
|
||||
- `key`: Buffer
|
||||
- `timeout`: Number miliseconds
|
||||
|
||||
#### `libp2p.contentRouting.provide(key, callback)`
|
||||
|
||||
- `key`: Buffer
|
||||
|
||||
|
||||
#### `libp2p.handle(protocol, handlerFunc [, matchFunc])`
|
||||
|
||||
> Handle new protocol
|
||||
@ -152,7 +199,7 @@ class Node extends libp2p {
|
||||
- `handlerFunc`: Function with signature `function (protocol, conn) {}`
|
||||
- `matchFunc`: Function for matching on protocol (exact matching, semver, etc). Default to exact match.
|
||||
|
||||
#### `libp2p.unhandle(protocol)
|
||||
#### `libp2p.unhandle(protocol)`
|
||||
|
||||
> Stop handling protocol
|
||||
|
||||
@ -176,7 +223,7 @@ class Node extends libp2p {
|
||||
|
||||
- `peer`: instance of [PeerInfo][]
|
||||
|
||||
#### `libp2p.isOn()`
|
||||
#### `libp2p.isStarted()`
|
||||
|
||||
> Check if libp2p is started
|
||||
|
||||
@ -194,15 +241,21 @@ class Node extends libp2p {
|
||||
|
||||
---------------------
|
||||
|
||||
`SOON™`
|
||||
`DHT methods exposed`
|
||||
|
||||
#### `libp2p.findPeers`
|
||||
#### `libp2p.dht.put(key, value, callback)`
|
||||
|
||||
#### `libp2p.findProviders`
|
||||
- `key`: Buffer
|
||||
- `value`: Buffer
|
||||
|
||||
#### `libp2p.record.put`
|
||||
#### `libp2p.dht.get(key, callback)`
|
||||
|
||||
#### `libp2p.record.get`
|
||||
- `key`: Buffer
|
||||
|
||||
#### `libp2p.dht.getMany(key, nVals, callback)`
|
||||
|
||||
- `key`: Buffer
|
||||
- `nVals`: Number
|
||||
|
||||
[PeerInfo]: https://github.com/libp2p/js-peer-info
|
||||
[PeerId]: https://github.com/libp2p/js-peer-id
|
||||
@ -210,6 +263,42 @@ class Node extends libp2p {
|
||||
[multiaddr]: https://github.com/multiformats/js-multiaddr
|
||||
[Connection]: https://github.com/libp2p/interface-connection
|
||||
|
||||
## Development
|
||||
|
||||
**Clone and install dependencies:**
|
||||
|
||||
```sh
|
||||
> git clone https://github.com/ipfs/js-ipfs.git
|
||||
> cd js-ipfs
|
||||
> npm install
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
#### Run unit tests
|
||||
|
||||
```sh
|
||||
# run all the unit tsts
|
||||
> npm test
|
||||
|
||||
# run just Node.js tests
|
||||
> npm run test:node
|
||||
|
||||
# run just Browser tests (Chrome)
|
||||
> npm run test:browser
|
||||
```
|
||||
|
||||
#### Run interop tests
|
||||
|
||||
```sh
|
||||
N/A
|
||||
```
|
||||
|
||||
#### Run benchmark tests
|
||||
|
||||
```sh
|
||||
N/A
|
||||
```
|
||||
|
||||
### Packages
|
||||
|
||||
@ -217,9 +306,6 @@ List of packages currently in existence for libp2p
|
||||
|
||||
| Package | Version | Dependencies | DevDependencies |
|
||||
|---------|---------|--------------|-----------------|
|
||||
| **Bundles** |
|
||||
| [`libp2p-ipfs-nodejs`](//github.com/ipfs/js-libp2p-ipfs-nodejs) | [](//github.com/ipfs/js-libp2p-ipfs-nodejs/releases) | [](https://david-dm.org/ipfs/js-libp2p-ipfs-nodejs) | [](https://david-dm.org/ipfs/js-libp2p-ipfs-nodejs?type=dev) |
|
||||
| [`libp2p-ipfs-browser`](//github.com/ipfs/js-libp2p-ipfs-browser) | [](//github.com/ipfs/js-libp2p-ipfs-browser/releases) | [](https://david-dm.org/ipfs/js-libp2p-ipfs-browser) | [](https://david-dm.org/ipfs/js-libp2p-ipfs-browser?type=dev) |
|
||||
| **Transports** |
|
||||
| [`libp2p-utp`](//github.com/libp2p/js-libp2p-utp) | [](//github.com/libp2p/js-libp2p-utp/releases) | [](https://david-dm.org/libp2p/js-libp2p-utp) | [](https://david-dm.org/libp2p/js-libp2p-utp?type=dev) |
|
||||
| [`libp2p-websockets`](//github.com/libp2p/js-libp2p-websockets) | [](//github.com/libp2p/js-libp2p-websockets/releases) | [](https://david-dm.org/libp2p/js-libp2p-websockets) | [](https://david-dm.org/libp2p/js-libp2p-websockets?type=dev) |
|
||||
@ -254,7 +340,7 @@ List of packages currently in existence for libp2p
|
||||
|
||||
The libp2p implementation in JavaScript is a work in progress. As such, there are a few things you can do right now to help out:
|
||||
|
||||
- Go through the modules and **check out existing issues**. This would be especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrasture behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically.
|
||||
- Go through the modules and **check out existing issues**. This would be especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically.
|
||||
- **Perform code reviews**. Most of this has been developed by @diasdavid, which means that more eyes will help a) speed the project along b) ensure quality and c) reduce possible future bugs.
|
||||
- **Add tests**. There can never be enough tests.
|
||||
|
||||
|
29
appveyor.yml
Normal file
29
appveyor.yml
Normal file
@ -0,0 +1,29 @@
|
||||
# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
|
||||
version: "{build}"
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "6"
|
||||
- nodejs_version: "8"
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
install:
|
||||
# Install Node.js
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
|
||||
# Upgrade npm
|
||||
- npm install -g npm
|
||||
|
||||
# Output our current versions for debugging
|
||||
- node --version
|
||||
- npm --version
|
||||
|
||||
# Install our package dependencies
|
||||
- npm install
|
||||
|
||||
test_script:
|
||||
- npm run test:node
|
||||
|
||||
build: off
|
2
ci/Jenkinsfile
vendored
Normal file
2
ci/Jenkinsfile
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
|
||||
javascript()
|
14
circle.yml
14
circle.yml
@ -5,8 +5,14 @@ machine:
|
||||
dependencies:
|
||||
pre:
|
||||
- google-chrome --version
|
||||
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
|
||||
- sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
|
||||
- curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
- for v in $(curl http://archive.ubuntu.com/ubuntu/pool/main/n/nss/ | grep "href=" | grep "libnss3.*deb\"" -o | grep -o "libnss3.*deb" | grep "3.28" | grep "14.04"); do curl -L -o $v http://archive.ubuntu.com/ubuntu/pool/main/n/nss/$v; done && rm libnss3-tools*_i386.deb libnss3-dev*_i386.deb
|
||||
- sudo dpkg -i google-chrome.deb || true
|
||||
- sudo dpkg -i libnss3*.deb || true
|
||||
- sudo apt-get update
|
||||
- sudo apt-get --only-upgrade install google-chrome-stable
|
||||
- google-chrome --version
|
||||
- sudo apt-get install -f || true
|
||||
- sudo dpkg -i libnss3*.deb
|
||||
- sudo apt-get install -f
|
||||
- sudo apt-get install --only-upgrade lsb-base
|
||||
- sudo dpkg -i google-chrome.deb
|
||||
- google-chrome --version
|
@ -1,6 +1,25 @@
|
||||
### Examples list
|
||||
# `js-libp2p` Examples and Tutorials
|
||||
|
||||
Here are some examples built with libp2p bundles.
|
||||
In this folder, you can find a variety of examples to help you get started in using js-libp2p, in Node.js and in the Browser. Every example as a specific purpose and some of each incorporate a full tutorial that you can follow through, helping you expand your knowledge about libp2p and p2p networks in general.
|
||||
|
||||
- https://github.com/ipfs/js-libp2p-ipfs/tree/master/examples/echo
|
||||
- https://github.com/ipfs/js-libp2p-ipfs/tree/master/examples/chat
|
||||
Let us know if you find any issue or if you want to contribute and add a new tutorial, feel welcome to submit a PR, thank you!
|
||||
|
||||
## Understanding how libp2p works
|
||||
|
||||
- [Transports](./transports)
|
||||
- [Protocol and Stream Muxing](./protocol-and-stream-muxing)
|
||||
- [Encrypted Communications](./encrypted-communications)
|
||||
- [Discovery Mechanisms](./discovery-mechanisms)
|
||||
- [Peer Routing](./peer-routing)
|
||||
- [Content Routing](./content-routing)
|
||||
- [PubSub](./pubsub)
|
||||
- [NAT Traversal](./nat-traversal)
|
||||
- Circuit Relay (future)
|
||||
- Naming (future)
|
||||
|
||||
## Other examples
|
||||
|
||||
- [Running libp2p in the Browser](./libp2p-in-the-browser)
|
||||
- Running libp2p in the Electron (future)
|
||||
- [The standard echo net example with libp2p](./echo)
|
||||
- [A simple chat app with](./chat)
|
||||
|
@ -1,4 +0,0 @@
|
||||
Using libp2p-swarm
|
||||
==================
|
||||
|
||||
|
@ -1,39 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var Swarm = require('libp2p-swarm')
|
||||
var tcp = require('libp2p-tcp')
|
||||
var multiaddr = require('multiaddr')
|
||||
var Id = require('peer-id')
|
||||
var Spdy = require('libp2p-spdy')
|
||||
var Libp2p = require('../../src')
|
||||
var Peer = require('peer-info')
|
||||
|
||||
// set up
|
||||
|
||||
var mh = multiaddr('/ip4/127.0.0.1/tcp/8010')
|
||||
var p = new Peer(Id.create(), [])
|
||||
var sw = new Swarm(p)
|
||||
|
||||
// create a libp2p node
|
||||
|
||||
var node = new Libp2p(sw)
|
||||
|
||||
node.swarm.addTransport('tcp', tcp, {multiaddr: mh}, {}, {port: 8010}, function () {
|
||||
// Ready to receive incoming connections
|
||||
|
||||
sw.addStreamMuxer('spdy', Spdy, {})
|
||||
|
||||
// dial to another node
|
||||
|
||||
var mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020')
|
||||
var p2 = new Peer(Id.create(), [mh2])
|
||||
|
||||
node.swarm.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) {
|
||||
if (err) {
|
||||
return console.error(err)
|
||||
}
|
||||
|
||||
console.log('-> connection is ready')
|
||||
process.stdin.pipe(conn).pipe(process.stdout)
|
||||
})
|
||||
})
|
@ -1,31 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var Swarm = require('libp2p-swarm')
|
||||
var tcp = require('libp2p-tcp')
|
||||
var multiaddr = require('multiaddr')
|
||||
var Id = require('peer-id')
|
||||
var Spdy = require('libp2p-spdy')
|
||||
var Libp2p = require('../../src')
|
||||
var Peer = require('peer-info')
|
||||
|
||||
// set up
|
||||
|
||||
var mh = multiaddr('/ip4/127.0.0.1/tcp/8020')
|
||||
var p = new Peer(Id.create(), [])
|
||||
var sw = new Swarm(p)
|
||||
|
||||
sw.addTransport('tcp', tcp, {multiaddr: mh}, {}, {port: 8020}, function () {
|
||||
// Ready to receive incoming connections
|
||||
|
||||
sw.addStreamMuxer('spdy', Spdy, {})
|
||||
|
||||
// create a libp2p node
|
||||
|
||||
var node = new Libp2p(sw)
|
||||
|
||||
// handle/mount a protocol
|
||||
|
||||
node.swarm.handleProtocol('/sparkles/1.0.0', function (conn) {
|
||||
process.stdin.pipe(conn).pipe(process.stdout)
|
||||
})
|
||||
})
|
1
examples/chat/README.md
Normal file
1
examples/chat/README.md
Normal file
@ -0,0 +1 @@
|
||||
# Chat example with libp2p
|
77
examples/chat/src/dialer.js
Normal file
77
examples/chat/src/dialer.js
Normal file
@ -0,0 +1,77 @@
|
||||
'use strict'
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const Node = require('./libp2p-bundle')
|
||||
const pull = require('pull-stream')
|
||||
const async = require('async')
|
||||
const Pushable = require('pull-pushable')
|
||||
const p = Pushable()
|
||||
let idListener
|
||||
|
||||
async.parallel([
|
||||
(callback) => {
|
||||
PeerId.createFromJSON(require('./peer-id-dialer'), (err, idDialer) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
callback(null, idDialer)
|
||||
})
|
||||
},
|
||||
(callback) => {
|
||||
PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
callback(null, idListener)
|
||||
})
|
||||
}
|
||||
], (err, ids) => {
|
||||
if (err) throw err
|
||||
const peerDialer = new PeerInfo(ids[0])
|
||||
peerDialer.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
const nodeDialer = new Node(peerDialer)
|
||||
|
||||
const peerListener = new PeerInfo(ids[1])
|
||||
idListener = ids[1]
|
||||
peerListener.multiaddrs.add('/ip4/127.0.0.1/tcp/10333')
|
||||
nodeDialer.start((err) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
console.log('Dialer ready, listening on:')
|
||||
|
||||
peerListener.multiaddrs.forEach((ma) => {
|
||||
console.log(ma.toString() + '/ipfs/' + idListener.toB58String())
|
||||
})
|
||||
|
||||
nodeDialer.dial(peerListener, '/chat/1.0.0', (err, conn) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
console.log('nodeA dialed to nodeB on protocol: /chat/1.0.0')
|
||||
console.log('Type a message and see what happens')
|
||||
// Write operation. Data sent as a buffer
|
||||
pull(
|
||||
p,
|
||||
conn
|
||||
)
|
||||
// Sink, data converted from buffer to utf8 string
|
||||
pull(
|
||||
conn,
|
||||
pull.map((data) => {
|
||||
return data.toString('utf8').replace('\n', '')
|
||||
}),
|
||||
pull.drain(console.log)
|
||||
)
|
||||
|
||||
process.stdin.setEncoding('utf8')
|
||||
process.openStdin().on('data', (chunk) => {
|
||||
var data = chunk.toString()
|
||||
p.push(data)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
80
examples/chat/src/libp2p-bundle.js
Normal file
80
examples/chat/src/libp2p-bundle.js
Normal file
@ -0,0 +1,80 @@
|
||||
'use strict'
|
||||
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const WS = require('libp2p-websockets')
|
||||
const Railing = require('libp2p-railing')
|
||||
const spdy = require('libp2p-spdy')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
const multiplex = require('libp2p-multiplex')
|
||||
const secio = require('libp2p-secio')
|
||||
const libp2p = require('../../..')
|
||||
|
||||
function mapMuxers (list) {
|
||||
return list.map((pref) => {
|
||||
if (typeof pref !== 'string') {
|
||||
return pref
|
||||
}
|
||||
switch (pref.trim().toLowerCase()) {
|
||||
case 'spdy': return spdy
|
||||
case 'multiplex': return multiplex
|
||||
default:
|
||||
throw new Error(pref + ' muxer not available')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getMuxers (muxers) {
|
||||
const muxerPrefs = process.env.LIBP2P_MUXER
|
||||
if (muxerPrefs && !muxers) {
|
||||
return mapMuxers(muxerPrefs.split(','))
|
||||
} else if (muxers) {
|
||||
return mapMuxers(muxers)
|
||||
} else {
|
||||
return [multiplex, spdy]
|
||||
}
|
||||
}
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (peerInfo, peerBook, options) {
|
||||
options = options || {}
|
||||
|
||||
const modules = {
|
||||
transport: [
|
||||
new TCP(),
|
||||
new WS()
|
||||
],
|
||||
connection: {
|
||||
muxer: getMuxers(options.muxer),
|
||||
crypto: [ secio ]
|
||||
},
|
||||
discovery: []
|
||||
}
|
||||
|
||||
if (options.dht) {
|
||||
modules.DHT = KadDHT
|
||||
}
|
||||
|
||||
if (options.mdns) {
|
||||
const mdns = new MulticastDNS(peerInfo, 'ipfs.local')
|
||||
modules.discovery.push(mdns)
|
||||
}
|
||||
|
||||
if (options.bootstrap) {
|
||||
const r = new Railing(options.bootstrap)
|
||||
modules.discovery.push(r)
|
||||
}
|
||||
|
||||
if (options.modules && options.modules.transport) {
|
||||
options.modules.transport.forEach((t) => modules.transport.push(t))
|
||||
}
|
||||
|
||||
if (options.modules && options.modules.discovery) {
|
||||
options.modules.discovery.forEach((d) => modules.discovery.push(d))
|
||||
}
|
||||
|
||||
super(modules, peerInfo, peerBook, options)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Node
|
54
examples/chat/src/listener.js
Normal file
54
examples/chat/src/listener.js
Normal file
@ -0,0 +1,54 @@
|
||||
'use strict'
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const Node = require('./libp2p-bundle.js')
|
||||
const pull = require('pull-stream')
|
||||
const Pushable = require('pull-pushable')
|
||||
const p = Pushable()
|
||||
|
||||
PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
const peerListener = new PeerInfo(idListener)
|
||||
peerListener.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
|
||||
const nodeListener = new Node(peerListener)
|
||||
|
||||
nodeListener.start((err) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
nodeListener.swarm.on('peer-mux-established', (peerInfo) => {
|
||||
console.log(peerInfo.id.toB58String())
|
||||
})
|
||||
|
||||
nodeListener.handle('/chat/1.0.0', (protocol, conn) => {
|
||||
pull(
|
||||
p,
|
||||
conn
|
||||
)
|
||||
|
||||
pull(
|
||||
conn,
|
||||
pull.map((data) => {
|
||||
return data.toString('utf8').replace('\n', '')
|
||||
}),
|
||||
pull.drain(console.log)
|
||||
)
|
||||
|
||||
process.stdin.setEncoding('utf8')
|
||||
process.openStdin().on('data', (chunk) => {
|
||||
var data = chunk.toString()
|
||||
p.push(data)
|
||||
})
|
||||
})
|
||||
|
||||
console.log('Listener ready, listening on:')
|
||||
peerListener.multiaddrs.forEach((ma) => {
|
||||
console.log(ma.toString() + '/ipfs/' + idListener.toB58String())
|
||||
})
|
||||
})
|
||||
})
|
5
examples/chat/src/peer-id-dialer.json
Normal file
5
examples/chat/src/peer-id-dialer.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"id": "Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP",
|
||||
"privKey": "CAASpwkwggSjAgEAAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAECggEAZnrCJ6IYiLyyRdr9SbKXCNDb4YByGYPEi/HT1aHgIJfFE1PSMjxcdytxfyjP4JJpVtPjiT9JFVU2ddoYu5qJN6tGwjVwgJEWg1UXmPaAw1T/drjS94kVsAs82qICtFmwp52Apg3dBZ0Qwq/8qE1XbG7lLyohIbfCBiL0tiPYMfkcsN9gnFT/kFCX0LVs2pa9fHCRMY9rqCc4/rWJa1w8sMuQ23y4lDaxKF9OZVvOHFQkbBDrkquWHE4r55fchCz/rJklkPJUNENuncBRu0/2X+p4IKFD1DnttXNwb8j4LPiSlLro1T0hiUr5gO2QmdYwXFF63Q3mjQy0+5I4eNbjjQKBgQDZvZy3gUKS/nQNkYfq9za80uLbIj/cWbO+ZZjXCsj0fNIcQFJcKMBoA7DjJvu2S/lf86/41YHkPdmrLAEQAkJ+5BBNOycjYK9minTEjIMMmZDTXXugZ62wnU6F46uLkgEChTqEP57Y6xwwV+JaEDFEsW5N1eE9lEVX9nGIr4phMwKBgQC1TazLuEt1WBx/iUT83ita7obXqoKNzwsS/MWfY2innzYZKDOqeSYZzLtt9uTtp4X4uLyPbYs0qFYhXLsUYMoGHNN8+NdjoyxCjQRJRBkMtaNR0lc5lVDWl3bTuJovjFCgAr9uqJrmI5OHcCIk/cDpdWb3nWaMihVlePmiTcTy9wKBgQCU0u7c1jKkudqks4XM6a+2HAYGdUBk4cLjLhnrUWnNAcuyl5wzdX8dGPi8KZb+IKuQE8WBNJ2VXVj7kBYh1QmSJVunDflQSvNYCOaKuOeRoxzD+y9Wkca74qkbBmPn/6FFEb7PSZTO+tPHjyodGNgz9XpJJRjQuBk1aDJtlF3m1QKBgE5SAr5ym65SZOU3UGUIOKRsfDW4Q/OsqDUImvpywCgBICaX9lHDShFFHwau7FA52ScL7vDquoMB4UtCOtLfyQYA9995w9oYCCurrVlVIJkb8jSLcADBHw3EmqF1kq3NqJqm9TmBfoDCh52vdCCUufxgKh33kfBOSlXuf7B8dgMbAoGAZ3r0/mBQX6S+s5+xCETMTSNv7TQzxgtURIpVs+ZVr2cMhWhiv+n0Omab9X9Z50se8cWl5lkvx8vn3D/XHHIPrMF6qk7RAXtvReb+PeitNvm0odqjFv0J2qki6fDs0HKwq4kojAXI1Md8Th0eobNjsy21fEEJT7uKMJdovI/SErI=",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAE="
|
||||
}
|
5
examples/chat/src/peer-id-listener.json
Normal file
5
examples/chat/src/peer-id-listener.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"id": "QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm",
|
||||
"privKey": "CAASqAkwggSkAgEAAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAECggEAQj0obPnVyjxLFZFnsFLgMHDCv9Fk5V5bOYtmxfvcm50us6ye+T8HEYWGUa9RrGmYiLweuJD34gLgwyzE1RwptHPj3tdNsr4NubefOtXwixlWqdNIjKSgPlaGULQ8YF2tm/kaC2rnfifwz0w1qVqhPReO5fypL+0ShyANVD3WN0Fo2ugzrniCXHUpR2sHXSg6K+2+qWdveyjNWog34b7CgpV73Ln96BWae6ElU8PR5AWdMnRaA9ucA+/HWWJIWB3Fb4+6uwlxhu2L50Ckq1gwYZCtGw63q5L4CglmXMfIKnQAuEzazq9T4YxEkp+XDnVZAOgnQGUBYpetlgMmkkh9qQKBgQDvsEs0ThzFLgnhtC2Jy//ZOrOvIAKAZZf/mS08AqWH3L0/Rjm8ZYbLsRcoWU78sl8UFFwAQhMRDBP9G+RPojWVahBL/B7emdKKnFR1NfwKjFdDVaoX5uNvZEKSl9UubbC4WZJ65u/cd5jEnj+w3ir9G8n+P1gp/0yBz02nZXFgSwKBgQDZPQr4HBxZL7Kx7D49ormIlB7CCn2i7mT11Cppn5ifUTrp7DbFJ2t9e8UNk6tgvbENgCKXvXWsmflSo9gmMxeEOD40AgAkO8Pn2R4OYhrwd89dECiKM34HrVNBzGoB5+YsAno6zGvOzLKbNwMG++2iuNXqXTk4uV9GcI8OnU5ZPQKBgCZUGrKSiyc85XeiSGXwqUkjifhHNh8yH8xPwlwGUFIZimnD4RevZI7OEtXw8iCWpX2gg9XGuyXOuKORAkF5vvfVriV4e7c9Ad4Igbj8mQFWz92EpV6NHXGCpuKqRPzXrZrNOA9PPqwSs+s9IxI1dMpk1zhBCOguWx2m+NP79NVhAoGBAI6WSoTfrpu7ewbdkVzTWgQTdLzYNe6jmxDf2ZbKclrf7lNr/+cYIK2Ud5qZunsdBwFdgVcnu/02czeS42TvVBgs8mcgiQc/Uy7yi4/VROlhOnJTEMjlU2umkGc3zLzDgYiRd7jwRDLQmMrYKNyEr02HFKFn3w8kXSzW5I8rISnhAoGBANhchHVtJd3VMYvxNcQb909FiwTnT9kl9pkjhwivx+f8/K8pDfYCjYSBYCfPTM5Pskv5dXzOdnNuCj6Y2H/9m2SsObukBwF0z5Qijgu1DsxvADVIKZ4rzrGb4uSEmM6200qjJ/9U98fVM7rvOraakrhcf9gRwuspguJQnSO9cLj6",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAE="
|
||||
}
|
58
examples/discovery-mechanisms/1.js
Normal file
58
examples/discovery-mechanisms/1.js
Normal file
@ -0,0 +1,58 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Multiplex = require('libp2p-multiplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const PeerInfo = require('peer-info')
|
||||
const Railing = require('libp2p-railing')
|
||||
const waterfall = require('async/waterfall')
|
||||
|
||||
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-nodejs.json
|
||||
const bootstrapers = [
|
||||
'/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
|
||||
'/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
|
||||
'/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'
|
||||
]
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [Multiplex],
|
||||
crypto: [SECIO]
|
||||
},
|
||||
discovery: [new Railing(bootstrapers)]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
node.on('peer:discovery', (peer) => {
|
||||
console.log('Discovered:', peer.id.toB58String())
|
||||
node.dial(peer, () => {})
|
||||
})
|
||||
|
||||
node.on('peer:connect', (peer) => {
|
||||
console.log('Connection established to:', peer.id.toB58String())
|
||||
})
|
||||
})
|
50
examples/discovery-mechanisms/2.js
Normal file
50
examples/discovery-mechanisms/2.js
Normal file
@ -0,0 +1,50 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Multiplex = require('libp2p-multiplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const PeerInfo = require('peer-info')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [Multiplex],
|
||||
crypto: [SECIO]
|
||||
},
|
||||
discovery: [new MulticastDNS(peerInfo, { interval: 1000 })]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node1.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
|
||||
node2.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
|
||||
})
|
161
examples/discovery-mechanisms/README.md
Normal file
161
examples/discovery-mechanisms/README.md
Normal file
@ -0,0 +1,161 @@
|
||||
# Peer Discovery Mechanisms
|
||||
|
||||
A Peer Discovery module enables libp2p to find peers to connect to. Think of these mechanisms as ways to join the rest of the network, as railing points.
|
||||
|
||||
With these system, a libp2p node can both have a set of nodes to always connect on boot (bootstraper nodes), discover nodes through locality (e.g connected in the same LAN) or through serendipity (random walks on a DHT).
|
||||
|
||||
These mechanisms save configuration and enable a node to operate without any explicit dials, it will just work.
|
||||
|
||||
## 1. Bootstrap list of Peers when booting a node
|
||||
|
||||
For this demo, we will connect to IPFS default bootstrapper nodes and so, we will need to support the same set of features those nodes have, that are: TCP, multiplex and SECIO. You can see the complete example at [1.js](./1.js).
|
||||
|
||||
First, we create our libp2p bundle.
|
||||
|
||||
```JavaScript
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [Multiplex],
|
||||
crypto: [SECIO]
|
||||
},
|
||||
discovery: [new Railing(bootstrapers)]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this bundle, we use a `bootstrappers` array listing peers to connect _on boot_. Here is the list used by js-ipfs and go-ipfs.
|
||||
|
||||
```JavaScript
|
||||
const bootstrapers = [
|
||||
'/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
|
||||
'/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
|
||||
'/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'
|
||||
]
|
||||
```
|
||||
|
||||
Now, once we create and start the node, we can listen for events such as `peer:discovery` and `peer:connect`, these events tell us when we found a peer, independently of the discovery mechanism used and when we actually dialed to that peer.
|
||||
|
||||
```
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
// Emitted when a peer has been found
|
||||
node.on('peer:discovery', (peer) => {
|
||||
console.log('Discovered:', peer.id.toB58String())
|
||||
// Note how we need to dial, even if just to warm up the Connection (by not
|
||||
// picking any protocol) in order to get a full Connection. The Peer Discovery
|
||||
// doesn't make any decisions for you.
|
||||
node.dial(peer, () => {})
|
||||
})
|
||||
|
||||
// Once the dial is complete, this event is emitted.
|
||||
node.on('peer:connect', (peer) => {
|
||||
console.log('Connection established to:', peer.id.toB58String())
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
From running [1.js](./1.js), you should see the following:
|
||||
|
||||
```bash
|
||||
> node 1.js
|
||||
Discovered: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
|
||||
Discovered: QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z
|
||||
Discovered: QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM
|
||||
Discovered: QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm
|
||||
Discovered: QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu
|
||||
Discovered: QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64
|
||||
Discovered: QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd
|
||||
Discovered: QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3
|
||||
Discovered: QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
Connection established to: QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3
|
||||
Connection established to: QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd
|
||||
Connection established to: QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64
|
||||
Connection established to: QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm
|
||||
Connection established to: QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM
|
||||
Connection established to: QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
Connection established to: QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
|
||||
Connection established to: QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z
|
||||
Connection established to: QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu
|
||||
```
|
||||
|
||||
## 2. MulticastDNS to find other peers in the network
|
||||
|
||||
For this example, we need `libp2p-mdns`, go ahead and `npm install` it. You can find the complete solution at [2.js](./2.js).
|
||||
|
||||
Update your libp2p bundle to include MulticastDNS.
|
||||
|
||||
```JavaScript
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [Multiplex],
|
||||
crypto: [SECIO]
|
||||
},
|
||||
// We set the interval here to 1 second so that is faster to observe. The
|
||||
// default is 10 seconds.
|
||||
discovery: [new MulticastDNS(peerInfo, { interval: 1000 })]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To observe it working, spawn two nodes.
|
||||
|
||||
```JavaScript
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node1.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
|
||||
node2.on('peer:discovery', (peer) => console.log('Discovered:', peer.id.toB58String()))
|
||||
})
|
||||
```
|
||||
|
||||
If you run this example, you will see a continuous stream of each peer discovering each other.
|
||||
|
||||
```bash
|
||||
> node 2.js
|
||||
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
|
||||
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
|
||||
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
|
||||
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
|
||||
Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
|
||||
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
|
||||
```
|
||||
|
||||
## 3. Where to find other Peer Discovery Mechanisms
|
||||
|
||||
There are plenty more Peer Discovery Mechanisms out there, you can:
|
||||
|
||||
- Find one in [libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star). Yes, a transport with discovery capabilities! This happens because WebRTC requires a rendezvous point for peers to exchange [SDP](https://tools.ietf.org/html/rfc4317) offer, which means we have one or more points that can introduce peers to each other. Think of it as MulticastDNS for the Web, as in MulticastDNS only works in LAN.
|
||||
- Any DHT will offer you a discovery capability. You can simple _random-walk_ the routing tables to find other peers to connect to.
|
||||
- You can create your own Discovery service, a registry, a list, a radio beacon, you name it!
|
1
examples/echo/README.md
Normal file
1
examples/echo/README.md
Normal file
@ -0,0 +1 @@
|
||||
# Echo example with libp2p
|
56
examples/echo/src/dialer.js
Normal file
56
examples/echo/src/dialer.js
Normal file
@ -0,0 +1,56 @@
|
||||
'use strict'
|
||||
/* eslint-disable no-console */
|
||||
|
||||
/*
|
||||
* Dialer Node
|
||||
*/
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const Node = require('./libp2p-bundle')
|
||||
const pull = require('pull-stream')
|
||||
const async = require('async')
|
||||
|
||||
async.parallel([
|
||||
(cb) => PeerId.createFromJSON(require('./id-d'), cb),
|
||||
(cb) => PeerId.createFromJSON(require('./id-l'), cb)
|
||||
], (err, ids) => {
|
||||
if (err) { throw err }
|
||||
|
||||
// Dialer
|
||||
const dialerId = ids[0]
|
||||
const dialerPeerInfo = new PeerInfo(dialerId)
|
||||
dialerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
const dialerNode = new Node(dialerPeerInfo)
|
||||
|
||||
// Peer to Dial
|
||||
const listenerPeerInfo = new PeerInfo(ids[1])
|
||||
const listenerId = ids[1]
|
||||
const listenerMultiaddr = '/ip4/127.0.0.1/tcp/10333/ipfs/' +
|
||||
listenerId.toB58String()
|
||||
listenerPeerInfo.multiaddrs.add(listenerMultiaddr)
|
||||
|
||||
dialerNode.start((err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Dialer ready, listening on:')
|
||||
dialerPeerInfo.multiaddrs.forEach((ma) => console.log(ma.toString() +
|
||||
'/ipfs/' + dialerId.toB58String()))
|
||||
|
||||
console.log('Dialing to peer:', listenerMultiaddr.toString())
|
||||
dialerNode.dial(listenerPeerInfo, '/echo/1.0.0', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('nodeA dialed to nodeB on protocol: /echo/1.0.0')
|
||||
|
||||
pull(
|
||||
pull.values(['hey']),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
if (err) { throw err }
|
||||
console.log('received echo:', data.toString())
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
5
examples/echo/src/id-d.json
Normal file
5
examples/echo/src/id-d.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"id": "Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP",
|
||||
"privKey": "CAASpwkwggSjAgEAAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAECggEAZnrCJ6IYiLyyRdr9SbKXCNDb4YByGYPEi/HT1aHgIJfFE1PSMjxcdytxfyjP4JJpVtPjiT9JFVU2ddoYu5qJN6tGwjVwgJEWg1UXmPaAw1T/drjS94kVsAs82qICtFmwp52Apg3dBZ0Qwq/8qE1XbG7lLyohIbfCBiL0tiPYMfkcsN9gnFT/kFCX0LVs2pa9fHCRMY9rqCc4/rWJa1w8sMuQ23y4lDaxKF9OZVvOHFQkbBDrkquWHE4r55fchCz/rJklkPJUNENuncBRu0/2X+p4IKFD1DnttXNwb8j4LPiSlLro1T0hiUr5gO2QmdYwXFF63Q3mjQy0+5I4eNbjjQKBgQDZvZy3gUKS/nQNkYfq9za80uLbIj/cWbO+ZZjXCsj0fNIcQFJcKMBoA7DjJvu2S/lf86/41YHkPdmrLAEQAkJ+5BBNOycjYK9minTEjIMMmZDTXXugZ62wnU6F46uLkgEChTqEP57Y6xwwV+JaEDFEsW5N1eE9lEVX9nGIr4phMwKBgQC1TazLuEt1WBx/iUT83ita7obXqoKNzwsS/MWfY2innzYZKDOqeSYZzLtt9uTtp4X4uLyPbYs0qFYhXLsUYMoGHNN8+NdjoyxCjQRJRBkMtaNR0lc5lVDWl3bTuJovjFCgAr9uqJrmI5OHcCIk/cDpdWb3nWaMihVlePmiTcTy9wKBgQCU0u7c1jKkudqks4XM6a+2HAYGdUBk4cLjLhnrUWnNAcuyl5wzdX8dGPi8KZb+IKuQE8WBNJ2VXVj7kBYh1QmSJVunDflQSvNYCOaKuOeRoxzD+y9Wkca74qkbBmPn/6FFEb7PSZTO+tPHjyodGNgz9XpJJRjQuBk1aDJtlF3m1QKBgE5SAr5ym65SZOU3UGUIOKRsfDW4Q/OsqDUImvpywCgBICaX9lHDShFFHwau7FA52ScL7vDquoMB4UtCOtLfyQYA9995w9oYCCurrVlVIJkb8jSLcADBHw3EmqF1kq3NqJqm9TmBfoDCh52vdCCUufxgKh33kfBOSlXuf7B8dgMbAoGAZ3r0/mBQX6S+s5+xCETMTSNv7TQzxgtURIpVs+ZVr2cMhWhiv+n0Omab9X9Z50se8cWl5lkvx8vn3D/XHHIPrMF6qk7RAXtvReb+PeitNvm0odqjFv0J2qki6fDs0HKwq4kojAXI1Md8Th0eobNjsy21fEEJT7uKMJdovI/SErI=",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAE="
|
||||
}
|
5
examples/echo/src/id-l.json
Normal file
5
examples/echo/src/id-l.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"id": "QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm",
|
||||
"privKey": "CAASqAkwggSkAgEAAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAECggEAQj0obPnVyjxLFZFnsFLgMHDCv9Fk5V5bOYtmxfvcm50us6ye+T8HEYWGUa9RrGmYiLweuJD34gLgwyzE1RwptHPj3tdNsr4NubefOtXwixlWqdNIjKSgPlaGULQ8YF2tm/kaC2rnfifwz0w1qVqhPReO5fypL+0ShyANVD3WN0Fo2ugzrniCXHUpR2sHXSg6K+2+qWdveyjNWog34b7CgpV73Ln96BWae6ElU8PR5AWdMnRaA9ucA+/HWWJIWB3Fb4+6uwlxhu2L50Ckq1gwYZCtGw63q5L4CglmXMfIKnQAuEzazq9T4YxEkp+XDnVZAOgnQGUBYpetlgMmkkh9qQKBgQDvsEs0ThzFLgnhtC2Jy//ZOrOvIAKAZZf/mS08AqWH3L0/Rjm8ZYbLsRcoWU78sl8UFFwAQhMRDBP9G+RPojWVahBL/B7emdKKnFR1NfwKjFdDVaoX5uNvZEKSl9UubbC4WZJ65u/cd5jEnj+w3ir9G8n+P1gp/0yBz02nZXFgSwKBgQDZPQr4HBxZL7Kx7D49ormIlB7CCn2i7mT11Cppn5ifUTrp7DbFJ2t9e8UNk6tgvbENgCKXvXWsmflSo9gmMxeEOD40AgAkO8Pn2R4OYhrwd89dECiKM34HrVNBzGoB5+YsAno6zGvOzLKbNwMG++2iuNXqXTk4uV9GcI8OnU5ZPQKBgCZUGrKSiyc85XeiSGXwqUkjifhHNh8yH8xPwlwGUFIZimnD4RevZI7OEtXw8iCWpX2gg9XGuyXOuKORAkF5vvfVriV4e7c9Ad4Igbj8mQFWz92EpV6NHXGCpuKqRPzXrZrNOA9PPqwSs+s9IxI1dMpk1zhBCOguWx2m+NP79NVhAoGBAI6WSoTfrpu7ewbdkVzTWgQTdLzYNe6jmxDf2ZbKclrf7lNr/+cYIK2Ud5qZunsdBwFdgVcnu/02czeS42TvVBgs8mcgiQc/Uy7yi4/VROlhOnJTEMjlU2umkGc3zLzDgYiRd7jwRDLQmMrYKNyEr02HFKFn3w8kXSzW5I8rISnhAoGBANhchHVtJd3VMYvxNcQb909FiwTnT9kl9pkjhwivx+f8/K8pDfYCjYSBYCfPTM5Pskv5dXzOdnNuCj6Y2H/9m2SsObukBwF0z5Qijgu1DsxvADVIKZ4rzrGb4uSEmM6200qjJ/9U98fVM7rvOraakrhcf9gRwuspguJQnSO9cLj6",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAE="
|
||||
}
|
80
examples/echo/src/libp2p-bundle.js
Normal file
80
examples/echo/src/libp2p-bundle.js
Normal file
@ -0,0 +1,80 @@
|
||||
'use strict'
|
||||
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const WS = require('libp2p-websockets')
|
||||
const Railing = require('libp2p-railing')
|
||||
const spdy = require('libp2p-spdy')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
const multiplex = require('libp2p-multiplex')
|
||||
const secio = require('libp2p-secio')
|
||||
const libp2p = require('../../..')
|
||||
|
||||
function mapMuxers (list) {
|
||||
return list.map((pref) => {
|
||||
if (typeof pref !== 'string') {
|
||||
return pref
|
||||
}
|
||||
switch (pref.trim().toLowerCase()) {
|
||||
case 'spdy': return spdy
|
||||
case 'multiplex': return multiplex
|
||||
default:
|
||||
throw new Error(pref + ' muxer not available')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getMuxers (muxers) {
|
||||
const muxerPrefs = process.env.LIBP2P_MUXER
|
||||
if (muxerPrefs && !muxers) {
|
||||
return mapMuxers(muxerPrefs.split(','))
|
||||
} else if (muxers) {
|
||||
return mapMuxers(muxers)
|
||||
} else {
|
||||
return [multiplex, spdy]
|
||||
}
|
||||
}
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (peerInfo, peerBook, options) {
|
||||
options = options || {}
|
||||
|
||||
const modules = {
|
||||
transport: [
|
||||
new TCP(),
|
||||
new WS()
|
||||
],
|
||||
connection: {
|
||||
muxer: getMuxers(options.muxer),
|
||||
crypto: [ secio ]
|
||||
},
|
||||
discovery: []
|
||||
}
|
||||
|
||||
if (options.dht) {
|
||||
modules.DHT = KadDHT
|
||||
}
|
||||
|
||||
if (options.mdns) {
|
||||
const mdns = new MulticastDNS(peerInfo, 'ipfs.local')
|
||||
modules.discovery.push(mdns)
|
||||
}
|
||||
|
||||
if (options.bootstrap) {
|
||||
const r = new Railing(options.bootstrap)
|
||||
modules.discovery.push(r)
|
||||
}
|
||||
|
||||
if (options.modules && options.modules.transport) {
|
||||
options.modules.transport.forEach((t) => modules.transport.push(t))
|
||||
}
|
||||
|
||||
if (options.modules && options.modules.discovery) {
|
||||
options.modules.discovery.forEach((d) => modules.discovery.push(d))
|
||||
}
|
||||
|
||||
super(modules, peerInfo, peerBook, options)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Node
|
44
examples/echo/src/listener.js
Normal file
44
examples/echo/src/listener.js
Normal file
@ -0,0 +1,44 @@
|
||||
'use strict'
|
||||
/* eslint-disable no-console */
|
||||
|
||||
/*
|
||||
* Listener Node
|
||||
*/
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const Node = require('./libp2p-bundle')
|
||||
const pull = require('pull-stream')
|
||||
const series = require('async/series')
|
||||
|
||||
let listenerId
|
||||
let listenerNode
|
||||
|
||||
series([
|
||||
(cb) => {
|
||||
PeerId.createFromJSON(require('./id-l'), (err, id) => {
|
||||
if (err) { return cb(err) }
|
||||
listenerId = id
|
||||
cb()
|
||||
})
|
||||
},
|
||||
(cb) => {
|
||||
const listenerPeerInfo = new PeerInfo(listenerId)
|
||||
listenerPeerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/10333')
|
||||
listenerNode = new Node(listenerPeerInfo)
|
||||
|
||||
listenerNode.swarm.on('peer-mux-established', (peerInfo) => {
|
||||
console.log('received dial to me from:', peerInfo.id.toB58String())
|
||||
})
|
||||
|
||||
listenerNode.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
|
||||
listenerNode.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Listener ready, listening on:')
|
||||
listenerNode.peerInfo.multiaddrs.forEach((ma) => {
|
||||
console.log(ma.toString() + '/ipfs/' + listenerId.toB58String())
|
||||
})
|
||||
})
|
59
examples/encrypted-communications/1.js
Normal file
59
examples/encrypted-communications/1.js
Normal file
@ -0,0 +1,59 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const SPDY = require('libp2p-spdy')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const pull = require('pull-stream')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [SPDY],
|
||||
crypto: [SECIO]
|
||||
}
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node2.handle('/a-protocol', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
node1.dial(node2.peerInfo, '/a-protocol', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['This information is sent out encrypted to the other peer']), conn)
|
||||
})
|
||||
})
|
39
examples/encrypted-communications/README.md
Normal file
39
examples/encrypted-communications/README.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Encrypted Communications
|
||||
|
||||
libp2p can leverage the encrypted communications from the transports it uses (i.e WebRTC). To ensure that every connection is encrypted, independently of how it was set up, libp2p also supports a set of modules that encrypt every communication established.
|
||||
|
||||
We call this usage a _connection upgrade_ where given a connection between peer A to peer B, a protocol handshake can be performed that gives that connection new properties.
|
||||
|
||||
A byproduct of having these encrypted communications modules is that we can authenticate the peers we are dialing to. You might have noticed that every time we dial to a peer in libp2p space, we always use its PeerId at the end (e.g /ip4/127.0.0.1/tcp/89765/ipfs/QmWCbVw1XZ8hiYBwwshPce2yaTDYTqTaP7GCHGpry3ykWb), this PeerId is generated by hashing the Public Key of the peer. With this, we can create a crypto challenge when dialing to another peer and prove that peer is the owner of a PrivateKey that matches the Public Key we know.
|
||||
|
||||
# 1. Set up encrypted communications with SECIO
|
||||
|
||||
We will build this example on top of example for [Protocol and Stream Multiplexing](../protocol-and-stream-multiplexing). You will need the module `libp2p-secio` to complete it, go ahead and `npm install libp2p-secio`.
|
||||
|
||||
SECIO is the crypto channel developed for IPFS, it is a TLS 1.3 like crypto channel that established an encrypted communication channel between two peers.
|
||||
|
||||
To add it to your libp2p bundle, all you have to do is:
|
||||
|
||||
```JavaScript
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [SPDY],
|
||||
// Attach secio as the crypto channel to use
|
||||
crypto: [SECIO]
|
||||
}
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And that's it, from now on, all your libp2p communications are encrypted. Try running the example [1.js](./1.js) to see it working.
|
||||
|
||||
If you want to want to learn more about how SECIO works, you can read the [great write up done by Dominic Tarr](https://github.com/auditdrivencrypto/secure-channel/blob/master/prior-art.md#ipfss-secure-channel).
|
||||
|
||||
Importante note: SECIO hasn't been audited and so, we do not recommend to trust its security. We intent to move to TLS 1.3 once the specification is finalized and an implementation exists that we can use.
|
1
examples/libp2p-in-the-browser/1/.gitignore
vendored
Normal file
1
examples/libp2p-in-the-browser/1/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
bundle.js
|
29
examples/libp2p-in-the-browser/1/package.json
Normal file
29
examples/libp2p-in-the-browser/1/package.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "libp2p-in-the-browser",
|
||||
"version": "0.1.0",
|
||||
"description": "See other nodes in the network using WebRTC Star discovery mechanism",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"bundle": "browserify src/index.js > public/bundle.js",
|
||||
"serve": "static public -p 9090 -H '{\"Cache-Control\": \"no-cache, must-revalidate\"}'",
|
||||
"start": "npm run bundle && npm run serve"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"browserify": "^14.5.0",
|
||||
"concat-stream": "^1.6.0",
|
||||
"detect-dom-ready": "^1.0.2",
|
||||
"node-static": "^0.7.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"detect-dom-ready": "^1.0.2",
|
||||
"libp2p": "^0.13.0",
|
||||
"libp2p-multiplex": "^0.5.0",
|
||||
"libp2p-railing": "^0.7.1",
|
||||
"libp2p-secio": "^0.8.1",
|
||||
"libp2p-spdy": "^0.11.0",
|
||||
"libp2p-webrtc-star": "^0.13.2",
|
||||
"libp2p-websockets": "^0.10.4",
|
||||
"peer-info": "^0.11.0"
|
||||
}
|
||||
}
|
14
examples/libp2p-in-the-browser/1/public/index.html
Normal file
14
examples/libp2p-in-the-browser/1/public/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>libp2p in the browser</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>libp2p node running \o/</h1>
|
||||
<div id="my-peer"></div>
|
||||
<div id="swarm"></div>
|
||||
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
55
examples/libp2p-in-the-browser/1/src/browser-bundle.js
Normal file
55
examples/libp2p-in-the-browser/1/src/browser-bundle.js
Normal file
@ -0,0 +1,55 @@
|
||||
'use strict'
|
||||
|
||||
const WebRTCStar = require('libp2p-webrtc-star')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
|
||||
const Multiplex = require('libp2p-multiplex')
|
||||
const SPDY = require('libp2p-spdy')
|
||||
const SECIO = require('libp2p-secio')
|
||||
|
||||
const Railing = require('libp2p-railing')
|
||||
const libp2p = require('libp2p')
|
||||
|
||||
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-browser.json
|
||||
const bootstrapers = [
|
||||
'/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
|
||||
'/dns4/sfo-1.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx',
|
||||
'/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
|
||||
'/dns4/sfo-2.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
|
||||
'/dns4/sfo-3.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
|
||||
'/dns4/sgp-1.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
|
||||
'/dns4/nyc-1.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
|
||||
'/dns4/nyc-2.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
|
||||
'/dns4/wss0.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmZMxNdpMkewiVZLMRxaNxUeZpDUb34pWjZ1kZvsd16Zic',
|
||||
'/dns4/wss1.bootstrap.libp2p.io/tcp/443/wss/ipfs/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6'
|
||||
]
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (peerInfo, peerBook, options) {
|
||||
options = options || {}
|
||||
|
||||
const wstar = new WebRTCStar()
|
||||
|
||||
const modules = {
|
||||
transport: [
|
||||
wstar,
|
||||
new WebSockets()
|
||||
],
|
||||
connection: {
|
||||
muxer: [
|
||||
Multiplex,
|
||||
SPDY
|
||||
],
|
||||
crypto: [SECIO]
|
||||
},
|
||||
discovery: [
|
||||
wstar.discovery,
|
||||
new Railing(bootstrapers)
|
||||
]
|
||||
}
|
||||
|
||||
super(modules, peerInfo, peerBook, options)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Node
|
24
examples/libp2p-in-the-browser/1/src/create-node.js
Normal file
24
examples/libp2p-in-the-browser/1/src/create-node.js
Normal file
@ -0,0 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
const PeerInfo = require('peer-info')
|
||||
const Node = require('./browser-bundle')
|
||||
|
||||
function createNode (callback) {
|
||||
PeerInfo.create((err, peerInfo) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
const peerIdStr = peerInfo.id.toB58String()
|
||||
const ma = `/dns4/star-signal.cloud.ipfs.team/wss/p2p-webrtc-star/ipfs/${peerIdStr}`
|
||||
|
||||
peerInfo.multiaddrs.add(ma)
|
||||
|
||||
const node = new Node(peerInfo)
|
||||
|
||||
node.idStr = peerIdStr
|
||||
callback(null, node)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = createNode
|
58
examples/libp2p-in-the-browser/1/src/index.js
Normal file
58
examples/libp2p-in-the-browser/1/src/index.js
Normal file
@ -0,0 +1,58 @@
|
||||
'use strict'
|
||||
|
||||
const domReady = require('detect-dom-ready')
|
||||
const createNode = require('./create-node')
|
||||
|
||||
domReady(() => {
|
||||
const myPeerDiv = document.getElementById('my-peer')
|
||||
const swarmDiv = document.getElementById('swarm')
|
||||
|
||||
createNode((err, node) => {
|
||||
if (err) {
|
||||
return console.log('Could not create the Node, check if your browser has WebRTC Support', err)
|
||||
}
|
||||
|
||||
node.on('peer:discovery', (peerInfo) => {
|
||||
console.log('Discovered a peer')
|
||||
const idStr = peerInfo.id.toB58String()
|
||||
console.log('Discovered: ' + idStr)
|
||||
|
||||
node.dial(peerInfo, (err, conn) => {
|
||||
if (err) { return console.log('Failed to dial:', idStr) }
|
||||
})
|
||||
})
|
||||
|
||||
node.on('peer:connect', (peerInfo) => {
|
||||
const idStr = peerInfo.id.toB58String()
|
||||
console.log('Got connection to: ' + idStr)
|
||||
const connDiv = document.createElement('div')
|
||||
connDiv.innerHTML = 'Connected to: ' + idStr
|
||||
connDiv.id = idStr
|
||||
swarmDiv.append(connDiv)
|
||||
})
|
||||
|
||||
node.on('peer:disconnect', (peerInfo) => {
|
||||
const idStr = peerInfo.id.toB58String()
|
||||
console.log('Lost connection to: ' + idStr)
|
||||
document.getElementById(idStr).remove()
|
||||
})
|
||||
|
||||
node.start((err) => {
|
||||
if (err) {
|
||||
return console.log('WebRTC not supported')
|
||||
}
|
||||
|
||||
const idStr = node.peerInfo.id.toB58String()
|
||||
|
||||
const idDiv = document
|
||||
.createTextNode('Node is ready. ID: ' + idStr)
|
||||
|
||||
myPeerDiv.append(idDiv)
|
||||
|
||||
console.log('Node is listening o/')
|
||||
|
||||
// NOTE: to stop the node
|
||||
// node.stop((err) => {})
|
||||
})
|
||||
})
|
||||
})
|
16
examples/libp2p-in-the-browser/README.md
Normal file
16
examples/libp2p-in-the-browser/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# libp2p running in the Browser
|
||||
|
||||
One of the primary goals with libp2p P2P was to get it fully working in the browser and interopable with the versions running in Go and in Node.js.
|
||||
|
||||
# 1. Setting up a simple app that lists connections to other nodes
|
||||
|
||||
Simple go into the folder [1](./1) and execute the following
|
||||
|
||||
```bash
|
||||
> cd 1
|
||||
> npm install
|
||||
> npm start
|
||||
# open your browser in port :9090
|
||||
```
|
||||
|
||||
[Version Published on IPFS](http://ipfs.io/ipfs/Qmbc1J7ehw1dNYachbkCWPto4RsnVvqCKNVzmYEod2gXcy)
|
2
examples/nat-traversal/README.md
Normal file
2
examples/nat-traversal/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# WIP - This example is still in the works
|
||||

|
67
examples/peer-and-content-routing/1.js
Normal file
67
examples/peer-and-content-routing/1.js
Normal file
@ -0,0 +1,67 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Multiplex = require('libp2p-multiplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const PeerInfo = require('peer-info')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [Multiplex],
|
||||
crypto: [SECIO]
|
||||
},
|
||||
// we add the DHT module that will enable Peer and Content Routing
|
||||
DHT: KadDHT
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
const node3 = nodes[2]
|
||||
|
||||
parallel([
|
||||
(cb) => node1.dial(node2.peerInfo, cb),
|
||||
(cb) => node2.dial(node3.peerInfo, cb),
|
||||
// Set up of the cons might take time
|
||||
(cb) => setTimeout(cb, 300)
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
node1.peerRouting.findPeer(node3.peerInfo.id, (err, peer) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Found it, multiaddrs are:')
|
||||
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
})
|
||||
})
|
||||
})
|
75
examples/peer-and-content-routing/2.js
Normal file
75
examples/peer-and-content-routing/2.js
Normal file
@ -0,0 +1,75 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Multiplex = require('libp2p-multiplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const PeerInfo = require('peer-info')
|
||||
const CID = require('cids')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [Multiplex],
|
||||
crypto: [SECIO]
|
||||
},
|
||||
// we add the DHT module that will enable Peer and Content Routing
|
||||
DHT: KadDHT
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
const node3 = nodes[2]
|
||||
|
||||
parallel([
|
||||
(cb) => node1.dial(node2.peerInfo, cb),
|
||||
(cb) => node2.dial(node3.peerInfo, cb),
|
||||
// Set up of the cons might take time
|
||||
(cb) => setTimeout(cb, 300)
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
|
||||
|
||||
node1.contentRouting.provide(cid, (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Node %s is providing %s', node1.peerInfo.id.toB58String(), cid.toBaseEncodedString())
|
||||
|
||||
node3.contentRouting.findProviders(cid, 5000, (err, providers) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Found provider:', providers[0].id.toB58String())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
106
examples/peer-and-content-routing/README.md
Normal file
106
examples/peer-and-content-routing/README.md
Normal file
@ -0,0 +1,106 @@
|
||||
# Peer and Content Routing
|
||||
|
||||
DHTs (Distributed Hash Tables) are one of the most common building blocks used when creating P2P networks. However, the name doesn't make justice to all the benefits it brings and putting the whole set of features in one box has proven to be limiting when we want to integrate multiple pieces together. With this in mind, we've come up with a new definition for what a DHT offers: Peer Routing and Content Routing.
|
||||
|
||||
Peer Routing is the category of modules that offer a way to find other peers in the network by intentionally issuing queries, iterative or recursive, until a Peer is found or the closest Peers, given the Peer Routing algorithm strategy are found.
|
||||
|
||||
Content Routing is the category of modules that offer a way to find where content lives in the network, it works in two steps: 1) Peers provide (announce) to the network that they are holders of specific content (multihashes) and 2) Peers issue queries to find where that content lives. A Content Routing mechanism could be as complex as a Kademlia DHT or a simple registry somewhere in the network.
|
||||
|
||||
# 1. Using Peer Routing to find other peers
|
||||
|
||||
This example builds on top of the [Protocol and Stream Muxing](../protocol-and-stream-muxing). We need to install `libp2p-kad-dht`, go ahead and `npm install libp2p-kad-dht`. If you want to see the final version, open [1.js](./1.js).
|
||||
|
||||
First, let's update our bundle to support Peer Routing and Content Routing.
|
||||
|
||||
```JavaScript
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [Multiplex],
|
||||
crypto: [SECIO]
|
||||
},
|
||||
// we add the DHT module that will enable Peer and Content Routing
|
||||
DHT: KadDHT
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once that is done, we can use the createNode function we developed in the previous example to create 3 nodes. Connect node 1 to node 2 and node 2 to node 3. We will use node 2 as a way to find the whereabouts of node 3
|
||||
|
||||
```JavaScript
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
const node3 = nodes[2]
|
||||
|
||||
parallel([
|
||||
(cb) => node1.dial(node2.peerInfo, cb),
|
||||
(cb) => node2.dial(node3.peerInfo, cb),
|
||||
// Set up of the cons might take time
|
||||
(cb) => setTimeout(cb, 100)
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
//
|
||||
node1.peerRouting.findPeer(node3.peerInfo.id, (err, peer) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Found it, multiaddrs are:')
|
||||
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
You should see the output being something like:
|
||||
|
||||
```Bash
|
||||
> node 1.js
|
||||
Found it, multiaddrs are:
|
||||
/ip4/127.0.0.1/tcp/63617/ipfs/QmWrFXvZr9S4iDqycyoyc2zDdrT1jg9wpdenUTdd1LTar6
|
||||
/ip4/192.168.86.41/tcp/63617/ipfs/QmWrFXvZr9S4iDqycyoyc2zDdrT1jg9wpdenUTdd1LTar6
|
||||
```
|
||||
|
||||
You have successfully used Peer Routing to find a peer that you were not directly connected. Now all you have to do is to dial to the multiaddrs you discovered.
|
||||
|
||||
# 2. Using Content Routing to find providers of content
|
||||
|
||||
With Content Routing, you can create records that are stored in multiple points in the network, these records can be resolved by you or other peers and they act as memos or rendezvous points. A great usage of this feature is to support discovery of content, where one node holds a file and instead of using a centralized tracker to inform other nodes that it holds that file, it simply puts a record in the network that can be resolved by other peers. Peer Routing and Content Routing are commonly known as Distributed Hash Tables, DHT.
|
||||
|
||||
You can find this example completed in [2.js](./2.js), however as you will see it is very simple to update the previous example.
|
||||
|
||||
Instead of calling `peerRouting.findPeer`, we will use `contentRouting.provide` and `contentRouting.findProviders`.
|
||||
|
||||
```JavaScript
|
||||
node1.contentRouting.provide(cid, (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Node %s is providing %s', node1.peerInfo.id.toB58String(), cid.toBaseEncodedString())
|
||||
|
||||
node3.contentRouting.findProviders(cid, 5000, (err, providers) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('Found provider:', providers[0].id.toB58String())
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
The output of your program should look like:
|
||||
|
||||
```bash
|
||||
> node 2.js
|
||||
Node QmSsmVPoTy3WpzwiNPnsKmonBaZjK2HitFs2nWUvwK31Pz is providing QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL
|
||||
Found provider: QmSsmVPoTy3WpzwiNPnsKmonBaZjK2HitFs2nWUvwK31Pz
|
||||
```
|
||||
|
||||
That's it, now you know how to find peers that have pieces of information that interest you!
|
||||
|
||||
# 3. Future Work
|
||||
|
||||
Currently, the only mechanisms for Peer and Content Routing come from the DHT, however we do have the intention to support:
|
||||
|
||||
- Multiple Peer Routing Mechanisms, including ones that do recursive searches (i.e [webrtc-explorer](http://daviddias.me/blog/webrtc-explorer-2-0-0-alpha-release/) like packet switching or [CJDNS](https://github.com/cjdelisle/cjdns) path finder)
|
||||
- Content Routing via PubSub
|
||||
- Content Routing via centralized index (i.e a tracker)
|
96
examples/protocol-and-stream-muxing/1.js
Normal file
96
examples/protocol-and-stream-muxing/1.js
Normal file
@ -0,0 +1,96 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const pull = require('pull-stream')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
// exact matching
|
||||
node2.handle('/your-protocol', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
// semver matching
|
||||
/*
|
||||
node2.handle('/another-protocol/1.0.1', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
*/
|
||||
|
||||
// custom func matching
|
||||
/*
|
||||
node2.handle('/custom-match-func', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
}, (myProtocol, requestedProtocol, callback) => {
|
||||
if (myProtocol.indexOf(requestedProtocol)) {
|
||||
callback(null, true)
|
||||
} else {
|
||||
callback(null, false)
|
||||
}
|
||||
})
|
||||
*/
|
||||
|
||||
node1.dial(node2.peerInfo, '/your-protocol', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['my own protocol, wow!']), conn)
|
||||
})
|
||||
|
||||
/*
|
||||
node1.dial(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['semver me please']), conn)
|
||||
})
|
||||
*/
|
||||
|
||||
/*
|
||||
node1.dial(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['do I fall into your criteria?']), conn)
|
||||
})
|
||||
*/
|
||||
})
|
79
examples/protocol-and-stream-muxing/2.js
Normal file
79
examples/protocol-and-stream-muxing/2.js
Normal file
@ -0,0 +1,79 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const SPDY = require('libp2p-spdy')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const series = require('async/series')
|
||||
const pull = require('pull-stream')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [SPDY]
|
||||
}
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node2.handle('/a', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
node2.handle('/b', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
series([
|
||||
(cb) => node1.dial(node2.peerInfo, '/a', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['protocol (a)']), conn)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node1.dial(node2.peerInfo, '/b', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['protocol (b)']), conn)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node1.dial(node2.peerInfo, '/b', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['another conn on protocol (b)']), conn)
|
||||
cb()
|
||||
})
|
||||
])
|
||||
})
|
83
examples/protocol-and-stream-muxing/3.js
Normal file
83
examples/protocol-and-stream-muxing/3.js
Normal file
@ -0,0 +1,83 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const SPDY = require('libp2p-spdy')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const series = require('async/series')
|
||||
const pull = require('pull-stream')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [SPDY]
|
||||
}
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
node1.handle('/node-1', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
node2.handle('/node-2', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
series([
|
||||
(cb) => node1.dial(node2.peerInfo, '/node-2', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['from 1 to 2']), conn)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node2.dial(node1.peerInfo, '/node-1', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['from 2 to 1']), conn)
|
||||
cb()
|
||||
})
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
console.log('Addresses by which both peers are connected')
|
||||
node1.peerBook
|
||||
.getAllArray()
|
||||
.forEach((peer) => console.log('node 1 to node 2:', peer.isConnected().toString()))
|
||||
node2.peerBook
|
||||
.getAllArray()
|
||||
.forEach((peer) => console.log('node 2 to node 1:', peer.isConnected().toString()))
|
||||
})
|
||||
})
|
174
examples/protocol-and-stream-muxing/README.md
Normal file
174
examples/protocol-and-stream-muxing/README.md
Normal file
@ -0,0 +1,174 @@
|
||||
# Protocol and Stream Multiplexing (aka muxing)
|
||||
|
||||
One of the specialties of libp2p is solving the bane of protocol discovery and handshake between machines. Before libp2p, you would have to assign a listener to a port and then through some process of formal specification you would assign ports to special protocols so that other hosts would know before hand which port to dial (e.g ssh (22), http (80), https (443), ftp (21), etc). With libp2p you don't need to do that anymore, not only you don't have to assign ports before hand, you don't even need to think about ports at all since all the protocol handshaking happens in the wire!
|
||||
|
||||
The feature of agreeing on a protocol over an established connection is what we call _protocol multiplexing_ and it is possible through [multistream-select](https://github.com/multiformats/multistream), another protocol that lets you agree per connection (or stream) which protocol is going to be talked over that connection (select), it also enables you to request the other end to tell you which protocols it supports (ls). You can learn more about multistream-select at its [specification repo](https://github.com/multiformats/multistream).
|
||||
|
||||
# 1. Handle multiple protocols
|
||||
|
||||
Let's see _protocol multiplexing_ in action! You will need the following modules for this example: `libp2p`, `libp2p-tcp`, `peer-info`, `async` and `pull-stream`. This example reuses the base left by the [Transports](../transports) example. You can see the complete solution at [1.js](./1.js).
|
||||
|
||||
After creating the nodes, we need to tell libp2p which protocols to handle.
|
||||
|
||||
```JavaScript
|
||||
// ...
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
// Here we are telling libp2p that is someone dials this node to talk with the `/your-protocol`
|
||||
// multicodec, the protocol identifier, please call this callback and give it the connection
|
||||
// so that incomming data can be handled
|
||||
node2.handle('/your-protocol', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
After the protocol is _handled_, now we can dial to it.
|
||||
|
||||
```JavaScript
|
||||
node1.dial(node2.peerInfo, '/your-protocol', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['my own protocol, wow!']), conn)
|
||||
})
|
||||
```
|
||||
|
||||
You might have seen this in the [Transports](../transports) examples. However, what it was not explained is that you can do more than exact string matching, for example, you can use semver.
|
||||
|
||||
```JavaScript
|
||||
node2.handle('/another-protocol/1.0.1', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
// ...
|
||||
node1.dial(node2.peerInfo, '/another-protocol/1.0.0', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['semver me please']), conn)
|
||||
})
|
||||
```
|
||||
|
||||
This feature is super power for network protocols. It works in the same way as versioning your RPC/REST API, but for anything that goes in the wire. We had to use this feature to upgrade protocols within the IPFS Stack (i.e Bitswap) and we successfully managed to do so without any network splits.
|
||||
|
||||
There is still one last feature, you can create your custom match functions.
|
||||
|
||||
```JavaScript
|
||||
node2.handle('/custom-match-func', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
}, (myProtocol, requestedProtocol, callback) => {
|
||||
// This is all custom. I'm checking the base path matches, think of this
|
||||
// as a HTTP routing table.
|
||||
if (myProtocol.indexOf(requestedProtocol)) {
|
||||
callback(null, true)
|
||||
} else {
|
||||
callback(null, false)
|
||||
}
|
||||
})
|
||||
// ...
|
||||
node1.dial(node2.peerInfo, '/custom-match-func/some-query', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['do I fall into your criteria?']), conn)
|
||||
})
|
||||
```
|
||||
|
||||
Try all of this out by executing [1.js](./1.js).
|
||||
|
||||
# 2. Reuse existing connection
|
||||
|
||||
The example above would require a node to create a whole new connection for every time it dials in one of the protocols, this is a waste of resources and also it might be simply not possible (e.g lack of file descriptors, not enough ports being open, etc). What we really want is to dial a connection once and then multiplex several virtual connections (stream) over a single connection, this is where _stream multiplexing_ comes into play.
|
||||
|
||||
Stream multiplexing is a old concept, in fact it happens in many of the layers of the [OSI System](https://en.wikipedia.org/wiki/OSI_model), in libp2p we make this feature to our avail by letting the user pick which module for stream multiplexing to use.
|
||||
|
||||
Currently, we have two available [libp2p-spdy](https://github.com/libp2p/js-libp2p-spdy) and [libp2p-multiplex](https://github.com/libp2p/js-libp2p-multiplex) and pluging them in is as easy as adding another transport. Let's revisit our libp2p bundle.
|
||||
|
||||
```JavaScript
|
||||
const SPDY = require('libp2p-spdy')
|
||||
//...
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
// Here we are adding the SPDY muxer to our libp2p bundle.
|
||||
// Thanks to protocol muxing, a libp2p bundle can support multiple Stream Muxers at the same
|
||||
// time and pick the right one when dialing to a node
|
||||
connection: {
|
||||
muxer: [SPDY]
|
||||
}
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With this, we can dial as many times as we want to a peer and always reuse the same established underlying connection.
|
||||
|
||||
```JavaScript
|
||||
node2.handle('/a', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
node2.handle('/b', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
series([
|
||||
(cb) => node1.dial(node2.peerInfo, '/a', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['protocol (a)']), conn)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node1.dial(node2.peerInfo, '/b', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['protocol (b)']), conn)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node1.dial(node2.peerInfo, '/b', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
pull(pull.values(['another conn on protocol (b)']), conn)
|
||||
cb()
|
||||
})
|
||||
])
|
||||
```
|
||||
|
||||
By running [2.js](./2.js) you should see the following result:
|
||||
|
||||
```
|
||||
> node 2.js
|
||||
protocol (a)
|
||||
protocol (b)
|
||||
another protocol (b)
|
||||
```
|
||||
|
||||
# 3. Bidirectional connections
|
||||
|
||||
There is one last trick on _protocol and stream multiplexing_ that libp2p uses to make everyone's life easier and that is _biderectional connection_.
|
||||
|
||||
With the aid of both mechanisms, we can reuse an incomming connection to dial streams out too, this is specially useful when you are behind tricky NAT, firewalls or if you are running in a browser, where you can have listening addrs, but you can dial out. By dialing out, you enable other peers to talk with you in Protocols that they want, simply by opening a new multiplexed stream.
|
||||
|
||||
You can see this working on example [3.js](./3.js). The result should look like the following:
|
||||
|
||||
```Bash
|
||||
> node 3.js
|
||||
from 1 to 2
|
||||
Addresses by which both peers are connected
|
||||
node 1 to node 2: /ip4/127.0.0.1/tcp/50629/ipfs/QmZwMKTo6wG4Te9A6M2eJnWDpR8uhsGed4YRegnV5DcKiv
|
||||
node 2 to node 1: /ip4/127.0.0.1/tcp/50630/ipfs/QmRgormJQeDyXhDKma11eUtksoh8vWmeBoxghVt4meauW9
|
||||
from 2 to 1
|
||||
```
|
68
examples/pubsub/1.js
Normal file
68
examples/pubsub/1.js
Normal file
@ -0,0 +1,68 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Multiplex = require('libp2p-multiplex')
|
||||
const SECIO = require('libp2p-secio')
|
||||
const PeerInfo = require('peer-info')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const FloodSub = require('libp2p-floodsub')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const series = require('async/series')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()],
|
||||
connection: {
|
||||
muxer: [Multiplex],
|
||||
crypto: [SECIO]
|
||||
},
|
||||
discovery: [new MulticastDNS(peerInfo, { interval: 2000 })]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
const fs1 = new FloodSub(node1)
|
||||
const fs2 = new FloodSub(node2)
|
||||
|
||||
series([
|
||||
(cb) => fs1.start(cb),
|
||||
(cb) => fs2.start(cb),
|
||||
(cb) => node1.once('peer:discovery', (peer) => node1.dial(peer, cb)),
|
||||
(cb) => setTimeout(cb, 500)
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
fs2.on('news', (msg) => console.log(msg.from, msg.data.toString()))
|
||||
fs2.subscribe('news')
|
||||
|
||||
setInterval(() => {
|
||||
fs1.publish('news', Buffer.from('Bird bird bird, bird is the word!'))
|
||||
}, 1000)
|
||||
})
|
||||
})
|
60
examples/pubsub/README.md
Normal file
60
examples/pubsub/README.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Publish Subscribe
|
||||
|
||||
Publish Subscribe is something out of scope for the modular networking stack that is libp2p, however, it is something that is enabled through the primitives that libp2p offers and so it has become one of the most interesting use cases for libp2p.
|
||||
|
||||
Currently, we have a PubSub implementation, [libp2p-floodsub](https://github.com/libp2p/js-libp2p-floodsub) and many more being researched at [research-pubsub](https://github.com/libp2p/research-pubsub).
|
||||
|
||||
We've seen many interesting use cases appear with this, here are some highlights:
|
||||
|
||||
- [Collaborative Text Editing](https://www.youtube.com/watch?v=-kdx8rJd8rQ)
|
||||
- [IPFS PubSub (using libp2p-floodsub) for IoT](https://www.youtube.com/watch?v=qLpM5pBDGiE).
|
||||
- [Real Time distributed Applications](https://www.youtube.com/watch?v=vQrbxyDPSXg)
|
||||
|
||||
## 1. Setting up a simple PubSub network on top of libp2p
|
||||
|
||||
For this example, we will use MulticastDNS for automatic Peer Discovery and libp2p-floodsub to give us the PubSub primitives we are looking for. This example is based the previous examples found in [Discovery Mechanisms](../discovery-mechanisms). You can find the complete version at [1.js](./1.js).
|
||||
|
||||
Getting PubSub is super simple, all you have to do is require the FloodSub module and pass it in a libp2p node, once you have done that you can start subscribing and publishing in any topic.
|
||||
|
||||
```JavaScript
|
||||
const FloodSub = require('libp2p-floodsub')
|
||||
|
||||
const fs1 = new FloodSub(node1)
|
||||
const fs2 = new FloodSub(node2)
|
||||
|
||||
series([
|
||||
(cb) => fs1.start(cb),
|
||||
(cb) => fs2.start(cb),
|
||||
(cb) => node1.once('peer:discovery', (peer) => node1.dial(peer, cb)),
|
||||
(cb) => setTimeout(cb, 500)
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
fs2.on('news', (msg) => console.log(msg.from, msg.data.toString()))
|
||||
fs2.subscribe('news')
|
||||
|
||||
setInterval(() => {
|
||||
fs1.publish('news', Buffer.from('Bird bird bird, bird is the word!'))
|
||||
}, 1000)
|
||||
})
|
||||
```
|
||||
|
||||
The output of the program should look like:
|
||||
|
||||
```
|
||||
> node 1.js
|
||||
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
|
||||
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
|
||||
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
|
||||
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
|
||||
QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82 Bird bird bird, bird is the word!
|
||||
```
|
||||
|
||||
## 2. Future work
|
||||
|
||||
libp2p/IPFS PubSub is enabling a whole set of Distributed Real Time applications using CRDT (Conflict-Free Replicated Data Types). It is still going through heavy research (and hacking) and we invite you to join the conversation at [research-CRDT](https://github.com/ipfs/research-CRDT). Here is a list of some of the exciting examples:
|
||||
|
||||
- [PubSub Room](https://github.com/ipfs-labs/ipfs-pubsub-room)
|
||||
- [Live DB - A always in Sync DB using CRDT](https://github.com/ipfs-labs/ipfs-live-db)
|
||||
- [IIIF Annotations over IPFS, CRDT and libp2p](https://www.youtube.com/watch?v=hmAniA6g9D0&feature=youtu.be&t=10m40s)
|
||||
- [orbit.chat - p2p chat application, fully running in the browser with js-ipfs, js-libp2p and orbit-db](http://orbit.chat/)
|
32
examples/transports/1.js
Normal file
32
examples/transports/1.js
Normal file
@ -0,0 +1,32 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
console.log('node has started (true/false):', node.isStarted())
|
||||
console.log('listening on:')
|
||||
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
})
|
62
examples/transports/2.js
Normal file
62
examples/transports/2.js
Normal file
@ -0,0 +1,62 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const pull = require('pull-stream')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
function printAddrs (node, number) {
|
||||
console.log('node %s is listening on:', number)
|
||||
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
printAddrs(node1, '1')
|
||||
printAddrs(node2, '2')
|
||||
|
||||
node2.handle('/print', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
node1.dial(node2.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
|
||||
pull(pull.values(['Hello', ' ', 'p2p', ' ', 'world', '!']), conn)
|
||||
})
|
||||
})
|
86
examples/transports/3.js
Normal file
86
examples/transports/3.js
Normal file
@ -0,0 +1,86 @@
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
const parallel = require('async/parallel')
|
||||
const pull = require('pull-stream')
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP(), new WebSockets()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
function createNode (addrs, callback) {
|
||||
if (!Array.isArray(addrs)) {
|
||||
addrs = [addrs]
|
||||
}
|
||||
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
addrs.forEach((addr) => peerInfo.multiaddrs.add(addr))
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
function printAddrs (node, number) {
|
||||
console.log('node %s is listening on:', number)
|
||||
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
}
|
||||
|
||||
function print (protocol, conn) {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
}
|
||||
|
||||
parallel([
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', cb),
|
||||
(cb) => createNode(['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws'], cb),
|
||||
(cb) => createNode('/ip4/127.0.0.1/tcp/20000/ws', cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
const node3 = nodes[2]
|
||||
|
||||
printAddrs(node1, '1')
|
||||
printAddrs(node2, '2')
|
||||
printAddrs(node3, '3')
|
||||
|
||||
node1.handle('/print', print)
|
||||
node2.handle('/print', print)
|
||||
node3.handle('/print', print)
|
||||
|
||||
node1.dial(node2.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
|
||||
pull(pull.values(['node 1 dialed to node 2 successfully']), conn)
|
||||
})
|
||||
|
||||
node2.dial(node3.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
|
||||
pull(pull.values(['node 2 dialed to node 3 successfully']), conn)
|
||||
})
|
||||
|
||||
node3.dial(node1.peerInfo, '/print', (err, conn) => {
|
||||
if (err) {
|
||||
console.log('node 3 failed to dial to node 1 with:', err.message)
|
||||
}
|
||||
})
|
||||
})
|
305
examples/transports/README.md
Normal file
305
examples/transports/README.md
Normal file
@ -0,0 +1,305 @@
|
||||
# [Transports](http://libp2p.io/implementations/#transports)
|
||||
|
||||
libp2p doesn't make assumptions for you, instead, it enables you as the developer of the application to pick the modules you need to run your application, which can vary depending on the runtime you are executing. A libp2p node can use one or more Transports to dial and listen for Connections. These transports are modules that offer a clean interface for dialing and listening, defined by the [interface-transport](https://github.com/libp2p/interface-transport) specification. Some examples of possible transports are: TCP, UTP, WebRTC, QUIC, HTTP, Pigeon and so on.
|
||||
|
||||
A more complete definition of what is a transport can be found on the [interface-transport](https://github.com/libp2p/interface-transport) specification. A way to recognize a candidate transport is through the badge:
|
||||
|
||||
[](https://raw.githubusercontent.com/diasdavid/interface-transport/master/img/badge.png)
|
||||
|
||||
## 1. Creating a libp2p Bundle with TCP
|
||||
|
||||
When using libp2p, you always want to create your own libp2p Bundle, that is, pick your set of modules and create your network stack with the properties you need. In this example, we will create a bundle with TCP. You can find the complete solution on the file [1.js](./1.js).
|
||||
|
||||
You will need 4 deps total, so go ahead and install all of them with:
|
||||
|
||||
```
|
||||
> npm install libp2p libp2p-tcp peer-info async
|
||||
```
|
||||
|
||||
Then, on your favorite text editor create a file with the `.js` extension. I've called mine `1.js`.
|
||||
|
||||
First thing is to create our own bundle! Insert:
|
||||
|
||||
```JavaScript
|
||||
'use strict'
|
||||
|
||||
const libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const PeerInfo = require('peer-info')
|
||||
const waterfall = require('async/waterfall')
|
||||
|
||||
// This MyBundle class is your libp2p bundle packed with TCP
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
// modules is a JS object that will describe the components
|
||||
// we want for our libp2p bundle
|
||||
const modules = {
|
||||
transport: [new TCP()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now that we have our own MyBundle class that extends libp2p, let's create a node with it. We will use `async/waterfall` just for code structure, but you don't need to. Append to the same file:
|
||||
|
||||
```JavaScript
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
// First we create a PeerInfo object, which will pack the
|
||||
// info about our peer. Creating a PeerInfo is an async
|
||||
// operation because we use the WebCrypto API
|
||||
// (yeei Universal JS)
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
// To signall the addresses we want to be available, we use
|
||||
// the multiaddr format, a self describable address
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
// Now we can create a node with that PeerInfo object
|
||||
node = new MyBundle(peerInfo)
|
||||
// Last, we start the node!
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => {
|
||||
if (err) { throw err }
|
||||
|
||||
// At this point the node has started
|
||||
console.log('node has started (true/false):', node.isStarted())
|
||||
// And we can print the now listening addresses.
|
||||
// If you are familiar with TCP, you might have noticed
|
||||
// that we specified the node to listen in 0.0.0.0 and port
|
||||
// 0, which means "listen in any network interface and pick
|
||||
// a port for me
|
||||
console.log('listening on:')
|
||||
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
})
|
||||
```
|
||||
|
||||
Running this should result in somehting like:
|
||||
|
||||
```bash
|
||||
> node 1.js
|
||||
node has started (true/false): true
|
||||
listening on:
|
||||
/ip4/127.0.0.1/tcp/61329/ipfs/QmW2cKTakTYqbQkUzBTEGXgWYFj1YEPeUndE1YWs6CBzDQ
|
||||
/ip4/192.168.2.156/tcp/61329/ipfs/QmW2cKTakTYqbQkUzBTEGXgWYFj1YEPeUndE1YWs6CBzDQ
|
||||
```
|
||||
|
||||
That `QmW2cKTakTYqbQkUzBTEGXgWYFj1YEPeUndE1YWs6CBzDQ` is the PeerId that was created during the PeerInfo generation.
|
||||
|
||||
## 2. Dialing from one node to another node
|
||||
|
||||
Now that we have our bundle, let's create two nodes and make them dial to each other! You can find the complete solution at [2.js](./2.js).
|
||||
|
||||
For this step, we will need one more dependency.
|
||||
|
||||
```bash
|
||||
> npm install pull-stream
|
||||
```
|
||||
|
||||
We are going to reuse the MyBundle class from step 1, but this time to make things simpler, we will create two functions, one to create nodes and another to print the addrs to avoid duplicating code.
|
||||
|
||||
```JavaScript
|
||||
function createNode (callback) {
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
|
||||
function printAddrs (node, number) {
|
||||
console.log('node %s is listening on:', number)
|
||||
node.peerInfo.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
}
|
||||
```
|
||||
|
||||
Now we are going to use `async/parallel` to create two nodes, print their addresses and dial from one node to the other.
|
||||
|
||||
```
|
||||
parallel([
|
||||
(cb) => createNode(cb),
|
||||
(cb) => createNode(cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
|
||||
printAddrs(node1, '1')
|
||||
printAddrs(node2, '2')
|
||||
|
||||
node2.handle('/print', (protocol, conn) => {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
})
|
||||
|
||||
node1.dial(node2.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
|
||||
pull(pull.values(['Hello', ' ', 'p2p', ' ', 'world', '!']), conn)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
The result should be look like:
|
||||
|
||||
```bash
|
||||
> node 2.js
|
||||
node 1 is listening on:
|
||||
/ip4/127.0.0.1/tcp/62279/ipfs/QmeM4wNWv1uci7UJjUXZYfvcy9uqAbw7G9icuxdqy88Mj9
|
||||
/ip4/192.168.2.156/tcp/62279/ipfs/QmeM4wNWv1uci7UJjUXZYfvcy9uqAbw7G9icuxdqy88Mj9
|
||||
node 2 is listening on:
|
||||
/ip4/127.0.0.1/tcp/62278/ipfs/QmWp58xJgzbouNJcyiNNTpZuqQCJU8jf6ixc7TZT9xEZhV
|
||||
/ip4/192.168.2.156/tcp/62278/ipfs/QmWp58xJgzbouNJcyiNNTpZuqQCJU8jf6ixc7TZT9xEZhV
|
||||
Hello p2p world!
|
||||
```
|
||||
|
||||
## 3. Using multiple transports
|
||||
|
||||
Next, we want to be available in multiple transports to increase our chances of having common transports in the network. A simple scenario, a node running in the browser only has access to HTTP, WebSockets and WebRTC since the browser doesn't let you open any other kind of transport, for this node to dial to some other node, that other node needs to share a common transport.
|
||||
|
||||
What we are going to do in this step is to create 3 nodes, one with TCP, another with TCP+WebSockets and another one with just WebSockets. The full solution can be found on [3.js](./3.js).
|
||||
|
||||
In this example, we will need to also install `libp2p-websockets`, go ahead and install:
|
||||
|
||||
```sh
|
||||
> npm install libp2p-websockets
|
||||
```
|
||||
|
||||
We want to create 3 nodes, one with TCP, one with TCP+WebSockets and one with just WebSockets. We need to update our `MyBundle` class to contemplate WebSockets as well:
|
||||
|
||||
```JavaScript
|
||||
const WebSockets = require('libp2p-websockets')
|
||||
// ...
|
||||
|
||||
class MyBundle extends libp2p {
|
||||
constructor (peerInfo) {
|
||||
const modules = {
|
||||
transport: [new TCP(), new WebSockets()]
|
||||
}
|
||||
super(modules, peerInfo)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now that we have our bundle ready, let's upgrade our createNode function to enable us to pick the addrs in which a node will start a listener.
|
||||
|
||||
```JavaScript
|
||||
function createNode (addrs, callback) {
|
||||
if (!Array.isArray(addrs)) {
|
||||
addrs = [addrs]
|
||||
}
|
||||
|
||||
let node
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerInfo.create(cb),
|
||||
(peerInfo, cb) => {
|
||||
addrs.forEach((addr) => peerInfo.multiaddrs.add(addr))
|
||||
node = new MyBundle(peerInfo)
|
||||
node.start(cb)
|
||||
}
|
||||
], (err) => callback(err, node))
|
||||
}
|
||||
```
|
||||
|
||||
As a rule, a libp2p node will only be capable of using a transport if: a) it has the module for it and b) it was given a multiaddr to listen on. The only exception to this rule is WebSockets in the browser, where a node can dial out, but unfortunately cannot open a socket.
|
||||
|
||||
Let's update our flow to create nodes and see how they behave when dialing to each other:
|
||||
|
||||
```JavaScript
|
||||
parallel([
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', cb),
|
||||
// Here we add an extra multiaddr that has a /ws at the end, this means that we want
|
||||
// to create a TCP socket, but mount it as WebSockets instead.
|
||||
(cb) => createNode(['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws'], cb),
|
||||
(cb) => createNode('/ip4/127.0.0.1/tcp/20000/ws', cb)
|
||||
], (err, nodes) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const node1 = nodes[0]
|
||||
const node2 = nodes[1]
|
||||
const node3 = nodes[2]
|
||||
|
||||
printAddrs(node1, '1')
|
||||
printAddrs(node2, '2')
|
||||
printAddrs(node3, '3')
|
||||
|
||||
node1.handle('/print', print)
|
||||
node2.handle('/print', print)
|
||||
node3.handle('/print', print)
|
||||
|
||||
// node 1 (TCP) dials to node 2 (TCP+WebSockets)
|
||||
node1.dial(node2.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
|
||||
pull(pull.values(['node 1 dialed to node 2 successfully']), conn)
|
||||
})
|
||||
|
||||
// node 2 (TCP+WebSockets) dials to node 2 (WebSockets)
|
||||
node2.dial(node3.peerInfo, '/print', (err, conn) => {
|
||||
if (err) { throw err }
|
||||
|
||||
pull(pull.values(['node 2 dialed to node 3 successfully']), conn)
|
||||
})
|
||||
|
||||
// node 3 (WebSockets) attempts to dial to node 1 (TCP)
|
||||
node3.dial(node1.peerInfo, '/print', (err, conn) => {
|
||||
if (err) {
|
||||
console.log('node 3 failed to dial to node 1 with:', err.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
`print` is a function created using the code from 2.js, but factored into its own function to save lines, here it is:
|
||||
|
||||
```JavaScript
|
||||
function print (protocol, conn) {
|
||||
pull(
|
||||
conn,
|
||||
pull.map((v) => v.toString()),
|
||||
pull.log()
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
If everything was set correctly, you now should see the following after you run the script:
|
||||
|
||||
```Bash
|
||||
> node 3.js
|
||||
node 1 is listening on:
|
||||
/ip4/127.0.0.1/tcp/62620/ipfs/QmWpWmcVJkF6EpmAaVDauku8g1uFGuxPsGP35XZp9GYEqs
|
||||
/ip4/192.168.2.156/tcp/62620/ipfs/QmWpWmcVJkF6EpmAaVDauku8g1uFGuxPsGP35XZp9GYEqs
|
||||
node 2 is listening on:
|
||||
/ip4/127.0.0.1/tcp/10000/ws/ipfs/QmWAQtWdzWXibgfyc7WRHhhv6MdqVKzXvyfSTnN2aAvixX
|
||||
/ip4/127.0.0.1/tcp/62619/ipfs/QmWAQtWdzWXibgfyc7WRHhhv6MdqVKzXvyfSTnN2aAvixX
|
||||
/ip4/192.168.2.156/tcp/62619/ipfs/QmWAQtWdzWXibgfyc7WRHhhv6MdqVKzXvyfSTnN2aAvixX
|
||||
node 3 is listening on:
|
||||
/ip4/127.0.0.1/tcp/20000/ws/ipfs/QmVq1PWh3VSDYdFqYMtqp4YQyXcrH27N7968tGdM1VQPj1
|
||||
node 3 failed to dial to node 1 with: No available transport to dial to
|
||||
node 1 dialed to node 2 successfully
|
||||
node 2 dialed to node 3 successfully
|
||||
```
|
||||
|
||||
As expected, we created 3 nodes, node 1 with TCP, node 2 with TCP+WebSockets and node 3 with just WebSockets. node 1 -> node 2 and node 2 -> node 3 managed to dial correctly because they shared a common transport, however, node 3 -> node 1 failed because they didn't share any.
|
||||
|
||||
## 4. How to create a new libp2p transport
|
||||
|
||||
Today there are already 3 transports available, one in the works and plenty to come, you can find these at [interface-transport implementations](https://github.com/libp2p/interface-transport#modules-that-implement-the-interface) list.
|
||||
|
||||
Adding more transports is done through the same way as you added TCP and WebSockets. Some transports might offer extra functionalities but for what is libp2p concern, as long as it follows the interface defined at the [spec](https://github.com/libp2p/interface-transport#api), it will be able to use it.
|
||||
|
||||
If you decide to implement a transport yourself, please consider adding to the list so that others can use it as well.
|
||||
|
||||
Hope this tutorial was useful. We are always looking to improve it, contributions are welcome!
|
87
package.json
87
package.json
@ -1,16 +1,19 @@
|
||||
{
|
||||
"name": "libp2p",
|
||||
"version": "0.8.0",
|
||||
"description": "JavaScript Skeleton for libp2p bundles",
|
||||
"version": "0.14.0",
|
||||
"description": "JavaScript base class for libp2p bundles",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"test": "aegir-test node",
|
||||
"lint": "aegir-lint",
|
||||
"release": "aegir-release node",
|
||||
"release-minor": "aegir-release --type minor",
|
||||
"release-major": "aegir-release --type major",
|
||||
"coverage": "aegir-coverage",
|
||||
"coverage-publish": "aegir-coverage publish"
|
||||
"lint": "aegir lint",
|
||||
"build": "aegir build",
|
||||
"test": "aegir test -t node -t browser",
|
||||
"test:node": "aegir test -t node",
|
||||
"test:browser": "aegir test -t browser",
|
||||
"release": "aegir release -t node -t browser",
|
||||
"release-minor": "aegir release --type minor -t node -t browser",
|
||||
"release-major": "aegir release --type major -t node -t browser",
|
||||
"coverage": "aegir coverage",
|
||||
"coverage-publish": "aegir coverage --provider coveralls"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -20,7 +23,7 @@
|
||||
"IPFS"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0.0",
|
||||
"node": ">=6.0.0",
|
||||
"npm": ">=3.0.0"
|
||||
},
|
||||
"pre-commit": [
|
||||
@ -33,26 +36,60 @@
|
||||
"url": "https://github.com/libp2p/js-libp2p/issues"
|
||||
},
|
||||
"homepage": "https://github.com/libp2p/js-libp2p",
|
||||
"devDependencies": {
|
||||
"aegir": "^11.0.1",
|
||||
"chai": "^3.5.0",
|
||||
"dirty-chai": "^1.2.2",
|
||||
"pre-commit": "^1.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"libp2p-ping": "~0.3.2",
|
||||
"libp2p-swarm": "~0.29.0",
|
||||
"mafmt": "^2.1.8",
|
||||
"multiaddr": "^2.3.0",
|
||||
"peer-book": "~0.4.0",
|
||||
"peer-id": "~0.8.6",
|
||||
"peer-info": "~0.9.2"
|
||||
"async": "^2.6.0",
|
||||
"libp2p-ping": "~0.6.0",
|
||||
"libp2p-swarm": "~0.34.0",
|
||||
"mafmt": "^3.0.2",
|
||||
"multiaddr": "^3.0.1",
|
||||
"peer-book": "~0.5.2",
|
||||
"peer-id": "~0.10.3",
|
||||
"peer-info": "~0.11.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aegir": "^12.2.0",
|
||||
"chai": "^4.1.2",
|
||||
"cids": "~0.5.2",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"electron-webrtc": "~0.3.0",
|
||||
"libp2p-circuit": "~0.1.4",
|
||||
"libp2p-kad-dht": "~0.6.0",
|
||||
"libp2p-mdns": "~0.9.1",
|
||||
"libp2p-multiplex": "~0.5.1",
|
||||
"libp2p-railing": "~0.7.1",
|
||||
"libp2p-secio": "~0.8.1",
|
||||
"libp2p-spdy": "~0.11.0",
|
||||
"libp2p-tcp": "~0.11.1",
|
||||
"libp2p-webrtc-star": "~0.13.3",
|
||||
"libp2p-websockets": "~0.10.4",
|
||||
"libp2p-websocket-star": "~0.7.2",
|
||||
"libp2p-websocket-star-rendezvous": "~0.2.2",
|
||||
"lodash.times": "^4.3.2",
|
||||
"pre-commit": "^1.2.2",
|
||||
"pull-goodbye": "0.0.2",
|
||||
"pull-serializer": "~0.3.2",
|
||||
"pull-stream": "^3.6.1",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"sinon": "^4.1.3",
|
||||
"wrtc": "0.0.63"
|
||||
},
|
||||
"contributors": [
|
||||
"Chris Bratlien <chrisbratlien@gmail.com>",
|
||||
"Daijiro Wachi <daijiro.wachi@gmail.com>",
|
||||
"David Dias <daviddias.p@gmail.com>",
|
||||
"Dmitriy Ryajov <dryajov@gmail.com>",
|
||||
"Elven <mon.samuel@qq.com>",
|
||||
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||
"Joel Gustafson <joelg@mit.edu>",
|
||||
"Lars Gierth <lgierth@users.noreply.github.com>",
|
||||
"Maciej Krüger <mkg20001@gmail.com>",
|
||||
"Nuno Nogueira <nunofmn@gmail.com>",
|
||||
"Pedro Teixeira <i@pgte.me>",
|
||||
"RasmusErik Voel Jensen <github@solsort.com>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"Ryan Bell <ryan@piing.net>",
|
||||
"greenkeeperio-bot <support@greenkeeper.io>",
|
||||
"mayerwin <mayerwin@users.noreply.github.com>"
|
||||
"mayerwin <mayerwin@users.noreply.github.com>",
|
||||
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
224
src/index.js
224
src/index.js
@ -1,19 +1,22 @@
|
||||
'use strict'
|
||||
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const assert = require('assert')
|
||||
|
||||
const setImmediate = require('async/setImmediate')
|
||||
const each = require('async/each')
|
||||
const series = require('async/series')
|
||||
|
||||
const Ping = require('libp2p-ping')
|
||||
const Swarm = require('libp2p-swarm')
|
||||
const PeerId = require('peer-id')
|
||||
const PeerInfo = require('peer-info')
|
||||
const mafmt = require('mafmt')
|
||||
const PeerBook = require('peer-book')
|
||||
const multiaddr = require('multiaddr')
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const assert = require('assert')
|
||||
const Ping = require('libp2p-ping')
|
||||
const setImmediate = require('async/setImmediate')
|
||||
|
||||
exports = module.exports
|
||||
|
||||
const OFFLINE_ERROR_MESSAGE = 'The libp2p node is not started yet'
|
||||
const NOT_STARTED_ERROR_MESSAGE = 'The libp2p node is not started yet'
|
||||
|
||||
class Node extends EventEmitter {
|
||||
constructor (_modules, _peerInfo, _peerBook, _options) {
|
||||
@ -24,21 +27,24 @@ class Node extends EventEmitter {
|
||||
this.modules = _modules
|
||||
this.peerInfo = _peerInfo
|
||||
this.peerBook = _peerBook || new PeerBook()
|
||||
this.isOnline = false
|
||||
_options = _options || {}
|
||||
|
||||
this._isStarted = false
|
||||
|
||||
this.swarm = new Swarm(this.peerInfo, this.peerBook)
|
||||
|
||||
// Attach stream multiplexers
|
||||
if (this.modules.connection.muxer) {
|
||||
if (this.modules.connection && this.modules.connection.muxer) {
|
||||
let muxers = this.modules.connection.muxer
|
||||
muxers = Array.isArray(muxers) ? muxers : [muxers]
|
||||
muxers.forEach((muxer) => {
|
||||
this.swarm.connection.addStreamMuxer(muxer)
|
||||
})
|
||||
muxers.forEach((muxer) => this.swarm.connection.addStreamMuxer(muxer))
|
||||
|
||||
// If muxer exists, we can use Identify
|
||||
this.swarm.connection.reuse()
|
||||
|
||||
// If muxer exists, we can use Relay for listening/dialing
|
||||
this.swarm.connection.enableCircuitRelay(_options.relay)
|
||||
|
||||
// Received incommind dial and muxer upgrade happened,
|
||||
// reuse this muxed connection
|
||||
this.swarm.on('peer-mux-established', (peerInfo) => {
|
||||
@ -52,7 +58,7 @@ class Node extends EventEmitter {
|
||||
}
|
||||
|
||||
// Attach crypto channels
|
||||
if (this.modules.connection.crypto) {
|
||||
if (this.modules.connection && this.modules.connection.crypto) {
|
||||
let cryptos = this.modules.connection.crypto
|
||||
cryptos = Array.isArray(cryptos) ? cryptos : [cryptos]
|
||||
cryptos.forEach((crypto) => {
|
||||
@ -73,9 +79,64 @@ class Node extends EventEmitter {
|
||||
// Mount default protocols
|
||||
Ping.mount(this.swarm)
|
||||
|
||||
// Not fully implemented in js-libp2p yet
|
||||
this.routing = undefined
|
||||
this.records = undefined
|
||||
// dht provided components (peerRouting, contentRouting, dht)
|
||||
if (_modules.DHT) {
|
||||
this._dht = new this.modules.DHT(this.swarm, {
|
||||
kBucketSize: 20,
|
||||
datastoer: _options.DHT && _options.DHT.datastore
|
||||
})
|
||||
}
|
||||
|
||||
this.peerRouting = {
|
||||
findPeer: (id, callback) => {
|
||||
if (!this._dht) {
|
||||
return callback(new Error('DHT is not available'))
|
||||
}
|
||||
|
||||
this._dht.findPeer(id, callback)
|
||||
}
|
||||
}
|
||||
|
||||
this.contentRouting = {
|
||||
findProviders: (key, timeout, callback) => {
|
||||
if (!this._dht) {
|
||||
return callback(new Error('DHT is not available'))
|
||||
}
|
||||
|
||||
this._dht.findProviders(key, timeout, callback)
|
||||
},
|
||||
provide: (key, callback) => {
|
||||
if (!this._dht) {
|
||||
return callback(new Error('DHT is not available'))
|
||||
}
|
||||
|
||||
this._dht.provide(key, callback)
|
||||
}
|
||||
}
|
||||
|
||||
this.dht = {
|
||||
put: (key, value, callback) => {
|
||||
if (!this._dht) {
|
||||
return callback(new Error('DHT is not available'))
|
||||
}
|
||||
|
||||
this._dht.put(key, value, callback)
|
||||
},
|
||||
get: (key, callback) => {
|
||||
if (!this._dht) {
|
||||
return callback(new Error('DHT is not available'))
|
||||
}
|
||||
|
||||
this._dht.get(key, callback)
|
||||
},
|
||||
getMany (key, nVals, callback) {
|
||||
if (!this._dht) {
|
||||
return callback(new Error('DHT is not available'))
|
||||
}
|
||||
|
||||
this._dht.getMany(key, nVals, callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -95,8 +156,8 @@ class Node extends EventEmitter {
|
||||
// so that we can have webrtc-star addrs without adding manually the id
|
||||
const maOld = []
|
||||
const maNew = []
|
||||
this.peerInfo.multiaddrs.forEach((ma) => {
|
||||
if (!mafmt.IPFS.matches(ma)) {
|
||||
this.peerInfo.multiaddrs.toArray().forEach((ma) => {
|
||||
if (!ma.getPeerId()) {
|
||||
maOld.push(ma)
|
||||
maNew.push(ma.encapsulate('/ipfs/' + this.peerInfo.id.toB58String()))
|
||||
}
|
||||
@ -104,7 +165,6 @@ class Node extends EventEmitter {
|
||||
this.peerInfo.multiaddrs.replace(maOld, maNew)
|
||||
|
||||
const multiaddrs = this.peerInfo.multiaddrs.toArray()
|
||||
|
||||
transports.forEach((transport) => {
|
||||
if (transport.filter(multiaddrs).length > 0) {
|
||||
this.swarm.transport.add(
|
||||
@ -117,31 +177,54 @@ class Node extends EventEmitter {
|
||||
}
|
||||
})
|
||||
|
||||
this.swarm.listen((err) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
if (ws) {
|
||||
this.swarm.transport.add(ws.tag || ws.constructor.name, ws)
|
||||
}
|
||||
series([
|
||||
(cb) => this.swarm.listen(cb),
|
||||
(cb) => {
|
||||
if (ws) {
|
||||
// always add dialing on websockets
|
||||
this.swarm.transport.add(ws.tag || ws.constructor.name, ws)
|
||||
}
|
||||
|
||||
this.isOnline = true
|
||||
|
||||
if (this.modules.discovery) {
|
||||
this.modules.discovery.forEach((discovery) => {
|
||||
setImmediate(() => discovery.start(() => {}))
|
||||
// all transports need to be setup before discover starts
|
||||
if (this.modules.discovery) {
|
||||
return each(this.modules.discovery, (d, cb) => d.start(cb), cb)
|
||||
}
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
// TODO: chicken-and-egg problem:
|
||||
// have to set started here because DHT requires libp2p is already started
|
||||
this._isStarted = true
|
||||
if (this._dht) {
|
||||
return this._dht.start(cb)
|
||||
}
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
// detect which multiaddrs we don't have a transport for and remove them
|
||||
const multiaddrs = this.peerInfo.multiaddrs.toArray()
|
||||
transports.forEach((transport) => {
|
||||
multiaddrs.forEach((multiaddr) => {
|
||||
if (!multiaddr.toString().match(/\/p2p-circuit($|\/)/) &&
|
||||
!transports.find((transport) => transport.filter(multiaddr).length > 0)) {
|
||||
this.peerInfo.multiaddrs.delete(multiaddr)
|
||||
}
|
||||
})
|
||||
})
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
this.emit('start')
|
||||
cb()
|
||||
}
|
||||
|
||||
callback()
|
||||
})
|
||||
], callback)
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop the libp2p node by closing its listeners and open connections
|
||||
*/
|
||||
stop (callback) {
|
||||
this.isOnline = false
|
||||
this._isStarted = false
|
||||
|
||||
if (this.modules.discovery) {
|
||||
this.modules.discovery.forEach((discovery) => {
|
||||
@ -149,48 +232,69 @@ class Node extends EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
this.swarm.close(callback)
|
||||
series([
|
||||
(cb) => {
|
||||
if (this._dht) {
|
||||
return this._dht.stop(cb)
|
||||
}
|
||||
cb()
|
||||
},
|
||||
(cb) => this.swarm.close(cb),
|
||||
(cb) => {
|
||||
this.emit('stop')
|
||||
cb()
|
||||
}
|
||||
], callback)
|
||||
}
|
||||
|
||||
isOn () {
|
||||
return this.isOnline
|
||||
isStarted () {
|
||||
return this._isStarted
|
||||
}
|
||||
|
||||
ping (peer, callback) {
|
||||
assert(this.isOn(), OFFLINE_ERROR_MESSAGE)
|
||||
const peerInfo = this._getPeerInfo(peer)
|
||||
callback(null, new Ping(this.swarm, peerInfo))
|
||||
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
|
||||
this._getPeerInfo(peer, (err, peerInfo) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
callback(null, new Ping(this.swarm, peerInfo))
|
||||
})
|
||||
}
|
||||
|
||||
dial (peer, protocol, callback) {
|
||||
assert(this.isOn(), OFFLINE_ERROR_MESSAGE)
|
||||
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
|
||||
|
||||
if (typeof protocol === 'function') {
|
||||
callback = protocol
|
||||
protocol = undefined
|
||||
}
|
||||
|
||||
let peerInfo
|
||||
try {
|
||||
peerInfo = this._getPeerInfo(peer)
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
this.swarm.dial(peerInfo, protocol, (err, conn) => {
|
||||
this._getPeerInfo(peer, (err, peerInfo) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
this.peerBook.put(peerInfo)
|
||||
callback(null, conn)
|
||||
|
||||
this.swarm.dial(peerInfo, protocol, (err, conn) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
this.peerBook.put(peerInfo)
|
||||
callback(null, conn)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
hangUp (peer, callback) {
|
||||
assert(this.isOn(), OFFLINE_ERROR_MESSAGE)
|
||||
const peerInfo = this._getPeerInfo(peer)
|
||||
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
|
||||
|
||||
this.swarm.hangUp(peerInfo, callback)
|
||||
this._getPeerInfo(peer, (err, peerInfo) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
this.swarm.hangUp(peerInfo, callback)
|
||||
})
|
||||
}
|
||||
|
||||
handle (protocol, handlerFunc, matchFunc) {
|
||||
@ -204,10 +308,12 @@ class Node extends EventEmitter {
|
||||
/*
|
||||
* Helper method to check the data type of peer and convert it to PeerInfo
|
||||
*/
|
||||
_getPeerInfo (peer) {
|
||||
_getPeerInfo (peer, callback) {
|
||||
let p
|
||||
// PeerInfo
|
||||
if (PeerInfo.isPeerInfo(peer)) {
|
||||
p = peer
|
||||
// Multiaddr instance (not string)
|
||||
} else if (multiaddr.isMultiaddr(peer)) {
|
||||
const peerIdB58Str = peer.getPeerId()
|
||||
try {
|
||||
@ -216,19 +322,19 @@ class Node extends EventEmitter {
|
||||
p = new PeerInfo(PeerId.createFromB58String(peerIdB58Str))
|
||||
}
|
||||
p.multiaddrs.add(peer)
|
||||
// PeerId
|
||||
} else if (PeerId.isPeerId(peer)) {
|
||||
const peerIdB58Str = peer.toB58String()
|
||||
try {
|
||||
p = this.peerBook.get(peerIdB58Str)
|
||||
} catch (err) {
|
||||
// TODO this is where PeerRouting comes into place
|
||||
throw new Error('No knowledge about: ' + peerIdB58Str)
|
||||
return this.peerRouting.findPeer(peer, callback)
|
||||
}
|
||||
} else {
|
||||
throw new Error('peer type not recognized')
|
||||
return setImmediate(() => callback(new Error('peer type not recognized')))
|
||||
}
|
||||
|
||||
return p
|
||||
setImmediate(() => callback(null, p))
|
||||
}
|
||||
}
|
||||
|
||||
|
4
test/browser.js
Normal file
4
test/browser.js
Normal file
@ -0,0 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
require('./base')
|
||||
require('./transports.browser')
|
225
test/circuit-relay.node.js
Normal file
225
test/circuit-relay.node.js
Normal file
@ -0,0 +1,225 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const pull = require('pull-stream')
|
||||
const waterfall = require('async/waterfall')
|
||||
const series = require('async/series')
|
||||
const parallel = require('async/parallel')
|
||||
const utils = require('./utils/node')
|
||||
const Circuit = require('libp2p-circuit')
|
||||
const multiaddr = require('multiaddr')
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
|
||||
const expect = chai.expect
|
||||
const sinon = require('sinon')
|
||||
|
||||
describe('circuit relay', function () {
|
||||
let handlerSpies = []
|
||||
let relayNode1
|
||||
let relayNode2
|
||||
let nodeWS1
|
||||
let nodeWS2
|
||||
let nodeTCP1
|
||||
let nodeTCP2
|
||||
|
||||
function setupNode (addrs, options, cb) {
|
||||
if (typeof options === 'function') {
|
||||
cb = options
|
||||
options = {}
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
|
||||
return utils.createNode(addrs, options, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
node.handle('/echo/1.0.0', utils.echo)
|
||||
node.start((err) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
handlerSpies.push(sinon.spy(node.swarm.transports[Circuit.tag].listeners[0].hopHandler, 'handle'))
|
||||
cb(node)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
before(function (done) {
|
||||
this.timeout(20000)
|
||||
|
||||
waterfall([
|
||||
// set up passive relay
|
||||
(cb) => setupNode([
|
||||
'/ip4/0.0.0.0/tcp/0/ws',
|
||||
'/ip4/0.0.0.0/tcp/0'
|
||||
], {
|
||||
relay: {
|
||||
enabled: true,
|
||||
hop: {
|
||||
enabled: true,
|
||||
active: false // passive relay
|
||||
}
|
||||
}
|
||||
}, (node) => {
|
||||
relayNode1 = node
|
||||
cb()
|
||||
}),
|
||||
// setup active relay
|
||||
(cb) => setupNode([
|
||||
'/ip4/0.0.0.0/tcp/0/ws',
|
||||
'/ip4/0.0.0.0/tcp/0'
|
||||
], {
|
||||
relay: {
|
||||
enabled: true,
|
||||
hop: {
|
||||
enabled: true,
|
||||
active: false // passive relay
|
||||
}
|
||||
}
|
||||
}, (node) => {
|
||||
relayNode2 = node
|
||||
cb()
|
||||
}),
|
||||
// setup node with WS
|
||||
(cb) => setupNode([
|
||||
'/ip4/0.0.0.0/tcp/0/ws'
|
||||
], {
|
||||
relay: {
|
||||
enabled: true
|
||||
}
|
||||
}, (node) => {
|
||||
nodeWS1 = node
|
||||
cb()
|
||||
}),
|
||||
// setup node with WS
|
||||
(cb) => setupNode([
|
||||
'/ip4/0.0.0.0/tcp/0/ws'
|
||||
], {
|
||||
relay: {
|
||||
enabled: true
|
||||
}
|
||||
}, (node) => {
|
||||
nodeWS2 = node
|
||||
cb()
|
||||
}),
|
||||
// set up node with TCP and listening on relay1
|
||||
(cb) => setupNode([
|
||||
'/ip4/0.0.0.0/tcp/0',
|
||||
`/ipfs/${relayNode1.peerInfo.id.toB58String()}/p2p-circuit`
|
||||
], {
|
||||
relay: {
|
||||
enabled: true
|
||||
}
|
||||
}, (node) => {
|
||||
nodeTCP1 = node
|
||||
cb()
|
||||
}),
|
||||
// set up node with TCP and listening on relay2 over TCP transport
|
||||
(cb) => setupNode([
|
||||
'/ip4/0.0.0.0/tcp/0',
|
||||
`/ip4/0.0.0.0/tcp/0/ipfs/${relayNode2.peerInfo.id.toB58String()}/p2p-circuit`
|
||||
], {
|
||||
relay: {
|
||||
enabled: true
|
||||
}
|
||||
}, (node) => {
|
||||
nodeTCP2 = node
|
||||
cb()
|
||||
})
|
||||
], (err) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
series([
|
||||
(cb) => nodeWS1.dial(relayNode1.peerInfo, cb),
|
||||
(cb) => nodeWS1.dial(relayNode2.peerInfo, cb),
|
||||
(cb) => nodeTCP1.dial(relayNode1.peerInfo, cb),
|
||||
(cb) => nodeTCP2.dial(relayNode2.peerInfo, cb)
|
||||
], done)
|
||||
})
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
parallel([
|
||||
(cb) => relayNode1.stop(cb),
|
||||
(cb) => relayNode2.stop(cb),
|
||||
(cb) => nodeWS1.stop(cb),
|
||||
(cb) => nodeWS2.stop(cb),
|
||||
(cb) => nodeTCP1.stop(cb),
|
||||
(cb) => nodeTCP2.stop(cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
describe('any relay', function () {
|
||||
this.timeout(20 * 1000)
|
||||
|
||||
it('should dial from WS1 to TCP1 over any R', (done) => {
|
||||
nodeWS1.dial(nodeTCP1.peerInfo, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(conn).to.exist()
|
||||
|
||||
pull(
|
||||
pull.values(['hello']),
|
||||
conn,
|
||||
pull.collect((err, result) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(result[0].toString()).to.equal('hello')
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should not dial - no R from WS2 to TCP1', (done) => {
|
||||
nodeWS2.dial(nodeTCP2.peerInfo, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.exist()
|
||||
expect(conn).to.not.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('explicit relay', function () {
|
||||
this.timeout(20 * 1000)
|
||||
|
||||
it('should dial from WS1 to TCP1 over R1', (done) => {
|
||||
nodeWS1.dial(nodeTCP1.peerInfo, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(conn).to.exist()
|
||||
|
||||
pull(
|
||||
pull.values(['hello']),
|
||||
conn,
|
||||
pull.collect((err, result) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(result[0].toString()).to.equal('hello')
|
||||
|
||||
const addr = multiaddr(handlerSpies[0].args[2][0].dstPeer.addrs[0]).toString()
|
||||
expect(addr).to.equal(`/ipfs/${nodeTCP1.peerInfo.id.toB58String()}`)
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should dial from WS1 to TCP2 over R2', (done) => {
|
||||
nodeWS1.dial(nodeTCP2.peerInfo, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(conn).to.exist()
|
||||
|
||||
pull(
|
||||
pull.values(['hello']),
|
||||
conn,
|
||||
pull.collect((err, result) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(result[0].toString()).to.equal('hello')
|
||||
|
||||
const addr = multiaddr(handlerSpies[1].args[2][0].dstPeer.addrs[0]).toString()
|
||||
expect(addr).to.equal(`/ipfs/${nodeTCP2.peerInfo.id.toB58String()}`)
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
91
test/content-routing.node.js
Normal file
91
test/content-routing.node.js
Normal file
@ -0,0 +1,91 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint max-nested-callbacks: ["error", 8] */
|
||||
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const expect = chai.expect
|
||||
const parallel = require('async/parallel')
|
||||
const _times = require('lodash.times')
|
||||
const CID = require('cids')
|
||||
const utils = require('./utils/node')
|
||||
const createNode = utils.createNode
|
||||
|
||||
describe('.contentRouting', () => {
|
||||
let nodeA
|
||||
let nodeB
|
||||
let nodeC
|
||||
let nodeD
|
||||
let nodeE
|
||||
|
||||
before(function (done) {
|
||||
this.timeout(5 * 1000)
|
||||
const tasks = _times(5, () => (cb) => {
|
||||
createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
mdns: false,
|
||||
dht: true
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
node.start((err) => cb(err, node))
|
||||
})
|
||||
})
|
||||
|
||||
parallel(tasks, (err, nodes) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeA = nodes[0]
|
||||
nodeB = nodes[1]
|
||||
nodeC = nodes[2]
|
||||
nodeD = nodes[3]
|
||||
nodeE = nodes[4]
|
||||
|
||||
parallel([
|
||||
(cb) => nodeA.dial(nodeB.peerInfo, cb),
|
||||
(cb) => nodeB.dial(nodeC.peerInfo, cb),
|
||||
(cb) => nodeC.dial(nodeD.peerInfo, cb),
|
||||
(cb) => nodeD.dial(nodeE.peerInfo, cb),
|
||||
(cb) => nodeE.dial(nodeA.peerInfo, cb)
|
||||
], done)
|
||||
})
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
parallel([
|
||||
(cb) => nodeA.stop(cb),
|
||||
(cb) => nodeB.stop(cb),
|
||||
(cb) => nodeC.stop(cb),
|
||||
(cb) => nodeD.stop(cb),
|
||||
(cb) => nodeE.stop(cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
describe('le ring', () => {
|
||||
const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
|
||||
|
||||
it('let kbucket get filled', (done) => {
|
||||
setTimeout(() => done(), 250)
|
||||
})
|
||||
|
||||
it('nodeA.contentRouting.provide', (done) => {
|
||||
nodeA.contentRouting.provide(cid, done)
|
||||
})
|
||||
|
||||
it('nodeE.contentRouting.findProviders for existing record', (done) => {
|
||||
nodeE.contentRouting.findProviders(cid, 5000, (err, providers) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(providers).to.have.length.above(0)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeC.contentRouting.findProviders for non existing record (timeout)', (done) => {
|
||||
const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSnnnn')
|
||||
|
||||
nodeE.contentRouting.findProviders(cid, 5000, (err, providers) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(providers).to.have.length(0)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
5
test/fixtures/test-data/test-id.json
vendored
Normal file
5
test/fixtures/test-data/test-id.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"id": "QmaG17D4kfTB2RNUCr16bSfVvUVt2Xn3rPYeqQDvnVcXFr",
|
||||
"privKey": "CAASqAkwggSkAgEAAoIBAQDBpXRrSLoVhP8C4YI0nm+YTb7UIe+xT9dwaMzKcGsH2zzz1lfxl54e1XNO+6Ut+If5jswpydgHhn9nGPod53sUIR2m+BiHOAH/Blgfa1nUKUkspts1MH3z5ZaO6Xo336Y0Uaw7UqfeIzKliTM6bpev2XIHyu0v/VJ2mylzfbDLMWqZs/shE3xwCJCD/PxoVpTlr/SzzF7MdgDMxkyiC3iLZ5Zkm+baPbi3mpKM0ue25Thedcc0KFjhQrjBfy5FPamrsMn5fnnoHwnQl9u7UWigzeC+7X+38EML1sVrV37ExxHPtM6882Ivjc7VN6zFHOHD2c9eVfbShkIf8YkVQUcFAgMBAAECggEAVE1mgGo58LJknml0WNn8tS5rfEiF5AhhPyOwvBTy04nDYFgZEykxgjTkrSbqgzfmYmOjSDICJUyNXGHISYqDz4CXOyBY9U0RuWeWp58BjVan75N4bRB+VNbHk9HbDkYEQlSoCW9ze0aRfvVa4v5QdRLSDMhwN+stokrsYcX/WIWYTM2e2jW+qQOzS8SJl7wYsgtd3WikrxwXkRL3sCMHEcgcPhoKacoD5Yr9cB0IC5vzhu4t/WMa+N2UEndcKGAbXsh8kA7BPFM6lqnEpOHpWEVEAYasAwFGUvUN9GwhtqpaNNS2sG6Nrz95cC99Nqx58uIXcTAJm3Fh/WfKJ6I1xQKBgQD+g7A5OSWw+i/zhTKVPJg93/eohViL0dGZT9Tf0/VslsFl00FwnZmBKA6BJ6ZL3hD00OcqIL3m6EzZ4q38U97XZxf2OUsPPJtl+Avqtlk16AHRHB9I17LGXJ30xZRkxL665oLms0D2T4NIZZX/uVMoS18lRvCZj1aEYQFCrZYgowKBgQDCxtA695S0vl6E3Q4G6MrDZK+2JqjaGL0XTnpHWiAjnk2lnV2CCZnWpEHT+ebF2fWx5nYQo5sugc6bS+4k9jRNUgxf2sQieZYCBjbnjCEVrPTm/dPTkaw1CQ/ox5/R1/Elbw8vteF9uUAvR0FL8Ss1Dqw6B2SxdTowxMy6qQ7sNwKBgG2N3eMj2DeP2egm45kdliK8L2yYyX6V+HTXyjf2kuQFGIZuIvMIw7S2u1eY65ooon/fFEIsCdJFGB+J1X6R05BAzi2sh8StP+7qkKadi1UK4w1R352JS2jbIRrlmXSuw7LL2njXnBTqMQaOw7xp14O2vePb32EaNBGTd+ltsvulAoGBALGIc4370oA4MIDb2Ag2MXKNmJbnf+piuB/BOTVGEZtFlDKLUArR43W/+/xRgKX/97FyhVS/OxfV21Kzj9oCy0NasMrB5RojRraLoYnFsPZH0mWlIGlsEtG4c9bR9XtYX4WmR+pN1r04mCc/xGWK6b4PpK2zxXT2i9ad2pmctGxbAoGBAIcp0UML5QCqvLmcob2/6PCRaYAxJBb9lDqOHredMgQih2hGnHFCyKk9eBAbFf/KN0guJTBDaAJRclcxsLLn7rV6grMNt+0EUepm7tWT0z5j8gNGbGGhuGDdqcmfJTc2EMdQrfhzYDN3rL1v3l+Ujwla2khL2ozE7SQ/KVeA1saY",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBpXRrSLoVhP8C4YI0nm+YTb7UIe+xT9dwaMzKcGsH2zzz1lfxl54e1XNO+6Ut+If5jswpydgHhn9nGPod53sUIR2m+BiHOAH/Blgfa1nUKUkspts1MH3z5ZaO6Xo336Y0Uaw7UqfeIzKliTM6bpev2XIHyu0v/VJ2mylzfbDLMWqZs/shE3xwCJCD/PxoVpTlr/SzzF7MdgDMxkyiC3iLZ5Zkm+baPbi3mpKM0ue25Thedcc0KFjhQrjBfy5FPamrsMn5fnnoHwnQl9u7UWigzeC+7X+38EML1sVrV37ExxHPtM6882Ivjc7VN6zFHOHD2c9eVfbShkIf8YkVQUcFAgMBAAE="
|
||||
}
|
5
test/fixtures/test-peer.json
vendored
Normal file
5
test/fixtures/test-peer.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"id": "Qmex1SSsueWFsUfjdkugJ5zhcnjddAt8TxcnDLUXKD9Sx7",
|
||||
"privKey": "CAASqAkwggSkAgEAAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAECggEAdBUzV/GaQ0nmoQrWvOnUxmFIho7kCjkh1NwnNVPNc+Msa1r7pcI9wJNPwap8j1w4L/cZuYhOJgcg+o2mWFiuULKZ4F9Ro/M89gZ038457g2/2pPu43c/Xoi/2YcAHXg0Gr+OCe2zCIyITBWKAFqyAzL6DubAxrJW2Ezj1LrZ+EZgMyzbh/go/eEGSJaaGkINeAkY144DqDWWWvzyhKhryipsGkZGEkVy9xJgMEI3ipVvuPez2XAvoyyeuinBBLe+Z2vY5G50XXzbIMhIQGLncHf9MwTv6wt1ilyOSLOXK0BoQbB76J3R3is5dSULXXP9r8VocjLBEkmBuf4FXAKzoQKBgQDNNS4F1XE1gxD8LPkL+aB/hi6eVHVPhr+w0I/9ATikcLGeUfBM2Gd6cZRPFtNVrv1p6ZF1D1UyGDknGbDBSQd9wLUgb0fDoo3jKYMGWq6G+VvaP5rzWQeBV8YV2EhSmUk1i6kiYe2ZE8WyrPie7iwpQIY60e2A8Ly0GKZiBZUcHQKBgQC9YDAVsGnEHFVFkTDpvw5HwEzCgTb2A3NgkGY3rTYZ7L6AFjqCYmUwFB8Fmbyc4kdFWNh8wfmq5Qrvl49NtaeukiqWKUUlB8uPdztB1P0IahA2ks0owStZlRifmwfgYyMd4xE17lhaOgQQJZZPxmP0F6mdOvb3YJafNURCdMS51wKBgEvvIM+h0tmFXXSjQ6kNvzlRMtD92ccKysYn9xAdMpOO6/r0wSH+dhQWEVZO0PcE4NsfReb2PIVj90ojtIdhebcr5xpQc1LORQjJJKXmSmzBux6AqNrhl+hhzXfp56FA/Zkly/lgGWaqrV5XqUxOP+Mn8EO1yNgMvRc7g94DyNB1AoGBAKLBuXHalXwDsdHBUB2Eo3xNLGt6bEcRfia+0+sEBdxQGQWylQScFkU09dh1YaIf44sZKa5HdBFJGpYCVxo9hmjFnK5Dt/Z0daHOonIY4INLzLVqg8KECoLKXkhGEIXsDjFQhukn+G1LMVTDSSU055DQiWjlVX4UWD9qo0jOXIkvAoGBAMP50p2X6PsWWZUuuR7i1JOJHRyQZPWdHh9p8SSLnCtEpHYZfJr4INXNmhnSiB/3TUnHix2vVKjosjMTCk/CjfzXV2H41WPOLZ2/Pi3SxCicWIRj4kCcWhkEuIF2jGkg1+jmNiCl/zNMaBOAIP3QbDPtqOWbYlPd2YIzdj6WQ6R4",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAE="
|
||||
}
|
40
test/multiaddr-trim.js
Normal file
40
test/multiaddr-trim.js
Normal file
@ -0,0 +1,40 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const expect = chai.expect
|
||||
const series = require('async/series')
|
||||
const createNode = require('./utils/node').createNode
|
||||
|
||||
describe('multiaddr trim', () => {
|
||||
it('non used multiaddrs get trimmed', (done) => {
|
||||
let node
|
||||
|
||||
series([
|
||||
(cb) => createNode([
|
||||
'/ip4/0.0.0.0/tcp/999/wss/p2p-webrtc-direct',
|
||||
'/ip4/127.0.0.1/tcp/55555/ws',
|
||||
'/ip4/0.0.0.0/tcp/0/'
|
||||
], (err, _node) => {
|
||||
expect(err).to.not.exist()
|
||||
node = _node
|
||||
const multiaddrs = node.peerInfo.multiaddrs.toArray()
|
||||
// multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
expect(multiaddrs).to.have.length(3)
|
||||
cb()
|
||||
}),
|
||||
(cb) => node.start(cb)
|
||||
], (err) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
const multiaddrs = node.peerInfo.multiaddrs.toArray()
|
||||
// console.log('--')
|
||||
// multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||
|
||||
expect(multiaddrs.length).to.at.least(2)
|
||||
expect(multiaddrs[0].toString()).to.match(/^\/ip4\/127\.0\.0\.1\/tcp\/[0-9]+\/ws\/ipfs\/\w+$/)
|
||||
node.stop(done)
|
||||
})
|
||||
})
|
||||
})
|
10
test/node.js
Normal file
10
test/node.js
Normal file
@ -0,0 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
require('./base')
|
||||
require('./transports.node')
|
||||
require('./stream-muxing.node')
|
||||
require('./peer-discovery.node')
|
||||
require('./peer-routing.node')
|
||||
require('./content-routing.node')
|
||||
require('./circuit-relay.node')
|
||||
require('./multiaddr-trim')
|
103
test/peer-discovery.node.js
Normal file
103
test/peer-discovery.node.js
Normal file
@ -0,0 +1,103 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const expect = chai.expect
|
||||
const signalling = require('libp2p-webrtc-star/src/sig-server')
|
||||
const parallel = require('async/parallel')
|
||||
const utils = require('./utils/node')
|
||||
const createNode = utils.createNode
|
||||
const echo = utils.echo
|
||||
|
||||
describe('peer discovery', () => {
|
||||
let nodeA
|
||||
let nodeB
|
||||
let port = 24642
|
||||
let ss
|
||||
|
||||
function setup (options) {
|
||||
before((done) => {
|
||||
port++
|
||||
parallel([
|
||||
(cb) => {
|
||||
signalling.start({ port: port }, (err, server) => {
|
||||
expect(err).to.not.exist()
|
||||
ss = server
|
||||
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()
|
||||
nodeA = 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()
|
||||
nodeB = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
], done)
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
parallel([
|
||||
(cb) => nodeA.stop(cb),
|
||||
(cb) => nodeB.stop(cb),
|
||||
(cb) => ss.stop(done)
|
||||
], done)
|
||||
})
|
||||
}
|
||||
|
||||
describe('MulticastDNS', () => {
|
||||
setup({ mdns: true })
|
||||
|
||||
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())
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// TODO needs a delay (this test is already long)
|
||||
describe.skip('WebRTCStar', () => {
|
||||
setup({ webRTCStar: true })
|
||||
|
||||
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())
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('MulticastDNS + WebRTCStar', () => {
|
||||
setup({
|
||||
webRTCStar: true,
|
||||
mdns: true
|
||||
})
|
||||
|
||||
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())
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
96
test/peer-routing.node.js
Normal file
96
test/peer-routing.node.js
Normal file
@ -0,0 +1,96 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint max-nested-callbacks: ["error", 8] */
|
||||
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const expect = chai.expect
|
||||
const parallel = require('async/parallel')
|
||||
const _times = require('lodash.times')
|
||||
const utils = require('./utils/node')
|
||||
const createNode = utils.createNode
|
||||
|
||||
describe('.peerRouting', () => {
|
||||
let nodeA
|
||||
let nodeB
|
||||
let nodeC
|
||||
let nodeD
|
||||
let nodeE
|
||||
|
||||
before(function (done) {
|
||||
this.timeout(5 * 1000)
|
||||
|
||||
const tasks = _times(5, () => (cb) => {
|
||||
createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
mdns: false,
|
||||
dht: true
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
node.start((err) => cb(err, node))
|
||||
})
|
||||
})
|
||||
|
||||
parallel(tasks, (err, nodes) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeA = nodes[0]
|
||||
nodeB = nodes[1]
|
||||
nodeC = nodes[2]
|
||||
nodeD = nodes[3]
|
||||
nodeE = nodes[4]
|
||||
|
||||
parallel([
|
||||
(cb) => nodeA.dial(nodeB.peerInfo, cb),
|
||||
(cb) => nodeB.dial(nodeC.peerInfo, cb),
|
||||
(cb) => nodeC.dial(nodeD.peerInfo, cb),
|
||||
(cb) => nodeD.dial(nodeE.peerInfo, cb),
|
||||
(cb) => nodeE.dial(nodeA.peerInfo, cb)
|
||||
], done)
|
||||
})
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
parallel([
|
||||
(cb) => nodeA.stop(cb),
|
||||
(cb) => nodeB.stop(cb),
|
||||
(cb) => nodeC.stop(cb),
|
||||
(cb) => nodeD.stop(cb),
|
||||
(cb) => nodeE.stop(cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
describe('el ring', () => {
|
||||
it('let kbucket get filled', (done) => {
|
||||
setTimeout(() => done(), 250)
|
||||
})
|
||||
|
||||
it('nodeA.dial by Id to node C', (done) => {
|
||||
nodeA.dial(nodeC.peerInfo.id, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeB.dial by Id to node D', (done) => {
|
||||
nodeB.dial(nodeD.peerInfo.id, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeC.dial by Id to node E', (done) => {
|
||||
nodeC.dial(nodeE.peerInfo.id, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeB.peerRouting.findPeer(nodeE.peerInfo.id)', (done) => {
|
||||
nodeB.peerRouting.findPeer(nodeE.peerInfo.id, (err, peerInfo) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(nodeE.peerInfo.id.toB58String()).to.equal(peerInfo.id.toB58String())
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
215
test/stream-muxing.node.js
Normal file
215
test/stream-muxing.node.js
Normal file
@ -0,0 +1,215 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const expect = chai.expect
|
||||
const parallel = require('async/parallel')
|
||||
const series = require('async/series')
|
||||
const pull = require('pull-stream')
|
||||
const utils = require('./utils/node')
|
||||
const createNode = utils.createNode
|
||||
const echo = utils.echo
|
||||
|
||||
function test (nodeA, nodeB, callback) {
|
||||
nodeA.dial(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hey')]),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(data).to.be.eql([Buffer.from('hey')])
|
||||
callback()
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function teardown (nodeA, nodeB, callback) {
|
||||
parallel([
|
||||
(cb) => nodeA.stop(cb),
|
||||
(cb) => nodeB.stop(cb)
|
||||
], callback)
|
||||
}
|
||||
|
||||
describe('stream muxing', () => {
|
||||
it('spdy only', function (done) {
|
||||
this.timeout(5 * 1000)
|
||||
|
||||
let nodeA
|
||||
let nodeB
|
||||
|
||||
function setup (callback) {
|
||||
parallel([
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
muxer: ['spdy']
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeA = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
muxer: ['spdy']
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeB = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
], callback)
|
||||
}
|
||||
|
||||
series([
|
||||
(cb) => setup(cb),
|
||||
(cb) => test(nodeA, nodeB, cb),
|
||||
(cb) => teardown(nodeA, nodeB, cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
it('multiplex only', (done) => {
|
||||
let nodeA
|
||||
let nodeB
|
||||
|
||||
function setup (callback) {
|
||||
parallel([
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
muxer: ['multiplex']
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeA = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
muxer: ['multiplex']
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeB = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
], callback)
|
||||
}
|
||||
|
||||
series([
|
||||
(cb) => setup(cb),
|
||||
(cb) => test(nodeA, nodeB, cb),
|
||||
(cb) => teardown(nodeA, nodeB, cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
it('spdy + multiplex', function (done) {
|
||||
this.timeout(5000)
|
||||
|
||||
let nodeA
|
||||
let nodeB
|
||||
|
||||
function setup (callback) {
|
||||
parallel([
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
muxer: ['spdy', 'multiplex']
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeA = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
muxer: ['spdy', 'multiplex']
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeB = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
], callback)
|
||||
}
|
||||
|
||||
series([
|
||||
(cb) => setup(cb),
|
||||
(cb) => test(nodeA, nodeB, cb),
|
||||
(cb) => teardown(nodeA, nodeB, cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
it('spdy + multiplex switched order', function (done) {
|
||||
this.timeout(5000)
|
||||
|
||||
let nodeA
|
||||
let nodeB
|
||||
|
||||
function setup (callback) {
|
||||
parallel([
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
muxer: ['spdy', 'multiplex']
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeA = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
muxer: ['multiplex', 'spdy']
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeB = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
], callback)
|
||||
}
|
||||
|
||||
series([
|
||||
(cb) => setup(cb),
|
||||
(cb) => test(nodeA, nodeB, cb),
|
||||
(cb) => teardown(nodeA, nodeB, cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
it('one without the other fails to establish a muxedConn', function (done) {
|
||||
this.timeout(5000)
|
||||
|
||||
let nodeA
|
||||
let nodeB
|
||||
|
||||
function setup (callback) {
|
||||
parallel([
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
muxer: ['spdy']
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeA = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', {
|
||||
muxer: ['multiplex']
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeB = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
], callback)
|
||||
}
|
||||
|
||||
series([
|
||||
(cb) => setup(cb),
|
||||
(cb) => {
|
||||
// it will just 'warm up a conn'
|
||||
expect(Object.keys(nodeA.swarm.muxers)).to.have.length(1)
|
||||
expect(Object.keys(nodeB.swarm.muxers)).to.have.length(1)
|
||||
|
||||
nodeA.dial(nodeB.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
|
||||
cb()
|
||||
})
|
||||
},
|
||||
(cb) => teardown(nodeA, nodeB, cb)
|
||||
], done)
|
||||
})
|
||||
})
|
424
test/transports.browser.js
Normal file
424
test/transports.browser.js
Normal file
@ -0,0 +1,424 @@
|
||||
/* eslint max-nested-callbacks: ["error", 8] */
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const expect = chai.expect
|
||||
const PeerInfo = require('peer-info')
|
||||
const PeerId = require('peer-id')
|
||||
const pull = require('pull-stream')
|
||||
const parallel = require('async/parallel')
|
||||
const goodbye = require('pull-goodbye')
|
||||
const serializer = require('pull-serializer')
|
||||
const w = require('webrtcsupport')
|
||||
|
||||
const Node = require('./utils/bundle.browser')
|
||||
const rawPeer = require('./fixtures/test-peer.json')
|
||||
|
||||
describe('transports', () => {
|
||||
describe('websockets', () => {
|
||||
let peerB
|
||||
let nodeA
|
||||
|
||||
before((done) => {
|
||||
const ma = '/ip4/127.0.0.1/tcp/9200/ws/ipfs/' + rawPeer.id
|
||||
|
||||
PeerId.createFromPrivKey(rawPeer.privKey, (err, id) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
peerB = new PeerInfo(id)
|
||||
peerB.multiaddrs.add(ma)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
after((done) => nodeA.stop(done))
|
||||
|
||||
it('create libp2pNode', (done) => {
|
||||
PeerInfo.create((err, peerInfo) => {
|
||||
expect(err).to.not.exist()
|
||||
peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
|
||||
|
||||
nodeA = new Node(peerInfo)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('create libp2pNode with multiplex only', (done) => {
|
||||
PeerInfo.create((err, peerInfo) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
const b = new Node(peerInfo, null, { muxer: ['multiplex'] })
|
||||
expect(b.modules.connection.muxer).to.eql([require('libp2p-multiplex')])
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('start libp2pNode', (done) => {
|
||||
nodeA.start(done)
|
||||
})
|
||||
|
||||
// General connectivity tests
|
||||
|
||||
it('libp2p.dial using Multiaddr nodeA to nodeB', (done) => {
|
||||
nodeA.dial(peerB.multiaddrs.toArray()[0], (err) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
setTimeout(check, 500) // Some time for Identify to finish
|
||||
|
||||
function check () {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('libp2p.dial using Multiaddr on Protocol nodeA to nodeB', (done) => {
|
||||
nodeA.dial(peerB.multiaddrs.toArray()[0], '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hey')]),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(data).to.eql([Buffer.from('hey')])
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('libp2p.hangUp using Multiaddr nodeA to nodeB', (done) => {
|
||||
nodeA.hangUp(peerB.multiaddrs.toArray()[0], (err) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('libp2p.dial using PeerInfo nodeA to nodeB', (done) => {
|
||||
nodeA.dial(peerB, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
setTimeout(check, 500) // Some time for Identify to finish
|
||||
|
||||
function check () {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('libp2p.dial using PeerInfo on Protocol nodeA to nodeB', (done) => {
|
||||
nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(err).to.not.exist()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hey')]),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(data).to.eql([Buffer.from('hey')])
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('libp2p.hangUp using PeerInfo nodeA to nodeB', (done) => {
|
||||
nodeA.hangUp(peerB, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(err).to.not.exist()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('stress', () => {
|
||||
it('one big write', (done) => {
|
||||
nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
const rawMessage = Buffer.alloc(100000)
|
||||
rawMessage.fill('a')
|
||||
|
||||
const s = serializer(goodbye({
|
||||
source: pull.values([rawMessage]),
|
||||
sink: pull.collect((err, results) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(results).to.have.length(1)
|
||||
expect(Buffer.from(results[0])).to.have.length(rawMessage.length)
|
||||
done()
|
||||
})
|
||||
}))
|
||||
pull(s, conn, s)
|
||||
})
|
||||
})
|
||||
|
||||
it('many writes', (done) => {
|
||||
nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
const s = serializer(goodbye({
|
||||
source: pull(
|
||||
pull.infinite(),
|
||||
pull.take(1000),
|
||||
pull.map((val) => Buffer.from(val.toString()))
|
||||
),
|
||||
sink: pull.collect((err, result) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(result).to.have.length(1000)
|
||||
done()
|
||||
})
|
||||
}))
|
||||
|
||||
pull(s, conn, s)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('webrtc-star', () => {
|
||||
if (!w.support) { return console.log('NO WEBRTC SUPPORT') }
|
||||
|
||||
let peer1
|
||||
let peer2
|
||||
let node1
|
||||
let node2
|
||||
|
||||
it('create two peerInfo with webrtc-star addrs', (done) => {
|
||||
parallel([
|
||||
(cb) => PeerId.create({ bits: 1024 }, cb),
|
||||
(cb) => PeerId.create({ bits: 1024 }, cb)
|
||||
], (err, ids) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
peer1 = new PeerInfo(ids[0])
|
||||
const ma1 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/ipfs/' + ids[0].toB58String()
|
||||
peer1.multiaddrs.add(ma1)
|
||||
|
||||
peer2 = new PeerInfo(ids[1])
|
||||
const ma2 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/ipfs/' + ids[1].toB58String()
|
||||
peer2.multiaddrs.add(ma2)
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('create two libp2p nodes with those peers', (done) => {
|
||||
node1 = new Node(peer1, null, { webRTCStar: true })
|
||||
node2 = new Node(peer2, null, { webRTCStar: true })
|
||||
done()
|
||||
})
|
||||
|
||||
it('listen on the two libp2p nodes', (done) => {
|
||||
parallel([
|
||||
(cb) => node1.start(cb),
|
||||
(cb) => node2.start(cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
it('handle a protocol on the first node', () => {
|
||||
node2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
|
||||
})
|
||||
|
||||
it('dial from the second node to the first node', (done) => {
|
||||
node1.dial(peer2, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
const text = 'hello'
|
||||
const peers1 = node1.peerBook.getAll()
|
||||
expect(Object.keys(peers1)).to.have.length(1)
|
||||
|
||||
const peers2 = node2.peerBook.getAll()
|
||||
expect(Object.keys(peers2)).to.have.length(1)
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from(text)]),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(data[0].toString()).to.equal(text)
|
||||
done()
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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(Object.keys(node1.swarm.muxedConns)).to.have.length(0)
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('create a third node and check that discovery works', (done) => {
|
||||
let counter = 0
|
||||
|
||||
function check () {
|
||||
if (++counter === 3) {
|
||||
expect(Object.keys(node1.swarm.muxedConns).length).to.equal(1)
|
||||
expect(Object.keys(node2.swarm.muxedConns).length).to.equal(1)
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
PeerId.create((err, id3) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
const peer3 = new PeerInfo(id3)
|
||||
const ma3 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/ipfs/' + id3.toB58String()
|
||||
peer3.multiaddrs.add(ma3)
|
||||
|
||||
node1.on('peer:discovery', (peerInfo) => node1.dial(peerInfo, check))
|
||||
node2.on('peer:discovery', (peerInfo) => node2.dial(peerInfo, check))
|
||||
|
||||
const node3 = new Node(peer3, null, { webRTCStar: true })
|
||||
node3.start(check)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('websocket-star', () => {
|
||||
let peer1
|
||||
let peer2
|
||||
let node1
|
||||
let node2
|
||||
|
||||
it('create two peerInfo with websocket-star addrs', (done) => {
|
||||
parallel([
|
||||
(cb) => PeerId.create({ bits: 1024 }, cb),
|
||||
(cb) => PeerId.create({ bits: 1024 }, cb)
|
||||
], (err, ids) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
peer1 = new PeerInfo(ids[0])
|
||||
const ma1 = '/ip4/127.0.0.1/tcp/14444/ws/p2p-websocket-star/'
|
||||
peer1.multiaddrs.add(ma1)
|
||||
|
||||
peer2 = new PeerInfo(ids[1])
|
||||
const ma2 = '/ip4/127.0.0.1/tcp/14444/ws/p2p-websocket-star/'
|
||||
peer2.multiaddrs.add(ma2)
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('create two libp2p nodes with those peers', (done) => {
|
||||
node1 = new Node(peer1, null, { wsStar: true })
|
||||
node2 = new Node(peer2, null, { wsStar: true })
|
||||
done()
|
||||
})
|
||||
|
||||
it('listen on the two libp2p nodes', (done) => {
|
||||
parallel([
|
||||
(cb) => node1.start(cb),
|
||||
(cb) => node2.start(cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
it('handle a protocol on the first node', () => {
|
||||
node2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
|
||||
})
|
||||
|
||||
it('dial from the second node to the first node', (done) => {
|
||||
node1.dial(peer2, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
const text = 'hello'
|
||||
const peers1 = node1.peerBook.getAll()
|
||||
expect(Object.keys(peers1)).to.have.length(1)
|
||||
|
||||
const peers2 = node2.peerBook.getAll()
|
||||
expect(Object.keys(peers2)).to.have.length(1)
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from(text)]),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(data[0].toString()).to.equal(text)
|
||||
done()
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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(Object.keys(node1.swarm.muxedConns)).to.have.length(0)
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('create a third node and check that discovery works', (done) => {
|
||||
let counter = 0
|
||||
|
||||
function check () {
|
||||
if (++counter === 3) {
|
||||
expect(Object.keys(node1.swarm.muxedConns).length).to.equal(1)
|
||||
expect(Object.keys(node2.swarm.muxedConns).length).to.equal(1)
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
PeerId.create((err, id3) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
const peer3 = new PeerInfo(id3)
|
||||
const ma3 = '/ip4/127.0.0.1/tcp/14444/ws/p2p-websocket-star/ipfs/' + id3.toB58String()
|
||||
peer3.multiaddrs.add(ma3)
|
||||
|
||||
node1.on('peer:discovery', (peerInfo) => node1.dial(peerInfo, check))
|
||||
node2.on('peer:discovery', (peerInfo) => node2.dial(peerInfo, check))
|
||||
|
||||
const node3 = new Node(peer3, null, { wsStar: true })
|
||||
node3.start(check)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
678
test/transports.node.js
Normal file
678
test/transports.node.js
Normal file
@ -0,0 +1,678 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const expect = chai.expect
|
||||
const parallel = require('async/parallel')
|
||||
const series = require('async/series')
|
||||
const pull = require('pull-stream')
|
||||
const utils = require('./utils/node.js')
|
||||
const signalling = require('libp2p-webrtc-star/src/sig-server')
|
||||
const rendezvous = require('libp2p-websocket-star-rendezvous')
|
||||
const WSStar = require('libp2p-websocket-star')
|
||||
const WRTCStar = require('libp2p-webrtc-star')
|
||||
const wrtc = require('wrtc')
|
||||
|
||||
const createNode = utils.createNode
|
||||
const echo = utils.echo
|
||||
|
||||
describe('transports', () => {
|
||||
describe('TCP only', () => {
|
||||
let nodeA
|
||||
let nodeB
|
||||
|
||||
before((done) => {
|
||||
parallel([
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeA = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
(cb) => createNode('/ip4/0.0.0.0/tcp/0', (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeB = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
], done)
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
parallel([
|
||||
(cb) => nodeA.stop(cb),
|
||||
(cb) => nodeB.stop(cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
it('nodeA.dial nodeB using PeerInfo without proto (warmup)', (done) => {
|
||||
nodeA.dial(nodeB.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
// Some time for Identify to finish
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
parallel([
|
||||
(cb) => {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(err).to.not.exist()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
const peers = nodeB.peerBook.getAll()
|
||||
expect(err).to.not.exist()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
cb()
|
||||
}
|
||||
], done)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeA.dial nodeB using PeerInfo', (done) => {
|
||||
nodeA.dial(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hey')]),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(data).to.be.eql([Buffer.from('hey')])
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeA.hangUp nodeB using PeerInfo (first)', (done) => {
|
||||
nodeA.hangUp(nodeB.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
parallel([
|
||||
(cb) => {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
const peers = nodeB.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
|
||||
expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0)
|
||||
cb()
|
||||
}
|
||||
], done)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeA.dial nodeB using multiaddr', (done) => {
|
||||
nodeA.dial(nodeB.peerInfo.multiaddrs.toArray()[0], '/echo/1.0.0', (err, conn) => {
|
||||
// Some time for Identify to finish
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
expect(err).to.not.exist()
|
||||
series([
|
||||
(cb) => {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1)
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
const peers = nodeB.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1)
|
||||
cb()
|
||||
}
|
||||
], () => {
|
||||
pull(
|
||||
pull.values([Buffer.from('hey')]),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(data).to.be.eql([Buffer.from('hey')])
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeA.hangUp nodeB using multiaddr (second)', (done) => {
|
||||
nodeA.hangUp(nodeB.peerInfo.multiaddrs.toArray()[0], (err) => {
|
||||
expect(err).to.not.exist()
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
parallel([
|
||||
(cb) => {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
const peers = nodeB.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
|
||||
expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0)
|
||||
cb()
|
||||
}
|
||||
], done)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeA.dial nodeB using PeerId', (done) => {
|
||||
nodeA.dial(nodeB.peerInfo.id, '/echo/1.0.0', (err, conn) => {
|
||||
// Some time for Identify to finish
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
expect(err).to.not.exist()
|
||||
series([
|
||||
(cb) => {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1)
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
const peers = nodeB.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1)
|
||||
cb()
|
||||
}
|
||||
], () => {
|
||||
pull(
|
||||
pull.values([Buffer.from('hey')]),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(data).to.eql([Buffer.from('hey')])
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeA.hangUp nodeB using PeerId (third)', (done) => {
|
||||
nodeA.hangUp(nodeB.peerInfo.multiaddrs.toArray()[0], (err) => {
|
||||
expect(err).to.not.exist()
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
parallel([
|
||||
(cb) => {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
const peers = nodeB.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0)
|
||||
cb()
|
||||
}
|
||||
], done)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('TCP + WebSockets', () => {
|
||||
let nodeTCP
|
||||
let nodeTCPnWS
|
||||
let nodeWS
|
||||
|
||||
before((done) => {
|
||||
parallel([
|
||||
(cb) => createNode([
|
||||
'/ip4/0.0.0.0/tcp/0'
|
||||
], (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeTCP = 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/25011/ws'
|
||||
], (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeTCPnWS = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
(cb) => createNode([
|
||||
'/ip4/127.0.0.1/tcp/25022/ws'
|
||||
], (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeWS = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
], done)
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
parallel([
|
||||
(cb) => nodeTCP.stop(cb),
|
||||
(cb) => nodeTCPnWS.stop(cb),
|
||||
(cb) => nodeWS.stop(cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
it('nodeTCP.dial nodeTCPnWS using PeerInfo', (done) => {
|
||||
nodeTCP.dial(nodeTCPnWS.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
// Some time for Identify to finish
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
parallel([
|
||||
(cb) => {
|
||||
const peers = nodeTCP.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(1)
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
const peers = nodeTCPnWS.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(1)
|
||||
cb()
|
||||
}
|
||||
], done)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeTCP.hangUp nodeTCPnWS using PeerInfo', (done) => {
|
||||
nodeTCP.hangUp(nodeTCPnWS.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
parallel([
|
||||
(cb) => {
|
||||
const peers = nodeTCP.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(0)
|
||||
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
const peers = nodeTCPnWS.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(0)
|
||||
cb()
|
||||
}
|
||||
], done)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeTCPnWS.dial nodeWS using PeerInfo', (done) => {
|
||||
nodeTCPnWS.dial(nodeWS.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
// Some time for Identify to finish
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
parallel([
|
||||
(cb) => {
|
||||
const peers = nodeTCPnWS.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(2)
|
||||
expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(1)
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
const peers = nodeWS.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(1)
|
||||
cb()
|
||||
}
|
||||
], done)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeTCPnWS.hangUp nodeWS using PeerInfo', (done) => {
|
||||
nodeTCPnWS.hangUp(nodeWS.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
setTimeout(check, 500)
|
||||
|
||||
function check () {
|
||||
parallel([
|
||||
(cb) => {
|
||||
const peers = nodeTCPnWS.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(2)
|
||||
expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(0)
|
||||
|
||||
cb()
|
||||
},
|
||||
(cb) => {
|
||||
const peers = nodeWS.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(0)
|
||||
cb()
|
||||
}
|
||||
], done)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Until https://github.com/libp2p/js-libp2p/issues/46 is resolved
|
||||
// Everynode will be able to dial in WebSockets
|
||||
it.skip('nodeTCP.dial nodeWS using PeerInfo is unsuccesful', (done) => {
|
||||
nodeTCP.dial(nodeWS.peerInfo, (err) => {
|
||||
expect(err).to.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('TCP + WebSockets + WebRTCStar', () => {
|
||||
let nodeAll
|
||||
let nodeTCP
|
||||
let nodeWS
|
||||
let nodeWStar
|
||||
|
||||
let ss
|
||||
|
||||
before(function (done) {
|
||||
this.timeout(5 * 1000)
|
||||
|
||||
parallel([
|
||||
(cb) => {
|
||||
signalling.start({ port: 24642 }, (err, server) => {
|
||||
expect(err).to.not.exist()
|
||||
ss = server
|
||||
cb()
|
||||
})
|
||||
},
|
||||
(cb) => {
|
||||
const wstar = new WRTCStar({wrtc: wrtc})
|
||||
createNode([
|
||||
'/ip4/0.0.0.0/tcp/0',
|
||||
'/ip4/127.0.0.1/tcp/25011/ws',
|
||||
'/ip4/127.0.0.1/tcp/24642/ws/p2p-webrtc-star'
|
||||
], {
|
||||
modules: {
|
||||
transport: [wstar],
|
||||
discovery: [wstar.discovery]
|
||||
}
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeAll = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
},
|
||||
(cb) => createNode([
|
||||
'/ip4/0.0.0.0/tcp/0'
|
||||
], (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeTCP = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
(cb) => createNode([
|
||||
'/ip4/127.0.0.1/tcp/25022/ws'
|
||||
], (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeWS = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
|
||||
(cb) => {
|
||||
const wstar = new WRTCStar({wrtc: wrtc})
|
||||
|
||||
createNode([
|
||||
'/ip4/127.0.0.1/tcp/24642/ws/p2p-webrtc-star'
|
||||
], {
|
||||
modules: {
|
||||
transport: [wstar],
|
||||
discovery: [wstar.discovery]
|
||||
}
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeWStar = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
}
|
||||
], done)
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
parallel([
|
||||
(cb) => nodeAll.stop(cb),
|
||||
(cb) => nodeTCP.stop(cb),
|
||||
(cb) => nodeWS.stop(cb),
|
||||
(cb) => nodeWStar.stop(cb),
|
||||
(cb) => ss.stop(cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
function check (otherNode, muxed, peers, callback) {
|
||||
let i = 1;
|
||||
[nodeAll, otherNode].forEach((node) => {
|
||||
expect(Object.keys(node.peerBook.getAll())).to.have.length(i-- ? peers : 1)
|
||||
expect(Object.keys(node.swarm.muxedConns)).to.have.length(muxed)
|
||||
})
|
||||
callback()
|
||||
}
|
||||
|
||||
it('nodeAll.dial nodeTCP using PeerInfo', (done) => {
|
||||
nodeAll.dial(nodeTCP.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeTCP, 1, 1, done), 500)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeAll.hangUp nodeTCP using PeerInfo', (done) => {
|
||||
nodeAll.hangUp(nodeTCP.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeTCP, 0, 1, done), 500)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeAll.dial nodeWS using PeerInfo', (done) => {
|
||||
nodeAll.dial(nodeWS.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeWS, 1, 2, done), 500)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeAll.hangUp nodeWS using PeerInfo', (done) => {
|
||||
nodeAll.hangUp(nodeWS.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeWS, 0, 2, done), 500)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeAll.dial nodeWStar using PeerInfo', function (done) {
|
||||
this.timeout(40 * 1000)
|
||||
|
||||
nodeAll.dial(nodeWStar.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeWStar, 1, 3, done), 500)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeAll.hangUp nodeWStar using PeerInfo', (done) => {
|
||||
nodeAll.hangUp(nodeWStar.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
setTimeout(() => check(nodeWStar, 0, 3, done), 500)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('TCP + WebSockets + WebSocketStar', () => {
|
||||
let nodeAll
|
||||
let nodeTCP
|
||||
let nodeWS
|
||||
let nodeWStar
|
||||
|
||||
let ss
|
||||
|
||||
before((done) => {
|
||||
parallel([
|
||||
(cb) => {
|
||||
rendezvous.start({ port: 24642 }, (err, server) => {
|
||||
expect(err).to.not.exist()
|
||||
ss = server
|
||||
cb()
|
||||
})
|
||||
},
|
||||
(cb) => {
|
||||
const wstar = new WSStar()
|
||||
createNode([
|
||||
'/ip4/0.0.0.0/tcp/0',
|
||||
'/ip4/127.0.0.1/tcp/25011/ws',
|
||||
'/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star'
|
||||
], {
|
||||
modules: {
|
||||
transport: [wstar],
|
||||
discovery: [wstar.discovery]
|
||||
}
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeAll = node
|
||||
wstar.lazySetId(node.peerInfo.id)
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
},
|
||||
(cb) => createNode([
|
||||
'/ip4/0.0.0.0/tcp/0'
|
||||
], (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeTCP = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
(cb) => createNode([
|
||||
'/ip4/127.0.0.1/tcp/25022/ws'
|
||||
], (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeWS = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
}),
|
||||
|
||||
(cb) => {
|
||||
const wstar = new WSStar({})
|
||||
|
||||
createNode([
|
||||
'/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star'
|
||||
], {
|
||||
modules: {
|
||||
transport: [wstar],
|
||||
discovery: [wstar.discovery]
|
||||
}
|
||||
}, (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeWStar = node
|
||||
wstar.lazySetId(node.peerInfo.id)
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(cb)
|
||||
})
|
||||
}
|
||||
], done)
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
parallel([
|
||||
(cb) => nodeAll.stop(cb),
|
||||
(cb) => nodeTCP.stop(cb),
|
||||
(cb) => nodeWS.stop(cb),
|
||||
(cb) => nodeWStar.stop(cb),
|
||||
(cb) => ss.stop(cb)
|
||||
], done)
|
||||
})
|
||||
|
||||
function check (otherNode, muxed, peers, done) {
|
||||
let i = 1;
|
||||
[nodeAll, otherNode].forEach((node) => {
|
||||
expect(Object.keys(node.peerBook.getAll())).to.have.length(i-- ? peers : 1)
|
||||
expect(Object.keys(node.swarm.muxedConns)).to.have.length(muxed)
|
||||
})
|
||||
done()
|
||||
}
|
||||
|
||||
it('nodeAll.dial nodeTCP using PeerInfo', (done) => {
|
||||
nodeAll.dial(nodeTCP.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeTCP, 1, 1, done), 500)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeAll.hangUp nodeTCP using PeerInfo', (done) => {
|
||||
nodeAll.hangUp(nodeTCP.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeTCP, 0, 1, done), 500)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeAll.dial nodeWS using PeerInfo', (done) => {
|
||||
nodeAll.dial(nodeWS.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeWS, 1, 2, done), 500)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeAll.hangUp nodeWS using PeerInfo', (done) => {
|
||||
nodeAll.hangUp(nodeWS.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeWS, 0, 2, done), 500)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeAll.dial nodeWStar using PeerInfo', (done) => {
|
||||
nodeAll.dial(nodeWStar.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeWStar, 1, 3, done), 500)
|
||||
})
|
||||
})
|
||||
|
||||
it('nodeAll.hangUp nodeWStar using PeerInfo', (done) => {
|
||||
nodeAll.hangUp(nodeWStar.peerInfo, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
// Some time for Identify to finish
|
||||
setTimeout(() => check(nodeWStar, 0, 3, done), 500)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
83
test/turbolence.node.js
Normal file
83
test/turbolence.node.js
Normal file
@ -0,0 +1,83 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const expect = chai.expect
|
||||
const multiaddr = require('multiaddr')
|
||||
const spawn = require('child_process').spawn
|
||||
const path = require('path')
|
||||
const pull = require('pull-stream')
|
||||
const utils = require('./utils/node')
|
||||
const createNode = utils.createNode
|
||||
const echo = utils.echo
|
||||
|
||||
describe('Turbolence tests', () => {
|
||||
let nodeA
|
||||
let nodeSpawn
|
||||
|
||||
before((done) => {
|
||||
createNode('/ip4/0.0.0.0/tcp/0', (err, node) => {
|
||||
expect(err).to.not.exist()
|
||||
nodeA = node
|
||||
node.handle('/echo/1.0.0', echo)
|
||||
node.start(done)
|
||||
})
|
||||
})
|
||||
|
||||
after((done) => nodeA.stop(done))
|
||||
|
||||
it('spawn a node in a different process', (done) => {
|
||||
const filePath = path.join(__dirname, './spawn-libp2p-node.js')
|
||||
|
||||
nodeSpawn = spawn(filePath, { env: process.env })
|
||||
|
||||
let spawned = false
|
||||
|
||||
nodeSpawn.stdout.on('data', (data) => {
|
||||
// console.log(data.toString())
|
||||
if (!spawned) {
|
||||
spawned = true
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
nodeSpawn.stderr.on('data', (data) => console.log(data.toString()))
|
||||
})
|
||||
|
||||
it('connect nodeA to that node', (done) => {
|
||||
const spawnedId = require('./test-data/test-id.json')
|
||||
const maddr = multiaddr('/ip4/127.0.0.1/tcp/12345/ipfs/' + spawnedId.id)
|
||||
|
||||
nodeA.dial(maddr, '/echo/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hey')]),
|
||||
conn,
|
||||
pull.collect((err, data) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(data).to.eql([Buffer.from('hey')])
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('crash that node, ensure nodeA continues going steady', (done) => {
|
||||
// TODO investigate why CI crashes
|
||||
setTimeout(() => nodeSpawn.kill('SIGKILL'), 1000)
|
||||
// nodeSpawn.kill('SIGKILL')
|
||||
setTimeout(check, 5000)
|
||||
|
||||
function check () {
|
||||
const peers = nodeA.peerBook.getAll()
|
||||
expect(Object.keys(peers)).to.have.length(1)
|
||||
expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
74
test/utils/bundle.browser.js
Normal file
74
test/utils/bundle.browser.js
Normal file
@ -0,0 +1,74 @@
|
||||
'use strict'
|
||||
|
||||
const WS = require('libp2p-websockets')
|
||||
const WebRTCStar = require('libp2p-webrtc-star')
|
||||
const WebSocketStar = require('libp2p-websocket-star')
|
||||
const spdy = require('libp2p-spdy')
|
||||
const multiplex = require('libp2p-multiplex')
|
||||
const secio = require('libp2p-secio')
|
||||
const Railing = require('libp2p-railing')
|
||||
const libp2p = require('../..')
|
||||
|
||||
function mapMuxers (list) {
|
||||
return list.map((pref) => {
|
||||
if (typeof pref !== 'string') {
|
||||
return pref
|
||||
}
|
||||
switch (pref.trim().toLowerCase()) {
|
||||
case 'spdy':
|
||||
return spdy
|
||||
case 'multiplex':
|
||||
return multiplex
|
||||
default:
|
||||
throw new Error(pref + ' muxer not available')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getMuxers (options) {
|
||||
if (options) {
|
||||
return mapMuxers(options)
|
||||
} else {
|
||||
return [multiplex, spdy]
|
||||
}
|
||||
}
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (peerInfo, peerBook, options) {
|
||||
options = options || {}
|
||||
const wrtcStar = new WebRTCStar({ id: peerInfo.id })
|
||||
const wsStar = new WebSocketStar({ id: peerInfo.id })
|
||||
|
||||
const modules = {
|
||||
transport: [
|
||||
new WS(),
|
||||
wrtcStar,
|
||||
wsStar
|
||||
],
|
||||
connection: {
|
||||
muxer: getMuxers(options.muxer),
|
||||
crypto: [
|
||||
secio
|
||||
]
|
||||
},
|
||||
discovery: []
|
||||
}
|
||||
|
||||
if (options.webRTCStar) {
|
||||
modules.discovery.push(wrtcStar.discovery)
|
||||
}
|
||||
|
||||
if (options.wsStar) {
|
||||
modules.discovery.push(wsStar.discovery)
|
||||
}
|
||||
|
||||
if (options.bootstrap) {
|
||||
const r = new Railing(options.bootstrap)
|
||||
modules.discovery.push(r)
|
||||
}
|
||||
|
||||
super(modules, peerInfo, peerBook, options)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Node
|
80
test/utils/bundle.node.js
Normal file
80
test/utils/bundle.node.js
Normal file
@ -0,0 +1,80 @@
|
||||
'use strict'
|
||||
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MulticastDNS = require('libp2p-mdns')
|
||||
const WS = require('libp2p-websockets')
|
||||
const Railing = require('libp2p-railing')
|
||||
const spdy = require('libp2p-spdy')
|
||||
const KadDHT = require('libp2p-kad-dht')
|
||||
const multiplex = require('libp2p-multiplex')
|
||||
const secio = require('libp2p-secio')
|
||||
const libp2p = require('../..')
|
||||
|
||||
function mapMuxers (list) {
|
||||
return list.map((pref) => {
|
||||
if (typeof pref !== 'string') {
|
||||
return pref
|
||||
}
|
||||
switch (pref.trim().toLowerCase()) {
|
||||
case 'spdy': return spdy
|
||||
case 'multiplex': return multiplex
|
||||
default:
|
||||
throw new Error(pref + ' muxer not available')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getMuxers (muxers) {
|
||||
const muxerPrefs = process.env.LIBP2P_MUXER
|
||||
if (muxerPrefs && !muxers) {
|
||||
return mapMuxers(muxerPrefs.split(','))
|
||||
} else if (muxers) {
|
||||
return mapMuxers(muxers)
|
||||
} else {
|
||||
return [multiplex, spdy]
|
||||
}
|
||||
}
|
||||
|
||||
class Node extends libp2p {
|
||||
constructor (peerInfo, peerBook, options) {
|
||||
options = options || {}
|
||||
|
||||
const modules = {
|
||||
transport: [
|
||||
new TCP(),
|
||||
new WS()
|
||||
],
|
||||
connection: {
|
||||
muxer: getMuxers(options.muxer),
|
||||
crypto: [ secio ]
|
||||
},
|
||||
discovery: []
|
||||
}
|
||||
|
||||
if (options.dht) {
|
||||
modules.DHT = KadDHT
|
||||
}
|
||||
|
||||
if (options.mdns) {
|
||||
const mdns = new MulticastDNS(peerInfo, 'ipfs.local')
|
||||
modules.discovery.push(mdns)
|
||||
}
|
||||
|
||||
if (options.bootstrap) {
|
||||
const r = new Railing(options.bootstrap)
|
||||
modules.discovery.push(r)
|
||||
}
|
||||
|
||||
if (options.modules && options.modules.transport) {
|
||||
options.modules.transport.forEach((t) => modules.transport.push(t))
|
||||
}
|
||||
|
||||
if (options.modules && options.modules.discovery) {
|
||||
options.modules.discovery.forEach((d) => modules.discovery.push(d))
|
||||
}
|
||||
|
||||
super(modules, peerInfo, peerBook, options)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Node
|
45
test/utils/node.js
Normal file
45
test/utils/node.js
Normal file
@ -0,0 +1,45 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
chai.use(require('dirty-chai'))
|
||||
const Node = require('./bundle.node')
|
||||
const PeerInfo = require('peer-info')
|
||||
const PeerId = require('peer-id')
|
||||
const waterfall = require('async/waterfall')
|
||||
const pull = require('pull-stream')
|
||||
|
||||
function createNode (multiaddrs, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options
|
||||
options = {}
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
|
||||
if (!Array.isArray(multiaddrs)) {
|
||||
multiaddrs = [multiaddrs]
|
||||
}
|
||||
|
||||
waterfall([
|
||||
(cb) => PeerId.create({ bits: 1024 }, cb),
|
||||
(peerId, cb) => PeerInfo.create(peerId, cb),
|
||||
(peerInfo, cb) => {
|
||||
multiaddrs.map((ma) => peerInfo.multiaddrs.add(ma))
|
||||
cb(null, peerInfo)
|
||||
},
|
||||
(peerInfo, cb) => {
|
||||
const node = new Node(peerInfo, undefined, options)
|
||||
cb(null, node)
|
||||
}
|
||||
], callback)
|
||||
}
|
||||
|
||||
function echo (protocol, conn) {
|
||||
pull(conn, conn)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createNode: createNode,
|
||||
echo: echo
|
||||
}
|
Reference in New Issue
Block a user