Skip to content
This repository has been archived by the owner on Aug 23, 2019. It is now read-only.

feat: Circuit Relay #224

Merged
merged 20 commits into from
Oct 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions gulpfile.js → .aegir.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict'

const gulp = require('gulp')
const PeerInfo = require('peer-info')
const PeerId = require('peer-id')
const WebSockets = require('libp2p-websockets')
Expand All @@ -23,7 +22,7 @@ const options = {
host: '127.0.0.1'
}

gulp.task('test:browser:before', (done) => {
function before (done) {
function createListenerA (cb) {
PeerId.createFromJSON(
JSON.parse(
Expand Down Expand Up @@ -79,15 +78,22 @@ gulp.task('test:browser:before', (done) => {
function echo (protocol, conn) {
pull(conn, conn)
}
})
}

gulp.task('test:browser:after', (done) => {
function after (done) {
let count = 0
const ready = () => ++count === 3 ? done() : null

swarmA.transport.close('ws', ready)
swarmB.close(ready)
sigS.stop(ready)
})
}

require('aegir/gulp')(gulp)
module.exports = {
hooks: {
browser: {
pre: before,
post: after
}
}
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

**/node_modules/
**/*.log
test/repo-tests*
Expand Down Expand Up @@ -38,5 +39,7 @@ test/test-data/go-ipfs-repo/LOCK
test/test-data/go-ipfs-repo/LOG
test/test-data/go-ipfs-repo/LOG.old

docs
# while testing npm5
package-lock.json
yarn.lock
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ addons:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- g++-4.8
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ const secio = require('libp2p-secio')
swarm.connection.crypto(secio.tag, secio.encrypt)
```

##### `swarm.connection.enableCircuitRelay(options)`

Enable circuit relaying.

- `options`
- enabled - activates relay dialing and listening functionality
- hop - an object with two properties
- enabled - enables circuit relaying
- active - is it an active or passive relay (default false)

### `swarm.dial(peer, protocol, callback)`

dial uses the best transport (whatever works first, in the future we can have some criteria), and jump starts the connection until the point where we have to negotiate the protocol. If a muxer is available, then drop the muxer onto that connection. Good to warm up connections or to check for connectivity. If we have already a muxer for that peerInfo, then do nothing.
Expand Down
6 changes: 5 additions & 1 deletion circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ dependencies:
pre:
- google-chrome --version
- 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 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
- google-chrome --version
42 changes: 22 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
"description": "libp2p swarm implementation in JavaScript",
"main": "src/index.js",
"scripts": {
"lint": "gulp lint",
"build": "gulp build",
"test": "gulp test --dom",
"test:node": "gulp test:node",
"test:browser": "gulp test:browser --dom",
"release": "gulp release --dom",
"release-minor": "gulp release --type minor --dom",
"release-major": "gulp release --type major --dom",
"coverage": "gulp coverage",
"coverage-publish": "aegir-coverage publish"
"lint": "aegir lint",
"build": "aegir build",
"test": "aegir test --target node --target browser --no-parallel",
"test:node": "aegir test --target node --no-parallel",
"test:browser": "aegir test --target browser --no-parallel",
"release": "aegir test release --target node --target browser --no-parallel",
"release-minor": "aegir release --type minor --target node --target browser --no-parallel",
"release-major": "aegir release --type major --target node --target browser --no-parallel",
"coverage": "aegir coverage",
"coverage-publish": "aegir coverage --provider coveralls"
},
"browser": {
"zlib": "browserify-zlib-next"
"zlib": "browserify-zlib"
},
"repository": {
"type": "git",
Expand All @@ -40,35 +40,37 @@
"npm": ">=3.0.0"
},
"devDependencies": {
"aegir": "^11.0.2",
"aegir": "^12.1.0",
"buffer-loader": "0.0.1",
"chai": "^4.1.2",
"dirty-chai": "^2.0.1",
"gulp": "^3.9.1",
"libp2p-multiplex": "~0.5.0",
"libp2p-secio": "~0.8.1",
"libp2p-spdy": "~0.11.0",
"libp2p-tcp": "~0.11.0",
"libp2p-tcp": "~0.11.1",
"libp2p-webrtc-star": "~0.13.2",
"libp2p-websockets": "~0.10.1",
"libp2p-websockets": "~0.10.2",
"peer-book": "~0.5.1",
"pre-commit": "^1.2.2",
"pull-goodbye": "0.0.2",
"peer-book": "~0.5.1",
"sinon": "^4.0.1",
"webrtcsupport": "^2.2.0"
},
"dependencies": {
"async": "^2.5.0",
"browserify-zlib-next": "^1.0.1",
"debug": "^3.0.1",
"browserify-zlib": "^0.2.0",
"debug": "^3.1.0",
"interface-connection": "~0.3.2",
"ip-address": "^5.8.8",
"libp2p-circuit": "~0.1.0",
"libp2p-identify": "~0.6.1",
"lodash.includes": "^4.3.0",
"multiaddr": "^3.0.1",
"multistream-select": "~0.13.5",
"once": "^1.4.0",
"peer-id": "~0.10.1",
"peer-info": "~0.11.0",
"peer-id": "^0.10.2",
"peer-info": "^0.11.0",
"pull-stream": "^3.6.1"
},
"contributors": [
Expand All @@ -88,4 +90,4 @@
"greenkeeper[bot] <greenkeeper[bot]@users.noreply.github.com>",
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <[email protected]>"
]
}
}
15 changes: 15 additions & 0 deletions src/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const debug = require('debug')
const log = debug('libp2p:swarm:connection')
const setImmediate = require('async/setImmediate')

const Circuit = require('libp2p-circuit')

const protocolMuxer = require('./protocol-muxer')
const plaintext = require('./plaintext')

Expand Down Expand Up @@ -92,6 +94,19 @@ module.exports = function connection (swarm) {
})
},

enableCircuitRelay (config) {
config = config || {}

if (config.enabled) {
if (!config.hop) {
Object.assign(config, { hop: { enabled: false, active: false } })
}

// TODO: should we enable circuit listener and dialer by default?
swarm.transport.add(Circuit.tag, new Circuit(swarm, config))
}
},

crypto (tag, encrypt) {
if (!tag && !encrypt) {
tag = plaintext.tag
Expand Down
33 changes: 26 additions & 7 deletions src/dial.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const multistream = require('multistream-select')
const Connection = require('interface-connection').Connection
const setImmediate = require('async/setImmediate')
const getPeerInfo = require('./get-peer-info')
const Circuit = require('libp2p-circuit')

const debug = require('debug')
const log = debug('libp2p:swarm:dial')

Expand Down Expand Up @@ -81,18 +83,22 @@ function dial (swarm) {
function attemptDial (pi, cb) {
const tKeys = swarm.availableTransports(pi)

if (tKeys.length === 0) {
return cb(new Error('No available transport to dial to'))
}

nextTransport(tKeys.shift())

function nextTransport (key) {
if (!key) {
return dialCircuit((err, circuit) => {
if (err) {
return cb(new Error('Could not dial in any of the transports or relays'))
}

cb(null, circuit)
})
}

log(`dialing transport ${key}`)
swarm.transport.dial(key, pi, (err, conn) => {
if (err) {
if (tKeys.length === 0) {
return cb(new Error('Could not dial in any of the transports'))
}
return nextTransport(tKeys.shift())
}

Expand Down Expand Up @@ -121,6 +127,19 @@ function dial (swarm) {
}
}

function dialCircuit (cb) {
log(`Falling back to dialing over circuit`)
pi.multiaddrs.add(`/p2p-circuit/ipfs/${pi.id.toB58String()}`)
swarm.transport.dial(Circuit.tag, pi, (err, conn) => {
if (err) {
log(err)
return cb(err)
}

cb(null, conn)
})
}

function attemptMuxerUpgrade (conn, cb) {
const muxers = Object.keys(swarm.muxers)
if (muxers.length === 0) {
Expand Down
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ function Swarm (peerInfo, peerBook) {

// Only listen on transports we actually have addresses for
return myTransports.filter((ts) => this.transports[ts].filter(myAddrs).length > 0)
// push Circuit to be the last proto to be dialed
.sort((a) => {
return a === 'Circuit' ? -1 : 0
})
}

// higher level (public) API
Expand Down
2 changes: 2 additions & 0 deletions src/limit-dialer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const map = require('async/map')
const debug = require('debug')
const once = require('once')

const log = debug('libp2p:swarm:dialer')

Expand Down Expand Up @@ -37,6 +38,7 @@ class LimitDialer {
log('dialMany:start')
// we use a token to track if we want to cancel following dials
const token = { cancel: false }
callback = once(callback) // only call callback once
Copy link
Member Author

@dryajov dryajov Aug 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dignifiedquire since dialMany will possibly succeed for more than one address, the callback could resolve multiple times, this breaks in a couple of places (I'll have to see exactly where, but I believe it was either libp2p or swarm). I think having the callback called only once, for the first address that gets resolved, makes sense here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we expect that, it should be set up such that the first one results in the callback being called, but in addition made sure that all others are canceled and no new ones are started to avoid waste

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, good idea. Would you mind if I leave this PR as is and open an issue to enhance dialMany in another issue? I don't want to hold this PR because of it.


map(addrs, (m, cb) => {
this.dialSingle(peer, transport, m, token, cb)
Expand Down
2 changes: 1 addition & 1 deletion src/transport.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ module.exports = function (swarm) {
if (!Array.isArray(multiaddrs)) {
multiaddrs = [multiaddrs]
}
log('dialing %s', key, multiaddrs.map((m) => m.toString()))
// filter the multiaddrs that are actually valid for this transport (use a func from the transport itself) (maybe even make the transport do that)
multiaddrs = dialables(t, multiaddrs)
log('dialing %s', key, multiaddrs.map((m) => m.toString()))

dialer.dialMany(pi.id, t, multiaddrs, (err, success) => {
if (err) {
Expand Down
4 changes: 2 additions & 2 deletions test/browser-swarm-with-muxing-plus-websockets.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const PeerBook = require('peer-book')

const Swarm = require('../src')

describe('high level API (swarm with spdy + websockets)', () => {
describe.skip('high level API (swarm with spdy + websockets)', () => {
let swarm
let peerDst

Expand Down Expand Up @@ -58,7 +58,7 @@ describe('high level API (swarm with spdy + websockets)', () => {
swarm.dial(peerDst, '/echo/1.0.0', (err, conn) => {
expect(err).to.not.exist()
pull(
pull.values([Buffer('hello')]),
pull.values([Buffer.from('hello')]),
conn,
pull.onEnd(done)
)
Expand Down
2 changes: 1 addition & 1 deletion test/browser-transport-websockets.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('transport - websockets', () => {
})

pull(
pull.values([Buffer('hey')]),
pull.values([Buffer.from('hey')]),
conn,
pull.collect((err, data) => {
expect(err).to.not.exist()
Expand Down
2 changes: 1 addition & 1 deletion test/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Swarm = require('../src')

describe('basics', () => {
it('throws on missing peerInfo', (done) => {
expect(Swarm).to.throw(Error)
expect(Swarm).to.throw(/You must provide a `peerInfo`/)
done()
})
})
Expand Down
Loading