helia-coord 1.2.4 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,7 @@
5
5
  // Local libraries
6
6
  import ThisNodeEntity from '../entities/this-node-entity.js'
7
7
  import Schema from './schema.js'
8
+ import Util from '../util/utils.js'
8
9
 
9
10
  class ThisNodeUseCases {
10
11
  constructor (localConfig = {}) {
@@ -16,6 +17,9 @@ class ThisNodeUseCases {
16
17
  )
17
18
  }
18
19
 
20
+ // Encapsulate dependencies
21
+ this.utils = new Util()
22
+
19
23
  // Optional JSON-LD used for announcements. If present, will override
20
24
  // default announcement object in Schema library.
21
25
  this.announceJsonLd = localConfig.announceJsonLd
@@ -34,7 +38,7 @@ class ThisNodeUseCases {
34
38
  this.v1Relays = localConfig.v1Relays
35
39
  // console.log('v1Relays: ', this.v1Relays)
36
40
  }
37
- console.log('this-node-use-cases.js v1Relays: ', this.v1Relays)
41
+ // console.log('this-node-use-cases.js v1Relays: ', this.v1Relays)
38
42
 
39
43
  // Bind 'this' object to all subfunctions
40
44
  this.updateUseCases = this.updateUseCases.bind(this)
@@ -234,6 +238,12 @@ class ThisNodeUseCases {
234
238
 
235
239
  // Called by an Interval, ensures connections are maintained to known pubsub
236
240
  // peers. This will heal connections if nodes drop in and out of the network.
241
+ // Connection workflow:
242
+ // - If peer is advertised as a circuit relay, try to connect directly through
243
+ // the advertised Circuit Relay IP address and port.
244
+ // - Attempt to connect to peer directly through its advertised multiaddrs.
245
+ // - Connect to peer through v2 Circuit Relays (built into ipfs-service-provider)
246
+ // - Connect to peer through v1 Circuit Relays (standalone v1 Circuit Relay)
237
247
  async refreshPeerConnections () {
238
248
  try {
239
249
  // console.log('this.thisNode: ', this.thisNode)
@@ -251,6 +261,8 @@ class ThisNodeUseCases {
251
261
  connectedPeers
252
262
  )
253
263
 
264
+ // console.log('thisNode.peerData: ', this.thisNode.peerData)
265
+
254
266
  // Loop through each known peer
255
267
  for (let i = 0; i < peers.length; i++) {
256
268
  // const thisPeer = this.state.peers[peer]
@@ -278,21 +290,74 @@ class ThisNodeUseCases {
278
290
  peerData = peerData[0]
279
291
  // console.log('peerData: ', peerData)
280
292
 
281
- // TODO: if broadcastedAt value is older than 10 minutes, skip connecting
293
+ // If broadcastedAt value is older than 10 minutes, skip connecting
282
294
  // to the peer. It may be stale information.
283
295
  if (!this.isFreshPeer(peerData)) {
284
296
  this.adapters.log.statusLog(2, `Peer ${peerData.from} is stale. Skipping.`)
285
297
  continue
286
298
  }
287
299
 
300
+ let connected = false
301
+
302
+ // If peer advertises itself as a Circuit Relay, connect to the circuit
303
+ // relay connection info
304
+ if (peerData.data && peerData.data.isCircuitRelay) {
305
+ const multiaddr = `/ip4/${peerData.data.circuitRelayInfo.ip4}/tcp/${peerData.data.circuitRelayInfo.tcpPort}/p2p/${thisPeer}`
306
+
307
+ connected = await this.adapters.ipfs.connectToPeer({ multiaddr })
308
+
309
+ if (connected) {
310
+ this.adapters.log.statusLog(2,
311
+ `Successfully connected to Circuit Relay peer ${thisPeer} through direct connection: ${multiaddr}.`
312
+ )
313
+
314
+ // Add the connection multiaddr to the peer, so that we can see
315
+ // exactly how we're connected to the peer.
316
+ const thisPeerData = this.thisNode.peerData.filter(x => x.from === thisPeer)
317
+ thisPeerData[0].data.connectionAddr = multiaddr
318
+
319
+ // Break out of the loop once we've made a successful connection.
320
+ continue
321
+ }
322
+ }
323
+
324
+ // Try a direct connection with the peer by going through
325
+ // the multiaddrs in the announcement object.
326
+ const filteredMultiaddrs = this.utils.filterMultiaddrs(peerData.data.ipfsMultiaddrs)
327
+ console.log('filteredMultiaddrs: ', filteredMultiaddrs)
328
+
329
+ for (let j = 0; j < filteredMultiaddrs.length; j++) {
330
+ const multiaddr = filteredMultiaddrs[j]
331
+
332
+ // Attempt to connect to the node through a circuit relay.
333
+ connected = await this.adapters.ipfs.connectToPeer({ multiaddr })
334
+
335
+ // If the connection was successful, break out of the relay loop.
336
+ // Otherwise try to connect through the next relay.
337
+ if (connected) {
338
+ this.adapters.log.statusLog(2,
339
+ `Successfully connected to peer ${thisPeer} through direct connection: ${multiaddr}.`
340
+ )
341
+
342
+ // Add the connection multiaddr to the peer, so that we can see
343
+ // exactly how we're connected to the peer.
344
+ const thisPeerData = this.thisNode.peerData.filter(x => x.from === thisPeer)
345
+ thisPeerData[0].data.connectionAddr = multiaddr
346
+
347
+ // Break out of the loop once we've made a successful connection.
348
+ break
349
+ }
350
+ }
351
+ if (connected) {
352
+ continue
353
+ }
354
+
288
355
  // Sort the Circuit Relays by the average of the aboutLatency
289
356
  // array. Connect to peers through the Relays with the lowest latencies
290
357
  // first.
291
358
  const sortedRelays = this.thisNode.useCases.relays.sortRelays(relays)
292
359
  // console.log(`sortedRelays: ${JSON.stringify(sortedRelays, null, 2)}`)
293
360
 
294
- let connected = false
295
-
296
361
  // Loop through each known circuit relay and attempt to connect to the
297
362
  // peer through a relay.
298
363
  for (let j = 0; j < sortedRelays.length; j++) {
package/lib/util/utils.js CHANGED
@@ -7,6 +7,28 @@ class Util {
7
7
  sleep (ms) {
8
8
  return new Promise(resolve => setTimeout(resolve, ms))
9
9
  }
10
+
11
+ // This function takes an array of multiaddrs as input, and returns an array
12
+ // filtered to remove any local network IP addresses.
13
+ filterMultiaddrs (multiaddrs) {
14
+ try {
15
+ const filteredMultiaddrs = multiaddrs.filter((x) => {
16
+ if (x.includes('127.0.0.1')) return false
17
+ if (x.includes('udp')) return false
18
+ if (x.includes('quic')) return false
19
+ if (x.includes('p2p-circuit')) return false
20
+ if (x.includes('192.168.')) return false
21
+ if (x.includes('172.16.')) return false
22
+ if (x.includes('/10.')) return false
23
+ return true
24
+ })
25
+
26
+ return filteredMultiaddrs
27
+ } catch (err) {
28
+ console.error('Error in util.js/filterMultiaddrs()')
29
+ throw err
30
+ }
31
+ }
10
32
  }
11
33
 
12
34
  export default Util
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helia-coord",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "A JS library for helping IPFS peers coordinate, find a common interest, and stay connected around that interest.",
5
5
  "main": "./index.js",
6
6
  "type": "module",
@@ -229,6 +229,7 @@ describe('#thisNode-Use-Cases', () => {
229
229
  sandbox.stub(uut.adapters.ipfs, 'getPeers').resolves(mockData.swarmPeers)
230
230
  sandbox.stub(uut.adapters.ipfs, 'connectToPeer').resolves(true)
231
231
  sandbox.stub(uut, 'isFreshPeer').returns(true)
232
+ sandbox.stub(uut.utils, 'filterMultiaddrs').returns([])
232
233
 
233
234
  // Connect to that peer.
234
235
  const result = await uut.refreshPeerConnections()
@@ -255,6 +256,7 @@ describe('#thisNode-Use-Cases', () => {
255
256
  sandbox.stub(uut.adapters.ipfs, 'connectToPeer')
256
257
  .onCall(0).resolves(false)
257
258
  .onCall(1).resolves(true)
259
+ sandbox.stub(uut.utils, 'filterMultiaddrs').returns([])
258
260
 
259
261
  uut.v1Relays = ['fake-v1-relay']
260
262
 
@@ -264,6 +266,67 @@ describe('#thisNode-Use-Cases', () => {
264
266
  assert.equal(result, true)
265
267
  })
266
268
 
269
+ it('should connect directly to circuit relays advertised IP and port', async () => {
270
+ await uut.createSelf({ type: 'node.js' })
271
+ // Add a circuit relay peer with advertised IP and port.
272
+ const ipfsId = 'QmbyYXKbnAmMbMGo8LRBZ58jYs58anqUzY1m4jxDmhDsje'
273
+ uut.thisNode.peerList = [ipfsId]
274
+ uut.thisNode.peerData = [{
275
+ from: ipfsId,
276
+ data: {
277
+ isCircuitRelay: true,
278
+ circuitRelayInfo: {
279
+ ip4: '123.456.7.8',
280
+ tcpPort: 4001
281
+ }
282
+ }
283
+ }]
284
+
285
+ // Add a peer
286
+ await uut.addSubnetPeer(mockData.announceObj)
287
+
288
+ // Force circuit relay to be used.
289
+ uut.thisNode.relayData = mockData.mockRelayData
290
+
291
+ // Mock dependencies
292
+ sandbox.stub(uut.adapters.ipfs, 'getPeers').resolves(mockData.swarmPeers)
293
+ sandbox.stub(uut, 'isFreshPeer').returns(true)
294
+ sandbox.stub(uut.adapters.ipfs, 'connectToPeer')
295
+ .onCall(0).resolves(true)
296
+ sandbox.stub(uut.utils, 'filterMultiaddrs').returns([])
297
+
298
+ // Connect to that peer.
299
+ const result = await uut.refreshPeerConnections()
300
+
301
+ assert.equal(result, true)
302
+ })
303
+
304
+ it('should connect directly to IPFS peers multiaddr', async () => {
305
+ await uut.createSelf({ type: 'node.js' })
306
+ // Add a circuit relay peer with advertised IP and port.
307
+ const ipfsId = 'QmbyYXKbnAmMbMGo8LRBZ58jYs58anqUzY1m4jxDmhDsje'
308
+ uut.thisNode.peerList = [ipfsId]
309
+ uut.thisNode.peerData = [{ from: ipfsId, data: {} }]
310
+
311
+ // Add a peer
312
+ await uut.addSubnetPeer(mockData.announceObj)
313
+
314
+ // Force circuit relay to be used.
315
+ uut.thisNode.relayData = mockData.mockRelayData
316
+
317
+ // Mock dependencies
318
+ sandbox.stub(uut.adapters.ipfs, 'getPeers').resolves(mockData.swarmPeers)
319
+ sandbox.stub(uut, 'isFreshPeer').returns(true)
320
+ sandbox.stub(uut.adapters.ipfs, 'connectToPeer')
321
+ .onCall(0).resolves(true)
322
+ sandbox.stub(uut.utils, 'filterMultiaddrs').returns(['/ip4/123.45.6.7/p2p/ipfs-id'])
323
+
324
+ // Connect to that peer.
325
+ const result = await uut.refreshPeerConnections()
326
+
327
+ assert.equal(result, true)
328
+ })
329
+
267
330
  it('should skip if peer is stale', async () => {
268
331
  await uut.createSelf({ type: 'node.js' })
269
332
  // Add a peer that is not in the list of connected peers.
@@ -28,4 +28,37 @@ describe('#utils.js', () => {
28
28
  assert.isOk(true)
29
29
  })
30
30
  })
31
+
32
+ describe('#filterMultiaddrs', () => {
33
+ it('should filter out undesired multiaddrs', () => {
34
+ const ipfsMultiaddrs = [
35
+ 'ip4/tcp/127.0.0.1/addr1',
36
+ 'ip4/udp/123.456.789.1/addr1',
37
+ 'ip4/quic/123.456.789.1/addr1',
38
+ 'ip4/tcp/123.456.789.1/p2p-circuit/p2p/addr1',
39
+ '/ip4/addr1',
40
+ '/ip4/192.168.0.1/addr1',
41
+ '/ip4/10.0.0.1/addr1',
42
+ '/ip4/172.16.0.1/addr1'
43
+ ]
44
+
45
+ const result = uut.filterMultiaddrs(ipfsMultiaddrs)
46
+ // console.log('result: ', result)
47
+
48
+ assert.equal(result.length, 1)
49
+ assert.equal(result[0], '/ip4/addr1')
50
+ })
51
+
52
+ it('should catch, report, and throw errors', () => {
53
+ try {
54
+ uut.filterMultiaddrs(4)
55
+
56
+ assert.fail('Unexpected code path')
57
+ } catch (err) {
58
+ // console.log(err)
59
+
60
+ assert.include(err.message, 'is not a function')
61
+ }
62
+ })
63
+ })
31
64
  })