@leofcoin/peernet 0.10.8 → 0.11.2

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