Skip to content

Commit 9af0e3a

Browse files
authored
fix: dont use peerinfo distinct (libp2p#334)
* fix: dont use peerinfo distinct * refactor: remove unneeded code * refactor: clean up * refactor: fix feedback
1 parent 951e0c9 commit 9af0e3a

File tree

3 files changed

+169
-4
lines changed

3 files changed

+169
-4
lines changed

src/transport.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const log = debug('libp2p:switch:transport')
99

1010
const LimitDialer = require('./limit-dialer')
1111
const { DIAL_TIMEOUT } = require('./constants')
12+
const { uniqueBy } = require('./utils')
1213

1314
// number of concurrent outbound dials to make per peer, same as go-libp2p-swtch
1415
const defaultPerPeerRateLimit = 8
@@ -124,10 +125,17 @@ class TransportManager {
124125
handler = this.switch._connectionHandler(key, handler)
125126

126127
const transport = this.switch.transports[key]
127-
const multiaddrs = TransportManager.dialables(
128-
transport,
129-
this.switch._peerInfo.multiaddrs.distinct()
130-
)
128+
let originalAddrs = this.switch._peerInfo.multiaddrs.toArray()
129+
130+
// Until TCP can handle distinct addresses on listen, https://github.com/libp2p/interface-transport/issues/41,
131+
// make sure we aren't trying to listen on duplicate ports. This also applies to websockets.
132+
originalAddrs = uniqueBy(originalAddrs, (addr) => {
133+
// Any non 0 port should register as unique
134+
const port = Number(addr.toOptions().port)
135+
return isNaN(port) || port === 0 ? addr.toString() : port
136+
})
137+
138+
const multiaddrs = TransportManager.dialables(transport, originalAddrs)
131139

132140
if (!transport.listeners) {
133141
transport.listeners = []

src/utils.js

+11
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,14 @@ module.exports.identifyDialer = (connection, cryptoPeerInfo) => {
4747
})
4848
})
4949
}
50+
51+
/**
52+
* Get unique values from `arr` using `getValue` to determine
53+
* what is used for uniqueness
54+
* @param {Array} arr The array to get unique values for
55+
* @param {function(value)} getValue The function to determine what is compared
56+
* @returns {Array}
57+
*/
58+
module.exports.uniqueBy = (arr, getValue) => {
59+
return [...new Map(arr.map((i) => [getValue(i), i])).values()]
60+
}

test/transport-manager.spec.js

+146
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@ const expect = chai.expect
77
chai.use(dirtyChai)
88
const Multiaddr = require('multiaddr')
99
const PeerInfo = require('peer-info')
10+
const sinon = require('sinon')
1011

1112
const TransportManager = require('../src/transport')
1213

1314
describe('Transport Manager', () => {
15+
afterEach(() => {
16+
sinon.restore()
17+
})
18+
1419
describe('dialables', () => {
1520
let peerInfo
1621
const dialAllTransport = { filter: addrs => addrs }
@@ -141,4 +146,145 @@ describe('Transport Manager', () => {
141146
expect(dialableAddrs[0].toString()).to.equal('/ip6/::1/tcp/4001')
142147
})
143148
})
149+
150+
describe('listen', () => {
151+
const listener = {
152+
once: function () {},
153+
listen: function () {},
154+
removeListener: function () {},
155+
getAddrs: function () {}
156+
}
157+
158+
it('should allow for multiple addresses with port 0', (done) => {
159+
const mockListener = sinon.stub(listener)
160+
mockListener.listen.callsArg(1)
161+
mockListener.getAddrs.callsArgWith(0, null, [])
162+
const mockSwitch = {
163+
_peerInfo: {
164+
multiaddrs: {
165+
toArray: () => [
166+
Multiaddr('/ip4/127.0.0.1/tcp/0'),
167+
Multiaddr('/ip4/0.0.0.0/tcp/0')
168+
],
169+
replace: () => {}
170+
}
171+
},
172+
_options: {},
173+
_connectionHandler: () => {},
174+
transports: {
175+
TCP: {
176+
filter: (addrs) => addrs,
177+
createListener: () => {
178+
return mockListener
179+
}
180+
}
181+
}
182+
}
183+
const transportManager = new TransportManager(mockSwitch)
184+
transportManager.listen('TCP', null, null, (err) => {
185+
expect(err).to.not.exist()
186+
expect(mockListener.listen.callCount).to.eql(2)
187+
done()
188+
})
189+
})
190+
191+
it('should filter out equal addresses', (done) => {
192+
const mockListener = sinon.stub(listener)
193+
mockListener.listen.callsArg(1)
194+
mockListener.getAddrs.callsArgWith(0, null, [])
195+
const mockSwitch = {
196+
_peerInfo: {
197+
multiaddrs: {
198+
toArray: () => [
199+
Multiaddr('/ip4/127.0.0.1/tcp/0'),
200+
Multiaddr('/ip4/127.0.0.1/tcp/0')
201+
],
202+
replace: () => {}
203+
}
204+
},
205+
_options: {},
206+
_connectionHandler: () => {},
207+
transports: {
208+
TCP: {
209+
filter: (addrs) => addrs,
210+
createListener: () => {
211+
return mockListener
212+
}
213+
}
214+
}
215+
}
216+
const transportManager = new TransportManager(mockSwitch)
217+
transportManager.listen('TCP', null, null, (err) => {
218+
expect(err).to.not.exist()
219+
expect(mockListener.listen.callCount).to.eql(1)
220+
done()
221+
})
222+
})
223+
224+
it('should account for addresses with no port', (done) => {
225+
const mockListener = sinon.stub(listener)
226+
mockListener.listen.callsArg(1)
227+
mockListener.getAddrs.callsArgWith(0, null, [])
228+
const mockSwitch = {
229+
_peerInfo: {
230+
multiaddrs: {
231+
toArray: () => [
232+
Multiaddr('/p2p-circuit'),
233+
Multiaddr('/p2p-websocket-star')
234+
],
235+
replace: () => {}
236+
}
237+
},
238+
_options: {},
239+
_connectionHandler: () => {},
240+
transports: {
241+
TCP: {
242+
filter: (addrs) => addrs,
243+
createListener: () => {
244+
return mockListener
245+
}
246+
}
247+
}
248+
}
249+
const transportManager = new TransportManager(mockSwitch)
250+
transportManager.listen('TCP', null, null, (err) => {
251+
expect(err).to.not.exist()
252+
expect(mockListener.listen.callCount).to.eql(2)
253+
done()
254+
})
255+
})
256+
257+
it('should filter out addresses with the same, non 0, port', (done) => {
258+
const mockListener = sinon.stub(listener)
259+
mockListener.listen.callsArg(1)
260+
mockListener.getAddrs.callsArgWith(0, null, [])
261+
const mockSwitch = {
262+
_peerInfo: {
263+
multiaddrs: {
264+
toArray: () => [
265+
Multiaddr('/ip4/127.0.0.1/tcp/8000'),
266+
Multiaddr('/dnsaddr/libp2p.io/tcp/8000')
267+
],
268+
replace: () => {}
269+
}
270+
},
271+
_options: {},
272+
_connectionHandler: () => {},
273+
transports: {
274+
TCP: {
275+
filter: (addrs) => addrs,
276+
createListener: () => {
277+
return mockListener
278+
}
279+
}
280+
}
281+
}
282+
const transportManager = new TransportManager(mockSwitch)
283+
transportManager.listen('TCP', null, null, (err) => {
284+
expect(err).to.not.exist()
285+
expect(mockListener.listen.callCount).to.eql(1)
286+
done()
287+
})
288+
})
289+
})
144290
})

0 commit comments

Comments
 (0)