helia-coord 1.1.3 → 1.2.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.
@@ -0,0 +1,13 @@
1
+ /*
2
+ Config file for storing global configuration settings.
3
+ */
4
+
5
+ const config = {
6
+ DEFAULT_COORDINATION_ROOM: 'psf-ipfs-coordination-002',
7
+ BCH_COINJOIN_ROOM: 'bch-coinjoin-001',
8
+
9
+ // Time between retrying private messages to a peer.
10
+ TIME_BETWEEN_RETRIES: 5000
11
+ }
12
+
13
+ export default config
@@ -0,0 +1,33 @@
1
+ # Theory of Operation
2
+
3
+ This document provides a high-level overview of what the helia-coord library does.
4
+ Helia-coord is middleware that controls a Helia IPFS node. It is used to form an on-the-fly, self-healing mesh network of other IPFS nodes. The nodes communicate over *pubsub channels*, and state is managed by *interval timers*.
5
+
6
+ ## Pubsub Channels
7
+
8
+ A series of pubsub channels are opened between the IPFS nodes on the network.
9
+
10
+ ### Coordination Channel
11
+ The coordination channel is a public, unencrypted channel that each node subscribes to. Every two minutes, a node will broadcast an 'announcement object', which contains information about the node and how to connect to it. This includes:
12
+
13
+ - The nodes IPFS ID
14
+ - Multiaddrs that can be used for other nodes to connect to it directly.
15
+ - The nodes public encryption key so that other nodes can send it e2ee messages.
16
+ - The nodes BCH and SLP addresses so that other nodes can send it payments and tokens.
17
+ - Metadata that describes what services the node offers and what versions it runs.
18
+
19
+ ### CoinJoin Channel
20
+ This channel is not fully developed. It's another public, unencrypted channel that will be used for nodes to coordinate a CoinJoin transaction for achieving financial privacy. This channel can be ignored for now.
21
+
22
+ ### This Nodes Private Channel
23
+ A pubsub channel is created using the nodes IPFS ID. Other nodes on the network will subscribe to this channel in order to send it encrypted messages. This channel is only used for receiving messages. The node never broadcasts messages on this channel.
24
+
25
+ The helia-coord library is consumed by higher-level software like [the pay-to-write database (P2WDB)](https://p2wdb.com), and different software comprising [the Cash Stack](https://cashstack.info). Once RPC commands received on this channel are decrypted, they are passed up to the consuming software via the `privateLog` object.
26
+
27
+ There are some low-level messages that are not passed up to the consuming software. These are ACK (acknowledge) and metric commands. The metrics commands are primarily used to measure and track latency between nodes and Circuit Relays. The helia-coord library will adjust its connection to achieve the lowest latency connections to other nodes on the network.
28
+
29
+ ### Other Nodes Private Channel
30
+ When a new node is discovered via its announcement on the *Coordination Channel*, the node will subscribe to that nodes private channel. The node will use this channel to send encrypted RPC commands to other nodes. This channel is only used for broadcasting. It is never used for receiving messages.
31
+
32
+ ## Interval Timers
33
+ A series of interval timers are defined in the `lib/controllser/timer-controller.js` file. These timers are periodically triggered in order to maintain the nodes state. They renew broken connections to other nodes, track latency between the node and Circuit Relays that it knows about, and other operations. These time-based function calls are what allow the node to create the mesh network on-the-fly and the self-heal the network as nodes come online or drop off the network.
@@ -23,15 +23,6 @@ class BchAdapter {
23
23
  // derivation path of 245 if a 12 word mnemonic is provided.
24
24
  async generateBchId () {
25
25
  try {
26
- // Generate a 12-word mnemonic, if one isn't provided.
27
- // if (!this.mnemonic) {
28
- // this.mnemonic = this.bchjs.Mnemonic.generate(
29
- // 128,
30
- // this.bchjs.Mnemonic.wordLists().english
31
- // )
32
- // }
33
- // console.log(`mnemonic: ${mnemonic}`)
34
-
35
26
  // root seed buffer
36
27
  const rootSeed = await this.bchjs.Mnemonic.toSeed(this.mnemonic)
37
28
 
@@ -64,15 +55,6 @@ class BchAdapter {
64
55
 
65
56
  async generatePrivateKey () {
66
57
  try {
67
- // Generate a 12-word mnemonic, if one isn't provided.
68
- // if (!this.mnemonic) {
69
- // this.mnemonic = this.bchjs.Mnemonic.generate(
70
- // 128,
71
- // this.bchjs.Mnemonic.wordLists().english
72
- // )
73
- // }
74
- // console.log(`mnemonic: ${mnemonic}`)
75
-
76
58
  // root seed buffer
77
59
  const rootSeed = await this.bchjs.Mnemonic.toSeed(this.mnemonic)
78
60
 
@@ -3,7 +3,7 @@
3
3
  for existing encryption libraries.
4
4
  */
5
5
 
6
- import BchEncrypt from 'bch-encrypt-lib/index.js'
6
+ import BchEncrypt from 'bch-encrypt-lib'
7
7
 
8
8
  class EncryptionAdapter {
9
9
  constructor (localConfig = {}) {
@@ -24,8 +24,6 @@ class EncryptionAdapter {
24
24
  // Encapsulate dependencies
25
25
  this.bchjs = this.bch.bchjs // Copy of bch-js
26
26
  this.bchEncrypt = new BchEncrypt({ bchjs: this.bchjs })
27
- // this.ipfs = encryptConfig.ipfs
28
- // this.orbitdb = encryptConfig.orbitdb
29
27
  }
30
28
 
31
29
  // Decrypt incoming messages on the pubsub channel for this node.
@@ -52,10 +50,6 @@ class EncryptionAdapter {
52
50
  // Exit quietly if the issue is a 'Bad MAC'. This seems to be a startup
53
51
  // issue.
54
52
  if (err.message.includes('Bad MAC')) {
55
- // throw new Error(
56
- // 'Bad MAC. Could not decrypt message. Peer may have stale encryption data for this node.'
57
- // )
58
-
59
53
  this.log.statusLog(
60
54
  2,
61
55
  `Bad MAC. Could not decrypt message. Peer ${sender} may have stale encryption data for this node.`
@@ -92,44 +86,6 @@ class EncryptionAdapter {
92
86
  throw err
93
87
  }
94
88
  }
95
-
96
- // Send an e2e encrypted message to a peer.
97
- // async sendEncryptedMsg (peer, msg) {
98
- // try {
99
- // // console.log('sendEncryptedMsg peer: ', peer)
100
- // // console.log('sendEncryptedMsg msg: ', msg)
101
- //
102
- // // const channel = peer.ipfsId.toString()
103
- // const pubKey = peer.encryptPubKey
104
- // // const orbitdbId = peer.orbitdb
105
- //
106
- // const msgBuf = Buffer.from(msg, 'utf8').toString('hex')
107
- // // console.log(`msgBuf: ${msgBuf}`)
108
- //
109
- // const encryptedHexStr = await this.bchEncrypt.encryption.encryptFile(
110
- // pubKey,
111
- // msgBuf
112
- // )
113
- // console.log(`encryptedHexStr: ${encryptedHexStr}`)
114
- //
115
- // // const msgBuf2 = Buffer.from(encryptedHexStr, 'hex')
116
- //
117
- // // Publish the message to the pubsub channel.
118
- // // TODO: This will be deprecated in the future in favor of publishing to
119
- // // the peers OrbitDB.
120
- // // await this.ipfs.pubsub.publish(channel, msgBuf2)
121
- //
122
- // // if (orbitdbId) {
123
- // // console.log(
124
- // // `Ready to send encrypted message to peer ${channel} on orbitdb ID ${orbitdbId}`
125
- // // )
126
- // // await this.orbitdb.sendToDb(channel, encryptedHexStr, orbitdbId)
127
- // // }
128
- // } catch (err) {
129
- // console.error('Error in sendEncryptedMsg()')
130
- // throw err
131
- // }
132
- // }
133
89
  }
134
90
 
135
91
  // module.exports = EncryptionAdapter
@@ -4,8 +4,6 @@
4
4
  */
5
5
 
6
6
  // Public npm libraries
7
- // const EventEmitter = require('events')
8
- // EventEmitter.defaultMaxListeners = 200
9
7
 
10
8
  // Local libraries
11
9
  import LogsAdapter from './logs-adapter.js'
@@ -15,8 +13,6 @@ import EncryptionAdapter from './encryption-adapter.js'
15
13
  import PubsubAdapter from './pubsub-adapter/index.js'
16
14
  import Gist from './gist.js'
17
15
 
18
- // const Gist = require('./gist')
19
-
20
16
  class Adapters {
21
17
  constructor (localConfig = {}) {
22
18
  // Dependency injection
@@ -3,10 +3,6 @@
3
3
  know specifics about the IPFS API.
4
4
  */
5
5
 
6
- // The amount of time to wait to connect to a peer, in milliseconds.
7
- // Increasing the time makes the network slower but more resilient to latency.
8
- // Decreasing the time makes the network faster, but more smaller and more fragile.
9
- // import bootstapNodes from '../../config/bootstrap-circuit-relays.js'
10
6
  import { multiaddr } from '@multiformats/multiaddr'
11
7
 
12
8
  const CONNECTION_TIMEOUT = 10000
@@ -27,6 +23,12 @@ class IpfsAdapter {
27
23
  )
28
24
  }
29
25
 
26
+ // Bind 'this' object to all subfunctions
27
+ this.start = this.start.bind(this)
28
+ this.connectToPeer = this.connectToPeer.bind(this)
29
+ this.disconnectFromPeer = this.disconnectFromPeer.bind(this)
30
+ this.getPeers = this.getPeers.bind(this)
31
+
30
32
  // 'embedded' node type used as default, will use embedded js-ipfs.
31
33
  // Alternative is 'external' which will use ipfs-http-client to control an
32
34
  // external IPFS node.
@@ -59,93 +61,12 @@ class IpfsAdapter {
59
61
  this.ipfs = await this.ipfs
60
62
 
61
63
  // Get ID information about this IPFS node.
62
- // const id2 = await this.ipfs.id()
63
- // this.state.ipfsPeerId = id2.id
64
- // this.ipfsPeerId = id2.id
65
64
  this.ipfsPeerId = this.ipfs.libp2p.peerId.toString()
66
65
 
67
66
  // Get multiaddrs that can be used to connect to this node.
68
- // const addrs = id2.addresses.map(elem => elem.toString())
69
67
  let addrs = this.ipfs.libp2p.getMultiaddrs()
70
68
  addrs = addrs.map(elem => elem.toString())
71
- // this.state.ipfsMultiaddrs = addrs
72
69
  this.ipfsMultiaddrs = addrs
73
-
74
- // Remove bootstrap nodes, as we have our own bootstrap nodes, and the
75
- // the default ones can spam our nodes with a ton of bandwidth.
76
- // await this.ipfs.config.set('Bootstrap', [
77
- // bootstapNodes.node[0].multiaddr,
78
- // bootstapNodes.node[1].multiaddr
79
- // ])
80
-
81
- // Settings specific to embedded js-ipfs.
82
- // if (this.nodeType === 'embedded') {
83
- // // Also remove default Delegates, as they are the same as the default
84
- // // Bootstrap nodes.
85
- // await this.ipfs.config.set('Addresses.Delegates', [])
86
- // }
87
-
88
- // Settings specific to external go-ipfs node.
89
- // if (this.nodeType === 'external') {
90
- // // Enable RelayClient
91
- // // https://github.com/ipfs/go-ipfs/blob/master/docs/config.md#swarmrelayclient
92
- // // https://github.com/ipfs/go-ipfs/releases/tag/v0.11.0
93
- // await this.ipfs.config.set('Swarm.RelayClient.Enabled', true)
94
-
95
- // // Enable hole punching for better p2p interaction.
96
- // // https://github.com/ipfs/go-ipfs/blob/master/docs/config.md#swarmenableholepunching
97
- // await this.ipfs.config.set('Swarm.EnableHolePunching', true)
98
-
99
- // // Enable websocket connections
100
- // await this.ipfs.config.set('Addresses.Swarm', [
101
- // `/ip4/0.0.0.0/tcp/${this.tcpPort}`,
102
- // `/ip6/::/tcp/${this.tcpPort}`,
103
- // // `/ip4/0.0.0.0/udp/${this.tcpPort}/quic`,
104
- // // `/ip6/::/udp/${this.tcpPort}/quic`,
105
- // `/ip4/0.0.0.0/tcp/${this.wsPort}`, // Websockets
106
- // `/ip6/::/tcp/${this.wsPort}`
107
- // ])
108
-
109
- // // Disable scanning of IP ranges. This is largely driven by Hetzner
110
- // await this.ipfs.config.set('Swarm.AddrFilters', [
111
- // '/ip4/10.0.0.0/ipcidr/8',
112
- // '/ip4/100.0.0.0/ipcidr/8',
113
- // '/ip4/169.254.0.0/ipcidr/16',
114
- // '/ip4/172.16.0.0/ipcidr/12',
115
- // '/ip4/192.0.0.0/ipcidr/24',
116
- // '/ip4/192.0.2.0/ipcidr/24',
117
- // '/ip4/192.168.0.0/ipcidr/16',
118
- // '/ip4/198.18.0.0/ipcidr/15',
119
- // '/ip4/198.51.100.0/ipcidr/24',
120
- // '/ip4/203.0.113.0/ipcidr/24',
121
- // '/ip4/240.0.0.0/ipcidr/4',
122
- // '/ip6/100::/ipcidr/64',
123
- // '/ip6/2001:2::/ipcidr/48',
124
- // '/ip6/2001:db8::/ipcidr/32',
125
- // '/ip6/fc00::/ipcidr/7',
126
- // '/ip6/fe80::/ipcidr/10'
127
- // ])
128
-
129
- // // go-ipfs v0.10.0
130
- // // await this.ipfs.config.set('Swarm.EnableRelayHop', true)
131
- // // await this.ipfs.config.set('Swarm.EnableAutoRelay', true)
132
-
133
- // // Disable peer discovery
134
- // // await this.ipfs.config.set('Routing.Type', 'none')
135
- // }
136
-
137
- // Disable preloading
138
- // await this.ipfs.config.set('preload.enabled', false)
139
-
140
- // Reduce the default number of peers thisNode connects to at one time.
141
- // await this.ipfs.config.set('Swarm.ConnMgr', {
142
- // LowWater: 10,
143
- // HighWater: 30,
144
- // GracePeriod: '2s'
145
- // })
146
-
147
- // Reduce the storage size, as this node should not be retaining much data.
148
- // await this.ipfs.config.set('Datastore.StorageMax', '2GB')
149
70
  } catch (err) {
150
71
  console.error('Error in ipfs-adapter.js/start()')
151
72
  throw err
@@ -154,43 +75,34 @@ class IpfsAdapter {
154
75
 
155
76
  // Attempts to connect to an IPFS peer, given its IPFS multiaddr.
156
77
  // Returns true if the connection succeeded. Otherwise returns false.
157
- async connectToPeer (ipfsAddr) {
78
+ async connectToPeer (inObj = {}) {
158
79
  try {
80
+ // console.log('connectToPeer() inObj: ', inObj)
81
+
159
82
  // TODO: Throw error if ipfs ID is passed, instead of a multiaddr.
160
- console.log('ipfsAddr: ', ipfsAddr)
83
+ // console.log('ipfsAddr: ', ipfsAddr)
84
+
85
+ const { multiaddr } = inObj
161
86
 
162
87
  // await this.ipfs.swarm.connect(ipfsAddr, { timeout: CONNECTION_TIMEOUT })
163
- await this.ipfs.libp2p.dial(this.multiaddr(ipfsAddr))
88
+ await this.ipfs.libp2p.dial(this.multiaddr(multiaddr))
89
+ // console.log('connectToPeer() result: ', result)
164
90
 
165
- this.log.statusLog(1, `Successfully connected to peer node ${ipfsAddr}`)
91
+ this.log.statusLog(1, `Successfully connected to peer node ${multiaddr}`)
166
92
 
167
- return true
93
+ return {
94
+ success: true,
95
+ details: null
96
+ }
168
97
  } catch (err) {
169
98
  /* exit quietly */
170
- console.warn(
171
- `Error trying to connect to peer node ${ipfsAddr}: ${err.message}`
172
- )
173
-
174
- // if (this.debugLevel === 1) {
175
- // this.statusLog(
176
- // `status: Error trying to connect to peer node ${ipfsAddr}`
177
- // )
178
- // } else if (this.debugLevel === 2) {
179
- // this.statusLog(
180
- // `status: Error trying to connect to peer node ${ipfsAddr}: `,
181
- // err
182
- // )
183
- // }
184
- this.log.statusLog(2, `Error trying to connect to peer node ${ipfsAddr}`)
185
- // console.log(`Error trying to connect to peer node ${ipfsAddr}: `, err)
186
-
187
- // this.log.statusLog(
188
- // 3,
189
- // `Error trying to connect to peer node ${ipfsAddr}: `,
190
- // err
191
- // )
99
+ // console.log('connectToPeer() Error connecting to peer: ', err)
100
+ this.log.statusLog(2, 'Error trying to connect to peer node : ', err.message)
192
101
 
193
- return false
102
+ return {
103
+ success: false,
104
+ details: err.message
105
+ }
194
106
  }
195
107
  }
196
108
 
@@ -241,16 +153,9 @@ class IpfsAdapter {
241
153
  try {
242
154
  // console.log('this.ipfs.libp2p: ', this.ipfs.libp2p)
243
155
 
244
- // Get connected peers
245
- // const connectedPeers = await this.ipfs.swarm.peers({
246
- // direction: true,
247
- // streams: true,
248
- // verbose: true,
249
- // latency: true
250
- // })
251
156
  let connectedPeers = await this.ipfs.libp2p.getPeers()
252
157
  connectedPeers = connectedPeers.map(x => x.toString())
253
- console.log('connectedPeers: ', connectedPeers)
158
+ this.log.statusLog(1, 'connectedPeers: ', connectedPeers)
254
159
 
255
160
  return connectedPeers
256
161
  } catch (err) {
@@ -260,5 +165,4 @@ class IpfsAdapter {
260
165
  }
261
166
  }
262
167
 
263
- // module.exports = IpfsAdapter
264
168
  export default IpfsAdapter
@@ -9,7 +9,6 @@ import { BroadcastRouter, PrivateChannelRouter } from './msg-router.js'
9
9
 
10
10
  // Libraries for working with default uint8Array Buffers that pubsub uses
11
11
  // for sending and receiving messages.
12
- // import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
13
12
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
14
13
 
15
14
  class PubsubAdapter {
@@ -45,19 +44,20 @@ class PubsubAdapter {
45
44
  this.messaging = new Messaging(localConfig)
46
45
  this.about = new AboutAdapter(localConfig)
47
46
 
48
- // 'embedded' node type used as default, will use embedded js-ipfs.
49
- // Alternative is 'external' which will use ipfs-http-client to control an
50
- // external IPFS node.
51
47
  this.nodeType = localConfig.nodeType
52
48
  if (!this.nodeType) {
53
- // console.log('No node type specified. Assuming embedded js-ipfs.')
54
- this.nodeType = 'embedded'
49
+ this.nodeType = 'node.js'
55
50
  }
56
- // console.log(`PubsubAdapter contructor node type: ${this.nodeType}`)
57
51
 
58
52
  // Bind functions that are called by event handlers
59
53
  this.parsePubsubMessage = this.parsePubsubMessage.bind(this)
60
54
  this.handleNewMessage = this.handleNewMessage.bind(this)
55
+ this.checkForDuplicateMsg = this.checkForDuplicateMsg.bind(this)
56
+ this.manageMsgCache = this.manageMsgCache.bind(this)
57
+
58
+ // State
59
+ this.trackedMsgs = [] // Used reject repeated messages
60
+ this.TRACKED_MSG_SIZE = 100
61
61
  }
62
62
 
63
63
  // Subscribe to a pubsub channel. Any data received on that channel is passed
@@ -71,11 +71,6 @@ class PubsubAdapter {
71
71
  // for this node. This applies to general broadcast channels like
72
72
  // the coordination channel that all nodes use to annouce themselves.
73
73
  if (chanName !== thisNodeId) {
74
- // console.log('this.ipfs: ', this.ipfs)
75
- // await this.ipfs.ipfs.pubsub.subscribe(chanName.toString(), async (msg) => {
76
- // await this.parsePubsubMessage(msg, handler, thisNode)
77
- // })
78
-
79
74
  // Instantiate the Broadcast message router library
80
75
  const bRouterOptions = {
81
76
  handler,
@@ -105,19 +100,10 @@ class PubsubAdapter {
105
100
 
106
101
  // Subscribe to the pubsbu channel. Route any incoming messages to the
107
102
  // this library.
108
- // await this.ipfs.ipfs.pubsub.subscribe(chanName.toString(), privateRouter.route)
109
103
  this.ipfs.ipfs.libp2p.services.pubsub.subscribe(chanName.toString())
110
104
 
111
105
  // Route incoming message events.
112
106
  this.ipfs.ipfs.libp2p.services.pubsub.addEventListener('message', privateRouter.route)
113
-
114
- // await this.ipfs.ipfs.pubsub.subscribe(chanName.toString(), async (msg) => {
115
- // const msgObj = await this.messaging.handleIncomingData(msg, thisNode)
116
- //
117
- // // If msgObj is false, then ignore it. Typically indicates an already
118
- // // processed message.
119
- // if (msgObj) { await this.handleNewMessage(msgObj, thisNode) }
120
- // })
121
107
  }
122
108
 
123
109
  this.log.statusLog(
@@ -150,7 +136,6 @@ class PubsubAdapter {
150
136
  // Pass the JSON RPC data to the private log to be handled by the app
151
137
  // consuming this library.
152
138
  if (!isAbout) {
153
- // console.log('handleNewMessage() forwarding payload on to handler.')
154
139
  this.privateLog(msgObj.data.payload, msgObj.from)
155
140
 
156
141
  return true
@@ -172,16 +157,11 @@ class PubsubAdapter {
172
157
  // console.log('thisNode: ', thisNode)
173
158
 
174
159
  const data = JSON.parse(decryptedStr)
175
- // console.log('data: ', data)
176
160
 
177
161
  // Handle /about JSON RPC queries.
178
162
  if (data.id.includes('metrics') && data.method === 'about') {
179
163
  // Request recieved, send response.
180
164
 
181
- // console.log('/about JSON RPC captured. Sending back announce object.')
182
-
183
- // console.log('thisNode.schema.state.announceJsonLd: ', thisNode.schema.state.announceJsonLd)
184
-
185
165
  const jsonResponse = `{"jsonrpc": "2.0", "id": "${data.id}", "result": {"method": "about", "receiver": "${from}", "value": ${JSON.stringify(thisNode.schema.state)}}}`
186
166
  // console.log(`Responding with this JSON RPC response: ${jsonResponse}`)
187
167
 
@@ -200,8 +180,6 @@ class PubsubAdapter {
200
180
  } else if (data.id.includes('metrics') && data.result && data.result.method === 'about') {
201
181
  // Response received.
202
182
 
203
- // console.log('JSON RPC /about response aquired.')
204
-
205
183
  // This event is handled by the about-adapter.js. It measures the
206
184
  // latency between peers.
207
185
  this.about.relayMetricsReceived(decryptedStr)
@@ -221,19 +199,13 @@ class PubsubAdapter {
221
199
  // Attempts to parse data coming in from a pubsub channel. It is assumed that
222
200
  // the data is a string in JSON format. If it isn't, parsing will throw an
223
201
  // error and the message will be ignored.
224
- async parsePubsubMessage (msg, handler, thisNode) {
202
+ async parsePubsubMessage (inObj = {}) {
225
203
  try {
226
- // console.log('message data: ', msg.data)
227
- // console.log('parsePubsubMessage msg: ', msg)
228
- // console.log('thisNode: ', thisNode)
204
+ const { msg, handler, thisNode } = inObj
229
205
 
230
- // let parsedData = uint8ArrayToString(msg.detail.data)
231
- // parsedData = JSON.parse(JSON.parse(parsedData))
232
- // console.log('parsedData: ', parsedData)
233
- // console.log('typeof parsedData: ', typeof parsedData)
234
- // console.log('msg.detail.data: ', JSON.stringify(parsedData, null, 2))
235
- // console.log('msg.detail.topic: ', msg.detail.topic)
236
- // console.log('msg.detail.from: ', msg.detail.from.toString())
206
+ // Skip any repeated messages
207
+ const shouldProcess = this.checkForDuplicateMsg(msg)
208
+ if (!shouldProcess) return false
237
209
 
238
210
  const thisNodeId = thisNode.ipfsId
239
211
 
@@ -250,16 +222,6 @@ class PubsubAdapter {
250
222
  // Ignore this message if it originated from this IPFS node.
251
223
  if (from === thisNodeId) return true
252
224
 
253
- // The data on browsers comes through as a uint8array, and on node.js
254
- // implementiong it comes through as a string. Browsers need to
255
- // convert the message from a uint8array to a string.
256
- // console.log('this.nodeType: ', this.nodeType)
257
- // if (thisNode.type === 'browser' || this.nodeType === 'external') {
258
- // // console.log('Node type is browser or external')
259
- // msg.data = new TextDecoder('utf-8').decode(msg.data)
260
- // }
261
- // console.log('msg.data: ', JSON.parse(msg.data.toString()))
262
-
263
225
  let data
264
226
  try {
265
227
  // Parse the data into a JSON object. It starts as a Buffer that needs
@@ -269,38 +231,65 @@ class PubsubAdapter {
269
231
 
270
232
  data = uint8ArrayToString(msg.detail.data)
271
233
  data = JSON.parse(JSON.parse(data))
272
- // console.log('data: ', data)
234
+ // console.log('parsePubsubMessage() 1 data: ', data)
273
235
 
274
- // if(data.data) {
275
- // data.data = JSON.parse(data.data)
276
- // }
277
- // console.log('parsePubsubMessage() data: ', data)
236
+ // console.log('pubsub-adapter/index.js parsePubsubMessage() collect test data: ', msg.detail.data.toString('hex'))
278
237
  } catch (err) {
279
- try {
280
- data = uint8ArrayToString(msg.detail.data)
281
- data = JSON.parse(data)
282
- // console.log('parsePubsubMessage() data: ', data)
283
- } catch (err) {
284
- console.log('pubsub-adapter/index.js parsePubsubMessage() collect test data: ', msg.detail.data.toString('hex'))
285
- throw new Error(`Failed to parse JSON in message from ${from} in pubsub channel ${channel}.`)
286
- }
238
+ this.log.statusLog(
239
+ 1,
240
+ `Failed to parse JSON in message from ${from} in pubsub channel ${channel}.`
241
+ )
242
+
243
+ return false
287
244
  }
288
245
 
289
246
  const retObj = { from, channel, data }
290
247
  // console.log(`new pubsub message received: ${JSON.stringify(retObj, null, 2)}`)
291
248
 
292
249
  // Hand retObj to the callback.
293
- handler(retObj)
250
+ await handler(retObj)
294
251
 
295
252
  return true
296
253
  } catch (err) {
297
- // console.error('Error in parsePubsubMessage(): ', err.message)
254
+ console.error('Error in parsePubsubMessage(): ', err.message)
298
255
  this.log.statusLog(2, `Error in parsePubsubMessage(): ${err.message}`)
299
256
  // Do not throw an error. This is a top-level function.
300
257
 
301
258
  return false
302
259
  }
303
260
  }
261
+
262
+ // Checks to see if a message has already been processed. This protects against
263
+ // redundent processing.
264
+ checkForDuplicateMsg (msg) {
265
+ const sn = msg.detail.sequenceNumber
266
+ // console.log('sn: ', sn)
267
+
268
+ const snExists = this.trackedMsgs.find((x) => x === sn)
269
+ // console.log('snExists: ', snExists)
270
+
271
+ // Maintain the tracked message cache.
272
+ this.manageMsgCache()
273
+
274
+ if (snExists) {
275
+ // console.log(`msg ${sn} already processed. Rejecting.`)
276
+ return false
277
+ }
278
+
279
+ // Add the sn to the array of tracked messages.
280
+ this.trackedMsgs.push(sn)
281
+
282
+ return true
283
+ }
284
+
285
+ // Keep the message queue to a resonable size.
286
+ manageMsgCache () {
287
+ // console.log(`trackedMsgs: `, this.trackedMsgs)
288
+
289
+ if (this.trackedMsgs.length > this.TRACKED_MSG_SIZE) {
290
+ this.trackedMsgs.shift()
291
+ }
292
+ }
304
293
  }
305
294
 
306
295
  // module.exports = PubsubAdapter