Skip to content
This repository was archived by the owner on Apr 29, 2020. It is now read-only.

Commit 0175dc4

Browse files
committed
Merge pull request #53 from ipfs/level-up
WIP - level up libp2p interface and error handling
2 parents 6e2c06a + 7f0a0db commit 0175dc4

File tree

6 files changed

+368
-103
lines changed

6 files changed

+368
-103
lines changed

gulpfile.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ gulp.task('libnode:start', (done) => {
2020

2121
node = new Node(peer)
2222
node.start(() => {
23-
node.swarm.handle('/echo/1.0.0', (conn) => {
23+
node.handle('/echo/1.0.0', (conn) => {
2424
conn.pipe(conn)
2525
})
2626
ready()
@@ -34,7 +34,7 @@ gulp.task('libnode:start', (done) => {
3434

3535
gulp.task('libnode:stop', (done) => {
3636
setTimeout(() => {
37-
node.swarm.close((err) => {
37+
node.stop((err) => {
3838
if (err) {
3939
throw err
4040
}

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"bl": "^1.1.2",
3636
"chai": "^3.5.0",
3737
"gulp": "^3.9.1",
38-
"libp2p-ipfs": "^0.9.0",
38+
"libp2p-ipfs": "^0.10.0",
3939
"peer-id": "^0.7.0",
4040
"pre-commit": "^1.1.3",
4141
"run-parallel": "^1.1.6",
@@ -44,15 +44,18 @@
4444
"dependencies": {
4545
"babel-runtime": "^6.9.0",
4646
"libp2p-spdy": "^0.6.1",
47-
"libp2p-swarm": "^0.19.0",
48-
"libp2p-webrtc-star": "^0.2.0",
49-
"libp2p-websockets": "^0.6.0",
47+
"libp2p-swarm": "^0.19.4",
48+
"libp2p-webrtc-star": "^0.2.1",
49+
"libp2p-websockets": "^0.6.1",
50+
"mafmt": "^2.1.1",
5051
"multiaddr": "^2.0.2",
52+
"peer-book": "^0.3.0",
53+
"peer-id": "^0.7.0",
5154
"peer-info": "^0.7.0"
5255
},
5356
"contributors": [
5457
"David Dias <[email protected]>",
5558
"dignifiedquire <[email protected]>",
5659
"greenkeeperio-bot <[email protected]>"
5760
]
58-
}
61+
}

src/index.js

Lines changed: 156 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,57 @@
22

33
const Swarm = require('libp2p-swarm')
44
const PeerInfo = require('peer-info')
5+
const PeerId = require('peer-id')
56
const WS = require('libp2p-websockets')
67
const WebRTCStar = require('libp2p-webrtc-star')
78
const spdy = require('libp2p-spdy')
89
const EE = require('events').EventEmitter
10+
const multiaddr = require('multiaddr')
11+
const PeerBook = require('peer-book')
12+
const mafmt = require('mafmt')
913

1014
exports = module.exports
1115

12-
exports.Node = function Node (peerInfo) {
16+
const OFFLINE_ERROR_MESSAGE = 'The libp2p node is not started yet'
17+
const IPFS_CODE = 421
18+
19+
exports.Node = function Node (pInfo, pBook) {
1320
if (!(this instanceof Node)) {
14-
return new Node(peerInfo)
21+
return new Node(pInfo, pBook)
22+
}
23+
24+
if (!pInfo) {
25+
pInfo = new PeerInfo()
26+
pInfo.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/0'))
1527
}
16-
if (!peerInfo) {
17-
peerInfo = new PeerInfo()
28+
29+
if (!pBook) {
30+
pBook = new PeerBook()
1831
}
1932

20-
this.peerInfo = peerInfo
33+
this.peerInfo = pInfo
34+
this.peerBook = pBook
2135

2236
// Swarm
23-
this.swarm = new Swarm(peerInfo)
37+
this.swarm = new Swarm(pInfo)
2438
this.swarm.connection.addStreamMuxer(spdy)
2539
this.swarm.connection.reuse()
2640

41+
this.swarm.on('peer-mux-established', (peerInfo) => {
42+
this.peerBook.put(peerInfo)
43+
})
44+
45+
this.swarm.on('peer-mux-closed', (peerInfo) => {
46+
this.peerBook.removeByB58String(peerInfo.id.toB58String())
47+
})
48+
49+
let isOnline = false
50+
2751
this.start = (callback) => {
2852
// if we have `webrtc-star` addrs, then add
2953
// the WebRTCStar transport
3054
const wstar = new WebRTCStar()
31-
if (wstar.filter(peerInfo.multiaddrs).length > 0) {
55+
if (wstar.filter(this.peerInfo.multiaddrs).length > 0) {
3256
this.swarm.transport.add('wstar', wstar)
3357
wstar.discovery.on('peer', (peerInfo) => {
3458
this.discovery.emit('peer', peerInfo)
@@ -40,20 +64,141 @@ exports.Node = function Node (peerInfo) {
4064
// WebSockets needs to be added after because
4165
// it can't have a listener on the browser
4266
this.swarm.transport.add('ws', new WS())
67+
isOnline = true
4368
callback()
4469
})
4570
} else {
4671
// if just WebSockets, no thing to listen
4772
this.swarm.transport.add('ws', new WS())
73+
isOnline = true
4874
callback()
4975
}
5076
}
5177

78+
this.stop = (callback) => {
79+
isOnline = false
80+
this.swarm.close(callback)
81+
}
82+
83+
this.dialById = (id, protocol, callback) => {
84+
if (typeof protocol === 'function') {
85+
callback = protocol
86+
protocol = undefined
87+
}
88+
89+
if (!isOnline) {
90+
return callback(new Error(OFFLINE_ERROR_MESSAGE))
91+
}
92+
// NOTE, these dialById only works if a previous dial
93+
// was made until we have PeerRouting
94+
// TODO support PeerRouting when it is Ready
95+
callback(new Error('not implemented yet'))
96+
}
97+
98+
this.dialByMultiaddr = (maddr, protocol, callback) => {
99+
if (typeof protocol === 'function') {
100+
callback = protocol
101+
protocol = undefined
102+
}
103+
104+
if (!isOnline) {
105+
return callback(new Error(OFFLINE_ERROR_MESSAGE))
106+
}
107+
108+
if (typeof maddr === 'string') {
109+
maddr = multiaddr(maddr)
110+
}
111+
112+
if (!mafmt.IPFS.matches(maddr.toString())) {
113+
return callback(new Error('multiaddr not valid'))
114+
}
115+
116+
const ipfsIdB58String = maddr.stringTuples().filter((tuple) => {
117+
if (tuple[0] === IPFS_CODE) {
118+
return true
119+
}
120+
})[0][1]
121+
122+
let peer
123+
try {
124+
peer = this.peerBook.getByB58String(ipfsIdB58String)
125+
} catch (err) {
126+
peer = new PeerInfo(PeerId.createFromB58String(ipfsIdB58String))
127+
}
128+
129+
peer.multiaddr.add(maddr)
130+
this.dialByPeerInfo(peer, protocol, callback)
131+
}
132+
133+
this.dialByPeerInfo = (peer, protocol, callback) => {
134+
if (typeof protocol === 'function') {
135+
callback = protocol
136+
protocol = undefined
137+
}
138+
if (!isOnline) {
139+
return callback(new Error(OFFLINE_ERROR_MESSAGE))
140+
}
141+
142+
this.swarm.dial(peer, protocol, (err, conn) => {
143+
if (err) {
144+
return callback(err)
145+
}
146+
this.peerBook.put(peer)
147+
callback(null, conn)
148+
})
149+
}
150+
151+
this.hangUpById = (id, callback) => {
152+
callback(new Error('not implemented yet'))
153+
// TODO
154+
}
155+
156+
this.hangUpByMultiaddr = (maddr, callback) => {
157+
if (!isOnline) {
158+
return callback(new Error(OFFLINE_ERROR_MESSAGE))
159+
}
160+
161+
if (typeof maddr === 'string') {
162+
maddr = multiaddr(maddr)
163+
}
164+
165+
if (!mafmt.IPFS.matches(maddr.toString())) {
166+
return callback(new Error('multiaddr not valid'))
167+
}
168+
169+
const ipfsIdB58String = maddr.stringTuples().filter((tuple) => {
170+
if (tuple[0] === IPFS_CODE) {
171+
return true
172+
}
173+
})[0][1]
174+
175+
try {
176+
const pi = this.peerBook.getByB58String(ipfsIdB58String)
177+
this.hangUpByPeerInfo(pi, callback)
178+
} catch (err) {
179+
// already disconnected
180+
callback()
181+
}
182+
}
183+
184+
this.hangUpByPeerInfo = (peer, callback) => {
185+
if (!isOnline) {
186+
return callback(new Error(OFFLINE_ERROR_MESSAGE))
187+
}
188+
189+
this.peerBook.removeByB58String(peer.id.toB58String())
190+
this.swarm.hangUp(peer, callback)
191+
}
192+
193+
this.handle = (protocol, handler) => {
194+
return this.swarm.handle(protocol, handler)
195+
}
196+
197+
this.unhandle = (protocol) => {
198+
return this.swarm.unhandle(protocol)
199+
}
200+
52201
this.discovery = new EE()
53202
this.routing = null
54203
this.records = null
55-
56-
this.dial = () => {
57-
throw new Error('THIS WILL BE EQUIVALENT TO THE ROUTED HOST FEATURE, IT WILL FIGURE OUT EVERYTHING :D')
58-
}
59204
}

test/peer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
{
2-
"privKey": "CAASQDA+AgEAAgkAiZMWorfx3pkCAwEAAQIIbl57fDLjyaUCBQDi+1JLAgUAmymkKwIEKc9xkQIFAIoy+KkCBHBfslE="
2+
"id": "Qmex1SSsueWFsUfjdkugJ5zhcnjddAt8TxcnDLUXKD9Sx7",
3+
"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",
4+
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAE="
35
}

test/webrtc-star-only.js

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const bl = require('bl')
1111
const libp2p = require('../src')
1212

1313
describe('libp2p-ipfs-browser (webrtc only)', function () {
14-
this.timeout(60 * 1000)
14+
this.timeout(15 * 1000)
1515

1616
let peer1
1717
let peer2
@@ -46,23 +46,48 @@ describe('libp2p-ipfs-browser (webrtc only)', function () {
4646
})
4747

4848
it('handle a protocol on the first node', (done) => {
49-
node2.swarm.handle('/echo/1.0.0', (conn) => {
49+
node2.handle('/echo/1.0.0', (conn) => {
5050
conn.pipe(conn)
5151
})
5252
done()
5353
})
5454

5555
it('dial from the second node to the first node', (done) => {
56-
node1.swarm.dial(peer2, '/echo/1.0.0', (err, conn) => {
57-
const text = 'hello'
56+
node1.dialByPeerInfo(peer2, '/echo/1.0.0', (err, conn) => {
5857
expect(err).to.not.exist
59-
conn.pipe(bl((err, data) => {
58+
setTimeout(check, 500)
59+
60+
function check () {
61+
const text = 'hello'
62+
const peers1 = node1.peerBook.getAll()
63+
expect(err).to.not.exist
64+
expect(Object.keys(peers1)).to.have.length(1)
65+
const peers2 = node2.peerBook.getAll()
66+
expect(err).to.not.exist
67+
expect(Object.keys(peers2)).to.have.length(1)
68+
conn.pipe(bl((err, data) => {
69+
expect(err).to.not.exist
70+
expect(data.toString()).to.equal(text)
71+
done()
72+
}))
73+
conn.write(text)
74+
conn.end()
75+
}
76+
})
77+
})
78+
79+
it('node1 hangUp node2', (done) => {
80+
node1.hangUpByPeerInfo(peer2, (err) => {
81+
expect(err).to.not.exist
82+
setTimeout(check, 500)
83+
84+
function check () {
85+
const peers = node1.peerBook.getAll()
6086
expect(err).to.not.exist
61-
expect(data.toString()).to.equal(text)
87+
expect(Object.keys(peers)).to.have.length(0)
88+
expect(Object.keys(node1.swarm.muxedConns)).to.have.length(0)
6289
done()
63-
}))
64-
conn.write(text)
65-
conn.end()
90+
}
6691
})
6792
})
6893

@@ -73,17 +98,17 @@ describe('libp2p-ipfs-browser (webrtc only)', function () {
7398
peer3.multiaddr.add(mh3)
7499

75100
node1.discovery.on('peer', (peerInfo) => {
76-
node1.swarm.dial(peerInfo)
101+
node1.dialByPeerInfo(peerInfo, () => {})
77102
})
78103
node2.discovery.on('peer', (peerInfo) => {
79-
node2.swarm.dial(peerInfo)
104+
node2.dialByPeerInfo(peerInfo, () => {})
80105
})
81106

82107
const node3 = new libp2p.Node(peer3)
83108
node3.start(() => {
84109
setTimeout(() => {
85-
expect(Object.keys(node1.swarm.muxedConns).length).to.equal(2)
86-
expect(Object.keys(node2.swarm.muxedConns).length).to.equal(2)
110+
expect(Object.keys(node1.swarm.muxedConns).length).to.equal(1)
111+
expect(Object.keys(node2.swarm.muxedConns).length).to.equal(1)
87112
done()
88113
}, 2000)
89114
})

0 commit comments

Comments
 (0)