helia-coord 1.9.7 → 2.0.0

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.
@@ -6,6 +6,8 @@
6
6
 
7
7
  // Global npm libraries
8
8
  import { createHelia } from 'helia'
9
+ import { libp2pRouting } from '@helia/routers'
10
+ import { bitswap } from '@helia/block-brokers'
9
11
  import fs from 'fs'
10
12
  import { FsBlockstore } from 'blockstore-fs'
11
13
  import { FsDatastore } from 'datastore-fs'
@@ -13,9 +15,10 @@ import { createLibp2p } from 'libp2p'
13
15
  import { tcp } from '@libp2p/tcp'
14
16
  import { noise } from '@chainsafe/libp2p-noise'
15
17
  import { yamux } from '@chainsafe/libp2p-yamux'
16
- // import { bootstrap } from '@libp2p/bootstrap'
18
+ import { bootstrap } from '@libp2p/bootstrap'
17
19
  // import { identifyService } from 'libp2p/identify'
18
20
  import { identify } from '@libp2p/identify'
21
+ import { kadDHT } from '@libp2p/kad-dht'
19
22
  // import { circuitRelayTransport } from 'libp2p/circuit-relay'
20
23
  import { circuitRelayTransport } from '@libp2p/circuit-relay-v2'
21
24
  import { gossipsub } from '@chainsafe/libp2p-gossipsub'
@@ -81,7 +84,11 @@ class CreateHeliaNode {
81
84
  // Configure services
82
85
  const services = {
83
86
  identify: identify(),
84
- pubsub: gossipsub({ allowPublishToZeroTopicPeers: true })
87
+ pubsub: gossipsub({ allowPublishToZeroTopicPeers: true }),
88
+ dht: kadDHT({
89
+ protocol: '/psf/kad/1.0.0',
90
+ clientMode: false
91
+ })
85
92
  }
86
93
 
87
94
  // libp2p is the networking layer that underpins Helia
@@ -108,6 +115,15 @@ class CreateHeliaNode {
108
115
  streamMuxers: [
109
116
  yamux()
110
117
  ],
118
+ peerDiscovery: [
119
+ bootstrap({
120
+ list: [
121
+ '/ip4/78.46.129.7/tcp/4001/p2p/12D3KooWEBzgK8a5TpMfLotuj7jJnEK41gbD9LZK6qCpxNrX43E9',
122
+ '/ip4/5.78.70.29/tcp/4001/p2p/12D3KooWSREJ6x2DJSYrA1xRD2Qs6D4DHncsHmNnTHuMKHnqpG2i',
123
+ '/ip4/143.198.134.59/tcp/4101/p2p/12D3KooWHogx3RcyNiSuY6SzS8GxzTAqtDCYzy17yQ1StWaLvX9j'
124
+ ]
125
+ })
126
+ ],
111
127
  services
112
128
  })
113
129
 
@@ -115,7 +131,13 @@ class CreateHeliaNode {
115
131
  const helia = await createHelia({
116
132
  blockstore,
117
133
  datastore,
118
- libp2p
134
+ libp2p,
135
+ routers: [
136
+ libp2pRouting(libp2p)
137
+ ],
138
+ blockBrokers: [
139
+ bitswap()
140
+ ]
119
141
  })
120
142
 
121
143
  return helia
@@ -10,6 +10,14 @@ import SlpWallet from 'minimal-slp-wallet'
10
10
  import IpfsCoord from '../index.js'
11
11
  import CreateHeliaNode from './create-helia-node.js'
12
12
 
13
+ // Catch uncaught errors from libp2p internals to prevent crashes.
14
+ process.on('uncaughtException', (err) => {
15
+ console.error('uncaughtException: ', err)
16
+ })
17
+ process.on('unhandledRejection', (err) => {
18
+ console.error('unhandledRejection: ', err)
19
+ })
20
+
13
21
  async function start () {
14
22
  try {
15
23
  // Create an instance of bch-js and IPFS.
@@ -26,7 +34,7 @@ async function start () {
26
34
  type: 'node.js',
27
35
  // type: 'browser'
28
36
  nodeType: 'external',
29
- debugLevel: 3
37
+ debugLevel: 2
30
38
  })
31
39
 
32
40
  await ipfsCoord.start()
@@ -0,0 +1,263 @@
1
+ /*
2
+ Production-grade Helia IPFS node factory.
3
+ This library creates and configures a Helia node with libp2p, including:
4
+ - KAD-DHT with custom protocol for private PSF network
5
+ - Bootstrap peer discovery
6
+ - Circuit relay transport and optional circuit relay server
7
+ - Persistent identity via keychain
8
+ - Content routing (libp2pRouting + bitswap)
9
+ - UnixFS file system
10
+
11
+ This is the canonical node configuration used by ipfs-service-provider
12
+ and other consumers of helia-coord.
13
+ */
14
+
15
+ // Global npm libraries
16
+ import { createHelia } from 'helia'
17
+ import { libp2pRouting } from '@helia/routers'
18
+ import { bitswap } from '@helia/block-brokers'
19
+ import fs from 'fs'
20
+ import { FsBlockstore } from 'blockstore-fs'
21
+ import { FsDatastore } from 'datastore-fs'
22
+ import { createLibp2p } from 'libp2p'
23
+ import { tcp } from '@libp2p/tcp'
24
+ import { noise } from '@chainsafe/libp2p-noise'
25
+ import { yamux } from '@chainsafe/libp2p-yamux'
26
+ import { bootstrap } from '@libp2p/bootstrap'
27
+ import { identify, identifyPush } from '@libp2p/identify'
28
+ import { kadDHT } from '@libp2p/kad-dht'
29
+ import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2'
30
+ import { gossipsub } from '@chainsafe/libp2p-gossipsub'
31
+ import { webSockets } from '@libp2p/websockets'
32
+ import { publicIpv4 } from 'public-ip'
33
+ import { multiaddr } from '@multiformats/multiaddr'
34
+ import { webRTC, webRTCDirect } from '@libp2p/webrtc'
35
+ import { keychain } from '@libp2p/keychain'
36
+ import { unixfs } from '@helia/unixfs'
37
+ import { ping } from '@libp2p/ping'
38
+ import { loadOrCreateSelfKey } from '@libp2p/config'
39
+
40
+ // Default PSF bootstrap peers
41
+ const DEFAULT_BOOTSTRAP_PEERS = [
42
+ '/ip4/78.46.129.7/tcp/4001/p2p/12D3KooWEBzgK8a5TpMfLotuj7jJnEK41gbD9LZK6qCpxNrX43E9',
43
+ '/ip4/5.78.70.29/tcp/4001/p2p/12D3KooWSREJ6x2DJSYrA1xRD2Qs6D4DHncsHmNnTHuMKHnqpG2i',
44
+ '/ip4/143.198.134.59/tcp/4101/p2p/12D3KooWHogx3RcyNiSuY6SzS8GxzTAqtDCYzy17yQ1StWaLvX9j'
45
+ ]
46
+
47
+ class CreateHeliaNode {
48
+ constructor (localConfig = {}) {
49
+ // Configurable options with production defaults
50
+ this.ipfsDir = localConfig.ipfsDir || './.ipfsdata/ipfs'
51
+ this.tcpPort = localConfig.tcpPort || 4001
52
+ this.wsPort = localConfig.wsPort || 4003
53
+ this.isCircuitRelay = localConfig.isCircuitRelay || false
54
+ this.bootstrapPeers = localConfig.bootstrapPeers || DEFAULT_BOOTSTRAP_PEERS
55
+
56
+ // getSeed is an async function that returns a seed string for keychain.
57
+ // If not provided, a random seed is generated.
58
+ if (localConfig.getSeed) {
59
+ this.getSeed = localConfig.getSeed
60
+ } else {
61
+ this.getSeed = async () => {
62
+ const seedNum = Math.floor(Math.random() * 1000000000000000000000)
63
+ return seedNum.toString()
64
+ }
65
+ }
66
+
67
+ // Encapsulate dependencies for testing/override
68
+ this.fs = fs
69
+ this.createLibp2p = createLibp2p
70
+ this.createHelia = createHelia
71
+ this.publicIp = publicIpv4
72
+ this.multiaddr = multiaddr
73
+
74
+ // Properties of this class instance
75
+ this.isReady = false
76
+
77
+ // Bind 'this' object to all subfunctions
78
+ this.start = this.start.bind(this)
79
+ this.createNode = this.createNode.bind(this)
80
+ this.stop = this.stop.bind(this)
81
+ this.ensureBlocksDir = this.ensureBlocksDir.bind(this)
82
+ }
83
+
84
+ // Start an IPFS node.
85
+ async start () {
86
+ try {
87
+ // Ensure the directory structure exists that is needed by the IPFS node to store data.
88
+ this.ensureBlocksDir()
89
+
90
+ // Create an IPFS node
91
+ const ipfs = await this.createNode()
92
+
93
+ this.id = ipfs.libp2p.peerId.toString()
94
+ console.log('IPFS ID: ', this.id)
95
+
96
+ // Attempt to guess our ip4 IP address.
97
+ const ip4 = await this.publicIp()
98
+ let detectedMultiaddr = `/ip4/${ip4}/tcp/${this.tcpPort}/p2p/${this.id}`
99
+ detectedMultiaddr = this.multiaddr(detectedMultiaddr)
100
+
101
+ // Get the multiaddrs for the node.
102
+ const multiaddrs = ipfs.libp2p.getMultiaddrs()
103
+ multiaddrs.push(detectedMultiaddr)
104
+ console.log('Multiaddrs: ', multiaddrs)
105
+
106
+ this.multiaddrs = multiaddrs
107
+
108
+ // Signal that this adapter is ready.
109
+ this.isReady = true
110
+
111
+ this.ipfs = ipfs
112
+
113
+ return this.ipfs
114
+ } catch (err) {
115
+ console.error('Error in create-helia-node.js/start()')
116
+ throw err
117
+ }
118
+ }
119
+
120
+ // This function creates an IPFS node using Helia.
121
+ // It returns the node as an object.
122
+ async createNode () {
123
+ try {
124
+ const ipfsDir = this.ipfsDir
125
+
126
+ // Create block and data stores.
127
+ const blockstore = new FsBlockstore(`${ipfsDir}/blockstore`)
128
+ const datastore = new FsDatastore(`${ipfsDir}/datastore`)
129
+
130
+ // Create an identity
131
+ const keychainInit = {
132
+ selfKey: 'myKey',
133
+ pass: await this.getSeed()
134
+ }
135
+ const privateKey = await loadOrCreateSelfKey(datastore, keychainInit)
136
+
137
+ // Configure services
138
+ const services = {
139
+ identify: identify(),
140
+ identifyPush: identifyPush(),
141
+ pubsub: gossipsub({ allowPublishToZeroTopicPeers: true }),
142
+ ping: ping(),
143
+ keychain: keychain(keychainInit),
144
+ dht: kadDHT({
145
+ protocol: '/psf/kad/1.0.0',
146
+ clientMode: false
147
+ })
148
+ }
149
+
150
+ // Conditionally add circuit relay server
151
+ if (this.isCircuitRelay) {
152
+ console.log('Helia (IPFS) node IS configured as Circuit Relay')
153
+ services.relay = circuitRelayServer({
154
+ hopTimeout: 30 * 1000,
155
+ reservations: {
156
+ maxReservations: 15,
157
+ reservationClearInterval: 300 * 1000,
158
+ applyDefaultLimit: true,
159
+ defaultDurationLimit: 2 * 60 * 1000,
160
+ defaultDataLimit: BigInt(2 << 7)
161
+ },
162
+ maxInboundHopStreams: 32,
163
+ maxOutboundHopStreams: 64
164
+ })
165
+ } else {
166
+ console.log('Helia (IPFS) node IS NOT configured as Circuit Relay')
167
+ }
168
+
169
+ const transports = [
170
+ tcp(),
171
+ webSockets(),
172
+ circuitRelayTransport({
173
+ discoverRelays: 3,
174
+ reservationConcurrency: 3
175
+ }),
176
+ webRTC(),
177
+ webRTCDirect()
178
+ ]
179
+
180
+ // libp2p is the networking layer that underpins Helia
181
+ const libp2p = await this.createLibp2p({
182
+ privateKey,
183
+ datastore,
184
+ addresses: {
185
+ listen: [
186
+ '/ip4/127.0.0.1/tcp/0',
187
+ `/ip4/0.0.0.0/tcp/${this.tcpPort}`,
188
+ `/ip4/0.0.0.0/tcp/${this.wsPort}/ws`,
189
+ '/webrtc',
190
+ '/p2p-circuit'
191
+ ]
192
+ },
193
+ transports,
194
+ connectionEncrypters: [
195
+ noise()
196
+ ],
197
+ streamMuxers: [
198
+ yamux()
199
+ ],
200
+ peerDiscovery: [
201
+ bootstrap({
202
+ list: this.bootstrapPeers
203
+ })
204
+ ],
205
+ services
206
+ })
207
+
208
+ // create a Helia node
209
+ const helia = await this.createHelia({
210
+ blockstore,
211
+ datastore,
212
+ libp2p,
213
+ routers: [
214
+ libp2pRouting(libp2p)
215
+ ],
216
+ blockBrokers: [
217
+ bitswap()
218
+ ]
219
+ })
220
+
221
+ // Attach IPFS file system.
222
+ const heliaFs = unixfs(helia)
223
+ helia.fs = heliaFs
224
+
225
+ return helia
226
+ } catch (err) {
227
+ console.error('Error creating Helia node: ', err)
228
+ throw err
229
+ }
230
+ }
231
+
232
+ async stop () {
233
+ await this.ipfs.stop()
234
+
235
+ return true
236
+ }
237
+
238
+ // Ensure that the directories exist to store blocks from the IPFS network.
239
+ // This function is called at startup, before the IPFS node is started.
240
+ ensureBlocksDir () {
241
+ try {
242
+ const ipfsDir = this.ipfsDir
243
+ const parentDir = ipfsDir.substring(0, ipfsDir.lastIndexOf('/'))
244
+
245
+ !this.fs.existsSync(parentDir) && this.fs.mkdirSync(parentDir, { recursive: true })
246
+
247
+ !this.fs.existsSync(ipfsDir) && this.fs.mkdirSync(ipfsDir, { recursive: true })
248
+
249
+ !this.fs.existsSync(`${ipfsDir}/blockstore`) && this.fs.mkdirSync(`${ipfsDir}/blockstore`)
250
+
251
+ !this.fs.existsSync(`${ipfsDir}/datastore`) && this.fs.mkdirSync(`${ipfsDir}/datastore`)
252
+
253
+ !this.fs.existsSync(`${ipfsDir}/datastore/pkcs8`) && this.fs.mkdirSync(`${ipfsDir}/datastore/pkcs8`)
254
+
255
+ return true
256
+ } catch (err) {
257
+ console.error('Error in create-helia-node.js/ensureBlocksDir(): ', err)
258
+ throw err
259
+ }
260
+ }
261
+ }
262
+
263
+ export default CreateHeliaNode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helia-coord",
3
- "version": "1.9.7",
3
+ "version": "2.0.0",
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",
@@ -20,48 +20,53 @@
20
20
  },
21
21
  "homepage": "https://github.com/Permissionless-Software-Foundation/helia-coord#readme",
22
22
  "devDependencies": {
23
- "@chainsafe/libp2p-gossipsub": "14.1.0",
24
- "@chainsafe/libp2p-noise": "16.0.1",
25
- "@chainsafe/libp2p-yamux": "7.0.1",
26
23
  "@istanbuljs/esm-loader-hook": "0.2.0",
27
- "@libp2p/circuit-relay-v2": "3.2.2",
28
- "@libp2p/identify": "3.0.22",
29
- "@libp2p/ping": "2.0.22",
30
- "@libp2p/tcp": "10.1.2",
31
- "@libp2p/webrtc": "5.2.2",
32
- "@libp2p/websockets": "9.2.2",
33
- "@multiformats/multiaddr": "12.3.5",
34
- "blockstore-fs": "2.0.2",
35
24
  "chai": "4.3.6",
36
25
  "cross-env": "7.0.3",
37
- "datastore-fs": "10.0.2",
38
26
  "delay": "6.0.0",
39
- "helia": "5.2.1",
40
- "libp2p": "2.7.2",
41
27
  "lodash.clonedeep": "4.5.0",
42
28
  "minimal-slp-wallet": "7.1.4",
43
29
  "mocha": "10.0.0",
44
30
  "nyc": "15.1.0",
45
- "public-ip": "6.0.1",
46
31
  "sinon": "14.0.0",
47
32
  "standard": "17.0.0"
48
33
  },
49
34
  "overrides": {
50
35
  "@multiformats/multiaddr-to-uri": "10.1.2",
51
36
  "@multiformats/multiaddr-matcher": "1.6.0",
37
+ "@libp2p/utils": "6.7.1",
52
38
  "helia": {
53
39
  "libp2p": "2.7.2",
54
40
  "@libp2p/upnp-nat": "3.0.0"
55
41
  }
56
42
  },
57
43
  "dependencies": {
44
+ "@chainsafe/libp2p-gossipsub": "14.1.0",
45
+ "@chainsafe/libp2p-noise": "16.0.1",
46
+ "@chainsafe/libp2p-yamux": "7.0.1",
58
47
  "@chris.troutner/retry-queue": "1.0.10",
48
+ "@helia/block-brokers": "4.0.4",
49
+ "@helia/routers": "3.0.0",
50
+ "@helia/unixfs": "4.0.2",
51
+ "@libp2p/bootstrap": "11.0.20",
52
+ "@libp2p/circuit-relay-v2": "3.2.2",
53
+ "@libp2p/config": "1.1.0",
54
+ "@libp2p/identify": "3.0.22",
55
+ "@libp2p/kad-dht": "14.2.3",
56
+ "@libp2p/keychain": "5.0.14",
57
+ "@libp2p/ping": "2.0.22",
58
+ "@libp2p/tcp": "10.1.2",
59
+ "@libp2p/webrtc": "5.2.2",
60
+ "@libp2p/websockets": "9.2.2",
61
+ "@multiformats/multiaddr": "12.3.5",
59
62
  "bch-encrypt-lib": "2.1.1",
63
+ "blockstore-fs": "2.0.2",
64
+ "datastore-fs": "10.0.2",
65
+ "helia": "5.2.1",
66
+ "libp2p": "2.7.2",
67
+ "public-ip": "6.0.1",
60
68
  "uuid": "9.0.0"
61
69
  },
62
- "peerDependencies": {
63
- "libp2p": "=2.7.2"
64
- },
65
70
  "exports": {
66
71
  ".": {
67
72
  "import": {
@@ -72,6 +77,14 @@
72
77
  "require": {
73
78
  "default": "./index.js"
74
79
  }
80
+ },
81
+ "./create-helia-node": {
82
+ "import": {
83
+ "default": "./lib/create-helia-node.js"
84
+ },
85
+ "require": {
86
+ "default": "./lib/create-helia-node.js"
87
+ }
75
88
  }
76
89
  }
77
90
  }