@leofcoin/peernet 0.16.6 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1126 +0,0 @@
1
- 'use strict';
2
-
3
- require('@vandeurenglenn/debug');
4
- var PubSub = require('@vandeurenglenn/little-pubsub');
5
- var codecFormatInterface = require('@leofcoin/codec-format-interface');
6
- var fetch$1 = require('node-fetch');
7
- var LeofcoinStorage = require('@leofcoin/storage/src/storage');
8
-
9
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
-
11
- function _interopNamespace(e) {
12
- if (e && e.__esModule) return e;
13
- var n = Object.create(null);
14
- if (e) {
15
- Object.keys(e).forEach(function (k) {
16
- if (k !== 'default') {
17
- var d = Object.getOwnPropertyDescriptor(e, k);
18
- Object.defineProperty(n, k, d.get ? d : {
19
- enumerable: true,
20
- get: function () { return e[k]; }
21
- });
22
- }
23
- });
24
- }
25
- n["default"] = e;
26
- return Object.freeze(n);
27
- }
28
-
29
- var PubSub__default = /*#__PURE__*/_interopDefaultLegacy(PubSub);
30
- var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch$1);
31
- var LeofcoinStorage__default = /*#__PURE__*/_interopDefaultLegacy(LeofcoinStorage);
32
-
33
- const BufferToUint8Array = data => {
34
- if (data.type === 'Buffer') {
35
- data = new Uint8Array(data.data);
36
- }
37
- return data
38
- };
39
-
40
- const protoFor = (message) => {
41
- const codec = new codecFormatInterface.Codec(message);
42
- if (!codec.name) throw new Error('proto not found')
43
- const Proto = globalThis.peernet.protos[codec.name];
44
- if (!Proto) throw (new Error(`No proto defined for ${codec.name}`))
45
- return new Proto(message)
46
- };
47
-
48
- /**
49
- * wether or not a peernet daemon is active
50
- * @return {Boolean}
51
- */
52
- const hasDaemon = async () => {
53
- try {
54
- let response = await fetch('http://127.0.0.1:1000/api/version');
55
- response = await response.json();
56
- return Boolean(response.client === '@peernet/api/http')
57
- } catch (e) {
58
- return false
59
- }
60
- };
61
-
62
- const https = () => {
63
- if (!globalThis.location) return false;
64
- return Boolean(globalThis.location.protocol === 'https:')
65
- };
66
-
67
- /**
68
- * Get current environment
69
- * @return {String} current environment [node, electron, browser]
70
- */
71
- const environment = () => {
72
- const _navigator = globalThis.navigator;
73
- if (!_navigator) {
74
- return 'node'
75
- } else if (_navigator && /electron/i.test(_navigator.userAgent)) {
76
- return 'electron'
77
- } else {
78
- return 'browser'
79
- }
80
- };
81
-
82
- /**
83
- * * Get current environment
84
- * @return {Object} result
85
- * @property {Boolean} reult.daemon whether or not daemon is running
86
- * @property {Boolean} reult.environment Current environment
87
- */
88
- const target = async () => {
89
- let daemon = false;
90
- if (!https()) daemon = await hasDaemon();
91
-
92
- return {daemon, environment: environment()}
93
- };
94
-
95
- class PeerDiscovery {
96
- constructor(id) {
97
- this.id = id;
98
- }
99
-
100
- _getPeerId(id) {
101
- if (!peernet.peerMap || peernet.peerMap && peernet.peerMap.size === 0) return false
102
-
103
- for (const entry of [...peernet.peerMap.entries()]) {
104
- for (const _id of entry[1]) {
105
- if (_id === id) return entry[0]
106
- }
107
- }
108
- }
109
-
110
- async discover(peer) {
111
- let id = this._getPeerId(peer.id);
112
- if (id) return id
113
- const data = await new peernet.protos['peernet-peer']({id: this.id});
114
- const node = await peernet.prepareMessage(peer.id, data.encoded);
115
-
116
- let response = await peer.request(node.encoded);
117
- response = await protoFor(response);
118
- response = await new peernet.protos['peernet-peer-response'](response.decoded.data);
119
-
120
- id = response.decoded.id;
121
- if (id === this.id) return;
122
-
123
- if (!peernet.peerMap.has(id)) peernet.peerMap.set(id, [peer.id]);
124
- else {
125
- const connections = peernet.peerMap.get(id);
126
- if (connections.indexOf(peer.id) === -1) {
127
- connections.push(peer.id);
128
- peernet.peerMap.set(peer.id, connections);
129
- }
130
- }
131
- return id
132
- }
133
-
134
- async discoverHandler(message, peer) {
135
- const {id, proto} = message;
136
- // if (typeof message.data === 'string') message.data = Buffer.from(message.data)
137
- if (proto.name === 'peernet-peer') {
138
- const from = proto.decoded.id;
139
- if (from === this.id) return;
140
-
141
- if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
142
- else {
143
- const connections = peernet.peerMap.get(from);
144
- if (connections.indexOf(peer.id) === -1) {
145
- connections.push(peer.id);
146
- peernet.peerMap.set(from, connections);
147
- }
148
- }
149
- const data = await new peernet.protos['peernet-peer-response']({id: this.id});
150
- const node = await peernet.prepareMessage(from, data.encoded);
151
-
152
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
153
- } else if (proto.name === 'peernet-peer-response') {
154
- const from = proto.decoded.id;
155
- if (from === this.id) return;
156
-
157
- if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
158
- else {
159
- const connections = peernet.peerMap.get(from);
160
- if (connections.indexOf(peer.id) === -1) {
161
- connections.push(peer.id);
162
- peernet.peerMap.set(from, connections);
163
- }
164
- }
165
- }
166
- }
167
- }
168
-
169
- /**
170
- * Keep history of fetched address and ptr
171
- * @property {Object} address
172
- * @property {Object} ptr
173
- */
174
- const lastFetched = {
175
- address: {
176
- value: undefined,
177
- timestamp: 0,
178
- },
179
- ptr: {
180
- value: undefined,
181
- timestamp: 0,
182
- },
183
- };
184
-
185
- const getAddress = async () => {
186
- const {address} = lastFetched;
187
- const now = Math.round(new Date().getTime() / 1000);
188
- if (now - address.timestamp > 1200000) {
189
- address.value = await fetch__default["default"]('https://icanhazip.com/');
190
- address.value = await address.value.text();
191
- address.timestamp = Math.round(new Date().getTime() / 1000);
192
- lastFetched.address = address;
193
- }
194
-
195
- return address.value
196
- };
197
-
198
- const degreesToRadians = (degrees) => {
199
- return degrees * Math.PI / 180;
200
- };
201
-
202
- const distanceInKmBetweenEarthCoordinates = (lat1, lon1, lat2, lon2) => {
203
- const earthRadiusKm = 6371;
204
-
205
- const dLat = degreesToRadians(lat2-lat1);
206
- const dLon = degreesToRadians(lon2-lon1);
207
-
208
- lat1 = degreesToRadians(lat1);
209
- lat2 = degreesToRadians(lat2);
210
- const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
211
- Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
212
- const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
213
- return earthRadiusKm * c;
214
- };
215
-
216
- class DhtEarth {
217
- /**
218
- *
219
- */
220
- constructor() {
221
- this.providerMap = new Map();
222
- }
223
-
224
- /**
225
- * @param {Object} address
226
- * @return {Object} {latitude: lat, longitude: lon}
227
- */
228
- async getCoordinates(address) {
229
- // const {address} = parseAddress(provider)
230
- const request = `https://whereis.leofcoin.org/?ip=${address}`;
231
- let response = await fetch__default["default"](request);
232
- response = await response.json();
233
- const {lat, lon} = response;
234
- return {latitude: lat, longitude: lon}
235
- }
236
-
237
- /**
238
- * @param {Object} peer
239
- * @param {Object} provider
240
- * @return {Object} {provider, distance}
241
- */
242
- async getDistance(peer, provider) {
243
- const {latitude, longitude} = await this.getCoordinates(provider.address);
244
- return {provider, distance: distanceInKmBetweenEarthCoordinates(peer.latitude, peer.longitude, latitude, longitude)}
245
- }
246
-
247
- /**
248
- * @param {Array} providers
249
- * @return {Object} closestPeer
250
- */
251
- async closestPeer(providers) {
252
- let all = [];
253
- const address = await getAddress();
254
- const peerLoc = await this.getCoordinates(address);
255
-
256
- for (const provider of providers) {
257
- if (provider.address === '127.0.0.1') all.push({provider, distance: 0});
258
- else all.push(this.getDistance(peerLoc, provider));
259
- }
260
-
261
- all = await Promise.all(all);
262
- all = all.sort((previous, current) => previous.distance - current.distance);
263
- return all[0].provider;
264
- }
265
-
266
- /**
267
- * @param {String} hash
268
- * @return {Array} providers
269
- */
270
- providersFor(hash) {
271
- return this.providerMap.get(hash);
272
- }
273
-
274
- /**
275
- * @param {String} address
276
- * @param {String} hash
277
- * @return {Array} providers
278
- */
279
- async addProvider(address, hash) {
280
- let providers = [];
281
- if (this.providerMap.has(hash)) providers = this.providerMap.get(hash);
282
-
283
- providers = new Set([...providers, address]);
284
- this.providerMap.set(hash, providers);
285
- return providers;
286
- }
287
- }
288
-
289
- var codecs = {
290
- // just a hash
291
- 'disco-hash': {
292
- codec: parseInt('30', 16),
293
- hashAlg: 'dbl-keccak-256', // ,
294
- // testnet: 'olivia'
295
- },
296
- 'peernet-peer-response': {
297
- codec: parseInt('707072', 16),
298
- hashAlg: 'keccak-256',
299
- },
300
- 'peernet-peer': {
301
- codec: parseInt('7070', 16),
302
- hashAlg: 'keccak-256',
303
- },
304
- 'peernet-dht': {
305
- codec: parseInt('706468', 16),
306
- hashAlg: 'keccak-256',
307
- },
308
- 'peernet-dht-response': {
309
- codec: parseInt('706472', 16),
310
- hashAlg: 'keccak-256',
311
- },
312
- // data
313
- 'peernet-data': {
314
- codec: parseInt('706461', 16),
315
- hashAlg: 'keccak-256',
316
- },
317
- 'peernet-data-response': {
318
- codec: parseInt('70646172', 16),
319
- hashAlg: 'keccak-256',
320
- },
321
- // message
322
- 'peernet-message': {
323
- codec: parseInt('706d65', 16),
324
- hashAlg: 'keccak-256',
325
- },
326
- // pubsub
327
- 'peernet-ps': {
328
- codec: parseInt('707073', 16),
329
- hashAlg: 'keccak-256',
330
- },
331
- 'peernet-response': {
332
- codec: parseInt('7072', 16),
333
- hashAlg: 'keccak-256',
334
- },
335
- 'peernet-request': {
336
- codec: parseInt('707271', 16),
337
- hashAlg: 'keccak-256',
338
- },
339
- // normal block
340
- 'leofcoin-block': {
341
- codec: parseInt('6c62', 16),
342
- hashAlg: 'dbl-keccak-512', // ,
343
- // testnet: 'olivia'
344
- },
345
- 'leofcoin-tx': {
346
- codec: parseInt('6c74', 16),
347
- hashAlg: 'dbl-keccak-512', // ,
348
- // testnet: 'olivia'
349
- },
350
- // itx
351
- 'leofcoin-itx': {
352
- codec: parseInt('6c69', 16),
353
- hashAlg: 'keccak-512', // ,
354
- // testnet: 'olivia'
355
- },
356
- // peer reputation
357
- 'leofcoin-pr': {
358
- codec: parseInt('6c70', 16),
359
- hashAlg: 'keccak-256', // ,
360
- // testnet: 'olivia'
361
- },
362
- // chat message
363
- 'chat-message': {
364
- codec: parseInt('70636d', 16),
365
- hashAlg: 'dbl-keccak-256',
366
- },
367
- 'peernet-file' : {
368
- codec: parseInt('7066', 16),
369
- hashAlg: 'keccak-256',
370
- },
371
- 'peernet-file-response' : {
372
- codec: parseInt('706672', 16),
373
- hashAlg: 'keccak-256',
374
- }
375
- };
376
-
377
- class MessageHandler {
378
- constructor(network) {
379
- this.network = network;
380
- }
381
- /**
382
- * hash and sign message
383
- *
384
- * @param {object} message
385
- * @param {Buffer} message.from peer id
386
- * @param {Buffer} message.to peer id
387
- * @param {string} message.data Peernet message
388
- * (PeernetMessage excluded) encoded as a string
389
- * @return message
390
- */
391
- async hashAndSignMessage(message) {
392
- let identity = await walletStore.get('identity');
393
- identity = JSON.parse(identity);
394
- if (!globalThis.MultiWallet) {
395
- const importee = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(/* webpackChunkName: "multi-wallet" */ '@leofcoin/multi-wallet')); });
396
- globalThis.MultiWallet = importee.default;
397
- }
398
- const wallet = new MultiWallet(this.network);
399
- wallet.recover(identity.mnemonic);
400
- message.decoded.signature = wallet.sign(Buffer.from(await message.hash).slice(0, 32));
401
- return message
402
- }
403
-
404
- /**
405
- * @param {String} from - peer id
406
- * @param {String} to - peer id
407
- * @param {String|PeernetMessage} data - data encoded message string
408
- * or the messageNode itself
409
- */
410
- async prepareMessage(message) {
411
- if (message.keys.indexOf('signature') !== -1) {
412
- message = await this.hashAndSignMessage(message);
413
- }
414
-
415
- return message
416
- }
417
- }
418
-
419
- const dataHandler = async message => {
420
- if (!message) return
421
-
422
- const {data, id, from} = message;
423
- const proto = await protoFor(data);
424
-
425
- peernet._protoHandler({id, proto}, peernet.client.connections[from], from);
426
- };
427
-
428
- const dhtError = (proto) => {
429
- const text = `Received proto ${proto.name} expected peernet-dht-response`;
430
- return new Error(`Routing error: ${text}`)
431
- };
432
-
433
- const nothingFoundError = (hash) => {
434
- return new Error(`nothing found for ${hash}`)
435
- };
436
-
437
- globalThis.LeofcoinStorage = LeofcoinStorage__default["default"];
438
-
439
- globalThis.leofcoin = globalThis.leofcoin || {};
440
- globalThis.pubsub = globalThis.pubsub || new PubSub__default["default"]();
441
- globalThis.globalSub = globalThis.globalSub || new PubSub__default["default"]({verbose: true});
442
-
443
- /**
444
- * @access public
445
- * @example
446
- * const peernet = new Peernet();
447
- */
448
- class Peernet {
449
- /**
450
- * @access public
451
- * @param {Object} options
452
- * @param {String} options.network - desired network
453
- * @param {String} options.stars - star list for selected network (these should match, don't mix networks)
454
- * @param {String} options.root - path to root directory
455
- * @param {String} options.storePrefix - prefix for datatores (lfc)
456
- *
457
- * @return {Promise} instance of Peernet
458
- *
459
- * @example
460
- * const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
461
- */
462
- constructor(options = {}) {
463
- this._discovered = [];
464
- /**
465
- * @property {String} network - current network
466
- */
467
- this.network = options.network || 'leofcoin';
468
- this.stars = options.stars;
469
- const parts = this.network.split(':');
470
- this.networkVersion = options.networkVersion ? options.networkVersion : parts.length > 1 ? parts[1] : 'mainnet';
471
-
472
- if (!options.storePrefix) options.storePrefix = 'lfc';
473
- if (!options.port) options.port = 2000;
474
- if (!options.root) {
475
- if (parts[1]) options.root = `.${parts[0]}/${parts[1]}`;
476
- else options.root = `.${this.network}`;
477
- }
478
-
479
- globalThis.peernet = this;
480
- this.bw = {
481
- up: 0,
482
- down: 0,
483
- };
484
- return this._init(options)
485
- }
486
-
487
- get defaultStores() {
488
- return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
489
- }
490
-
491
- get codecs() {
492
- return codecs
493
- }
494
-
495
- addProto(name, proto) {
496
- if (!globalThis.peernet.protos[name]) globalThis.peernet.protos[name] = proto;
497
- }
498
-
499
- addCodec(name, codec) {
500
- if (!this.codecs[name]) this.codecs[name] = codec;
501
- }
502
-
503
- async addStore(name, prefix, root, isPrivate = true) {
504
- if (name === 'block' || name === 'transaction' || name === 'chain' ||
505
- name === 'data' || name === 'message') isPrivate = false;
506
-
507
- let Storage;
508
- if (this.hasDaemon) {
509
- Storage = LeofcoinStorageClient;
510
- } else {
511
- Storage = LeofcoinStorage__default["default"];
512
- }
513
- globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
514
- await new Storage(name, root);
515
-
516
- globalThis[`${name}Store`].private = isPrivate;
517
- if (!isPrivate) this.stores.push(name);
518
- }
519
-
520
-
521
- /**
522
- * @see MessageHandler
523
- */
524
- prepareMessage(data) {
525
- return this._messageHandler.prepareMessage(data)
526
- }
527
-
528
- /**
529
- * @access public
530
- *
531
- * @return {Array} peerId
532
- */
533
- get peers() {
534
- return Object.keys(this.client.connections)
535
- }
536
-
537
- get connections() {
538
- return Object.values(this.client.connections)
539
- }
540
-
541
- get peerEntries() {
542
- return Object.entries(this.client.connections)
543
- }
544
-
545
- /**
546
- * @return {String} id - peerId
547
- */
548
- getConnection(id) {
549
- return this.client.connections[id]
550
- }
551
-
552
- /**
553
- * @private
554
- *
555
- * @param {Object} options
556
- * @param {String} options.root - path to root directory
557
- *
558
- * @return {Promise} instance of Peernet
559
- */
560
- async _init(options) {
561
- // peernetDHT aka closesPeer by coordinates
562
- /**
563
- * @type {Object}
564
- * @property {Object} peer Instance of Peer
565
- */
566
- this.dht = new DhtEarth();
567
- /**
568
- * @type {Map}
569
- * @property {Object} peer Instance of Peer
570
- */
571
- this.stores = [];
572
- this.requestProtos = {};
573
- this.storePrefix = options.storePrefix;
574
- this.root = options.root;
575
-
576
- const {
577
- RequestMessage,
578
- ResponseMessage,
579
- PeerMessage,
580
- PeerMessageResponse,
581
- PeernetMessage,
582
- DHTMessage,
583
- DHTMessageResponse,
584
- DataMessage,
585
- DataMessageResponse,
586
- PsMessage,
587
- ChatMessage,
588
- PeernetFile
589
- // FolderMessageResponse
590
- } = await Promise.resolve().then(function () { return require(/* webpackChunkName: "messages" */ './messages-f46a3ca1.js'); });
591
-
592
- /**
593
- * proto Object containing protos
594
- * @type {Object}
595
- * @property {PeernetMessage} protos[peernet-message] messageNode
596
- * @property {DHTMessage} protos[peernet-dht] messageNode
597
- * @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
598
- * @property {DataMessage} protos[peernet-data] messageNode
599
- * @property {DataMessageResponse} protos[peernet-data-response] messageNode
600
- */
601
-
602
- globalThis.peernet.protos = {
603
- 'peernet-request': RequestMessage,
604
- 'peernet-response': ResponseMessage,
605
- 'peernet-peer': PeerMessage,
606
- 'peernet-peer-response': PeerMessageResponse,
607
- 'peernet-message': PeernetMessage,
608
- 'peernet-dht': DHTMessage,
609
- 'peernet-dht-response': DHTMessageResponse,
610
- 'peernet-data': DataMessage,
611
- 'peernet-data-response': DataMessageResponse,
612
- 'peernet-ps': PsMessage,
613
- 'chat-message': ChatMessage,
614
- 'peernet-file': PeernetFile
615
- };
616
-
617
- this._messageHandler = new MessageHandler(this.network);
618
-
619
- const {daemon, environment} = await target();
620
- this.hasDaemon = daemon;
621
-
622
- for (const store of this.defaultStores) {
623
- await this.addStore(store, options.storePrefix, options.root);
624
- }
625
-
626
- const accountExists = await accountStore.has('public');
627
- if (accountExists) {
628
- const pub = await accountStore.get('public');
629
- this.id = JSON.parse(new TextDecoder().decode(pub)).walletId;
630
- let accounts = await walletStore.get('accounts');
631
- accounts = new TextDecoder().decode(accounts);
632
- const selected = await walletStore.get('selected-account');
633
- globalThis.peernet.selectedAccount = new TextDecoder().decode(selected);
634
-
635
- // fixing account issue (string while needs to be a JSON)
636
- // TODO: remove when on mainnet
637
- try {
638
- this.accounts = JSON.parse(accounts);
639
- } catch (e) {
640
- this.accounts = [accounts.split(',')];
641
- }
642
- } else {
643
- const importee = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(/* webpackChunkName: "generate-account" */ '@leofcoin/generate-account')); });
644
- const generateAccount = importee.default;
645
- const {identity, accounts, config} = await generateAccount(this.network);
646
- // await accountStore.put('config', JSON.stringify(config));
647
- await accountStore.put('public', JSON.stringify({walletId: identity.walletId}));
648
-
649
- await walletStore.put('version', String(1));
650
- await walletStore.put('accounts', JSON.stringify(accounts));
651
- await walletStore.put('selected-account', accounts[0][1]);
652
- await walletStore.put('identity', JSON.stringify(identity));
653
-
654
- globalThis.peernet.selectedAccount = accounts[0][1];
655
- this.id = identity.walletId;
656
- }
657
- this._peerHandler = new PeerDiscovery(this.id);
658
- this.peerId = this.id;
659
-
660
- pubsub.subscribe('peer:connected', async (peer) => {
661
- // console.log(peer);
662
- // console.log({connected: peer.id, as: this._getPeerId(peer.id) });
663
- // peer.on('peernet.data', async (message) => {
664
- // const id = message.id
665
- // message = new PeernetMessage(Buffer.from(message.data.data))
666
- // const proto = protoFor(message.decoded.data)
667
- // this._protoHandler({id, proto}, peer)
668
- // })
669
- });
670
-
671
- /**
672
- * converts data -> message -> proto
673
- * @see DataHandler
674
- */
675
- pubsub.subscribe('peer:data', dataHandler);
676
-
677
-
678
- const importee = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(/* webpackChunkName: "peernet-swarm" */ '@leofcoin/peernet-swarm')); });
679
- /**
680
- * @access public
681
- * @type {PeernetClient}
682
- */
683
- this.client = new importee.default(this.id, this.networkVersion, this.stars);
684
- if (globalThis.onbeforeunload) {
685
- globalThis.addEventListener('beforeunload', async () => this.client.close());
686
- } else {
687
- process.on('SIGTERM', async () => {
688
- process.stdin.resume();
689
- await this.client.close();
690
- process.exit();
691
- });
692
- }
693
- return this
694
- }
695
-
696
- addRequestHandler(name, method) {
697
- this.requestProtos[name] = method;
698
- }
699
-
700
- sendMessage(peer, id, data) {
701
- if (peer.readyState === 'open') {
702
- peer.send(data, id);
703
- this.bw.up += data.length;
704
- } else if (peer.readyState === 'closed') ;
705
-
706
- }
707
-
708
- /**
709
- * @private
710
- *
711
- * @param {Buffer} message - peernet message
712
- * @param {PeernetPeer} peer - peernet peer
713
- */
714
- async _protoHandler(message, peer, from) {
715
-
716
- const {id, proto} = message;
717
- this.bw.down += proto.encoded.length;
718
- if (proto.name === 'peernet-dht') {
719
- let { hash, store } = proto.decoded;
720
- let has;
721
-
722
- if (!store) {
723
- has = await this.has(hash);
724
- } else {
725
- store = globalThis[`${store}Store`];
726
- if (store.private) has = false;
727
- else has = await store.has(hash);
728
- }
729
- const data = await new globalThis.peernet.protos['peernet-dht-response']({hash, has});
730
- const node = await this.prepareMessage(data);
731
-
732
- this.sendMessage(peer, id, node.encoded);
733
- } else if (proto.name === 'peernet-data') {
734
- let { hash, store } = proto.decoded;
735
- let data;
736
- if (!store) {
737
- store = await this.whichStore([...this.stores], hash);
738
- } else {
739
- store = globalThis[`${store}Store`];
740
- }
741
- if (store && !store.private) {
742
- data = await store.get(hash);
743
-
744
- if (data) {
745
- data = await new globalThis.peernet.protos['peernet-data-response']({hash, data});
746
-
747
- const node = await this.prepareMessage(data);
748
- this.sendMessage(peer, id, node.encoded);
749
- }
750
- }
751
-
752
- } else if (proto.name === 'peernet-request') {
753
- const method = this.requestProtos[proto.decoded.request];
754
- if (method) {
755
- const data = await method();
756
- const node = await this.prepareMessage(data);
757
- this.sendMessage(peer, id, node.encoded);
758
- }
759
- } else if (proto.name === 'peernet-ps' && peer.peerId !== this.id) {
760
- globalSub.publish(proto.decoded.topic, proto.decoded.data);
761
- }
762
- // }
763
- }
764
-
765
- /**
766
- * performs a walk and resolves first encounter
767
- *
768
- * @param {String} hash
769
- */
770
- async walk(hash) {
771
- if (!hash) throw new Error('hash expected, received undefined')
772
- const data = await new globalThis.peernet.protos['peernet-dht']({hash});
773
- this.client.id;
774
- const walk = async peer => {
775
- const node = await this.prepareMessage(data);
776
- let result = await peer.request(node.encoded);
777
- result = new Uint8Array(Object.values(result));
778
- const proto = await protoFor(result);
779
- if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
780
-
781
- // TODO: give ip and port (just used for location)
782
- if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
783
- peer.connection.remoteFamily = 'ipv4';
784
- peer.connection.remoteAddress = '127.0.0.1';
785
- peer.connection.remotePort = '0000';
786
- }
787
-
788
- const peerInfo = {
789
- family: peer.connection.remoteFamily || peer.connection.localFamily,
790
- address: peer.connection.remoteAddress || peer.connection.localAddress,
791
- port: peer.connection.remotePort || peer.connection.localPort,
792
- id: peer.peerId,
793
- };
794
-
795
- if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash);
796
- };
797
- let walks = [];
798
- for (const peer of this.connections) {
799
- if (peer.peerId !== this.id) {
800
- walks.push(walk(peer));
801
- }
802
- }
803
- return Promise.all(walks)
804
- }
805
-
806
- /**
807
- * Override DHT behavior, try's finding the content three times
808
- *
809
- * @param {String} hash
810
- */
811
- async providersFor(hash) {
812
- let providers = await this.dht.providersFor(hash);
813
- // walk the network to find a provider
814
- if (!providers || providers.length === 0) {
815
- await this.walk(hash);
816
- providers = await this.dht.providersFor(hash);
817
- // second walk the network to find a provider
818
- if (!providers || providers.length === 0) {
819
- await this.walk(hash);
820
- providers = await this.dht.providersFor(hash);
821
- }
822
- // last walk
823
- if (!providers || providers.length === 0) {
824
- await this.walk(hash);
825
- providers = await this.dht.providersFor(hash);
826
- }
827
- }
828
- // undefined if no providers given
829
- return providers
830
- }
831
-
832
- get block() {
833
- return {
834
- get: async (hash) => {
835
- const data = await blockStore.has(hash);
836
- if (data) return blockStore.get(hash)
837
- return this.requestData(hash, 'block')
838
- },
839
- put: async (hash, data) => {
840
- if (await blockStore.has(hash)) return
841
- return await blockStore.put(hash, data)
842
- },
843
- has: async (hash) => await blockStore.has(hash, 'block'),
844
- }
845
- }
846
-
847
- get transaction() {
848
- return {
849
- get: async (hash) => {
850
- const data = await transactionStore.has(hash);
851
- if (data) return await transactionStore.get(hash)
852
- return this.requestData(hash, 'transaction')
853
- },
854
- put: async (hash, data) => {
855
- if (await transactionStore.has(hash)) return
856
- return await transactionStore.put(hash, data)
857
- },
858
- has: async (hash) => await transactionStore.has(hash),
859
- }
860
- }
861
-
862
- async requestData(hash, store) {
863
- const providers = await this.providersFor(hash);
864
- if (!providers || providers.size === 0) throw nothingFoundError(hash)
865
- debug(`found ${providers.size} provider(s) for ${hash}`);
866
- // get closest peer on earth
867
- const closestPeer = await this.dht.closestPeer(providers);
868
- // get peer instance by id
869
- if (!closestPeer || !closestPeer.id) return this.requestData(hash, store?.name ? store?.name : store)
870
-
871
- const id = closestPeer.id;
872
- if (this.connections) {
873
- let closest = this.connections.filter((peer) => {
874
- if (peer.peerId === id) return peer
875
- });
876
-
877
- let data = await new globalThis.peernet.protos['peernet-data']({hash, store: store?.name ? store?.name : store});
878
-
879
- const node = await this.prepareMessage(data);
880
- if (closest[0]) data = await closest[0].request(node.encoded);
881
- else {
882
- closest = this.connections.filter((peer) => {
883
- if (peer.peerId === id) return peer
884
- });
885
- if (closest[0]) data = await closest[0].request(node.encoded);
886
- }
887
- data = new Uint8Array(Object.values(data));
888
- const proto = await protoFor(data);
889
- // TODO: store data automaticly or not
890
- return BufferToUint8Array(proto.decoded.data)
891
-
892
- // this.put(hash, proto.decoded.data)
893
- }
894
- return null
895
- }
896
-
897
-
898
- get message() {
899
- return {
900
- /**
901
- * Get content for given message hash
902
- *
903
- * @param {String} hash
904
- */
905
- get: async (hash) => {
906
- debug(`get message ${hash}`);
907
- const message = await messageStore.has(hash);
908
- if (message) return await messageStore.get(hash)
909
- return this.requestData(hash, 'message')
910
- },
911
- /**
912
- * put message content
913
- *
914
- * @param {String} hash
915
- * @param {Buffer} message
916
- */
917
- put: async (hash, message) => await messageStore.put(hash, message),
918
- /**
919
- * @param {String} hash
920
- * @return {Boolean}
921
- */
922
- has: async (hash) => await messageStore.has(hash),
923
- }
924
- }
925
-
926
- get data() {
927
- return {
928
- /**
929
- * Get content for given data hash
930
- *
931
- * @param {String} hash
932
- */
933
- get: async (hash) => {
934
- debug(`get data ${hash}`);
935
- const data = await dataStore.has(hash);
936
- if (data) return await dataStore.get(hash)
937
- return this.requestData(hash, 'data')
938
- },
939
- /**
940
- * put data content
941
- *
942
- * @param {String} hash
943
- * @param {Buffer} data
944
- */
945
- put: async (hash, data) => await dataStore.put(hash, data),
946
- /**
947
- * @param {String} hash
948
- * @return {Boolean}
949
- */
950
- has: async (hash) => await dataStore.has(hash),
951
- }
952
- }
953
-
954
- get folder() {
955
- return {
956
- /**
957
- * Get content for given data hash
958
- *
959
- * @param {String} hash
960
- */
961
- get: async (hash) => {
962
- debug(`get data ${hash}`);
963
- const data = await dataStore.has(hash);
964
- if (data) return await dataStore.get(hash)
965
- return this.requestData(hash, 'data')
966
- },
967
- /**
968
- * put data content
969
- *
970
- * @param {String} hash
971
- * @param {Buffer} data
972
- */
973
- put: async (hash, data) => await dataStore.put(hash, data),
974
- /**
975
- * @param {String} hash
976
- * @return {Boolean}
977
- */
978
- has: async (hash) => await dataStore.has(hash),
979
- }
980
- }
981
-
982
- async addFolder(files) {
983
- const links = [];
984
- for (const file of files) {
985
- const fileNode = await new globalThis.peernet.protos['peernet-file'](file);
986
- const hash = await fileNode.hash;
987
- await dataStore.put(hash, fileNode.encoded);
988
- links.push({hash, path: file.path});
989
- }
990
- const node = await new globalThis.peernet.protos['peernet-file']({path: '/', links});
991
- const hash = await node.hash;
992
- await dataStore.put(hash, node.encoded);
993
-
994
- return hash
995
- }
996
-
997
- async ls(hash, options) {
998
- let data;
999
- const has = await dataStore.has(hash);
1000
- if (has) data = await dataStore.get(hash);
1001
- else data = await this.requestData(hash, 'data');
1002
-
1003
- const node = await new peernet.protos['peernet-file'](data);
1004
- await node.decode();
1005
- console.log(data);
1006
- const paths = [];
1007
- if (node.decoded?.links.length === 0) throw new Error(`${hash} is a file`)
1008
- for (const {path, hash} of node.decoded.links) {
1009
- paths.push({path, hash});
1010
- }
1011
- if (options?.pin) await dataStore.put(hash, node.encoded);
1012
- return paths
1013
- }
1014
-
1015
- async cat(hash, options) {
1016
- let data;
1017
- const has = await dataStore.has(hash);
1018
- if (has) data = await dataStore.get(hash);
1019
- else data = await this.requestData(hash, 'data');
1020
- const node = await new peernet.protos['peernet-file'](data);
1021
-
1022
- if (node.decoded?.links.length > 0) throw new Error(`${hash} is a directory`)
1023
- if (options?.pin) await dataStore.put(hash, node.encoded);
1024
- return node.decoded.content
1025
- }
1026
-
1027
- /**
1028
- * goes trough given stores and tries to find data for given hash
1029
- * @param {Array} stores
1030
- * @param {string} hash
1031
- */
1032
- async whichStore(stores, hash) {
1033
- let store = stores.pop();
1034
- store = globalThis[`${store}Store`];
1035
- if (store) {
1036
- const has = await store.has(hash);
1037
- if (has) return store
1038
- if (stores.length > 0) return this.whichStore(stores, hash)
1039
- } else return null
1040
- }
1041
-
1042
- /**
1043
- * Get content for given hash
1044
- *
1045
- * @param {String} hash - the hash of the wanted data
1046
- * @param {String} store - storeName to access
1047
- */
1048
- async get(hash, store) {
1049
- debug(`get ${hash}`);
1050
- let data;
1051
- if (store) store = globalThis[`${store}Store`];
1052
- if (!store) store = await this.whichStore([...this.stores], hash);
1053
- if (store && await store.has(hash)) data = await store.get(hash);
1054
- if (data) return data
1055
-
1056
- return this.requestData(hash, store?.name ? store.name : store)
1057
- }
1058
-
1059
- /**
1060
- * put content
1061
- *
1062
- * @param {String} hash
1063
- * @param {Buffer} data
1064
- * @param {String} store - storeName to access
1065
- */
1066
- async put(hash, data, store = 'data') {
1067
- store = globalThis[`${store}Store`];
1068
- return store.put(hash, data)
1069
- }
1070
-
1071
- /**
1072
- * @param {String} hash
1073
- * @return {Boolean}
1074
- */
1075
- async has(hash) {
1076
- const store = await this.whichStore([...this.stores], hash);
1077
- if (store) {
1078
- if (store.private) return false
1079
- else return true
1080
- }
1081
- return false
1082
- }
1083
-
1084
- /**
1085
- *
1086
- * @param {String} topic
1087
- * @param {String|Object|Array|Boolean|Buffer} data
1088
- */
1089
- async publish(topic, data) {
1090
- // globalSub.publish(topic, data)
1091
- const id = Math.random().toString(36).slice(-12);
1092
- data = await new globalThis.peernet.protos['peernet-ps']({data, topic});
1093
- for (const peer of this.connections) {
1094
- if (peer.peerId !== this.peerId) {
1095
- const node = await this.prepareMessage(data);
1096
- this.sendMessage(peer, id, node.encoded);
1097
- }
1098
- // TODO: if peer subscribed
1099
- }
1100
- }
1101
-
1102
- createHash(data, name) {
1103
- return new CodeHash(data, {name})
1104
- }
1105
-
1106
- /**
1107
- *
1108
- * @param {String} topic
1109
- * @param {Method} cb
1110
- */
1111
- async subscribe(topic, cb) {
1112
- // TODO: if peer subscribed
1113
- globalSub.subscribe(topic, cb);
1114
- }
1115
-
1116
- async removePeer(peer) {
1117
- return this.client.removePeer(peer)
1118
- }
1119
-
1120
- get Buffer() {
1121
- return Buffer
1122
- }
1123
- }
1124
- globalThis.Peernet = Peernet;
1125
-
1126
- module.exports = Peernet;