helia-coord 1.6.0 → 1.7.1

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.
@@ -92,7 +92,8 @@ class CreateHeliaNode {
92
92
  '/ip4/127.0.0.1/tcp/0',
93
93
  '/ip4/0.0.0.0/tcp/4001',
94
94
  '/ip4/0.0.0.0/tcp/4003/ws',
95
- '/webrtc'
95
+ '/webrtc',
96
+ '/p2p-circuit'
96
97
  ]
97
98
  },
98
99
  transports: [
@@ -0,0 +1,5 @@
1
+ # webRTC example
2
+
3
+ This directory contains example code that attempts to follow the [this libp2p webRTC README](https://github.com/libp2p/js-libp2p/tree/main/packages/transport-webrtc) in order to connect a two nodes running behind NATs, through a public Circuit Relay server.
4
+
5
+
@@ -0,0 +1,159 @@
1
+ /*
2
+ This library creates a Helia IPFS node. This is done prior to attaching
3
+ helia-coord to the node.
4
+ This library is called by start-node.js.
5
+ */
6
+
7
+ // Global npm libraries
8
+ import { createHelia } from 'helia'
9
+ import fs from 'fs'
10
+ import { FsBlockstore } from 'blockstore-fs'
11
+ import { FsDatastore } from 'datastore-fs'
12
+ import { createLibp2p } from 'libp2p'
13
+ import { tcp } from '@libp2p/tcp'
14
+ import { noise } from '@chainsafe/libp2p-noise'
15
+ import { yamux } from '@chainsafe/libp2p-yamux'
16
+ // import { bootstrap } from '@libp2p/bootstrap'
17
+ // import { identifyService } from 'libp2p/identify'
18
+ import { identify } from '@libp2p/identify'
19
+ // import { circuitRelayTransport } from 'libp2p/circuit-relay'
20
+ import { circuitRelayTransport } from '@libp2p/circuit-relay-v2'
21
+ import { gossipsub } from '@chainsafe/libp2p-gossipsub'
22
+ import { webSockets } from '@libp2p/websockets'
23
+ import { publicIpv4 } from 'public-ip'
24
+ import { multiaddr } from '@multiformats/multiaddr'
25
+ import { webRTC } from '@libp2p/webrtc'
26
+ import { ping } from '@libp2p/ping'
27
+
28
+ const ROOT_DIR = './'
29
+ const IPFS_DIR = './.ipfsdata/ipfs'
30
+
31
+ class CreateHeliaNode {
32
+ constructor () {
33
+ this.publicIp = publicIpv4
34
+ }
35
+
36
+ // Start an IPFS node.
37
+ async start () {
38
+ try {
39
+ // Ensure the directory structure exists that is needed by the IPFS node to store data.
40
+ this.ensureBlocksDir()
41
+
42
+ // Create an IPFS node
43
+ const ipfs = await this.createNode()
44
+ // console.log('ipfs: ', ipfs)
45
+
46
+ this.id = ipfs.libp2p.peerId.toString()
47
+ console.log('IPFS ID: ', this.id)
48
+
49
+ // Attempt to guess our ip4 IP address.
50
+ const ip4 = await this.publicIp()
51
+ let detectedMultiaddr = `/ip4/${ip4}/tcp/4001/p2p/${this.id}`
52
+ detectedMultiaddr = multiaddr(detectedMultiaddr)
53
+
54
+ // Get the multiaddrs for the node.
55
+ const multiaddrs = ipfs.libp2p.getMultiaddrs()
56
+ multiaddrs.push(detectedMultiaddr)
57
+ console.log('Multiaddrs: ', multiaddrs)
58
+
59
+ this.multiaddrs = multiaddrs
60
+
61
+ // Signal that this adapter is ready.
62
+ this.isReady = true
63
+
64
+ this.ipfs = ipfs
65
+
66
+ return this.ipfs
67
+ } catch (err) {
68
+ console.error('Error in start()')
69
+
70
+ throw err
71
+ }
72
+ }
73
+
74
+ // This function creates an IPFS node using Helia.
75
+ // It returns the node as an object.
76
+ async createNode () {
77
+ try {
78
+ // Create block and data stores.
79
+ const blockstore = new FsBlockstore(`${IPFS_DIR}/blockstore`)
80
+ const datastore = new FsDatastore(`${IPFS_DIR}/datastore`)
81
+
82
+ // Configure services
83
+ const services = {
84
+ identify: identify(),
85
+ pubsub: gossipsub({ allowPublishToZeroTopicPeers: true }),
86
+ ping: ping()
87
+ }
88
+
89
+ // libp2p is the networking layer that underpins Helia
90
+ const libp2p = await createLibp2p({
91
+ datastore,
92
+ addresses: {
93
+ listen: [
94
+ '/ip4/127.0.0.1/tcp/0',
95
+ '/ip4/0.0.0.0/tcp/4001',
96
+ '/ip4/0.0.0.0/tcp/4003/ws',
97
+ '/webrtc',
98
+ '/p2p-circuit'
99
+ ]
100
+ },
101
+ transports: [
102
+ tcp(),
103
+ webSockets(),
104
+ circuitRelayTransport({ discoverRelays: 3 }),
105
+ webRTC()
106
+ ],
107
+ connectionEncrypters: [
108
+ noise()
109
+ ],
110
+ streamMuxers: [
111
+ yamux()
112
+ ],
113
+ services
114
+ })
115
+
116
+ // create a Helia node
117
+ const helia = await createHelia({
118
+ blockstore,
119
+ datastore,
120
+ libp2p
121
+ })
122
+
123
+ return helia
124
+ } catch (err) {
125
+ console.error('Error creating Helia node: ', err)
126
+
127
+ throw err
128
+ }
129
+ }
130
+
131
+ async stop () {
132
+ await this.ipfs.stop()
133
+
134
+ return true
135
+ }
136
+
137
+ // Ensure that the directories exist to store blocks from the IPFS network.
138
+ // This function is called at startup, before the IPFS node is started.
139
+ ensureBlocksDir () {
140
+ try {
141
+ !fs.existsSync(`${ROOT_DIR}.ipfsdata`) && fs.mkdirSync(`${ROOT_DIR}.ipfsdata`)
142
+
143
+ !fs.existsSync(`${IPFS_DIR}`) && fs.mkdirSync(`${IPFS_DIR}`)
144
+
145
+ !fs.existsSync(`${IPFS_DIR}/blockstore`) && fs.mkdirSync(`${IPFS_DIR}/blockstore`)
146
+
147
+ !fs.existsSync(`${IPFS_DIR}/datastore`) && fs.mkdirSync(`${IPFS_DIR}/datastore`)
148
+
149
+ // !fs.existsSync(`${IPFS_DIR}/datastore/peers`) && fs.mkdirSync(`${IPFS_DIR}/datastore/peers`)
150
+
151
+ return true
152
+ } catch (err) {
153
+ console.error('Error in ensureBlocksDir(): ', err)
154
+ throw err
155
+ }
156
+ }
157
+ }
158
+
159
+ export default CreateHeliaNode
@@ -0,0 +1,77 @@
1
+ /*
2
+ 1. Starts node.
3
+ 2. Waits to connect to Circuit Relay.
4
+ 3. Connects to remote node.
5
+ */
6
+
7
+ // Global npm libraries
8
+ import SlpWallet from 'minimal-slp-wallet'
9
+ import { WebRTC } from '@multiformats/multiaddr-matcher'
10
+ import { multiaddr } from '@multiformats/multiaddr'
11
+ import delay from 'delay'
12
+
13
+ // Local libraries
14
+ // import IpfsCoord from '../index.js'
15
+ import CreateHeliaNode from './create-helia-node.js'
16
+
17
+ const relayMA = '/ip4/5.78.70.29/tcp/4001/p2p/12D3KooWNbQrdvEpzKuZ6rxkAF9vBH56HeGJ9drmrF9bhBJBa2Nq'
18
+ // const remoteMa = '12D3KooWDaKkuwzCNEWUEfjYSh7SCjqpEdx7PW4pAD5P5CFZQmqW'
19
+
20
+ async function start () {
21
+ try {
22
+ // Create an instance of bch-js and IPFS.
23
+ const wallet = new SlpWallet()
24
+ await wallet.walletInfoPromise
25
+
26
+ const createHeliaNode = new CreateHeliaNode()
27
+ const thisNode = await createHeliaNode.start()
28
+
29
+ const relayMultiaddr = multiaddr(relayMA)
30
+ console.log('Relay multiaddr: ', relayMultiaddr)
31
+ await thisNode.libp2p.dial(relayMultiaddr, {
32
+ signal: AbortSignal.timeout(10000)
33
+ })
34
+
35
+ console.log('Connected to relay.')
36
+
37
+ let webRTCMultiaddr
38
+
39
+ // wait for the listener to make a reservation on the relay
40
+ while (true) {
41
+ webRTCMultiaddr = thisNode.libp2p.getMultiaddrs().find(ma => WebRTC.matches(ma))
42
+ console.log('WebRTC multiaddr: ', webRTCMultiaddr)
43
+ const allAddrs = thisNode.libp2p.getMultiaddrs()
44
+ console.log('All multiaddrs: ', allAddrs)
45
+
46
+ // const mas = thisNode.libp2p.getMultiaddrs()
47
+ // console.log('Multiaddrs: ', mas)
48
+
49
+ if (webRTCMultiaddr != null) {
50
+ break
51
+ }
52
+
53
+ // try again later
54
+ const now = new Date()
55
+ console.log('Waiting for reservation...', now.toISOString())
56
+ await delay(1000)
57
+ }
58
+ console.log('WebRTC multiaddr: ', webRTCMultiaddr)
59
+
60
+ // Pass bch-js and IPFS to ipfs-coord when instantiating it.
61
+ // const ipfsCoord = new IpfsCoord({
62
+ // ipfs,
63
+ // wallet,
64
+ // type: 'node.js',
65
+ // // type: 'browser'
66
+ // nodeType: 'external',
67
+ // debugLevel: 2
68
+ // })
69
+
70
+ // await ipfsCoord.start()
71
+ // console.log('IPFS and the coordination library is ready.')
72
+ } catch (err) {
73
+ console.error('Error in start(): ', err)
74
+ }
75
+ }
76
+
77
+ start()
@@ -0,0 +1,171 @@
1
+ /*
2
+ This library creates a Helia IPFS node. This is done prior to attaching
3
+ helia-coord to the node.
4
+ This library is called by start-node.js.
5
+ */
6
+
7
+ // Global npm libraries
8
+ import { createHelia } from 'helia'
9
+ import fs from 'fs'
10
+ import { FsBlockstore } from 'blockstore-fs'
11
+ import { FsDatastore } from 'datastore-fs'
12
+ import { createLibp2p } from 'libp2p'
13
+ import { tcp } from '@libp2p/tcp'
14
+ import { noise } from '@chainsafe/libp2p-noise'
15
+ import { yamux } from '@chainsafe/libp2p-yamux'
16
+ // import { bootstrap } from '@libp2p/bootstrap'
17
+ // import { identifyService } from 'libp2p/identify'
18
+ import { identify } from '@libp2p/identify'
19
+ // import { circuitRelayTransport } from '@libp2p/circuit-relay-v2'
20
+ import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2'
21
+ import { gossipsub } from '@chainsafe/libp2p-gossipsub'
22
+ import { webSockets } from '@libp2p/websockets'
23
+ import { publicIpv4 } from 'public-ip'
24
+ import { multiaddr } from '@multiformats/multiaddr'
25
+ import { webRTC } from '@libp2p/webrtc'
26
+ import { ping } from '@libp2p/ping'
27
+
28
+ const ROOT_DIR = './'
29
+ const IPFS_DIR = './.ipfsdata/ipfs'
30
+
31
+ class CreateHeliaNode {
32
+ constructor () {
33
+ this.publicIp = publicIpv4
34
+ }
35
+
36
+ // Start an IPFS node.
37
+ async start () {
38
+ try {
39
+ // Ensure the directory structure exists that is needed by the IPFS node to store data.
40
+ this.ensureBlocksDir()
41
+
42
+ // Create an IPFS node
43
+ const ipfs = await this.createNode()
44
+ // console.log('ipfs: ', ipfs)
45
+
46
+ this.id = ipfs.libp2p.peerId.toString()
47
+ console.log('IPFS ID: ', this.id)
48
+
49
+ // Attempt to guess our ip4 IP address.
50
+ const ip4 = await this.publicIp()
51
+ let detectedMultiaddr = `/ip4/${ip4}/tcp/4001/p2p/${this.id}`
52
+ detectedMultiaddr = multiaddr(detectedMultiaddr)
53
+
54
+ // Get the multiaddrs for the node.
55
+ const multiaddrs = ipfs.libp2p.getMultiaddrs()
56
+ multiaddrs.push(detectedMultiaddr)
57
+ console.log('Multiaddrs: ', multiaddrs)
58
+
59
+ this.multiaddrs = multiaddrs
60
+
61
+ // Signal that this adapter is ready.
62
+ this.isReady = true
63
+
64
+ this.ipfs = ipfs
65
+
66
+ return this.ipfs
67
+ } catch (err) {
68
+ console.error('Error in start()')
69
+
70
+ throw err
71
+ }
72
+ }
73
+
74
+ // This function creates an IPFS node using Helia.
75
+ // It returns the node as an object.
76
+ async createNode () {
77
+ try {
78
+ // Create block and data stores.
79
+ const blockstore = new FsBlockstore(`${IPFS_DIR}/blockstore`)
80
+ const datastore = new FsDatastore(`${IPFS_DIR}/datastore`)
81
+
82
+ // Configure services
83
+ const services = {
84
+ identify: identify(),
85
+ pubsub: gossipsub({ allowPublishToZeroTopicPeers: true }),
86
+ ping: ping(),
87
+ relay: circuitRelayServer({ // makes the node function as a relay server
88
+ hopTimeout: 30 * 1000, // incoming relay requests must be resolved within this time limit
89
+ advertise: true,
90
+ reservations: {
91
+ maxReservations: 15, // how many peers are allowed to reserve relay slots on this server
92
+ reservationClearInterval: 300 * 1000, // how often to reclaim stale reservations
93
+ applyDefaultLimit: true, // whether to apply default data/duration limits to each relayed connection
94
+ defaultDurationLimit: 2 * 60 * 1000, // the default maximum amount of time a relayed connection can be open for
95
+ defaultDataLimit: BigInt(2 << 7), // the default maximum number of bytes that can be transferred over a relayed connection
96
+ maxInboundHopStreams: 32, // how many inbound HOP streams are allow simultaneously
97
+ maxOutboundHopStreams: 64 // how many outbound HOP streams are allow simultaneously
98
+ }
99
+ })
100
+ }
101
+
102
+ // libp2p is the networking layer that underpins Helia
103
+ const libp2p = await createLibp2p({
104
+ datastore,
105
+ addresses: {
106
+ listen: [
107
+ '/ip4/127.0.0.1/tcp/0',
108
+ '/ip4/0.0.0.0/tcp/4001',
109
+ '/ip4/0.0.0.0/tcp/4003/ws',
110
+ '/webrtc'
111
+ ]
112
+ },
113
+ transports: [
114
+ tcp(),
115
+ webSockets(),
116
+ circuitRelayTransport({ discoverRelays: 3 }),
117
+ webRTC()
118
+ ],
119
+ connectionEncrypters: [
120
+ noise()
121
+ ],
122
+ streamMuxers: [
123
+ yamux()
124
+ ],
125
+ services
126
+ })
127
+
128
+ // create a Helia node
129
+ const helia = await createHelia({
130
+ blockstore,
131
+ datastore,
132
+ libp2p
133
+ })
134
+
135
+ return helia
136
+ } catch (err) {
137
+ console.error('Error creating Helia node: ', err)
138
+
139
+ throw err
140
+ }
141
+ }
142
+
143
+ async stop () {
144
+ await this.ipfs.stop()
145
+
146
+ return true
147
+ }
148
+
149
+ // Ensure that the directories exist to store blocks from the IPFS network.
150
+ // This function is called at startup, before the IPFS node is started.
151
+ ensureBlocksDir () {
152
+ try {
153
+ !fs.existsSync(`${ROOT_DIR}.ipfsdata`) && fs.mkdirSync(`${ROOT_DIR}.ipfsdata`)
154
+
155
+ !fs.existsSync(`${IPFS_DIR}`) && fs.mkdirSync(`${IPFS_DIR}`)
156
+
157
+ !fs.existsSync(`${IPFS_DIR}/blockstore`) && fs.mkdirSync(`${IPFS_DIR}/blockstore`)
158
+
159
+ !fs.existsSync(`${IPFS_DIR}/datastore`) && fs.mkdirSync(`${IPFS_DIR}/datastore`)
160
+
161
+ // !fs.existsSync(`${IPFS_DIR}/datastore/peers`) && fs.mkdirSync(`${IPFS_DIR}/datastore/peers`)
162
+
163
+ return true
164
+ } catch (err) {
165
+ console.error('Error in ensureBlocksDir(): ', err)
166
+ throw err
167
+ }
168
+ }
169
+ }
170
+
171
+ export default CreateHeliaNode
@@ -0,0 +1,71 @@
1
+ /*
2
+ 1. Starts node.
3
+ 2. Waits to connect to Circuit Relay.
4
+ 3. Connects to remote node.
5
+ */
6
+
7
+ // Global npm libraries
8
+ import SlpWallet from 'minimal-slp-wallet'
9
+ import { WebRTC } from '@multiformats/multiaddr-matcher'
10
+ import { multiaddr } from '@multiformats/multiaddr'
11
+ import delay from 'delay'
12
+
13
+ // Local libraries
14
+ // import IpfsCoord from '../index.js'
15
+ import CreateHeliaNode from './create-helia-node.js'
16
+
17
+ const relayMA = '/ip4/5.78.70.29/tcp/4001/p2p/12D3KooWNbQrdvEpzKuZ6rxkAF9vBH56HeGJ9drmrF9bhBJBa2Nq'
18
+ // const remoteMa = '12D3KooWDaKkuwzCNEWUEfjYSh7SCjqpEdx7PW4pAD5P5CFZQmqW'
19
+
20
+ async function start () {
21
+ try {
22
+ // Create an instance of bch-js and IPFS.
23
+ const wallet = new SlpWallet()
24
+ await wallet.walletInfoPromise
25
+
26
+ const createHeliaNode = new CreateHeliaNode()
27
+ const thisNode = await createHeliaNode.start()
28
+
29
+ await thisNode.libp2p.dial(multiaddr(relayMA), {
30
+ signal: AbortSignal.timeout(10000)
31
+ })
32
+
33
+ console.log('Connected to relay.')
34
+
35
+ let webRTCMultiaddr
36
+
37
+ // wait for the listener to make a reservation on the relay
38
+ while (true) {
39
+ webRTCMultiaddr = thisNode.libp2p.getMultiaddrs().find(ma => WebRTC.matches(ma))
40
+ // const mas = thisNode.libp2p.getMultiaddrs()
41
+ // console.log('Multiaddrs: ', mas)
42
+
43
+ if (webRTCMultiaddr != null) {
44
+ break
45
+ }
46
+
47
+ // try again later
48
+ const now = new Date()
49
+ console.log('Waiting for reservation...', now.toISOString())
50
+ await delay(1000)
51
+ }
52
+ console.log('WebRTC multiaddr: ', webRTCMultiaddr)
53
+
54
+ // Pass bch-js and IPFS to ipfs-coord when instantiating it.
55
+ // const ipfsCoord = new IpfsCoord({
56
+ // ipfs,
57
+ // wallet,
58
+ // type: 'node.js',
59
+ // // type: 'browser'
60
+ // nodeType: 'external',
61
+ // debugLevel: 2
62
+ // })
63
+
64
+ // await ipfsCoord.start()
65
+ // console.log('IPFS and the coordination library is ready.')
66
+ } catch (err) {
67
+ console.error('Error in start(): ', err)
68
+ }
69
+ }
70
+
71
+ start()
@@ -48,6 +48,7 @@ class TimerControllers {
48
48
  this.relaySearchInterval = 63000 * 2
49
49
  this.checkBlacklistInterval = 64000 * 2
50
50
  this.listPubsubChannelsInterval = 65000 * 2
51
+ this.getWebRtcMultiaddrInterval = 66000 * 2
51
52
 
52
53
  // Bind 'this' object to all subfunctions
53
54
  this.startTimers = this.startTimers.bind(this)
@@ -58,6 +59,7 @@ class TimerControllers {
58
59
  this.managePeers = this.managePeers.bind(this)
59
60
  this.blacklist = this.blacklist.bind(this)
60
61
  this.searchForRelays = this.searchForRelays.bind(this)
62
+ this.getWebRtcMultiaddr = this.getWebRtcMultiaddr.bind(this)
61
63
  }
62
64
 
63
65
  startTimers (thisNode) {
@@ -89,6 +91,10 @@ class TimerControllers {
89
91
  await _this.listPubsubChannels()
90
92
  }, this.listPubsubChannelsInterval)
91
93
 
94
+ this.getWebRtcMultiaddrHandle = setInterval(async function () {
95
+ await _this.getWebRtcMultiaddr(thisNode, useCases)
96
+ }, this.getWebRtcMultiaddrInterval)
97
+
92
98
  // DEBUG: report the state of thisNode.
93
99
  // setInterval(function () {
94
100
  // const tempThisNode = clonedeep(thisNode)
@@ -114,7 +120,8 @@ class TimerControllers {
114
120
  peerTimerHandle: this.peerTimerHandle,
115
121
  relaySearchHandle: this.relaySearchHandle,
116
122
  checkBlacklistHandle: this.checkBlacklistHandle,
117
- listPubsubChannelsHandle: this.listPubsubChannelsHandle
123
+ listPubsubChannelsHandle: this.listPubsubChannelsHandle,
124
+ getWebRtcMultiaddrHandle: this.getWebRtcMultiaddrHandle
118
125
  }
119
126
  }
120
127
 
@@ -126,6 +133,7 @@ class TimerControllers {
126
133
  clearInterval(this.relaySearchHandle)
127
134
  clearInterval(this.checkBlacklistHandle)
128
135
  clearInterval(this.listPubsubChannelsHandle)
136
+ clearInterval(this.getWebRtcMultiaddrHandle)
129
137
  }
130
138
 
131
139
  async listPubsubChannels () {
@@ -205,22 +213,32 @@ class TimerControllers {
205
213
  // Disable the timer interval while this function executes.
206
214
  clearInterval(this.announceTimerHandle)
207
215
 
216
+ // Get multiaddrs that can be used to connect to this node.
217
+ const multiaddrs = useCases.peer.getMultiaddrs()
218
+ // console.log('manageAnnouncement(): multiaddrs: ', multiaddrs)
219
+
208
220
  // Get the information needed for the announcement.
209
221
  const announceObj = {
210
222
  ipfsId: thisNode.ipfsId,
211
- ipfsMultiaddrs: thisNode.ipfsMultiaddrs,
223
+ // ipfsMultiaddrs: thisNode.ipfsMultiaddrs,
224
+ ipfsMultiaddrs: multiaddrs,
212
225
  type: thisNode.type,
213
226
  // orbitdbId: thisNode.orbit.id,
214
227
 
215
228
  // TODO: Allow node.js apps to pass a config setting to override this.
216
229
  isCircuitRelay: false
217
230
  }
231
+ // console.log('announceObj: ', announceObj)
218
232
 
219
233
  // Generate the announcement message.
220
234
  const announceMsgObj = thisNode.schema.announcement(announceObj)
221
235
  // console.log(`announceMsgObj: ${JSON.stringify(announceMsgObj, null, 2)}`)
222
236
 
237
+ // Overwrite the default multiaddrs with the latest multiaddrs.
238
+ announceMsgObj.ipfsMultiaddrs = multiaddrs
239
+
223
240
  const announceMsgStr = JSON.stringify(announceMsgObj)
241
+ console.log('announceMsgStr: ', announceMsgStr)
224
242
 
225
243
  // Publish the announcement to the pubsub channel.
226
244
  await this.adapters.pubsub.messaging.publishToPubsubChannel(
@@ -405,6 +423,21 @@ class TimerControllers {
405
423
  return false
406
424
  }
407
425
  }
426
+
427
+ // Extract any webRTC multiaddrs from the node's list of multiaddrs.
428
+ // Add them to the announcement object.
429
+ getWebRtcMultiaddr (thisNode, useCases) {
430
+ try {
431
+ console.log('---->Entering getWebRtcMultiaddr() Timer Controller.<----')
432
+
433
+ useCases.peer.getWebRtcMultiaddr({ thisNode })
434
+ } catch (err) {
435
+ console.error('Error in timer-controller.js/getWebRtcMultiaddr(): ', err)
436
+
437
+ // Note: Do not throw an error. This is a top-level function.
438
+ return false
439
+ }
440
+ }
408
441
  }
409
442
 
410
443
  export default TimerControllers
@@ -5,6 +5,7 @@
5
5
  // Global npm libraries
6
6
  import { multiaddr } from '@multiformats/multiaddr'
7
7
  import globalConfig from '../../config/global-config.js'
8
+ import { WebRTC } from '@multiformats/multiaddr-matcher'
8
9
 
9
10
  // Local libraries
10
11
  import Util from '../util/utils.js'
@@ -32,7 +33,6 @@ class PeerUseCases {
32
33
 
33
34
  // Bind 'this' object to all subfunctions.
34
35
  this.updateThisNode = this.updateThisNode.bind(this)
35
- this.updateThisNode = this.updateThisNode.bind(this)
36
36
  this.connectToPeer = this.connectToPeer.bind(this)
37
37
  this.sendPrivateMessage = this.sendPrivateMessage.bind(this)
38
38
  this.addSubnetPeer = this.addSubnetPeer.bind(this)
@@ -41,6 +41,9 @@ class PeerUseCases {
41
41
  this.queryAbout = this.queryAbout.bind(this)
42
42
  this.sendRPC = this.sendRPC.bind(this)
43
43
  this.relayMetricsHandler = this.relayMetricsHandler.bind(this)
44
+ this.getWebRtcMultiaddr = this.getWebRtcMultiaddr.bind(this)
45
+ this.refreshPeerConnections = this.refreshPeerConnections.bind(this)
46
+ this.getMultiaddrs = this.getMultiaddrs.bind(this)
44
47
 
45
48
  // Inject the relayMetricsHandler function into the pubsub adapter.
46
49
  this.adapters.pubsub.injectMetricsHandler(this.relayMetricsHandler)
@@ -315,7 +318,7 @@ class PeerUseCases {
315
318
  // console.log('this.thisNode: ', this.thisNode)
316
319
 
317
320
  // const relays = this.cr.state.relays
318
- const relays = this.thisNode.relayData
321
+ // const relays = this.thisNode.relayData
319
322
  const peers = this.thisNode.peerList
320
323
  // console.log('peers: ', peers)
321
324
 
@@ -387,7 +390,7 @@ class PeerUseCases {
387
390
  for (let j = 0; j < filteredMultiaddrs.length; j++) {
388
391
  const multiaddr = filteredMultiaddrs[j]
389
392
  this.adapters.log.statusLog(1,
390
- `Trying a direct connecto to peer ${thisPeer} with this multiaddr: ${multiaddr}.`
393
+ `Trying a direct connection to peer ${thisPeer} with this multiaddr: ${multiaddr}.`
391
394
  )
392
395
 
393
396
  // Attempt to connect to the node through a circuit relay.
@@ -426,6 +429,49 @@ class PeerUseCases {
426
429
 
427
430
  // Skip this section if the peer has a preference for direct connection.
428
431
  if (connectPref !== 'direct') {
432
+ // Retrieve the webRTC multiaddrs from the peers announcement object.
433
+ const filteredMultiaddrs = peerData.data.ipfsMultiaddrs.filter(x => x.includes('/p2p-circuit/'))
434
+ this.adapters.log.statusLog(1, 'webRTC filteredMultiaddrs: ', filteredMultiaddrs)
435
+
436
+ // If the peer has no webRTC multiaddrs, then skip this peer
437
+ if (filteredMultiaddrs.length === 0) {
438
+ this.adapters.log.statusLog(1, `Peer ${thisPeer} has no webRTC multiaddrs. Skipping.`)
439
+ continue
440
+ }
441
+
442
+ // Loop through each webRTC multiaddr and attempt to connect to the peer.
443
+ for (let j = 0; j < filteredMultiaddrs.length; j++) {
444
+ const multiaddr = filteredMultiaddrs[j]
445
+ this.adapters.log.statusLog(1, `Trying to connect to peer ${thisPeer} through webRTC multiaddr: ${multiaddr}.`)
446
+
447
+ connected = await this.adapters.ipfs.connectToPeer({ multiaddr })
448
+
449
+ // If the connection was successful, break out of the relay loop.
450
+ // Otherwise try to connect through the next relay.
451
+ if (connected.success) {
452
+ this.adapters.log.statusLog(1,
453
+ `Successfully connected to peer ${thisPeer} through CR connection: ${multiaddr}.`
454
+ )
455
+
456
+ // Add the connection multiaddr for this peer to the thisNode object.
457
+ this.updatePeerConnectionInfo({ thisPeer })
458
+
459
+ // Add the connection multiaddr to the peer, so that we can see
460
+ // exactly how we're connected to the peer.
461
+ const thisPeerData = this.thisNode.peerData.filter(x => x.from === thisPeer)
462
+ thisPeerData[0].data.connectionAddr = multiaddr
463
+ peerData.data.connectionAddr = multiaddr
464
+
465
+ // Break out of the loop once we've made a successful connection.
466
+ break
467
+ } else {
468
+ this.adapters.log.statusLog(1,
469
+ `Failed to connect to peer ${thisPeer} through CR connection: ${multiaddr}. Reason: ${connected.details}`
470
+ )
471
+ }
472
+ }
473
+
474
+ /*
429
475
  // Sort the Circuit Relays by the average of the aboutLatency
430
476
  // array. Connect to peers through the Relays with the lowest latencies
431
477
  // first.
@@ -483,6 +529,7 @@ class PeerUseCases {
483
529
  }
484
530
  }
485
531
  }
532
+ */
486
533
  }
487
534
 
488
535
  if (connected.success) {
@@ -636,6 +683,76 @@ class PeerUseCases {
636
683
  relayMetricsHandler (inData) {
637
684
  this.incomingData = inData
638
685
  }
686
+
687
+ // Extract any webRTC multiaddrs from the node's list of multiaddrs.
688
+ // Add them to the announcement object.
689
+ getWebRtcMultiaddr (inObj = {}) {
690
+ try {
691
+ // console.log('---->Entering getWebRtcMultiaddr() Use Case.<----')
692
+
693
+ const { thisNode } = inObj
694
+
695
+ // Get multiaddrs that can be used to connect to this node.
696
+ const addrs = this.adapters.ipfs.ipfs.libp2p.getMultiaddrs()
697
+
698
+ const webRtcAddrs = addrs.filter(x => WebRTC.matches(x))
699
+ // console.log('webRTC addrs: ', webRtcAddrs)
700
+
701
+ // If there are any webRTC multiaddrs, add them to the announcement object.
702
+ if (webRtcAddrs.length) {
703
+ // Remove any previous webRTC multiaddrs from the announcement object.
704
+ const existingMultiaddrs = thisNode.ipfsMultiaddrs
705
+ const noRtcAddrs = existingMultiaddrs.filter(x => !x.includes('/p2p-circuit/webrtc'))
706
+
707
+ // Add the new webRTC multiaddrs to the announcement object.
708
+ webRtcAddrs.forEach(x => {
709
+ noRtcAddrs.push(x.toString())
710
+ })
711
+
712
+ // Add the new webRTC multiaddrs to the announcement object.
713
+ thisNode.ipfsMultiaddrs = noRtcAddrs
714
+ // console.log(`Updated thisNode.ipfsMultiaddrs: ${JSON.stringify(thisNode.ipfsMultiaddrs, null, 2)}`)
715
+ }
716
+
717
+ // return webRtcAddrs
718
+ } catch (err) {
719
+ console.error('Error in peer-use-cases.js/getWebRtcMultiaddr(): ', err)
720
+ return false
721
+ }
722
+ }
723
+
724
+ // This function expects an array of multiaddrs. It removes any elements
725
+ // that contain /p2p-circuit/webrtc, retrieves any webRTC multiaddrs from
726
+ // the node, adds them to the array, and returns the updated array.
727
+ // This information is then used in the announcement object, to let other
728
+ // nodes know how to connect to this node using webRTC.
729
+ getMultiaddrs (inObj = {}) {
730
+ try {
731
+ // Get multiaddrs that can be used to connect to this node.
732
+ const addrs = this.adapters.ipfs.ipfs.libp2p.getMultiaddrs()
733
+ // console.log('addrs: ', addrs)
734
+
735
+ const addrStrs = addrs.map(x => x.toString())
736
+ // console.log('addrStrs: ', addrStrs)
737
+
738
+ // Filter out multiaddrs with a low chance of success
739
+ const filteredAddrs = addrStrs.filter(x => {
740
+ if (x.includes('127.0.0.1')) return false
741
+ if (x.includes('udp')) return false
742
+ if (x.includes('quic')) return false
743
+ if (x.includes('192.168.')) return false
744
+ if (x.includes('172.1') || x.includes('172.2') || x.includes('172.3')) return false
745
+ if (x.includes('/10.')) return false
746
+ return true
747
+ })
748
+ // console.log('filteredAddrs: ', filteredAddrs)
749
+
750
+ return filteredAddrs
751
+ } catch (err) {
752
+ console.error('Error in getMultiaddrs(): ', err)
753
+ return []
754
+ }
755
+ }
639
756
  }
640
757
 
641
758
  export default PeerUseCases
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helia-coord",
3
- "version": "1.6.0",
3
+ "version": "1.7.1",
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",
@@ -26,14 +26,17 @@
26
26
  "@istanbuljs/esm-loader-hook": "0.2.0",
27
27
  "@libp2p/circuit-relay-v2": "3.1.11",
28
28
  "@libp2p/identify": "3.0.18",
29
+ "@libp2p/ping": "2.0.18",
29
30
  "@libp2p/tcp": "10.0.18",
30
31
  "@libp2p/webrtc": "4.1.6",
31
32
  "@libp2p/websockets": "9.1.5",
32
33
  "@multiformats/multiaddr": "12.3.5",
34
+ "@multiformats/multiaddr-matcher": "1.6.0",
33
35
  "blockstore-fs": "2.0.2",
34
36
  "chai": "4.3.6",
35
37
  "cross-env": "7.0.3",
36
38
  "datastore-fs": "10.0.2",
39
+ "delay": "6.0.0",
37
40
  "helia": "5.2.0",
38
41
  "lodash.clonedeep": "4.5.0",
39
42
  "minimal-slp-wallet": "5.12.0",
@@ -22,7 +22,8 @@ class UseCasesMock {
22
22
  }
23
23
  this.peer = {
24
24
  addSubnetPeer: async () => {},
25
- refreshPeerConnections: async () => {}
25
+ refreshPeerConnections: async () => {},
26
+ getMultiaddrs: async () => {}
26
27
  }
27
28
  }
28
29
  }
@@ -434,31 +434,32 @@ describe('#Use-Cases-Peer', () => {
434
434
  assert.equal(result, true)
435
435
  })
436
436
 
437
- it('should refresh a connection', async () => {
438
- // await uut.createSelf({ type: 'node.js' })
439
- // Add a peer that is not in the list of connected peers.
440
- const ipfsId = 'QmbyYXKbnAmMbMGo8LRBZ58jYs58anqUzY1m4jxDmhDsje'
441
- uut.thisNode.peerList = [ipfsId]
442
- uut.thisNode.peerData = [{ from: ipfsId, data: {} }]
437
+ // 2/16/25 CT: Commented out to try new webRTC connection logic.
438
+ // it('should refresh a connection', async () => {
439
+ // // await uut.createSelf({ type: 'node.js' })
440
+ // // Add a peer that is not in the list of connected peers.
441
+ // const ipfsId = 'QmbyYXKbnAmMbMGo8LRBZ58jYs58anqUzY1m4jxDmhDsje'
442
+ // uut.thisNode.peerList = [ipfsId]
443
+ // uut.thisNode.peerData = [{ from: ipfsId, data: {} }]
443
444
 
444
- // Add a peer
445
- await uut.addSubnetPeer(mockData.announceObj)
445
+ // // Add a peer
446
+ // await uut.addSubnetPeer(mockData.announceObj)
446
447
 
447
- // Force circuit relay to be used.
448
- uut.thisNode.relayData = mockData.mockRelayData
448
+ // // Force circuit relay to be used.
449
+ // uut.thisNode.relayData = mockData.mockRelayData
449
450
 
450
- // Mock dependencies
451
- sandbox.stub(uut.adapters.ipfs, 'getPeers').resolves(mockData.swarmPeers)
452
- sandbox.stub(uut.adapters.ipfs, 'connectToPeer').resolves(true)
453
- sandbox.stub(uut, 'isFreshPeer').returns(true)
454
- sandbox.stub(uut.utils, 'filterMultiaddrs').returns([])
455
- sandbox.stub(uut.relayUseCases, 'sortRelays').returns(mockData.mockRelayData)
451
+ // // Mock dependencies
452
+ // sandbox.stub(uut.adapters.ipfs, 'getPeers').resolves(mockData.swarmPeers)
453
+ // sandbox.stub(uut.adapters.ipfs, 'connectToPeer').resolves(true)
454
+ // sandbox.stub(uut, 'isFreshPeer').returns(true)
455
+ // sandbox.stub(uut.utils, 'filterMultiaddrs').returns([])
456
+ // sandbox.stub(uut.relayUseCases, 'sortRelays').returns(mockData.mockRelayData)
456
457
 
457
- // Connect to that peer.
458
- const result = await uut.refreshPeerConnections()
458
+ // // Connect to that peer.
459
+ // const result = await uut.refreshPeerConnections()
459
460
 
460
- assert.equal(result, true)
461
- })
461
+ // assert.equal(result, true)
462
+ // })
462
463
 
463
464
  it('should connect directly to circuit relays advertised IP and port', async () => {
464
465
  // Add a circuit relay peer with advertised IP and port.
@@ -520,62 +521,64 @@ describe('#Use-Cases-Peer', () => {
520
521
  assert.equal(result, true)
521
522
  })
522
523
 
523
- it('should report connection errors when connecting directly to IPFS peers multiaddr', async () => {
524
- // Add a circuit relay peer with advertised IP and port.
525
- const ipfsId = 'QmbyYXKbnAmMbMGo8LRBZ58jYs58anqUzY1m4jxDmhDsje'
526
- uut.thisNode.peerList = [ipfsId]
527
- uut.thisNode.peerData = [{ from: ipfsId, data: {} }]
528
-
529
- // Add a peer
530
- await uut.addSubnetPeer(mockData.announceObj)
531
-
532
- // Force circuit relay to be used.
533
- uut.thisNode.relayData = mockData.mockRelayData
534
-
535
- // Mock dependencies
536
- sandbox.stub(uut.adapters.ipfs, 'getPeers').resolves(mockData.swarmPeers)
537
- sandbox.stub(uut, 'isFreshPeer').returns(true)
538
- sandbox.stub(uut.adapters.ipfs, 'connectToPeer')
539
- .onCall(0).resolves({ success: false })
540
- .onCall(1).resolves({ sucdess: true })
541
- sandbox.stub(uut.utils, 'filterMultiaddrs').returns(['/ip4/123.45.6.7/p2p/ipfs-id'])
542
- sandbox.stub(uut.relayUseCases, 'sortRelays').returns(mockData.mockRelayData)
543
-
544
- // Connect to that peer.
545
- const result = await uut.refreshPeerConnections()
546
-
547
- assert.equal(result, true)
548
- })
549
-
550
- it('should connect through v2 Circuit Relay', async () => {
551
- // Add a circuit relay peer with advertised IP and port.
552
- const ipfsId = 'QmbyYXKbnAmMbMGo8LRBZ58jYs58anqUzY1m4jxDmhDsje'
553
- uut.thisNode.peerList = [ipfsId]
554
- uut.thisNode.peerData = [{ from: ipfsId, data: {} }]
555
-
556
- // Add a peer
557
- await uut.addSubnetPeer(mockData.announceObj)
558
-
559
- // Force circuit relay to be used.
560
- uut.thisNode.relayData = mockData.mockRelayData
561
-
562
- // Mock dependencies
563
- sandbox.stub(uut.adapters.ipfs, 'getPeers').resolves(mockData.swarmPeers)
564
- sandbox.stub(uut, 'isFreshPeer').returns(true)
565
- sandbox.stub(uut.adapters.ipfs, 'connectToPeer')
566
- .onCall(0).resolves({ success: true })
567
- sandbox.stub(uut.utils, 'filterMultiaddrs').returns([])
568
- sandbox.stub(uut.relayUseCases, 'sortRelays').returns([{
569
- multiaddr: '/ip4/123.45.6.7/p2p/ipfs-id',
570
- connected: true
571
- }])
572
- sandbox.stub(uut, 'updatePeerConnectionInfo').returns()
573
-
574
- // Connect to that peer.
575
- const result = await uut.refreshPeerConnections()
576
-
577
- assert.equal(result, true)
578
- })
524
+ // 2/16/25 CT: Commented out to try new webRTC connection logic.
525
+ // it('should report connection errors when connecting directly to IPFS peers multiaddr', async () => {
526
+ // // Add a circuit relay peer with advertised IP and port.
527
+ // const ipfsId = 'QmbyYXKbnAmMbMGo8LRBZ58jYs58anqUzY1m4jxDmhDsje'
528
+ // uut.thisNode.peerList = [ipfsId]
529
+ // uut.thisNode.peerData = [{ from: ipfsId, data: {} }]
530
+
531
+ // // Add a peer
532
+ // await uut.addSubnetPeer(mockData.announceObj)
533
+
534
+ // // Force circuit relay to be used.
535
+ // uut.thisNode.relayData = mockData.mockRelayData
536
+
537
+ // // Mock dependencies
538
+ // sandbox.stub(uut.adapters.ipfs, 'getPeers').resolves(mockData.swarmPeers)
539
+ // sandbox.stub(uut, 'isFreshPeer').returns(true)
540
+ // sandbox.stub(uut.adapters.ipfs, 'connectToPeer')
541
+ // .onCall(0).resolves({ success: false })
542
+ // .onCall(1).resolves({ sucdess: true })
543
+ // sandbox.stub(uut.utils, 'filterMultiaddrs').returns(['/ip4/123.45.6.7/p2p/ipfs-id'])
544
+ // sandbox.stub(uut.relayUseCases, 'sortRelays').returns(mockData.mockRelayData)
545
+
546
+ // // Connect to that peer.
547
+ // const result = await uut.refreshPeerConnections()
548
+
549
+ // assert.equal(result, true)
550
+ // })
551
+
552
+ // 2/16/25 CT: Commented out to try new webRTC connection logic.
553
+ // it('should connect through v2 Circuit Relay', async () => {
554
+ // // Add a circuit relay peer with advertised IP and port.
555
+ // const ipfsId = 'QmbyYXKbnAmMbMGo8LRBZ58jYs58anqUzY1m4jxDmhDsje'
556
+ // uut.thisNode.peerList = [ipfsId]
557
+ // uut.thisNode.peerData = [{ from: ipfsId, data: {} }]
558
+
559
+ // // Add a peer
560
+ // await uut.addSubnetPeer(mockData.announceObj)
561
+
562
+ // // Force circuit relay to be used.
563
+ // uut.thisNode.relayData = mockData.mockRelayData
564
+
565
+ // // Mock dependencies
566
+ // sandbox.stub(uut.adapters.ipfs, 'getPeers').resolves(mockData.swarmPeers)
567
+ // sandbox.stub(uut, 'isFreshPeer').returns(true)
568
+ // sandbox.stub(uut.adapters.ipfs, 'connectToPeer')
569
+ // .onCall(0).resolves({ success: true })
570
+ // sandbox.stub(uut.utils, 'filterMultiaddrs').returns([])
571
+ // sandbox.stub(uut.relayUseCases, 'sortRelays').returns([{
572
+ // multiaddr: '/ip4/123.45.6.7/p2p/ipfs-id',
573
+ // connected: true
574
+ // }])
575
+ // sandbox.stub(uut, 'updatePeerConnectionInfo').returns()
576
+
577
+ // // Connect to that peer.
578
+ // const result = await uut.refreshPeerConnections()
579
+
580
+ // assert.equal(result, true)
581
+ // })
579
582
 
580
583
  it('should skip if peer is stale', async () => {
581
584
  // Add a peer that is not in the list of connected peers.