@leofcoin/peernet 1.0.2 → 1.0.5

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