@leofcoin/peernet 0.10.7 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) 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/peernet.js +101788 -93018
  10. package/dist/commonjs/client-1a1f75e6.js +324 -0
  11. package/dist/commonjs/{codec-6367213c.js → codec-8c8c652f.js} +198 -188
  12. package/dist/commonjs/codec-format-interface.js +169 -152
  13. package/dist/commonjs/codec.js +4 -4
  14. package/dist/commonjs/dht-response.js +13 -13
  15. package/dist/commonjs/dht.js +24 -24
  16. package/dist/commonjs/hash.js +151 -141
  17. package/dist/commonjs/{http-a94c5a81.js → http-7bbac90a.js} +19 -15
  18. package/dist/commonjs/peernet-message.js +13 -13
  19. package/dist/commonjs/peernet.js +1901 -1794
  20. package/dist/commonjs/request.js +13 -13
  21. package/dist/commonjs/response.js +13 -13
  22. package/dist/module/peernet.js +2460 -2346
  23. package/index.html +4 -6
  24. package/package.json +22 -14
  25. package/rollup.config.js +33 -5
  26. package/rollup0.config.js +7 -0
  27. package/src/client.js +75 -75
  28. package/src/codec/codec-format-interface.js +172 -155
  29. package/src/codec/codec.js +124 -114
  30. package/src/codec/codecs.js +79 -79
  31. package/src/dht/dht.js +121 -121
  32. package/src/discovery/peer-discovery.js +75 -75
  33. package/src/handlers/message.js +52 -52
  34. package/src/hash/hash.js +155 -145
  35. package/src/http/client/http-client.js +44 -44
  36. package/src/messages/chat-message.js +14 -14
  37. package/src/messages/data-response.js +14 -14
  38. package/src/messages/data.js +18 -18
  39. package/src/messages/dht-response.js +14 -15
  40. package/src/messages/dht.js +25 -25
  41. package/src/messages/peer-response.js +14 -14
  42. package/src/messages/peer.js +14 -14
  43. package/src/messages/peernet-message.js +14 -14
  44. package/src/messages/ps.js +14 -14
  45. package/src/messages/request.js +14 -14
  46. package/src/messages/response.js +14 -14
  47. package/src/peer.js +67 -67
  48. package/src/peernet.js +612 -697
  49. package/src/proto/chat-message.proto.js +7 -7
  50. package/src/utils/utils.js +78 -78
  51. package/test/codec.js +3 -2
  52. package/test/messages.js +7 -4
  53. package/test.js +11 -4
  54. package/webpack.config.js +35 -0
  55. package/coverage/lcov-report/codec.js.html +0 -677
  56. package/coverage/lcov-report/hash.js.html +0 -551
  57. package/debug.log +0 -3
  58. package/dist/browser/peernet.js.tmp-browserify-14074318104595318069 +0 -0
  59. package/dist/browser/peernet.js.tmp-browserify-45407634493269122267 +0 -0
  60. package/dist/browser/peernet.js.tmp-browserify-53722389064799025427 +0 -0
  61. package/dist/browser/peernet.js.tmp-browserify-96323030449218949300 +0 -0
  62. package/dist/codec/codec-format-interface.js +0 -433
  63. package/dist/codec/codec.js +0 -199
  64. package/dist/commonjs/codec-73adfc0f.js +0 -205
  65. package/dist/commonjs/http-2c603501.js +0 -324
  66. package/dist/commonjs/http-42a6e555.js +0 -324
  67. package/dist/commonjs/http-43f4fafe.js +0 -324
  68. package/dist/commonjs/peernet-message-b6925673.js +0 -32
  69. package/dist/hash/hash.js +0 -340
  70. package/dist/messages/dht-response.js +0 -454
  71. package/dist/messages/dht.js +0 -453
  72. package/dist/messages/peernet.js +0 -456
  73. package/dist/module/http-273664bd.js +0 -317
  74. package/dist/module/http-8fe3c0d7.js +0 -317
  75. package/dist/module/http-c780c991.js +0 -317
  76. package/dist/module/http-f13e0d77.js +0 -317
package/src/peernet.js CHANGED
@@ -1,697 +1,612 @@
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})
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 = 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 = new TextDecoder().decode(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(Buffer.from(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 ? Buffer.from(JSON.stringify(data)) : Buffer.from(data)});
285
+
286
+ const node = await this.prepareMessage(from, data.encoded)
287
+ peer.send(Buffer.from(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
+ this.bw.up += node.encoded.length
303
+ }
304
+ } else if (proto.name === 'peernet-ps' &&
305
+ this._getPeerId(peer.id) !== this.id.toString()) {
306
+ globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString())
307
+ }
308
+ // }
309
+ }
310
+
311
+ /**
312
+ * performs a walk and resolves first encounter
313
+ *
314
+ * @param {String} hash
315
+ */
316
+ async walk(hash) {
317
+ if (!hash) throw new Error('hash expected, received undefined')
318
+ const data = new DHTMessage({hash})
319
+ const clientId = this.client.id
320
+ for (const peer of this.peers) {
321
+ const node = await this.prepareMessage(peer.id, data.encoded)
322
+
323
+ const result = await peer.request(node.encoded)
324
+
325
+ let proto = protoFor(result.data)
326
+
327
+ if (proto.name !== 'peernet-message') throw encapsulatedError()
328
+ const from = proto.decoded.from
329
+ proto = protoFor(proto.decoded.data)
330
+
331
+ if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
332
+
333
+ // TODO: give ip and port (just used for location)
334
+ if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
335
+ peer.connection.remoteFamily = 'ipv4'
336
+ peer.connection.remoteAddress = '127.0.0.1'
337
+ peer.connection.remotePort = '0000'
338
+ }
339
+
340
+ const peerInfo = {
341
+ family: peer.connection.remoteFamily || peer.connection.localFamily,
342
+ address: peer.connection.remoteAddress || peer.connection.localAddress,
343
+ port: peer.connection.remotePort || peer.connection.localPort,
344
+ id: from,
345
+ }
346
+
347
+ if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash)
348
+ }
349
+ return
350
+ }
351
+
352
+ /**
353
+ * Override DHT behavior, try's finding the content three times
354
+ *
355
+ * @param {String} hash
356
+ */
357
+ async providersFor(hash) {
358
+ let providers = await this.dht.providersFor(hash)
359
+ // walk the network to find a provider
360
+ if (!providers || providers.length === 0) {
361
+ await this.walk(hash)
362
+ providers = await this.dht.providersFor(hash)
363
+ // second walk the network to find a provider
364
+ if (!providers || providers.length === 0) {
365
+ await this.walk(hash)
366
+ providers = await this.dht.providersFor(hash)
367
+ }
368
+ // last walk
369
+ if (!providers || providers.length === 0) {
370
+ await this.walk(hash)
371
+ providers = await this.dht.providersFor(hash)
372
+ }
373
+ }
374
+ // undefined if no providers given
375
+ return providers
376
+ }
377
+
378
+ get block() {
379
+ return {
380
+ get: async (hash) => {
381
+ const data = await blockStore.has(hash)
382
+ if (data) return await blockStore.get(hash)
383
+ return this.requestData(hash, 'block')
384
+ },
385
+ put: async (hash, data) => {
386
+ if (await blockStore.has(hash)) return
387
+ return await blockStore.put(hash, data)
388
+ },
389
+ has: async (hash) => await blockStore.has(hash, 'block'),
390
+ }
391
+ }
392
+
393
+ get transaction() {
394
+ return {
395
+ get: async (hash) => {
396
+ const data = await transactionStore.has(hash)
397
+ if (data) return await transactionStore.get(hash)
398
+ return this.requestData(hash, 'transaction')
399
+ },
400
+ put: async (hash, data) => {
401
+ if (await transactionStore.has(hash)) return
402
+ return await transactionStore.put(hash, data)
403
+ },
404
+ has: async (hash) => await transactionStore.has(hash),
405
+ }
406
+ }
407
+
408
+ async requestData(hash, store) {
409
+ const providers = await this.providersFor(hash)
410
+ if (!providers || providers.size === 0) throw nothingFoundError(hash)
411
+ debug(`found ${providers.size} provider(s) for ${hash}`)
412
+ // get closest peer on earth
413
+ const closestPeer = await this.dht.closestPeer(providers)
414
+ // get peer instance by id
415
+ if (!closestPeer || !closestPeer.id) return this.requestData(hash, store.name ? store.name : store)
416
+
417
+ const id = closestPeer.id.toString()
418
+ if (this.peers) {
419
+ let closest = this.peers.filter((peer) => {
420
+ if (this._getPeerId(peer.id) === id) return peer
421
+ })
422
+
423
+ let data = new DataMessage({hash, store: store.name ? store.name : store});
424
+
425
+ const node = await this.prepareMessage(id, data.encoded)
426
+ if (closest[0]) data = await closest[0].request(node.encoded)
427
+ else {
428
+ closest = this.peers.filter((peer) => {
429
+ if (peer.id.toString() === id) return peer
430
+ })
431
+ if (closest[0]) data = await closest[0].request(node.encoded)
432
+ }
433
+ if (data.data) {
434
+ let proto = protoFor(Buffer.from(data.data))
435
+ proto = protoFor(proto.decoded.data)
436
+ return proto.decoded.data
437
+ }
438
+
439
+ // this.put(hash, proto.decoded.data)
440
+ }
441
+ return null
442
+ }
443
+
444
+
445
+ get message() {
446
+ return {
447
+ /**
448
+ * Get content for given message hash
449
+ *
450
+ * @param {String} hash
451
+ */
452
+ get: async (hash) => {
453
+ debug(`get message ${hash}`)
454
+ const message = await messageStore.has(hash)
455
+ if (message) return await messageStore.get(hash)
456
+ return this.requestData(hash, 'message')
457
+ },
458
+ /**
459
+ * put message content
460
+ *
461
+ * @param {String} hash
462
+ * @param {Buffer} message
463
+ */
464
+ put: async (hash, message) => await messageStore.put(hash, message),
465
+ /**
466
+ * @param {String} hash
467
+ * @return {Boolean}
468
+ */
469
+ has: async (hash) => await messageStore.has(hash),
470
+ }
471
+ }
472
+
473
+ get data() {
474
+ return {
475
+ /**
476
+ * Get content for given data hash
477
+ *
478
+ * @param {String} hash
479
+ */
480
+ get: async (hash) => {
481
+ debug(`get data ${hash}`)
482
+ const data = await dataStore.has(hash)
483
+ if (data) return await dataStore.get(hash)
484
+ return this.requestData(hash, 'data')
485
+ },
486
+ /**
487
+ * put data content
488
+ *
489
+ * @param {String} hash
490
+ * @param {Buffer} data
491
+ */
492
+ put: async (hash, data) => await dataStore.put(hash, data),
493
+ /**
494
+ * @param {String} hash
495
+ * @return {Boolean}
496
+ */
497
+ has: async (hash) => await dataStore.has(hash),
498
+ }
499
+ }
500
+
501
+ /**
502
+ * goes trough given stores and tries to find data for given hash
503
+ * @param {Array} stores
504
+ * @param {string} hash
505
+ */
506
+ async whichStore(stores, hash) {
507
+ let store = stores.pop()
508
+ const name = store
509
+ store = globalThis[`${store}Store`]
510
+ if (store) {
511
+ const has = await store.has(hash)
512
+ if (has) return store
513
+ if (stores.length > 0) return this.whichStore(stores, hash)
514
+ } else return null
515
+ }
516
+
517
+ /**
518
+ * Get content for given hash
519
+ *
520
+ * @param {String} hash - the hash of the wanted data
521
+ * @param {String} store - storeName to access
522
+ */
523
+ async get(hash, store) {
524
+ debug(`get ${hash}`)
525
+ let data
526
+ if (store) store = globalThis[`${store}Store`]
527
+ if (!store) store = await this.whichStore([...this.stores], hash)
528
+ if (store && await store.has(hash)) data = await store.get(hash)
529
+ if (data) return data
530
+
531
+ return this.requestData(hash, store.name ? store.name : store)
532
+ }
533
+
534
+ /**
535
+ * put content
536
+ *
537
+ * @param {String} hash
538
+ * @param {Buffer} data
539
+ * @param {String} store - storeName to access
540
+ */
541
+ async put(hash, data, store = 'data') {
542
+ store = globalThis[`${store}Store`]
543
+ return store.put(hash, data)
544
+ }
545
+
546
+ /**
547
+ * @param {String} hash
548
+ * @return {Boolean}
549
+ */
550
+ async has(hash) {
551
+ const store = await this.whichStore([...this.stores], hash)
552
+ if (store) {
553
+ if (store.private) return false
554
+ else return true
555
+ }
556
+ return false
557
+ }
558
+
559
+ /**
560
+ *
561
+ * @param {String} topic
562
+ * @param {String|Object|Array|Boolean|Buffer} data
563
+ */
564
+ async publish(topic, data) {
565
+ // globalSub.publish(topic, data)
566
+ if (!Buffer.isBuffer(topic)) topic = Buffer.from(topic)
567
+ if (!Buffer.isBuffer(data)) data = Buffer.from(data)
568
+ const id = Math.random().toString(36).slice(-12)
569
+ data = new PsMessage({data, topic})
570
+ for (const peer of this.peers) {
571
+ if (peer.connection._connected) {
572
+ if (peer.id.toString() !== this.peerId.toString()) {
573
+ const node = await this.prepareMessage(peer.id, data.encoded)
574
+ peer.send(Buffer.from(JSON.stringify({id, data: node.encoded})))
575
+ }
576
+ } else {
577
+ this.removePeer(peer)
578
+ }
579
+ // TODO: if peer subscribed
580
+ }
581
+ }
582
+
583
+ createHash(data, name) {
584
+ return new Hash(data, {name})
585
+ }
586
+
587
+ /**
588
+ *
589
+ * @param {String} topic
590
+ * @param {Method} cb
591
+ */
592
+ async subscribe(topic, cb) {
593
+ // TODO: if peer subscribed
594
+ globalSub.subscribe(topic, cb)
595
+ }
596
+
597
+ async removePeer(peer) {
598
+ connections.delete(peer.id)
599
+ }
600
+
601
+ get Buffer() {
602
+ return Buffer
603
+ }
604
+ // async block(index) {
605
+ // const _values = []
606
+ // for (const peer of this.peers) {
607
+ // const value = await peer.request({type: 'block', index})
608
+ // console.log(value);
609
+ // }
610
+ //
611
+ // }
612
+ }