@leofcoin/peernet 0.9.3 → 0.9.6

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.
Files changed (62) hide show
  1. package/dist/browser/peernet.js +651 -646
  2. package/dist/commonjs/codec-73adfc0f.js +205 -0
  3. package/dist/commonjs/codec-format-interface.js +5 -5
  4. package/dist/commonjs/codec.js +6 -199
  5. package/dist/commonjs/dht-response.js +1 -1
  6. package/dist/commonjs/dht.js +1 -1
  7. package/dist/commonjs/hash.js +4 -4
  8. package/dist/commonjs/peernet-message.js +1 -1
  9. package/dist/commonjs/peernet.js +653 -648
  10. package/dist/commonjs/request.js +1 -1
  11. package/dist/commonjs/response.js +1 -1
  12. package/dist/module/peernet.js +10 -4
  13. package/package.json +1 -1
  14. package/src/client.js +71 -0
  15. package/src/codec/codec-format-interface.js +139 -0
  16. package/src/codec/codec.js +114 -0
  17. package/src/codec/codecs.js +79 -0
  18. package/src/dht/dht.js +125 -0
  19. package/src/discovery/peer-discovery.js +75 -0
  20. package/src/errors/errors.js +12 -0
  21. package/src/handlers/message.js +52 -0
  22. package/src/hash/hash.js +145 -0
  23. package/src/http/api.js +115 -0
  24. package/src/http/client/api.js +41 -0
  25. package/src/http/client/client.js +10 -0
  26. package/src/http/client/http-client.js +44 -0
  27. package/src/http/client/storage.js +36 -0
  28. package/src/http/http.js +28 -0
  29. package/src/messages/chat-message.js +14 -0
  30. package/src/messages/data-response.js +14 -0
  31. package/src/messages/data.js +14 -0
  32. package/src/messages/dht-response.js +15 -0
  33. package/src/messages/dht.js +25 -0
  34. package/src/messages/peer-response.js +14 -0
  35. package/src/messages/peer.js +14 -0
  36. package/src/messages/peernet-message.js +14 -0
  37. package/src/messages/ps.js +14 -0
  38. package/src/messages/request.js +14 -0
  39. package/src/messages/response.js +14 -0
  40. package/src/peer-info.js +9 -0
  41. package/src/peer.js +43 -0
  42. package/src/peernet.js +680 -0
  43. package/src/proto/chat-message.proto.js +7 -0
  44. package/src/proto/data-response.proto.js +7 -0
  45. package/src/proto/data.proto.js +7 -0
  46. package/src/proto/dht-response.proto.js +7 -0
  47. package/src/proto/dht.proto.js +7 -0
  48. package/src/proto/peer-response.proto.js +6 -0
  49. package/src/proto/peer.proto.js +6 -0
  50. package/src/proto/peernet.proto.js +9 -0
  51. package/src/proto/ps.proto.js +6 -0
  52. package/src/proto/request.proto.js +6 -0
  53. package/src/proto/response.proto.js +6 -0
  54. package/src/server.js +25 -0
  55. package/src/utils/utils.js +78 -0
  56. package/.nyc_output/39a61420-013f-4db1-a597-7c5444da26e7.json +0 -1
  57. package/.nyc_output/4b387323-32a3-4eee-8f05-d13f2e0a5bf4.json +0 -1
  58. package/.nyc_output/ef71cf24-d9d9-45dd-814f-8d53cb6769f3.json +0 -1
  59. package/.nyc_output/processinfo/39a61420-013f-4db1-a597-7c5444da26e7.json +0 -1
  60. package/.nyc_output/processinfo/4b387323-32a3-4eee-8f05-d13f2e0a5bf4.json +0 -1
  61. package/.nyc_output/processinfo/ef71cf24-d9d9-45dd-814f-8d53cb6769f3.json +0 -1
  62. package/.nyc_output/processinfo/index.json +0 -1
package/src/peernet.js ADDED
@@ -0,0 +1,680 @@
1
+ import Pubsub from '@vandeurenglenn/little-pubsub'
2
+ import Client from './client'
3
+ import LeofcoinStorage from './../node_modules/@leofcoin/storage/src/level.js'
4
+ import http from './http/http.js'
5
+ import httpClient from './http/client/client.js'
6
+ import LeofcoinStorageClient from './http/client/storage.js'
7
+ import PeernetMessage from './messages/peernet-message.js'
8
+ import DHTMessage from './messages/dht.js'
9
+ import DHTMessageResponse from './messages/dht-response.js'
10
+ import DataMessage from './messages/data.js'
11
+ import PsMessage from './messages/ps.js'
12
+ import PeerMessage from './messages/peer.js'
13
+ import RequestMessage from './messages/request.js'
14
+ import ResponseMessage from './messages/response.js'
15
+ import PeerMessageResponse from './messages/peer-response.js'
16
+ import DataMessageResponse from './messages/data-response.js'
17
+ import ChatMessage from './messages/chat-message.js'
18
+ import PeerDiscovery from './discovery/peer-discovery'
19
+ import DHT from './dht/dht.js'
20
+ import Hash from './hash/hash'
21
+ import codecs from './codec/codecs'
22
+ import { debug, protoFor, target } from './utils/utils.js'
23
+ import generateAccount from
24
+ './../node_modules/@leofcoin/generate-account/dist/module/generate-account.js'
25
+ import MessageHandler from './handlers/message.js'
26
+ import { encapsulatedError, dhtError,
27
+ nothingFoundError } from './errors/errors.js'
28
+
29
+ globalThis.leofcoin = globalThis.leofcoin || {}
30
+ globalThis.globalSub = globalThis.globalSub || new Pubsub({verbose: true})
31
+
32
+ /**
33
+ * @access public
34
+ * @example
35
+ * const peernet = new Peernet();
36
+ */
37
+ export default class Peernet {
38
+ /**
39
+ * @access public
40
+ * @param {Object} options
41
+ * @param {String} options.network - desired network
42
+ * @param {String} options.root - path to root directory
43
+ * @param {String} options.storePrefix - prefix for datatores (lfc)
44
+ *
45
+ * @return {Promise} instance of Peernet
46
+ *
47
+ * @example
48
+ * const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
49
+ */
50
+ constructor(options = {}) {
51
+ this._discovered = []
52
+ /**
53
+ * @property {String} network - current network
54
+ */
55
+ this.network = options.network || 'leofcoin'
56
+ const parts = this.network.split(':')
57
+
58
+ if (!options.storePrefix) options.storePrefix = 'lfc'
59
+ if (!options.port) options.port = 2000
60
+ if (!options.root) {
61
+ if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`
62
+ else options.root = `.${this.network}/peernet`
63
+ }
64
+ globalThis.peernet = this
65
+ return this._init(options)
66
+ }
67
+
68
+ get defaultStores() {
69
+ return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
70
+ }
71
+
72
+ addProto(name, proto) {
73
+ if (!this.protos[name]) this.protos[name] = proto
74
+ }
75
+
76
+ addCodec(name, codec) {
77
+ if (!this.codecs[name]) this.codecs[name] = codec
78
+ }
79
+
80
+ async addStore(name, prefix, root, isPrivate = true) {
81
+ if (name === 'block' || name === 'transaction' || name === 'chain' ||
82
+ name === 'data' || name === 'message') isPrivate = false
83
+
84
+ let Storage
85
+ if (this.hasDaemon) {
86
+ Storage = LeofcoinStorageClient
87
+ } else {
88
+ Storage = LeofcoinStorage
89
+ }
90
+ globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
91
+ await new Storage(`${prefix}-${name}`, root)
92
+
93
+ globalThis[`${name}Store`].private = isPrivate
94
+ if (!isPrivate) this.stores.push(name)
95
+ }
96
+
97
+
98
+ /**
99
+ * @see MessageHandler
100
+ */
101
+ prepareMessage(to, data) {
102
+ return this._messageHandler.prepareMessage(this.id, to, data)
103
+ }
104
+
105
+ /**
106
+ * @access public
107
+ *
108
+ * @return {Array} peerId
109
+ */
110
+ get peers() {
111
+ return [...connections.values()]
112
+ }
113
+
114
+ /**
115
+ * @private
116
+ *
117
+ * @param {Object} options
118
+ * @param {String} options.root - path to root directory
119
+ *
120
+ * @return {Promise} instance of Peernet
121
+ */
122
+ async _init(options) {
123
+ // peernetDHT aka closesPeer by coordinates
124
+ /**
125
+ * @type {Object}
126
+ * @property {Object} peer Instance of Peer
127
+ */
128
+ this.dht = new DHT()
129
+ /**
130
+ * @type {Map}
131
+ * @property {Object} peer Instance of Peer
132
+ */
133
+ this.peerMap = new Map()
134
+ this.stores = []
135
+ this.requestProtos = {}
136
+ this.storePrefix = options.storePrefix
137
+ this.root = options.root
138
+
139
+ /**
140
+ * proto Object containing protos
141
+ * @type {Object}
142
+ * @property {PeernetMessage} protos[peernet-message] messageNode
143
+ * @property {DHTMessage} protos[peernet-dht] messageNode
144
+ * @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
145
+ * @property {DataMessage} protos[peernet-data] messageNode
146
+ * @property {DataMessageResponse} protos[peernet-data-response] messageNode
147
+ */
148
+ globalThis.peernet.protos = {
149
+ 'peernet-request': RequestMessage,
150
+ 'peernet-response': ResponseMessage,
151
+ 'peernet-peer': PeerMessage,
152
+ 'peernet-peer-response': PeerMessageResponse,
153
+ 'peernet-message': PeernetMessage,
154
+ 'peernet-dht': DHTMessage,
155
+ 'peernet-dht-response': DHTMessageResponse,
156
+ 'peernet-data': DataMessage,
157
+ 'peernet-data-response': DataMessageResponse,
158
+ 'peernet-ps': PsMessage,
159
+ 'chat-message': ChatMessage,
160
+ }
161
+
162
+ this.protos = globalThis.peernet.protos
163
+ this.codecs = codecs
164
+
165
+ this._messageHandler = new MessageHandler(this.network)
166
+
167
+ const {daemon, environment} = await target()
168
+ this.hasDaemon = daemon
169
+
170
+ if (this.hasDaemon) {
171
+ globalThis.peernet.client = await httpClient({
172
+ protocol: 'peernet-v0.1.0', host: '127.0.0.1', port: options.port,
173
+ })
174
+ } else {
175
+ if (environment !== 'browser') http(options)
176
+ }
177
+
178
+ for (const store of this.defaultStores) {
179
+ await this.addStore(store, options.storePrefix, options.root)
180
+ }
181
+
182
+ try {
183
+ const pub = await accountStore.get('public')
184
+ this.id = pub.walletId
185
+ } catch (e) {
186
+ if (e.code === 'ERR_NOT_FOUND') {
187
+ const wallet = {}
188
+ const {identity, accounts, config} = await generateAccount(this.network)
189
+ wallet.identity = identity
190
+ wallet.accounts = accounts
191
+ wallet.version = 1
192
+ walletStore.put(wallet)
193
+ await accountStore.put('config', config);
194
+ await accountStore.put('public', {walletId: wallet.identity.walletId});
195
+
196
+ this.id = wallet.identity.walletId
197
+ } else {
198
+ throw e
199
+ }
200
+ }
201
+ this._peerHandler = new PeerDiscovery(this.id)
202
+ // peernet id
203
+ const id = Buffer.from(this.id.slice(0, 32))
204
+ this.peerId = id
205
+
206
+ pubsub.subscribe('peer:discovered', async (peer) => {
207
+ this._peerHandler.discover(peer)
208
+ peer.on('peernet.data', async (message) => {
209
+ const id = message.id
210
+ message = new PeernetMessage(Buffer.from(message.data.data))
211
+ const proto = protoFor(message.decoded.data)
212
+ await this._protoHandler({id, proto}, peer)
213
+ const fulldId = this._getPeerId(peer.id)
214
+ if (fulldId && this._discovered.indexOf(peer.id) === -1) {
215
+ this._discovered.push(peer.id)
216
+ pubsub.publish('peer:connected', peer)
217
+ }
218
+ })
219
+ })
220
+ pubsub.subscribe('peer:disconnected', async (peer) => {
221
+ let index = this._discovered.indexOf(peer.id)
222
+ if (index !== -1) this._discovered.splice(index, 1)
223
+ const id = this._getPeerId(peer.id)
224
+ let peerIds = this.peerMap.get(id)
225
+
226
+ if (peerIds) {
227
+ index = peerIds.indexOf(peer.id)
228
+ if (index !== -1) peerIds.splice(index, 1)
229
+ } else {
230
+ peerIds = []
231
+ }
232
+
233
+ if (peerIds.length === 0) this.peerMap.delete(id)
234
+ else this.peerMap.set(id, peerIds)
235
+ })
236
+ pubsub.subscribe('peer:connected', async (peer) => {
237
+ console.log({connected: peer.id, as: this._getPeerId(peer.id) });
238
+ // peer.on('peernet.data', async (message) => {
239
+ // const id = message.id
240
+ // message = new PeernetMessage(Buffer.from(message.data.data))
241
+ // const proto = protoFor(message.decoded.data)
242
+ // this._protoHandler({id, proto}, peer)
243
+ // })
244
+ })
245
+
246
+ /**
247
+ * @access public
248
+ * @type {PeernetClient}
249
+ */
250
+ this.client = new Client({...options, id})
251
+ return this
252
+ }
253
+
254
+ _getPeerId(id) {
255
+ for (const entry of [...this.peerMap.entries()]) {
256
+ for (const _id of entry[1]) {
257
+ if (_id === id) return entry[0]
258
+ }
259
+ }
260
+ }
261
+
262
+ addRequestHandler(name, method) {
263
+ this.requestProtos[name] = method
264
+ }
265
+
266
+ /**
267
+ * @private
268
+ *
269
+ * @param {Buffer} message - peernet message
270
+ * @param {PeernetPeer} peer - peernet peer
271
+ */
272
+ async _protoHandler(message, peer) {
273
+ const {id, proto} = message
274
+ if (proto.name === 'peernet-peer') {
275
+ const from = proto.decoded.id
276
+ if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
277
+ else {
278
+ const connections = this.peerMap.get(from)
279
+ if (connections.indexOf(peer.id) === -1) {
280
+ connections.push(peer.id)
281
+ this.peerMap.set(from, connections)
282
+ }
283
+ }
284
+ const data = new PeerMessageResponse({id: this.id})
285
+ const node = await this.prepareMessage(from, data.encoded)
286
+
287
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
288
+ } else if (proto.name === 'peernet-peer-response') {
289
+ const from = proto.decoded.id
290
+ if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
291
+ else {
292
+ const connections = this.peerMap.get(from)
293
+ if (connections.indexOf(peer.id) === -1) {
294
+ connections.push(peer.id)
295
+ this.peerMap.set(from, connections)
296
+ }
297
+ }
298
+ } else {
299
+ let from = this._getPeerId(peer.id)
300
+ if (!from) {
301
+ const data = new PeerMessage({id: this.id})
302
+ const node = await this.prepareMessage(peer.id, data.encoded)
303
+
304
+ let response = await peer.request(node.encoded)
305
+ response = protoFor(response)
306
+ response = new PeerMessageResponse(response.decoded.data)
307
+
308
+ from = response.decoded.id
309
+ if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
310
+ else {
311
+ const connections = this.peerMap.get(from)
312
+ if (connections.indexOf(peer.id) === -1) {
313
+ connections.push(peer.id)
314
+ this.peerMap.set(from, connections)
315
+ }
316
+ }
317
+ }
318
+ if (proto.name === 'peernet-dht') {
319
+ let { hash, store } = proto.decoded
320
+ let has;
321
+
322
+ if (!store) {
323
+ has = await this.has(hash)
324
+ } else {
325
+ store = globalThis[`${store}Store`]
326
+ if (store.private) has = false
327
+ else has = await store.has(hash)
328
+ }
329
+ const data = new DHTMessageResponse({hash, has})
330
+ const node = await this.prepareMessage(from, data.encoded)
331
+
332
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
333
+ } else if (proto.name === 'peernet-data') {
334
+ let { hash, store } = proto.decoded
335
+ let data
336
+
337
+ if (!store) {
338
+ data = await this.get(hash)
339
+ } else {
340
+ store = globalThis[`${store}Store`]
341
+ if (store.private) {
342
+ // TODO: ban
343
+ return
344
+ } else data = await store.get(hash)
345
+ }
346
+
347
+ if (data) {
348
+ data = new DataMessageResponse({hash, data: Buffer.from(data)})
349
+
350
+ const node = await this.prepareMessage(from, data.encoded)
351
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
352
+ }
353
+ } else if (proto.name === 'peernet-peer') {
354
+ const from = proto.decoded.id
355
+ if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
356
+ else {
357
+ const connections = this.peerMap.get(from)
358
+ connections.push(peer.id)
359
+ this.peerMap.set(from, connections)
360
+ }
361
+ const data = new PeerMessage({id: this.id})
362
+ const node = await this.prepareMessage(from, data.encoded)
363
+
364
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
365
+ } else if (proto.name === 'peernet-request') {
366
+ // TODO: make dynamic
367
+ // exposeddevapi[proto.decoded.request](proto.decoded.params)
368
+ const method = this.requestProtos[proto.decoded.request]
369
+ if (method) {
370
+ const data = await method()
371
+ const node = await this.prepareMessage(from, data.encoded)
372
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
373
+ }
374
+ } else if (proto.name === 'peernet-ps' &&
375
+ this._getPeerId(peer.id) !== this.id.toString()) {
376
+ globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString())
377
+ }
378
+ }
379
+ }
380
+
381
+ /**
382
+ * performs a walk and resolves first encounter
383
+ *
384
+ * @param {String} hash
385
+ */
386
+ async walk(hash) {
387
+ if (!hash) throw new Error('hash expected, received undefined')
388
+ const data = new DHTMessage({hash})
389
+ const clientId = this.client.id
390
+ for (const peer of this.peers) {
391
+ const node = await this.prepareMessage(peer.id, data.encoded)
392
+
393
+ const result = await peer.request(node.encoded)
394
+
395
+ let proto = protoFor(result.data)
396
+
397
+ if (proto.name !== 'peernet-message') throw encapsulatedError()
398
+ const from = proto.decoded.from
399
+ proto = protoFor(proto.decoded.data)
400
+
401
+ if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
402
+
403
+ // TODO: give ip and port (just used for location)
404
+ if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
405
+ peer.connection.remoteFamily = 'ipv4'
406
+ peer.connection.remoteAddress = '127.0.0.1'
407
+ peer.connection.remotePort = '0000'
408
+ }
409
+
410
+ const peerInfo = {
411
+ family: peer.connection.remoteFamily || peer.connection.localFamily,
412
+ address: peer.connection.remoteAddress || peer.connection.localAddress,
413
+ port: peer.connection.remotePort || peer.connection.localPort,
414
+ id: from,
415
+ }
416
+
417
+ if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash)
418
+ }
419
+ return
420
+ }
421
+
422
+ /**
423
+ * Override DHT behavior, try's finding the content three times
424
+ *
425
+ * @param {String} hash
426
+ */
427
+ async providersFor(hash) {
428
+ let providers = await this.dht.providersFor(hash)
429
+ // walk the network to find a provider
430
+ if (!providers || providers.length === 0) {
431
+ await this.walk(hash)
432
+ providers = await this.dht.providersFor(hash)
433
+ // second walk the network to find a provider
434
+ if (!providers || providers.length === 0) {
435
+ await this.walk(hash)
436
+ providers = await this.dht.providersFor(hash)
437
+ }
438
+ // last walk
439
+ if (!providers || providers.length === 0) {
440
+ await this.walk(hash)
441
+ providers = await this.dht.providersFor(hash)
442
+ }
443
+ }
444
+ // undefined if no providers given
445
+ return providers
446
+ }
447
+
448
+ get block() {
449
+ return {
450
+ get: async (hash) => {
451
+ const data = await blockStore.has(hash)
452
+ if (data) return await blockStore.get(hash)
453
+ return this.requestData(hash)
454
+ },
455
+ put: async (hash, data) => {
456
+ if (await blockStore.has(hash)) return
457
+ return await blockStore.put(hash, data)
458
+ },
459
+ has: async (hash) => await blockStore.has(hash, 'block'),
460
+ }
461
+ }
462
+
463
+ get transaction() {
464
+ return {
465
+ get: async (hash) => {
466
+ const data = await transactionStore.has(hash)
467
+ if (data) return await transactionStore.get(hash)
468
+ return this.requestData(hash, 'transaction')
469
+ },
470
+ put: async (hash, data) => {
471
+ if (await transactionStore.has(hash)) return
472
+ return await transactionStore.put(hash, data)
473
+ },
474
+ has: async (hash) => await transactionStore.has(hash),
475
+ }
476
+ }
477
+
478
+ async requestData(hash, store) {
479
+ const providers = await this.providersFor(hash)
480
+ if (!providers || providers.size === 0) throw nothingFoundError(hash)
481
+ debug(`found ${providers.size} provider(s) for ${hash}`)
482
+ // get closest peer on earth
483
+ const closestPeer = await this.dht.closestPeer(providers)
484
+ // get peer instance by id
485
+ if (!closestPeer || !closestPeer.id) return this.requestData(hash, store)
486
+
487
+ const id = closestPeer.id.toString()
488
+ if (this.peers) {
489
+ let closest = this.peers.filter((peer) => {
490
+ if (this._getPeerId(peer.id) === id) return peer
491
+ })
492
+
493
+ let data = new DataMessage({hash, store})
494
+
495
+ const node = await this.prepareMessage(id, data.encoded)
496
+ if (closest[0]) data = await closest[0].request(node.encoded)
497
+ else {
498
+ closest = this.peers.filter((peer) => {
499
+ if (peer.id.toString() === id) return peer
500
+ })
501
+ if (closest[0]) data = await closest[0].request(node.encoded)
502
+ }
503
+ if (data.data) {
504
+ let proto = protoFor(Buffer.from(data.data))
505
+ proto = protoFor(proto.decoded.data)
506
+ return proto.decoded.data
507
+ }
508
+
509
+ // this.put(hash, proto.decoded.data)
510
+ }
511
+ return null
512
+ }
513
+
514
+
515
+ get message() {
516
+ return {
517
+ /**
518
+ * Get content for given message hash
519
+ *
520
+ * @param {String} hash
521
+ */
522
+ get: async (hash) => {
523
+ debug(`get message ${hash}`)
524
+ const message = await messageStore.has(hash)
525
+ if (message) return await messageStore.get(hash)
526
+ return this.requestData(hash, 'message')
527
+ },
528
+ /**
529
+ * put message content
530
+ *
531
+ * @param {String} hash
532
+ * @param {Buffer} message
533
+ */
534
+ put: async (hash, message) => await messageStore.put(hash, message),
535
+ /**
536
+ * @param {String} hash
537
+ * @return {Boolean}
538
+ */
539
+ has: async (hash) => await messageStore.has(hash),
540
+ }
541
+ }
542
+
543
+ get data() {
544
+ return {
545
+ /**
546
+ * Get content for given data hash
547
+ *
548
+ * @param {String} hash
549
+ */
550
+ get: async (hash) => {
551
+ debug(`get data ${hash}`)
552
+ const data = await dataStore.has(hash)
553
+ if (data) return await dataStore.get(hash)
554
+ return this.requestData(hash, 'data')
555
+ },
556
+ /**
557
+ * put data content
558
+ *
559
+ * @param {String} hash
560
+ * @param {Buffer} data
561
+ */
562
+ put: async (hash, data) => await dataStore.put(hash, data),
563
+ /**
564
+ * @param {String} hash
565
+ * @return {Boolean}
566
+ */
567
+ has: async (hash) => await dataStore.has(hash),
568
+ }
569
+ }
570
+
571
+ /**
572
+ * goes trough given stores and tries to find data for given hash
573
+ * @param {Array} stores
574
+ * @param {string} hash
575
+ */
576
+ async whichStore(stores, hash) {
577
+ let store = stores.pop()
578
+ const name = store
579
+ store = globalThis[`${store}Store`]
580
+ if (store) {
581
+ const has = await store.has(hash)
582
+ if (has) return store
583
+ if (stores.length > 0) return this.whichStore(stores, hash)
584
+ } else return null
585
+ }
586
+
587
+ /**
588
+ * Get content for given hash
589
+ *
590
+ * @param {String} hash
591
+ */
592
+ async get(hash, store) {
593
+ debug(`get ${hash}`)
594
+ let data
595
+ if (store) store = globalThis[`${store}Store`]
596
+ if (!store) store = await this.whichStore([...this.stores], hash)
597
+ if (store && await store.has(hash)) data = await store.get(hash)
598
+ if (data) return data
599
+
600
+ return this.requestData(hash, 'data')
601
+ }
602
+
603
+ /**
604
+ * put content
605
+ *
606
+ * @param {String} hash
607
+ * @param {Buffer} data
608
+ */
609
+ async put(hash, data, store = 'data') {
610
+ store = globalThis[`${store}Store`]
611
+ return await store.put(hash, data)
612
+ }
613
+
614
+ /**
615
+ * @param {String} hash
616
+ * @return {Boolean}
617
+ */
618
+ async has(hash) {
619
+ const store = await this.whichStore([...this.stores], hash)
620
+ if (store) {
621
+ if (store.private) return false
622
+ else return true
623
+ }
624
+ return false
625
+ }
626
+
627
+ /**
628
+ *
629
+ * @param {String} topic
630
+ * @param {String|Object|Array|Boolean|Buffer} data
631
+ */
632
+ async publish(topic, data) {
633
+ // globalSub.publish(topic, data)
634
+ if (!Buffer.isBuffer(topic)) topic = Buffer.from(topic)
635
+ if (!Buffer.isBuffer(data)) data = Buffer.from(data)
636
+ const id = Math.random().toString(36).slice(-12)
637
+ data = new PsMessage({data, topic})
638
+ for (const peer of this.peers) {
639
+ if (peer.connection._connected) {
640
+ if (peer.id.toString() !== this.peerId.toString()) {
641
+ const node = await this.prepareMessage(peer.id, data.encoded)
642
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
643
+ }
644
+ } else {
645
+ this.removePeer(peer)
646
+ }
647
+ // TODO: if peer subscribed
648
+ }
649
+ }
650
+
651
+ createHash(data, name) {
652
+ return new Hash(data, {name})
653
+ }
654
+
655
+ /**
656
+ *
657
+ * @param {String} topic
658
+ * @param {Method} cb
659
+ */
660
+ async subscribe(topic, cb) {
661
+ // TODO: if peer subscribed
662
+ globalSub.subscribe(topic, cb)
663
+ }
664
+
665
+ async removePeer(peer) {
666
+ connections.delete(peer.id)
667
+ }
668
+
669
+ get Buffer() {
670
+ return Buffer
671
+ }
672
+ // async block(index) {
673
+ // const _values = []
674
+ // for (const peer of this.peers) {
675
+ // const value = await peer.request({type: 'block', index})
676
+ // console.log(value);
677
+ // }
678
+ //
679
+ // }
680
+ }
@@ -0,0 +1,7 @@
1
+ export default `
2
+ message ChatMessage {
3
+ required string value = 1;
4
+ required string author = 2;
5
+ required uint64 timestamp = 3;
6
+ repeated string files = 4;
7
+ }`
@@ -0,0 +1,7 @@
1
+ export default `
2
+ // PeernetDataMessageResponse
3
+ message PeernetDataMessageResponse {
4
+ required string hash = 1;
5
+ required bytes data = 2;
6
+ }
7
+ `
@@ -0,0 +1,7 @@
1
+ export default `
2
+ // PeernetDataMessage
3
+ message PeernetDataMessage {
4
+ required string hash = 1;
5
+ optional string store = 2;
6
+ }
7
+ `
@@ -0,0 +1,7 @@
1
+ export default `
2
+ // PeernetDHTMessageResponse
3
+ message PeernetDHTMessageResponse {
4
+ required string hash = 1;
5
+ required bool has = 2;
6
+ }
7
+ `