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

Commit 19445c5

Browse files
dmitriy ryajovdmitriy ryajov
dmitriy ryajov
authored and
dmitriy ryajov
committed
feat: initial implementation of circuit relaying
1 parent 67962a5 commit 19445c5

File tree

6 files changed

+346
-19
lines changed

6 files changed

+346
-19
lines changed

package.json

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,52 @@
11
{
22
"name": "libp2p-circuit",
3-
"version": "0.9.2",
3+
"version": "0.0.1",
44
"description": "JavaScript implementation of circuit/switch relaying",
55
"main": "src/index.js",
66
"scripts": {
77
"lint": "aegir-lint",
8-
"test": "gulp test",
9-
"test:node": "gulp test:node",
10-
"test:browser": "gulp test:browser",
11-
"build": "gulp build",
12-
"release": "gulp release",
13-
"release-minor": "gulp release --type minor",
14-
"release-major": "gulp release --type major",
15-
"coverage": "gulp coverage",
8+
"build": "aegir-build",
9+
"test": "aegir-test --env node",
10+
"release": "aegir-release",
11+
"release-minor": "aegir-release --type minor",
12+
"release-major": "aegir-release --type major",
13+
"coverage": "aegir-coverage",
1614
"coverage-publish": "aegir-coverage publish"
1715
},
18-
"browser": {
19-
"pull-ws/server": false
20-
},
2116
"pre-commit": [
2217
"lint",
2318
"test"
2419
],
2520
"repository": {
2621
"type": "git",
27-
"url": "git+https://github.com/libp2p/js-libp2p-websockets.git"
22+
"url": "git+https://github.com/libp2p/js-libp2p-circuit.git"
2823
},
2924
"keywords": [
3025
"IPFS"
3126
],
32-
"author": "David Dias <[email protected]>",
27+
"author": "Dmitriy Ryajov <[email protected]>",
3328
"license": "MIT",
3429
"bugs": {
35-
"url": "https://github.com/dryajov/js-libp2p-circuit/issues"
30+
"url": "https://github.com/libp2p/js-libp2p-circuit/issues"
3631
},
37-
"homepage": "https://github.com/dryajov/js-libp2p-circuit#readme",
32+
"homepage": "https://github.com/libp2p/js-libp2p-circuit#readme",
3833
"devDependencies": {
3934
"aegir": "^10.0.0",
35+
"chai": "^3.5.0",
36+
"libp2p-ipfs-nodejs": "^0.19.0",
37+
"peer-id": "^0.8.2",
38+
"peer-info": "^0.8.3",
4039
"pre-commit": "^1.2.2"
4140
},
42-
"contributors": [
43-
"Dmitriy Ryajov <[email protected]>"
44-
]
41+
"contributors": [],
42+
"dependencies": {
43+
"async": "^2.1.5",
44+
"debug": "^2.6.1",
45+
"interface-connection": "^0.3.1",
46+
"lodash": "^4.17.4",
47+
"multiaddr": "^2.2.1",
48+
"multistream-select": "^0.13.4",
49+
"pull-handshake": "^1.1.4",
50+
"pull-stream": "^3.5.0"
51+
}
4552
}

src/config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict'
2+
3+
const debug = require('debug')
4+
5+
const log = debug('libp2p:circuit')
6+
log.err = debug('libp2p:circuit:error')
7+
8+
module.exports = {
9+
log: log,
10+
multicodec: '/ipfs/relay/circuit/1.0.0'
11+
}

src/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
'use strict'
2+
3+
exports.Dialer = require('./transport/dialer')
4+
exports.Listener = require('./transport/listener')
5+
exports.Peer = require('./peer')
6+
exports.Relay = require('./relay')

src/peer.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict'
2+
3+
/**
4+
* The known state of a connected peer.
5+
*/
6+
class Peer {
7+
/**
8+
* @param {Connection} conn
9+
* @param {PeerInfo} peerInfo
10+
*/
11+
constructor (conn, peerInfo) {
12+
/**
13+
* @type {Connection}
14+
*/
15+
this.conn = conn
16+
17+
/**
18+
* @type {PeerInfo}
19+
*/
20+
this.peerInfo = peerInfo
21+
}
22+
23+
/**
24+
* Attach a connection
25+
* @param {Connection} conn
26+
* @returns {void}
27+
*/
28+
attachConnection (conn) {
29+
this.conn = conn
30+
}
31+
32+
/**
33+
* Is the peer connected currently?
34+
*
35+
* @type {boolean}
36+
*/
37+
get isConnected () {
38+
return Boolean(this.conn)
39+
}
40+
}
41+
42+
module.exports = Peer

src/relay.js

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
'use strict'
2+
3+
const pull = require('pull-stream')
4+
const lp = require('pull-length-prefixed')
5+
const multiaddr = require('multiaddr')
6+
const config = require('./config')
7+
const Peer = require('./peer')
8+
const handshake = require('pull-handshake')
9+
const mss = require('multistream-select')
10+
const Connection = require('interface-connection').Connection
11+
12+
const multicodec = require('./config').multicodec
13+
14+
const log = config.log
15+
16+
class Relay {
17+
constructor (libp2p) {
18+
this.libp2p = libp2p
19+
this.peers = new Map()
20+
21+
this._onConnection = this._onConnection.bind(this)
22+
this._dialPeer = this._dialPeer.bind(this)
23+
}
24+
25+
start (cb) {
26+
this.libp2p.handle(multicodec, this._onConnection)
27+
cb()
28+
}
29+
30+
stop (cb) {
31+
this.libp2p.unhandle(multicodec)
32+
cb()
33+
}
34+
35+
_dialPeer (ma, callback) {
36+
let idB58Str
37+
38+
try {
39+
idB58Str = ma.peerId() // try to get the peerid from the multiaddr
40+
} catch (err) {
41+
log.err(err)
42+
}
43+
44+
if (idB58Str) {
45+
const peer = this.peers.get(idB58Str)
46+
if (peer && peer.isConnected()) {
47+
return
48+
}
49+
}
50+
51+
this.libp2p.dialByMultiaddr(ma, multicodec, (err, conn) => {
52+
if (err) {
53+
log.err(err)
54+
return callback(err)
55+
}
56+
57+
conn.getPeerInfo((err, peerInfo) => {
58+
if (err) {
59+
err(err)
60+
return
61+
}
62+
63+
const idB58Str = peerInfo.id.toB58String()
64+
// If already had a dial to me, just add the conn
65+
if (!this.peers.has(idB58Str)) {
66+
this.peers.set(idB58Str, new Peer(conn, peerInfo))
67+
}
68+
69+
const peer = this.peers.get(idB58Str)
70+
callback(null, peer)
71+
})
72+
})
73+
}
74+
75+
_onConnection (protocol, conn) {
76+
conn.getPeerInfo((err, peerInfo) => {
77+
if (err) {
78+
log.err('Failed to identify incomming conn', err)
79+
return pull(pull.empty(), conn)
80+
}
81+
82+
const idB58Str = peerInfo.id.toB58String()
83+
let srcPeer = this.peers.get(idB58Str)
84+
if (!srcPeer) {
85+
log('new peer', idB58Str)
86+
srcPeer = new Peer(conn, peerInfo)
87+
this.peers.set(idB58Str, srcPeer)
88+
}
89+
this._processConnection(srcPeer, conn)
90+
})
91+
}
92+
93+
_processConnection (srcPeer, conn) {
94+
let stream = handshake({timeout: 1000 * 60})
95+
let shake = stream.handshake
96+
97+
lp.decodeFromReader(shake, (err, msg) => {
98+
if (err) {
99+
log.err(err)
100+
return pull(pull.empty(), conn)
101+
}
102+
103+
let addr = multiaddr(msg.toString())
104+
srcPeer.attachConnection(new Connection(shake.rest(), conn))
105+
this._circuit(srcPeer, addr)
106+
})
107+
108+
pull(stream, conn, stream)
109+
}
110+
111+
_circuit (srcPeer, ma, callback) {
112+
this._dialPeer(ma, (err, destPeer) => {
113+
if (err) {
114+
log.err(err)
115+
return callback(err)
116+
}
117+
118+
let srcAddrs = destPeer.peerInfo.distinctMultiaddr()
119+
120+
if (!(srcAddrs && srcAddrs.length > 0)) {
121+
log.err(`No valid multiaddress for peer!`)
122+
}
123+
124+
let stream = handshake({timeout: 1000 * 60}, callback)
125+
let shake = stream.handshake
126+
127+
mss.util.writeEncoded(shake, `${srcAddrs[0].toString()}/ipfs/${srcPeer.peerInfo.id.toB58String()}`)
128+
pull(stream, destPeer.conn, stream)
129+
pull(srcPeer.conn, shake.rest(), srcPeer.conn)
130+
})
131+
}
132+
}
133+
134+
module.exports = Relay

test/index.spec.js

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/* eslint-env mocha */
2+
'use strict'
3+
4+
const Node = require('libp2p-ipfs-nodejs')
5+
const PeerInfo = require('peer-info')
6+
const series = require('async/series')
7+
const parallel = require('async/parallel')
8+
const pull = require('pull-stream')
9+
10+
const Relay = require('../src').Relay
11+
const Dialer = require('../src').Dialer
12+
const Listener = require('../src').Listener
13+
14+
const expect = require('chai').expect
15+
16+
describe(`test circuit`, () => {
17+
let srcNode
18+
let dstNode
19+
let relayNode
20+
21+
let srcPeer
22+
let dstPeer
23+
let relayPeer
24+
25+
let dialer
26+
let relayCircuit
27+
let listener
28+
29+
let portBase = 9000 // TODO: randomize or mock sockets
30+
before((done) => {
31+
series([
32+
(cb) => {
33+
PeerInfo.create((err, info) => {
34+
srcPeer = info
35+
srcPeer.multiaddr.add(`/ip4/0.0.0.0/tcp/${portBase++}`)
36+
srcNode = new Node(srcPeer)
37+
cb(err)
38+
})
39+
},
40+
(cb) => {
41+
PeerInfo.create((err, info) => {
42+
dstPeer = info
43+
dstPeer.multiaddr.add(`/ip4/0.0.0.0/tcp/${portBase++}`)
44+
dstNode = new Node(dstPeer)
45+
cb(err)
46+
})
47+
},
48+
(cb) => {
49+
PeerInfo.create((err, info) => {
50+
relayPeer = info
51+
relayPeer.multiaddr.add(`/ip4/0.0.0.0/tcp/${portBase++}`)
52+
relayNode = new Node(relayPeer)
53+
cb(err)
54+
})
55+
},
56+
(cb) => {
57+
let relays = new Map()
58+
relays.set(relayPeer.id.toB58String(), relayPeer)
59+
dialer = new Dialer(srcNode, relays)
60+
cb()
61+
},
62+
(cb) => {
63+
relayCircuit = new Relay(relayNode)
64+
relayCircuit.start(cb)
65+
}],
66+
(err) => done(err)
67+
)
68+
})
69+
70+
beforeEach((done) => {
71+
parallel([
72+
(cb) => {
73+
srcNode.start(cb)
74+
},
75+
(cb) => {
76+
dstNode.start(cb)
77+
},
78+
(cb) => {
79+
relayNode.start(cb)
80+
}
81+
], (err) => done(err))
82+
})
83+
84+
afterEach((done) => {
85+
parallel([
86+
(cb) => {
87+
srcNode.stop(cb)
88+
},
89+
(cb) => {
90+
dstNode.stop(cb)
91+
},
92+
(cb) => {
93+
relayNode.stop(cb)
94+
}
95+
], (err) => done(err))
96+
})
97+
98+
it(`should connect to relay peer`, (done) => {
99+
listener = new Listener(dstNode, (conn) => {
100+
pull(
101+
conn,
102+
pull.map((data) => {
103+
return data.toString().split('').reverse().join('')
104+
}),
105+
conn
106+
)
107+
})
108+
109+
listener.listen(() => {
110+
})
111+
112+
dialer.dial(dstPeer, (err, conn) => {
113+
if (err) {
114+
done(err)
115+
}
116+
117+
pull(
118+
pull.values(['hello']),
119+
conn,
120+
pull.collect((err, data) => {
121+
expect(data[0].toString()).to.equal('olleh')
122+
done(err)
123+
})
124+
)
125+
})
126+
}).timeout(50000)
127+
})

0 commit comments

Comments
 (0)