@leofcoin/peernet 0.16.5 → 0.16.7

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